select.php000064400000014412147206762310006546 0ustar00 true, 'selectable-values' => true, ) ); } function wpcf7_select_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $validation_error = wpcf7_get_validation_error( $tag->name ); $class = wpcf7_form_controls_class( $tag->type ); if ( $validation_error ) { $class .= ' wpcf7-not-valid'; } $atts = array(); $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true ); $atts['autocomplete'] = $tag->get_option( 'autocomplete', '[-0-9a-zA-Z]+', true ); if ( $tag->is_required() ) { $atts['aria-required'] = 'true'; } if ( $validation_error ) { $atts['aria-invalid'] = 'true'; $atts['aria-describedby'] = wpcf7_get_validation_error_reference( $tag->name ); } else { $atts['aria-invalid'] = 'false'; } $multiple = $tag->has_option( 'multiple' ); $include_blank = $tag->has_option( 'include_blank' ); $first_as_label = $tag->has_option( 'first_as_label' ); if ( $tag->has_option( 'size' ) ) { $size = $tag->get_option( 'size', 'int', true ); if ( $size ) { $atts['size'] = $size; } elseif ( $multiple ) { $atts['size'] = 4; } else { $atts['size'] = 1; } } if ( $data = (array) $tag->get_data_option() ) { $tag->values = array_merge( $tag->values, array_values( $data ) ); $tag->labels = array_merge( $tag->labels, array_values( $data ) ); } $values = $tag->values; $labels = $tag->labels; $default_choice = $tag->get_default_option( null, array( 'multiple' => $multiple, ) ); if ( $include_blank or empty( $values ) ) { array_unshift( $labels, __( '—Please choose an option—', 'contact-form-7' ) ); array_unshift( $values, '' ); } elseif ( $first_as_label ) { $values[0] = ''; } $html = ''; $hangover = wpcf7_get_hangover( $tag->name ); foreach ( $values as $key => $value ) { if ( $hangover ) { $selected = in_array( $value, (array) $hangover, true ); } else { $selected = in_array( $value, (array) $default_choice, true ); } $item_atts = array( 'value' => $value, 'selected' => $selected, ); $label = isset( $labels[$key] ) ? $labels[$key] : $value; $html .= sprintf( '', wpcf7_format_atts( $item_atts ), esc_html( $label ) ); } $atts['multiple'] = (bool) $multiple; $atts['name'] = $tag->name . ( $multiple ? '[]' : '' ); $html = sprintf( '%4$s', esc_attr( $tag->name ), wpcf7_format_atts( $atts ), $html, $validation_error ); return $html; } add_action( 'wpcf7_swv_create_schema', 'wpcf7_swv_add_select_rules', 10, 2 ); function wpcf7_swv_add_select_rules( $schema, $contact_form ) { $tags = $contact_form->scan_form_tags( array( 'type' => array( 'select*' ), ) ); foreach ( $tags as $tag ) { $schema->add_rule( wpcf7_swv_create_rule( 'required', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_required' ), ) ) ); } } add_action( 'wpcf7_swv_create_schema', 'wpcf7_swv_add_select_enum_rules', 20, 2 ); function wpcf7_swv_add_select_enum_rules( $schema, $contact_form ) { $tags = $contact_form->scan_form_tags( array( 'basetype' => array( 'select' ), ) ); $values = array_reduce( $tags, function ( $values, $tag ) { if ( ! isset( $values[$tag->name] ) ) { $values[$tag->name] = array(); } $tag_values = array_merge( (array) $tag->values, (array) $tag->get_data_option() ); if ( $tag->has_option( 'first_as_label' ) ) { $tag_values = array_slice( $tag_values, 1 ); } $values[$tag->name] = array_merge( $values[$tag->name], $tag_values ); return $values; }, array() ); foreach ( $values as $field => $field_values ) { $field_values = array_map( static function ( $value ) { return html_entity_decode( (string) $value, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); }, $field_values ); $field_values = array_filter( array_unique( $field_values ), static function ( $value ) { return '' !== $value; } ); $schema->add_rule( wpcf7_swv_create_rule( 'enum', array( 'field' => $field, 'accept' => array_values( $field_values ), 'error' => $contact_form->filter_message( __( "Undefined value was submitted through this field.", 'contact-form-7' ) ), ) ) ); } } /* Tag generator */ add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_menu', 25, 0 ); function wpcf7_add_tag_generator_menu() { $tag_generator = WPCF7_TagGenerator::get_instance(); $tag_generator->add( 'menu', __( 'drop-down menu', 'contact-form-7' ), 'wpcf7_tag_generator_menu', array( 'version' => '2' ) ); } function wpcf7_tag_generator_menu( $contact_form, $options ) { $field_types = array( 'select' => array( 'display_name' => __( 'Drop-down menu', 'contact-form-7' ), 'heading' => __( 'Drop-down menu form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a drop-down menu.', 'contact-form-7' ), ), ); $tgg = new WPCF7_TagGeneratorGenerator( $options['content'] ); ?>

array( 'href' => true ), 'strong' => array(), ), array( 'http', 'https' ) ); echo $description; ?>

print( 'field_type', array( 'with_required' => true, 'select_options' => array( 'select' => $field_types['select']['display_name'], ), ) ); $tgg->print( 'field_name' ); $tgg->print( 'class_attr' ); $tgg->print( 'selectable_values', array( 'first_as_label' => true, ) ); ?>
add_service( 'sendinblue', WPCF7_Sendinblue::get_instance() ); } add_action( 'wpcf7_submit', 'wpcf7_sendinblue_submit', 10, 2 ); /** * Callback to the wpcf7_submit action hook. Creates a contact * based on the submission. */ function wpcf7_sendinblue_submit( $contact_form, $result ) { if ( $contact_form->in_demo_mode() ) { return; } $service = WPCF7_Sendinblue::get_instance(); if ( ! $service->is_active() ) { return; } if ( empty( $result['posted_data_hash'] ) ) { return; } if ( empty( $result['status'] ) or ! in_array( $result['status'], array( 'mail_sent', 'mail_failed' ) ) ) { return; } $submission = WPCF7_Submission::get_instance(); $consented = true; foreach ( $contact_form->scan_form_tags( 'feature=name-attr' ) as $tag ) { if ( $tag->has_option( 'consent_for:sendinblue' ) and null == $submission->get_posted_data( $tag->name ) ) { $consented = false; break; } } if ( ! $consented ) { return; } $prop = wp_parse_args( $contact_form->prop( 'sendinblue' ), array( 'enable_contact_list' => false, 'contact_lists' => array(), 'enable_transactional_email' => false, 'email_template' => 0, ) ); if ( ! $prop['enable_contact_list'] ) { return; } $attributes = wpcf7_sendinblue_collect_parameters(); $params = array( 'contact' => array(), 'email' => array(), ); if ( ! empty( $attributes['EMAIL'] ) or ! empty( $attributes['SMS'] ) ) { $params['contact'] = apply_filters( 'wpcf7_sendinblue_contact_parameters', array( 'email' => $attributes['EMAIL'], 'attributes' => (object) $attributes, 'listIds' => (array) $prop['contact_lists'], 'updateEnabled' => false, ) ); } if ( $prop['enable_transactional_email'] and $prop['email_template'] ) { $first_name = isset( $attributes['FIRSTNAME'] ) ? trim( $attributes['FIRSTNAME'] ) : ''; $last_name = isset( $attributes['LASTNAME'] ) ? trim( $attributes['LASTNAME'] ) : ''; if ( $first_name or $last_name ) { $email_to_name = sprintf( /* translators: 1: first name, 2: last name */ _x( '%1$s %2$s', 'personal name', 'contact-form-7' ), $first_name, $last_name ); } else { $email_to_name = ''; } $params['email'] = apply_filters( 'wpcf7_sendinblue_email_parameters', array( 'templateId' => absint( $prop['email_template'] ), 'to' => array( array( 'name' => $email_to_name, 'email' => $attributes['EMAIL'], ), ), 'params' => (object) $attributes, 'tags' => array( 'Contact Form 7' ), ) ); } if ( is_email( $attributes['EMAIL'] ) ) { $token = null; do_action_ref_array( 'wpcf7_doi', array( 'wpcf7_sendinblue', array( 'email_to' => $attributes['EMAIL'], 'properties' => $params, ), &$token, ) ); if ( isset( $token ) ) { return; } } if ( ! empty( $params['contact'] ) ) { $contact_id = $service->create_contact( $params['contact'] ); if ( $contact_id and ! empty( $params['email'] ) ) { $service->send_email( $params['email'] ); } } } /** * Collects parameters for Sendinblue contact data based on submission. * * @return array Sendinblue contact parameters. */ function wpcf7_sendinblue_collect_parameters() { $params = array(); $submission = WPCF7_Submission::get_instance(); foreach ( (array) $submission->get_posted_data() as $name => $val ) { $name = strtoupper( $name ); if ( 'YOUR-' == substr( $name, 0, 5 ) ) { $name = substr( $name, 5 ); } if ( $val ) { $params += array( $name => $val, ); } } if ( isset( $params['SMS'] ) ) { $sms = trim( implode( ' ', (array) $params['SMS'] ) ); $sms = preg_replace( '/[#*].*$/', '', $sms ); // Remove extension $is_international = false || str_starts_with( $sms, '+' ) || str_starts_with( $sms, '00' ); if ( $is_international ) { $sms = preg_replace( '/^[+0]+/', '', $sms ); } $sms = preg_replace( '/[^0-9]/', '', $sms ); if ( $is_international and 6 < strlen( $sms ) and strlen( $sms ) < 16 ) { $params['SMS'] = '+' . $sms; } else { // Invalid telephone number unset( $params['SMS'] ); } } if ( isset( $params['NAME'] ) ) { $your_name = implode( ' ', (array) $params['NAME'] ); $your_name = explode( ' ', $your_name ); if ( ! isset( $params['LASTNAME'] ) ) { $params['LASTNAME'] = implode( ' ', array_slice( $your_name, 1 ) ); } if ( ! isset( $params['FIRSTNAME'] ) ) { $params['FIRSTNAME'] = implode( ' ', array_slice( $your_name, 0, 1 ) ); } } $params = array_map( function ( $param ) { if ( is_array( $param ) ) { $param = wpcf7_array_flatten( $param ); $param = reset( $param ); } return $param; }, $params ); $params = apply_filters( 'wpcf7_sendinblue_collect_parameters', $params ); return $params; } sendinblue/service.php000064400000022305147206762310011057 0ustar00api_key = $option['api_key']; } } public function get_title() { return __( 'Brevo', 'contact-form-7' ); } public function is_active() { return (bool) $this->get_api_key(); } public function get_api_key() { return $this->api_key; } public function get_categories() { return array( 'email_marketing' ); } public function icon() { } public function link() { echo wpcf7_link( 'https://get.brevo.com/wpcf7-integration', 'brevo.com' ); } protected function log( $url, $request, $response ) { wpcf7_log_remote_request( $url, $request, $response ); } protected function menu_page_url( $args = '' ) { $args = wp_parse_args( $args, array() ); $url = menu_page_url( 'wpcf7-integration', false ); $url = add_query_arg( array( 'service' => 'sendinblue' ), $url ); if ( ! empty( $args ) ) { $url = add_query_arg( $args, $url ); } return $url; } protected function save_data() { WPCF7::update_option( 'sendinblue', array( 'api_key' => $this->api_key, ) ); } protected function reset_data() { $this->api_key = null; $this->save_data(); } public function load( $action = '' ) { if ( 'setup' == $action and 'POST' == $_SERVER['REQUEST_METHOD'] ) { check_admin_referer( 'wpcf7-sendinblue-setup' ); if ( ! empty( $_POST['reset'] ) ) { $this->reset_data(); $redirect_to = $this->menu_page_url( 'action=setup' ); } else { $this->api_key = trim( $_POST['api_key'] ?? '' ); $confirmed = $this->confirm_key(); if ( true === $confirmed ) { $redirect_to = $this->menu_page_url( array( 'message' => 'success', ) ); $this->save_data(); } elseif ( false === $confirmed ) { $redirect_to = $this->menu_page_url( array( 'action' => 'setup', 'message' => 'unauthorized', ) ); } else { $redirect_to = $this->menu_page_url( array( 'action' => 'setup', 'message' => 'invalid', ) ); } } wp_safe_redirect( $redirect_to ); exit(); } } public function admin_notice( $message = '' ) { if ( 'unauthorized' === $message ) { wp_admin_notice( sprintf( '%1$s: %2$s', esc_html( __( "Error", 'contact-form-7' ) ), esc_html( __( "You have not been authenticated. Make sure the provided API key is correct.", 'contact-form-7' ) ) ), 'type=error' ); } if ( 'invalid' === $message ) { wp_admin_notice( sprintf( '%1$s: %2$s', esc_html( __( "Error", 'contact-form-7' ) ), esc_html( __( "Invalid key values.", 'contact-form-7' ) ) ), 'type=error' ); } if ( 'success' === $message ) { wp_admin_notice( esc_html( __( "Settings saved.", 'contact-form-7' ) ), 'type=success' ); } } public function display( $action = '' ) { echo sprintf( '

%s

', esc_html( __( "Store and organize your contacts while protecting user privacy on Brevo, the leading CRM & email marketing platform in Europe. Brevo offers unlimited contacts and advanced marketing features.", 'contact-form-7' ) ) ); echo sprintf( '

%s

', wpcf7_link( __( 'https://contactform7.com/sendinblue-integration/', 'contact-form-7' ), __( 'Brevo integration', 'contact-form-7' ) ) ); if ( $this->is_active() ) { echo sprintf( '

%s

', esc_html( __( "Brevo is active on this site.", 'contact-form-7' ) ) ); } if ( 'setup' == $action ) { $this->display_setup(); } else { echo sprintf( '

%2$s

', esc_url( $this->menu_page_url( 'action=setup' ) ), esc_html( __( 'Setup integration', 'contact-form-7' ) ) ); } } private function display_setup() { $api_key = $this->get_api_key(); ?>
is_active() ) { echo esc_html( wpcf7_mask_password( $api_key, 4, 8 ) ); echo sprintf( '', esc_attr( $api_key ) ); } else { echo sprintf( '', esc_attr( $api_key ) ); } ?>
is_active() ) { submit_button( _x( 'Remove key', 'API keys', 'contact-form-7' ), 'small', 'reset' ); } else { submit_button( __( 'Save changes', 'contact-form-7' ) ); } ?>
array( 'Accept' => 'application/json', 'Content-Type' => 'application/json; charset=utf-8', 'API-Key' => $this->get_api_key(), ), ); $response = wp_remote_get( $endpoint, $request ); $response_code = (int) wp_remote_retrieve_response_code( $response ); if ( 200 === $response_code ) { // 200 OK return true; } elseif ( 401 === $response_code ) { // 401 Unauthorized return false; } elseif ( 400 <= $response_code ) { if ( WP_DEBUG ) { $this->log( $endpoint, $request, $response ); } } } public function get_lists( $options = '' ) { $options = wp_parse_args( $options, array( 'limit' => 50, 'offset' => 0, ) ); $endpoint = add_query_arg( $options, 'https://api.sendinblue.com/v3/contacts/lists' ); $request = array( 'headers' => array( 'Accept' => 'application/json', 'Content-Type' => 'application/json; charset=utf-8', 'API-Key' => $this->get_api_key(), ), ); $response = wp_remote_get( $endpoint, $request ); $response_code = (int) wp_remote_retrieve_response_code( $response ); if ( 200 === $response_code ) { // 200 OK $response_body = wp_remote_retrieve_body( $response ); $response_body = json_decode( $response_body, true ); if ( empty( $response_body['lists'] ) ) { return array(); } else { return (array) $response_body['lists']; } } elseif ( 400 <= $response_code ) { if ( WP_DEBUG ) { $this->log( $endpoint, $request, $response ); } } } public function get_templates() { $endpoint = add_query_arg( array( 'templateStatus' => 'true', 'limit' => 100, 'offset' => 0, ), 'https://api.sendinblue.com/v3/smtp/templates' ); $request = array( 'headers' => array( 'Accept' => 'application/json', 'Content-Type' => 'application/json; charset=utf-8', 'API-Key' => $this->get_api_key(), ), ); $response = wp_remote_get( $endpoint, $request ); $response_code = (int) wp_remote_retrieve_response_code( $response ); if ( 200 === $response_code ) { // 200 OK $response_body = wp_remote_retrieve_body( $response ); $response_body = json_decode( $response_body, true ); if ( empty( $response_body['templates'] ) ) { return array(); } else { return (array) $response_body['templates']; } } elseif ( 400 <= $response_code ) { if ( WP_DEBUG ) { $this->log( $endpoint, $request, $response ); } } } public function create_contact( $properties ) { $endpoint = 'https://api.sendinblue.com/v3/contacts'; $request = array( 'headers' => array( 'Accept' => 'application/json', 'Content-Type' => 'application/json; charset=utf-8', 'API-Key' => $this->get_api_key(), ), 'body' => wp_json_encode( $properties ), ); $response = wp_remote_post( $endpoint, $request ); $response_code = (int) wp_remote_retrieve_response_code( $response ); if ( in_array( $response_code, array( 201, 204 ), true ) ) { $contact_id = wp_remote_retrieve_body( $response ); return $contact_id; } elseif ( 400 <= $response_code ) { if ( WP_DEBUG ) { $this->log( $endpoint, $request, $response ); } } return false; } public function send_email( $properties ) { $endpoint = 'https://api.sendinblue.com/v3/smtp/email'; $request = array( 'headers' => array( 'Accept' => 'application/json', 'Content-Type' => 'application/json; charset=utf-8', 'API-Key' => $this->get_api_key(), ), 'body' => wp_json_encode( $properties ), ); $response = wp_remote_post( $endpoint, $request ); $response_code = (int) wp_remote_retrieve_response_code( $response ); if ( 201 === $response_code ) { // 201 Transactional email sent $message_id = wp_remote_retrieve_body( $response ); return $message_id; } elseif ( 400 <= $response_code ) { if ( WP_DEBUG ) { $this->log( $endpoint, $request, $response ); } } return false; } } sendinblue/contact-form-properties.php000064400000016441147206762310014211 0ustar00is_active() ) { $properties += array( 'sendinblue' => array(), ); } return $properties; } add_action( 'wpcf7_save_contact_form', 'wpcf7_sendinblue_save_contact_form', 10, 3 ); /** * Saves the sendinblue property value. */ function wpcf7_sendinblue_save_contact_form( $contact_form, $args, $context ) { $service = WPCF7_Sendinblue::get_instance(); if ( ! $service->is_active() ) { return; } $prop = (array) ( $_POST['wpcf7-sendinblue'] ?? array() ); $prop = wp_parse_args( $prop, array( 'enable_contact_list' => false, 'contact_lists' => array(), 'enable_transactional_email' => false, 'email_template' => 0, ) ); $prop['contact_lists'] = array_map( 'absint', $prop['contact_lists'] ); $prop['email_template'] = absint( $prop['email_template'] ); $contact_form->set_properties( array( 'sendinblue' => $prop, ) ); } add_filter( 'wpcf7_editor_panels', 'wpcf7_sendinblue_editor_panels', 10, 1 ); /** * Builds the editor panel for the sendinblue property. */ function wpcf7_sendinblue_editor_panels( $panels ) { $service = WPCF7_Sendinblue::get_instance(); if ( ! $service->is_active() ) { return $panels; } $contact_form = WPCF7_ContactForm::get_current(); $prop = wp_parse_args( $contact_form->prop( 'sendinblue' ), array( 'enable_contact_list' => false, 'contact_lists' => array(), 'enable_transactional_email' => false, 'email_template' => 0, ) ); $editor_panel = static function () use ( $prop, $service ) { $description = sprintf( esc_html( __( "You can set up the Brevo integration here. For details, see %s.", 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/sendinblue-integration/', 'contact-form-7' ), __( 'Brevo integration', 'contact-form-7' ) ) ); $lists = wpcf7_sendinblue_get_lists(); $templates = $service->get_templates(); ?>

array( 'title' => __( 'Brevo', 'contact-form-7' ), 'callback' => $editor_panel, ), ); return $panels; } /** * Retrieves contact lists from Brevo's database. */ function wpcf7_sendinblue_get_lists() { static $lists = array(); $service = WPCF7_Sendinblue::get_instance(); if ( ! empty( $lists ) or ! $service->is_active() ) { return $lists; } $limit = 50; $offset = 0; while ( count( $lists ) < $limit * 10 ) { $lists_next = (array) $service->get_lists( array( 'limit' => $limit, 'offset' => $offset, ) ); if ( ! empty( $lists_next ) ) { $lists = array_merge( $lists, $lists_next ); } if ( count( $lists_next ) < $limit ) { break; } $offset += $limit; } return $lists; } sendinblue/doi.php000064400000004433147206762310010174 0ustar00 apply_filters( 'wpcf7_sendinblue_doi_optin_callback', 'wpcf7_sendinblue_doi_default_optin_callback' ), 'email_callback' => apply_filters( 'wpcf7_sendinblue_doi_email_callback', 'wpcf7_sendinblue_doi_default_email_callback' ), ) ); } /** * Default optin_callback function. */ function wpcf7_sendinblue_doi_default_optin_callback( $properties ) { $service = WPCF7_Sendinblue::get_instance(); if ( ! $service->is_active() ) { return; } if ( ! empty( $properties['contact'] ) ) { $contact_id = $service->create_contact( $properties['contact'] ); if ( $contact_id and ! empty( $properties['email'] ) ) { $service->send_email( $properties['email'] ); } } } /** * Default email_callback function. */ function wpcf7_sendinblue_doi_default_email_callback( $args ) { if ( ! isset( $args['token'] ) or ! isset( $args['email_to'] ) ) { return; } $site_title = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); $link = add_query_arg( array( 'doitoken' => $args['token'] ), home_url() ); $to = $args['email_to']; $subject = sprintf( /* translators: %s: blog name */ __( 'Opt-in confirmation from %s', 'contact-form-7' ), $site_title ); $message = sprintf( /* translators: 1: blog name, 2: confirmation link */ __( 'Hello, This is a confirmation email sent from %1$s. We have received your submission to our web form, according to which you have allowed us to add you to our contact list. But, the process has not yet been completed. To complete it, please click the following link. %2$s If it was not your intention, or if you have no idea why you received this message, please do not click on the link, and ignore this message. We will never collect or use your personal data without your clear consent. Sincerely, %1$s', 'contact-form-7' ), $site_title, $link ); wp_mail( $to, $subject, $message ); } number.php000064400000014441147206762310006561 0ustar00 true, ) ); } function wpcf7_number_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $validation_error = wpcf7_get_validation_error( $tag->name ); $class = wpcf7_form_controls_class( $tag->type ); $class .= ' wpcf7-validates-as-number'; if ( $validation_error ) { $class .= ' wpcf7-not-valid'; } $atts = array(); $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true ); $atts['min'] = $tag->get_option( 'min', 'signed_num', true ); $atts['max'] = $tag->get_option( 'max', 'signed_num', true ); $atts['step'] = $tag->get_option( 'step', 'num', true ); $atts['readonly'] = $tag->has_option( 'readonly' ); $atts['autocomplete'] = $tag->get_option( 'autocomplete', '[-0-9a-zA-Z]+', true ); if ( $tag->is_required() ) { $atts['aria-required'] = 'true'; } if ( $validation_error ) { $atts['aria-invalid'] = 'true'; $atts['aria-describedby'] = wpcf7_get_validation_error_reference( $tag->name ); } else { $atts['aria-invalid'] = 'false'; } $value = (string) reset( $tag->values ); if ( $tag->has_option( 'placeholder' ) or $tag->has_option( 'watermark' ) ) { $atts['placeholder'] = $value; $value = ''; } $value = $tag->get_default_option( $value ); $value = wpcf7_get_hangover( $tag->name, $value ); $atts['value'] = $value; if ( 'range' === $tag->basetype ) { if ( ! wpcf7_is_number( $atts['min'] ) ) { $atts['min'] = '0'; } if ( ! wpcf7_is_number( $atts['max'] ) ) { $atts['max'] = '100'; } if ( '' === $atts['value'] ) { if ( $atts['min'] < $atts['max'] ) { $atts['value'] = ( $atts['min'] + $atts['max'] ) / 2; } else { $atts['value'] = $atts['min']; } } } $atts['type'] = $tag->basetype; $atts['name'] = $tag->name; $html = sprintf( '%3$s', esc_attr( $tag->name ), wpcf7_format_atts( $atts ), $validation_error ); return $html; } add_action( 'wpcf7_swv_create_schema', 'wpcf7_swv_add_number_rules', 10, 2 ); function wpcf7_swv_add_number_rules( $schema, $contact_form ) { $tags = $contact_form->scan_form_tags( array( 'basetype' => array( 'number', 'range' ), ) ); foreach ( $tags as $tag ) { if ( $tag->is_required() ) { $schema->add_rule( wpcf7_swv_create_rule( 'required', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_required' ), ) ) ); } $schema->add_rule( wpcf7_swv_create_rule( 'number', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_number' ), ) ) ); $min = $tag->get_option( 'min', 'signed_num', true ); $max = $tag->get_option( 'max', 'signed_num', true ); if ( 'range' === $tag->basetype ) { if ( ! wpcf7_is_number( $min ) ) { $min = '0'; } if ( ! wpcf7_is_number( $max ) ) { $max = '100'; } } if ( wpcf7_is_number( $min ) ) { $schema->add_rule( wpcf7_swv_create_rule( 'minnumber', array( 'field' => $tag->name, 'threshold' => $min, 'error' => wpcf7_get_message( 'number_too_small' ), ) ) ); } if ( wpcf7_is_number( $max ) ) { $schema->add_rule( wpcf7_swv_create_rule( 'maxnumber', array( 'field' => $tag->name, 'threshold' => $max, 'error' => wpcf7_get_message( 'number_too_large' ), ) ) ); } } } /* Messages */ add_filter( 'wpcf7_messages', 'wpcf7_number_messages', 10, 1 ); function wpcf7_number_messages( $messages ) { return array_merge( $messages, array( 'invalid_number' => array( 'description' => __( "Number format that the sender entered is invalid", 'contact-form-7' ), 'default' => __( "Please enter a number.", 'contact-form-7' ), ), 'number_too_small' => array( 'description' => __( "Number is smaller than minimum limit", 'contact-form-7' ), 'default' => __( "This field has a too small number.", 'contact-form-7' ), ), 'number_too_large' => array( 'description' => __( "Number is larger than maximum limit", 'contact-form-7' ), 'default' => __( "This field has a too large number.", 'contact-form-7' ), ), ) ); } /* Tag generator */ add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_number', 18, 0 ); function wpcf7_add_tag_generator_number() { $tag_generator = WPCF7_TagGenerator::get_instance(); $tag_generator->add( 'number', __( 'number', 'contact-form-7' ), 'wpcf7_tag_generator_number', array( 'version' => '2' ) ); } function wpcf7_tag_generator_number( $contact_form, $options ) { $field_types = array( 'number' => array( 'display_name' => __( 'Number field', 'contact-form-7' ), 'heading' => __( 'Number field form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a number input field.', 'contact-form-7' ), ), ); $tgg = new WPCF7_TagGeneratorGenerator( $options['content'] ); ?>

array( 'href' => true ), 'strong' => array(), ), array( 'http', 'https' ) ); echo $description; ?>

print( 'field_type', array( 'with_required' => true, 'select_options' => array( 'number' => __( 'Spinbox', 'contact-form-7' ), 'range' => __( 'Slider', 'contact-form-7' ), ), ) ); $tgg->print( 'field_name' ); $tgg->print( 'class_attr' ); $tgg->print( 'min_max', array( 'title' => __( 'Range', 'contact-form-7' ), 'min_option' => 'min:', 'max_option' => 'max:', ) ); $tgg->print( 'default_value', array( 'type' => 'number', 'with_placeholder' => false, ) ); ?>
true, 'zero-controls-container' => true, 'not-for-mail' => true, ) ); } function wpcf7_count_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $targets = wpcf7_scan_form_tags( array( 'name' => $tag->name ) ); $maxlength = $minlength = null; while ( $targets ) { $target = array_shift( $targets ); if ( 'count' != $target->type ) { $maxlength = $target->get_maxlength_option(); $minlength = $target->get_minlength_option(); break; } } if ( $maxlength and $minlength and $maxlength < $minlength ) { $maxlength = $minlength = null; } if ( $tag->has_option( 'down' ) ) { $value = (int) $maxlength; $class = 'wpcf7-character-count down'; } else { $value = '0'; $class = 'wpcf7-character-count up'; } $atts = array(); $atts['id'] = $tag->get_id_option(); $atts['class'] = $tag->get_class_option( $class ); $atts['data-target-name'] = $tag->name; $atts['data-starting-value'] = $value; $atts['data-current-value'] = $value; $atts['data-maximum-value'] = $maxlength; $atts['data-minimum-value'] = $minlength; $html = sprintf( '%2$s', wpcf7_format_atts( $atts ), $value ); return $html; } textarea.php000064400000011613147206762310007104 0ustar00 true ) ); } function wpcf7_textarea_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $validation_error = wpcf7_get_validation_error( $tag->name ); $class = wpcf7_form_controls_class( $tag->type ); if ( $validation_error ) { $class .= ' wpcf7-not-valid'; } $atts = array(); $atts['cols'] = $tag->get_cols_option( '40' ); $atts['rows'] = $tag->get_rows_option( '10' ); $atts['maxlength'] = $tag->get_maxlength_option( '2000' ); $atts['minlength'] = $tag->get_minlength_option(); if ( $atts['maxlength'] and $atts['minlength'] and $atts['maxlength'] < $atts['minlength'] ) { unset( $atts['maxlength'], $atts['minlength'] ); } $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true ); $atts['readonly'] = $tag->has_option( 'readonly' ); $atts['autocomplete'] = $tag->get_option( 'autocomplete', '[-0-9a-zA-Z]+', true ); if ( $tag->is_required() ) { $atts['aria-required'] = 'true'; } if ( $validation_error ) { $atts['aria-invalid'] = 'true'; $atts['aria-describedby'] = wpcf7_get_validation_error_reference( $tag->name ); } else { $atts['aria-invalid'] = 'false'; } $value = empty( $tag->content ) ? (string) reset( $tag->values ) : $tag->content; if ( $tag->has_option( 'placeholder' ) or $tag->has_option( 'watermark' ) ) { $atts['placeholder'] = $value; $value = ''; } $value = $tag->get_default_option( $value ); $value = wpcf7_get_hangover( $tag->name, $value ); $atts['name'] = $tag->name; $html = sprintf( '%4$s', esc_attr( $tag->name ), wpcf7_format_atts( $atts ), esc_textarea( $value ), $validation_error ); return $html; } add_action( 'wpcf7_swv_create_schema', 'wpcf7_swv_add_textarea_rules', 10, 2 ); function wpcf7_swv_add_textarea_rules( $schema, $contact_form ) { $tags = $contact_form->scan_form_tags( array( 'basetype' => array( 'textarea' ), ) ); foreach ( $tags as $tag ) { if ( $tag->is_required() ) { $schema->add_rule( wpcf7_swv_create_rule( 'required', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_required' ), ) ) ); } if ( $minlength = $tag->get_minlength_option() ) { $schema->add_rule( wpcf7_swv_create_rule( 'minlength', array( 'field' => $tag->name, 'threshold' => absint( $minlength ), 'error' => wpcf7_get_message( 'invalid_too_short' ), ) ) ); } if ( $maxlength = $tag->get_maxlength_option( '2000' ) ) { $schema->add_rule( wpcf7_swv_create_rule( 'maxlength', array( 'field' => $tag->name, 'threshold' => absint( $maxlength ), 'error' => wpcf7_get_message( 'invalid_too_long' ), ) ) ); } } } /* Tag generator */ add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_textarea', 20, 0 ); function wpcf7_add_tag_generator_textarea() { $tag_generator = WPCF7_TagGenerator::get_instance(); $tag_generator->add( 'textarea', __( 'text area', 'contact-form-7' ), 'wpcf7_tag_generator_textarea', array( 'version' => '2' ) ); } function wpcf7_tag_generator_textarea( $contact_form, $options ) { $field_types = array( 'textarea' => array( 'display_name' => __( 'Text area', 'contact-form-7' ), 'heading' => __( 'Text area form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a multi-line plain text input area.', 'contact-form-7' ), ), ); $tgg = new WPCF7_TagGeneratorGenerator( $options['content'] ); ?>

array( 'href' => true ), 'strong' => array(), ), array( 'http', 'https' ) ); echo $description; ?>

print( 'field_type', array( 'with_required' => true, 'select_options' => array( 'textarea' => $field_types['textarea']['display_name'], ), ) ); $tgg->print( 'field_name' ); $tgg->print( 'class_attr' ); $tgg->print( 'min_max', array( 'title' => __( 'Length', 'contact-form-7' ), 'min_option' => 'minlength:', 'max_option' => 'maxlength:', ) ); $tgg->print( 'default_value', array( 'with_placeholder' => true, 'use_content' => true, ) ); ?>
true, 'selectable-values' => true, 'multiple-controls-container' => true, ) ); } function wpcf7_checkbox_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $validation_error = wpcf7_get_validation_error( $tag->name ); $class = wpcf7_form_controls_class( $tag->type ); if ( $validation_error ) { $class .= ' wpcf7-not-valid'; } $label_first = $tag->has_option( 'label_first' ); $use_label_element = $tag->has_option( 'use_label_element' ); $exclusive = $tag->has_option( 'exclusive' ); $free_text = $tag->has_option( 'free_text' ); $multiple = false; if ( 'checkbox' == $tag->basetype ) { $multiple = ! $exclusive; } else { // radio $exclusive = false; } if ( $exclusive ) { $class .= ' wpcf7-exclusive-checkbox'; } $atts = array(); $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); if ( $validation_error ) { $atts['aria-describedby'] = wpcf7_get_validation_error_reference( $tag->name ); } $tabindex = $tag->get_option( 'tabindex', 'signed_int', true ); if ( false !== $tabindex ) { $tabindex = (int) $tabindex; } $html = ''; $count = 0; if ( $data = (array) $tag->get_data_option() ) { if ( $free_text ) { $tag->values = array_merge( array_slice( $tag->values, 0, -1 ), array_values( $data ), array_slice( $tag->values, -1 ) ); $tag->labels = array_merge( array_slice( $tag->labels, 0, -1 ), array_values( $data ), array_slice( $tag->labels, -1 ) ); } else { $tag->values = array_merge( $tag->values, array_values( $data ) ); $tag->labels = array_merge( $tag->labels, array_values( $data ) ); } } $values = $tag->values; $labels = $tag->labels; $default_choice = $tag->get_default_option( null, array( 'multiple' => $multiple, ) ); $hangover = wpcf7_get_hangover( $tag->name, $multiple ? array() : '' ); foreach ( $values as $key => $value ) { if ( $hangover ) { $checked = in_array( $value, (array) $hangover, true ); } else { $checked = in_array( $value, (array) $default_choice, true ); } if ( isset( $labels[$key] ) ) { $label = $labels[$key]; } else { $label = $value; } $item_atts = array( 'type' => $tag->basetype, 'name' => $tag->name . ( $multiple ? '[]' : '' ), 'value' => $value, 'checked' => $checked, 'tabindex' => $tabindex, ); $item_atts = wpcf7_format_atts( $item_atts ); if ( $label_first ) { // put label first, input last $item = sprintf( '%1$s', esc_html( $label ), $item_atts ); } else { $item = sprintf( '%1$s', esc_html( $label ), $item_atts ); } if ( $use_label_element ) { $item = ''; } if ( false !== $tabindex and 0 < $tabindex ) { $tabindex += 1; } $class = 'wpcf7-list-item'; $count += 1; if ( 1 == $count ) { $class .= ' first'; } if ( count( $values ) == $count ) { // last round $class .= ' last'; if ( $free_text ) { $free_text_name = sprintf( '_wpcf7_free_text_%s', $tag->name ); $free_text_atts = array( 'name' => $free_text_name, 'class' => 'wpcf7-free-text', 'tabindex' => $tabindex, ); if ( wpcf7_is_posted() and isset( $_POST[$free_text_name] ) ) { $free_text_atts['value'] = wp_unslash( $_POST[$free_text_name] ); } $free_text_atts = wpcf7_format_atts( $free_text_atts ); $item .= sprintf( ' ', $free_text_atts ); $class .= ' has-free-text'; } } $item = '' . $item . ''; $html .= $item; } $html = sprintf( '%3$s%4$s', esc_attr( $tag->name ), wpcf7_format_atts( $atts ), $html, $validation_error ); return $html; } add_action( 'wpcf7_swv_create_schema', 'wpcf7_swv_add_checkbox_rules', 10, 2 ); function wpcf7_swv_add_checkbox_rules( $schema, $contact_form ) { $tags = $contact_form->scan_form_tags( array( 'basetype' => array( 'checkbox', 'radio' ), ) ); foreach ( $tags as $tag ) { if ( $tag->is_required() or 'radio' === $tag->type ) { $schema->add_rule( wpcf7_swv_create_rule( 'required', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_required' ), ) ) ); } if ( 'radio' === $tag->type or $tag->has_option( 'exclusive' ) ) { $schema->add_rule( wpcf7_swv_create_rule( 'maxitems', array( 'field' => $tag->name, 'threshold' => 1, 'error' => $contact_form->filter_message( __( "Too many items are selected.", 'contact-form-7' ) ), ) ) ); } } } add_action( 'wpcf7_swv_create_schema', 'wpcf7_swv_add_checkbox_enum_rules', 20, 2 ); function wpcf7_swv_add_checkbox_enum_rules( $schema, $contact_form ) { $tags = $contact_form->scan_form_tags( array( 'basetype' => array( 'checkbox', 'radio' ), ) ); $values = array_reduce( $tags, function ( $values, $tag ) { if ( $tag->has_option( 'free_text' ) ) { $values[$tag->name] = 'free_text'; } if ( isset( $values[$tag->name] ) and ! is_array( $values[$tag->name] ) // Maybe 'free_text' ) { return $values; } if ( ! isset( $values[$tag->name] ) ) { $values[$tag->name] = array(); } $tag_values = array_merge( (array) $tag->values, (array) $tag->get_data_option() ); $values[$tag->name] = array_merge( $values[$tag->name], $tag_values ); return $values; }, array() ); foreach ( $values as $field => $field_values ) { if ( ! is_array( $field_values ) ) { // Maybe 'free_text' continue; } $field_values = array_map( static function ( $value ) { return html_entity_decode( (string) $value, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); }, $field_values ); $field_values = array_filter( array_unique( $field_values ), static function ( $value ) { return '' !== $value; } ); $schema->add_rule( wpcf7_swv_create_rule( 'enum', array( 'field' => $field, 'accept' => array_values( $field_values ), 'error' => $contact_form->filter_message( __( "Undefined value was submitted through this field.", 'contact-form-7' ) ), ) ) ); } } add_filter( 'wpcf7_posted_data_checkbox', 'wpcf7_posted_data_checkbox', 10, 3 ); add_filter( 'wpcf7_posted_data_checkbox*', 'wpcf7_posted_data_checkbox', 10, 3 ); add_filter( 'wpcf7_posted_data_radio', 'wpcf7_posted_data_checkbox', 10, 3 ); function wpcf7_posted_data_checkbox( $value, $value_orig, $form_tag ) { if ( $form_tag->has_option( 'free_text' ) ) { $value = (array) $value; $free_text_name = sprintf( '_wpcf7_free_text_%s', $form_tag->name ); $free_text = wp_unslash( $_POST[$free_text_name] ?? '' ); $last_val = array_pop( $value ); if ( isset( $last_val ) ) { $last_val = sprintf( '%s %s', $last_val, $free_text ); $value[] = trim( $last_val ); } } return $value; } /* Tag generator */ add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_checkbox_and_radio', 30, 0 ); function wpcf7_add_tag_generator_checkbox_and_radio() { $tag_generator = WPCF7_TagGenerator::get_instance(); $basetypes = array( 'checkbox' => __( 'checkboxes', 'contact-form-7' ), 'radio' => __( 'radio buttons', 'contact-form-7' ), ); foreach ( $basetypes as $id => $title ) { $tag_generator->add( $id, $title, 'wpcf7_tag_generator_checkbox', array( 'version' => '2' ) ); } } function wpcf7_tag_generator_checkbox( $contact_form, $options ) { $field_types = array( 'checkbox' => array( 'display_name' => __( 'Checkboxes', 'contact-form-7' ), 'heading' => __( 'Checkboxes form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a group of checkboxes.', 'contact-form-7' ), ), 'radio' => array( 'display_name' => __( 'Radio buttons', 'contact-form-7' ), 'heading' => __( 'Radio buttons form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a group of radio buttons.', 'contact-form-7' ), ), ); $basetype = $options['id']; if ( ! in_array( $basetype, array_keys( $field_types ) ) ) { $basetype = 'checkbox'; } $tgg = new WPCF7_TagGeneratorGenerator( $options['content'] ); ?>

array( 'href' => true ), 'strong' => array(), ), array( 'http', 'https' ) ); echo $description; ?>

print( 'field_type', array( 'with_required' => 'checkbox' === $basetype, 'select_options' => array( $basetype => $field_types[$basetype]['display_name'], ), ) ); $tgg->print( 'field_name' ); $tgg->print( 'class_attr' ); $tgg->print( 'selectable_values', array( 'use_label_element' => 'checked', ) ); ?>
locale(); } foreach ( (array) $options as $option ) { $option = explode( '.', $option ); $type = $option[0]; if ( isset( $option[1] ) ) { $args['group'] = $option[1]; } else { unset( $args['group'] ); } if ( $list = listo( $type, $args ) ) { $data = array_merge( (array) $data, $list ); } } return $data; } recaptcha/index.js000064400000001646147206762310010162 0ustar00document.addEventListener("DOMContentLoaded",(e=>{var t;wpcf7_recaptcha={...null!==(t=wpcf7_recaptcha)&&void 0!==t?t:{}};const c=wpcf7_recaptcha.sitekey,{homepage:n,contactform:a}=wpcf7_recaptcha.actions,o=e=>{const{action:t,func:n,params:a}=e;grecaptcha.execute(c,{action:t}).then((e=>{const c=new CustomEvent("wpcf7grecaptchaexecuted",{detail:{action:t,token:e}});document.dispatchEvent(c)})).then((()=>{"function"==typeof n&&n(...a)})).catch((e=>console.error(e)))};if(grecaptcha.ready((()=>{o({action:n})})),document.addEventListener("change",(e=>{o({action:a})})),"undefined"!=typeof wpcf7&&"function"==typeof wpcf7.submit){const e=wpcf7.submit;wpcf7.submit=(t,c={})=>{o({action:a,func:e,params:[t,c]})}}document.addEventListener("wpcf7grecaptchaexecuted",(e=>{const t=document.querySelectorAll('form.wpcf7-form input[name="_wpcf7_recaptcha_response"]');for(let c=0;cadd_service( 'recaptcha', WPCF7_RECAPTCHA::get_instance() ); } add_action( 'wp_enqueue_scripts', 'wpcf7_recaptcha_enqueue_scripts', 20, 0 ); /** * Enqueues frontend scripts for reCAPTCHA. */ function wpcf7_recaptcha_enqueue_scripts() { $service = WPCF7_RECAPTCHA::get_instance(); if ( ! $service->is_active() ) { return; } $url = 'https://www.google.com/recaptcha/api.js'; if ( apply_filters( 'wpcf7_use_recaptcha_net', false ) ) { $url = 'https://www.recaptcha.net/recaptcha/api.js'; } wp_register_script( 'google-recaptcha', add_query_arg( array( 'render' => $service->get_sitekey(), ), $url ), array(), '3.0', array( 'in_footer' => true ) ); $assets = include( wpcf7_plugin_path( 'modules/recaptcha/index.asset.php' ) ); $assets = wp_parse_args( $assets, array( 'dependencies' => array(), 'version' => WPCF7_VERSION, ) ); wp_register_script( 'wpcf7-recaptcha', wpcf7_plugin_url( 'modules/recaptcha/index.js' ), array_merge( $assets['dependencies'], array( 'google-recaptcha', 'wp-polyfill', ) ), $assets['version'], array( 'in_footer' => true ) ); wp_enqueue_script( 'wpcf7-recaptcha' ); $wpcf7_recaptcha_obj = array( 'sitekey' => $service->get_sitekey(), 'actions' => apply_filters( 'wpcf7_recaptcha_actions', array( 'homepage' => 'homepage', 'contactform' => 'contactform', ) ), ); wp_add_inline_script( 'wpcf7-recaptcha', sprintf( 'var wpcf7_recaptcha = %s;', wp_json_encode( $wpcf7_recaptcha_obj, JSON_PRETTY_PRINT ) ), 'before' ); } add_filter( 'wpcf7_form_hidden_fields', 'wpcf7_recaptcha_add_hidden_fields', 100, 1 ); /** * Adds hidden form field for reCAPTCHA. */ function wpcf7_recaptcha_add_hidden_fields( $fields ) { $service = WPCF7_RECAPTCHA::get_instance(); if ( ! $service->is_active() ) { return $fields; } return array_merge( $fields, array( '_wpcf7_recaptcha_response' => '', ) ); } add_filter( 'wpcf7_spam', 'wpcf7_recaptcha_verify_response', 9, 2 ); /** * Verifies reCAPTCHA token on the server side. */ function wpcf7_recaptcha_verify_response( $spam, $submission ) { if ( $spam ) { return $spam; } $service = WPCF7_RECAPTCHA::get_instance(); if ( ! $service->is_active() ) { return $spam; } $token = trim( $_POST['_wpcf7_recaptcha_response'] ?? '' ); if ( $service->verify( $token ) ) { // Human $spam = false; } else { // Bot $spam = true; if ( '' === $token ) { $submission->add_spam_log( array( 'agent' => 'recaptcha', 'reason' => __( 'reCAPTCHA response token is empty.', 'contact-form-7' ), ) ); } else { $submission->add_spam_log( array( 'agent' => 'recaptcha', 'reason' => sprintf( __( 'reCAPTCHA score (%1$.2f) is lower than the threshold (%2$.2f).', 'contact-form-7' ), $service->get_last_score(), $service->get_threshold() ), ) ); } } return $spam; } add_action( 'wpcf7_init', 'wpcf7_recaptcha_add_form_tag_recaptcha', 10, 0 ); /** * Registers form-tag types for reCAPTCHA. */ function wpcf7_recaptcha_add_form_tag_recaptcha() { $service = WPCF7_RECAPTCHA::get_instance(); if ( ! $service->is_active() ) { return; } wpcf7_add_form_tag( 'recaptcha', '__return_empty_string', // no output array( 'display-block' => true ) ); } add_action( 'wpcf7_upgrade', 'wpcf7_upgrade_recaptcha_v2_v3', 10, 2 ); /** * Adds warnings for users upgrading from reCAPTCHA v2 to v3. */ function wpcf7_upgrade_recaptcha_v2_v3( $new_ver, $old_ver ) { if ( version_compare( '5.1-dev', $old_ver, '<=' ) ) { return; } $service = WPCF7_RECAPTCHA::get_instance(); if ( ! $service->is_active() or $service->get_global_sitekey() ) { return; } // Maybe v2 keys are used now. Warning necessary. WPCF7::update_option( 'recaptcha_v2_v3_warning', true ); WPCF7::update_option( 'recaptcha', null ); } add_action( 'wpcf7_admin_menu', 'wpcf7_admin_init_recaptcha_v2_v3', 10, 0 ); /** * Adds filters and actions for warnings. */ function wpcf7_admin_init_recaptcha_v2_v3() { if ( ! WPCF7::get_option( 'recaptcha_v2_v3_warning' ) ) { return; } add_filter( 'wpcf7_admin_menu_change_notice', 'wpcf7_admin_menu_change_notice_recaptcha_v2_v3', 10, 1 ); add_action( 'wpcf7_admin_warnings', 'wpcf7_admin_warnings_recaptcha_v2_v3', 5, 3 ); } /** * Increments the admin menu counter for the Integration page. */ function wpcf7_admin_menu_change_notice_recaptcha_v2_v3( $counts ) { $counts['wpcf7-integration'] += 1; return $counts; } /** * Prints warnings on the admin screen. */ function wpcf7_admin_warnings_recaptcha_v2_v3( $page, $action, $object ) { if ( 'wpcf7-integration' !== $page ) { return; } $message = sprintf( esc_html( __( "API keys for reCAPTCHA v3 are different from those for v2; keys for v2 do not work with the v3 API. You need to register your sites again to get new keys for v3. For details, see %s.", 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/recaptcha/', 'contact-form-7' ), __( 'reCAPTCHA (v3)', 'contact-form-7' ) ) ); wp_admin_notice( $message, 'type=warning' ); } recaptcha/service.php000064400000020142147206762310010656 0ustar00sitekeys = WPCF7::get_option( 'recaptcha' ); } public function get_title() { return __( 'reCAPTCHA', 'contact-form-7' ); } public function is_active() { $sitekey = $this->get_sitekey(); $secret = $this->get_secret( $sitekey ); return $sitekey && $secret; } public function get_categories() { return array( 'spam_protection' ); } public function icon() { } public function link() { echo wpcf7_link( 'https://www.google.com/recaptcha/intro/index.html', 'google.com/recaptcha' ); } public function get_global_sitekey() { static $sitekey = ''; if ( $sitekey ) { return $sitekey; } if ( defined( 'WPCF7_RECAPTCHA_SITEKEY' ) ) { $sitekey = WPCF7_RECAPTCHA_SITEKEY; } $sitekey = apply_filters( 'wpcf7_recaptcha_sitekey', $sitekey ); return $sitekey; } public function get_global_secret() { static $secret = ''; if ( $secret ) { return $secret; } if ( defined( 'WPCF7_RECAPTCHA_SECRET' ) ) { $secret = WPCF7_RECAPTCHA_SECRET; } $secret = apply_filters( 'wpcf7_recaptcha_secret', $secret ); return $secret; } public function get_sitekey() { if ( $this->get_global_sitekey() and $this->get_global_secret() ) { return $this->get_global_sitekey(); } if ( empty( $this->sitekeys ) or ! is_array( $this->sitekeys ) ) { return false; } $sitekeys = array_keys( $this->sitekeys ); return $sitekeys[0]; } public function get_secret( $sitekey ) { if ( $this->get_global_sitekey() and $this->get_global_secret() ) { return $this->get_global_secret(); } $sitekeys = (array) $this->sitekeys; if ( isset( $sitekeys[$sitekey] ) ) { return $sitekeys[$sitekey]; } else { return false; } } protected function log( $url, $request, $response ) { wpcf7_log_remote_request( $url, $request, $response ); } public function verify( $token ) { $is_human = false; if ( empty( $token ) or ! $this->is_active() ) { return $is_human; } $endpoint = 'https://www.google.com/recaptcha/api/siteverify'; if ( apply_filters( 'wpcf7_use_recaptcha_net', false ) ) { $endpoint = 'https://www.recaptcha.net/recaptcha/api/siteverify'; } $sitekey = $this->get_sitekey(); $secret = $this->get_secret( $sitekey ); $request = array( 'body' => array( 'secret' => $secret, 'response' => $token, ), ); $response = wp_remote_post( sanitize_url( $endpoint ), $request ); if ( 200 != wp_remote_retrieve_response_code( $response ) ) { if ( WP_DEBUG ) { $this->log( $endpoint, $request, $response ); } return $is_human; } $response_body = wp_remote_retrieve_body( $response ); $response_body = json_decode( $response_body, true ); $this->last_score = $score = isset( $response_body['score'] ) ? $response_body['score'] : 0; $threshold = $this->get_threshold(); $is_human = $threshold < $score; $is_human = apply_filters( 'wpcf7_recaptcha_verify_response', $is_human, $response_body ); if ( $submission = WPCF7_Submission::get_instance() ) { $submission->push( 'recaptcha', array( 'version' => '3.0', 'threshold' => $threshold, 'response' => $response_body, ) ); } return $is_human; } public function get_threshold() { return apply_filters( 'wpcf7_recaptcha_threshold', 0.50 ); } public function get_last_score() { return $this->last_score; } protected function menu_page_url( $args = '' ) { $args = wp_parse_args( $args, array() ); $url = menu_page_url( 'wpcf7-integration', false ); $url = add_query_arg( array( 'service' => 'recaptcha' ), $url ); if ( ! empty( $args ) ) { $url = add_query_arg( $args, $url ); } return $url; } protected function save_data() { WPCF7::update_option( 'recaptcha', $this->sitekeys ); } protected function reset_data() { $this->sitekeys = null; $this->save_data(); } public function load( $action = '' ) { if ( 'setup' == $action and 'POST' == $_SERVER['REQUEST_METHOD'] ) { check_admin_referer( 'wpcf7-recaptcha-setup' ); if ( ! empty( $_POST['reset'] ) ) { $this->reset_data(); $redirect_to = $this->menu_page_url( 'action=setup' ); } else { $sitekey = trim( $_POST['sitekey'] ?? '' ); $secret = trim( $_POST['secret'] ?? '' ); if ( $sitekey and $secret ) { $this->sitekeys = array( $sitekey => $secret ); $this->save_data(); $redirect_to = $this->menu_page_url( array( 'message' => 'success', ) ); } else { $redirect_to = $this->menu_page_url( array( 'action' => 'setup', 'message' => 'invalid', ) ); } } if ( WPCF7::get_option( 'recaptcha_v2_v3_warning' ) ) { WPCF7::update_option( 'recaptcha_v2_v3_warning', false ); } wp_safe_redirect( $redirect_to ); exit(); } } public function admin_notice( $message = '' ) { if ( 'invalid' === $message ) { wp_admin_notice( sprintf( '%1$s: %2$s', esc_html( __( "Error", 'contact-form-7' ) ), esc_html( __( "Invalid key values.", 'contact-form-7' ) ) ), 'type=error' ); } if ( 'success' === $message ) { wp_admin_notice( esc_html( __( "Settings saved.", 'contact-form-7' ) ), 'type=success' ); } } public function display( $action = '' ) { echo sprintf( '

%s

', esc_html( __( "reCAPTCHA protects you against spam and other types of automated abuse. With Contact Form 7’s reCAPTCHA integration module, you can block abusive form submissions by spam bots.", 'contact-form-7' ) ) ); echo sprintf( '

%s

', wpcf7_link( __( 'https://contactform7.com/recaptcha/', 'contact-form-7' ), __( 'reCAPTCHA (v3)', 'contact-form-7' ) ) ); if ( $this->is_active() ) { echo sprintf( '

%s

', esc_html( __( "reCAPTCHA is active on this site.", 'contact-form-7' ) ) ); } if ( 'setup' == $action ) { $this->display_setup(); } else { echo sprintf( '

%2$s

', esc_url( $this->menu_page_url( 'action=setup' ) ), esc_html( __( 'Setup Integration', 'contact-form-7' ) ) ); } } private function display_setup() { $sitekey = $this->is_active() ? $this->get_sitekey() : ''; $secret = $this->is_active() ? $this->get_secret( $sitekey ) : ''; ?>
is_active() ) { echo esc_html( $sitekey ); echo sprintf( '', esc_attr( $sitekey ) ); } else { echo sprintf( '', esc_attr( $sitekey ) ); } ?>
is_active() ) { echo esc_html( wpcf7_mask_password( $secret, 4, 4 ) ); echo sprintf( '', esc_attr( $secret ) ); } else { echo sprintf( '', esc_attr( $secret ) ); } ?>
is_active() ) { if ( $this->get_global_sitekey() and $this->get_global_secret() ) { // nothing } else { submit_button( _x( 'Remove Keys', 'API keys', 'contact-form-7' ), 'small', 'reset' ); } } else { submit_button( __( 'Save Changes', 'contact-form-7' ) ); } ?>
array(), 'version' => WPCF7_VERSION, ); file.php000064400000014326147206762310006212 0ustar00 true, 'file-uploading' => true, ) ); } function wpcf7_file_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $validation_error = wpcf7_get_validation_error( $tag->name ); $class = wpcf7_form_controls_class( $tag->type ); if ( $validation_error ) { $class .= ' wpcf7-not-valid'; } $atts = array(); $atts['size'] = $tag->get_size_option( '40' ); $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $atts['capture'] = $tag->get_option( 'capture', '(user|environment)', true ); $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true ); $atts['accept'] = wpcf7_acceptable_filetypes( $tag->get_option( 'filetypes' ), 'attr' ); if ( $tag->is_required() ) { $atts['aria-required'] = 'true'; } if ( $validation_error ) { $atts['aria-invalid'] = 'true'; $atts['aria-describedby'] = wpcf7_get_validation_error_reference( $tag->name ); } else { $atts['aria-invalid'] = 'false'; } $atts['type'] = 'file'; $atts['name'] = $tag->name; $html = sprintf( '%3$s', esc_attr( $tag->name ), wpcf7_format_atts( $atts ), $validation_error ); return $html; } add_action( 'wpcf7_swv_create_schema', 'wpcf7_swv_add_file_rules', 10, 2 ); function wpcf7_swv_add_file_rules( $schema, $contact_form ) { $tags = $contact_form->scan_form_tags( array( 'basetype' => array( 'file' ), ) ); foreach ( $tags as $tag ) { if ( $tag->is_required() ) { $schema->add_rule( wpcf7_swv_create_rule( 'requiredfile', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_required' ), ) ) ); } $schema->add_rule( wpcf7_swv_create_rule( 'file', array( 'field' => $tag->name, 'accept' => explode( ',', wpcf7_acceptable_filetypes( $tag->get_option( 'filetypes' ), 'attr' ) ), 'error' => wpcf7_get_message( 'upload_file_type_invalid' ), ) ) ); $schema->add_rule( wpcf7_swv_create_rule( 'maxfilesize', array( 'field' => $tag->name, 'threshold' => $tag->get_limit_option(), 'error' => wpcf7_get_message( 'upload_file_too_large' ), ) ) ); } } add_filter( 'wpcf7_mail_tag_replaced_file', 'wpcf7_file_mail_tag', 10, 4 ); add_filter( 'wpcf7_mail_tag_replaced_file*', 'wpcf7_file_mail_tag', 10, 4 ); function wpcf7_file_mail_tag( $replaced, $submitted, $html, $mail_tag ) { $submission = WPCF7_Submission::get_instance(); $uploaded_files = $submission->uploaded_files(); $name = $mail_tag->field_name(); if ( ! empty( $uploaded_files[$name] ) ) { $paths = (array) $uploaded_files[$name]; $paths = array_map( 'wp_basename', $paths ); $replaced = wpcf7_flat_join( $paths, array( 'separator' => wp_get_list_item_separator(), ) ); } return $replaced; } /* Tag generator */ add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_file', 50, 0 ); function wpcf7_add_tag_generator_file() { $tag_generator = WPCF7_TagGenerator::get_instance(); $tag_generator->add( 'file', __( 'file', 'contact-form-7' ), 'wpcf7_tag_generator_file', array( 'version' => '2' ) ); } function wpcf7_tag_generator_file( $contact_form, $options ) { $field_types = array( 'file' => array( 'display_name' => __( 'File uploading field', 'contact-form-7' ), 'heading' => __( 'File uploading field form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a file uploading field.', 'contact-form-7' ), ), ); $tgg = new WPCF7_TagGeneratorGenerator( $options['content'] ); ?>

array( 'href' => true ), 'strong' => array(), ), array( 'http', 'https' ) ); echo $description; ?>

print( 'field_type', array( 'with_required' => true, 'select_options' => array( 'file' => $field_types['file']['display_name'], ), ) ); $tgg->print( 'field_name' ); $tgg->print( 'class_attr' ); ?>
true, 'selectable-values' => true, ) ); } function wpcf7_acceptance_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $validation_error = wpcf7_get_validation_error( $tag->name ); $class = wpcf7_form_controls_class( $tag->type ); if ( $validation_error ) { $class .= ' wpcf7-not-valid'; } if ( $tag->has_option( 'invert' ) ) { $class .= ' invert'; } if ( $tag->has_option( 'optional' ) ) { $class .= ' optional'; } $atts = array( 'class' => trim( $class ), ); $item_atts = array( 'type' => 'checkbox', 'name' => $tag->name, 'value' => '1', 'tabindex' => $tag->get_option( 'tabindex', 'signed_int', true ), 'checked' => $tag->has_option( 'default:on' ), 'class' => $tag->get_class_option() ? $tag->get_class_option() : null, 'id' => $tag->get_id_option(), ); if ( $validation_error ) { $item_atts['aria-invalid'] = 'true'; $item_atts['aria-describedby'] = wpcf7_get_validation_error_reference( $tag->name ); } else { $item_atts['aria-invalid'] = 'false'; } $item_atts = wpcf7_format_atts( $item_atts ); $content = empty( $tag->content ) ? (string) reset( $tag->values ) : $tag->content; $content = trim( $content ); if ( $content ) { if ( $tag->has_option( 'label_first' ) ) { $html = sprintf( '%2$s', $item_atts, $content ); } else { $html = sprintf( '%2$s', $item_atts, $content ); } $html = sprintf( '', $html ); } else { $html = sprintf( '', $item_atts ); } $html = sprintf( '%3$s%4$s', esc_attr( $tag->name ), wpcf7_format_atts( $atts ), $html, $validation_error ); return $html; } /* Validation filter */ add_filter( 'wpcf7_validate_acceptance', 'wpcf7_acceptance_validation_filter', 10, 2 ); function wpcf7_acceptance_validation_filter( $result, $tag ) { if ( ! wpcf7_acceptance_as_validation() ) { return $result; } if ( $tag->has_option( 'optional' ) ) { return $result; } $name = $tag->name; $value = ( ! empty( $_POST[$name] ) ? 1 : 0 ); $invert = $tag->has_option( 'invert' ); if ( $invert and $value or ! $invert and ! $value ) { $result->invalidate( $tag, wpcf7_get_message( 'accept_terms' ) ); } return $result; } /* Acceptance filter */ add_filter( 'wpcf7_acceptance', 'wpcf7_acceptance_filter', 10, 2 ); function wpcf7_acceptance_filter( $accepted, $submission ) { $tags = wpcf7_scan_form_tags( array( 'type' => 'acceptance' ) ); foreach ( $tags as $tag ) { $name = $tag->name; if ( empty( $name ) ) { continue; } $value = ( ! empty( $_POST[$name] ) ? 1 : 0 ); $content = empty( $tag->content ) ? (string) reset( $tag->values ) : $tag->content; $content = trim( $content ); if ( $value and $content ) { $submission->add_consent( $name, $content ); } if ( $tag->has_option( 'optional' ) ) { continue; } $invert = $tag->has_option( 'invert' ); if ( $invert and $value or ! $invert and ! $value ) { $accepted = false; } } return $accepted; } add_filter( 'wpcf7_form_class_attr', 'wpcf7_acceptance_form_class_attr', 10, 1 ); function wpcf7_acceptance_form_class_attr( $class_attr ) { if ( wpcf7_acceptance_as_validation() ) { return $class_attr . ' wpcf7-acceptance-as-validation'; } return $class_attr; } function wpcf7_acceptance_as_validation() { if ( ! $contact_form = wpcf7_get_current_contact_form() ) { return false; } return $contact_form->is_true( 'acceptance_as_validation' ); } add_filter( 'wpcf7_mail_tag_replaced_acceptance', 'wpcf7_acceptance_mail_tag', 10, 4 ); function wpcf7_acceptance_mail_tag( $replaced, $submitted, $html, $mail_tag ) { $form_tag = $mail_tag->corresponding_form_tag(); if ( ! $form_tag ) { return $replaced; } if ( ! empty( $submitted ) ) { $replaced = __( 'Consented', 'contact-form-7' ); } else { $replaced = __( 'Not consented', 'contact-form-7' ); } $content = empty( $form_tag->content ) ? (string) reset( $form_tag->values ) : $form_tag->content; if ( ! $html ) { $content = wp_strip_all_tags( $content ); } $content = trim( $content ); if ( $content ) { $replaced = sprintf( /* translators: 1: 'Consented' or 'Not consented', 2: conditions */ _x( '%1$s: %2$s', 'mail output for acceptance checkboxes', 'contact-form-7' ), $replaced, $content ); } return $replaced; } /* Tag generator */ add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_acceptance', 35, 0 ); function wpcf7_add_tag_generator_acceptance() { $tag_generator = WPCF7_TagGenerator::get_instance(); $tag_generator->add( 'acceptance', __( 'acceptance', 'contact-form-7' ), 'wpcf7_tag_generator_acceptance', array( 'version' => '2' ) ); } function wpcf7_tag_generator_acceptance( $contact_form, $options ) { $field_types = array( 'acceptance' => array( 'display_name' => __( 'Acceptance checkbox', 'contact-form-7' ), 'heading' => __( 'Acceptance checkbox form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for an acceptance checkbox.', 'contact-form-7' ), ), ); $tgg = new WPCF7_TagGeneratorGenerator( $options['content'] ); ?>

array( 'href' => true ), 'strong' => array(), ), array( 'http', 'https' ) ); echo $description; ?>


print( 'field_name' ); $tgg->print( 'class_attr' ); ?>
', wpcf7_format_atts( array( 'type' => 'text', 'required' => true, 'value' => __( 'Put the condition for consent here.', 'contact-form-7' ), 'data-tag-part' => 'content', 'aria-labelledby' => $tgg->ref( 'value-legend' ), ) ) ); ?>
true, ) ); } function wpcf7_response_form_tag_handler( $tag ) { if ( $contact_form = wpcf7_get_current_contact_form() ) { return $contact_form->form_response_output(); } } quiz.php000064400000014450147206762310006261 0ustar00 true, 'do-not-store' => true, 'not-for-mail' => true, ) ); } function wpcf7_quiz_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $validation_error = wpcf7_get_validation_error( $tag->name ); $class = wpcf7_form_controls_class( $tag->type ); if ( $validation_error ) { $class .= ' wpcf7-not-valid'; } $atts = array(); $atts['size'] = $tag->get_size_option( '40' ); $atts['maxlength'] = $tag->get_maxlength_option(); $atts['minlength'] = $tag->get_minlength_option(); if ( $atts['maxlength'] and $atts['minlength'] and $atts['maxlength'] < $atts['minlength'] ) { unset( $atts['maxlength'], $atts['minlength'] ); } $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true ); $atts['autocomplete'] = 'off'; $atts['aria-required'] = 'true'; if ( $validation_error ) { $atts['aria-invalid'] = 'true'; $atts['aria-describedby'] = wpcf7_get_validation_error_reference( $tag->name ); } else { $atts['aria-invalid'] = 'false'; } $pipes = $tag->pipes; if ( $pipes instanceof WPCF7_Pipes and ! $pipes->zero() ) { $pipe = $pipes->random_pipe(); $question = $pipe->before; $answer = $pipe->after; } else { // default quiz $question = '1+1=?'; $answer = '2'; } $answer = wpcf7_canonicalize( $answer, array( 'strip_separators' => true, ) ); $atts['type'] = 'text'; $atts['name'] = $tag->name; $html = sprintf( '%6$s', esc_attr( $tag->name ), esc_html( $question ), wpcf7_format_atts( $atts ), $tag->name, wp_hash( $answer, 'wpcf7_quiz' ), $validation_error ); return $html; } /* Validation filter */ add_filter( 'wpcf7_validate_quiz', 'wpcf7_quiz_validation_filter', 10, 2 ); function wpcf7_quiz_validation_filter( $result, $tag ) { $name = $tag->name; $answer = wp_unslash( $_POST[$name] ?? '' ); $answer = wpcf7_canonicalize( $answer, array( 'strip_separators' => true, ) ); $answer_hash = wp_hash( $answer, 'wpcf7_quiz' ); $expected_hash = (string) ( $_POST['_wpcf7_quiz_answer_' . $name] ?? '' ); if ( ! hash_equals( $expected_hash, $answer_hash ) ) { $result->invalidate( $tag, wpcf7_get_message( 'quiz_answer_not_correct' ) ); } return $result; } /* Ajax echo filter */ add_filter( 'wpcf7_refill_response', 'wpcf7_quiz_ajax_refill', 10, 1 ); add_filter( 'wpcf7_feedback_response', 'wpcf7_quiz_ajax_refill', 10, 1 ); function wpcf7_quiz_ajax_refill( $items ) { if ( ! is_array( $items ) ) { return $items; } $fes = wpcf7_scan_form_tags( array( 'type' => 'quiz' ) ); if ( empty( $fes ) ) { return $items; } $refill = array(); foreach ( $fes as $fe ) { $name = $fe['name']; $pipes = $fe['pipes']; if ( empty( $name ) ) { continue; } if ( $pipes instanceof WPCF7_Pipes and ! $pipes->zero() ) { $pipe = $pipes->random_pipe(); $question = $pipe->before; $answer = $pipe->after; } else { // default quiz $question = '1+1=?'; $answer = '2'; } $answer = wpcf7_canonicalize( $answer, array( 'strip_separators' => true, ) ); $refill[$name] = array( $question, wp_hash( $answer, 'wpcf7_quiz' ) ); } if ( ! empty( $refill ) ) { $items['quiz'] = $refill; } return $items; } /* Messages */ add_filter( 'wpcf7_messages', 'wpcf7_quiz_messages', 10, 1 ); function wpcf7_quiz_messages( $messages ) { $messages = array_merge( $messages, array( 'quiz_answer_not_correct' => array( 'description' => __( "Sender does not enter the correct answer to the quiz", 'contact-form-7' ), 'default' => __( "The answer to the quiz is incorrect.", 'contact-form-7' ), ), ) ); return $messages; } /* Tag generator */ add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_quiz', 40, 0 ); function wpcf7_add_tag_generator_quiz() { $tag_generator = WPCF7_TagGenerator::get_instance(); $tag_generator->add( 'quiz', __( 'quiz', 'contact-form-7' ), 'wpcf7_tag_generator_quiz', array( 'version' => '2' ) ); } function wpcf7_tag_generator_quiz( $contact_form, $options ) { $field_types = array( 'quiz' => array( 'display_name' => __( 'Quiz', 'contact-form-7' ), 'heading' => __( 'Quiz form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a quiz.', 'contact-form-7' ), ), ); $tgg = new WPCF7_TagGeneratorGenerator( $options['content'] ); ?>

array( 'href' => true ), 'strong' => array(), ), array( 'http', 'https' ) ); echo $description; ?>

print( 'field_type', array( 'select_options' => array( 'quiz' => $field_types['quiz']['display_name'], ), ) ); $tgg->print( 'field_name' ); $tgg->print( 'class_attr' ); ?>
%2$s', wpcf7_format_atts( array( 'id' => $tgg->ref( 'selectable-values-description' ), ) ), esc_html( __( "One pipe-separated question-answer pair (question|answer) per line.", 'contact-form-7' ) ) ); ?>
%2$s', wpcf7_format_atts( array( 'required' => true, 'data-tag-part' => 'value', 'aria-labelledby' => $tgg->ref( 'selectable-values-legend' ), 'aria-describedby' => $tgg->ref( 'selectable-values-description' ), ) ), esc_html( __( "The capital of Brazil? | Rio", 'contact-form-7' ) ) ); ?>
true, 'zero-controls-container' => true, 'not-for-mail' => true, ) ); // CAPTCHA-Response (input) wpcf7_add_form_tag( 'captchar', 'wpcf7_captchar_form_tag_handler', array( 'name-attr' => true, 'do-not-store' => true, 'not-for-mail' => true, ) ); } function wpcf7_captchac_form_tag_handler( $tag ) { if ( ! class_exists( 'ReallySimpleCaptcha' ) ) { $error = sprintf( /* translators: %s: link labeled 'Really Simple CAPTCHA' */ esc_html( __( "To use CAPTCHA, you need %s plugin installed.", 'contact-form-7' ) ), wpcf7_link( 'https://wordpress.org/plugins/really-simple-captcha/', 'Really Simple CAPTCHA' ) ); return sprintf( '%s', $error ); } if ( empty( $tag->name ) ) { return ''; } $class = wpcf7_form_controls_class( $tag->type ); $class .= ' wpcf7-captcha-' . str_replace( ':', '', $tag->name ); $atts = array(); $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $op = array( // Default 'img_size' => array( 72, 24 ), 'base' => array( 6, 18 ), 'font_size' => 14, 'font_char_width' => 15, ); $op = array_merge( $op, wpcf7_captchac_options( $tag->options ) ); if ( ! $filename = wpcf7_generate_captcha( $op ) ) { return ''; } if ( ! empty( $op['img_size'] ) ) { if ( isset( $op['img_size'][0] ) ) { $atts['width'] = $op['img_size'][0]; } if ( isset( $op['img_size'][1] ) ) { $atts['height'] = $op['img_size'][1]; } } $atts['alt'] = 'captcha'; $atts['src'] = wpcf7_captcha_url( $filename ); $atts = wpcf7_format_atts( $atts ); $prefix = substr( $filename, 0, strrpos( $filename, '.' ) ); $html = sprintf( '', esc_attr( sprintf( '_wpcf7_captcha_challenge_%s', $tag->name ) ), esc_attr( $prefix ), $atts ); return $html; } function wpcf7_captchar_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $validation_error = wpcf7_get_validation_error( $tag->name ); $class = wpcf7_form_controls_class( $tag->type ); if ( $validation_error ) { $class .= ' wpcf7-not-valid'; } $atts = array(); $atts['size'] = $tag->get_size_option( '40' ); $atts['maxlength'] = $tag->get_maxlength_option(); $atts['minlength'] = $tag->get_minlength_option(); if ( $atts['maxlength'] and $atts['minlength'] and $atts['maxlength'] < $atts['minlength'] ) { unset( $atts['maxlength'], $atts['minlength'] ); } $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true ); $atts['autocomplete'] = 'off'; if ( $validation_error ) { $atts['aria-invalid'] = 'true'; $atts['aria-describedby'] = wpcf7_get_validation_error_reference( $tag->name ); } else { $atts['aria-invalid'] = 'false'; } $value = (string) reset( $tag->values ); if ( wpcf7_is_posted() ) { $value = ''; } if ( $tag->has_option( 'placeholder' ) or $tag->has_option( 'watermark' ) ) { $atts['placeholder'] = $value; $value = ''; } $atts['value'] = $value; $atts['type'] = 'text'; $atts['name'] = $tag->name; $html = sprintf( '%3$s', esc_attr( $tag->name ), wpcf7_format_atts( $atts ), $validation_error ); return $html; } /* Validation filter */ add_filter( 'wpcf7_validate_captchar', 'wpcf7_captcha_validation_filter', 10, 2 ); function wpcf7_captcha_validation_filter( $result, $tag ) { $type = $tag->type; $name = $tag->name; $captchac = '_wpcf7_captcha_challenge_' . $name; $prefix = (string) ( $_POST[$captchac] ?? '' ); $response = (string) ( $_POST[$name] ?? '' ); $response = wpcf7_canonicalize( $response ); if ( 0 === strlen( $prefix ) or ! wpcf7_check_captcha( $prefix, $response ) ) { $result->invalidate( $tag, wpcf7_get_message( 'captcha_not_match' ) ); } if ( 0 !== strlen( $prefix ) ) { wpcf7_remove_captcha( $prefix ); } return $result; } /* Ajax echo filter */ add_filter( 'wpcf7_refill_response', 'wpcf7_captcha_ajax_refill', 10, 1 ); add_filter( 'wpcf7_feedback_response', 'wpcf7_captcha_ajax_refill', 10, 1 ); function wpcf7_captcha_ajax_refill( $items ) { if ( ! is_array( $items ) ) { return $items; } $tags = wpcf7_scan_form_tags( array( 'type' => 'captchac' ) ); if ( empty( $tags ) ) { return $items; } $refill = array(); foreach ( $tags as $tag ) { $name = $tag->name; $options = $tag->options; if ( empty( $name ) ) { continue; } $op = wpcf7_captchac_options( $options ); if ( $filename = wpcf7_generate_captcha( $op ) ) { $captcha_url = wpcf7_captcha_url( $filename ); $refill[$name] = $captcha_url; } } if ( ! empty( $refill ) ) { $items['captcha'] = $refill; } return $items; } /* Messages */ add_filter( 'wpcf7_messages', 'wpcf7_captcha_messages', 10, 1 ); function wpcf7_captcha_messages( $messages ) { $messages = array_merge( $messages, array( 'captcha_not_match' => array( 'description' => __( "The code that sender entered does not match the CAPTCHA", 'contact-form-7' ), 'default' => __( 'Your entered code is incorrect.', 'contact-form-7' ), ), ) ); return $messages; } /* Warning message */ add_action( 'wpcf7_admin_warnings', 'wpcf7_captcha_display_warning_message', 10, 3 ); function wpcf7_captcha_display_warning_message( $page, $action, $object ) { if ( $object instanceof WPCF7_ContactForm ) { $contact_form = $object; } else { return; } $has_tags = (bool) $contact_form->scan_form_tags( array( 'type' => array( 'captchac' ) ) ); if ( ! $has_tags ) { return; } if ( ! class_exists( 'ReallySimpleCaptcha' ) ) { return; } $uploads_dir = wpcf7_captcha_tmp_dir(); wpcf7_init_captcha(); if ( ! is_dir( $uploads_dir ) or ! wp_is_writable( $uploads_dir ) ) { $message = sprintf( __( 'This contact form contains CAPTCHA fields, but the temporary folder for the files (%s) does not exist or is not writable. You can create the folder or change its permission manually.', 'contact-form-7' ), $uploads_dir ); wp_admin_notice( esc_html( $message ), 'type=warning' ); } if ( ! function_exists( 'imagecreatetruecolor' ) or ! function_exists( 'imagettftext' ) ) { $message = __( "This contact form contains CAPTCHA fields, but the necessary libraries (GD and FreeType) are not available on your server.", 'contact-form-7' ); wp_admin_notice( esc_html( $message ), 'type=warning' ); } } /* CAPTCHA functions */ function wpcf7_init_captcha() { static $captcha = null; if ( $captcha ) { return $captcha; } if ( class_exists( 'ReallySimpleCaptcha' ) ) { $captcha = new ReallySimpleCaptcha(); } else { return false; } $dir = trailingslashit( wpcf7_captcha_tmp_dir() ); $captcha->tmp_dir = $dir; if ( is_callable( array( $captcha, 'make_tmp_dir' ) ) ) { $result = $captcha->make_tmp_dir(); if ( ! $result ) { return false; } return $captcha; } $result = wp_mkdir_p( $dir ); if ( ! $result ) { return false; } $htaccess_file = path_join( $dir, '.htaccess' ); if ( file_exists( $htaccess_file ) ) { list( $first_line_comment ) = (array) file( $htaccess_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES ); if ( '# Apache 2.4+' === $first_line_comment ) { return $captcha; } } if ( $handle = @fopen( $htaccess_file, 'w' ) ) { fwrite( $handle, "# Apache 2.4+\n" ); fwrite( $handle, "\n" ); fwrite( $handle, " Require all denied\n" ); fwrite( $handle, ' ' . "\n" ); fwrite( $handle, " Require all granted\n" ); fwrite( $handle, " \n" ); fwrite( $handle, "\n" ); fwrite( $handle, "\n" ); fwrite( $handle, "# Apache 2.2\n" ); fwrite( $handle, "\n" ); fwrite( $handle, " Order deny,allow\n" ); fwrite( $handle, " Deny from all\n" ); fwrite( $handle, ' ' . "\n" ); fwrite( $handle, " Allow from all\n" ); fwrite( $handle, " \n" ); fwrite( $handle, "\n" ); fclose( $handle ); } return $captcha; } /** * Returns the directory path for Really Simple CAPTCHA files. * * @return string Directory path. */ function wpcf7_captcha_tmp_dir() { if ( defined( 'WPCF7_CAPTCHA_TMP_DIR' ) ) { $dir = path_join( WP_CONTENT_DIR, WPCF7_CAPTCHA_TMP_DIR ); wp_mkdir_p( $dir ); if ( wpcf7_is_file_path_in_content_dir( $dir ) ) { return $dir; } } $dir = path_join( wpcf7_upload_dir( 'dir' ), 'wpcf7_captcha' ); wp_mkdir_p( $dir ); return $dir; } function wpcf7_captcha_tmp_url() { if ( defined( 'WPCF7_CAPTCHA_TMP_URL' ) ) { return WPCF7_CAPTCHA_TMP_URL; } else { return path_join( wpcf7_upload_dir( 'url' ), 'wpcf7_captcha' ); } } function wpcf7_captcha_url( $filename ) { $url = path_join( wpcf7_captcha_tmp_url(), $filename ); if ( is_ssl() and 'http:' == substr( $url, 0, 5 ) ) { $url = 'https:' . substr( $url, 5 ); } return apply_filters( 'wpcf7_captcha_url', sanitize_url( $url ) ); } function wpcf7_generate_captcha( $options = null ) { if ( ! $captcha = wpcf7_init_captcha() ) { return false; } if ( ! is_dir( $captcha->tmp_dir ) or ! wp_is_writable( $captcha->tmp_dir ) ) { return false; } $img_type = imagetypes(); if ( $img_type & IMG_PNG ) { $captcha->img_type = 'png'; } elseif ( $img_type & IMG_GIF ) { $captcha->img_type = 'gif'; } elseif ( $img_type & IMG_JPG ) { $captcha->img_type = 'jpeg'; } else { return false; } if ( is_array( $options ) ) { if ( isset( $options['img_size'] ) ) { $captcha->img_size = $options['img_size']; } if ( isset( $options['base'] ) ) { $captcha->base = $options['base']; } if ( isset( $options['font_size'] ) ) { $captcha->font_size = $options['font_size']; } if ( isset( $options['font_char_width'] ) ) { $captcha->font_char_width = $options['font_char_width']; } if ( isset( $options['fg'] ) ) { $captcha->fg = $options['fg']; } if ( isset( $options['bg'] ) ) { $captcha->bg = $options['bg']; } } $prefix = wp_rand(); $captcha_word = $captcha->generate_random_word(); return $captcha->generate_image( $prefix, $captcha_word ); } function wpcf7_check_captcha( $prefix, $response ) { if ( ! $captcha = wpcf7_init_captcha() ) { return false; } return $captcha->check( $prefix, $response ); } function wpcf7_remove_captcha( $prefix ) { if ( ! $captcha = wpcf7_init_captcha() ) { return false; } // Contact Form 7 generates $prefix with wp_rand() if ( preg_match( '/[^0-9]/', $prefix ) ) { return false; } $captcha->remove( $prefix ); } add_action( 'shutdown', 'wpcf7_cleanup_captcha_files', 20, 0 ); function wpcf7_cleanup_captcha_files() { if ( ! $captcha = wpcf7_init_captcha() ) { return false; } if ( is_callable( array( $captcha, 'cleanup' ) ) ) { return $captcha->cleanup(); } $dir = trailingslashit( wpcf7_captcha_tmp_dir() ); if ( ! is_dir( $dir ) or ! is_readable( $dir ) or ! wp_is_writable( $dir ) ) { return false; } if ( $handle = opendir( $dir ) ) { while ( false !== ( $file = readdir( $handle ) ) ) { if ( ! preg_match( '/^[0-9]+\.(php|txt|png|gif|jpeg)$/', $file ) ) { continue; } $stat = stat( path_join( $dir, $file ) ); if ( $stat['mtime'] + HOUR_IN_SECONDS < time() ) { @unlink( path_join( $dir, $file ) ); } } closedir( $handle ); } } function wpcf7_captchac_options( $options ) { if ( ! is_array( $options ) ) { return array(); } $op = array(); $image_size_array = preg_grep( '%^size:[smlSML]$%', $options ); if ( $image_size = array_shift( $image_size_array ) ) { preg_match( '%^size:([smlSML])$%', $image_size, $is_matches ); switch ( strtolower( $is_matches[1] ) ) { case 's': $op['img_size'] = array( 60, 20 ); $op['base'] = array( 6, 15 ); $op['font_size'] = 11; $op['font_char_width'] = 13; break; case 'l': $op['img_size'] = array( 84, 28 ); $op['base'] = array( 6, 20 ); $op['font_size'] = 17; $op['font_char_width'] = 19; break; case 'm': default: $op['img_size'] = array( 72, 24 ); $op['base'] = array( 6, 18 ); $op['font_size'] = 14; $op['font_char_width'] = 15; } } $fg_color_array = preg_grep( '%^fg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', $options ); if ( $fg_color = array_shift( $fg_color_array ) ) { preg_match( '%^fg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', $fg_color, $fc_matches ); if ( 3 == strlen( $fc_matches[1] ) ) { $r = substr( $fc_matches[1], 0, 1 ); $g = substr( $fc_matches[1], 1, 1 ); $b = substr( $fc_matches[1], 2, 1 ); $op['fg'] = array( hexdec( $r . $r ), hexdec( $g . $g ), hexdec( $b . $b ), ); } elseif ( 6 == strlen( $fc_matches[1] ) ) { $r = substr( $fc_matches[1], 0, 2 ); $g = substr( $fc_matches[1], 2, 2 ); $b = substr( $fc_matches[1], 4, 2 ); $op['fg'] = array( hexdec( $r ), hexdec( $g ), hexdec( $b ), ); } } $bg_color_array = preg_grep( '%^bg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', $options ); if ( $bg_color = array_shift( $bg_color_array ) ) { preg_match( '%^bg:#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$%', $bg_color, $bc_matches ); if ( 3 == strlen( $bc_matches[1] ) ) { $r = substr( $bc_matches[1], 0, 1 ); $g = substr( $bc_matches[1], 1, 1 ); $b = substr( $bc_matches[1], 2, 1 ); $op['bg'] = array( hexdec( $r . $r ), hexdec( $g . $g ), hexdec( $b . $b ), ); } elseif ( 6 == strlen( $bc_matches[1] ) ) { $r = substr( $bc_matches[1], 0, 2 ); $g = substr( $bc_matches[1], 2, 2 ); $b = substr( $bc_matches[1], 4, 2 ); $op['bg'] = array( hexdec( $r ), hexdec( $g ), hexdec( $b ), ); } } return $op; } flamingo.php000064400000020531147206762310007062 0ustar00in_demo_mode() ) { return; } $cases = (array) apply_filters( 'wpcf7_flamingo_submit_if', array( 'spam', 'mail_sent', 'mail_failed' ) ); if ( empty( $result['status'] ) or ! in_array( $result['status'], $cases ) ) { return; } $submission = WPCF7_Submission::get_instance(); if ( ! $submission or ! $posted_data = $submission->get_posted_data() ) { return; } if ( $submission->get_meta( 'do_not_store' ) ) { return; } // Exclude do-not-store form-tag values. $posted_data = array_filter( $posted_data, static function ( $name ) use ( $contact_form ) { return ! $contact_form->scan_form_tags( array( 'name' => $name, 'feature' => 'do-not-store', ) ); }, ARRAY_FILTER_USE_KEY ); $email = wpcf7_flamingo_get_value( 'email', $contact_form ); $name = wpcf7_flamingo_get_value( 'name', $contact_form ); $subject = wpcf7_flamingo_get_value( 'subject', $contact_form ); $meta = array(); $special_mail_tags = array( 'serial_number', 'remote_ip', 'user_agent', 'url', 'date', 'time', 'post_id', 'post_name', 'post_title', 'post_url', 'post_author', 'post_author_email', 'site_title', 'site_description', 'site_url', 'site_admin_email', 'user_login', 'user_email', 'user_display_name', ); foreach ( $special_mail_tags as $smt ) { $tagname = sprintf( '_%s', $smt ); $mail_tag = new WPCF7_MailTag( sprintf( '[%s]', $tagname ), $tagname, '' ); $meta[$smt] = apply_filters( 'wpcf7_special_mail_tags', null, $tagname, false, $mail_tag ); } $timestamp = $submission->get_meta( 'timestamp' ); if ( $timestamp and $datetime = date_create( '@' . $timestamp ) ) { $datetime->setTimezone( wp_timezone() ); $last_contacted = $datetime->format( 'Y-m-d H:i:s' ); } else { $last_contacted = '0000-00-00 00:00:00'; } if ( 'mail_sent' == $result['status'] ) { $flamingo_contact = Flamingo_Contact::add( array( 'email' => $email, 'name' => $name, 'last_contacted' => $last_contacted, ) ); } $post_meta = get_post_meta( $contact_form->id(), '_flamingo', true ); $channel_id = isset( $post_meta['channel'] ) ? (int) $post_meta['channel'] : wpcf7_flamingo_add_channel( $contact_form->name(), $contact_form->title() ); if ( $channel_id ) { if ( ! isset( $post_meta['channel'] ) or $post_meta['channel'] !== $channel_id ) { $post_meta = empty( $post_meta ) ? array() : (array) $post_meta; $post_meta = array_merge( $post_meta, array( 'channel' => $channel_id, ) ); update_post_meta( $contact_form->id(), '_flamingo', $post_meta ); } $channel = get_term( $channel_id, Flamingo_Inbound_Message::channel_taxonomy ); if ( ! $channel or is_wp_error( $channel ) ) { $channel = 'contact-form-7'; } else { $channel = $channel->slug; } } else { $channel = 'contact-form-7'; } $args = array( 'channel' => $channel, 'status' => $submission->get_status(), 'subject' => $subject, 'from' => trim( sprintf( '%s <%s>', $name, $email ) ), 'from_name' => $name, 'from_email' => $email, 'fields' => $posted_data, 'meta' => $meta, 'akismet' => $submission->pull( 'akismet' ), 'spam' => ( 'spam' == $result['status'] ), 'consent' => $submission->collect_consent(), 'timestamp' => $timestamp, 'posted_data_hash' => $submission->get_posted_data_hash(), ); if ( $args['spam'] ) { $args['spam_log'] = $submission->get_spam_log(); } $args['recaptcha'] = $submission->pull( 'recaptcha' ); $args = apply_filters( 'wpcf7_flamingo_inbound_message_parameters', $args ); $flamingo_inbound = Flamingo_Inbound_Message::add( $args ); if ( empty( $flamingo_contact ) ) { $flamingo_contact_id = 0; } elseif ( method_exists( $flamingo_contact, 'id' ) ) { $flamingo_contact_id = $flamingo_contact->id(); } else { $flamingo_contact_id = $flamingo_contact->id; } if ( empty( $flamingo_inbound ) ) { $flamingo_inbound_id = 0; } elseif ( method_exists( $flamingo_inbound, 'id' ) ) { $flamingo_inbound_id = $flamingo_inbound->id(); } else { $flamingo_inbound_id = $flamingo_inbound->id; } $result += array( 'flamingo_contact_id' => absint( $flamingo_contact_id ), 'flamingo_inbound_id' => absint( $flamingo_inbound_id ), ); do_action( 'wpcf7_after_flamingo', $result ); } function wpcf7_flamingo_get_value( $field, $contact_form ) { if ( empty( $field ) or empty( $contact_form ) ) { return false; } $value = ''; if ( in_array( $field, array( 'email', 'name', 'subject' ) ) ) { $template = $contact_form->pref( 'flamingo_' . $field ); if ( null === $template ) { $template = sprintf( '[your-%s]', $field ); } else { $template = trim( wpcf7_strip_quote( $template ) ); } $value = wpcf7_mail_replace_tags( $template ); } $value = apply_filters( 'wpcf7_flamingo_get_value', $value, $field, $contact_form ); return $value; } function wpcf7_flamingo_add_channel( $slug, $name = '' ) { if ( ! class_exists( 'Flamingo_Inbound_Message' ) ) { return false; } $parent = term_exists( 'contact-form-7', Flamingo_Inbound_Message::channel_taxonomy ); if ( ! $parent ) { $parent = wp_insert_term( __( 'Contact Form 7', 'contact-form-7' ), Flamingo_Inbound_Message::channel_taxonomy, array( 'slug' => 'contact-form-7' ) ); if ( is_wp_error( $parent ) ) { return false; } } $parent = (int) $parent['term_id']; if ( ! is_taxonomy_hierarchical( Flamingo_Inbound_Message::channel_taxonomy ) ) { // backward compat for Flamingo 1.0.4 and lower return $parent; } if ( empty( $name ) ) { $name = $slug; } $channel = term_exists( $slug, Flamingo_Inbound_Message::channel_taxonomy, $parent ); if ( ! $channel ) { $channel = wp_insert_term( $name, Flamingo_Inbound_Message::channel_taxonomy, array( 'slug' => $slug, 'parent' => $parent ) ); if ( is_wp_error( $channel ) ) { return false; } } return (int) $channel['term_id']; } add_action( 'wpcf7_after_update', 'wpcf7_flamingo_update_channel', 10, 1 ); function wpcf7_flamingo_update_channel( $contact_form ) { if ( ! class_exists( 'Flamingo_Inbound_Message' ) ) { return false; } $post_meta = get_post_meta( $contact_form->id(), '_flamingo', true ); $channel = isset( $post_meta['channel'] ) ? get_term( $post_meta['channel'], Flamingo_Inbound_Message::channel_taxonomy ) : get_term_by( 'slug', $contact_form->name(), Flamingo_Inbound_Message::channel_taxonomy ); if ( ! $channel or is_wp_error( $channel ) ) { return; } if ( $channel->name !== wp_unslash( $contact_form->title() ) ) { wp_update_term( $channel->term_id, Flamingo_Inbound_Message::channel_taxonomy, array( 'name' => $contact_form->title(), 'slug' => $contact_form->name(), 'parent' => $channel->parent, ) ); } } add_filter( 'wpcf7_special_mail_tags', 'wpcf7_flamingo_serial_number', 10, 4 ); /** * Returns output string of a special mail-tag. * * @param string $output The string to be output. * @param string $name The tag name of the special mail-tag. * @param bool $html Whether the mail-tag is used in an HTML content. * @param WPCF7_MailTag $mail_tag An object representation of the mail-tag. * @return string Output of the given special mail-tag. */ function wpcf7_flamingo_serial_number( $output, $name, $html, $mail_tag = null ) { if ( ! $mail_tag instanceof WPCF7_MailTag ) { wpcf7_doing_it_wrong( sprintf( '%s()', __FUNCTION__ ), __( 'The fourth parameter ($mail_tag) must be an instance of the WPCF7_MailTag class.', 'contact-form-7' ), '5.2.2' ); } if ( '_serial_number' != $name ) { return $output; } if ( ! class_exists( 'Flamingo_Inbound_Message' ) or ! method_exists( 'Flamingo_Inbound_Message', 'count' ) ) { return $output; } if ( ! $contact_form = WPCF7_ContactForm::get_current() ) { return $output; } $post_meta = get_post_meta( $contact_form->id(), '_flamingo', true ); $channel_id = isset( $post_meta['channel'] ) ? (int) $post_meta['channel'] : wpcf7_flamingo_add_channel( $contact_form->name(), $contact_form->title() ); if ( $channel_id ) { return 1 + (int) Flamingo_Inbound_Message::count( array( 'channel_id' => $channel_id ) ); } return 0; } disallowed-list.php000064400000003423147206762310010367 0ustar00get_posted_data() ); $target[] = $submission->get_meta( 'remote_ip' ); $target[] = $submission->get_meta( 'user_agent' ); $target = implode( "\n", $target ); $word = wpcf7_check_disallowed_list( $target ); $word = wpcf7_apply_filters_deprecated( 'wpcf7_submission_is_blacklisted', array( $word, $submission ), '5.3', 'wpcf7_submission_has_disallowed_words' ); $word = apply_filters( 'wpcf7_submission_has_disallowed_words', $word, $submission ); if ( $word ) { if ( is_bool( $word ) ) { $reason = __( "Disallowed words are used.", 'contact-form-7' ); } else { $reason = sprintf( __( "Disallowed words (%s) are used.", 'contact-form-7' ), implode( ', ', (array) $word ) ); } $submission->add_spam_log( array( 'agent' => 'disallowed_list', 'reason' => $reason, ) ); } $spam = (bool) $word; return $spam; } function wpcf7_check_disallowed_list( $target ) { $mod_keys = get_option( 'disallowed_keys' ); if ( is_scalar( $mod_keys ) ) { $mod_keys = trim( $mod_keys ); } else { $mod_keys = ''; } if ( '' === $mod_keys ) { return false; } foreach ( explode( "\n", $mod_keys ) as $word ) { $word = trim( $word ); $length = strlen( $word ); if ( $length < 2 or 256 < $length ) { continue; } $pattern = sprintf( '#%s#i', preg_quote( $word, '#' ) ); if ( preg_match( $pattern, $target ) ) { return $word; } } return false; } function wpcf7_blacklist_check( $target ) { wpcf7_deprecated_function( __FUNCTION__, '5.3', 'wpcf7_check_disallowed_list' ); return wpcf7_check_disallowed_list( $target ); } akismet/akismet.php000064400000014567147206762310010374 0ustar00add_service( 'akismet', WPCF7_Akismet::get_instance() ); } add_filter( 'wpcf7_spam', 'wpcf7_akismet', 10, 2 ); function wpcf7_akismet( $spam, $submission ) { if ( $spam ) { return $spam; } if ( ! wpcf7_akismet_is_available() ) { return false; } if ( ! $params = wpcf7_akismet_submitted_params() ) { return false; } $comment = array( 'comment_type' => 'contact-form', 'comment_author' => $params['author'], 'comment_author_email' => $params['author_email'], 'comment_author_url' => $params['author_url'], 'comment_content' => $params['content'], 'blog' => home_url(), 'blog_lang' => get_locale(), 'blog_charset' => get_option( 'blog_charset' ), 'user_ip' => $submission->get_meta( 'remote_ip' ), 'user_agent' => $submission->get_meta( 'user_agent' ), 'referrer' => $_SERVER['HTTP_REFERER'] ?? '', ); $datetime = date_create_immutable( '@' . $submission->get_meta( 'timestamp' ) ); if ( $datetime ) { $comment['comment_date_gmt'] = $datetime->format( DATE_ATOM ); } if ( $permalink = get_permalink() ) { $comment['permalink'] = $permalink; } $server_vars = array_diff_key( $_SERVER, array_flip( array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' ) ) ); $comment = array_merge( $comment, $server_vars ); $comment = apply_filters( 'wpcf7_akismet_parameters', $comment ); if ( wpcf7_akismet_comment_check( $comment ) ) { $spam = true; $submission->add_spam_log( array( 'agent' => 'akismet', 'reason' => __( "Akismet returns a spam response.", 'contact-form-7' ), ) ); } else { $spam = false; } return $spam; } /** * Returns true if Akismet is active and has a valid API key. */ function wpcf7_akismet_is_available() { if ( is_callable( array( 'Akismet', 'get_api_key' ) ) ) { return (bool) Akismet::get_api_key(); } return false; } /** * Returns an array of parameters based on the current form submission. * Returns false if Akismet is not active on the contact form. */ function wpcf7_akismet_submitted_params() { $akismet_tags = array_filter( wpcf7_scan_form_tags(), static function ( $tag ) { $akismet_option = $tag->get_option( 'akismet', '(author|author_email|author_url)', true ); return (bool) $akismet_option; } ); if ( ! $akismet_tags ) { // Akismet is not active on this contact form. return false; } $params = array( 'author' => '', 'author_email' => '', 'author_url' => '', 'content' => '', ); foreach ( (array) $_POST as $key => $val ) { if ( '_wpcf7' == substr( $key, 0, 6 ) or '_wpnonce' == $key ) { continue; } $vals = array_filter( wpcf7_array_flatten( $val ), static function ( $val ) { return '' !== trim( $val ); } ); if ( empty( $vals ) ) { continue; } if ( $tags = wpcf7_scan_form_tags( array( 'name' => $key ) ) ) { $tag = $tags[0]; $akismet_option = $tag->get_option( 'akismet', '(author|author_email|author_url)', true ); if ( 'author' === $akismet_option ) { $params['author'] = sprintf( '%s %s', $params['author'], implode( ' ', $vals ) ); continue; } if ( 'author_email' === $akismet_option and '' === $params['author_email'] ) { $params['author_email'] = $vals[0]; continue; } if ( 'author_url' === $akismet_option and '' === $params['author_url'] ) { $params['author_url'] = $vals[0]; continue; } $vals = array_filter( $vals, static function ( $val ) use ( $tag ) { if ( wpcf7_form_tag_supports( $tag->type, 'selectable-values' ) and in_array( $val, $tag->labels ) ) { return false; } else { return true; } } ); } if ( $vals ) { $params['content'] .= "\n\n" . implode( ', ', $vals ); } } $params = array_map( 'trim', $params ); return $params; } /** * Sends data to Akismet. * * @param array $comment Submission and environment data. * @return bool True if Akismet called it spam, or false otherwise. */ function wpcf7_akismet_comment_check( $comment ) { $spam = false; $query_string = wpcf7_build_query( $comment ); if ( is_callable( array( 'Akismet', 'http_post' ) ) ) { $response = Akismet::http_post( $query_string, 'comment-check' ); } else { return $spam; } if ( 'true' == $response[1] ) { $spam = true; } if ( $submission = WPCF7_Submission::get_instance() ) { $submission->push( 'akismet', array( 'comment' => $comment, 'spam' => $spam, ) ); } return apply_filters( 'wpcf7_akismet_comment_check', $spam, $comment ); } add_filter( 'wpcf7_posted_data', 'wpcf7_akismet_posted_data', 10, 1 ); /** * Removes Akismet-related properties from the posted data. * * This does not affect the $_POST variable itself. * * @link https://plugins.trac.wordpress.org/browser/akismet/tags/5.0/_inc/akismet-frontend.js */ function wpcf7_akismet_posted_data( $posted_data ) { if ( wpcf7_akismet_is_available() ) { $posted_data = array_diff_key( $posted_data, array( 'ak_bib' => '', 'ak_bfs' => '', 'ak_bkpc' => '', 'ak_bkp' => '', 'ak_bmc' => '', 'ak_bmcc' => '', 'ak_bmk' => '', 'ak_bck' => '', 'ak_bmmc' => '', 'ak_btmc' => '', 'ak_bsc' => '', 'ak_bte' => '', 'ak_btec' => '', 'ak_bmm' => '', ) ); } return $posted_data; } add_filter( 'wpcf7_default_template', 'wpcf7_akismet_default_template', 10, 2 ); function wpcf7_akismet_default_template( $template, $prop ) { if ( ! wpcf7_akismet_is_available() ) { return $template; } if ( 'form' === $prop ) { $template = str_replace( array( '[text* your-name ', '[email* your-email ', ), array( '[text* your-name akismet:author ', '[email* your-email akismet:author_email ', ), $template ); $privacy_notice = sprintf( '%s %s', __( "This form uses Akismet to reduce spam.", 'contact-form-7' ), wpcf7_link( 'https://akismet.com/privacy/', __( "Learn how your data is processed.", 'contact-form-7' ), array( 'target' => '_blank', 'rel' => 'nofollow noopener', ) ) ); $template .= "\n\n" . $privacy_notice; } return $template; } akismet/service.php000064400000002770147206762310010370 0ustar00%s

', esc_html( __( "CAPTCHAs are designed to distinguish spambots from humans, and are therefore helpless against human spammers. In contrast to CAPTCHAs, Akismet checks form submissions against the global database of spam; this means Akismet is a comprehensive solution against spam. This is why we consider Akismet to be the centerpiece of the spam prevention strategy.", 'contact-form-7' ) ) ); echo sprintf( '

%s

', wpcf7_link( __( 'https://contactform7.com/spam-filtering-with-akismet/', 'contact-form-7' ), __( 'Spam filtering with Akismet', 'contact-form-7' ) ) ); if ( $this->is_active() ) { echo sprintf( '

%s

', esc_html( __( "Akismet is active on this site.", 'contact-form-7' ) ) ); } } } submit.php000064400000004477147206762310006604 0ustar00type, 'has-spinner' ); $atts = array(); $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true ); $value = isset( $tag->values[0] ) ? $tag->values[0] : ''; if ( empty( $value ) ) { $value = __( 'Send', 'contact-form-7' ); } $atts['type'] = 'submit'; $atts['value'] = $value; $atts = wpcf7_format_atts( $atts ); $html = sprintf( '', $atts ); return $html; } /* Tag generator */ add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_submit', 55, 0 ); function wpcf7_add_tag_generator_submit() { $tag_generator = WPCF7_TagGenerator::get_instance(); $tag_generator->add( 'submit', __( 'submit', 'contact-form-7' ), 'wpcf7_tag_generator_submit', array( 'version' => '2' ) ); } function wpcf7_tag_generator_submit( $contact_form, $options ) { $field_types = array( 'submit' => array( 'display_name' => __( 'Submit button', 'contact-form-7' ), 'heading' => __( 'Submit button form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a submit button.', 'contact-form-7' ), ), ); $tgg = new WPCF7_TagGeneratorGenerator( $options['content'] ); ?>

array( 'href' => true ), 'strong' => array(), ), array( 'http', 'https' ) ); echo $description; ?>

print( 'field_type', array( 'select_options' => array( 'submit' => $field_types['submit']['display_name'], ), ) ); $tgg->print( 'class_attr' ); $tgg->print( 'default_value', array( 'title' => __( 'Label', 'contact-form-7' ), ) ); ?>
get_contact_form(); $do_doi = apply_filters( 'wpcf7_do_doi', ! $contact_form->is_false( 'doi' ), $agent_name, $args ); if ( ! $do_doi ) { return; } $token = doihelper_start_session( $agent_name, $args ); } text.php000064400000020001147206762310006242 0ustar00 true, ) ); } function wpcf7_text_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $validation_error = wpcf7_get_validation_error( $tag->name ); $class = wpcf7_form_controls_class( $tag->type, 'wpcf7-text' ); if ( in_array( $tag->basetype, array( 'email', 'url', 'tel' ) ) ) { $class .= ' wpcf7-validates-as-' . $tag->basetype; } if ( $validation_error ) { $class .= ' wpcf7-not-valid'; } $atts = array(); $atts['size'] = $tag->get_size_option( '40' ); $atts['maxlength'] = $tag->get_maxlength_option( '400' ); $atts['minlength'] = $tag->get_minlength_option(); if ( $atts['maxlength'] and $atts['minlength'] and $atts['maxlength'] < $atts['minlength'] ) { unset( $atts['maxlength'], $atts['minlength'] ); } $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $atts['list'] = $tag->get_option( 'list', 'id', true ); $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true ); $atts['readonly'] = $tag->has_option( 'readonly' ); $atts['autocomplete'] = $tag->get_option( 'autocomplete', '[-0-9a-zA-Z]+', true ); if ( $tag->is_required() ) { $atts['aria-required'] = 'true'; } if ( $validation_error ) { $atts['aria-invalid'] = 'true'; $atts['aria-describedby'] = wpcf7_get_validation_error_reference( $tag->name ); } else { $atts['aria-invalid'] = 'false'; } $value = (string) reset( $tag->values ); if ( $tag->has_option( 'placeholder' ) or $tag->has_option( 'watermark' ) ) { $atts['placeholder'] = $value; $value = ''; } $value = $tag->get_default_option( $value ); $value = wpcf7_get_hangover( $tag->name, $value ); $atts['value'] = $value; $atts['type'] = $tag->basetype; $atts['name'] = $tag->name; $html = sprintf( '%3$s', esc_attr( $tag->name ), wpcf7_format_atts( $atts ), $validation_error ); return $html; } add_action( 'wpcf7_swv_create_schema', 'wpcf7_swv_add_text_rules', 10, 2 ); function wpcf7_swv_add_text_rules( $schema, $contact_form ) { $tags = $contact_form->scan_form_tags( array( 'basetype' => array( 'text', 'email', 'url', 'tel' ), ) ); foreach ( $tags as $tag ) { if ( $tag->is_required() ) { $schema->add_rule( wpcf7_swv_create_rule( 'required', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_required' ), ) ) ); } if ( 'email' === $tag->basetype ) { $schema->add_rule( wpcf7_swv_create_rule( 'email', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_email' ), ) ) ); } if ( 'url' === $tag->basetype ) { $schema->add_rule( wpcf7_swv_create_rule( 'url', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_url' ), ) ) ); } if ( 'tel' === $tag->basetype ) { $schema->add_rule( wpcf7_swv_create_rule( 'tel', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_tel' ), ) ) ); } if ( $minlength = $tag->get_minlength_option() ) { $schema->add_rule( wpcf7_swv_create_rule( 'minlength', array( 'field' => $tag->name, 'threshold' => absint( $minlength ), 'error' => wpcf7_get_message( 'invalid_too_short' ), ) ) ); } if ( $maxlength = $tag->get_maxlength_option( '400' ) ) { $schema->add_rule( wpcf7_swv_create_rule( 'maxlength', array( 'field' => $tag->name, 'threshold' => absint( $maxlength ), 'error' => wpcf7_get_message( 'invalid_too_long' ), ) ) ); } } } /* Messages */ add_filter( 'wpcf7_messages', 'wpcf7_text_messages', 10, 1 ); function wpcf7_text_messages( $messages ) { $messages = array_merge( $messages, array( 'invalid_email' => array( 'description' => __( "Email address that the sender entered is invalid", 'contact-form-7' ), 'default' => __( "Please enter an email address.", 'contact-form-7' ), ), 'invalid_url' => array( 'description' => __( "URL that the sender entered is invalid", 'contact-form-7' ), 'default' => __( "Please enter a URL.", 'contact-form-7' ), ), 'invalid_tel' => array( 'description' => __( "Telephone number that the sender entered is invalid", 'contact-form-7' ), 'default' => __( "Please enter a telephone number.", 'contact-form-7' ), ), ) ); return $messages; } /* Tag generator */ add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_text', 15, 0 ); function wpcf7_add_tag_generator_text() { $tag_generator = WPCF7_TagGenerator::get_instance(); $basetypes = array( 'text' => __( 'text', 'contact-form-7' ), 'email' => __( 'email', 'contact-form-7' ), 'url' => __( 'URL', 'contact-form-7' ), 'tel' => __( 'tel', 'contact-form-7' ), ); foreach ( $basetypes as $id => $title ) { $tag_generator->add( $id, $title, 'wpcf7_tag_generator_text', array( 'version' => '2' ) ); } } function wpcf7_tag_generator_text( $contact_form, $options ) { $field_types = array( 'text' => array( 'display_name' => __( 'Text field', 'contact-form-7' ), 'heading' => __( 'Text field form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a single-line plain text input field.', 'contact-form-7' ), 'maybe_purpose' => 'author_name', ), 'email' => array( 'display_name' => __( 'Email address field', 'contact-form-7' ), 'heading' => __( 'Email address field form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for an email address input field.', 'contact-form-7' ), 'maybe_purpose' => 'author_email', ), 'url' => array( 'display_name' => __( 'URL field', 'contact-form-7' ), 'heading' => __( 'URL field form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a URL input field.', 'contact-form-7' ), 'maybe_purpose' => 'author_url', ), 'tel' => array( 'display_name' => __( 'Telephone number field', 'contact-form-7' ), 'heading' => __( 'Telephone number field form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a telephone number input field.', 'contact-form-7' ), 'maybe_purpose' => 'author_tel', ), ); $basetype = $options['id']; if ( ! in_array( $basetype, array_keys( $field_types ) ) ) { $basetype = 'text'; } $tgg = new WPCF7_TagGeneratorGenerator( $options['content'] ); ?>

array( 'href' => true ), 'strong' => array(), ), array( 'http', 'https' ) ); echo $description; ?>

print( 'field_type', array( 'with_required' => true, 'select_options' => array( $basetype => $field_types[$basetype]['display_name'], ), ) ); $tgg->print( 'field_name', array( 'ask_if' => $field_types[$basetype]['maybe_purpose'] ) ); $tgg->print( 'class_attr' ); $tgg->print( 'min_max', array( 'title' => __( 'Length', 'contact-form-7' ), 'min_option' => 'minlength:', 'max_option' => 'maxlength:', ) ); $tgg->print( 'default_value', array( 'with_placeholder' => true, ) ); ?>
add_service( 'constant_contact', $service ); } add_action( 'wpcf7_submit', 'wpcf7_constant_contact_submit', 10, 2 ); /** * Callback to the wpcf7_submit action hook. Creates a contact * based on the submission. */ function wpcf7_constant_contact_submit( $contact_form, $result ) { $service = WPCF7_ConstantContact::get_instance(); if ( ! $service->is_active() ) { return; } if ( $contact_form->in_demo_mode() ) { return; } $do_submit = true; if ( empty( $result['status'] ) or ! in_array( $result['status'], array( 'mail_sent' ) ) ) { $do_submit = false; } $prop = $contact_form->prop( 'constant_contact' ); if ( empty( $prop['enable_contact_list'] ) ) { $do_submit = false; } $do_submit = apply_filters( 'wpcf7_constant_contact_submit', $do_submit, $contact_form, $result ); if ( ! $do_submit ) { return; } $submission = WPCF7_Submission::get_instance(); $consented = true; foreach ( $contact_form->scan_form_tags( 'feature=name-attr' ) as $tag ) { if ( $tag->has_option( 'consent_for:constant_contact' ) and null == $submission->get_posted_data( $tag->name ) ) { $consented = false; break; } } if ( ! $consented ) { return; } $request_builder_class_name = apply_filters( 'wpcf7_constant_contact_contact_post_request_builder', 'WPCF7_ConstantContact_ContactPostRequest' ); if ( ! class_exists( $request_builder_class_name ) ) { return; } $request_builder = new $request_builder_class_name; $request_builder->build( $submission ); if ( ! $request_builder->is_valid() ) { return; } $email = $request_builder->get_email_address(); if ( $email ) { if ( $service->email_exists( $email ) ) { return; } $token = null; do_action_ref_array( 'wpcf7_doi', array( 'wpcf7_constant_contact', array( 'email_to' => $email, 'properties' => $request_builder->to_array(), ), &$token, ) ); if ( isset( $token ) ) { return; } } $service->create_contact( $request_builder->to_array() ); } constant-contact/service.php000064400000030103147206762310012204 0ustar00authorization_endpoint = self::authorization_endpoint; $this->token_endpoint = self::token_endpoint; $option = (array) WPCF7::get_option( self::service_name ); if ( isset( $option['client_id'] ) ) { $this->client_id = $option['client_id']; } if ( isset( $option['client_secret'] ) ) { $this->client_secret = $option['client_secret']; } if ( isset( $option['access_token'] ) ) { $this->access_token = $option['access_token']; } if ( isset( $option['refresh_token'] ) ) { $this->refresh_token = $option['refresh_token']; } if ( $this->is_active() ) { if ( isset( $option['contact_lists'] ) ) { $this->contact_lists = $option['contact_lists']; } } add_action( 'wpcf7_admin_init', array( $this, 'auth_redirect' ) ); } public function auth_redirect() { $auth = isset( $_GET['auth'] ) ? trim( $_GET['auth'] ) : ''; if ( self::service_name === $auth and current_user_can( 'wpcf7_manage_integration' ) ) { $redirect_to = add_query_arg( array( 'service' => self::service_name, 'action' => 'auth_redirect', 'code' => isset( $_GET['code'] ) ? trim( $_GET['code'] ) : '', 'state' => isset( $_GET['state'] ) ? trim( $_GET['state'] ) : '', ), menu_page_url( 'wpcf7-integration', false ) ); wp_safe_redirect( $redirect_to ); exit(); } } protected function save_data() { $option = array_merge( (array) WPCF7::get_option( self::service_name ), array( 'client_id' => $this->client_id, 'client_secret' => $this->client_secret, 'access_token' => $this->access_token, 'refresh_token' => $this->refresh_token, 'contact_lists' => $this->contact_lists, ) ); WPCF7::update_option( self::service_name, $option ); } protected function reset_data() { $this->client_id = ''; $this->client_secret = ''; $this->access_token = ''; $this->refresh_token = ''; $this->contact_lists = array(); $this->save_data(); } public function get_title() { return __( 'Constant Contact', 'contact-form-7' ); } public function get_categories() { return array( 'email_marketing' ); } public function icon() { } public function link() { echo 'constantcontact.com'; } protected function get_redirect_uri() { return admin_url( '/?auth=' . self::service_name ); } protected function menu_page_url( $args = '' ) { $args = wp_parse_args( $args, array() ); $url = menu_page_url( 'wpcf7-integration', false ); $url = add_query_arg( array( 'service' => self::service_name ), $url ); if ( ! empty( $args ) ) { $url = add_query_arg( $args, $url ); } return $url; } public function load( $action = '' ) { if ( 'auth_redirect' == $action ) { $code = isset( $_GET['code'] ) ? urldecode( $_GET['code'] ) : ''; $state = isset( $_GET['state'] ) ? urldecode( $_GET['state'] ) : ''; if ( $code and $state and wpcf7_verify_nonce( $state, 'wpcf7_constant_contact_authorize' ) ) { $response = $this->request_token( $code ); } if ( ! empty( $this->access_token ) ) { $message = 'success'; } else { $message = 'failed'; } wp_safe_redirect( $this->menu_page_url( array( 'action' => 'setup', 'message' => $message, ) ) ); exit(); } if ( 'setup' == $action and 'POST' == $_SERVER['REQUEST_METHOD'] ) { check_admin_referer( 'wpcf7-constant-contact-setup' ); if ( ! empty( $_POST['reset'] ) ) { $this->reset_data(); } else { $this->client_id = isset( $_POST['client_id'] ) ? trim( $_POST['client_id'] ) : ''; $this->client_secret = isset( $_POST['client_secret'] ) ? trim( $_POST['client_secret'] ) : ''; $this->save_data(); $this->authorize( 'contact_data offline_access' ); } wp_safe_redirect( $this->menu_page_url( 'action=setup' ) ); exit(); } } protected function authorize( $scope = '' ) { $endpoint = add_query_arg( array_map( 'urlencode', array( 'response_type' => 'code', 'client_id' => $this->client_id, 'redirect_uri' => $this->get_redirect_uri(), 'scope' => $scope, 'state' => wpcf7_create_nonce( 'wpcf7_constant_contact_authorize' ), ) ), $this->authorization_endpoint ); if ( wp_redirect( sanitize_url( $endpoint ) ) ) { exit(); } } public function email_exists( $email ) { $endpoint = add_query_arg( array( 'email' => $email, 'status' => 'all', ), 'https://api.cc.email/v3/contacts' ); $request = array( 'method' => 'GET', 'headers' => array( 'Accept' => 'application/json', 'Content-Type' => 'application/json; charset=utf-8', ), ); $response = $this->remote_request( $endpoint, $request ); if ( 400 <= (int) wp_remote_retrieve_response_code( $response ) ) { if ( WP_DEBUG ) { $this->log( $endpoint, $request, $response ); } return false; } $response_body = wp_remote_retrieve_body( $response ); if ( empty( $response_body ) ) { return false; } $response_body = json_decode( $response_body, true ); return ! empty( $response_body['contacts'] ); } public function create_contact( $properties ) { $endpoint = 'https://api.cc.email/v3/contacts'; $request = array( 'method' => 'POST', 'headers' => array( 'Accept' => 'application/json', 'Content-Type' => 'application/json; charset=utf-8', ), 'body' => wp_json_encode( $properties ), ); $response = $this->remote_request( $endpoint, $request ); if ( 400 <= (int) wp_remote_retrieve_response_code( $response ) ) { if ( WP_DEBUG ) { $this->log( $endpoint, $request, $response ); } return false; } } public function get_contact_lists() { $endpoint = 'https://api.cc.email/v3/contact_lists'; $request = array( 'method' => 'GET', 'headers' => array( 'Accept' => 'application/json', 'Content-Type' => 'application/json; charset=utf-8', ), ); $response = $this->remote_request( $endpoint, $request ); if ( 400 <= (int) wp_remote_retrieve_response_code( $response ) ) { if ( WP_DEBUG ) { $this->log( $endpoint, $request, $response ); } return false; } $response_body = wp_remote_retrieve_body( $response ); if ( empty( $response_body ) ) { return false; } $response_body = json_decode( $response_body, true ); if ( ! empty( $response_body['lists'] ) ) { return (array) $response_body['lists']; } else { return array(); } } public function update_contact_lists( $selection = array() ) { $contact_lists = array(); $contact_lists_on_api = $this->get_contact_lists(); if ( false !== $contact_lists_on_api ) { foreach ( (array) $contact_lists_on_api as $list ) { if ( isset( $list['list_id'] ) ) { $list_id = trim( $list['list_id'] ); } else { continue; } if ( isset( $this->contact_lists[$list_id]['selected'] ) ) { $list['selected'] = $this->contact_lists[$list_id]['selected']; } else { $list['selected'] = array(); } $contact_lists[$list_id] = $list; } } else { $contact_lists = $this->contact_lists; } foreach ( (array) $selection as $key => $ids_or_names ) { foreach( $contact_lists as $list_id => $list ) { if ( in_array( $list['list_id'], (array) $ids_or_names, true ) or in_array( $list['name'], (array) $ids_or_names, true ) ) { $contact_lists[$list_id]['selected'][$key] = true; } else { unset( $contact_lists[$list_id]['selected'][$key] ); } } } $this->contact_lists = $contact_lists; if ( $selection ) { $this->save_data(); } return $this->contact_lists; } public function admin_notice( $message = '' ) { switch ( $message ) { case 'success': wp_admin_notice( esc_html( __( "Connection established.", 'contact-form-7' ) ), 'type=success' ); break; case 'failed': wp_admin_notice( sprintf( '%1$s: %2$s', esc_html( __( "Error", 'contact-form-7' ) ), esc_html( __( "Failed to establish connection. Please double-check your configuration.", 'contact-form-7' ) ) ), 'type=error' ); break; case 'updated': wp_admin_notice( esc_html( __( "Configuration updated.", 'contact-form-7' ) ), 'type=success' ); break; } } public function display( $action = '' ) { echo sprintf( '

%1$s %2$s

', esc_html( __( 'Warning:', 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/2024/02/02/we-end-the-constant-contact-integration/', 'contact-form-7' ), __( "This feature is deprecated. You are not recommended to use it.", 'contact-form-7' ) ) ); echo sprintf( '

%s

', esc_html( __( "The Constant Contact integration module allows you to send contact data collected through your contact forms to the Constant Contact API. You can create reliable email subscription services in a few easy steps.", 'contact-form-7' ) ) ); echo sprintf( '

%s

', wpcf7_link( __( 'https://contactform7.com/constant-contact-integration/', 'contact-form-7' ), __( 'Constant Contact integration', 'contact-form-7' ) ) ); if ( $this->is_active() ) { echo sprintf( '

%s

', esc_html( __( "This site is connected to the Constant Contact API.", 'contact-form-7' ) ) ); } if ( 'setup' == $action ) { $this->display_setup(); } else { echo sprintf( '

%2$s

', esc_url( $this->menu_page_url( 'action=setup' ) ), esc_html( __( 'Setup Integration', 'contact-form-7' ) ) ); } } private function display_setup() { ?>
is_active() ) { echo esc_html( $this->client_id ); echo sprintf( '', esc_attr( $this->client_id ) ); } else { echo sprintf( '', esc_attr( $this->client_id ) ); } ?>
is_active() ) { echo esc_html( wpcf7_mask_password( $this->client_secret, 4, 4 ) ); echo sprintf( '', esc_attr( $this->client_secret ) ); } else { echo sprintf( '', esc_attr( $this->client_secret ) ); } ?>
', $this->get_redirect_uri() ); ?>

is_active() ) { submit_button( _x( 'Reset Keys', 'API keys', 'contact-form-7' ), 'small', 'reset' ); } else { submit_button( __( 'Connect to the Constant Contact API', 'contact-form-7' ) ); } ?>
set_create_source( 'Contact' ); $this->set_first_name( $submission->get_posted_string( 'your-first-name' ) ); $this->set_last_name( $submission->get_posted_string( 'your-last-name' ) ); if ( ! ( $this->first_name || $this->last_name ) and $your_name = $submission->get_posted_string( 'your-name' ) ) { $your_name = preg_split( '/[\s]+/', $your_name, 2 ); $this->set_first_name( array_shift( $your_name ) ); $this->set_last_name( array_shift( $your_name ) ); } $this->set_email_address( $submission->get_posted_string( 'your-email' ), 'implicit' ); $this->set_job_title( $submission->get_posted_string( 'your-job-title' ) ); $this->set_company_name( $submission->get_posted_string( 'your-company-name' ) ); $this->set_birthday( $submission->get_posted_string( 'your-birthday-month' ), $submission->get_posted_string( 'your-birthday-day' ) ); if ( ! ( $this->birthday_month && $this->birthday_day ) ) { $date = $submission->get_posted_string( 'your-birthday' ); if ( preg_match( '/^(\d{4})-(\d{2})-(\d{2})$/', $date, $matches ) ) { $this->set_birthday( $matches[2], $matches[3] ); } } $this->set_anniversary( $submission->get_posted_string( 'your-anniversary' ) ); $this->add_phone_number( $submission->get_posted_string( 'your-phone-number' ) ); $this->add_street_address( $submission->get_posted_string( 'your-address-street' ), $submission->get_posted_string( 'your-address-city' ), $submission->get_posted_string( 'your-address-state' ), $submission->get_posted_string( 'your-address-postal-code' ), $submission->get_posted_string( 'your-address-country' ) ); $service_option = (array) WPCF7::get_option( 'constant_contact' ); $contact_lists = isset( $service_option['contact_lists'] ) ? $service_option['contact_lists'] : array(); $contact_form = $submission->get_contact_form(); $prop = $contact_form->prop( 'constant_contact' ); if ( ! empty( $prop['enable_contact_list'] ) and ! empty( $prop['contact_lists'] ) ) { foreach ( (array) $prop['contact_lists'] as $list_id ) { $this->add_list_membership( $list_id ); } } } public function is_valid() { return $this->create_source && ( $this->email_address || $this->first_name || $this->last_name ); } public function to_array() { $output = array( 'email_address' => $this->email_address, 'first_name' => $this->first_name, 'last_name' => $this->last_name, 'job_title' => $this->job_title, 'company_name' => $this->company_name, 'create_source' => $this->create_source, 'birthday_month' => $this->birthday_month, 'birthday_day' => $this->birthday_day, 'anniversary' => $this->anniversary, 'custom_fields' => $this->custom_fields, 'phone_numbers' => $this->phone_numbers, 'street_addresses' => $this->street_addresses, 'list_memberships' => $this->list_memberships, ); return array_filter( $output ); } public function get_email_address() { if ( isset( $this->email_address['address'] ) ) { return $this->email_address['address']; } return ''; } public function set_email_address( $address, $permission_to_send = '' ) { if ( ! wpcf7_is_email( $address ) or 80 < $this->strlen( $address ) ) { return false; } $types_of_permission = array( 'implicit', 'explicit', 'deprecate', 'pending', 'unsubscribe', 'temp_hold', 'not_set', ); if ( ! in_array( $permission_to_send, $types_of_permission ) ) { $permission_to_send = 'implicit'; } return $this->email_address = array( 'address' => $address, 'permission_to_send' => $permission_to_send, ); } public function set_first_name( $first_name ) { $first_name = trim( $first_name ); if ( empty( $first_name ) or 50 < $this->strlen( $first_name ) ) { return false; } return $this->first_name = $first_name; } public function set_last_name( $last_name ) { $last_name = trim( $last_name ); if ( empty( $last_name ) or 50 < $this->strlen( $last_name ) ) { return false; } return $this->last_name = $last_name; } public function set_job_title( $job_title ) { $job_title = trim( $job_title ); if ( empty( $job_title ) or 50 < $this->strlen( $job_title ) ) { return false; } return $this->job_title = $job_title; } public function set_company_name( $company_name ) { $company_name = trim( $company_name ); if ( empty( $company_name ) or 50 < $this->strlen( $company_name ) ) { return false; } return $this->company_name = $company_name; } public function set_create_source( $create_source ) { if ( ! in_array( $create_source, array( 'Contact', 'Account' ) ) ) { return false; } return $this->create_source = $create_source; } public function set_birthday( $month, $day ) { $month = (int) $month; $day = (int) $day; if ( $month < 1 || 12 < $month or $day < 1 || 31 < $day ) { return false; } $this->birthday_month = $month; $this->birthday_day = $day; return array( $this->birthday_month, $this->birthday_day ); } public function set_anniversary( $anniversary ) { $pattern = sprintf( '#^(%s)$#', implode( '|', array( '\d{1,2}/\d{1,2}/\d{4}', '\d{4}/\d{1,2}/\d{1,2}', '\d{4}-\d{1,2}-\d{1,2}', '\d{1,2}-\d{1,2}-\d{4}', ) ) ); if ( ! preg_match( $pattern, $anniversary ) ) { return false; } return $this->anniversary = $anniversary; } public function add_custom_field( $custom_field_id, $value ) { $uuid_pattern = '/^[0-9a-f-]+$/i'; $value = trim( $value ); if ( 25 <= count( $this->custom_fields ) or ! preg_match( $uuid_pattern, $custom_field_id ) or 255 < $this->strlen( $value ) ) { return false; } return $this->custom_fields[] = array( 'custom_field_id' => $custom_field_id, 'value' => $value, ); } public function add_phone_number( $phone_number, $kind = 'home' ) { $phone_number = trim( $phone_number ); if ( empty( $phone_number ) or ! wpcf7_is_tel( $phone_number ) or 25 < $this->strlen( $phone_number ) or 2 <= count( $this->phone_numbers ) or ! in_array( $kind, array( 'home', 'work', 'other' ) ) ) { return false; } return $this->phone_numbers[] = array( 'phone_number' => $phone_number, 'kind' => $kind, ); } public function add_street_address( $street, $city, $state, $postal_code, $country, $kind = 'home' ) { $street = trim( $street ); $city = trim( $city ); $state = trim( $state ); $postal_code = trim( $postal_code ); $country = trim( $country ); if ( ! ( $street || $city || $state || $postal_code || $country ) or 1 <= count( $this->street_addresses ) or ! in_array( $kind, array( 'home', 'work', 'other' ) ) or 255 < $this->strlen( $street ) or 50 < $this->strlen( $city ) or 50 < $this->strlen( $state ) or 50 < $this->strlen( $postal_code ) or 50 < $this->strlen( $country ) ) { return false; } return $this->street_addresses[] = array( 'kind' => $kind, 'street' => $street, 'city' => $city, 'state' => $state, 'postal_code' => $postal_code, 'country' => $country, ); } public function add_list_membership( $list_id ) { $uuid_pattern = '/^[0-9a-f-]+$/i'; if ( 50 <= count( $this->list_memberships ) or ! preg_match( $uuid_pattern, $list_id ) ) { return false; } return $this->list_memberships[] = $list_id; } protected function strlen( $text ) { return wpcf7_count_code_units( $text ); } } constant-contact/contact-form-properties.php000064400000013654147206762310015346 0ustar00is_active() ) { $properties += array( 'constant_contact' => array(), ); } return $properties; } add_filter( 'wpcf7_contact_form_property_constant_contact', 'wpcf7_constant_contact_setup_property', 10, 2 ); /** * Sets up the constant_contact property value. For back-compat, this attempts * to take over the value from old settings if the property is empty. */ function wpcf7_constant_contact_setup_property( $property, $contact_form ) { if ( ! empty( $property ) ) { $property = wp_parse_args( $property, array( 'enable_contact_list' => false, 'contact_lists' => array(), ) ); return $property; } $property = array( 'enable_contact_list' => true, 'contact_lists' => array(), ); if ( $contact_form->initial() ) { return $property; } $service_option = (array) WPCF7::get_option( 'constant_contact' ); $property['enable_contact_list'] = ! $contact_form->is_false( 'constant_contact' ); if ( isset( $service_option['contact_lists'] ) ) { $contact_lists = (array) $service_option['contact_lists']; $contact_lists_selected = array(); foreach ( $contact_lists as $list ) { if ( empty( $list['selected'] ) ) { continue; } foreach ( (array) $list['selected'] as $key => $val ) { if ( ! isset( $contact_lists_selected[$key] ) ) { $contact_lists_selected[$key] = array(); } $contact_lists_selected[$key][] = $list['list_id']; } } $related_keys = array( sprintf( 'wpcf7_contact_form:%d', $contact_form->id() ), 'default', ); foreach ( $related_keys as $key ) { if ( ! empty( $contact_lists_selected[$key] ) ) { $property['contact_lists'] = $contact_lists_selected[$key]; break; } } } return $property; } add_action( 'wpcf7_save_contact_form', 'wpcf7_constant_contact_save_contact_form', 10, 1 ); /** * Saves the constant_contact property value. */ function wpcf7_constant_contact_save_contact_form( $contact_form ) { $service = WPCF7_ConstantContact::get_instance(); if ( ! $service->is_active() ) { return; } $prop = isset( $_POST['wpcf7-ctct'] ) ? (array) $_POST['wpcf7-ctct'] : array(); $prop = wp_parse_args( $prop, array( 'enable_contact_list' => false, 'contact_lists' => array(), ) ); $contact_form->set_properties( array( 'constant_contact' => $prop, ) ); } add_filter( 'wpcf7_editor_panels', 'wpcf7_constant_contact_editor_panels', 10, 1 ); /** * Builds the editor panel for the constant_contact property. */ function wpcf7_constant_contact_editor_panels( $panels ) { $service = WPCF7_ConstantContact::get_instance(); if ( ! $service->is_active() ) { return $panels; } $contact_form = WPCF7_ContactForm::get_current(); $prop = wp_parse_args( $contact_form->prop( 'constant_contact' ), array( 'enable_contact_list' => false, 'contact_lists' => array(), ) ); $editor_panel = static function () use ( $prop, $service ) { $description = sprintf( esc_html( __( "You can set up the Constant Contact integration here. For details, see %s.", 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/constant-contact-integration/', 'contact-form-7' ), __( 'Constant Contact integration', 'contact-form-7' ) ) ); $lists = $service->get_contact_lists(); ?>

array( 'title' => __( 'Constant Contact', 'contact-form-7' ), 'callback' => $editor_panel, ), ); return $panels; } constant-contact/doi.php000064400000004226147206762310011326 0ustar00 apply_filters( 'wpcf7_constant_contact_doi_optin_callback', 'wpcf7_constant_contact_doi_default_optin_callback' ), 'email_callback' => apply_filters( 'wpcf7_constant_contact_doi_email_callback', 'wpcf7_constant_contact_doi_default_email_callback' ), ) ); } /** * Default optin_callback function. */ function wpcf7_constant_contact_doi_default_optin_callback( $properties ) { $service = WPCF7_ConstantContact::get_instance(); if ( $service->is_active() ) { $service->create_contact( $properties ); } } /** * Default email_callback function. */ function wpcf7_constant_contact_doi_default_email_callback( $args ) { if ( ! isset( $args['token'] ) or ! isset( $args['email_to'] ) ) { return; } $site_title = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); $link = add_query_arg( array( 'doitoken' => $args['token'] ), home_url() ); $to = $args['email_to']; $subject = sprintf( /* translators: %s: blog name */ __( 'Opt-in confirmation from %s', 'contact-form-7' ), $site_title ); $message = sprintf( /* translators: 1: blog name, 2: confirmation link */ __( 'Hello, This is a confirmation email sent from %1$s. We have received your submission to our web form, according to which you have allowed us to add you to our contact list. But, the process has not yet been completed. To complete it, please click the following link. %2$s If it was not your intention, or if you have no idea why you received this message, please do not click on the link, and ignore this message. We will never collect or use your personal data without your clear consent. Sincerely, %1$s', 'contact-form-7' ), $site_title, $link ); wp_mail( $to, $subject, $message ); } date.php000064400000013337147206762310006211 0ustar00 true, ) ); } function wpcf7_date_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $validation_error = wpcf7_get_validation_error( $tag->name ); $class = wpcf7_form_controls_class( $tag->type ); $class .= ' wpcf7-validates-as-date'; if ( $validation_error ) { $class .= ' wpcf7-not-valid'; } $atts = array(); $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true ); $atts['min'] = $tag->get_date_option( 'min' ); $atts['max'] = $tag->get_date_option( 'max' ); $atts['step'] = $tag->get_option( 'step', 'int', true ); $atts['readonly'] = $tag->has_option( 'readonly' ); $atts['autocomplete'] = $tag->get_option( 'autocomplete', '[-0-9a-zA-Z]+', true ); if ( $tag->is_required() ) { $atts['aria-required'] = 'true'; } if ( $validation_error ) { $atts['aria-invalid'] = 'true'; $atts['aria-describedby'] = wpcf7_get_validation_error_reference( $tag->name ); } else { $atts['aria-invalid'] = 'false'; } $value = (string) reset( $tag->values ); if ( $tag->has_option( 'placeholder' ) or $tag->has_option( 'watermark' ) ) { $atts['placeholder'] = $value; $value = ''; } $value = $tag->get_default_option( $value ); if ( $value ) { $datetime_obj = date_create_immutable( preg_replace( '/[_]+/', ' ', $value ), wp_timezone() ); if ( $datetime_obj ) { $value = $datetime_obj->format( 'Y-m-d' ); } } $value = wpcf7_get_hangover( $tag->name, $value ); $atts['value'] = $value; $atts['type'] = $tag->basetype; $atts['name'] = $tag->name; $html = sprintf( '%3$s', esc_attr( $tag->name ), wpcf7_format_atts( $atts ), $validation_error ); return $html; } add_action( 'wpcf7_swv_create_schema', 'wpcf7_swv_add_date_rules', 10, 2 ); function wpcf7_swv_add_date_rules( $schema, $contact_form ) { $tags = $contact_form->scan_form_tags( array( 'basetype' => array( 'date' ), ) ); foreach ( $tags as $tag ) { if ( $tag->is_required() ) { $schema->add_rule( wpcf7_swv_create_rule( 'required', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_required' ), ) ) ); } $schema->add_rule( wpcf7_swv_create_rule( 'date', array( 'field' => $tag->name, 'error' => wpcf7_get_message( 'invalid_date' ), ) ) ); $min = $tag->get_date_option( 'min' ); $max = $tag->get_date_option( 'max' ); if ( false !== $min ) { $schema->add_rule( wpcf7_swv_create_rule( 'mindate', array( 'field' => $tag->name, 'threshold' => $min, 'error' => wpcf7_get_message( 'date_too_early' ), ) ) ); } if ( false !== $max ) { $schema->add_rule( wpcf7_swv_create_rule( 'maxdate', array( 'field' => $tag->name, 'threshold' => $max, 'error' => wpcf7_get_message( 'date_too_late' ), ) ) ); } } } /* Messages */ add_filter( 'wpcf7_messages', 'wpcf7_date_messages', 10, 1 ); function wpcf7_date_messages( $messages ) { return array_merge( $messages, array( 'invalid_date' => array( 'description' => __( "Date format that the sender entered is invalid", 'contact-form-7' ), 'default' => __( "Please enter a date in YYYY-MM-DD format.", 'contact-form-7' ), ), 'date_too_early' => array( 'description' => __( "Date is earlier than minimum limit", 'contact-form-7' ), 'default' => __( "This field has a too early date.", 'contact-form-7' ), ), 'date_too_late' => array( 'description' => __( "Date is later than maximum limit", 'contact-form-7' ), 'default' => __( "This field has a too late date.", 'contact-form-7' ), ), ) ); } /* Tag generator */ add_action( 'wpcf7_admin_init', 'wpcf7_add_tag_generator_date', 19, 0 ); function wpcf7_add_tag_generator_date() { $tag_generator = WPCF7_TagGenerator::get_instance(); $tag_generator->add( 'date', __( 'date', 'contact-form-7' ), 'wpcf7_tag_generator_date', array( 'version' => '2' ) ); } function wpcf7_tag_generator_date( $contact_form, $options ) { $field_types = array( 'date' => array( 'display_name' => __( 'Date field', 'contact-form-7' ), 'heading' => __( 'Date field form-tag generator', 'contact-form-7' ), 'description' => __( 'Generates a form-tag for a date input field.', 'contact-form-7' ), ), ); $tgg = new WPCF7_TagGeneratorGenerator( $options['content'] ); ?>

array( 'href' => true ), 'strong' => array(), ), array( 'http', 'https' ) ); echo $description; ?>

print( 'field_type', array( 'with_required' => true, 'select_options' => array( 'date' => $field_types['date']['display_name'], ), ) ); $tgg->print( 'field_name' ); $tgg->print( 'class_attr' ); $tgg->print( 'min_max', array( 'type' => 'date', 'title' => __( 'Range', 'contact-form-7' ), 'min_option' => 'min:', 'max_option' => 'max:', ) ); $tgg->print( 'default_value', array( 'type' => 'date', 'with_placeholder' => false, ) ); ?>
{const t=()=>{const e=document.querySelectorAll("form.wpcf7-form .wpcf7-stripe");for(let t=0;t{const t=e.detail.unitTag,s=`${t}-ve-stripe-card-element`,n=document.querySelector(`#${t} form`),o=n.closest(".wpcf7").querySelector(".screen-reader-response"),d=n.querySelector(".wpcf7-stripe .wpcf7-form-control-wrap"),c=n.querySelector(".wpcf7-stripe button.first"),a=n.querySelector(".wpcf7-stripe button.second"),l=n.querySelector('[name="_wpcf7_stripe_payment_intent"]');if(!l)return;l.setAttribute("value","");const u=e=>{const t=o.querySelector("ul"),r=t.querySelector(`li#${s}`);r&&r.remove();const i=document.createElement("li");i.setAttribute("id",s),i.insertAdjacentText("beforeend",e.message),t.appendChild(i)},p=e=>{const t=d.querySelector(".wpcf7-form-control");t.classList.add("wpcf7-not-valid"),t.setAttribute("aria-describedby",s);const r=document.createElement("span");r.setAttribute("class","wpcf7-not-valid-tip"),r.setAttribute("aria-hidden","true"),r.insertAdjacentText("beforeend",e.message),d.appendChild(r),d.querySelectorAll("[aria-invalid]").forEach((e=>{e.setAttribute("aria-invalid","true")})),t.closest(".use-floating-validation-tip")&&(t.addEventListener("focus",(e=>{r.setAttribute("style","display: none")})),r.addEventListener("mouseover",(e=>{r.setAttribute("style","display: none")})))},f=()=>{o.querySelectorAll(`ul li#${s}`).forEach((e=>{e.remove()})),d.querySelectorAll(".wpcf7-not-valid-tip").forEach((e=>{e.remove()})),d.querySelectorAll("[aria-invalid]").forEach((e=>{e.setAttribute("aria-invalid","false")})),d.querySelectorAll(".wpcf7-form-control").forEach((e=>{e.removeAttribute("aria-describedby"),e.classList.remove("wpcf7-not-valid")}))};if("payment_required"===e.detail.status){const s=e.detail.apiResponse.stripe.payment_intent;s.id&&l.setAttribute("value",s.id);const o=i.getElement("card")||i.create("card");o.mount(`#${t} .wpcf7-stripe .card-element`),o.clear(),d.classList.remove("hidden"),c.classList.add("hidden"),a.classList.remove("hidden"),a.disabled=!0,o.addEventListener("change",(e=>{if(f(),e.error){const t={message:e.error.message};u(t),p(t),a.disabled=!0}else a.disabled=!1})),a.addEventListener("click",(e=>{f(),a.disabled=!0,n.classList.add("submitting"),wpcf7.blocked||r.confirmCardPayment(s.client_secret,{payment_method:{card:o}}).then((e=>{if(e.error){e.error.decline_code&&["fraudulent","lost_card","merchant_blacklist","pickup_card","restricted_card","security_violation","service_not_allowed","stolen_card","transaction_not_allowed"].includes(e.error.decline_code)&&(wpcf7.blocked=!0),n.classList.remove("submitting");const t={message:e.error.message};u(t),p(t)}else"succeeded"===e.paymentIntent.status&&wpcf7.submit(n)}))}))}else d.classList.add("hidden"),c.classList.remove("hidden"),a.classList.add("hidden"),["mail_sent","mail_failed"].includes(e.detail.status)&&(c.disabled=!0)}))}));stripe/stripe.php000064400000015517147206762310010112 0ustar00add_service( 'stripe', WPCF7_Stripe::get_instance() ); } add_action( 'wpcf7_enqueue_scripts', 'wpcf7_stripe_enqueue_scripts', 10, 0 ); /** * Enqueues scripts and styles for the Stripe module. */ function wpcf7_stripe_enqueue_scripts() { $service = WPCF7_Stripe::get_instance(); if ( ! $service->is_active() ) { return; } wp_enqueue_style( 'wpcf7-stripe', wpcf7_plugin_url( 'modules/stripe/style.css' ), array(), WPCF7_VERSION, 'all' ); wp_register_script( 'stripe', 'https://js.stripe.com/v3/', array(), null ); $assets = include( wpcf7_plugin_path( 'modules/stripe/index.asset.php' ) ); $assets = wp_parse_args( $assets, array( 'dependencies' => array(), 'version' => WPCF7_VERSION, ) ); wp_enqueue_script( 'wpcf7-stripe', wpcf7_plugin_url( 'modules/stripe/index.js' ), array_merge( $assets['dependencies'], array( 'wp-polyfill', 'contact-form-7', 'stripe', ) ), $assets['version'], array( 'in_footer' => true ) ); $api_keys = $service->get_api_keys(); if ( $api_keys['publishable'] ) { wp_add_inline_script( 'wpcf7-stripe', sprintf( 'var wpcf7_stripe = %s;', wp_json_encode( array( 'publishable_key' => $api_keys['publishable'], ), JSON_PRETTY_PRINT ) ), 'before' ); } } add_filter( 'wpcf7_skip_spam_check', 'wpcf7_stripe_skip_spam_check', 10, 2 ); /** * Skips the spam check if it is not necessary. * * @return bool True if the spam check is not necessary. */ function wpcf7_stripe_skip_spam_check( $skip_spam_check, $submission ) { $service = WPCF7_Stripe::get_instance(); if ( ! $service->is_active() ) { return $skip_spam_check; } if ( ! empty( $_POST['_wpcf7_stripe_payment_intent'] ) ) { $pi_id = trim( $_POST['_wpcf7_stripe_payment_intent'] ); $payment_intent = $service->api()->retrieve_payment_intent( $pi_id ); if ( isset( $payment_intent['status'] ) and ( 'succeeded' === $payment_intent['status'] ) ) { $submission->push( 'payment_intent', $pi_id ); } } if ( ! empty( $submission->pull( 'payment_intent' ) ) and $submission->verify_posted_data_hash() ) { $skip_spam_check = true; } return $skip_spam_check; } add_action( 'wpcf7_before_send_mail', 'wpcf7_stripe_before_send_mail', 10, 3 ); /** * Creates Stripe's Payment Intent. */ function wpcf7_stripe_before_send_mail( $contact_form, &$abort, $submission ) { $service = WPCF7_Stripe::get_instance(); if ( ! $service->is_active() ) { return; } $tags = $contact_form->scan_form_tags( array( 'type' => 'stripe' ) ); if ( ! $tags ) { return; } if ( ! empty( $submission->pull( 'payment_intent' ) ) ) { return; } $tag = $tags[0]; $amount = $tag->get_option( 'amount', 'int', true ); $currency = $tag->get_option( 'currency', '[a-zA-Z]{3}', true ); $payment_intent_params = apply_filters( 'wpcf7_stripe_payment_intent_parameters', array( 'amount' => $amount ? absint( $amount ) : null, 'currency' => $currency ? strtolower( $currency ) : null, 'receipt_email' => $submission->get_posted_data( 'your-email' ), ) ); $payment_intent = $service->api()->create_payment_intent( $payment_intent_params ); if ( $payment_intent ) { $submission->add_result_props( array( 'stripe' => array( 'payment_intent' => array( 'id' => $payment_intent['id'], 'client_secret' => $payment_intent['client_secret'], ), ), ) ); $submission->set_status( 'payment_required' ); $submission->set_response( __( "Payment is required. Please pay by credit card.", 'contact-form-7' ) ); } $abort = true; } /** * Returns payment link URL. * * @param string $pi_id Payment Intent ID. * @return string The URL. */ function wpcf7_stripe_get_payment_link( $pi_id ) { return sprintf( 'https://dashboard.stripe.com/payments/%s', urlencode( $pi_id ) ); } add_filter( 'wpcf7_special_mail_tags', 'wpcf7_stripe_smt', 10, 4 ); /** * Registers the [_stripe_payment_link] special mail-tag. */ function wpcf7_stripe_smt( $output, $tag_name, $html, $mail_tag = null ) { if ( '_stripe_payment_link' === $tag_name ) { $submission = WPCF7_Submission::get_instance(); $pi_id = $submission->pull( 'payment_intent' ); if ( ! empty( $pi_id ) ) { $output = wpcf7_stripe_get_payment_link( $pi_id ); } } return $output; } add_filter( 'wpcf7_flamingo_inbound_message_parameters', 'wpcf7_stripe_add_flamingo_inbound_message_params', 10, 1 ); /** * Adds Stripe-related meta data to Flamingo Inbound Message parameters. */ function wpcf7_stripe_add_flamingo_inbound_message_params( $args ) { $submission = WPCF7_Submission::get_instance(); $pi_id = $submission->pull( 'payment_intent' ); if ( empty( $pi_id ) ) { return $args; } $pi_link = wpcf7_stripe_get_payment_link( $pi_id ); $meta = (array) $args['meta']; $meta['stripe_payment_link'] = $pi_link; $args['meta'] = $meta; return $args; } add_action( 'wpcf7_init', 'wpcf7_add_form_tag_stripe', 10, 0 ); /** * Registers the stripe form-tag handler. */ function wpcf7_add_form_tag_stripe() { wpcf7_add_form_tag( 'stripe', 'wpcf7_stripe_form_tag_handler', array( 'display-block' => true, 'singular' => true, ) ); } /** * Defines the stripe form-tag handler. * * @return string HTML content that replaces a stripe form-tag. */ function wpcf7_stripe_form_tag_handler( $tag ) { $card_element = sprintf( '
', wpcf7_format_atts( array( 'class' => 'card-element wpcf7-form-control', 'aria-invalid' => 'false', ) ) ); $card_element = sprintf( '', $card_element ); $button_1_label = __( 'Proceed to checkout', 'contact-form-7' ); if ( isset( $tag->values[0] ) ) { $button_1_label = trim( $tag->values[0] ); } $button_1 = sprintf( '', wpcf7_format_atts( array( 'type' => 'submit', 'class' => 'first', ) ), esc_html( $button_1_label ) ); $button_2_label = __( 'Complete payment', 'contact-form-7' ); if ( isset( $tag->values[1] ) ) { $button_2_label = trim( $tag->values[1] ); } $button_2 = sprintf( '', wpcf7_format_atts( array( 'type' => 'button', 'class' => 'second hidden', ) ), esc_html( $button_2_label ) ); $buttons = sprintf( '%1$s %2$s', $button_1, $button_2 ); return sprintf( '
%1$s %2$s %3$s
', $card_element, $buttons, '' ); } stripe/style.css000064400000000473147206762310007740 0ustar00.wpcf7 .wpcf7-stripe .wpcf7-form-control-wrap { margin: .6em 0; } .wpcf7 .wpcf7-stripe .wpcf7-form-control { display: block; background: #f6f7f7; padding: 12px 12px; border: 1px solid #787c82; } .wpcf7 .wpcf7-stripe button:disabled { cursor: not-allowed; } .wpcf7 .wpcf7-stripe .hidden { display: none; } stripe/api.php000064400000006667147206762310007363 0ustar00secret = $secret; } /** * Sends a debug information for a remote request to the PHP error log. * * @param string $url URL to retrieve. * @param array $request Request arguments. * @param array|WP_Error $response The response or WP_Error on failure. */ private function log( $url, $request, $response ) { wpcf7_log_remote_request( $url, $request, $response ); } /** * Returns default set of HTTP request headers used for Stripe API. * * @link https://stripe.com/docs/building-plugins#setappinfo * * @return array An associative array of headers. */ private function default_headers() { $app_info = array( 'name' => self::app_name, 'partner_id' => self::partner_id, 'url' => self::app_url, 'version' => WPCF7_VERSION, ); $ua = array( 'lang' => 'php', 'lang_version' => PHP_VERSION, 'application' => $app_info, ); $headers = array( 'Authorization' => sprintf( 'Bearer %s', $this->secret ), 'Stripe-Version' => self::api_version, 'X-Stripe-Client-User-Agent' => wp_json_encode( $ua ), 'User-Agent' => sprintf( '%1$s/%2$s (%3$s)', self::app_name, WPCF7_VERSION, self::app_url ), ); return $headers; } /** * Creates a Payment Intent. * * @link https://stripe.com/docs/api/payment_intents/create * * @param string|array $args Optional. Arguments to control behavior. * @return array|bool An associative array if 200 OK, false otherwise. */ public function create_payment_intent( $args = '' ) { $args = wp_parse_args( $args, array( 'amount' => 0, 'currency' => '', 'receipt_email' => '', ) ); if ( ! is_email( $args['receipt_email'] ) ) { unset( $args['receipt_email'] ); } $endpoint = 'https://api.stripe.com/v1/payment_intents'; $request = array( 'headers' => $this->default_headers(), 'body' => $args, ); $response = wp_remote_post( sanitize_url( $endpoint ), $request ); if ( 200 != wp_remote_retrieve_response_code( $response ) ) { if ( WP_DEBUG ) { $this->log( $endpoint, $request, $response ); } return false; } $response_body = wp_remote_retrieve_body( $response ); $response_body = json_decode( $response_body, true ); return $response_body; } /** * Retrieve a Payment Intent. * * @link https://stripe.com/docs/api/payment_intents/retrieve * * @param string $id Payment Intent identifier. * @return array|bool An associative array if 200 OK, false otherwise. */ public function retrieve_payment_intent( $id ) { $endpoint = sprintf( 'https://api.stripe.com/v1/payment_intents/%s', urlencode( $id ) ); $request = array( 'headers' => $this->default_headers(), ); $response = wp_remote_get( sanitize_url( $endpoint ), $request ); if ( 200 != wp_remote_retrieve_response_code( $response ) ) { if ( WP_DEBUG ) { $this->log( $endpoint, $request, $response ); } return false; } $response_body = wp_remote_retrieve_body( $response ); $response_body = json_decode( $response_body, true ); return $response_body; } } stripe/service.php000064400000013574147206762310010245 0ustar00api_keys = array( 'publishable' => $option['api_keys']['publishable'], 'secret' => $option['api_keys']['secret'], ); } } public function get_title() { return __( 'Stripe', 'contact-form-7' ); } public function is_active() { return (bool) $this->get_api_keys(); } public function api() { if ( $this->is_active() ) { $api = new WPCF7_Stripe_API( $this->api_keys['secret'] ); return $api; } } public function get_api_keys() { return $this->api_keys; } public function get_categories() { return array( 'payments' ); } public function icon() { } public function link() { echo wpcf7_link( 'https://stripe.com/', 'stripe.com' ); } protected function menu_page_url( $args = '' ) { $args = wp_parse_args( $args, array() ); $url = menu_page_url( 'wpcf7-integration', false ); $url = add_query_arg( array( 'service' => 'stripe' ), $url ); if ( ! empty( $args ) ) { $url = add_query_arg( $args, $url ); } return $url; } protected function save_data() { WPCF7::update_option( 'stripe', array( 'api_keys' => $this->api_keys, ) ); } protected function reset_data() { $this->api_keys = null; $this->save_data(); } public function load( $action = '' ) { if ( 'setup' == $action and 'POST' == $_SERVER['REQUEST_METHOD'] ) { check_admin_referer( 'wpcf7-stripe-setup' ); if ( ! empty( $_POST['reset'] ) ) { $this->reset_data(); $redirect_to = $this->menu_page_url( 'action=setup' ); } else { $publishable = trim( $_POST['publishable'] ?? '' ); $secret = trim( $_POST['secret'] ?? '' ); if ( $publishable and $secret ) { $this->api_keys = array( 'publishable' => $publishable, 'secret' => $secret, ); $this->save_data(); $redirect_to = $this->menu_page_url( array( 'message' => 'success', ) ); } else { $redirect_to = $this->menu_page_url( array( 'action' => 'setup', 'message' => 'invalid', ) ); } } wp_safe_redirect( $redirect_to ); exit(); } } public function admin_notice( $message = '' ) { if ( 'invalid' === $message ) { wp_admin_notice( sprintf( '%1$s: %2$s', esc_html( __( "Error", 'contact-form-7' ) ), esc_html( __( "Invalid key values.", 'contact-form-7' ) ) ), 'type=error' ); } if ( 'success' === $message ) { wp_admin_notice( esc_html( __( "Settings saved.", 'contact-form-7' ) ), 'type=success' ); } } public function display( $action = '' ) { echo sprintf( '

%s

', // https://stripe.com/docs/partners/support#intro esc_html( __( "Stripe is a simple and powerful way to accept payments online. Stripe has no setup fees, no monthly fees, and no hidden costs. Millions of businesses rely on Stripe’s software tools to accept payments securely and expand globally.", 'contact-form-7' ) ) ); echo sprintf( '

%s

', wpcf7_link( __( 'https://contactform7.com/stripe-integration/', 'contact-form-7' ), __( 'Stripe integration', 'contact-form-7' ) ) ); if ( $this->is_active() ) { echo sprintf( '

%s

', esc_html( __( "Stripe is active on this site.", 'contact-form-7' ) ) ); } if ( 'setup' == $action ) { $this->display_setup(); } elseif ( is_ssl() or WP_DEBUG ) { echo sprintf( '

%2$s

', esc_url( $this->menu_page_url( 'action=setup' ) ), esc_html( __( 'Setup Integration', 'contact-form-7' ) ) ); } else { echo sprintf( '

%s

', esc_html( __( "Stripe is not available on this site. It requires an HTTPS-enabled site.", 'contact-form-7' ) ) ); } } private function display_setup() { $api_keys = $this->get_api_keys(); if ( $api_keys ) { $publishable = $api_keys['publishable']; $secret = $api_keys['secret']; } else { $publishable = ''; $secret = ''; } ?>
is_active() ) { echo esc_html( $publishable ); echo sprintf( '', esc_attr( $publishable ) ); } else { echo sprintf( '', esc_attr( $publishable ) ); } ?>
is_active() ) { echo esc_html( wpcf7_mask_password( $secret ) ); echo sprintf( '', esc_attr( $secret ) ); } else { echo sprintf( '', esc_attr( $secret ) ); } ?>
is_active() ) { submit_button( _x( 'Remove Keys', 'API keys', 'contact-form-7' ), 'small', 'reset' ); } else { submit_button( __( 'Save Changes', 'contact-form-7' ) ); } ?>
array(), 'version' => WPCF7_VERSION, ); hidden.php000064400000001410147206762310006514 0ustar00 true, 'display-hidden' => true, ) ); } function wpcf7_hidden_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $atts = array(); $class = wpcf7_form_controls_class( $tag->type ); $atts['class'] = $tag->get_class_option( $class ); $atts['id'] = $tag->get_id_option(); $value = (string) reset( $tag->values ); $value = $tag->get_default_option( $value ); $atts['value'] = $value; $atts['type'] = 'hidden'; $atts['name'] = $tag->name; $atts = wpcf7_format_atts( $atts ); $html = sprintf( '', $atts ); return $html; } reflection.php000064400000004420147206762310007417 0ustar00 true, 'display-block' => true, 'not-for-mail' => true, ) ); wpcf7_add_form_tag( 'output', 'wpcf7_output_form_tag_handler', array( 'name-attr' => true, 'not-for-mail' => true, ) ); } /** * The form-tag handler for the reflection type. */ function wpcf7_reflection_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $values = $tag->values ? $tag->values : array( '' ); if ( ! wpcf7_get_validation_error( $tag->name ) ) { $hangover = array_filter( (array) wpcf7_get_hangover( $tag->name ) ); if ( $hangover ) { $values = $hangover; } } $content = array_reduce( $values, static function ( $carry, $item ) use ( $tag ) { $output_tag = sprintf( '%2$s', wpcf7_format_atts( array( 'name' => $tag->name, 'data-default' => $item, ) ), ( '' !== $item ) ? esc_html( $item ) : ' ' ); return $carry . $output_tag; }, '' ); $html = sprintf( '
%2$s
', wpcf7_format_atts( array( 'data-reflection-of' => $tag->name, 'class' => $tag->get_class_option( wpcf7_form_controls_class( $tag->type ) ), 'id' => $tag->get_id_option(), ) ), $content ); return $html; } /** * The form-tag handler for the output type. */ function wpcf7_output_form_tag_handler( $tag ) { if ( empty( $tag->name ) ) { return ''; } $value = (string) reset( $tag->values ); if ( ! wpcf7_get_validation_error( $tag->name ) ) { $hangover = array_filter( (array) wpcf7_get_hangover( $tag->name ) ); if ( $hangover ) { $value = (string) reset( $hangover ); } } $html = sprintf( '%2$s', wpcf7_format_atts( array( 'data-reflection-of' => $tag->name, 'data-default' => $value, 'name' => $tag->name, 'class' => $tag->get_class_option( wpcf7_form_controls_class( $tag->type ) ), 'id' => $tag->get_id_option(), ) ), esc_html( $value ) ); return $html; } announcements/module.php000064400000010057147207015700011425 0ustar00
get_js_assets_url( 'announcements-app' ), [ 'wp-i18n', ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'announcements-app', 'elementor' ); $this->print_config( 'announcements-app' ); } /** * Get initialization settings to use in frontend. * * @return array[] */ protected function get_init_settings(): array { $active_announcements = $this->get_active_announcements(); $additional_settings = []; foreach ( $active_announcements as $announcement ) { $additional_settings[] = $announcement->get_prepared_data(); //@TODO - replace with ajax request from the front after actually triggered $announcement->after_triggered(); } return [ 'announcements' => $additional_settings, ]; } /** * Enqueue the module styles. */ public function enqueue_styles() { wp_enqueue_style( 'announcements-app', $this->get_css_assets_url( 'modules/announcements/announcements' ), [], ELEMENTOR_VERSION ); } /** * Retrieve all announcement in raw format ( array ). * * @return array[] */ private function get_raw_announcements(): array { $raw_announcements = []; if ( Preferences::is_ai_enabled( get_current_user_id() ) ) { $raw_announcements[] = $this->get_ai_announcement_data(); } // DO NOT USE THIS FILTER return apply_filters( 'elementor/announcements/raw_announcements', $raw_announcements ); } private function get_ai_announcement_data(): array { return [ 'title' => __( 'Discover your new superpowers ', 'elementor' ), 'description' => __( '

With AI for text, code, image generation and editing, you can bring your vision to life faster than ever. Start your free trial now - no credit card required!

', 'elementor' ), 'media' => [ 'type' => 'image', 'src' => ELEMENTOR_ASSETS_URL . 'images/announcement.png?' . ELEMENTOR_VERSION, ], 'cta' => [ [ 'label' => __( 'Let\'s do it', 'elementor' ), 'variant' => 'primary', 'target' => '_top', 'url' => '#welcome-ai', ], [ 'label' => __( 'Skip', 'elementor' ), 'variant' => 'secondary', ], ], 'triggers' => [ [ 'action' => 'aiStarted', ], ], ]; } /** * Retrieve all announcement objects. * * @return array */ private function get_announcements(): array { $announcements = []; foreach ( $this->get_raw_announcements() as $announcement_data ) { $announcements[] = new Announcement( $announcement_data ); } return $announcements; } /** * Retrieve all active announcement objects. * * @return array */ private function get_active_announcements(): array { $active_announcements = []; foreach ( $this->get_announcements() as $announcement ) { if ( $announcement->is_active() ) { $active_announcements[] = $announcement; } } return $active_announcements; } public function __construct() { parent::__construct(); add_action( 'elementor/init', [ $this, 'on_elementor_init' ] ); } public function on_elementor_init() { if ( empty( $this->get_active_announcements() ) ) { return; } add_action( 'elementor/editor/footer', function () { $this->render_app_wrapper(); } ); add_action( 'elementor/editor/after_enqueue_scripts', function () { $this->enqueue_scripts(); $this->enqueue_styles(); } ); } } announcements/triggers/ai-started.php000064400000001152147207015700014017 0ustar00 $this->name ] ); } /** * @return bool */ public function is_active(): bool { return ! User::get_introduction_meta( 'ai_get_started' ) && ! User::get_introduction_meta( $this->name ); } } announcements/triggers/is-flex-container-inactive.php000064400000002241147207015700017111 0ustar00get_user_announcement_count(); return ! empty( $user_counter ) ? (int) $user_counter : 0; } public function after_triggered() { $new_counter = $this->get_view_count() + 1; update_user_meta( get_current_user_id(), self::USER_META_KEY, $new_counter ); } /** * @return bool */ public function is_active(): bool { $is_feature_active = Plugin::$instance->experiments->is_feature_active( 'container' ); $counter = $this->get_user_announcement_count(); return ! $is_feature_active && (int) $counter < 1; } /** * @return string */ private function get_user_announcement_count(): string { return get_user_meta( get_current_user_id(), self::USER_META_KEY, true ); } } announcements/classes/trigger-base.php000064400000000672147207015700014152 0ustar00name; } /** * @return bool */ public function is_active(): bool { return true; } public function after_triggered() { } } announcements/classes/utils.php000064400000001436147207015700012736 0ustar00raw_data = $data; $this->set_triggers(); } /** * @return array */ protected function get_triggers(): array { return $this->triggers; } protected function set_triggers() { $triggers = $this->raw_data['triggers'] ?? []; foreach ( $triggers as $trigger ) { $this->triggers[] = Utils::get_trigger_object( $trigger ); } } /** * is_active * @return bool */ public function is_active(): bool { $triggers = $this->get_triggers(); if ( empty( $triggers ) ) { return true; } foreach ( $triggers as $trigger ) { if ( ! $trigger->is_active() ) { return false; } } return true; } public function after_triggered() { foreach ( $this->get_triggers() as $trigger ) { if ( $trigger->is_active() ) { $trigger->after_triggered(); } } } /** * @return array */ public function get_prepared_data(): array { $raw_data = $this->raw_data; unset( $raw_data['triggers'] ); return $raw_data; } } home/module.php000064400000005406147207015700007502 0ustar00register_layout_experiment(); if ( ! $this->is_experiment_active() ) { return; } add_action( 'elementor/admin/menu/after_register', function ( Admin_Menu_Manager $admin_menu, array $hooks ) { $hook_suffix = 'toplevel_page_elementor'; add_action( "admin_print_scripts-{$hook_suffix}", [ $this, 'enqueue_home_screen_scripts' ] ); }, 10, 2 ); add_filter( 'elementor/document/urls/edit', [ $this, 'add_active_document_to_edit_link' ] ); } public function enqueue_home_screen_scripts(): void { if ( ! current_user_can( 'manage_options' ) ) { return; } $min_suffix = Utils::is_script_debug() ? '' : '.min'; wp_enqueue_script( 'e-home-screen', ELEMENTOR_ASSETS_URL . 'js/e-home-screen' . $min_suffix . '.js', [ 'react', 'react-dom', 'elementor-common', 'elementor-v2-ui', ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'e-home-screen', 'elementor' ); wp_localize_script( 'e-home-screen', 'elementorHomeScreenData', $this->get_app_js_config() ); } public function is_experiment_active(): bool { return Plugin::$instance->experiments->is_feature_active( self::PAGE_ID ); } public function add_active_document_to_edit_link( $edit_link ) { $active_document = Utils::get_super_global_value( $_GET, 'active-document' ) ?? null; $active_tab = Utils::get_super_global_value( $_GET, 'active-tab' ) ?? null; if ( $active_document ) { $edit_link = add_query_arg( 'active-document', $active_document, $edit_link ); } if ( $active_tab ) { $edit_link = add_query_arg( 'active-tab', $active_tab, $edit_link ); } return $edit_link; } private function register_layout_experiment(): void { Plugin::$instance->experiments->add_feature( [ 'name' => static::PAGE_ID, 'title' => esc_html__( 'Elementor Home Screen', 'elementor' ), 'description' => esc_html__( 'Default Elementor menu page.', 'elementor' ), 'hidden' => true, 'default' => Experiments_Manager::STATE_ACTIVE, ] ); } private function get_app_js_config(): array { return API::get_home_screen_items(); } public static function get_elementor_settings_page_id(): string { return Plugin::$instance->experiments->is_feature_active( self::PAGE_ID ) ? 'elementor-settings' : Settings::PAGE_ID; } } home/classes/transformations-manager.php000064400000003707147207015700014515 0ustar00home_screen_data = $home_screen_data; $this->wordpress_adapter = new Wordpress_Adapter(); $this->plugin_status_adapter = new Plugin_Status_Adapter( $this->wordpress_adapter ); $this->transformation_classes = $this->get_transformation_classes(); } public function run_transformations(): array { if ( ! empty( self::$cached_data ) ) { return self::$cached_data; } $transformations = self::TRANSFORMATIONS; foreach ( $transformations as $transformation_id ) { $this->home_screen_data = $this->transformation_classes[ $transformation_id ]->transform( $this->home_screen_data ); } self::$cached_data = $this->home_screen_data; return $this->home_screen_data; } private function get_transformation_classes(): array { $classes = []; $transformations = self::TRANSFORMATIONS; $arguments = [ 'wordpress_adapter' => $this->wordpress_adapter, 'plugin_status_adapter' => $this->plugin_status_adapter, ]; foreach ( $transformations as $transformation_id ) { $class_name = '\\Elementor\\Modules\\Home\\Transformations\\' . $transformation_id; $classes[ $transformation_id ] = new $class_name( $arguments ); } return $classes; } } home/transformations/filter-plugins.php000064400000005207147207015700014411 0ustar00get_add_ons_installation_status( $home_screen_data['add_ons']['repeater'] ); return $home_screen_data; } private function is_plugin( $add_on ): bool { return 'link' !== $add_on['type']; } private function get_add_ons_installation_status( array $add_ons ): array { $transformed_add_ons = []; foreach ( $add_ons as $add_on ) { if ( $this->is_plugin( $add_on ) ) { $this->handle_plugin_add_on( $add_on, $transformed_add_ons ); } else { $transformed_add_ons[] = $add_on; } } return $transformed_add_ons; } private function get_plugin_installation_status( $add_on ): string { $plugin_path = $add_on['file_path']; if ( ! $this->plugin_status_adapter->is_plugin_installed( $plugin_path ) ) { if ( 'wporg' === $add_on['type'] ) { return self::PLUGIN_IS_NOT_INSTALLED_FROM_WPORG; } return self::PLUGIN_IS_NOT_INSTALLED_NOT_FROM_WPORG; } if ( $this->wordpress_adapter->is_plugin_active( $plugin_path ) ) { return self::PLUGIN_IS_ACTIVATED; } return self::PLUGIN_IS_INSTALLED_NOT_ACTIVATED; } private function handle_plugin_add_on( array $add_on, array &$transformed_add_ons ): void { $installation_status = $this->get_plugin_installation_status( $add_on ); if ( self::PLUGIN_IS_ACTIVATED === $installation_status ) { return; } switch ( $this->get_plugin_installation_status( $add_on ) ) { case self::PLUGIN_IS_NOT_INSTALLED_NOT_FROM_WPORG: break; case self::PLUGIN_IS_NOT_INSTALLED_FROM_WPORG: $installation_url = $this->plugin_status_adapter->get_install_plugin_url( $add_on['file_path'] ); $add_on['url'] = html_entity_decode( $installation_url ); $add_on['target'] = '_self'; break; case self::PLUGIN_IS_INSTALLED_NOT_ACTIVATED: $activation_url = $this->plugin_status_adapter->get_activate_plugin_url( $add_on['file_path'] ); $add_on['url'] = html_entity_decode( $activation_url ); $add_on['button_label'] = esc_html__( 'Activate', 'elementor' ); $add_on['target'] = '_self'; break; } $transformed_add_ons[] = $add_on; } } home/transformations/filter-top-section-by-license.php000064400000002100147207015700017211 0ustar00has_pro = Utils::has_pro(); } private function is_valid_item( $item ) { if ( isset( $item['license'] ) ) { $has_pro_json_not_free = $this->has_pro && 'pro' === $item['license'][0]; $is_not_pro_json_not_pro = ! $this->has_pro && 'free' === $item['license'][0]; return $has_pro_json_not_free || $is_not_pro_json_not_pro; } } public function transform( array $home_screen_data ): array { foreach ( $home_screen_data['top_with_licences'] as $index => $item ) { if ( $this->is_valid_item( $item ) ) { $new_top[] = $item; } } $home_screen_data['top_with_licences'] = reset( $new_top ); unset( $home_screen_data['top'] ); return $home_screen_data; } } home/transformations/filter-sidebar-upgrade-by-license.php000064400000002412147207015700020011 0ustar00has_pro = Utils::has_pro(); } private function is_valid_item( $item ) { $has_pro_json_not_free = $this->has_pro && 'pro' === $item['license'][0]; $is_not_pro_json_not_pro = ! $this->has_pro && 'free' === $item['license'][0]; $should_show = ! isset( $item['show'] ) || 'true' === $item['show']; return $has_pro_json_not_free && $should_show || $is_not_pro_json_not_pro && $should_show; } public function transform( array $home_screen_data ): array { $new_sidebar_upgrade = []; foreach ( $home_screen_data['sidebar_upgrade'] as $index => $item ) { if ( $this->is_valid_item( $item ) ) { $new_sidebar_upgrade[] = $item; } } if ( empty( $new_sidebar_upgrade ) ) { unset( $home_screen_data['sidebar_upgrade'] ); return $home_screen_data; } $home_screen_data['sidebar_upgrade'] = reset( $new_sidebar_upgrade ); return $home_screen_data; } } home/transformations/create-new-page-url.php000064400000000756147207015700015215 0ustar00documents->get_create_new_post_url( 'page' ); return $home_screen_data; } } home/transformations/create-site-settings-url.php000064400000005536147207015700016315 0ustar00get_site_settings_url_config(); $home_screen_data['get_started']['repeater'] = array_map( function( $repeater_item ) use ( $site_settings_url_config ) { if ( ! in_array( $repeater_item['title'], static::SITE_SETTINGS_ITEMS, true ) ) { return $repeater_item; } if ( ! empty( $repeater_item['tab_id'] ) ) { $site_settings_url_config['url'] = add_query_arg( [ 'active-tab' => $repeater_item['tab_id'] ], $site_settings_url_config['url'] ); } return array_merge( $repeater_item, $site_settings_url_config ); }, $home_screen_data['get_started']['repeater'] ); return $home_screen_data; } private function get_site_settings_url_config(): array { $existing_elementor_page = $this->get_elementor_page(); $site_settings_url = ! empty( $existing_elementor_page ) ? $this->get_elementor_edit_url( $existing_elementor_page->ID ) : $this->get_elementor_create_new_page_url(); return [ 'new_page' => empty( $existing_elementor_page ), 'url' => $site_settings_url, 'type' => static::URL_TYPE, ]; } private function get_elementor_create_new_page_url(): string { $active_kit_id = Plugin::$instance->kits_manager->get_active_id(); if ( empty( $active_kit_id ) ) { return Plugin::$instance->documents->get_create_new_post_url( 'page' ); } return add_query_arg( [ 'active-document' => $active_kit_id ], Plugin::$instance->documents->get_create_new_post_url( 'page' ) ); } private function get_elementor_edit_url( int $post_id ): string { $active_kit_id = Plugin::$instance->kits_manager->get_active_id(); $document = Plugin::$instance->documents->get( $post_id ); if ( ! $document ) { return ''; } return add_query_arg( [ 'active-document' => $active_kit_id ], $document->get_edit_url() ); } private function get_elementor_page() { $args = [ 'meta_key' => Document::BUILT_WITH_ELEMENTOR_META_KEY, 'sort_order' => 'asc', 'sort_column' => 'post_date', ]; $pages = get_pages( $args ); if ( empty( $pages ) ) { return null; } $show_page_on_front = 'page' === get_option( 'show_on_front' ); if ( ! $show_page_on_front ) { return $pages[0]; } $home_page_id = get_option( 'page_on_front' ); foreach ( $pages as $page ) { if ( (string) $page->ID === $home_page_id ) { return $page; } } return $pages[0]; } } home/transformations/base/transformations-abstract.php000064400000000762147207015700017412 0ustar00wordpress_adapter = $args['wordpress_adapter'] ?? null; $this->plugin_status_adapter = $args['plugin_status_adapter'] ?? null; } abstract public function transform( array $home_screen_data ): array; } home/transformations/filter-condition-introduction-meta.php000064400000003617147207015700020364 0ustar00introduction_meta_data = User::get_introduction_meta() ?? []; } public function transform( array $home_screen_data ): array { $introduction_meta_conditions = $this->get_introduction_meta_conditions( $home_screen_data ); $active_addons = $this->get_activated_addons( $introduction_meta_conditions ); $home_screen_data['add_ons']['repeater'] = $this->get_inactive_addons( $home_screen_data, $active_addons ); return $home_screen_data; } private function get_introduction_meta_conditions( $home_screen_data ): array { $add_ons = $home_screen_data['add_ons']['repeater']; $conditions = []; foreach ( $add_ons as $add_on ) { if ( array_key_exists( 'condition', $add_on ) && 'introduction_meta' === $add_on['condition']['key'] ) { $conditions[ $add_on['title'] ] = $add_on['condition']['value']; } } return $conditions; } private function get_activated_addons( $conditions ): array { $active_addons = []; foreach ( $conditions as $add_on_title => $introduction_meta_value ) { if ( ! empty( $this->introduction_meta_data[ $introduction_meta_value ] ) ) { $active_addons[] = $add_on_title; } } return $active_addons; } private function get_inactive_addons( $home_screen_data, $active_addons ): array { $add_ons = $home_screen_data['add_ons']['repeater']; $inactive_add_ons = []; foreach ( $add_ons as $add_on ) { if ( ! in_array( $add_on['title'], $active_addons ) ) { $inactive_add_ons[] = $add_on; } } return $inactive_add_ons; } } home/transformations/filter-get-started-by-license.php000064400000002015147207015700017175 0ustar00has_pro = Utils::has_pro(); } private function is_valid_item( $item ) { $has_pro_json_not_free = $this->has_pro && 'pro' === $item['license'][0]; $is_not_pro_json_not_pro = ! $this->has_pro && 'free' === $item['license'][0]; return $has_pro_json_not_free || $is_not_pro_json_not_pro; } public function transform( array $home_screen_data ): array { $new_get_started = []; foreach ( $home_screen_data['get_started'] as $index => $item ) { if ( $this->is_valid_item( $item ) ) { $new_get_started[] = $item; } } $home_screen_data['get_started'] = reset( $new_get_started ); return $home_screen_data; } } home/api.php000064400000003325147207015700006764 0ustar00run_transformations(); } private static function fetch_data(): array { $response = wp_remote_get( self::HOME_SCREEN_DATA_URL ); if ( is_wp_error( $response ) ) { return []; } $data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( empty( $data['home-screen'] ) || ! is_array( $data['home-screen'] ) ) { return []; } return $data['home-screen']; } private static function get_transient( $cache_key ) { $cache = get_option( $cache_key ); if ( empty( $cache['timeout'] ) ) { return false; } if ( current_time( 'timestamp' ) > $cache['timeout'] ) { return false; } return json_decode( $cache['value'], true ); } private static function set_transient( $cache_key, $value, $expiration = '+12 hours' ): bool { $data = [ 'timeout' => strtotime( $expiration, current_time( 'timestamp' ) ), 'value' => json_encode( $value ), ]; return update_option( $cache_key, $data, false ); } } gutenberg/module.php000064400000014121147207015700010526 0ustar00 function( $request_value, $object ) { if ( ! User::is_current_user_can_edit( $object->ID ) ) { return false; } $document = Plugin::$instance->documents->get( $object->ID ); if ( ! $document ) { return false; } $document->set_is_built_with_elementor( false ); return true; }, ] ); } /** * @since 2.1.0 * @access public */ public function enqueue_assets() { $document = Plugin::$instance->documents->get( get_the_ID() ); if ( ! $document || ! $document->is_editable_by_current_user() ) { return; } $this->is_gutenberg_editor_active = true; $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; wp_enqueue_script( 'elementor-gutenberg', ELEMENTOR_ASSETS_URL . 'js/gutenberg' . $suffix . '.js', [ 'jquery' ], ELEMENTOR_VERSION, true ); $elementor_settings = [ 'isElementorMode' => $document->is_built_with_elementor(), 'editLink' => $document->get_edit_url(), ]; Utils::print_js_config( 'elementor-gutenberg', 'ElementorGutenbergSettings', $elementor_settings ); } /** * @since 2.1.0 * @access public */ public function print_admin_js_template() { if ( ! $this->is_gutenberg_editor_active ) { return; } ?> ID ) ) { return false; } if ( ! static::is_built_with_elementor( $post ) ) { return false; } if ( static::is_gutenberg_in_post( $post ) ) { return false; } return true; } private static function is_built_with_elementor( $post ) : bool { $document = Plugin::$instance->documents->get( $post->ID ); if ( ! $document || ! $document->is_built_with_elementor() ) { return false; } return true; } private static function is_gutenberg_in_post( $post ) : bool { if ( has_blocks( $post ) ) { return true; } if ( static::current_theme_is_fse_theme() ) { return true; } return false; } private static function current_theme_is_fse_theme() : bool { if ( function_exists( 'wp_is_block_theme' ) ) { return (bool) wp_is_block_theme(); } if ( function_exists( 'gutenberg_is_fse_theme' ) ) { return (bool) gutenberg_is_fse_theme(); } return false; } } generator-tag/module.php000064400000004460147207015700011310 0ustar00get_generator_content(); echo '' . PHP_EOL; } private function get_generator_content(): string { $active_features = $this->get_active_features(); $settings = $this->get_generator_tag_settings(); $tags = [ 'Elementor ' . ELEMENTOR_VERSION, ]; if ( ! empty( $active_features ) ) { $tags[] = 'features: ' . implode( ', ', $active_features ); } if ( ! empty( $settings ) ) { $tags[] = 'settings: ' . implode( ', ', $settings ); } return implode( '; ', $tags ); } private function get_active_features(): array { $active_features = []; foreach ( Plugin::$instance->experiments->get_active_features() as $feature_slug => $feature ) { if ( isset( $feature['generator_tag'] ) && $feature['generator_tag'] ) { $active_features[] = $feature_slug; } } return $active_features; } private function get_generator_tag_settings(): array { return apply_filters( 'elementor/generator_tag/settings', [] ); } public function register_admin_settings( Settings $settings ) { $settings->add_field( Settings::TAB_ADVANCED, Settings::TAB_ADVANCED, 'meta_generator_tag', [ 'label' => esc_html__( 'Generator Tag', 'elementor' ), 'field_args' => [ 'type' => 'select', 'std' => '', 'options' => [ '' => esc_html__( 'Enable', 'elementor' ), '1' => esc_html__( 'Disable', 'elementor' ), ], 'desc' => esc_html__( 'A generator tag is a meta element that indicates the attributes used to create a webpage. It is used for analytical purposes.', 'elementor' ), ], ] ); } } checklist/module.php000064400000010423147207015700010516 0ustar00wordpress_adapter = $wordpress_adapter ?? new Wordpress_Adapter(); parent::__construct(); $this->register_experiment(); if ( ! $this->is_experiment_active() ) { return; } $this->init_user_progress(); $this->user_progress = $this->user_progress ?? $this->get_user_progress_from_db(); $this->steps_manager = new Steps_Manager( $this ); $this->enqueue_editor_scripts(); } /** * Get the module name. * * @return string */ public function get_name() : string { return 'e-checklist'; } /** * Checks if the experiment is active * * @return bool */ public function is_experiment_active() : bool { return Plugin::$instance->experiments->is_feature_active( self::EXPERIMENT_ID ); } /** * Gets user's progress from db * * @return array { * @type bool $is_hidden * @type int $last_opened_timestamp * @type array $steps { * @type string $step_id => { * @type bool $is_marked_completed * @type bool $is_completed * } * } * } */ public function get_user_progress_from_db() : array { return json_decode( $this->wordpress_adapter->get_option( self::DB_OPTION_KEY ), true ); } /** * Using the step's ID, get the progress of the step should it exist * * @param $step_id * * @return null|array { * @type bool $is_marked_completed * @type bool $is_completed * } */ public function get_step_progress( $step_id ) : ?array { return $this->user_progress['steps'][ $step_id ] ?? null; } /** * Update the progress of a step * * @param $step_id * @param $step_progress * * @return void */ public function set_step_progress( $step_id, $step_progress ) : void { $this->user_progress['steps'][ $step_id ] = $step_progress; $this->update_user_progress_in_db(); } /** * @return Steps_Manager */ public function get_steps_manager() : Steps_Manager { return $this->steps_manager; } /** * @return Wordpress_Adapter */ public function get_wordpress_adapter() : Wordpress_Adapter { return $this->wordpress_adapter; } public function enqueue_editor_scripts() : void { add_action( 'elementor/editor/before_enqueue_scripts', function () { $min_suffix = Utils::is_script_debug() ? '' : '.min'; wp_enqueue_script( $this->get_name(), ELEMENTOR_ASSETS_URL . 'js/checklist' . $min_suffix . '.js', [ 'react', 'react-dom', 'elementor-common', 'elementor-v2-ui', 'elementor-v2-icons', 'elementor-v2-editor-app-bar', 'elementor-web-cli', ], ELEMENTOR_VERSION, true ); wp_set_script_translations( $this->get_name(), 'elementor' ); } ); } private function register_experiment() : void { Plugin::$instance->experiments->add_feature( [ 'name' => self::EXPERIMENT_ID, 'title' => esc_html__( 'Launchpad Checklist', 'elementor' ), 'description' => esc_html__( 'Launchpad Checklist feature to boost productivity and deliver your site faster', 'elementor' ), 'release_status' => Manager::RELEASE_STATUS_ALPHA, 'hidden' => true, ] ); } private function init_user_progress() : void { $default_settings = [ 'is_hidden' => false, 'last_opened_timestamp' => time(), 'steps' => [], ]; $this->wordpress_adapter->add_option( self::DB_OPTION_KEY, wp_json_encode( $default_settings ) ); } private function update_user_progress_in_db() : void { $this->wordpress_adapter->update_option( self::DB_OPTION_KEY, wp_json_encode( $this->user_progress ) ); } } checklist/steps/step-base.php000064400000010006147207015700012247 0ustar00module = $module; $this->wordpress_adapter = $wordpress_adapter ?? new Wordpress_Adapter(); $this->user_progress = $module->get_step_progress( $this->get_id() ) ?? $this->get_step_initial_progress(); } public function get_learn_more_text() : string { return esc_html__( 'Learn more', 'elementor' ); } public function get_learn_more_url() : string { return 'https://go.elementor.com/getting-started-with-elementor/'; } /** * Marking a step as completed based on user's desire * * @return void */ public function mark_as_completed() : void { $this->user_progress[ self::MARKED_AS_COMPLETED_KEY ] = true; $this->set_step_progress(); } /** * Unmarking a step as completed based on user's desire * * @return void */ public function unmark_as_completed() : void { $this->user_progress[ self::MARKED_AS_COMPLETED_KEY ] = false; $this->set_step_progress(); } /** * Marking a step as completed if it was completed once, and it's suffice to marketing's requirements * * @return void */ public function maybe_mark_as_completed() : void { $is_immutable_completed = $this->user_progress[ self::IMMUTABLE_COMPLETION_KEY ] ?? false; if ( ! $is_immutable_completed && $this->get_is_completion_immutable() && $this->is_absolute_completed() ) { $this->user_progress[ self::IMMUTABLE_COMPLETION_KEY ] = true; $this->set_step_progress(); } } /** * Returns the step marked as completed value * * @return bool */ public function is_marked_as_completed() : bool { return $this->user_progress[ self::MARKED_AS_COMPLETED_KEY ]; } /** * Returns the step completed value * * @return bool */ public function is_immutable_completed() : bool { return $this->user_progress[ self::IMMUTABLE_COMPLETION_KEY ]; } /** * Sets and returns the initial progress of the step * * @return array */ public function get_step_initial_progress() : array { $initial_progress = [ self::MARKED_AS_COMPLETED_KEY => false, self::IMMUTABLE_COMPLETION_KEY => false, ]; $this->module->set_step_progress( $this->get_id(), $initial_progress ); return $initial_progress; } /** * Sets the step progress * * @return void */ private function set_step_progress() : void { $this->module->set_step_progress( $this->get_id(), $this->user_progress ); } } checklist/steps/create-pages.php000064400000002101147207015700012721 0ustar00wordpress_adapter->get_pages( [ 'meta_key' => '_elementor_version', 'number' => 3, ] ) ?? []; return count( $pages ) >= 3; } public function get_title() : string { return esc_html__( 'Create your first 3 pages', 'elementor' ); } public function get_description() : string { return esc_html__( 'Jumpstart your creation with professional designs form the Template Library or start from scratch.', 'elementor' ); } public function get_cta_text() : string { return esc_html__( 'Create a new page', 'elementor' ); } public function get_cta_url() : string { return Plugin::$instance->documents->get_create_new_post_url( 'page' ); } public function get_is_completion_immutable() : bool { return true; } } checklist/steps-manager.php000064400000010121147207015700011772 0ustar00module = $module; $this->register_steps(); } /** * Gets formatted and ordered array of step ( step data, is_marked_completed and is_completed ) * * @return array */ public function get_steps_for_frontend() : array { $formatted_steps = []; foreach ( $this->get_step_ids() as $step_id ) { $instance = $this->step_instances[ $step_id ]; $is_marked_as_completed = $instance->is_marked_as_completed(); $step = [ 'should_allow_undo' => $is_marked_as_completed, 'is_completed' => $instance->is_immutable_completed() || $instance->is_marked_as_completed() || $instance->is_absolute_completed(), 'config' => $this->get_step_config( $step_id ), ]; $formatted_steps[] = $step; } return $formatted_steps; } /** * Marks a step as completed, returns true if the step was found and marked or false otherwise * * @param string $step_id * * @return void */ public function mark_step_as_completed( string $step_id ) : void { foreach ( $this->step_instances as $step ) { if ( $step->get_id() === $step_id ) { $step->mark_as_completed(); return; } } } /** * Unmarks a step as completed, returns true if the step was found and unmarked or false otherwise * * @param string $step_id * * @return void */ public function unmark_step_as_completed( string $step_id ) : void { foreach ( $this->step_instances as $step ) { if ( $step->get_id() === $step_id ) { $step->unmark_as_completed(); return; } } } /** * Maybe marks a step as completed (depending on if source allows it), returns true if the step was found and marked or false otherwise * * @param $step_id * * @return void */ public function maybe_set_step_as_immutable_completed( string $step_id ) : void { foreach ( $this->step_instances as $step ) { if ( $step->get_id() === $step_id ) { $step->maybe_mark_as_completed(); return; } } } public function get_step_by_id( string $step_id ) : ?Step_Base { return $this->step_instances[ $step_id ] ?? null; } /** * @return array */ public function get_step_config( $step_id ) : array { $step_instance = $this->step_instances[ $step_id ]; return $step_instance ? [ 'id' => $step_instance->get_id(), 'title' => $step_instance->get_title(), 'description' => $step_instance->get_description(), 'learn_more_text' => $step_instance->get_learn_more_text(), 'learn_more_url' => $step_instance->get_learn_more_url(), 'cta_text' => $step_instance->get_cta_text(), 'cta_url' => $step_instance->get_cta_url(), Step_Base::IS_COMPLETION_IMMUTABLE => $step_instance->get_is_completion_immutable(), ] : []; } /** * Getting the step instances array based on source's order * * @return void */ private function register_steps() : void { foreach ( $this->get_step_ids() as $step_id ) { $step_instance = $this->get_step_instance( $step_id ); if ( $step_instance && ! isset( $this->step_instances[ $step_id ] ) ) { $this->step_instances[ $step_id ] = $step_instance; } } } /** * Using step data->id, instanciates and returns the step class or null if the class does not exist * * @param $step_data * * @return Step_Base|null */ private function get_step_instance( string $step_id ) : ?Step_Base { $class_name = '\\Elementor\\Modules\\Checklist\\Steps\\' . $step_id; if ( ! class_exists( $class_name ) ) { return null; } /** @var Step_Base $step */ return new $class_name( $this->module, $this->module->get_wordpress_adapter() ); } /** * Returns the steps config from source * * @return array */ private static function get_step_ids() : array { return [ Create_Pages::STEP_ID ]; } } checklist/checklist-module-interface.php000064400000001127147207015700014424 0ustar00register_ajax_action( 'enable_safe_mode', [ $this, 'ajax_enable_safe_mode' ] ); $ajax->register_ajax_action( 'disable_safe_mode', [ $this, 'disable_safe_mode' ] ); } /** * @param Tools $tools_page */ public function add_admin_button( $tools_page ) { $tools_page->add_fields( Settings::TAB_GENERAL, 'tools', [ 'safe_mode' => [ 'label' => esc_html__( 'Safe Mode', 'elementor' ), 'field_args' => [ 'type' => 'select', 'std' => $this->is_enabled() ? 'global' : '', 'options' => [ '' => esc_html__( 'Disable', 'elementor' ), 'global' => esc_html__( 'Enable', 'elementor' ), ], 'desc' => esc_html__( 'Safe Mode allows you to troubleshoot issues by only loading the editor, without loading the theme or any other plugin.', 'elementor' ), ], ], ] ); } public function on_update_safe_mode( $value ) { if ( 'yes' === $value || 'global' === $value ) { $this->enable_safe_mode(); } else { $this->disable_safe_mode(); } return $value; } /** * @throws \Exception */ public function ajax_enable_safe_mode( $data ) { if ( ! current_user_can( 'install_plugins' ) ) { throw new \Exception( 'Access denied.' ); } // It will run `$this->>update_safe_mode`. update_option( 'elementor_safe_mode', 'yes' ); $document = Plugin::$instance->documents->get( $data['editor_post_id'] ); if ( $document ) { return add_query_arg( 'elementor-mode', 'safe', $document->get_edit_url() ); } return false; } public function enable_safe_mode() { if ( ! current_user_can( 'install_plugins' ) ) { return; } WP_Filesystem(); $this->update_allowed_plugins(); if ( ! is_dir( WPMU_PLUGIN_DIR ) ) { wp_mkdir_p( WPMU_PLUGIN_DIR ); add_option( 'elementor_safe_mode_created_mu_dir', true ); } if ( ! is_dir( WPMU_PLUGIN_DIR ) ) { wp_die( esc_html__( 'Cannot enable Safe Mode', 'elementor' ) ); } $results = copy_dir( __DIR__ . '/mu-plugin/', WPMU_PLUGIN_DIR ); if ( is_wp_error( $results ) ) { return; } $token = hash( 'sha256', wp_rand() ); // Only who own this key can use 'elementor-safe-mode'. update_option( self::OPTION_TOKEN, $token ); // Save for later use. setcookie( self::OPTION_TOKEN, $token, time() + HOUR_IN_SECONDS, COOKIEPATH, '', is_ssl(), true ); } public function disable_safe_mode() { if ( ! current_user_can( 'install_plugins' ) ) { return; } $file_path = WP_CONTENT_DIR . '/mu-plugins/elementor-safe-mode.php'; if ( file_exists( $file_path ) ) { unlink( $file_path ); } if ( get_option( 'elementor_safe_mode_created_mu_dir' ) ) { // It will be removed only if it's empty and don't have other mu-plugins. @rmdir( WPMU_PLUGIN_DIR ); } delete_option( 'elementor_safe_mode' ); delete_option( 'elementor_safe_mode_allowed_plugins' ); delete_option( 'theme_mods_elementor-safe' ); delete_option( 'elementor_safe_mode_created_mu_dir' ); delete_option( self::OPTION_TOKEN ); setcookie( self::OPTION_TOKEN, '', 1, '', '', is_ssl(), true ); } public function filter_preview_url( $url ) { return add_query_arg( 'elementor-mode', 'safe', $url ); } public function filter_template() { return ELEMENTOR_PATH . 'modules/page-templates/templates/canvas.php'; } public function print_safe_mode_css() { ?> print_safe_mode_css() ?>

  • ', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped '' ); ?>
  • ', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped '' ); ?>
', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped '' ); ?>
is_allowed_post_type() ) { return; } $this->print_safe_mode_css(); ?> ' . esc_html__( 'Disable Safe Mode', 'elementor' ) . ''; return $actions; } public function on_deactivated_plugin( $plugin ) { if ( ELEMENTOR_PLUGIN_BASE === $plugin ) { $this->disable_safe_mode(); return; } $allowed_plugins = get_option( 'elementor_safe_mode_allowed_plugins', [] ); $plugin_key = array_search( $plugin, $allowed_plugins, true ); if ( $plugin_key ) { unset( $allowed_plugins[ $plugin_key ] ); update_option( 'elementor_safe_mode_allowed_plugins', $allowed_plugins ); } } public function update_allowed_plugins() { $allowed_plugins = [ 'elementor' => ELEMENTOR_PLUGIN_BASE, ]; if ( defined( 'ELEMENTOR_PRO_PLUGIN_BASE' ) ) { $allowed_plugins['elementor_pro'] = ELEMENTOR_PRO_PLUGIN_BASE; } if ( defined( 'WC_PLUGIN_BASENAME' ) ) { $allowed_plugins['woocommerce'] = WC_PLUGIN_BASENAME; } update_option( 'elementor_safe_mode_allowed_plugins', $allowed_plugins ); } public function __construct() { if ( current_user_can( 'install_plugins' ) ) { add_action( 'elementor/admin/after_create_settings/elementor-tools', [ $this, 'add_admin_button' ] ); } add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); $plugin_file = self::MU_PLUGIN_FILE_NAME; add_filter( "plugin_action_links_{$plugin_file}", [ $this, 'plugin_action_links' ] ); // Use pre_update, in order to catch cases that $value === $old_value and it not updated. add_filter( 'pre_update_option_elementor_safe_mode', [ $this, 'on_update_safe_mode' ], 10, 2 ); add_action( 'elementor/safe_mode/init', [ $this, 'run_safe_mode' ] ); add_action( 'elementor/editor/footer', [ $this, 'print_try_safe_mode' ] ); if ( $this->is_enabled() ) { add_action( 'activated_plugin', [ $this, 'update_allowed_plugins' ] ); add_action( 'deactivated_plugin', [ $this, 'on_deactivated_plugin' ] ); } } private function is_allowed_post_type() { $allowed_post_types = [ 'post', 'page', 'product', Source_Local::CPT, ]; $current_post_type = get_post_type( Plugin::$instance->editor->get_post_id() ); return in_array( $current_post_type, $allowed_post_types ); } } safe-mode/mu-plugin/elementor-safe-mode.php000064400000007466147207015700014702 0ustar00 '' . esc_html__( 'Learn More', 'elementor' ) . '', ]; $plugin_meta = array_merge( $plugin_meta, $row_meta ); } return $plugin_meta; } public function __construct() { add_filter( 'plugin_row_meta', [ $this, 'plugin_row_meta' ], 10, 4 ); $enabled_type = $this->is_enabled(); if ( ! $enabled_type || ! $this->is_valid_token() ) { return; } if ( ! $this->is_requested() && 'global' !== $enabled_type ) { return; } if ( ! $this->is_editor() && ! $this->is_editor_preview() && ! $this->is_editor_ajax() ) { return; } $this->add_hooks(); } } new Safe_Mode(); web-cli/module.php000064400000002125147207015700010067 0ustar00get_js_assets_url( 'web-cli' ), [ 'jquery', ], ELEMENTOR_VERSION, true ); $this->print_config( 'elementor-web-cli' ); } protected function get_init_settings() { return [ 'isDebug' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ), 'urls' => [ 'rest' => get_rest_url(), 'assets' => ELEMENTOR_ASSETS_URL, ], 'nonce' => wp_create_nonce( 'wp_rest' ), 'version' => ELEMENTOR_VERSION, ]; } } nested-accordion/module.php000064400000002304147207015700011765 0ustar00experiments->is_feature_active( 'nested-elements' ); } public function get_name() { return 'nested-accordion'; } public function __construct() { parent::__construct(); add_action( 'elementor/frontend/after_register_styles', [ $this, 'register_styles' ] ); add_action( 'elementor/editor/before_enqueue_scripts', function () { wp_enqueue_script( $this->get_name(), $this->get_js_assets_url( $this->get_name() ), [ 'nested-elements', ], ELEMENTOR_VERSION, true ); } ); } /** * Register styles. * * At build time, Elementor compiles `/modules/nested-accordion/assets/scss/frontend.scss` * to `/assets/css/widget-nested-accordion.min.css`. * * @return void */ public function register_styles() { wp_register_style( 'widget-nested-accordion', $this->get_css_assets_url( 'widget-nested-accordion', null, true, true ), [ 'elementor-frontend' ], ELEMENTOR_VERSION ); } } nested-accordion/widgets/nested-accordion.php000064400000072740147207015700015402 0ustar00experiments->is_feature_active( 'nested-elements' ); } protected function item_content_container( int $index ) { return [ 'elType' => 'container', 'settings' => [ '_title' => sprintf( __( 'item #%s', 'elementor' ), $index ), 'content_width' => 'full', ], ]; } protected function get_default_children_elements() { return [ $this->item_content_container( 1 ), $this->item_content_container( 2 ), $this->item_content_container( 3 ), ]; } protected function get_default_repeater_title_setting_key() { return 'item_title'; } protected function get_default_children_title() { return esc_html__( 'Item #%d', 'elementor' ); } protected function get_default_children_placeholder_selector() { return '.e-n-accordion'; } protected function get_default_children_container_placeholder_selector() { return '.e-n-accordion-item'; } protected function get_html_wrapper_class() { return 'elementor-widget-n-accordion'; } protected function register_controls() { $this->start_controls_section( 'section_items', [ 'label' => esc_html__( 'Layout', 'elementor' ), ] ); $repeater = new Repeater(); $repeater->add_control( 'item_title', [ 'label' => esc_html__( 'Title', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => esc_html__( 'Item Title', 'elementor' ), 'placeholder' => esc_html__( 'Item Title', 'elementor' ), 'label_block' => true, 'dynamic' => [ 'active' => true, ], ] ); $repeater->add_control( 'element_css_id', [ 'label' => esc_html__( 'CSS ID', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => '', 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ), 'style_transfer' => false, ] ); $this->add_control( 'items', [ 'label' => esc_html__( 'Items', 'elementor' ), 'type' => Control_Nested_Repeater::CONTROL_TYPE, 'fields' => $repeater->get_controls(), 'default' => [ [ 'item_title' => esc_html__( 'Item #1', 'elementor' ), ], [ 'item_title' => esc_html__( 'Item #2', 'elementor' ), ], [ 'item_title' => esc_html__( 'Item #3', 'elementor' ), ], ], 'title_field' => '{{{ item_title }}}', 'button_text' => 'Add Item', ] ); $this->add_responsive_control( 'accordion_item_title_position_horizontal', [ 'label' => esc_html__( 'Item Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'separator' => 'before', 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => 'eicon-flex eicon-align-start-h', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-h-align-center', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => 'eicon-flex eicon-align-end-h', ], 'stretch' => [ 'title' => esc_html__( 'Stretch', 'elementor' ), 'icon' => 'eicon-h-align-stretch', ], ], 'selectors_dictionary' => [ 'start' => '--n-accordion-title-justify-content: initial; --n-accordion-title-flex-grow: initial;', 'center' => '--n-accordion-title-justify-content: center; --n-accordion-title-flex-grow: initial;', 'end' => '--n-accordion-title-justify-content: flex-end; --n-accordion-title-flex-grow: initial;', 'stretch' => '--n-accordion-title-justify-content: space-between; --n-accordion-title-flex-grow: 1;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], ] ); $this->add_control( 'heading_accordion_item_title_icon', [ 'type' => Controls_Manager::HEADING, 'label' => esc_html__( 'Icon', 'elementor' ), 'separator' => 'before', ] ); $this->add_responsive_control( 'accordion_item_title_icon_position', [ 'label' => esc_html__( 'Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => 'eicon-h-align-left', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => 'eicon-h-align-right', ], ], 'selectors_dictionary' => [ 'start' => '--n-accordion-title-icon-order: -1;', 'end' => '--n-accordion-title-icon-order: initial;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], ] ); $this->add_control( 'accordion_item_title_icon', [ 'label' => esc_html__( 'Expand', 'elementor' ), 'type' => Controls_Manager::ICONS, 'default' => [ 'value' => 'fas fa-plus', 'library' => 'fa-solid', ], 'skin' => 'inline', 'label_block' => false, ] ); $this->add_control( 'accordion_item_title_icon_active', [ 'label' => esc_html__( 'Collapse', 'elementor' ), 'type' => Controls_Manager::ICONS, 'fa4compatibility' => 'icon_active', 'default' => [ 'value' => 'fas fa-minus', 'library' => 'fa-solid', ], 'condition' => [ 'accordion_item_title_icon[value]!' => '', ], 'skin' => 'inline', 'label_block' => false, ] ); $this->add_control( 'title_tag', [ 'label' => esc_html__( 'Title HTML Tag', 'elementor' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'h1' => 'H1', 'h2' => 'H2', 'h3' => 'H3', 'h4' => 'H4', 'h5' => 'H5', 'h6' => 'H6', 'div' => 'div', 'span' => 'span', 'p' => 'p', ], 'selectors_dictionary' => [ 'h1' => '--n-accordion-title-font-size: 2.5rem;', 'h2' => '--n-accordion-title-font-size: 2rem;', 'h3' => '--n-accordion-title-font-size: 1,75rem;', 'h4' => '--n-accordion-title-font-size: 1.5rem;', 'h5' => '--n-accordion-title-font-size: 1rem;', 'h6' => '--n-accordion-title-font-size: 1rem; ', 'div' => '--n-accordion-title-font-size: 1rem;', 'span' => '--n-accordion-title-font-size: 1rem; ', 'p' => '--n-accordion-title-font-size: 1rem;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], 'default' => 'div', 'separator' => 'before', 'render_type' => 'template', ] ); $this->add_control( 'faq_schema', [ 'label' => esc_html__( 'FAQ Schema', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Yes', 'elementor' ), 'label_off' => esc_html__( 'No', 'elementor' ), 'default' => 'no', ] ); $this->add_control( 'faq_schema_message', [ 'type' => Controls_Manager::ALERT, 'alert_type' => 'info', 'content' => esc_html__( 'Let Google know that this section contains an FAQ. Make sure to only use it only once per page', 'elementor' ), 'condition' => [ 'faq_schema[value]' => 'yes', ], ] ); $this->end_controls_section(); $this->start_controls_section( 'section_interactions', [ 'label' => esc_html__( 'Interactions', 'elementor' ), ] ); $this->add_control( 'default_state', [ 'label' => esc_html__( 'Default State', 'elementor' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'expanded' => esc_html__( 'First expanded', 'elementor' ), 'all_collapsed' => esc_html__( 'All collapsed', 'elementor' ), ], 'default' => 'expanded', 'frontend_available' => true, ] ); $this->add_control( 'max_items_expended', [ 'label' => esc_html__( 'Max Items Expanded', 'elementor' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'one' => esc_html__( 'One', 'elementor' ), 'multiple' => esc_html__( 'Multiple', 'elementor' ), ], 'default' => 'one', 'frontend_available' => true, ] ); $this->add_control( 'n_accordion_animation_duration', [ 'label' => esc_html__( 'Animation Duration', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 's', 'ms' ], 'default' => [ 'unit' => 'ms', 'size' => 400, ], 'frontend_available' => true, ] ); $this->end_controls_section(); $this->add_style_tab(); } private function add_style_tab() { $this->add_accordion_style_section(); $this->add_header_style_section(); $this->add_content_style_section(); } private function add_accordion_style_section() { $this->start_controls_section( 'section_accordion_style', [ 'label' => esc_html__( 'Accordion', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_responsive_control( 'accordion_item_title_space_between', [ 'label' => esc_html__( 'Space between Items', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'max' => 200, ], 'em' => [ 'max' => 20, ], 'rem' => [ 'max' => 20, ], ], 'default' => [ 'size' => 0, ], 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-item-title-space-between: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'accordion_item_title_distance_from_content', [ 'label' => esc_html__( 'Distance from content', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'max' => 200, ], 'em' => [ 'max' => 20, ], 'rem' => [ 'max' => 20, ], ], 'default' => [ 'size' => 0, ], 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-item-title-distance-from-content: {{SIZE}}{{UNIT}}', ], ] ); $this->start_controls_tabs( 'accordion_border_and_background' ); foreach ( [ 'normal', 'hover', 'active' ] as $state ) { $this->add_border_and_radius_style( $state ); } $this->end_controls_tabs(); $this->add_responsive_control( 'accordion_border_radius', [ 'label' => esc_html__( 'Border Radius', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ], 'separator' => 'before', ] ); $this->add_responsive_control( 'accordion_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ '{{WRAPPER}} ' => '--n-accordion-padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ], ] ); $this->end_controls_section(); } private function add_content_style_section() { $low_specificity_accordion_item_selector = ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item ) > .e-con'; $this->start_controls_section( 'section_content_style', [ 'label' => esc_html__( 'Content', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'content_background', 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'selector' => $low_specificity_accordion_item_selector, ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'content_border', 'selector' => $low_specificity_accordion_item_selector, 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Border Color', 'elementor' ), ], 'width' => [ 'label' => esc_html__( 'Border Width', 'elementor' ), ], ], ] ); $this->add_responsive_control( 'content_border_radius', [ 'label' => esc_html__( 'Border Radius', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ $low_specificity_accordion_item_selector => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ], ] ); $this->add_responsive_control( 'content_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ $low_specificity_accordion_item_selector => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};', ], ] ); $this->end_controls_section(); } private function add_header_style_section() { $this->start_controls_section( 'section_header_style', [ 'label' => esc_html__( 'Header', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'heading_header_style_title', [ 'type' => Controls_Manager::HEADING, 'label' => esc_html__( 'Title', 'elementor' ), ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'title_typography', 'selector' => ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item > .e-n-accordion-item-title > .e-n-accordion-item-title-header ) > .e-n-accordion-item-title-text', 'fields_options' => [ 'font_size' => [ 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-title-font-size: {{SIZE}}{{UNIT}}', ], ], ], ] ); $this->start_controls_tabs( 'header_title_color_style' ); foreach ( [ 'normal', 'hover', 'active' ] as $state ) { $this->add_header_style( $state, 'title' ); } $this->end_controls_tabs(); $this->add_control( 'heading_icon_style_title', [ 'type' => Controls_Manager::HEADING, 'label' => esc_html__( 'Icon', 'elementor' ), 'separator' => 'before', ] ); $this->add_responsive_control( 'icon_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'em' => [ 'max' => 10, ], 'rem' => [ 'max' => 10, ], ], 'default' => [ 'unit' => 'px', 'size' => 15, ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-icon-size: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'icon_spacing', [ 'label' => esc_html__( 'Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'max' => 400, ], 'vw' => [ 'max' => 50, 'step' => 0.1, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-accordion-icon-gap: {{SIZE}}{{UNIT}}', ], 'condition' => [ 'accordion_item_title_position_horizontal!' => 'stretch', ], ] ); $this->start_controls_tabs( 'header_icon_color_style' ); foreach ( [ 'normal', 'hover', 'active' ] as $state ) { $this->add_header_style( $state, 'icon' ); } $this->end_controls_tabs(); $this->end_controls_section(); } private function add_header_style( $state, $context ) { $variable = '--n-accordion-' . $context . '-' . $state . '-color'; switch ( $state ) { case 'hover': $translated_tab_text = esc_html__( 'Hover', 'elementor' ); $translated_tab_css_selector = ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item:not([open]) > .e-n-accordion-item-title:hover > .e-n-accordion-item-title-header ) > .e-n-accordion-item-title-text'; break; case 'active': $translated_tab_text = esc_html__( 'Active', 'elementor' ); $translated_tab_css_selector = ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item[open] > .e-n-accordion-item-title > .e-n-accordion-item-title-header ) > .e-n-accordion-item-title-text'; break; default: $translated_tab_text = esc_html__( 'Normal', 'elementor' ); $translated_tab_css_selector = ':where( {{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item:not([open]) > .e-n-accordion-item-title:not(hover) > .e-n-accordion-item-title-header ) > .e-n-accordion-item-title-text'; break; } $this->start_controls_tab( 'header_' . $state . '_' . $context, [ 'label' => $translated_tab_text, ] ); $this->add_control( $state . '_' . $context . '_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}}' => $variable . ': {{VALUE}};', ], ] ); if ( 'title' === $context ) { $this->add_group_control( Group_Control_Text_Shadow::get_type(), [ 'name' => $context . '_' . $state . '_text_shadow', 'selector' => '{{WRAPPER}} ' . $translated_tab_css_selector, 'fields_options' => [ 'text_shadow_type' => [ 'label' => esc_html__( 'Shadow', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Text_Stroke::get_type(), [ 'name' => $context . '_' . $state . '_stroke', 'selector' => '{{WRAPPER}} ' . $translated_tab_css_selector, ] ); } $this->end_controls_tab(); } /** * @string $state */ private function add_border_and_radius_style( $state ) { $selector = '{{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item > .e-n-accordion-item-title'; $translated_tab_text = esc_html__( 'Normal', 'elementor' ); switch ( $state ) { case 'hover': $selector .= ':hover'; $translated_tab_text = esc_html__( 'Hover', 'elementor' ); break; case 'active': $selector = '{{WRAPPER}} > .elementor-widget-container > .e-n-accordion > .e-n-accordion-item[open] > .e-n-accordion-item-title'; $translated_tab_text = esc_html__( 'Active', 'elementor' ); break; } $this->start_controls_tab( 'accordion_' . $state . '_border_and_background', [ 'label' => $translated_tab_text, ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'accordion_background_' . $state, 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Color', 'elementor' ), ], ], 'selector' => $selector, ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'accordion_border_' . $state, 'selector' => $selector, ] ); $this->end_controls_tab(); } private function is_active_icon_exist( $settings ):bool { return array_key_exists( 'accordion_item_title_icon_active', $settings ) && ! empty( $settings['accordion_item_title_icon_active'] ) && ! empty( $settings['accordion_item_title_icon_active']['value'] ); } private function render_accordion_icons( $settings ) { $icon_html = Icons_Manager::try_get_icon_html( $settings['accordion_item_title_icon'], [ 'aria-hidden' => 'true' ] ); $icon_active_html = $this->is_active_icon_exist( $settings ) ? Icons_Manager::try_get_icon_html( $settings['accordion_item_title_icon_active'], [ 'aria-hidden' => 'true' ] ) : $icon_html; ob_start(); ?> get_settings_for_display(); $items = $settings['items']; $id_int = substr( $this->get_id_int(), 0, 3 ); $items_title_html = ''; $icons_content = $this->render_accordion_icons( $settings ); $this->add_render_attribute( 'elementor-accordion', 'class', 'e-n-accordion' ); $this->add_render_attribute( 'elementor-accordion', 'aria-label', 'Accordion. Open links with Enter or Space, close with Escape, and navigate with Arrow Keys' ); $default_state = $settings['default_state']; $title_html_tag = Utils::validate_html_tag( $settings['title_tag'] ); $faq_schema = []; foreach ( $items as $index => $item ) { $accordion_count = $index + 1; $item_setting_key = $this->get_repeater_setting_key( 'item_title', 'items', $index ); $item_summary_key = $this->get_repeater_setting_key( 'item_summary', 'items', $index ); $item_classes = [ 'e-n-accordion-item' ]; $item_id = empty( $item['element_css_id'] ) ? 'e-n-accordion-item-' . $id_int . $index : $item['element_css_id']; $item_title = $item['item_title']; $is_open = 'expanded' === $default_state && 0 === $index ? 'open' : ''; $aria_expanded = 'expanded' === $default_state && 0 === $index; $this->add_render_attribute( $item_setting_key, [ 'id' => $item_id, 'class' => $item_classes, ] ); $this->add_render_attribute( $item_summary_key, [ 'class' => [ 'e-n-accordion-item-title' ], 'data-accordion-index' => $accordion_count, 'tabindex' => 0 === $index ? 0 : -1, 'aria-expanded' => $aria_expanded ? 'true' : 'false', 'aria-controls' => $item_id, ] ); $title_render_attributes = $this->get_render_attribute_string( $item_setting_key ); $title_render_attributes = $title_render_attributes . ' ' . $is_open; $summary_render_attributes = $this->get_render_attribute_string( $item_summary_key ); // items content. ob_start(); $this->print_child( $index, $item_id ); $item_content = ob_get_clean(); $faq_schema[ $item_title ] = $item_content; ob_start(); ?>
> > $item_title " ); ?>
print_render_attribute_string( 'elementor-accordion' ); ?>>
'https://schema.org', '@type' => 'FAQPage', 'mainEntity' => [], ]; foreach ( $faq_schema as $name => $text ) { $json['mainEntity'][] = [ '@type' => 'Question', 'name' => wp_strip_all_tags( $name ), 'acceptedAnswer' => [ '@type' => 'Answer', 'text' => wp_strip_all_tags( $text ), ], ]; } ?> get_children(); if ( ! empty( $children[ $index ] ) ) { // Add data-tab-index attribute to the content area. $add_attribute_to_container = function ( $should_render, $container ) use ( $item_id ) { $this->add_attributes_to_container( $container, $item_id ); return $should_render; }; add_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container, 10, 3 ); $children[ $index ]->print_element(); remove_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container ); } } protected function add_attributes_to_container( $container, $item_id ) { $container->add_render_attribute( '_wrapper', [ 'role' => 'region', 'aria-labelledby' => $item_id, ] ); } protected function get_initial_config(): array { if ( Plugin::$instance->experiments->is_feature_active( 'e_nested_atomic_repeaters' ) ) { return array_merge( parent::get_initial_config(), [ 'support_improved_repeaters' => true, 'target_container' => [ '.e-n-accordion' ], 'node' => 'details', 'is_interlaced' => true, ] ); } return parent::get_initial_config(); } protected function content_template_single_repeater_item() { ?> <# const elementUid = view.getIDInt().toString().substring( 0, 3 ) + view.collection.length; const itemWrapperAttributes = { 'id': 'e-n-accordion-item-' + elementUid, 'class': [ 'e-n-accordion-item', 'e-normal' ], }; const itemTitleAttributes = { 'class': [ 'e-n-accordion-item-title' ], 'data-accordion-index': view.collection.length + 1, 'tabindex': -1, 'aria-expanded': 'false', 'aria-controls': 'e-n-accordion-item-' + elementUid, }; const itemTitleTextAttributes = { 'class': [ 'e-n-accordion-item-title-text' ], 'data-binding-type': 'repeater-item', 'data-binding-repeater-name': 'items', 'data-binding-setting': ['item_title'], 'data-binding-index': view.collection.length + 1, 'data-binding-dynamic': 'true', }; view.addRenderAttribute( 'details-container', itemWrapperAttributes, null, true ); view.addRenderAttribute( 'summary-container', itemTitleAttributes, null, true ); view.addRenderAttribute( 'text-container', itemTitleTextAttributes, null, true ); #>
{{{ data.item_title }}}
<# if ( settings['items'] ) { const elementUid = view.getIDInt().toString().substring( 0, 3 ), titleHTMLTag = elementor.helpers.validateHTMLTag( settings.title_tag ), defaultState = settings.default_state, itemTitleIcon = elementor.helpers.renderIcon( view, settings['accordion_item_title_icon'], { 'aria-hidden': true }, 'i', 'object' ) ?? '', itemTitleIconActive = '' === settings.accordion_item_title_icon_active.value ? itemTitleIcon : elementor.helpers.renderIcon( view, settings['accordion_item_title_icon_active'], { 'aria-hidden': true }, 'i', 'object' ); #> <# _.each( settings['items'], function( item, index ) { const itemCount = index + 1, itemUid = elementUid + index, itemTitleTextKey = 'item-title-text-' + itemUid, itemWrapperKey = itemUid, itemTitleKey = 'item-' + itemUid, ariaExpanded = 'expanded' === defaultState && 0 === index ? 'true' : 'false'; if ( '' !== item.element_css_id ) { itemId = item.element_css_id; } else { itemId = 'e-n-accordion-item-' + itemUid; } const itemWrapperAttributes = { 'id': itemId, 'class': [ 'e-n-accordion-item', 'e-normal' ], }; if ( defaultState === 'expanded' && index === 0) { itemWrapperAttributes['open'] = true; } view.addRenderAttribute( itemWrapperKey, itemWrapperAttributes ); view.addRenderAttribute( itemTitleKey, { 'class': ['e-n-accordion-item-title'], 'data-accordion-index': itemCount, 'tabindex': 0 === index ? 0 : -1, 'aria-expanded': ariaExpanded, 'aria-controls': itemId, }); view.addRenderAttribute( itemTitleTextKey, { 'class': ['e-n-accordion-item-title-text'], 'data-binding-type': 'repeater-item', 'data-binding-repeater-name': 'items', 'data-binding-setting': ['item_title'], 'data-binding-index': itemCount, 'data-binding-dynamic': 'true', }); #>
<{{{ titleHTMLTag }}} {{{ view.getRenderAttributeString( itemTitleTextKey ) }}}> {{{ item.item_title }}} <# if (settings.accordion_item_title_icon.value) { #> {{{ itemTitleIconActive.value }}} {{{ itemTitleIcon.value }}} <# } #>
<# } ); #> <# } #>
is_editable_by_current_user() ) { return; } $this->documents[ $document->get_main_id() ] = $document; } /** * Scripts for module. */ public function enqueue_scripts() { if ( empty( $this->documents ) ) { return; } // Should load 'elementor-admin-bar' before 'admin-bar' wp_dequeue_script( 'admin-bar' ); wp_enqueue_script( 'elementor-admin-bar', $this->get_js_assets_url( 'elementor-admin-bar' ), [ 'elementor-frontend-modules' ], ELEMENTOR_VERSION, true ); // This is a core script of WordPress, it is not required to pass the 'ver' argument. wp_enqueue_script( // phpcs:ignore WordPress.WP.EnqueuedResourceParameters 'admin-bar', null, [ 'elementor-admin-bar' ], false, true ); $this->print_config( 'elementor-admin-bar' ); } /** * Creates admin bar menu items config. * * @return array */ public function get_init_settings() { $settings = []; if ( ! empty( $this->documents ) ) { $settings['elementor_edit_page'] = $this->get_edit_button_config(); } /** * Admin bar settings in the frontend. * * Register admin_bar config to parse later in the frontend and add to the admin bar with JS. * * @since 3.0.0 * * @param array $settings the admin_bar config */ $settings = apply_filters( 'elementor/frontend/admin_bar/settings', $settings ); return $settings; } /** * Creates the config for 'Edit with elementor' menu item. * * @return array */ private function get_edit_button_config() { $queried_object_id = get_queried_object_id(); $href = null; if ( is_singular() && isset( $this->documents[ $queried_object_id ] ) ) { $href = $this->documents[ $queried_object_id ]->get_edit_url(); unset( $this->documents[ $queried_object_id ] ); } return [ 'id' => 'elementor_edit_page', 'title' => esc_html__( 'Edit with Elementor', 'elementor' ), 'href' => $href, 'children' => array_map( function ( $document ) { return [ 'id' => "elementor_edit_doc_{$document->get_main_id()}", 'title' => $document->get_post()->post_title, 'sub_title' => $document::get_title(), 'href' => $document->get_edit_url(), ]; }, $this->documents ), ]; } /** * Module constructor. */ public function __construct() { add_action( 'elementor/frontend/before_get_builder_content', [ $this, 'add_document_to_admin_bar' ], 10, 2 ); add_action( 'wp_footer', [ $this, 'enqueue_scripts' ], 11 /* after third party scripts */ ); } } kit-elements-defaults/module.php000064400000002433147207015700012755 0ustar00get_js_assets_url( 'kit-elements-defaults-editor' ), [ 'elementor-common', 'elementor-editor-modules', 'elementor-editor-document', 'wp-i18n', ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'elementor-kit-elements-defaults-editor', 'elementor' ); } public function __construct() { parent::__construct(); add_action( 'elementor/editor/before_enqueue_scripts', function () { $this->enqueue_scripts(); } ); Plugin::$instance->data_manager_v2->register_controller( new Controller() ); ( new Usage() )->register(); if ( is_admin() ) { ( new Import_Export() )->register(); } } } kit-elements-defaults/import-export/import-export.php000064400000001553147207015700017154 0ustar00register( new Export_Runner() ); } ); add_action( 'elementor/import-export/import-kit', function ( Import $import ) { $import->register( new Import_Runner() ); } ); } } kit-elements-defaults/import-export/runners/import.php000064400000004146147207015700017332 0ustar00kits_manager->get_active_kit(); $file_name = Import_Export::FILE_NAME; $default_values = ImportExportUtils::read_json_file( "{$data['extracted_directory_path']}/{$file_name}.json" ); if ( ! $kit || ! $default_values ) { return []; } $element_types = array_keys( Plugin::$instance->elements_manager->get_element_types() ); $widget_types = array_keys( Plugin::$instance->widgets_manager->get_widget_types() ); $types = array_merge( $element_types, $widget_types ); $sanitizer = new Settings_Sanitizer( Plugin::$instance->elements_manager, $widget_types ); $default_values = ( new Collection( $default_values ) ) ->filter( function ( $settings, $type ) use ( $types ) { return in_array( $type, $types, true ); } ) ->map( function ( $settings, $type ) use ( $sanitizer, $kit ) { return $sanitizer ->for( $type ) ->using( $settings ) ->remove_invalid_settings() ->kses_deep() ->prepare_for_import( $kit ) ->get(); } ) ->all(); $kit->update_json_meta( Module::META_KEY, $default_values ); return $default_values; } } kit-elements-defaults/import-export/runners/export.php000064400000003252147207015700017336 0ustar00kits_manager->get_active_kit(); if ( ! $kit ) { return [ 'manifest' => [], 'files' => [], ]; } $default_values = $kit->get_json_meta( Module::META_KEY ); if ( ! $default_values ) { return [ 'manifest' => [], 'files' => [], ]; } $sanitizer = new Settings_Sanitizer( Plugin::$instance->elements_manager, array_keys( Plugin::$instance->widgets_manager->get_widget_types() ) ); $default_values = ( new Collection( $default_values ) ) ->map( function ( $settings, $type ) use ( $sanitizer, $kit ) { return $sanitizer ->for( $type ) ->using( $settings ) ->remove_invalid_settings() ->kses_deep() ->prepare_for_export( $kit ) ->get(); } ) ->all(); return [ 'files' => [ 'path' => Import_Export::FILE_NAME, 'data' => $default_values, ], ]; } } kit-elements-defaults/data/controller.php000064400000007744147207015700014576 0ustar00index_endpoint->register_item_route(\WP_REST_Server::EDITABLE, [ 'id_arg_name' => 'type', 'id_arg_type_regex' => '[\w\-\_]+', 'type' => [ 'type' => 'string', 'description' => 'The type of the element.', 'required' => true, 'validate_callback' => function( $type ) { return $this->validate_type( $type ); }, ], 'settings' => [ 'description' => 'All the default values for the requested type', 'required' => true, 'type' => 'object', 'validate_callback' => function( $settings ) { return is_array( $settings ); }, 'sanitize_callback' => function( $settings, \WP_REST_Request $request ) { Performance::set_use_style_controls( true ); $sanitizer = new Settings_Sanitizer( Plugin::$instance->elements_manager, array_keys( Plugin::$instance->widgets_manager->get_widget_types() ) ); $sanitized_data = $sanitizer ->for( $request->get_param( 'type' ) ) ->using( $settings ) ->remove_invalid_settings() ->kses_deep() ->get(); Performance::set_use_style_controls( false ); return $sanitized_data; }, ], ] ); $this->index_endpoint->register_item_route(\WP_REST_Server::DELETABLE, [ 'id_arg_name' => 'type', 'id_arg_type_regex' => '[\w\-\_]+', 'type' => [ 'type' => 'string', 'description' => 'The type of the element.', 'required' => true, 'validate_callback' => function( $type ) { return $this->validate_type( $type ); }, ], ] ); } public function get_collection_params() { return []; } public function get_items( $request ) { $this->validate_kit(); $kit = Plugin::$instance->kits_manager->get_active_kit(); return (object) $kit->get_json_meta( Module::META_KEY ); } public function update_item( $request ) { $this->validate_kit(); $kit = Plugin::$instance->kits_manager->get_active_kit(); $data = $kit->get_json_meta( Module::META_KEY ); $data[ $request->get_param( 'type' ) ] = $request->get_param( 'settings' ); $kit->update_json_meta( Module::META_KEY, $data ); return (object) []; } public function delete_item( $request ) { $this->validate_kit(); $kit = Plugin::$instance->kits_manager->get_active_kit(); $data = $kit->get_json_meta( Module::META_KEY ); unset( $data[ $request->get_param( 'type' ) ] ); $kit->update_json_meta( Module::META_KEY, $data ); return (object) []; } private function validate_kit() { $kit = Plugin::$instance->kits_manager->get_active_kit(); $is_valid_kit = $kit && $kit->get_main_id(); if ( ! $is_valid_kit ) { throw new Error_404( 'Kit doesn\'t exist.' ); } } private function validate_type( $param ) { $element_types = array_keys( Plugin::$instance->elements_manager->get_element_types() ); $widget_types = array_keys( Plugin::$instance->widgets_manager->get_widget_types() ); return in_array( $param, array_merge( $element_types, $widget_types ), true ); } public function get_items_permissions_check( $request ) { return current_user_can( 'edit_posts' ); } // TODO: Should be removed once the infra will support it. public function get_item_permissions_check( $request ) { return $this->get_items_permissions_check( $request ); } public function update_item_permissions_check( $request ) { return current_user_can( 'manage_options' ); } public function delete_item_permissions_check( $request ) { return current_user_can( 'manage_options' ); } } kit-elements-defaults/usage.php000064400000001364147207015700012576 0ustar00get_usage_data(); return $params; } ); } private function get_usage_data() { $elements_defaults = $this->get_elements_defaults() ?? []; return [ 'count' => count( $elements_defaults ), 'elements' => array_keys( $elements_defaults ), ]; } private function get_elements_defaults() { $kit = Plugin::$instance->kits_manager->get_active_kit(); return $kit->get_json_meta( Module::META_KEY ); } } kit-elements-defaults/utils/settings-sanitizer.php000064400000013105147207015700016474 0ustar00elements_manager = $elements_manager; $this->widget_types = $widget_types; } /** * @param $type * * @return $this */ public function for( $type ) { $this->pending_element = $this->create_element( $type ); return $this; } /** * @param $settings * * @return $this */ public function using( $settings ) { $this->pending_settings = $settings; return $this; } /** * @return $this */ public function reset() { $this->pending_element = null; $this->pending_settings = null; return $this; } /** * @return bool */ public function is_prepared() { return $this->pending_element && is_array( $this->pending_settings ); } /** * @return $this */ public function remove_invalid_settings() { if ( ! $this->is_prepared() ) { return $this; } $valid_settings_keys = $this->get_valid_settings_keys( $this->pending_element->get_controls() ); $this->pending_settings = $this->filter_invalid_settings( $this->pending_settings, array_merge( $valid_settings_keys, self::SPECIAL_SETTINGS ) ); foreach ( self::SPECIAL_SETTINGS as $special_setting ) { if ( ! isset( $this->pending_settings[ $special_setting ] ) ) { continue; } $this->pending_settings[ $special_setting ] = $this->filter_invalid_settings( $this->pending_settings[ $special_setting ], $valid_settings_keys ); } return $this; } public function kses_deep() { if ( ! $this->is_prepared() ) { return $this; } $this->pending_settings = map_deep( $this->pending_settings, function( $value ) { if ( ! is_string( $value ) ) { return $value; } return wp_kses_post( $value ); } ); return $this; } /** * @param Document $document * * @return $this */ public function prepare_for_export( Document $document ) { return $this->run_import_export_sanitize_process( $document, 'on_export' ); } /** * @param Document $document * * @return $this */ public function prepare_for_import( Document $document ) { return $this->run_import_export_sanitize_process( $document, 'on_import' ); } /** * @return array */ public function get() { if ( ! $this->is_prepared() ) { return []; } $settings = $this->pending_settings; $this->reset(); return $settings; } /** * @param string $type * @param array $settings * * @return Element_Base|null */ private function create_element( $type ) { $is_widget = in_array( $type, $this->widget_types, true ); $is_inner_section = 'inner-section' === $type; if ( $is_inner_section ) { return $this->elements_manager->create_element_instance( [ 'elType' => 'section', 'isInner' => true, 'id' => '0', ] ); } if ( $is_widget ) { return $this->elements_manager->create_element_instance( [ 'elType' => 'widget', 'widgetType' => $type, 'id' => '0', ] ); } return $this->elements_manager->create_element_instance( [ 'elType' => $type, 'id' => '0', ] ); } /** * @param Document $document * @param $process_type * * @return $this */ private function run_import_export_sanitize_process( Document $document, $process_type ) { if ( ! $this->is_prepared() ) { return $this; } $result = $document->process_element_import_export( $this->pending_element, $process_type, [ 'settings' => $this->pending_settings ] ); if ( empty( $result['settings'] ) ) { return $this; } $this->pending_settings = $result['settings']; return $this; } /** * Get all the available settings of a specific element, including responsive settings. * * @param array $controls * * @return array */ private function get_valid_settings_keys( $controls ) { if ( ! $controls ) { return []; } $control_keys = array_keys( $controls ); $optional_responsive_keys = [ Breakpoints_Manager::BREAKPOINT_KEY_MOBILE, Breakpoints_Manager::BREAKPOINT_KEY_MOBILE_EXTRA, Breakpoints_Manager::BREAKPOINT_KEY_TABLET, Breakpoints_Manager::BREAKPOINT_KEY_TABLET_EXTRA, Breakpoints_Manager::BREAKPOINT_KEY_LAPTOP, Breakpoints_Manager::BREAKPOINT_KEY_WIDESCREEN, ]; $settings = []; foreach ( $control_keys as $control_key ) { // Add the responsive settings. foreach ( $optional_responsive_keys as $responsive_key ) { $settings[] = "{$control_key}_{$responsive_key}"; } // Add the setting itself (not responsive). $settings[] = $control_key; } return $settings; } /** * Remove invalid settings. * * @param $settings * @param $valid_settings_keys * * @return array */ private function filter_invalid_settings( $settings, $valid_settings_keys ) { return array_filter( $settings, function ( $setting_key ) use ( $valid_settings_keys ) { return in_array( $setting_key, $valid_settings_keys, true ); }, ARRAY_FILTER_USE_KEY ); } } history/module.php000064400000002121147207015700010242 0ustar00common->add_template( __DIR__ . '/views/history-panel-template.php' ); Plugin::$instance->common->add_template( __DIR__ . '/views/revisions-panel-template.php' ); } /** * History module constructor. * * Initializing Elementor history module. * * @since 1.7.0 * @access public */ public function __construct() { add_action( 'elementor/editor/init', [ $this, 'add_templates' ] ); } } history/revisions-manager.php000064400000023664147207015700012425 0ustar00documents->get( $post_id ); if ( $document && $document->is_built_with_elementor() ) { $post_content .= ''; } return $post_content; } /** * @since 2.0.0 * @access public * @static */ public static function remove_temp_post_content() { global $post; $document = Plugin::$instance->documents->get( $post->ID ); if ( ! $document || ! $document->is_built_with_elementor() ) { return; } $post->post_content = str_replace( '', '', $post->post_content ); } /** * @since 1.7.0 * @access public * @static * * @param int $post_id * @param array $query_args * @param bool $parse_result * * @return array */ public static function get_revisions( $post_id = 0, $query_args = [], $parse_result = true ) { $post = get_post( $post_id ); if ( ! $post || empty( $post->ID ) ) { return []; } $revisions = []; $default_query_args = [ 'posts_per_page' => self::MAX_REVISIONS_TO_DISPLAY, 'meta_key' => '_elementor_data', ]; $query_args = array_merge( $default_query_args, $query_args ); $posts = wp_get_post_revisions( $post->ID, $query_args ); if ( ! wp_revisions_enabled( $post ) ) { $autosave = Utils::get_post_autosave( $post->ID ); if ( $autosave ) { if ( $parse_result ) { array_unshift( $posts, $autosave ); } else { array_unshift( $posts, $autosave->ID ); } } } if ( $parse_result ) { array_unshift( $posts, $post ); } else { array_unshift( $posts, $post->ID ); return $posts; } $current_time = current_time( 'timestamp' ); /** @var \WP_Post $revision */ foreach ( $posts as $revision ) { $date = date_i18n( _x( 'M j @ H:i', 'revision date format', 'elementor' ), strtotime( $revision->post_modified ) ); $human_time = human_time_diff( strtotime( $revision->post_modified ), $current_time ); if ( $revision->ID === $post->ID ) { $type = 'current'; $type_label = esc_html__( 'Current Version', 'elementor' ); } elseif ( false !== strpos( $revision->post_name, 'autosave' ) ) { $type = 'autosave'; $type_label = esc_html__( 'Autosave', 'elementor' ); } else { $type = 'revision'; $type_label = esc_html__( 'Revision', 'elementor' ); } if ( ! isset( self::$authors[ $revision->post_author ] ) ) { self::$authors[ $revision->post_author ] = [ 'avatar' => get_avatar( $revision->post_author, 22 ), 'display_name' => get_the_author_meta( 'display_name', $revision->post_author ), ]; } $revisions[] = [ 'id' => $revision->ID, 'author' => self::$authors[ $revision->post_author ]['display_name'], 'timestamp' => strtotime( $revision->post_modified ), 'date' => sprintf( /* translators: 1: Human readable time difference, 2: Date. */ esc_html__( '%1$s ago (%2$s)', 'elementor' ), '', '' ), 'type' => $type, 'typeLabel' => $type_label, 'gravatar' => self::$authors[ $revision->post_author ]['avatar'], ]; } return $revisions; } /** * @since 1.9.2 * @access public * @static */ public static function update_autosave( $autosave_data ) { self::save_revision( $autosave_data['ID'] ); } /** * @since 1.7.0 * @access public * @static */ public static function save_revision( $revision_id ) { $parent_id = wp_is_post_revision( $revision_id ); if ( $parent_id ) { Plugin::$instance->db->safe_copy_elementor_meta( $parent_id, $revision_id ); } } /** * @since 1.7.0 * @access public * @static */ public static function restore_revision( $parent_id, $revision_id ) { $parent = Plugin::$instance->documents->get( $parent_id ); $revision = Plugin::$instance->documents->get( $revision_id ); if ( ! $parent || ! $revision ) { return; } $is_built_with_elementor = $revision->is_built_with_elementor(); $parent->set_is_built_with_elementor( $is_built_with_elementor ); if ( ! $is_built_with_elementor ) { return; } Plugin::$instance->db->copy_elementor_meta( $revision_id, $parent_id ); $post_css = Post_CSS::create( $parent_id ); $post_css->update(); } /** * @since 2.3.0 * @access public * @static * * @param $data * * @return array * @throws \Exception */ public static function ajax_get_revision_data( array $data ) { if ( ! isset( $data['id'] ) ) { throw new \Exception( 'You must set the revision ID.' ); } $revision = Plugin::$instance->documents->get_with_permissions( $data['id'] ); return [ 'settings' => $revision->get_settings(), 'elements' => $revision->get_elements_data(), ]; } /** * @since 1.7.0 * @access public * @static */ public static function add_revision_support_for_all_post_types() { $post_types = get_post_types_by_support( 'elementor' ); foreach ( $post_types as $post_type ) { add_post_type_support( $post_type, 'revisions' ); } } /** * @since 2.0.0 * @access public * @static * @param array $return_data * @param Document $document * * @return array */ public static function on_ajax_save_builder_data( $return_data, $document ) { $post_id = $document->get_main_id(); $latest_revisions = self::get_revisions( $post_id, [ 'posts_per_page' => 1, ] ); $all_revision_ids = self::get_revisions( $post_id, [ 'fields' => 'ids', ], false ); // Send revisions data only if has revisions. if ( ! empty( $latest_revisions ) ) { $current_revision_id = self::current_revision_id( $post_id ); $return_data = array_replace_recursive( $return_data, [ 'config' => [ 'document' => [ 'revisions' => [ 'current_id' => $current_revision_id, ], ], ], 'latest_revisions' => $latest_revisions, 'revisions_ids' => $all_revision_ids, ] ); } return $return_data; } /** * @since 1.7.0 * @access public * @static */ public static function db_before_save( $status, $has_changes ) { if ( $has_changes ) { self::handle_revision(); } } public static function document_config( $settings, $post_id ) { $settings['revisions'] = [ 'enabled' => ( $post_id && wp_revisions_enabled( get_post( $post_id ) ) ), 'current_id' => self::current_revision_id( $post_id ), ]; return $settings; } /** * Localize settings. * * Add new localized settings for the revisions manager. * * Fired by `elementor/editor/editor_settings` filter. * * @since 1.7.0 * @deprecated 3.1.0 * @access public * @static */ public static function editor_settings() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); return []; } /** * @throws \Exception */ public static function ajax_get_revisions( $data ) { Plugin::$instance->documents->check_permissions( $data['editor_post_id'] ); return self::get_revisions(); } /** * @since 2.3.0 * @access public * @static */ public static function register_ajax_actions( Ajax $ajax ) { $ajax->register_ajax_action( 'get_revisions', [ __CLASS__, 'ajax_get_revisions' ] ); $ajax->register_ajax_action( 'get_revision_data', [ __CLASS__, 'ajax_get_revision_data' ] ); } /** * @since 1.7.0 * @access private * @static */ private static function register_actions() { add_action( 'wp_restore_post_revision', [ __CLASS__, 'restore_revision' ], 10, 2 ); add_action( 'init', [ __CLASS__, 'add_revision_support_for_all_post_types' ], 9999 ); add_filter( 'elementor/document/config', [ __CLASS__, 'document_config' ], 10, 2 ); add_action( 'elementor/db/before_save', [ __CLASS__, 'db_before_save' ], 10, 2 ); add_action( '_wp_put_post_revision', [ __CLASS__, 'save_revision' ] ); add_action( 'wp_creating_autosave', [ __CLASS__, 'update_autosave' ] ); add_action( 'elementor/ajax/register_actions', [ __CLASS__, 'register_ajax_actions' ] ); // Hack to avoid delete the auto-save revision in WP editor. add_filter( 'edit_post_content', [ __CLASS__, 'avoid_delete_auto_save' ], 10, 2 ); add_action( 'edit_form_after_title', [ __CLASS__, 'remove_temp_post_content' ] ); if ( wp_doing_ajax() ) { add_filter( 'elementor/documents/ajax_save/return_data', [ __CLASS__, 'on_ajax_save_builder_data' ], 10, 2 ); } } /** * @since 1.9.0 * @access private * @static */ private static function current_revision_id( $post_id ) { $current_revision_id = $post_id; $autosave = Utils::get_post_autosave( $post_id ); if ( is_object( $autosave ) ) { $current_revision_id = $autosave->ID; } return $current_revision_id; } } history/views/revisions-panel-template.php000064400000006744147207015700015060 0ustar00 history/views/history-panel-template.php000064400000003664147207015700014536 0ustar00 element-manager/module.php000064400000005023147207015700011606 0ustar00register_endpoints(); add_action( 'elementor/admin/menu/register', function( Admin_Menu_Manager $admin_menu ) { $admin_menu->register( static::PAGE_ID, new Admin_Menu_App() ); }, 25 ); add_action( 'elementor/admin/menu/after_register', function ( Admin_Menu_Manager $admin_menu, array $hooks ) { if ( ! empty( $hooks[ static::PAGE_ID ] ) ) { add_action( "admin_print_scripts-{$hooks[ static::PAGE_ID ]}", [ $this, 'enqueue_assets' ] ); add_action( "admin_footer-{$hooks[ static::PAGE_ID ]}", [ $this, 'print_styles' ], 1000 ); } }, 10, 2 ); add_filter( 'elementor/widgets/is_widget_enabled', function( $should_register, Widget_Base $widget_instance ) { return ! Options::is_element_disabled( $widget_instance->get_name() ); }, 10, 2 ); add_filter( 'elementor/system-info/usage/settings', function( $usage ) { $disabled_elements = Options::get_disabled_elements(); if ( ! empty( $disabled_elements ) ) { $usage['disabled_elements'] = implode( ', ', $disabled_elements ); } return $usage; } ); add_filter( 'elementor/tracker/send_tracking_data_params', function( $params ) { $disabled_elements = Options::get_disabled_elements(); if ( ! empty( $disabled_elements ) ) { $params['usages']['disabled_elements'] = $disabled_elements; } return $params; } ); } public function enqueue_assets() { wp_enqueue_script( 'e-element-manager-app', $this->get_js_assets_url( 'element-manager-admin' ), [ 'wp-element', 'wp-components', 'wp-dom-ready', 'wp-i18n', ], ELEMENTOR_VERSION ); wp_localize_script( 'e-element-manager-app', 'eElementManagerConfig', [ 'nonce' => wp_create_nonce( 'e-element-manager-app' ), 'ajaxurl' => admin_url( 'admin-ajax.php' ), ] ); wp_set_script_translations( 'e-element-manager-app', 'elementor' ); wp_enqueue_style( 'wp-components' ); wp_enqueue_style( 'wp-format-library' ); } public function print_styles() { ?> '; echo '

' . esc_html__( 'Element Manager', 'elementor' ) . '

'; echo '
'; echo ''; } } element-manager/ajax.php000064400000013137147207015700011251 0ustar00verify_permission(); $this->force_enabled_all_elements(); $widgets = []; $plugins = []; foreach ( Plugin::$instance->widgets_manager->get_widget_types() as $widget ) { $widget_title = sanitize_user( $widget->get_title() ); if ( empty( $widget_title ) || ! $widget->show_in_panel() ) { continue; } $plugin_name = $this->get_plugin_name_from_widget_instance( $widget ); if ( ! in_array( $plugin_name, $plugins ) ) { $plugins[] = $plugin_name; } $widgets[] = [ 'name' => $widget->get_name(), 'plugin' => $plugin_name, 'title' => $widget_title, 'icon' => $widget->get_icon(), ]; } $notice_id = 'e-element-manager-intro-1'; $data = [ 'disabled_elements' => Options::get_disabled_elements(), 'promotion_widgets' => [], 'widgets' => $widgets, 'plugins' => $plugins, 'notice_data' => [ 'notice_id' => $notice_id, 'is_viewed' => User::is_user_notice_viewed( $notice_id ), ], 'promotion_data' => [ 'manager_permissions' => [ 'pro' => $this->get_element_manager_promotion( [ 'text' => esc_html__( 'Upgrade Now', 'elementor' ), 'url' => self::FREE_TO_PRO_PERMISSIONS_PROMOTION_URL, ], 'pro_permissions' ), 'advanced' => $this->get_element_manager_promotion( [ 'text' => esc_html__( 'Upgrade Now', 'elementor' ), 'url' => self::PRO_TO_ADVANCED_PERMISSIONS_PROMOTION_URL, ], 'advanced_permissions' ), ], 'element_manager' => $this->get_element_manager_promotion( [ 'text' => esc_html__( 'Upgrade Now', 'elementor' ), 'url' => self::ELEMENT_MANAGER_PROMOTION_URL, ], 'element_manager' ), ], ]; if ( ! Utils::has_pro() ) { $data['promotion_widgets'] = Api::get_promotion_widgets(); } $data['additional_data'] = apply_filters( 'elementor/element_manager/admin_app_data/additional_data', [] ); wp_send_json_success( $data ); } private function get_element_manager_promotion( $promotion_data, $filter_id ): array { return Filtered_Promotions_Manager::get_filtered_promotion_data( $promotion_data, 'elementor/element_manager/admin_app_data/promotion_data/' . $filter_id, 'url' ); } private function verify_permission() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( esc_html__( 'You do not have permission to edit these settings.', 'elementor' ) ); } $nonce = Utils::get_super_global_value( $_POST, 'nonce' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'e-element-manager-app' ) ) { wp_send_json_error( esc_html__( 'Invalid nonce.', 'elementor' ) ); } } private function force_enabled_all_elements() { remove_all_filters( 'elementor/widgets/is_widget_enabled' ); } private function get_plugin_name_from_widget_instance( $widget ) { if ( in_array( 'wordpress', $widget->get_categories() ) ) { return esc_html__( 'WordPress Widgets', 'elementor' ); } $class_reflection = new \ReflectionClass( $widget ); $plugin_basename = plugin_basename( $class_reflection->getFileName() ); $plugin_directory = strtok( $plugin_basename, '/' ); $plugins_data = get_plugins( '/' . $plugin_directory ); $plugin_data = array_shift( $plugins_data ); return $plugin_data['Name'] ?? esc_html__( 'Unknown', 'elementor' ); } public function ajax_save_disabled_elements() { $this->verify_permission(); $elements = Utils::get_super_global_value( $_POST, 'widgets' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( empty( $elements ) ) { wp_send_json_error( esc_html__( 'No elements to save.', 'elementor' ) ); } $disabled_elements = json_decode( $elements ); if ( ! is_array( $disabled_elements ) ) { wp_send_json_error( esc_html__( 'Unexpected elements data.', 'elementor' ) ); } Options::update_disabled_elements( $disabled_elements ); do_action( 'elementor/element_manager/save_disabled_elements' ); wp_send_json_success(); } public function ajax_get_widgets_usage() { $this->verify_permission(); /** @var Usage_Module $usage_module */ $usage_module = Usage_Module::instance(); $usage_module->recalc_usage(); $widgets_usage = []; foreach ( $usage_module->get_formatted_usage( 'raw' ) as $data ) { foreach ( $data['elements'] as $element => $count ) { if ( ! isset( $widgets_usage[ $element ] ) ) { $widgets_usage[ $element ] = 0; } $widgets_usage[ $element ] += $count; } } wp_send_json_success( $widgets_usage ); } } element-manager/options.php000064400000001024147207015700012011 0ustar00documents ->register_document_type( 'not-supported', Documents\Not_Supported::get_class_full_name() ) ->register_document_type( 'page', Documents\Page::get_class_full_name() ) ->register_document_type( 'section', Documents\Section::get_class_full_name() ); $experiments_manager = Plugin::$instance->experiments; // Register `Container` document type only if the experiment is active. if ( $experiments_manager->is_feature_active( 'container' ) ) { Plugin::$instance->documents ->register_document_type( 'container', Documents\Container::get_class_full_name() ); } } } library/traits/library.php000064400000002021147207015700011671 0ustar00get_name() ); //PHPCS - Not a user input printf( '%s', $admin_filter_url, $this->get_title() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** * Save document type. * * Set new/updated document type. * * @since 3.1.0 * @access public */ public function save_template_type() { parent::save_template_type(); wp_set_object_terms( $this->post->ID, $this->get_name(), Source_Local::TAXONOMY_TYPE_SLUG ); } } library/documents/page.php000064400000003521147207015700011642 0ustar00get_main_id(); } /** * @since 3.1.0 * @access protected */ protected function register_controls() { parent::register_controls(); Post::register_hide_title_control( $this ); Post::register_style_controls( $this ); } protected function get_remote_library_config() { $config = parent::get_remote_library_config(); $config['type'] = 'page'; $config['default_route'] = 'templates/pages'; return $config; } } library/documents/library-document.php000064400000003261147207015700014207 0ustar00 true, ]; return $config; } public function get_content( $with_css = false ) { return do_shortcode( parent::get_content( $with_css ) ); } } library/documents/section.php000064400000001665147207015700012401 0ustar00user_id = $user_id; } /** * @param null $vendor * @param null $resource * @param false $ignore_cache * * @return array */ public function get( $vendor = null, $resource = null, $ignore_cache = false ) { if ( $ignore_cache || empty( $this->cache ) ) { $this->cache = get_user_meta( $this->user_id, self::USER_META_KEY, true ); } if ( ! $this->cache || ! is_array( $this->cache ) ) { return []; } if ( $vendor && $resource ) { $key = $this->get_key( $vendor, $resource ); return isset( $this->cache[ $key ] ) ? $this->cache[ $key ] : []; } return $this->cache; } /** * @param $vendor * @param $resource * @param $id * * @return bool */ public function exists( $vendor, $resource, $id ) { return in_array( $id, $this->get( $vendor, $resource ), true ); } /** * @param $vendor * @param $resource * @param array $value * * @return $this * @throws \Exception */ public function save( $vendor, $resource, $value = [] ) { $all_favorites = $this->get(); $all_favorites[ $this->get_key( $vendor, $resource ) ] = $value; $result = update_user_meta( $this->user_id, self::USER_META_KEY, $all_favorites ); if ( false === $result ) { throw new \Exception( 'Failed to save user favorites.' ); } $this->cache = $all_favorites; return $this; } /** * @param $vendor * @param $resource * @param $id * * @return $this * @throws \Exception */ public function add( $vendor, $resource, $id ) { $favorites = $this->get( $vendor, $resource ); if ( in_array( $id, $favorites, true ) ) { return $this; } $favorites[] = $id; $this->save( $vendor, $resource, $favorites ); return $this; } /** * @param $vendor * @param $resource * @param $id * * @return $this * @throws \Exception */ public function remove( $vendor, $resource, $id ) { $favorites = $this->get( $vendor, $resource ); if ( ! in_array( $id, $favorites, true ) ) { return $this; } $favorites = array_filter( $favorites, function ( $item ) use ( $id ) { return $item !== $id; } ); $this->save( $vendor, $resource, $favorites ); return $this; } /** * @param $vendor * @param $resource * * @return string */ private function get_key( $vendor, $resource ) { return "{$vendor}/{$resource}"; } } ai/module.php000064400000074273147207015700007153 0ustar00register(); } if ( ! Preferences::is_ai_enabled( get_current_user_id() ) ) { return; } $this->register_layout_experiment(); add_action( 'elementor/connect/apps/register', function ( ConnectModule $connect_module ) { $connect_module->register_app( 'ai', Ai::get_class_name() ); } ); add_action( 'elementor/ajax/register_actions', function( $ajax ) { $handlers = [ 'ai_get_user_information' => [ $this, 'ajax_ai_get_user_information' ], 'ai_get_remote_config' => [ $this, 'ajax_ai_get_remote_config' ], 'ai_get_remote_frontend_config' => [ $this, 'ajax_ai_get_remote_frontend_config' ], 'ai_get_completion_text' => [ $this, 'ajax_ai_get_completion_text' ], 'ai_get_excerpt' => [ $this, 'ajax_ai_get_excerpt' ], 'ai_get_featured_image' => [ $this, 'ajax_ai_get_featured_image' ], 'ai_get_edit_text' => [ $this, 'ajax_ai_get_edit_text' ], 'ai_get_custom_code' => [ $this, 'ajax_ai_get_custom_code' ], 'ai_get_custom_css' => [ $this, 'ajax_ai_get_custom_css' ], 'ai_set_get_started' => [ $this, 'ajax_ai_set_get_started' ], 'ai_set_status_feedback' => [ $this, 'ajax_ai_set_status_feedback' ], 'ai_get_image_prompt_enhancer' => [ $this, 'ajax_ai_get_image_prompt_enhancer' ], 'ai_get_text_to_image' => [ $this, 'ajax_ai_get_text_to_image' ], 'ai_get_image_to_image' => [ $this, 'ajax_ai_get_image_to_image' ], 'ai_get_image_to_image_mask' => [ $this, 'ajax_ai_get_image_to_image_mask' ], 'ai_get_image_to_image_mask_cleanup' => [ $this, 'ajax_ai_get_image_to_image_mask_cleanup' ], 'ai_get_image_to_image_outpainting' => [ $this, 'ajax_ai_get_image_to_image_outpainting' ], 'ai_get_image_to_image_upscale' => [ $this, 'ajax_ai_get_image_to_image_upscale' ], 'ai_get_image_to_image_remove_background' => [ $this, 'ajax_ai_get_image_to_image_remove_background' ], 'ai_get_image_to_image_replace_background' => [ $this, 'ajax_ai_get_image_to_image_replace_background' ], 'ai_upload_image' => [ $this, 'ajax_ai_upload_image' ], 'ai_generate_layout' => [ $this, 'ajax_ai_generate_layout' ], 'ai_get_layout_prompt_enhancer' => [ $this, 'ajax_ai_get_layout_prompt_enhancer' ], 'ai_get_history' => [ $this, 'ajax_ai_get_history' ], 'ai_delete_history_item' => [ $this, 'ajax_ai_delete_history_item' ], 'ai_toggle_favorite_history_item' => [ $this, 'ajax_ai_toggle_favorite_history_item' ], ]; foreach ( $handlers as $tag => $callback ) { $ajax->register_ajax_action( $tag, $callback ); } } ); add_action( 'elementor/editor/before_enqueue_scripts', function() { $this->enqueue_main_script(); if ( $this->is_layout_active() ) { $this->enqueue_layout_script(); } } ); add_action( 'elementor/editor/after_enqueue_styles', function() { wp_enqueue_style( 'elementor-ai-editor', $this->get_css_assets_url( 'modules/ai/editor' ), [], ELEMENTOR_VERSION ); } ); add_action( 'elementor/preview/enqueue_styles', function() { if ( $this->is_layout_active() ) { wp_enqueue_style( 'elementor-ai-layout-preview', $this->get_css_assets_url( 'modules/ai/layout-preview' ), [], ELEMENTOR_VERSION ); } } ); if ( is_admin() ) { add_action( 'wp_enqueue_media', [ $this, 'enqueue_ai_media_library' ] ); } add_action( 'enqueue_block_editor_assets', function() { wp_enqueue_script( 'elementor-ai-gutenberg', $this->get_js_assets_url( 'ai-gutenberg' ), [ 'jquery', 'elementor-v2-ui', 'elementor-v2-icons', 'wp-blocks', 'wp-element', 'wp-editor', 'wp-data', 'wp-components', 'wp-compose', 'wp-i18n', 'wp-hooks', 'elementor-ai-media-library', ], ELEMENTOR_VERSION, true ); wp_localize_script( 'elementor-ai-gutenberg', 'ElementorAiConfig', [ 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ), 'connect_url' => $this->get_ai_connect_url(), ] ); wp_set_script_translations( 'elementor-ai-gutenberg', 'elementor' ); }); add_filter( 'elementor/document/save/data', function ( $data ) { if ( $this->is_layout_active() ) { return $this->remove_temporary_containers( $data ); } return $data; } ); } private function register_layout_experiment() { Plugin::$instance->experiments->add_feature( [ 'name' => static::LAYOUT_EXPERIMENT, 'title' => esc_html__( 'Build with AI', 'elementor' ), 'description' => esc_html__( 'Tap into the potential of AI to easily create and customize containers to your specifications, right within Elementor. This feature comes packed with handy AI tools, including generation, variations, and URL references.', 'elementor' ), 'default' => Experiments_Manager::STATE_ACTIVE, 'release_status' => Experiments_Manager::RELEASE_STATUS_STABLE, 'dependencies' => [ 'container', ], ] ); } public function enqueue_ai_media_library() { wp_enqueue_script( 'elementor-ai-media-library', $this->get_js_assets_url( 'ai-media-library' ), [ 'jquery', 'elementor-v2-ui', 'elementor-v2-icons', 'media-grid', ], ELEMENTOR_VERSION, true ); wp_localize_script( 'elementor-ai-media-library', 'ElementorAiConfig', [ 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ), 'connect_url' => $this->get_ai_connect_url(), ] ); wp_set_script_translations( 'elementor-ai-media-library', 'elementor' ); } private function enqueue_main_script() { wp_enqueue_script( 'elementor-ai', $this->get_js_assets_url( 'ai' ), [ 'react', 'react-dom', 'backbone-marionette', 'elementor-web-cli', 'wp-date', 'elementor-common', 'elementor-editor-modules', 'elementor-editor-document', 'elementor-v2-ui', 'elementor-v2-icons', ], ELEMENTOR_VERSION, true ); $config = [ 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ), 'connect_url' => $this->get_ai_connect_url(), ]; if ( $this->get_ai_app()->is_connected() ) { // Use a cached version, don't call the API on every editor load. $config['usage'] = $this->get_ai_app()->get_cached_usage(); } wp_localize_script( 'elementor-ai', 'ElementorAiConfig', $config ); wp_set_script_translations( 'elementor-ai', 'elementor' ); } private function enqueue_layout_script() { wp_enqueue_script( 'elementor-ai-layout', $this->get_js_assets_url( 'ai-layout' ), [ 'react', 'react-dom', 'backbone-marionette', 'elementor-common', 'elementor-web-cli', 'elementor-editor-modules', 'elementor-ai', 'elementor-v2-ui', 'elementor-v2-icons', ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'elementor-ai-layout', 'elementor' ); } private function is_layout_active() { return Plugin::$instance->experiments->is_feature_active( self::LAYOUT_EXPERIMENT ); } private function remove_temporary_containers( $data ) { if ( empty( $data['elements'] ) ) { return $data; } // If for some reason the document has been saved during an AI Layout session, // ensure that the temporary containers are removed from the data. $data['elements'] = array_filter( $data['elements'], function( $element ) { $is_preview_container = strpos( $element['id'], 'e-ai-preview-container' ) === 0; $is_screenshot_container = strpos( $element['id'], 'e-ai-screenshot-container' ) === 0; return ! $is_preview_container && ! $is_screenshot_container; } ); return $data; } private function get_ai_connect_url() { $app = $this->get_ai_app(); return $app->get_admin_url( 'authorize', [ 'utm_source' => 'ai-popup', 'utm_campaign' => 'connect-account', 'utm_medium' => 'wp-dash', 'source' => 'generic', ] ); } public function ajax_ai_get_user_information( $data ) { $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { return [ 'is_connected' => false, 'connect_url' => $this->get_ai_connect_url(), ]; } $user_usage = wp_parse_args( $app->get_usage(), [ 'hasAiSubscription' => false, 'usedQuota' => 0, 'quota' => 100, ] ); return [ 'is_connected' => true, 'is_get_started' => User::get_introduction_meta( 'ai_get_started' ), 'usage' => $user_usage, ]; } public function ajax_ai_get_remote_config() { $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { return []; } return $app->get_remote_config(); } public function ajax_ai_get_remote_frontend_config( $data ) { $callback = function () use ( $data ) { return $this->get_ai_app()->get_remote_frontend_config( $data ); }; return Utils::get_cached_callback( $callback, 'ai_remote_frontend_config-' . get_current_user_id(), HOUR_IN_SECONDS ); } public function verify_upload_permissions( $data ) { $referer = wp_get_referer(); if ( str_contains( $referer, 'wp-admin/upload.php' ) && current_user_can( 'upload_files' ) ) { return; } $this->verify_permissions( $data['editor_post_id'] ); } private function verify_permissions( $editor_post_id ) { $document = Plugin::$instance->documents->get( $editor_post_id ); if ( ! $document ) { throw new \Exception( 'Document not found' ); } if ( ! $document->is_editable_by_current_user() ) { throw new \Exception( 'Access denied' ); } } public function ajax_ai_get_image_prompt_enhancer( $data ) { $this->verify_upload_permissions( $data ); $app = $this->get_ai_app(); if ( empty( $data['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_prompt_enhanced( $data['prompt'], [], $request_ids ); $this->throw_on_error( $result ); return [ 'text' => $result['text'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_completion_text( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_completion_text( $data['payload']['prompt'], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'text' => $result['text'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_excerpt( $data ): array { $app = $this->get_ai_app(); if ( empty( $data['payload']['content'] ) ) { throw new \Exception( 'Missing content' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'Not connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_excerpt( $data['payload']['content'], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'text' => $result['text'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_featured_image( $data ): array { $this->verify_upload_permissions( $data ); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_featured_image( $data, $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } private function get_ai_app() : Ai { return Plugin::$instance->common->get_component( 'connect' )->get_app( 'ai' ); } private function get_request_context( $data ) { if ( empty( $data['context'] ) ) { return []; } return $data['context']; } private function get_request_ids( $data ) { if ( empty( $data['requestIds'] ) ) { return new \stdClass(); } return $data['requestIds']; } public function ajax_ai_get_edit_text( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['input'] ) ) { throw new \Exception( 'Missing input' ); } if ( empty( $data['payload']['instruction'] ) ) { throw new \Exception( 'Missing instruction' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_edit_text( $data, $context, $request_ids ); $this->throw_on_error( $result ); return [ 'text' => $result['text'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_custom_code( $data ) { $app = $this->get_ai_app(); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( empty( $data['payload']['language'] ) ) { throw new \Exception( 'Missing language' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_custom_code( $data, $context, $request_ids ); $this->throw_on_error( $result ); return [ 'text' => $result['text'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_custom_css( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( empty( $data['payload']['html_markup'] ) ) { $data['html_markup'] = ''; } if ( empty( $data['payload']['element_id'] ) ) { throw new \Exception( 'Missing element_id' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_custom_css( $data, $context, $request_ids ); $this->throw_on_error( $result ); return [ 'text' => $result['text'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_set_get_started( $data ) { $app = $this->get_ai_app(); User::set_introduction_viewed( [ 'introductionKey' => 'ai_get_started', ] ); return $app->set_get_started(); } public function ajax_ai_set_status_feedback( $data ) { if ( empty( $data['response_id'] ) ) { throw new \Exception( 'Missing response_id' ); } $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $app->set_status_feedback( $data['response_id'] ); return []; } public function ajax_ai_get_text_to_image( $data ) { $this->verify_upload_permissions( $data ); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_text_to_image( $data, $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image( $data ) { $this->verify_upload_permissions( $data ); $app = $this->get_ai_app(); if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) { throw new \Exception( 'Missing Image' ); } if ( empty( $data['payload']['settings'] ) ) { throw new \Exception( 'Missing prompt settings' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image( [ 'prompt' => $data['payload']['prompt'], 'promptSettings' => $data['payload']['settings'], 'attachment_id' => $data['payload']['image']['id'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image_upscale( $data ) { $this->verify_upload_permissions( $data ); $app = $this->get_ai_app(); if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) { throw new \Exception( 'Missing Image' ); } if ( empty( $data['payload']['promptSettings'] ) ) { throw new \Exception( 'Missing prompt settings' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image_upscale( [ 'promptSettings' => $data['payload']['promptSettings'], 'attachment_id' => $data['payload']['image']['id'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image_replace_background( $data ) { $this->verify_upload_permissions( $data ); $app = $this->get_ai_app(); if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) { throw new \Exception( 'Missing Image' ); } if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Prompt Missing' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image_replace_background( [ 'attachment_id' => $data['payload']['image']['id'], 'prompt' => $data['payload']['prompt'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image_remove_background( $data ) { $this->verify_upload_permissions( $data ); $app = $this->get_ai_app(); if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) { throw new \Exception( 'Missing Image' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image_remove_background( [ 'attachment_id' => $data['payload']['image']['id'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image_mask( $data ) { $this->verify_upload_permissions( $data ); $app = $this->get_ai_app(); if ( empty( $data['payload']['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) { throw new \Exception( 'Missing Image' ); } if ( empty( $data['payload']['settings'] ) ) { throw new \Exception( 'Missing prompt settings' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } if ( empty( $data['payload']['mask'] ) ) { throw new \Exception( 'Missing Mask' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image_mask( [ 'prompt' => $data['payload']['prompt'], 'attachment_id' => $data['payload']['image']['id'], 'mask' => $data['payload']['mask'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image_mask_cleanup( $data ) { $this->verify_upload_permissions( $data ); $app = $this->get_ai_app(); if ( empty( $data['payload']['image'] ) || empty( $data['payload']['image']['id'] ) ) { throw new \Exception( 'Missing Image' ); } if ( empty( $data['payload']['settings'] ) ) { throw new \Exception( 'Missing prompt settings' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } if ( empty( $data['payload']['mask'] ) ) { throw new \Exception( 'Missing Mask' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image_mask_cleanup( [ 'attachment_id' => $data['payload']['image']['id'], 'mask' => $data['payload']['mask'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_get_image_to_image_outpainting( $data ) { $this->verify_upload_permissions( $data ); $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } if ( empty( $data['payload']['mask'] ) ) { throw new \Exception( 'Missing Expended Image' ); } $context = $this->get_request_context( $data ); $request_ids = $this->get_request_ids( $data['payload'] ); $result = $app->get_image_to_image_out_painting( [ 'size' => $data['payload']['size'], 'position' => $data['payload']['position'], 'mask' => $data['payload']['mask'], 'image_base64' => $data['payload']['image_base64'], ], $context, $request_ids ); $this->throw_on_error( $result ); return [ 'images' => $result['images'], 'response_id' => $result['responseId'], 'usage' => $result['usage'], ]; } public function ajax_ai_upload_image( $data ) { if ( empty( $data['image'] ) ) { throw new \Exception( 'Missing image data' ); } $image = $data['image']; if ( empty( $image['image_url'] ) ) { throw new \Exception( 'Missing image_url' ); } $image_data = $this->upload_image( $image['image_url'], $data['prompt'], $data['editor_post_id'] ); if ( is_wp_error( $image_data ) ) { throw new \Exception( $image_data->get_error_message() ); } if ( ! empty( $image['use_gallery_image'] ) && ! empty( $image['id'] ) ) { $app = $this->get_ai_app(); $app->set_used_gallery_image( $image['id'] ); } return [ 'image' => array_merge( $image_data, $data ), ]; } public function ajax_ai_generate_layout( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['prompt'] ) && empty( $data['attachments'] ) ) { throw new \Exception( 'Missing prompt / attachments' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $result = $app->generate_layout( $data, $this->prepare_generate_layout_context( $data ) ); if ( is_wp_error( $result ) ) { $message = $result->get_error_message(); if ( is_array( $message ) ) { $message = implode( ', ', $message ); throw new \Exception( $message ); } $this->throw_on_error( $result ); } $elements = $result['text']['elements'] ?? []; $base_template_id = $result['baseTemplateId'] ?? null; $template_type = $result['templateType'] ?? null; if ( empty( $elements ) || ! is_array( $elements ) ) { throw new \Exception( 'unknown_error' ); } if ( 1 === count( $elements ) ) { $template = $elements[0]; } else { $template = [ 'elType' => 'container', 'elements' => $elements, 'settings' => [ 'content_width' => 'full', 'flex_gap' => [ 'column' => '0', 'row' => '0', 'unit' => 'px', ], 'padding' => [ 'unit' => 'px', 'top' => '0', 'right' => '0', 'bottom' => '0', 'left' => '0', 'isLinked' => true, ], ], ]; } return [ 'all' => [], 'text' => $template, 'response_id' => $result['responseId'], 'usage' => $result['usage'], 'base_template_id' => $base_template_id, 'template_type' => $template_type, ]; } public function ajax_ai_get_layout_prompt_enhancer( $data ) { $this->verify_permissions( $data['editor_post_id'] ); $app = $this->get_ai_app(); if ( empty( $data['prompt'] ) ) { throw new \Exception( 'Missing prompt' ); } if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $result = $app->get_layout_prompt_enhanced( $data['prompt'], $data['enhance_type'], $this->prepare_generate_layout_context( $data ) ); $this->throw_on_error( $result ); return [ 'text' => $result['text'] ?? $data['prompt'], 'response_id' => $result['responseId'] ?? '', 'usage' => $result['usage'] ?? '', ]; } private function prepare_generate_layout_context( $data ) { $request_context = $this->get_request_context( $data ); $kit = Plugin::$instance->kits_manager->get_active_kit(); if ( ! $kit ) { return $request_context; } $kits_data = Collection::make( $kit->get_data()['settings'] ?? [] ); $colors = $kits_data ->filter( function ( $_, $key ) { return in_array( $key, [ 'system_colors', 'custom_colors' ], true ); } ) ->flatten() ->filter( function ( $val ) { return ! empty( $val['_id'] ); } ) ->map( function ( $val ) { return [ 'id' => $val['_id'], 'label' => $val['title'] ?? null, 'value' => $val['color'] ?? null, ]; } ); $typography = $kits_data ->filter( function ( $_, $key ) { return in_array( $key, [ 'system_typography', 'custom_typography' ], true ); } ) ->flatten() ->filter( function ( $val ) { return ! empty( $val['_id'] ); } ) ->map( function ( $val ) { $font_size = null; if ( isset( $val['typography_font_size']['unit'], $val['typography_font_size']['size'] ) ) { $prop = $val['typography_font_size']; $font_size = 'custom' === $prop['unit'] ? $prop['size'] : $prop['size'] . $prop['unit']; } return [ 'id' => $val['_id'], 'label' => $val['title'] ?? null, 'value' => [ 'family' => $val['typography_font_family'] ?? null, 'weight' => $val['typography_font_weight'] ?? null, 'style' => $val['typography_font_style'] ?? null, 'size' => $font_size, ], ]; } ); $request_context['globals'] = [ 'colors' => $colors->all(), 'typography' => $typography->all(), ]; return $request_context; } private function upload_image( $image_url, $image_title, $parent_post_id = 0 ) { if ( ! current_user_can( 'upload_files' ) ) { throw new \Exception( 'Not Allowed to Upload images' ); } $attachment_id = media_sideload_image( $image_url, $parent_post_id, $image_title, 'id' ); if ( ! empty( $attachment_id['error'] ) ) { return new \WP_Error( 'upload_error', $attachment_id['error'] ); } return [ 'id' => $attachment_id, 'url' => wp_get_attachment_image_url( $attachment_id, 'full' ), 'alt' => $image_title, 'source' => 'library', ]; } public function ajax_ai_get_history( $data ): array { $type = $data['type'] ?? self::HISTORY_TYPE_ALL; if ( ! in_array( $type, self::VALID_HISTORY_TYPES, true ) ) { throw new \Exception( 'Invalid history type' ); } $page = sanitize_text_field( $data['page'] ?? 1 ); $limit = sanitize_text_field( $data['limit'] ?? 10 ); $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $result = $app->get_history_by_type( $type, $page, $limit, $context ); if ( is_wp_error( $result ) ) { throw new \Exception( $result->get_error_message() ); } return $result; } public function ajax_ai_delete_history_item( $data ): array { if ( empty( $data['id'] ) || ! wp_is_uuid( $data['id'] ) ) { throw new \Exception( 'Missing id parameter' ); } $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $result = $app->delete_history_item( $data['id'], $context ); if ( is_wp_error( $result ) ) { throw new \Exception( $result->get_error_message() ); } return []; } public function ajax_ai_toggle_favorite_history_item( $data ): array { if ( empty( $data['id'] ) || ! wp_is_uuid( $data['id'] ) ) { throw new \Exception( 'Missing id parameter' ); } $app = $this->get_ai_app(); if ( ! $app->is_connected() ) { throw new \Exception( 'not_connected' ); } $context = $this->get_request_context( $data ); $result = $app->toggle_favorite_history_item( $data['id'], $context ); if ( is_wp_error( $result ) ) { throw new \Exception( $result->get_error_message() ); } return []; } /** * @param mixed $result */ private function throw_on_error( $result ): void { if ( is_wp_error( $result ) ) { wp_send_json_error( [ 'message' => $result->get_error_message(), 'extra_data' => $result->get_error_data(), ] ); } } } ai/preferences.php000064400000005566147207015700010166 0ustar00add_personal_options_settings( $user ); } ); add_action( 'personal_options_update', function ( $user_id ) { $this->update_personal_options_settings( $user_id ); } ); add_action( 'edit_user_profile_update', function ( $user_id ) { $this->update_personal_options_settings( $user_id ); } ); } /** * Determine if AI features are enabled for a user. * * @param int $user_id - User ID. * * @return bool */ public static function is_ai_enabled( $user_id ) { return (bool) User::get_user_option_with_default( static::ENABLE_AI, $user_id, true ); } /** * Add settings to the "Personal Options". * * @param \WP_User $user - User object. * * @return void */ protected function add_personal_options_settings( \WP_User $user ) { if ( ! $this->has_permissions_to_edit_user( $user->ID ) ) { return; } $ai_value = User::get_user_option_with_default( static::ENABLE_AI, $user->ID, '1' ); ?>

has_permissions_to_edit_user( $user_id ) ) { return; } $ai_value = empty( $_POST[ static::ENABLE_AI ] ) ? '0' : '1'; update_user_option( $user_id, static::ENABLE_AI, sanitize_text_field( $ai_value ) ); } /** * Determine if the current user has permission to view/change preferences of a user. * * @param int $user_id * * @return bool */ protected function has_permissions_to_edit_user( $user_id ) { return current_user_can( 'edit_user', $user_id ); } } ai/connect/ai.php000064400000046245147207015700007706 0ustar00ai_request( 'POST', 'status/check', [ 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function get_cached_usage() { $cache_key = 'elementor_ai_usage'; $cache_time = 24 * HOUR_IN_SECONDS; $usage = get_site_transient( $cache_key ); if ( ! $usage ) { $usage = $this->get_usage(); set_site_transient( $cache_key, $usage, $cache_time ); } return $usage; } public function get_remote_config() { return $this->ai_request( 'GET', 'remote-config/config', [ 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function get_remote_frontend_config( $data ) { return $this->ai_request( 'POST', 'remote-config/frontend-config', [ 'client_name' => $data['payload']['client_name'], 'client_version' => $data['payload']['client_version'], 'client_session_id' => $data['payload']['client_session_id'], 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], false, '', 'json' ); } /** * get_file_payload * @param $filename * @param $file_type * @param $file_path * @param $boundary * * @return string */ private function get_file_payload( $filename, $file_type, $file_path, $boundary ) { $name = $filename ?? basename( $file_path ); $mine_type = 'image' === $file_type ? image_type_to_mime_type( exif_imagetype( $file_path ) ) : $file_type; $payload = ''; // Upload the file $payload .= '--' . $boundary; $payload .= "\r\n"; $payload .= 'Content-Disposition: form-data; name="' . esc_attr( $name ) . '"; filename="' . esc_attr( $name ) . '"' . "\r\n"; $payload .= 'Content-Type: ' . $mine_type . "\r\n"; $payload .= "\r\n"; $payload .= file_get_contents( $file_path ); $payload .= "\r\n"; return $payload; } private function get_upload_request_body( $body, $file, $boundary, $file_name = '' ) { $payload = ''; // add all body fields as standard POST fields: foreach ( $body as $name => $value ) { $payload .= '--' . $boundary; $payload .= "\r\n"; $payload .= 'Content-Disposition: form-data; name="' . esc_attr( $name ) . '"' . "\r\n\r\n"; $payload .= $value; $payload .= "\r\n"; } if ( is_array( $file ) ) { foreach ( $file as $key => $file_data ) { $payload .= $this->get_file_payload( $file_data['name'], $file_data['type'], $file_data['path'], $boundary ); } } else { $image_mime = image_type_to_mime_type( exif_imagetype( $file ) ); // @todo: add validation for supported image types if ( empty( $file_name ) ) { $file_name = basename( $file ); } $payload .= $this->get_file_payload( $file_name, $image_mime, $file, $boundary ); } $payload .= '--' . $boundary . '--'; return $payload; } private function ai_request( $method, $endpoint, $body, $file = false, $file_name = '', $format = 'default' ) { $headers = [ 'x-elementor-ai-version' => '2', ]; if ( $file ) { $boundary = wp_generate_password( 24, false ); $body = $this->get_upload_request_body( $body, $file, $boundary, $file_name ); // add content type header $headers['Content-Type'] = 'multipart/form-data; boundary=' . $boundary; } elseif ( 'json' === $format ) { $headers['Content-Type'] = 'application/json'; $body = wp_json_encode( $body ); } return $this->http_request( $method, $endpoint, [ 'timeout' => 100, 'headers' => $headers, 'body' => $body, ], [ 'return_type' => static::HTTP_RETURN_TYPE_ARRAY, 'with_error_data' => true, ] ); } public function set_get_started() { return $this->ai_request( 'POST', 'status/get-started', [ 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function set_status_feedback( $response_id ) { return $this->ai_request( 'POST', 'status/feedback/' . $response_id, [ 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function set_used_gallery_image( $image_id ) { return $this->ai_request( 'POST', 'status/used-gallery-image/' . $image_id, [ 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function get_completion_text( $prompt, $context, $request_ids ) { return $this->ai_request( 'POST', 'text/completion', [ 'prompt' => $prompt, 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], false, '', 'json' ); } public function get_excerpt( $prompt, $context, $request_ids ) { $excerpt_length = apply_filters( 'excerpt_length', 55 ); return $this->ai_request( 'POST', 'text/get-excerpt', [ 'content' => $prompt, 'maxLength' => $excerpt_length, 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], false, '', 'json' ); } /** * get_image_prompt_enhanced * @param $prompt * * @return mixed|\WP_Error */ public function get_image_prompt_enhanced( $prompt, $context, $request_ids ) { return $this->ai_request( 'POST', 'text/enhance-image-prompt', [ 'prompt' => $prompt, 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function get_edit_text( $data, $context, $request_ids ) { return $this->ai_request( 'POST', 'text/edit', [ 'input' => $data['payload']['input'], 'instruction' => $data['payload']['instruction'], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], false, '', 'json' ); } public function get_custom_code( $data, $context, $request_ids ) { return $this->ai_request( 'POST', 'text/custom-code', [ 'prompt' => $data['payload']['prompt'], 'language' => $data['payload']['language'], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], false, '', 'json' ); } public function get_custom_css( $data, $context, $request_ids ) { return $this->ai_request( 'POST', 'text/custom-css', [ 'prompt' => $data['payload']['prompt'], 'html_markup' => $data['payload']['html_markup'], 'element_id' => $data['payload']['element_id'], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], false, '', 'json' ); } /** * get_text_to_image * @param $prompt * @param $prompt_settings * * @return mixed|\WP_Error */ public function get_text_to_image( $data, $context, $request_ids ) { return $this->ai_request( 'POST', 'image/text-to-image', [ self::PROMPT => $data['payload']['prompt'], self::IMAGE_TYPE => $data['payload']['settings'][ self::IMAGE_TYPE ] . '/' . $data['payload']['settings'][ self::STYLE_PRESET ], self::ASPECT_RATIO => $data['payload']['settings'][ self::ASPECT_RATIO ], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], false, '', 'json' ); } /** * get_featured_image * @param $prompt * @param $prompt_settings * * @return mixed|\WP_Error */ public function get_featured_image( $data, $context, $request_ids ) { return $this->ai_request( 'POST', 'image/text-to-image/featured-image', [ self::PROMPT => $data['payload']['prompt'], self::IMAGE_TYPE => $data['payload']['settings'][ self::IMAGE_TYPE ] . '/' . $data['payload']['settings'][ self::STYLE_PRESET ], self::ASPECT_RATIO => $data['payload']['settings'][ self::ASPECT_RATIO ], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], false, '', 'json' ); } /** * get_image_to_image * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image( $image_data, $context, $request_ids ) { $image_file = get_attached_file( $image_data['attachment_id'] ); if ( ! $image_file ) { throw new \Exception( 'Image file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image', [ self::PROMPT => $image_data[ self::PROMPT ], self::IMAGE_TYPE => $image_data['promptSettings'][ self::IMAGE_TYPE ] . '/' . $image_data['promptSettings'][ self::STYLE_PRESET ], self::IMAGE_STRENGTH => $image_data['promptSettings'][ self::IMAGE_STRENGTH ], self::ASPECT_RATIO => $image_data['promptSettings'][ self::ASPECT_RATIO ], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], $image_file, 'image' ); return $result; } /** * get_image_to_image_upscale * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image_upscale( $image_data, $context, $request_ids ) { $image_file = get_attached_file( $image_data['attachment_id'] ); if ( ! $image_file ) { throw new \Exception( 'Image file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image/upscale', [ self::IMAGE_RESOLUTION => $image_data['promptSettings']['upscale_to'], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], $image_file, 'image' ); return $result; } /** * get_image_to_image_remove_background * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image_remove_background( $image_data, $context, $request_ids ) { $image_file = get_attached_file( $image_data['attachment_id'] ); if ( ! $image_file ) { throw new \Exception( 'Image file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image/remove-background', [ 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], $image_file, 'image' ); return $result; } /** * get_image_to_image_remove_text * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image_replace_background( $image_data, $context, $request_ids ) { $image_file = get_attached_file( $image_data['attachment_id'] ); if ( ! $image_file ) { throw new \Exception( 'Image file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image/replace-background', [ self::PROMPT => $image_data[ self::PROMPT ], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], $image_file, 'image' ); return $result; } /** * store_temp_file * used to store a temp file for the AI request and deletes it once the request is done * @param $file_content * @param $file_ext * * @return string */ private function store_temp_file( $file_content, $file_ext = '' ) { $temp_file = str_replace( '.tmp', '', wp_tempnam() . $file_ext ); file_put_contents( $temp_file, $file_content ); // make sure the temp file is deleted on shutdown register_shutdown_function( function () use ( $temp_file ) { if ( file_exists( $temp_file ) ) { unlink( $temp_file ); } } ); return $temp_file; } /** * get_image_to_image_out_painting * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image_out_painting( $image_data, $context, $request_ids ) { $img_content = str_replace( ' ', '+', $image_data['mask'] ); $img_content = substr( $img_content, strpos( $img_content, ',' ) + 1 ); $img_content = base64_decode( $img_content ); $mask_file = $this->store_temp_file( $img_content, '.png' ); if ( ! $mask_file ) { throw new \Exception( 'Expended Image file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image/outpainting', [ 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), 'size' => wp_json_encode( $image_data['size'] ), 'position' => wp_json_encode( $image_data['position'] ), 'image_base64' => $image_data['image_base64'], $image_data['image'], ], [ [ 'name' => 'image', 'type' => 'image', 'path' => $mask_file, ], ] ); return $result; } /** * get_image_to_image_mask * @param $image_data * * @return mixed|\WP_Error * @throws \Exception */ public function get_image_to_image_mask( $image_data, $context, $request_ids ) { $image_file = get_attached_file( $image_data['attachment_id'] ); $mask_file = $this->store_temp_file( $image_data['mask'], '.svg' ); if ( ! $image_file ) { throw new \Exception( 'Image file not found' ); } if ( ! $mask_file ) { throw new \Exception( 'Mask file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image/inpainting', [ self::PROMPT => $image_data[ self::PROMPT ], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), 'image_base64' => $image_data['image_base64'], ], [ [ 'name' => 'image', 'type' => 'image', 'path' => $image_file, ], [ 'name' => 'mask_image', 'type' => 'image/svg+xml', 'path' => $mask_file, ], ] ); return $result; } public function get_image_to_image_mask_cleanup( $image_data, $context, $request_ids ) { $image_file = get_attached_file( $image_data['attachment_id'] ); $mask_file = $this->store_temp_file( $image_data['mask'], '.svg' ); if ( ! $image_file ) { throw new \Exception( 'Image file not found' ); } if ( ! $mask_file ) { throw new \Exception( 'Mask file not found' ); } $result = $this->ai_request( 'POST', 'image/image-to-image/cleanup', [ self::PROMPT => $image_data[ self::PROMPT ], 'context' => wp_json_encode( $context ), 'ids' => $request_ids, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), 'image_base64' => $image_data['image_base64'], ], [ [ 'name' => 'image', 'type' => 'image', 'path' => $image_file, ], [ 'name' => 'mask_image', 'type' => 'image/svg+xml', 'path' => $mask_file, ], ] ); return $result; } public function generate_layout( $data, $context ) { $endpoint = 'generate/layout'; $body = [ 'prompt' => $data['prompt'], 'variationType' => (int) $data['variationType'], 'ids' => $data['ids'], ]; if ( ! empty( $data['prevGeneratedIds'] ) ) { $body['generatedBaseTemplatesIds'] = $data['prevGeneratedIds']; } if ( ! empty( $data['attachments'] ) ) { $attachment = $data['attachments'][0]; switch ( $attachment['type'] ) { case 'json': $endpoint = 'generate/generate-json-variation'; $body['json'] = [ 'type' => 'elementor', 'elements' => [ $attachment['content'] ], 'label' => $attachment['label'], 'source' => $attachment['source'], ]; break; case 'url': $endpoint = 'generate/html-to-elementor'; $html = wp_json_encode( $attachment['content'] ); $body['html'] = $html; $body['htmlFetchedUrl'] = $attachment['label']; break; } } $context['currentContext'] = $data['currentContext']; $context['features'] = [ 'supportedFeatures' => [ 'Taxonomy' ], ]; if ( ElementorUtils::has_pro() ) { $context['features']['subscriptions'] = [ 'Pro' ]; } if ( Plugin::$instance->experiments->is_feature_active( 'container_grid' ) ) { $context['features']['supportedFeatures'][] = 'Grid'; } if ( Plugin::instance()->experiments->get_active_features()['nested-elements'] ) { $context['features']['supportedFeatures'][] = 'Nested'; } if ( Plugin::instance()->experiments->get_active_features()['mega-menu'] ) { $context['features']['supportedFeatures'][] = 'MegaMenu'; } if ( class_exists( 'WC' ) ) { $context['features']['supportedFeatures'][] = 'WooCommerce'; } $metadata = [ 'context' => $context, 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), 'config' => [ 'generate' => [ 'all' => true, ], ], ]; $body = array_merge( $body, $metadata ); // Temp hack for platforms that filters the http_request_args, and it breaks JSON requests. remove_all_filters( 'http_request_args' ); return $this->ai_request( 'POST', $endpoint, $body, false, '', 'json' ); } public function get_layout_prompt_enhanced( $prompt, $enhance_type, $context ) { return $this->ai_request( 'POST', 'generate/enhance-prompt', [ 'prompt' => $prompt, 'enhance_type' => $enhance_type, 'context' => wp_json_encode( $context ), 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ], false, '', 'json' ); } public function get_history_by_type( $type, $page, $limit, $context = [] ) { $endpoint = Module::HISTORY_TYPE_ALL === $type ? 'history' : add_query_arg( [ 'page' => $page, 'limit' => $limit, ], "history/{$type}" ); return $this->ai_request( 'POST', $endpoint, [ 'context' => wp_json_encode( $context ), 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function delete_history_item( $id, $context = [] ) { return $this->ai_request( 'DELETE', 'history/' . $id, [ 'context' => wp_json_encode( $context ), 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } public function toggle_favorite_history_item( $id, $context = [] ) { return $this->ai_request( 'POST', sprintf( 'history/%s/favorite', $id ), [ 'context' => wp_json_encode( $context ), 'api_version' => ELEMENTOR_VERSION, 'site_lang' => get_bloginfo( 'language' ), ] ); } protected function init() {} } compatibility-tag/module.php000064400000003251147207015700012170 0ustar00merge( $this->get_plugins_with_plugin_title_in_their_name() ); } /** * Get all the plugins that has the name of the current plugin in their name. * * @return Collection */ private function get_plugins_with_plugin_title_in_their_name() { return Plugin::$instance->wp ->get_plugins() ->except( [ 'elementor/elementor.php', 'elementor-beta/elementor-beta.php', 'block-builder/block-builder.php', ] ) ->filter( function ( array $data ) { return false !== strpos( strtolower( $data['Name'] ), 'elementor' ); } ); } } compatibility-tag/views/plugin-update-message-compatibility.php000064400000004256147207015700021115 0ustar00
- get_plugin_label() ), $new_version->__toString() // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); ?>

$plugin_data ) : ?> get_plugin_header() ] = esc_html__( 'Unknown', 'elementor' ); } ?>
get_plugin_label() ) ); ?>
get_plugin_header() ] ); ?>
compatibility-tag/compatibility-tag-report.php000064400000010414147207015700015635 0ustar00compatibility_tag_service = $this->_properties['fields']['compatibility_tag_service']; $this->plugin_label = $this->_properties['fields']['plugin_label']; $this->plugin_version = $this->_properties['fields']['plugin_version']; $this->plugins_to_check = $this->_properties['fields']['plugins_to_check']; } /** * The title of the report * * @return string */ public function get_title() { return $this->plugin_label . ' - Compatibility Tag'; } /** * Report fields * * @return string[] */ public function get_fields() { return [ 'report_data' => '', ]; } /** * Report data. * * @return string[] * @throws \Exception */ public function get_report_data() { $compatibility_status = $this->compatibility_tag_service->check( $this->plugin_version, $this->plugins_to_check ); return [ 'value' => $compatibility_status, ]; } public function get_html_report_data() { $compatibility_status = $this->compatibility_tag_service->check( $this->plugin_version, $this->plugins_to_check ); $compatibility_status = $this->get_html_from_compatibility_status( $compatibility_status ); return [ 'value' => $compatibility_status, ]; } public function get_raw_report_data() { $compatibility_status = $this->compatibility_tag_service->check( $this->plugin_version, $this->plugins_to_check ); $compatibility_status = $this->get_raw_from_compatibility_status( $compatibility_status ); return [ 'value' => $compatibility_status, ]; } /** * Merge compatibility status with the plugins data. * * @param array $compatibility_status * * @return Collection */ private function merge_compatibility_status_with_plugins( array $compatibility_status ) { $labels = $this->get_report_labels(); $compatibility_status = ( new Collection( $compatibility_status ) ) ->map( function ( $value ) use ( $labels ) { $status = isset( $labels[ $value ] ) ? $labels[ $value ] : esc_html__( 'Unknown', 'elementor' ); return [ 'compatibility_status' => $status ]; } ); return Plugin::$instance->wp ->get_plugins() ->only( $compatibility_status->keys() ) ->merge_recursive( $compatibility_status ); } /** * Format compatibility status into HTML. * * @param array $compatibility_status * * @return string */ private function get_html_from_compatibility_status( array $compatibility_status ) { return $this->merge_compatibility_status_with_plugins( $compatibility_status ) ->map( function ( array $plugin ) { return " {$plugin['Name']} {$plugin['compatibility_status']} "; } ) ->implode( '' ); } /** * Format compatibility status into raw string. * * @param array $compatibility_status * * @return string */ private function get_raw_from_compatibility_status( array $compatibility_status ) { return PHP_EOL . $this->merge_compatibility_status_with_plugins( $compatibility_status ) ->map( function ( array $plugin ) { return "\t{$plugin['Name']}: {$plugin['compatibility_status']}"; } ) ->implode( PHP_EOL ); } /** * @return array */ private function get_report_labels() { return [ Compatibility_Tag::COMPATIBLE => esc_html__( 'Compatible', 'elementor' ), Compatibility_Tag::INCOMPATIBLE => esc_html__( 'Incompatible', 'elementor' ), Compatibility_Tag::HEADER_NOT_EXISTS => esc_html__( 'Compatibility not specified', 'elementor' ), Compatibility_Tag::INVALID_VERSION => esc_html__( 'Compatibility unknown', 'elementor' ), Compatibility_Tag::PLUGIN_NOT_EXISTS => esc_html__( 'Error', 'elementor' ), ]; } } compatibility-tag/compatibility-tag.php000064400000003707147207015700014333 0ustar00header = $header; } /** * Return if plugins is compatible or not. * * @param Version $version * @param array $plugins_names * * @return array * @throws \Exception */ public function check( Version $version, array $plugins_names ) { return ( new Collection( $plugins_names ) ) ->map_with_keys( function ( $plugin_name ) use ( $version ) { return [ $plugin_name => $this->is_compatible( $version, $plugin_name ) ]; } ) ->all(); } /** * Check single plugin if is compatible or not. * * @param Version $version * @param $plugin_name * * @return string * @throws \Exception */ private function is_compatible( Version $version, $plugin_name ) { $plugins = Plugin::$instance->wp->get_plugins(); if ( ! isset( $plugins[ $plugin_name ] ) ) { return self::PLUGIN_NOT_EXISTS; } $requested_plugin = $plugins[ $plugin_name ]; if ( empty( $requested_plugin[ $this->header ] ) ) { return self::HEADER_NOT_EXISTS; } if ( ! Version::is_valid_version( $requested_plugin[ $this->header ] ) ) { return self::INVALID_VERSION; } if ( $version->compare( '>', $requested_plugin[ $this->header ], Version::PART_MAJOR_2 ) ) { return self::INCOMPATIBLE; } return self::COMPATIBLE; } } compatibility-tag/base-module.php000064400000007505147207015700013106 0ustar00compatibility_tag_service ) { $this->compatibility_tag_service = new Compatibility_Tag( $this->get_plugin_header() ); } return $this->compatibility_tag_service; } /** * Add allowed headers to plugins. * * @param array $headers * @param $compatibility_tag_header * * @return array */ protected function enable_elementor_headers( array $headers, $compatibility_tag_header ) { $headers[] = $compatibility_tag_header; return $headers; } /** * @return Collection */ protected function get_plugins_to_check() { return $this->get_plugins_with_header(); } /** * Append a compatibility message to the update plugin warning. * * @param array $args * * @throws \Exception */ protected function on_plugin_update_message( array $args ) { $new_version = Version::create_from_string( $args['new_version'] ); if ( $new_version->compare( '=', $args['Version'], Version::PART_MAJOR_2 ) ) { return; } $plugins = $this->get_plugins_to_check(); $plugins_compatibility = $this->get_compatibility_tag_service()->check( $new_version, $plugins->keys() ); $plugins = $plugins->filter( function ( $data, $plugin_name ) use ( $plugins_compatibility ) { return Compatibility_Tag::COMPATIBLE !== $plugins_compatibility[ $plugin_name ]; } ); if ( $plugins->is_empty() ) { return; } include __DIR__ . '/views/plugin-update-message-compatibility.php'; } /** * Get all plugins with specific header. * * @return Collection */ private function get_plugins_with_header() { return Plugin::$instance->wp ->get_plugins() ->filter( function ( array $plugin ) { return ! empty( $plugin[ $this->get_plugin_header() ] ); } ); } /** * @return string */ abstract protected function get_plugin_header(); /** * @return string */ abstract protected function get_plugin_label(); /** * @return string */ abstract protected function get_plugin_name(); /** * @return string */ abstract protected function get_plugin_version(); /** * Base_Module constructor. * * @throws \Exception */ public function __construct() { add_filter( 'extra_plugin_headers', function ( array $headers ) { return $this->enable_elementor_headers( $headers, $this->get_plugin_header() ); } ); add_action( 'in_plugin_update_message-' . $this->get_plugin_name(), function ( array $args ) { $this->on_plugin_update_message( $args ); }, 11 /* After the warning message for backup */ ); add_action( 'elementor/system_info/get_allowed_reports', function () { $plugin_short_name = basename( $this->get_plugin_name(), '.php' ); System_Info::add_report( "{$plugin_short_name}_compatibility", [ 'file_name' => __DIR__ . '/compatibility-tag-report.php', 'class_name' => __NAMESPACE__ . '\Compatibility_Tag_Report', 'fields' => [ 'compatibility_tag_service' => $this->get_compatibility_tag_service(), 'plugin_label' => $this->get_plugin_label(), 'plugin_version' => Version::create_from_string( $this->get_plugin_version() ), 'plugins_to_check' => $this->get_plugins_to_check() ->only( get_option( 'active_plugins' ) ) ->keys(), ], ] ); } ); } } apps/module.php000064400000007506147207015700007520 0ustar00register( static::PAGE_ID, new Admin_Menu_Apps() ); }, 115 ); add_action( 'elementor/admin/menu/after_register', function ( Admin_Menu_Manager $admin_menu, array $hooks ) { if ( ! empty( $hooks[ static::PAGE_ID ] ) ) { add_action( "admin_print_scripts-{$hooks[ static::PAGE_ID ]}", [ $this, 'enqueue_assets' ] ); } }, 10, 2 ); add_filter( 'elementor/finder/categories', function( array $categories ) { $categories['site']['items']['apps'] = [ 'title' => esc_html__( 'Add-ons', 'elementor' ), 'url' => admin_url( 'admin.php?page=' . static::PAGE_ID ), 'icon' => 'apps', 'keywords' => [ 'apps', 'addon', 'plugin', 'extension', 'integration' ], ]; return $categories; } ); // Add the Elementor Apps link to the plugin install action links. add_filter( 'install_plugins_tabs', [ $this, 'add_elementor_plugin_install_action_link' ] ); add_action( 'install_plugins_pre_elementor', [ $this, 'maybe_open_elementor_tab' ] ); add_action( 'admin_print_styles-plugin-install.php', [ $this, 'add_plugins_page_styles' ] ); } public function enqueue_assets() { add_filter( 'admin_body_class', [ $this, 'body_status_classes' ] ); wp_enqueue_style( 'elementor-apps', $this->get_css_assets_url( 'modules/apps/admin' ), [], ELEMENTOR_VERSION ); } public function body_status_classes( $admin_body_classes ) { $admin_body_classes .= ' elementor-apps-page'; return $admin_body_classes; } public function add_elementor_plugin_install_action_link( $tabs ) { $tabs['elementor'] = esc_html__( 'For Elementor', 'elementor' ); return $tabs; } public function maybe_open_elementor_tab() { if ( ! isset( $_GET['tab'] ) || 'elementor' !== $_GET['tab'] ) { return; } $elementor_url = add_query_arg( [ 'page' => static::PAGE_ID, 'tab' => 'elementor', 'ref' => 'plugins', ], admin_url( 'admin.php' ) ); wp_safe_redirect( $elementor_url ); exit; } public function add_plugins_page_styles() { ?> ' . esc_html__( 'New! Popular Add-ons', 'elementor' ) . ''; $pointer_content .= '

' . esc_html__( 'Discover our collection of plugins and add-ons carefully selected to enhance your Elementor website and unleash your creativity.', 'elementor' ) . '

'; $pointer_content .= sprintf( '

%s

', admin_url( 'admin.php?page=' . Module::PAGE_ID ), esc_html__( 'Explore Add-ons', 'elementor' ) ) ?> =' ); } } apps/admin-apps-page.php000064400000014354147207015700011175 0ustar00

*

is_plugin_active( $app['file_path'] ) ) { return null; } if ( self::$plugin_status_adapter->is_plugin_installed( $app['file_path'] ) ) { if ( current_user_can( 'activate_plugins' ) ) { $app['action_label'] = esc_html__( 'Activate', 'elementor' ); $app['action_url'] = self::$plugin_status_adapter->get_activate_plugin_url( $app['file_path'] ); } else { $app['action_label'] = esc_html__( 'Cannot Activate', 'elementor' ); $app['action_url'] = '#'; } } else { if ( current_user_can( 'install_plugins' ) ) { $app['action_label'] = esc_html__( 'Install', 'elementor' ); $app['action_url'] = self::$plugin_status_adapter->get_install_plugin_url( $app['file_path'] ); } else { $app['action_label'] = esc_html__( 'Cannot Install', 'elementor' ); $app['action_url'] = '#'; } } return $app; } private static function is_ecom_app( $app ) { return isset( $app['type'] ) && 'ecom' === $app['type']; } private static function filter_ecom_app( $app ) { if ( self::$wordpress_adapter->is_plugin_active( $app['file_path'] ) ) { return null; } if ( ! self::$plugin_status_adapter->is_plugin_installed( $app['file_path'] ) ) { return $app; } if ( current_user_can( 'activate_plugins' ) ) { $app['action_label'] = esc_html__( 'Activate', 'elementor' ); $app['action_url'] = self::$plugin_status_adapter->get_activate_plugin_url( $app['file_path'] ); } else { $app['action_label'] = esc_html__( 'Cannot Activate', 'elementor' ); $app['action_url'] = '#'; } $app['target'] = '_self'; return $app; } private static function get_images_url() { return ELEMENTOR_URL . 'modules/apps/images/'; } private static function is_elementor_pro_installed() { return defined( 'ELEMENTOR_PRO_VERSION' ); } private static function render_plugin_item( $plugin ) { ?>
>
<?php echo esc_attr( $plugin['name'] ); ?>

register_experiments(); $this->register_shortcode(); if ( ! Plugin::$instance->experiments->is_feature_active( 'e_element_cache' ) ) { return; } $this->add_advanced_tab_actions(); if ( is_admin() ) { add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 100 ); } $this->clear_cache_on_site_changed(); } private function register_experiments() { Plugin::$instance->experiments->add_feature( [ 'name' => 'e_element_cache', 'title' => esc_html__( 'Element Caching', 'elementor' ), 'tag' => esc_html__( 'Performance', 'elementor' ), 'description' => esc_html__( 'Elements caching reduces loading times by serving up a copy of an element instead of rendering it fresh every time the page is loaded. When active, Elementor will determine which elements can benefit from static loading - but you can override this.', 'elementor' ), 'release_status' => ExperimentsManager::RELEASE_STATUS_BETA, 'default' => ExperimentsManager::STATE_INACTIVE, 'new_site' => [ 'default_active' => true, 'minimum_installation_version' => '3.23.0', ], 'generator_tag' => true, ] ); } private function register_shortcode() { add_shortcode( 'elementor-element', function ( $atts ) { if ( empty( $atts['data'] ) ) { return ''; } $widget_data = json_decode( base64_decode( $atts['data'] ), true ); if ( empty( $widget_data ) || ! is_array( $widget_data ) ) { return ''; } ob_start(); $element = Plugin::$instance->elements_manager->create_element_instance( $widget_data ); if ( $element ) { $element->print_element(); } return ob_get_clean(); } ); } private function add_advanced_tab_actions() { $hooks = array( 'elementor/element/common/_section_style/after_section_end' => '_css_classes', // Widgets ); foreach ( $hooks as $hook => $injection_position ) { add_action( $hook, function( $element, $args ) use ( $injection_position ) { $this->add_control_to_advanced_tab( $element, $args, $injection_position ); }, 10, 2 ); } } private function add_control_to_advanced_tab( Element_Base $element, $args, $injection_position ) { $element->start_injection( [ 'of' => $injection_position, ] ); $control_data = [ 'label' => esc_html__( 'Cache Settings', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => '', 'options' => [ '' => esc_html__( 'Default', 'elementor' ), 'yes' => esc_html__( 'Inactive', 'elementor' ), 'no' => esc_html__( 'Active', 'elementor' ), ], ]; $element->add_control( '_element_cache', $control_data ); $element->end_injection(); } public function register_admin_fields( Settings $settings ) { $settings->add_field( Settings::TAB_PERFORMANCE, Settings::TAB_PERFORMANCE, 'element_cache_ttl', [ 'label' => esc_html__( 'Element Cache Expiration', 'elementor' ), 'field_args' => [ 'class' => 'elementor-element-cache-ttl', 'type' => 'select', 'std' => '24', 'options' => [ '1' => esc_html__( '1 Hour', 'elementor' ), '6' => esc_html__( '6 Hours', 'elementor' ), '12' => esc_html__( '12 Hours', 'elementor' ), '24' => esc_html__( '1 Day', 'elementor' ), '72' => esc_html__( '3 Days', 'elementor' ), '168' => esc_html__( '1 Week', 'elementor' ), '336' => esc_html__( '2 Weeks', 'elementor' ), '720' => esc_html__( '1 Month', 'elementor' ), '8760' => esc_html__( '1 Year', 'elementor' ), ], 'desc' => esc_html__( 'Specify the duration for which data is stored in the cache. Elements caching speeds up loading by serving pre-rendered copies of elements, rather than rendering them fresh each time. This control ensures efficient performance and up-to-date content.', 'elementor' ), ], ] ); } private function clear_cache_on_site_changed() { add_action( 'activated_plugin', [ $this, 'clear_cache' ] ); add_action( 'deactivated_plugin', [ $this, 'clear_cache' ] ); add_action( 'switch_theme', [ $this, 'clear_cache' ] ); add_action( 'upgrader_process_complete', [ $this, 'clear_cache' ] ); } public function clear_cache() { Plugin::$instance->files_manager->clear_cache(); } } wp-cli/module.php000064400000002451147207015700007742 0ustar00register_logger( 'cli', 'Elementor\Modules\WpCli\Cli_Logger' ); $logger->set_default_logger( 'cli' ); } public function init_common() { Plugin::$instance->init_common(); } /** * * @since 2.1.0 * @access public */ public function __construct() { add_action( 'cli_init', [ $this, 'init_common' ] ); add_action( 'elementor/loggers/register', [ $this, 'register_cli_logger' ] ); \WP_CLI::add_command( 'elementor', '\Elementor\Modules\WpCli\Command' ); \WP_CLI::add_command( 'elementor update', '\Elementor\Modules\WpCli\Update' ); \WP_CLI::add_command( 'elementor library', '\Elementor\Modules\WpCli\Library' ); } } wp-cli/command.php000064400000010116147207015700010070 0ustar00 'ids', 'number' => 0, ] ); foreach ( $blog_ids as $blog_id ) { switch_to_blog( $blog_id ); Plugin::$instance->files_manager->clear_cache(); \WP_CLI::success( 'Flushed the Elementor CSS Cache for site - ' . get_option( 'home' ) ); restore_current_blog(); } } else { Plugin::$instance->files_manager->clear_cache(); \WP_CLI::success( 'Flushed the Elementor CSS Cache' ); } } /** * Print system info powered by Elementor * * ## EXAMPLES * * 1. wp elementor system-info * - This will print the System Info in JSON format * * @since 3.0.11 * @access public * @alias system-info */ public function system_info() { echo wp_json_encode( \Elementor\Tracker::get_tracking_data() ); } /** * Replace old URLs with new URLs in all Elementor pages. * * [--force] * Suppress error messages. instead, return "0 database rows affected.". * * ## EXAMPLES * * 1. wp elementor replace-urls * - This will replace all URLs with the URL. * * 2. wp elementor replace-urls --force * - This will replace all URLs with the URL without throw errors. * * @access public * @alias replace-urls */ public function replace_urls( $args, $assoc_args ) { if ( empty( $args[0] ) ) { \WP_CLI::error( 'Please set the `old` URL' ); } if ( empty( $args[1] ) ) { \WP_CLI::error( 'Please set the `new` URL' ); } try { $results = Utils::replace_urls( $args[0], $args[1] ); \WP_CLI::success( $results ); } catch ( \Exception $e ) { if ( isset( $assoc_args['force'] ) ) { \WP_CLI::success( '0 database rows affected.' ); } else { \WP_CLI::error( $e->getMessage() ); } } } /** * Sync Elementor Library. * * ## EXAMPLES * * 1. wp elementor sync-library * - This will sync the library with Elementor cloud library. * * @since 2.1.0 * @access public * @alias sync-library */ public function sync_library( $args, $assoc_args ) { // TODO: // \WP_CLI::warning( 'command is deprecated since 2.8.0 Please use: wp elementor library sync' ); $data = Api::get_library_data( true ); if ( empty( $data ) ) { \WP_CLI::error( 'Cannot sync library.' ); } \WP_CLI::success( 'Library has been synced.' ); } /** * Import template files to the Library. * * ## EXAMPLES * * 1. wp elementor import-library * - This will import a file or a zip of multiple files to the library. * * @since 2.1.0 * @access public * @alias import-library */ public function import_library( $args, $assoc_args ) { // TODO: // \WP_CLI::warning( 'command is deprecated since 2.8.0 Please use: wp elementor library import' ); if ( empty( $args[0] ) ) { \WP_CLI::error( 'Please set file path.' ); } /** @var Source_Local $source */ $source = Plugin::$instance->templates_manager->get_source( 'local' ); $imported_items = $source->import_template( basename( $args[0] ), $args[0] ); if ( is_wp_error( $imported_items ) ) { \WP_CLI::error( $imported_items->get_error_message() ); } \WP_CLI::success( count( $imported_items ) . ' item(s) has been imported.' ); } } wp-cli/library.php000064400000015473147207015700010131 0ustar00 'ids', 'number' => 0, ] ); foreach ( $blog_ids as $blog_id ) { switch_to_blog( $blog_id ); \WP_CLI::line( 'Site #' . $blog_id . ' - ' . get_option( 'blogname' ) ); $this->do_sync( isset( $assoc_args['force'] ) ); \WP_CLI::success( 'Done! - ' . get_option( 'home' ) ); restore_current_blog(); } } else { $this->do_sync( isset( $assoc_args['force'] ) ); \WP_CLI::success( 'Done!' ); } } /** * Import template files to the Library. * * [--returnType] * Forms of output. Possible values are 'ids', 'info'. * if this parameter won't be specified, the import info will be output. * * ## EXAMPLES * * 1. wp elementor library import * - This will import a file or a zip of multiple files to the library. * - file-path can be a path or url. * * 2. wp elementor library import --returnType=info,ids * * @param $args * @param $assoc_args * * @since 2.8.0 * @access public */ public function import( $args, $assoc_args ) { if ( empty( $args[0] ) ) { \WP_CLI::error( 'Please set file path.' ); } $file = $args[0]; $imported_items_ids = []; $return_type = \WP_CLI\Utils\get_flag_value( $assoc_args, 'returnType', 'info' ); /** @var Source_Local $source */ $source = Plugin::$instance->templates_manager->get_source( 'local' ); if ( filter_var( $file, FILTER_VALIDATE_URL ) ) { $tmp_path = download_url( $file ); if ( is_wp_error( $tmp_path ) ) { \WP_CLI::error( $tmp_path->get_error_message() ); } $file = $tmp_path; } $imported_items = $source->import_template( basename( $file ), $file ); if ( is_wp_error( $imported_items ) ) { \WP_CLI::error( $imported_items->get_error_message() ); } foreach ( $imported_items as $item ) { $imported_items_ids[] = $item['template_id']; } $imported_items_ids = implode( ',', $imported_items_ids ); if ( 'ids' === $return_type ) { \WP_CLI::line( $imported_items_ids ); } else { \WP_CLI::success( count( $imported_items ) . ' item(s) has been imported.' ); } if ( isset( $tmp_path ) ) { // Remove the temporary file, now that we're done with it. Plugin::$instance->uploads_manager->remove_file_or_dir( $file ); } } /** * Import all template files from a directory. * * ## EXAMPLES * * 1. wp elementor library import-dir * - This will import all JSON files from * * @param $args * * @since 3.4.7 * @access public * @alias import-dir */ public function import_dir( $args ) { if ( empty( $args[0] ) ) { \WP_CLI::error( 'Please set dir path.' ); } $dir = $args[0]; if ( ! file_exists( $dir ) ) { \WP_CLI::error( "Dir `{$dir}` not found." ); } $files = glob( $dir . '/*.json' ); if ( empty( $files ) ) { \WP_CLI::error( 'Files not found.' ); } /** @var Source_Local $source */ $source = Plugin::$instance->templates_manager->get_source( 'local' ); $succeed = []; $errors = []; foreach ( $files as $file ) { $basename = basename( $file ); if ( ! file_exists( $file ) ) { $errors[ $basename ] = $file . ' file not found.'; continue; } $imported_items = $source->import_template( $basename, $file ); if ( is_wp_error( $imported_items ) ) { $errors[ $basename ] = $imported_items->get_error_message(); } else { $succeed[ $basename ] = true; } } $succeed_message = count( $succeed ) . ' item(s) has been imported.'; if ( ! empty( $errors ) ) { $error_message = var_export( $errors, 1 ); if ( ! empty( $succeed ) ) { $error_message = $succeed_message . ' ' . count( $errors ) . ' has errors: ' . $error_message; } \WP_CLI::error( $error_message ); } \WP_CLI::success( $succeed_message ); } /** * Connect site to Elementor Library. * (Network is not supported) * * --user * The user to connect * * --token * A connect token from Elementor Account Dashboard. * * ## EXAMPLES * * 1. wp elementor library connect --user=admin --token= * - This will connect the admin to Elementor library. * * @param $args * @param $assoc_args * * @since 2.8.0 * @access public */ public function connect( $args, $assoc_args ) { if ( ! get_current_user_id() ) { \WP_CLI::error( 'Please set user to connect (--user=).' ); } if ( empty( $assoc_args['token'] ) ) { \WP_CLI::error( 'Please set connect token.' ); } $_REQUEST['mode'] = 'cli'; $_REQUEST['token'] = $assoc_args['token']; $app = $this->get_library_app(); $app->set_auth_mode( 'cli' ); $app->action_authorize(); $app->action_get_token(); } /** * Disconnect site from Elementor Library. * * --user * The user to disconnect * * ## EXAMPLES * * 1. wp elementor library disconnect --user=admin * - This will disconnect the admin from Elementor library. * * @param $args * @param $assoc_args * * @since 2.8.0 * @access public */ public function disconnect() { if ( ! get_current_user_id() ) { \WP_CLI::error( 'Please set user to connect (--user=).' ); } $_REQUEST['mode'] = 'cli'; $this->get_library_app()->action_disconnect(); } private function do_sync() { $data = Api::get_library_data( true ); if ( empty( $data ) ) { \WP_CLI::error( 'Cannot sync library.' ); } } /** * @return \Elementor\Core\Common\Modules\Connect\Apps\Library */ private function get_library_app() { $connect = Plugin::$instance->common->get_component( 'connect' ); $app = $connect->get_app( 'library' ); // Before init. if ( ! $app ) { $connect->init(); $app = $connect->get_app( 'library' ); } return $app; } } wp-cli/cli-logger.php000064400000001154147207015700010500 0ustar00format( 'raw' ); switch ( $item->type ) { case self::LEVEL_WARNING: \WP_CLI::warning( $message ); break; case self::LEVEL_ERROR: \WP_CLI::error( $message, false ); break; default: \WP_CLI::log( $message ); break; } parent::save_log( $item ); } } wp-cli/update.php000064400000005054147207015700007741 0ustar00 'ids', 'number' => 0, ] ); foreach ( $blog_ids as $blog_id ) { switch_to_blog( $blog_id ); \WP_CLI::line( 'Site #' . $blog_id . ' - ' . get_option( 'blogname' ) ); $this->do_db_upgrade( $assoc_args ); \WP_CLI::success( 'Done! - ' . get_option( 'home' ) ); restore_current_blog(); } } else { $this->do_db_upgrade( $assoc_args ); } } protected function get_update_db_manager_class() { return '\Elementor\Core\Upgrade\Manager'; } protected function do_db_upgrade( $assoc_args ) { $manager_class = $this->get_update_db_manager_class(); /** @var \Elementor\Core\Upgrade\Manager $manager */ $manager = new $manager_class(); $updater = $manager->get_task_runner(); if ( $updater->is_process_locked() && empty( $assoc_args['force'] ) ) { \WP_CLI::warning( 'Oops! Process is already running. Use --force to force run.' ); return; } if ( ! $manager->should_upgrade() ) { \WP_CLI::success( 'The DB is already updated!' ); return; } $callbacks = $manager->get_upgrade_callbacks(); $did_tasks = false; if ( ! empty( $callbacks ) ) { Plugin::$instance->logger->get_logger()->info( 'Update DB has been started', [ 'meta' => [ 'plugin' => $manager->get_plugin_label(), 'from' => $manager->get_current_version(), 'to' => $manager->get_new_version(), ], ] ); $updater->handle_immediately( $callbacks ); $did_tasks = true; } $manager->on_runner_complete( $did_tasks ); \WP_CLI::success( count( $callbacks ) . ' updates(s) has been applied.' ); } } shapes/module.php000064400000003554147207015700010037 0ustar00get_css_assets_url( 'widget-shapes', null, true, true ), [ 'elementor-frontend' ], ELEMENTOR_VERSION ); } /** * Return a translated user-friendly list of the available SVG shapes. * * @param bool $add_custom Determine if the output should include the `Custom` option. * * @return array List of paths. */ public static function get_paths( $add_custom = true ) { $paths = [ 'wave' => esc_html__( 'Wave', 'elementor' ), 'arc' => esc_html__( 'Arc', 'elementor' ), 'circle' => esc_html__( 'Circle', 'elementor' ), 'line' => esc_html__( 'Line', 'elementor' ), 'oval' => esc_html__( 'Oval', 'elementor' ), 'spiral' => esc_html__( 'Spiral', 'elementor' ), ]; if ( $add_custom ) { $paths['custom'] = esc_html__( 'Custom', 'elementor' ); } return $paths; } /** * Get an SVG Path URL from the pre-defined ones. * * @param string $path - Path name. * * @return string */ public static function get_path_url( $path ) { return ELEMENTOR_ASSETS_URL . 'svg-paths/' . $path . '.svg'; } /** * Get the module's associated widgets. * * @return string[] */ protected function get_widgets() { return [ 'TextPath', ]; } /** * Retrieve the module name. * * @return string */ public function get_name() { return 'shapes'; } } shapes/widgets/text-path.php000064400000035124147207015700012134 0ustar00start_controls_section( 'section_content_text_path', [ 'label' => esc_html__( 'Text Path', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); $this->add_control( 'text', [ 'label' => esc_html__( 'Text', 'elementor' ), 'type' => Controls_Manager::TEXT, 'label_block' => true, 'default' => esc_html__( 'Add Your Curvy Text Here', 'elementor' ), 'frontend_available' => true, 'render_type' => 'none', 'dynamic' => [ 'active' => true, ], ] ); $this->add_control( 'path', [ 'label' => esc_html__( 'Path Type', 'elementor' ), 'type' => Controls_Manager::SELECT, 'options' => Shapes_Module::get_paths(), 'default' => 'wave', ] ); $this->add_control( 'custom_path', [ 'label' => esc_html__( 'SVG', 'elementor' ), 'type' => Controls_Manager::MEDIA, 'media_types' => [ 'svg', ], 'condition' => [ 'path' => 'custom', ], 'dynamic' => [ 'active' => true, ], 'description' => sprintf( '%1$s %2$s', esc_html__( 'Want to create custom text paths with SVG?', 'elementor' ), esc_html__( 'Learn more', 'elementor' ) ), ] ); $this->add_control( 'link', [ 'label' => esc_html__( 'Link', 'elementor' ), 'type' => Controls_Manager::URL, 'label_block' => true, 'dynamic' => [ 'active' => true, ], 'placeholder' => esc_html__( 'Paste URL or type', 'elementor' ), 'frontend_available' => true, ] ); $this->add_responsive_control( 'align', [ 'label' => esc_html__( 'Alignment', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'default' => '', 'options' => [ 'left' => [ 'title' => esc_html__( 'Left', 'elementor' ), 'icon' => 'eicon-text-align-left', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-text-align-center', ], 'right' => [ 'title' => esc_html__( 'Right', 'elementor' ), 'icon' => 'eicon-text-align-right', ], ], 'selectors' => [ '{{WRAPPER}}' => '--alignment: {{VALUE}}', ], 'frontend_available' => true, ] ); $this->add_control( 'text_path_direction', [ 'label' => esc_html__( 'Text Direction', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => '', 'options' => [ '' => esc_html__( 'Default', 'elementor' ), 'rtl' => esc_html__( 'RTL', 'elementor' ), 'ltr' => esc_html__( 'LTR', 'elementor' ), ], 'selectors' => [ '{{WRAPPER}}' => '--direction: {{VALUE}}', ], 'frontend_available' => true, ] ); $this->add_control( 'show_path', [ 'label' => esc_html__( 'Show Path', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'On', 'elementor' ), 'label_off' => esc_html__( 'Off', 'elementor' ), 'return_value' => self::DEFAULT_PATH_FILL, 'separator' => 'before', 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--path-stroke: {{VALUE}}; --path-fill: transparent;', ], ] ); $this->end_controls_section(); } /** * Register style controls under style tab. */ protected function register_style_tab() { /** * Text Path styling section. */ $this->start_controls_section( 'section_style_text_path', [ 'label' => esc_html__( 'Text Path', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_responsive_control( 'size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'range' => [ '%' => [ 'min' => 0, 'max' => 100, 'step' => 10, ], 'px' => [ 'max' => 800, 'step' => 50, ], ], 'default' => [ 'size' => 500, ], 'tablet_default' => [ 'size' => 500, ], 'mobile_default' => [ 'size' => 500, ], 'selectors' => [ '{{WRAPPER}}' => '--width: {{SIZE}}{{UNIT}};', ], ] ); $this->add_responsive_control( 'rotation', [ 'label' => esc_html__( 'Rotate', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'deg', 'grad', 'rad', 'turn', 'custom' ], 'default' => [ 'unit' => 'deg', ], 'tablet_default' => [ 'unit' => 'deg', ], 'mobile_default' => [ 'unit' => 'deg', ], 'selectors' => [ '{{WRAPPER}}' => '--rotate: {{SIZE}}{{UNIT}};', ], ] ); $this->add_control( 'text_heading', [ 'label' => esc_html__( 'Text', 'elementor' ), 'type' => Controls_Manager::HEADING, ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'text_typography', 'selector' => '{{WRAPPER}}', 'global' => [ 'default' => Global_Typography::TYPOGRAPHY_TEXT, ], 'fields_options' => [ 'font_size' => [ 'default' => [ 'size' => '20', 'unit' => 'px', ], 'size_units' => [ 'px' ], ], // Text decoration isn't an inherited property, so it's required to explicitly // target the specific `textPath` element. 'text_decoration' => [ 'selectors' => [ '{{WRAPPER}} textPath' => 'text-decoration: {{VALUE}};', ], ], ], ] ); $this->add_group_control( Group_Control_Text_Stroke::get_type(), [ 'name' => 'text_stroke', 'selector' => '{{WRAPPER}} textPath', ] ); $this->add_responsive_control( 'word_spacing', [ 'label' => esc_html__( 'Word Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'min' => -20, 'max' => 20, ], 'em' => [ 'min' => -1, 'max' => 1, ], 'rem' => [ 'min' => -1, 'max' => 1, ], ], 'default' => [ 'size' => '', ], 'tablet_default' => [ 'size' => '', ], 'mobile_default' => [ 'size' => '', ], 'selectors' => [ '{{WRAPPER}}' => '--word-spacing: {{SIZE}}{{UNIT}};', ], ] ); $this->add_control( 'start_point', [ 'label' => esc_html__( 'Starting Point', 'elementor' ) . ' (%)', 'type' => Controls_Manager::SLIDER, 'size_units' => [ '%' ], 'range' => [ 'px' => [ 'min' => -100, 'max' => 100, 'step' => 1, ], ], 'default' => [ 'unit' => '%', 'size' => 0, ], 'frontend_available' => true, 'render_type' => 'none', ] ); $this->start_controls_tabs( 'text_style' ); /** * Normal tab. */ $this->start_controls_tab( 'text_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'text_color_normal', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--text-color: {{VALUE}};', ], ] ); $this->end_controls_tab(); /** * Hover tab. */ $this->start_controls_tab( 'text_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'text_color_hover', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--text-color-hover: {{VALUE}};', ], ] ); $this->add_control( 'hover_animation', [ 'label' => esc_html__( 'Hover Animation', 'elementor' ), 'type' => Controls_Manager::HOVER_ANIMATION, ] ); $this->add_control( 'hover_transition', [ 'label' => esc_html__( 'Transition Duration', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 's', 'ms', 'custom' ], 'default' => [ 'unit' => 's', 'size' => 0.3, ], 'selectors' => [ '{{WRAPPER}}' => '--transition: {{SIZE}}{{UNIT}}', ], ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->end_controls_section(); /** * Path styling section. */ $this->start_controls_section( 'section_style_path', [ 'label' => esc_html__( 'Path', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, 'condition' => [ 'show_path!' => '', ], ] ); $this->start_controls_tabs( 'path_style' ); /** * Normal tab. */ $this->start_controls_tab( 'path_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'path_fill_normal', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--path-fill: {{VALUE}};', ], ] ); $this->add_control( 'stroke_heading_normal', [ 'label' => esc_html__( 'Stroke', 'elementor' ), 'type' => Controls_Manager::HEADING, ] ); $this->add_control( 'stroke_color_normal', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => self::DEFAULT_PATH_FILL, 'selectors' => [ '{{WRAPPER}}' => '--stroke-color: {{VALUE}};', ], ] ); $this->add_control( 'stroke_width_normal', [ 'label' => esc_html__( 'Width', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'default' => [ 'size' => 1, ], 'range' => [ 'px' => [ 'min' => 1, 'max' => 20, ], 'em' => [ 'max' => 2, ], 'rem' => [ 'max' => 2, ], ], 'selectors' => [ '{{WRAPPER}}' => '--stroke-width: {{SIZE}}{{UNIT}}', ], ] ); $this->end_controls_tab(); /** * Hover tab. */ $this->start_controls_tab( 'path_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'path_fill_hover', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--path-fill-hover: {{VALUE}};', ], ] ); $this->add_control( 'stroke_heading_hover', [ 'label' => esc_html__( 'Stroke', 'elementor' ), 'type' => Controls_Manager::HEADING, ] ); $this->add_control( 'stroke_color_hover', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '', 'selectors' => [ '{{WRAPPER}}' => '--stroke-color-hover: {{VALUE}};', ], ] ); $this->add_control( 'stroke_width_hover', [ 'label' => esc_html__( 'Width', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'default' => [ 'size' => '', ], 'range' => [ 'px' => [ 'min' => 1, 'max' => 20, ], 'em' => [ 'max' => 2, ], 'rem' => [ 'max' => 2, ], ], 'selectors' => [ '{{WRAPPER}}' => '--stroke-width-hover: {{SIZE}}{{UNIT}}', ], ] ); $this->add_control( 'stroke_transition', [ 'label' => esc_html__( 'Transition Duration', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 's', 'ms', 'custom' ], 'default' => [ 'unit' => 's', 'size' => 0.3, ], 'selectors' => [ '{{WRAPPER}}' => '--stroke-transition: {{SIZE}}{{UNIT}}', ], ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->end_controls_section(); } /** * Register Text Path widget controls. * * Adds different input fields to allow the user to change and customize the widget settings. * * @access protected */ protected function register_controls() { $this->register_content_tab(); $this->register_style_tab(); } /** * Render Text Path widget output on the frontend. * * Written in PHP and used to generate the final HTML. * * @access protected */ protected function render() { $settings = $this->get_settings_for_display(); // Get the path URL. $path_url = ( 'custom' === $settings['path'] ) ? wp_get_attachment_url( $settings['custom_path']['id'] ) : Shapes_Module::get_path_url( $settings['path'] ); // Remove the HTTP protocol to prevent Mixed Content error. $path_url = preg_replace( '/^https?:/i', '', $path_url ); // Add Text Path attributes. $this->add_render_attribute( 'text_path', [ 'class' => 'e-text-path', 'data-text' => htmlentities( esc_attr( $settings['text'] ) ), 'data-url' => esc_url( $path_url ), 'data-link-url' => esc_url( $settings['link']['url'] ?? '' ), ] ); // Add hover animation. if ( ! empty( $settings['hover_animation'] ) ) { $this->add_render_attribute( 'text_path', 'class', 'elementor-animation-' . $settings['hover_animation'] ); } // Render. ?>
print_render_attribute_string( 'text_path' ); ?>>
register_experiment(); } public function get_name() { return 'editor-events'; } public static function get_editor_events_config() { $can_send_events = defined( 'ELEMENTOR_EDITOR_EVENTS_MIXPANEL_TOKEN' ) && Tracker::is_allow_track() && Plugin::$instance->experiments->is_feature_active( self::EXPERIMENT_NAME ); $settings = [ 'can_send_events' => $can_send_events, 'elementor_version' => ELEMENTOR_VERSION, 'site_url' => hash( 'sha256', get_site_url() ), 'wp_version' => get_bloginfo( 'version' ), 'user_agent' => esc_html( Utils::get_super_global_value( $_SERVER, 'HTTP_USER_AGENT' ) ), 'site_language' => get_locale(), 'site_key' => get_option( Base_App::OPTION_CONNECT_SITE_KEY ), 'subscription_id' => null, 'token' => defined( 'ELEMENTOR_EDITOR_EVENTS_MIXPANEL_TOKEN' ) ? ELEMENTOR_EDITOR_EVENTS_MIXPANEL_TOKEN : '', ]; return $settings; } private function register_experiment() { Plugin::$instance->experiments->add_feature( [ 'name' => static::EXPERIMENT_NAME, 'title' => esc_html__( 'Elementor Editor Events', 'elementor' ), 'description' => esc_html__( 'Editor events processing', 'elementor' ), 'hidden' => true, 'default' => Experiments_Manager::STATE_INACTIVE, ] ); } } floating-buttons/module.php000064400000041122147207015700012044 0ustar00experiments->is_feature_active( 'container' ); } public static function get_floating_elements_types() { return [ 'floating-buttons' => esc_html__( 'Floating Buttons', 'elementor' ), 'floating-bars' => esc_html__( 'Floating Bars', 'elementor' ), ]; } // TODO: This is a hidden experiment which needs to remain enabled like this until 3.26 for pro compatibility. public static function get_experimental_data() { return [ 'name' => self::EXPERIMENT_NAME, 'title' => esc_html__( 'Floating Buttons', 'elementor' ), 'hidden' => true, 'default' => Manager::STATE_ACTIVE, 'release_status' => Manager::RELEASE_STATUS_STABLE, 'mutable' => false, ]; } public function get_name(): string { return static::EXPERIMENT_NAME; } public function get_widgets(): array { return [ 'Contact_Buttons', 'Floating_Bars_Var_1', ]; } private function register_admin_menu_legacy( Admin_Menu_Manager $admin_menu ) { $menu_args = $this->get_contact_menu_args(); $function = $menu_args['function']; if ( is_callable( $function ) ) { $admin_menu->register( $menu_args['menu_slug'], new Floating_Buttons_Empty_View_Menu_Item( $function ) ); } else { $admin_menu->register( $menu_args['menu_slug'], new Floating_Buttons_Menu_Item() ); }; } public function __construct() { parent::__construct(); if ( Floating_Buttons::is_creating_floating_buttons_page() || Floating_Buttons::is_editing_existing_floating_buttons_page() ) { Controls_Manager::add_tab( Widget_Contact_Button_Base::TAB_ADVANCED, esc_html__( 'Advanced', 'elementor' ) ); Controls_Manager::add_tab( Widget_Floating_Bars_Base::TAB_ADVANCED, esc_html__( 'Advanced', 'elementor' ) ); } $this->register_contact_pages_cpt(); if ( ! ElementorUtils::has_pro() ) { add_action( 'elementor/documents/register', function ( Documents_Manager $documents_manager ) { $documents_manager->register_document_type( static::FLOATING_BUTTONS_DOCUMENT_TYPE, Floating_Buttons::get_class_full_name() ); } ); } add_action( 'current_screen', function() { $screen = get_current_screen(); if ( $screen && 'edit-e-floating-buttons' === $screen->id ) { $this->flush_permalinks_on_elementor_version_change(); } }); add_action( 'wp_ajax_elementor_send_clicks', [ $this, 'handle_click_tracking' ] ); add_action( 'wp_ajax_nopriv_elementor_send_clicks', [ $this, 'handle_click_tracking' ] ); add_action( 'elementor/frontend/after_register_styles', [ $this, 'register_styles' ] ); add_action( 'elementor/controls/register', function ( Controls_Manager $controls_manager ) { $controls_manager->register( new Hover_Animation_Floating_Buttons() ); }); add_filter( 'elementor/widget/common/register_css_attributes_control', function ( $common_controls ) { if ( Floating_Buttons::is_creating_floating_buttons_page() || Floating_Buttons::is_editing_existing_floating_buttons_page() ) { return false; } return $common_controls; } ); add_filter( 'elementor/settings/controls/checkbox_list_cpt/post_type_objects', function ( $post_types ) { unset( $post_types[ static::CPT_FLOATING_BUTTONS ] ); return $post_types; } ); add_filter( 'elementor/template_library/sources/local/is_valid_template_type', function ( $is_valid_template_type, $cpt ) { if ( in_array( static::CPT_FLOATING_BUTTONS, $cpt, true ) ) { return true; } return $is_valid_template_type; }, 10, 2 ); if ( ! ElementorUtils::has_pro() ) { add_action( 'wp_footer', function () { $this->render_floating_buttons(); } ); } add_action( 'elementor/admin-top-bar/is-active', function ( $is_top_bar_active, $current_screen ) { if ( strpos( $current_screen->id ?? '', static::CPT_FLOATING_BUTTONS ) !== false ) { return true; } return $is_top_bar_active; }, 10, 2 ); add_action( 'elementor/admin/menu/register', function( Admin_Menu_Manager $admin_menu ) { $this->register_admin_menu_legacy( $admin_menu ); }, Source_Local::ADMIN_MENU_PRIORITY + 20 ); add_action( 'elementor/admin/localize_settings', function ( array $settings ) { return $this->admin_localize_settings( $settings ); } ); add_action( 'elementor/editor/localize_settings', function ( $data ) { return $this->editor_localize_settings( $data ); } ); add_filter( 'elementor/template_library/sources/local/register_taxonomy_cpts', function ( array $cpts ) { $cpts[] = static::CPT_FLOATING_BUTTONS; return $cpts; } ); add_action( 'admin_init', function () { $action = filter_input( INPUT_GET, 'action' ); $menu_args = $this->get_contact_menu_args(); switch ( $action ) { case 'remove_from_entire_site': $post = filter_input( INPUT_GET, 'post', FILTER_VALIDATE_INT ); check_admin_referer( 'remove_from_entire_site_' . $post ); delete_post_meta( $post, '_elementor_conditions' ); wp_redirect( $menu_args['menu_slug'] ); exit; case 'set_as_entire_site': $post = filter_input( INPUT_GET, 'post', FILTER_VALIDATE_INT ); check_admin_referer( 'set_as_entire_site_' . $post ); $posts = get_posts( [ 'post_type' => static::CPT_FLOATING_BUTTONS, 'posts_per_page' => -1, 'post_status' => 'publish', 'fields' => 'ids', 'no_found_rows' => true, 'update_post_term_cache' => false, 'update_post_meta_cache' => false, 'meta_query' => Floating_Buttons::get_meta_query_for_floating_buttons( Floating_Buttons::get_floating_element_type( $post ) ), ] ); foreach ( $posts as $post_id ) { delete_post_meta( $post_id, '_elementor_conditions' ); } update_post_meta( $post, '_elementor_conditions', [ 'include/general' ] ); wp_redirect( $menu_args['menu_slug'] ); exit; default: break; } } ); add_action( 'manage_' . static::CPT_FLOATING_BUTTONS . '_posts_columns', function( $posts_columns ) { $source_local = Plugin::$instance->templates_manager->get_source( 'local' ); unset( $posts_columns['date'] ); unset( $posts_columns['comments'] ); $posts_columns['click_tracking'] = esc_html__( 'Click Tracking', 'elementor' ); if ( ! ElementorUtils::has_pro() ) { $posts_columns['instances'] = esc_html__( 'Instances', 'elementor' ); } return $source_local->admin_columns_headers( $posts_columns ); } ); add_action( 'manage_' . static::CPT_FLOATING_BUTTONS . '_posts_custom_column', [ $this, 'set_admin_columns_content' ], 10, 2 ); add_action( 'admin_bar_menu', function ( $admin_bar ) { $this->override_admin_bar_add_contact( $admin_bar ); }, 100 ); } public function is_preview_for_document( $post_id ) { $preview_id = ElementorUtils::get_super_global_value( $_GET, 'preview_id' ); $preview = ElementorUtils::get_super_global_value( $_GET, 'preview' ); return 'true' === $preview && (int) $post_id === (int) $preview_id; } public function handle_click_tracking() { $data = filter_input_array( INPUT_POST, [ 'clicks' => [ 'filter' => FILTER_VALIDATE_INT, 'flags' => FILTER_REQUIRE_ARRAY, ], '_nonce' => FILTER_UNSAFE_RAW, ] ); if ( ! wp_verify_nonce( $data['_nonce'], static::CLICK_TRACKING_NONCE ) ) { wp_send_json_error( [ 'message' => 'Invalid nonce' ] ); } if ( ! check_ajax_referer( static::CLICK_TRACKING_NONCE, '_nonce', false ) ) { wp_send_json_error( [ 'message' => 'Invalid referrer' ] ); } $posts_to_update = []; foreach ( $data['clicks'] as $post_id ) { if ( ! isset( $posts_to_update[ $post_id ] ) ) { $starting_clicks = (int) get_post_meta( $post_id, static::META_CLICK_TRACKING, true ); $posts_to_update[ $post_id ] = $starting_clicks ? $starting_clicks : 0; } $posts_to_update[ $post_id ] ++; } foreach ( $posts_to_update as $post_id => $clicks ) { update_post_meta( $post_id, static::META_CLICK_TRACKING, $clicks ); } wp_send_json_success(); } public function set_admin_columns_content( $column_name, $post_id ) { $document = Plugin::$instance->documents->get( $post_id ); if ( method_exists( $document, 'admin_columns_content' ) ) { $document->admin_columns_content( $column_name ); } switch ( $column_name ) { case 'click_tracking': $click_tracking = get_post_meta( $post_id, static::META_CLICK_TRACKING, true ); echo esc_html( $click_tracking ); break; case 'instances': if ( ElementorUtils::has_pro() ) { break; } $instances = get_post_meta( $post_id, '_elementor_conditions', true ); if ( $instances ) { echo esc_html__( 'Entire Site', 'elementor' ); } break; default: break; } } public function flush_permalinks_on_elementor_version_change() { if ( get_option( static::ROUTER_OPTION_KEY ) !== ELEMENTOR_VERSION ) { flush_rewrite_rules(); update_option( static::ROUTER_OPTION_KEY, ELEMENTOR_VERSION ); } } private function get_trashed_contact_posts(): array { if ( $this->trashed_contact_pages ) { return $this->trashed_contact_pages; } $this->trashed_contact_pages = $this->get_trashed_posts( static::CPT_FLOATING_BUTTONS, static::FLOATING_BUTTONS_DOCUMENT_TYPE ); return $this->trashed_contact_pages; } private function get_trashed_posts( string $cpt, string $document_type ) { $query = new \WP_Query( [ 'no_found_rows' => true, 'post_type' => $cpt, 'post_status' => 'trash', 'posts_per_page' => 1, 'meta_key' => '_elementor_template_type', 'meta_value' => $document_type, ] ); return $query->posts; } private function get_add_new_contact_page_url() { if ( ElementorUtils::has_pro() ) { return Plugin::$instance->documents->get_create_new_post_url( static::CPT_FLOATING_BUTTONS, static::FLOATING_BUTTONS_DOCUMENT_TYPE ); } return Plugin::$instance->documents->get_create_new_post_url( static::CPT_FLOATING_BUTTONS, static::FLOATING_BUTTONS_DOCUMENT_TYPE ) . '#library'; } public function print_empty_contact_pages_page() { $template_sources = Plugin::$instance->templates_manager->get_registered_sources(); $source_local = $template_sources['local']; $trashed_posts = $this->get_trashed_contact_posts(); ?>
print_blank_state_template( esc_html__( 'Floating Element', 'elementor' ), $this->get_add_new_contact_page_url(), nl2br( esc_html__( 'Add a Floating element so your users can easily get in touch!', 'elementor' ) ) ); if ( ! empty( $trashed_posts ) ) : ?>
', '' ); ?>
get_contact_menu_args()['menu_slug']; if ( static::CPT_FLOATING_BUTTONS === $contact_menu_slug ) { $contact_menu_slug = 'admin.php?page=' . $contact_menu_slug; } $additional_settings = [ 'urls' => [ 'addNewLinkUrlContact' => $this->get_add_new_contact_page_url(), 'viewContactPageUrl' => $contact_menu_slug, ], 'contactPages' => [ 'hasPages' => $this->has_contact_pages(), ], ]; return array_replace_recursive( $settings, $additional_settings ); } private function register_contact_pages_cpt() { $this->register_post_type( Floating_Buttons::get_labels(), static::CPT_FLOATING_BUTTONS ); } private function register_post_type( array $labels, string $cpt ) { $args = [ 'labels' => $labels, 'public' => true, 'show_in_menu' => 'edit.php?post_type=elementor_library&tabs_group=library', 'show_in_nav_menus' => false, 'capability_type' => 'page', 'taxonomies' => [ Source_Local::TAXONOMY_TYPE_SLUG ], 'supports' => [ 'title', 'editor', 'comments', 'revisions', 'trackbacks', 'author', 'excerpt', 'page-attributes', 'thumbnail', 'custom-fields', 'post-formats', 'elementor', ], ]; register_post_type( $cpt, $args ); } private function has_contact_pages(): bool { if ( null !== $this->has_contact_pages ) { return $this->has_contact_pages; } $this->has_contact_pages = $this->has_pages( static::CPT_FLOATING_BUTTONS, static::FLOATING_BUTTONS_DOCUMENT_TYPE ); return $this->has_contact_pages; } private function has_pages( string $cpt, string $document_type ): bool { $posts_query = new \WP_Query( [ 'no_found_rows' => true, 'post_type' => $cpt, 'post_status' => 'any', 'posts_per_page' => 1, 'meta_key' => '_elementor_template_type', 'meta_value' => $document_type, ] ); return $posts_query->post_count > 0; } private function get_contact_menu_args(): array { if ( $this->has_contact_pages() ) { $menu_slug = static::ADMIN_PAGE_SLUG_CONTACT; $function = null; } else { $menu_slug = static::CPT_FLOATING_BUTTONS; $function = [ $this, 'print_empty_contact_pages_page' ]; } return [ 'menu_slug' => $menu_slug, 'function' => $function, ]; } public function override_admin_bar_add_contact( $admin_bar ): void { $new_contact_page_node = $admin_bar->get_node( 'new-e-floating-buttons' ); if ( $new_contact_page_node ) { $new_contact_page_node->href = $this->get_add_new_contact_page_url(); $admin_bar->add_node( $new_contact_page_node ); } } private function editor_localize_settings( $data ) { $data['admin_floating_button_admin_url'] = admin_url( $this->get_contact_menu_args()['menu_slug'] ); return $data; } private function render_floating_buttons(): void { if ( Plugin::$instance->preview->is_preview_mode() ) { $post_id = ElementorUtils::get_super_global_value( $_GET, 'elementor-preview' ); $document = Plugin::$instance->documents->get( $post_id ); if ( $document instanceof Document && $document->get_name() === static::FLOATING_BUTTONS_DOCUMENT_TYPE ) { return; } } $query = new \WP_Query( [ 'post_type' => static::CPT_FLOATING_BUTTONS, 'posts_per_page' => - 1, 'post_status' => 'publish', 'fields' => 'ids', 'meta_key' => '_elementor_conditions', 'meta_compare' => 'EXISTS', ] ); if ( ! $query->have_posts() ) { return; } foreach ( $query->posts as $post_id ) { $conditions = get_post_meta( $post_id, '_elementor_conditions', true ); if ( ! $conditions ) { continue; } if ( in_array( 'include/general', $conditions ) && ! $this->is_preview_for_document( $post_id ) && get_the_ID() !== $post_id ) { $document = Plugin::$instance->documents->get( $post_id ); $document->print_content(); } } } /** * Register styles. * * At build time, Elementor compiles `/modules/floating-buttons/assets/scss/frontend.scss` * to `/assets/css/widget-floating-buttons.min.css`. * * @return void */ public function register_styles() { wp_register_style( 'widget-floating-buttons', $this->get_css_assets_url( 'widget-floating-buttons', null, true, true ), [ 'elementor-icons' ], ELEMENTOR_VERSION ); } } floating-buttons/documents/floating-buttons.php000064400000017733147207015700016072 0ustar00preview->is_preview_mode( $this->get_main_id() ) ) { // PHPCS - the method builder_wrapper is safe. echo $plugin->preview->builder_wrapper( '' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } else { // PHPCS - the method get_content is safe. echo $this->get_content(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } public function get_location() { return self::get_property( 'location' ); } public static function get_type() { return Floating_Buttons_Module::FLOATING_BUTTONS_DOCUMENT_TYPE; } public static function register_post_fields_control( $document ) {} public static function register_hide_title_control( $document ) {} public function get_name() { return Floating_Buttons_Module::FLOATING_BUTTONS_DOCUMENT_TYPE; } public function filter_admin_row_actions( $actions ) { unset( $actions['edit'] ); unset( $actions['inline hide-if-no-js'] ); $built_with_elementor = parent::filter_admin_row_actions( [] ); if ( isset( $actions['trash'] ) ) { $delete = $actions['trash']; unset( $actions['trash'] ); $actions['trash'] = $delete; } if ( 'publish' === $this->get_post()->post_status ) { $actions = $this->set_as_entire_site( $actions ); } return $built_with_elementor + $actions; } public static function get_meta_query_for_floating_buttons( string $floating_element_type ): array { $meta_query = [ 'relation' => 'AND', [ 'key' => '_elementor_conditions', 'compare' => 'EXISTS', ], ]; if ( 'floating-buttons' === $floating_element_type ) { $meta_query[] = [ 'relation' => 'OR', [ 'key' => Module::FLOATING_ELEMENTS_TYPE_META_KEY, 'compare' => 'NOT EXISTS', ], [ 'key' => Module::FLOATING_ELEMENTS_TYPE_META_KEY, 'value' => 'floating-buttons', ], ]; } else { $meta_query[] = [ 'key' => Module::FLOATING_ELEMENTS_TYPE_META_KEY, 'value' => $floating_element_type, ]; } return $meta_query; } /** * Tries to find the post id of the floating element that is set as entire site. * If found, returns the post id, otherwise returns 0. * * @param string $floating_element_type * * @return int */ public static function get_set_as_entire_site_post_id( string $floating_element_type ): int { static $types = []; if ( isset( $types[ $floating_element_type ] ) ) { return $types[ $floating_element_type ]; } $query = new \WP_Query( [ 'post_type' => Floating_Buttons_Module::CPT_FLOATING_BUTTONS, 'posts_per_page' => -1, 'post_status' => 'publish', 'fields' => 'ids', 'no_found_rows' => true, 'update_post_term_cache' => false, 'meta_query' => static::get_meta_query_for_floating_buttons( $floating_element_type ), ] ); foreach ( $query->get_posts() as $post_id ) { $conditions = get_post_meta( $post_id, '_elementor_conditions', true ); if ( ! $conditions ) { continue; } if ( in_array( 'include/general', $conditions ) ) { $types[ $floating_element_type ] = $post_id; return $post_id; } } return 0; } public function set_as_entire_site( $actions ) { $floating_element_type = static::get_floating_element_type( $this->get_main_id() ); $current_set_as_entire_site_post_id = static::get_set_as_entire_site_post_id( $floating_element_type ); if ( $current_set_as_entire_site_post_id === $this->get_main_id() ) { $actions['set_as_entire_site'] = sprintf( '%s', $this->get_post()->ID, wp_create_nonce( 'remove_from_entire_site_' . $this->get_post()->ID ), esc_html__( 'Remove From Entire Site', 'elementor' ) ); } else { $actions['set_as_entire_site'] = sprintf( '%s', $this->get_post()->ID, wp_create_nonce( 'set_as_entire_site_' . $this->get_post()->ID ), esc_html__( 'Set as Entire Site', 'elementor' ) ); } return $actions; } public static function get_title() { return esc_html__( 'Floating Element', 'elementor' ); } public static function get_plural_title() { return esc_html__( 'Floating Elements', 'elementor' ); } public static function get_create_url() { return parent::get_create_url() . '#library'; } public function save( $data ) { if ( empty( $data['settings']['template'] ) ) { $data['settings']['template'] = Page_Templates_Module::TEMPLATE_CANVAS; } return parent::save( $data ); } public function admin_columns_content( $column_name ) { if ( 'elementor_library_type' === $column_name ) { $admin_filter_url = admin_url( Source_Local::ADMIN_MENU_SLUG . '&elementor_library_type=' . $this->get_name() ); $meta = static::get_floating_element_type( $this->get_main_id() ); printf( '%s', $admin_filter_url, Module::get_floating_elements_types()[ $meta ] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } public function get_edit_url() { return add_query_arg( [ 'post' => $this->get_main_id(), 'action' => 'elementor', 'floating_element' => get_post_meta( $this->get_main_id(), Module::FLOATING_ELEMENTS_TYPE_META_KEY, true ), ], admin_url( 'post.php' ) ); } protected function get_remote_library_config() { $config = [ 'type' => 'floating_button', 'default_route' => 'templates/floating-buttons', 'autoImportSettings' => true, ]; return array_replace_recursive( parent::get_remote_library_config(), $config ); } } floating-buttons/classes/render/contact-buttons-core-render.php000064400000003773147207015700021037 0ustar00build_layout_render_attribute(); $this->add_content_wrapper_render_attribute(); $content_classnames = 'e-contact-buttons__content'; $animation_duration = $this->settings['style_chat_box_animation_duration']; if ( ! empty( $animation_duration ) ) { $content_classnames .= ' has-animation-duration-' . $animation_duration; } $this->widget->add_render_attribute( 'content', [ 'class' => $content_classnames, ] ); ?>
widget->get_render_attribute_string( 'layout' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
widget->get_render_attribute_string( 'content-wrapper' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
widget->get_render_attribute_string( 'content' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> render_top_bar(); $this->render_message_bubble(); $this->render_send_button(); ?>
render_chat_button(); ?>
widget->add_render_attribute( 'layout', [ 'class' => $layout_classnames, 'id' => $this->settings['advanced_custom_css_id'], 'data-document-id' => get_the_ID(), 'aria-role' => 'dialog', ] ); } protected function add_content_wrapper_render_attribute() { $this->widget->add_render_attribute( 'content-wrapper', [ 'aria-hidden' => 'true', 'aria-label' => __( 'Links window', 'elementor' ), 'class' => 'e-contact-buttons__content-wrapper hidden', 'id' => 'e-contact-buttons__content-wrapper', ] ); } } floating-buttons/classes/render/floating-bars-core-render.php000064400000010627147207015700020434 0ustar00settings['announcement_icon'] ?? ''; if ( '' !== $icon['value'] ) : ?> 'true' ] ); ?> settings['announcement_text'] ?? ''; $this->widget->add_render_attribute( 'announcement_text', [ 'class' => 'e-floating-bars__announcement-text', ] ); if ( '' !== $text ) : ?>

widget->print_render_attribute_string( 'announcement_text' ); ?>>

settings['cta_icon'] ?? ''; $icon_classnames = 'e-floating-bars__cta-icon'; $this->widget->add_render_attribute( 'cta-icon', [ 'class' => $icon_classnames, ] ); if ( '' !== $icon['value'] ) : ?> widget->get_render_attribute_string( 'cta-icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> 'true' ] ); ?> settings['cta_link'] ?? ''; $text = $this->settings['cta_text'] ?? ''; $hover_animation = $this->settings['style_cta_button_hover_animation']; $corners = $this->settings['style_cta_button_corners']; $link_type = $this->settings['style_cta_type']; $entrance_animation = $this->settings['style_cta_button_animation']; $has_border = $this->settings['style_cta_button_show_border']; $cta_classnames = 'e-floating-bars__cta-button'; if ( ! empty( $hover_animation ) ) { $cta_classnames .= ' elementor-animation-' . $hover_animation; } if ( ! empty( $corners ) ) { $cta_classnames .= ' has-corners-' . $corners; } if ( ! empty( $link_type ) ) { $cta_classnames .= ' is-type-' . $link_type; } if ( ! empty( $entrance_animation ) && 'none' != $entrance_animation ) { $cta_classnames .= ' has-entrance-animation'; } if ( 'yes' == $has_border ) { $cta_classnames .= ' has-border'; } $this->widget->add_render_attribute( 'cta-button', [ 'class' => $cta_classnames, ] ); $this->widget->add_render_attribute( 'cta_text', [ 'class' => 'e-floating-bars__cta-text', ] ); if ( ! empty( $text ) ) { $this->widget->add_link_attributes( 'cta-button', $link ); ?> settings['accessible_name']; $close_button_classnames = 'e-floating-bars__close-button'; $this->widget->add_render_attribute( 'close-button', [ 'class' => $close_button_classnames, 'aria-label' => sprintf( /* translators: 1: Accessible name. */ esc_html__( 'Close %1$s', 'elementor' ), $accessible_name, ), 'type' => 'button', 'aria-controls' => 'e-floating-bars', ] ); ?> build_layout_render_attribute(); $has_close_button = $this->settings['floating_bar_close_switch']; ?>
widget->get_render_attribute_string( 'layout' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> render_announcement_text(); $this->render_announcement_icon(); $this->render_cta_button(); if ( 'yes' === $has_close_button ) { $this->render_close_button(); } ?>
widget = $widget; $this->settings = $widget->get_settings_for_display(); } protected function add_layout_render_attribute( $layout_classnames ) { $this->widget->add_render_attribute( 'layout', [ 'class' => $layout_classnames, 'id' => $this->settings['advanced_custom_css_id'], 'data-document-id' => get_the_ID(), 'role' => 'alertdialog', ] ); } public static function get_layout_classnames( Widget_Floating_Bars_Base $widget, array $settings ): string { $layout_classnames = 'e-floating-bars e-' . $widget->get_name(); $vertical_position = $settings['advanced_vertical_position']; $is_sticky = $settings['advanced_toggle_sticky']; $has_close_button = $settings['floating_bar_close_switch']; $layout_classnames .= ' has-vertical-position-' . $vertical_position; if ( 'yes' === $has_close_button ) { $layout_classnames .= ' has-close-button'; } if ( 'yes' === $is_sticky ) { $layout_classnames .= ' is-sticky'; } return $layout_classnames; } protected function build_layout_render_attribute(): void { $layout_classnames = static::get_layout_classnames( $this->widget, $this->settings ); $this->add_layout_render_attribute( $layout_classnames ); } } floating-buttons/classes/render/contact-buttons-render-base.php000064400000040761147207015700021017 0ustar00widget = $widget; $this->settings = $widget->get_settings_for_display(); } protected function render_chat_button_icon(): void { $platform = $this->settings['chat_button_platform'] ?? ''; $mapping = Social_Network_Provider::get_icon_mapping( $platform ); $icon_lib = explode( ' ', $mapping )[0]; $library = 'fab' === $icon_lib ? 'fa-brands' : 'fa-solid'; Icons_Manager::render_icon( [ 'library' => $library, 'value' => $mapping, ], [ 'aria-hidden' => 'true' ] ); } protected function render_chat_button(): void { $platform = $this->settings['chat_button_platform'] ?? ''; $display_dot = $this->settings['chat_button_show_dot'] ?? ''; $button_size = $this->settings['style_chat_button_size']; $hover_animation = $this->settings['style_button_color_hover_animation']; $entrance_animation = $this->settings['style_chat_button_animation']; $entrance_animation_duration = $this->settings['style_chat_button_animation_duration']; $entrance_animation_delay = $this->settings['style_chat_button_animation_delay']; $accessible_name = $this->settings['chat_aria_label']; $button_classnames = 'e-contact-buttons__chat-button e-contact-buttons__chat-button-shadow'; if ( ! empty( $button_size ) ) { $button_classnames .= ' has-size-' . $button_size; } if ( ! empty( $hover_animation ) ) { $button_classnames .= ' elementor-animation-' . $hover_animation; } if ( ! empty( $entrance_animation ) && 'none' != $entrance_animation ) { $button_classnames .= ' has-entrance-animation'; } if ( ! empty( $entrance_animation_delay ) ) { $button_classnames .= ' has-entrance-animation-delay'; } if ( ! empty( $entrance_animation_duration ) ) { $button_classnames .= ' has-entrance-animation-duration-' . $entrance_animation_duration; } if ( 'yes' === $display_dot ) { $button_classnames .= ' has-dot'; } $this->widget->add_render_attribute( 'button', [ 'class' => $button_classnames, 'aria-controls' => 'e-contact-buttons__content-wrapper', 'aria-label' => sprintf( /* translators: 1: Accessible name. */ esc_html__( 'Toggle %1$s', 'elementor' ), $accessible_name, ), 'type' => 'button', ] ); ?>
settings['chat_aria_label']; $this->widget->add_render_attribute( 'close-button', [ 'class' => 'e-contact-buttons__close-button', 'aria-controls' => 'e-contact-buttons__content-wrapper', 'aria-label' => sprintf( /* translators: 1: Accessible name. */ esc_html__( 'Close %1$s', 'elementor' ), $accessible_name, ), 'type' => 'button', ] ); ?> settings['top_bar_image'] ?? []; $has_profile_image = ! empty( $profile_image_value ) && ( ! empty( $profile_image_value['url'] || ! empty( $profile_image_value['id'] ) ) ); $profile_image_size = $this->settings['style_top_bar_image_size']; $display_profile_dot = $this->settings['top_bar_show_dot']; $profile_image_classnames = 'e-contact-buttons__profile-image'; if ( ! empty( $profile_image_size ) ) { $profile_image_classnames .= ' has-size-' . $profile_image_size; } if ( 'yes' === $display_profile_dot ) { $profile_image_classnames .= ' has-dot'; } $top_bar_title = $this->settings['top_bar_title'] ?? ''; $top_bar_subtitle = $this->settings['top_bar_subtitle'] ?? ''; $has_top_bar_title = ! empty( $top_bar_title ); $has_top_bar_subtitle = ! empty( $top_bar_subtitle ); $this->widget->add_render_attribute( 'profile-image', [ 'class' => $profile_image_classnames, ] ); ?>
render_close_button(); ?>
widget->get_render_attribute_string( 'profile-image' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> 'e-contact-buttons__profile-image-el', ] ); } else { $this->widget->add_render_attribute( 'profile-image-src', [ 'alt' => '', 'class' => 'e-contact-buttons__profile-image-el', 'src' => esc_url( $profile_image_value['url'] ), ] ); ?> widget->get_render_attribute_string( 'profile-image-src' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> />

settings['chat_button_show_animation']; ?>
settings['message_bubble_name'] ?? ''; $message_bubble_body = $this->settings['message_bubble_body'] ?? ''; $has_message_bubble_name = ! empty( $message_bubble_name ); $has_message_bubble_body = ! empty( $message_bubble_body ); $time_format = $this->settings['chat_button_time_format']; ?>

settings['chat_button_show_animation'] ?? false; $has_typing_animation = $show_animation && 'yes' === $show_animation; if ( $has_typing_animation ) { $message_bubble_classnames .= ' has-typing-animation'; } $this->widget->add_render_attribute( 'message-bubble', [ 'class' => $message_bubble_classnames, ] ); ?>
widget->get_render_attribute_string( 'message-bubble' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> render_message_bubble_typing_animation(); $this->render_message_bubble_container(); ?>
settings['contact_cta_text'] ?? ''; ?>

settings['contact_repeater'] ?? []; $icons_size = $this->settings['style_contact_button_size'] ?? 'small'; $hover_animation = $this->settings['style_contact_button_hover_animation']; ?>
render_contact_text(); $this->render_contact_links(); ?>
settings['chat_button_platform'] ?? ''; $send_button_text = $this->settings['send_button_text']; $hover_animation = $this->settings['style_send_hover_animation']; $cta_classnames = 'e-contact-buttons__send-cta'; $link = [ 'platform' => $platform, 'number' => $this->settings['chat_button_number'] ?? '', 'username' => $this->settings['chat_button_username'] ?? '', 'email_data' => [ 'chat_button_mail' => $this->settings['chat_button_mail'], 'chat_button_mail_subject' => $this->settings['chat_button_mail_subject'] ?? '', 'chat_button_mail_body' => $this->settings['chat_button_mail_body'] ?? '', ], 'viber_action' => $this->settings['chat_button_viber_action'], ]; $formatted_link = $this->get_formatted_link( $link, 'chat_button' ); if ( ! empty( $hover_animation ) ) { $cta_classnames .= ' elementor-animation-' . $hover_animation; } $this->widget->add_render_attribute( 'formatted-cta', [ 'class' => $cta_classnames, 'href' => $formatted_link, 'rel' => 'noopener noreferrer', 'target' => '_blank', ] ); ?> widget->add_link_attributes( $key, $link['location'] ); break; case Social_Network_Provider::URL: if ( empty( $link['url']['url'] ) ) { $link['url']['url'] = '#'; } $this->widget->add_link_attributes( $key, $link['url'] ); break; default: break; } } protected function build_layout_render_attribute(): void { $layout_classnames = 'e-contact-buttons e-' . $this->widget->get_name(); $platform = $this->settings['chat_button_platform'] ?? ''; $border_radius = $this->settings['style_chat_box_corners']; $alignment_position_horizontal = $this->settings['advanced_horizontal_position']; $alignment_position_vertical = $this->settings['advanced_vertical_position']; $has_animations = ! empty( $this->settings['style_chat_box_exit_animation'] ) || ! empty( $this->settings['style_chat_box_entrance_animation'] ); $custom_classes = $this->settings['advanced_custom_css_classes'] ?? ''; $icon_name_mapping = Social_Network_Provider::get_name_mapping( $platform ); if ( ! empty( $platform ) ) { $layout_classnames .= ' has-platform-' . $icon_name_mapping; } if ( ! empty( $border_radius ) ) { $layout_classnames .= ' has-corners-' . $border_radius; } if ( ! empty( $alignment_position_horizontal ) ) { $layout_classnames .= ' has-h-alignment-' . $alignment_position_horizontal; } if ( ! empty( $alignment_position_vertical ) ) { $layout_classnames .= ' has-v-alignment-' . $alignment_position_vertical; } if ( $has_animations ) { $layout_classnames .= ' has-animations'; } if ( $custom_classes ) { $layout_classnames .= ' ' . $custom_classes; } $this->add_layout_render_attribute( $layout_classnames ); } } floating-buttons/widgets/floating-bars-var-1.php000064400000001642147207015700015704 0ustar00add_inline_editing_attributes( 'announcement_text', 'none' ); $this->add_inline_editing_attributes( 'cta_text', 'none' ); parent::render(); } } floating-buttons/widgets/contact-buttons.php000064400000001263147207015700015356 0ustar00 true, ] ); } public static function get_configuration() { return [ 'content' => [ 'announcement_section' => [ 'icon_default' => [ 'value' => 'fas fa-tshirt', 'library' => 'fa-solid', ], 'text_label' => esc_html__( 'Text', 'elementor' ), 'text_default' => esc_html__( 'Just in! Cool summer tees', 'elementor' ), ], 'floating_bar_section' => [ 'close_switch_default' => 'yes', 'has_pause_switch' => false, 'accessible_name_default' => esc_html__( 'Banner', 'elementor' ), ], ], 'style' => [ 'floating_bar_section' => [ 'has_close_bg' => false, 'close_position_selectors' => [ '{{WRAPPER}} .e-floating-bars__close-button' => 'inset-inline-{{VALUE}}: 10px;', ], 'has_close_position_control' => true, 'background_selector' => '{{WRAPPER}} .e-floating-bars', 'align_elements_selector' => [ '{{WRAPPER}} .e-floating-bars' => 'justify-content: {{VALUE}};', '{{WRAPPER}} .e-floating-bars__cta-button-container' => 'justify-content: {{VALUE}};', '{{WRAPPER}} .e-floating-bars__announcement-text' => 'text-align: {{VALUE}};', ], ], ], 'advanced' => [], ]; } protected function register_controls(): void { $this->add_content_tab(); $this->add_style_tab(); $this->add_advanced_tab(); } protected function add_announcement_content_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'announcement_content_section', [ 'label' => __( 'Announcement', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); $this->add_control( 'announcement_icon', [ 'label' => esc_html__( 'Icon', 'elementor' ), 'type' => Controls_Manager::ICONS, 'fa4compatibility' => 'icon', 'default' => $config['content']['announcement_section']['icon_default'], 'skin' => 'inline', 'label_block' => false, 'icon_exclude_inline_options' => [], ] ); $this->add_control( 'announcement_text', [ 'label' => $config['content']['announcement_section']['text_label'], 'type' => Controls_Manager::TEXTAREA, 'dynamic' => [ 'active' => true, ], 'placeholder' => esc_html__( 'Enter your text here', 'elementor' ), 'default' => $config['content']['announcement_section']['text_default'], ] ); $this->end_controls_section(); } protected function add_cta_button_content_section(): void { $this->start_controls_section( 'cta_button_content_section', [ 'label' => __( 'CTA Button', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); $this->add_control( 'cta_text', [ 'label' => esc_html__( 'Text', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'placeholder' => esc_html__( 'Enter text', 'elementor' ), 'default' => esc_html__( 'Shop now', 'elementor' ), ], ); $this->add_control( 'cta_link', [ 'label' => esc_html__( 'Link', 'elementor' ), 'type' => Controls_Manager::URL, 'placeholder' => esc_html__( 'Paste URL or type', 'elementor' ), 'dynamic' => [ 'active' => true, ], 'default' => [ 'url' => '', 'is_external' => true, 'nofollow' => false, ], ] ); $this->add_control( 'cta_icon', [ 'label' => esc_html__( 'Icon', 'elementor' ), 'type' => Controls_Manager::ICONS, 'fa4compatibility' => 'icon', 'skin' => 'inline', 'label_block' => false, 'icon_exclude_inline_options' => [], ] ); $this->end_controls_section(); } protected function add_accessible_name_control(): void { $config = static::get_configuration(); $this->add_control( 'accessible_name', [ 'label' => esc_html__( 'Accessible Name', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'placeholder' => esc_html__( 'Enter text', 'elementor' ), 'default' => $config['content']['floating_bar_section']['accessible_name_default'], 'condition' => [ 'floating_bar_close_switch' => 'yes', ], ], ); } protected function add_floating_bar_content_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'floating_bar_content_section', [ 'label' => __( 'Floating Bar', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); if ( $config['content']['floating_bar_section']['has_pause_switch'] ) { $this->add_control( 'floating_bar_pause_switch', [ 'label' => esc_html__( 'Pause and Play', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Show', 'elementor' ), 'label_off' => esc_html__( 'Hide', 'elementor' ), 'return_value' => 'yes', 'default' => 'no', ] ); $this->add_control( 'floating_bar_pause_icon', [ 'label' => esc_html__( 'Pause Icon', 'elementor' ), 'type' => Controls_Manager::ICONS, 'fa4compatibility' => 'icon', 'default' => [ 'value' => 'fas fa-pause', 'library' => 'fa-solid', ], 'skin' => 'inline', 'label_block' => false, 'exclude_inline_options' => [ 'none' ], 'recommended' => [ 'fa-regular' => [ 'pause-circle', ], 'fa-solid' => [ 'pause-circle', ], ], 'condition' => [ 'floating_bar_pause_switch' => 'yes', ], ], ); $this->add_control( 'floating_bar_play_icon', [ 'label' => esc_html__( 'Play Icon', 'elementor' ), 'type' => Controls_Manager::ICONS, 'fa4compatibility' => 'icon', 'default' => [ 'value' => 'fas fa-play', 'library' => 'fa-solid', ], 'skin' => 'inline', 'label_block' => false, 'exclude_inline_options' => [ 'none' ], 'recommended' => [ 'fa-regular' => [ 'play-circle', ], 'fa-solid' => [ 'play-circle', ], ], 'condition' => [ 'floating_bar_pause_switch' => 'yes', ], ], ); } $this->add_control( 'floating_bar_close_switch', [ 'label' => esc_html__( 'Close Button', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Show', 'elementor' ), 'label_off' => esc_html__( 'Hide', 'elementor' ), 'return_value' => 'yes', 'default' => $config['content']['floating_bar_section']['close_switch_default'], ] ); $this->add_accessible_name_control(); $this->end_controls_section(); } protected function add_headlines_content_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'headlines_content', [ 'label' => esc_html__( 'Headlines', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); $repeater = new Repeater(); $repeater->add_control( 'headlines_icon', [ 'label' => esc_html__( 'Icon', 'elementor' ), 'type' => Controls_Manager::ICONS, 'fa4compatibility' => 'icon', 'skin' => 'inline', 'label_block' => false, 'icon_exclude_inline_options' => [], ] ); $repeater->add_control( 'headlines_text', [ 'label' => esc_html__( 'Text', 'elementor' ), 'type' => Controls_Manager::TEXTAREA, 'placeholder' => esc_html__( 'Enter your text', 'elementor' ), 'default' => esc_html__( 'Item Title', 'elementor' ), 'dynamic' => [ 'active' => true, ], ] ); $repeater->add_control( 'headlines_url', [ 'label' => esc_html__( 'Link', 'elementor' ), 'type' => Controls_Manager::URL, 'placeholder' => esc_html__( 'Paste URL or type', 'elementor' ), 'dynamic' => [ 'active' => true, ], 'frontend_available' => true, ] ); $this->add_control( 'headlines_repeater', [ 'type' => Controls_Manager::REPEATER, 'fields' => $repeater->get_controls(), 'title_field' => '{{{ headlines_text }}}', 'prevent_empty' => true, 'button_text' => esc_html__( 'Add Item', 'elementor' ), 'default' => [ [ 'headlines_text' => esc_html__( 'Item #1', 'elementor' ), ], [ 'headlines_text' => esc_html__( 'Item #2', 'elementor' ), ], [ 'headlines_text' => esc_html__( 'Item #3', 'elementor' ), ], ], ] ); $this->end_controls_section(); } protected function add_announcement_style_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'style_announcement', [ 'label' => esc_html__( 'Announcement', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'style_announcement_icon_heading', [ 'label' => esc_html__( 'Icon', 'elementor' ), 'type' => Controls_Manager::HEADING, 'conditions' => [ 'relation' => 'and', 'terms' => [ [ 'name' => 'announcement_icon[value]', 'operator' => '!==', 'value' => '', ], [ 'name' => 'announcement_icon[value]', 'operator' => '!==', 'value' => null, ], ], ], ] ); $this->add_control( 'style_announcement_icon_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-announcement-icon-color: {{VALUE}}', ], 'conditions' => [ 'relation' => 'and', 'terms' => [ [ 'name' => 'announcement_icon[value]', 'operator' => '!==', 'value' => '', ], [ 'name' => 'announcement_icon[value]', 'operator' => '!==', 'value' => null, ], ], ], ] ); $this->add_responsive_control( 'style_announcement_icon_position', [ 'label' => esc_html__( 'Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Left', 'elementor' ), 'icon' => 'eicon-h-align-left', ], 'end' => [ 'title' => esc_html__( 'Right', 'elementor' ), 'icon' => 'eicon-h-align-right', ], ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars__announcement-icon' => 'order: {{VALUE}};', ], 'selectors_dictionary' => [ 'start' => '-1', 'end' => '2', ], 'default' => 'start', 'toggle' => false, 'conditions' => [ 'relation' => 'and', 'terms' => [ [ 'name' => 'announcement_icon[value]', 'operator' => '!==', 'value' => '', ], [ 'name' => 'announcement_icon[value]', 'operator' => '!==', 'value' => null, ], ], ], ] ); $this->add_responsive_control( 'style_announcement_icon_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'min' => 0, 'max' => 150, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-announcement-icon-size: {{SIZE}}{{UNIT}}', ], 'separator' => 'after', 'conditions' => [ 'relation' => 'and', 'terms' => [ [ 'name' => 'announcement_icon[value]', 'operator' => '!==', 'value' => '', ], [ 'name' => 'announcement_icon[value]', 'operator' => '!==', 'value' => null, ], ], ], ] ); $this->add_control( 'style_announcement_text_heading', [ 'label' => $config['content']['announcement_section']['text_label'], 'type' => Controls_Manager::HEADING, ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_announcement_text_typography', 'selector' => '{{WRAPPER}} .e-floating-bars__announcement-text', ] ); $this->add_control( 'style_announcement_text_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-announcement-text-color: {{VALUE}}', ], ] ); $this->end_controls_section(); } protected function add_cta_button_style_section(): void { $this->start_controls_section( 'style_cta_button', [ 'label' => esc_html__( 'CTA Button', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'style_cta_type', [ 'label' => esc_html__( 'Type', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'button', 'options' => [ 'button' => esc_html__( 'Button', 'elementor' ), 'link' => esc_html__( 'Link', 'elementor' ), ], ] ); $this->add_responsive_control( 'style_cta_icon_position', [ 'label' => esc_html__( 'Icon Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'default' => is_rtl() ? 'row-reverse' : 'row', 'toggle' => false, 'options' => [ 'row' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => 'eicon-h-align-left', ], 'row-reverse' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => 'eicon-h-align-right', ], ], 'selectors_dictionary' => [ 'left' => is_rtl() ? 'row-reverse' : 'row', 'right' => is_rtl() ? 'row' : 'row-reverse', ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars__cta-button' => 'flex-direction: {{VALUE}};', ], 'condition' => [ 'cta_icon[value]!' => '', ], ] ); $this->add_control( 'style_cta_icon_spacing', [ 'label' => esc_html__( 'Icon Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'max' => 50, ], 'em' => [ 'max' => 5, ], 'rem' => [ 'max' => 5, ], ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-cta-button-icon-gap: {{SIZE}}{{UNIT}};', ], 'conditions' => [ 'relation' => 'and', 'terms' => [ [ 'name' => 'cta_icon[value]', 'operator' => '!==', 'value' => '', ], [ 'name' => 'cta_icon[value]', 'operator' => '!==', 'value' => null, ], ], ], ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_cta_typography', 'selector' => '{{WRAPPER}} .e-floating-bars__cta-button', ] ); $this->start_controls_tabs( 'style_cta_button_tabs' ); $this->start_controls_tab( 'style_cta_button_tabs_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'style_cta_button_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-cta-button-text-color: {{VALUE}}', ], ] ); $this->add_control( 'style_cta_button_bg_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-cta-button-bg-color: {{VALUE}}', ], 'condition' => [ 'style_cta_type' => 'button', ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'style_cta_button_tabs_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'style_cta_button_text_color_hover', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-cta-button-text-color-hover: {{VALUE}}', ], ] ); $this->add_control( 'style_cta_button_bg_color_hover', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-cta-button-bg-color-hover: {{VALUE}}', ], 'condition' => [ 'style_cta_type' => 'button', ], ] ); $this->add_control( 'style_cta_button_border_color_hover', [ 'label' => esc_html__( 'Border Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-cta-button-border-color-hover: {{VALUE}}', ], 'conditions' => [ 'relation' => 'and', 'terms' => [ [ 'name' => 'style_cta_button_show_border', 'operator' => '===', 'value' => 'yes', ], [ 'name' => 'style_cta_type', 'operator' => '===', 'value' => 'button', ], ], ], ] ); $this->add_control( 'style_cta_button_hover_animation', [ 'label' => esc_html__( 'Hover Animation', 'elementor' ), 'type' => Controls_Manager::HOVER_ANIMATION, 'frontend_available' => true, ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->add_control( 'style_cta_button_show_border', [ 'label' => esc_html__( 'Border', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Yes', 'elementor' ), 'label_off' => esc_html__( 'No', 'elementor' ), 'return_value' => 'yes', 'default' => 'yes', 'separator' => 'before', 'condition' => [ 'style_cta_type' => 'button', ], ] ); $this->add_responsive_control( 'style_cta_button_border_width', [ 'label' => esc_html__( 'Border Width', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'min' => 10, 'max' => 100, ], 'px' => [ 'min' => 0, 'max' => 10, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-cta-button-border-width: {{SIZE}}{{UNIT}}', ], 'conditions' => [ 'relation' => 'and', 'terms' => [ [ 'name' => 'style_cta_button_show_border', 'operator' => '===', 'value' => 'yes', ], [ 'name' => 'style_cta_type', 'operator' => '===', 'value' => 'button', ], ], ], ] ); $this->add_control( 'style_cta_button_border_color', [ 'label' => esc_html__( 'Border Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-cta-button-border-color: {{VALUE}}', ], 'conditions' => [ 'relation' => 'and', 'terms' => [ [ 'name' => 'style_cta_button_show_border', 'operator' => '===', 'value' => 'yes', ], [ 'name' => 'style_cta_type', 'operator' => '===', 'value' => 'button', ], ], ], ] ); $this->add_control( 'style_cta_button_corners', [ 'label' => esc_html__( 'Corners', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'round', 'options' => [ 'round' => esc_html__( 'Round', 'elementor' ), 'rounded' => esc_html__( 'Rounded', 'elementor' ), 'sharp' => esc_html__( 'Sharp', 'elementor' ), ], 'condition' => [ 'style_cta_type' => 'button', ], ] ); $this->add_responsive_control( 'style_cta_button_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem' ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-cta-button-padding-block-end: {{BOTTOM}}{{UNIT}}; --e-floating-bars-cta-button-padding-block-start: {{TOP}}{{UNIT}}; --e-floating-bars-cta-button-padding-inline-end: {{RIGHT}}{{UNIT}}; --e-floating-bars-cta-button-padding-inline-start: {{LEFT}}{{UNIT}};', ], 'separator' => 'before', 'condition' => [ 'style_cta_type' => 'button', ], ] ); $this->add_responsive_control( 'style_cta_button_animation', [ 'label' => esc_html__( 'Entrance Animation', 'elementor' ), 'type' => Controls_Manager::ANIMATION, 'frontend_available' => true, 'separator' => 'before', ] ); $this->add_control( 'style_cta_button_animation_duration', [ 'label' => esc_html__( 'Animation Duration', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => '1000', 'options' => [ '2000' => esc_html__( 'Slow', 'elementor' ), '1000' => esc_html__( 'Normal', 'elementor' ), '800' => esc_html__( 'Fast', 'elementor' ), ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-cta-button-animation-duration: {{VALUE}}ms', ], 'prefix_class' => 'animated-', 'conditions' => [ 'relation' => 'and', 'terms' => [ [ 'name' => 'style_cta_button_animation', 'operator' => '!==', 'value' => '', ], [ 'name' => 'style_cta_button_animation', 'operator' => '!==', 'value' => 'none', ], ], ], ] ); $this->add_control( 'style_cta_button_animation_delay', [ 'label' => esc_html__( 'Animation Delay', 'elementor' ) . ' (ms)', 'type' => Controls_Manager::NUMBER, 'min' => 0, 'step' => 100, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-cta-button-animation-delay: {{SIZE}}ms;', ], 'render_type' => 'none', 'frontend_available' => true, 'conditions' => [ 'relation' => 'and', 'terms' => [ [ 'name' => 'style_cta_button_animation', 'operator' => '!==', 'value' => '', ], [ 'name' => 'style_cta_button_animation', 'operator' => '!==', 'value' => 'none', ], ], ], ] ); $this->end_controls_section(); } protected function add_floating_bar_background_style_controls(): void { $config = static::get_configuration(); $this->add_control( 'floating_bar_background_heading', [ 'label' => esc_html__( 'Background', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'floating_bar_background_type', 'types' => [ 'classic', 'gradient' ], 'selector' => $config['style']['floating_bar_section']['background_selector'], 'fields_options' => [ 'background' => [ 'default' => 'classic', ], 'position' => [ 'default' => 'center center', ], 'size' => [ 'default' => 'cover', ], ], ] ); $this->add_control( 'floating_bar_background_overlay_heading', [ 'label' => esc_html__( 'Background Overlay', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'floating_bar_background_overlay_type', 'types' => [ 'classic', 'gradient' ], 'selector' => '{{WRAPPER}} .e-floating-bars__overlay', 'fields_options' => [ 'background' => [ 'default' => 'classic', ], 'position' => [ 'default' => 'center center', ], 'size' => [ 'default' => 'cover', ], ], ] ); $this->add_responsive_control( 'floating_bar_background_overlay_opacity', [ 'label' => esc_html__( 'Opacity', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'max' => 1, 'min' => 0, 'step' => 0.01, ], ], 'default' => [ 'unit' => '%', 'size' => 0.5, ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-background-overlay-opacity: {{SIZE}};', ], ] ); } protected function add_floating_bar_close_button_style_controls(): void { $config = static::get_configuration(); $this->add_control( 'floating_bar_close_button_heading', [ 'label' => esc_html__( 'Close Button', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', 'condition' => [ 'floating_bar_close_switch' => 'yes', ], ] ); if ( $config['style']['floating_bar_section']['has_close_position_control'] ) { $this->add_responsive_control( 'floating_bar_close_button_position', [ 'label' => esc_html__( 'Horizontal position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Left', 'elementor' ), 'icon' => 'eicon-h-align-left', ], 'end' => [ 'title' => esc_html__( 'Right', 'elementor' ), 'icon' => 'eicon-h-align-right', ], ], 'default' => 'end', 'toggle' => false, 'selectors' => $config['style']['floating_bar_section']['close_position_selectors'], 'condition' => [ 'floating_bar_close_switch' => 'yes', ], ] ); } $this->add_control( 'floating_bar_close_button_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-close-button-color: {{VALUE}}', ], 'condition' => [ 'floating_bar_close_switch' => 'yes', ], ] ); $this->add_responsive_control( 'style_floating_bar_close_button_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'min' => 0, 'max' => 150, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-close-icon-size: {{SIZE}}{{UNIT}}', ], 'condition' => [ 'floating_bar_close_switch' => 'yes', ], ] ); if ( $config['style']['floating_bar_section']['has_close_bg'] ) { $this->add_control( 'floating_bar_close_bg_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-close-button-bg-color: {{VALUE}}', ], 'condition' => [ 'floating_bar_close_switch' => 'yes', ], 'separator' => 'after', ] ); } } protected function add_floating_bar_pause_style_controls(): void { $config = static::get_configuration(); $this->add_control( 'floating_bar_pause_button_heading', [ 'label' => esc_html__( 'Pause and Play', 'elementor' ), 'type' => Controls_Manager::HEADING, 'condition' => [ 'floating_bar_pause_switch' => 'yes', ], ] ); $this->add_control( 'floating_bar_pause_button_color', [ 'label' => esc_html__( 'Icon Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-pause-play-icon-color: {{VALUE}}', ], 'condition' => [ 'floating_bar_pause_switch' => 'yes', ], ] ); $this->add_control( 'floating_bar_pause_bg_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-pause-play-bg-color: {{VALUE}}', ], 'condition' => [ 'floating_bar_pause_switch' => 'yes', ], ] ); } protected function add_floating_bar_style_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'style_floating_bar', [ 'label' => esc_html__( 'Floating Bar', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_floating_bar_close_button_style_controls(); $this->add_responsive_control( 'style_floating_bar_elements_align', [ 'label' => esc_html__( 'Align Elements', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'toggle' => false, 'default' => 'center', 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => 'eicon-align-start-h', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-align-center-h', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => 'eicon-align-end-h', ], 'space-between' => [ 'title' => esc_html__( 'Stretch', 'elementor' ), 'icon' => 'eicon-align-stretch-h', ], ], 'selectors' => $config['style']['floating_bar_section']['align_elements_selector'], 'separator' => 'before', ] ); $this->add_responsive_control( 'style_floating_bar_elements_spacing', [ 'label' => esc_html__( 'Element spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'min' => 0, 'max' => 50, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-elements-gap: {{SIZE}}{{UNIT}}', ], 'conditions' => [ 'relation' => 'and', 'terms' => [ [ 'name' => 'style_floating_bar_elements_align', 'operator' => '!==', 'value' => 'stretch', ], ], ], ] ); $this->add_responsive_control( 'style_floating_bar_elements_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem' ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-elements-padding-block-end: {{BOTTOM}}{{UNIT}}; --e-floating-bars-elements-padding-block-start: {{TOP}}{{UNIT}}; --e-floating-bars-elements-padding-inline-end: {{RIGHT}}{{UNIT}}; --e-floating-bars-elements-padding-inline-start: {{LEFT}}{{UNIT}};', ], ] ); $this->add_floating_bar_background_style_controls(); $this->end_controls_section(); } protected function add_headlines_style_section(): void { $this->start_controls_section( 'style_headlines', [ 'label' => esc_html__( 'Headline', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'style_headlines_icon_heading', [ 'label' => esc_html__( 'Icon', 'elementor' ), 'type' => Controls_Manager::HEADING, ] ); $this->add_control( 'style_headlines_icon_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-headline-icon-color: {{VALUE}}', ], ] ); $this->add_responsive_control( 'style_headlines_icon_position', [ 'label' => esc_html__( 'Icon Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'default' => is_rtl() ? 'row-reverse' : 'row', 'toggle' => false, 'options' => [ 'row' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => 'eicon-h-align-left', ], 'row-reverse' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => 'eicon-h-align-right', ], ], 'selectors_dictionary' => [ 'row' => is_rtl() ? 'row-reverse' : 'row', 'row-reverse' => is_rtl() ? 'row' : 'row-reverse', ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars__headline' => '--e-floating-bars-headline-icon-position: {{VALUE}};', ], ] ); $this->add_responsive_control( 'style_headlines_icon_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'min' => 0, 'max' => 150, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-headline-icon-size: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'style_headlines_icon_spacing', [ 'label' => esc_html__( 'Icon Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'max' => 50, ], 'em' => [ 'max' => 5, ], 'rem' => [ 'max' => 5, ], ], 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-headline-icon-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->add_control( 'style_headline_text_heading', [ 'label' => esc_html__( 'Text', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_headline_text_typography', 'selector' => '{{WRAPPER}} .e-floating-bars__headline-text', ] ); $this->start_controls_tabs( 'style_headline_tabs' ); $this->start_controls_tab( 'style_headline_tabs_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'style_headline_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-headline-text-color: {{VALUE}}', ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'style_headline_tabs_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'style_headline_text_color_hover', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-floating-bars' => '--e-floating-bars-headline-text-color-hover: {{VALUE}}', ], ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->end_controls_section(); } protected function add_advanced_tab(): void { Controls_Manager::add_tab( static::TAB_ADVANCED, esc_html__( 'Advanced', 'elementor' ) ); $this->start_controls_section( 'advanced_layout_section', [ 'label' => esc_html__( 'Layout', 'elementor' ), 'tab' => static::TAB_ADVANCED, ] ); $this->add_control( 'advanced_vertical_position', [ 'label' => esc_html__( 'Vertical Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'top' => [ 'title' => esc_html__( 'Top', 'elementor' ), 'icon' => 'eicon-v-align-top', ], 'bottom' => [ 'title' => esc_html__( 'Bottom', 'elementor' ), 'icon' => 'eicon-v-align-bottom', ], ], 'default' => 'top', 'toggle' => false, ] ); $this->add_control( 'advanced_toggle_sticky', [ 'label' => esc_html__( 'Sticky', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Yes', 'elementor' ), 'label_off' => esc_html__( 'No', 'elementor' ), 'return_value' => 'yes', 'default' => 'yes', ] ); $this->end_controls_section(); $this->start_controls_section( 'advanced_responsive_section', [ 'label' => esc_html__( 'Responsive', 'elementor' ), 'tab' => static::TAB_ADVANCED, ] ); $this->add_control( 'responsive_description', [ 'raw' => __( 'Responsive visibility will take effect only on preview mode or live page, and not while editing in Elementor.', 'elementor' ), 'type' => Controls_Manager::RAW_HTML, 'content_classes' => 'elementor-descriptor', ] ); $this->add_hidden_device_controls(); $this->end_controls_section(); $this->start_controls_section( 'advanced_custom_controls_section', [ 'label' => esc_html__( 'CSS', 'elementor' ), 'tab' => static::TAB_ADVANCED, ] ); $this->add_control( 'advanced_custom_css_id', [ 'label' => esc_html__( 'CSS ID', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => '', 'ai' => [ 'active' => false, ], 'dynamic' => [ 'active' => true, ], 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ), 'style_transfer' => false, ] ); $this->add_control( 'advanced_custom_css_classes', [ 'label' => esc_html__( 'CSS Classes', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => '', 'ai' => [ 'active' => false, ], 'dynamic' => [ 'active' => true, ], 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor' ), ] ); $this->end_controls_section(); Plugin::$instance->controls_manager->add_custom_css_controls( $this, static::TAB_ADVANCED ); Plugin::$instance->controls_manager->add_custom_attributes_controls( $this, static::TAB_ADVANCED ); } protected function add_content_tab(): void { $this->add_announcement_content_section(); $this->add_cta_button_content_section(); $this->add_floating_bar_content_section(); } protected function add_style_tab(): void { $this->add_announcement_style_section(); $this->add_cta_button_style_section(); $this->add_floating_bar_style_section(); } protected function render(): void { $render_strategy = new Floating_Bars_Core_Render( $this ); $render_strategy->render(); } } floating-buttons/base/widget-contact-button-base.php000064400000245301147207015700016633 0ustar00experiments->is_feature_active( 'e_font_icon_svg' ) ) { return parent::get_style_depends(); } return [ 'elementor-icons-fa-solid', 'elementor-icons-fa-brands', 'elementor-icons-fa-regular' ]; } public function hide_on_search(): bool { return true; } protected function get_initial_config(): array { return array_merge( parent::get_initial_config(), [ 'commonMerged' => true, ] ); } public static function get_configuration() { return [ 'content' => [ 'chat_button_section' => [ 'section_name' => esc_html__( 'Chat Button', 'elementor' ), 'has_platform' => true, 'has_icon' => false, 'icon_default' => [ 'value' => 'far fa-comment-dots', 'library' => 'fa-regular', ], 'icons_recommended' => [ 'fa-regular' => [ 'comment', 'comment-dots', 'comment-alt', ], 'fa-solid' => [ 'ellipsis-v', ], ], 'has_notification_dot' => true, 'has_notification_dot_default_enabled' => true, 'has_active_tab' => false, 'has_display_text' => false, 'display_text_label' => esc_html__( 'Call now', 'elementor' ), 'has_display_text_select' => true, 'platform' => [ 'group' => [ Social_Network_Provider::EMAIL, Social_Network_Provider::SMS, Social_Network_Provider::WHATSAPP, Social_Network_Provider::SKYPE, Social_Network_Provider::MESSENGER, Social_Network_Provider::VIBER, ], 'default' => Social_Network_Provider::WHATSAPP, ], 'chat_aria_label' => Floating_Buttons::get_title(), 'defaults' => [ 'mail' => null, 'mail_subject' => null, 'mail_body' => null, 'number' => null, 'username' => null, 'location' => [ 'is_external' => true, ], 'url' => [ 'is_external' => true, ], ], 'has_accessible_name' => true, ], 'top_bar_section' => [ 'section_name' => esc_html__( 'Top Bar', 'elementor' ), 'has_image' => true, 'has_active_dot' => true, 'has_subtitle' => true, 'title' => [ 'label' => esc_html__( 'Name', 'elementor' ), 'default' => esc_html__( 'Rob Jones', 'elementor' ), 'placeholder' => esc_html__( 'Type your name here', 'elementor' ), 'dynamic' => false, 'ai' => false, 'label_block' => false, ], 'subtitle' => [ 'label' => esc_html__( 'Title', 'elementor' ), 'default' => esc_html__( 'Store Manager', 'elementor' ), 'placeholder' => esc_html__( 'Type your title here', 'elementor' ), 'dynamic' => false, 'ai' => false, 'label_block' => false, ], ], 'message_bubble_section' => [ 'has_typing_animation' => true, ], 'contact_section' => [ 'section_name' => esc_html__( 'Contact Buttons', 'elementor' ), 'has_cta_text' => true, 'repeater' => [ 'has_tooltip' => false, 'tooltip_label' => esc_html__( 'Text', 'elementor' ), 'tooltip_default' => esc_html__( 'Tooltip', 'elementor' ), 'tooltip_placeholder' => esc_html__( 'Enter icon text', 'elementor' ), 'has_title' => false, 'has_description' => false, ], 'platform' => [ 'group-1' => [ Social_Network_Provider::EMAIL, Social_Network_Provider::SMS, Social_Network_Provider::WHATSAPP, Social_Network_Provider::SKYPE, Social_Network_Provider::MESSENGER, Social_Network_Provider::VIBER, ], 'limit' => 5, 'min_items' => 0, ], 'default' => [ [ 'contact_icon_platform' => Social_Network_Provider::WHATSAPP, ], [ 'contact_icon_platform' => Social_Network_Provider::EMAIL, ], [ 'contact_icon_platform' => Social_Network_Provider::SMS, ], [ 'contact_icon_platform' => Social_Network_Provider::VIBER, ], [ 'contact_icon_platform' => Social_Network_Provider::MESSENGER, ], ], 'has_accessible_name' => true, ], 'send_button_section' => [ 'section_name' => esc_html__( 'Send Button', 'elementor' ), 'has_link' => false, 'text' => [ 'default' => esc_html__( 'Click to start chat', 'elementor' ), ], ], ], 'style' => [ 'has_platform_colors' => true, 'chat_button_section' => [ 'has_entrance_animation' => true, 'has_box_shadow' => true, 'has_drop_shadow' => false, 'has_padding' => false, 'has_button_size' => true, 'button_size_default' => 'small', 'has_typography' => false, 'has_icon_position' => false, 'has_icon_spacing' => false, 'has_tabs' => true, 'has_platform_color_controls' => false, 'hover_animation_type' => 'default', 'icon_color_label' => esc_html__( 'Icon Color', 'elementor' ), ], 'top_bar_section' => [ 'has_title_heading' => true, 'title_heading_label' => esc_html__( 'Name', 'elementor' ), 'subtitle_heading_label' => esc_html__( 'Title', 'elementor' ), 'has_style_close_button' => true, 'has_close_button_heading' => false, 'has_background' => true, 'has_background_heading' => false, ], 'message_bubble_section' => [ 'has_chat_background' => true, ], 'contact_section' => [ 'has_buttons_heading' => true, 'buttons_heading_label' => esc_html__( 'Buttons', 'elementor' ), 'has_buttons_size' => true, 'has_box_shadow' => false, 'has_buttons_spacing' => false, 'has_hover_animation' => true, 'has_chat_box_animation' => false, 'has_icon_bg_color' => true, 'has_button_bar' => false, 'has_tabs' => true, 'has_text_color' => false, 'has_bg_color' => false, 'has_padding' => false, 'has_button_corners' => false, 'has_typography' => false, 'icon_color_label' => esc_html__( 'Icon Color', 'elementor' ), 'has_hover_transition_duration' => false, ], 'send_button_section' => [ 'has_platform_colors' => true, 'has_icon_color' => true, 'has_background_color' => true, 'has_text_color' => false, 'has_typography' => true, 'typography_selector' => '{{WRAPPER}} .e-contact-buttons__send-cta', ], 'chat_box_section' => [ 'section_name' => esc_html__( 'Chat Box', 'elementor' ), 'has_width' => true, 'has_padding' => false, ], ], 'advanced' => [ 'has_layout_position' => true, 'horizontal_position_default' => 'end', 'has_mobile_full_width' => false, 'has_vertical_offset' => true, 'has_horizontal_offset' => true, ], ]; } public function get_icon(): string { return 'eicon-commenting-o'; } public function get_categories(): array { return [ 'general' ]; } protected function register_controls(): void { $this->add_content_tab(); $this->add_style_tab(); $this->add_advanced_tab(); } private function social_media_controls(): void { $config = static::get_configuration(); $this->add_control( 'chat_button_mail', [ 'label' => esc_html__( 'Email', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'label_block' => true, 'placeholder' => esc_html__( '@', 'elementor' ), 'default' => $config['content']['chat_button_section']['defaults']['mail'], 'condition' => [ 'chat_button_platform' => Social_Network_Provider::EMAIL, ], ], ); $this->add_control( 'chat_button_mail_subject', [ 'label' => esc_html__( 'Subject', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'default' => $config['content']['chat_button_section']['defaults']['mail_subject'], 'condition' => [ 'chat_button_platform' => Social_Network_Provider::EMAIL, ], ], ); $this->add_control( 'chat_button_mail_body', [ 'label' => esc_html__( 'Message', 'elementor' ), 'type' => Controls_Manager::TEXTAREA, 'default' => $config['content']['chat_button_section']['defaults']['mail_body'], 'condition' => [ 'chat_button_platform' => Social_Network_Provider::EMAIL, ], ] ); $this->add_control( 'chat_button_number', [ 'label' => esc_html__( 'Number', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'label_block' => true, 'placeholder' => esc_html__( '+', 'elementor' ), 'default' => $config['content']['chat_button_section']['defaults']['number'], 'condition' => [ 'chat_button_platform' => [ Social_Network_Provider::SMS, Social_Network_Provider::WHATSAPP, Social_Network_Provider::VIBER, Social_Network_Provider::TELEPHONE, ], ], ], ); $this->add_control( 'chat_button_username', [ 'label' => esc_html__( 'Username', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'label_block' => true, 'default' => $config['content']['chat_button_section']['defaults']['username'], 'condition' => [ 'chat_button_platform' => [ Social_Network_Provider::SKYPE, Social_Network_Provider::MESSENGER, ], ], ], ); $this->add_control( 'chat_button_viber_action', [ 'label' => esc_html__( 'Action', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'chat', 'options' => [ 'chat' => 'Chat', 'contact' => 'Contact', ], 'condition' => [ 'chat_button_platform' => Social_Network_Provider::VIBER, ], ] ); $this->add_control( 'chat_button_waze', [ 'label' => esc_html__( 'Location', 'elementor' ), 'type' => Controls_Manager::URL, 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'label_block' => true, 'placeholder' => esc_html__( 'Paste Waze link', 'elementor' ), 'default' => $config['content']['chat_button_section']['defaults']['location'], 'condition' => [ 'chat_button_platform' => [ Social_Network_Provider::WAZE, ], ], ], ); $this->add_control( 'chat_button_url', [ 'label' => esc_html__( 'Link', 'elementor' ), 'type' => Controls_Manager::URL, 'default' => [ 'is_external' => true, ], 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'autocomplete' => true, 'label_block' => true, 'default' => $config['content']['chat_button_section']['defaults']['url'], 'condition' => [ 'chat_button_platform' => [ Social_Network_Provider::URL, ], ], ], ); } private function get_display_text_condition( $condition ) { $config = static::get_configuration(); if ( true == $config['content']['chat_button_section']['has_display_text_select'] ) { return $condition; } return null; } protected function add_chat_button_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'chat_button_section', [ 'label' => $config['content']['chat_button_section']['section_name'], 'tab' => Controls_Manager::TAB_CONTENT, ] ); if ( $config['content']['chat_button_section']['has_accessible_name'] ) { $this->add_control( 'chat_aria_label', [ 'label' => esc_html__( 'Accessible name', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => $config['content']['chat_button_section']['chat_aria_label'], 'placeholder' => esc_html__( 'Add accessible name', 'elementor' ), 'dynamic' => [ 'active' => true, ], ], ); } if ( $config['content']['chat_button_section']['has_platform'] ) { $this->add_control( 'chat_button_platform', [ 'label' => esc_html__( 'Platform', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => $config['content']['chat_button_section']['platform']['default'], 'options' => Social_Network_Provider::get_social_networks_text( $config['content']['chat_button_section']['platform']['group'] ), ] ); $this->social_media_controls(); } if ( $config['content']['chat_button_section']['has_icon'] ) { $this->add_control( 'chat_button_icon', [ 'label' => esc_html__( 'Icon', 'elementor' ), 'type' => Controls_Manager::ICONS, 'default' => $config['content']['chat_button_section']['icon_default'], 'recommended' => $config['content']['chat_button_section']['icons_recommended'], ] ); } if ( $config['content']['chat_button_section']['has_notification_dot'] ) { $notification_dot_return_value = 'yes'; $notification_dot_default = $notification_dot_return_value; // Only clear if explicitly passed if ( false === $config['content']['chat_button_section']['has_notification_dot_default_enabled'] ) { $notification_dot_default = ''; } $this->add_control( 'chat_button_show_dot', [ 'label' => esc_html__( 'Notification Dot', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Show', 'elementor' ), 'label_off' => esc_html__( 'Hide', 'elementor' ), 'return_value' => $notification_dot_return_value, 'default' => $notification_dot_default, ] ); } if ( $config['content']['chat_button_section']['has_display_text'] ) { if ( $config['content']['chat_button_section']['has_display_text_select'] ) { $this->add_control( 'chat_button_display_text_select', [ 'label' => esc_html__( 'Display Text', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'details', 'options' => [ 'details' => esc_html__( 'Contact Details', 'elementor' ), 'cta' => esc_html__( 'Call to Action', 'elementor' ), ], ] ); } $this->add_control( 'chat_button_display_text', [ 'label' => esc_html__( 'Call to Action Text', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'placeholder' => esc_html__( 'Enter the text', 'elementor' ), 'default' => $config['content']['chat_button_section']['display_text_label'], 'condition' => $this->get_display_text_condition([ 'chat_button_display_text_select' => 'cta', ] ), ], ); } $this->end_controls_section(); } protected function add_top_bar_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'top_bar_section', [ 'label' => $config['content']['top_bar_section']['section_name'], 'tab' => Controls_Manager::TAB_CONTENT, ] ); $this->add_control( 'top_bar_title', [ 'label' => $config['content']['top_bar_section']['title']['label'], 'type' => Controls_Manager::TEXT, 'default' => $config['content']['top_bar_section']['title']['default'], 'placeholder' => $config['content']['top_bar_section']['title']['placeholder'], 'dynamic' => [ 'active' => $config['content']['top_bar_section']['title']['dynamic'], ], 'ai' => [ 'active' => $config['content']['top_bar_section']['title']['ai'], ], 'label_block' => $config['content']['top_bar_section']['title']['label_block'], ] ); if ( $config['content']['top_bar_section']['has_subtitle'] ) { $this->add_control( 'top_bar_subtitle', [ 'label' => $config['content']['top_bar_section']['subtitle']['label'], 'type' => Controls_Manager::TEXT, 'default' => $config['content']['top_bar_section']['subtitle']['default'], 'placeholder' => $config['content']['top_bar_section']['subtitle']['placeholder'], $config['content']['top_bar_section']['subtitle']['dynamic'], 'ai' => [ 'active' => $config['content']['top_bar_section']['subtitle']['ai'], ], 'label_block' => $config['content']['top_bar_section']['title']['label_block'], ] ); } if ( $config['content']['top_bar_section']['has_image'] ) { $this->add_control( 'top_bar_image', [ 'label' => esc_html__( 'Profile Image', 'elementor' ), 'type' => Controls_Manager::MEDIA, 'default' => [ 'url' => Utils::get_placeholder_image_src(), ], ] ); } if ( $config['content']['top_bar_section']['has_active_dot'] ) { $this->add_control( 'top_bar_show_dot', [ 'label' => esc_html__( 'Active Dot', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Show', 'elementor' ), 'label_off' => esc_html__( 'Hide', 'elementor' ), 'return_value' => 'yes', 'default' => 'yes', ] ); } $this->end_controls_section(); } protected function add_message_bubble_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'message_bubble_section', [ 'label' => esc_html__( 'Message Bubble', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); $this->add_control( 'message_bubble_name', [ 'label' => esc_html__( 'Name', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => esc_html__( 'Rob', 'elementor' ), 'placeholder' => esc_html__( 'Type your name here', 'elementor' ), ] ); $this->add_control( 'message_bubble_body', [ 'label' => esc_html__( 'Message', 'elementor' ), 'type' => Controls_Manager::TEXTAREA, 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'default' => esc_html__( 'Hey, how can I help you today?', 'elementor' ), 'placeholder' => esc_html__( 'Message', 'elementor' ), ], ); $this->add_control( 'chat_button_time_format', [ 'label' => esc_html__( 'Time format', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => '12h', 'options' => [ '12h' => esc_html__( '2:20 PM', 'elementor' ), '24h' => esc_html__( '14:20', 'elementor' ), ], ] ); if ( $config['content']['message_bubble_section']['has_typing_animation'] ) { $this->add_control( 'chat_button_show_animation', [ 'label' => esc_html__( 'Typing Animation', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Show', 'elementor' ), 'label_off' => esc_html__( 'Hide', 'elementor' ), 'return_value' => 'yes', 'default' => 'yes', ] ); } $this->end_controls_section(); } protected function add_contact_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'contact_section', [ 'label' => $config['content']['contact_section']['section_name'], 'tab' => Controls_Manager::TAB_CONTENT, ] ); if ( $config['content']['contact_section']['has_accessible_name'] ) { $this->add_control( 'contact_aria_label', [ 'label' => esc_html__( 'Accessible name', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => $config['content']['chat_button_section']['chat_aria_label'], 'placeholder' => esc_html__( 'Add accessible name', 'elementor' ), 'dynamic' => [ 'active' => true, ], ], ); } if ( $config['content']['contact_section']['has_cta_text'] ) { $this->add_control( 'contact_cta_text', [ 'label' => esc_html__( 'Call to Action Text', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => esc_html__( 'Start conversation:', 'elementor' ), 'placeholder' => esc_html__( 'Type your text here', 'elementor' ), 'label_block' => true, ] ); } if ( $config['content']['contact_section']['platform']['limit'] ) { if ( $config['content']['contact_section']['platform']['min_items'] ) { $this->add_control( 'contact_custom_panel_alert', [ 'type' => Controls_Manager::ALERT, 'alert_type' => 'info', 'content' => sprintf( __( 'Add between %1$d to %2$d contact buttons', 'elementor' ), $config['content']['contact_section']['platform']['min_items'], $config['content']['contact_section']['platform']['limit'] ), ] ); } else { $this->add_control( 'contact_custom_panel_alert', [ 'type' => Controls_Manager::ALERT, 'alert_type' => 'info', 'content' => sprintf( __( 'Add up to %d contact buttons', 'elementor' ), $config['content']['contact_section']['platform']['limit'] ), ] ); } } $repeater = new Repeater(); $repeater->add_control( 'contact_icon_platform', [ 'label' => esc_html__( 'Platform', 'elementor' ), 'type' => Controls_Manager::SELECT, 'options' => Social_Network_Provider::get_social_networks_text( $config['content']['contact_section']['platform']['group-1'] ), 'default' => Social_Network_Provider::WHATSAPP, ], ); if ( $config['content']['contact_section']['repeater']['has_tooltip'] ) { $repeater->add_control( 'contact_tooltip', [ 'label' => $config['content']['contact_section']['repeater']['tooltip_label'], 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'default' => $config['content']['contact_section']['repeater']['tooltip_default'], 'placeholder' => $config['content']['contact_section']['repeater']['tooltip_placeholder'], ], ); } if ( $config['content']['contact_section']['repeater']['has_title'] ) { $repeater->add_control( 'contact_title', [ 'label' => 'Title', 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'default' => 'Title', 'placeholder' => esc_html__( 'Enter title', 'elementor' ), ], ); } if ( $config['content']['contact_section']['repeater']['has_description'] ) { $repeater->add_control( 'contact_description', [ 'label' => 'Description', 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'default' => 'Description', 'placeholder' => esc_html__( 'Enter description', 'elementor' ), ], ); } $repeater->add_control( 'contact_icon_mail', [ 'label' => esc_html__( 'Email', 'elementor' ), 'type' => Controls_Manager::TEXT, 'placeholder' => esc_html__( 'Enter your email', 'elementor' ), 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'label_block' => true, 'condition' => [ 'contact_icon_platform' => [ Social_Network_Provider::EMAIL, ], ], ], ); $repeater->add_control( 'contact_icon_mail_subject', [ 'label' => esc_html__( 'Subject', 'elementor' ), 'type' => Controls_Manager::TEXT, 'placeholder' => esc_html__( 'Subject', 'elementor' ), 'label_block' => true, 'condition' => [ 'contact_icon_platform' => [ Social_Network_Provider::EMAIL, ], ], ] ); $repeater->add_control( 'contact_icon_mail_body', [ 'label' => esc_html__( 'Message', 'elementor' ), 'type' => Controls_Manager::TEXTAREA, 'placeholder' => esc_html__( 'Message', 'elementor' ), 'label_block' => true, 'condition' => [ 'contact_icon_platform' => [ Social_Network_Provider::EMAIL, ], ], ] ); $repeater->add_control( 'contact_icon_number', [ 'label' => esc_html__( 'Number', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'label_block' => true, 'placeholder' => esc_html__( '+', 'elementor' ), 'condition' => [ 'contact_icon_platform' => [ Social_Network_Provider::SMS, Social_Network_Provider::WHATSAPP, Social_Network_Provider::VIBER, Social_Network_Provider::TELEPHONE, ], ], ], ); $repeater->add_control( 'contact_icon_username', [ 'label' => esc_html__( 'Username', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'label_block' => true, 'placeholder' => esc_html__( 'Enter your username', 'elementor' ), 'condition' => [ 'contact_icon_platform' => [ Social_Network_Provider::MESSENGER, Social_Network_Provider::SKYPE, ], ], ], ); $repeater->add_control( 'contact_icon_url', [ 'label' => esc_html__( 'Link', 'elementor' ), 'type' => Controls_Manager::URL, 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'autocomplete' => true, 'label_block' => true, 'condition' => [ 'contact_icon_platform' => [ Social_Network_Provider::URL, ], ], 'default' => [ 'is_external' => true, ], 'placeholder' => esc_html__( 'Paste URL or type', 'elementor' ), ], ); $repeater->add_control( 'contact_icon_waze', [ 'label' => esc_html__( 'Location', 'elementor' ), 'type' => Controls_Manager::URL, 'default' => [ 'is_external' => true, ], 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'placeholder' => esc_html__( 'Paste Waze link', 'elementor' ), 'condition' => [ 'contact_icon_platform' => [ Social_Network_Provider::WAZE, ], ], 'ai' => [ 'active' => false, ], ], ); $repeater->add_control( 'contact_icon_viber_action', [ 'label' => esc_html__( 'Action', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'chat', 'dynamic' => [ 'active' => true, ], 'options' => [ 'chat' => 'Chat', 'contact' => 'Contact', ], 'condition' => [ 'contact_icon_platform' => Social_Network_Provider::VIBER, ], ] ); $this->add_control( 'contact_repeater', [ 'max_items' => $config['content']['contact_section']['platform']['limit'], 'min_items' => $config['content']['contact_section']['platform']['min_items'], 'type' => Controls_Manager::REPEATER, 'fields' => $repeater->get_controls(), 'title_field' => $this->get_icon_title_field(), 'prevent_empty' => true, 'button_text' => esc_html__( 'Add Item', 'elementor' ), 'default' => $config['content']['contact_section']['default'], ] ); $this->end_controls_section(); } protected function get_icon_title_field(): string { $platform_icons_js = json_encode( Social_Network_Provider::get_social_networks_icons() ); $platform_text_js = json_encode( Social_Network_Provider::get_social_networks_text() ); return << {{{ text_mapping[contact_icon_platform] }}} JS; } protected function add_send_button_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'send_button_section', [ 'label' => $config['content']['send_button_section']['section_name'], 'tab' => Controls_Manager::TAB_CONTENT, ] ); $this->add_control( 'send_button_text', [ 'label' => esc_html__( 'Text', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => $config['content']['send_button_section']['text']['default'], 'placeholder' => esc_html__( 'Type your text here', 'elementor' ), 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], ] ); if ( $config['content']['send_button_section']['has_link'] ) { $this->add_control( 'send_button_url', [ 'label' => esc_html__( 'Link', 'elementor' ), 'type' => Controls_Manager::URL, 'default' => [ 'is_external' => true, ], 'dynamic' => [ 'active' => true, ], 'ai' => [ 'active' => false, ], 'autocomplete' => true, 'label_block' => true, ], ); } $this->end_controls_section(); } protected function add_content_tab(): void { $this->add_chat_button_section(); $this->add_top_bar_section(); $this->add_message_bubble_section(); $this->add_send_button_section(); } private function get_platform_color_condition( $condition ) { $config = static::get_configuration(); if ( true == $config['style']['has_platform_colors'] ) { return $condition; } return null; } protected function add_style_chat_button_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'style_chat_button', [ 'label' => $config['content']['chat_button_section']['section_name'], 'tab' => Controls_Manager::TAB_STYLE, ] ); if ( $config['style']['chat_button_section']['has_button_size'] ) { $this->add_control( 'style_chat_button_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => $config['style']['chat_button_section']['button_size_default'], 'options' => [ 'small' => esc_html__( 'Small', 'elementor' ), 'medium' => esc_html__( 'Medium', 'elementor' ), 'large' => esc_html__( 'Large', 'elementor' ), ], ] ); } if ( $config['style']['chat_button_section']['has_icon_position'] ) { $this->add_responsive_control( 'style_chat_button_horizontal_position', [ 'label' => esc_html__( 'Icon Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Left', 'elementor' ), 'icon' => 'eicon-h-align-left', ], 'end' => [ 'title' => esc_html__( 'Right', 'elementor' ), 'icon' => 'eicon-h-align-right', ], ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons__chat-button svg' => 'order: {{VALUE}};', ], 'selectors_dictionary' => [ 'start' => '-1', 'end' => '2', ], 'default' => 'start', 'mobile_default' => 'start', 'toggle' => true, ] ); } if ( $config['style']['chat_button_section']['has_icon_spacing'] ) { $this->add_responsive_control( 'style_chat_button_spacing', [ 'label' => esc_html__( 'Icon Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'min' => 10, 'max' => 100, ], 'px' => [ 'min' => 0, 'max' => 100, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-chat-button-gap: {{SIZE}}{{UNIT}}', ], 'separator' => 'before', ] ); } if ( $config['style']['chat_button_section']['has_typography'] ) { $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_top_bar_title_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__chat-button', ] ); } if ( $config['style']['chat_button_section']['has_tabs'] ) { $this->start_controls_tabs( 'style_button_color_tabs' ); $this->start_controls_tab( 'style_button_color_tabs_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); if ( $config['style']['has_platform_colors'] ) { $this->add_control( 'style_button_color_select', [ 'label' => esc_html__( 'Colors', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'default', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), 'custom' => esc_html__( 'Custom', 'elementor' ), ], ] ); } $this->add_control( 'style_button_color_icon', [ 'label' => $config['style']['chat_button_section']['icon_color_label'], 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-button-icon: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_button_color_select' => 'custom', ] ), ] ); $this->add_control( 'style_button_color_background', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-button-bg: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_button_color_select' => 'custom', ] ), ] ); $this->end_controls_tab(); $this->start_controls_tab( 'style_button_color_tabs_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); if ( $config['style']['has_platform_colors'] ) { $this->add_control( 'style_button_color_select_hover', [ 'label' => esc_html__( 'Colors', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'default', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), 'custom' => esc_html__( 'Custom', 'elementor' ), ], ] ); } $this->add_control( 'style_button_color_icon_hover', [ 'label' => $config['style']['chat_button_section']['icon_color_label'], 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-button-icon-hover: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_button_color_select_hover' => 'custom', ] ), ] ); $this->add_control( 'style_button_color_background_hover', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-button-bg-hover: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_button_color_select_hover' => 'custom', ] ), ] ); if ( 'default' == $config['style']['chat_button_section']['hover_animation_type'] ) { $this->add_hover_animation_control( 'style_button_color_hover_animation', ); } $this->end_controls_tab(); if ( $config['content']['chat_button_section']['has_active_tab'] ) { $this->start_controls_tab( 'style_button_color_tabs_active', [ 'label' => esc_html__( 'Active', 'elementor' ), ] ); $this->add_control( 'style_button_color_icon_active', [ 'label' => esc_html__( 'Icon Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-active-button-color: {{VALUE}}', ], ] ); $this->add_control( 'style_button_color_background_active', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-active-button-bg: {{VALUE}}', ], ] ); $this->end_controls_tab(); } $this->end_controls_tabs(); } if ( $config['style']['chat_button_section']['has_platform_color_controls'] ) { $this->add_control( 'style_platform_control_select', [ 'label' => esc_html__( 'Colors', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'default', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), 'custom' => esc_html__( 'Custom', 'elementor' ), ], 'separator' => 'before', ] ); $this->add_control( 'style_button_color_icon', [ 'label' => $config['style']['chat_button_section']['icon_color_label'], 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-button-icon: {{VALUE}}', ], 'condition' => [ 'style_platform_control_select' => 'custom', ], ] ); $this->add_control( 'style_button_color_background', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-button-bg: {{VALUE}}', ], 'condition' => [ 'style_platform_control_select' => 'custom', ], ] ); } if ( $config['style']['chat_button_section']['has_entrance_animation'] ) { $this->add_responsive_control( 'style_chat_button_animation', [ 'label' => esc_html__( 'Entrance Animation', 'elementor' ), 'type' => Controls_Manager::ANIMATION, 'frontend_available' => true, 'separator' => 'before', ] ); $this->add_control( 'style_chat_button_animation_duration', [ 'label' => esc_html__( 'Animation Duration', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'normal', 'options' => [ 'slow' => esc_html__( 'Slow', 'elementor' ), 'normal' => esc_html__( 'Normal', 'elementor' ), 'fast' => esc_html__( 'Fast', 'elementor' ), ], 'prefix_class' => 'animated-', ] ); $this->add_control( 'style_chat_button_animation_delay', [ 'label' => esc_html__( 'Animation Delay', 'elementor' ) . ' (ms)', 'type' => Controls_Manager::NUMBER, 'min' => 0, 'step' => 100, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-button-chat-button-animation-delay: {{SIZE}}ms;', ], 'render_type' => 'none', 'frontend_available' => true, 'separator' => 'after', ] ); } if ( $config['style']['chat_button_section']['has_box_shadow'] ) { $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'style_chat_button_box_shadow', 'selector' => '{{WRAPPER}} .e-contact-buttons__chat-button-shadow', ] ); } if ( $config['style']['chat_button_section']['has_drop_shadow'] ) { $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'style_chat_button_drop_shadow', 'fields_options' => [ 'box_shadow' => [ 'selectors' => [ '{{WRAPPER}} .e-contact-buttons__chat-button-drop-shadow' => 'filter: drop-shadow({{HORIZONTAL}}px {{VERTICAL}}px {{BLUR}}px {{COLOR}});', ], ], ], ] ); } if ( $config['style']['chat_button_section']['has_padding'] ) { $this->add_responsive_control( 'style_chat_button_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-chat-button-padding-block-end: {{BOTTOM}}{{UNIT}}; --e-contact-buttons-chat-button-padding-block-start: {{TOP}}{{UNIT}}; --e-contact-buttons-chat-button-padding-inline-end: {{RIGHT}}{{UNIT}}; --e-contact-buttons-chat-button-padding-inline-start: {{LEFT}}{{UNIT}};', ], 'separator' => 'before', ] ); } if ( 'custom' == $config['style']['chat_button_section']['hover_animation_type'] ) { $this->add_control( 'style_chat_button_custom_animation_heading', [ 'label' => esc_html__( 'Hover Animation', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_control( 'style_chat_button_custom_animation_alert', [ 'type' => Controls_Manager::ALERT, 'alert_type' => 'info', 'content' => __( 'Hover animation is desktop only', 'elementor' ), ] ); $this->add_control( 'style_chat_button_custom_animation_transition', [ 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)', 'type' => Controls_Manager::SLIDER, 'range' => [ 's' => [ 'min' => 0, 'max' => 3, 'step' => 0.1, ], ], 'default' => [ 'unit' => 's', 'size' => 0.3, ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-transition-duration: {{SIZE}}{{UNIT}}', ], ] ); } $this->end_controls_section(); } protected function add_style_top_bar_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'style_top_bar_section', [ 'label' => $config['content']['top_bar_section']['section_name'], 'tab' => Controls_Manager::TAB_STYLE, ] ); if ( $config['content']['top_bar_section']['has_image'] ) { $this->add_control( 'style_top_bar_profile_heading', [ 'label' => esc_html__( 'Profile Image', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_control( 'style_top_bar_image_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'medium', 'options' => [ 'small' => esc_html__( 'Small', 'elementor' ), 'medium' => esc_html__( 'Medium', 'elementor' ), 'large' => esc_html__( 'Large', 'elementor' ), ], ] ); } if ( $config['style']['has_platform_colors'] ) { $this->add_control( 'style_top_bar_colors', [ 'label' => esc_html__( 'Colors', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'default', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), 'custom' => esc_html__( 'Custom', 'elementor' ), ], 'separator' => 'before', ] ); } if ( $config['style']['top_bar_section']['has_title_heading'] ) { $this->add_control( 'style_top_bar_title_heading', [ 'label' => $config['style']['top_bar_section']['title_heading_label'], 'type' => Controls_Manager::HEADING, 'separator' => ! $config['style']['has_platform_colors'] ? 'before' : false, ] ); } $this->add_control( 'style_top_bar_title_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-top-bar-title: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_top_bar_colors' => 'custom', ] ), ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_top_bar_title_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__top-bar-title', ] ); if ( $config['content']['top_bar_section']['has_subtitle'] ) { $this->add_control( 'style_top_bar_subtitle_heading', [ 'label' => $config['style']['top_bar_section']['subtitle_heading_label'], 'type' => Controls_Manager::HEADING, 'separator' => false, ] ); $this->add_control( 'style_top_bar_subtitle_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-top-bar-subtitle: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_top_bar_colors' => 'custom', ] ), ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_top_bar_subtitle_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__top-bar-subtitle', ] ); } $close_and_background_partial_divider = 'before'; if ( $config['style']['top_bar_section']['has_style_close_button'] ) { if ( $config['style']['top_bar_section']['has_close_button_heading'] ) { $this->add_control( 'style_top_bar_close_button_heading', [ 'label' => esc_html__( 'Close Button', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => $close_and_background_partial_divider, 'condition' => $this->get_platform_color_condition( [ 'style_top_bar_colors' => 'custom', ] ), ] ); $close_and_background_partial_divider = false; } $this->add_control( 'style_top_bar_close_button_color', [ 'label' => esc_html__( 'Close Button Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-close-button-color: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_top_bar_colors' => 'custom', ] ), 'separator' => $close_and_background_partial_divider, ] ); $close_and_background_partial_divider = false; } if ( $config['style']['top_bar_section']['has_background'] ) { if ( $config['style']['top_bar_section']['has_background_heading'] ) { $this->add_control( 'style_top_bar_background_heading', [ 'label' => esc_html__( 'Background', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => $close_and_background_partial_divider, 'condition' => $this->get_platform_color_condition( [ 'style_top_bar_colors' => 'custom', ] ), ] ); $close_and_background_partial_divider = false; } $this->add_control( 'style_top_bar_background_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-top-bar-bg: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_top_bar_colors' => 'custom', ] ), 'separator' => $close_and_background_partial_divider, ] ); } $this->end_controls_section(); } protected function add_style_message_bubble_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'style_bubble_section', [ 'label' => esc_html__( 'Message Bubble', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); if ( $config['style']['has_platform_colors'] ) { $this->add_control( 'style_bubble_colors', [ 'label' => esc_html__( 'Colors', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'default', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), 'custom' => esc_html__( 'Custom', 'elementor' ), ], ] ); } $this->add_control( 'style_bubble_name_heading', [ 'label' => esc_html__( 'Name', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => false, ] ); $this->add_control( 'style_bubble_name_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-message-bubble-name: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_bubble_colors' => 'custom', ] ), ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_bubble_name_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__message-bubble-name', ] ); $this->add_control( 'style_bubble_message_heading', [ 'label' => esc_html__( 'Message', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => false, ] ); $this->add_control( 'style_bubble_message_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-message-bubble-body: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_bubble_colors' => 'custom', ] ), ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_bubble_message_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__message-bubble-body', ] ); $this->add_control( 'style_bubble_time_heading', [ 'label' => esc_html__( 'Time', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => false, ] ); $this->add_control( 'style_bubble_time_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-message-bubble-time: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_bubble_colors' => 'custom', ] ), ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_bubble_time_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__message-bubble-time', ] ); $this->add_control( 'style_bubble_background_color', [ 'label' => esc_html__( 'Bubble Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-message-bubble-bubble-bg: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_bubble_colors' => 'custom', ] ), 'separator' => 'before', ] ); if ( $config['style']['message_bubble_section']['has_chat_background'] ) { $this->add_control( 'style_bubble_chat_color', [ 'label' => esc_html__( 'Chat Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-message-bubble-chat-bg: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_bubble_colors' => 'custom', ] ), ] ); } $this->end_controls_section(); } protected function add_style_contact_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'style_contact_section', [ 'label' => $config['content']['contact_section']['section_name'], 'tab' => Controls_Manager::TAB_STYLE, ] ); if ( $config['content']['contact_section']['has_cta_text'] ) { $this->add_control( 'style_contact_text_heading', [ 'label' => esc_html__( 'Call to Action Text', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => false, 'condition' => $this->get_platform_color_condition( [ 'style_bubble_colors' => 'custom', ] ), ] ); $this->add_control( 'style_contact_text_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-text: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_contact_text_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__contact-text', ] ); } if ( $config['style']['contact_section']['has_buttons_heading'] ) { $this->add_control( 'style_contact_buttons_heading', [ 'label' => $config['style']['contact_section']['buttons_heading_label'], 'type' => Controls_Manager::HEADING, 'separator' => false, 'condition' => $this->get_platform_color_condition( [ 'style_bubble_colors' => 'custom', ] ), ] ); } if ( $config['style']['contact_section']['has_buttons_size'] ) { $this->add_control( 'style_contact_button_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'small', 'options' => [ 'small' => esc_html__( 'Small', 'elementor' ), 'medium' => esc_html__( 'Medium', 'elementor' ), 'large' => esc_html__( 'Large', 'elementor' ), ], ] ); } if ( $config['style']['contact_section']['has_text_color'] ) { $this->add_control( 'style_contact_button_text_color', [ 'label' => $config['style']['contact_section']['icon_color_label'], 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-button-icon: {{VALUE}}', ], ] ); } if ( $config['style']['contact_section']['has_typography'] ) { $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_contact_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__contact-icon-link', ] ); } if ( $config['style']['contact_section']['has_bg_color'] ) { $this->add_control( 'style_contact_button_bg_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-button-bg: {{VALUE}}', ], ] ); } if ( $config['style']['contact_section']['has_tabs'] ) { $this->start_controls_tabs( 'style_contact_button_color_tabs' ); $this->start_controls_tab( 'style_contact_button_color_tabs_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'style_contact_button_color_icon', [ 'label' => $config['style']['contact_section']['icon_color_label'], 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-button-icon: {{VALUE}}', ], ] ); if ( $config['style']['contact_section']['has_icon_bg_color'] ) { $this->add_control( 'style_contact_button_color_background', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-button-bg: {{VALUE}}', ], ] ); } $this->end_controls_tab(); $this->start_controls_tab( 'style_contact_button_color_tabs_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'style_contact_button_color_icon_hover', [ 'label' => esc_html__( 'Icon Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-button-icon-hover: {{VALUE}}', ], ] ); if ( $config['style']['contact_section']['has_icon_bg_color'] ) { $this->add_control( 'style_contact_button_color_background_hover', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-button-bg-hover: {{VALUE}}', ], ] ); } if ( $config['style']['contact_section']['has_hover_animation'] ) { $this->add_hover_animation_control( 'style_contact_button_hover_animation', ); } $this->end_controls_tab(); $this->end_controls_tabs(); } if ( $config['style']['contact_section']['has_buttons_spacing'] ) { $this->add_responsive_control( 'style_contact_buttons_spacing', [ 'label' => esc_html__( 'Buttons Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'min' => 10, 'max' => 100, ], 'px' => [ 'min' => 0, 'max' => 100, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-gap: {{SIZE}}{{UNIT}}', ], 'separator' => 'before', ] ); } if ( $config['style']['contact_section']['has_button_corners'] ) { $this->add_control( 'style_contact_corners', [ 'label' => esc_html__( 'Corners', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'round', 'options' => [ 'round' => esc_html__( 'Round', 'elementor' ), 'rounded' => esc_html__( 'Rounded', 'elementor' ), 'sharp' => esc_html__( 'Sharp', 'elementor' ), ], ] ); } if ( $config['style']['contact_section']['has_box_shadow'] ) { $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'style_contact_icons_box_shadow', 'selector' => '{{WRAPPER}} .e-contact-buttons__contact-box-shadow', ] ); } if ( $config['content']['contact_section']['repeater']['has_tooltip'] ) { $this->add_control( 'style_contact_tooltip_heading', [ 'label' => esc_html__( 'Tooltips', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_control( 'style_contact_tooltip_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-tooltip-text: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_contact_tooltip_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__contact-tooltip', ] ); $this->add_control( 'style_contact_tooltip_bg_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-tooltip-bg: {{VALUE}}', ], ] ); } if ( $config['style']['contact_section']['has_chat_box_animation'] ) { $this->chat_box_animation_controls(); } if ( $config['style']['contact_section']['has_button_bar'] ) { $this->add_control( 'style_contact_button_bar_heading', [ 'label' => esc_html__( 'Button Bar', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_control( 'style_contact_button_bar_bg_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-button-bar-bg: {{VALUE}}', ], ] ); $this->add_control( 'style_contact_button_bar_corners', [ 'label' => esc_html__( 'Corners', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'round', 'options' => [ 'round' => esc_html__( 'Round', 'elementor' ), 'rounded' => esc_html__( 'Rounded', 'elementor' ), 'sharp' => esc_html__( 'Sharp', 'elementor' ), ], ] ); $this->add_responsive_control( 'style_contact_button_bar_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-button-bar-padding-block-end: {{BOTTOM}}{{UNIT}}; --e-contact-buttons-button-bar-padding-block-start: {{TOP}}{{UNIT}}; --e-contact-buttons-button-bar-padding-inline-end: {{RIGHT}}{{UNIT}}; --e-contact-buttons-button-bar-padding-inline-start: {{LEFT}}{{UNIT}};', ], 'separator' => 'before', ] ); } if ( $config['style']['contact_section']['has_padding'] ) { $this->add_responsive_control( 'style_contact_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-padding-block-end: {{BOTTOM}}{{UNIT}}; --e-contact-buttons-contact-padding-block-start: {{TOP}}{{UNIT}}; --e-contact-buttons-contact-padding-inline-end: {{RIGHT}}{{UNIT}}; --e-contact-buttons-contact-padding-inline-start: {{LEFT}}{{UNIT}};', ], 'separator' => 'before', ] ); } if ( $config['style']['contact_section']['has_hover_transition_duration'] ) { $this->add_control( 'style_contact_custom_animation_heading', [ 'label' => esc_html__( 'Animation', 'elementor' ), 'type' => Controls_Manager::HEADING, ] ); $this->add_control( 'style_contact_custom_animation_alert', [ 'type' => Controls_Manager::ALERT, 'alert_type' => 'info', 'content' => __( 'Adjust transition duration to change the speed of the hover animation on desktop and the click animation on touchscreen.', 'elementor' ), ] ); $this->add_control( 'style_contact_custom_animation_transition', [ 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)', 'type' => Controls_Manager::SLIDER, 'range' => [ 's' => [ 'min' => 0, 'max' => 3, 'step' => 0.1, ], ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-transition-duration: {{SIZE}}{{UNIT}}', ], ] ); } $this->end_controls_section(); } protected function add_style_resource_links_section(): void { $this->start_controls_section( 'style_resource_links_section', [ 'label' => esc_html__( 'Resource Links', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'style_resource_links_icons_heading', [ 'label' => esc_html__( 'Icons', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => false, ] ); $this->add_control( 'style_resource_links_button_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'small', 'options' => [ 'small' => esc_html__( 'Small', 'elementor' ), 'medium' => esc_html__( 'Medium', 'elementor' ), 'large' => esc_html__( 'Large', 'elementor' ), ], ] ); $this->add_control( 'style_resource_links_color_select', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'custom', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), 'custom' => esc_html__( 'Custom', 'elementor' ), ], ] ); $this->add_control( 'style_contact_icon_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-button-icon: {{VALUE}}', ], 'condition' => [ 'style_resource_links_color_select' => 'custom', ], ] ); $this->add_control( 'style_resource_links_title_heading', [ 'label' => esc_html__( 'Title', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => false, ] ); $this->add_control( 'style_resource_links_title_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-title-text-color: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_resource_links_title_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__contact-title', ] ); $this->add_control( 'style_resource_links_description_heading', [ 'label' => esc_html__( 'Description', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => false, ] ); $this->add_control( 'style_resource_links_description_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-description-text-color: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_resource_links_description_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__contact-description', ] ); $this->add_control( 'style_resource_links_bg_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-contact-button-bg: {{VALUE}}', ], 'separator' => 'before', ] ); $this->add_hover_animation_control( 'style_resource_links_hover_animation', ); $this->end_controls_section(); } protected function add_style_info_links_section(): void { $this->start_controls_section( 'style_info_links_section', [ 'label' => esc_html__( 'Info Links', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'style_info_links_icon_position', [ 'label' => esc_html__( 'Icon Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Left', 'elementor' ), 'icon' => 'eicon-h-align-left', ], 'end' => [ 'title' => esc_html__( 'Right', 'elementor' ), 'icon' => 'eicon-h-align-right', ], ], 'default' => 'start', 'toggle' => true, ] ); $this->add_control( 'style_info_links_icon_spacing', [ 'label' => esc_html__( 'Icon Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'min' => 10, 'max' => 100, ], 'px' => [ 'min' => 0, 'max' => 20, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-icon-link-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'style_info_links_link_spacing', [ 'label' => esc_html__( 'Link Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'min' => 10, 'max' => 100, ], 'px' => [ 'min' => 0, 'max' => 10, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-icon-link-spacing: {{SIZE}}{{UNIT}}', ], ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_info_links_typography', 'selector' => '{{WRAPPER}} .e-contact-buttons__contact-icon-link', ] ); $this->start_controls_tabs( 'style_info_links_tabs' ); $this->start_controls_tab( 'style_info_links_tabs_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'style_info_links_normal_text_color', [ 'label' => esc_html__( 'Text and Icon Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-icon-link-text-color: {{VALUE}}', ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'style_info_links_tabs_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'style_info_links_hover_text_color', [ 'label' => esc_html__( 'Text and Icon Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-icon-link-text-color-hover: {{VALUE}}', ], ] ); $this->add_control( 'style_info_links_hover_animation', [ 'label' => esc_html__( 'Hover Animation', 'elementor' ), 'type' => Controls_Manager::HOVER_ANIMATION, 'frontend_available' => true, ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->add_control( 'style_info_links_dividers', [ 'label' => esc_html__( 'Dividers', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Show', 'elementor' ), 'label_off' => esc_html__( 'Hide', 'elementor' ), 'return_value' => 'yes', 'default' => 'yes', 'separator' => 'before', ] ); $this->add_control( 'style_info_links_divider_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-icon-link-divider-color: {{VALUE}}', ], 'condition' => [ 'style_info_links_dividers' => 'yes', ], ] ); $this->add_responsive_control( 'style_info_links_divider_weight', [ 'label' => esc_html__( 'Weight', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'min' => 10, 'max' => 100, ], 'px' => [ 'min' => 1, 'max' => 10, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-icon-link-divider-weight: {{SIZE}}{{UNIT}}', ], 'condition' => [ 'style_info_links_dividers' => 'yes', ], ] ); $this->end_controls_section(); } protected function add_style_send_button_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'style_send_section', [ 'label' => $config['content']['send_button_section']['section_name'], 'tab' => Controls_Manager::TAB_STYLE, ] ); if ( $config['style']['send_button_section']['has_typography'] ) { $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'style_send_typography', 'selector' => $config['style']['send_button_section']['typography_selector'], ] ); } $this->start_controls_tabs( 'style_send_tabs' ); $this->start_controls_tab( 'style_send_tabs_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); if ( $config['style']['send_button_section']['has_platform_colors'] ) { $this->add_control( 'style_send_normal_colors', [ 'label' => esc_html__( 'Colors', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'default', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), 'custom' => esc_html__( 'Custom', 'elementor' ), ], ] ); } if ( $config['style']['send_button_section']['has_icon_color'] ) { $this->add_control( 'style_send_normal_icon_color', [ 'label' => esc_html__( 'Icon Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-send-button-icon: {{VALUE}}', ], 'condition' => [ 'style_send_normal_colors' => 'custom', ], ] ); } if ( $config['style']['send_button_section']['has_text_color'] ) { $this->add_control( 'style_send_normal_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-send-button-text: {{VALUE}}', ], ] ); } if ( $config['style']['send_button_section']['has_background_color'] ) { $this->add_control( 'style_send_normal_background_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-send-button-bg: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_send_normal_colors' => 'custom', ] ), ] ); } $this->end_controls_tab(); $this->start_controls_tab( 'style_send_tabs_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); if ( $config['style']['send_button_section']['has_platform_colors'] ) { $this->add_control( 'style_send_hover_colors', [ 'label' => esc_html__( 'Colors', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'default', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), 'custom' => esc_html__( 'Custom', 'elementor' ), ], ] ); } if ( $config['style']['send_button_section']['has_icon_color'] ) { $this->add_control( 'style_send_hover_icon_color', [ 'label' => esc_html__( 'Icon Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-send-button-icon-hover: {{VALUE}}', ], 'condition' => [ 'style_send_hover_colors' => 'custom', ], ] ); } if ( $config['style']['send_button_section']['has_text_color'] ) { $this->add_control( 'style_send_hover_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-send-button-text-hover: {{VALUE}}', ], ] ); } if ( $config['style']['send_button_section']['has_background_color'] ) { $this->add_control( 'style_send_hover_background_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-send-button-bg-hover: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_send_hover_colors' => 'custom', ] ), ] ); } $this->add_hover_animation_control( 'style_send_hover_animation', ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->add_responsive_control( 'style_chat_button_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-send-button-padding-block-end: {{BOTTOM}}{{UNIT}}; --e-contact-buttons-send-button-padding-block-start: {{TOP}}{{UNIT}}; --e-contact-buttons-send-button-padding-inline-end: {{RIGHT}}{{UNIT}}; --e-contact-buttons-send-button-padding-inline-start: {{LEFT}}{{UNIT}};', ], 'separator' => 'before', ] ); $this->end_controls_section(); } protected function chat_box_animation_controls(): void { $this->add_responsive_control( 'style_chat_box_entrance_animation', [ 'label' => esc_html__( 'Open Animation', 'elementor' ), 'type' => Controls_Manager::ANIMATION, 'frontend_available' => true, 'separator' => 'before', ] ); $this->add_responsive_control( 'style_chat_box_exit_animation', [ 'label' => esc_html__( 'Close Animation', 'elementor' ), 'type' => Controls_Manager::EXIT_ANIMATION, 'frontend_available' => true, ] ); $this->add_control( 'style_chat_box_animation_duration', [ 'label' => esc_html__( 'Animation Duration', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'normal', 'options' => [ 'slow' => esc_html__( 'Slow', 'elementor' ), 'normal' => esc_html__( 'Normal', 'elementor' ), 'fast' => esc_html__( 'Fast', 'elementor' ), ], 'prefix_class' => 'animated-', ] ); } protected function add_style_chat_box_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'style_chat_box_section', [ 'label' => $config['style']['chat_box_section']['section_name'], 'tab' => Controls_Manager::TAB_STYLE, ] ); if ( $config['style']['has_platform_colors'] ) { $this->add_control( 'style_chat_box_bg_select', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'default', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), 'custom' => esc_html__( 'Custom', 'elementor' ), ], ] ); } $this->add_control( 'style_chat_box_bg_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-chat-box-bg: {{VALUE}}', ], 'condition' => $this->get_platform_color_condition( [ 'style_chat_box_bg_select' => 'custom', ] ), ] ); if ( $config['style']['chat_box_section']['has_width'] ) { $this->add_responsive_control( 'style_chat_box_width', [ 'label' => esc_html__( 'Width', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'min' => 10, 'max' => 100, ], 'px' => [ 'min' => 0, 'max' => 400, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-chat-box-width: {{SIZE}}{{UNIT}}', ], ] ); } $this->add_control( 'style_chat_box_corners', [ 'label' => esc_html__( 'Corners', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'rounded', 'options' => [ 'round' => esc_html__( 'Round', 'elementor' ), 'rounded' => esc_html__( 'Rounded', 'elementor' ), 'sharp' => esc_html__( 'Sharp', 'elementor' ), ], 'separator' => 'before', ] ); $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'style_chat_box_box_shadow', 'selector' => '{{WRAPPER}} .e-contact-buttons__content', ] ); if ( $config['style']['chat_box_section']['has_padding'] ) { $this->add_responsive_control( 'style_chat_box_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-chat-box-padding-block-end: {{BOTTOM}}{{UNIT}}; --e-contact-buttons-chat-box-padding-block-start: {{TOP}}{{UNIT}}; --e-contact-buttons-chat-box-padding-inline-end: {{RIGHT}}{{UNIT}}; --e-contact-buttons-chat-box-padding-inline-start: {{LEFT}}{{UNIT}};', ], ] ); } $this->chat_box_animation_controls(); $this->end_controls_section(); } protected function add_style_tab(): void { $this->add_style_chat_button_section(); $this->add_style_top_bar_section(); $this->add_style_message_bubble_section(); $this->add_style_send_button_section(); $this->add_style_chat_box_section(); } private function add_advanced_tab(): void { $config = static::get_configuration(); Controls_Manager::add_tab( static::TAB_ADVANCED, esc_html__( 'Advanced', 'elementor' ) ); if ( $config['advanced']['has_layout_position'] ) { $this->start_controls_section( 'advanced_layout_section', [ 'label' => esc_html__( 'Layout', 'elementor' ), 'tab' => static::TAB_ADVANCED, ] ); $this->add_control( 'advanced_horizontal_position', [ 'label' => esc_html__( 'Horizontal Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Left', 'elementor' ), 'icon' => 'eicon-h-align-left', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-h-align-center', ], 'end' => [ 'title' => esc_html__( 'Right', 'elementor' ), 'icon' => 'eicon-h-align-right', ], ], 'default' => $config['advanced']['horizontal_position_default'], 'toggle' => false, ] ); if ( $config['advanced']['has_horizontal_offset'] ) { $this->add_responsive_control( 'advanced_horizontal_offset', [ 'label' => esc_html__( 'Offset', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'min' => 10, 'max' => 100, ], 'px' => [ 'min' => 0, 'max' => 100, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-horizontal-offset: {{SIZE}}{{UNIT}}', ], 'condition' => [ 'advanced_horizontal_position' => [ 'start', 'end', ], ], ] ); } $this->add_control( 'advanced_vertical_position', [ 'label' => esc_html__( 'Vertical Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'top' => [ 'title' => esc_html__( 'Top', 'elementor' ), 'icon' => 'eicon-v-align-top', ], 'middle' => [ 'title' => esc_html__( 'Middle', 'elementor' ), 'icon' => 'eicon-v-align-middle', ], 'bottom' => [ 'title' => esc_html__( 'Bottom', 'elementor' ), 'icon' => 'eicon-v-align-bottom', ], ], 'default' => 'bottom', 'toggle' => false, ] ); if ( $config['advanced']['has_vertical_offset'] ) { $this->add_responsive_control( 'advanced_vertical_offset', [ 'label' => esc_html__( 'Offset', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'min' => 10, 'max' => 100, ], 'px' => [ 'min' => 0, 'max' => 100, ], ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .e-contact-buttons' => '--e-contact-buttons-vertical-offset: {{SIZE}}{{UNIT}}', ], 'condition' => [ 'advanced_vertical_position' => [ 'top', 'bottom', ], ], ] ); } if ( $config['advanced']['has_mobile_full_width'] ) { $this->add_control( 'advanced_mobile_full_width', [ 'label' => esc_html__( 'Full Width on Mobile', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Yes', 'elementor' ), 'label_off' => esc_html__( 'No', 'elementor' ), 'return_value' => 'yes', 'default' => 'yes', ] ); } $this->end_controls_section(); } $this->start_controls_section( 'advanced_responsive_section', [ 'label' => esc_html__( 'Responsive', 'elementor' ), 'tab' => static::TAB_ADVANCED, ] ); $this->add_control( 'responsive_description', [ 'raw' => __( 'Responsive visibility will take effect only on preview mode or live page, and not while editing in Elementor.', 'elementor' ), 'type' => Controls_Manager::RAW_HTML, 'content_classes' => 'elementor-descriptor', ] ); $this->add_hidden_device_controls(); $this->end_controls_section(); $this->start_controls_section( 'advanced_custom_controls_section', [ 'label' => esc_html__( 'CSS', 'elementor' ), 'tab' => static::TAB_ADVANCED, ] ); $this->add_control( 'advanced_custom_css_id', [ 'label' => esc_html__( 'CSS ID', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => '', 'ai' => [ 'active' => false, ], 'dynamic' => [ 'active' => true, ], 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ), 'style_transfer' => false, ] ); $this->add_control( 'advanced_custom_css_classes', [ 'label' => esc_html__( 'CSS Classes', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => '', 'ai' => [ 'active' => false, ], 'dynamic' => [ 'active' => true, ], 'title' => esc_html__( 'Add your custom class WITHOUT the dot. e.g: my-class', 'elementor' ), ] ); $this->end_controls_section(); Plugin::$instance->controls_manager->add_custom_css_controls( $this, static::TAB_ADVANCED ); Plugin::$instance->controls_manager->add_custom_attributes_controls( $this, static::TAB_ADVANCED ); } protected function render(): void { $render_strategy = new Contact_Buttons_Core_Render( $this ); $render_strategy->render(); } } floating-buttons/admin-menu-items/floating-buttons-menu-item.php000064400000001266147207015700021132 0ustar00render_callback = $render_callback; } public function render() { ( $this->render_callback )(); } } floating-buttons/control/hover-animation-floating-buttons.php000064400000000662147207015700020640 0ustar00 'Grow', 'pulse' => 'Pulse', 'push' => 'Push', 'float' => 'Float', ]; } } system-info/module.php000064400000021542147207015700011026 0ustar00 [], 'wordpress' => [], 'theme' => [], 'user' => [], 'plugins' => [], 'network_plugins' => [], 'mu_plugins' => [], ]; public function get_capability() { return $this->capability; } /** * Main system info page constructor. * * Initializing Elementor system info page. * * @since 2.9.0 * @access public */ public function __construct() { $this->add_actions(); } /** * Get default settings. * * Retrieve the default settings. Used to reset the report settings on * initialization. * * @since 2.9.0 * @access protected * * @return array Default settings. */ protected function get_init_settings() { $settings = []; $reporter_properties = Base::get_properties_keys(); array_push( $reporter_properties, 'category', 'name', 'class_name' ); $settings['reporter_properties'] = $reporter_properties; $settings['reportFilePrefix'] = ''; return $settings; } /** * Add actions. * * Register filters and actions for the main system info page. * * @since 2.9.0 * @access private */ private function add_actions() { add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu_manager ) { $this->register_menu( $admin_menu_manager ); }, Settings::ADMIN_MENU_PRIORITY + 30 ); add_action( 'wp_ajax_elementor_system_info_download_file', [ $this, 'download_file' ] ); } /** * Register admin menu. * * Add new Elementor system info admin menu. * * Fired by `admin_menu` action. * * @since 2.9.0 * @access private */ private function register_menu( Admin_Menu_Manager $admin_menu ) { $admin_menu->register( 'elementor-system-info', new System_Info_Menu_Item( $this ) ); } /** * Display page. * * Output the content for the main system info page. * * @since 2.9.0 * @access public */ public function display_page() { $reports_info = self::get_allowed_reports(); $reports = $this->load_reports( $reports_info ); ?>

print_report( $reports, 'html' ); ?>


capability ) ) { wp_die( esc_html__( 'You do not have permission to download this file.', 'elementor' ) ); } $reports_info = self::get_allowed_reports(); $reports = $this->load_reports( $reports_info ); $domain = parse_url( site_url(), PHP_URL_HOST ); header( 'Content-Type: text/plain' ); header( 'Content-Disposition:attachment; filename=system-info-' . $domain . '-' . gmdate( 'd-m-Y' ) . '.txt' ); $this->print_report( $reports ); die; } /** * Get report class. * * Retrieve the class of the report for any given report type. * * @since 2.9.0 * @access public * * @param string $reporter_type The type of the report. * * @return string The class of the report. */ public function get_reporter_class( $reporter_type ) { return __NAMESPACE__ . '\Reporters\\' . ucfirst( $reporter_type ); } /** * Load reports. * * Retrieve the system info reports. * * @since 2.9.0 * @access public * * @param array $reports An array of system info reports. * * @return array An array of system info reports. */ public function load_reports( $reports ) { $result = []; foreach ( $reports as $report_name => $report_info ) { $reporter_params = [ 'name' => $report_name, ]; $reporter_params = array_merge( $reporter_params, $report_info ); $reporter = $this->create_reporter( $reporter_params ); if ( ! $reporter instanceof Base ) { continue; } $result[ $report_name ] = [ 'report' => $reporter, 'label' => $reporter->get_title(), ]; if ( ! empty( $report_info['sub'] ) ) { $result[ $report_name ]['sub'] = $this->load_reports( $report_info['sub'] ); } } return $result; } /** * Create a report. * * Register a new report that will be displayed in Elementor system info page. * * @param array $properties Report properties. * * @return \WP_Error|false|Base Base instance if the report was created, * False or WP_Error otherwise. *@since 2.9.0 * @access public * */ public function create_reporter( array $properties ) { $properties = Model_Helper::prepare_properties( $this->get_settings( 'reporter_properties' ), $properties ); $reporter_class = $properties['class_name'] ? $properties['class_name'] : $this->get_reporter_class( $properties['name'] ); $reporter = new $reporter_class( $properties ); if ( ! ( $reporter instanceof Base ) ) { return new \WP_Error( 'Each reporter must to be an instance or sub-instance of `Base` class.' ); } if ( ! $reporter->is_enabled() ) { return false; } return $reporter; } /** * Print report. * * Output the system info page reports using an output template. * * @since 2.9.0 * @access public * * @param array $reports An array of system info reports. * @param string $template Output type from the templates folder. Available * templates are `raw` and `html`. Default is `raw`. */ public function print_report( $reports, $template = 'raw' ) { static $tabs_count = 0; $template_path = __DIR__ . '/templates/' . $template . '.php'; require $template_path; } /** * Get allowed reports. * * Retrieve the available reports in Elementor system info page. * * @since 2.9.0 * @access public * @static * * @return array Available reports in Elementor system info page. */ public static function get_allowed_reports() { do_action( 'elementor/system_info/get_allowed_reports' ); return self::$reports; } /** * Add report. * * Register a new report to Elementor system info page. * * @since 2.9.0 * @access public * @static * * @param string $report_name The name of the report. * @param array $report_info Report info. */ public static function add_report( $report_name, $report_info ) { self::$reports[ $report_name ] = $report_info; } } system-info/templates/html.php000064400000001057147207015700012502 0ustar00 $report ) : ?>
print_html(); ?>
print_html_label( ( $report['label'] ) ); ?>
$report ) : $report['report']->print_raw( $tabs_count ); if ( ! empty( $report['sub'] ) ) : $this->print_report( $report['sub'], $template, true ); endif; endforeach; $tabs_count--; system-info/helpers/model-helper.php000064400000003312147207015700013553 0ustar00system_info_page = $system_info_page; } public function is_visible() { return true; } public function get_parent_slug() { return Settings::PAGE_ID; } public function get_label() { return esc_html__( 'System Info', 'elementor' ); } public function get_page_title() { return esc_html__( 'System Info', 'elementor' ); } public function get_capability() { return $this->system_info_page->get_capability(); } public function render() { $this->system_info_page->display_page(); } } system-info/reporters/user.php000064400000003761147207015700012547 0ustar00ID; } /** * Get user reporter title. * * Retrieve user reporter title. * * @since 1.0.0 * @access public * * @return string Reporter title. */ public function get_title() { return 'User'; } /** * Get user report fields. * * Retrieve the required fields for the user report. * * @since 1.0.0 * @access public * * @return array Required report fields with field ID and field label. */ public function get_fields() { return [ 'role' => 'Role', 'locale' => 'WP Profile lang', 'agent' => 'User Agent', ]; } /** * Get user role. * * Retrieve the user role. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The user role. * } */ public function get_role() { $role = null; $current_user = wp_get_current_user(); if ( ! empty( $current_user->roles ) ) { $role = $current_user->roles[0]; } return [ 'value' => $role, ]; } /** * Get user profile language. * * Retrieve the user profile language. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value User profile language. * } */ public function get_locale() { return [ 'value' => get_bloginfo( 'language' ), ]; } /** * Get user agent. * * Retrieve user agent. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value HTTP user agent. * } */ public function get_agent() { return [ 'value' => esc_html( Utils::get_super_global_value( $_SERVER, 'HTTP_USER_AGENT' ) ), ]; } } system-info/reporters/plugins.php000064400000003740147207015700013247 0ustar00plugins ) { $this->plugins = Plugin::$instance->wp->get_active_plugins()->all(); } return $this->plugins; } /** * Get active plugins reporter title. * * Retrieve active plugins reporter title. * * @since 1.0.0 * @access public * * @return string Reporter title. */ public function get_title() { return 'Active Plugins'; } /** * Is enabled. * * Whether there are active plugins or not. * * @since 1.0.0 * @access public * * @return bool True if the site has active plugins, False otherwise. */ public function is_enabled() { return ! ! $this->get_plugins(); } /** * Get active plugins report fields. * * Retrieve the required fields for the active plugins report. * * @since 1.0.0 * @access public * * @return array Required report fields with field ID and field label. */ public function get_fields() { return [ 'active_plugins' => 'Active Plugins', ]; } /** * Get active plugins. * * Retrieve the sites active plugins. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The active plugins list. * } */ public function get_active_plugins() { return [ 'value' => $this->get_plugins(), ]; } } system-info/reporters/theme.php000064400000012020147207015700012657 0ustar00 'Name', 'version' => 'Version', 'author' => 'Author', 'is_child_theme' => 'Child Theme', ]; if ( $this->get_parent_theme() ) { $parent_fields = [ 'parent_name' => 'Parent Theme Name', 'parent_version' => 'Parent Theme Version', 'parent_author' => 'Parent Theme Author', ]; $fields = array_merge( $fields, $parent_fields ); } return $fields; } /** * Get theme. * * Retrieve the theme. * * @since 1.0.0 * @deprecated 3.1.0 Use `get_theme()` method instead. * @access protected * * @return \WP_Theme WordPress theme object. */ protected function _get_theme() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0', 'get_theme()' ); return $this->get_theme(); } /** * Get theme. * * Retrieve the theme. * * @since 3.1.0 * @access private * * @return \WP_Theme WordPress theme object. */ private function get_theme() { if ( is_null( $this->theme ) ) { $this->theme = wp_get_theme(); } return $this->theme; } /** * Get parent theme. * * Retrieve the parent theme. * * @since 1.0.0 * @access protected * * @return \WP_Theme|false WordPress theme object, or false if the current theme is not a child theme. */ protected function get_parent_theme() { return $this->get_theme()->parent(); } /** * Get theme name. * * Retrieve the theme name. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The theme name. * } */ public function get_name() { return [ 'value' => $this->get_theme()->get( 'Name' ), ]; } /** * Get theme author. * * Retrieve the theme author. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The theme author. * } */ public function get_author() { return [ 'value' => $this->get_theme()->get( 'Author' ), ]; } /** * Get theme version. * * Retrieve the theme version. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The theme version. * } */ public function get_version() { return [ 'value' => $this->get_theme()->get( 'Version' ), ]; } /** * Is the theme is a child theme. * * Whether the theme is a child theme. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Yes if the theme is a child theme, No otherwise. * @type string $recommendation Theme source code modification recommendation. * } */ public function get_is_child_theme() { $is_child_theme = is_child_theme(); $result = [ 'value' => $is_child_theme ? 'Yes' : 'No', ]; if ( ! $is_child_theme ) { $result['recommendation'] = sprintf( /* translators: %s: WordPress child themes documentation. */ __( 'If you want to modify the source code of your theme, we recommend using a child theme.', 'elementor' ), 'https://go.elementor.com/wordpress-child-themes/' ); } return $result; } /** * Get parent theme version. * * Retrieve the parent theme version. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The parent theme version. * } */ public function get_parent_version() { return [ 'value' => $this->get_parent_theme()->get( 'Version' ), ]; } /** * Get parent theme author. * * Retrieve the parent theme author. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The parent theme author. * } */ public function get_parent_author() { return [ 'value' => $this->get_parent_theme()->get( 'Author' ), ]; } /** * Get parent theme name. * * Retrieve the parent theme name. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The parent theme name. * } */ public function get_parent_name() { return [ 'value' => $this->get_parent_theme()->get( 'Name' ), ]; } } system-info/reporters/server.php000064400000027005147207015700013074 0ustar00 'Operating System', 'software' => 'Software', 'mysql_version' => 'MySQL version', 'php_version' => 'PHP Version', 'php_memory_limit' => 'PHP Memory Limit', 'php_max_input_vars' => 'PHP Max Input Vars', 'php_max_post_size' => 'PHP Max Post Size', 'gd_installed' => 'GD Installed', 'zip_installed' => 'ZIP Installed', 'write_permissions' => 'Write Permissions', 'elementor_library' => 'Elementor Library', ]; } /** * Get server operating system. * * Retrieve the server operating system. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Server operating system. * } */ public function get_os() { return [ 'value' => PHP_OS, ]; } /** * Get server software. * * Retrieve the server software. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Server software. * } */ public function get_software() { return [ 'value' => Utils::get_super_global_value( $_SERVER, 'SERVER_SOFTWARE' ), ]; } /** * Get PHP version. * * Retrieve the PHP version. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value PHP version. * @type string $recommendation Minimum PHP version recommendation. * @type bool $warning Whether to display a warning. * } */ public function get_php_version() { $result = [ 'value' => PHP_VERSION, ]; $recommended_php_version = '7.4'; if ( version_compare( $result['value'], $recommended_php_version, '<' ) ) { $result['recommendation'] = sprintf( /* translators: %s: Recommended PHP version. */ esc_html__( 'We recommend using PHP version %s or higher.', 'elementor' ), $recommended_php_version ); $result['warning'] = true; } return $result; } /** * Get PHP memory limit. * * Retrieve the PHP memory limit. * * @return array { * Report data. * * @type string $value PHP memory limit. * @type string $recommendation Recommendation memory limit. * @type bool $warning Whether to display a warning. True if the limit * is below the recommended 128M, False otherwise. * } */ public function get_php_memory_limit() { $result = [ 'value' => (string) ini_get( 'memory_limit' ), ]; $min_recommended_memory = '128M'; $preferred_memory = '256M'; $memory_limit_bytes = wp_convert_hr_to_bytes( $result['value'] ); $min_recommended_bytes = wp_convert_hr_to_bytes( $min_recommended_memory ); if ( $memory_limit_bytes < $min_recommended_bytes ) { $result['recommendation'] = sprintf( /* translators: 1: Minimum recommended_memory, 2: Preferred memory, 3: WordPress wp-config memory documentation. */ __( 'We recommend setting memory to at least %1$s. (%2$s or higher is preferred) For more information, read about how to increase memory allocated to PHP.', 'elementor' ), $min_recommended_memory, $preferred_memory, 'https://go.elementor.com/wordpress-wp-config-memory/' ); $result['warning'] = true; } return $result; } /** * Get PHP `max_input_vars`. * * Retrieve the value of `max_input_vars` from `php.ini` configuration file. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value PHP `max_input_vars`. * } */ public function get_php_max_input_vars() { return [ 'value' => ini_get( 'max_input_vars' ), ]; } /** * Get PHP `post_max_size`. * * Retrieve the value of `post_max_size` from `php.ini` configuration file. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value PHP `post_max_size`. * } */ public function get_php_max_post_size() { return [ 'value' => ini_get( 'post_max_size' ), ]; } /** * Get GD installed. * * Whether the GD extension is installed. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Yes if the GD extension is installed, No otherwise. * @type bool $warning Whether to display a warning. True if the GD extension is installed, False otherwise. * } */ public function get_gd_installed() { $gd_installed = extension_loaded( 'gd' ); return [ 'value' => $gd_installed ? 'Yes' : 'No', 'warning' => ! $gd_installed, ]; } /** * Get ZIP installed. * * Whether the ZIP extension is installed. * * @since 2.1.0 * @access public * * @return array { * Report data. * * @type string $value Yes if the ZIP extension is installed, No otherwise. * @type bool $warning Whether to display a warning. True if the ZIP extension is installed, False otherwise. * } */ public function get_zip_installed() { $zip_installed = extension_loaded( 'zip' ); return [ 'value' => $zip_installed ? 'Yes' : 'No', 'warning' => ! $zip_installed, ]; } /** * Get MySQL version. * * Retrieve the MySQL version. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value MySQL version. * } */ public function get_mysql_version() { global $wpdb; $db_server_version = $wpdb->get_results( "SHOW VARIABLES WHERE `Variable_name` IN ( 'version_comment', 'innodb_version' )", OBJECT_K ); $db_server_version_string = $db_server_version['version_comment']->Value . ' v'; // On some hosts, `innodb_version` is empty, in PHP 8.1. if ( isset( $db_server_version['innodb_version'] ) ) { $db_server_version_string .= $db_server_version['innodb_version']->Value; } else { $db_server_version_string .= $wpdb->get_var( 'SELECT VERSION() AS version' ); } return [ 'value' => $db_server_version_string, ]; } /** * Get write permissions. * Check whether the required paths for have writing permissions. * * @since 1.9.0 * @access public * * @return array { * Report data. * * @type string $value Writing permissions status. * @type bool $warning Whether to display a warning. True if some required * folders don't have writing permissions, False otherwise. * } */ public function get_write_permissions() : array { $paths_to_check = [ static::KEY_PATH_HTACCESS_FILE => $this->get_system_path( static::KEY_PATH_HTACCESS_FILE ), static::KEY_PATH_UPLOADS_DIR => $this->get_system_path( static::KEY_PATH_UPLOADS_DIR ), static::KEY_PATH_ELEMENTOR_UPLOADS_DIR => $this->get_system_path( static::KEY_PATH_ELEMENTOR_UPLOADS_DIR ), ]; $paths_permissions = $this->get_paths_permissions( $paths_to_check ); $write_problems = []; if ( ! $paths_permissions[ static::KEY_PATH_UPLOADS_DIR ]['write'] ) { $write_problems[] = 'WordPress uploads directory'; } if ( $paths_permissions[ self::KEY_PATH_ELEMENTOR_UPLOADS_DIR ]['exists'] && ! $paths_permissions[ self::KEY_PATH_ELEMENTOR_UPLOADS_DIR ]['write'] ) { $write_problems[] = 'Elementor uploads directory'; } if ( $paths_permissions[ self::KEY_PATH_HTACCESS_FILE ]['exists'] && ! $paths_permissions[ self::KEY_PATH_HTACCESS_FILE ]['write'] ) { $write_problems[] = '.htaccess file'; } if ( $write_problems ) { $value = 'There are some writing permissions issues with the following directories/files:' . "\n\t\t - "; $value .= implode( "\n\t\t - ", $write_problems ); } else { $value = 'All right'; } return [ 'value' => $value, 'warning' => ! ! $write_problems, ]; } /** * Check for elementor library connectivity. * * Check whether the remote elementor library is reachable. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The status of elementor library connectivity. * @type bool $warning Whether to display a warning. True if elementor * * library is not reachable, False otherwise. * } */ public function get_elementor_library() { $response = wp_remote_get( Api::$api_info_url, [ 'timeout' => 5, 'body' => [ // Which API version is used 'api_version' => ELEMENTOR_VERSION, // Which language to return 'site_lang' => get_bloginfo( 'language' ), ], ] ); if ( is_wp_error( $response ) ) { return [ 'value' => 'Not connected (' . $response->get_error_message() . ')', 'warning' => true, ]; } $http_response_code = wp_remote_retrieve_response_code( $response ); if ( 200 !== (int) $http_response_code ) { $error_msg = 'HTTP Error (' . $http_response_code . ')'; return [ 'value' => 'Not connected (' . $error_msg . ')', 'warning' => true, ]; } $info_data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( empty( $info_data ) ) { return [ 'value' => 'Not connected (Returns invalid JSON)', 'warning' => true, ]; } return [ 'value' => 'Connected', ]; } /** * @param $paths [] Paths to check permissions. * @return array []{exists: bool, read: bool, write: bool, execute: bool} */ public function get_paths_permissions( $paths ) : array { $permissions = []; foreach ( $paths as $key_path => $path ) { $permissions[ $key_path ] = $this->get_path_permissions( $path ); } return $permissions; } /** * Get path by path key. * * @param $path_key * @return string */ public function get_system_path( $path_key ) : string { switch ( $path_key ) { case static::KEY_PATH_WP_CONTENT_DIR: return WP_CONTENT_DIR; case static::KEY_PATH_HTACCESS_FILE: return file_exists( ABSPATH . '/.htaccess' ) ? ABSPATH . '/.htaccess' : ''; case static::KEY_PATH_UPLOADS_DIR: return wp_upload_dir()['basedir'] ?? ''; case static::KEY_PATH_ELEMENTOR_UPLOADS_DIR: if ( empty( wp_upload_dir()['basedir'] ) ) { return ''; } $elementor_uploads_dir = wp_upload_dir()['basedir'] . '/elementor'; return is_dir( $elementor_uploads_dir ) ? $elementor_uploads_dir : ''; default: return ''; } } /** * Check the permissions of a path. * * @param $path * @return array{exists: bool, read: bool, write: bool, execute: bool} */ public function get_path_permissions( $path ) : array { if ( empty( $path ) ) { return [ 'exists' => false, 'read' => false, 'write' => false, 'execute' => false, ]; } return [ 'exists' => true, 'read' => is_readable( $path ), 'write' => is_writeable( $path ), 'execute' => is_executable( $path ), ]; } } system-info/reporters/base.php000064400000012511147207015700012474 0ustar00get_report( 'html' ) as $field ) { $warning_class = ! empty( $field['warning'] ) ? ' class="elementor-warning"' : ''; $log_label = ! empty( $field['label'] ) ? $field['label'] . ':' : ''; ?> > get_report( 'raw' ); echo PHP_EOL . $indent . '== ' . $this->get_title() . ' =='; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo PHP_EOL; foreach ( $report as $field_name => $field ) : $sub_indent = str_repeat( "\t", $tabs_count ); $label = $field['label']; if ( ! empty( $label ) ) { $label .= ': '; } echo "{$sub_indent}{$label}{$field['value']}" . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped endforeach; } /** * Get report. * * Retrieve the report with all it's containing fields. * * @since 2.9.0 * @access public * * @return \WP_Error | array { * Report fields. * * @type string $name Field name. * @type string $label Field label. * } */ final public function get_report( $format = '' ) { $result = []; $format = ( empty( $format ) ) ? '' : $format . '_'; foreach ( $this->get_fields() as $field_name => $field_label ) { $method = 'get_' . $format . $field_name; if ( ! method_exists( $this, $method ) ) { $method = 'get_' . $field_name; //fallback: if ( ! method_exists( $this, $method ) ) { return new \WP_Error( sprintf( "Getter method for the field '%s' wasn't found in %s.", $field_name, get_called_class() ) ); } } $reporter_field = [ 'name' => $field_name, 'label' => $field_label, ]; $reporter_field = array_merge( $reporter_field, $this->$method() ); $result[ $field_name ] = $reporter_field; } return $result; } /** * Get properties keys. * * Retrieve the keys of the properties. * * @since 2.9.0 * @access public * @static * * @return array { * Property keys. * * @type string $name Property name. * @type string $fields Property fields. * } */ public static function get_properties_keys() { return [ 'name', 'format', 'fields', ]; } /** * Filter possible properties. * * Retrieve possible properties filtered by property keys. * * @since 2.9.0 * @access public * @static * * @param array $properties Properties to filter. * * @return array Possible properties filtered by property keys. */ final public static function filter_possible_properties( $properties ) { return Model_Helper::filter_possible_properties( self::get_properties_keys(), $properties ); } /** * Set properties. * * Add/update properties to the report. * * @since 2.9.0 * @access public * * @param array $key Property key. * @param array $value Optional. Property value. Default is `null`. */ final public function set_properties( $key, $value = null ) { if ( is_array( $key ) ) { $key = self::filter_possible_properties( $key ); foreach ( $key as $sub_key => $sub_value ) { $this->set_properties( $sub_key, $sub_value ); } return; } if ( ! in_array( $key, self::get_properties_keys(), true ) ) { return; } $this->_properties[ $key ] = $value; } /** * Reporter base constructor. * * Initializing the reporter base class. * * @since 2.9.0 * @access public * * @param array $properties Optional. Properties to filter. Default is `null`. */ public function __construct( $properties = null ) { $this->_properties = array_fill_keys( self::get_properties_keys(), null ); if ( $properties ) { $this->set_properties( $properties, null ); } } } system-info/reporters/wordpress.php000064400000012544147207015700013620 0ustar00 'Version', 'site_url' => 'Site URL', 'home_url' => 'Home URL', 'is_multisite' => 'WP Multisite', 'max_upload_size' => 'Max Upload Size', 'memory_limit' => 'Memory limit', 'max_memory_limit' => 'Max Memory limit', 'permalink_structure' => 'Permalink Structure', 'language' => 'Language', 'timezone' => 'Timezone', 'admin_email' => 'Admin Email', 'debug_mode' => 'Debug Mode', ]; } /** * Get WordPress memory limit. * * Retrieve the WordPress memory limit. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress memory limit. * } */ public function get_memory_limit() { return [ 'value' => (string) WP_MEMORY_LIMIT, ]; } /** * Get WordPress max memory limit. * * Retrieve the WordPress max memory limit. * * @return array { * Report data. * * @type string $value WordPress max memory limit. * } */ public function get_max_memory_limit() { return [ 'value' => (string) WP_MAX_MEMORY_LIMIT, ]; } /** * Get WordPress version. * * Retrieve the WordPress version. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress version. * } */ public function get_version() { return [ 'value' => get_bloginfo( 'version' ), ]; } /** * Is multisite. * * Whether multisite is enabled or not. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Yes if multisite is enabled, No otherwise. * } */ public function get_is_multisite() { return [ 'value' => is_multisite() ? 'Yes' : 'No', ]; } /** * Get site URL. * * Retrieve WordPress site URL. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress site URL. * } */ public function get_site_url() { return [ 'value' => get_site_url(), ]; } /** * Get home URL. * * Retrieve WordPress home URL. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress home URL. * } */ public function get_home_url() { return [ 'value' => get_home_url(), ]; } /** * Get permalink structure. * * Retrieve the permalink structure * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress permalink structure. * } */ public function get_permalink_structure() { global $wp_rewrite; $structure = $wp_rewrite->permalink_structure; if ( ! $structure ) { $structure = 'Plain'; } return [ 'value' => $structure, ]; } /** * Get site language. * * Retrieve the site language. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress site language. * } */ public function get_language() { return [ 'value' => get_locale(), ]; } /** * Get PHP `max_upload_size`. * * Retrieve the value of maximum upload file size defined in `php.ini` configuration file. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Maximum upload file size allowed. * } */ public function get_max_upload_size() { return [ 'value' => size_format( wp_max_upload_size() ), ]; } /** * Get WordPress timezone. * * Retrieve WordPress timezone. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress timezone. * } */ public function get_timezone() { $timezone = get_option( 'timezone_string' ); if ( ! $timezone ) { $timezone = get_option( 'gmt_offset' ); } return [ 'value' => $timezone, ]; } /** * Get WordPress administrator email. * * Retrieve WordPress administrator email. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value WordPress administrator email. * } */ public function get_admin_email() { return [ 'value' => get_option( 'admin_email' ), ]; } /** * Get debug mode. * * Whether WordPress debug mode is enabled or not. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value Active if debug mode is enabled, Inactive otherwise. * } */ public function get_debug_mode() { return [ 'value' => WP_DEBUG ? 'Active' : 'Inactive', ]; } } system-info/reporters/base-plugin.php000064400000004201147207015700013765 0ustar00get_report( 'html' ) as $field ) { foreach ( $field['value'] as $plugin_info ) : ?> %s', $plugin_info['PluginURI'], $plugin_info['Name'] ); else : $plugin_name = $plugin_info['Name']; endif; if ( $plugin_info['Version'] ) : $plugin_name .= ' - ' . $plugin_info['Version']; endif; Utils::print_unescaped_internal_string( $plugin_name ); ?> %s', $plugin_info['AuthorURI'], $plugin_info['Author'] ); else : $author = $plugin_info['Author']; endif; Utils::print_unescaped_internal_string( "By $author" ); endif; ?> get_report( 'raw' ) as $field_name => $field ) : $sub_indent = str_repeat( "\t", $tabs_count ); echo "== {$field['label']} ==" . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped foreach ( $field['value'] as $plugin_info ) : $plugin_properties = array_intersect_key( $plugin_info, $required_plugins_properties ); echo esc_html( $sub_indent . $plugin_info['Name'] ); foreach ( $plugin_properties as $property_name => $property ) : echo PHP_EOL . "{$sub_indent}\t{$property_name}: {$property}"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped endforeach; echo PHP_EOL . PHP_EOL; endforeach; endforeach; } } system-info/reporters/mu-plugins.php000064400000003660147207015700013667 0ustar00plugins ) { $this->plugins = get_mu_plugins(); } return $this->plugins; } /** * Is enabled. * * Whether there are must-use plugins or not. * * @since 1.0.0 * @access public * * @return bool True if the site has must-use plugins, False otherwise. */ public function is_enabled() { return ! ! $this->get_mu_plugins(); } /** * Get must-use plugins reporter title. * * Retrieve must-use plugins reporter title. * * @since 1.0.0 * @access public * * @return string Reporter title. */ public function get_title() { return 'Must-Use Plugins'; } /** * Get must-use plugins report fields. * * Retrieve the required fields for the must-use plugins report. * * @since 1.0.0 * @access public * * @return array Required report fields with field ID and field label. */ public function get_fields() { return [ 'must_use_plugins' => 'Must-Use Plugins', ]; } /** * Get must-use plugins. * * Retrieve the sites must-use plugins. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The must-use plugins list. * } */ public function get_must_use_plugins() { return [ 'value' => $this->get_mu_plugins(), ]; } } system-info/reporters/network-plugins.php000064400000004273147207015700014740 0ustar00plugins ) { $active_plugins = get_site_option( 'active_sitewide_plugins' ); $this->plugins = array_intersect_key( get_plugins(), $active_plugins ); } return $this->plugins; } /** * Is enabled. * * Whether there are active network plugins or not. * * @since 1.0.0 * @access public * * @return bool True if the site has active network plugins, False otherwise. */ public function is_enabled() { if ( ! is_multisite() ) { return false; }; return ! ! $this->get_network_plugins(); } /** * Get network plugins report fields. * * Retrieve the required fields for the network plugins report. * * @since 1.0.0 * @access public * * @return array Required report fields with field ID and field label. */ public function get_fields() { return [ 'network_active_plugins' => 'Network Plugins', ]; } /** * Get active network plugins. * * Retrieve the sites active network plugins. * * @since 1.0.0 * @access public * * @return array { * Report data. * * @type string $value The active network plugins list. * } */ public function get_network_active_plugins() { return [ 'value' => $this->get_network_plugins(), ]; } } nested-elements/module.php000064400000003133147207015700011641 0ustar00 self::EXPERIMENT_NAME, 'title' => esc_html__( 'Nested Elements', 'elementor' ), 'description' => sprintf( '%1$s %2$s', esc_html__( 'Create a rich user experience by layering widgets together inside "Nested" Tabs, etc. When turned on, we’ll automatically enable new nested features. Your old widgets won’t be affected.', 'elementor' ), esc_html__( 'Learn more', 'elementor' ) ), 'release_status' => Experiments_Manager::RELEASE_STATUS_BETA, 'default' => Experiments_Manager::STATE_INACTIVE, 'dependencies' => [ 'container', ], 'new_site' => [ 'default_active' => true, 'minimum_installation_version' => '3.24.0', ], ]; } public function get_name() { return 'nested-elements'; } public function __construct() { parent::__construct(); add_action( 'elementor/controls/register', function ( $controls_manager ) { $controls_manager->register( new Controls\Control_Nested_Repeater() ); } ); add_action( 'elementor/editor/before_enqueue_scripts', function () { wp_enqueue_script( $this->get_name(), $this->get_js_assets_url( $this->get_name() ), [ 'elementor-common', ], ELEMENTOR_VERSION, true ); } ); } } nested-elements/base/widget-nested-base.php000064400000007126147207015700014747 0ustar00 'container', * 'settings' => [ * '_title' => __( 'Tab #1', 'elementor' ), * ], * ], * @return string */ protected function get_default_children_title() { return esc_html__( 'Item #%d', 'elementor' ); } /** * Get default children placeholder selector, Empty string, means will be added at the end view. * * @return string */ protected function get_default_children_placeholder_selector() { return ''; } protected function get_default_children_container_placeholder_selector() { return ''; } protected function is_dynamic_content(): bool { return false; } /** * @inheritDoc * * To support nesting. */ protected function _get_default_child_type( array $element_data ) { return Plugin::$instance->elements_manager->get_element_types( $element_data['elType'] ); } /** * @inheritDoc * * Adding new 'defaults' config for handling children elements. */ protected function get_initial_config() { return array_merge( parent::get_initial_config(), [ 'defaults' => [ 'elements' => $this->get_default_children_elements(), 'elements_title' => $this->get_default_children_title(), 'elements_placeholder_selector' => $this->get_default_children_placeholder_selector(), 'child_container_placeholder_selector' => $this->get_default_children_container_placeholder_selector(), 'repeater_title_setting' => $this->get_default_repeater_title_setting_key(), ], 'support_nesting' => true, ] ); } /** * @inheritDoc * * Each element including its children elements. */ public function get_raw_data( $with_html_content = false ) { $elements = []; $data = $this->get_data(); $children = $this->get_children(); foreach ( $children as $child ) { $child_raw_data = $child->get_raw_data( $with_html_content ); $elements[] = $child_raw_data; } return [ 'id' => $this->get_id(), 'elType' => $data['elType'], 'widgetType' => $data['widgetType'], 'settings' => $data['settings'], 'elements' => $elements, ]; } /** * Print child, helper method to print the child element. * * @param int $index */ public function print_child( $index ) { $children = $this->get_children(); if ( ! empty( $children[ $index ] ) ) { $children[ $index ]->print_element(); } } protected function content_template_single_repeater_item() {} public function print_template() { parent::print_template(); if ( $this->get_initial_config()['support_improved_repeaters'] ?? false ) { ?> is_widget_present( $data ) ) { return $data; } return Plugin::$instance->db->iterate_data( $data, function ( $element ) { if ( $this->is_target_widget( $element ) ) { $element['settings']['title'] = Plugin::$instance->widgets_manager->get_widget_types( self::WIDGET_TO_SANITIZE )->sanitize( $element['settings']['title'] ); } return $element; }); } private function is_target_widget( $element ) { return self::WIDGET_TO_SANITIZE === $element['widgetType']; } private function is_widget_present( array $elements ): bool { $json = wp_json_encode( $elements ); return false !== strpos( $json, '"widgetType":"' . self::WIDGET_TO_SANITIZE . '"' ); } } content-sanitizer/interfaces/sanitizable.php000064400000000310147207015700015362 0ustar00get_js_assets_url( 'admin-notifications' ), [ 'elementor-v2-ui', 'elementor-v2-icons', 'elementor-v2-query', 'wp-i18n', ], ELEMENTOR_VERSION, true ); wp_localize_script( 'e-admin-notifications', 'elementorNotifications', $this->get_app_js_config() ); wp_set_script_translations( 'e-editor-notifications', 'elementor' ); }, 5 /* Before Elementor's admin enqueue scripts */ ); add_action( 'elementor/editor/v2/scripts/enqueue', [ $this, 'enqueue_editor_scripts' ] ); add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_editor_scripts' ] ); add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); } public function enqueue_editor_scripts() { $deps = [ 'elementor-editor', 'elementor-v2-ui', 'elementor-v2-icons', 'elementor-v2-query', 'wp-i18n', ]; $is_editor_v2 = current_action() === 'elementor/editor/v2/scripts/enqueue'; if ( $is_editor_v2 ) { $deps[] = 'elementor-v2-editor-app-bar'; } wp_enqueue_script( 'e-editor-notifications', $this->get_js_assets_url( 'editor-notifications' ), $deps, ELEMENTOR_VERSION, true ); wp_localize_script( 'e-editor-notifications', 'elementorNotifications', $this->get_app_js_config() ); wp_set_script_translations( 'e-editor-notifications', 'elementor' ); } private function get_app_js_config() : array { return [ 'is_unread' => Options::has_unread_notifications(), ]; } public function register_ajax_actions( $ajax ) { $ajax->register_ajax_action( 'notifications_get', [ $this, 'ajax_get_notifications' ] ); } public function ajax_get_notifications() { $notifications = API::get_notifications_by_conditions( true ); Options::mark_notification_read( $notifications ); return $notifications; } } notifications/api.php000064400000011606147207015700010706 0ustar00parent() ) { $theme = wp_get_theme()->parent(); } if ( $theme->get_template() === $condition['theme'] ) { $version = $theme->version; } else { $version = ''; } $result = version_compare( $version, $condition['version'], $condition['operator'] ); break; case 'introduction_meta': $result = User::get_introduction_meta( $condition['meta'] ); break; default: /** * Filters the notification condition, whether to check the group or not. * * The dynamic portion of the hook name, `$condition['type']`, refers to the condition type. * * @since 3.19.0 * * @param bool $result Whether to check the group. * @param array $condition Notification condition. */ $result = apply_filters( "elementor/notifications/condition/{$condition['type']}", $result, $condition ); break; } if ( ( $is_or_relation && $result ) || ( ! $is_or_relation && ! $result ) ) { return $result; } } return $result; } private static function get_transient( $cache_key ) { $cache = get_option( $cache_key ); if ( empty( $cache['timeout'] ) ) { return false; } if ( current_time( 'timestamp' ) > $cache['timeout'] ) { return false; } return json_decode( $cache['value'], true ); } private static function set_transient( $cache_key, $value, $expiration = '+12 hours' ) { $data = [ 'timeout' => strtotime( $expiration, current_time( 'timestamp' ) ), 'value' => json_encode( $value ), ]; return update_option( $cache_key, $data, false ); } } notifications/options.php000064400000003427147207015700011632 0ustar00ID}" ); if ( false === $unread_notifications ) { $notifications = API::get_notifications_by_conditions(); $notifications_ids = wp_list_pluck( $notifications, 'id' ); $unread_notifications = array_diff( $notifications_ids, static::get_notifications_dismissed() ); set_transient( "elementor_unread_notifications_{$current_user->ID}", $unread_notifications, HOUR_IN_SECONDS ); } return ! empty( $unread_notifications ); } public static function get_notifications_dismissed() { $current_user = wp_get_current_user(); if ( ! $current_user ) { return []; } $notifications_dismissed = get_user_meta( $current_user->ID, '_e_notifications_dismissed', true ); if ( ! is_array( $notifications_dismissed ) ) { $notifications_dismissed = []; } return $notifications_dismissed; } public static function mark_notification_read( $notifications ) : bool { $current_user = wp_get_current_user(); if ( ! $current_user ) { return false; } $notifications_dismissed = static::get_notifications_dismissed(); foreach ( $notifications as $notification ) { if ( ! in_array( $notification['id'], $notifications_dismissed, true ) ) { $notifications_dismissed[] = $notification['id']; } } $notifications_dismissed = array_unique( $notifications_dismissed ); update_user_meta( $current_user->ID, '_e_notifications_dismissed', $notifications_dismissed ); delete_transient( "elementor_unread_notifications_{$current_user->ID}" ); return true; } } image-loading-optimization/module.php000064400000025707147207015700014001 0ustar00filter_images( $buffer ); } /** * Check for image in the content provided and apply optimization logic on them. * * @param string $content Content to be analyzed. * @return string Content with optimized images. */ private function filter_images( $content ) { return preg_replace_callback( '/]+>/', function ( $matches ) { return $this->loading_optimization_image( $matches[0] ); }, $content ); } /** * Apply loading optimization logic on the image. * * @param mixed $image Original image tag. * @return string Optimized image. */ public function loading_optimization_image( $image ) { if ( isset( self::$image_visited[ $image ] ) ) { return self::$image_visited[ $image ]; } $optimized_image = $this->add_loading_optimization_attrs( $image ); self::$image_visited[ $image ] = $optimized_image; return $optimized_image; } /** * Adds optimization attributes to an `img` HTML tag. * * @param string $image The HTML `img` tag where the attribute should be added. * @return string Converted `img` tag with optimization attributes added. */ private function add_loading_optimization_attrs( $image ) { $width = preg_match( '/ width=["\']([0-9]+)["\']/', $image, $match_width ) ? (int) $match_width[1] : null; $height = preg_match( '/ height=["\']([0-9]+)["\']/', $image, $match_height ) ? (int) $match_height[1] : null; $loading_val = preg_match( '/ loading=["\']([A-Za-z]+)["\']/', $image, $match_loading ) ? $match_loading[1] : null; $fetchpriority_val = preg_match( '/ fetchpriority=["\']([A-Za-z]+)["\']/', $image, $match_fetchpriority ) ? $match_fetchpriority[1] : null; // Images should have height and dimension width for the loading optimization attributes to be added. if ( ! str_contains( $image, ' width="' ) || ! str_contains( $image, ' height="' ) ) { return $image; } $optimization_attrs = $this->get_loading_optimization_attributes( [ 'width' => $width, 'height' => $height, 'loading' => $loading_val, 'fetchpriority' => $fetchpriority_val, ] ); if ( ! empty( $optimization_attrs['fetchpriority'] ) ) { $image = str_replace( 'increase_content_media_count( 0 ); $increase_count = true; if ( $content_media_count < $this->omit_threshold ) { $maybe_in_viewport = true; } else { $maybe_in_viewport = false; } } if ( $maybe_in_viewport ) { $loading_attrs = $this->maybe_add_fetchpriority_high_attr( $loading_attrs, $attr ); } else { $loading_attrs['loading'] = 'lazy'; } if ( $increase_count ) { $this->increase_content_media_count(); } elseif ( $maybe_increase_count ) { if ( $this->get_min_priority_img_pixels() <= $attr['width'] * $attr['height'] ) { $this->increase_content_media_count(); } } return $loading_attrs; } /** * Helper to get the minimum threshold for number of pixels an image needs to have to be considered "priority". * * @return int The minimum number of pixels (width * height). Default is 50000. */ private function get_min_priority_img_pixels() { /** * Filter the minimum pixel threshold used to determine if an image should have fetchpriority="high" applied. * * @see https://developer.wordpress.org/reference/hooks/wp_min_priority_img_pixels/ * * @param int $pixels The minimum number of pixels (with * height). * @return int The filtered value. */ return apply_filters( 'elementor/image-loading-optimization/min_priority_img_pixels', $this->min_priority_img_pixels ); } /** * Keeps a count of media image. * * @param int $amount Amount by which count must be increased. * @return int current image count. */ private function increase_content_media_count( $amount = 1 ) { static $content_media_count = 0; $content_media_count += $amount; return $content_media_count; } /** * Determines whether to add `fetchpriority='high'` to loading attributes. * * @param array $loading_attrs Array of the loading optimization attributes for the element. * @param array $attr Array of the attributes for the element. * @return array Updated loading optimization attributes for the element. */ private function maybe_add_fetchpriority_high_attr( $loading_attrs, $attr ) { if ( isset( $attr['fetchpriority'] ) ) { if ( 'high' === $attr['fetchpriority'] ) { $loading_attrs['fetchpriority'] = 'high'; $this->high_priority_element_flag( false ); } return $loading_attrs; } // Lazy-loading and `fetchpriority="high"` are mutually exclusive. if ( isset( $loading_attrs['loading'] ) && 'lazy' === $loading_attrs['loading'] ) { return $loading_attrs; } if ( ! $this->high_priority_element_flag() ) { return $loading_attrs; } if ( $this->get_min_priority_img_pixels() <= $attr['width'] * $attr['height'] ) { $loading_attrs['fetchpriority'] = 'high'; $this->high_priority_element_flag( false ); } return $loading_attrs; } /** * Accesses a flag that indicates if an element is a possible candidate for `fetchpriority='high'`. * * @param bool $value Optional. Used to change the static variable. Default null. * @return bool Returns true if high-priority element was marked already, otherwise false. */ private function high_priority_element_flag( $value = null ) { static $high_priority_element = true; if ( is_bool( $value ) ) { $high_priority_element = $value; } return $high_priority_element; } } dynamic-tags/module.php000064400000005725147207015700011136 0ustar00register_groups(); add_action( 'elementor/dynamic_tags/register', [ $this, 'register_tags' ] ); } /** * Get module name. * * Retrieve the dynamic tags module name. * * @since 2.0.0 * @access public * * @return string Module name. */ public function get_name() { return 'dynamic_tags'; } /** * Get classes names. * * Retrieve the dynamic tag classes names. * * @since 2.0.0 * @access public * * @return array Tag dynamic tag classes names. */ public function get_tag_classes_names() { return []; } /** * Get groups. * * Retrieve the dynamic tag groups. * * @since 2.0.0 * @access public * * @return array Tag dynamic tag groups. */ public function get_groups() { return [ self::BASE_GROUP => [ 'title' => 'Base Tags', ], ]; } /** * Register groups. * * Add all the available tag groups. * * @since 2.0.0 * @access private */ private function register_groups() { foreach ( $this->get_groups() as $group_name => $group_settings ) { Plugin::$instance->dynamic_tags->register_group( $group_name, $group_settings ); } } /** * Register tags. * * Add all the available dynamic tags. * * @since 2.0.0 * @access public * * @param Manager $dynamic_tags */ public function register_tags( $dynamic_tags ) { foreach ( $this->get_tag_classes_names() as $tag_class ) { /** @var Base_Tag $class_name */ $class_name = $this->get_reflection()->getNamespaceName() . '\Tags\\' . $tag_class; $dynamic_tags->register( new $class_name() ); } } } container-converter/module.php000064400000010123147207015700012531 0ustar00experiments->is_feature_active( 'container' ); } /** * Enqueue the module scripts. * * @return void */ public function enqueue_scripts() { wp_enqueue_script( 'container-converter', $this->get_js_assets_url( 'container-converter' ), [ 'elementor-editor' ], ELEMENTOR_VERSION, true ); } /** * Enqueue the module styles. * * @return void */ public function enqueue_styles() { wp_enqueue_style( 'container-converter', $this->get_css_assets_url( 'modules/container-converter/editor' ), [], ELEMENTOR_VERSION ); } /** * Add a convert button to sections. * * @param \Elementor\Controls_Stack $controls_stack * * @return void */ protected function add_section_convert_button( Controls_Stack $controls_stack ) { if ( ! Plugin::$instance->editor->is_edit_mode() ) { return; } $controls_stack->start_injection( [ 'of' => '_title', ] ); $controls_stack->add_control( 'convert_to_container', [ 'type' => Controls_Manager::BUTTON, 'label' => esc_html__( 'Convert to container', 'elementor' ), 'text' => esc_html__( 'Convert', 'elementor' ), 'button_type' => 'default', 'description' => esc_html__( 'Copies all of the selected sections and columns and pastes them in a container beneath the original.', 'elementor' ), 'separator' => 'after', 'event' => static::EVENT_NAME, ] ); $controls_stack->end_injection(); } /** * Add a convert button to page settings. * * @param \Elementor\Controls_Stack $controls_stack * * @return void */ protected function add_page_convert_button( Controls_Stack $controls_stack ) { if ( ! Plugin::$instance->editor->is_edit_mode() || ! $this->page_contains_sections( $controls_stack ) || ! Plugin::$instance->role_manager->user_can( 'design' ) ) { return; } $controls_stack->start_injection( [ 'of' => 'post_title', 'at' => 'before', ] ); $controls_stack->add_control( 'convert_to_container', [ 'type' => Controls_Manager::BUTTON, 'label' => esc_html__( 'Convert to container', 'elementor' ), 'text' => esc_html__( 'Convert', 'elementor' ), 'button_type' => 'default', 'description' => esc_html__( 'Copies all of the selected sections and columns and pastes them in a container beneath the original.', 'elementor' ), 'separator' => 'after', 'event' => static::EVENT_NAME, ] ); $controls_stack->end_injection(); } /** * Checks if document has any Section elements. * * @param \Elementor\Controls_Stack $controls_stack * * @return bool */ protected function page_contains_sections( $controls_stack ) { $data = $controls_stack->get_elements_data(); if ( ! is_array( $data ) ) { return false; } foreach ( $data as $element ) { if ( isset( $element['elType'] ) && 'section' === $element['elType'] ) { return true; } } return false; } /** * Initialize the Container-Converter module. * * @return void */ public function __construct() { add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); add_action( 'elementor/editor/after_enqueue_styles', [ $this, 'enqueue_styles' ] ); add_action( 'elementor/element/section/section_layout/after_section_end', function ( Controls_Stack $controls_stack ) { $this->add_section_convert_button( $controls_stack ); } ); add_action( 'elementor/documents/register_controls', function ( Controls_Stack $controls_stack ) { $this->add_page_convert_button( $controls_stack ); } ); } } promotions/module.php000064400000005231147207015700010757 0ustar00handle_external_redirects(); } ); add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu ) { $this->register_menu_items( $admin_menu ); }, static::ADMIN_MENU_PRIORITY ); add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu ) { $this->register_promotion_menu_item( $admin_menu ); }, static::ADMIN_MENU_PROMOTIONS_PRIORITY ); add_action( 'elementor/widgets/register', function( Widgets_Manager $manager ) { foreach ( Api::get_promotion_widgets() as $widget_data ) { $manager->register( new Widgets\Pro_Widget_Promotion( [], [ 'widget_name' => $widget_data['name'], 'widget_title' => $widget_data['title'], ] ) ); } } ); } private function handle_external_redirects() { if ( empty( $_GET['page'] ) ) { return; } if ( 'go_elementor_pro' === $_GET['page'] ) { wp_redirect( Go_Pro_Promotion_Item::get_url() ); die; } } private function register_menu_items( Admin_Menu_Manager $admin_menu ) { $admin_menu->register( 'e-form-submissions', new Form_Submissions_Promotion_Item() ); $admin_menu->register( 'elementor_custom_fonts', new Custom_Fonts_Promotion_Item() ); $admin_menu->register( 'elementor_custom_icons', new Custom_Icons_Promotion_Item() ); $admin_menu->register( 'elementor_custom_code', new Custom_Code_Promotion_Item() ); $admin_menu->register( 'popup_templates', new Popups_Promotion_Item() ); } private function register_promotion_menu_item( Admin_Menu_Manager $admin_menu ) { $admin_menu->register( 'go_elementor_pro', new Go_Pro_Promotion_Item() ); } } promotions/widgets/pro-widget-promotion.php000064400000005270147207015700015250 0ustar00widget_data['widget_name']; } public function get_title() { return $this->widget_data['widget_title']; } public function on_import( $element ) { $element['settings']['__should_import'] = true; return $element; } protected function register_controls() {} protected function render() { if ( $this->is_editor_render() ) { $this->render_promotion(); } else { $this->render_empty_content(); } } private function is_editor_render(): bool { return \Elementor\Plugin::$instance->editor->is_edit_mode(); } private function render_promotion() { $promotion = Filtered_Promotions_Manager::get_filtered_promotion_data( [ 'image_url' => esc_url( $this->get_promotion_image_url() ), 'text' => sprintf( esc_html__( 'This result includes the Elementor Pro %s widget. Upgrade now to unlock it and grow your web creation toolkit.', 'elementor' ), esc_html( $this->widget_data['widget_title'] ) ), 'upgrade_url' => esc_url( 'https://go.elementor.com/go-pro-element-pro/' ), ], 'elementor/pro-widget/promotion', 'upgrade_url' ); ?>

Go Pro

widget_data = [ 'widget_name' => $args['widget_name'], 'widget_title' => $args['widget_title'], ]; parent::__construct( $data, $args ); } public function render_plain_content( $instance = [] ) {} } promotions/admin-menu-items/interfaces/promotion-menu-item.php000064400000000732147207015700020713 0ustar00promotion_data = [ 'title' => esc_html__( 'Get Popup Builder', 'elementor' ), 'content' => esc_html__( 'The Popup Builder lets you take advantage of all the amazing features in Elementor, so you can build beautiful & highly converting popups. Get Elementor Pro and start designing your popups today.', 'elementor' ), 'action_button' => [ 'text' => esc_html__( 'Upgrade Now', 'elementor' ), 'url' => 'https://go.elementor.com/go-pro-popup-builder/', ], ]; $this->promotion_data = Filtered_Promotions_Manager::get_filtered_promotion_data( $this->promotion_data, 'elementor/templates/popup', 'action_button', 'url' ); } public function get_parent_slug() { return Source_Local::ADMIN_MENU_SLUG; } public function get_name() { return 'popups'; } public function get_label() { return esc_html__( 'Popups', 'elementor' ); } public function get_page_title() { return esc_html__( 'Popups', 'elementor' ); } public function get_promotion_title() { return $this->promotion_data['title']; } public function get_promotion_description() { return $this->promotion_data['content']; } /** * @deprecated use get_promotion_description instead * @return void */ public function render_promotion_description() { echo $this->get_promotion_description(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } public function get_cta_url() { return $this->promotion_data['action_button']['url']; } public function get_cta_text() { return $this->promotion_data['action_button']['text']; } } promotions/admin-menu-items/custom-icons-promotion-item.php000064400000002220147207015700020241 0ustar00' ); } protected function get_content_lines(): array { return [ sprintf( esc_html__( 'Expand your icon library beyond FontAwesome and add icon %s libraries of your choice', 'elementor' ), '
' ), esc_html__( 'Add any icon, anywhere on your website', 'elementor' ), ]; } protected function get_cta_url(): string { return 'https://go.elementor.com/go-pro-custom-icons/'; } protected function get_video_url(): string { return 'https://www.youtube-nocookie.com/embed/PsowinxDWfM?si=SV9Z3TLz3_XEy5C6'; } } promotions/admin-menu-items/custom-fonts-promotion-item.php000064400000002205147207015700020262 0ustar00' ), ]; } protected function get_cta_url(): string { return 'https://go.elementor.com/go-pro-custom-fonts/'; } protected function get_video_url(): string { return 'https://www.youtube-nocookie.com/embed/j_guJkm28eY?si=cdd2TInwuGDTtCGD'; } } promotions/admin-menu-items/base-promotion-item.php000064400000003713147207015700016540 0ustar00 $this->get_promotion_title(), 'description' => $this->get_promotion_description(), 'image' => $this->get_image_url(), 'upgrade_text' => $this->get_cta_text(), 'upgrade_url' => $this->get_cta_url(), ]; $config = Filtered_Promotions_Manager::get_filtered_promotion_data( $config, 'elementor/' . $this->get_name() . '/custom_promotion', 'upgrade_url' ); $description = $config['description'] ?? $this->get_promotion_description() ?? ''; ?>

get_promotion_title() ); ?>

get_cta_text() ); ?>
' ); } protected function get_content_lines(): array { return [ esc_html__( 'Create single or multi-step forms to engage and convert visitors', 'elementor' ), esc_html__( 'Use any field to collect the information you need', 'elementor' ), esc_html__( 'Integrate your favorite marketing software*', 'elementor' ), esc_html__( 'Collect lead submissions directly within your WordPress Admin to manage, analyze and perform bulk actions on the submitted lead*', 'elementor' ), ]; } protected function get_cta_url():string { return 'https://go.elementor.com/go-pro-submissions/'; } protected function get_video_url():string { return 'https://www.youtube-nocookie.com/embed/LNfnwba9C-8?si=JLHk3UAexnvTfU1a'; } protected function get_side_note():string { return esc_html__( '* Requires an Advanced subscription or higher', 'elementor' ); } } promotions/admin-menu-items/go-pro-promotion-item.php000064400000003030147207015700017021 0ustar00 $upgrade_text ] )['upgrade_text'] ?? $upgrade_text; } public function get_page_title() { return ''; } public function get_capability() { return 'manage_options'; } public static function get_url() { $url = self::URL; $filtered_url = apply_filters( 'elementor/admin_menu/custom_promotion', [ 'upgrade_url' => $url ] )['upgrade_url'] ?? ''; $promotion_data = Filtered_Promotions_Manager::get_filtered_promotion_data( [ 'upgrade_url' => $filtered_url ], 'elementor/admin_menu/custom_promotion', 'upgrade_url' ); return $promotion_data ['upgrade_url']; } public function render() { // Redirects from the module on `admin_init`. die; } } promotions/admin-menu-items/base-promotion-template.php000064400000006002147207015700017407 0ustar00get_content_lines() ) ) { ?>
    get_content_lines() as $item ) { ?>
get_promotion_data(); ?> build_promotion_data_array(), 'elementor/' . $this->get_name() . '/custom_promotion', 'cta_url' ); } /** * @return array */ private function build_promotion_data_array(): array { return [ 'promotion_title' => $this->get_promotion_title(), 'cta_url' => $this->get_cta_url(), 'cta_text' => $this->get_cta_text(), 'video_url' => $this->get_video_url(), 'lines' => $this->get_lines(), 'side_note' => $this->get_side_note(), ]; } } promotions/admin-menu-items/custom-code-promotion-item.php000064400000002542147207015700020047 0ustar00get_js_assets_url( 'color-thief', 'assets/lib/color-thief/', true ), [ 'elementor-editor' ], ELEMENTOR_VERSION, true ); } /** * Module constructor - Initialize the Eye-Dropper module. * * @return void */ public function __construct() { add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); } } performance-lab/module.php000064400000003441147207015700011604 0ustar00performance_lab_get_webp_src( $value['id'], 'full', $value['url'] ); } return $value; } public function __construct() { parent::__construct(); if ( $this->is_performance_lab_is_active() ) { add_filter( 'elementor/files/css/property', function( $value, $css_property, $matches ) { return $this->replace_css_with_webp( $value, $css_property, $matches ); }, 10, 3 ); } } } editor-app-bar/module.php000064400000005001147207015700011347 0ustar00register_experiment(); if ( Plugin::$instance->experiments->is_feature_active( self::EXPERIMENT_NAME ) ) { add_filter( 'elementor/editor/v2/packages', fn( $packages ) => $this->add_packages( $packages ) ); add_filter( 'elementor/editor/v2/styles', fn( $styles ) => $this->add_styles( $styles ) ); add_filter( 'elementor/editor/templates', fn( $templates ) => $this->remove_templates( $templates ) ); add_action( 'elementor/editor/v2/scripts/enqueue', fn() => $this->dequeue_scripts() ); add_action( 'elementor/editor/v2/styles/enqueue', fn() => $this->dequeue_styles() ); } } private function register_experiment() { Plugin::$instance->experiments->add_feature( [ 'name' => self::EXPERIMENT_NAME, 'title' => esc_html__( 'Editor Top Bar', 'elementor' ), 'description' => sprintf( '%1$s %2$s', esc_html__( 'Get a sneak peek of the new Editor powered by React. The beautiful design and experimental layout of the Top bar are just some of the exciting tools on their way.', 'elementor' ), esc_html__( 'Learn more', 'elementor' ) ), 'default' => Experiments_Manager::STATE_INACTIVE, 'release_status' => Experiments_Manager::RELEASE_STATUS_STABLE, 'new_site' => [ 'default_active' => true, 'minimum_installation_version' => '3.23.0', ], ] ); } private function add_packages( $packages ) { return array_merge( $packages, self::PACKAGES ); } private function add_styles( $styles ) { return array_merge( $styles, self::STYLES ); } private function remove_templates( $templates ) { return array_diff( $templates, [ 'responsive-bar' ] ); } private function dequeue_scripts() { wp_dequeue_script( 'elementor-responsive-bar' ); } private function dequeue_styles() { wp_dequeue_style( 'elementor-responsive-bar' ); } } styleguide/module.php000064400000006464147207015700010735 0ustar00preview->is_preview(); if ( ! $is_preview ) { return; } $this->enqueue_app_initiator( $is_preview ); } ); add_action( 'elementor/controls/register', [ $this, 'register_controls' ] ); add_action( 'elementor/element/after_section_start', [ $this, 'add_styleguide_enable_controls' ], 10, 3 ); } /** * Retrieve the module name. * * @return string */ public function get_name() { return 'styleguide'; } /** * Enqueue scripts. * * @return void */ public function enqueue_main_scripts() { wp_enqueue_script( static::ASSETS_HANDLE, $this->get_js_assets_url( static::ASSETS_SRC ), [ 'elementor-editor' ], ELEMENTOR_VERSION, true ); $kit_id = Plugin::$instance->kits_manager->get_active_id(); wp_localize_script( static::ASSETS_HANDLE, 'elementorStyleguideConfig', [ 'activeKitId' => $kit_id, ] ); } public function enqueue_app_initiator( $is_preview = false ) { $dependencies = [ 'wp-i18n', 'react', 'react-dom', ]; if ( ! $is_preview ) { $dependencies[] = static::ASSETS_HANDLE; } wp_enqueue_script( static::ASSETS_HANDLE . '-app-initiator', $this->get_js_assets_url( static::ASSETS_SRC . '-app-initiator' ), $dependencies, ELEMENTOR_VERSION, true ); } public function enqueue_styles() { wp_enqueue_style( static::ASSETS_HANDLE, $this->get_css_assets_url( 'modules/' . static::ASSETS_SRC . '/' . static::ASSETS_SRC ), [], ELEMENTOR_VERSION ); } public function register_controls() { $controls_manager = Plugin::$instance->controls_manager; $controls_manager->register( new Switcher() ); } /** * Add the Enable Styleguide Preview controls to Global Colors and Global Fonts. * * @param $element * @param string $section_id * @param array $args */ public function add_styleguide_enable_controls( $element, $section_id, $args ) { if ( 'kit' !== $element->get_name() || ! in_array( $section_id, [ 'section_global_colors', 'section_text_style' ] ) ) { return; } $control_name = str_replace( 'global-', '', $args['tab'] ) . '_enable_styleguide_preview'; $element->add_control( $control_name, [ 'label' => esc_html__( 'Show global settings', 'elementor' ), 'type' => Switcher::CONTROL_TYPE, 'description' => esc_html__( 'Temporarily overlay the canvas with the style guide to preview your changes to global colors and fonts.', 'elementor' ), 'separator' => 'after', 'label_off' => esc_html__( 'No', 'elementor' ), 'label_on' => esc_html__( 'Yes', 'elementor' ), 'on_change_command' => true, ] ); } } styleguide/controls/switcher.php000064400000000762147207015700013136 0ustar00deprecation = new Deprecation( ELEMENTOR_VERSION ); add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'register_scripts' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'register_scripts' ] ); add_action( 'wp_enqueue_scripts', [ $this, 'register_scripts' ] ); add_action( 'elementor/frontend/after_register_scripts', [ $this, 'register_scripts' ] ); add_action( 'elementor/common/after_register_scripts', [ $this, 'register_scripts' ] ); } public function get_name() { return 'dev-tools'; } public function register_scripts() { wp_register_script( 'elementor-dev-tools', $this->get_js_assets_url( 'dev-tools' ), [], ELEMENTOR_VERSION, true ); $this->print_config( 'elementor-dev-tools' ); } protected function get_init_settings() { return [ 'isDebug' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ), 'urls' => [ 'assets' => ELEMENTOR_ASSETS_URL, ], 'deprecation' => $this->deprecation->get_settings(), ]; } } dev-tools/deprecation.php000064400000023015147207015700011472 0ustar00current_version = $current_version; } public function get_settings() { return [ 'soft_notices' => $this->soft_deprecated_notices, 'soft_version_count' => self::SOFT_VERSIONS_COUNT, 'hard_version_count' => self::HARD_VERSIONS_COUNT, 'current_version' => ELEMENTOR_VERSION, ]; } /** * Get total of major. * * Since `get_total_major` cannot determine how much really versions between 2.9.0 and 3.3.0 if there is 2.10.0 version for example, * versions with major2 more then 9 will be added to total. * * @since 3.1.0 * * @param array $parsed_version * * @return int */ public function get_total_major( $parsed_version ) { $major1 = $parsed_version['major1']; $major2 = $parsed_version['major2']; $major2 = $major2 > 9 ? 9 : $major2; $minor = 0; $total = intval( "{$major1}{$major2}{$minor}" ); if ( $total > 99 ) { $total = $total / 10; } else { $total = intval( $total / 10 ); } if ( $parsed_version['major2'] > 9 ) { $total += $parsed_version['major2'] - 9; } return $total; } /** * Get next version. * * @since 3.1.0 * * @param string $version * @param int $count * * @return string|false */ public function get_next_version( $version, $count = 1 ) { $version = $this->parse_version( $version ); if ( ! $version ) { return false; } $version['total'] = $this->get_total_major( $version ) + $count; $total = $version['total']; if ( $total > 9 ) { $version['major1'] = intval( $total / 10 ); $version['major2'] = $total % 10; } else { $version['major1'] = 0; $version['major2'] = $total; } $version['minor'] = 0; return $this->implode_version( $version ); } /** * Implode parsed version to string version. * * @since 3.1.0 * * @param array $parsed_version * * @return string */ public function implode_version( $parsed_version ) { $major1 = $parsed_version['major1']; $major2 = $parsed_version['major2']; $minor = $parsed_version['minor']; return "{$major1}.{$major2}.{$minor}"; } /** * Parse to an informative array. * * @since 3.1.0 * * @param string $version * * @return array|false */ public function parse_version( $version ) { $version_explode = explode( '.', $version ); $version_explode_count = count( $version_explode ); if ( $version_explode_count < 3 || $version_explode_count > 4 ) { trigger_error( 'Invalid Semantic Version string provided' ); return false; } list( $major1, $major2, $minor ) = $version_explode; $result = [ 'major1' => intval( $major1 ), 'major2' => intval( $major2 ), 'minor' => intval( $minor ), ]; if ( $version_explode_count > 3 ) { $result['build'] = $version_explode[3]; } return $result; } /** * Compare two versions, result is equal to diff of major versions. * Notice: If you want to compare between 2.9.0 and 3.3.0, and there is also a 2.10.0 version, you cannot get the right comparison * Since $this->deprecation->get_total_major cannot determine how much really versions between 2.9.0 and 3.3.0. * * @since 3.1.0 * * @param {string} $version1 * @param {string} $version2 * * @return int|false */ public function compare_version( $version1, $version2 ) { $version1 = self::parse_version( $version1 ); $version2 = self::parse_version( $version2 ); if ( $version1 && $version2 ) { $versions = [ &$version1, &$version2 ]; foreach ( $versions as &$version ) { $version['total'] = self::get_total_major( $version ); } return $version1['total'] - $version2['total']; } return false; } /** * Check Deprecation * * Checks whether the given entity is valid. If valid, this method checks whether the deprecation * should be soft (browser console notice) or hard (use WordPress' native deprecation methods). * * @since 3.1.0 * * @param string $entity - The Deprecated entity (the function/hook itself) * @param string $version * @param string $replacement Optional * @param string $base_version Optional. Default is `null` * * @return bool|void * @throws \Exception */ private function check_deprecation( $entity, $version, $replacement, $base_version = null ) { if ( null === $base_version ) { $base_version = $this->current_version; } $diff = $this->compare_version( $base_version, $version ); if ( false === $diff ) { throw new \Exception( 'Invalid deprecation diff.' ); } $print_deprecated = false; if ( defined( 'WP_DEBUG' ) && WP_DEBUG && $diff <= self::SOFT_VERSIONS_COUNT ) { // Soft deprecated. if ( ! isset( $this->soft_deprecated_notices[ $entity ] ) ) { $this->soft_deprecated_notices[ $entity ] = [ $version, $replacement, ]; } if ( defined( 'ELEMENTOR_DEBUG' ) && ELEMENTOR_DEBUG ) { $print_deprecated = true; } } return $print_deprecated; } /** * Deprecated Function * * Handles the deprecation process for functions. * * @since 3.1.0 * * @param string $function * @param string $version * @param string $replacement Optional. Default is '' * @param string $base_version Optional. Default is `null` * @throws \Exception */ public function deprecated_function( $function, $version, $replacement = '', $base_version = null ) { $print_deprecated = $this->check_deprecation( $function, $version, $replacement, $base_version ); if ( $print_deprecated ) { // PHPCS - We need to echo special characters because they can exist in function calls. _deprecated_function( $function, esc_html( $version ), $replacement ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } /** * Deprecated Hook * * Handles the deprecation process for hooks. * * @since 3.1.0 * * @param string $hook * @param string $version * @param string $replacement Optional. Default is '' * @param string $base_version Optional. Default is `null` * @throws \Exception */ public function deprecated_hook( $hook, $version, $replacement = '', $base_version = null ) { $print_deprecated = $this->check_deprecation( $hook, $version, $replacement, $base_version ); if ( $print_deprecated ) { _deprecated_hook( esc_html( $hook ), esc_html( $version ), esc_html( $replacement ) ); } } /** * Deprecated Argument * * Handles the deprecation process for function arguments. * * @since 3.1.0 * * @param string $argument * @param string $version * @param string $replacement * @param string $message * @throws \Exception */ public function deprecated_argument( $argument, $version, $replacement = '', $message = '' ) { $print_deprecated = $this->check_deprecation( $argument, $version, $replacement ); if ( $print_deprecated ) { $message = empty( $message ) ? '' : ' ' . $message; // These arguments are escaped because they are printed later, and are not escaped when printed. $error_message_args = [ esc_html( $argument ), esc_html( $version ) ]; if ( $replacement ) { /* translators: 1: Function argument, 2: Elementor version number, 3: Replacement argument name. */ $translation_string = esc_html__( 'The %1$s argument is deprecated since version %2$s! Use %3$s instead.', 'elementor' ); $error_message_args[] = $replacement; } else { /* translators: 1: Function argument, 2: Elementor version number. */ $translation_string = esc_html__( 'The %1$s argument is deprecated since version %2$s!', 'elementor' ); } trigger_error( vsprintf( // PHPCS - $translation_string is already escaped above. $translation_string, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped // PHPCS - $error_message_args is an array. $error_message_args // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ) . esc_html( $message ), E_USER_DEPRECATED ); } } /** * Do Deprecated Action * * A method used to run deprecated actions through Elementor's deprecation process. * * @since 3.1.0 * * @param string $hook * @param array $args * @param string $version * @param string $replacement * @param null|string $base_version * * @throws \Exception */ public function do_deprecated_action( $hook, $args, $version, $replacement = '', $base_version = null ) { if ( ! has_action( $hook ) ) { return; } $this->deprecated_hook( $hook, $version, $replacement, $base_version ); do_action_ref_array( $hook, $args ); } /** * Apply Deprecated Filter * * A method used to run deprecated filters through Elementor's deprecation process. * * @since 3.2.0 * * @param string $hook * @param array $args * @param string $version * @param string $replacement * @param null|string $base_version * * @return mixed * @throws \Exception */ public function apply_deprecated_filter( $hook, $args, $version, $replacement = '', $base_version = null ) { if ( ! has_action( $hook ) ) { // `$args` should be an array, but in order to keep BC, we need to support non-array values. if ( is_array( $args ) ) { return $args[0] ?? null; } return $args; } // BC - See the comment above. if ( ! is_array( $args ) ) { $args = [ $args ]; } // Avoid associative arrays. $args = array_values( $args ); $this->deprecated_hook( $hook, $version, $replacement, $base_version ); return apply_filters_ref_array( $hook, $args ); } } page-templates/module.php000064400000026055147207015700011465 0ustar00documents->get_doc_for_frontend( get_the_ID() ); if ( $document && $document::get_property( 'support_wp_page_templates' ) ) { $page_template = $document->get_meta( '_wp_page_template' ); $template_path = $this->get_template_path( $page_template ); if ( self::TEMPLATE_THEME !== $page_template && ! $template_path && $document->is_built_with_elementor() ) { $kit_default_template = Plugin::$instance->kits_manager->get_current_settings( 'default_page_template' ); $template_path = $this->get_template_path( $kit_default_template ); } if ( $template_path ) { $template = $template_path; Plugin::$instance->inspector->add_log( 'Page Template', Plugin::$instance->inspector->parse_template_path( $template ), $document->get_edit_url() ); } } } return $template; } /** * Add WordPress templates. * * Adds Elementor templates to all the post types that support * Elementor. * * Fired by `init` action. * * @since 2.0.0 * @access public */ public function add_wp_templates_support() { $post_types = get_post_types_by_support( 'elementor' ); foreach ( $post_types as $post_type ) { add_filter( "theme_{$post_type}_templates", [ $this, 'add_page_templates' ], 10, 4 ); } } /** * Add page templates. * * Add the Elementor page templates to the theme templates. * * Fired by `theme_{$post_type}_templates` filter. * * @since 2.0.0 * @access public * @static * * @param array $page_templates Array of page templates. Keys are filenames, * checks are translated names. * * @param \WP_Theme $wp_theme * @param \WP_Post $post * * @return array Page templates. */ public function add_page_templates( $page_templates, $wp_theme, $post ) { if ( $post ) { // FIX ME: Gutenberg not send $post as WP_Post object, just the post ID. $post_id = ! empty( $post->ID ) ? $post->ID : $post; $document = Plugin::$instance->documents->get( $post_id ); if ( $document && ! $document::get_property( 'support_wp_page_templates' ) ) { return $page_templates; } } $page_templates = [ self::TEMPLATE_CANVAS => esc_html__( 'Elementor Canvas', 'elementor' ), self::TEMPLATE_HEADER_FOOTER => esc_html__( 'Elementor Full Width', 'elementor' ), self::TEMPLATE_THEME => esc_html__( 'Theme', 'elementor' ), ] + $page_templates; return $page_templates; } /** * Set print callback. * * Set the page template callback. * * @since 2.0.0 * @access public * * @param callable $callback */ public function set_print_callback( $callback ) { $this->print_callback = $callback; } /** * Print callback. * * Prints the page template content using WordPress loop. * * @since 2.0.0 * @access public */ public function print_callback() { while ( have_posts() ) : the_post(); the_content(); endwhile; } /** * Print content. * * Prints the page template content. * * @since 2.0.0 * @access public */ public function print_content() { if ( ! $this->print_callback ) { $this->print_callback = [ $this, 'print_callback' ]; } call_user_func( $this->print_callback ); } /** * Get page template path. * * Retrieve the path for any given page template. * * @since 2.0.0 * @access public * * @param string $page_template The page template name. * * @return string Page template path. */ public function get_template_path( $page_template ) { $template_path = ''; switch ( $page_template ) { case self::TEMPLATE_CANVAS: $template_path = __DIR__ . '/templates/canvas.php'; break; case self::TEMPLATE_HEADER_FOOTER: $template_path = __DIR__ . '/templates/header-footer.php'; break; } return $template_path; } /** * Register template control. * * Adds custom controls to any given document. * * Fired by `update_post_metadata` action. * * @since 2.0.0 * @access public * * @param Document $document The document instance. */ public function action_register_template_control( $document ) { if ( ( $document instanceof PageBase || $document instanceof LibraryPageDocument ) && $document::get_property( 'support_page_layout' ) ) { $this->register_template_control( $document ); } } /** * Register template control. * * Adds custom controls to any given document. * * @since 2.0.0 * @access public * * @param Document $document The document instance. * @param string $control_id Optional. The control ID. Default is `template`. */ public function register_template_control( $document, $control_id = 'template' ) { if ( ! Utils::is_cpt_custom_templates_supported() ) { return; } require_once ABSPATH . '/wp-admin/includes/template.php'; $document->start_injection( [ 'of' => 'post_status', 'fallback' => [ 'of' => 'post_title', ], ] ); $control_options = [ 'options' => array_flip( get_page_templates( null, $document->get_main_post()->post_type ) ), ]; $this->add_template_controls( $document, $control_id, $control_options ); $document->end_injection(); } // The $options variable is an array of $control_options to overwrite the default public function add_template_controls( Document $document, $control_id, $control_options ) { // Default Control Options $default_control_options = [ 'label' => esc_html__( 'Page Layout', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'default', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), ], ]; $control_options = array_replace_recursive( $default_control_options, $control_options ); $document->add_control( $control_id, $control_options ); $document->add_control( $control_id . '_default_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '' . esc_html__( 'The default page template as defined in Elementor Panel → Hamburger Menu → Site Settings.', 'elementor' ) . '', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => 'default', ], ] ); $document->add_control( $control_id . '_theme_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '' . esc_html__( 'Default Page Template from your theme.', 'elementor' ) . '', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => self::TEMPLATE_THEME, ], ] ); $document->add_control( $control_id . '_canvas_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '' . esc_html__( 'No header, no footer, just Elementor', 'elementor' ) . '', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => self::TEMPLATE_CANVAS, ], ] ); $document->add_control( $control_id . '_header_footer_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '' . esc_html__( 'This template includes the header, full-width content and footer', 'elementor' ) . '', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => self::TEMPLATE_HEADER_FOOTER, ], ] ); if ( $document instanceof Kit ) { $document->add_control( 'reload_preview_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => esc_html__( 'Changes will be reflected in the preview only after the page reloads.', 'elementor' ), 'content_classes' => 'elementor-descriptor', ] ); } } /** * Filter metadata update. * * Filters whether to update metadata of a specific type. * * Elementor don't allow WordPress to update the parent page template * during `wp_update_post`. * * Fired by `update_{$meta_type}_metadata` filter. * * @since 2.0.0 * @access public * * @param bool $check Whether to allow updating metadata for the given type. * @param int $object_id Object ID. * @param string $meta_key Meta key. * * @return bool Whether to allow updating metadata of a specific type. */ public function filter_update_meta( $check, $object_id, $meta_key ) { if ( '_wp_page_template' === $meta_key && Plugin::$instance->common ) { /** @var \Elementor\Core\Common\Modules\Ajax\Module $ajax */ $ajax = Plugin::$instance->common->get_component( 'ajax' ); $ajax_data = $ajax->get_current_action_data(); $is_autosave_action = $ajax_data && 'save_builder' === $ajax_data['action'] && Document::STATUS_AUTOSAVE === $ajax_data['data']['status']; // Don't allow WP to update the parent page template. // (during `wp_update_post` from page-settings or save_plain_text). if ( $is_autosave_action && ! wp_is_post_autosave( $object_id ) && Document::STATUS_DRAFT !== get_post_status( $object_id ) ) { $check = false; } } return $check; } /** * Support `wp_body_open` action, available since WordPress 5.2. * * @since 2.7.0 * @access public */ public static function body_open() { wp_body_open(); } /** * Page templates module constructor. * * Initializing Elementor page templates module. * * @since 2.0.0 * @access public */ public function __construct() { add_action( 'init', [ $this, 'add_wp_templates_support' ] ); add_filter( 'template_include', [ $this, 'template_include' ], 11 /* After Plugins/WooCommerce */ ); add_action( 'elementor/documents/register_controls', [ $this, 'action_register_template_control' ] ); add_filter( 'update_post_metadata', [ $this, 'filter_update_meta' ], 10, 3 ); } } page-templates/templates/canvas.php000064400000002517147207015700013446 0ustar00frontend->add_body_class( 'elementor-template-canvas' ); ?> > <?php echo wp_get_document_title(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> > modules_manager->get_modules( 'page-templates' )->print_content(); /** * After canvas page template content. * * Fires after the content of Elementor canvas page template. * * @since 1.0.0 */ do_action( 'elementor/page_templates/canvas/after_content' ); wp_footer(); ?> page-templates/templates/header-footer.php000064400000001332147207015700014711 0ustar00frontend->add_body_class( 'elementor-template-full-width' ); get_header(); /** * Before Header-Footer page template content. * * Fires before the content of Elementor Header-Footer page template. * * @since 2.0.0 */ do_action( 'elementor/page_templates/header-footer/before_content' ); \Elementor\Plugin::$instance->modules_manager->get_modules( 'page-templates' )->print_content(); /** * After Header-Footer page template content. * * Fires after the content of Elementor Header-Footer page template. * * @since 2.0.0 */ do_action( 'elementor/page_templates/header-footer/after_content' ); get_footer(); nested-tabs/module.php000064400000002653147207015700010764 0ustar00experiments->is_feature_active( NestedElementsModule::EXPERIMENT_NAME ); } public function get_name() { return 'nested-tabs'; } public function __construct() { parent::__construct(); add_action( 'elementor/frontend/after_register_styles', [ $this, 'register_styles' ] ); add_action( 'elementor/editor/before_enqueue_scripts', function () { wp_enqueue_script( $this->get_name(), $this->get_js_assets_url( $this->get_name() ), [ 'nested-elements', ], ELEMENTOR_VERSION, true ); } ); } /** * Register styles. * * At build time, Elementor compiles `/modules/nested-tabs/assets/scss/frontend.scss` * to `/assets/css/widget-nested-tabs.min.css`. * * @return void */ public function register_styles() { $direction_suffix = is_rtl() ? '-rtl' : ''; $has_custom_breakpoints = Plugin::$instance->breakpoints->has_custom_breakpoints(); wp_register_style( 'widget-nested-tabs', $this->get_frontend_file_url( "widget-nested-tabs{$direction_suffix}.min.css", $has_custom_breakpoints ), [ 'elementor-frontend' ], $has_custom_breakpoints ? null : ELEMENTOR_VERSION ); } } nested-tabs/widgets/nested-tabs.php000064400000117535147207015700013364 0ustar00 'container', 'settings' => [ '_title' => sprintf( __( 'Tab #%s', 'elementor' ), $index ), 'content_width' => 'full', ], ]; } protected function get_default_children_elements() { return [ $this->tab_content_container( 1 ), $this->tab_content_container( 2 ), $this->tab_content_container( 3 ), ]; } protected function get_default_repeater_title_setting_key() { return 'tab_title'; } protected function get_default_children_title() { return esc_html__( 'Tab #%d', 'elementor' ); } protected function get_default_children_placeholder_selector() { return '.e-n-tabs-content'; } protected function get_html_wrapper_class() { return 'elementor-widget-n-tabs'; } protected function register_controls() { $start = is_rtl() ? 'right' : 'left'; $end = is_rtl() ? 'left' : 'right'; $start_logical = is_rtl() ? 'end' : 'start'; $end_logical = is_rtl() ? 'start' : 'end'; $heading_selector_non_touch_device = '{{WRAPPER}}.elementor-widget-n-tabs > .elementor-widget-container > .e-n-tabs[data-touch-mode="false"] > .e-n-tabs-heading'; $heading_selector_touch_device = '{{WRAPPER}}.elementor-widget-n-tabs > .elementor-widget-container > .e-n-tabs[data-touch-mode="true"] > .e-n-tabs-heading'; $heading_selector = '{{WRAPPER}}.elementor-widget-n-tabs > .elementor-widget-container > .e-n-tabs > .e-n-tabs-heading'; $content_selector = ':where( {{WRAPPER}}.elementor-widget-n-tabs > .elementor-widget-container > .e-n-tabs > .e-n-tabs-content ) > .e-con'; $this->start_controls_section( 'section_tabs', [ 'label' => esc_html__( 'Tabs', 'elementor' ), ] ); $repeater = new Repeater(); $repeater->add_control( 'tab_title', [ 'label' => esc_html__( 'Title', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => esc_html__( 'Tab Title', 'elementor' ), 'placeholder' => esc_html__( 'Tab Title', 'elementor' ), 'label_block' => true, 'dynamic' => [ 'active' => true, ], ] ); $repeater->add_control( 'tab_icon', [ 'label' => esc_html__( 'Icon', 'elementor' ), 'type' => Controls_Manager::ICONS, 'fa4compatibility' => 'icon', 'skin' => 'inline', 'label_block' => false, ] ); $repeater->add_control( 'tab_icon_active', [ 'label' => esc_html__( 'Active Icon', 'elementor' ), 'type' => Controls_Manager::ICONS, 'fa4compatibility' => 'icon', 'skin' => 'inline', 'label_block' => false, 'condition' => [ 'tab_icon[value]!' => '', ], ] ); $repeater->add_control( 'element_id', [ 'label' => esc_html__( 'CSS ID', 'elementor' ), 'type' => Controls_Manager::TEXT, 'default' => '', 'ai' => [ 'active' => false, ], 'dynamic' => [ 'active' => true, ], 'title' => esc_html__( 'Add your custom id WITHOUT the Pound key. e.g: my-id', 'elementor' ), 'style_transfer' => false, 'classes' => 'elementor-control-direction-ltr', ] ); $this->add_control( 'tabs', [ 'label' => esc_html__( 'Tabs Items', 'elementor' ), 'type' => Control_Nested_Repeater::CONTROL_TYPE, 'fields' => $repeater->get_controls(), 'default' => [ [ 'tab_title' => esc_html__( 'Tab #1', 'elementor' ), ], [ 'tab_title' => esc_html__( 'Tab #2', 'elementor' ), ], [ 'tab_title' => esc_html__( 'Tab #3', 'elementor' ), ], ], 'title_field' => '{{{ tab_title }}}', 'button_text' => 'Add Tab', ] ); $styling_block_start = '--n-tabs-direction: column; --n-tabs-heading-direction: row; --n-tabs-heading-width: initial; --n-tabs-title-flex-basis: content; --n-tabs-title-flex-shrink: 0;'; $styling_block_end = '--n-tabs-direction: column-reverse; --n-tabs-heading-direction: row; --n-tabs-heading-width: initial; --n-tabs-title-flex-basis: content; --n-tabs-title-flex-shrink: 0'; $styling_inline_end = '--n-tabs-direction: row-reverse; --n-tabs-heading-direction: column; --n-tabs-heading-width: 240px; --n-tabs-title-flex-basis: initial; --n-tabs-title-flex-shrink: initial;'; $styling_inline_start = '--n-tabs-direction: row; --n-tabs-heading-direction: column; --n-tabs-heading-width: 240px; --n-tabs-title-flex-basis: initial; --n-tabs-title-flex-shrink: initial;'; $this->add_responsive_control( 'tabs_direction', [ 'label' => esc_html__( 'Direction', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'block-start' => [ 'title' => esc_html__( 'Above', 'elementor' ), 'icon' => 'eicon-v-align-top', ], 'block-end' => [ 'title' => esc_html__( 'Below', 'elementor' ), 'icon' => 'eicon-v-align-bottom', ], 'inline-end' => [ 'title' => esc_html__( 'After', 'elementor' ), 'icon' => 'eicon-h-align-' . $end, ], 'inline-start' => [ 'title' => esc_html__( 'Before', 'elementor' ), 'icon' => 'eicon-h-align-' . $start, ], ], 'separator' => 'before', 'selectors_dictionary' => [ 'block-start' => $styling_block_start, 'block-end' => $styling_block_end, 'inline-end' => $styling_inline_end, 'inline-start' => $styling_inline_start, // Styling duplication for BC reasons. 'top' => $styling_block_start, 'bottom' => $styling_block_end, 'end' => $styling_inline_end, 'start' => $styling_inline_start, ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], 'control_type' => 'content', ] ); $this->add_responsive_control( 'tabs_justify_horizontal', [ 'label' => esc_html__( 'Justify', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => "eicon-align-$start_logical-h", ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-align-center-h', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => "eicon-align-$end_logical-h", ], 'stretch' => [ 'title' => esc_html__( 'Stretch', 'elementor' ), 'icon' => 'eicon-align-stretch-h', ], ], 'selectors_dictionary' => [ 'start' => '--n-tabs-heading-justify-content: flex-start; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 0;', 'center' => '--n-tabs-heading-justify-content: center; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 0;', 'end' => '--n-tabs-heading-justify-content: flex-end; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 0;', 'stretch' => '--n-tabs-heading-justify-content: initial; --n-tabs-title-width: 100%; --n-tabs-title-height: initial; --n-tabs-title-align-items: center; --n-tabs-title-flex-grow: 1;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], 'condition' => [ 'tabs_direction' => [ '', 'block-start', 'block-end', 'top', 'bottom', ], ], 'frontend_available' => true, ] ); $this->add_responsive_control( 'tabs_justify_vertical', [ 'label' => esc_html__( 'Justify', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => 'eicon-align-start-v', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-align-center-v', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => 'eicon-align-end-v', ], 'stretch' => [ 'title' => esc_html__( 'Stretch', 'elementor' ), 'icon' => 'eicon-align-stretch-v', ], ], 'selectors_dictionary' => [ 'start' => '--n-tabs-heading-justify-content: flex-start; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial; --n-tabs-heading-wrap: wrap; --n-tabs-title-flex-basis: content', 'center' => '--n-tabs-heading-justify-content: center; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial; --n-tabs-heading-wrap: wrap; --n-tabs-title-flex-basis: content', 'end' => '--n-tabs-heading-justify-content: flex-end; --n-tabs-title-width: initial; --n-tabs-title-height: initial; --n-tabs-title-align-items: initial; --n-tabs-heading-wrap: wrap; --n-tabs-title-flex-basis: content', 'stretch' => '--n-tabs-heading-justify-content: flex-start; --n-tabs-title-width: initial; --n-tabs-title-height: 100%; --n-tabs-title-align-items: center; --n-tabs-heading-wrap: nowrap; --n-tabs-title-flex-basis: auto', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], 'condition' => [ 'tabs_direction' => [ 'inline-start', 'inline-end', 'start', 'end', ], ], ] ); $this->add_responsive_control( 'tabs_width', [ 'label' => esc_html__( 'Width', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'min' => 10, 'max' => 50, ], 'px' => [ 'min' => 20, 'max' => 600, ], ], 'default' => [ 'unit' => '%', ], 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-heading-width: {{SIZE}}{{UNIT}}', ], 'condition' => [ 'tabs_direction' => [ 'inline-start', 'inline-end', 'start', 'end', ], ], ] ); $this->add_responsive_control( 'title_alignment', [ 'label' => esc_html__( 'Align Title', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'start' => [ 'title' => esc_html__( 'Start', 'elementor' ), 'icon' => 'eicon-text-align-left', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor' ), 'icon' => 'eicon-text-align-center', ], 'end' => [ 'title' => esc_html__( 'End', 'elementor' ), 'icon' => 'eicon-text-align-right', ], ], 'selectors_dictionary' => [ 'start' => '--n-tabs-title-justify-content: flex-start; --n-tabs-title-align-items: flex-start; --n-tabs-title-text-align: start;', 'center' => '--n-tabs-title-justify-content: center; --n-tabs-title-align-items: center; --n-tabs-title-text-align: center;', 'end' => '--n-tabs-title-justify-content: flex-end; --n-tabs-title-align-items: flex-end; --n-tabs-title-text-align: end;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], ] ); $this->end_controls_section(); $this->start_controls_section( 'section_tabs_responsive', [ 'label' => esc_html__( 'Additional Settings', 'elementor' ), ] ); $this->add_responsive_control( 'horizontal_scroll', [ 'label' => esc_html__( 'Horizontal Scroll', 'elementor' ), 'type' => Controls_Manager::SELECT, 'description' => esc_html__( 'Note: Scroll tabs if they don’t fit into their parent container.', 'elementor' ), 'options' => [ 'disable' => esc_html__( 'Disable', 'elementor' ), 'enable' => esc_html__( 'Enable', 'elementor' ), ], 'default' => 'disable', 'selectors_dictionary' => [ 'disable' => '--n-tabs-heading-wrap: wrap; --n-tabs-heading-overflow-x: initial; --n-tabs-title-white-space: initial;', 'enable' => '--n-tabs-heading-wrap: nowrap; --n-tabs-heading-overflow-x: scroll; --n-tabs-title-white-space: nowrap;', ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], 'frontend_available' => true, 'condition' => [ 'tabs_direction' => [ '', 'block-start', 'block-end', 'top', 'bottom', ], ], ] ); $dropdown_options = [ 'none' => esc_html__( 'None', 'elementor' ), ]; $excluded_breakpoints = [ 'laptop', 'tablet_extra', 'widescreen', ]; foreach ( Plugin::$instance->breakpoints->get_active_breakpoints() as $breakpoint_key => $breakpoint_instance ) { // Exclude the larger breakpoints from the dropdown selector. if ( in_array( $breakpoint_key, $excluded_breakpoints, true ) ) { continue; } $dropdown_options[ $breakpoint_key ] = sprintf( /* translators: 1: Breakpoint label, 2: `>` character, 3: Breakpoint value. */ esc_html__( '%1$s (%2$s %3$dpx)', 'elementor' ), $breakpoint_instance->get_label(), '>', $breakpoint_instance->get_value() ); } $this->add_control( 'breakpoint_selector', [ 'label' => esc_html__( 'Breakpoint', 'elementor' ), 'type' => Controls_Manager::SELECT, 'description' => esc_html__( 'Note: Choose at which breakpoint tabs will automatically switch to a vertical (“accordion”) layout.', 'elementor' ), 'options' => $dropdown_options, 'default' => 'mobile', 'prefix_class' => 'e-n-tabs-', ] ); $this->end_controls_section(); $this->start_controls_section( 'section_tabs_style', [ 'label' => esc_html__( 'Tabs', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_responsive_control( 'tabs_title_space_between', [ 'label' => esc_html__( 'Gap between tabs', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'max' => 400, ], 'em' => [ 'max' => 40, ], 'rem' => [ 'max' => 40, ], ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'tabs_title_spacing', [ 'label' => esc_html__( 'Distance from content', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'max' => 400, ], 'em' => [ 'max' => 40, ], 'rem' => [ 'max' => 40, ], ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->start_controls_tabs( 'tabs_title_style' ); $this->start_controls_tab( 'tabs_title_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'tabs_title_background_color', 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'selector' => '{{WRAPPER}} > .elementor-widget-container > .e-n-tabs > .e-n-tabs-heading > .e-n-tab-title[aria-selected="false"]:not( :hover )', 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'selectors' => [ '{{SELECTOR}}' => 'background: {{VALUE}}', ], ], ], ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'tabs_title_border', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"false\"]:not( :hover )", 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Border Color', 'elementor' ), ], 'width' => [ 'label' => esc_html__( 'Border Width', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'tabs_title_box_shadow', 'label' => esc_html__( 'Shadow', 'elementor' ), 'separator' => 'after', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"false\"]:not( :hover )", ] ); $this->end_controls_tab(); $this->start_controls_tab( 'tabs_title_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'tabs_title_background_color_hover', 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'selector' => "{$heading_selector_non_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'background' => [ 'default' => 'classic', ], 'color' => [ 'global' => [ 'default' => Global_Colors::COLOR_ACCENT, ], 'label' => esc_html__( 'Background Color', 'elementor' ), 'selectors' => [ '{{SELECTOR}}' => 'background: {{VALUE}};', ], ], ], ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'tabs_title_border_hover', 'selector' => "{$heading_selector_non_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Border Color', 'elementor' ), ], 'width' => [ 'label' => esc_html__( 'Border Width', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'tabs_title_box_shadow_hover', 'label' => esc_html__( 'Shadow', 'elementor' ), 'separator' => 'after', 'selector' => "{$heading_selector_non_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", ] ); $this->add_control( 'hover_animation', [ 'label' => esc_html__( 'Hover Animation', 'elementor' ), 'type' => Controls_Manager::HOVER_ANIMATION, ] ); $this->add_control( 'tabs_title_transition_duration', [ 'label' => esc_html__( 'Transition Duration', 'elementor' ) . ' (s)', 'type' => Controls_Manager::SLIDER, 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-transition: {{SIZE}}s', ], 'range' => [ 'px' => [ 'min' => 0, 'max' => 3, 'step' => 0.1, ], ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'tabs_title_active', [ 'label' => esc_html__( 'Active', 'elementor' ), ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'tabs_title_background_color_active', 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"true\"], {$heading_selector_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'background' => [ 'default' => 'classic', ], 'color' => [ 'global' => [ 'default' => Global_Colors::COLOR_ACCENT, ], 'label' => esc_html__( 'Background Color', 'elementor' ), 'selectors' => [ '{{SELECTOR}}' => 'background: {{VALUE}};', ], ], ], ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'tabs_title_border_active', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"true\"], {$heading_selector_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Border Color', 'elementor' ), ], 'width' => [ 'label' => esc_html__( 'Border Width', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'tabs_title_box_shadow_active', 'label' => esc_html__( 'Shadow', 'elementor' ), 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"true\"], {$heading_selector_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->add_responsive_control( 'tabs_title_border_radius', [ 'label' => esc_html__( 'Border Radius', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'separator' => 'before', 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ], ] ); $this->add_responsive_control( 'padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-padding-top: {{TOP}}{{UNIT}}; --n-tabs-title-padding-right: {{RIGHT}}{{UNIT}}; --n-tabs-title-padding-bottom: {{BOTTOM}}{{UNIT}}; --n-tabs-title-padding-left: {{LEFT}}{{UNIT}};', ], ] ); $this->end_controls_section(); $this->start_controls_section( 'section_title_style', [ 'label' => esc_html__( 'Titles', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'title_typography', 'global' => [ 'default' => Global_Typography::TYPOGRAPHY_ACCENT, ], 'selector' => "{$heading_selector} > :is( .e-n-tab-title > .e-n-tab-title-text, .e-n-tab-title )", 'fields_options' => [ 'font_size' => [ 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-font-size: {{SIZE}}{{UNIT}}', ], ], ], ] ); $this->start_controls_tabs( 'title_style' ); $this->start_controls_tab( 'title_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'title_text_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-color: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Text_Shadow::get_type(), [ 'name' => 'title_text_shadow', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"false\"]:not( :hover )", 'fields_options' => [ 'text_shadow_type' => [ 'label' => esc_html__( 'Shadow', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Text_Stroke::get_type(), [ 'name' => 'title_text_stroke', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"false\"]:not( :hover ) :is( span, a, i )", 'fields_options' => [ 'text_stroke_type' => [ 'label' => esc_html__( 'Stroke', 'elementor' ), ], ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'title_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'title_text_color_hover', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} [data-touch-mode="false"] .e-n-tab-title[aria-selected="false"]:hover' => '--n-tabs-title-color-hover: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Text_Shadow::get_type(), [ 'name' => 'title_text_shadow_hover', 'selector' => "{$heading_selector_non_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'text_shadow_type' => [ 'label' => esc_html__( 'Shadow', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Text_Stroke::get_type(), [ 'name' => 'title_text_stroke_hover', 'selector' => "{$heading_selector_non_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover :is( span, a, i )", 'fields_options' => [ 'text_stroke_type' => [ 'label' => esc_html__( 'Stroke', 'elementor' ), ], ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'title_active', [ 'label' => esc_html__( 'Active', 'elementor' ), ] ); $this->add_control( 'title_text_color_active', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-title-color-active: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Text_Shadow::get_type(), [ 'name' => 'title_text_shadow_active', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"true\"], {$heading_selector_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover", 'fields_options' => [ 'text_shadow_type' => [ 'label' => esc_html__( 'Shadow', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Text_Stroke::get_type(), [ 'name' => 'title_text_stroke_active', 'selector' => "{$heading_selector} > .e-n-tab-title[aria-selected=\"true\"] :is( span, a, i ), {$heading_selector_touch_device} > .e-n-tab-title[aria-selected=\"false\"]:hover :is( span, a, i )", 'fields_options' => [ 'text_stroke_type' => [ 'label' => esc_html__( 'Stroke', 'elementor' ), ], ], ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->end_controls_section(); $this->start_controls_section( 'icon_section_style', [ 'label' => esc_html__( 'Icon', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $styling_block_start = '--n-tabs-title-direction: column; --n-tabs-icon-order: initial; --n-tabs-title-justify-content-toggle: center; --n-tabs-title-align-items-toggle: initial;'; $styling_block_end = '--n-tabs-title-direction: column; --n-tabs-icon-order: 1; --n-tabs-title-justify-content-toggle: center; --n-tabs-title-align-items-toggle: initial;'; $styling_inline_start = '--n-tabs-title-direction: row; --n-tabs-icon-order: initial; --n-tabs-title-justify-content-toggle: initial; --n-tabs-title-align-items-toggle: center;'; $styling_inline_end = '--n-tabs-title-direction: row; --n-tabs-icon-order: 1; --n-tabs-title-justify-content-toggle: initial; --n-tabs-title-align-items-toggle: center;'; $this->add_responsive_control( 'icon_position', [ 'label' => esc_html__( 'Position', 'elementor' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'block-start' => [ 'title' => esc_html__( 'Above', 'elementor' ), 'icon' => 'eicon-v-align-top', ], 'inline-end' => [ 'title' => esc_html__( 'After', 'elementor' ), 'icon' => 'eicon-h-align-' . $end, ], 'block-end' => [ 'title' => esc_html__( 'Below', 'elementor' ), 'icon' => 'eicon-v-align-bottom', ], 'inline-start' => [ 'title' => esc_html__( 'Before', 'elementor' ), 'icon' => 'eicon-h-align-' . $start, ], ], 'selectors_dictionary' => [ // The toggle variables for 'align items' and 'justify content' have been added to separate the styling of the two 'flex direction' modes. 'block-start' => $styling_block_start, 'inline-end' => $styling_inline_end, 'block-end' => $styling_block_end, 'inline-start' => $styling_inline_start, // Styling duplication for BC reasons. 'top' => $styling_block_start, 'bottom' => $styling_block_end, 'start' => $styling_inline_start, 'end' => $styling_inline_end, ], 'selectors' => [ '{{WRAPPER}}' => '{{VALUE}}', ], ] ); $this->add_responsive_control( 'icon_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'max' => 100, ], 'em' => [ 'max' => 10, ], 'rem' => [ 'max' => 10, ], ], 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-icon-size: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'icon_spacing', [ 'label' => esc_html__( 'Spacing', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'max' => 400, ], 'vw' => [ 'max' => 50, 'step' => 0.1, ], ], 'size_units' => [ 'px', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-icon-gap: {{SIZE}}{{UNIT}}', ], ] ); $this->start_controls_tabs( 'icon_style_states' ); $this->start_controls_tab( 'icon_section_normal', [ 'label' => esc_html__( 'Normal', 'elementor' ), ] ); $this->add_control( 'icon_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-icon-color: {{VALUE}};', ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'icon_section_hover', [ 'label' => esc_html__( 'Hover', 'elementor' ), ] ); $this->add_control( 'icon_color_hover', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} [data-touch-mode="false"] .e-n-tab-title[aria-selected="false"]:hover' => '--n-tabs-icon-color-hover: {{VALUE}};', ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'icon_section_active', [ 'label' => esc_html__( 'Active', 'elementor' ), ] ); $this->add_control( 'icon_color_active', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}}' => '--n-tabs-icon-color-active: {{VALUE}};', ], ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->end_controls_section(); $this->start_controls_section( 'section_box_style', [ 'label' => esc_html__( 'Content', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'box_background_color', 'types' => [ 'classic', 'gradient' ], 'exclude' => [ 'image' ], 'selector' => $content_selector, 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Background Color', 'elementor' ), ], ], ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'box_border', 'selector' => $content_selector, 'fields_options' => [ 'color' => [ 'label' => esc_html__( 'Border Color', 'elementor' ), ], 'width' => [ 'label' => esc_html__( 'Border Width', 'elementor' ), ], ], ] ); $this->add_responsive_control( 'box_border_radius', [ 'label' => esc_html__( 'Border Radius', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ $content_selector => '--border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ], ] ); $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'box_shadow_box_shadow', 'selector' => $content_selector, 'condition' => [ 'box_height!' => 'height', ], ] ); $this->add_responsive_control( 'box_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ $content_selector => '--padding-top: {{TOP}}{{UNIT}}; --padding-right: {{RIGHT}}{{UNIT}}; --padding-bottom: {{BOTTOM}}{{UNIT}}; --padding-left: {{LEFT}}{{UNIT}};', ], ] ); $this->end_controls_section(); } protected function render_tab_titles_html( $item_settings ): void { $setting_key = $this->get_repeater_setting_key( 'tab_title', 'tabs', $item_settings['index'] ); $title = $item_settings['item']['tab_title']; $css_classes = [ 'e-n-tab-title' ]; if ( $item_settings['settings']['hover_animation'] ) { $css_classes[] = 'elementor-animation-' . $item_settings['settings']['hover_animation']; } $this->add_render_attribute( $setting_key, [ 'id' => $item_settings['tab_id'], 'class' => $css_classes, 'aria-selected' => 1 === $item_settings['tab_count'] ? 'true' : 'false', 'data-tab-index' => $item_settings['tab_count'], 'role' => 'tab', 'tabindex' => 1 === $item_settings['tab_count'] ? '0' : '-1', 'aria-controls' => $item_settings['container_id'], 'style' => '--n-tabs-title-order: ' . $item_settings['tab_count'] . ';', ] ); ?> is_active_icon_exist( $item_settings['item'] ) ? $item_settings['item']['tab_icon_active'] : $icon_settings; ?> print_render_attribute_string( 'tab-icon' ); ?>> 'true' ] ); ?> 'true' ] ); ?> $item ) { $item_settings = $this->tab_item_settings[ $index ]; $this->print_child( $item_settings['index'], $item_settings ); } } /** * Print the content area. * * @param int $index * @param array $item_settings */ public function print_child( $index, $item_settings = [] ) { $children = $this->get_children(); $child_ids = []; foreach ( $children as $child ) { $child_ids[] = $child->get_id(); } // Add data-tab-index attribute to the content area. $add_attribute_to_container = function ( $should_render, $container ) use ( $item_settings, $child_ids ) { if ( in_array( $container->get_id(), $child_ids ) ) { $this->add_attributes_to_container( $container, $item_settings ); } return $should_render; }; add_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container, 10, 3 ); if ( isset( $children[ $index ] ) ) { $children[ $index ]->print_element(); } remove_filter( 'elementor/frontend/container/should_render', $add_attribute_to_container ); } protected function add_attributes_to_container( $container, $item_settings ) { $container->add_render_attribute( '_wrapper', [ 'id' => $item_settings['container_id'], 'role' => 'tabpanel', 'aria-labelledby' => $item_settings['tab_id'], 'data-tab-index' => $item_settings['tab_count'], 'style' => '--n-tabs-title-order: ' . $item_settings['tab_count'] . ';', 'class' => 0 === $item_settings['index'] ? 'e-active' : '', ] ); } protected function render() { $settings = $this->get_settings_for_display(); $widget_number = $this->get_id_int(); if ( ! empty( $settings['link'] ) ) { $this->add_link_attributes( 'elementor-tabs', $settings['link'] ); } $this->add_render_attribute( 'elementor-tabs', [ 'class' => 'e-n-tabs', 'data-widget-number' => $widget_number, 'aria-label' => esc_html__( 'Tabs. Open items with Enter or Space, close with Escape and navigate using the Arrow keys.', 'elementor' ), ] ); $this->add_render_attribute( 'tab-title-text', 'class', 'e-n-tab-title-text' ); $this->add_render_attribute( 'tab-icon', 'class', 'e-n-tab-icon' ); $this->add_render_attribute( 'tab-icon-active', 'class', [ 'e-n-tab-icon' ] ); ?>
print_render_attribute_string( 'elementor-tabs' ); ?>>
$item ) { $tab_count = $index + 1; $tab_id = empty( $item['element_id'] ) ? 'e-n-tab-title-' . $widget_number . $tab_count : $item['element_id']; $item_settings = [ 'index' => $index, 'tab_count' => $tab_count, 'tab_id' => $tab_id, 'container_id' => 'e-n-tab-content-' . $widget_number . $tab_count, 'widget_number' => $widget_number, 'item' => $item, 'settings' => $settings, ]; $this->tab_item_settings[] = $item_settings; $this->render_tab_titles_html( $item_settings ); } ?>
render_tab_containers_html( $settings ); ?>
experiments->is_feature_active( 'e_nested_atomic_repeaters' ) ) { return array_merge( parent::get_initial_config(), [ 'support_improved_repeaters' => true, 'target_container' => [ '.e-n-tabs-heading' ], 'node' => 'button', ] ); } return parent::get_initial_config(); } protected function content_template_single_repeater_item() { ?> <# const tabIndex = view.collection.length, elementUid = view.getIDInt().toString(), item = data, hoverAnimationSetting = view?.container?.settings?.attributes?.hover_animation; hoverAnimationClass = hoverAnimationSetting ? `elementor-animation-${ hoverAnimationSetting }` : ''; #> content_template_single_item( '{{ tabIndex }}', '{{ item }}', '{{ elementUid }}', '{{ hoverAnimationClass }}' ); } protected function content_template() { ?> <# const elementUid = view.getIDInt().toString(); #>
<# if ( settings['tabs'] ) { #>
<# _.each( settings['tabs'], function( item, index ) { const tabIndex = index, hoverAnimationSetting = settings['hover_animation'], hoverAnimationClass = hoverAnimationSetting ? `elementor-animation-${ hoverAnimationSetting }` : ''; #> content_template_single_item( '{{ tabIndex }}', '{{ item }}', '{{ elementUid }}', '{{ hoverAnimationClass }}' ); ?> <# } ); #>
<# } #>
<# const tabCount = tabIndex + 1, tabId = item.element_id ? item.element_id : 'e-n-tab-title-' + elementUid + ( tabIndex + 1 ), tabUid = elementUid + tabCount, tabIcon = elementor.helpers.renderIcon( view, item.tab_icon, { 'aria-hidden': true }, 'i' , 'object' ), activeTabIcon = item.tab_icon_active.value ? elementor.helpers.renderIcon( view, item.tab_icon_active, { 'aria-hidden': true }, 'i' , 'object' ) : tabIcon, escapedHoverAnimationClass = _.escape( hoverAnimationClass ); view.addRenderAttribute( 'tab-title', { 'id': tabId, 'class': [ 'e-n-tab-title',escapedHoverAnimationClass ], 'data-tab-index': tabCount, 'role': 'tab', 'aria-selected': 1 === tabCount ? 'true' : 'false', 'tabindex': 1 === tabCount ? '0' : '-1', 'aria-controls': 'e-n-tab-content-' + tabUid, 'style': '--n-tabs-title-order: ' + tabCount + ';', }, null, true ); view.addRenderAttribute( 'tab-title-text', { 'class': [ 'e-n-tab-title-text' ], 'data-binding-type': 'repeater-item', 'data-binding-repeater-name': 'tabs', 'data-binding-setting': [ 'tab_title' ], 'data-binding-index': tabCount, 'data-binding-dynamic': 'true', }, null, true ); view.addRenderAttribute( 'tab-icon', { 'class': [ 'e-n-tab-icon' ], 'data-binding-type': 'repeater-item', 'data-binding-repeater-name': 'tabs', 'data-binding-setting': [ 'tab_icon', 'tab_icon_active' ], 'data-binding-index': tabCount, }, null, true ); #> register( Widgets::class ); $this->populate(); Plugin::instance()->data_manager_v2->register_controller( new Controller() ); add_filter( 'elementor/tracker/send_tracking_data_params', [ $this, 'add_tracking_data' ] ); } /** * Add usage data related to favorites. * * @param $params * * @return array */ public function add_tracking_data( $params ) { $params['usages']['favorites'] = $this->get(); return $params; } public function get_name() { return 'favorites'; } /** * Get user favorites by type. * * @param string[]|string $type * * @return array */ public function get( $type = null ) { if ( null === $type ) { $type = array_keys( $this->types ); } if ( is_array( $type ) ) { return array_intersect_key( $this->combined(), array_flip( (array) $type ) ); } return $this->type_instance( $type ) ->values(); } /** * Merge new user favorites to a type. * * @param string $type * @param array|string $favorites * @param bool $store * * @return array|bool */ public function merge( $type, $favorites, $store = true ) { return $this->update( $type, $favorites, static::ACTION_MERGE, $store ); } /** * Delete existing favorites from a type. * * @param string $type * @param array|string $favorites * @param bool $store * * @return array|int */ public function delete( $type, $favorites, $store = true ) { return $this->update( $type, $favorites, static::ACTION_DELETE, $store ); } /** * Update favorites on a type by merging or deleting from it. * * @param $type * @param $favorites * @param $action * @param bool $store * * @return array|boolean */ public function update( $type, $favorites, $action, $store = true ) { $type_instance = $this->type_instance( $type ); $favorites = $type_instance->prepare( $favorites ); switch ( $action ) { case static::ACTION_MERGE: $type_instance->merge( $favorites ); break; case static::ACTION_DELETE: $type_instance->filter( function( $value ) use ( $favorites ) { return ! in_array( $value, $favorites, true ); } ); break; default: $this->action_doesnt_exists( $action ); } if ( $store && ! $this->store() ) { return false; } return $type_instance->values(); } /** * Get registered favorites type instance. * * @param string $type * * @return Favorites_Type */ public function type_instance( $type ) { return $this->types[ $type ]; } /** * Register a new type class. * * @param string $class */ public function register( $class ) { $type_instance = new $class(); $this->types[ $type_instance->get_name() ] = $type_instance; } /** * Returns all available types keys. * * @return string[] */ public function available() { return array_keys( $this->types ); } /** * Combine favorites from all types into a single array. * * @return array */ protected function combined() { $all = []; foreach ( $this->types as $type ) { $favorites = $type->values(); if ( ! empty( $favorites ) ) { $all[ $type->get_name() ] = $favorites; } } return $all; } /** * Populate all type classes with the stored data. */ protected function populate() { $combined = $this->retrieve(); foreach ( $this->types as $key => $type ) { if ( isset( $combined[ $key ] ) ) { $type->merge( $combined[ $key ] ); } } } /** * Retrieve stored user favorites types. * * @return mixed|false */ protected function retrieve() { return get_user_option( static::OPTION_NAME ); } /** * Update all changes to user favorites type. * * @return int|bool */ protected function store() { return update_user_option( get_current_user_id(), static::OPTION_NAME, $this->combined() ); } /** * Throw action doesn't exist exception. * * @param string $action */ public function action_doesnt_exists( $action ) { throw new \InvalidArgumentException( sprintf( "Action '%s' to apply on favorites doesn't exists", $action ) ); } } favorites/controller.php000064400000003720147207015700011447 0ustar00get_module(); $type = $request->get_param( 'id' ); $favorite = $request->get_param( 'favorite' ); $module->update( $type, $favorite, $module::ACTION_MERGE ); return $module->get( $type ); } public function delete_item( $request ) { $module = $this->get_module(); $type = $request->get_param( 'id' ); $favorite = $request->get_param( 'favorite' ); $module->update( $type, $favorite, $module::ACTION_DELETE ); return $module->get( $type ); } public function create_item_permissions_check( $request ) { return current_user_can( 'edit_posts' ); } public function delete_item_permissions_check( $request ) { return $this->create_item_permissions_check( $request ); } /** * Get the favorites module instance. * * @return Module */ protected function get_module() { return Plugin::instance()->modules_manager->get_modules( 'favorites' ); } public function register_endpoints() { $this->index_endpoint->register_item_route( \WP_REST_Server::CREATABLE, [ 'id_arg_type_regex' => '[\w]+', 'id' => [ 'description' => 'Type of favorites.', 'type' => 'string', 'required' => true, ], 'favorite' => [ 'description' => 'The favorite slug to create.', 'type' => 'string', 'required' => true, ], ] ); $this->index_endpoint->register_item_route( \WP_REST_Server::DELETABLE, [ 'id_arg_type_regex' => '[\w]+', 'id' => [ 'description' => 'Type of favorites.', 'type' => 'string', 'required' => true, ], 'favorite' => [ 'description' => 'The favorite slug to delete.', 'type' => 'string', 'required' => true, ], ] ); } } favorites/types/widgets.php000064400000002767147207015700012110 0ustar00get_available() ); } /** * Get all available widgets. * * @return string[] */ public function get_available() { return array_merge( array_keys( Plugin::instance()->widgets_manager->get_widget_types() ), array_keys( Plugin::instance()->elements_manager->get_element_types() ) ); } /** * Update the categories of a widget inside a filter. * * @param $document */ public function update_widget_categories( $document ) { foreach ( $this->values() as $favorite ) { $widget = Plugin::$instance->widgets_manager->get_widget_types( $favorite ); // If it's not a widget, maybe it's an element. if ( ! $widget ) { $widget = Plugin::$instance->elements_manager->get_element_types( $favorite ); } if ( $widget ) { $widget->set_config( 'categories', [ static::CATEGORY_SLUG ] ); } } } } favorites/favorites-type.php000064400000001446147207015700012250 0ustar00values(); } if ( ! is_array( $favorites ) ) { return [ $favorites ]; } return $favorites; } } link-in-bio/module.php000064400000002577147207015700010670 0ustar00 self::EXPERIMENT_NAME, 'title' => esc_html__( 'Link In Bio', 'elementor' ), 'hidden' => true, 'default' => Manager::STATE_ACTIVE, 'release_status' => Manager::RELEASE_STATUS_STABLE, 'mutable' => false, ]; } public function __construct() { parent::__construct(); add_action( 'elementor/frontend/after_register_styles', [ $this, 'register_styles' ] ); } /** * Register styles. * * At build time, Elementor compiles `/modules/link-in-bio/assets/scss/frontend.scss` * to `/assets/css/widget-link-in-bio.min.css`. * * @return void */ public function register_styles() { wp_register_style( 'widget-link-in-bio', $this->get_css_assets_url( 'widget-link-in-bio', null, true, true ), [ 'elementor-frontend' ], ELEMENTOR_VERSION ); } } link-in-bio/classes/render/core-render.php000064400000001502147207015700014507 0ustar00build_layout_render_attribute(); ?>
widget->get_render_attribute_string( 'layout' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
widget = $widget; $this->settings = $widget->get_settings_for_display(); } protected function render_image_links(): void { $image_links_value_initial = $this->settings['image_links'] ?? []; $image_links_columns_value = $this->settings['image_links_per_row'] ?? 2; /** * if empty returns a sub-array with all empty values * Check for this here to avoid rendering container when empty */ $image_links_value = $this->clean_array( $image_links_value_initial ); $has_image_links = ! empty( $image_links_value ); if ( ! $has_image_links ) { return; } $image_links_classnames = 'e-link-in-bio__image-links'; if ( ! empty( $image_links_columns_value ) ) { $image_links_classnames .= ' has-' . $image_links_columns_value . '-columns'; } $this->widget->add_render_attribute( 'image-links', [ 'class' => $image_links_classnames, ] ); ?>
widget->get_render_attribute_string( 'image-links' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> $image_link ) { $formatted_link = $image_link['image_links_url']['url'] ?? ''; $image_link_image = $image_link['image_links_image'] ?? []; // Manage Link class variations $image_link_classnames = 'e-link-in-bio__image-links-link'; // Manage Link attributes $url_attrs = [ 'class' => $image_link_classnames, 'href' => esc_url( $formatted_link ), ]; $url_combined_attrs = $this->get_link_attributes( $image_link['image_links_url'], $url_attrs ); foreach ( $url_combined_attrs as $attr_key => $attr_value ) { $this->widget->add_render_attribute( 'image-links-link' . $key, [ $attr_key => $attr_value, ] ); } ?> widget->get_render_attribute_string( 'image-links-link' . $key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> 'e-link-in-bio__image-links-img', ] ); } else { $this->widget->add_render_attribute( 'image-links-img-' . $key, [ 'alt' => '', 'class' => 'e-link-in-bio__image-links-img', 'src' => esc_url( $image_link_image['url'] ), ] ); ?> widget->get_render_attribute_string( 'image-links-img-' . $key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> />
settings['cta_links_corners'] ?? 'rounded'; $ctas_props_show_border = $this->settings['cta_links_show_border'] ?? false; $ctas_props_type = $this->settings['cta_links_type'] ?? 'button'; $ctas_value_initial = $this->settings['cta_link'] ?? []; /** * $this->settings['cta_link'] if empty returns a sub-array with all empty values * Check for this here to avoid rendering container when empty */ $ctas_value = $this->clean_array( $ctas_value_initial ); $has_ctas = ! empty( $ctas_value ); if ( ! $has_ctas ) { return; } $this->widget->add_render_attribute( 'ctas', [ 'class' => 'e-link-in-bio__ctas has-type-' . $ctas_props_type, ] ); ?>
widget->get_render_attribute_string( 'ctas' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> $cta ) { $formatted_link = $this->get_formatted_link_based_on_type_for_cta( $cta ); $cta_image = $cta['cta_link_image'] ?? []; $cta_has_image = ! empty( $cta_image ) && ( ! empty( $cta_image['url'] || ! empty( $cta_image['id'] ) ) ) && 'button' === $ctas_props_type; // Manage Link class variations $ctas_classnames = 'e-link-in-bio__cta is-type-' . $ctas_props_type; if ( 'button' === $ctas_props_type && $ctas_props_show_border ) { $ctas_classnames .= ' has-border'; } if ( $cta_has_image ) { $ctas_classnames .= ' has-image'; } if ( 'button' === $ctas_props_type ) { $ctas_classnames .= ' has-corners-' . $ctas_props_corners; } // Manage Link attributes $url_attrs = [ 'class' => $ctas_classnames, 'href' => esc_url( $formatted_link ), ]; if ( Social_Network_Provider::FILE_DOWNLOAD === $cta['cta_link_type'] || Social_Network_Provider::VCF === $cta['cta_link_type'] ) { $url_attrs['download'] = 'download'; } $cta_url = $cta['cta_link_url']; if ( Social_Network_Provider::WAZE == $cta['cta_link_type'] ) { $cta_url = $cta['cta_link_location']; } $url_combined_attrs = $this->get_link_attributes( $cta_url, $url_attrs ); foreach ( $url_combined_attrs as $attr_key => $attr_value ) { $this->widget->add_render_attribute( 'cta-' . $key, [ $attr_key => $attr_value, ] ); } ?> widget->get_render_attribute_string( 'cta-' . $key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> 'e-link-in-bio__cta-image-element', ] ); } else { $this->widget->add_render_attribute( 'cta-link-image' . $key, [ 'alt' => '', 'class' => 'e-link-in-bio__cta-image-element', 'src' => esc_url( $cta_image['url'] ), ] ); ?> widget->get_render_attribute_string( 'cta-link-image' . $key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> />
settings['icons_border_show_border'] ?? false; $icons_props_size = $this->settings['icons_size'] ?? 'small'; $icons_value = $this->settings['icon'] ?? []; $has_icons = ! empty( $icons_value ); if ( ! $has_icons ) { return; } $this->widget->add_render_attribute( 'icons', [ 'class' => 'e-link-in-bio__icons has-size-' . $icons_props_size, ] ); ?>
widget->get_render_attribute_string( 'icons' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>> $icon ) { $formatted_link = $this->get_formatted_link_for_icon( $icon ); $icon_class_names = 'e-link-in-bio__icon is-size-' . $icons_props_size; if ( $icons_props_show_border ) { $icon_class_names .= ' has-border'; } $this->widget->add_render_attribute( 'icon-' . $key, [ 'class' => $icon_class_names, ] ); // Manage Link attributes $url_attrs = [ 'aria-label' => esc_attr( $icon['icon_platform'] ), 'class' => 'e-link-in-bio__icon-link', 'href' => esc_url( $formatted_link ), ]; $icon_url = $icon['icon_url']; if ( Social_Network_Provider::WAZE == $icon['icon_platform'] ) { $icon_url = $icon['icon_location']; } $url_combined_attrs = $this->get_link_attributes( $icon_url, $url_attrs ); foreach ( $url_combined_attrs as $attr_key => $attr_value ) { $this->widget->add_render_attribute( 'icon-link-' . $key, [ $attr_key => $attr_value, ] ); } ?>
settings['bio_heading_tag'] ?? 'h2'; $bio_heading_value = $this->settings['bio_heading'] ?? ''; $bio_title_props_tag = $this->settings['bio_title_tag'] ?? 'h2'; $bio_title_value = $this->settings['bio_title'] ?? ''; if ( 'top' === $this->widget->get_description_position() ) { $bio_about_heading_props_tag = $this->settings['bio_about_tag'] ?? 'h3'; $bio_about_heading_value = $this->settings['bio_about'] ?? ''; $bio_description_value = $this->settings['bio_description'] ?? ''; } $has_bio_about_heading = ! empty( $bio_about_heading_value ); $has_bio_description = ! empty( $bio_description_value ); $has_bio_heading = ! empty( $bio_heading_value ); $has_bio_title = ! empty( $bio_title_value ); if ( $has_bio_heading || $has_bio_title || $has_bio_about_heading || $has_bio_description ) { ?> widget->get_description_position() ) { return; } $bio_about_heading_props_tag = $this->settings['bio_about_tag'] ?? 'h3'; $bio_about_heading_value = $this->settings['bio_about'] ?? ''; $bio_description_value = $this->settings['bio_description'] ?? ''; $has_bio_description = ! empty( $bio_description_value ); $has_bio_about_heading = ! empty( $bio_about_heading_value ); if ( $has_bio_about_heading || $has_bio_description ) { ?> [ 'props' => [], 'should_render' => false, 'value' => $this->settings['identity_image_cover'] ?? [], ], 'primary_image' => [ 'props' => [], 'should_render' => false, 'value' => $this->settings['identity_image'] ?? [], ], ]; $output_images['primary_image']['should_render'] = ! empty( $output_images['primary_image']['value'] ) && ( ! empty( $output_images['primary_image']['value']['url'] || ! empty( $output_images['primary_image']['value']['id'] ) ) ); $output_images['secondary_image']['should_render'] = ! empty( $output_images['secondary_image']['value'] ) && ( ! empty( $output_images['secondary_image']['value']['url'] || ! empty( $output_images['secondary_image']['value']['id'] ) ) ); if ( ! $output_images['primary_image']['should_render'] && ! $output_images['secondary_image']['should_render'] ) { return; } $output_images = $this->set_primary_image_properties( $output_images ); $output_images = $this->set_secondary_image_properties( $output_images ); ?> settings['advanced_layout_full_screen_height'] ?? ''; $layout_props_full_height_controls = $this->settings['advanced_layout_full_screen_height_controls'] ?? ''; $layout_props_full_width = $this->settings['advanced_layout_full_width_custom'] ?? ''; $layout_props_show_border = $this->settings['background_show_border'] ?? ''; $custom_classes = $this->settings['advanced_custom_css_classes'] ?? ''; $layout_classnames = 'e-link-in-bio e-' . $this->widget->get_name(); if ( 'yes' === $layout_props_show_border ) { $layout_classnames .= ' has-border'; } if ( 'yes' === $layout_props_full_width ) { $layout_classnames .= ' is-full-width'; } if ( 'yes' === $layout_props_full_height ) { $layout_classnames .= ' is-full-height'; } if ( ! empty( $layout_props_full_height_controls ) ) { foreach ( $layout_props_full_height_controls as $breakpoint ) { $layout_classnames .= ' is-full-height-' . $breakpoint; } } if ( $custom_classes ) { $layout_classnames .= ' ' . $custom_classes; } $attrs = [ 'class' => $layout_classnames, ]; if ( ! empty( $this->settings['advanced_custom_css_id'] ) ) { $attrs['id'] = $this->settings['advanced_custom_css_id']; } $this->widget->add_render_attribute( 'layout', $attrs ); } private function set_primary_image_properties( array $output_images ): array { if ( $output_images['primary_image']['should_render'] ) { $output_images['primary_image']['props']['shape'] = $this->settings['identity_image_shape'] ?? 'circle'; $output_images['primary_image']['props']['style'] = $this->settings['identity_image_style'] ?? 'profile'; $output_images['primary_image']['props']['show_border'] = $this->settings['identity_image_show_border'] ?? false; $output_images['primary_image']['props']['show_bottom_border'] = $this->settings['identity_image_bottom_show_border'] ?? false; } return $output_images; } private function set_secondary_image_properties( array $output_images ): array { if ( $output_images['secondary_image']['should_render'] ) { $output_images['secondary_image']['props']['style'] = 'cover'; $output_images['secondary_image']['props']['show_bottom_border'] = $this->settings['identity_image_bottom_show_border'] ?? false; if ( ! empty( $this->settings['identity_section_style_cover_divider_bottom'] ) ) { $output_images['secondary_image']['props']['has_shape_divider'] = true; // Remove border if a shaped divider is applied $output_images['secondary_image']['props']['show_bottom_border'] = false; } $output_images['primary_image']['props']['style'] = 'profile'; } return $output_images; } } link-in-bio/widgets/link-in-bio.php000064400000001423147207015700013146 0ustar00experiments->is_feature_active( 'e_font_icon_svg' ) ) { return parent::get_style_depends(); } return [ 'elementor-icons-fa-solid', 'elementor-icons-fa-brands', 'elementor-icons-fa-regular' ]; } public static function get_configuration() { return [ 'content' => [ 'identity_section' => [ 'identity_image_style' => [ 'default' => 'profile', ], 'has_heading_text' => false, 'has_profile_image_controls' => false, ], 'bio_section' => [ 'title' => [ 'default' => esc_html__( 'Kitchen Chronicles', 'elementor' ), ], 'description' => [ 'default' => esc_html__( 'Join me on my journey to a healthier lifestyle', 'elementor' ), ], 'has_about_field' => false, ], 'icon_section' => [ 'has_text' => false, 'platform' => [ 'group-1' => [ Social_Network_Provider::EMAIL, Social_Network_Provider::TELEPHONE, Social_Network_Provider::MESSENGER, Social_Network_Provider::WAZE, Social_Network_Provider::WHATSAPP, ], 'limit' => 5, ], 'default' => [ [ 'icon_platform' => Social_Network_Provider::FACEBOOK, ], [ 'icon_platform' => Social_Network_Provider::INSTAGRAM, ], [ 'icon_platform' => Social_Network_Provider::TIKTOK, ], ], ], 'cta_section' => [ 'cta_max' => 0, 'cta_has_image' => false, 'cta_repeater_defaults' => [ [ 'cta_link_text' => esc_html__( 'Get Healthy', 'elementor' ), ], [ 'cta_link_text' => esc_html__( 'Top 10 Recipes', 'elementor' ), ], [ 'cta_link_text' => esc_html__( 'Meal Prep', 'elementor' ), ], [ 'cta_link_text' => esc_html__( 'Healthy Living Resources', 'elementor' ), ], ], ], 'image_links_section' => false, ], 'style' => [ 'identity_section' => [ 'has_profile_image_shape' => true, 'profile_image_max' => 115, 'cover_image_max' => 1000, ], 'cta_section' => [ 'has_dividers' => false, 'has_image_border' => false, 'has_link_type' => [ 'default' => 'button', ], 'has_corners' => [ 'default' => 'rounded', 'options' => [ 'round' => esc_html__( 'Round', 'elementor' ), 'rounded' => esc_html__( 'Rounded', 'elementor' ), 'sharp' => esc_html__( 'Sharp', 'elementor' ), ], ], 'has_padding' => true, 'has_background_control' => true, 'has_cta_control_text' => false, 'has_border_control' => [ 'prefix' => 'cta_links', 'show_border_args' => [ 'condition' => [ 'cta_links_type' => 'button', ], ], 'border_width_args' => [ 'condition' => [ 'cta_links_type' => 'button', ], 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-ctas-border-width: {{SIZE}}{{UNIT}}', ], ], 'border_color_args' => [ 'condition' => [ 'cta_links_type' => 'button', ], 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-ctas-border-color: {{VALUE}}', ], ], ], ], 'border_section' => [ 'field_options' => false, 'overlay_field_options' => false, ], 'image_links_section' => false, ], ]; } public function get_description_position() { return 'top'; } public function get_icon(): string { return 'eicon-site-identity'; } public function get_categories(): array { return [ 'link-in-bio' ]; } public function get_keywords(): array { return [ 'buttons', 'bio', 'widget', 'link in bio' ]; } public function get_image_position_options(): array { return [ '' => esc_html__( 'Default', 'elementor' ), 'center center' => esc_html__( 'Center Center', 'elementor' ), 'center left' => esc_html__( 'Center Left', 'elementor' ), 'center right' => esc_html__( 'Center Right', 'elementor' ), 'top center' => esc_html__( 'Top Center', 'elementor' ), 'top left' => esc_html__( 'Top Left', 'elementor' ), 'top right' => esc_html__( 'Top Right', 'elementor' ), 'bottom center' => esc_html__( 'Bottom Center', 'elementor' ), 'bottom left' => esc_html__( 'Bottom Left', 'elementor' ), 'bottom right' => esc_html__( 'Bottom Right', 'elementor' ), ]; } protected function register_controls(): void { $this->add_content_tab(); $this->add_style_tab(); } protected function render(): void { $render_strategy = new Core_Render( $this ); $render_strategy->render(); } protected function add_image_links_controls() { $config = static::get_configuration(); if ( empty( $config['content']['image_links_section'] ) ) { return; } $this->start_controls_section( 'image_links_section', [ 'label' => esc_html__( 'Image Links', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); if ( ! empty( $config['content']['image_links_section']['images_max'] ) ) { $this->add_control( 'image_links_alert', [ 'type' => Controls_Manager::ALERT, 'alert_type' => 'info', 'content' => sprintf( __( 'Add up to %d Images', 'elementor' ), $config['content']['image_links_section']['images_max'] ), ] ); } $this->add_icons_per_row_control( 'image_links_per_row', [ '1' => '1', '2' => '2', '3' => '3', ], '2', esc_html__( 'Images Per Row', 'elementor' ), '--e-link-in-bio-image-links-columns', ); $repeater = new Repeater(); $repeater->add_control( 'image_links_image', [ 'label' => esc_html__( 'Choose Image', 'elementor' ), 'type' => Controls_Manager::MEDIA, 'label_block' => true, 'default' => [ 'url' => Utils::get_placeholder_image_src(), ], ] ); $repeater->add_control( 'image_links_url', [ 'label' => esc_html__( 'Link', 'elementor' ), 'type' => Controls_Manager::URL, 'dynamic' => [ 'active' => true, ], 'autocomplete' => true, 'label_block' => true, 'placeholder' => esc_html__( 'Paste URL or type', 'elementor' ), 'default' => [ 'is_external' => true, ], ], ); $this->add_control( 'image_links', [ 'type' => Controls_Manager::REPEATER, 'max_items' => $config['content']['image_links_section']['images_max'] ?? 0, 'fields' => $repeater->get_controls(), 'prevent_empty' => true, 'button_text' => esc_html__( 'Add item', 'elementor' ), 'default' => $config['content']['image_links_section']['images_repeater_defaults'] ?? [], ] ); $this->end_controls_section(); } protected function add_cta_controls() { $config = static::get_configuration(); if ( empty( $config['content']['cta_section'] ) ) { return; } $this->start_controls_section( 'cta_section', [ 'label' => esc_html__( 'CTA Link Buttons', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); if ( ! empty( $config['content']['cta_section']['cta_max'] ) ) { $this->add_control( 'cta_section_alert', [ 'type' => Controls_Manager::ALERT, 'alert_type' => 'info', 'content' => sprintf( __( 'Add up to %d CTA links', 'elementor' ), $config['content']['cta_section']['cta_max'] ), ] ); } $repeater = new Repeater(); $repeater->add_control( 'cta_link_text', [ 'label' => esc_html__( 'Text', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'default' => esc_html__( 'CTA link', 'elementor' ), 'placeholder' => esc_html__( 'Enter link text', 'elementor' ), ], ); if ( $config['content']['cta_section']['cta_has_image'] ) { $repeater->add_control( 'cta_link_image', [ 'label' => esc_html__( 'Choose Image', 'elementor' ), 'type' => Controls_Manager::MEDIA, 'label_block' => true, 'default' => [ 'url' => Utils::get_placeholder_image_src(), ], ] ); } $repeater->add_control( 'cta_link_type', [ 'label' => esc_html__( 'Link Type', 'elementor' ), 'type' => Controls_Manager::SELECT, 'groups' => [ [ 'label' => '', 'options' => Social_Network_Provider::get_social_networks_text( [ Social_Network_Provider::URL, Social_Network_Provider::FILE_DOWNLOAD, ] ), ], [ 'label' => ' --', 'options' => Social_Network_Provider::get_social_networks_text( [ Social_Network_Provider::EMAIL, Social_Network_Provider::TELEPHONE, Social_Network_Provider::MESSENGER, Social_Network_Provider::WAZE, Social_Network_Provider::WHATSAPP, ] ), ], ], 'default' => Social_Network_Provider::URL, ], ); $repeater->add_control( 'cta_link_file', [ 'label' => esc_html__( 'Choose File', 'elementor' ), 'type' => Controls_Manager::MEDIA, 'label_block' => true, 'media_type' => [ 'application/pdf' ], 'condition' => [ 'cta_link_type' => [ Social_Network_Provider::FILE_DOWNLOAD, ], ], 'ai' => [ 'active' => false, ], ], ); $repeater->add_control( 'cta_link_url', [ 'label' => esc_html__( 'Link', 'elementor' ), 'type' => Controls_Manager::URL, 'dynamic' => [ 'active' => true, ], 'autocomplete' => true, 'label_block' => true, 'condition' => [ 'cta_link_type' => [ Social_Network_Provider::URL, ], ], 'placeholder' => esc_html__( 'Enter your link', 'elementor' ), 'default' => [ 'is_external' => true, ], ], ); $repeater->add_control( 'cta_link_mail', [ 'label' => esc_html__( 'Mail', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'condition' => [ 'cta_link_type' => [ Social_Network_Provider::EMAIL, ], ], 'placeholder' => esc_html__( 'Enter your email', 'elementor' ), ], ); $repeater->add_control( 'cta_link_mail_subject', [ 'label' => esc_html__( 'Subject', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'condition' => [ 'cta_link_type' => [ Social_Network_Provider::EMAIL, ], ], 'placeholder' => esc_html__( 'Subject', 'elementor' ), ], ); $repeater->add_control( 'cta_link_mail_body', [ 'label' => esc_html__( 'Message', 'elementor' ), 'type' => Controls_Manager::TEXTAREA, 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'condition' => [ 'cta_link_type' => [ Social_Network_Provider::EMAIL, ], ], 'placeholder' => esc_html__( 'Message', 'elementor' ), ], ); $repeater->add_control( 'cta_link_number', [ 'label' => esc_html__( 'Number', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'condition' => [ 'cta_link_type' => [ Social_Network_Provider::TELEPHONE, Social_Network_Provider::WHATSAPP, ], ], 'placeholder' => esc_html__( 'Enter your number', 'elementor' ), ], ); $repeater->add_control( 'cta_link_location', [ 'label' => esc_html__( 'Location', 'elementor' ), 'type' => Controls_Manager::URL, 'dynamic' => [ 'active' => true, ], 'default' => [ 'is_external' => true, ], 'label_block' => true, 'condition' => [ 'cta_link_type' => [ Social_Network_Provider::WAZE, ], ], 'placeholder' => esc_html__( 'Paste Waze link', 'elementor' ), ], ); $repeater->add_control( 'cta_link_username', [ 'label' => esc_html__( 'Username', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'condition' => [ 'cta_link_type' => [ Social_Network_Provider::MESSENGER, ], ], 'placeholder' => esc_html__( 'Enter your username', 'elementor' ), ], ); $this->add_control( 'cta_link', [ 'type' => Controls_Manager::REPEATER, 'max_items' => $config['content']['cta_section']['cta_max'] ?? 0, 'fields' => $repeater->get_controls(), 'title_field' => '{{{ cta_link_text }}}', 'button_text' => esc_html__( 'Add CTA Link', 'elementor' ), 'default' => $config['content']['cta_section']['cta_repeater_defaults'], ] ); $this->end_controls_section(); } protected function add_icons_controls(): void { $config = static::get_configuration(); $this->start_controls_section( 'icons_section', [ 'label' => esc_html__( 'Icons', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); if ( $config['content']['icon_section']['platform']['limit'] ) { $this->add_control( 'custom_panel_alert', [ 'type' => Controls_Manager::ALERT, 'alert_type' => 'info', 'content' => sprintf( __( 'Add up to %d icons', 'elementor' ), $config['content']['icon_section']['platform']['limit'] ), ] ); } $repeater = new Repeater(); if ( $config['content']['icon_section']['has_text'] ) { $repeater->add_control( 'icon_text', [ 'label' => esc_html__( 'Text', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'placeholder' => esc_html__( 'Enter icon text', 'elementor' ), ], ); } $repeater->add_control( 'icon_platform', [ 'label' => esc_html__( 'Platform', 'elementor' ), 'type' => Controls_Manager::SELECT, 'groups' => [ [ 'label' => '', 'options' => Social_Network_Provider::get_social_networks_text( $config['content']['icon_section']['platform']['group-1'] ), ], [ 'label' => ' --', 'options' => Social_Network_Provider::get_social_networks_text( [ Social_Network_Provider::FACEBOOK, Social_Network_Provider::INSTAGRAM, Social_Network_Provider::LINKEDIN, Social_Network_Provider::PINTEREST, Social_Network_Provider::TIKTOK, Social_Network_Provider::TWITTER, Social_Network_Provider::YOUTUBE, ] ), ], [ 'label' => ' --', 'options' => Social_Network_Provider::get_social_networks_text( [ Social_Network_Provider::APPLEMUSIC, Social_Network_Provider::BEHANCE, Social_Network_Provider::DRIBBBLE, Social_Network_Provider::SPOTIFY, Social_Network_Provider::SOUNDCLOUD, Social_Network_Provider::VIMEO, ] ), ], ], 'default' => Social_Network_Provider::FACEBOOK, ], ); $repeater->add_control( 'icon_url', [ 'label' => esc_html__( 'Link', 'elementor' ), 'type' => Controls_Manager::URL, 'dynamic' => [ 'active' => true, ], 'autocomplete' => true, 'label_block' => true, 'placeholder' => esc_html__( 'Enter your link', 'elementor' ), 'default' => [ 'is_external' => true, ], 'condition' => [ 'icon_platform' => [ Social_Network_Provider::VIMEO, Social_Network_Provider::FACEBOOK, Social_Network_Provider::SOUNDCLOUD, Social_Network_Provider::SPOTIFY, Social_Network_Provider::INSTAGRAM, Social_Network_Provider::LINKEDIN, Social_Network_Provider::PINTEREST, Social_Network_Provider::TIKTOK, Social_Network_Provider::TWITTER, Social_Network_Provider::YOUTUBE, Social_Network_Provider::APPLEMUSIC, Social_Network_Provider::BEHANCE, Social_Network_Provider::DRIBBBLE, Social_Network_Provider::SPOTIFY, Social_Network_Provider::SOUNDCLOUD, Social_Network_Provider::URL, ], ], ], ); $repeater->add_control( 'icon_mail', [ 'label' => esc_html__( 'Email', 'elementor' ), 'type' => Controls_Manager::TEXT, 'placeholder' => esc_html__( 'Enter your email', 'elementor' ), 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'condition' => [ 'icon_platform' => [ Social_Network_Provider::EMAIL, ], ], 'ai' => [ 'active' => false, ], ] ); $repeater->add_control( 'icon_mail_subject', [ 'label' => esc_html__( 'Subject', 'elementor' ), 'type' => Controls_Manager::TEXT, 'placeholder' => esc_html__( 'Subject', 'elementor' ), 'label_block' => true, 'condition' => [ 'icon_platform' => [ Social_Network_Provider::EMAIL, ], ], ] ); $repeater->add_control( 'icon_mail_body', [ 'label' => esc_html__( 'Message', 'elementor' ), 'type' => Controls_Manager::TEXTAREA, 'placeholder' => esc_html__( 'Message', 'elementor' ), 'label_block' => true, 'condition' => [ 'icon_platform' => [ Social_Network_Provider::EMAIL, ], ], ] ); $repeater->add_control( 'icon_number', [ 'label' => esc_html__( 'Number', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'placeholder' => esc_html__( '+', 'elementor' ), 'condition' => [ 'icon_platform' => [ Social_Network_Provider::TELEPHONE, Social_Network_Provider::WHATSAPP, ], ], 'ai' => [ 'active' => false, ], ], ); $repeater->add_control( 'icon_location', [ 'label' => esc_html__( 'Location', 'elementor' ), 'type' => Controls_Manager::URL, 'dynamic' => [ 'active' => true, ], 'default' => [ 'is_external' => true, ], 'label_block' => true, 'placeholder' => esc_html__( 'Paste Waze link', 'elementor' ), 'condition' => [ 'icon_platform' => [ Social_Network_Provider::WAZE, ], ], 'ai' => [ 'active' => false, ], ], ); $repeater->add_control( 'icon_username', [ 'label' => esc_html__( 'Username', 'elementor' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'label_block' => true, 'placeholder' => esc_html__( 'Enter your username', 'elementor' ), 'condition' => [ 'icon_platform' => [ Social_Network_Provider::MESSENGER, ], ], ], ); $this->add_control( 'icon', [ 'max_items' => $config['content']['icon_section']['platform']['limit'], 'type' => Controls_Manager::REPEATER, 'fields' => $repeater->get_controls(), 'title_field' => $this->get_icon_title_field(), 'prevent_empty' => true, 'button_text' => esc_html__( 'Add Icon', 'elementor' ), 'default' => $config['content']['icon_section']['default'], ] ); $this->end_controls_section(); } protected function get_icon_title_field(): string { $platform_icons_js = json_encode( Social_Network_Provider::get_social_networks_icons() ); return << {{{ icon_platform }}} JS; } protected function add_style_tab(): void { $this->add_style_identity_controls(); $this->add_style_bio_controls(); $this->add_style_icons_controls(); $this->add_style_cta_section(); $this->add_style_image_links_controls(); $this->add_style_background_controls(); } protected function add_bio_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'bio_section', [ 'label' => esc_html__( 'Bio', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); $this->add_control( 'bio_heading', [ 'label' => esc_html__( 'Heading', 'elementor' ), 'type' => Controls_Manager::TEXTAREA, 'dynamic' => [ 'active' => true, ], 'placeholder' => esc_html__( 'Heading', 'elementor' ), 'default' => esc_html__( 'Sara Parker', 'elementor' ), ] ); $this->add_html_tag_control( 'bio_heading_tag', 'h2' ); $this->add_control( 'bio_title', [ 'label' => esc_html__( 'Title or Tagline', 'elementor' ), 'type' => Controls_Manager::TEXTAREA, 'dynamic' => [ 'active' => true, ], 'placeholder' => esc_html__( 'Title', 'elementor' ), 'default' => $config['content']['bio_section']['title']['default'], ] ); $this->add_html_tag_control( 'bio_title_tag', 'h3' ); if ( $config['content']['bio_section']['has_about_field'] ) { $this->add_control( 'bio_about', [ 'label' => esc_html__( 'About Heading', 'elementor' ), 'type' => Controls_Manager::TEXTAREA, 'dynamic' => [ 'active' => true, ], 'placeholder' => esc_html__( 'About', 'elementor' ), 'default' => esc_html__( 'About Me', 'elementor' ), ] ); $this->add_html_tag_control( 'bio_about_tag', 'h3' ); } $this->add_control( 'bio_description', [ 'label' => esc_html__( 'Description', 'elementor' ), 'type' => Controls_Manager::TEXTAREA, 'dynamic' => [ 'active' => true, ], 'placeholder' => esc_html__( 'Description', 'elementor' ), 'default' => $config['content']['bio_section']['description']['default'], ] ); $this->end_controls_section(); } protected function add_identity_section(): void { $config = static::get_configuration(); $this->start_controls_section( 'identity_section', [ 'label' => esc_html__( 'Identity', 'elementor' ), 'tab' => Controls_Manager::TAB_CONTENT, ] ); if ( $config['content']['identity_section']['has_profile_image_controls'] ) { $this->add_control( 'identity_heading_cover', [ 'label' => esc_html__( 'Cover', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_control( 'identity_image_cover', [ 'label' => esc_html__( 'Choose Image', 'elementor' ), 'type' => Controls_Manager::MEDIA, 'default' => [ 'url' => Utils::get_placeholder_image_src(), ], ] ); $this->add_responsive_control( 'identity_image_cover_position', [ 'label' => esc_html__( 'Position', 'elementor' ), 'type' => Controls_Manager::SELECT, 'desktop_default' => 'center center', 'tablet_default' => 'center center', 'mobile_default' => 'center center', 'options' => $this->get_image_position_options(), 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-identity-image-cover-position: {{VALUE}}', ], 'condition' => [ 'identity_image_cover[url]!' => '', ], ] ); } if ( $config['content']['identity_section']['has_heading_text'] ) { $this->add_control( 'identity_heading', [ 'label' => $config['content']['identity_section']['has_heading_text'], 'type' => Controls_Manager::HEADING, ] ); } if ( $config['content']['identity_section']['identity_image_style'] ) { $this->add_control( 'identity_image_style', [ 'label' => esc_html__( 'Image style', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => $config['content']['identity_section']['identity_image_style']['default'], 'options' => [ 'profile' => esc_html__( 'Profile', 'elementor' ), 'cover' => esc_html__( 'Cover', 'elementor' ), ], ] ); } $this->add_control( 'identity_image', [ 'label' => esc_html__( 'Choose Image', 'elementor' ), 'type' => Controls_Manager::MEDIA, 'default' => [ 'url' => Utils::get_placeholder_image_src(), ], ] ); $this->add_responsive_control( 'identity_image_position', [ 'label' => esc_html__( 'Position', 'elementor' ), 'type' => Controls_Manager::SELECT, 'desktop_default' => 'center center', 'tablet_default' => 'center center', 'mobile_default' => 'center center', 'options' => $this->get_image_position_options(), 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-identity-image-profile-position: {{VALUE}}', ], 'condition' => [ 'identity_image[url]!' => '', ], ] ); $this->end_controls_section(); } protected function add_style_image_links_controls(): void { $config = static::get_configuration(); if ( empty( $config['style']['image_links_section'] ) ) { return; } $this->start_controls_section( 'image_links_section_style', [ 'label' => esc_html__( 'Image Links', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_responsive_control( 'image_links_height', [ 'label' => esc_html__( 'Image Height', 'elementor' ) . ' (px)', 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px' ], 'range' => [ 'px' => [ 'min' => 0, 'max' => 300, 'step' => 1, ], ], 'default' => [ 'unit' => 'px', ], 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-image-links-height: {{SIZE}}{{UNIT}}', ], ] ); if ( $config['style']['image_links_section']['has_border_control'] ) { $this->add_borders_control( $config['style']['image_links_section']['has_border_control']['prefix'], $config['style']['image_links_section']['has_border_control']['show_border_args'], $config['style']['image_links_section']['has_border_control']['border_width_args'], $config['style']['image_links_section']['has_border_control']['border_color_args'], ); } $this->end_controls_section(); } protected function add_style_cta_section(): void { $config = static::get_configuration(); if ( empty( $config['style']['cta_section'] ) ) { return; } $this->start_controls_section( 'cta_links_section_style', [ 'label' => esc_html__( 'CTA Link Buttons', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); if ( $config['style']['cta_section']['has_cta_control_text'] ) { $this->add_control( 'cta_links_heading', [ 'label' => $config['style']['cta_section']['has_cta_control_text'], 'type' => Controls_Manager::HEADING, ] ); } if ( $config['style']['cta_section']['has_link_type'] ) { $this->add_control( 'cta_links_type', [ 'label' => esc_html__( 'Type', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => $config['style']['cta_section']['has_link_type']['default'], 'options' => [ 'button' => esc_html__( 'Button', 'elementor' ), 'link' => esc_html__( 'Link', 'elementor' ), ], ] ); } $this->add_control( 'cta_links_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-ctas-text-color: {{VALUE}}', '{{WRAPPER}} .e-link-in-bio__cta.is-type-link' => '--e-link-in-bio-ctas-text-color: {{VALUE}}', ], ] ); $condition_if_has_links = []; if ( $config['style']['cta_section']['has_link_type'] ) { $condition_if_has_links = [ 'cta_links_type' => 'button', ]; } if ( $config['style']['cta_section']['has_background_control'] ) { $this->add_control( 'cta_links_background_color', [ 'label' => esc_html__( 'Background Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'condition' => $condition_if_has_links, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-ctas-background-color: {{VALUE}}', ], ] ); } $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'cta_links_typography', 'selector' => '{{WRAPPER}} .e-link-in-bio__cta', ] ); if ( $config['style']['cta_section']['has_border_control'] ) { $this->add_borders_control( $config['style']['cta_section']['has_border_control']['prefix'], $config['style']['cta_section']['has_border_control']['show_border_args'], $config['style']['cta_section']['has_border_control']['border_width_args'], $config['style']['cta_section']['has_border_control']['border_color_args'], ); } if ( $config['style']['cta_section']['has_corners'] ) { $this->add_control( 'cta_links_corners', [ 'label' => esc_html__( 'Corners', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => $config['style']['cta_section']['has_corners']['default'], 'options' => $config['style']['cta_section']['has_corners']['options'], 'condition' => $condition_if_has_links, ] ); } if ( $config['style']['cta_section']['has_padding'] ) { $this->add_control( 'cta_links_hr', [ 'type' => Controls_Manager::DIVIDER, ] ); $this->add_responsive_control( 'cta_links_padding', [ 'label' => esc_html__( 'Padding', 'elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px', '%', 'em', 'rem' ], 'default' => [ 'unit' => 'px', 'isLinked' => false, ], 'condition' => $condition_if_has_links, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-ctas-padding-block-end: {{BOTTOM}}{{UNIT}}; --e-link-in-bio-ctas-padding-block-start: {{TOP}}{{UNIT}}; --e-link-in-bio-ctas-padding-inline-end: {{RIGHT}}{{UNIT}}; --e-link-in-bio-ctas-padding-inline-start: {{LEFT}}{{UNIT}};', ], ] ); } if ( $config['style']['cta_section']['has_dividers'] ) { $this->add_control( 'cta_links_hr', [ 'type' => Controls_Manager::HEADING, 'label' => esc_html__( 'Dividers', 'elementor' ), 'separator' => 'before', ] ); $this->add_control( 'cta_links_divider_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio__cta' => 'border-bottom-color: {{VALUE}}', ], ] ); $this->add_control( 'cta_links_divider_width', [ 'label' => esc_html__( 'Weight', 'elementor' ) . ' (px)', 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px' ], 'range' => [ 'px' => [ 'min' => 0, 'max' => 10, 'step' => 1, ], ], 'default' => [ 'unit' => 'px', ], 'selectors' => [ '{{WRAPPER}} .e-link-in-bio__cta' => 'border-bottom-width: {{SIZE}}{{UNIT}}', ], ] ); } $this->end_controls_section(); } protected function add_style_identity_controls(): void { $config = static::get_configuration(); $this->start_controls_section( 'identity_section_style', [ 'label' => esc_html__( 'Identity', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $condition = []; if ( $config['content']['identity_section']['identity_image_style'] ) { $condition = [ 'identity_image_style' => 'profile', ]; } $this->add_identity_image_profile_controls( $condition ); $condition = [ 'identity_image_style' => 'cover', ]; $this->add_identity_image_cover_control( $condition ); $this->end_controls_section(); } protected function add_content_tab(): void { $this->add_identity_section(); $this->add_bio_section(); $this->add_icons_controls(); $this->add_cta_controls(); $this->add_image_links_controls(); } protected function add_style_bio_controls(): void { $this->start_controls_section( 'bio_section_style', [ 'label' => esc_html__( 'Bio', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'bio_heading_heading', [ 'label' => esc_html__( 'Heading', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_control( 'bio_heading_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-heading-color: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'bio_heading_typography', 'selector' => '{{WRAPPER}} .e-link-in-bio__heading', ] ); $this->add_control( 'bio_title_heading', [ 'label' => esc_html__( 'Title or Tagline', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_control( 'bio_title_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-title-color: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'bio_title_typography', 'selector' => '{{WRAPPER}} .e-link-in-bio__title', ] ); $this->add_control( 'bio_description_heading', [ 'label' => esc_html__( 'Description', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_control( 'bio_description_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-description-color: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'bio_description_typography', 'selector' => '{{WRAPPER}} .e-link-in-bio__description', ] ); $this->end_controls_section(); } protected function add_style_icons_controls(): void { $this->start_controls_section( 'icons_section_style', [ 'label' => esc_html__( 'Icons', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'icons_color', [ 'label' => esc_html__( 'Color', 'elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-icon-color: {{VALUE}}', ], ] ); $this->add_control( 'icons_size', [ 'label' => esc_html__( 'Size', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'small', 'options' => [ 'small' => esc_html__( 'Small', 'elementor' ), 'medium' => esc_html__( 'Medium', 'elementor' ), 'large' => esc_html__( 'Large', 'elementor' ), ], ] ); $this->end_controls_section(); } protected function add_style_background_controls(): void { $config = static::get_configuration(); // Defaults for background image and overlay $bg_section_image_field_option_defaults = [ 'background' => [ 'default' => 'classic', ], 'position' => [ 'default' => 'center center', ], 'size' => [ 'default' => 'cover', ], ]; // Background image $bg_image_field_options = $bg_section_image_field_option_defaults; if ( $config['style']['border_section']['field_options'] ) { $bg_image_field_options = array_merge( $bg_section_image_field_option_defaults, $config['style']['border_section']['field_options'] ); } // Background overlay $bg_overlay_image_field_options = $bg_section_image_field_option_defaults; if ( $config['style']['border_section']['overlay_field_options'] ) { $bg_overlay_image_field_options = array_merge( $bg_section_image_field_option_defaults, $config['style']['border_section']['overlay_field_options'] ); } $this->start_controls_section( 'background_border_section_style', [ 'label' => esc_html__( 'Box', 'elementor' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'background_border_background', [ 'label' => esc_html__( 'Background', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'background_border_background_group', 'types' => [ 'classic', 'gradient' ], 'selector' => '{{WRAPPER}} .e-link-in-bio__bg', 'fields_options' => $bg_image_field_options, ] ); $this->add_control( 'background_border_background_overlay', [ 'label' => esc_html__( 'Background Overlay', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'background_border_background_overlay_group', 'types' => [ 'classic', 'gradient' ], 'selector' => '{{WRAPPER}} .e-link-in-bio__bg-overlay', 'fields_options' => $bg_overlay_image_field_options, ] ); $this->add_responsive_control( 'background_overlay_opacity', [ 'label' => esc_html__( 'Opacity', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'range' => [ '%' => [ 'max' => 1, 'min' => 0.10, 'step' => 0.01, ], ], 'default' => [ 'unit' => '%', 'size' => 0.5, ], 'condition' => [ 'background_border_background_overlay_group_background!' => '', ], 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--background-overlay-opacity: {{SIZE}};', ], ] ); $this->add_borders_control( 'background', [ 'selectors' => [], 'separator' => 'before', ], [ 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-border-width: {{SIZE}}{{UNIT}};', ], ], [ 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-border-color: {{VALUE}};', ], ] ); $this->add_control( 'background_dimensions', [ 'label' => esc_html__( 'Dimensions', 'elementor' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ] ); $this->add_control( 'advanced_layout_full_width_custom', [ 'label' => esc_html__( 'Full Width', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Yes', 'elementor' ), 'label_off' => esc_html__( 'No', 'elementor' ), 'default' => '', ] ); $this->add_responsive_control( 'advanced_layout_width', [ 'label' => esc_html__( 'Layout Width', 'elementor' ) . ' (px)', 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px' ], 'range' => [ 'px' => [ 'min' => 0, 'max' => 500, 'step' => 1, ], ], 'default' => [ 'unit' => 'px', ], 'condition' => [ 'advanced_layout_full_width_custom' => '', ], 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-container-width: {{SIZE}}{{UNIT}};', ], ] ); $this->add_responsive_control( 'advanced_layout_content_width', [ 'label' => esc_html__( 'Content Width', 'elementor' ) . ' (px)', 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px' ], 'range' => [ 'px' => [ 'min' => 0, 'max' => 400, 'step' => 1, ], ], 'default' => [ 'unit' => 'px', ], 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-content-width: {{SIZE}}{{UNIT}};', ], ] ); $this->add_control( 'advanced_layout_full_screen_height', [ 'label' => esc_html__( 'Full Screen Height', 'elementor' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'Yes', 'elementor' ), 'label_off' => esc_html__( 'No', 'elementor' ), 'return_value' => 'yes', 'default' => '', 'condition' => [ 'advanced_layout_full_width_custom' => 'yes', ], ], ); $configured_breakpoints = $this->get_configured_breakpoints(); $this->add_control( 'advanced_layout_full_screen_height_controls', [ 'label' => esc_html__( 'Apply Full Screen Height on', 'elementor' ), 'type' => Controls_Manager::SELECT2, 'label_block' => true, 'multiple' => true, 'options' => $configured_breakpoints['devices_options'], 'default' => $configured_breakpoints['active_devices'], 'condition' => [ 'advanced_layout_full_width_custom' => 'yes', 'advanced_layout_full_screen_height' => 'yes', ], ] ); $this->end_controls_section(); } protected function add_identity_image_profile_controls( array $condition ): void { $config = static::get_configuration(); $this->add_responsive_control( 'identity_image_size', [ 'label' => esc_html__( 'Image Size', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'range' => [ 'px' => [ 'min' => 0, 'max' => $config['style']['identity_section']['profile_image_max'] ?? 150, ], ], 'default' => [ 'unit' => 'px', ], 'condition' => $condition, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-identity-image-profile-width: {{SIZE}}{{UNIT}};', ], ] ); if ( $config['style']['identity_section']['has_profile_image_shape'] ) { $this->add_control( 'identity_image_shape', [ 'label' => esc_html__( 'Image Shape', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'circle', 'options' => [ 'circle' => esc_html__( 'Circle', 'elementor' ), 'square' => esc_html__( 'Square', 'elementor' ), ], 'condition' => $condition, ] ); } $this->add_borders_control( 'identity_image', [ 'condition' => $condition, ], [ 'condition' => $condition, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-identity-image-profile-border-width: {{SIZE}}{{UNIT}};', ], ], [ 'condition' => $condition, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-identity-image-profile-border-color: {{VALUE}};', ], ] ); } protected function add_identity_image_cover_control( array $condition ): void { $this->add_responsive_control( 'identity_image_height', [ 'label' => esc_html__( 'Image Height', 'elementor' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'range' => [ 'px' => [ 'min' => 0, 'max' => $config['style']['identity_section']['cover_image_max'] ?? 1000, 'step' => 1, ], '%' => [ 'min' => 0, 'max' => 100, ], ], 'default' => [ 'unit' => 'px', ], 'condition' => $condition, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-identity-image-cover-height: {{SIZE}}{{UNIT}};', ], ] ); $this->add_borders_control( 'identity_image_bottom', [ 'condition' => $condition, 'label' => esc_html__( 'Bottom Border', 'elementor' ), ], [ 'condition' => $condition, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-identity-image-cover-border-bottom-width: {{SIZE}}{{UNIT}};', ], ], [ 'condition' => $condition, 'selectors' => [ '{{WRAPPER}} .e-link-in-bio' => '--e-link-in-bio-identity-image-cover-border-color: {{VALUE}};', ], ] ); } } landing-pages/module.php000064400000042235147207015700011264 0ustar00 'landing-pages', 'title' => esc_html__( 'Landing Pages', 'elementor' ), 'description' => esc_html__( 'Adds a new Elementor content type that allows creating beautiful landing pages instantly in a streamlined workflow.', 'elementor' ), 'release_status' => Experiments_Manager::RELEASE_STATUS_BETA, 'default' => Experiments_Manager::STATE_ACTIVE, 'new_site' => [ 'default_inactive' => true, 'minimum_installation_version' => '3.22.0', ], ]; } /** * Get Trashed Landing Pages Posts * * Returns the posts property of a WP_Query run for Landing Pages with post_status of 'trash'. * * @since 3.1.0 * * @return array trashed posts */ private function get_trashed_landing_page_posts() { if ( $this->trashed_posts ) { return $this->trashed_posts; } // `'posts_per_page' => 1` is because this is only used as an indicator to whether there are any trashed landing pages. $trashed_posts_query = new \WP_Query( [ 'no_found_rows' => true, 'post_type' => self::CPT, 'post_status' => 'trash', 'posts_per_page' => 1, 'meta_key' => '_elementor_template_type', 'meta_value' => self::DOCUMENT_TYPE, ] ); $this->trashed_posts = $trashed_posts_query->posts; return $this->trashed_posts; } private function has_landing_pages() { if ( null !== $this->has_pages ) { return $this->has_pages; } $posts_query = new \WP_Query( [ 'no_found_rows' => true, 'post_type' => self::CPT, 'post_status' => 'any', 'posts_per_page' => 1, 'meta_key' => '_elementor_template_type', 'meta_value' => self::DOCUMENT_TYPE, ] ); $this->has_pages = $posts_query->post_count > 0; return $this->has_pages; } /** * Is Elementor Landing Page. * * Check whether the post is an Elementor Landing Page. * * @since 3.1.0 * @access public * * @param \WP_Post $post Post Object * * @return bool Whether the post was built with Elementor. */ public function is_elementor_landing_page( $post ) { return self::CPT === $post->post_type; } private function get_menu_args() { if ( $this->has_landing_pages() ) { $menu_slug = self::ADMIN_PAGE_SLUG; $function = null; } else { $menu_slug = self::CPT; $function = [ $this, 'print_empty_landing_pages_page' ]; } return [ 'menu_slug' => $menu_slug, 'function' => $function, ]; } private function register_admin_menu( MainMenu $menu ) { $landing_pages_title = esc_html__( 'Landing Pages', 'elementor' ); $menu_args = array_merge( $this->get_menu_args(), [ 'page_title' => $landing_pages_title, 'menu_title' => $landing_pages_title, 'index' => 20, ] ); $menu->add_submenu( $menu_args ); } /** * Add Submenu Page * * Adds the 'Landing Pages' submenu item to the 'Templates' menu item. * * @since 3.1.0 */ private function register_admin_menu_legacy( Admin_Menu_Manager $admin_menu ) { $menu_args = $this->get_menu_args(); $slug = $menu_args['menu_slug']; $function = $menu_args['function']; if ( is_callable( $function ) ) { $admin_menu->register( $slug, new Landing_Pages_Empty_View_Menu_Item( $function ) ); } else { $admin_menu->register( $slug, new Landing_Pages_Menu_Item() ); } } /** * Get 'Add New' Landing Page URL * * Retrieves the custom URL for the admin dashboard's 'Add New' button in the Landing Pages admin screen. This URL * creates a new Landing Pages and directly opens the Elementor Editor with the Template Library modal open on the * Landing Pages tab. * * @since 3.1.0 * * @return string */ private function get_add_new_landing_page_url() { if ( ! $this->new_lp_url ) { $this->new_lp_url = Plugin::$instance->documents->get_create_new_post_url( self::CPT, self::DOCUMENT_TYPE ) . '#library'; } return $this->new_lp_url; } /** * Get Empty Landing Pages Page * * Prints the HTML content of the page that is displayed when there are no existing landing pages in the DB. * Added as the callback to add_submenu_page. * * @since 3.1.0 */ public function print_empty_landing_pages_page() { $template_sources = Plugin::$instance->templates_manager->get_registered_sources(); $source_local = $template_sources['local']; $trashed_posts = $this->get_trashed_landing_page_posts(); ?>
print_blank_state_template( esc_html__( 'Landing Page', 'elementor' ), $this->get_add_new_landing_page_url(), esc_html__( 'Build Effective Landing Pages for your business\' marketing campaigns.', 'elementor' ) ); if ( ! empty( $trashed_posts ) ) : ?>
', '' ); ?>
base ) { return $this->is_elementor_landing_page( get_post() ); } return false; } /** * Admin Localize Settings * * Enables adding properties to the globally available elementorAdmin.config JS object in the Admin Dashboard. * Runs on the 'elementor/admin/localize_settings' filter. * * @since 3.1.0 * * @param $settings * @return array|null */ private function admin_localize_settings( $settings ) { $additional_settings = [ 'urls' => [ 'addNewLandingPageUrl' => $this->get_add_new_landing_page_url(), ], 'landingPages' => [ 'landingPagesHasPages' => $this->has_landing_pages(), 'isLandingPageAdminEdit' => $this->is_landing_page_admin_edit(), ], ]; return array_replace_recursive( $settings, $additional_settings ); } /** * Register Landing Pages CPT * * @since 3.1.0 */ private function register_landing_page_cpt() { $labels = [ 'name' => esc_html__( 'Landing Pages', 'elementor' ), 'singular_name' => esc_html__( 'Landing Page', 'elementor' ), 'add_new' => esc_html__( 'Add New', 'elementor' ), 'add_new_item' => esc_html__( 'Add New Landing Page', 'elementor' ), 'edit_item' => esc_html__( 'Edit Landing Page', 'elementor' ), 'new_item' => esc_html__( 'New Landing Page', 'elementor' ), 'all_items' => esc_html__( 'All Landing Pages', 'elementor' ), 'view_item' => esc_html__( 'View Landing Page', 'elementor' ), 'search_items' => esc_html__( 'Search Landing Pages', 'elementor' ), 'not_found' => esc_html__( 'No landing pages found', 'elementor' ), 'not_found_in_trash' => esc_html__( 'No landing pages found in trash', 'elementor' ), 'parent_item_colon' => '', 'menu_name' => esc_html__( 'Landing Pages', 'elementor' ), ]; $args = [ 'labels' => $labels, 'public' => true, 'show_in_menu' => 'edit.php?post_type=elementor_library&tabs_group=library', 'capability_type' => 'page', 'taxonomies' => [ Source_Local::TAXONOMY_TYPE_SLUG ], 'supports' => [ 'title', 'editor', 'comments', 'revisions', 'trackbacks', 'author', 'excerpt', 'page-attributes', 'thumbnail', 'custom-fields', 'post-formats', 'elementor' ], ]; register_post_type( self::CPT, $args ); } /** * Remove Post Type Slug * * Landing Pages are supposed to act exactly like pages. This includes their URLs being directly under the site's * domain name. Since "Landing Pages" is a CPT, WordPress automatically adds the landing page slug as a prefix to * it's posts' permalinks. This method checks if the post's post type is Landing Pages, and if it is, it removes * the CPT slug from the requested post URL. * * Runs on the 'post_type_link' filter. * * @since 3.1.0 * * @param $post_link * @param $post * @param $leavename * @return string|string[] */ private function remove_post_type_slug( $post_link, $post, $leavename ) { // Only try to modify the permalink if the post is a Landing Page. if ( self::CPT !== $post->post_type || 'publish' !== $post->post_status ) { return $post_link; } // Any slug prefixes need to be removed from the post link. return trailingslashit( get_home_url() ) . trailingslashit( $post->post_name ); } /** * Adjust Landing Page Query * * Since Landing Pages are a CPT but should act like pages, the WP_Query that is used to fetch the page from the * database needs to be adjusted. This method adds the Landing Pages CPT to the list of queried post types, to * make sure the database query finds the correct Landing Page to display. * Runs on the 'pre_get_posts' action. * * @since 3.1.0 * * @param \WP_Query $query */ private function adjust_landing_page_query( \WP_Query $query ) { // Only handle actual pages. if ( ! $query->is_main_query() // If the query is not for a page. || ! isset( $query->query['page'] ) // If the query is for a static home/blog page. || is_home() // If the post type comes already set, the main query is probably a custom one made by another plugin. // In this case we do not want to intervene in order to not cause a conflict. || isset( $query->query['post_type'] ) ) { return; } // Create the post types property as an array and include the landing pages CPT in it. $query_post_types = [ 'post', 'page', self::CPT ]; // Since WordPress determined this is supposed to be a page, we'll pre-set the post_type query arg to make sure // it includes the Landing Page CPT, so when the query is parsed, our CPT will be a legitimate match to the // Landing Page's permalink (that is directly under the domain, without a CPT slug prefix). In some cases, // The 'name' property will be set, and in others it is the 'pagename', so we have to cover both cases. if ( ! empty( $query->query['name'] ) ) { $query->set( 'post_type', $query_post_types ); } elseif ( ! empty( $query->query['pagename'] ) && false === strpos( $query->query['pagename'], '/' ) ) { $query->set( 'post_type', $query_post_types ); // We also need to set the name query var since redirect_guess_404_permalink() relies on it. add_filter( 'pre_redirect_guess_404_permalink', function( $value ) use ( $query ) { set_query_var( 'name', $query->query['pagename'] ); return $value; } ); } } /** * Handle 404 * * This method runs after a page is not found in the database, but before a page is returned as a 404. * These cases are handled in this filter callback, that runs on the 'pre_handle_404' filter. * * In some cases (such as when a site uses custom permalink structures), WordPress's WP_Query does not identify a * Landing Page's URL as a post belonging to the Landing Page CPT. Some cases are handled successfully by the * adjust_landing_page_query() method, but some are not and still trigger a 404 process. This method handles such * cases by overriding the $wp_query global to fetch the correct landing page post entry. * * For example, since Landing Pages slugs come directly after the site domain name, WP_Query might parse the post * as a category page. Since there is no category matching the slug, it triggers a 404 process. In this case, we * run a query for a Landing Page post with the passed slug ($query->query['category_name']. If a Landing Page * with the passed slug is found, we override the global $wp_query with the new, correct query. * * @param $current_value * @param $query * @return false */ private function handle_404( $current_value, $query ) { global $wp_query; // If another plugin/theme already used this filter, exit here to avoid conflicts. if ( $current_value ) { return $current_value; } if ( // Make sure we only intervene in the main query. ! $query->is_main_query() // If a post was found, this is not a 404 case, so do not intervene. || ! empty( $query->posts ) // This filter is only meant to deal with wrong queries where the only query var is 'category_name'. // If there is no 'category_name' query var, do not intervene. || empty( $query->query['category_name'] ) // If the query is for a real taxonomy (determined by it including a table to search in, such as the // wp_term_relationships table), do not intervene. || ! empty( $query->tax_query->table_aliases ) ) { return false; } // Search for a Landing Page with the same name passed as the 'category name'. $possible_new_query = new \WP_Query( [ 'no_found_rows' => true, 'post_type' => self::CPT, 'name' => $query->query['category_name'], ] ); // Only if such a Landing Page is found, override the query to fetch the correct page. if ( ! empty( $possible_new_query->posts ) ) { $wp_query = $possible_new_query; //phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited } return false; } public function __construct() { $this->permalink_structure = get_option( 'permalink_structure' ); $this->register_landing_page_cpt(); // If there is a permalink structure set to the site, run the hooks that modify the Landing Pages permalinks to // match WordPress' native 'Pages' post type. if ( '' !== $this->permalink_structure ) { // Landing Pages' post link needs to be modified to be identical to the pages permalink structure. This // needs to happen in both the admin and the front end, since post links are also used in the admin pages. add_filter( 'post_type_link', function( $post_link, $post, $leavename ) { return $this->remove_post_type_slug( $post_link, $post, $leavename ); }, 10, 3 ); // The query itself only has to be manipulated when pages are viewed in the front end. if ( ! is_admin() || wp_doing_ajax() ) { add_action( 'pre_get_posts', function ( $query ) { $this->adjust_landing_page_query( $query ); } ); // Handle cases where visiting a Landing Page's URL returns 404. add_filter( 'pre_handle_404', function ( $value, $query ) { return $this->handle_404( $value, $query ); }, 10, 2 ); } } add_action( 'elementor/documents/register', function( Documents_Manager $documents_manager ) { $documents_manager->register_document_type( self::DOCUMENT_TYPE, Landing_Page::get_class_full_name() ); } ); add_action( 'elementor/admin/menu/register', function( Admin_Menu_Manager $admin_menu ) { $this->register_admin_menu_legacy( $admin_menu ); }, Source_Local::ADMIN_MENU_PRIORITY + 20 ); // Add the custom 'Add New' link for Landing Pages into Elementor's admin config. add_action( 'elementor/admin/localize_settings', function( array $settings ) { return $this->admin_localize_settings( $settings ); } ); add_filter( 'elementor/template_library/sources/local/register_taxonomy_cpts', function( array $cpts ) { $cpts[] = self::CPT; return $cpts; } ); // In the Landing Pages Admin Table page - Overwrite Template type column header title. add_action( 'manage_' . Landing_Pages_Module::CPT . '_posts_columns', function( $posts_columns ) { /** @var Source_Local $source_local */ $source_local = Plugin::$instance->templates_manager->get_source( 'local' ); return $source_local->admin_columns_headers( $posts_columns ); } ); // In the Landing Pages Admin Table page - Overwrite Template type column row values. add_action( 'manage_' . Landing_Pages_Module::CPT . '_posts_custom_column', function( $column_name, $post_id ) { /** @var Landing_Page $document */ $document = Plugin::$instance->documents->get( $post_id ); $document->admin_columns_content( $column_name ); }, 10, 2 ); // Overwrite the Admin Bar's 'New +' Landing Page URL with the link that creates the new LP in Elementor // with the Template Library modal open. add_action( 'admin_bar_menu', function( $admin_bar ) { // Get the Landing Page menu node. $new_landing_page_node = $admin_bar->get_node( 'new-e-landing-page' ); if ( $new_landing_page_node ) { $new_landing_page_node->href = $this->get_add_new_landing_page_url(); $admin_bar->add_node( $new_landing_page_node ); } }, 100 ); } } landing-pages/documents/landing-page.php000064400000004437147207015700014330 0ustar00print_admin_column_type(); } } protected function get_remote_library_config() { $config = [ 'type' => 'lp', 'default_route' => 'templates/landing-pages', 'autoImportSettings' => true, ]; return array_replace_recursive( parent::get_remote_library_config(), $config ); } } landing-pages/admin-menu-items/landing-pages-empty-view-menu-item.php000064400000001010147207015700021645 0ustar00render_callback = $render_callback; } public function render() { ( $this->render_callback )(); } } landing-pages/admin-menu-items/landing-pages-menu-item.php000064400000001250147207015700017547 0ustar00
get_css_assets_url( 'admin-top-bar', null, 'default', true ), [], ELEMENTOR_VERSION ); /** * Before admin top bar enqueue scripts. * * Fires before Elementor admin top bar scripts are enqueued. * * @since 3.19.0 */ do_action( 'elementor/admin_top_bar/before_enqueue_scripts', $this ); wp_enqueue_script( 'elementor-admin-top-bar', $this->get_js_assets_url( 'admin-top-bar' ), [ 'elementor-common', 'react', 'react-dom', 'tipsy', ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'elementor-admin-top-bar', 'elementor' ); $min_suffix = Utils::is_script_debug() ? '' : '.min'; wp_enqueue_script( 'tipsy', ELEMENTOR_ASSETS_URL . 'lib/tipsy/tipsy' . $min_suffix . '.js', [ 'jquery', ], '1.0.0', true ); $this->print_config(); } private function add_frontend_settings() { $settings = []; $settings['is_administrator'] = current_user_can( 'manage_options' ); // TODO: Find a better way to add apps page url to the admin top bar. $settings['apps_url'] = admin_url( 'admin.php?page=elementor-apps' ); $settings['promotion'] = [ 'text' => __( 'Upgrade Now', 'elementor' ), 'url' => 'https://go.elementor.com/wp-dash-admin-top-bar-upgrade/', ]; $settings['promotion'] = Filtered_Promotions_Manager::get_filtered_promotion_data( $settings['promotion'], 'elementor/admin_top_bar/go_pro_promotion', 'url' ); $current_screen = get_current_screen(); /** @var \Elementor\Core\Common\Modules\Connect\Apps\Library $library */ $library = Plugin::$instance->common->get_component( 'connect' )->get_app( 'library' ); if ( $library ) { $settings = array_merge( $settings, [ 'is_user_connected' => $library->is_connected(), 'connect_url' => $library->get_admin_url( 'authorize', [ 'utm_source' => 'top-bar', 'utm_medium' => 'wp-dash', 'utm_campaign' => 'connect-account', 'utm_content' => $current_screen->id, 'source' => 'generic', ] ), ] ); } $this->set_settings( $settings ); do_action( 'elementor/admin-top-bar/init', $this ); } private function is_top_bar_active() { $current_screen = get_current_screen(); if ( ! $current_screen ) { return false; } $is_elementor_page = strpos( $current_screen->id ?? '', 'elementor' ) !== false; $is_elementor_post_type_page = strpos( $current_screen->post_type ?? '', 'elementor' ) !== false; return apply_filters( 'elementor/admin-top-bar/is-active', $is_elementor_page || $is_elementor_post_type_page, $current_screen ); } /** * Module constructor. */ public function __construct() { parent::__construct(); add_action( 'current_screen', function () { if ( ! $this->is_top_bar_active() ) { return; } $this->add_frontend_settings(); add_action( 'in_admin_header', function () { $this->render_admin_top_bar(); } ); add_action( 'admin_enqueue_scripts', function () { $this->enqueue_scripts(); } ); } ); } } site-navigation/module.php000064400000003774147207015700011661 0ustar00data_manager_v2->register_controller( new Controller() ); $is_tests = Utils::is_elementor_tests(); $is_v2_experiment_on = Plugin::$instance->experiments->is_feature_active( 'editor_v2' ); if ( ! $is_v2_experiment_on && ! $is_tests ) { return; } $this->register_pages_panel_experiment(); if ( Plugin::$instance->experiments->is_feature_active( self::PAGES_PANEL_EXPERIMENT_NAME ) ) { add_filter( 'elementor/editor/v2/scripts/env', function( $env ) { $env['@elementor/editor-site-navigation'] = [ 'is_pages_panel_active' => true, ]; return $env; } ); $this->register_rest_fields(); } } /** * Retrieve the module name. * * @return string */ public function get_name() { return 'site-navigation'; } /** * Register Experiment * * @since 3.16.0 * * @return void * @throws \Exception */ private function register_pages_panel_experiment() { Plugin::$instance->experiments->add_feature( [ 'name' => self::PAGES_PANEL_EXPERIMENT_NAME, 'title' => esc_html__( 'Pages Panel', 'elementor' ), 'release_status' => Experiments_Manager::RELEASE_STATUS_ALPHA, 'default' => Experiments_Manager::STATE_INACTIVE, 'hidden' => true, 'dependencies' => [ 'editor_v2', ], ] ); } private function register_rest_fields() { add_action( 'rest_api_init', function() { ( new Page_User_Can() )->register_rest_field(); } ); } } site-navigation/data/endpoints/recent-posts.php000064400000004762147207015700015734 0ustar00 [ 'description' => 'Number of posts to return', 'type' => 'integer', 'required' => true, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ], 'post_type' => [ 'description' => 'Post types to retrieve', 'type' => 'array', 'required' => false, 'default' => [ 'page', 'post', Source_Local::CPT ], 'sanitize_callback' => 'rest_sanitize_array', 'validate_callback' => 'rest_validate_request_arg', ], 'post__not_in' => [ 'description' => 'Post id`s to exclude', 'type' => 'array', 'required' => [], 'sanitize_callback' => 'wp_parse_id_list', 'validate_callback' => 'rest_validate_request_arg', ], ]; parent::register_items_route( $methods, $args ); } public function get_name() { return 'recent-posts'; } public function get_format() { return 'site-navigation/recent-posts'; } public function get_items( $request ) { $args = [ 'posts_per_page' => $request->get_param( 'posts_per_page' ), 'post_type' => $request->get_param( 'post_type' ), 'fields' => 'ids', 'meta_query' => [ [ 'key' => Document::TYPE_META_KEY, 'value' => Kit::get_type(), // Exclude kits. 'compare' => '!=', ], ], ]; $exclude = $request->get_param( 'post__not_in' ); if ( ! empty( $exclude ) ) { $args['post__not_in'] = $exclude; } $recently_edited_query = Utils::get_recently_edited_posts_query( $args ); $recent = []; foreach ( $recently_edited_query->posts as $id ) { $document = Plugin::$instance->documents->get( $id ); $recent[] = [ 'id' => $id, 'title' => get_the_title( $id ), 'edit_url' => $document->get_edit_url(), 'date_modified' => get_post_timestamp( $id, 'modified' ), 'type' => [ 'post_type' => get_post_type( $id ), 'doc_type' => $document->get_name(), 'label' => $document->get_title(), ], 'user_can' => [ 'edit' => current_user_can( 'edit_post', $id ), ], ]; } return $recent; } } site-navigation/data/endpoints/add-new-post.php000064400000004174147207015700015605 0ustar00 [ 'description' => 'Post type to create', 'type' => 'string', 'required' => false, 'default' => 'post', 'sanitize_callback' => function ( $value ) { return sanitize_text_field( $value ); }, 'validate_callback' => 'rest_validate_request_arg', ], ]; $this->register_items_route( \WP_REST_Server::CREATABLE, $args ); } public function get_name() { return 'add-new-post'; } public function get_format() { return 'site-navigation/add-new-post'; } public function create_items( $request ) { $post_type = $request->get_param( 'post_type' ); if ( ! $this->validate_post_type( $post_type ) ) { return new \WP_Error( 400, sprintf( 'Post type %s does not exist.', $post_type ), [ 'status' => 400 ] ); } if ( ! User::is_current_user_can_edit_post_type( $post_type ) ) { return new \WP_Error( 401, sprintf( 'User dont have capability to create page of type - %s.', $post_type ), [ 'status' => 401 ] ); } // Temporary solution for the fact that documents creation not using the actual registered post types. $post_type = $this->map_post_type( $post_type ); $document = Plugin::$instance->documents->create( $post_type ); if ( is_wp_error( $document ) ) { return new \WP_Error( 500, sprintf( 'Error while creating %s.', $post_type ) ); } return [ 'id' => $document->get_main_id(), 'edit_url' => $document->get_edit_url(), ]; } private function validate_post_type( $post_type ): bool { $post_types = get_post_types(); return in_array( $post_type, $post_types ); } /** * Map post type to Elementor document type. * * @param $post_type * * @return string */ private function map_post_type( $post_type ): string { $post_type_map = [ 'page' => 'wp-page', 'post' => 'wp-post', ]; return $post_type_map[ $post_type ] ?? $post_type; } } site-navigation/data/endpoints/homepage.php000064400000001214147207015700015060 0ustar00 [ 'description' => 'Post id to duplicate', 'type' => 'integer', 'required' => true, 'sanitize_callback' => 'absint', 'validate_callback' => 'rest_validate_request_arg', ], 'title' => [ 'description' => 'Post title', 'type' => 'string', 'required' => false, 'sanitize_callback' => function ( $value ) { return sanitize_text_field( $value ); }, 'validate_callback' => 'rest_validate_request_arg', ], ]; $this->register_items_route( \WP_REST_Server::CREATABLE, $args ); } public function get_name() { return 'duplicate-post'; } public function get_format() { return 'site-navigation/duplicate-post'; } public function create_items( $request ) { $post_id = $request->get_param( 'post_id' ); $post_title = $request->get_param( 'title' ); $post = get_post( $post_id ); if ( ! User::is_current_user_can_edit_post_type( $post->post_type ) ) { return new \WP_Error( 401, sprintf( 'User dont have capability to create page of type - %s.', $post->post_type ), [ 'status' => 401 ] ); } if ( ! $post ) { return new \WP_Error( 500, 'Post not found' ); } $new_post_id = $this->duplicate_post( $post, $post_title ); if ( is_wp_error( $new_post_id ) ) { return new \WP_Error( 500, 'Error while duplicating post.' ); } //Duplicate all post meta $this->duplicate_post_meta( $post_id, $new_post_id ); //Duplicate all taxonomies $this->duplicate_post_taxonomies( $post_id, $new_post_id ); return [ 'post_id' => $new_post_id, ]; } /** * Duplicate post * * @param $post * * @return int|\WP_Error */ private function duplicate_post( $post, $post_title ) { $post_status = 'draft'; $current_user = wp_get_current_user(); $new_post_author = $current_user->ID; $args = [ 'comment_status' => $post->comment_status, 'ping_status' => $post->ping_status, 'post_author' => $new_post_author, 'post_content' => $post->post_content, 'post_excerpt' => $post->post_excerpt, 'post_parent' => $post->post_parent, 'post_password' => $post->post_password, 'post_status' => $post_status, 'post_title' => $post_title, 'post_type' => $post->post_type, 'to_ping' => $post->to_ping, 'menu_order' => $post->menu_order, ]; return wp_insert_post( $args ); } /** * Duplicate the associated post meta to the new post ID. * * @param int $post_id * @param int $new_post_id */ private function duplicate_post_meta( int $post_id, int $new_post_id ) { $post_meta = get_post_meta( $post_id ); if ( empty( $post_meta ) || ! is_array( $post_meta ) ) { return; } foreach ( $post_meta as $key => $values ) { if ( '_wp_old_slug' === $key ) { // Ignore this meta key continue; } foreach ( $values as $value ) { $value = maybe_unserialize( $value ); add_post_meta( $new_post_id, $key, wp_slash( $value ) ); } } } /** * duplicate_post_taxonomies * * @param int $post_id * @param int $new_post_id */ private function duplicate_post_taxonomies( $post_id, $new_post_id ) { $taxonomies = array_map( 'sanitize_text_field', get_object_taxonomies( get_post_type( $post_id ) ) ); if ( empty( $taxonomies ) || ! is_array( $taxonomies ) ) { return; } foreach ( $taxonomies as $taxonomy ) { $post_terms = wp_get_object_terms( $post_id, $taxonomy, [ 'fields' => 'slugs' ] ); if ( ! is_wp_error( $post_terms ) ) { wp_set_object_terms( $new_post_id, $post_terms, $taxonomy, false ); } } } } site-navigation/data/controller.php000064400000002761147207015700013463 0ustar00get_items_permissions_check( $request ); } public function create_item_permissions_check( $request ): bool { return $this->create_items_permissions_check( $request ); } public function register_endpoints() { $this->register_endpoint( new Recent_Posts( $this ) ); $this->register_endpoint( new Add_New_Post( $this ) ); if ( Plugin::$instance->experiments->is_feature_active( 'pages_panel' ) ) { $this->register_endpoint( new Duplicate_Post( $this ) ); $this->register_endpoint( new Homepage( $this ) ); } } protected function register_index_endpoint() { // Bypass, currently does not required. } } site-navigation/rest-fields/page-user-can.php000064400000001647147207015700015241 0ustar00 [ $this, 'get_callback' ], 'schema' => [ 'description' => __( 'Whether the current user can edit or delete this post', 'elementor' ), 'type' => 'array', ], ] ); } public function get_callback( $post ) { $can_edit = current_user_can( 'edit_post', $post['id'] ); $can_delete = current_user_can( 'delete_post', $post['id'] ); return [ 'edit' => $can_edit, 'delete' => $can_delete, ]; } } atomic-widgets/module.php000064400000004276147207015700011476 0ustar00register_experiment(); if ( Plugin::$instance->experiments->is_feature_active( self::EXPERIMENT_NAME ) ) { add_filter( 'elementor/editor/v2/packages', fn( $packages ) => $this->add_packages( $packages ) ); add_filter( 'elementor/widgets/register', fn( Widgets_Manager $widgets_manager ) => $this->register_widgets( $widgets_manager ) ); add_action( 'elementor/editor/after_enqueue_scripts', fn() => $this->enqueue_scripts() ); } } private function register_experiment() { Plugin::$instance->experiments->add_feature( [ 'name' => self::EXPERIMENT_NAME, 'title' => esc_html__( 'Atomic Widgets', 'elementor' ), 'description' => esc_html__( 'Enable atomic widgets.', 'elementor' ), 'hidden' => true, 'default' => Experiments_Manager::STATE_INACTIVE, 'release_status' => Experiments_Manager::RELEASE_STATUS_ALPHA, ] ); } private function add_packages( $packages ) { return array_merge( $packages, self::PACKAGES ); } private function register_widgets( Widgets_Manager $widgets_manager ) { $widgets_manager->register( new Atomic_Heading() ); $widgets_manager->register( new Atomic_Image() ); } /** * Enqueue the module scripts. * * @return void */ private function enqueue_scripts() { wp_enqueue_script( 'elementor-atomic-widgets-editor', $this->get_js_assets_url( 'atomic-widgets-editor' ), [ 'elementor-editor' ], ELEMENTOR_VERSION, true ); } } atomic-widgets/schema/atomic-prop.php000064400000000774147207015700013702 0ustar00default_value = $default_value; return $this; } public function get_default() { return $this->default_value; } public function jsonSerialize(): array { return [ 'default' => $this->default_value, ]; } } atomic-widgets/widgets/atomic-heading.php000064400000004537147207015700014530 0ustar00get_atomic_settings(); // TODO: Move the validation/sanitization to the props schema constraints. $escaped_tag = Utils::validate_html_tag( $settings['tag'] ); $escaped_title = esc_html( $settings['title'] ); $class = ''; if ( ! empty( $settings['classes'] ) ) { $class = "class='" . esc_attr( $settings['classes'] ) . "'"; } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo "<$escaped_tag $class>$escaped_title"; } protected function define_atomic_controls(): array { $tag_control = Select_Control::bind_to( 'tag' ) ->set_label( esc_html__( 'Tag', 'elementor' ) ) ->set_options( [ [ 'value' => 'h1', 'label' => 'H1', ], [ 'value' => 'h2', 'label' => 'H2', ], [ 'value' => 'h3', 'label' => 'H3', ], [ 'value' => 'h4', 'label' => 'H4', ], [ 'value' => 'h5', 'label' => 'H5', ], [ 'value' => 'h6', 'label' => 'H6', ], ]); $title_control = Textarea_Control::bind_to( 'title' ) ->set_label( __( 'Title', 'elementor' ) ) ->set_placeholder( __( 'Type your title here', 'elementor' ) ); $tag_and_title_section = Section::make() ->set_label( __( 'Content', 'elementor' ) ) ->set_items( [ $tag_control, $title_control, ]); return [ $tag_and_title_section, ]; } protected static function define_props_schema(): array { return [ 'classes' => Atomic_Prop::make(), 'tag' => Atomic_Prop::make() ->default( 'h2' ), 'title' => Atomic_Prop::make() ->default( __( 'Your Title Here', 'elementor' ) ), ]; } } atomic-widgets/widgets/atomic-image.php000064400000005464147207015700014213 0ustar00get_atomic_settings(); // TODO: Replace with actual URL prop $image_url = $settings['url']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo ""; } private function get_image_size_options() { $wp_image_sizes = self::get_wp_image_sizes(); $image_sizes = []; foreach ( $wp_image_sizes as $size_key => $size_attributes ) { $control_title = ucwords( str_replace( '_', ' ', $size_key ) ); if ( is_array( $size_attributes ) ) { $control_title .= sprintf( ' - %d*%d', $size_attributes['width'], $size_attributes['height'] ); } $image_sizes[] = [ 'label' => $control_title, 'value' => $size_key, ]; } $image_sizes[] = [ 'label' => esc_html__( 'Full', 'elementor' ), 'value' => 'full', ]; return $image_sizes; } private static function get_wp_image_sizes() { $default_image_sizes = get_intermediate_image_sizes(); $additional_sizes = wp_get_additional_image_sizes(); $image_sizes = []; foreach ( $default_image_sizes as $size ) { $image_sizes[ $size ] = [ 'width' => (int) get_option( $size . '_size_w' ), 'height' => (int) get_option( $size . '_size_h' ), 'crop' => (bool) get_option( $size . '_crop' ), ]; } if ( $additional_sizes ) { $image_sizes = array_merge( $image_sizes, $additional_sizes ); } // /** This filter is documented in wp-admin/includes/media.php */ return apply_filters( 'image_size_names_choose', $image_sizes ); } protected function define_atomic_controls(): array { $options = $this->get_image_size_options(); $resolution_control = Select_Control::bind_to( 'image_size' ) ->set_label( esc_html__( 'Image Resolution', 'elementor' ) ) ->set_options( $options ); $content_section = Section::make() ->set_label( esc_html__( 'Content', 'elementor' ) ) ->set_items( [ $resolution_control, ]); return [ $content_section, ]; } protected static function define_props_schema(): array { return [ 'image_size' => Atomic_Prop::make() ->default( 'large' ), 'url' => Atomic_Prop::make() ->default( Utils::get_placeholder_image_src() ), ]; } } atomic-widgets/base/atomic-widget-base.php000064400000007415147207015700014566 0ustar00version = $data['version'] ?? '0.0'; $this->styles = $data['styles'] ?? []; } public function get_atomic_controls() { $controls = $this->define_atomic_controls(); return $this->get_valid_controls( $controls ); } private function get_valid_controls( array $controls ): array { $valid_controls = []; $schema = static::get_props_schema(); foreach ( $controls as $control ) { if ( $control instanceof Section ) { $cloned_section = clone $control; $cloned_section->set_items( $this->get_valid_controls( $control->get_items() ) ); $valid_controls[] = $cloned_section; continue; } if ( ! ( $control instanceof Atomic_Control_Base ) ) { $this->safe_throw( 'Control must be an instance of `Atomic_Control_Base`.' ); continue; } $prop_name = $control->get_bind(); if ( ! $prop_name ) { $this->safe_throw( 'Control is missing a bound prop from the schema.' ); continue; } if ( ! array_key_exists( $prop_name, $schema ) ) { $this->safe_throw( "Prop `{$prop_name}` is not defined in the schema of `{$this->get_name()}`. Did you forget to define it?" ); continue; } $valid_controls[] = $control; } return $valid_controls; } private function safe_throw( string $message ) { if ( ! defined( 'ELEMENTOR_DEBUG' ) || ! ELEMENTOR_DEBUG ) { return; } throw new \Exception( $message ); } abstract protected function define_atomic_controls(): array; final public function get_controls( $control_id = null ) { if ( ! empty( $control_id ) ) { return null; } return []; } final public function get_initial_config() { $config = parent::get_initial_config(); $config['atomic_controls'] = $this->get_atomic_controls(); $config['version'] = $this->version; return $config; } final public function get_data_for_save() { $data = parent::get_data_for_save(); $data['version'] = $this->version; return $data; } final public function get_raw_data( $with_html_content = false ) { $raw_data = parent::get_raw_data( $with_html_content ); $raw_data['styles'] = $this->styles; return $raw_data; } final public function get_stack( $with_common_controls = true ) { return [ 'controls' => [], 'tabs' => [], ]; } final public function get_atomic_settings(): array { $schema = static::get_props_schema(); $raw_settings = $this->get_settings(); $transformed_settings = []; foreach ( $schema as $key => $prop ) { if ( array_key_exists( $key, $raw_settings ) ) { $transformed_settings[ $key ] = $raw_settings[ $key ]; } else { $transformed_settings[ $key ] = $prop->get_default(); } $transformed_settings[ $key ] = $this->transform_setting( $transformed_settings[ $key ] ); } return $transformed_settings; } public static function get_props_schema() { return static::define_props_schema(); } private function transform_setting( $setting ) { if ( ! $this->is_transformable( $setting ) ) { return $setting; } switch ( $setting['$$type'] ) { case 'classes': return is_array( $setting['value'] ) ? join( ' ', $setting['value'] ) : ''; default: return null; } } private function is_transformable( $setting ): bool { return ! empty( $setting['$$type'] ) && 'string' === getType( $setting['$$type'] ) && isset( $setting['value'] ); } abstract protected static function define_props_schema(): array; } atomic-widgets/base/atomic-control-base.php000064400000002035147207015700014754 0ustar00bind = $prop_name; } public function get_bind() { return $this->bind; } public function set_label( string $label ): self { $this->label = $label; return $this; } public function set_description( string $description ): self { $this->description = $description; return $this; } public function jsonSerialize(): array { return [ 'type' => 'control', 'value' => [ 'type' => $this->get_type(), 'bind' => $this->get_bind(), 'label' => $this->label, 'description' => $this->description, 'props' => $this->get_props(), ], ]; } } atomic-widgets/controls/section.php000064400000001641147207015700013511 0ustar00label = $label; return $this; } public function set_description( string $description ): self { $this->description = $description; return $this; } public function set_items( array $items ): self { $this->items = $items; return $this; } public function get_items() { return $this->items; } public function jsonSerialize(): array { return [ 'type' => 'section', 'value' => [ 'label' => $this->label, 'description' => $this->description, 'items' => $this->items, ], ]; } } atomic-widgets/controls/types/textarea-control.php000064400000001067147207015700016506 0ustar00placeholder = $placeholder; return $this; } public function get_props(): array { return [ 'placeholder' => $this->placeholder, ]; } } atomic-widgets/controls/types/select-control.php000064400000001032147207015700016140 0ustar00options = $options; return $this; } public function get_props(): array { return [ 'options' => $this->options, ]; } } lazyload/module.php000064400000005275147207015700010375 0ustar00is_lazy_load_background_images_enabled() ) { return; } add_action( 'wp_head', function() { if ( ! $this->should_lazyload() ) { return; } ?> should_lazyload() ) { return; } ?> preview->is_preview_mode() && ! Plugin::$instance->editor->is_edit_mode(); } private static function is_lazy_load_background_images_enabled(): bool { return '1' === get_option( 'elementor_lazy_load_background_images', '1' ); } } notes/module.php000064400000002323147207015700007675 0ustar00get_js_assets_url( 'notes' ), [ 'elementor-editor' ], ELEMENTOR_VERSION, true ); wp_set_script_translations( 'elementor-notes', 'elementor' ); } /** * Enqueue the module styles. * * @return void */ public function enqueue_styles() { wp_enqueue_style( 'elementor-notes', $this->get_css_assets_url( 'modules/notes/editor' ), [ 'elementor-editor' ], ELEMENTOR_VERSION ); } /** * @return bool */ public static function is_active() { return ! Utils::has_pro(); } /** * Initialize the Notes module. * * @return void */ public function __construct() { parent::__construct(); add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); add_action( 'elementor/editor/after_enqueue_styles', [ $this, 'enqueue_styles' ] ); } } usage/module.php000064400000041522147207015700007655 0ustar00 $elements ) { $doc_class = Plugin::$instance->documents->get_document_type( $doc_type ); if ( 'html' === $format && $doc_class ) { $doc_title = $doc_class::get_title(); } else { $doc_title = $doc_type; } $doc_count = $this->get_doc_type_count( $doc_class, $doc_type ); $tab_group = $doc_class::get_property( 'admin_tab_group' ); if ( 'html' === $format && $tab_group ) { $doc_title = ucwords( $tab_group ) . ' - ' . $doc_title; } // Replace element type with element title. foreach ( $elements as $element_type => $data ) { unset( $elements[ $element_type ] ); if ( in_array( $element_type, [ 'section', 'column' ], true ) ) { continue; } $widget_instance = Plugin::$instance->widgets_manager->get_widget_types( $element_type ); if ( 'html' === $format && $widget_instance ) { $widget_title = $widget_instance->get_title(); } else { $widget_title = $element_type; } $elements[ $widget_title ] = $data['count']; } // Sort elements by key. ksort( $elements ); $usage[ $doc_type ] = [ 'title' => $doc_title, 'elements' => $elements, 'count' => $doc_count, ]; // ' ? 1 : 0;' In sorters is compatibility for PHP8.0. // Sort usage by title. uasort( $usage, function( $a, $b ) { return ( $a['title'] > $b['title'] ) ? 1 : 0; } ); // If title includes '-' will have lower priority. uasort( $usage, function( $a ) { return strpos( $a['title'], '-' ) ? 1 : 0; } ); } return $usage; } /** * Before document Save. * * Called on elementor/document/before_save, remove document from global & set saving flag. * * @param Document $document * @param array $data new settings to save. */ public function before_document_save( $document, $data ) { $current_status = get_post_status( $document->get_post() ); $new_status = isset( $data['settings']['post_status'] ) ? $data['settings']['post_status'] : ''; if ( $current_status === $new_status ) { $this->remove_from_global( $document ); } $this->is_document_saving = true; } /** * After document save. * * Called on elementor/document/after_save, adds document to global & clear saving flag. * * @param Document $document */ public function after_document_save( $document ) { if ( Document::STATUS_PUBLISH === $document->get_post()->post_status || Document::STATUS_PRIVATE === $document->get_post()->post_status ) { $this->save_document_usage( $document ); } $this->is_document_saving = false; } /** * On status change. * * Called on transition_post_status. * * @param string $new_status * @param string $old_status * @param \WP_Post $post */ public function on_status_change( $new_status, $old_status, $post ) { if ( wp_is_post_autosave( $post ) ) { return; } // If it's from elementor editor, the usage should be saved via `before_document_save`/`after_document_save`. if ( $this->is_document_saving ) { return; } $document = Plugin::$instance->documents->get( $post->ID ); if ( ! $document ) { return; } $is_public_unpublish = 'publish' === $old_status && 'publish' !== $new_status; $is_private_unpublish = 'private' === $old_status && 'private' !== $new_status; if ( $is_public_unpublish || $is_private_unpublish ) { $this->remove_from_global( $document ); } $is_public_publish = 'publish' !== $old_status && 'publish' === $new_status; $is_private_publish = 'private' !== $old_status && 'private' === $new_status; if ( $is_public_publish || $is_private_publish ) { $this->save_document_usage( $document ); } } /** * On before delete post. * * Called on on_before_delete_post. * * @param int $post_id */ public function on_before_delete_post( $post_id ) { $document = Plugin::$instance->documents->get( $post_id ); if ( $document->get_id() !== $document->get_main_id() ) { return; } $this->remove_from_global( $document ); } /** * Add's tracking data. * * Called on elementor/tracker/send_tracking_data_params. * * @param array $params * * @return array */ public function add_tracking_data( $params ) { $params['usages']['elements'] = get_option( self::OPTION_NAME ); return $params; } /** * Recalculate usage. * * Recalculate usage for all elementor posts. * * @param int $limit * @param int $offset * * @return int */ public function recalc_usage( $limit = -1, $offset = 0 ) { // While requesting recalc_usage, data should be deleted. // if its in a batch the data should be deleted only on the first batch. if ( 0 === $offset ) { delete_option( self::OPTION_NAME ); } $post_types = get_post_types( array( 'public' => true ) ); $query = new \WP_Query( [ 'no_found_rows' => true, 'meta_key' => '_elementor_data', 'post_type' => $post_types, 'post_status' => [ 'publish', 'private' ], 'posts_per_page' => $limit, 'offset' => $offset, ] ); foreach ( $query->posts as $post ) { $document = Plugin::$instance->documents->get( $post->ID ); if ( ! $document ) { continue; } $this->after_document_save( $document ); } // Clear query memory before leave. wp_cache_flush(); return count( $query->posts ); } /** * Increase controls count. * * Increase controls count, for each element. * * @param array &$element_ref * @param string $tab * @param string $section * @param string $control * @param int $count */ private function increase_controls_count( &$element_ref, $tab, $section, $control, $count ) { if ( ! isset( $element_ref['controls'][ $tab ] ) ) { $element_ref['controls'][ $tab ] = []; } if ( ! isset( $element_ref['controls'][ $tab ][ $section ] ) ) { $element_ref['controls'][ $tab ][ $section ] = []; } if ( ! isset( $element_ref['controls'][ $tab ][ $section ][ $control ] ) ) { $element_ref['controls'][ $tab ][ $section ][ $control ] = 0; } $element_ref['controls'][ $tab ][ $section ][ $control ] += $count; } /** * Add Controls * * Add's controls to this element_ref, returns changed controls count. * * @param array $settings_controls * @param array $element_controls * @param array &$element_ref * * @return int ($changed_controls_count). */ private function add_controls( $settings_controls, $element_controls, &$element_ref ) { $changed_controls_count = 0; // Loop over all element settings. foreach ( $settings_controls as $control => $value ) { if ( empty( $element_controls[ $control ] ) ) { continue; } $control_config = $element_controls[ $control ]; if ( ! isset( $control_config['section'], $control_config['default'] ) ) { continue; } $tab = $control_config['tab']; $section = $control_config['section']; // If setting value is not the control default. if ( $value !== $control_config['default'] ) { $this->increase_controls_count( $element_ref, $tab, $section, $control, 1 ); $changed_controls_count++; } } return $changed_controls_count; } /** * Add general controls. * * Extract general controls to element ref, return clean `$settings_control`. * * @param array $settings_controls * @param array &$element_ref * * @return array ($settings_controls). */ private function add_general_controls( $settings_controls, &$element_ref ) { if ( ! empty( $settings_controls[ Manager::DYNAMIC_SETTING_KEY ] ) ) { $settings_controls = array_merge( $settings_controls, $settings_controls[ Manager::DYNAMIC_SETTING_KEY ] ); // Add dynamic count to controls under `general` tab. $this->increase_controls_count( $element_ref, self::GENERAL_TAB, Manager::DYNAMIC_SETTING_KEY, 'count', count( $settings_controls[ Manager::DYNAMIC_SETTING_KEY ] ) ); } return $settings_controls; } /** * Add to global. * * Add's usage to global (update database). * * @param string $doc_name * @param array $doc_usage */ private function add_to_global( $doc_name, $doc_usage ) { $global_usage = get_option( self::OPTION_NAME, [] ); foreach ( $doc_usage as $element_type => $element_data ) { if ( ! isset( $global_usage[ $doc_name ] ) ) { $global_usage[ $doc_name ] = []; } if ( ! isset( $global_usage[ $doc_name ][ $element_type ] ) ) { $global_usage[ $doc_name ][ $element_type ] = [ 'count' => 0, 'controls' => [], ]; } $global_element_ref = &$global_usage[ $doc_name ][ $element_type ]; $global_element_ref['count'] += $element_data['count']; if ( empty( $element_data['controls'] ) ) { continue; } foreach ( $element_data['controls'] as $tab => $sections ) { foreach ( $sections as $section => $controls ) { foreach ( $controls as $control => $count ) { $this->increase_controls_count( $global_element_ref, $tab, $section, $control, $count ); } } } } update_option( self::OPTION_NAME, $global_usage, false ); } /** * Remove from global. * * Remove's usage from global (update database). * * @param Document $document */ private function remove_from_global( $document ) { $prev_usage = $document->get_meta( self::META_KEY ); if ( empty( $prev_usage ) ) { return; } $doc_name = $document->get_name(); $global_usage = get_option( self::OPTION_NAME, [] ); foreach ( $prev_usage as $element_type => $doc_value ) { if ( isset( $global_usage[ $doc_name ][ $element_type ]['count'] ) ) { $global_usage[ $doc_name ][ $element_type ]['count'] -= $prev_usage[ $element_type ]['count']; if ( 0 === $global_usage[ $doc_name ][ $element_type ]['count'] ) { unset( $global_usage[ $doc_name ][ $element_type ] ); if ( 0 === count( $global_usage[ $doc_name ] ) ) { unset( $global_usage[ $doc_name ] ); } continue; } foreach ( $prev_usage[ $element_type ]['controls'] as $tab => $sections ) { foreach ( $sections as $section => $controls ) { foreach ( $controls as $control => $count ) { if ( isset( $global_usage[ $doc_name ][ $element_type ]['controls'][ $tab ][ $section ][ $control ] ) ) { $section_ref = &$global_usage[ $doc_name ][ $element_type ]['controls'][ $tab ][ $section ]; $section_ref[ $control ] -= $count; if ( 0 === $section_ref[ $control ] ) { unset( $section_ref[ $control ] ); } } } } } } } update_option( self::OPTION_NAME, $global_usage, false ); $document->delete_meta( self::META_KEY ); } /** * Get elements usage. * * Get's the current elements usage by passed elements array parameter. * * @param array $elements * * @return array */ private function get_elements_usage( $elements ) { $usage = []; Plugin::$instance->db->iterate_data( $elements, function ( $element ) use ( &$usage ) { if ( empty( $element['widgetType'] ) ) { $type = $element['elType']; $element_instance = Plugin::$instance->elements_manager->get_element_types( $type ); } else { $type = $element['widgetType']; $element_instance = Plugin::$instance->widgets_manager->get_widget_types( $type ); } if ( ! isset( $usage[ $type ] ) ) { $usage[ $type ] = [ 'count' => 0, 'control_percent' => 0, 'controls' => [], ]; } $usage[ $type ]['count']++; if ( ! $element_instance ) { return $element; } $element_controls = $element_instance->get_controls(); if ( isset( $element['settings'] ) ) { $settings_controls = $element['settings']; $element_ref = &$usage[ $type ]; // Add dynamic values. $settings_controls = $this->add_general_controls( $settings_controls, $element_ref ); $changed_controls_count = $this->add_controls( $settings_controls, $element_controls, $element_ref ); $percent = ! empty( $element_controls ) ? $changed_controls_count / ( count( $element_controls ) / 100 ) : 0; $usage[ $type ] ['control_percent'] = (int) round( $percent ); } return $element; } ); return $usage; } /** * Save document usage. * * Save requested document usage, and update global. * * @param Document $document */ private function save_document_usage( Document $document ) { if ( ! $document::get_property( 'is_editable' ) && ! $document->is_built_with_elementor() ) { return; } // Get data manually to avoid conflict with `\Elementor\Core\Base\Document::get_elements_data... convert_to_elementor`. $data = $document->get_json_meta( '_elementor_data' ); if ( ! empty( $data ) ) { try { $usage = $this->get_elements_usage( $document->get_elements_raw_data( $data ) ); $document->update_meta( self::META_KEY, $usage ); $this->add_to_global( $document->get_name(), $usage ); } catch ( \Exception $exception ) { Plugin::$instance->logger->get_logger()->error( $exception->getMessage(), [ 'document_id' => $document->get_id(), 'document_name' => $document->get_name(), ] ); return; }; } } public static function get_settings_usage() { $usage = []; $settings_tab = Plugin::$instance->settings->get_tabs(); $settings = array_merge( $settings_tab[ Settings::TAB_GENERAL ]['sections'], $settings_tab[ Settings::TAB_ADVANCED ]['sections'] ); foreach ( $settings as $setting_data ) { foreach ( $setting_data['fields'] as $field_name => $field_data ) { $is_hidden_field = ( empty( $field_data['field_args']['type'] ) || 'hidden' === $field_data['field_args']['type'] ); if ( $is_hidden_field ) { continue; } $setting_value = get_option( 'elementor_' . $field_name ); if ( empty( $setting_value ) ) { continue; } $is_default_value = ( ! empty( $field_data['field_args']['std'] ) && $setting_value === $field_data['field_args']['std'] ); if ( $is_default_value ) { continue; } $usage[ $field_name ] = $setting_value; } } $usage = apply_filters( 'elementor/system-info/usage/settings', $usage ); return $usage; } /** * Add system info report. */ public function add_system_info_report() { System_Info::add_report( 'usage', [ 'file_name' => __DIR__ . '/usage-reporter.php', 'class_name' => __NAMESPACE__ . '\Usage_Reporter', ] ); System_Info::add_report( 'settings', [ 'file_name' => __DIR__ . '/settings-reporter.php', 'class_name' => __NAMESPACE__ . '\Settings_Reporter', ] ); } /** * Usage module constructor. * * Initializing Elementor usage module. * * @access public */ public function __construct() { if ( ! Tracker::is_allow_track() ) { return; } add_action( 'transition_post_status', [ $this, 'on_status_change' ], 10, 3 ); add_action( 'before_delete_post', [ $this, 'on_before_delete_post' ] ); add_action( 'elementor/document/before_save', [ $this, 'before_document_save' ], 10, 2 ); add_action( 'elementor/document/after_save', [ $this, 'after_document_save' ] ); add_filter( 'elementor/tracker/send_tracking_data_params', [ $this, 'add_tracking_data' ] ); add_action( 'admin_init', [ $this, 'add_system_info_report' ], 50 ); } } usage/settings-reporter.php000064400000002360147207015700012065 0ustar00 '', ]; } public function get_settings() : array { $usage_settings_text = ''; $settings = Module::get_settings_usage(); foreach ( $settings as $setting_name => $setting_value ) { $setting_value_text = is_array( $setting_value ) ? implode( ', ', $setting_value ) : $setting_value; $usage_settings_text .= '' . $setting_name . '' . $setting_value_text . ''; } return [ 'value' => $usage_settings_text, ]; } public function get_raw_settings() : array { $usage_settings = PHP_EOL; $settings = Module::get_settings_usage(); foreach ( $settings as $setting_name => $setting_value ) { $setting_value_text = is_array( $setting_value ) ? implode( ', ', $setting_value ) : $setting_value; $usage_settings .= "\t" . $setting_name . ': ' . $setting_value_text . PHP_EOL; } return [ 'value' => $usage_settings, ]; } } usage/usage-reporter.php000064400000006011147207015700011326 0ustar00 '', ]; } public function print_html_label( $label ) { $title = $this->get_title(); if ( empty( $_GET[ self::RECALC_ACTION ] ) ) { // phpcs:ignore -- nonce validation is not required here. $nonce = wp_create_nonce( self::RECALC_ACTION ); $url = add_query_arg( [ self::RECALC_ACTION => 1, '_wpnonce' => $nonce, ] ); $title .= 'Recalculate'; } else { $title .= $this->get_remove_recalc_query_string_script(); } parent::print_html_label( $title ); } public function get_usage() { /** @var Module $module */ $module = Module::instance(); if ( ! empty( $_GET[ self::RECALC_ACTION ] ) ) { // phpcs:ignore $nonce = Utils::get_super_global_value( $_GET, '_wpnonce' ); if ( ! wp_verify_nonce( $nonce, self::RECALC_ACTION ) ) { wp_die( 'Invalid Nonce', 'Invalid Nonce', [ 'back_link' => true, ] ); } $module->recalc_usage(); } $usage = ''; foreach ( $module->get_formatted_usage() as $doc_type => $data ) { $usage .= '' . $data['title'] . ' ( ' . $data['count'] . ' )'; foreach ( $data['elements'] as $element => $count ) { $usage .= $element . ': ' . $count . PHP_EOL; } $usage .= ''; } return [ 'value' => $usage, ]; } public function get_raw_usage() { /** @var Module $module */ $module = Module::instance(); $usage = PHP_EOL; foreach ( $module->get_formatted_usage( 'raw' ) as $doc_type => $data ) { $usage .= "\t{$data['title']} : " . $data['count'] . PHP_EOL; foreach ( $data['elements'] as $element => $count ) { $usage .= "\t\t{$element} : {$count}" . PHP_EOL; } } return [ 'value' => $usage, ]; } /** * Removes the "elementor_usage_recalc" param from the query string to avoid recalc every refresh. * When using a redirect header in place of this approach it throws an error because some components have already output some content. * * @return string */ private function get_remove_recalc_query_string_script() { ob_start(); ?> get_post_id(); $document = Plugin::$instance->documents->get( $post_id ); if ( ! $document ) { return; } ?> .wfls-btn-default.wfls-dropdown-toggle{color:#00709e;background-color:#e6e6e6;border-color:#004561}.wfls-btn-default:active:hover,.wfls-btn-default:active:focus,.wfls-btn-default:active.focus,.wfls-btn-default.active:hover,.wfls-btn-default.active:focus,.wfls-btn-default.active.focus,.wfls-open>.wfls-btn-default.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-default.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-default.wfls-dropdown-toggle.focus{color:#00709e;background-color:#d4d4d4;border-color:#00161f}.wfls-btn-default:active,.wfls-btn-default.wfls-active,.wfls-open>.wfls-btn-default.wfls-dropdown-toggle{background-image:none}.wfls-btn-default.wfls-disabled,.wfls-btn-default[disabled],.wfls-btn-default[readonly],fieldset[disabled] .wfls-btn-default{color:#777;background-color:#fff;border-color:#e2e2e2;cursor:not-allowed;opacity:0.75}.wfls-btn-default.wfls-disabled:hover,.wfls-btn-default.wfls-disabled:focus,.wfls-btn-default.wfls-disabled.wfls-focus,.wfls-btn-default[disabled]:hover,.wfls-btn-default[disabled]:focus,.wfls-btn-default[disabled].wfls-focus,.wfls-btn-default[readonly]:hover,.wfls-btn-default[readonly]:focus,.wfls-btn-default[readonly].wfls-focus,fieldset[disabled] .wfls-btn-default:hover,fieldset[disabled] .wfls-btn-default:focus,fieldset[disabled] .wfls-btn-default.wfls-focus{background-color:#fff;border-color:#00709e}.wfls-btn-default .wfls-badge{color:#fff;background-color:#00709e}.wfls-btn-primary{color:#fff;background-color:#00709e;border-color:#005e85}.wfls-btn-primary:focus,.wfls-btn-primary.focus{color:#fff;background-color:#004c6b;border-color:#000405}.wfls-btn-primary:hover{color:#fff;background-color:#004c6b;border-color:#003347}.wfls-btn-primary:active,.wfls-btn-primary.active,.wfls-open>.wfls-btn-primary.wfls-dropdown-toggle{color:#fff;background-color:#004c6b;border-color:#003347}.wfls-btn-primary:active:hover,.wfls-btn-primary:active:focus,.wfls-btn-primary:active.focus,.wfls-btn-primary.active:hover,.wfls-btn-primary.active:focus,.wfls-btn-primary.active.focus,.wfls-open>.wfls-btn-primary.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-primary.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-primary.wfls-dropdown-toggle.focus{color:#fff;background-color:#003347;border-color:#000405}.wfls-btn-primary:active,.wfls-btn-primary.wfls-active,.wfls-open>.wfls-btn-primary.wfls-dropdown-toggle{background-image:none}.wfls-btn-primary.wfls-disabled,.wfls-btn-primary[disabled],.wfls-btn-primary[readonly],fieldset[disabled] .wfls-btn-primary{color:#fff;background-color:#59a2c0;border-color:#5996b0;cursor:not-allowed;opacity:0.75}.wfls-btn-primary.wfls-disabled:hover,.wfls-btn-primary.wfls-disabled:focus,.wfls-btn-primary.wfls-disabled.wfls-focus,.wfls-btn-primary[disabled]:hover,.wfls-btn-primary[disabled]:focus,.wfls-btn-primary[disabled].wfls-focus,.wfls-btn-primary[readonly]:hover,.wfls-btn-primary[readonly]:focus,.wfls-btn-primary[readonly].wfls-focus,fieldset[disabled] .wfls-btn-primary:hover,fieldset[disabled] .wfls-btn-primary:focus,fieldset[disabled] .wfls-btn-primary.wfls-focus{background-color:#00709e;border-color:#005e85}.wfls-btn-primary .wfls-badge{color:#00709e;background-color:#fff}.wfls-btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.wfls-btn-success:focus,.wfls-btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.wfls-btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.wfls-btn-success:active,.wfls-btn-success.active,.wfls-open>.wfls-btn-success.wfls-dropdown-toggle{color:#fff;background-color:#449d44;border-color:#398439}.wfls-btn-success:active:hover,.wfls-btn-success:active:focus,.wfls-btn-success:active.focus,.wfls-btn-success.active:hover,.wfls-btn-success.active:focus,.wfls-btn-success.active.focus,.wfls-open>.wfls-btn-success.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-success.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-success.wfls-dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#255625}.wfls-btn-success:active,.wfls-btn-success.wfls-active,.wfls-open>.wfls-btn-success.wfls-dropdown-toggle{background-image:none}.wfls-btn-success.wfls-disabled,.wfls-btn-success[disabled],.wfls-btn-success[readonly],fieldset[disabled] .wfls-btn-success{color:#fff;background-color:#95d195;border-color:#8bca8b;cursor:not-allowed;opacity:0.75}.wfls-btn-success.wfls-disabled:hover,.wfls-btn-success.wfls-disabled:focus,.wfls-btn-success.wfls-disabled.wfls-focus,.wfls-btn-success[disabled]:hover,.wfls-btn-success[disabled]:focus,.wfls-btn-success[disabled].wfls-focus,.wfls-btn-success[readonly]:hover,.wfls-btn-success[readonly]:focus,.wfls-btn-success[readonly].wfls-focus,fieldset[disabled] .wfls-btn-success:hover,fieldset[disabled] .wfls-btn-success:focus,fieldset[disabled] .wfls-btn-success.wfls-focus{background-color:#5cb85c;border-color:#4cae4c}.wfls-btn-success .wfls-badge{color:#5cb85c;background-color:#fff}.wfls-btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.wfls-btn-info:focus,.wfls-btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.wfls-btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.wfls-btn-info:active,.wfls-btn-info.active,.wfls-open>.wfls-btn-info.wfls-dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#269abc}.wfls-btn-info:active:hover,.wfls-btn-info:active:focus,.wfls-btn-info:active.focus,.wfls-btn-info.active:hover,.wfls-btn-info.active:focus,.wfls-btn-info.active.focus,.wfls-open>.wfls-btn-info.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-info.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-info.wfls-dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1b6d85}.wfls-btn-info:active,.wfls-btn-info.wfls-active,.wfls-open>.wfls-btn-info.wfls-dropdown-toggle{background-image:none}.wfls-btn-info.wfls-disabled,.wfls-btn-info[disabled],.wfls-btn-info[readonly],fieldset[disabled] .wfls-btn-info{color:#fff;background-color:#94d6ea;border-color:#87d1e7;cursor:not-allowed;opacity:0.75}.wfls-btn-info.wfls-disabled:hover,.wfls-btn-info.wfls-disabled:focus,.wfls-btn-info.wfls-disabled.wfls-focus,.wfls-btn-info[disabled]:hover,.wfls-btn-info[disabled]:focus,.wfls-btn-info[disabled].wfls-focus,.wfls-btn-info[readonly]:hover,.wfls-btn-info[readonly]:focus,.wfls-btn-info[readonly].wfls-focus,fieldset[disabled] .wfls-btn-info:hover,fieldset[disabled] .wfls-btn-info:focus,fieldset[disabled] .wfls-btn-info.wfls-focus{background-color:#5bc0de;border-color:#46b8da}.wfls-btn-info .wfls-badge{color:#5bc0de;background-color:#fff}.wfls-btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.wfls-btn-warning:focus,.wfls-btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.wfls-btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.wfls-btn-warning:active,.wfls-btn-warning.active,.wfls-open>.wfls-btn-warning.wfls-dropdown-toggle{color:#fff;background-color:#ec971f;border-color:#d58512}.wfls-btn-warning:active:hover,.wfls-btn-warning:active:focus,.wfls-btn-warning:active.focus,.wfls-btn-warning.active:hover,.wfls-btn-warning.active:focus,.wfls-btn-warning.active.focus,.wfls-open>.wfls-btn-warning.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-warning.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-warning.wfls-dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#985f0d}.wfls-btn-warning:active,.wfls-btn-warning.wfls-active,.wfls-open>.wfls-btn-warning.wfls-dropdown-toggle{background-image:none}.wfls-btn-warning.wfls-disabled,.wfls-btn-warning[disabled],.wfls-btn-warning[readonly],fieldset[disabled] .wfls-btn-warning{color:#fff;background-color:#f5ca8c;border-color:#f4c37c;cursor:not-allowed;opacity:0.75}.wfls-btn-warning.wfls-disabled:hover,.wfls-btn-warning.wfls-disabled:focus,.wfls-btn-warning.wfls-disabled.wfls-focus,.wfls-btn-warning[disabled]:hover,.wfls-btn-warning[disabled]:focus,.wfls-btn-warning[disabled].wfls-focus,.wfls-btn-warning[readonly]:hover,.wfls-btn-warning[readonly]:focus,.wfls-btn-warning[readonly].wfls-focus,fieldset[disabled] .wfls-btn-warning:hover,fieldset[disabled] .wfls-btn-warning:focus,fieldset[disabled] .wfls-btn-warning.wfls-focus{background-color:#f0ad4e;border-color:#eea236}.wfls-btn-warning .wfls-badge{color:#f0ad4e;background-color:#fff}.wfls-btn-danger{color:#fff;background-color:#930000;border-color:#7a0000}.wfls-btn-danger:focus,.wfls-btn-danger.focus{color:#fff;background-color:#600000;border-color:#000}.wfls-btn-danger:hover{color:#fff;background-color:#600000;border-color:#3c0000}.wfls-btn-danger:active,.wfls-btn-danger.active,.wfls-open>.wfls-btn-danger.wfls-dropdown-toggle{color:#fff;background-color:#600000;border-color:#3c0000}.wfls-btn-danger:active:hover,.wfls-btn-danger:active:focus,.wfls-btn-danger:active.focus,.wfls-btn-danger.active:hover,.wfls-btn-danger.active:focus,.wfls-btn-danger.active.focus,.wfls-open>.wfls-btn-danger.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-danger.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-danger.wfls-dropdown-toggle.focus{color:#fff;background-color:#3c0000;border-color:#000}.wfls-btn-danger:active,.wfls-btn-danger.wfls-active,.wfls-open>.wfls-btn-danger.wfls-dropdown-toggle{background-image:none}.wfls-btn-danger.wfls-disabled,.wfls-btn-danger[disabled],.wfls-btn-danger[readonly],fieldset[disabled] .wfls-btn-danger{color:#fff;background-color:#b95959;border-color:#a95959;cursor:not-allowed;opacity:0.75}.wfls-btn-danger.wfls-disabled:hover,.wfls-btn-danger.wfls-disabled:focus,.wfls-btn-danger.wfls-disabled.wfls-focus,.wfls-btn-danger[disabled]:hover,.wfls-btn-danger[disabled]:focus,.wfls-btn-danger[disabled].wfls-focus,.wfls-btn-danger[readonly]:hover,.wfls-btn-danger[readonly]:focus,.wfls-btn-danger[readonly].wfls-focus,fieldset[disabled] .wfls-btn-danger:hover,fieldset[disabled] .wfls-btn-danger:focus,fieldset[disabled] .wfls-btn-danger.wfls-focus{background-color:#930000;border-color:#7a0000}.wfls-btn-danger .wfls-badge{color:#930000;background-color:#fff}.wfls-btn-callout{font-weight:600;text-transform:uppercase}.wfls-btn-callout-subtle{font-weight:400;text-transform:uppercase}.wfls-btn-link{color:#00709e;font-weight:normal;border-radius:0}.wfls-btn-link,.wfls-btn-link:active,.wfls-btn-link.wfls-active,.wfls-btn-link[disabled],fieldset[disabled] .wfls-btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.wfls-btn-link,.wfls-btn-link:hover,.wfls-btn-link:focus,.wfls-btn-link:active{border-color:transparent}.wfls-btn-link:hover,.wfls-btn-link:focus{color:#003a52;text-decoration:underline;background-color:transparent}.wfls-btn-link[disabled]:hover,.wfls-btn-link[disabled]:focus,fieldset[disabled] .wfls-btn-link:hover,fieldset[disabled] .wfls-btn-link:focus{color:#777;text-decoration:none}.wfls-btn-lg,.wfls-btn-group-lg>.wfls-btn{padding:10px 16px;font-size:18px;line-height:1.33333;border-radius:6px}.wfls-btn-sm,.wfls-btn-group-sm>.wfls-btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.wfls-btn-xs,.wfls-btn-group-xs>.wfls-btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.wfls-btn-block{display:block;width:100%}.wfls-btn-block+.wfls-btn-block{margin-top:5px}input[type="submit"].wfls-btn-block,input[type="reset"].wfls-btn-block,input[type="button"].wfls-btn-block{width:100%}.wfls-btn-group,.wfls-btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.wfls-btn-group>.wfls-btn,.wfls-btn-group-vertical>.wfls-btn{position:relative;float:left}.wfls-btn-group>.wfls-btn:hover,.wfls-btn-group>.wfls-btn:focus,.wfls-btn-group>.wfls-btn:active,.wfls-btn-group>.wfls-btn.wfls-active,.wfls-btn-group-vertical>.wfls-btn:hover,.wfls-btn-group-vertical>.wfls-btn:focus,.wfls-btn-group-vertical>.wfls-btn:active,.wfls-btn-group-vertical>.wfls-btn.wfls-active{z-index:2}.wfls-btn-group .wfls-btn+.wfls-btn,.wfls-btn-group .wfls-btn+.wfls-btn-group,.wfls-btn-group .wfls-btn-group+.wfls-btn,.wfls-btn-group .wfls-btn-group+.wfls-btn-group{margin-left:-1px}.wfls-btn-toolbar{margin-left:-5px}.wfls-btn-toolbar:before,.wfls-btn-toolbar:after{content:" ";display:table}.wfls-btn-toolbar:after{clear:both}.wfls-btn-toolbar .wfls-btn,.wfls-btn-toolbar .wfls-btn-group,.wfls-btn-toolbar .wfls-input-group{float:left}.wfls-btn-toolbar>.wfls-btn,.wfls-btn-toolbar>.wfls-btn-group,.wfls-btn-toolbar>.wfls-input-group{margin-left:5px}.wfls-btn-group>.wfls-btn:not(:first-child):not(:last-child):not(.wfls-dropdown-toggle){border-radius:0}.wfls-btn-group>.wfls-btn:first-child{margin-left:0}.wfls-btn-group>.wfls-btn:first-child:not(:last-child):not(.wfls-dropdown-toggle){-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wfls-btn-group>.wfls-btn:last-child:not(:first-child),.wfls-btn-group>.wfls-dropdown-toggle:not(:first-child){-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0}.wfls-btn-group>.wfls-btn-group{float:left}.wfls-btn-group>.wfls-btn-group:not(:first-child):not(:last-child)>.wfls-btn{border-radius:0}.wfls-btn-group>.wfls-btn-group:first-child:not(:last-child)>.wfls-btn:last-child,.wfls-btn-group>.wfls-btn-group:first-child:not(:last-child)>.wfls-dropdown-toggle{-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wfls-btn-group>.wfls-btn-group:last-child:not(:first-child)>.wfls-btn:first-child{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0}.wfls-btn-group .wfls-dropdown-toggle:active,.wfls-btn-group.wfls-open .wfls-dropdown-toggle{outline:0}.wfls-btn-group>.wfls-btn+.wfls-dropdown-toggle{padding-left:8px;padding-right:8px}.wfls-btn-group>.wfls-btn-lg+.wfls-dropdown-toggle,.wfls-btn-group-lg.wfls-btn-group>.wfls-btn+.wfls-dropdown-toggle{padding-left:12px;padding-right:12px}.wfls-btn-group.open .wfls-dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.wfls-btn-group.open .wfls-dropdown-toggle.wfls-btn-link{-webkit-box-shadow:none;box-shadow:none}.wfls-btn .wfls-caret{margin-left:0}.wfls-btn-lg .wfls-caret,.wfls-btn-group-lg>.wfls-btn .wfls-caret{border-width:5px 5px 0;border-bottom-width:0}.wfls-dropup .wfls-btn-lg .wfls-caret,.wfls-dropup .wfls-btn-group-lg>.wfls-btn .wfls-caret{border-width:0 5px 5px}.wfls-btn-group-vertical>.wfls-btn,.wfls-btn-group-vertical>.wfls-btn-group,.wfls-btn-group-vertical>.wfls-btn-group>.wfls-btn{display:block;float:none;width:100%;max-width:100%}.wfls-btn-group-vertical>.wfls-btn-group:before,.wfls-btn-group-vertical>.wfls-btn-group:after{content:" ";display:table}.wfls-btn-group-vertical>.wfls-btn-group:after{clear:both}.wfls-btn-group-vertical>.wfls-btn-group>.wfls-btn{float:none}.wfls-btn-group-vertical>.wfls-btn+.wfls-btn,.wfls-btn-group-vertical>.wfls-btn+.wfls-btn-group,.wfls-btn-group-vertical>.wfls-btn-group+.wfls-btn,.wfls-btn-group-vertical>.wfls-btn-group+.wfls-btn-group{margin-top:-1px;margin-left:0}.wfls-btn-group-vertical>.wfls-btn:not(:first-child):not(:last-child){border-radius:0}.wfls-btn-group-vertical>.wfls-btn:first-child:not(:last-child){-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wfls-btn-group-vertical>.wfls-btn:last-child:not(:first-child){-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.wfls-btn-group-vertical>.wfls-btn-group:not(:first-child):not(:last-child)>.wfls-btn{border-radius:0}.wfls-btn-group-vertical>.wfls-btn-group:first-child:not(:last-child)>.wfls-btn:last-child,.wfls-btn-group-vertical>.wfls-btn-group:first-child:not(:last-child)>.wfls-dropdown-toggle{-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wfls-btn-group-vertical>.wfls-btn-group:last-child:not(:first-child)>.wfls-btn:first-child{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0}.wfls-btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.wfls-btn-group-justified>.wfls-btn,.wfls-btn-group-justified>.wfls-btn-group{float:none;display:table-cell;width:1%}.wfls-btn-group-justified>.wfls-btn-group .wfls-btn{width:100%}.wfls-btn-group-justified>.wfls-btn-group .wfls-dropdown-menu{left:auto}[data-toggle="buttons"]>.wfls-btn input[type="radio"],[data-toggle="buttons"]>.wfls-btn input[type="checkbox"],[data-toggle="buttons"]>.wfls-btn-group>.wfls-btn input[type="radio"],[data-toggle="buttons"]>.wfls-btn-group>.wfls-btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.wfls-pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.wfls-pagination>li{display:inline}.wfls-pagination>li>a,.wfls-pagination>li>span{position:relative;float:left;padding:.5rem 1.25rem;line-height:1.42857;text-decoration:none;color:#00709e;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.wfls-pagination>li:first-child>a,.wfls-pagination>li:first-child>span{margin-left:0;-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px}.wfls-pagination>li:last-child>a,.wfls-pagination>li:last-child>span{-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.wfls-pagination>li>a:hover,.wfls-pagination>li>a:focus,.wfls-pagination>li>span:hover,.wfls-pagination>li>span:focus{z-index:2;color:#003a52;background-color:#e2e2e2;border-color:#ddd}.wfls-pagination>.wfls-active>a,.wfls-pagination>.wfls-active>a:hover,.wfls-pagination>.wfls-active>a:focus,.wfls-pagination>.wfls-active>span,.wfls-pagination>.wfls-active>span:hover,.wfls-pagination>.wfls-active>span:focus{z-index:3;color:#fff;background-color:#00709e;border-color:#00709e;cursor:default}.wfls-pagination>.wfls-disabled>span,.wfls-pagination>.wfls-disabled>span:hover,.wfls-pagination>.wfls-disabled>span:focus,.wfls-pagination>.wfls-disabled>a,.wfls-pagination>.wfls-disabled>a:hover,.wfls-pagination>.wfls-disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.wfls-pagination-lg>li>a,.wfls-pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.33333}.wfls-pagination-lg>li:first-child>a,.wfls-pagination-lg>li:first-child>span{-moz-border-radius-topleft:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px}.wfls-pagination-lg>li:last-child>a,.wfls-pagination-lg>li:last-child>span{-moz-border-radius-topright:6px;-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-moz-border-radius-bottomright:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px}.wfls-pagination-sm>li>a,.wfls-pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.wfls-pagination-sm>li:first-child>a,.wfls-pagination-sm>li:first-child>span{-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px}.wfls-pagination-sm>li:last-child>a,.wfls-pagination-sm>li:last-child>span{-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-moz-border-radius-bottomright:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px}#wfls-notices{margin-top:15px}#wfls-notices .wfls-admin-notice{margin-left:0px;margin-right:0px}.wfls-success-text,.wfls-notice-text{display:inline-block;vertical-align:middle;line-height:1.3;font-size:16px;font-weight:bold;font-style:italic}.wfls-notice{margin:12px 0;padding:8px;background-color:#ffffe0;border:1px solid #ffd975;border-width:1px 1px 1px 10px}.wfls-notice-text{color:#6d798c}.wfls-success{margin:12px 0;padding:8px;background-color:#ffffff;border:1px solid #16bc9b;border-width:1px 1px 1px 10px}.wfls-success-text{color:#11967a}.wfls-premium-callout{border:1px solid #dfdfdf;background-color:#ffffff;padding:16px;margin:20px 0 0;text-align:center}.wfls-premium-callout ul{margin:8px 0;padding:0 0 0 15px}.wfls-premium-callout ul li{list-style-type:disc;margin:0;padding:0}.wfls-premium-callout .center{text-align:center;margin:0}.wfls-premium-callout .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709e}small.wfls-sub-status{display:block}.wfls-grace-period-container{display:flex;align-items:center;justify-content:left;margin-bottom:1rem;margin-top:1rem}.wfls-grace-period-container .wfls-grace-period-input-container{margin-right:1.5rem;text-align:center}.wfls-grace-period-container .wfls-grace-period-input-container label{display:block;font-weight:bold}.wfls-grace-period-container .wfls-grace-period-input-container input{width:3em;text-align:center}#wfls-reset-grace-period-failed{text-align:center}#toplevel_page_WFLS .wp-menu-image img{max-width:16px;max-height:16px} login-security/css/login.1731600600.css000064400000005141147207020750013206 0ustar00#wfls-prompt-overlay{position:absolute;top:0px;right:0px;bottom:0px;left:0px;background-color:#fff;padding:26px 24px 46px;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:stretch;justify-content:stretch}.woocommerce #wfls-prompt-overlay{padding:0;display:block}#wfls-prompt-wrapper{-webkit-flex-grow:1;flex-grow:1;width:100%}.login form .wfls-textarea{font-size:1rem;width:100%;padding:3px;margin:2px 6px 16px 0;background:#fbfbfb;height:150px}.login form .wfls-remember-device-wrapper{font-weight:400;float:left;margin-bottom:0}.login form .wfls-remember-device-wrapper label{font-size:12px;line-height:19px}.wfls-2fa-code-help{text-decoration:none}.wfls-registration-captcha-contact{text-decoration:underline}.Zebra_Tooltip{background:0 0;position:absolute;z-index:8000}.Zebra_Tooltip .Zebra_Tooltip_Message{background:#000;border-radius:5px;box-shadow:0 0 6px rgba(0,0,0,0.6);color:#fff;font-size:12px;font-family:Tahoma,Arial,Helvetica,sans-serif;line-height:1.4;*margin-right:0;max-width:250px;padding:10px;position:relative;_width:expression(document.body.clientWidth > 250px ? '250px': 'auto');border:0 solid #000}.Zebra_Tooltip .Zebra_Tooltip_Message.Zebra_Tooltip_Has_Close{padding-right:23px}.Zebra_Tooltip .Zebra_Tooltip_Arrow{position:absolute;width:20px;height:10px;overflow:hidden}.Zebra_Tooltip .Zebra_Tooltip_Arrow.Zebra_Tooltip_Arrow_Bottom{bottom:0}.Zebra_Tooltip .Zebra_Tooltip_Arrow.Zebra_Tooltip_Arrow_Bottom div{top:0;border-color:#000 transparent transparent;_border-bottom-color:pink}.Zebra_Tooltip .Zebra_Tooltip_Arrow.Zebra_Tooltip_Arrow_Bottom div.Zebra_Tooltip_Arrow_Border{border-color:#000 transparent transparent}.Zebra_Tooltip .Zebra_Tooltip_Arrow.Zebra_Tooltip_Arrow_Top{top:0}.Zebra_Tooltip .Zebra_Tooltip_Arrow.Zebra_Tooltip_Arrow_Top div{bottom:0;border-color:transparent transparent #000;_border-top-color:pink}.Zebra_Tooltip .Zebra_Tooltip_Arrow.Zebra_Tooltip_Arrow_Top div.Zebra_Tooltip_Arrow_Border{border-color:transparent transparent #000}.Zebra_Tooltip .Zebra_Tooltip_Arrow div{position:absolute;border-style:solid;border-width:10px;width:0;height:0;left:0;_border-left-color:pink;_border-right-color:pink;_filter:chroma(color=pink)}.Zebra_Tooltip .Zebra_Tooltip_Arrow div.Zebra_Tooltip_Arrow_Border{border-width:10px;box-shadow:0 0 6px rgba(0,0,0,0.6);left:0}.Zebra_Tooltip .Zebra_Tooltip_Close{color:#fff;font-family:Arial,sans-serif;font-size:18px;line-height:1;padding:0 4px;position:absolute;right:2px;text-decoration:none;top:2px}.Zebra_Tooltip .Zebra_Tooltip_Close:hover{color:#000;background:#c2d076;border-radius:5px}.grecaptcha-badge{z-index:65535} login-security/css/font-awesome.1731600600.css000064400000101207147207020750014502 0ustar00/*! * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */@font-face{font-family:'FontAwesome';src:url("../../../fonts/fontawesome-webfont.woff?v=4.7.0") format("woff");font-weight:normal;font-style:normal}.wfls-fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.wfls-fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.wfls-fa-2x{font-size:2em}.wfls-fa-3x{font-size:3em}.wfls-fa-4x{font-size:4em}.wfls-fa-5x{font-size:5em}.wfls-fa-fw{width:1.28571em;text-align:center}.wfls-fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.wfls-fa-ul>li{position:relative}.wfls-fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.wfls-fa-li.wfls-fa-lg{left:-1.85714em}.wfls-fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.wfls-fa-pull-left{float:left}.wfls-fa-pull-right{float:right}.wfls-fa.wfls-fa-pull-left{margin-right:.3em}.wfls-fa.wfls-fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.wfls-fa.pull-left{margin-right:.3em}.wfls-fa.pull-right{margin-left:.3em}.wfls-fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.wfls-fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.wfls-fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.wfls-fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.wfls-fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.wfls-fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.wfls-fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .wfls-fa-rotate-90,:root .wfls-fa-rotate-180,:root .wfls-fa-rotate-270,:root .wfls-fa-flip-horizontal,:root .wfls-fa-flip-vertical{filter:none}.wfls-fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.wfls-fa-stack-1x,.wfls-fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.wfls-fa-stack-1x{line-height:inherit}.wfls-fa-stack-2x{font-size:2em}.wfls-fa-inverse{color:#fff}.wfls-fa-glass:before{content:""}.wfls-fa-music:before{content:""}.wfls-fa-search:before{content:""}.wfls-fa-envelope-o:before{content:""}.wfls-fa-heart:before{content:""}.wfls-fa-star:before{content:""}.wfls-fa-star-o:before{content:""}.wfls-fa-user:before{content:""}.wfls-fa-film:before{content:""}.wfls-fa-th-large:before{content:""}.wfls-fa-th:before{content:""}.wfls-fa-th-list:before{content:""}.wfls-fa-check:before{content:""}.wfls-fa-remove:before,.wfls-fa-close:before,.wfls-fa-times:before{content:""}.wfls-fa-search-plus:before{content:""}.wfls-fa-search-minus:before{content:""}.wfls-fa-power-off:before{content:""}.wfls-fa-signal:before{content:""}.wfls-fa-gear:before,.wfls-fa-cog:before{content:""}.wfls-fa-trash-o:before{content:""}.wfls-fa-home:before{content:""}.wfls-fa-file-o:before{content:""}.wfls-fa-clock-o:before{content:""}.wfls-fa-road:before{content:""}.wfls-fa-download:before{content:""}.wfls-fa-arrow-circle-o-down:before{content:""}.wfls-fa-arrow-circle-o-up:before{content:""}.wfls-fa-inbox:before{content:""}.wfls-fa-play-circle-o:before{content:""}.wfls-fa-rotate-right:before,.wfls-fa-repeat:before{content:""}.wfls-fa-refresh:before{content:""}.wfls-fa-list-alt:before{content:""}.wfls-fa-lock:before{content:""}.wfls-fa-flag:before{content:""}.wfls-fa-headphones:before{content:""}.wfls-fa-volume-off:before{content:""}.wfls-fa-volume-down:before{content:""}.wfls-fa-volume-up:before{content:""}.wfls-fa-qrcode:before{content:""}.wfls-fa-barcode:before{content:""}.wfls-fa-tag:before{content:""}.wfls-fa-tags:before{content:""}.wfls-fa-book:before{content:""}.wfls-fa-bookmark:before{content:""}.wfls-fa-print:before{content:""}.wfls-fa-camera:before{content:""}.wfls-fa-font:before{content:""}.wfls-fa-bold:before{content:""}.wfls-fa-italic:before{content:""}.wfls-fa-text-height:before{content:""}.wfls-fa-text-width:before{content:""}.wfls-fa-align-left:before{content:""}.wfls-fa-align-center:before{content:""}.wfls-fa-align-right:before{content:""}.wfls-fa-align-justify:before{content:""}.wfls-fa-list:before{content:""}.wfls-fa-dedent:before,.wfls-fa-outdent:before{content:""}.wfls-fa-indent:before{content:""}.wfls-fa-video-camera:before{content:""}.wfls-fa-photo:before,.wfls-fa-image:before,.wfls-fa-picture-o:before{content:""}.wfls-fa-pencil:before{content:""}.wfls-fa-map-marker:before{content:""}.wfls-fa-adjust:before{content:""}.wfls-fa-tint:before{content:""}.wfls-fa-edit:before,.wfls-fa-pencil-square-o:before{content:""}.wfls-fa-share-square-o:before{content:""}.wfls-fa-check-square-o:before{content:""}.wfls-fa-arrows:before{content:""}.wfls-fa-step-backward:before{content:""}.wfls-fa-fast-backward:before{content:""}.wfls-fa-backward:before{content:""}.wfls-fa-play:before{content:""}.wfls-fa-pause:before{content:""}.wfls-fa-stop:before{content:""}.wfls-fa-forward:before{content:""}.wfls-fa-fast-forward:before{content:""}.wfls-fa-step-forward:before{content:""}.wfls-fa-eject:before{content:""}.wfls-fa-chevron-left:before{content:""}.wfls-fa-chevron-right:before{content:""}.wfls-fa-plus-circle:before{content:""}.wfls-fa-minus-circle:before{content:""}.wfls-fa-times-circle:before{content:""}.wfls-fa-check-circle:before{content:""}.wfls-fa-question-circle:before{content:""}.wfls-fa-info-circle:before{content:""}.wfls-fa-crosshairs:before{content:""}.wfls-fa-times-circle-o:before{content:""}.wfls-fa-check-circle-o:before{content:""}.wfls-fa-ban:before{content:""}.wfls-fa-arrow-left:before{content:""}.wfls-fa-arrow-right:before{content:""}.wfls-fa-arrow-up:before{content:""}.wfls-fa-arrow-down:before{content:""}.wfls-fa-mail-forward:before,.wfls-fa-share:before{content:""}.wfls-fa-expand:before{content:""}.wfls-fa-compress:before{content:""}.wfls-fa-plus:before{content:""}.wfls-fa-minus:before{content:""}.wfls-fa-asterisk:before{content:""}.wfls-fa-exclamation-circle:before{content:""}.wfls-fa-gift:before{content:""}.wfls-fa-leaf:before{content:""}.wfls-fa-fire:before{content:""}.wfls-fa-eye:before{content:""}.wfls-fa-eye-slash:before{content:""}.wfls-fa-warning:before,.wfls-fa-exclamation-triangle:before{content:""}.wfls-fa-plane:before{content:""}.wfls-fa-calendar:before{content:""}.wfls-fa-random:before{content:""}.wfls-fa-comment:before{content:""}.wfls-fa-magnet:before{content:""}.wfls-fa-chevron-up:before{content:""}.wfls-fa-chevron-down:before{content:""}.wfls-fa-retweet:before{content:""}.wfls-fa-shopping-cart:before{content:""}.wfls-fa-folder:before{content:""}.wfls-fa-folder-open:before{content:""}.wfls-fa-arrows-v:before{content:""}.wfls-fa-arrows-h:before{content:""}.wfls-fa-bar-chart-o:before,.wfls-fa-bar-chart:before{content:""}.wfls-fa-twitter-square:before{content:""}.wfls-fa-facebook-square:before{content:""}.wfls-fa-camera-retro:before{content:""}.wfls-fa-key:before{content:""}.wfls-fa-gears:before,.wfls-fa-cogs:before{content:""}.wfls-fa-comments:before{content:""}.wfls-fa-thumbs-o-up:before{content:""}.wfls-fa-thumbs-o-down:before{content:""}.wfls-fa-star-half:before{content:""}.wfls-fa-heart-o:before{content:""}.wfls-fa-sign-out:before{content:""}.wfls-fa-linkedin-square:before{content:""}.wfls-fa-thumb-tack:before{content:""}.wfls-fa-external-link:before{content:""}.wfls-fa-sign-in:before{content:""}.wfls-fa-trophy:before{content:""}.wfls-fa-github-square:before{content:""}.wfls-fa-upload:before{content:""}.wfls-fa-lemon-o:before{content:""}.wfls-fa-phone:before{content:""}.wfls-fa-square-o:before{content:""}.wfls-fa-bookmark-o:before{content:""}.wfls-fa-phone-square:before{content:""}.wfls-fa-twitter:before{content:""}.wfls-fa-facebook-f:before,.wfls-fa-facebook:before{content:""}.wfls-fa-github:before{content:""}.wfls-fa-unlock:before{content:""}.wfls-fa-credit-card:before{content:""}.wfls-fa-feed:before,.wfls-fa-rss:before{content:""}.wfls-fa-hdd-o:before{content:""}.wfls-fa-bullhorn:before{content:""}.wfls-fa-bell:before{content:""}.wfls-fa-certificate:before{content:""}.wfls-fa-hand-o-right:before{content:""}.wfls-fa-hand-o-left:before{content:""}.wfls-fa-hand-o-up:before{content:""}.wfls-fa-hand-o-down:before{content:""}.wfls-fa-arrow-circle-left:before{content:""}.wfls-fa-arrow-circle-right:before{content:""}.wfls-fa-arrow-circle-up:before{content:""}.wfls-fa-arrow-circle-down:before{content:""}.wfls-fa-globe:before{content:""}.wfls-fa-wrench:before{content:""}.wfls-fa-tasks:before{content:""}.wfls-fa-filter:before{content:""}.wfls-fa-briefcase:before{content:""}.wfls-fa-arrows-alt:before{content:""}.wfls-fa-group:before,.wfls-fa-users:before{content:""}.wfls-fa-chain:before,.wfls-fa-link:before{content:""}.wfls-fa-cloud:before{content:""}.wfls-fa-flask:before{content:""}.wfls-fa-cut:before,.wfls-fa-scissors:before{content:""}.wfls-fa-copy:before,.wfls-fa-files-o:before{content:""}.wfls-fa-paperclip:before{content:""}.wfls-fa-save:before,.wfls-fa-floppy-o:before{content:""}.wfls-fa-square:before{content:""}.wfls-fa-navicon:before,.wfls-fa-reorder:before,.wfls-fa-bars:before{content:""}.wfls-fa-list-ul:before{content:""}.wfls-fa-list-ol:before{content:""}.wfls-fa-strikethrough:before{content:""}.wfls-fa-underline:before{content:""}.wfls-fa-table:before{content:""}.wfls-fa-magic:before{content:""}.wfls-fa-truck:before{content:""}.wfls-fa-pinterest:before{content:""}.wfls-fa-pinterest-square:before{content:""}.wfls-fa-google-plus-square:before{content:""}.wfls-fa-google-plus:before{content:""}.wfls-fa-money:before{content:""}.wfls-fa-caret-down:before{content:""}.wfls-fa-caret-up:before{content:""}.wfls-fa-caret-left:before{content:""}.wfls-fa-caret-right:before{content:""}.wfls-fa-columns:before{content:""}.wfls-fa-unsorted:before,.wfls-fa-sort:before{content:""}.wfls-fa-sort-down:before,.wfls-fa-sort-desc:before{content:""}.wfls-fa-sort-up:before,.wfls-fa-sort-asc:before{content:""}.wfls-fa-envelope:before{content:""}.wfls-fa-linkedin:before{content:""}.wfls-fa-rotate-left:before,.wfls-fa-undo:before{content:""}.wfls-fa-legal:before,.wfls-fa-gavel:before{content:""}.wfls-fa-dashboard:before,.wfls-fa-tachometer:before{content:""}.wfls-fa-comment-o:before{content:""}.wfls-fa-comments-o:before{content:""}.wfls-fa-flash:before,.wfls-fa-bolt:before{content:""}.wfls-fa-sitemap:before{content:""}.wfls-fa-umbrella:before{content:""}.wfls-fa-paste:before,.wfls-fa-clipboard:before{content:""}.wfls-fa-lightbulb-o:before{content:""}.wfls-fa-exchange:before{content:""}.wfls-fa-cloud-download:before{content:""}.wfls-fa-cloud-upload:before{content:""}.wfls-fa-user-md:before{content:""}.wfls-fa-stethoscope:before{content:""}.wfls-fa-suitcase:before{content:""}.wfls-fa-bell-o:before{content:""}.wfls-fa-coffee:before{content:""}.wfls-fa-cutlery:before{content:""}.wfls-fa-file-text-o:before{content:""}.wfls-fa-building-o:before{content:""}.wfls-fa-hospital-o:before{content:""}.wfls-fa-ambulance:before{content:""}.wfls-fa-medkit:before{content:""}.wfls-fa-fighter-jet:before{content:""}.wfls-fa-beer:before{content:""}.wfls-fa-h-square:before{content:""}.wfls-fa-plus-square:before{content:""}.wfls-fa-angle-double-left:before{content:""}.wfls-fa-angle-double-right:before{content:""}.wfls-fa-angle-double-up:before{content:""}.wfls-fa-angle-double-down:before{content:""}.wfls-fa-angle-left:before{content:""}.wfls-fa-angle-right:before{content:""}.wfls-fa-angle-up:before{content:""}.wfls-fa-angle-down:before{content:""}.wfls-fa-desktop:before{content:""}.wfls-fa-laptop:before{content:""}.wfls-fa-tablet:before{content:""}.wfls-fa-mobile-phone:before,.wfls-fa-mobile:before{content:""}.wfls-fa-circle-o:before{content:""}.wfls-fa-quote-left:before{content:""}.wfls-fa-quote-right:before{content:""}.wfls-fa-spinner:before{content:""}.wfls-fa-circle:before{content:""}.wfls-fa-mail-reply:before,.wfls-fa-reply:before{content:""}.wfls-fa-github-alt:before{content:""}.wfls-fa-folder-o:before{content:""}.wfls-fa-folder-open-o:before{content:""}.wfls-fa-smile-o:before{content:""}.wfls-fa-frown-o:before{content:""}.wfls-fa-meh-o:before{content:""}.wfls-fa-gamepad:before{content:""}.wfls-fa-keyboard-o:before{content:""}.wfls-fa-flag-o:before{content:""}.wfls-fa-flag-checkered:before{content:""}.wfls-fa-terminal:before{content:""}.wfls-fa-code:before{content:""}.wfls-fa-mail-reply-all:before,.wfls-fa-reply-all:before{content:""}.wfls-fa-star-half-empty:before,.wfls-fa-star-half-full:before,.wfls-fa-star-half-o:before{content:""}.wfls-fa-location-arrow:before{content:""}.wfls-fa-crop:before{content:""}.wfls-fa-code-fork:before{content:""}.wfls-fa-unlink:before,.wfls-fa-chain-broken:before{content:""}.wfls-fa-question:before{content:""}.wfls-fa-info:before{content:""}.wfls-fa-exclamation:before{content:""}.wfls-fa-superscript:before{content:""}.wfls-fa-subscript:before{content:""}.wfls-fa-eraser:before{content:""}.wfls-fa-puzzle-piece:before{content:""}.wfls-fa-microphone:before{content:""}.wfls-fa-microphone-slash:before{content:""}.wfls-fa-shield:before{content:""}.wfls-fa-calendar-o:before{content:""}.wfls-fa-fire-extinguisher:before{content:""}.wfls-fa-rocket:before{content:""}.wfls-fa-maxcdn:before{content:""}.wfls-fa-chevron-circle-left:before{content:""}.wfls-fa-chevron-circle-right:before{content:""}.wfls-fa-chevron-circle-up:before{content:""}.wfls-fa-chevron-circle-down:before{content:""}.wfls-fa-html5:before{content:""}.wfls-fa-css3:before{content:""}.wfls-fa-anchor:before{content:""}.wfls-fa-unlock-alt:before{content:""}.wfls-fa-bullseye:before{content:""}.wfls-fa-ellipsis-h:before{content:""}.wfls-fa-ellipsis-v:before{content:""}.wfls-fa-rss-square:before{content:""}.wfls-fa-play-circle:before{content:""}.wfls-fa-ticket:before{content:""}.wfls-fa-minus-square:before{content:""}.wfls-fa-minus-square-o:before{content:""}.wfls-fa-level-up:before{content:""}.wfls-fa-level-down:before{content:""}.wfls-fa-check-square:before{content:""}.wfls-fa-pencil-square:before{content:""}.wfls-fa-external-link-square:before{content:""}.wfls-fa-share-square:before{content:""}.wfls-fa-compass:before{content:""}.wfls-fa-toggle-down:before,.wfls-fa-caret-square-o-down:before{content:""}.wfls-fa-toggle-up:before,.wfls-fa-caret-square-o-up:before{content:""}.wfls-fa-toggle-right:before,.wfls-fa-caret-square-o-right:before{content:""}.wfls-fa-euro:before,.wfls-fa-eur:before{content:""}.wfls-fa-gbp:before{content:""}.wfls-fa-dollar:before,.wfls-fa-usd:before{content:""}.wfls-fa-rupee:before,.wfls-fa-inr:before{content:""}.wfls-fa-cny:before,.wfls-fa-rmb:before,.wfls-fa-yen:before,.wfls-fa-jpy:before{content:""}.wfls-fa-ruble:before,.wfls-fa-rouble:before,.wfls-fa-rub:before{content:""}.wfls-fa-won:before,.wfls-fa-krw:before{content:""}.wfls-fa-bitcoin:before,.wfls-fa-btc:before{content:""}.wfls-fa-file:before{content:""}.wfls-fa-file-text:before{content:""}.wfls-fa-sort-alpha-asc:before{content:""}.wfls-fa-sort-alpha-desc:before{content:""}.wfls-fa-sort-amount-asc:before{content:""}.wfls-fa-sort-amount-desc:before{content:""}.wfls-fa-sort-numeric-asc:before{content:""}.wfls-fa-sort-numeric-desc:before{content:""}.wfls-fa-thumbs-up:before{content:""}.wfls-fa-thumbs-down:before{content:""}.wfls-fa-youtube-square:before{content:""}.wfls-fa-youtube:before{content:""}.wfls-fa-xing:before{content:""}.wfls-fa-xing-square:before{content:""}.wfls-fa-youtube-play:before{content:""}.wfls-fa-dropbox:before{content:""}.wfls-fa-stack-overflow:before{content:""}.wfls-fa-instagram:before{content:""}.wfls-fa-flickr:before{content:""}.wfls-fa-adn:before{content:""}.wfls-fa-bitbucket:before{content:""}.wfls-fa-bitbucket-square:before{content:""}.wfls-fa-tumblr:before{content:""}.wfls-fa-tumblr-square:before{content:""}.wfls-fa-long-arrow-down:before{content:""}.wfls-fa-long-arrow-up:before{content:""}.wfls-fa-long-arrow-left:before{content:""}.wfls-fa-long-arrow-right:before{content:""}.wfls-fa-apple:before{content:""}.wfls-fa-windows:before{content:""}.wfls-fa-android:before{content:""}.wfls-fa-linux:before{content:""}.wfls-fa-dribbble:before{content:""}.wfls-fa-skype:before{content:""}.wfls-fa-foursquare:before{content:""}.wfls-fa-trello:before{content:""}.wfls-fa-female:before{content:""}.wfls-fa-male:before{content:""}.wfls-fa-gittip:before,.wfls-fa-gratipay:before{content:""}.wfls-fa-sun-o:before{content:""}.wfls-fa-moon-o:before{content:""}.wfls-fa-archive:before{content:""}.wfls-fa-bug:before{content:""}.wfls-fa-vk:before{content:""}.wfls-fa-weibo:before{content:""}.wfls-fa-renren:before{content:""}.wfls-fa-pagelines:before{content:""}.wfls-fa-stack-exchange:before{content:""}.wfls-fa-arrow-circle-o-right:before{content:""}.wfls-fa-arrow-circle-o-left:before{content:""}.wfls-fa-toggle-left:before,.wfls-fa-caret-square-o-left:before{content:""}.wfls-fa-dot-circle-o:before{content:""}.wfls-fa-wheelchair:before{content:""}.wfls-fa-vimeo-square:before{content:""}.wfls-fa-turkish-lira:before,.wfls-fa-try:before{content:""}.wfls-fa-plus-square-o:before{content:""}.wfls-fa-space-shuttle:before{content:""}.wfls-fa-slack:before{content:""}.wfls-fa-envelope-square:before{content:""}.wfls-fa-wordpress:before{content:""}.wfls-fa-openid:before{content:""}.wfls-fa-institution:before,.wfls-fa-bank:before,.wfls-fa-university:before{content:""}.wfls-fa-mortar-board:before,.wfls-fa-graduation-cap:before{content:""}.wfls-fa-yahoo:before{content:""}.wfls-fa-google:before{content:""}.wfls-fa-reddit:before{content:""}.wfls-fa-reddit-square:before{content:""}.wfls-fa-stumbleupon-circle:before{content:""}.wfls-fa-stumbleupon:before{content:""}.wfls-fa-delicious:before{content:""}.wfls-fa-digg:before{content:""}.wfls-fa-pied-piper-pp:before{content:""}.wfls-fa-pied-piper-alt:before{content:""}.wfls-fa-drupal:before{content:""}.wfls-fa-joomla:before{content:""}.wfls-fa-language:before{content:""}.wfls-fa-fax:before{content:""}.wfls-fa-building:before{content:""}.wfls-fa-child:before{content:""}.wfls-fa-paw:before{content:""}.wfls-fa-spoon:before{content:""}.wfls-fa-cube:before{content:""}.wfls-fa-cubes:before{content:""}.wfls-fa-behance:before{content:""}.wfls-fa-behance-square:before{content:""}.wfls-fa-steam:before{content:""}.wfls-fa-steam-square:before{content:""}.wfls-fa-recycle:before{content:""}.wfls-fa-automobile:before,.wfls-fa-car:before{content:""}.wfls-fa-cab:before,.wfls-fa-taxi:before{content:""}.wfls-fa-tree:before{content:""}.wfls-fa-spotify:before{content:""}.wfls-fa-deviantart:before{content:""}.wfls-fa-soundcloud:before{content:""}.wfls-fa-database:before{content:""}.wfls-fa-file-pdf-o:before{content:""}.wfls-fa-file-word-o:before{content:""}.wfls-fa-file-excel-o:before{content:""}.wfls-fa-file-powerpoint-o:before{content:""}.wfls-fa-file-photo-o:before,.wfls-fa-file-picture-o:before,.wfls-fa-file-image-o:before{content:""}.wfls-fa-file-zip-o:before,.wfls-fa-file-archive-o:before{content:""}.wfls-fa-file-sound-o:before,.wfls-fa-file-audio-o:before{content:""}.wfls-fa-file-movie-o:before,.wfls-fa-file-video-o:before{content:""}.wfls-fa-file-code-o:before{content:""}.wfls-fa-vine:before{content:""}.wfls-fa-codepen:before{content:""}.wfls-fa-jsfiddle:before{content:""}.wfls-fa-life-bouy:before,.wfls-fa-life-buoy:before,.wfls-fa-life-saver:before,.wfls-fa-support:before,.wfls-fa-life-ring:before{content:""}.wfls-fa-circle-o-notch:before{content:""}.wfls-fa-ra:before,.wfls-fa-resistance:before,.wfls-fa-rebel:before{content:""}.wfls-fa-ge:before,.wfls-fa-empire:before{content:""}.wfls-fa-git-square:before{content:""}.wfls-fa-git:before{content:""}.wfls-fa-y-combinator-square:before,.wfls-fa-yc-square:before,.wfls-fa-hacker-news:before{content:""}.wfls-fa-tencent-weibo:before{content:""}.wfls-fa-qq:before{content:""}.wfls-fa-wechat:before,.wfls-fa-weixin:before{content:""}.wfls-fa-send:before,.wfls-fa-paper-plane:before{content:""}.wfls-fa-send-o:before,.wfls-fa-paper-plane-o:before{content:""}.wfls-fa-history:before{content:""}.wfls-fa-circle-thin:before{content:""}.wfls-fa-header:before{content:""}.wfls-fa-paragraph:before{content:""}.wfls-fa-sliders:before{content:""}.wfls-fa-share-alt:before{content:""}.wfls-fa-share-alt-square:before{content:""}.wfls-fa-bomb:before{content:""}.wfls-fa-soccer-ball-o:before,.wfls-fa-futbol-o:before{content:""}.wfls-fa-tty:before{content:""}.wfls-fa-binoculars:before{content:""}.wfls-fa-plug:before{content:""}.wfls-fa-slideshare:before{content:""}.wfls-fa-twitch:before{content:""}.wfls-fa-yelp:before{content:""}.wfls-fa-newspaper-o:before{content:""}.wfls-fa-wifi:before{content:""}.wfls-fa-calculator:before{content:""}.wfls-fa-paypal:before{content:""}.wfls-fa-google-wallet:before{content:""}.wfls-fa-cc-visa:before{content:""}.wfls-fa-cc-mastercard:before{content:""}.wfls-fa-cc-discover:before{content:""}.wfls-fa-cc-amex:before{content:""}.wfls-fa-cc-paypal:before{content:""}.wfls-fa-cc-stripe:before{content:""}.wfls-fa-bell-slash:before{content:""}.wfls-fa-bell-slash-o:before{content:""}.wfls-fa-trash:before{content:""}.wfls-fa-copyright:before{content:""}.wfls-fa-at:before{content:""}.wfls-fa-eyedropper:before{content:""}.wfls-fa-paint-brush:before{content:""}.wfls-fa-birthday-cake:before{content:""}.wfls-fa-area-chart:before{content:""}.wfls-fa-pie-chart:before{content:""}.wfls-fa-line-chart:before{content:""}.wfls-fa-lastfm:before{content:""}.wfls-fa-lastfm-square:before{content:""}.wfls-fa-toggle-off:before{content:""}.wfls-fa-toggle-on:before{content:""}.wfls-fa-bicycle:before{content:""}.wfls-fa-bus:before{content:""}.wfls-fa-ioxhost:before{content:""}.wfls-fa-angellist:before{content:""}.wfls-fa-cc:before{content:""}.wfls-fa-shekel:before,.wfls-fa-sheqel:before,.wfls-fa-ils:before{content:""}.wfls-fa-meanpath:before{content:""}.wfls-fa-buysellads:before{content:""}.wfls-fa-connectdevelop:before{content:""}.wfls-fa-dashcube:before{content:""}.wfls-fa-forumbee:before{content:""}.wfls-fa-leanpub:before{content:""}.wfls-fa-sellsy:before{content:""}.wfls-fa-shirtsinbulk:before{content:""}.wfls-fa-simplybuilt:before{content:""}.wfls-fa-skyatlas:before{content:""}.wfls-fa-cart-plus:before{content:""}.wfls-fa-cart-arrow-down:before{content:""}.wfls-fa-diamond:before{content:""}.wfls-fa-ship:before{content:""}.wfls-fa-user-secret:before{content:""}.wfls-fa-motorcycle:before{content:""}.wfls-fa-street-view:before{content:""}.wfls-fa-heartbeat:before{content:""}.wfls-fa-venus:before{content:""}.wfls-fa-mars:before{content:""}.wfls-fa-mercury:before{content:""}.wfls-fa-intersex:before,.wfls-fa-transgender:before{content:""}.wfls-fa-transgender-alt:before{content:""}.wfls-fa-venus-double:before{content:""}.wfls-fa-mars-double:before{content:""}.wfls-fa-venus-mars:before{content:""}.wfls-fa-mars-stroke:before{content:""}.wfls-fa-mars-stroke-v:before{content:""}.wfls-fa-mars-stroke-h:before{content:""}.wfls-fa-neuter:before{content:""}.wfls-fa-genderless:before{content:""}.wfls-fa-facebook-official:before{content:""}.wfls-fa-pinterest-p:before{content:""}.wfls-fa-whatsapp:before{content:""}.wfls-fa-server:before{content:""}.wfls-fa-user-plus:before{content:""}.wfls-fa-user-times:before{content:""}.wfls-fa-hotel:before,.wfls-fa-bed:before{content:""}.wfls-fa-viacoin:before{content:""}.wfls-fa-train:before{content:""}.wfls-fa-subway:before{content:""}.wfls-fa-medium:before{content:""}.wfls-fa-yc:before,.wfls-fa-y-combinator:before{content:""}.wfls-fa-optin-monster:before{content:""}.wfls-fa-opencart:before{content:""}.wfls-fa-expeditedssl:before{content:""}.wfls-fa-battery-4:before,.wfls-fa-battery:before,.wfls-fa-battery-full:before{content:""}.wfls-fa-battery-3:before,.wfls-fa-battery-three-quarters:before{content:""}.wfls-fa-battery-2:before,.wfls-fa-battery-half:before{content:""}.wfls-fa-battery-1:before,.wfls-fa-battery-quarter:before{content:""}.wfls-fa-battery-0:before,.wfls-fa-battery-empty:before{content:""}.wfls-fa-mouse-pointer:before{content:""}.wfls-fa-i-cursor:before{content:""}.wfls-fa-object-group:before{content:""}.wfls-fa-object-ungroup:before{content:""}.wfls-fa-sticky-note:before{content:""}.wfls-fa-sticky-note-o:before{content:""}.wfls-fa-cc-jcb:before{content:""}.wfls-fa-cc-diners-club:before{content:""}.wfls-fa-clone:before{content:""}.wfls-fa-balance-scale:before{content:""}.wfls-fa-hourglass-o:before{content:""}.wfls-fa-hourglass-1:before,.wfls-fa-hourglass-start:before{content:""}.wfls-fa-hourglass-2:before,.wfls-fa-hourglass-half:before{content:""}.wfls-fa-hourglass-3:before,.wfls-fa-hourglass-end:before{content:""}.wfls-fa-hourglass:before{content:""}.wfls-fa-hand-grab-o:before,.wfls-fa-hand-rock-o:before{content:""}.wfls-fa-hand-stop-o:before,.wfls-fa-hand-paper-o:before{content:""}.wfls-fa-hand-scissors-o:before{content:""}.wfls-fa-hand-lizard-o:before{content:""}.wfls-fa-hand-spock-o:before{content:""}.wfls-fa-hand-pointer-o:before{content:""}.wfls-fa-hand-peace-o:before{content:""}.wfls-fa-trademark:before{content:""}.wfls-fa-registered:before{content:""}.wfls-fa-creative-commons:before{content:""}.wfls-fa-gg:before{content:""}.wfls-fa-gg-circle:before{content:""}.wfls-fa-tripadvisor:before{content:""}.wfls-fa-odnoklassniki:before{content:""}.wfls-fa-odnoklassniki-square:before{content:""}.wfls-fa-get-pocket:before{content:""}.wfls-fa-wikipedia-w:before{content:""}.wfls-fa-safari:before{content:""}.wfls-fa-chrome:before{content:""}.wfls-fa-firefox:before{content:""}.wfls-fa-opera:before{content:""}.wfls-fa-internet-explorer:before{content:""}.wfls-fa-tv:before,.wfls-fa-television:before{content:""}.wfls-fa-contao:before{content:""}.wfls-fa-500px:before{content:""}.wfls-fa-amazon:before{content:""}.wfls-fa-calendar-plus-o:before{content:""}.wfls-fa-calendar-minus-o:before{content:""}.wfls-fa-calendar-times-o:before{content:""}.wfls-fa-calendar-check-o:before{content:""}.wfls-fa-industry:before{content:""}.wfls-fa-map-pin:before{content:""}.wfls-fa-map-signs:before{content:""}.wfls-fa-map-o:before{content:""}.wfls-fa-map:before{content:""}.wfls-fa-commenting:before{content:""}.wfls-fa-commenting-o:before{content:""}.wfls-fa-houzz:before{content:""}.wfls-fa-vimeo:before{content:""}.wfls-fa-black-tie:before{content:""}.wfls-fa-fonticons:before{content:""}.wfls-fa-reddit-alien:before{content:""}.wfls-fa-edge:before{content:""}.wfls-fa-credit-card-alt:before{content:""}.wfls-fa-codiepie:before{content:""}.wfls-fa-modx:before{content:""}.wfls-fa-fort-awesome:before{content:""}.wfls-fa-usb:before{content:""}.wfls-fa-product-hunt:before{content:""}.wfls-fa-mixcloud:before{content:""}.wfls-fa-scribd:before{content:""}.wfls-fa-pause-circle:before{content:""}.wfls-fa-pause-circle-o:before{content:""}.wfls-fa-stop-circle:before{content:""}.wfls-fa-stop-circle-o:before{content:""}.wfls-fa-shopping-bag:before{content:""}.wfls-fa-shopping-basket:before{content:""}.wfls-fa-hashtag:before{content:""}.wfls-fa-bluetooth:before{content:""}.wfls-fa-bluetooth-b:before{content:""}.wfls-fa-percent:before{content:""}.wfls-fa-gitlab:before{content:""}.wfls-fa-wpbeginner:before{content:""}.wfls-fa-wpforms:before{content:""}.wfls-fa-envira:before{content:""}.wfls-fa-universal-access:before{content:""}.wfls-fa-wheelchair-alt:before{content:""}.wfls-fa-question-circle-o:before{content:""}.wfls-fa-blind:before{content:""}.wfls-fa-audio-description:before{content:""}.wfls-fa-volume-control-phone:before{content:""}.wfls-fa-braille:before{content:""}.wfls-fa-assistive-listening-systems:before{content:""}.wfls-fa-asl-interpreting:before,.wfls-fa-american-sign-language-interpreting:before{content:""}.wfls-fa-deafness:before,.wfls-fa-hard-of-hearing:before,.wfls-fa-deaf:before{content:""}.wfls-fa-glide:before{content:""}.wfls-fa-glide-g:before{content:""}.wfls-fa-signing:before,.wfls-fa-sign-language:before{content:""}.wfls-fa-low-vision:before{content:""}.wfls-fa-viadeo:before{content:""}.wfls-fa-viadeo-square:before{content:""}.wfls-fa-snapchat:before{content:""}.wfls-fa-snapchat-ghost:before{content:""}.wfls-fa-snapchat-square:before{content:""}.wfls-fa-pied-piper:before{content:""}.wfls-fa-first-order:before{content:""}.wfls-fa-yoast:before{content:""}.wfls-fa-themeisle:before{content:""}.wfls-fa-google-plus-circle:before,.wfls-fa-google-plus-official:before{content:""}.wfls-fa-fa:before,.wfls-fa-font-awesome:before{content:""}.wfls-fa-handshake-o:before{content:""}.wfls-fa-envelope-open:before{content:""}.wfls-fa-envelope-open-o:before{content:""}.wfls-fa-linode:before{content:""}.wfls-fa-address-book:before{content:""}.wfls-fa-address-book-o:before{content:""}.wfls-fa-vcard:before,.wfls-fa-address-card:before{content:""}.wfls-fa-vcard-o:before,.wfls-fa-address-card-o:before{content:""}.wfls-fa-user-circle:before{content:""}.wfls-fa-user-circle-o:before{content:""}.wfls-fa-user-o:before{content:""}.wfls-fa-id-badge:before{content:""}.wfls-fa-drivers-license:before,.wfls-fa-id-card:before{content:""}.wfls-fa-drivers-license-o:before,.wfls-fa-id-card-o:before{content:""}.wfls-fa-quora:before{content:""}.wfls-fa-free-code-camp:before{content:""}.wfls-fa-telegram:before{content:""}.wfls-fa-thermometer-4:before,.wfls-fa-thermometer:before,.wfls-fa-thermometer-full:before{content:""}.wfls-fa-thermometer-3:before,.wfls-fa-thermometer-three-quarters:before{content:""}.wfls-fa-thermometer-2:before,.wfls-fa-thermometer-half:before{content:""}.wfls-fa-thermometer-1:before,.wfls-fa-thermometer-quarter:before{content:""}.wfls-fa-thermometer-0:before,.wfls-fa-thermometer-empty:before{content:""}.wfls-fa-shower:before{content:""}.wfls-fa-bathtub:before,.wfls-fa-s15:before,.wfls-fa-bath:before{content:""}.wfls-fa-podcast:before{content:""}.wfls-fa-window-maximize:before{content:""}.wfls-fa-window-minimize:before{content:""}.wfls-fa-window-restore:before{content:""}.wfls-fa-times-rectangle:before,.wfls-fa-window-close:before{content:""}.wfls-fa-times-rectangle-o:before,.wfls-fa-window-close-o:before{content:""}.wfls-fa-bandcamp:before{content:""}.wfls-fa-grav:before{content:""}.wfls-fa-etsy:before{content:""}.wfls-fa-imdb:before{content:""}.wfls-fa-ravelry:before{content:""}.wfls-fa-eercast:before{content:""}.wfls-fa-microchip:before{content:""}.wfls-fa-snowflake-o:before{content:""}.wfls-fa-superpowers:before{content:""}.wfls-fa-wpexplorer:before{content:""}.wfls-fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} login-security/css/colorbox.1731600600.css000064400000021554147207020750013733 0ustar00#wflscolorbox,#wflscboxOverlay,#wflscboxWrapper{position:absolute;top:0;left:0;z-index:9999;overflow:hidden}#wflscboxOverlay{position:fixed;width:100%;height:100%}#wflscboxMiddleLeft,#wflscboxBottomLeft{clear:left}#wflscboxContent{position:relative}#wflscboxLoadedContent{overflow:auto}#wflscboxTitle{margin:0}#wflscboxLoadingOverlay,#wflscboxLoadingGraphic{position:absolute;top:0;left:0;width:100%}#wflscboxPrevious,#wflscboxNext,#wflscboxClose,#wflscboxSlideshow{cursor:pointer}.wflscboxPhoto{float:left;margin:auto;border:0;display:block}.wflscboxIframe{width:100%;height:100%;display:block;border:0}#wflscboxOverlay{background:#777;background:-webkit-radial-gradient(rgba(120,120,120,0.8), rgba(100,100,100,0.8) 50%, #464646);background:-moz-radial-gradient(rgba(120,120,120,0.6), rgba(100,100,100,0.8) 20%, #464646)}#wflscboxContent{background:#fff;overflow:hidden;padding:0 0 8px;margin:20px;-webkit-border-radius:3px 3px 2px 2px;-moz-border-radius:3px 3px 2px 2px;border-radius:3px 3px 2px 2px;-webkit-box-shadow:0 2px 4px rgba(0,0,0,0.4);-moz-box-shadow:0 2px 4px rgba(0,0,0,0.4);box-shadow:0 2px 4px rgba(0,0,0,0.4);-webkit-background-clip:padding-box}#wflscboxError{padding:50px;border:1px solid #ccc}#wflscboxLoadedContent{margin:10px 20px 28px 20px;font-family:Arial;color:#333;-webkit-border-radius:2px 2px 0 0;-moz-border-radius:2px 2px 0 0;border-radius:2px 2px 0 0}#wflscboxTitle{position:absolute;bottom:8px;left:5px;text-align:center;width:100%;color:#949494}#wflscboxCurrent{position:absolute;bottom:8px;left:63px;color:#949494;text-indent:-9999px}#wflscboxSlideshow{position:absolute;bottom:8px;right:35px;color:#0092ef}#wflscboxPrevious{position:absolute;bottom:5px;left:5px;background:url(../img/lightbox-controls.png) no-repeat -75px 0;width:25px;height:25px;text-indent:-9999px}#wflscboxPrevious.hover{background-position:-75px -25px}#wflscboxNext{position:absolute;bottom:5px;left:32px;background:url(../img/lightbox-controls.png) no-repeat -50px 0;width:25px;height:25px;text-indent:-9999px}#wflscboxNext.hover{background-position:-50px -25px}#wflscboxLoadingOverlay{background:url(../img/loading_background.png) no-repeat center center}#wflscboxLoadingGraphic{background:url(../img/loading.gif) no-repeat center center}#wflscboxClose{position:absolute;bottom:5px;right:5px;background:url(../img/lightbox-controls.png) no-repeat -25px 0;width:25px;height:25px;text-indent:-9999px}#wflscboxClose.hover{background-position:-25px -25px}#wflscolorbox.wfls-modal ul,#wflscolorbox.wfls-modal li{padding:0;margin:0}#wflscolorbox.wfls-modal #wflscboxContent{padding:0}#wflscolorbox.wfls-modal #wflscboxLoadedContent{margin:0}#wflscolorbox.wfls-modal .wfls-modal-success{overflow:auto;background-color:#00709e}#wflscolorbox.wfls-modal .wfls-modal-success .wfls-model-success-wrapper{margin:0.25rem;background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA5MS42NSA3MC43OSI+PHBhdGggZD0iTTkwLjA4LDguNzMsODIuOTEsMS41N2E1LjE5LDUuMTksMCwwLDAtNy40MywwTDMzLjMxLDQzLjc0LDE2LjE2LDI2LjU5YTUuMiw1LjIsMCwwLDAtNy40MywwTDEuNTcsMzMuNzdhNS4xOSw1LjE5LDAsMCwwLDAsNy40M2wyOCwyOGE1LjIsNS4yLDAsMCwwLDcuNDMsMEw5MC4wOCwxNi4xN2E1LjE5LDUuMTksMCwwLDAsMC03LjQzWm0wLDAiIGZpbGw9IiMwMDdjYWMiLz48L3N2Zz4=");background-position:top center;background-repeat:no-repeat}#wflscolorbox.wfls-modal .wfls-modal-header{min-height:44px;padding:1rem;width:100%;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;position:relative;border-bottom:1px solid #d9d9d9}#wflscolorbox.wfls-modal .wfls-modal-header .wfls-modal-header-content{max-width:75%}#wflscolorbox.wfls-modal .wfls-modal-header .wfls-modal-header-content .wfls-modal-title{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:0.95rem;width:100%;transition:color 0.2s ease-in}#wflscolorbox.wfls-modal .wfls-modal-header .wfls-modal-header-content .wfls-modal-subtitle{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;margin-top:.125rem;margin-bottom:.125rem;font-size:.575rem;color:#4f748e}#wflscolorbox.wfls-modal .wfls-modal-header .wfls-modal-header-action{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-end;justify-content:flex-end}#wflscolorbox.wfls-modal .wfls-modal-header .wfls-modal-header-action .wfls-modal-header-action-close a{color:#525355}#wflscolorbox.wfls-modal .wfls-modal-success .wfls-modal-header{border-bottom:0px}#wflscolorbox.wfls-modal .wfls-modal-success .wfls-modal-header .wfls-modal-header-content{max-width:100%;width:100%;padding-top:1rem}#wflscolorbox.wfls-modal .wfls-modal-success .wfls-modal-header .wfls-modal-header-content .wfls-modal-title{font-size:1.3125rem;font-weight:300;line-height:1.5;text-align:center;color:#ffffff}#wflscolorbox.wfls-modal .wfls-modal-content{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;padding:1rem}#wflscolorbox.wfls-modal .wfls-modal-content>*:first-child{margin-top:0}#wflscolorbox.wfls-modal .wfls-modal-content select,#wflscolorbox.wfls-modal .wfls-modal-content select option,#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default{font-size:0.7rem;font-weight:bold}@media (min-width: 768px){#wflscolorbox.wfls-modal .wfls-modal-content select,#wflscolorbox.wfls-modal .wfls-modal-content select option,#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default{font-size:0.8rem}}@media (min-width: 992px){#wflscolorbox.wfls-modal .wfls-modal-content select,#wflscolorbox.wfls-modal .wfls-modal-content select option,#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default{font-size:0.9rem}}#wflscolorbox.wfls-modal .wfls-modal-content .wfls-option-select-option,#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default,#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default .wflsselect2-selection--single{text-align:left;height:40px;border-radius:0;border:0;background-color:#ffffff;box-shadow:1px 1px 1px 2px rgba(215,215,215,0.35)}#wflscolorbox.wfls-modal .wfls-modal-content .wfls-option-select-option .wflsselect2-selection__rendered,#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default .wflsselect2-selection__rendered,#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default .wflsselect2-selection--single .wflsselect2-selection__rendered{color:#333;line-height:40px}#wflscolorbox.wfls-modal .wfls-modal-content .wfls-option-select-option .wflsselect2-selection__arrow,#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default .wflsselect2-selection__arrow,#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default .wflsselect2-selection--single .wflsselect2-selection__arrow{height:38px}#wflscolorbox.wfls-modal .wfls-modal-content .wfls-option-select-option.wflsselect2-container--disabled .wflsselect2-selection__rendered,#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default.wflsselect2-container--disabled .wflsselect2-selection__rendered,#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default .wflsselect2-selection--single.wflsselect2-container--disabled .wflsselect2-selection__rendered{color:#aaa}#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default .wflsselect2-selection--single .wflsselect2-selection__arrow b{border-color:#333 transparent transparent}#wflscolorbox.wfls-modal .wfls-modal-content .wflsselect2-container--default.wflsselect2-container--open .wflsselect2-selection--single .wflsselect2-selection__arrow b{border-color:transparent transparent #333}#wflscolorbox.wfls-modal .wfls-modal-success .wfls-modal-content{text-align:center;color:#ffffff;padding:0 1.5rem 2rem 1.5rem}#wflscolorbox.wfls-modal .wfls-modal-success .wfls-modal-content a{text-decoration:underline;color:#fff}#wflscolorbox.wfls-modal .wfls-modal-footer{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;min-height:44px;padding:1rem;width:100%;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;position:relative;background-color:#f1f1f1;border-top:1px solid #d9d9d9}#wflscolorbox.wfls-modal .wfls-modal-success .wfls-modal-footer{background-color:#ccc;border-top:1px solid #bfbfbf}#wflscolorbox,#wflscolorbox:before,#wflscolorbox:after{box-sizing:content-box}#wflscolorbox h1,#wflscolorbox h2,#wflscolorbox h3,#wflscolorbox h4,#wflscolorbox h5,#wflscolorbox h6{display:block;font-weight:600}#wflscolorbox h1{font-size:2em;margin:.67em 0}#wflscolorbox h2,#wflscolorbox h3{font-size:1.3em;margin:1em 0}#wflscolorbox h1,#wflscolorbox h2,#wflscolorbox h3{color:#23282d}#wflscolorbox p{font-size:13px;line-height:1.5;margin:1em 0} login-security/css/ionicons.1731600600.css000064400000156223147207020750013727 0ustar00@font-face{font-family:"Ionicons";src:url("../../../fonts/ionicons.woff?v=2.0.0") format("woff");font-weight:normal;font-style:normal}.wf-ion,.wf-ionicons,.wfls-ion-alert:before,.wfls-ion-alert-circled:before,.wfls-ion-android-add:before,.wfls-ion-android-add-circle:before,.wfls-ion-android-alarm-clock:before,.wfls-ion-android-alert:before,.wfls-ion-android-apps:before,.wfls-ion-android-archive:before,.wfls-ion-android-arrow-back:before,.wfls-ion-android-arrow-down:before,.wfls-ion-android-arrow-dropdown:before,.wfls-ion-android-arrow-dropdown-circle:before,.wfls-ion-android-arrow-dropleft:before,.wfls-ion-android-arrow-dropleft-circle:before,.wfls-ion-android-arrow-dropright:before,.wfls-ion-android-arrow-dropright-circle:before,.wfls-ion-android-arrow-dropup:before,.wfls-ion-android-arrow-dropup-circle:before,.wfls-ion-android-arrow-forward:before,.wfls-ion-android-arrow-up:before,.wfls-ion-android-attach:before,.wfls-ion-android-bar:before,.wfls-ion-android-bicycle:before,.wfls-ion-android-boat:before,.wfls-ion-android-bookmark:before,.wfls-ion-android-bulb:before,.wfls-ion-android-bus:before,.wfls-ion-android-calendar:before,.wfls-ion-android-call:before,.wfls-ion-android-camera:before,.wfls-ion-android-cancel:before,.wfls-ion-android-car:before,.wfls-ion-android-cart:before,.wfls-ion-android-chat:before,.wfls-ion-android-checkbox:before,.wfls-ion-android-checkbox-blank:before,.wfls-ion-android-checkbox-outline:before,.wfls-ion-android-checkbox-outline-blank:before,.wfls-ion-android-checkmark-circle:before,.wfls-ion-android-clipboard:before,.wfls-ion-android-close:before,.wfls-ion-android-cloud:before,.wfls-ion-android-cloud-circle:before,.wfls-ion-android-cloud-done:before,.wfls-ion-android-cloud-outline:before,.wfls-ion-android-color-palette:before,.wfls-ion-android-compass:before,.wfls-ion-android-contact:before,.wfls-ion-android-contacts:before,.wfls-ion-android-contract:before,.wfls-ion-android-create:before,.wfls-ion-android-delete:before,.wfls-ion-android-desktop:before,.wfls-ion-android-document:before,.wfls-ion-android-done:before,.wfls-ion-android-done-all:before,.wfls-ion-android-download:before,.wfls-ion-android-drafts:before,.wfls-ion-android-exit:before,.wfls-ion-android-expand:before,.wfls-ion-android-favorite:before,.wfls-ion-android-favorite-outline:before,.wfls-ion-android-film:before,.wfls-ion-android-folder:before,.wfls-ion-android-folder-open:before,.wfls-ion-android-funnel:before,.wfls-ion-android-globe:before,.wfls-ion-android-hand:before,.wfls-ion-android-hangout:before,.wfls-ion-android-happy:before,.wfls-ion-android-home:before,.wfls-ion-android-image:before,.wfls-ion-android-laptop:before,.wfls-ion-android-list:before,.wfls-ion-android-locate:before,.wfls-ion-android-lock:before,.wfls-ion-android-mail:before,.wfls-ion-android-map:before,.wfls-ion-android-menu:before,.wfls-ion-android-microphone:before,.wfls-ion-android-microphone-off:before,.wfls-ion-android-more-horizontal:before,.wfls-ion-android-more-vertical:before,.wfls-ion-android-navigate:before,.wfls-ion-android-notifications:before,.wfls-ion-android-notifications-none:before,.wfls-ion-android-notifications-off:before,.wfls-ion-android-open:before,.wfls-ion-android-options:before,.wfls-ion-android-people:before,.wfls-ion-android-person:before,.wfls-ion-android-person-add:before,.wfls-ion-android-phone-landscape:before,.wfls-ion-android-phone-portrait:before,.wfls-ion-android-pin:before,.wfls-ion-android-plane:before,.wfls-ion-android-playstore:before,.wfls-ion-android-print:before,.wfls-ion-android-radio-button-off:before,.wfls-ion-android-radio-button-on:before,.wfls-ion-android-refresh:before,.wfls-ion-android-remove:before,.wfls-ion-android-remove-circle:before,.wfls-ion-android-restaurant:before,.wfls-ion-android-sad:before,.wfls-ion-android-search:before,.wfls-ion-android-send:before,.wfls-ion-android-settings:before,.wfls-ion-android-share:before,.wfls-ion-android-share-alt:before,.wfls-ion-android-star:before,.wfls-ion-android-star-half:before,.wfls-ion-android-star-outline:before,.wfls-ion-android-stopwatch:before,.wfls-ion-android-subway:before,.wfls-ion-android-sunny:before,.wfls-ion-android-sync:before,.wfls-ion-android-textsms:before,.wfls-ion-android-time:before,.wfls-ion-android-train:before,.wfls-ion-android-unlock:before,.wfls-ion-android-upload:before,.wfls-ion-android-volume-down:before,.wfls-ion-android-volume-mute:before,.wfls-ion-android-volume-off:before,.wfls-ion-android-volume-up:before,.wfls-ion-android-walk:before,.wfls-ion-android-warning:before,.wfls-ion-android-watch:before,.wfls-ion-android-wifi:before,.wfls-ion-aperture:before,.wfls-ion-archive:before,.wfls-ion-arrow-down-a:before,.wfls-ion-arrow-down-b:before,.wfls-ion-arrow-down-c:before,.wfls-ion-arrow-expand:before,.wfls-ion-arrow-graph-down-left:before,.wfls-ion-arrow-graph-down-right:before,.wfls-ion-arrow-graph-up-left:before,.wfls-ion-arrow-graph-up-right:before,.wfls-ion-arrow-left-a:before,.wfls-ion-arrow-left-b:before,.wfls-ion-arrow-left-c:before,.wfls-ion-arrow-move:before,.wfls-ion-arrow-resize:before,.wfls-ion-arrow-return-left:before,.wfls-ion-arrow-return-right:before,.wfls-ion-arrow-right-a:before,.wfls-ion-arrow-right-b:before,.wfls-ion-arrow-right-c:before,.wfls-ion-arrow-shrink:before,.wfls-ion-arrow-swap:before,.wfls-ion-arrow-up-a:before,.wfls-ion-arrow-up-b:before,.wfls-ion-arrow-up-c:before,.wfls-ion-asterisk:before,.wfls-ion-at:before,.wfls-ion-backspace:before,.wfls-ion-backspace-outline:before,.wfls-ion-bag:before,.wfls-ion-battery-charging:before,.wfls-ion-battery-empty:before,.wfls-ion-battery-full:before,.wfls-ion-battery-half:before,.wfls-ion-battery-low:before,.wfls-ion-beaker:before,.wfls-ion-beer:before,.wfls-ion-bluetooth:before,.wfls-ion-bonfire:before,.wfls-ion-bookmark:before,.wfls-ion-bowtie:before,.wfls-ion-briefcase:before,.wfls-ion-bug:before,.wfls-ion-calculator:before,.wfls-ion-calendar:before,.wfls-ion-camera:before,.wfls-ion-card:before,.wfls-ion-cash:before,.wfls-ion-chatbox:before,.wfls-ion-chatbox-working:before,.wfls-ion-chatboxes:before,.wfls-ion-chatbubble:before,.wfls-ion-chatbubble-working:before,.wfls-ion-chatbubbles:before,.wfls-ion-checkmark:before,.wfls-ion-checkmark-circled:before,.wfls-ion-checkmark-round:before,.wfls-ion-chevron-down:before,.wfls-ion-chevron-left:before,.wfls-ion-chevron-right:before,.wfls-ion-chevron-up:before,.wfls-ion-clipboard:before,.wfls-ion-clock:before,.wfls-ion-close:before,.wfls-ion-close-circled:before,.wfls-ion-close-round:before,.wfls-ion-closed-captioning:before,.wfls-ion-cloud:before,.wfls-ion-code:before,.wfls-ion-code-download:before,.wfls-ion-code-working:before,.wfls-ion-coffee:before,.wfls-ion-compass:before,.wfls-ion-compose:before,.wfls-ion-connection-bars:before,.wfls-ion-contrast:before,.wfls-ion-crop:before,.wfls-ion-cube:before,.wfls-ion-disc:before,.wfls-ion-document:before,.wfls-ion-document-text:before,.wfls-ion-drag:before,.wfls-ion-earth:before,.wfls-ion-easel:before,.wfls-ion-edit:before,.wfls-ion-egg:before,.wfls-ion-eject:before,.wfls-ion-email:before,.wfls-ion-email-unread:before,.wfls-ion-erlenmeyer-flask:before,.wfls-ion-erlenmeyer-flask-bubbles:before,.wfls-ion-eye:before,.wfls-ion-eye-disabled:before,.wfls-ion-female:before,.wfls-ion-filing:before,.wfls-ion-film-marker:before,.wfls-ion-fireball:before,.wfls-ion-flag:before,.wfls-ion-flame:before,.wfls-ion-flash:before,.wfls-ion-flash-off:before,.wfls-ion-folder:before,.wfls-ion-fork:before,.wfls-ion-fork-repo:before,.wfls-ion-forward:before,.wfls-ion-funnel:before,.wfls-ion-gear-a:before,.wfls-ion-gear-b:before,.wfls-ion-grid:before,.wfls-ion-hammer:before,.wfls-ion-happy:before,.wfls-ion-happy-outline:before,.wfls-ion-headphone:before,.wfls-ion-heart:before,.wfls-ion-heart-broken:before,.wfls-ion-help:before,.wfls-ion-help-buoy:before,.wfls-ion-help-circled:before,.wfls-ion-home:before,.wfls-ion-icecream:before,.wfls-ion-image:before,.wfls-ion-images:before,.wfls-ion-information:before,.wfls-ion-information-circled:before,.wfls-ion-ionic:before,.wfls-ion-ios-alarm:before,.wfls-ion-ios-alarm-outline:before,.wfls-ion-ios-albums:before,.wfls-ion-ios-albums-outline:before,.wfls-ion-ios-americanfootball:before,.wfls-ion-ios-americanfootball-outline:before,.wfls-ion-ios-analytics:before,.wfls-ion-ios-analytics-outline:before,.wfls-ion-ios-arrow-back:before,.wfls-ion-ios-arrow-down:before,.wfls-ion-ios-arrow-forward:before,.wfls-ion-ios-arrow-left:before,.wfls-ion-ios-arrow-right:before,.wfls-ion-ios-arrow-thin-down:before,.wfls-ion-ios-arrow-thin-left:before,.wfls-ion-ios-arrow-thin-right:before,.wfls-ion-ios-arrow-thin-up:before,.wfls-ion-ios-arrow-up:before,.wfls-ion-ios-at:before,.wfls-ion-ios-at-outline:before,.wfls-ion-ios-barcode:before,.wfls-ion-ios-barcode-outline:before,.wfls-ion-ios-baseball:before,.wfls-ion-ios-baseball-outline:before,.wfls-ion-ios-basketball:before,.wfls-ion-ios-basketball-outline:before,.wfls-ion-ios-bell:before,.wfls-ion-ios-bell-outline:before,.wfls-ion-ios-body:before,.wfls-ion-ios-body-outline:before,.wfls-ion-ios-bolt:before,.wfls-ion-ios-bolt-outline:before,.wfls-ion-ios-book:before,.wfls-ion-ios-book-outline:before,.wfls-ion-ios-bookmarks:before,.wfls-ion-ios-bookmarks-outline:before,.wfls-ion-ios-box:before,.wfls-ion-ios-box-outline:before,.wfls-ion-ios-briefcase:before,.wfls-ion-ios-briefcase-outline:before,.wfls-ion-ios-browsers:before,.wfls-ion-ios-browsers-outline:before,.wfls-ion-ios-calculator:before,.wfls-ion-ios-calculator-outline:before,.wfls-ion-ios-calendar:before,.wfls-ion-ios-calendar-outline:before,.wfls-ion-ios-camera:before,.wfls-ion-ios-camera-outline:before,.wfls-ion-ios-cart:before,.wfls-ion-ios-cart-outline:before,.wfls-ion-ios-chatboxes:before,.wfls-ion-ios-chatboxes-outline:before,.wfls-ion-ios-chatbubble:before,.wfls-ion-ios-chatbubble-outline:before,.wfls-ion-ios-checkmark:before,.wfls-ion-ios-checkmark-empty:before,.wfls-ion-ios-checkmark-outline:before,.wfls-ion-ios-circle-filled:before,.wfls-ion-ios-circle-outline:before,.wfls-ion-ios-clock:before,.wfls-ion-ios-clock-outline:before,.wfls-ion-ios-close:before,.wfls-ion-ios-close-empty:before,.wfls-ion-ios-close-outline:before,.wfls-ion-ios-cloud:before,.wfls-ion-ios-cloud-download:before,.wfls-ion-ios-cloud-download-outline:before,.wfls-ion-ios-cloud-outline:before,.wfls-ion-ios-cloud-upload:before,.wfls-ion-ios-cloud-upload-outline:before,.wfls-ion-ios-cloudy:before,.wfls-ion-ios-cloudy-night:before,.wfls-ion-ios-cloudy-night-outline:before,.wfls-ion-ios-cloudy-outline:before,.wfls-ion-ios-cog:before,.wfls-ion-ios-cog-outline:before,.wfls-ion-ios-color-filter:before,.wfls-ion-ios-color-filter-outline:before,.wfls-ion-ios-color-wand:before,.wfls-ion-ios-color-wand-outline:before,.wfls-ion-ios-compose:before,.wfls-ion-ios-compose-outline:before,.wfls-ion-ios-contact:before,.wfls-ion-ios-contact-outline:before,.wfls-ion-ios-copy:before,.wfls-ion-ios-copy-outline:before,.wfls-ion-ios-crop:before,.wfls-ion-ios-crop-strong:before,.wfls-ion-ios-download:before,.wfls-ion-ios-download-outline:before,.wfls-ion-ios-drag:before,.wfls-ion-ios-email:before,.wfls-ion-ios-email-outline:before,.wfls-ion-ios-eye:before,.wfls-ion-ios-eye-outline:before,.wfls-ion-ios-fastforward:before,.wfls-ion-ios-fastforward-outline:before,.wfls-ion-ios-filing:before,.wfls-ion-ios-filing-outline:before,.wfls-ion-ios-film:before,.wfls-ion-ios-film-outline:before,.wfls-ion-ios-flag:before,.wfls-ion-ios-flag-outline:before,.wfls-ion-ios-flame:before,.wfls-ion-ios-flame-outline:before,.wfls-ion-ios-flask:before,.wfls-ion-ios-flask-outline:before,.wfls-ion-ios-flower:before,.wfls-ion-ios-flower-outline:before,.wfls-ion-ios-folder:before,.wfls-ion-ios-folder-outline:before,.wfls-ion-ios-football:before,.wfls-ion-ios-football-outline:before,.wfls-ion-ios-game-controller-a:before,.wfls-ion-ios-game-controller-a-outline:before,.wfls-ion-ios-game-controller-b:before,.wfls-ion-ios-game-controller-b-outline:before,.wfls-ion-ios-gear:before,.wfls-ion-ios-gear-outline:before,.wfls-ion-ios-glasses:before,.wfls-ion-ios-glasses-outline:before,.wfls-ion-ios-grid-view:before,.wfls-ion-ios-grid-view-outline:before,.wfls-ion-ios-heart:before,.wfls-ion-ios-heart-outline:before,.wfls-ion-ios-help:before,.wfls-ion-ios-help-empty:before,.wfls-ion-ios-help-outline:before,.wfls-ion-ios-home:before,.wfls-ion-ios-home-outline:before,.wfls-ion-ios-infinite:before,.wfls-ion-ios-infinite-outline:before,.wfls-ion-ios-information:before,.wfls-ion-ios-information-empty:before,.wfls-ion-ios-information-outline:before,.wfls-ion-ios-ionic-outline:before,.wfls-ion-ios-keypad:before,.wfls-ion-ios-keypad-outline:before,.wfls-ion-ios-lightbulb:before,.wfls-ion-ios-lightbulb-outline:before,.wfls-ion-ios-list:before,.wfls-ion-ios-list-outline:before,.wfls-ion-ios-location:before,.wfls-ion-ios-location-outline:before,.wfls-ion-ios-locked:before,.wfls-ion-ios-locked-outline:before,.wfls-ion-ios-loop:before,.wfls-ion-ios-loop-strong:before,.wfls-ion-ios-medical:before,.wfls-ion-ios-medical-outline:before,.wfls-ion-ios-medkit:before,.wfls-ion-ios-medkit-outline:before,.wfls-ion-ios-mic:before,.wfls-ion-ios-mic-off:before,.wfls-ion-ios-mic-outline:before,.wfls-ion-ios-minus:before,.wfls-ion-ios-minus-empty:before,.wfls-ion-ios-minus-outline:before,.wfls-ion-ios-monitor:before,.wfls-ion-ios-monitor-outline:before,.wfls-ion-ios-moon:before,.wfls-ion-ios-moon-outline:before,.wfls-ion-ios-more:before,.wfls-ion-ios-more-outline:before,.wfls-ion-ios-musical-note:before,.wfls-ion-ios-musical-notes:before,.wfls-ion-ios-navigate:before,.wfls-ion-ios-navigate-outline:before,.wfls-ion-ios-nutrition:before,.wfls-ion-ios-nutrition-outline:before,.wfls-ion-ios-paper:before,.wfls-ion-ios-paper-outline:before,.wfls-ion-ios-paperplane:before,.wfls-ion-ios-paperplane-outline:before,.wfls-ion-ios-partlysunny:before,.wfls-ion-ios-partlysunny-outline:before,.wfls-ion-ios-pause:before,.wfls-ion-ios-pause-outline:before,.wfls-ion-ios-paw:before,.wfls-ion-ios-paw-outline:before,.wfls-ion-ios-people:before,.wfls-ion-ios-people-outline:before,.wfls-ion-ios-person:before,.wfls-ion-ios-person-outline:before,.wfls-ion-ios-personadd:before,.wfls-ion-ios-personadd-outline:before,.wfls-ion-ios-photos:before,.wfls-ion-ios-photos-outline:before,.wfls-ion-ios-pie:before,.wfls-ion-ios-pie-outline:before,.wfls-ion-ios-pint:before,.wfls-ion-ios-pint-outline:before,.wfls-ion-ios-play:before,.wfls-ion-ios-play-outline:before,.wfls-ion-ios-plus:before,.wfls-ion-ios-plus-empty:before,.wfls-ion-ios-plus-outline:before,.wfls-ion-ios-pricetag:before,.wfls-ion-ios-pricetag-outline:before,.wfls-ion-ios-pricetags:before,.wfls-ion-ios-pricetags-outline:before,.wfls-ion-ios-printer:before,.wfls-ion-ios-printer-outline:before,.wfls-ion-ios-pulse:before,.wfls-ion-ios-pulse-strong:before,.wfls-ion-ios-rainy:before,.wfls-ion-ios-rainy-outline:before,.wfls-ion-ios-recording:before,.wfls-ion-ios-recording-outline:before,.wfls-ion-ios-redo:before,.wfls-ion-ios-redo-outline:before,.wfls-ion-ios-refresh:before,.wfls-ion-ios-refresh-empty:before,.wfls-ion-ios-refresh-outline:before,.wfls-ion-ios-reload:before,.wfls-ion-ios-reverse-camera:before,.wfls-ion-ios-reverse-camera-outline:before,.wfls-ion-ios-rewind:before,.wfls-ion-ios-rewind-outline:before,.wfls-ion-ios-rose:before,.wfls-ion-ios-rose-outline:before,.wfls-ion-ios-search:before,.wfls-ion-ios-search-strong:before,.wfls-ion-ios-settings:before,.wfls-ion-ios-settings-strong:before,.wfls-ion-ios-shuffle:before,.wfls-ion-ios-shuffle-strong:before,.wfls-ion-ios-skipbackward:before,.wfls-ion-ios-skipbackward-outline:before,.wfls-ion-ios-skipforward:before,.wfls-ion-ios-skipforward-outline:before,.wfls-ion-ios-snowy:before,.wfls-ion-ios-speedometer:before,.wfls-ion-ios-speedometer-outline:before,.wfls-ion-ios-star:before,.wfls-ion-ios-star-half:before,.wfls-ion-ios-star-outline:before,.wfls-ion-ios-stopwatch:before,.wfls-ion-ios-stopwatch-outline:before,.wfls-ion-ios-sunny:before,.wfls-ion-ios-sunny-outline:before,.wfls-ion-ios-telephone:before,.wfls-ion-ios-telephone-outline:before,.wfls-ion-ios-tennisball:before,.wfls-ion-ios-tennisball-outline:before,.wfls-ion-ios-thunderstorm:before,.wfls-ion-ios-thunderstorm-outline:before,.wfls-ion-ios-time:before,.wfls-ion-ios-time-outline:before,.wfls-ion-ios-timer:before,.wfls-ion-ios-timer-outline:before,.wfls-ion-ios-toggle:before,.wfls-ion-ios-toggle-outline:before,.wfls-ion-ios-trash:before,.wfls-ion-ios-trash-outline:before,.wfls-ion-ios-undo:before,.wfls-ion-ios-undo-outline:before,.wfls-ion-ios-unlocked:before,.wfls-ion-ios-unlocked-outline:before,.wfls-ion-ios-upload:before,.wfls-ion-ios-upload-outline:before,.wfls-ion-ios-videocam:before,.wfls-ion-ios-videocam-outline:before,.wfls-ion-ios-volume-high:before,.wfls-ion-ios-volume-low:before,.wfls-ion-ios-wineglass:before,.wfls-ion-ios-wineglass-outline:before,.wfls-ion-ios-world:before,.wfls-ion-ios-world-outline:before,.wfls-ion-ipad:before,.wfls-ion-iphone:before,.wfls-ion-ipod:before,.wfls-ion-jet:before,.wfls-ion-key:before,.wfls-ion-knife:before,.wfls-ion-laptop:before,.wfls-ion-leaf:before,.wfls-ion-levels:before,.wfls-ion-lightbulb:before,.wfls-ion-link:before,.wfls-ion-load-a:before,.wfls-ion-load-b:before,.wfls-ion-load-c:before,.wfls-ion-load-d:before,.wfls-ion-location:before,.wfls-ion-lock-combination:before,.wfls-ion-locked:before,.wfls-ion-log-in:before,.wfls-ion-log-out:before,.wfls-ion-loop:before,.wfls-ion-magnet:before,.wfls-ion-male:before,.wfls-ion-man:before,.wfls-ion-map:before,.wfls-ion-medkit:before,.wfls-ion-merge:before,.wfls-ion-mic-a:before,.wfls-ion-mic-b:before,.wfls-ion-mic-c:before,.wfls-ion-minus:before,.wfls-ion-minus-circled:before,.wfls-ion-minus-round:before,.wfls-ion-model-s:before,.wfls-ion-monitor:before,.wfls-ion-more:before,.wfls-ion-mouse:before,.wfls-ion-music-note:before,.wfls-ion-navicon:before,.wfls-ion-navicon-round:before,.wfls-ion-navigate:before,.wfls-ion-network:before,.wfls-ion-no-smoking:before,.wfls-ion-nuclear:before,.wfls-ion-outlet:before,.wfls-ion-paintbrush:before,.wfls-ion-paintbucket:before,.wfls-ion-paper-airplane:before,.wfls-ion-paperclip:before,.wfls-ion-pause:before,.wfls-ion-person:before,.wfls-ion-person-add:before,.wfls-ion-person-stalker:before,.wfls-ion-pie-graph:before,.wfls-ion-pin:before,.wfls-ion-pinpoint:before,.wfls-ion-pizza:before,.wfls-ion-plane:before,.wfls-ion-planet:before,.wfls-ion-play:before,.wfls-ion-playstation:before,.wfls-ion-plus:before,.wfls-ion-plus-circled:before,.wfls-ion-plus-round:before,.wfls-ion-podium:before,.wfls-ion-pound:before,.wfls-ion-power:before,.wfls-ion-pricetag:before,.wfls-ion-pricetags:before,.wfls-ion-printer:before,.wfls-ion-pull-request:before,.wfls-ion-qr-scanner:before,.wfls-ion-quote:before,.wfls-ion-radio-waves:before,.wfls-ion-record:before,.wfls-ion-refresh:before,.wfls-ion-reply:before,.wfls-ion-reply-all:before,.wfls-ion-ribbon-a:before,.wfls-ion-ribbon-b:before,.wfls-ion-sad:before,.wfls-ion-sad-outline:before,.wfls-ion-scissors:before,.wfls-ion-search:before,.wfls-ion-settings:before,.wfls-ion-share:before,.wfls-ion-shuffle:before,.wfls-ion-skip-backward:before,.wfls-ion-skip-forward:before,.wfls-ion-social-android:before,.wfls-ion-social-android-outline:before,.wfls-ion-social-angular:before,.wfls-ion-social-angular-outline:before,.wfls-ion-social-apple:before,.wfls-ion-social-apple-outline:before,.wfls-ion-social-bitcoin:before,.wfls-ion-social-bitcoin-outline:before,.wfls-ion-social-buffer:before,.wfls-ion-social-buffer-outline:before,.wfls-ion-social-chrome:before,.wfls-ion-social-chrome-outline:before,.wfls-ion-social-codepen:before,.wfls-ion-social-codepen-outline:before,.wfls-ion-social-css3:before,.wfls-ion-social-css3-outline:before,.wfls-ion-social-designernews:before,.wfls-ion-social-designernews-outline:before,.wfls-ion-social-dribbble:before,.wfls-ion-social-dribbble-outline:before,.wfls-ion-social-dropbox:before,.wfls-ion-social-dropbox-outline:before,.wfls-ion-social-euro:before,.wfls-ion-social-euro-outline:before,.wfls-ion-social-facebook:before,.wfls-ion-social-facebook-outline:before,.wfls-ion-social-foursquare:before,.wfls-ion-social-foursquare-outline:before,.wfls-ion-social-freebsd-devil:before,.wfls-ion-social-github:before,.wfls-ion-social-github-outline:before,.wfls-ion-social-google:before,.wfls-ion-social-google-outline:before,.wfls-ion-social-googleplus:before,.wfls-ion-social-googleplus-outline:before,.wfls-ion-social-hackernews:before,.wfls-ion-social-hackernews-outline:before,.wfls-ion-social-html5:before,.wfls-ion-social-html5-outline:before,.wfls-ion-social-instagram:before,.wfls-ion-social-instagram-outline:before,.wfls-ion-social-javascript:before,.wfls-ion-social-javascript-outline:before,.wfls-ion-social-linkedin:before,.wfls-ion-social-linkedin-outline:before,.wfls-ion-social-markdown:before,.wfls-ion-social-nodejs:before,.wfls-ion-social-octocat:before,.wfls-ion-social-pinterest:before,.wfls-ion-social-pinterest-outline:before,.wfls-ion-social-python:before,.wfls-ion-social-reddit:before,.wfls-ion-social-reddit-outline:before,.wfls-ion-social-rss:before,.wfls-ion-social-rss-outline:before,.wfls-ion-social-sass:before,.wfls-ion-social-skype:before,.wfls-ion-social-skype-outline:before,.wfls-ion-social-snapchat:before,.wfls-ion-social-snapchat-outline:before,.wfls-ion-social-tumblr:before,.wfls-ion-social-tumblr-outline:before,.wfls-ion-social-tux:before,.wfls-ion-social-twitch:before,.wfls-ion-social-twitch-outline:before,.wfls-ion-social-twitter:before,.wfls-ion-social-twitter-outline:before,.wfls-ion-social-usd:before,.wfls-ion-social-usd-outline:before,.wfls-ion-social-vimeo:before,.wfls-ion-social-vimeo-outline:before,.wfls-ion-social-whatsapp:before,.wfls-ion-social-whatsapp-outline:before,.wfls-ion-social-windows:before,.wfls-ion-social-windows-outline:before,.wfls-ion-social-wordpress:before,.wfls-ion-social-wordpress-outline:before,.wfls-ion-social-yahoo:before,.wfls-ion-social-yahoo-outline:before,.wfls-ion-social-yen:before,.wfls-ion-social-yen-outline:before,.wfls-ion-social-youtube:before,.wfls-ion-social-youtube-outline:before,.wfls-ion-soup-can:before,.wfls-ion-soup-can-outline:before,.wfls-ion-speakerphone:before,.wfls-ion-speedometer:before,.wfls-ion-spoon:before,.wfls-ion-star:before,.wfls-ion-stats-bars:before,.wfls-ion-steam:before,.wfls-ion-stop:before,.wfls-ion-thermometer:before,.wfls-ion-thumbsdown:before,.wfls-ion-thumbsup:before,.wfls-ion-toggle:before,.wfls-ion-toggle-filled:before,.wfls-ion-transgender:before,.wfls-ion-trash-a:before,.wfls-ion-trash-b:before,.wfls-ion-trophy:before,.wfls-ion-tshirt:before,.wfls-ion-tshirt-outline:before,.wfls-ion-umbrella:before,.wfls-ion-university:before,.wfls-ion-unlocked:before,.wfls-ion-upload:before,.wfls-ion-usb:before,.wfls-ion-videocamera:before,.wfls-ion-volume-high:before,.wfls-ion-volume-low:before,.wfls-ion-volume-medium:before,.wfls-ion-volume-mute:before,.wfls-ion-wand:before,.wfls-ion-waterdrop:before,.wfls-ion-wifi:before,.wfls-ion-wineglass:before,.wfls-ion-woman:before,.wfls-ion-wrench:before,.wfls-ion-xbox:before{display:inline-block;font-family:"Ionicons" !important;speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;text-rendering:auto;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.wfls-ion-alert:before{content:""}.wfls-ion-alert-circled:before{content:""}.wfls-ion-android-add:before{content:""}.wfls-ion-android-add-circle:before{content:""}.wfls-ion-android-alarm-clock:before{content:""}.wfls-ion-android-alert:before{content:""}.wfls-ion-android-apps:before{content:""}.wfls-ion-android-archive:before{content:""}.wfls-ion-android-arrow-back:before{content:""}.wfls-ion-android-arrow-down:before{content:""}.wfls-ion-android-arrow-dropdown:before{content:""}.wfls-ion-android-arrow-dropdown-circle:before{content:""}.wfls-ion-android-arrow-dropleft:before{content:""}.wfls-ion-android-arrow-dropleft-circle:before{content:""}.wfls-ion-android-arrow-dropright:before{content:""}.wfls-ion-android-arrow-dropright-circle:before{content:""}.wfls-ion-android-arrow-dropup:before{content:""}.wfls-ion-android-arrow-dropup-circle:before{content:""}.wfls-ion-android-arrow-forward:before{content:""}.wfls-ion-android-arrow-up:before{content:""}.wfls-ion-android-attach:before{content:""}.wfls-ion-android-bar:before{content:""}.wfls-ion-android-bicycle:before{content:""}.wfls-ion-android-boat:before{content:""}.wfls-ion-android-bookmark:before{content:""}.wfls-ion-android-bulb:before{content:""}.wfls-ion-android-bus:before{content:""}.wfls-ion-android-calendar:before{content:""}.wfls-ion-android-call:before{content:""}.wfls-ion-android-camera:before{content:""}.wfls-ion-android-cancel:before{content:""}.wfls-ion-android-car:before{content:""}.wfls-ion-android-cart:before{content:""}.wfls-ion-android-chat:before{content:""}.wfls-ion-android-checkbox:before{content:""}.wfls-ion-android-checkbox-blank:before{content:""}.wfls-ion-android-checkbox-outline:before{content:""}.wfls-ion-android-checkbox-outline-blank:before{content:""}.wfls-ion-android-checkmark-circle:before{content:""}.wfls-ion-android-clipboard:before{content:""}.wfls-ion-android-close:before{content:""}.wfls-ion-android-cloud:before{content:""}.wfls-ion-android-cloud-circle:before{content:""}.wfls-ion-android-cloud-done:before{content:""}.wfls-ion-android-cloud-outline:before{content:""}.wfls-ion-android-color-palette:before{content:""}.wfls-ion-android-compass:before{content:""}.wfls-ion-android-contact:before{content:""}.wfls-ion-android-contacts:before{content:""}.wfls-ion-android-contract:before{content:""}.wfls-ion-android-create:before{content:""}.wfls-ion-android-delete:before{content:""}.wfls-ion-android-desktop:before{content:""}.wfls-ion-android-document:before{content:""}.wfls-ion-android-done:before{content:""}.wfls-ion-android-done-all:before{content:""}.wfls-ion-android-download:before{content:""}.wfls-ion-android-drafts:before{content:""}.wfls-ion-android-exit:before{content:""}.wfls-ion-android-expand:before{content:""}.wfls-ion-android-favorite:before{content:""}.wfls-ion-android-favorite-outline:before{content:""}.wfls-ion-android-film:before{content:""}.wfls-ion-android-folder:before{content:""}.wfls-ion-android-folder-open:before{content:""}.wfls-ion-android-funnel:before{content:""}.wfls-ion-android-globe:before{content:""}.wfls-ion-android-hand:before{content:""}.wfls-ion-android-hangout:before{content:""}.wfls-ion-android-happy:before{content:""}.wfls-ion-android-home:before{content:""}.wfls-ion-android-image:before{content:""}.wfls-ion-android-laptop:before{content:""}.wfls-ion-android-list:before{content:""}.wfls-ion-android-locate:before{content:""}.wfls-ion-android-lock:before{content:""}.wfls-ion-android-mail:before{content:""}.wfls-ion-android-map:before{content:""}.wfls-ion-android-menu:before{content:""}.wfls-ion-android-microphone:before{content:""}.wfls-ion-android-microphone-off:before{content:""}.wfls-ion-android-more-horizontal:before{content:""}.wfls-ion-android-more-vertical:before{content:""}.wfls-ion-android-navigate:before{content:""}.wfls-ion-android-notifications:before{content:""}.wfls-ion-android-notifications-none:before{content:""}.wfls-ion-android-notifications-off:before{content:""}.wfls-ion-android-open:before{content:""}.wfls-ion-android-options:before{content:""}.wfls-ion-android-people:before{content:""}.wfls-ion-android-person:before{content:""}.wfls-ion-android-person-add:before{content:""}.wfls-ion-android-phone-landscape:before{content:""}.wfls-ion-android-phone-portrait:before{content:""}.wfls-ion-android-pin:before{content:""}.wfls-ion-android-plane:before{content:""}.wfls-ion-android-playstore:before{content:""}.wfls-ion-android-print:before{content:""}.wfls-ion-android-radio-button-off:before{content:""}.wfls-ion-android-radio-button-on:before{content:""}.wfls-ion-android-refresh:before{content:""}.wfls-ion-android-remove:before{content:""}.wfls-ion-android-remove-circle:before{content:""}.wfls-ion-android-restaurant:before{content:""}.wfls-ion-android-sad:before{content:""}.wfls-ion-android-search:before{content:""}.wfls-ion-android-send:before{content:""}.wfls-ion-android-settings:before{content:""}.wfls-ion-android-share:before{content:""}.wfls-ion-android-share-alt:before{content:""}.wfls-ion-android-star:before{content:""}.wfls-ion-android-star-half:before{content:""}.wfls-ion-android-star-outline:before{content:""}.wfls-ion-android-stopwatch:before{content:""}.wfls-ion-android-subway:before{content:""}.wfls-ion-android-sunny:before{content:""}.wfls-ion-android-sync:before{content:""}.wfls-ion-android-textsms:before{content:""}.wfls-ion-android-time:before{content:""}.wfls-ion-android-train:before{content:""}.wfls-ion-android-unlock:before{content:""}.wfls-ion-android-upload:before{content:""}.wfls-ion-android-volume-down:before{content:""}.wfls-ion-android-volume-mute:before{content:""}.wfls-ion-android-volume-off:before{content:""}.wfls-ion-android-volume-up:before{content:""}.wfls-ion-android-walk:before{content:""}.wfls-ion-android-warning:before{content:""}.wfls-ion-android-watch:before{content:""}.wfls-ion-android-wifi:before{content:""}.wfls-ion-aperture:before{content:""}.wfls-ion-archive:before{content:""}.wfls-ion-arrow-down-a:before{content:""}.wfls-ion-arrow-down-b:before{content:""}.wfls-ion-arrow-down-c:before{content:""}.wfls-ion-arrow-expand:before{content:""}.wfls-ion-arrow-graph-down-left:before{content:""}.wfls-ion-arrow-graph-down-right:before{content:""}.wfls-ion-arrow-graph-up-left:before{content:""}.wfls-ion-arrow-graph-up-right:before{content:""}.wfls-ion-arrow-left-a:before{content:""}.wfls-ion-arrow-left-b:before{content:""}.wfls-ion-arrow-left-c:before{content:""}.wfls-ion-arrow-move:before{content:""}.wfls-ion-arrow-resize:before{content:""}.wfls-ion-arrow-return-left:before{content:""}.wfls-ion-arrow-return-right:before{content:""}.wfls-ion-arrow-right-a:before{content:""}.wfls-ion-arrow-right-b:before{content:""}.wfls-ion-arrow-right-c:before{content:""}.wfls-ion-arrow-shrink:before{content:""}.wfls-ion-arrow-swap:before{content:""}.wfls-ion-arrow-up-a:before{content:""}.wfls-ion-arrow-up-b:before{content:""}.wfls-ion-arrow-up-c:before{content:""}.wfls-ion-asterisk:before{content:""}.wfls-ion-at:before{content:""}.wfls-ion-backspace:before{content:""}.wfls-ion-backspace-outline:before{content:""}.wfls-ion-bag:before{content:""}.wfls-ion-battery-charging:before{content:""}.wfls-ion-battery-empty:before{content:""}.wfls-ion-battery-full:before{content:""}.wfls-ion-battery-half:before{content:""}.wfls-ion-battery-low:before{content:""}.wfls-ion-beaker:before{content:""}.wfls-ion-beer:before{content:""}.wfls-ion-bluetooth:before{content:""}.wfls-ion-bonfire:before{content:""}.wfls-ion-bookmark:before{content:""}.wfls-ion-bowtie:before{content:""}.wfls-ion-briefcase:before{content:""}.wfls-ion-bug:before{content:""}.wfls-ion-calculator:before{content:""}.wfls-ion-calendar:before{content:""}.wfls-ion-camera:before{content:""}.wfls-ion-card:before{content:""}.wfls-ion-cash:before{content:""}.wfls-ion-chatbox:before{content:""}.wfls-ion-chatbox-working:before{content:""}.wfls-ion-chatboxes:before{content:""}.wfls-ion-chatbubble:before{content:""}.wfls-ion-chatbubble-working:before{content:""}.wfls-ion-chatbubbles:before{content:""}.wfls-ion-checkmark:before{content:""}.wfls-ion-checkmark-circled:before{content:""}.wfls-ion-checkmark-round:before{content:""}.wfls-ion-chevron-down:before{content:""}.wfls-ion-chevron-left:before{content:""}.wfls-ion-chevron-right:before{content:""}.wfls-ion-chevron-up:before{content:""}.wfls-ion-clipboard:before{content:""}.wfls-ion-clock:before{content:""}.wfls-ion-close:before{content:""}.wfls-ion-close-circled:before{content:""}.wfls-ion-close-round:before{content:""}.wfls-ion-closed-captioning:before{content:""}.wfls-ion-cloud:before{content:""}.wfls-ion-code:before{content:""}.wfls-ion-code-download:before{content:""}.wfls-ion-code-working:before{content:""}.wfls-ion-coffee:before{content:""}.wfls-ion-compass:before{content:""}.wfls-ion-compose:before{content:""}.wfls-ion-connection-bars:before{content:""}.wfls-ion-contrast:before{content:""}.wfls-ion-crop:before{content:""}.wfls-ion-cube:before{content:""}.wfls-ion-disc:before{content:""}.wfls-ion-document:before{content:""}.wfls-ion-document-text:before{content:""}.wfls-ion-drag:before{content:""}.wfls-ion-earth:before{content:""}.wfls-ion-easel:before{content:""}.wfls-ion-edit:before{content:""}.wfls-ion-egg:before{content:""}.wfls-ion-eject:before{content:""}.wfls-ion-email:before{content:""}.wfls-ion-email-unread:before{content:""}.wfls-ion-erlenmeyer-flask:before{content:""}.wfls-ion-erlenmeyer-flask-bubbles:before{content:""}.wfls-ion-eye:before{content:""}.wfls-ion-eye-disabled:before{content:""}.wfls-ion-female:before{content:""}.wfls-ion-filing:before{content:""}.wfls-ion-film-marker:before{content:""}.wfls-ion-fireball:before{content:""}.wfls-ion-flag:before{content:""}.wfls-ion-flame:before{content:""}.wfls-ion-flash:before{content:""}.wfls-ion-flash-off:before{content:""}.wfls-ion-folder:before{content:""}.wfls-ion-fork:before{content:""}.wfls-ion-fork-repo:before{content:""}.wfls-ion-forward:before{content:""}.wfls-ion-funnel:before{content:""}.wfls-ion-gear-a:before{content:""}.wfls-ion-gear-b:before{content:""}.wfls-ion-grid:before{content:""}.wfls-ion-hammer:before{content:""}.wfls-ion-happy:before{content:""}.wfls-ion-happy-outline:before{content:""}.wfls-ion-headphone:before{content:""}.wfls-ion-heart:before{content:""}.wfls-ion-heart-broken:before{content:""}.wfls-ion-help:before{content:""}.wfls-ion-help-buoy:before{content:""}.wfls-ion-help-circled:before{content:""}.wfls-ion-home:before{content:""}.wfls-ion-icecream:before{content:""}.wfls-ion-image:before{content:""}.wfls-ion-images:before{content:""}.wfls-ion-information:before{content:""}.wfls-ion-information-circled:before{content:""}.wfls-ion-ionic:before{content:""}.wfls-ion-ios-alarm:before{content:""}.wfls-ion-ios-alarm-outline:before{content:""}.wfls-ion-ios-albums:before{content:""}.wfls-ion-ios-albums-outline:before{content:""}.wfls-ion-ios-americanfootball:before{content:""}.wfls-ion-ios-americanfootball-outline:before{content:""}.wfls-ion-ios-analytics:before{content:""}.wfls-ion-ios-analytics-outline:before{content:""}.wfls-ion-ios-arrow-back:before{content:""}.wfls-ion-ios-arrow-down:before{content:""}.wfls-ion-ios-arrow-forward:before{content:""}.wfls-ion-ios-arrow-left:before{content:""}.wfls-ion-ios-arrow-right:before{content:""}.wfls-ion-ios-arrow-thin-down:before{content:""}.wfls-ion-ios-arrow-thin-left:before{content:""}.wfls-ion-ios-arrow-thin-right:before{content:""}.wfls-ion-ios-arrow-thin-up:before{content:""}.wfls-ion-ios-arrow-up:before{content:""}.wfls-ion-ios-at:before{content:""}.wfls-ion-ios-at-outline:before{content:""}.wfls-ion-ios-barcode:before{content:""}.wfls-ion-ios-barcode-outline:before{content:""}.wfls-ion-ios-baseball:before{content:""}.wfls-ion-ios-baseball-outline:before{content:""}.wfls-ion-ios-basketball:before{content:""}.wfls-ion-ios-basketball-outline:before{content:""}.wfls-ion-ios-bell:before{content:""}.wfls-ion-ios-bell-outline:before{content:""}.wfls-ion-ios-body:before{content:""}.wfls-ion-ios-body-outline:before{content:""}.wfls-ion-ios-bolt:before{content:""}.wfls-ion-ios-bolt-outline:before{content:""}.wfls-ion-ios-book:before{content:""}.wfls-ion-ios-book-outline:before{content:""}.wfls-ion-ios-bookmarks:before{content:""}.wfls-ion-ios-bookmarks-outline:before{content:""}.wfls-ion-ios-box:before{content:""}.wfls-ion-ios-box-outline:before{content:""}.wfls-ion-ios-briefcase:before{content:""}.wfls-ion-ios-briefcase-outline:before{content:""}.wfls-ion-ios-browsers:before{content:""}.wfls-ion-ios-browsers-outline:before{content:""}.wfls-ion-ios-calculator:before{content:""}.wfls-ion-ios-calculator-outline:before{content:""}.wfls-ion-ios-calendar:before{content:""}.wfls-ion-ios-calendar-outline:before{content:""}.wfls-ion-ios-camera:before{content:""}.wfls-ion-ios-camera-outline:before{content:""}.wfls-ion-ios-cart:before{content:""}.wfls-ion-ios-cart-outline:before{content:""}.wfls-ion-ios-chatboxes:before{content:""}.wfls-ion-ios-chatboxes-outline:before{content:""}.wfls-ion-ios-chatbubble:before{content:""}.wfls-ion-ios-chatbubble-outline:before{content:""}.wfls-ion-ios-checkmark:before{content:""}.wfls-ion-ios-checkmark-empty:before{content:""}.wfls-ion-ios-checkmark-outline:before{content:""}.wfls-ion-ios-circle-filled:before{content:""}.wfls-ion-ios-circle-outline:before{content:""}.wfls-ion-ios-clock:before{content:""}.wfls-ion-ios-clock-outline:before{content:""}.wfls-ion-ios-close:before{content:""}.wfls-ion-ios-close-empty:before{content:""}.wfls-ion-ios-close-outline:before{content:""}.wfls-ion-ios-cloud:before{content:""}.wfls-ion-ios-cloud-download:before{content:""}.wfls-ion-ios-cloud-download-outline:before{content:""}.wfls-ion-ios-cloud-outline:before{content:""}.wfls-ion-ios-cloud-upload:before{content:""}.wfls-ion-ios-cloud-upload-outline:before{content:""}.wfls-ion-ios-cloudy:before{content:""}.wfls-ion-ios-cloudy-night:before{content:""}.wfls-ion-ios-cloudy-night-outline:before{content:""}.wfls-ion-ios-cloudy-outline:before{content:""}.wfls-ion-ios-cog:before{content:""}.wfls-ion-ios-cog-outline:before{content:""}.wfls-ion-ios-color-filter:before{content:""}.wfls-ion-ios-color-filter-outline:before{content:""}.wfls-ion-ios-color-wand:before{content:""}.wfls-ion-ios-color-wand-outline:before{content:""}.wfls-ion-ios-compose:before{content:""}.wfls-ion-ios-compose-outline:before{content:""}.wfls-ion-ios-contact:before{content:""}.wfls-ion-ios-contact-outline:before{content:""}.wfls-ion-ios-copy:before{content:""}.wfls-ion-ios-copy-outline:before{content:""}.wfls-ion-ios-crop:before{content:""}.wfls-ion-ios-crop-strong:before{content:""}.wfls-ion-ios-download:before{content:""}.wfls-ion-ios-download-outline:before{content:""}.wfls-ion-ios-drag:before{content:""}.wfls-ion-ios-email:before{content:""}.wfls-ion-ios-email-outline:before{content:""}.wfls-ion-ios-eye:before{content:""}.wfls-ion-ios-eye-outline:before{content:""}.wfls-ion-ios-fastforward:before{content:""}.wfls-ion-ios-fastforward-outline:before{content:""}.wfls-ion-ios-filing:before{content:""}.wfls-ion-ios-filing-outline:before{content:""}.wfls-ion-ios-film:before{content:""}.wfls-ion-ios-film-outline:before{content:""}.wfls-ion-ios-flag:before{content:""}.wfls-ion-ios-flag-outline:before{content:""}.wfls-ion-ios-flame:before{content:""}.wfls-ion-ios-flame-outline:before{content:""}.wfls-ion-ios-flask:before{content:""}.wfls-ion-ios-flask-outline:before{content:""}.wfls-ion-ios-flower:before{content:""}.wfls-ion-ios-flower-outline:before{content:""}.wfls-ion-ios-folder:before{content:""}.wfls-ion-ios-folder-outline:before{content:""}.wfls-ion-ios-football:before{content:""}.wfls-ion-ios-football-outline:before{content:""}.wfls-ion-ios-game-controller-a:before{content:""}.wfls-ion-ios-game-controller-a-outline:before{content:""}.wfls-ion-ios-game-controller-b:before{content:""}.wfls-ion-ios-game-controller-b-outline:before{content:""}.wfls-ion-ios-gear:before{content:""}.wfls-ion-ios-gear-outline:before{content:""}.wfls-ion-ios-glasses:before{content:""}.wfls-ion-ios-glasses-outline:before{content:""}.wfls-ion-ios-grid-view:before{content:""}.wfls-ion-ios-grid-view-outline:before{content:""}.wfls-ion-ios-heart:before{content:""}.wfls-ion-ios-heart-outline:before{content:""}.wfls-ion-ios-help:before{content:""}.wfls-ion-ios-help-empty:before{content:""}.wfls-ion-ios-help-outline:before{content:""}.wfls-ion-ios-home:before{content:""}.wfls-ion-ios-home-outline:before{content:""}.wfls-ion-ios-infinite:before{content:""}.wfls-ion-ios-infinite-outline:before{content:""}.wfls-ion-ios-information:before{content:""}.wfls-ion-ios-information-empty:before{content:""}.wfls-ion-ios-information-outline:before{content:""}.wfls-ion-ios-ionic-outline:before{content:""}.wfls-ion-ios-keypad:before{content:""}.wfls-ion-ios-keypad-outline:before{content:""}.wfls-ion-ios-lightbulb:before{content:""}.wfls-ion-ios-lightbulb-outline:before{content:""}.wfls-ion-ios-list:before{content:""}.wfls-ion-ios-list-outline:before{content:""}.wfls-ion-ios-location:before{content:""}.wfls-ion-ios-location-outline:before{content:""}.wfls-ion-ios-locked:before{content:""}.wfls-ion-ios-locked-outline:before{content:""}.wfls-ion-ios-loop:before{content:""}.wfls-ion-ios-loop-strong:before{content:""}.wfls-ion-ios-medical:before{content:""}.wfls-ion-ios-medical-outline:before{content:""}.wfls-ion-ios-medkit:before{content:""}.wfls-ion-ios-medkit-outline:before{content:""}.wfls-ion-ios-mic:before{content:""}.wfls-ion-ios-mic-off:before{content:""}.wfls-ion-ios-mic-outline:before{content:""}.wfls-ion-ios-minus:before{content:""}.wfls-ion-ios-minus-empty:before{content:""}.wfls-ion-ios-minus-outline:before{content:""}.wfls-ion-ios-monitor:before{content:""}.wfls-ion-ios-monitor-outline:before{content:""}.wfls-ion-ios-moon:before{content:""}.wfls-ion-ios-moon-outline:before{content:""}.wfls-ion-ios-more:before{content:""}.wfls-ion-ios-more-outline:before{content:""}.wfls-ion-ios-musical-note:before{content:""}.wfls-ion-ios-musical-notes:before{content:""}.wfls-ion-ios-navigate:before{content:""}.wfls-ion-ios-navigate-outline:before{content:""}.wfls-ion-ios-nutrition:before{content:""}.wfls-ion-ios-nutrition-outline:before{content:""}.wfls-ion-ios-paper:before{content:""}.wfls-ion-ios-paper-outline:before{content:""}.wfls-ion-ios-paperplane:before{content:""}.wfls-ion-ios-paperplane-outline:before{content:""}.wfls-ion-ios-partlysunny:before{content:""}.wfls-ion-ios-partlysunny-outline:before{content:""}.wfls-ion-ios-pause:before{content:""}.wfls-ion-ios-pause-outline:before{content:""}.wfls-ion-ios-paw:before{content:""}.wfls-ion-ios-paw-outline:before{content:""}.wfls-ion-ios-people:before{content:""}.wfls-ion-ios-people-outline:before{content:""}.wfls-ion-ios-person:before{content:""}.wfls-ion-ios-person-outline:before{content:""}.wfls-ion-ios-personadd:before{content:""}.wfls-ion-ios-personadd-outline:before{content:""}.wfls-ion-ios-photos:before{content:""}.wfls-ion-ios-photos-outline:before{content:""}.wfls-ion-ios-pie:before{content:""}.wfls-ion-ios-pie-outline:before{content:""}.wfls-ion-ios-pint:before{content:""}.wfls-ion-ios-pint-outline:before{content:""}.wfls-ion-ios-play:before{content:""}.wfls-ion-ios-play-outline:before{content:""}.wfls-ion-ios-plus:before{content:""}.wfls-ion-ios-plus-empty:before{content:""}.wfls-ion-ios-plus-outline:before{content:""}.wfls-ion-ios-pricetag:before{content:""}.wfls-ion-ios-pricetag-outline:before{content:""}.wfls-ion-ios-pricetags:before{content:""}.wfls-ion-ios-pricetags-outline:before{content:""}.wfls-ion-ios-printer:before{content:""}.wfls-ion-ios-printer-outline:before{content:""}.wfls-ion-ios-pulse:before{content:""}.wfls-ion-ios-pulse-strong:before{content:""}.wfls-ion-ios-rainy:before{content:""}.wfls-ion-ios-rainy-outline:before{content:""}.wfls-ion-ios-recording:before{content:""}.wfls-ion-ios-recording-outline:before{content:""}.wfls-ion-ios-redo:before{content:""}.wfls-ion-ios-redo-outline:before{content:""}.wfls-ion-ios-refresh:before{content:""}.wfls-ion-ios-refresh-empty:before{content:""}.wfls-ion-ios-refresh-outline:before{content:""}.wfls-ion-ios-reload:before{content:""}.wfls-ion-ios-reverse-camera:before{content:""}.wfls-ion-ios-reverse-camera-outline:before{content:""}.wfls-ion-ios-rewind:before{content:""}.wfls-ion-ios-rewind-outline:before{content:""}.wfls-ion-ios-rose:before{content:""}.wfls-ion-ios-rose-outline:before{content:""}.wfls-ion-ios-search:before{content:""}.wfls-ion-ios-search-strong:before{content:""}.wfls-ion-ios-settings:before{content:""}.wfls-ion-ios-settings-strong:before{content:""}.wfls-ion-ios-shuffle:before{content:""}.wfls-ion-ios-shuffle-strong:before{content:""}.wfls-ion-ios-skipbackward:before{content:""}.wfls-ion-ios-skipbackward-outline:before{content:""}.wfls-ion-ios-skipforward:before{content:""}.wfls-ion-ios-skipforward-outline:before{content:""}.wfls-ion-ios-snowy:before{content:""}.wfls-ion-ios-speedometer:before{content:""}.wfls-ion-ios-speedometer-outline:before{content:""}.wfls-ion-ios-star:before{content:""}.wfls-ion-ios-star-half:before{content:""}.wfls-ion-ios-star-outline:before{content:""}.wfls-ion-ios-stopwatch:before{content:""}.wfls-ion-ios-stopwatch-outline:before{content:""}.wfls-ion-ios-sunny:before{content:""}.wfls-ion-ios-sunny-outline:before{content:""}.wfls-ion-ios-telephone:before{content:""}.wfls-ion-ios-telephone-outline:before{content:""}.wfls-ion-ios-tennisball:before{content:""}.wfls-ion-ios-tennisball-outline:before{content:""}.wfls-ion-ios-thunderstorm:before{content:""}.wfls-ion-ios-thunderstorm-outline:before{content:""}.wfls-ion-ios-time:before{content:""}.wfls-ion-ios-time-outline:before{content:""}.wfls-ion-ios-timer:before{content:""}.wfls-ion-ios-timer-outline:before{content:""}.wfls-ion-ios-toggle:before{content:""}.wfls-ion-ios-toggle-outline:before{content:""}.wfls-ion-ios-trash:before{content:""}.wfls-ion-ios-trash-outline:before{content:""}.wfls-ion-ios-undo:before{content:""}.wfls-ion-ios-undo-outline:before{content:""}.wfls-ion-ios-unlocked:before{content:""}.wfls-ion-ios-unlocked-outline:before{content:""}.wfls-ion-ios-upload:before{content:""}.wfls-ion-ios-upload-outline:before{content:""}.wfls-ion-ios-videocam:before{content:""}.wfls-ion-ios-videocam-outline:before{content:""}.wfls-ion-ios-volume-high:before{content:""}.wfls-ion-ios-volume-low:before{content:""}.wfls-ion-ios-wineglass:before{content:""}.wfls-ion-ios-wineglass-outline:before{content:""}.wfls-ion-ios-world:before{content:""}.wfls-ion-ios-world-outline:before{content:""}.wfls-ion-ipad:before{content:""}.wfls-ion-iphone:before{content:""}.wfls-ion-ipod:before{content:""}.wfls-ion-jet:before{content:""}.wfls-ion-key:before{content:""}.wfls-ion-knife:before{content:""}.wfls-ion-laptop:before{content:""}.wfls-ion-leaf:before{content:""}.wfls-ion-levels:before{content:""}.wfls-ion-lightbulb:before{content:""}.wfls-ion-link:before{content:""}.wfls-ion-load-a:before{content:""}.wfls-ion-load-b:before{content:""}.wfls-ion-load-c:before{content:""}.wfls-ion-load-d:before{content:""}.wfls-ion-location:before{content:""}.wfls-ion-lock-combination:before{content:""}.wfls-ion-locked:before{content:""}.wfls-ion-log-in:before{content:""}.wfls-ion-log-out:before{content:""}.wfls-ion-loop:before{content:""}.wfls-ion-magnet:before{content:""}.wfls-ion-male:before{content:""}.wfls-ion-man:before{content:""}.wfls-ion-map:before{content:""}.wfls-ion-medkit:before{content:""}.wfls-ion-merge:before{content:""}.wfls-ion-mic-a:before{content:""}.wfls-ion-mic-b:before{content:""}.wfls-ion-mic-c:before{content:""}.wfls-ion-minus:before{content:""}.wfls-ion-minus-circled:before{content:""}.wfls-ion-minus-round:before{content:""}.wfls-ion-model-s:before{content:""}.wfls-ion-monitor:before{content:""}.wfls-ion-more:before{content:""}.wfls-ion-mouse:before{content:""}.wfls-ion-music-note:before{content:""}.wfls-ion-navicon:before{content:""}.wfls-ion-navicon-round:before{content:""}.wfls-ion-navigate:before{content:""}.wfls-ion-network:before{content:""}.wfls-ion-no-smoking:before{content:""}.wfls-ion-nuclear:before{content:""}.wfls-ion-outlet:before{content:""}.wfls-ion-paintbrush:before{content:""}.wfls-ion-paintbucket:before{content:""}.wfls-ion-paper-airplane:before{content:""}.wfls-ion-paperclip:before{content:""}.wfls-ion-pause:before{content:""}.wfls-ion-person:before{content:""}.wfls-ion-person-add:before{content:""}.wfls-ion-person-stalker:before{content:""}.wfls-ion-pie-graph:before{content:""}.wfls-ion-pin:before{content:""}.wfls-ion-pinpoint:before{content:""}.wfls-ion-pizza:before{content:""}.wfls-ion-plane:before{content:""}.wfls-ion-planet:before{content:""}.wfls-ion-play:before{content:""}.wfls-ion-playstation:before{content:""}.wfls-ion-plus:before{content:""}.wfls-ion-plus-circled:before{content:""}.wfls-ion-plus-round:before{content:""}.wfls-ion-podium:before{content:""}.wfls-ion-pound:before{content:""}.wfls-ion-power:before{content:""}.wfls-ion-pricetag:before{content:""}.wfls-ion-pricetags:before{content:""}.wfls-ion-printer:before{content:""}.wfls-ion-pull-request:before{content:""}.wfls-ion-qr-scanner:before{content:""}.wfls-ion-quote:before{content:""}.wfls-ion-radio-waves:before{content:""}.wfls-ion-record:before{content:""}.wfls-ion-refresh:before{content:""}.wfls-ion-reply:before{content:""}.wfls-ion-reply-all:before{content:""}.wfls-ion-ribbon-a:before{content:""}.wfls-ion-ribbon-b:before{content:""}.wfls-ion-sad:before{content:""}.wfls-ion-sad-outline:before{content:""}.wfls-ion-scissors:before{content:""}.wfls-ion-search:before{content:""}.wfls-ion-settings:before{content:""}.wfls-ion-share:before{content:""}.wfls-ion-shuffle:before{content:""}.wfls-ion-skip-backward:before{content:""}.wfls-ion-skip-forward:before{content:""}.wfls-ion-social-android:before{content:""}.wfls-ion-social-android-outline:before{content:""}.wfls-ion-social-angular:before{content:""}.wfls-ion-social-angular-outline:before{content:""}.wfls-ion-social-apple:before{content:""}.wfls-ion-social-apple-outline:before{content:""}.wfls-ion-social-bitcoin:before{content:""}.wfls-ion-social-bitcoin-outline:before{content:""}.wfls-ion-social-buffer:before{content:""}.wfls-ion-social-buffer-outline:before{content:""}.wfls-ion-social-chrome:before{content:""}.wfls-ion-social-chrome-outline:before{content:""}.wfls-ion-social-codepen:before{content:""}.wfls-ion-social-codepen-outline:before{content:""}.wfls-ion-social-css3:before{content:""}.wfls-ion-social-css3-outline:before{content:""}.wfls-ion-social-designernews:before{content:""}.wfls-ion-social-designernews-outline:before{content:""}.wfls-ion-social-dribbble:before{content:""}.wfls-ion-social-dribbble-outline:before{content:""}.wfls-ion-social-dropbox:before{content:""}.wfls-ion-social-dropbox-outline:before{content:""}.wfls-ion-social-euro:before{content:""}.wfls-ion-social-euro-outline:before{content:""}.wfls-ion-social-facebook:before{content:""}.wfls-ion-social-facebook-outline:before{content:""}.wfls-ion-social-foursquare:before{content:""}.wfls-ion-social-foursquare-outline:before{content:""}.wfls-ion-social-freebsd-devil:before{content:""}.wfls-ion-social-github:before{content:""}.wfls-ion-social-github-outline:before{content:""}.wfls-ion-social-google:before{content:""}.wfls-ion-social-google-outline:before{content:""}.wfls-ion-social-googleplus:before{content:""}.wfls-ion-social-googleplus-outline:before{content:""}.wfls-ion-social-hackernews:before{content:""}.wfls-ion-social-hackernews-outline:before{content:""}.wfls-ion-social-html5:before{content:""}.wfls-ion-social-html5-outline:before{content:""}.wfls-ion-social-instagram:before{content:""}.wfls-ion-social-instagram-outline:before{content:""}.wfls-ion-social-javascript:before{content:""}.wfls-ion-social-javascript-outline:before{content:""}.wfls-ion-social-linkedin:before{content:""}.wfls-ion-social-linkedin-outline:before{content:""}.wfls-ion-social-markdown:before{content:""}.wfls-ion-social-nodejs:before{content:""}.wfls-ion-social-octocat:before{content:""}.wfls-ion-social-pinterest:before{content:""}.wfls-ion-social-pinterest-outline:before{content:""}.wfls-ion-social-python:before{content:""}.wfls-ion-social-reddit:before{content:""}.wfls-ion-social-reddit-outline:before{content:""}.wfls-ion-social-rss:before{content:""}.wfls-ion-social-rss-outline:before{content:""}.wfls-ion-social-sass:before{content:""}.wfls-ion-social-skype:before{content:""}.wfls-ion-social-skype-outline:before{content:""}.wfls-ion-social-snapchat:before{content:""}.wfls-ion-social-snapchat-outline:before{content:""}.wfls-ion-social-tumblr:before{content:""}.wfls-ion-social-tumblr-outline:before{content:""}.wfls-ion-social-tux:before{content:""}.wfls-ion-social-twitch:before{content:""}.wfls-ion-social-twitch-outline:before{content:""}.wfls-ion-social-twitter:before{content:""}.wfls-ion-social-twitter-outline:before{content:""}.wfls-ion-social-usd:before{content:""}.wfls-ion-social-usd-outline:before{content:""}.wfls-ion-social-vimeo:before{content:""}.wfls-ion-social-vimeo-outline:before{content:""}.wfls-ion-social-whatsapp:before{content:""}.wfls-ion-social-whatsapp-outline:before{content:""}.wfls-ion-social-windows:before{content:""}.wfls-ion-social-windows-outline:before{content:""}.wfls-ion-social-wordpress:before{content:""}.wfls-ion-social-wordpress-outline:before{content:""}.wfls-ion-social-yahoo:before{content:""}.wfls-ion-social-yahoo-outline:before{content:""}.wfls-ion-social-yen:before{content:""}.wfls-ion-social-yen-outline:before{content:""}.wfls-ion-social-youtube:before{content:""}.wfls-ion-social-youtube-outline:before{content:""}.wfls-ion-soup-can:before{content:""}.wfls-ion-soup-can-outline:before{content:""}.wfls-ion-speakerphone:before{content:""}.wfls-ion-speedometer:before{content:""}.wfls-ion-spoon:before{content:""}.wfls-ion-star:before{content:""}.wfls-ion-stats-bars:before{content:""}.wfls-ion-steam:before{content:""}.wfls-ion-stop:before{content:""}.wfls-ion-thermometer:before{content:""}.wfls-ion-thumbsdown:before{content:""}.wfls-ion-thumbsup:before{content:""}.wfls-ion-toggle:before{content:""}.wfls-ion-toggle-filled:before{content:""}.wfls-ion-transgender:before{content:""}.wfls-ion-trash-a:before{content:""}.wfls-ion-trash-b:before{content:""}.wfls-ion-trophy:before{content:""}.wfls-ion-tshirt:before{content:""}.wfls-ion-tshirt-outline:before{content:""}.wfls-ion-umbrella:before{content:""}.wfls-ion-university:before{content:""}.wfls-ion-unlocked:before{content:""}.wfls-ion-upload:before{content:""}.wfls-ion-usb:before{content:""}.wfls-ion-videocamera:before{content:""}.wfls-ion-volume-high:before{content:""}.wfls-ion-volume-low:before{content:""}.wfls-ion-volume-medium:before{content:""}.wfls-ion-volume-mute:before{content:""}.wfls-ion-wand:before{content:""}.wfls-ion-waterdrop:before{content:""}.wfls-ion-wifi:before{content:""}.wfls-ion-wineglass:before{content:""}.wfls-ion-woman:before{content:""}.wfls-ion-wrench:before{content:""}.wfls-ion-xbox:before{content:""} login-security/css/embedded.1731600600.css000064400000001050147207020750013622 0ustar00#wfls-management-embedded .wfls-flex-row-wrapped{flex-wrap:wrap;-webkit-flex-wrap:wrap}#wfls-management-embedded ul.wfls-recovery-codes{margin-left:0;padding-left:0}#wfls-management-embedded .wfls-btn{text-decoration:none}#wfls-management-embedded #wfls-qr-code-text{margin-top:0.5rem}#wfls-management-embedded>div>div:not(:last-child){margin-bottom:0.75rem}#wfls-management-embedded>div.wfls-no-bottom-column-margin>div:not(:last-child){margin-bottom:0}#wfls-management-embedded input[type=text]{width:auto}.wfls-modal-footer ul{list-style-type:none} login-security/css/woocommerce-account.1731600600.css000064400000020516147207020750016052 0ustar00.woocommerce-MyAccount-navigation ul li.woocommerce-MyAccount-navigation-link--wordfence-2fa a::before{background-color:currentColor;mask-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+Cjxzdmcgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDMyIDMyIiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zOnNlcmlmPSJodHRwOi8vd3d3LnNlcmlmLmNvbS8iIHN0eWxlPSJmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MS40MTQyMTsiPgogICAgPGc+CiAgICAgICAgPHBhdGggZD0iTTE2LDguNTlMMTcuNTksMTIuMDhMMTcuNiwxMi4xMUwxNy4wNiwxMi4xMUwxNy4wNiwxMi4xMkMxNy4wNywxMi4xNyAxNy4wOCwxMi4yMiAxNy4wOCwxMi4yOEMxNy4wOCwxMi40NSAxNy4wNCwxMi42MSAxNi45NiwxMi43NUMxNi45MywxMi44MiAxNi44OCwxMi44OCAxNi44NCwxMi45M0MxNi44NCwxMi45MyAxNi43NywxMy42OSAxNi43OCwxNS4xMUMxNi43OCwxNS45NiAxNi44MywxNy4yMSAxNi45MywxOC41OEMxOC41OSwxOC42NyAyMC4xMywxOC44NiAyMS41NSwxOS4xMUwyMS41NSwxMy4xOUwyMS4xNCwxMy4xOUwyMi4yLDEwLjg3TDIzLjIyLDEzLjJMMjIuODEsMTMuMkwyMi44MSwxOS4zNUMyNC4zMiwxOS42NiAyNS42NiwyMC4wNCAyNi44MiwyMC40MkwyNi44MiwxNS4yN0wyNi40MSwxNS4yN0wyNy40NywxMi45NUwyOC40OSwxNS4yOEwyOC4wOCwxNS4yOEwyOC4wOCwyMC44NkMyOS40NSwyMS4zNyAzMC40OCwyMS44NSAzMS4xMSwyMi4xN0MzMS45LDE0LjcyIDMwLjI4LDguMjYgMzAuMjgsOC4yNkMyMi43MSw4LjAxIDE2LDQgMTYsNEwxNiw4LjU5WiIgc3R5bGU9ImZpbGw6cmdiKDMzLDE0MSwxOTMpO2ZpbGwtcnVsZTpub256ZXJvOyIvPgogICAgICAgIDxwYXRoIGQ9Ik0yOC4wNCwyMi4xOEwyOC4wNCwyOEwyOS44MSwyOEMzMC4wNiwyNy4xOSAzMC4yNywyNi4zNiAzMC40NSwyNS41NUMzMC42OCwyNC43NCAzMC44MSwyNC4wNyAzMC45LDIzLjU5QzMwLjksMjMuNTkgMzAuOSwyMy41NyAzMC45LDIzLjU2QzMwLjkxLDIzLjUzIDMwLjkxLDIzLjUgMzAuOTEsMjMuNDdDMzAuNDcsMjMuMjMgMjkuNDksMjIuNzMgMjguMDQsMjIuMThaIiBzdHlsZT0iZmlsbDpyZ2IoMzMsMTQxLDE5Myk7ZmlsbC1ydWxlOm5vbnplcm87Ii8+CiAgICAgICAgPHBhdGggZD0iTTIyLjc5LDIwLjYxTDIyLjc5LDI4TDI2LjgsMjhMMjYuOCwyMS43MkMyNS42NiwyMS4zMyAyNC4zMSwyMC45NCAyMi43OSwyMC42MVoiIHN0eWxlPSJmaWxsOnJnYigzMywxNDEsMTkzKTtmaWxsLXJ1bGU6bm9uemVybzsiLz4KICAgICAgICA8cGF0aCBkPSJNMjEuNTQsMjhMMjEuNTQsMjAuMzZDMjAuMTYsMjAuMTEgMTguNjQsMTkuOTEgMTcuMDIsMTkuODFDMTcuMDIsMTkuODIgMTcuMDIsMTkuODIgMTcuMDIsMTkuODNDMTcuMDIsMTkuODYgMTcuMTMsMjAuNjggMTcuMTQsMjAuODFDMTcuMzksMjIuNyAxNy45LDI1LjY3IDE4LjQzLDI3Ljk5TDE4LjQsMjcuOTlMMjEuNTQsMjcuOTlMMjEuNTQsMjhaIiBzdHlsZT0iZmlsbDpyZ2IoMzMsMTQxLDE5Myk7ZmlsbC1ydWxlOm5vbnplcm87Ii8+CiAgICAgICAgPHBhdGggZD0iTTEzLjU3LDI4QzE0LjA5LDI1LjY4IDE0LjYsMjIuNzEgMTQuODYsMjAuODJMMTQuODUsMjAuODJMMTQuODYsMjAuODJDMTQuODgsMjAuNjggMTQuOTgsMTkuODcgMTQuOTgsMTkuODRDMTQuOTgsMTkuODMgMTQuOTgsMTkuODMgMTQuOTgsMTkuODJDMTMuMzUsMTkuOTIgMTEuODMsMjAuMTIgMTAuNDYsMjAuMzdMMTAuNDYsMjhMMTMuNTksMjhMMTMuNTcsMjhaIiBzdHlsZT0iZmlsbDpyZ2IoMzMsMTQxLDE5Myk7ZmlsbC1ydWxlOm5vbnplcm87Ii8+CiAgICAgICAgPHBhdGggZD0iTTMuOTYsMjAuODZMMy45NiwxNS4yOEwzLjU1LDE1LjI4TDQuNTcsMTIuOTVMNS42MywxNS4yN0w1LjIyLDE1LjI3TDUuMjIsMjAuNDJDNi4zOCwyMC4wNCA3LjcyLDE5LjY3IDkuMjMsMTkuMzVMOS4yMywxMy4yTDguODIsMTMuMkw5Ljg0LDEwLjg3TDEwLjksMTMuMTlMMTAuNDksMTMuMTlMMTAuNDksMTkuMTFDMTEuOTEsMTguODYgMTMuNDUsMTguNjcgMTUuMTEsMTguNThDMTUuMjEsMTcuMjEgMTUuMjYsMTUuOTYgMTUuMjYsMTUuMTFDMTUuMjcsMTMuNyAxNS4yLDEyLjkzIDE1LjIsMTIuOTNDMTUuMTUsMTIuODcgMTUuMTEsMTIuODEgMTUuMDgsMTIuNzVDMTUsMTIuNjEgMTQuOTYsMTIuNDUgMTQuOTYsMTIuMjhDMTQuOTYsMTIuMjMgMTQuOTcsMTIuMTcgMTQuOTgsMTIuMTJMMTQuOTgsMTIuMTFMMTQuNDQsMTIuMTFMMTQuNDUsMTIuMDhMMTYsOC41OUwxNiw0QzE2LDQgOS4yOSw4LjAxIDEuNzUsOC4yNkMxLjc1LDguMjYgMC4xMywxNC43MiAwLjkyLDIyLjE3QzEuNTYsMjEuODUgMi41OCwyMS4zNiAzLjk2LDIwLjg2WiIgc3R5bGU9ImZpbGw6cmdiKDMzLDE0MSwxOTMpO2ZpbGwtcnVsZTpub256ZXJvOyIvPgogICAgICAgIDxwYXRoIGQ9Ik01LjIsMjEuNzJMNS4yLDI4TDkuMjEsMjhMOS4yMSwyMC42MUM3LjY0LDIwLjk1IDYuMjgsMjEuMzUgNS4yLDIxLjcyWiIgc3R5bGU9ImZpbGw6cmdiKDMzLDE0MSwxOTMpO2ZpbGwtcnVsZTpub256ZXJvOyIvPgogICAgICAgIDxwYXRoIGQ9Ik0xLjA5LDIzLjQ3QzEuMDgsMjMuNDcgMS4wOCwyMy40NyAxLjA5LDIzLjQ3QzEuMDksMjMuNSAxLjEsMjMuNTMgMS4xLDIzLjU2QzEuMSwyMy41NyAxLjEsMjMuNTggMS4xLDIzLjU5QzEuMTgsMjQuMDcgMS4zMiwyNC43NCAxLjU1LDI1LjU1QzEuNzMsMjYuMzYgMS45NSwyNy4xOSAyLjE5LDI4TDMuOTUsMjhMMy45NSwyMi4xN0MyLjUxLDIyLjczIDEuNTMsMjMuMjMgMS4wOSwyMy40N1oiIHN0eWxlPSJmaWxsOnJnYigzMywxNDEsMTkzKTtmaWxsLXJ1bGU6bm9uemVybzsiLz4KICAgIDwvZz4KPC9zdmc+Cg==");-webkit-mask-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+Cjxzdmcgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDMyIDMyIiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zOnNlcmlmPSJodHRwOi8vd3d3LnNlcmlmLmNvbS8iIHN0eWxlPSJmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MS40MTQyMTsiPgogICAgPGc+CiAgICAgICAgPHBhdGggZD0iTTE2LDguNTlMMTcuNTksMTIuMDhMMTcuNiwxMi4xMUwxNy4wNiwxMi4xMUwxNy4wNiwxMi4xMkMxNy4wNywxMi4xNyAxNy4wOCwxMi4yMiAxNy4wOCwxMi4yOEMxNy4wOCwxMi40NSAxNy4wNCwxMi42MSAxNi45NiwxMi43NUMxNi45MywxMi44MiAxNi44OCwxMi44OCAxNi44NCwxMi45M0MxNi44NCwxMi45MyAxNi43NywxMy42OSAxNi43OCwxNS4xMUMxNi43OCwxNS45NiAxNi44MywxNy4yMSAxNi45MywxOC41OEMxOC41OSwxOC42NyAyMC4xMywxOC44NiAyMS41NSwxOS4xMUwyMS41NSwxMy4xOUwyMS4xNCwxMy4xOUwyMi4yLDEwLjg3TDIzLjIyLDEzLjJMMjIuODEsMTMuMkwyMi44MSwxOS4zNUMyNC4zMiwxOS42NiAyNS42NiwyMC4wNCAyNi44MiwyMC40MkwyNi44MiwxNS4yN0wyNi40MSwxNS4yN0wyNy40NywxMi45NUwyOC40OSwxNS4yOEwyOC4wOCwxNS4yOEwyOC4wOCwyMC44NkMyOS40NSwyMS4zNyAzMC40OCwyMS44NSAzMS4xMSwyMi4xN0MzMS45LDE0LjcyIDMwLjI4LDguMjYgMzAuMjgsOC4yNkMyMi43MSw4LjAxIDE2LDQgMTYsNEwxNiw4LjU5WiIgc3R5bGU9ImZpbGw6cmdiKDMzLDE0MSwxOTMpO2ZpbGwtcnVsZTpub256ZXJvOyIvPgogICAgICAgIDxwYXRoIGQ9Ik0yOC4wNCwyMi4xOEwyOC4wNCwyOEwyOS44MSwyOEMzMC4wNiwyNy4xOSAzMC4yNywyNi4zNiAzMC40NSwyNS41NUMzMC42OCwyNC43NCAzMC44MSwyNC4wNyAzMC45LDIzLjU5QzMwLjksMjMuNTkgMzAuOSwyMy41NyAzMC45LDIzLjU2QzMwLjkxLDIzLjUzIDMwLjkxLDIzLjUgMzAuOTEsMjMuNDdDMzAuNDcsMjMuMjMgMjkuNDksMjIuNzMgMjguMDQsMjIuMThaIiBzdHlsZT0iZmlsbDpyZ2IoMzMsMTQxLDE5Myk7ZmlsbC1ydWxlOm5vbnplcm87Ii8+CiAgICAgICAgPHBhdGggZD0iTTIyLjc5LDIwLjYxTDIyLjc5LDI4TDI2LjgsMjhMMjYuOCwyMS43MkMyNS42NiwyMS4zMyAyNC4zMSwyMC45NCAyMi43OSwyMC42MVoiIHN0eWxlPSJmaWxsOnJnYigzMywxNDEsMTkzKTtmaWxsLXJ1bGU6bm9uemVybzsiLz4KICAgICAgICA8cGF0aCBkPSJNMjEuNTQsMjhMMjEuNTQsMjAuMzZDMjAuMTYsMjAuMTEgMTguNjQsMTkuOTEgMTcuMDIsMTkuODFDMTcuMDIsMTkuODIgMTcuMDIsMTkuODIgMTcuMDIsMTkuODNDMTcuMDIsMTkuODYgMTcuMTMsMjAuNjggMTcuMTQsMjAuODFDMTcuMzksMjIuNyAxNy45LDI1LjY3IDE4LjQzLDI3Ljk5TDE4LjQsMjcuOTlMMjEuNTQsMjcuOTlMMjEuNTQsMjhaIiBzdHlsZT0iZmlsbDpyZ2IoMzMsMTQxLDE5Myk7ZmlsbC1ydWxlOm5vbnplcm87Ii8+CiAgICAgICAgPHBhdGggZD0iTTEzLjU3LDI4QzE0LjA5LDI1LjY4IDE0LjYsMjIuNzEgMTQuODYsMjAuODJMMTQuODUsMjAuODJMMTQuODYsMjAuODJDMTQuODgsMjAuNjggMTQuOTgsMTkuODcgMTQuOTgsMTkuODRDMTQuOTgsMTkuODMgMTQuOTgsMTkuODMgMTQuOTgsMTkuODJDMTMuMzUsMTkuOTIgMTEuODMsMjAuMTIgMTAuNDYsMjAuMzdMMTAuNDYsMjhMMTMuNTksMjhMMTMuNTcsMjhaIiBzdHlsZT0iZmlsbDpyZ2IoMzMsMTQxLDE5Myk7ZmlsbC1ydWxlOm5vbnplcm87Ii8+CiAgICAgICAgPHBhdGggZD0iTTMuOTYsMjAuODZMMy45NiwxNS4yOEwzLjU1LDE1LjI4TDQuNTcsMTIuOTVMNS42MywxNS4yN0w1LjIyLDE1LjI3TDUuMjIsMjAuNDJDNi4zOCwyMC4wNCA3LjcyLDE5LjY3IDkuMjMsMTkuMzVMOS4yMywxMy4yTDguODIsMTMuMkw5Ljg0LDEwLjg3TDEwLjksMTMuMTlMMTAuNDksMTMuMTlMMTAuNDksMTkuMTFDMTEuOTEsMTguODYgMTMuNDUsMTguNjcgMTUuMTEsMTguNThDMTUuMjEsMTcuMjEgMTUuMjYsMTUuOTYgMTUuMjYsMTUuMTFDMTUuMjcsMTMuNyAxNS4yLDEyLjkzIDE1LjIsMTIuOTNDMTUuMTUsMTIuODcgMTUuMTEsMTIuODEgMTUuMDgsMTIuNzVDMTUsMTIuNjEgMTQuOTYsMTIuNDUgMTQuOTYsMTIuMjhDMTQuOTYsMTIuMjMgMTQuOTcsMTIuMTcgMTQuOTgsMTIuMTJMMTQuOTgsMTIuMTFMMTQuNDQsMTIuMTFMMTQuNDUsMTIuMDhMMTYsOC41OUwxNiw0QzE2LDQgOS4yOSw4LjAxIDEuNzUsOC4yNkMxLjc1LDguMjYgMC4xMywxNC43MiAwLjkyLDIyLjE3QzEuNTYsMjEuODUgMi41OCwyMS4zNiAzLjk2LDIwLjg2WiIgc3R5bGU9ImZpbGw6cmdiKDMzLDE0MSwxOTMpO2ZpbGwtcnVsZTpub256ZXJvOyIvPgogICAgICAgIDxwYXRoIGQ9Ik01LjIsMjEuNzJMNS4yLDI4TDkuMjEsMjhMOS4yMSwyMC42MUM3LjY0LDIwLjk1IDYuMjgsMjEuMzUgNS4yLDIxLjcyWiIgc3R5bGU9ImZpbGw6cmdiKDMzLDE0MSwxOTMpO2ZpbGwtcnVsZTpub256ZXJvOyIvPgogICAgICAgIDxwYXRoIGQ9Ik0xLjA5LDIzLjQ3QzEuMDgsMjMuNDcgMS4wOCwyMy40NyAxLjA5LDIzLjQ3QzEuMDksMjMuNSAxLjEsMjMuNTMgMS4xLDIzLjU2QzEuMSwyMy41NyAxLjEsMjMuNTggMS4xLDIzLjU5QzEuMTgsMjQuMDcgMS4zMiwyNC43NCAxLjU1LDI1LjU1QzEuNzMsMjYuMzYgMS45NSwyNy4xOSAyLjE5LDI4TDMuOTUsMjhMMy45NSwyMi4xN0MyLjUxLDIyLjczIDEuNTMsMjMuMjMgMS4wOSwyMy40N1oiIHN0eWxlPSJmaWxsOnJnYigzMywxNDEsMTkzKTtmaWxsLXJ1bGU6bm9uemVybzsiLz4KICAgIDwvZz4KPC9zdmc+Cg==");mask-position:center;-webkit-mask-position:center;mask-repeat:no-repeat;-webkit-mask-repeat:no-repeat} login-security/css/admin.1731600600.css000064400000522337147207020750013201 0ustar00.wfls-clearfix:before,.wfls-clearfix:after{content:" ";display:table}.wfls-clearfix:after{clear:both}.wfls-btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;text-transform:uppercase;padding:.4rem 1rem;font-size:.875rem;line-height:1.3125rem;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media (min-width: 768px){.wfls-btn{padding:.5rem 1.25rem;font-size:.875rem;line-height:1.3125rem;border-radius:4px}}.wfls-btn:focus,.wfls-btn.wfls-focus,.wfls-btn:active:focus,.wfls-btn:active.wfls-focus,.wfls-btn.wfls-active:focus,.wfls-btn.wfls-active.wfls-focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.wfls-btn:hover,.wfls-btn:focus,.wfls-btn.wfls-focus{color:#00709e;text-decoration:none !important}.wfls-btn:active,.wfls-btn.wfls-active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.wfls-btn.wfls-disabled,.wfls-btn[disabled],.wfls-btn[readonly],fieldset[disabled] .wfls-btn{cursor:not-allowed;-webkit-box-shadow:none;box-shadow:none}a.wfls-btn{text-decoration:none}a.wfls-btn.wfls-disabled,fieldset[disabled] a.wfls-btn{cursor:not-allowed;pointer-events:none}.wfls-btn-default{color:#00709e;background-color:#fff;border-color:#00709e}.wfls-btn-default:focus,.wfls-btn-default.focus{color:#00709e;background-color:#e6e6e6;border-color:#00161f}.wfls-btn-default:hover{color:#00709e;background-color:#e6e6e6;border-color:#004561}.wfls-btn-default:active,.wfls-btn-default.active,.wfls-open>.wfls-btn-default.wfls-dropdown-toggle{color:#00709e;background-color:#e6e6e6;border-color:#004561}.wfls-btn-default:active:hover,.wfls-btn-default:active:focus,.wfls-btn-default:active.focus,.wfls-btn-default.active:hover,.wfls-btn-default.active:focus,.wfls-btn-default.active.focus,.wfls-open>.wfls-btn-default.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-default.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-default.wfls-dropdown-toggle.focus{color:#00709e;background-color:#d4d4d4;border-color:#00161f}.wfls-btn-default:active,.wfls-btn-default.wfls-active,.wfls-open>.wfls-btn-default.wfls-dropdown-toggle{background-image:none}.wfls-btn-default.wfls-disabled,.wfls-btn-default[disabled],.wfls-btn-default[readonly],fieldset[disabled] .wfls-btn-default{color:#777;background-color:#fff;border-color:#e2e2e2;cursor:not-allowed;opacity:0.75}.wfls-btn-default.wfls-disabled:hover,.wfls-btn-default.wfls-disabled:focus,.wfls-btn-default.wfls-disabled.wfls-focus,.wfls-btn-default[disabled]:hover,.wfls-btn-default[disabled]:focus,.wfls-btn-default[disabled].wfls-focus,.wfls-btn-default[readonly]:hover,.wfls-btn-default[readonly]:focus,.wfls-btn-default[readonly].wfls-focus,fieldset[disabled] .wfls-btn-default:hover,fieldset[disabled] .wfls-btn-default:focus,fieldset[disabled] .wfls-btn-default.wfls-focus{background-color:#fff;border-color:#00709e}.wfls-btn-default .wfls-badge{color:#fff;background-color:#00709e}.wfls-btn-primary{color:#fff;background-color:#00709e;border-color:#005e85}.wfls-btn-primary:focus,.wfls-btn-primary.focus{color:#fff;background-color:#004c6b;border-color:#000405}.wfls-btn-primary:hover{color:#fff;background-color:#004c6b;border-color:#003347}.wfls-btn-primary:active,.wfls-btn-primary.active,.wfls-open>.wfls-btn-primary.wfls-dropdown-toggle{color:#fff;background-color:#004c6b;border-color:#003347}.wfls-btn-primary:active:hover,.wfls-btn-primary:active:focus,.wfls-btn-primary:active.focus,.wfls-btn-primary.active:hover,.wfls-btn-primary.active:focus,.wfls-btn-primary.active.focus,.wfls-open>.wfls-btn-primary.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-primary.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-primary.wfls-dropdown-toggle.focus{color:#fff;background-color:#003347;border-color:#000405}.wfls-btn-primary:active,.wfls-btn-primary.wfls-active,.wfls-open>.wfls-btn-primary.wfls-dropdown-toggle{background-image:none}.wfls-btn-primary.wfls-disabled,.wfls-btn-primary[disabled],.wfls-btn-primary[readonly],fieldset[disabled] .wfls-btn-primary{color:#fff;background-color:#59a2c0;border-color:#5996b0;cursor:not-allowed;opacity:0.75}.wfls-btn-primary.wfls-disabled:hover,.wfls-btn-primary.wfls-disabled:focus,.wfls-btn-primary.wfls-disabled.wfls-focus,.wfls-btn-primary[disabled]:hover,.wfls-btn-primary[disabled]:focus,.wfls-btn-primary[disabled].wfls-focus,.wfls-btn-primary[readonly]:hover,.wfls-btn-primary[readonly]:focus,.wfls-btn-primary[readonly].wfls-focus,fieldset[disabled] .wfls-btn-primary:hover,fieldset[disabled] .wfls-btn-primary:focus,fieldset[disabled] .wfls-btn-primary.wfls-focus{background-color:#00709e;border-color:#005e85}.wfls-btn-primary .wfls-badge{color:#00709e;background-color:#fff}.wfls-btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.wfls-btn-success:focus,.wfls-btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.wfls-btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.wfls-btn-success:active,.wfls-btn-success.active,.wfls-open>.wfls-btn-success.wfls-dropdown-toggle{color:#fff;background-color:#449d44;border-color:#398439}.wfls-btn-success:active:hover,.wfls-btn-success:active:focus,.wfls-btn-success:active.focus,.wfls-btn-success.active:hover,.wfls-btn-success.active:focus,.wfls-btn-success.active.focus,.wfls-open>.wfls-btn-success.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-success.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-success.wfls-dropdown-toggle.focus{color:#fff;background-color:#398439;border-color:#255625}.wfls-btn-success:active,.wfls-btn-success.wfls-active,.wfls-open>.wfls-btn-success.wfls-dropdown-toggle{background-image:none}.wfls-btn-success.wfls-disabled,.wfls-btn-success[disabled],.wfls-btn-success[readonly],fieldset[disabled] .wfls-btn-success{color:#fff;background-color:#95d195;border-color:#8bca8b;cursor:not-allowed;opacity:0.75}.wfls-btn-success.wfls-disabled:hover,.wfls-btn-success.wfls-disabled:focus,.wfls-btn-success.wfls-disabled.wfls-focus,.wfls-btn-success[disabled]:hover,.wfls-btn-success[disabled]:focus,.wfls-btn-success[disabled].wfls-focus,.wfls-btn-success[readonly]:hover,.wfls-btn-success[readonly]:focus,.wfls-btn-success[readonly].wfls-focus,fieldset[disabled] .wfls-btn-success:hover,fieldset[disabled] .wfls-btn-success:focus,fieldset[disabled] .wfls-btn-success.wfls-focus{background-color:#5cb85c;border-color:#4cae4c}.wfls-btn-success .wfls-badge{color:#5cb85c;background-color:#fff}.wfls-btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.wfls-btn-info:focus,.wfls-btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.wfls-btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.wfls-btn-info:active,.wfls-btn-info.active,.wfls-open>.wfls-btn-info.wfls-dropdown-toggle{color:#fff;background-color:#31b0d5;border-color:#269abc}.wfls-btn-info:active:hover,.wfls-btn-info:active:focus,.wfls-btn-info:active.focus,.wfls-btn-info.active:hover,.wfls-btn-info.active:focus,.wfls-btn-info.active.focus,.wfls-open>.wfls-btn-info.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-info.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-info.wfls-dropdown-toggle.focus{color:#fff;background-color:#269abc;border-color:#1b6d85}.wfls-btn-info:active,.wfls-btn-info.wfls-active,.wfls-open>.wfls-btn-info.wfls-dropdown-toggle{background-image:none}.wfls-btn-info.wfls-disabled,.wfls-btn-info[disabled],.wfls-btn-info[readonly],fieldset[disabled] .wfls-btn-info{color:#fff;background-color:#94d6ea;border-color:#87d1e7;cursor:not-allowed;opacity:0.75}.wfls-btn-info.wfls-disabled:hover,.wfls-btn-info.wfls-disabled:focus,.wfls-btn-info.wfls-disabled.wfls-focus,.wfls-btn-info[disabled]:hover,.wfls-btn-info[disabled]:focus,.wfls-btn-info[disabled].wfls-focus,.wfls-btn-info[readonly]:hover,.wfls-btn-info[readonly]:focus,.wfls-btn-info[readonly].wfls-focus,fieldset[disabled] .wfls-btn-info:hover,fieldset[disabled] .wfls-btn-info:focus,fieldset[disabled] .wfls-btn-info.wfls-focus{background-color:#5bc0de;border-color:#46b8da}.wfls-btn-info .wfls-badge{color:#5bc0de;background-color:#fff}.wfls-btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.wfls-btn-warning:focus,.wfls-btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.wfls-btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.wfls-btn-warning:active,.wfls-btn-warning.active,.wfls-open>.wfls-btn-warning.wfls-dropdown-toggle{color:#fff;background-color:#ec971f;border-color:#d58512}.wfls-btn-warning:active:hover,.wfls-btn-warning:active:focus,.wfls-btn-warning:active.focus,.wfls-btn-warning.active:hover,.wfls-btn-warning.active:focus,.wfls-btn-warning.active.focus,.wfls-open>.wfls-btn-warning.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-warning.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-warning.wfls-dropdown-toggle.focus{color:#fff;background-color:#d58512;border-color:#985f0d}.wfls-btn-warning:active,.wfls-btn-warning.wfls-active,.wfls-open>.wfls-btn-warning.wfls-dropdown-toggle{background-image:none}.wfls-btn-warning.wfls-disabled,.wfls-btn-warning[disabled],.wfls-btn-warning[readonly],fieldset[disabled] .wfls-btn-warning{color:#fff;background-color:#f5ca8c;border-color:#f4c37c;cursor:not-allowed;opacity:0.75}.wfls-btn-warning.wfls-disabled:hover,.wfls-btn-warning.wfls-disabled:focus,.wfls-btn-warning.wfls-disabled.wfls-focus,.wfls-btn-warning[disabled]:hover,.wfls-btn-warning[disabled]:focus,.wfls-btn-warning[disabled].wfls-focus,.wfls-btn-warning[readonly]:hover,.wfls-btn-warning[readonly]:focus,.wfls-btn-warning[readonly].wfls-focus,fieldset[disabled] .wfls-btn-warning:hover,fieldset[disabled] .wfls-btn-warning:focus,fieldset[disabled] .wfls-btn-warning.wfls-focus{background-color:#f0ad4e;border-color:#eea236}.wfls-btn-warning .wfls-badge{color:#f0ad4e;background-color:#fff}.wfls-btn-danger{color:#fff;background-color:#930000;border-color:#7a0000}.wfls-btn-danger:focus,.wfls-btn-danger.focus{color:#fff;background-color:#600000;border-color:#000}.wfls-btn-danger:hover{color:#fff;background-color:#600000;border-color:#3c0000}.wfls-btn-danger:active,.wfls-btn-danger.active,.wfls-open>.wfls-btn-danger.wfls-dropdown-toggle{color:#fff;background-color:#600000;border-color:#3c0000}.wfls-btn-danger:active:hover,.wfls-btn-danger:active:focus,.wfls-btn-danger:active.focus,.wfls-btn-danger.active:hover,.wfls-btn-danger.active:focus,.wfls-btn-danger.active.focus,.wfls-open>.wfls-btn-danger.wfls-dropdown-toggle:hover,.wfls-open>.wfls-btn-danger.wfls-dropdown-toggle:focus,.wfls-open>.wfls-btn-danger.wfls-dropdown-toggle.focus{color:#fff;background-color:#3c0000;border-color:#000}.wfls-btn-danger:active,.wfls-btn-danger.wfls-active,.wfls-open>.wfls-btn-danger.wfls-dropdown-toggle{background-image:none}.wfls-btn-danger.wfls-disabled,.wfls-btn-danger[disabled],.wfls-btn-danger[readonly],fieldset[disabled] .wfls-btn-danger{color:#fff;background-color:#b95959;border-color:#a95959;cursor:not-allowed;opacity:0.75}.wfls-btn-danger.wfls-disabled:hover,.wfls-btn-danger.wfls-disabled:focus,.wfls-btn-danger.wfls-disabled.wfls-focus,.wfls-btn-danger[disabled]:hover,.wfls-btn-danger[disabled]:focus,.wfls-btn-danger[disabled].wfls-focus,.wfls-btn-danger[readonly]:hover,.wfls-btn-danger[readonly]:focus,.wfls-btn-danger[readonly].wfls-focus,fieldset[disabled] .wfls-btn-danger:hover,fieldset[disabled] .wfls-btn-danger:focus,fieldset[disabled] .wfls-btn-danger.wfls-focus{background-color:#930000;border-color:#7a0000}.wfls-btn-danger .wfls-badge{color:#930000;background-color:#fff}.wfls-btn-callout{font-weight:600;text-transform:uppercase}.wfls-btn-callout-subtle{font-weight:400;text-transform:uppercase}.wfls-btn-link{color:#00709e;font-weight:normal;border-radius:0}.wfls-btn-link,.wfls-btn-link:active,.wfls-btn-link.wfls-active,.wfls-btn-link[disabled],fieldset[disabled] .wfls-btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.wfls-btn-link,.wfls-btn-link:hover,.wfls-btn-link:focus,.wfls-btn-link:active{border-color:transparent}.wfls-btn-link:hover,.wfls-btn-link:focus{color:#003a52;text-decoration:underline;background-color:transparent}.wfls-btn-link[disabled]:hover,.wfls-btn-link[disabled]:focus,fieldset[disabled] .wfls-btn-link:hover,fieldset[disabled] .wfls-btn-link:focus{color:#777;text-decoration:none}.wfls-btn-lg,.wfls-btn-group-lg>.wfls-btn{padding:10px 16px;font-size:18px;line-height:1.33333;border-radius:6px}.wfls-btn-sm,.wfls-btn-group-sm>.wfls-btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.wfls-btn-xs,.wfls-btn-group-xs>.wfls-btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.wfls-btn-block{display:block;width:100%}.wfls-btn-block+.wfls-btn-block{margin-top:5px}input[type="submit"].wfls-btn-block,input[type="reset"].wfls-btn-block,input[type="button"].wfls-btn-block{width:100%}.wfls-btn-group,.wfls-btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.wfls-btn-group>.wfls-btn,.wfls-btn-group-vertical>.wfls-btn{position:relative;float:left}.wfls-btn-group>.wfls-btn:hover,.wfls-btn-group>.wfls-btn:focus,.wfls-btn-group>.wfls-btn:active,.wfls-btn-group>.wfls-btn.wfls-active,.wfls-btn-group-vertical>.wfls-btn:hover,.wfls-btn-group-vertical>.wfls-btn:focus,.wfls-btn-group-vertical>.wfls-btn:active,.wfls-btn-group-vertical>.wfls-btn.wfls-active{z-index:2}.wfls-btn-group .wfls-btn+.wfls-btn,.wfls-btn-group .wfls-btn+.wfls-btn-group,.wfls-btn-group .wfls-btn-group+.wfls-btn,.wfls-btn-group .wfls-btn-group+.wfls-btn-group{margin-left:-1px}.wfls-btn-toolbar{margin-left:-5px}.wfls-btn-toolbar:before,.wfls-btn-toolbar:after{content:" ";display:table}.wfls-btn-toolbar:after{clear:both}.wfls-btn-toolbar .wfls-btn,.wfls-btn-toolbar .wfls-btn-group,.wfls-btn-toolbar .wfls-input-group{float:left}.wfls-btn-toolbar>.wfls-btn,.wfls-btn-toolbar>.wfls-btn-group,.wfls-btn-toolbar>.wfls-input-group{margin-left:5px}.wfls-btn-group>.wfls-btn:not(:first-child):not(:last-child):not(.wfls-dropdown-toggle){border-radius:0}.wfls-btn-group>.wfls-btn:first-child{margin-left:0}.wfls-btn-group>.wfls-btn:first-child:not(:last-child):not(.wfls-dropdown-toggle){-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wfls-btn-group>.wfls-btn:last-child:not(:first-child),.wfls-btn-group>.wfls-dropdown-toggle:not(:first-child){-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0}.wfls-btn-group>.wfls-btn-group{float:left}.wfls-btn-group>.wfls-btn-group:not(:first-child):not(:last-child)>.wfls-btn{border-radius:0}.wfls-btn-group>.wfls-btn-group:first-child:not(:last-child)>.wfls-btn:last-child,.wfls-btn-group>.wfls-btn-group:first-child:not(:last-child)>.wfls-dropdown-toggle{-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wfls-btn-group>.wfls-btn-group:last-child:not(:first-child)>.wfls-btn:first-child{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0}.wfls-btn-group .wfls-dropdown-toggle:active,.wfls-btn-group.wfls-open .wfls-dropdown-toggle{outline:0}.wfls-btn-group>.wfls-btn+.wfls-dropdown-toggle{padding-left:8px;padding-right:8px}.wfls-btn-group>.wfls-btn-lg+.wfls-dropdown-toggle,.wfls-btn-group-lg.wfls-btn-group>.wfls-btn+.wfls-dropdown-toggle{padding-left:12px;padding-right:12px}.wfls-btn-group.open .wfls-dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.wfls-btn-group.open .wfls-dropdown-toggle.wfls-btn-link{-webkit-box-shadow:none;box-shadow:none}.wfls-btn .wfls-caret{margin-left:0}.wfls-btn-lg .wfls-caret,.wfls-btn-group-lg>.wfls-btn .wfls-caret{border-width:5px 5px 0;border-bottom-width:0}.wfls-dropup .wfls-btn-lg .wfls-caret,.wfls-dropup .wfls-btn-group-lg>.wfls-btn .wfls-caret{border-width:0 5px 5px}.wfls-btn-group-vertical>.wfls-btn,.wfls-btn-group-vertical>.wfls-btn-group,.wfls-btn-group-vertical>.wfls-btn-group>.wfls-btn{display:block;float:none;width:100%;max-width:100%}.wfls-btn-group-vertical>.wfls-btn-group:before,.wfls-btn-group-vertical>.wfls-btn-group:after{content:" ";display:table}.wfls-btn-group-vertical>.wfls-btn-group:after{clear:both}.wfls-btn-group-vertical>.wfls-btn-group>.wfls-btn{float:none}.wfls-btn-group-vertical>.wfls-btn+.wfls-btn,.wfls-btn-group-vertical>.wfls-btn+.wfls-btn-group,.wfls-btn-group-vertical>.wfls-btn-group+.wfls-btn,.wfls-btn-group-vertical>.wfls-btn-group+.wfls-btn-group{margin-top:-1px;margin-left:0}.wfls-btn-group-vertical>.wfls-btn:not(:first-child):not(:last-child){border-radius:0}.wfls-btn-group-vertical>.wfls-btn:first-child:not(:last-child){-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wfls-btn-group-vertical>.wfls-btn:last-child:not(:first-child){-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.wfls-btn-group-vertical>.wfls-btn-group:not(:first-child):not(:last-child)>.wfls-btn{border-radius:0}.wfls-btn-group-vertical>.wfls-btn-group:first-child:not(:last-child)>.wfls-btn:last-child,.wfls-btn-group-vertical>.wfls-btn-group:first-child:not(:last-child)>.wfls-dropdown-toggle{-moz-border-radius-bottomleft:0;-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomright:0;-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}.wfls-btn-group-vertical>.wfls-btn-group:last-child:not(:first-child)>.wfls-btn:first-child{-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0}.wfls-btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.wfls-btn-group-justified>.wfls-btn,.wfls-btn-group-justified>.wfls-btn-group{float:none;display:table-cell;width:1%}.wfls-btn-group-justified>.wfls-btn-group .wfls-btn{width:100%}.wfls-btn-group-justified>.wfls-btn-group .wfls-dropdown-menu{left:auto}[data-toggle="buttons"]>.wfls-btn input[type="radio"],[data-toggle="buttons"]>.wfls-btn input[type="checkbox"],[data-toggle="buttons"]>.wfls-btn-group>.wfls-btn input[type="radio"],[data-toggle="buttons"]>.wfls-btn-group>.wfls-btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.wfls-pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.wfls-pagination>li{display:inline}.wfls-pagination>li>a,.wfls-pagination>li>span{position:relative;float:left;padding:.5rem 1.25rem;line-height:1.42857;text-decoration:none;color:#00709e;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.wfls-pagination>li:first-child>a,.wfls-pagination>li:first-child>span{margin-left:0;-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px}.wfls-pagination>li:last-child>a,.wfls-pagination>li:last-child>span{-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.wfls-pagination>li>a:hover,.wfls-pagination>li>a:focus,.wfls-pagination>li>span:hover,.wfls-pagination>li>span:focus{z-index:2;color:#003a52;background-color:#e2e2e2;border-color:#ddd}.wfls-pagination>.wfls-active>a,.wfls-pagination>.wfls-active>a:hover,.wfls-pagination>.wfls-active>a:focus,.wfls-pagination>.wfls-active>span,.wfls-pagination>.wfls-active>span:hover,.wfls-pagination>.wfls-active>span:focus{z-index:3;color:#fff;background-color:#00709e;border-color:#00709e;cursor:default}.wfls-pagination>.wfls-disabled>span,.wfls-pagination>.wfls-disabled>span:hover,.wfls-pagination>.wfls-disabled>span:focus,.wfls-pagination>.wfls-disabled>a,.wfls-pagination>.wfls-disabled>a:hover,.wfls-pagination>.wfls-disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.wfls-pagination-lg>li>a,.wfls-pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.33333}.wfls-pagination-lg>li:first-child>a,.wfls-pagination-lg>li:first-child>span{-moz-border-radius-topleft:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px}.wfls-pagination-lg>li:last-child>a,.wfls-pagination-lg>li:last-child>span{-moz-border-radius-topright:6px;-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-moz-border-radius-bottomright:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px}.wfls-pagination-sm>li>a,.wfls-pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.wfls-pagination-sm>li:first-child>a,.wfls-pagination-sm>li:first-child>span{-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px}.wfls-pagination-sm>li:last-child>a,.wfls-pagination-sm>li:last-child>span{-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-moz-border-radius-bottomright:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px}@-ms-viewport{width:device-width}.wfls-visible-xs{display:none !important}.wfls-visible-sm{display:none !important}.wfls-visible-md{display:none !important}.wfls-visible-lg{display:none !important}.wfls-visible-xs-block,.wfls-visible-xs-inline,.wfls-visible-xs-inline-block,.wfls-visible-sm-block,.wfls-visible-sm-inline,.wfls-visible-sm-inline-block,.wfls-visible-md-block,.wfls-visible-md-inline,.wfls-visible-md-inline-block,.wfls-visible-lg-block,.wfls-visible-lg-inline,.wfls-visible-lg-inline-block{display:none !important}@media (max-width: 767px){.wfls-visible-xs{display:block !important}table.wfls-visible-xs{display:table !important}tr.wfls-visible-xs{display:table-row !important}th.wfls-visible-xs,td.wfls-visible-xs{display:table-cell !important}}@media (max-width: 767px){.wfls-visible-xs-block{display:block !important}}@media (max-width: 767px){.wfls-visible-xs-inline{display:inline !important}}@media (max-width: 767px){.wfls-visible-xs-inline-block{display:inline-block !important}}@media (min-width: 768px) and (max-width: 991px){.wfls-visible-sm{display:block !important}table.wfls-visible-sm{display:table !important}tr.wfls-visible-sm{display:table-row !important}th.wfls-visible-sm,td.wfls-visible-sm{display:table-cell !important}}@media (min-width: 768px) and (max-width: 991px){.wfls-visible-sm-block{display:block !important}}@media (min-width: 768px) and (max-width: 991px){.wfls-visible-sm-inline{display:inline !important}}@media (min-width: 768px) and (max-width: 991px){.wfls-visible-sm-inline-block{display:inline-block !important}}@media (min-width: 992px) and (max-width: 1199px){.wfls-visible-md{display:block !important}table.wfls-visible-md{display:table !important}tr.wfls-visible-md{display:table-row !important}th.wfls-visible-md,td.wfls-visible-md{display:table-cell !important}}@media (min-width: 992px) and (max-width: 1199px){.wfls-visible-md-block{display:block !important}}@media (min-width: 992px) and (max-width: 1199px){.wfls-visible-md-inline{display:inline !important}}@media (min-width: 992px) and (max-width: 1199px){.wfls-visible-md-inline-block{display:inline-block !important}}@media (min-width: 1200px){.wfls-visible-lg{display:block !important}table.wfls-visible-lg{display:table !important}tr.wfls-visible-lg{display:table-row !important}th.wfls-visible-lg,td.wfls-visible-lg{display:table-cell !important}}@media (min-width: 1200px){.wfls-visible-lg-block{display:block !important}}@media (min-width: 1200px){.wfls-visible-lg-inline{display:inline !important}}@media (min-width: 1200px){.wfls-visible-lg-inline-block{display:inline-block !important}}@media (max-width: 767px){.wfls-hidden-xs{display:none !important}}@media (min-width: 768px) and (max-width: 991px){.wfls-hidden-sm{display:none !important}}@media (min-width: 992px) and (max-width: 1199px){.wfls-hidden-md{display:none !important}}@media (min-width: 1200px){.wfls-hidden-lg{display:none !important}}.wfls-visible-print{display:none !important}@media print{.wfls-visible-print{display:block !important}table.wfls-visible-print{display:table !important}tr.wfls-visible-print{display:table-row !important}th.wfls-visible-print,td.wfls-visible-print{display:table-cell !important}}.wfls-visible-print-block{display:none !important}@media print{.wfls-visible-print-block{display:block !important}}.wfls-visible-print-inline{display:none !important}@media print{.wfls-visible-print-inline{display:inline !important}}.wfls-visible-print-inline-block{display:none !important}@media print{.wfls-visible-print-inline-block{display:inline-block !important}}@media print{.wfls-hidden-print{display:none !important}}.wfls-container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.wfls-container:before,.wfls-container:after{content:" ";display:table}.wfls-container:after{clear:both}@media (min-width: 768px){.wfls-container{width:750px}}@media (min-width: 992px){.wfls-container{width:970px}}@media (min-width: 1200px){.wfls-container{width:1170px}}.wfls-container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.wfls-container-fluid:before,.wfls-container-fluid:after{content:" ";display:table}.wfls-container-fluid:after{clear:both}.wfls-row{margin-left:-15px;margin-right:-15px}.wfls-row:before,.wfls-row:after{content:" ";display:table}.wfls-row:after{clear:both}.wfls-col-xs-1,.wfls-col-sm-1,.wfls-col-md-1,.wfls-col-lg-1,.wfls-col-xs-2,.wfls-col-sm-2,.wfls-col-md-2,.wfls-col-lg-2,.wfls-col-xs-3,.wfls-col-sm-3,.wfls-col-md-3,.wfls-col-lg-3,.wfls-col-xs-4,.wfls-col-sm-4,.wfls-col-md-4,.wfls-col-lg-4,.wfls-col-xs-5,.wfls-col-sm-5,.wfls-col-md-5,.wfls-col-lg-5,.wfls-col-xs-6,.wfls-col-sm-6,.wfls-col-md-6,.wfls-col-lg-6,.wfls-col-xs-7,.wfls-col-sm-7,.wfls-col-md-7,.wfls-col-lg-7,.wfls-col-xs-8,.wfls-col-sm-8,.wfls-col-md-8,.wfls-col-lg-8,.wfls-col-xs-9,.wfls-col-sm-9,.wfls-col-md-9,.wfls-col-lg-9,.wfls-col-xs-10,.wfls-col-sm-10,.wfls-col-md-10,.wfls-col-lg-10,.wfls-col-xs-11,.wfls-col-sm-11,.wfls-col-md-11,.wfls-col-lg-11,.wfls-col-xs-12,.wfls-col-sm-12,.wfls-col-md-12,.wfls-col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px;box-sizing:border-box}.wfls-col-xs-1,.wfls-col-xs-2,.wfls-col-xs-3,.wfls-col-xs-4,.wfls-col-xs-5,.wfls-col-xs-6,.wfls-col-xs-7,.wfls-col-xs-8,.wfls-col-xs-9,.wfls-col-xs-10,.wfls-col-xs-11,.wfls-col-xs-12{float:left}.wfls-col-xs-1{width:8.33333%}.wfls-col-xs-2{width:16.66667%}.wfls-col-xs-3{width:25%}.wfls-col-xs-4{width:33.33333%}.wfls-col-xs-5{width:41.66667%}.wfls-col-xs-6{width:50%}.wfls-col-xs-7{width:58.33333%}.wfls-col-xs-8{width:66.66667%}.wfls-col-xs-9{width:75%}.wfls-col-xs-10{width:83.33333%}.wfls-col-xs-11{width:91.66667%}.wfls-col-xs-12{width:100%}.wfls-col-xs-pull-0{right:auto}.wfls-col-xs-pull-1{right:8.33333%}.wfls-col-xs-pull-2{right:16.66667%}.wfls-col-xs-pull-3{right:25%}.wfls-col-xs-pull-4{right:33.33333%}.wfls-col-xs-pull-5{right:41.66667%}.wfls-col-xs-pull-6{right:50%}.wfls-col-xs-pull-7{right:58.33333%}.wfls-col-xs-pull-8{right:66.66667%}.wfls-col-xs-pull-9{right:75%}.wfls-col-xs-pull-10{right:83.33333%}.wfls-col-xs-pull-11{right:91.66667%}.wfls-col-xs-pull-12{right:100%}.wfls-col-xs-push-0{left:auto}.wfls-col-xs-push-1{left:8.33333%}.wfls-col-xs-push-2{left:16.66667%}.wfls-col-xs-push-3{left:25%}.wfls-col-xs-push-4{left:33.33333%}.wfls-col-xs-push-5{left:41.66667%}.wfls-col-xs-push-6{left:50%}.wfls-col-xs-push-7{left:58.33333%}.wfls-col-xs-push-8{left:66.66667%}.wfls-col-xs-push-9{left:75%}.wfls-col-xs-push-10{left:83.33333%}.wfls-col-xs-push-11{left:91.66667%}.wfls-col-xs-push-12{left:100%}.wfls-col-xs-offset-0{margin-left:0%}.wfls-col-xs-offset-1{margin-left:8.33333%}.wfls-col-xs-offset-2{margin-left:16.66667%}.wfls-col-xs-offset-3{margin-left:25%}.wfls-col-xs-offset-4{margin-left:33.33333%}.wfls-col-xs-offset-5{margin-left:41.66667%}.wfls-col-xs-offset-6{margin-left:50%}.wfls-col-xs-offset-7{margin-left:58.33333%}.wfls-col-xs-offset-8{margin-left:66.66667%}.wfls-col-xs-offset-9{margin-left:75%}.wfls-col-xs-offset-10{margin-left:83.33333%}.wfls-col-xs-offset-11{margin-left:91.66667%}.wfls-col-xs-offset-12{margin-left:100%}.wfls-col-xs-half-padding-left{padding-left:8px}.wfls-col-xs-half-padding-right{padding-right:7px}@media (min-width: 768px){.wfls-col-sm-1,.wfls-col-sm-2,.wfls-col-sm-3,.wfls-col-sm-4,.wfls-col-sm-5,.wfls-col-sm-6,.wfls-col-sm-7,.wfls-col-sm-8,.wfls-col-sm-9,.wfls-col-sm-10,.wfls-col-sm-11,.wfls-col-sm-12{float:left}.wfls-col-sm-1{width:8.33333%}.wfls-col-sm-2{width:16.66667%}.wfls-col-sm-3{width:25%}.wfls-col-sm-4{width:33.33333%}.wfls-col-sm-5{width:41.66667%}.wfls-col-sm-6{width:50%}.wfls-col-sm-7{width:58.33333%}.wfls-col-sm-8{width:66.66667%}.wfls-col-sm-9{width:75%}.wfls-col-sm-10{width:83.33333%}.wfls-col-sm-11{width:91.66667%}.wfls-col-sm-12{width:100%}.wfls-col-sm-pull-0{right:auto}.wfls-col-sm-pull-1{right:8.33333%}.wfls-col-sm-pull-2{right:16.66667%}.wfls-col-sm-pull-3{right:25%}.wfls-col-sm-pull-4{right:33.33333%}.wfls-col-sm-pull-5{right:41.66667%}.wfls-col-sm-pull-6{right:50%}.wfls-col-sm-pull-7{right:58.33333%}.wfls-col-sm-pull-8{right:66.66667%}.wfls-col-sm-pull-9{right:75%}.wfls-col-sm-pull-10{right:83.33333%}.wfls-col-sm-pull-11{right:91.66667%}.wfls-col-sm-pull-12{right:100%}.wfls-col-sm-push-0{left:auto}.wfls-col-sm-push-1{left:8.33333%}.wfls-col-sm-push-2{left:16.66667%}.wfls-col-sm-push-3{left:25%}.wfls-col-sm-push-4{left:33.33333%}.wfls-col-sm-push-5{left:41.66667%}.wfls-col-sm-push-6{left:50%}.wfls-col-sm-push-7{left:58.33333%}.wfls-col-sm-push-8{left:66.66667%}.wfls-col-sm-push-9{left:75%}.wfls-col-sm-push-10{left:83.33333%}.wfls-col-sm-push-11{left:91.66667%}.wfls-col-sm-push-12{left:100%}.wfls-col-sm-offset-0{margin-left:0%}.wfls-col-sm-offset-1{margin-left:8.33333%}.wfls-col-sm-offset-2{margin-left:16.66667%}.wfls-col-sm-offset-3{margin-left:25%}.wfls-col-sm-offset-4{margin-left:33.33333%}.wfls-col-sm-offset-5{margin-left:41.66667%}.wfls-col-sm-offset-6{margin-left:50%}.wfls-col-sm-offset-7{margin-left:58.33333%}.wfls-col-sm-offset-8{margin-left:66.66667%}.wfls-col-sm-offset-9{margin-left:75%}.wfls-col-sm-offset-10{margin-left:83.33333%}.wfls-col-sm-offset-11{margin-left:91.66667%}.wfls-col-sm-offset-12{margin-left:100%}.wfls-col-sm-half-padding-left{padding-left:8px}.wfls-col-sm-half-padding-right{padding-right:7px}}@media (min-width: 992px){.wfls-col-md-1,.wfls-col-md-2,.wfls-col-md-3,.wfls-col-md-4,.wfls-col-md-5,.wfls-col-md-6,.wfls-col-md-7,.wfls-col-md-8,.wfls-col-md-9,.wfls-col-md-10,.wfls-col-md-11,.wfls-col-md-12{float:left}.wfls-col-md-1{width:8.33333%}.wfls-col-md-2{width:16.66667%}.wfls-col-md-3{width:25%}.wfls-col-md-4{width:33.33333%}.wfls-col-md-5{width:41.66667%}.wfls-col-md-6{width:50%}.wfls-col-md-7{width:58.33333%}.wfls-col-md-8{width:66.66667%}.wfls-col-md-9{width:75%}.wfls-col-md-10{width:83.33333%}.wfls-col-md-11{width:91.66667%}.wfls-col-md-12{width:100%}.wfls-col-md-pull-0{right:auto}.wfls-col-md-pull-1{right:8.33333%}.wfls-col-md-pull-2{right:16.66667%}.wfls-col-md-pull-3{right:25%}.wfls-col-md-pull-4{right:33.33333%}.wfls-col-md-pull-5{right:41.66667%}.wfls-col-md-pull-6{right:50%}.wfls-col-md-pull-7{right:58.33333%}.wfls-col-md-pull-8{right:66.66667%}.wfls-col-md-pull-9{right:75%}.wfls-col-md-pull-10{right:83.33333%}.wfls-col-md-pull-11{right:91.66667%}.wfls-col-md-pull-12{right:100%}.wfls-col-md-push-0{left:auto}.wfls-col-md-push-1{left:8.33333%}.wfls-col-md-push-2{left:16.66667%}.wfls-col-md-push-3{left:25%}.wfls-col-md-push-4{left:33.33333%}.wfls-col-md-push-5{left:41.66667%}.wfls-col-md-push-6{left:50%}.wfls-col-md-push-7{left:58.33333%}.wfls-col-md-push-8{left:66.66667%}.wfls-col-md-push-9{left:75%}.wfls-col-md-push-10{left:83.33333%}.wfls-col-md-push-11{left:91.66667%}.wfls-col-md-push-12{left:100%}.wfls-col-md-offset-0{margin-left:0%}.wfls-col-md-offset-1{margin-left:8.33333%}.wfls-col-md-offset-2{margin-left:16.66667%}.wfls-col-md-offset-3{margin-left:25%}.wfls-col-md-offset-4{margin-left:33.33333%}.wfls-col-md-offset-5{margin-left:41.66667%}.wfls-col-md-offset-6{margin-left:50%}.wfls-col-md-offset-7{margin-left:58.33333%}.wfls-col-md-offset-8{margin-left:66.66667%}.wfls-col-md-offset-9{margin-left:75%}.wfls-col-md-offset-10{margin-left:83.33333%}.wfls-col-md-offset-11{margin-left:91.66667%}.wfls-col-md-offset-12{margin-left:100%}.wfls-col-md-half-padding-left{padding-left:8px}.wfls-col-md-half-padding-right{padding-right:7px}}@media (min-width: 1200px){.wfls-col-lg-1,.wfls-col-lg-2,.wfls-col-lg-3,.wfls-col-lg-4,.wfls-col-lg-5,.wfls-col-lg-6,.wfls-col-lg-7,.wfls-col-lg-8,.wfls-col-lg-9,.wfls-col-lg-10,.wfls-col-lg-11,.wfls-col-lg-12{float:left}.wfls-col-lg-1{width:8.33333%}.wfls-col-lg-2{width:16.66667%}.wfls-col-lg-3{width:25%}.wfls-col-lg-4{width:33.33333%}.wfls-col-lg-5{width:41.66667%}.wfls-col-lg-6{width:50%}.wfls-col-lg-7{width:58.33333%}.wfls-col-lg-8{width:66.66667%}.wfls-col-lg-9{width:75%}.wfls-col-lg-10{width:83.33333%}.wfls-col-lg-11{width:91.66667%}.wfls-col-lg-12{width:100%}.wfls-col-lg-pull-0{right:auto}.wfls-col-lg-pull-1{right:8.33333%}.wfls-col-lg-pull-2{right:16.66667%}.wfls-col-lg-pull-3{right:25%}.wfls-col-lg-pull-4{right:33.33333%}.wfls-col-lg-pull-5{right:41.66667%}.wfls-col-lg-pull-6{right:50%}.wfls-col-lg-pull-7{right:58.33333%}.wfls-col-lg-pull-8{right:66.66667%}.wfls-col-lg-pull-9{right:75%}.wfls-col-lg-pull-10{right:83.33333%}.wfls-col-lg-pull-11{right:91.66667%}.wfls-col-lg-pull-12{right:100%}.wfls-col-lg-push-0{left:auto}.wfls-col-lg-push-1{left:8.33333%}.wfls-col-lg-push-2{left:16.66667%}.wfls-col-lg-push-3{left:25%}.wfls-col-lg-push-4{left:33.33333%}.wfls-col-lg-push-5{left:41.66667%}.wfls-col-lg-push-6{left:50%}.wfls-col-lg-push-7{left:58.33333%}.wfls-col-lg-push-8{left:66.66667%}.wfls-col-lg-push-9{left:75%}.wfls-col-lg-push-10{left:83.33333%}.wfls-col-lg-push-11{left:91.66667%}.wfls-col-lg-push-12{left:100%}.wfls-col-lg-offset-0{margin-left:0%}.wfls-col-lg-offset-1{margin-left:8.33333%}.wfls-col-lg-offset-2{margin-left:16.66667%}.wfls-col-lg-offset-3{margin-left:25%}.wfls-col-lg-offset-4{margin-left:33.33333%}.wfls-col-lg-offset-5{margin-left:41.66667%}.wfls-col-lg-offset-6{margin-left:50%}.wfls-col-lg-offset-7{margin-left:58.33333%}.wfls-col-lg-offset-8{margin-left:66.66667%}.wfls-col-lg-offset-9{margin-left:75%}.wfls-col-lg-offset-10{margin-left:83.33333%}.wfls-col-lg-offset-11{margin-left:91.66667%}.wfls-col-lg-offset-12{margin-left:100%}.wfls-col-lg-half-padding-left{padding-left:8px}.wfls-col-lg-half-padding-right{padding-right:7px}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}label.wfls-plain{font-weight:normal}label.wfls-control-label.wfls-disabled{pointer-events:none}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:1.5rem;font-size:14px;line-height:1.42857;color:#555}.wfls-form-control{display:block;width:100%;height:38px;padding:.5rem 1.25rem;font-size:14px;line-height:1.42857;color:#555;background-color:#fff;background-image:none;border:1px solid #ddd;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-o-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s}.wfls-form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.wfls-form-control::-moz-placeholder{color:#bfbfbf;opacity:1}.wfls-form-control:-ms-input-placeholder{color:#bfbfbf}.wfls-form-control::-webkit-input-placeholder{color:#bfbfbf}.wfls-form-control::-ms-expand{border:0;background-color:transparent}.wfls-form-control[disabled],.wfls-form-control[readonly],fieldset[disabled] .wfls-form-control{background-color:#e2e2e2;opacity:1}.wfls-form-control[disabled],.wfls-form-control[readonly],fieldset[disabled] .wfls-form-control{cursor:not-allowed;pointer-events:none}textarea.wfls-form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio: 0){input[type="date"].wfls-form-control,input[type="time"].wfls-form-control,input[type="datetime-local"].wfls-form-control,input[type="month"].wfls-form-control{line-height:38px}input[type="date"].wfls-input-sm,.wfls-input-group-sm input[type="date"],input[type="time"].wfls-input-sm,.wfls-input-group-sm input[type="time"],input[type="datetime-local"].wfls-input-sm,.wfls-input-group-sm input[type="datetime-local"],input[type="month"].wfls-input-sm,.wfls-input-group-sm input[type="month"]{line-height:30px}input[type="date"].wfls-input-lg,.wfls-input-group-lg input[type="date"],input[type="time"].wfls-input-lg,.wfls-input-group-lg input[type="time"],input[type="datetime-local"].wfls-input-lg,.wfls-input-group-lg input[type="datetime-local"],input[type="month"].wfls-input-lg,.wfls-input-group-lg input[type="month"]{line-height:46px}}.wfls-form-group{margin-bottom:8px}.wfls-form-group.wfls-sub-group label{color:#666666;font-weight:normal;padding-left:20px}.wfls-form-group.wfls-focus{border-left:4px solid #11967a;padding-bottom:8px;background-color:#e5e5e5}.wfls-form-group.wfls-focus label{margin-left:-4px}.wfls-radio,.wfls-checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.wfls-radio label,.wfls-checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.wfls-radio input[type="radio"],.wfls-radio-inline input[type="radio"],.wfls-checkbox input[type="checkbox"],.wfls-checkbox-inline input[type="checkbox"]{margin-top:4px \9}.wfls-radio-offset{padding-left:29px}@media (min-width: 768px){.wfls-radio-offset{padding-left:20px}}.wfls-radio+.wfls-radio,.wfls-checkbox+.wfls-checkbox{margin-top:-5px}.wfls-radio-inline,.wfls-checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.wfls-radio-inline+.wfls-radio-inline,.wfls-checkbox-inline+.wfls-checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="radio"][readonly],input[type="radio"].wfls-disabled,fieldset[disabled] input[type="radio"],input[type="checkbox"][disabled],input[type="checkbox"][readonly],input[type="checkbox"].wfls-disabled,fieldset[disabled] input[type="checkbox"]{cursor:not-allowed;pointer-events:none}.wfls-radio-inline.wfls-disabled,fieldset[disabled] .wfls-radio-inline,.wfls-checkbox-inline.wfls-disabled,fieldset[disabled] .wfls-checkbox-inline{cursor:not-allowed}.wfls-radio.wfls-disabled label,fieldset[disabled] .wfls-radio label,.wfls-checkbox.wfls-disabled label,fieldset[disabled] .wfls-checkbox label{cursor:not-allowed;pointer-events:none}.wfls-form-control-static{padding-top:1.5rem;padding-bottom:1.5rem;margin:0;line-height:1}.wfls-form-control-static.wfls-input-lg,.wfls-form-control-static.wfls-input-sm{padding-left:0;padding-right:0}.wfls-input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.wfls-input-sm{height:30px;line-height:30px}textarea.wfls-input-sm,select[multiple].wfls-input-sm{height:auto}.wfls-form-group-sm .wfls-form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.wfls-form-group-sm select.wfls-form-control{height:30px;line-height:30px}.wfls-form-group-sm textarea.wfls-form-control,.wfls-form-group-sm select[multiple].wfls-form-control{height:auto}.wfls-form-group-sm .wfls-form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.wfls-input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33333;border-radius:6px}select.wfls-input-lg{height:46px;line-height:46px}textarea.wfls-input-lg,select[multiple].wfls-input-lg{height:auto}.wfls-form-group-lg .wfls-form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33333;border-radius:6px}.wfls-form-group-lg select.wfls-form-control{height:46px;line-height:46px}.wfls-form-group-lg textarea.wfls-form-control,.wfls-form-group-lg select[multiple].wfls-form-control{height:auto}.wfls-form-group-lg .wfls-form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.33333}.wfls-has-feedback{position:relative}.wfls-has-feedback .wfls-form-control{padding-right:47.5px}.wfls-form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:38px;height:38px;line-height:38px;text-align:center;pointer-events:none}.wfls-input-lg+.wfls-form-control-feedback,.wfls-input-group-lg+.wfls-form-control-feedback,.wfls-form-group-lg .wfls-form-control+.wfls-form-control-feedback{width:46px;height:46px;line-height:46px}.wfls-input-sm+.wfls-form-control-feedback,.wfls-input-group-sm+.wfls-form-control-feedback,.wfls-form-group-sm .wfls-form-control+.wfls-form-control-feedback{width:30px;height:30px;line-height:30px}.wfls-has-success .wfls-help-block,.wfls-has-success .wfls-control-label,.wfls-has-success .wfls-radio,.wfls-has-success .wfls-checkbox,.wfls-has-success .wfls-radio-inline,.wfls-has-success .wfls-checkbox-inline,.wfls-has-success.wfls-radio label,.wfls-has-success.wfls-checkbox label,.wfls-has-success.wfls-radio-inline label,.wfls-has-success.wfls-checkbox-inline label{color:#3c763d}.wfls-has-success .wfls-form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.wfls-has-success .wfls-form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.wfls-has-success .wfls-input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.wfls-has-success .wfls-form-control-feedback{color:#3c763d}.wfls-has-warning .wfls-help-block,.wfls-has-warning .wfls-control-label,.wfls-has-warning .wfls-radio,.wfls-has-warning .wfls-checkbox,.wfls-has-warning .wfls-radio-inline,.wfls-has-warning .wfls-checkbox-inline,.wfls-has-warning.wfls-radio label,.wfls-has-warning.wfls-checkbox label,.wfls-has-warning.wfls-radio-inline label,.wfls-has-warning.wfls-checkbox-inline label{color:#8a6d3b}.wfls-has-warning .wfls-form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.wfls-has-warning .wfls-form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.wfls-has-warning .wfls-input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.wfls-has-warning .wfls-form-control-feedback{color:#8a6d3b}.wfls-has-error .wfls-help-block,.wfls-has-error .wfls-control-label,.wfls-has-error .wfls-radio,.wfls-has-error .wfls-checkbox,.wfls-has-error .wfls-radio-inline,.wfls-has-error .wfls-checkbox-inline,.wfls-has-error.wfls-radio label,.wfls-has-error.wfls-checkbox label,.wfls-has-error.wfls-radio-inline label,.wfls-has-error.wfls-checkbox-inline label{color:#a94442}.wfls-has-error .wfls-form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.wfls-has-error .wfls-form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.wfls-has-error .wfls-input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.wfls-has-error .wfls-form-control-feedback{color:#a94442}.wfls-has-feedback label ~ .wfls-form-control-feedback{top:25px}.wfls-has-feedback label.wfls-sr-only ~ .wfls-form-control-feedback{top:0}.wfls-help-block{display:block;margin-top:5px;color:#737373}@media (min-width: 768px){.wfls-form-inline .wfls-form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.wfls-form-inline .wfls-form-control{display:inline-block;width:auto;vertical-align:middle}.wfls-form-inline .wfls-form-control-static{display:inline-block}.wfls-form-inline .wfls-input-group{display:inline-table;vertical-align:middle}.wfls-form-inline .wfls-input-group .wfls-input-group-addon,.wfls-form-inline .wfls-input-group .wfls-input-group-btn,.wfls-form-inline .wfls-input-group .wfls-form-control{width:auto}.wfls-form-inline .wfls-input-group>.wfls-form-control{width:100%}.wfls-form-inline .wfls-control-label{margin-bottom:0;vertical-align:middle}.wfls-form-inline .wfls-radio,.wfls-form-inline .wfls-checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.wfls-form-inline .wfls-radio label,.wfls-form-inline .wfls-checkbox label{padding-left:0}.wfls-form-inline .wfls-radio input[type="radio"],.wfls-form-inline .wfls-checkbox input[type="checkbox"]{position:relative;margin-left:0}.wfls-form-inline .wfls-has-feedback .wfls-form-control-feedback{top:0}}.wfls-form-horizontal .wfls-radio,.wfls-form-horizontal .wfls-checkbox,.wfls-form-horizontal .wfls-radio-inline,.wfls-form-horizontal .wfls-checkbox-inline{margin-top:0;margin-bottom:0;padding-top:1.5rem}.wfls-form-horizontal .wfls-radio,.wfls-form-horizontal .wfls-checkbox{min-height:29px}.wfls-form-horizontal .wfls-form-group{margin-left:-15px;margin-right:-15px}.wfls-form-horizontal .wfls-form-group:before,.wfls-form-horizontal .wfls-form-group:after{content:" ";display:table}.wfls-form-horizontal .wfls-form-group:after{clear:both}@media (min-width: 768px){.wfls-form-horizontal .wfls-control-label{text-align:right;margin-bottom:0;padding-top:1.5rem}}.wfls-form-horizontal .wfls-has-feedback .wfls-form-control-feedback{right:15px}@media (min-width: 768px){.wfls-form-horizontal .wfls-form-group-lg .wfls-control-label{padding-top:11px;font-size:18px}}@media (min-width: 768px){.wfls-form-horizontal .wfls-form-group-sm .wfls-control-label{padding-top:6px;font-size:12px}}@media (min-width: 768px){#input-recaptchaSiteKey,#input-recaptchaSecret{min-width:400px}}hr.wfls-half{border:0 !important;border-bottom:1px solid #e4e4e4 !important;width:50%;margin:1.25rem auto}.wrap.wordfence-ls{direction:ltr}@media (min-width: 768px){.wrap.wordfence-ls{max-width:750px}}@media (min-width: 992px){.wrap.wordfence-ls{max-width:970px}}@media (min-width: 1200px){.wrap.wordfence-ls{max-width:1170px}}.wrap.wordfence-ls>.wfls-container-fluid{padding-left:0px;padding-right:0px}.wrap.wordfence-ls .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709e}.wrap.wordfence-ls a{text-decoration:none}.wrap.wordfence-ls a:hover{text-decoration:underline}.wrap.wordfence-ls a.wfls-btn:hover{text-decoration:none}.wrap.wordfence-ls p,.wrap.wordfence-ls td,.wrap.wordfence-ls li{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:.875rem;color:#2d2d2d;line-height:1.3125;font-weight:normal}.wrap.wordfence-ls p strong,.wrap.wordfence-ls td strong,.wrap.wordfence-ls li strong{font-weight:600}.wrap.wordfence-ls p em,.wrap.wordfence-ls td em,.wrap.wordfence-ls li em{font-weight:normal}.wrap.wordfence-ls h1,.wrap.wordfence-ls h2,.wrap.wordfence-ls h3,.wrap.wordfence-ls h4,.wrap.wordfence-ls h5,.wrap.wordfence-ls h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;color:#2d2d2d;font-weight:700}.wrap.wordfence-ls h2{font-size:1.3125rem;line-height:1.5}.wrap.wordfence-ls h3{font-size:1.125rem}.wrap.wordfence-ls h4{font-size:1rem}a{color:#00709e}.wfls-inline-help{color:#9f9fa0}.wfls-inline-help:hover{color:#00709e}.wordfenceWrap{margin:20px 0 0 20px}#wfHeading:after{content:'.';visibility:hidden;display:block;clear:both;height:0px}.wfls-header-icon{background-image:url(../img/header.svg);width:32px;height:32px;background-position:0 0;background-repeat:no-repeat;padding:0;margin:0 5px 0 0;float:left}a.wfhelp{margin:0 3px 0 3px;text-decoration:none;display:inline-block;vertical-align:middle;font:normal normal normal 14px/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased}a.wfhelp:before{content:'\f29c'}.wordfence .resulticon{display:block;float:left;width:16px;height:16px;background-position:0 0;background-repeat:no-repeat;border-width:0;padding:0;margin:0 3px 0 0;background-image:url(../img/icons/bullet_yellow.png)}.wordfenceBoldTD{font-weight:bold}.wfAjax24{display:none;width:24px;height:24px;background-image:url(../img/icons/ajax24.gif);margin:0;padding:0}div.wfLoadingWhite32{width:32px;height:32px;background-image:url(../img/icons/ajaxWhite32x32.gif);margin:0;padding:0}.wfTabsContainer{background-color:#FFF;overflow:hidden;border:1px solid #CCC;padding:15px;min-height:200px;-webkit-font-smoothing:antialiased}#wfTabs::after{content:".";display:block;height:0;width:0;line-height:0;clear:both;visibility:hidden}#wfTabs a{float:left;z-index:10;height:18px;margin:0 5px -1px 0;padding:5px 8px;border:1px solid #CCC;text-decoration:none;background-color:#EFEFEF;color:#21759B;-moz-border-radius-topright:3px;-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px}#wfTabs a.selected{border-bottom:1px solid #FFF;background-color:#FFF;color:#777}.wordfenceTopTab{display:none;margin-top:15px}.wordfenceTopTab.active{display:block}.wordfenceHelpLink{margin-top:15px}.wfAjaxLight128{background-image:url(../img/icons/ajax3.gif)}.wfStrong{font-weight:bold}.wordfenceModeElem{width:1px;height:1px;opacity:0}.wfWarn{color:#F00}img.wfFlag{vertical-align:middle;margin:-3px 4px 0 0}.wfHitTime{font-style:italic}.wfAvatar img{vertical-align:middle;margin-right:0.5rem}.wfls-hex-sequence{color:#587ECB}.wfLoadMoreButton.disabled,.wfLoadMoreButton[disabled]{pointer-events:none;opacity:0.65}table.wfConfigForm th{font-weight:normal;text-align:left;padding:2px 3px 1px 0;vertical-align:middle}table.wfConfigForm td{vertical-align:middle}table.wfConfigForm td.align-top{vertical-align:top}table th.wfConfigEnable{font-weight:bold;min-width:25%}.wfSavedMsg{display:none;color:#A00}table th.wfSubheading{font-weight:bold;padding-top:10px}h3.wfConfigHeading{font-size:22px;color:#777;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:italic;font-weight:normal}.wfTipText{color:#777;font-family:Georgia,Times New Roman,Times,serif;font-style:italic}.wfBlackCursor{color:#FFF}.wfls-spinner{display:inline-block;width:4px}.wferror{color:#F00}#wordfenceWorking{padding:10px 40px 6px 16px;z-index:100000;position:fixed;right:16px;bottom:0px;background-color:#fcb214;border:5px solid #fcb214;border-width:6px 15px 6px 6px;color:#525355;font-size:12px;font-weight:bold;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;background-image:url("../img/icons/working-indicator.gif");background-position:100% 50%;background-repeat:no-repeat}@media (max-width: 960px){#wordfenceWorking{left:auto;right:0px}}#paidWrap{position:relative}.paidInnerMsg{width:500px;margin:150px auto 0 auto;color:#000;font-size:18px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:1.8em;text-align:center;-webkit-font-smoothing:antialiased}.wfMarker{height:1px;width:1px}.wfPaidOnlyNotice{width:500px;background-color:#FFFFE0;border:1px solid #000;padding:10px;margin:20px}.wfOnOffSwitch{display:inline-block;position:relative !important;width:69px !important;-webkit-user-select:none !important;-moz-user-select:none !important;-ms-user-select:none !important;user-select:none !important}.wfOnOffSwitch-checkbox{display:none !important}.wfOnOffSwitch-label{display:block !important;overflow:hidden !important;cursor:pointer !important;border:2px solid #999999 !important;border-radius:19px !important;margin:0}.wfOnOffSwitch-inner{width:200% !important;margin-left:-100% !important;-webkit-transition:margin 0.3s ease-in !important;-o-transition:margin 0.3s ease-in !important;transition:margin 0.3s ease-in !important;-webkit-transition-delay:0s !important;transition-delay:0s !important}.wfOnOffSwitch-inner:before,.wfOnOffSwitch-inner:after{float:left !important;width:50% !important;height:19px !important;padding:0 !important;line-height:19px !important;font-size:14px !important;color:white !important;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif !important;font-weight:bold !important;-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;-moz-border-radius:19px !important;-webkit-border-radius:19px;border-radius:19px !important;-webkit-box-shadow:0 9.5px 0 rgba(0,0,0,0.08) inset !important;box-shadow:0 9.5px 0 rgba(0,0,0,0.08) inset !important}.wfOnOffSwitch-inner:before{content:"ON" !important;padding-left:10px !important;background-color:#30D965 !important;color:#FFFFFF !important;-moz-border-radius:19px 0 0 19px !important;-webkit-border-radius:19px;border-radius:19px 0 0 19px !important}.wfOnOffSwitch-inner:after{content:"OFF" !important;padding-right:10px !important;background-color:#EEEEEE !important;color:#999999 !important;text-align:right !important;-moz-border-radius:0 19px 19px 0 !important;-webkit-border-radius:0;border-radius:0 19px 19px 0 !important}.wfOnOffSwitch-switch{width:19px !important;margin:0 !important;background:#FFFFFF !important;border:2px solid #999999 !important;-moz-border-radius:19px !important;-webkit-border-radius:19px;border-radius:19px !important;position:absolute !important;top:0 !important;bottom:0 !important;right:46px !important;-webkit-transition:all 0.3s ease-in !important;-o-transition:all 0.3s ease-in !important;transition:all 0.3s ease-in !important;-webkit-transition-delay:0s !important;transition-delay:0s !important;background-image:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjEiLz48c3RvcCBvZmZzZXQ9IjgwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIwLjAiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2dyYWQpIiAvPjwvc3ZnPiA=') !important;background-size:100%;background-image:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0,0,0,0.1)),color-stop(80%, rgba(0,0,0,0))) !important;background-image:-moz-linear-gradient(center top, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0) 80%) !important;background-image:-webkit-linear-gradient(center top, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0) 80%) !important;background-image:linear-gradient(to center bottom, rgba(0,0,0,0.1) 0%,rgba(0,0,0,0) 80%) !important;box-shadow:0 1px 1px white inset !important}.wfOnOffSwitch-checkbox:checked+.wfOnOffSwitch-label .wfOnOffSwitch-inner{margin-left:0 !important}.wfOnOffSwitch-checkbox:checked+.wfOnOffSwitch-label .wfOnOffSwitch-switch{right:0 !important}#wordfenceConfigWarning,#wordfenceAdminEmailWarning{clear:left;margin-top:5px}.wfls-striped-table{width:100%;max-width:100%;border-collapse:collapse}.wfls-striped-table th{border-left:1px solid #bdbdbd}.wfls-striped-table th:first-of-type{border-left:0}.wfls-striped-table th,.wfls-striped-table td{padding:1rem}.wfls-striped-table thead th,.wfls-striped-table thead td,.wfls-striped-table tfoot th,.wfls-striped-table tfoot td,.wfls-striped-table tbody.thead th,.wfls-striped-table tbody.thead td{background-color:#ebebeb;color:#777;font-weight:bold;text-align:left}.wfls-striped-table tbody tr.even td,.wfls-striped-table tbody tr:nth-child(2n) td{background-color:#ffffff}.wfls-striped-table tbody tr td,.wfls-striped-table tbody tr.odd td{background-color:#fafafa}.wfls-striped-table tbody tr:hover>td{background-color:#fffbd8}.wfls-striped-table tbody.empty-row tr td{border-width:0;padding:8px 0;background-color:transparent}.wfls-striped-table .wfls-result-error,.wfls-block-list .wfls-result-error{color:#d0514c !important;font-weight:bold}.wfls-striped-table .wfls-result-error:before,.wfls-block-list .wfls-result-error:before{content:"\2718"}.wfls-striped-table .wfls-result-success{max-width:20%}.wfls-striped-table .wfls-result-success,.wfls-block-list .wfls-result-success{color:#008c10 !important;font-weight:bold}.wfls-striped-table .wfls-result-success:before,.wfls-block-list .wfls-result-success:before{content:"\2713"}.wfls-striped-table .wfls-result-success:before,.wfls-block-list .wfls-result-success:before,.wfls-striped-table .wfls-result-error:before,.wfls-block-list .wfls-result-error:before{font-size:16px;display:inline-block;margin:0px 8px 0px 0px}.wfls-striped-table .wfls-result-inactive,.wfls-block-list .wfls-result-inactive{font-weight:bold;color:#666666 !important}.wfls-fixed-table{table-layout:fixed}pre.wfls-pre{margin:8px 0 20px;padding:12px;background:#ffffff;border:1px solid #999999;overflow:auto}.wfls-center{text-align:center}#wfConfigForm,.wfls-diagnostics-wrapper{max-width:1035px}.wfls-hidden{display:none !important}.wfls-card{position:relative;margin:0 auto .625rem;padding:1rem;box-sizing:border-box;background:#fff;box-shadow:0 0 0 1px rgba(200,215,225,0.5),0 1px 2px #e9eff3}.wfls-card .wfls-card-inner{min-height:76px;width:100%;padding:8px;box-sizing:border-box;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;position:relative}.wfls-card .wfls-card-inner .wfls-card-content{max-width:75%}.wfls-card .wfls-card-inner .wfls-card-content .wfls-card-title{font-size:1.125rem;width:100%}.wfls-card .wfls-card-inner .wfls-card-content .wfls-card-subtitle{margin-top:.125rem;margin-bottom:.125rem;font-size:.875rem;color:#4f748e}.wfls-card .wfls-card-inner .wfls-card-action{position:absolute;top:0;right:0;height:100%;background:none;border:0;outline:0;width:48px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;vertical-align:middle;transition:transform 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275),color 0.2s ease-in}.wfls-card .wfls-card-inner .wfls-card-action .wfls-card-action-chevron{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJjaGV2cm9uLW9iamVjdCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiCgkgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyNCAyNCIKCSB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBhdGggaWQ9ImNoZXZyb24iIGQ9Ik0yMCA5bC04IDgtOC04IDEuNDE0LTEuNDE0TDEyIDE0LjE3Mmw2LjU4Ni02LjU4NiIvPgo8L3N2Zz4K");background-repeat:no-repeat;background-position:center center;width:24px;height:24px;fill:#87a6bc}.wfls-card .wfls-card-inner .wfls-card-action .wfls-card-action-checkbox{background-image:url(../img/checkbox.png);background-repeat:no-repeat;background-position:left center;width:29px;height:29px}.wfls-card .wfls-card-inner .wfls-card-action .wfls-card-action-checkbox.checked{background-position:right center}.wfls-card .wfls-card-extra{display:none;padding:0.5rem;margin-top:1rem;border-top:1px solid #f3f6f8}@media (min-width: 768px){.wfls-card .wfls-card-extra{padding:1rem}}.wfls-card.active .wfls-card-extra{display:block}.wfls-card.wfls-card-left .wfls-card-content{margin-left:48px}.wfls-card.wfls-card-left .wfls-card-action{right:auto;left:0px}.wfls-card.disabled .wfls-card-content .wfls-card-title{color:#aaaaaa}.wfls-card.disabled .wfls-card-content .wfls-card-subtitle{color:#8ea6be}.wfls-inline-block{display:inline-block}@media (max-width: 767px){.wfls-inline-block-xs{display:inline-block}}.wfls-full-width{width:100%;max-width:100%}.wfls-no-top{margin-top:0 !important}.wfls-add-top{margin-top:1rem !important}.wfls-add-top-large{margin-top:1.5rem !important}.wfls-add-top-medium{margin-top:0.75rem !important}.wfls-add-top-small{margin-top:0.5rem !important}.wfls-add-top-smaller{margin-top:0.25rem !important}.wfls-no-bottom{margin-bottom:0 !important}.wfls-add-bottom{margin-bottom:1rem !important}.wfls-add-bottom-large{margin-bottom:1.5rem !important}.wfls-add-bottom-medium{margin-bottom:0.75rem !important}.wfls-add-bottom-small{margin-bottom:0.5rem !important}.wfls-add-bottom-smaller{margin-bottom:0.25rem !important}.wfls-padding-no-top{padding-top:0 !important}.wfls-no-right{margin-right:0 !important}.wfls-padding-no-bottom{padding-bottom:0 !important}.wfls-padding-no-left{padding-left:0 !important}.wfls-padding-no-right{padding-right:0 !important}.wfls-padding-add-top{padding-top:1rem !important}.wfls-padding-add-top-small{padding-top:0.5rem !important}.wfls-padding-add-top-medium{padding-top:0.75rem !important}.wfls-padding-add-top-large{padding-top:1.5rem !important}.wfls-padding-add-top-responsive{padding-top:1rem !important}@media (min-width: 768px){.wfls-padding-add-top-responsive{padding-top:1.5rem !important}}.wfls-padding-add-bottom{padding-bottom:1rem !important}.wfls-padding-add-bottom-small{padding-bottom:0.5rem !important}.wfls-padding-add-bottom-medium{padding-bottom:0.75rem !important}.wfls-padding-add-bottom-large{padding-bottom:1.5rem !important}.wfls-padding-add-bottom-responsive{padding-bottom:1rem !important}@media (min-width: 768px){.wfls-padding-add-bottom-responsive{padding-bottom:1.5rem !important}}.wfls-padding-no-bottom{padding-bottom:0 !important}.wfls-padding-add-left{padding-left:1rem !important}.wfls-padding-add-left-small{padding-left:0.5rem !important}.wfls-padding-add-left-medium{padding-left:0.75rem !important}.wfls-padding-add-left-large{padding-left:1.5rem !important}.wfls-padding-add-left-responsive{padding-left:1rem !important}@media (min-width: 768px){.wfls-padding-add-left-responsive{padding-left:1.5rem !important}}.wfls-padding-add-right{padding-right:1rem !important}.wfls-padding-add-right-small{padding-right:0.5rem !important}.wfls-padding-add-right-medium{padding-right:0.75rem !important}.wfls-padding-add-right-large{padding-right:1.5rem !important}.wfls-padding-add-right-responsive{padding-right:1rem !important}@media (min-width: 768px){.wfls-padding-add-right-responsive{padding-right:1.5rem !important}}.wfls-left{text-align:left !important}.wfls-center{text-align:center !important}.wfls-block-center{margin:0 auto}.wfls-right{text-align:right !important}.wfls-block-right{margin:0 0 0 auto}@media (max-width: 767px){.wfls-left-xs{text-align:left !important}.wfls-center-xs{text-align:center !important}.wfls-padding-add-top-xs{padding-top:1rem !important}.wfls-padding-add-top-xs-small{padding-top:0.5rem !important}.wfls-padding-add-top-xs-large{padding-top:1.5rem !important}.wfls-padding-add-bottom-xs{padding-bottom:1rem !important}.wfls-padding-add-bottom-xs-small{padding-bottom:0.5rem !important}.wfls-padding-add-bottom-xs-large{padding-bottom:1.5rem !important}}@media (min-width: 768px) and (max-width: 991px){.wfls-left-sm{text-align:left !important}.wfls-center-sm{text-align:center !important}}@media (min-width: 992px) and (max-width: 1199px){.wfls-left-md{text-align:left !important}.wfls-center-md{text-align:center !important}}@media (min-width: 1200px){.wfls-left-lg{text-align:left !important}.wfls-center-lg{text-align:center !important}}.wfls-border-no-top{border-top:none !important}.wfls-border-no-right{border-right:none !important}.wfls-border-no-bottom{border-bottom:none !important}.wfls-border-no-left{border-left:none !important}.wfls-overflow-x-auto{overflow-x:auto}.wfls-overflow-y-auto{overflow-y:auto}@media (max-width: 767px){.wfls-overflow-x-auto-xs{overflow-x:auto}.wfls-overflow-y-auto-xs{overflow-y:auto}}.wfls-blue{color:#00709e !important}.wfls-blue-light{color:#008cc1 !important}.wfls-gray-dark{color:#2d2d2d !important}.wfls-gray-blue{color:#3f596b !important}.wfls-green-dark{color:#11967a !important}.wfls-green-light{color:#16bc9b !important}.wfls-red-dark{color:#930000 !important}.wfls-red-light{color:#c10000 !important}.wfls-yellow-dark{color:#fcb214 !important}.wfls-yellow-light{color:#ffd10a !important}.wfls-gray{color:#525355 !important}.wfls-gray-light{color:#9f9fa0 !important}.wfls-nowrap{white-space:nowrap}.wfls-tip{color:#fcb214;font-size:1.1rem;margin-right:0.25rem}.wfls-text-small{font-size:85% !important}.wfls-text-plain{font-weight:400 !important}.wfls-scroll-x::-webkit-scrollbar,.wfls-scroll-y::-webkit-scrollbar{-webkit-appearance:none;width:7px;height:7px}.wfls-scroll-x::-webkit-scrollbar-thumb,.wfls-scroll-y::-webkit-scrollbar-thumb{border-radius:4px;background-color:rgba(0,0,0,0.194);-webkit-box-shadow:0 0 1px rgba(255,255,255,0.5)}.wfls-split-word{word-wrap:break-word;word-break:break-all}@media (max-width: 767px){.wfls-split-word-xs{word-wrap:break-word;word-break:break-all;white-space:normal !important}}.wfselect2-container{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;min-width:200px}@media (min-width: 768px){.wfselect2-container{min-width:280px}}@media (min-width: 992px){.wfselect2-container{min-width:320px}}@media (max-width: 767px){.wfselect2-container .wfselect2-search.wfselect2-search--inline{margin:0 !important}}.wfls-select2-placeholder-fix .wfselect2-search__field{width:auto !important}.wfls-page-title{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;margin-top:0.5rem}.wfls-page-title>*{-webkit-flex-grow:0;flex-grow:0;min-width:0}.wfls-page-title>*:first-child{-webkit-flex-grow:0;flex-grow:0;min-width:32px;-webkit-flex-basis:32px;flex-basis:32px;padding-right:0.25rem}.wfls-page-title .wordfence-icon32{margin:0;margin-right:0.5rem}.wfls-page-title h2{padding:0 !important}.wfls-page-title .wfOnOffSwitch{-webkit-flex-basis:69px;flex-basis:69px;-webkit-flex-shrink:0;flex-shrink:0;margin-left:0.5rem}.wfls-tab-container{background-color:#fff}@media (min-width: 768px){.wfls-tab-container{background-color:unset}}.wfls-page-tabs,.wfls-page-fixed-tabs{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;border-bottom:1px solid #d0d0d0;margin:0;margin-top:0.5rem;margin-left:-10px;margin-right:-10px}@media (min-width: 768px){.wfls-page-tabs,.wfls-page-fixed-tabs{margin-left:0;margin-right:0}}.wfls-page-tabs>*,.wfls-page-fixed-tabs>*{-webkit-flex-grow:0;flex-grow:0;min-width:0}.wfls-page-tabs>*:first-child,.wfls-page-fixed-tabs>*:first-child{-webkit-flex-grow:0;flex-grow:0;min-width:32px;-webkit-flex-basis:32px;flex-basis:32px}.wfls-page-tabs .wordfence-icon32,.wfls-page-fixed-tabs .wordfence-icon32{margin:0;margin-right:0.5rem;margin-left:0.5rem}@media (min-width: 768px){.wfls-page-tabs .wordfence-icon32,.wfls-page-fixed-tabs .wordfence-icon32{margin-left:0}}.wfls-page-tabs .wfls-text-tab,.wfls-page-fixed-tabs .wfls-text-tab{margin:0;margin-left:0.5rem;color:#333}.wfls-page-tabs .wfls-tab,.wfls-page-fixed-tabs .wfls-tab{border:1px solid #fff;border-top-right-radius:0.5rem;border-top-left-radius:0.5rem;border-bottom:none;margin-bottom:-1px;margin-right:0.5rem;color:#333}@media (min-width: 768px){.wfls-page-tabs .wfls-tab,.wfls-page-fixed-tabs .wfls-tab{border:1px solid #d0d0d0;background:#e6e6e6}}.wfls-page-tabs .wfls-tab a,.wfls-page-fixed-tabs .wfls-tab a{display:block;padding:0.5rem 1rem;font-size:14px;line-height:24px;text-decoration:none;font-weight:bold;color:#333}.wfls-page-tabs .wfls-tab.wfls-active,.wfls-page-tabs .wfls-tab:hover,.wfls-page-fixed-tabs .wfls-tab.wfls-active,.wfls-page-fixed-tabs .wfls-tab:hover{border-bottom:1px solid #f1f1f1;background:#f1f1f1;color:#00709e;-webkit-box-shadow:none;box-shadow:none}.wfls-page-tabs .wfls-tab.wfls-active a,.wfls-page-tabs .wfls-tab:hover a,.wfls-page-fixed-tabs .wfls-tab.wfls-active a,.wfls-page-fixed-tabs .wfls-tab:hover a{color:#00709e}.wfls-tab-content{display:none;margin-top:15px}.wfls-tab-content.wfls-active{display:block}.wfls-fixed-tab-content{margin-top:15px}.wfls-section-title{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start}.wfls-section-title>*{-webkit-flex-grow:0;flex-grow:0;min-width:0}.wfls-section-title>h1,.wfls-section-title>h2,.wfls-section-title>h3,.wfls-section-title>h4,.wfls-section-title>h5,.wfls-section-title>h6{-webkit-flex-grow:1;flex-grow:1;color:#2d2d2d !important;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif !important;line-height:1.5rem !important;font-weight:700 !important;padding:0 !important;margin:0 !important}@media (min-width: 768px){.wfls-section-title>h1,.wfls-section-title>h2,.wfls-section-title>h3,.wfls-section-title>h4,.wfls-section-title>h5,.wfls-section-title>h6{padding-right:0.25rem !important}}.wfls-section-title h2{font-size:1.3125rem;line-height:1.5}.wfls-section-title h3{font-size:1.125rem}.wfls-section-title h4{font-size:1rem}.wfls-section-title .wordfence-icon32{margin:0;margin-right:0.5rem}.wfls-status-circular{position:relative}.wfls-status-circular-text{position:absolute;left:50%;top:50%;padding:0;margin:0;transform:translate(-50%, -50%);color:#aaa;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:1.3125rem;font-weight:300;line-height:1.5}.wfls-status-circular .wfls-status-overlay-text{position:absolute;left:50%;top:50%;padding:0;margin:0;width:200%;text-align:center;transform:translate(-50%, -50%);font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:.875rem;font-weight:normal;line-height:1.3125;opacity:0.0;color:#777}.wfls-status-warning,.wfls-status-critical,.wfls-status-payment-expiring,.wfls-status-renewing{width:100px;height:100px;margin-bottom:1rem}.wfls-status-warning svg path{fill:#fcb214}.wfls-status-critical svg path{fill:#930000}.wfls-status-payment-expiring svg rect,.wfls-status-payment-expiring svg path{fill:#930000}.wfls-status-renewing svg rect,.wfls-status-renewing svg path{fill:#11967a}#howGetIPs-preview{color:#8c8c8c}#howGetIPs-preview strong{color:#666}.wfls-scrollTop{background:#424242;bottom:30px;right:15px;position:fixed;z-index:999;display:none}.wfls-scrollTop a{background:#959595;display:block;padding:4px 5px;line-height:32px;width:32px;color:#ffffff;text-align:center}.wfls-back-icon{color:#00709e;margin-right:0.75rem;font-size:1.5rem !important}.wfls-back-link-chevron{margin-left:1rem}.wfls-back-link-chevron:first-of-type{margin-left:0}.wfls-back-link{font-weight:bold;text-decoration:none}.wfls-premium-link{font-weight:bold}.wfls-boolean-switch{border:1px solid #aaa;display:block;cursor:pointer;width:54px;height:30px;min-width:54px;min-height:30px;-moz-border-radius:15px;-webkit-border-radius:15px;border-radius:15px;line-height:30px !important;background-color:#ffffff;position:relative;box-sizing:border-box;transition:background-color 0.2s ease-in-out, border-color 0.2s ease-in-out}@media (min-width: 768px){.wfls-boolean-switch{width:34px;height:20px;min-width:34px;min-height:20px;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;line-height:20px !important}}.wfls-boolean-switch .wfls-boolean-switch-handle{position:relative;display:block;border:1px solid #aaa;background-color:#fff;width:30px;height:30px;min-width:30px;min-height:30px;-moz-border-radius:15px;-webkit-border-radius:15px;border-radius:15px;margin-top:-1px;box-sizing:border-box;left:-1px;transition:border-color 0.2s ease-in-out, left 0.2s ease-in-out}@media (min-width: 768px){.wfls-boolean-switch .wfls-boolean-switch-handle{width:20px;height:20px;min-width:20px;min-height:20px;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px}}.wfls-boolean-switch.wfls-active{border:1px solid #00709e;background-color:#00709e}.wfls-boolean-switch.wfls-active .wfls-boolean-switch-handle{border:1px solid #00709e;left:25px}@media (min-width: 768px){.wfls-boolean-switch.wfls-active .wfls-boolean-switch-handle{left:15px}}.wfls-boolean-switch.wfls-disabled{pointer-events:none;border-color:#e2e2e2}.wfls-boolean-switch.wfls-disabled .wfls-boolean-switch-handle{border-color:#e2e2e2}.wfls-boolean-switch.wfls-disabled.wfls-active{border-color:#e2e2e2;background-color:#e2e2e2}.wfls-boolean-switch.wfls-disabled.wfls-active .wfls-boolean-switch-handle{border-color:#e2e2e2}.wfls-option-checkbox,[type=checkbox].wfls-option-checkbox+label:before{content:"";font-family:"Ionicons" !important;display:block;cursor:pointer;width:30px;height:30px;min-width:30px;min-height:30px;-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;line-height:30px !important;text-align:center !important;background-color:#ffffff !important;box-shadow:0px 0px 0px 1px #aaa;color:#ffffff !important;font-size:30px !important;font-weight:normal !important}@media (min-width: 768px){.wfls-option-checkbox,[type=checkbox].wfls-option-checkbox+label:before{width:20px;height:20px;min-width:20px;min-height:20px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;line-height:21px !important;font-size:25px !important}}@media (min-width: 768px){.wfls-option-checkbox{position:relative}.wfls-option-checkbox>*{position:absolute;top:9px;left:50%;transform:translateX(-50%) translateY(-50%)}}.wfls-option-radio,[type=radio].wfls-option-radio+label:before{content:"\f401";font-family:"Ionicons" !important;display:block;cursor:pointer;width:30px;height:30px;min-width:30px;min-height:30px;line-height:30px !important;text-align:center !important;color:#ccc !important;font-size:30px !important;font-weight:normal !important}@media (min-width: 768px){.wfls-option-radio,[type=radio].wfls-option-radio+label:before{width:20px;height:20px;min-width:20px;min-height:20px;-moz-border-radius:10px;-webkit-border-radius:10px;border-radius:10px;line-height:20px !important}}[type=checkbox].wfls-option-checkbox.wfls-small+label:before{width:20px;height:20px;min-width:20px;min-height:20px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;line-height:21px !important;font-size:20px !important}[type=radio].wfls-option-radio.wfls-small+label:before{width:20px;height:20px;min-width:20px;min-height:20px;line-height:21px !important;font-size:20px !important}[type=checkbox].wfls-option-checkbox+label:before,[type=radio].wfls-option-radio+label:before{text-align:center !important;text-indent:0px;display:inline-block;vertical-align:-6px;margin:0px 5px 0px 0px;font-weight:normal;font-style:normal}[type=checkbox].wfls-option-checkbox.wfls-small+label:before,[type=radio].wfls-option-radio.wfls-small+label:before{text-indent:0px;vertical-align:-3px}.wfls-option-checkbox.wfls-checked,[type=checkbox].wfls-option-checkbox:checked+label:before{color:#ffffff !important;box-shadow:0px 0px 0px 1px #00709e !important;background-color:#00709e !important}.wfls-option-checkbox.wfls-disabled,[type=checkbox].wfls-option-checkbox:disabled+label:before{color:#f1f1f1 !important;box-shadow:0px 0px 0px 1px #e2e2e2 !important;background-color:#f1f1f1 !important}.wfls-option-checkbox.wfls-checked.wfls-disabled,[type=checkbox].wfls-option-checkbox:disabled:checked+label:before{color:#777 !important;box-shadow:0px 0px 0px 1px #e2e2e2 !important;background-color:#f1f1f1 !important}.wfls-option-radio.wfls-checked,[type=radio].wfls-option-radio:checked+label:before{content:"\f3a7";color:#00709e !important}.wfls-option-checkbox[type=checkbox],.wfls-option-checkbox[type=radio],.wfls-option-radio[type=checkbox],.wfls-option-radio[type=radio]{position:absolute;left:-9999px}.wfls-option-text input[type="text"],input.wfls-input-text{text-align:left;width:100%;height:40px;border-radius:0;border:0;background-color:#ffffff;box-shadow:1px 1px 1px 2px rgba(215,215,215,0.65)}.wfls-option-text input[type="text"]:placeholder-shown,input.wfls-input-text:placeholder-shown{font-style:italic;color:#bfbfbf}::-webkit-input-placeholder{color:#bfbfbf}:-moz-placeholder{color:#bfbfbf;opacity:1}::-moz-placeholder{color:#bfbfbf;opacity:1}:-ms-input-placeholder{color:#bfbfbf}::-ms-input-placeholder{color:#bfbfbf}::placeholder{color:#bfbfbf}.wfls-option-premium .wfls-option-title,.wfls-option-premium .wfls-option-title>ul>li,.wfls-option.wfls-disabled .wfls-option-title,.wfls-option.wfls-disabled .wfls-option-title>ul>li{color:#aaa !important}.wfls-option-premium .wfls-option-checkbox,.wfls-option-premium .wfls-option-radio,.wfls-option.wfls-disabled .wfls-option-checkbox,.wfls-option.wfls-disabled .wfls-option-radio{opacity:0.5}.wfls-block .wfls-block-content .wfls-block-labeled-value.wfls-protection-status .wfls-block-labeled-value-value{padding-top:0}.wfls-block .wfls-block-content .wfls-block-labeled-value.wfls-protection-status .wfls-block-labeled-value-value .wfls-fa{font-size:8rem}.wfls-block .wfls-block-content .wfls-block-labeled-value.wfls-protection-status .wfls-block-labeled-value-value svg{width:160px}.wfls-block .wfls-block-content .wfls-block-labeled-value.wfls-protection-status .wfls-block-labeled-value-label{font-size:1.35rem;font-weight:300;padding-bottom:0}.wfls-block .wfls-block-content .wfls-block-labeled-value.wfls-protection-status-premium .wfls-block-labeled-value-value{color:#9f9fa0}.wfls-block .wfls-block-content .wfls-block-labeled-value.wfls-protection-status-premium .wfls-block-labeled-value-value svg{fill:#9f9fa0}.wfls-block .wfls-block-content .wfls-block-labeled-value.wfls-protection-status-premium .wfls-block-labeled-value-label{color:#9f9fa0}.wfls-indeterminate-progress{-webkit-animation:wfls-indeterminate-progress-keyframes 1s steps(8, end) infinite;-o-animation:wfls-indeterminate-progress-keyframes 1s steps(8, end) infinite;animation:wfls-indeterminate-progress-keyframes 1s steps(8, end) infinite}.wfls-indeterminate-progress path{fill:#00709e}@-moz-keyframes wfls-indeterminate-progress-keyframes{to{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes wfls-indeterminate-progress-keyframes{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes wfls-indeterminate-progress-keyframes{to{-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.wfls-flex-row{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:nowrap;flex-wrap:nowrap}.wfls-flex-row .wfls-flex-row-1{-webkit-flex-grow:1;flex-grow:1}.wfls-flex-row .wfls-flex-row-0{-webkit-flex-grow:0;flex-grow:0}.wfls-flex-row.wfls-flex-row-wrappable,.wfls-flex-row.wfls-flex-row-xs-wrappable{-webkit-flex-wrap:wrap;flex-wrap:wrap}.wfls-flex-row.wfls-flex-row-equal-heights{-webkit-align-items:stretch;align-items:stretch}.wfls-flex-row .wfls-flex-item-full-width{width:100%}.wfls-flex-row .wfls-flex-item-xs-100{-webkit-flex-basis:100%;flex-basis:100%;max-width:100%}@media (min-width: 768px){.wfls-flex-row.wfls-flex-row-xs-wrappable{-webkit-flex-wrap:nowrap;flex-wrap:nowrap}.wfls-flex-row .wfls-flex-item-sm-50{-webkit-flex-basis:50%;flex-basis:50%}}.wfls-switch{display:-webkit-flex !important;display:flex !important;-webkit-align-items:stretch !important;align-items:stretch !important;-webkit-justify-content:flex-start !important;justify-content:flex-start !important;-webkit-flex-direction:row !important;flex-direction:row !important;margin:0;padding:0}.wfls-switch>li{margin:0 !important;padding:0.5rem 0.7rem !important;text-transform:uppercase;cursor:pointer;color:#aaa;font-weight:400;border-top:1px solid #bfbfbf;border-bottom:1px solid #bfbfbf;border-right:1px solid #bfbfbf}.wfls-switch>li:first-of-type{border-left:1px solid #bfbfbf;-moz-border-radius-topleft:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px}.wfls-switch>li:last-of-type{-moz-border-radius-topright:6px;-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-moz-border-radius-bottomright:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px}.wfls-switch>li.wfls-active{color:#ffffff;background-color:#00709e}.wfls-tooltip,.ui-widget.wfls-tooltip{max-width:600px;font-size:0.75rem;overflow-wrap:break-word}.wfls-widget-learning-mode{border-top:1px solid #eee;margin:0 -1rem;padding:1rem;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;-webkit-flex-direction:row;flex-direction:row}@media (min-width: 768px){.wfls-widget-learning-mode{padding:1.5rem}}.wfls-widget-learning-mode svg{width:18px}.wfls-widget-learning-mode svg path{fill:#aaa}.wfls-widget-learning-mode span{padding-left:0.5rem;font-size:.875rem;line-height:1.3125;font-weight:600}.wfls-drawer-overlay{position:fixed;top:0px;right:0px;bottom:0px;left:160px;background-color:rgba(0,0,0,0.5);z-index:9980;padding:5rem 0}.folded .wfls-drawer-overlay{left:36px}@media only screen and (max-width: 960px){.auto-fold .wfls-drawer-overlay{left:36px}}.rtl .wfls-drawer-overlay{right:160px;left:0px}.rtl .folded .wfls-drawer-overlay{right:36px}@media only screen and (max-width: 960px){.rtl .auto-fold .wfls-drawer-overlay{right:36px}}@media screen and (max-width: 782px){.wfls-drawer-overlay,.folded .wfls-drawer-overlay,.auto-fold .wfls-drawer-overlay,.rtl .wfls-drawer-overlay,.rtl .folded .wfls-drawer-overlay,.rtl .auto-fold .wfls-drawer-overlay{left:0px;right:0px}}.wfls-drawer{background-color:#ffffff;position:fixed;top:32px;bottom:0px;right:0px;z-index:9981}.wfls-drawer .wfls-modal{display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column;height:100%}.wfls-drawer .wfls-modal ul,.wfls-drawer .wfls-modal li{padding:0;margin:0}.wfls-drawer .wfls-modal .wfls-modal-header{-webkit-flex-shrink:0;flex-shrink:0;min-height:44px;padding:1rem;width:100%;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;position:relative;background-color:#00709e;color:#ffffff}.wfls-drawer .wfls-modal .wfls-modal-header .wfls-modal-header-content{max-width:75%}.wfls-drawer .wfls-modal .wfls-modal-header .wfls-modal-header-content .wfls-modal-title{font-size:1.3125rem;line-height:1.5;font-weight:300;width:100%;transition:color 0.2s ease-in}.wfls-drawer .wfls-modal .wfls-modal-header .wfls-modal-header-content .wfls-modal-subtitle{margin-top:.125rem;margin-bottom:.125rem;font-size:.575rem;color:#4f748e}.wfls-drawer .wfls-modal .wfls-modal-content{-webkit-flex-grow:1;flex-grow:1;display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column;padding:1rem}.wfls-drawer .wfls-modal .wfls-modal-content>*:first-child{margin-top:0}.wfls-drawer .wfls-modal .wfls-modal-content select,.wfls-drawer .wfls-modal .wfls-modal-content select option,.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default{font-size:0.7rem;font-weight:bold}@media (min-width: 768px){.wfls-drawer .wfls-modal .wfls-modal-content select,.wfls-drawer .wfls-modal .wfls-modal-content select option,.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default{font-size:0.8rem}}@media (min-width: 992px){.wfls-drawer .wfls-modal .wfls-modal-content select,.wfls-drawer .wfls-modal .wfls-modal-content select option,.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default{font-size:0.9rem}}.wfls-drawer .wfls-modal .wfls-modal-content .wfls-option-select-option,.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default,.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default .wfselect2-selection--single{text-align:left;height:40px;border-radius:0;border:0;background-color:#ffffff;box-shadow:1px 1px 1px 2px rgba(215,215,215,0.35)}.wfls-drawer .wfls-modal .wfls-modal-content .wfls-option-select-option .wfselect2-selection__rendered,.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default .wfselect2-selection__rendered,.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default .wfselect2-selection--single .wfselect2-selection__rendered{color:#333;line-height:40px}.wfls-drawer .wfls-modal .wfls-modal-content .wfls-option-select-option .wfselect2-selection__arrow,.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default .wfselect2-selection__arrow,.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default .wfselect2-selection--single .wfselect2-selection__arrow{height:38px}.wfls-drawer .wfls-modal .wfls-modal-content .wfls-option-select-option.wfselect2-container--disabled .wfselect2-selection__rendered,.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default.wfselect2-container--disabled .wfselect2-selection__rendered,.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default .wfselect2-selection--single.wfselect2-container--disabled .wfselect2-selection__rendered{color:#aaa}.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default .wfselect2-selection--single .wfselect2-selection__arrow b{border-color:#333 transparent transparent}.wfls-drawer .wfls-modal .wfls-modal-content .wfselect2-container--default.wfselect2-container--open .wfselect2-selection--single .wfselect2-selection__arrow b{border-color:transparent transparent #333}.wfls-drawer .wfls-modal .wfls-modal-footer{-webkit-flex-shrink:0;flex-shrink:0;min-height:44px;padding:1rem;width:100%;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;position:relative;background-color:#f1f1f1;border-top:1px solid #d9d9d9}.wfls-mobile-menu-overlay{position:fixed;top:0px;right:0px;bottom:0px;left:0px;background-color:rgba(0,0,0,0.5);z-index:100000}.wfls-mobile-menu-overlay>.wfls-mobile-menu-tap-hint{position:absolute;top:25%;left:50%;transform:translateX(-50%);color:#ffffff;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:1.3125rem;font-weight:300;line-height:1.5}.wfls-mobile-menu{position:fixed;left:50%;transform:translateX(-50%);z-index:100001}.wfls-mobile-menu>.wfls-mobile-menu-items{margin:0;padding:0 0 0.25rem 0;list-style:none}.wfls-mobile-menu>.wfls-mobile-menu-items>li{margin:0;padding:0.25rem 0}.wfls-mobile-menu>.wfls-mobile-menu-items>li>a{box-sizing:border-box}.wfls-callout-warning{background-color:#feecc4;padding:0.8rem 1.25rem}.wfls-tip-light-bulb{color:#fcb214;font-size:1.5rem;font-weight:bold}.wfls-tip-info-message{padding-left:0.5rem !important;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:.875rem;color:#2d2d2d;line-height:1.3125;font-weight:normal}#wfls-qr-code{width:175px;height:175px;margin:0 auto}@media (min-width: 500px){#wfls-qr-code{width:256px;height:256px}}#wfls-qr-code-text{max-width:100%}#wfls-activate-field{margin:0 auto;font-size:1.5rem;display:block;text-align:center}.wfls-recovery-codes{list-style-type:none}.wfls-recovery-codes li{font-family:monospace !important;text-align:center}#wfls-recovery-download .dashicons,#wfls-recovery-new-download .dashicons{line-height:26px}#wfls-ip-source-trusted-proxies{display:none}ul.wfls-option.wfls-option-howgetips .wfls-option-ip-source-details{display:-webkit-flex;display:flex;-webkit-align-items:flex-start;align-items:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:0.8rem;margin-top:0.5rem}#wfls-ip-source-preview{color:#8c8c8c}#wfls-ip-source-preview strong{color:#666}.wfls-inline-notice{background:#fff;border:1px solid #ccd0d4;border-left-color:#ffb900;border-left-width:4px;box-shadow:0 1px 1px rgba(0,0,0,0.04);padding:4px 12px;display:flex;justify-content:flex-start;align-items:center}.wfls-inline-notice>*{flex-grow:1}.wfls-inline-notice:first-child{flex-grow:0;flex-shrink:0}.wfls-inline-notice span{padding-left:0.5rem}.wfls-page-indicator{vertical-align:bottom}#wfls-customer-2fa-required-warning{margin-top:0}#wfls-activation-help-link-container{margin-right:0.5rem}.wfls-block{display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column;position:relative;margin:0 auto 0.5rem;box-sizing:border-box;background-color:#fff;box-shadow:0 0 0 1px rgba(200,215,225,0.25),0 1px 2px #e9eff3}.wfls-block.wfls-block-transparent{background-color:transparent;box-shadow:none}.wfls-block .wfls-block-banner{min-height:44px;margin:0 -1rem;padding:0;box-sizing:border-box;position:relative;background-color:#fcb214;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-direction:row;flex-direction:row}.wfls-block .wfls-block-banner>li{margin:0;padding:0.75rem 1rem}@media (min-width: 768px){.wfls-block .wfls-block-banner{margin:0 -1.5rem}.wfls-block .wfls-block-banner>li{padding:0.75rem 1.5rem}}.wfls-block .wfls-block-header{-webkit-flex-shrink:0;flex-shrink:0;-webkit-flex-grow:0;flex-grow:0;padding:1rem 0 0.5rem 0;width:100%;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;position:relative}.wfls-block .wfls-block-header.wfls-block-header-border-bottom{border-bottom:1px solid #e2e2e2;padding-left:1rem;padding-right:1rem}@media (min-width: 768px){.wfls-block .wfls-block-header.wfls-block-header-border-bottom{padding-right:1.5rem;padding-left:1.5rem}}.wfls-block .wfls-block-header .wfls-block-header-content{max-width:75%}.wfls-block .wfls-block-header .wfls-block-header-content .wfls-block-title{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:0.9rem;width:100%;transition:color 0.2s ease-in}.wfls-block .wfls-block-header .wfls-block-header-content .wfls-block-subtitle{margin-top:.125rem;margin-bottom:.125rem;font-size:0.775rem}.wfls-block .wfls-block-header .wfls-block-header-action{position:absolute;top:0;right:0;height:100%;background:none;border:0;outline:0;width:48px;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-end;justify-content:flex-end;vertical-align:middle;transition:transform 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275),color 0.2s ease-in}.wfls-block .wfls-block-header .wfls-block-header-action.wfls-block-header-action-text{width:auto}.wfls-block .wfls-block-header .wfls-block-header-action.wfls-block-header-action-text.wfls-block-header-action-text-success{color:#11967a}.wfls-block .wfls-block-header .wfls-block-header-action.wfls-block-header-action-text.wfls-block-header-action-text-warning{color:#930000}.wfls-block .wfls-block-header .wfls-block-header-action.wfls-block-header-action-text.wfls-block-header-action-text-warning a{color:#930000}.wfls-block .wfls-block-header .wfls-block-header-action .wfls-block-header-action-chevron{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJjaGV2cm9uLW9iamVjdCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiCgkgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAyNCAyNCIKCSB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHBhdGggaWQ9ImNoZXZyb24iIGQ9Ik0yMCA5bC04IDgtOC04IDEuNDE0LTEuNDE0TDEyIDE0LjE3Mmw2LjU4Ni02LjU4NiIvPgo8L3N2Zz4K");background-repeat:no-repeat;background-position:center center;width:24px;height:24px}.wfls-block .wfls-block-header .wfls-block-header-action .wfls-block-header-action-disclosure{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJkaXNjbG9zdXJlLWNsb3NlZC1vYmplY3QiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHdpZHRoPSIxMnB4IiBoZWlnaHQ9IjEycHgiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTIgMTIiCgkgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGlkPSJkaXNjbG9zdXJlLWNsb3NlZCIgZD0iTSA2IDAgbCA2IDYgLTYgNiAwIC0xMiIgZmlsbD0iIzc3NyIvPgo8L3N2Zz4=");background-repeat:no-repeat;background-position:center center;width:12px;height:12px;transition:transform 0.15s cubic-bezier(0.175, 0.885, 0.32, 1.275),background 0.2s ease-in}.wfls-block .wfls-block-header .wfls-block-header-action .wfls-block-header-action-checkbox{background-image:url(../img/checkbox.png);background-repeat:no-repeat;background-position:left center;width:29px;height:29px}.wfls-block .wfls-block-header .wfls-block-header-action .wfls-block-header-action-checkbox.wfls-checked{background-position:right center}.wfls-block .wfls-block-content{-webkit-flex-grow:1;flex-grow:1;display:none;padding:0 1rem}@media (min-width: 768px){.wfls-block .wfls-block-content{padding:0 1.5rem}}.wfls-block .wfls-block-content .wfls-block-list{margin:0 -1rem;padding:0;list-style:none}@media (min-width: 768px){.wfls-block .wfls-block-content .wfls-block-list{margin:0 -1.5rem}}.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-striped>li:nth-of-type(odd){background-color:#f9f9f9}.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-striped>li:nth-of-type(even){background-color:#ffffff}.wfls-block .wfls-block-content .wfls-block-list>li{display:block;min-height:44px;padding:0 1rem;margin:0;border-top:1px solid #e2e2e2;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between}@media (min-width: 768px){.wfls-block .wfls-block-content .wfls-block-list>li{padding:0 1.5rem}}.wfls-block .wfls-block-content .wfls-block-list>li>*:first-child{-webkit-flex-grow:1;flex-grow:1;min-width:0}.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-horizontal{box-sizing:border-box;margin-top:-1px;display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-wrap:wrap;flex-wrap:wrap}.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-horizontal>li{-webkit-flex-grow:1;flex-grow:1;-webkit-flex-basis:100%;flex-basis:100%;border-left:1px solid #e2e2e2}@media (min-width: 768px){.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-horizontal>li{-webkit-flex-basis:50%;flex-basis:50%}}@media (min-width: 992px){.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-horizontal>li{-webkit-flex-basis:25%;flex-basis:25%}}.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-horizontal.wfls-block-list-nowrap{overflow-y:auto;-webkit-flex-wrap:nowrap;flex-wrap:nowrap}.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-horizontal.wfls-block-list-nowrap>li{-webkit-flex-shrink:0;flex-shrink:0}.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-horizontal.wfls-block-list-horizontal-5>li{-webkit-flex-grow:1;flex-grow:1;-webkit-flex-basis:20%;flex-basis:20%}.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-horizontal>*:first-child{border-left:0}@media (min-width: 768px){.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-horizontal.wfls-block-list-equal>li{max-width:50%}}@media (min-width: 992px){.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-horizontal.wfls-block-list-equal>li{max-width:25%}}.wfls-block .wfls-block-content .wfls-block-list.wfls-block-list-horizontal.wfls-block-list-horizontal-5.wfls-block-list-equal>li{max-width:20%}.wfls-block .wfls-block-content .wfls-block-list .wfls-block-list-state{text-align:center}@media (min-width: 1200px){.wfls-block .wfls-block-content .wfls-block-list .wfls-block-list-state{text-align:left}}.wfls-block .wfls-block-content .wfls-block-list .wfls-block-list-state-enabled .wfls-fa{color:#11967a}.wfls-block .wfls-block-content .wfls-block-list .wfls-block-list-state-disabled .wfls-fa{color:#525355}.wfls-block .wfls-block-content .wfls-block-list .wfls-block-list-state-premium{color:#9f9fa0}.wfls-block .wfls-block-content .wfls-block-list .wfls-block-list-dismiss{padding-left:2rem;font-size:1.25rem}.wfls-block .wfls-block-content .wfls-block-list .wfls-block-list-dismiss a{color:#525355}.wfls-block .wfls-block-content:first-child>.wfls-block-list>li:first-child{border-top:none}.wfls-block .wfls-block-content .wfls-block-left-right{margin:0 -1rem;padding:0;list-style:none;display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-wrap:wrap;flex-wrap:wrap}@media (min-width: 768px){.wfls-block .wfls-block-content .wfls-block-left-right{margin:0 -1.5rem}}.wfls-block .wfls-block-content .wfls-block-left-right.wfls-block-left-right-nowrap{-webkit-flex-wrap:nowrap;flex-wrap:nowrap}.wfls-block .wfls-block-content .wfls-block-left-right>li{display:block;min-height:44px;padding:0;margin:0;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between}.wfls-block .wfls-block-content .wfls-block-left-right>li>*:first-child{-webkit-flex-grow:1;flex-grow:1;min-width:0}.wfls-block .wfls-block-content .wfls-block-left-right>li.wfls-left{text-align:left}.wfls-block .wfls-block-content .wfls-block-left-right>li.wfls-right{text-align:right}.wfls-block .wfls-block-content .wfls-block-left-center-right{margin:0 -1rem;padding:0;list-style:none;display:-webkit-flex;display:flex;-webkit-align-items:stretch;align-items:stretch;-webkit-align-content:center;align-content:center;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:nowrap;flex-wrap:nowrap}.wfls-block .wfls-block-content .wfls-block-left-center-right>li{display:block;min-height:44px;padding:0;margin:0;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-grow:1;flex-grow:1;-webkit-flex-basis:33.3333%;flex-basis:33.3333%;max-width:33.3333%}.wfls-block .wfls-block-content .wfls-block-left-center-right>li a{text-decoration:none;font-size:.875rem}.wfls-block .wfls-block-content .wfls-block-left-center-right>li.wfls-left{text-align:left}.wfls-block .wfls-block-content .wfls-block-left-center-right>li.wfls-center{text-align:center;-webkit-justify-content:center;justify-content:center}.wfls-block .wfls-block-content .wfls-block-left-center-right>li.wfls-center .wordfence-icon32{margin:0}.wfls-block .wfls-block-content .wfls-block-left-center-right>li.wfls-right{text-align:right;-webkit-justify-content:flex-end;justify-content:flex-end}.wfls-block .wfls-block-content .wfls-block-labeled-value{box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-direction:column;flex-direction:column;margin:0 -1rem;padding:1rem}@media (min-width: 768px){.wfls-block .wfls-block-content .wfls-block-labeled-value{margin:0 -1.5rem;padding:1.5rem}}.wfls-block .wfls-block-content .wfls-block-labeled-value-value{font-size:3rem;line-height:3rem;color:#9f9fa0;padding:1rem}.wfls-block .wfls-block-content .wfls-block-labeled-value-label{font-size:0.75rem;color:#9f9fa0;padding:0 1rem 1rem 1rem}.wfls-block .wfls-block-footer{-webkit-flex-shrink:0;flex-shrink:0;-webkit-flex-grow:0;flex-grow:0;min-height:44px;padding:1rem;width:100%;box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;position:relative;background-color:#e4e4e4}.wfls-block .wfls-block-footer .wfls-block-footer-content{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;width:100%}.wfls-block .wfls-block-footer .wfls-block-footer-content>*{-webkit-flex-grow:1;flex-grow:1}.wfls-block .wfls-block-footer .wfls-block-footer-content .wfls-block-title{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:0.9rem;width:100%;transition:color 0.2s ease-in}.wfls-block .wfls-block-footer .wfls-block-footer-content .wfls-block-subtitle{margin-top:.125rem;margin-bottom:.125rem;font-size:0.775rem}.wfls-block .wfls-block-footer .wfls-block-footer-action{-webkit-flex-grow:0;flex-grow:0;background:none;border:0;outline:0;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-end;justify-content:flex-end;vertical-align:middle}.wfls-block.wfls-block-no-header .wfls-block-content .wfls-block-list>li{border-top:none}.wfls-block.wfls-active .wfls-block-content,.wfls-block.wfls-always-active .wfls-block-content{display:block}.wfls-block.wfls-active>.wfls-block-header>.wfls-block-header-content>.wfls-block-title{color:#00709e}.wfls-block.wfls-active>.wfls-block-header>.wfls-block-header-content>.wfls-block-header-action>.wfls-block-header-action-disclosure{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJkaXNjbG9zdXJlLWNsb3NlZC1vYmplY3QiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHdpZHRoPSIxMnB4IiBoZWlnaHQ9IjEycHgiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTIgMTIiCgkgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGlkPSJkaXNjbG9zdXJlLWNsb3NlZCIgZD0iTSA2IDAgbCA2IDYgLTYgNiAwIC0xMiIgZmlsbD0iIzAwNzA5ZSIvPgo8L3N2Zz4=") !important;transform:rotate(90deg)}.wfls-block.wfls-disabled>.wfls-block-header>.wfls-block-header-content>.wfls-block-title,.wfls-block.wfls-disabled>.wfls-block-header>.wfls-block-header-content>.wfls-block-subtitle{color:#bfbfbf !important}.wfls-block.wfls-disabled>.wfls-block-header>.wfls-block-header-content>.wfls-block-header-action>.wfls-block-header-action-disclosure{background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJkaXNjbG9zdXJlLWNsb3NlZC1vYmplY3QiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHdpZHRoPSIxMnB4IiBoZWlnaHQ9IjEycHgiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTIgMTIiCgkgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGlkPSJkaXNjbG9zdXJlLWNsb3NlZCIgZD0iTSA2IDAgbCA2IDYgLTYgNiAwIC0xMiIgZmlsbD0iI2JkYmRiZCIvPgo8L3N2Zz4=") !important;transform:rotate(0deg)}.wfls-block.wfls-disabled>.wfls-block-content{display:none !important}.wfls-block.wfls-block-header-left .wfls-block-header-content{margin-left:48px}.wfls-block.wfls-block-header-left .wfls-block-header-action{right:auto;left:0px}.wfls-block.wfls-disabled .wfls-dashboard-item-content .wfls-block-title{color:#aaaaaa}.wfls-block.wfls-disabled .wfls-dashboard-item-content .wfls-block-subtitle{color:#8ea6be}.wfls-section-title{margin-bottom:1rem}.wfls-status-detail{box-sizing:border-box;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-direction:column;flex-direction:column;margin:0 -1rem;padding:1rem}.wfls-status-detail p{margin:0 0 0.45rem 0}.wfls-status-detail .wfls-status-circular{margin-bottom:1rem}.wfls-status-detail .wfls-status-detail-title{font-weight:700 !important;font-size:1rem !important;line-height:1.3125 !important}.wfls-status-detail .wfls-status-detail-subtitle{font-size:.875rem !important;line-height:1.3125 !important;font-weight:normal !important;text-align:center}.wfls-status-detail .wfls-status-detail-link>a{font-weight:600 !important;font-size:0.85rem !important}.wfls-block-navigation-option{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-align-content:flex-start;align-content:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-wrap:nowrap;flex-wrap:nowrap}.wfls-block-navigation-option svg.wfls-block-navigation-option-icon{width:50px;min-width:50px;fill:#9f9fa0}.wfls-block-navigation-option:hover{cursor:pointer}.wfls-block-navigation-option:hover a{text-decoration:underline}.wfls-block-navigation-option:hover svg.wfls-block-navigation-option-icon{fill:#00709e}.wfls-select-group{display:-webkit-flex !important;display:flex !important;-webkit-align-items:center !important;align-items:center !important;-webkit-justify-content:flex-start !important;justify-content:flex-start !important;-webkit-flex-direction:row !important;flex-direction:row !important;-webkit-flex-grow:0 !important;flex-grow:0 !important}.wfls-select-group .wfselect2-container{min-width:200px}@media (max-width: 767px){.wfls-select-group .wfselect2-container{max-width:100px}}.wfls-select-group .wfselect2-container--default .wfselect2-selection--single{display:block;width:100%;height:38px;padding:.5rem 1.25rem;font-size:14px;line-height:1.42857;color:#2b2b2b;background-color:#fff;background-image:none;border:1px solid #ddd;border-radius:4px;border-top-right-radius:0;border-bottom-right-radius:0;border-right:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;-o-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s;transition:border-color ease-in-out 0.15s,box-shadow ease-in-out 0.15s}.wfls-select-group .wfselect2-container--default .wfselect2-selection--single .wfselect2-selection__rendered{color:#2b2b2b;line-height:inherit}.wfls-select-group .wfselect2-container--default .wfselect2-selection--single .wfselect2-selection__arrow{height:38px}.wfls-select-group .wfls-form-control{display:inline-block;width:auto;border-top-left-radius:0;border-bottom-left-radius:0;margin-left:0}.wfls-flex-horizontal{display:-webkit-flex !important;display:flex !important;-webkit-align-items:center !important;align-items:center !important;-webkit-justify-content:flex-start !important;justify-content:flex-start !important;-webkit-flex-direction:row !important;flex-direction:row !important}.wfls-flex-horizontal>*:first-child{-webkit-flex-grow:0 !important;flex-grow:0 !important}.wfls-flex-horizontal.wfls-flex-align-left{-webkit-align-items:flex-start !important;align-items:flex-start !important}.wfls-flex-horizontal.wfls-flex-align-right{-webkit-justify-content:flex-end !important;justify-content:flex-end !important}.wfls-flex-horizontal.wfls-flex-full-width{width:100%}.wfls-flex-horizontal.wfls-flex-full-width>*:last-child{-webkit-flex-grow:1 !important;flex-grow:1 !important}.wfls-flex-horizontal.wfls-flex-full-width.wfls-flex-grow-first>*:first-child{-webkit-flex-grow:1 !important;flex-grow:1 !important}.wfls-flex-horizontal.wfls-flex-full-width.wfls-flex-grow-first>*:last-child{-webkit-flex-grow:0 !important;flex-grow:0 !important}.wfls-flex-horizontal.wfls-flex-full-width.wfls-flex-grow-all>*:first-child,.wfls-flex-horizontal.wfls-flex-full-width.wfls-flex-grow-all>*{-webkit-flex-grow:1 !important;flex-grow:1 !important}.wfls-flex-horizontal>li{padding:0;margin:0}.wfls-flex-vertical{display:-webkit-flex !important;display:flex !important;-webkit-align-items:center !important;align-items:center !important;-webkit-justify-content:flex-start !important;justify-content:flex-start !important;-webkit-flex-direction:column !important;flex-direction:column !important}.wfls-flex-vertical>*:first-child{-webkit-flex-grow:0 !important;flex-grow:0 !important}.wfls-flex-vertical.wfls-flex-align-left{-webkit-align-items:flex-start !important;align-items:flex-start !important}.wfls-flex-vertical.wfls-flex-align-right{-webkit-align-items:flex-end !important;align-items:flex-end !important}.wfls-flex-vertical.wfls-flex-full-width{-webkit-align-items:stretch !important;align-items:stretch !important}@media (max-width: 767px){.wfls-flex-vertical.wfls-flex-align-left-xs{-webkit-align-items:flex-start !important;align-items:flex-start !important}}@media (min-width: 768px) and (max-width: 991px){.wfls-flex-vertical.wfls-flex-align-left-sm{-webkit-align-items:flex-start !important;align-items:flex-start !important}}@media (min-width: 992px) and (max-width: 1199px){.wfls-flex-vertical.wfls-flex-align-left-md{-webkit-align-items:flex-start !important;align-items:flex-start !important}}@media (min-width: 1200px){.wfls-flex-vertical.wfls-flex-align-left-lg{-webkit-align-items:flex-start !important;align-items:flex-start !important}}.wfls-flex-vertical>li{padding:0;margin:0}@media (max-width: 767px){.wfls-flex-vertical-xs{display:-webkit-flex !important;display:flex !important;-webkit-align-items:center !important;align-items:center !important;-webkit-justify-content:flex-start !important;justify-content:flex-start !important;-webkit-flex-direction:column !important;flex-direction:column !important}.wfls-flex-vertical-xs>*:first-child{-webkit-flex-grow:0 !important;flex-grow:0 !important}.wfls-flex-vertical-xs.wfls-flex-align-left{-webkit-align-items:flex-start !important;align-items:flex-start !important}.wfls-flex-vertical-xs.wfls-flex-align-right{-webkit-align-items:flex-end !important;align-items:flex-end !important}.wfls-flex-vertical-xs.wfls-flex-full-width{-webkit-align-items:stretch !important;align-items:stretch !important}}ul.wfls-option,.wfls-form-field{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:nowrap;flex-wrap:nowrap;padding:1rem 0;position:relative}ul.wfls-option li,.wfls-form-field li{margin:0;padding:0}ul.wfls-option.wfls-option-no-spacing,.wfls-form-field.wfls-option-no-spacing{padding:0;margin:0}ul.wfls-option.wfls-option-toggled>*:last-child,ul.wfls-option.wfls-option-toggled-boolean-switch>*:last-child,ul.wfls-option.wfls-option-toggled-select>*:last-child,ul.wfls-option.wfls-option-select>*:last-child,ul.wfls-option.wfls-option-text>*:last-child,ul.wfls-option.wfls-option-textarea>*:last-child,ul.wfls-option.wfls-option-switch>*:last-child,ul.wfls-option.wfls-option-footer>*:last-child,.wfls-form-field.wfls-option-toggled>*:last-child,.wfls-form-field.wfls-option-toggled-boolean-switch>*:last-child,.wfls-form-field.wfls-option-toggled-select>*:last-child,.wfls-form-field.wfls-option-select>*:last-child,.wfls-form-field.wfls-option-text>*:last-child,.wfls-form-field.wfls-option-textarea>*:last-child,.wfls-form-field.wfls-option-switch>*:last-child,.wfls-form-field.wfls-option-footer>*:last-child{margin-right:1rem}@media (max-width: 768px){ul.wfls-option.wfls-option-footer,.wfls-form-field.wfls-option-footer{-webkit-flex-direction:column;flex-direction:column}}ul.wfls-option>.wfls-option-content,.wfls-form-field>.wfls-option-content{-webkit-flex-grow:1;flex-grow:1}ul.wfls-option>.wfls-option-content>ul,.wfls-form-field>.wfls-option-content>ul{display:-webkit-flex;display:flex;-webkit-align-items:flex-start;align-items:flex-start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:column;flex-direction:column;-webkit-flex-wrap:nowrap;flex-wrap:nowrap;width:100%}ul.wfls-option>.wfls-option-content>ul>*:first-child,.wfls-form-field>.wfls-option-content>ul>*:first-child{-webkit-flex-grow:1;flex-grow:1}@media (min-width: 768px){ul.wfls-option>.wfls-option-content>ul,.wfls-form-field>.wfls-option-content>ul{-webkit-flex-direction:row;flex-direction:row;-webkit-align-items:center;align-items:center}}ul.wfls-option.wfls-option-toggled-segmented *,.wfls-form-field.wfls-option-toggled-segmented *{-webkit-flex-grow:0;flex-grow:0}ul.wfls-option.wfls-option-toggled-segmented *:first-child,.wfls-form-field.wfls-option-toggled-segmented *:first-child{-webkit-flex-grow:1;flex-grow:1}ul.wfls-option.wfls-option-toggled-segmented>*:last-child,.wfls-form-field.wfls-option-toggled-segmented>*:last-child{margin-left:1rem}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-title,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-title{font-size:.8rem}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-align-content:stretch;align-content:stretch;-webkit-flex-wrap:nowrap;flex-wrap:nowrap}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label{-webkit-flex-grow:1;flex-grow:1;-webkit-flex-basis:50%;flex-basis:50%;display:block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;text-transform:uppercase;padding:.5rem 1.25rem;font-size:14px;line-height:1.42857;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;color:#00709e;background-color:#fff;border-color:#00709e;border-radius:0}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.focus{color:#00709e;background-color:#e6e6e6;border-color:#00161f}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label:hover{color:#00709e;background-color:#e6e6e6;border-color:#004561}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label:active,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.active,.wfls-open>ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-dropdown-toggle,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label:active,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.active,.wfls-open>.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-dropdown-toggle{color:#00709e;background-color:#e6e6e6;border-color:#004561}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label:active:hover,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label:active:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label:active.focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.active:hover,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.active:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.active.focus,.wfls-open>ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-dropdown-toggle:hover,.wfls-open>ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-dropdown-toggle:focus,.wfls-open>ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-dropdown-toggle.focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label:active:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label:active:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label:active.focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.active:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.active:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.active.focus,.wfls-open>.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-dropdown-toggle:hover,.wfls-open>.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-dropdown-toggle:focus,.wfls-open>.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-dropdown-toggle.focus{color:#00709e;background-color:#d4d4d4;border-color:#00161f}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label:active,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-active,.wfls-open>ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-dropdown-toggle,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label:active,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-active,.wfls-open>.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-dropdown-toggle{background-image:none}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-disabled,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label[disabled],ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label[readonly],fieldset[disabled] ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-disabled,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label[disabled],.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label[readonly],fieldset[disabled] .wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label{color:#777;background-color:#fff;border-color:#e2e2e2;cursor:not-allowed;opacity:0.75}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-disabled:hover,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-disabled:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-disabled.wfls-focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label[disabled]:hover,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label[disabled]:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label[disabled].wfls-focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label[readonly]:hover,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label[readonly]:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label[readonly].wfls-focus,fieldset[disabled] ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label:hover,fieldset[disabled] ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label:focus,fieldset[disabled] ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-disabled:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-disabled:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-disabled.wfls-focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label[disabled]:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label[disabled]:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label[disabled].wfls-focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label[readonly]:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label[readonly]:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label[readonly].wfls-focus,fieldset[disabled] .wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label:hover,fieldset[disabled] .wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label:focus,fieldset[disabled] .wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-focus{background-color:#fff;border-color:#00709e}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label .wfls-badge,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label .wfls-badge{color:#fff;background-color:#00709e}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-segment-first,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-segment-first{border-radius:4px 0 0 4px}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments label.wfls-segment-last,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments label.wfls-segment-last{border-radius:0 4px 4px 0}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio],.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]{position:absolute;left:-9999px}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label{color:#fff;background-color:#00709e;border-color:#005e85}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.focus{color:#fff;background-color:#004c6b;border-color:#000405}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:hover{color:#fff;background-color:#004c6b;border-color:#003347}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:active,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.active,.wfls-open>ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-dropdown-toggle,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:active,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.active,.wfls-open>.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-dropdown-toggle{color:#fff;background-color:#004c6b;border-color:#003347}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:active:hover,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:active:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:active.focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.active:hover,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.active:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.active.focus,.wfls-open>ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-dropdown-toggle:hover,.wfls-open>ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-dropdown-toggle:focus,.wfls-open>ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-dropdown-toggle.focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:active:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:active:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:active.focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.active:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.active:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.active.focus,.wfls-open>.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-dropdown-toggle:hover,.wfls-open>.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-dropdown-toggle:focus,.wfls-open>.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-dropdown-toggle.focus{color:#fff;background-color:#003347;border-color:#000405}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:active,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-active,.wfls-open>ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-dropdown-toggle,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:active,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-active,.wfls-open>.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-dropdown-toggle{background-image:none}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-disabled,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[disabled],ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[readonly],fieldset[disabled] ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-disabled,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[disabled],.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[readonly],fieldset[disabled] .wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label{color:#fff;background-color:#59a2c0;border-color:#5996b0;cursor:not-allowed;opacity:0.75}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-disabled:hover,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-disabled:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-disabled.wfls-focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[disabled]:hover,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[disabled]:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[disabled].wfls-focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[readonly]:hover,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[readonly]:focus,ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[readonly].wfls-focus,fieldset[disabled] ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:hover,fieldset[disabled] ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:focus,fieldset[disabled] ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-disabled:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-disabled:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-disabled.wfls-focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[disabled]:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[disabled]:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[disabled].wfls-focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[readonly]:hover,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[readonly]:focus,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label[readonly].wfls-focus,fieldset[disabled] .wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:hover,fieldset[disabled] .wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label:focus,fieldset[disabled] .wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label.wfls-focus{background-color:#00709e;border-color:#005e85}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label .wfls-badge,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:checked+label .wfls-badge{color:#00709e;background-color:#fff}ul.wfls-option.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:disabled+label,.wfls-form-field.wfls-option-toggled-segmented .wfls-option-segments [type=radio]:disabled+label{cursor:not-allowed;filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=65);opacity:.65}ul.wfls-option.wfls-option-toggled-multiple,ul.wfls-option.wfls-option-2fa-roles,.wfls-form-field.wfls-option-toggled-multiple,.wfls-form-field.wfls-option-2fa-roles{-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:flex-start;align-items:flex-start}ul.wfls-option.wfls-option-toggled-multiple>.wfls-option-title,ul.wfls-option.wfls-option-2fa-roles>.wfls-option-title,.wfls-form-field.wfls-option-toggled-multiple>.wfls-option-title,.wfls-form-field.wfls-option-2fa-roles>.wfls-option-title{font-weight:600}ul.wfls-option.wfls-option-2fa-roles .wfls-option-content ul,.wfls-form-field.wfls-option-2fa-roles .wfls-option-content ul{flex-wrap:wrap}ul.wfls-option.wfls-option-2fa-roles .wfls-option-content ul li,.wfls-form-field.wfls-option-2fa-roles .wfls-option-content ul li{flex-grow:0;display:inline;margin:0 0.75rem 0.75rem 0}ul.wfls-option.wfls-option-2fa-roles .wfls-option-content ul li label,.wfls-form-field.wfls-option-2fa-roles .wfls-option-content ul li label{display:block;font-weight:bold}ul.wfls-option.wfls-option-2fa-roles .wfls-option-content ul li select,.wfls-form-field.wfls-option-2fa-roles .wfls-option-content ul li select{display:block}ul.wfls-option.wfls-option-2fa-roles .wfls-2fa-grace-period-container label,ul.wfls-option.wfls-option-2fa-roles .wfls-2fa-grace-period-container input,.wfls-form-field.wfls-option-2fa-roles .wfls-2fa-grace-period-container label,.wfls-form-field.wfls-option-2fa-roles .wfls-2fa-grace-period-container input{vertical-align:middle}ul.wfls-option.wfls-option-2fa-roles .wfls-2fa-grace-period-container label,.wfls-form-field.wfls-option-2fa-roles .wfls-2fa-grace-period-container label{margin-bottom:0}ul.wfls-option.wfls-option-2fa-roles .wfls-2fa-grace-period-container .wfls-primary-label,.wfls-form-field.wfls-option-2fa-roles .wfls-2fa-grace-period-container .wfls-primary-label{display:block;margin-bottom:0.25rem}ul.wfls-option.wfls-option-2fa-roles .wfls-2fa-grace-period-container #wfls-grace-period-zero-warning,.wfls-form-field.wfls-option-2fa-roles .wfls-2fa-grace-period-container #wfls-grace-period-zero-warning{margin-top:0.5rem}ul.wfls-option.wfls-option-2fa-roles .wfls-2fa-notification-action,.wfls-form-field.wfls-option-2fa-roles .wfls-2fa-notification-action{margin-top:0.5rem}ul.wfls-option.wfls-option-2fa-roles .wfls-2fa-notification-action div,.wfls-form-field.wfls-option-2fa-roles .wfls-2fa-notification-action div{display:inline-block}ul.wfls-option.wfls-option-2fa-roles .wfls-2fa-notification-action label,.wfls-form-field.wfls-option-2fa-roles .wfls-2fa-notification-action label{display:block}ul.wfls-option.wfls-option-2fa-roles .wfls-2fa-notification-action h4,.wfls-form-field.wfls-option-2fa-roles .wfls-2fa-notification-action h4{margin:0}ul.wfls-option.wfls-option-2fa-roles .wfls-2fa-notification-action p,.wfls-form-field.wfls-option-2fa-roles .wfls-2fa-notification-action p{margin-top:0.5rem}ul.wfls-option.wfls-option-2fa-roles .wfls-2fa-notification-action p small,.wfls-form-field.wfls-option-2fa-roles .wfls-2fa-notification-action p small{display:inline}ul.wfls-option.wfls-option-2fa-roles small,.wfls-form-field.wfls-option-2fa-roles small{margin-top:0.5rem;display:block}ul.wfls-option>.wfls-option-spacer,.wfls-form-field>.wfls-option-spacer{-webkit-flex-grow:0;flex-grow:0;-webkit-flex-shrink:0;flex-shrink:0;margin:0 1rem 0 0;width:30px;height:30px}@media (min-width: 768px){ul.wfls-option>.wfls-option-spacer,.wfls-form-field>.wfls-option-spacer{width:20px;height:20px}}ul.wfls-option>.wfls-option-premium-lock,.wfls-form-field>.wfls-option-premium-lock{-webkit-flex-grow:0;flex-grow:0;-webkit-flex-shrink:0;flex-shrink:0;background:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB3aWR0aD0iMjRweCIgaGVpZ2h0PSIzMHB4IiB2aWV3Qm94PSIwIDAgMjQgMzAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KCTxwYXRoIGQ9Ik0yMy45NDksMTQuMjMzYy0wLjM3OSwtMC4zOSAtMC45MDQsLTAuNjA2IC0xLjQ0OCwtMC41OTVsLTAuNjgzLDBsMCwtNC4wOTJjMC4wMzIsLTIuNTM1IC0wLjk4NiwtNC45NzMgLTIuODEyLC02LjczMWMtMS43NTksLTEuODI4IC00LjE5OCwtMi44NDcgLTYuNzM0LC0yLjgxNWMtMi41MzYsLTAuMDMyIC00Ljk3NiwwLjk4NyAtNi43MzQsMi44MTVjLTEuODI2LDEuNzU4IC0yLjg0NCw0LjE5NiAtMi44MTIsNi43MzFsMCw0LjA4OWwtMC42OCwwYy0wLjU0NCwtMC4wMTEgLTEuMDY5LDAuMjA1IC0xLjQ0OCwwLjU5NWMtMC4zOTUsMC4zODIgLTAuNjEyLDAuOTEyIC0wLjU5OCwxLjQ2MWwwLDEyLjI2NmMtMC4wMTEsMC41NDQgMC4yMDQsMS4wNjkgMC41OTUsMS40NDhjMC4zNzksMC4zOTEgMC45MDQsMC42MDYgMS40NDgsMC41OTVsMjAuNDU4LDBjMC4wMDMsMCAwLjAwNiwwIDAuMDEsMGMxLjExNywwIDIuMDM2LC0wLjkxOSAyLjAzNiwtMi4wMzdjMCwtMC4wMDMgMCwtMC4wMDYgMCwtMC4wMDlsMCwtMTIuMjYzYzAuMDExLC0wLjU0NCAtMC4yMDYsLTEuMDY5IC0wLjU5OCwtMS40NDhsMCwtMC4wMVptLTYuMjExLC0wLjU5NWwtMTAuOTE5LDBsMCwtNC4wOTJjLTAuMDIyLC0xLjQ1MSAwLjU1NywtMi44NDggMS41OTksLTMuODU4YzEuMDA5LC0xLjA0MiAyLjQwNywtMS42MjEgMy44NTcsLTEuNTk4YzEuNDUxLC0wLjAyMyAyLjg0OCwwLjU1NiAzLjg1OCwxLjU5OGMxLjA0MiwxLjAwOSAxLjYyMSwyLjQwNyAxLjU5OCwzLjg1OGwwLjAwNyw0LjA5MloiIGZpbGw9IiNkMWQxZDEiLz4KPC9zdmc+");background-repeat:no-repeat;background-position:center center;background-size:contain;margin:0 1rem 0 0;width:30px;height:30px}@media (min-width: 768px){ul.wfls-option>.wfls-option-premium-lock,.wfls-form-field>.wfls-option-premium-lock{margin:0 2rem 0 1rem;width:20px;height:20px}}ul.wfls-option>.wfls-option-checkbox,.wfls-form-field>.wfls-option-checkbox{-webkit-flex-grow:0;flex-grow:0;-webkit-flex-shrink:0;flex-shrink:0;margin:0 1rem 0 0}ul.wfls-option>.wfls-boolean-switch,.wfls-form-field>.wfls-boolean-switch{-webkit-flex-grow:0;flex-grow:0;-webkit-flex-shrink:0;flex-shrink:0;margin:0 1rem 0 0}@media (min-width: 768px){ul.wfls-option>.wfls-boolean-switch,.wfls-form-field>.wfls-boolean-switch{margin:0 1rem 0 1rem}}ul.wfls-option.wfls-option-no-spacing>.wfls-boolean-switch,.wfls-form-field.wfls-option-no-spacing>.wfls-boolean-switch{margin:0}ul.wfls-option>.wfls-option-radio-container,.wfls-form-field>.wfls-option-radio-container{-webkit-flex-grow:0;flex-grow:0;-webkit-flex-shrink:0;flex-shrink:0;margin:0 1rem 0 0}@media (min-width: 768px){ul.wfls-option>.wfls-option-radio-container,.wfls-form-field>.wfls-option-radio-container{margin:0 2rem 0 1rem}}ul.wfls-option>.wfls-option-radio-container [type=radio].wfls-option-radio+label:before,.wfls-form-field>.wfls-option-radio-container [type=radio].wfls-option-radio+label:before{margin:0}ul.wfls-option>li>.wfls-option-title,ul.wfls-option>.wfls-option-title,ul.wfls-option>.wfls-option-content>ul>.wfls-option-title,.wfls-form-field>li>.wfls-option-title,.wfls-form-field>.wfls-option-title,.wfls-form-field>.wfls-option-content>ul>.wfls-option-title{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:0.9rem;color:#2b2b2b;padding-right:0.5rem}ul.wfls-option>li>.wfls-option-title.wfls-option-title-top,ul.wfls-option>.wfls-option-title.wfls-option-title-top,ul.wfls-option>.wfls-option-content>ul>.wfls-option-title.wfls-option-title-top,.wfls-form-field>li>.wfls-option-title.wfls-option-title-top,.wfls-form-field>.wfls-option-title.wfls-option-title-top,.wfls-form-field>.wfls-option-content>ul>.wfls-option-title.wfls-option-title-top{-webkit-align-self:flex-start;align-self:flex-start}ul.wfls-option>li>.wfls-option-title.wfls-option-title-bottom,ul.wfls-option>.wfls-option-title.wfls-option-title-bottom,ul.wfls-option>.wfls-option-content>ul>.wfls-option-title.wfls-option-title-bottom,.wfls-form-field>li>.wfls-option-title.wfls-option-title-bottom,.wfls-form-field>.wfls-option-title.wfls-option-title-bottom,.wfls-form-field>.wfls-option-content>ul>.wfls-option-title.wfls-option-title-bottom{-webkit-align-self:flex-end;align-self:flex-end}ul.wfls-option .wfls-option-subtitle,.wfls-form-field .wfls-option-subtitle{padding-top:0.25rem;font-size:0.75rem}ul.wfls-option .wfls-flex-vertical .wfls-option-title,.wfls-form-field .wfls-flex-vertical .wfls-option-title{padding-bottom:0.75rem}ul.wfls-option.wfls-flex-vertical>.wfls-option-subtitle,.wfls-form-field.wfls-flex-vertical>.wfls-option-subtitle{padding-top:0.25rem !important;font-size:0.75rem !important}ul.wfls-option .wfls-option-checkboxes,ul.wfls-option .wfls-option-checkboxes>ul,.wfls-form-field .wfls-option-checkboxes,.wfls-form-field .wfls-option-checkboxes>ul{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:nowrap;flex-wrap:nowrap}ul.wfls-option .wfls-option-checkboxes.wfls-option-checkboxes-wrap,ul.wfls-option .wfls-option-checkboxes>ul.wfls-option-checkboxes-wrap,.wfls-form-field .wfls-option-checkboxes.wfls-option-checkboxes-wrap,.wfls-form-field .wfls-option-checkboxes>ul.wfls-option-checkboxes-wrap{-webkit-flex-wrap:wrap;flex-wrap:wrap}ul.wfls-option .wfls-option-checkboxes,.wfls-form-field .wfls-option-checkboxes{margin-top:0.5rem}ul.wfls-option .wfls-option-checkboxes>ul,.wfls-form-field .wfls-option-checkboxes>ul{margin-top:0.5rem;margin-right:1rem}@media (min-width: 768px){ul.wfls-option .wfls-option-checkboxes>ul,.wfls-form-field .wfls-option-checkboxes>ul{margin-right:1.5rem}}ul.wfls-option .wfls-option-checkboxes>ul:last-of-type,.wfls-form-field .wfls-option-checkboxes>ul:last-of-type{margin-right:0}ul.wfls-option .wfls-option-checkboxes>ul>.wfls-option-checkbox,.wfls-form-field .wfls-option-checkboxes>ul>.wfls-option-checkbox{margin:0 1rem 0 0}ul.wfls-option li.wfls-option-text,ul.wfls-option li.wfls-option-textarea,ul.wfls-option td.wfls-option-text,.wfls-form-field li.wfls-option-text,.wfls-form-field li.wfls-option-textarea,.wfls-form-field td.wfls-option-text{padding-top:0.5rem}@media (min-width: 768px){ul.wfls-option li.wfls-option-text,ul.wfls-option li.wfls-option-textarea,ul.wfls-option td.wfls-option-text,.wfls-form-field li.wfls-option-text,.wfls-form-field li.wfls-option-textarea,.wfls-form-field td.wfls-option-text{-webkit-flex-grow:1;flex-grow:1;text-align:right;padding-left:1rem;padding-top:0}}ul.wfls-option li.wfls-option-text>input[type="text"],.wfls-form-field li.wfls-option-text>input[type="text"]{max-width:240px}@media (min-width: 768px){ul.wfls-option li.wfls-option-text>input[type="text"],.wfls-form-field li.wfls-option-text>input[type="text"]{max-width:280px}}@media (min-width: 992px){ul.wfls-option li.wfls-option-text>input[type="text"],.wfls-form-field li.wfls-option-text>input[type="text"]{max-width:340px}}ul.wfls-option li.wfls-option-text.wfls-option-full-width>input[type="text"],.wfls-form-field li.wfls-option-text.wfls-option-full-width>input[type="text"]{max-width:100%;width:100%}ul.wfls-option li.wfls-option-textarea,.wfls-form-field li.wfls-option-textarea{min-width:150px;max-width:240px}@media (min-width: 768px){ul.wfls-option li.wfls-option-textarea,.wfls-form-field li.wfls-option-textarea{min-width:200px;max-width:400px}}@media (min-width: 992px){ul.wfls-option li.wfls-option-textarea,.wfls-form-field li.wfls-option-textarea{min-width:250px;max-width:500px}}ul.wfls-option li.wfls-option-textarea>textarea,.wfls-form-field li.wfls-option-textarea>textarea{width:100%;height:80px;min-width:150px;max-width:240px}@media (min-width: 768px){ul.wfls-option li.wfls-option-textarea>textarea,.wfls-form-field li.wfls-option-textarea>textarea{min-width:200px;max-width:280px}}@media (min-width: 992px){ul.wfls-option li.wfls-option-textarea>textarea,.wfls-form-field li.wfls-option-textarea>textarea{min-width:250px;max-width:340px}}ul.wfls-option li.wfls-option-textarea>.wfls-flex-vertical>li>textarea,.wfls-form-field li.wfls-option-textarea>.wfls-flex-vertical>li>textarea{width:100%;height:80px;box-sizing:border-box}ul.wfls-option li.wfls-option-textarea>.wfls-flex-vertical>li.wfls-option-subtitle,.wfls-form-field li.wfls-option-textarea>.wfls-flex-vertical>li.wfls-option-subtitle{width:100%;text-align:left}ul.wfls-option li.wfls-option-switch,.wfls-form-field li.wfls-option-switch{-webkit-flex-grow:1;flex-grow:1}ul.wfls-option li.wfls-option-switch.wfls-right .wfls-switch,.wfls-form-field li.wfls-option-switch.wfls-right .wfls-switch{justify-content:flex-end !important}ul.wfls-option li.wfls-option-select,.wfls-form-field li.wfls-option-select{-webkit-flex-grow:1;flex-grow:1;text-align:right}ul.wfls-option li.wfls-option-select select,ul.wfls-option li.wfls-option-select select option,ul.wfls-option li.wfls-option-select .wfselect2-container--default,.wfls-form-field li.wfls-option-select select,.wfls-form-field li.wfls-option-select select option,.wfls-form-field li.wfls-option-select .wfselect2-container--default{font-size:0.7rem;font-weight:bold}@media (min-width: 768px){ul.wfls-option li.wfls-option-select select,ul.wfls-option li.wfls-option-select select option,ul.wfls-option li.wfls-option-select .wfselect2-container--default,.wfls-form-field li.wfls-option-select select,.wfls-form-field li.wfls-option-select select option,.wfls-form-field li.wfls-option-select .wfselect2-container--default{font-size:0.8rem}}@media (min-width: 992px){ul.wfls-option li.wfls-option-select select,ul.wfls-option li.wfls-option-select select option,ul.wfls-option li.wfls-option-select .wfselect2-container--default,.wfls-form-field li.wfls-option-select select,.wfls-form-field li.wfls-option-select select option,.wfls-form-field li.wfls-option-select .wfselect2-container--default{font-size:0.9rem}}ul.wfls-option li.wfls-option-select .wfls-option-select-option,ul.wfls-option li.wfls-option-select .wfselect2-container--default,ul.wfls-option li.wfls-option-select .wfselect2-container--default .wfselect2-selection--single,.wfls-form-field li.wfls-option-select .wfls-option-select-option,.wfls-form-field li.wfls-option-select .wfselect2-container--default,.wfls-form-field li.wfls-option-select .wfselect2-container--default .wfselect2-selection--single{text-align:left;height:40px;border-radius:0;border:0;background-color:#fff;box-shadow:1px 1px 1px 2px rgba(215,215,215,0.35)}ul.wfls-option li.wfls-option-select .wfls-option-select-option .wfselect2-selection__rendered,ul.wfls-option li.wfls-option-select .wfselect2-container--default .wfselect2-selection__rendered,ul.wfls-option li.wfls-option-select .wfselect2-container--default .wfselect2-selection--single .wfselect2-selection__rendered,.wfls-form-field li.wfls-option-select .wfls-option-select-option .wfselect2-selection__rendered,.wfls-form-field li.wfls-option-select .wfselect2-container--default .wfselect2-selection__rendered,.wfls-form-field li.wfls-option-select .wfselect2-container--default .wfselect2-selection--single .wfselect2-selection__rendered{color:#333;line-height:40px}ul.wfls-option li.wfls-option-select .wfls-option-select-option .wfselect2-selection__arrow,ul.wfls-option li.wfls-option-select .wfselect2-container--default .wfselect2-selection__arrow,ul.wfls-option li.wfls-option-select .wfselect2-container--default .wfselect2-selection--single .wfselect2-selection__arrow,.wfls-form-field li.wfls-option-select .wfls-option-select-option .wfselect2-selection__arrow,.wfls-form-field li.wfls-option-select .wfselect2-container--default .wfselect2-selection__arrow,.wfls-form-field li.wfls-option-select .wfselect2-container--default .wfselect2-selection--single .wfselect2-selection__arrow{height:38px}ul.wfls-option li.wfls-option-select .wfls-option-select-option.wfselect2-container--disabled .wfselect2-selection__rendered,ul.wfls-option li.wfls-option-select .wfselect2-container--default.wfselect2-container--disabled .wfselect2-selection__rendered,ul.wfls-option li.wfls-option-select .wfselect2-container--default .wfselect2-selection--single.wfselect2-container--disabled .wfselect2-selection__rendered,.wfls-form-field li.wfls-option-select .wfls-option-select-option.wfselect2-container--disabled .wfselect2-selection__rendered,.wfls-form-field li.wfls-option-select .wfselect2-container--default.wfselect2-container--disabled .wfselect2-selection__rendered,.wfls-form-field li.wfls-option-select .wfselect2-container--default .wfselect2-selection--single.wfselect2-container--disabled .wfselect2-selection__rendered{color:#aaa}ul.wfls-option li.wfls-option-select .wfselect2-container--default .wfselect2-selection--single .wfselect2-selection__arrow b,.wfls-form-field li.wfls-option-select .wfselect2-container--default .wfselect2-selection--single .wfselect2-selection__arrow b{border-color:#333 transparent transparent}ul.wfls-option li.wfls-option-select .wfselect2-container--default.wfselect2-container--open .wfselect2-selection--single .wfselect2-selection__arrow b,.wfls-form-field li.wfls-option-select .wfselect2-container--default.wfselect2-container--open .wfselect2-selection--single .wfselect2-selection__arrow b{border-color:transparent transparent #333}ul.wfls-option.wfls-option-token select,.wfls-form-field.wfls-option-token select{width:240px}@media (min-width: 768px){ul.wfls-option.wfls-option-token select,.wfls-form-field.wfls-option-token select{width:280px}}@media (min-width: 992px){ul.wfls-option.wfls-option-token select,.wfls-form-field.wfls-option-token select{width:320px}}ul.wfls-option.wfls-option-token .wfselect2-container--default,ul.wfls-option.wfls-option-token .wfselect2-container--default .wfselect2-selection--multiple,.wfls-form-field.wfls-option-token .wfselect2-container--default,.wfls-form-field.wfls-option-token .wfselect2-container--default .wfselect2-selection--multiple{border-color:#e2e2e2}ul.wfls-option.wfls-option-token .wfselect2-container--default .wfselect2-selection__choice,ul.wfls-option.wfls-option-token .wfselect2-container--default .wfselect2-selection--multiple .wfselect2-selection__choice,.wfls-form-field.wfls-option-token .wfselect2-container--default .wfselect2-selection__choice,.wfls-form-field.wfls-option-token .wfselect2-container--default .wfselect2-selection--multiple .wfselect2-selection__choice{background-color:#fff;border-color:#e2e2e2;padding:0.5rem}ul.wfls-option.wfls-option-token .wfselect2-container--default .wfselect2-search__field,ul.wfls-option.wfls-option-token .wfselect2-container--default .wfselect2-selection--multiple .wfselect2-search__field,.wfls-form-field.wfls-option-token .wfselect2-container--default .wfselect2-search__field,.wfls-form-field.wfls-option-token .wfselect2-container--default .wfselect2-selection--multiple .wfselect2-search__field{margin-right:5px;margin-top:5px;padding:0.5rem 0}ul.wfls-option.wfls-child-option,.wfls-form-field.wfls-child-option{margin-left:calc(1rem + 20px)}ul.wfls-option .wfls-woocommerce-customer-integration-message,.wfls-form-field .wfls-woocommerce-customer-integration-message{margin-top:0;margin-bottom:0.5rem}ul.wfls-option .wfls-woocommerce-customer-integration-message small,.wfls-form-field .wfls-woocommerce-customer-integration-message small{margin-top:0}#wfls-option-require-2fa{margin-top:1rem}#wfls-option-require-2fa>li>ul{margin-left:1rem}@media (min-width: 768px){#wfls-option-require-2fa>li>ul{margin-left:1.5rem}}#wfls-option-require-2fa>li>ul:first-of-type{margin-left:0}#wfls-option-require-2fa>li>ul>.wfls-option-checkbox{margin:0 1rem 0 0}.wfls-option-sub{padding-left:2rem !important;margin-left:30px !important}.wfls-select2-suppress-dropdown .wfselect2-results,.wfls-select2-suppress-dropdown .wfselect2-dropdown{display:none}.wfls-options-controls{direction:ltr;background:#ffffff;border-bottom:1px solid #e2e2e2;position:absolute;left:160px;right:0px;top:46px;z-index:900;padding-left:15px;padding-right:15px}@media (min-width: 616px){.wfls-options-controls{position:fixed}}.wfls-options-controls .wfls-block{margin:0}@media (min-width: 782px){.wfls-options-controls .wfls-block{margin:0.5rem 0}}.wfls-options-controls,.sticky-menu .wfls-options-controls{top:32px}.folded .wfls-options-controls{left:36px}@media only screen and (max-width: 960px){.auto-fold .wfls-options-controls{left:36px}}.rtl .wfls-options-controls{right:160px;left:0px}.rtl .folded .wfls-options-controls{right:36px}@media only screen and (max-width: 960px){.rtl .auto-fold .wfls-options-controls{right:36px}}@media screen and (max-width: 782px){.wfls-options-controls,.folded .wfls-options-controls,.auto-fold .wfls-options-controls,.rtl .wfls-options-controls,.rtl .folded .wfls-options-controls,.rtl .auto-fold .wfls-options-controls{left:-10px;right:0px}}.wfls-options-controls-spacer{height:45px}@media (min-width: 782px){.wfls-options-controls-spacer{height:75px}}.wfls-options-controls-spacer,.sticky-menu .wfls-options-controls-spacer{top:61px}.wordfence .wfselect2-container .wfselect2-selection--single{border:1px solid #dadada;font-weight:normal;font-size:0.8rem}.wfls-block-title h3{margin:0;font-size:1.25rem}.wfls-save-banner{position:sticky;z-index:9998;top:32px;padding:0.5rem 1.5rem 0.5rem 0;background-color:#FFFFFF;margin-bottom:0.5rem;text-align:right}#wfls-notices{margin-top:15px}#wfls-notices .wfls-admin-notice{margin-left:0px;margin-right:0px}.wfls-success-text,.wfls-notice-text{display:inline-block;vertical-align:middle;line-height:1.3;font-size:16px;font-weight:bold;font-style:italic}.wfls-notice{margin:12px 0;padding:8px;background-color:#ffffe0;border:1px solid #ffd975;border-width:1px 1px 1px 10px}.wfls-notice-text{color:#6d798c}.wfls-success{margin:12px 0;padding:8px;background-color:#ffffff;border:1px solid #16bc9b;border-width:1px 1px 1px 10px}.wfls-success-text{color:#11967a}.wfls-premium-callout{border:1px solid #dfdfdf;background-color:#ffffff;padding:16px;margin:20px 0 0;text-align:center}.wfls-premium-callout ul{margin:8px 0;padding:0 0 0 15px}.wfls-premium-callout ul li{list-style-type:disc;margin:0;padding:0}.wfls-premium-callout .center{text-align:center;margin:0}.wfls-premium-callout .button-primary{text-align:center;text-transform:uppercase;font-weight:bold;background-color:#00709e}table.wfls-table{background-color:transparent;border-collapse:collapse;border-spacing:0}table.wfls-table td,table.wfls-table th{padding:0}.wfls-table caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}.wfls-table th{text-align:left}.wfls-table{width:100%;max-width:100%;margin-bottom:20px}.wfls-table>thead>tr>th,.wfls-table>thead>tr>td,.wfls-table>tbody>tr>th,.wfls-table>tbody>tr>td,.wfls-table>tfoot>tr>th,.wfls-table>tfoot>tr>td{padding:8px;line-height:1.42857;vertical-align:top;border-top:1px solid #ddd}.wfls-table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.wfls-table>caption+thead>tr:first-child>th,.wfls-table>caption+thead>tr:first-child>td,.wfls-table>colgroup+thead>tr:first-child>th,.wfls-table>colgroup+thead>tr:first-child>td,.wfls-table>thead:first-child>tr:first-child>th,.wfls-table>thead:first-child>tr:first-child>td{border-top:0}.wfls-table>tbody+tbody{border-top:2px solid #ddd}.wfls-table .wfls-table{background-color:#fff}.wfls-table-header-separators>thead>tr>th{border-left:2px solid #ddd}.wfls-table-header-separators>thead>tr>th:first-child{border-left:0}.wfls-table-condensed>thead>tr>th,.wfls-table-condensed>thead>tr>td,.wfls-table-condensed>tbody>tr>th,.wfls-table-condensed>tbody>tr>td,.wfls-table-condensed>tfoot>tr>th,.wfls-table-condensed>tfoot>tr>td{padding:5px}.wfls-table-expanded>thead>tr>th,.wfls-table-expanded>thead>tr>td,.wfls-table-expanded>tbody>tr>th,.wfls-table-expanded>tbody>tr>td,.wfls-table-expanded>tfoot>tr>th,.wfls-table-expanded>tfoot>tr>td{padding:0.5rem 1rem}@media (min-width: 768px){.wfls-table-expanded>thead>tr>th,.wfls-table-expanded>thead>tr>td,.wfls-table-expanded>tbody>tr>th,.wfls-table-expanded>tbody>tr>td,.wfls-table-expanded>tfoot>tr>th,.wfls-table-expanded>tfoot>tr>td{padding:1rem 1.5rem}}.wfls-table-bordered{border:1px solid #ddd}.wfls-table-bordered>thead>tr>th,.wfls-table-bordered>thead>tr>td,.wfls-table-bordered>tbody>tr>th,.wfls-table-bordered>tbody>tr>td,.wfls-table-bordered>tfoot>tr>th,.wfls-table-bordered>tfoot>tr>td{border:1px solid #ddd}.wfls-table-bordered>thead>tr>th,.wfls-table-bordered>thead>tr>td{border-bottom-width:2px}.wfls-table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.wfls-table-hover>tbody>tr:hover{background-color:#f5f5f5}table.wfls-table col[class*="col-"]{position:static;float:none;display:table-column}table.wfls-table td[class*="col-"],table.wfls-table th[class*="col-"]{position:static;float:none;display:table-cell}.wfls-table>thead>tr>td.active,.wfls-table>thead>tr>th.active,.wfls-table>thead>tr.active>td,.wfls-table>thead>tr.active>th,.wfls-table>tbody>tr>td.active,.wfls-table>tbody>tr>th.active,.wfls-table>tbody>tr.active>td,.wfls-table>tbody>tr.active>th,.wfls-table>tfoot>tr>td.active,.wfls-table>tfoot>tr>th.active,.wfls-table>tfoot>tr.active>td,.wfls-table>tfoot>tr.active>th{background-color:#f5f5f5}.wfls-table-hover>tbody>tr>td.active:hover,.wfls-table-hover>tbody>tr>th.active:hover,.wfls-table-hover>tbody>tr.active:hover>td,.wfls-table-hover>tbody>tr:hover>.active,.wfls-table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.wfls-table>thead>tr>td.success,.wfls-table>thead>tr>th.success,.wfls-table>thead>tr.success>td,.wfls-table>thead>tr.success>th,.wfls-table>tbody>tr>td.success,.wfls-table>tbody>tr>th.success,.wfls-table>tbody>tr.success>td,.wfls-table>tbody>tr.success>th,.wfls-table>tfoot>tr>td.success,.wfls-table>tfoot>tr>th.success,.wfls-table>tfoot>tr.success>td,.wfls-table>tfoot>tr.success>th{background-color:#dff0d8}.wfls-table-hover>tbody>tr>td.success:hover,.wfls-table-hover>tbody>tr>th.success:hover,.wfls-table-hover>tbody>tr.success:hover>td,.wfls-table-hover>tbody>tr:hover>.success,.wfls-table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.wfls-table>thead>tr>td.info,.wfls-table>thead>tr>th.info,.wfls-table>thead>tr.info>td,.wfls-table>thead>tr.info>th,.wfls-table>tbody>tr>td.info,.wfls-table>tbody>tr>th.info,.wfls-table>tbody>tr.info>td,.wfls-table>tbody>tr.info>th,.wfls-table>tfoot>tr>td.info,.wfls-table>tfoot>tr>th.info,.wfls-table>tfoot>tr.info>td,.wfls-table>tfoot>tr.info>th{background-color:#d9edf7}.wfls-table-hover>tbody>tr>td.info:hover,.wfls-table-hover>tbody>tr>th.info:hover,.wfls-table-hover>tbody>tr.info:hover>td,.wfls-table-hover>tbody>tr:hover>.info,.wfls-table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.wfls-table>thead>tr>td.warning,.wfls-table>thead>tr>th.warning,.wfls-table>thead>tr.warning>td,.wfls-table>thead>tr.warning>th,.wfls-table>tbody>tr>td.warning,.wfls-table>tbody>tr>th.warning,.wfls-table>tbody>tr.warning>td,.wfls-table>tbody>tr.warning>th,.wfls-table>tfoot>tr>td.warning,.wfls-table>tfoot>tr>th.warning,.wfls-table>tfoot>tr.warning>td,.wfls-table>tfoot>tr.warning>th{background-color:#fcf8e3}.wfls-table-hover>tbody>tr>td.warning:hover,.wfls-table-hover>tbody>tr>th.warning:hover,.wfls-table-hover>tbody>tr.warning:hover>td,.wfls-table-hover>tbody>tr:hover>.warning,.wfls-table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.wfls-table>thead>tr>td.danger,.wfls-table>thead>tr>th.danger,.wfls-table>thead>tr.danger>td,.wfls-table>thead>tr.danger>th,.wfls-table>tbody>tr>td.danger,.wfls-table>tbody>tr>th.danger,.wfls-table>tbody>tr.danger>td,.wfls-table>tbody>tr.danger>th,.wfls-table>tfoot>tr>td.danger,.wfls-table>tfoot>tr>th.danger,.wfls-table>tfoot>tr.danger>td,.wfls-table>tfoot>tr.danger>th{background-color:#f2dede}.wfls-table-hover>tbody>tr>td.danger:hover,.wfls-table-hover>tbody>tr>th.danger:hover,.wfls-table-hover>tbody>tr.danger:hover>td,.wfls-table-hover>tbody>tr:hover>.danger,.wfls-table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.wfls-table-responsive{overflow-x:auto;min-height:0.01%}@media screen and (max-width: 767px){.wfls-table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.wfls-table-responsive>.wfls-table{margin-bottom:0}.wfls-table-responsive>.wfls-table>thead>tr>th,.wfls-table-responsive>.wfls-table>thead>tr>td,.wfls-table-responsive>.wfls-table>tbody>tr>th,.wfls-table-responsive>.wfls-table>tbody>tr>td,.wfls-table-responsive>.wfls-table>tfoot>tr>th,.wfls-table-responsive>.wfls-table>tfoot>tr>td{white-space:nowrap}.wfls-table-responsive>.wfls-table-bordered{border:0}.wfls-table-responsive>.wfls-table-bordered>thead>tr>th:first-child,.wfls-table-responsive>.wfls-table-bordered>thead>tr>td:first-child,.wfls-table-responsive>.wfls-table-bordered>tbody>tr>th:first-child,.wfls-table-responsive>.wfls-table-bordered>tbody>tr>td:first-child,.wfls-table-responsive>.wfls-table-bordered>tfoot>tr>th:first-child,.wfls-table-responsive>.wfls-table-bordered>tfoot>tr>td:first-child{border-left:0}.wfls-table-responsive>.wfls-table-bordered>thead>tr>th:last-child,.wfls-table-responsive>.wfls-table-bordered>thead>tr>td:last-child,.wfls-table-responsive>.wfls-table-bordered>tbody>tr>th:last-child,.wfls-table-responsive>.wfls-table-bordered>tbody>tr>td:last-child,.wfls-table-responsive>.wfls-table-bordered>tfoot>tr>th:last-child,.wfls-table-responsive>.wfls-table-bordered>tfoot>tr>td:last-child{border-right:0}.wfls-table-responsive>.wfls-table-bordered>tbody>tr:last-child>th,.wfls-table-responsive>.wfls-table-bordered>tbody>tr:last-child>td,.wfls-table-responsive>.wfls-table-bordered>tfoot>tr:last-child>th,.wfls-table-responsive>.wfls-table-bordered>tfoot>tr:last-child>td{border-bottom:0}}.wfls-sortable{position:relative;padding-right:2rem !important}.wfls-sortable .wfls-sorted-ascending,.wfls-sortable .wfls-sorted-descending{display:none;position:absolute;right:1rem;top:50%;transform:translateY(-50%)}.wfls-sortable.wfls-unsorted:hover .wfls-sorted-ascending{display:block}.wfls-sortable.wfls-unsorted,.wfls-sortable.wfls-sorted-ascending,.wfls-sortable.wfls-sorted-descending{cursor:pointer}.wfls-sortable.wfls-unsorted:hover,.wfls-sortable.wfls-sorted-ascending:hover,.wfls-sortable.wfls-sorted-descending:hover{background-color:#e0e0e0}.wfls-sortable.wfls-sorted-ascending,.wfls-sortable.wfls-sorted-descending{background-color:#e0e0e0}.wfls-sortable.wfls-sorted-ascending .wfls-sorted-ascending{display:block}.wfls-sortable.wfls-sorted-descending .wfls-sorted-descending{display:block}.wfls-nav{margin-bottom:0;padding-left:0;list-style:none}.wfls-nav:before,.wfls-nav:after{content:" ";display:table}.wfls-nav:after{clear:both}.wfls-nav>li{position:relative;display:block}.wfls-nav>li>a{position:relative;display:block;padding:8px 12px}.wfls-nav>li>a:hover,.wfls-nav>li>a:focus{text-decoration:none;background-color:#e2e2e2}.wfls-nav>li.wfls-disabled>a{color:#777}.wfls-nav>li.wfls-disabled>a:hover,.wfls-nav>li.wfls-disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.wfls-nav .wfls-open>a,.wfls-nav .wfls-open>a:hover,.wfls-nav .wfls-open>a:focus{background-color:#e2e2e2;border-color:#00709e}.wfls-nav>li>a>img{max-width:none}.wfls-nav-tabs{border-bottom:1px solid #d0d0d0}.wfls-nav-tabs>li{float:left;margin-bottom:-1px}.wfls-nav-tabs>li>a{margin-right:2px;line-height:1.42857;border:1px solid transparent;border-radius:4px 4px 0 0}.wfls-nav-tabs>li>a:hover{border-color:#e2e2e2 #e2e2e2 #d0d0d0}.wfls-nav-tabs>li.wfls-active>a,.wfls-nav-tabs>li.wfls-active>a:hover,.wfls-nav-tabs>li.wfls-active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.wfls-nav-pills>li{float:left}.wfls-nav-pills>li>a{border-radius:4px;text-decoration:none;position:relative;display:block;padding:8px 12px}.wfls-nav-pills>li>a:hover,.wfls-nav-pills>li>a:focus{text-decoration:none !important;background-color:#e2e2e2}.wfls-nav-pills>li+li{margin-left:2px}.wfls-nav-pills>li.wfls-active>a,.wfls-nav-pills>li.wfls-active>a:hover,.wfls-nav-pills>li.wfls-active>a:focus{color:#fff;background-color:#00709e}.wfls-nav-pills.wfls-nav-pills-bordered>li>a{border:1px solid #e2e2e2}.wfls-nav-pills.wfls-nav-pills-connected>li>a{border-radius:0;border-right-width:0px}.wfls-nav-pills.wfls-nav-pills-connected>li+li{margin-left:0}.wfls-nav-pills.wfls-nav-pills-connected>li.active+li>a{border-left-width:0px}.wfls-nav-pills.wfls-nav-pills-connected>li:first-of-type>a{-moz-border-radius:4px 0 0 4px;-webkit-border-radius:4px;border-radius:4px 0 0 4px}.wfls-nav-pills.wfls-nav-pills-connected>li:last-of-type>a{-moz-border-radius:0 4px 4px 0;-webkit-border-radius:0;border-radius:0 4px 4px 0;border-right-width:1px}.wfls-nav-stacked>li{float:none}.wfls-nav-stacked>li+li{margin-top:2px;margin-left:0}.wfls-nav-justified,.wfls-nav-tabs.wfls-nav-justified{width:100%}.wfls-nav-justified>li,.wfls-nav-tabs.wfls-nav-justified>li{float:none}.wfls-nav-justified>li>a,.wfls-nav-tabs.wfls-nav-justified>li>a{text-align:center;margin-bottom:5px}.wfls-nav-justified>.wfls-dropdown .wfls-dropdown-menu{top:auto;left:auto}@media (min-width: 768px){.wfls-nav-justified>li,.wfls-nav-tabs.wfls-nav-justified>li{display:table-cell;width:1%}.wfls-nav-justified>li>a,.wfls-nav-tabs.wfls-nav-justified>li>a{margin-bottom:0}}.wfls-nav-tabs-justified,.wfls-nav-tabs.wfls-nav-justified{border-bottom:0}.wfls-nav-tabs-justified>li>a,.wfls-nav-tabs.wfls-nav-justified>li>a{margin-right:0;border-radius:4px}.wfls-nav-tabs-justified>.wfls-active>a,.wfls-nav-tabs.wfls-nav-justified>.wfls-active>a,.wfls-nav-tabs-justified>.wfls-active>a:hover,.wfls-nav-tabs.wfls-nav-justified>.wfls-active>a:hover,.wfls-nav-tabs-justified>.wfls-active>a:focus,.wfls-nav-tabs.wfls-nav-justified>.wfls-active>a:focus{border:1px solid #ddd}@media (min-width: 768px){.wfls-nav-tabs-justified>li>a,.wfls-nav-tabs.wfls-nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.wfls-nav-tabs-justified>.wfls-active>a,.wfls-nav-tabs.wfls-nav-justified>.wfls-active>a,.wfls-nav-tabs-justified>.wfls-active>a:hover,.wfls-nav-tabs.wfls-nav-justified>.wfls-active>a:hover,.wfls-nav-tabs-justified>.wfls-active>a:focus,.wfls-nav-tabs.wfls-nav-justified>.wfls-active>a:focus{border-bottom-color:#fff}}.wfls-tab-content>.wfls-tab-pane{display:none}.wfls-tab-content>.wfls-active{display:block}.wfls-nav-tabs .wfls-dropdown-menu{margin-top:-1px;-moz-border-radius-topleft:0;-webkit-border-top-left-radius:0;border-top-left-radius:0;-moz-border-radius-topright:0;-webkit-border-top-right-radius:0;border-top-right-radius:0}.wfls-navbar-brand{float:left;padding:12px 8px;font-size:18px;line-height:20px;margin:10px 0 0 0}.wfls-navbar-brand:hover,.wfls-navbar-brand:focus{text-decoration:none}.wfls-navbar-brand>img{display:block}@media (min-width: 768px){.navbar>.container .wfls-navbar-brand,.navbar>.container-fluid .wfls-navbar-brand{margin-left:-8px}}.wfls-caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.wfls-dropup,.wfls-dropdown{position:relative}.wfls-dropdown-toggle:focus{outline:0}.wfls-dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;text-align:left;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.wfls-dropdown-menu .wfls-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.wfls-dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857;color:#333;text-decoration:none;white-space:nowrap}.wfls-dropdown-menu>li>a:hover,.wfls-dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.wfls-dropdown-menu>.wfls-active>a,.wfls-dropdown-menu>.wfls-active>a:hover,.wfls-dropdown-menu>.wfls-active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#00709e}.wfls-dropdown-menu>.wfls-disabled>a,.wfls-dropdown-menu>.wfls-disabled>a:hover,.wfls-dropdown-menu>.wfls-disabled>a:focus{color:#777}.wfls-dropdown-menu>.wfls-disabled>a:hover,.wfls-dropdown-menu>.wfls-disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.wfls-open>.wfls-dropdown-menu{display:block}.wfls-open>a{outline:0}.wfls-dropdown-menu-right{left:auto;right:0}.wfls-dropdown-menu-left{left:0;right:auto}.wfls-dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857;color:#777;white-space:nowrap}.wfls-dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.wfls-pull-right>.wfls-dropdown-menu{right:0;left:auto}.wfls-dropup .wfls-caret,.wfls-navbar-fixed-bottom .wfls-dropdown .wfls-caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9;content:""}.wfls-dropup .wfls-dropdown-menu,.wfls-navbar-fixed-bottom .wfls-dropdown .wfls-dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width: 768px){.wfls-navbar-right .wfls-dropdown-menu{right:0;left:auto}.wfls-navbar-right .wfls-dropdown-menu-left{left:0;right:auto}}.wfls-mobile-dropdown{border:1px solid #ccc;margin-left:.5em;padding:5px 10px;font-size:14px;line-height:24px;margin:10px 10px 0 0;background:#f1f1f1;color:#000;font-weight:600;text-decoration:none}.wfls-alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.wfls-alert h4{margin-top:0;color:inherit}.wfls-alert .wfls-alert-link{font-weight:bold}.wfls-alert>p,.wfls-alert>ul{margin-bottom:0}.wfls-alert>p+p{margin-top:5px}.wfls-alert-dismissable,.wfls-alert-dismissible{padding-right:35px}.wfls-alert-dismissable .close,.wfls-alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.wfls-alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.wfls-alert-success hr{border-top-color:#c9e2b3}.wfls-alert-success .alert-link{color:#2b542c}.wfls-alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.wfls-alert-info hr{border-top-color:#a6e1ec}.wfls-alert-info .alert-link{color:#245269}.wfls-alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.wfls-alert-warning hr{border-top-color:#f7e1b5}.wfls-alert-warning .alert-link{color:#66512c}.wfls-alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.wfls-alert-danger hr{border-top-color:#e4b9c0}.wfls-alert-danger .alert-link{color:#843534}#wfls-onboarding-standalone-modal{margin-bottom:1rem}#wfls-onboarding-standalone-modal #wfls-onboarding-standalone-modal-header{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-flex-direction:row;flex-direction:row;background-color:#00709e;color:#ffffff;padding:1rem 2rem}#wfls-onboarding-standalone-modal #wfls-onboarding-standalone-modal-header #wfls-onboarding-standalone-modal-dismiss{font-size:1.35rem;color:#ffffff;text-decoration:none}#wfls-onboarding-standalone-modal #wfls-onboarding-standalone-modal-header #wfls-onboarding-standalone-modal-dismiss:hover{color:#f1f1f1}#wfls-onboarding-standalone-modal #wfls-onboarding-standalone-modal-header>*:last-child{-webkit-flex-grow:1;flex-grow:1;text-align:right}#wfls-onboarding-standalone-modal #wfls-onboarding-standalone-modal-content{background-color:#ffffff;padding:1rem 2rem}#wfls-onboarding-standalone-modal #wfls-onboarding-standalone-modal-content>p{margin-bottom:0}#wfls-onboarding-standalone-modal #wfls-onboarding-standalone-modal-content>p:first-child{margin-top:0}#wfls-onboarding-standalone-modal #wfls-onboarding-standalone-modal-footer{background-color:#525355;color:#ffffff;padding:0.5rem 2rem}#wfls-onboarding-standalone-modal #wfls-onboarding-standalone-modal-footer>ul{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-end;justify-content:flex-end;-webkit-flex-direction:row;flex-direction:row;margin:0;padding:0}#wfls-onboarding-standalone-modal #wfls-onboarding-standalone-modal-footer>ul>li{margin:0;padding:0 0 0 1rem}.wfls-grace-period-container{display:flex;align-items:center;justify-content:left;margin-bottom:1rem;margin-top:1rem}.wfls-grace-period-container .wfls-grace-period-input-container{margin-right:1.5rem;text-align:center}.wfls-grace-period-container .wfls-grace-period-input-container label{display:block;font-weight:bold}.wfls-grace-period-container .wfls-grace-period-input-container input{width:3em;text-align:center}#wfls-reset-grace-period-failed{text-align:center}.wfls-recaptcha-score-history .wfls-recaptcha-chart-container{position:relative;display:inline-block;width:350px;max-width:100%}@media screen and (max-width: 480px){.wfls-recaptcha-score-history .wfls-recaptcha-chart-container{width:70vw}}#toplevel_page_WFLS .wp-menu-image img{max-width:16px;max-height:16px} login-security/classes/model/request.php000064400000013405147207020750014466 0ustar00get(Controller_Settings::OPTION_IP_SOURCE); } $record = $this->_ip($source); if (is_array($record)) { list($ip, $variable) = $record; if (isset($_SERVER[$variable]) && strpos($_SERVER[$variable], ',') !== false) { $items = preg_replace('/[\s,]/', '', explode(',', $_SERVER[$variable])); $output = ''; foreach ($items as $i) { if ($ip == $i) { $output .= ', ' . esc_html($i) . ''; } else { $output .= ', ' . esc_html($i); } } return substr($output, 2); } return '' . esc_html($ip) . ''; } return false; } public function ip($refreshCache = false) { if (WORDFENCE_LS_FROM_CORE) { return \wfUtils::getIP($refreshCache); } if (!isset($this->_cachedIP) || $refreshCache) { $this->_cachedIP = $this->_ip(Controller_Settings::shared()->get(Controller_Settings::OPTION_IP_SOURCE), Controller_Settings::shared()->trusted_proxies()); } return $this->_cachedIP[0]; //Format is array(, ) } public function ip_for_field($source, $trusted_proxies) { return $this->_ip($source, $trusted_proxies); } protected function _ip($source = null, $trusted_proxies = null) { if ($source === null) { $source = Controller_Settings::shared()->get(Controller_Settings::OPTION_IP_SOURCE); } $possible_ips = $this->_possible_ips($source); if ($trusted_proxies === null) { $trusted_proxies = array(); } return $this->_find_preferred_ip($possible_ips, $trusted_proxies); } protected function _possible_ips($source = null) { $defaultIP = (is_array($_SERVER) && isset($_SERVER[self::IP_SOURCE_REMOTE_ADDR])) ? array($_SERVER[self::IP_SOURCE_REMOTE_ADDR], self::IP_SOURCE_REMOTE_ADDR) : array('127.0.0.1', self::IP_SOURCE_REMOTE_ADDR); if ($source) { if ($source == self::IP_SOURCE_REMOTE_ADDR) { return array($defaultIP); } $check = array( array((isset($_SERVER[$source]) ? $_SERVER[$source] : ''), $source), $defaultIP, ); return $check; } $check = array($defaultIP); if (isset($_SERVER[self::IP_SOURCE_X_FORWARDED_FOR])) { $check[] = array($_SERVER[self::IP_SOURCE_X_FORWARDED_FOR], self::IP_SOURCE_X_FORWARDED_FOR); } if (isset($_SERVER[self::IP_SOURCE_X_REAL_IP])) { $check[] = array($_SERVER[self::IP_SOURCE_X_REAL_IP], self::IP_SOURCE_X_REAL_IP); } return $check; } protected function _find_preferred_ip($possible_ips, $trusted_proxies) { $privates = array(); foreach ($possible_ips as $entry) { list($value, $var) = $entry; if (is_array($value)) { // An array of IPs foreach ($value as $index => $j) { if (!Model_IP::is_valid_ip($j)) { $j = preg_replace('/:\d+$/', '', $j); //Strip off port if present } if (Model_IP::is_valid_ip($j)) { if (Model_IP::is_ipv6_mapped_ipv4($j)) { $j = Model_IP::inet_ntop(Model_IP::inet_pton($j)); } foreach ($trusted_proxies as $proxy) { if (!empty($proxy)) { if (Controller_Whitelist::shared()->ip_in_range($j, $proxy) && $index < count($value) - 1) { continue 2; } } } if (filter_var($j, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) { $privates[] = array($j, $var); } else { return array($j, $var); } } } continue; } $skipToNext = false; $separators = array(',', ' ', "\t"); foreach ($separators as $char) { // A list of IPs separated by : 192.0.2.15,192.0.2.35,192.0.2.254 if (strpos($value, $char) !== false) { $sp = explode($char, $value); $sp = array_reverse($sp); foreach ($sp as $index => $j) { $j = trim($j); if (!Model_IP::is_valid_ip($j)) { $j = preg_replace('/:\d+$/', '', $j); //Strip off port } if (Model_IP::is_valid_ip($j)) { if (Model_IP::is_ipv6_mapped_ipv4($j)) { $j = Model_IP::inet_ntop(Model_IP::inet_pton($j)); } foreach ($trusted_proxies as $proxy) { if (!empty($proxy)) { if (Controller_Whitelist::shared()->ip_in_range($j, $proxy) && $index < count($sp) - 1) { continue 2; } } } if (filter_var($j, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) { $privates[] = array($j, $var); } else { return array($j, $var); } } } $skipToNext = true; break; } } if ($skipToNext) { continue; } //Skip to next item because this one had a comma/space/tab, but we didn't find a valid, non-private address // A literal IP if (!Model_IP::is_valid_ip($value)) { $value = preg_replace('/:\d+$/', '', $value); //Strip off port } if (Model_IP::is_valid_ip($value)) { if (Model_IP::is_ipv6_mapped_ipv4($value)) { $value = Model_IP::inet_ntop(Model_IP::inet_pton($value)); } if (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) { $privates[] = array($value, $var); } else { return array($value, $var); } } } if (count($privates) > 0) { return $privates[0]; } return false; } }login-security/classes/model/compat.php000064400000000370147207020750014256 0ustar00_id = $id; $this->_title = $title; $this->_helpURL = $helpURL; $this->_helpLink = $helpLink; } public function __get($name) { switch ($name) { case 'id': return $this->_id; case 'title': return $this->_title; case 'helpURL': return $this->_helpURL; case 'helpLink': return $this->_helpLink; } throw new \OutOfBoundsException('Invalid key: ' . $name); } }login-security/classes/model/view/tab.php000064400000001630147207020750014513 0ustar00_id = $id; $this->_a = $a; $this->_tabTitle = $tabTitle; $this->_pageTitle = $pageTitle; $this->_active = $active; } public function __get($name) { switch ($name) { case 'id': return $this->_id; case 'a': return $this->_a; case 'tabTitle': return $this->_tabTitle; case 'pageTitle': return $this->_pageTitle; case 'active': return $this->_active; } throw new \OutOfBoundsException('Invalid key: ' . $name); } }login-security/classes/model/crypto.php000064400000020161147207020750014313 0ustar00set(Controller_Settings::OPTION_SHARED_HASH_SECRET_KEY, bin2hex(self::random_bytes(32))); Controller_Settings::shared()->set(Controller_Settings::OPTION_SHARED_SYMMETRIC_SECRET_KEY, bin2hex(self::random_bytes(32))); Controller_Settings::shared()->set(Controller_Settings::OPTION_LAST_SECRET_REFRESH, Controller_Time::time(), true); } /** * Returns the secret for hashing. * * @return string */ public static function shared_hash_secret() { return Controller_Settings::shared()->get(Controller_Settings::OPTION_SHARED_HASH_SECRET_KEY); } /** * Returns the secret for symmetric encryption. * * @return string */ public static function shared_symmetric_secret() { return Controller_Settings::shared()->get(Controller_Settings::OPTION_SHARED_SYMMETRIC_SECRET_KEY); } /** * Returns whether or not the installation has the required crypto support for this to work. * * @return bool */ public static function has_required_crypto_functions() { if (function_exists('openssl_get_publickey') && function_exists('openssl_get_cipher_methods')) { $ciphers = openssl_get_cipher_methods(); return array_search('aes-256-cbc', $ciphers) !== false; } return false; } /** * Utility */ public static function random_bytes($bytes) { $bytes = (int) $bytes; if (function_exists('random_bytes')) { try { $rand = random_bytes($bytes); if (is_string($rand) && self::strlen($rand) === $bytes) { return $rand; } } catch (\Exception $e) { // Fall through } catch (\TypeError $e) { // Fall through } catch (\Error $e) { // Fall through } } if (function_exists('mcrypt_create_iv')) { // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.mcrypt_create_ivDeprecatedRemoved,PHPCompatibility.Extensions.RemovedExtensions.mcryptDeprecatedRemoved,PHPCompatibility.Constants.RemovedConstants.mcrypt_dev_urandomDeprecatedRemoved $rand = @mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM); if (is_string($rand) && self::strlen($rand) === $bytes) { return $rand; } } if (function_exists('openssl_random_pseudo_bytes')) { $rand = @openssl_random_pseudo_bytes($bytes, $strong); if (is_string($rand) && self::strlen($rand) === $bytes) { return $rand; } } // Last resort is insecure $return = ''; for ($i = 0; $i < $bytes; $i++) { $return .= chr(mt_rand(0, 255)); } return $return; } /** * Polyfill for random_int. * * @param int $min * @param int $max * @return int */ public static function random_int($min = 0, $max = 0x7FFFFFFF) { if (function_exists('random_int')) { try { return random_int($min, $max); } catch (\Exception $e) { // Fall through } catch (\TypeError $e) { // Fall through } catch (\Error $e) { // Fall through } } $diff = $max - $min; $bytes = self::random_bytes(4); if ($bytes === false || self::strlen($bytes) != 4) { throw new \RuntimeException("Unable to get 4 bytes"); } $val = @unpack("Nint", $bytes); $val = $val['int'] & 0x7FFFFFFF; $fp = (float) $val / 2147483647.0; // convert to [0,1] return (int) (round($fp * $diff) + $min); } public static function uuid() { return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', // 32 bits for "time_low" self::random_int(0, 0xffff), self::random_int(0, 0xffff), // 16 bits for "time_mid" self::random_int(0, 0xffff), // 16 bits for "time_hi_and_version", // four most significant bits holds version number 4 self::random_int(0, 0x0fff) | 0x4000, // 16 bits, 8 bits for "clk_seq_hi_res", // 8 bits for "clk_seq_low", // two most significant bits holds zero and one for variant DCE1.1 self::random_int(0, 0x3fff) | 0x8000, // 48 bits for "node" self::random_int(0, 0xffff), self::random_int(0, 0xffff), self::random_int(0, 0xffff) ); } /** * Set the mbstring internal encoding to a binary safe encoding when func_overload * is enabled. * * When mbstring.func_overload is in use for multi-byte encodings, the results from * strlen() and similar functions respect the utf8 characters, causing binary data * to return incorrect lengths. * * This function overrides the mbstring encoding to a binary-safe encoding, and * resets it to the users expected encoding afterwards through the * `reset_mbstring_encoding` function. * * It is safe to recursively call this function, however each * `_mbstring_binary_safe_encoding()` call must be followed up with an equal number * of `_reset_mbstring_encoding()` calls. * * @see Model_Crypto::_reset_mbstring_encoding * * @staticvar array $encodings * @staticvar bool $overloaded * * @param bool $reset Optional. Whether to reset the encoding back to a previously-set encoding. * Default false. */ protected static function _mbstring_binary_safe_encoding($reset = false) { static $encodings = array(); static $overloaded = null; if (is_null($overloaded)) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.mbstring_func_overloadDeprecated $overloaded = function_exists('mb_internal_encoding') && (ini_get('mbstring.func_overload') & 2); } if (false === $overloaded) { return; } if (!$reset) { $encoding = mb_internal_encoding(); array_push($encodings, $encoding); mb_internal_encoding('ISO-8859-1'); } if ($reset && $encodings) { $encoding = array_pop($encodings); mb_internal_encoding($encoding); } } /** * Reset the mbstring internal encoding to a users previously set encoding. * * @see Model_Crypto::_mbstring_binary_safe_encoding */ protected static function _reset_mbstring_encoding() { self::_mbstring_binary_safe_encoding(true); } /** * @param callable $function * @param array $args * @return mixed */ protected static function _call_mb_string_function($function, $args) { self::_mbstring_binary_safe_encoding(); $return = call_user_func_array($function, $args); self::_reset_mbstring_encoding(); return $return; } /** * Multibyte safe strlen. * * @param $binary * @return int */ public static function strlen($binary) { $args = func_get_args(); return self::_call_mb_string_function('strlen', $args); } /** * @param $haystack * @param $needle * @param int $offset * @return int */ public static function stripos($haystack, $needle, $offset = 0) { $args = func_get_args(); return self::_call_mb_string_function('stripos', $args); } /** * @param $string * @return mixed */ public static function strtolower($string) { $args = func_get_args(); return self::_call_mb_string_function('strtolower', $args); } /** * @param $string * @param $start * @param $length * @return mixed */ public static function substr($string, $start, $length = null) { if ($length === null) { $length = self::strlen($string); } return self::_call_mb_string_function('substr', array( $string, $start, $length )); } /** * @param $haystack * @param $needle * @param int $offset * @return mixed */ public static function strpos($haystack, $needle, $offset = 0) { $args = func_get_args(); return self::_call_mb_string_function('strpos', $args); } /** * @param string $haystack * @param string $needle * @param int $offset * @param int $length * @return mixed */ public static function substr_count($haystack, $needle, $offset = 0, $length = null) { if ($length === null) { $length = self::strlen($haystack); } return self::_call_mb_string_function('substr_count', array( $haystack, $needle, $offset, $length )); } /** * @param $string * @return mixed */ public static function strtoupper($string) { $args = func_get_args(); return self::_call_mb_string_function('strtoupper', $args); } /** * @param string $haystack * @param string $needle * @param int $offset * @return mixed */ public static function strrpos($haystack, $needle, $offset = 0) { $args = func_get_args(); return self::_call_mb_string_function('strrpos', $args); } }login-security/classes/model/notice.php000064400000002144147207020750014255 0ustar00_id = $id; $this->_severity = $severity; $this->_messageHTML = $messageHTML; $this->_category = $category; } public function display_notice() { $severityClass = 'notice-info'; if ($this->_severity == self::SEVERITY_CRITICAL) { $severityClass = 'notice-error'; } else if ($this->_severity == self::SEVERITY_WARNING) { $severityClass = 'notice-warning'; } echo '

' . $this->_messageHTML . '

' . sprintf(__('Dismiss', 'wordfence'), esc_attr($this->_id)) . '

'; } }login-security/classes/model/tokenbucket.php000064400000013516147207020750015317 0ustar00_identifier = $identifier; $this->_bucketSize = $bucketSize; $this->_tokensPerSecond = $tokensPerSecond; $this->_backing = $backing; if ($backing == self::BACKING_REDIS) { $this->_redis = new \Redis(); $this->_redis->pconnect('127.0.0.1'); } } /** * Attempts to acquire a lock for the bucket. * * @param int $timeout * @return bool Whether or not the lock was acquired. */ private function _lock($timeout = 30) { if ($this->_backing == self::BACKING_WP_OPTIONS) { $start = microtime(true); while (!$this->_wp_options_create_lock($this->_identifier)) { if (microtime(true) - $start > $timeout) { return false; } usleep(5000); // 5 ms } return true; } else if ($this->_backing == self::BACKING_REDIS) { if ($this->_redis === false) { return false; } $start = microtime(true); while (!$this->_redis->setnx('lock:' . $this->_identifier, '1')) { if (microtime(true) - $start > $timeout) { return false; } usleep(5000); // 5 ms } $this->_redis->expire('lock:' . $this->_identifier, 30); return true; } return false; } private function _unlock() { if ($this->_backing == self::BACKING_WP_OPTIONS) { $this->_wp_options_release_lock($this->_identifier); } else if ($this->_backing == self::BACKING_REDIS) { if ($this->_redis === false) { return; } $this->_redis->del('lock:' . $this->_identifier); } } private function _wp_options_create_lock($name, $timeout = null) { //Our own version of WP_Upgrader::create_lock global $wpdb; if (!$timeout) { $timeout = 3600; } $lock_option = 'wfls_' . $name . '.lock'; $lock_result = $wpdb->query($wpdb->prepare("INSERT IGNORE INTO `{$wpdb->options}` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, 'no')", $lock_option, time())); if (!$lock_result) { $lock_result = get_option($lock_option); if (!$lock_result) { return false; } if ($lock_result > (time() - $timeout)) { return false; } $this->_wp_options_release_lock($name); return $this->_wp_options_create_lock($name, $timeout); } return true; } private function _wp_options_release_lock($name) { return delete_option('wfls_' . $name . '.lock'); } /** * Atomically checks the available token count, creating the initial record if needed, and updates the available token count if the requested number of tokens is available. * * @param int $tokenCount * @return bool Whether or not there were enough tokens to satisfy the request. */ public function consume($tokenCount = 1) { if (!$this->_lock()) { return false; } if ($this->_backing == self::BACKING_WP_OPTIONS) { $record = get_transient('wflsbucket:' . $this->_identifier); } else if ($this->_backing == self::BACKING_REDIS) { $record = $this->_redis->get('bucket:' . $this->_identifier); } else { $this->_unlock(); return false; } if ($record === false) { if ($tokenCount > $this->_bucketSize) { $this->_unlock(); return false; } $this->_bootstrap($this->_bucketSize - $tokenCount); $this->_unlock(); return true; } $tokens = min($this->_secondsToTokens(microtime(true) - (float) $record), $this->_bucketSize); if ($tokenCount > $tokens) { $this->_unlock(); return false; } if ($this->_backing == self::BACKING_WP_OPTIONS) { set_transient('wflsbucket:' . $this->_identifier, (string) (microtime(true) - $this->_tokensToSeconds($tokens - $tokenCount)), ceil($this->_tokensToSeconds($this->_bucketSize))); } else if ($this->_backing == self::BACKING_REDIS) { $this->_redis->set('bucket:' . $this->_identifier, (string) (microtime(true) - $this->_tokensToSeconds($tokens - $tokenCount))); } $this->_unlock(); return true; } public function reset() { if (!$this->_lock()) { return false; } if ($this->_backing == self::BACKING_WP_OPTIONS) { delete_transient('wflsbucket:' . $this->_identifier); } else if ($this->_backing == self::BACKING_REDIS) { $this->_redis->del('bucket:' . $this->_identifier); } $this->_unlock(); } /** * Creates an initial record with the given number of tokens. * * @param int $initialTokens */ protected function _bootstrap($initialTokens) { $microtime = microtime(true) - $this->_tokensToSeconds($initialTokens); if ($this->_backing == self::BACKING_WP_OPTIONS) { set_transient('wflsbucket:' . $this->_identifier, (string) $microtime, ceil($this->_tokensToSeconds($this->_bucketSize))); } else if ($this->_backing == self::BACKING_REDIS) { $this->_redis->set('bucket:' . $this->_identifier, (string) $microtime); } } protected function _tokensToSeconds($tokens) { return $tokens / $this->_tokensPerSecond; } protected function _secondsToTokens($seconds) { return (int) $seconds * $this->_tokensPerSecond; } } login-security/classes/model/script.php000064400000002375147207020750014306 0ustar00registered) { wp_enqueue_script($this->handle); } else { wp_enqueue_script($this->handle, $this->source, $this->dependencies, $this->version); } if ($this->translationObjectName && !empty($this->translations)) { wp_localize_script($this->handle, $this->translationObjectName, $this->translations); } } public function isEnqueued() { return wp_script_is($this->handle); } public function renderInline() { if (empty($this->source)) return; ?> handle, $this->source, $this->dependencies, $this->version); return parent::register(); } public function withTranslation($placeholder, $translation) { $this->translations[$placeholder] = $translation; return $this; } public function withTranslations($translations) { $this->translations = $translations; return $this; } public function setTranslationObjectName($name) { $this->translationObjectName = $name; return $this; } }login-security/classes/model/2fainitializationdata.php000064400000002412147207020750017244 0ustar00user = $user; $this->raw_secret = Model_Crypto::random_bytes(20); } public function get_user() { return $this->user; } public function get_raw_secret() { return $this->raw_secret; } public function get_base32_secret() { if ($this->base32_secret === null) $this->base32_secret = Utility_BaseConversion::base32_encode($this->raw_secret); return $this->base32_secret; } private function generate_otp_url() { return "otpauth://totp/" . rawurlencode(preg_replace('~^https?://(?:www\.)?~i', '', home_url()) . ':' . $this->user->user_login) . '?secret=' . $this->get_base32_secret() . '&algorithm=SHA1&digits=6&period=30&issuer=' . rawurlencode(preg_replace('~^https?://(?:www\.)?~i', '', home_url())); } public function get_otp_url() { if ($this->otp_url === null) $this->otp_url = $this->generate_otp_url(); return $this->otp_url; } public function get_recovery_codes() { if ($this->recovery_codes === null) $this->recovery_codes = Controller_Users::shared()->regenerate_recovery_codes(); return $this->recovery_codes; } }login-security/classes/model/settings.php000064400000001203147207020750014627 0ustar00path = WORDFENCE_LS_PATH . 'views'; $this->view = $view; $this->data = $data; } /** * @return string * @throws ViewNotFoundException */ public function render() { $view = preg_replace('/\.{2,}/', '.', $this->view); $path = $this->path . '/' . $view . $this->file_extension; if (!file_exists($path)) { throw new ViewNotFoundException('The view ' . $path . ' does not exist or is not readable.'); } extract($this->data, EXTR_SKIP); ob_start(); /** @noinspection PhpIncludeInspection */ include $path; return ob_get_clean(); } /** * @return string */ public function __toString() { try { return $this->render(); } catch (ViewNotFoundException $e) { return defined('WP_DEBUG') && WP_DEBUG ? $e->getMessage() : 'The view could not be loaded.'; } } /** * @param $data * @return $this */ public function addData($data) { $this->data = array_merge($data, $this->data); return $this; } /** * @return array */ public function getData() { return $this->data; } /** * @param array $data * @return $this */ public function setData($data) { $this->data = $data; return $this; } /** * @return string */ public function getView() { return $this->view; } /** * @param string $view * @return $this */ public function setView($view) { $this->view = $view; return $this; } /** * Prevent POP */ public function __wakeup() { $this->path = WORDFENCE_LS_PATH . 'views'; $this->view = null; $this->data = array(); $this->file_extension = '.php'; } } class ViewNotFoundException extends \Exception { } login-security/classes/model/ip.php000064400000012004147207020750013400 0ustar00 $group) { if ($group == '0000' && !$done_collapse) { if ($in_collapse) { $groups[$index] = ''; continue; } $groups[$index] = ':'; $in_collapse = true; continue; } if ($in_collapse) { $done_collapse = true; } $groups[$index] = ltrim($groups[$index], '0'); if (strlen($groups[$index]) === 0) { $groups[$index] = '0'; } } $ip = join(':', array_filter($groups, 'strlen')); $ip = str_replace(':::', '::', $ip); return $ip == ':' ? '::' : $ip; } return false; } /** * Returns the packed binary representation of an IP address from the human readable version. * * @param string $ip * @return string */ public static function inet_pton($ip) { if (self::has_ipv6()) { $pton = @inet_pton($ip); if ($pton === false) { return false; } } else { if (preg_match('/^(?:\d{1,3}(?:\.|$)){4}/', $ip)) { // IPv4 $octets = explode('.', $ip); $pton = chr($octets[0]) . chr($octets[1]) . chr($octets[2]) . chr($octets[3]); } else if (preg_match('/^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/i', $ip)) { // IPv6 if ($ip === '::') { $pton = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; } else { $colon_count = substr_count($ip, ':'); $dbl_colon_pos = strpos($ip, '::'); if ($dbl_colon_pos !== false) { $ip = str_replace('::', str_repeat(':0000', (($dbl_colon_pos === 0 || $dbl_colon_pos === strlen($ip) - 2) ? 9 : 8) - $colon_count) . ':', $ip); $ip = trim($ip, ':'); } $ip_groups = explode(':', $ip); $ipv6_bin = ''; foreach ($ip_groups as $ip_group) { $ipv6_bin .= pack('H*', str_pad($ip_group, 4, '0', STR_PAD_LEFT)); } if (Model_Crypto::strlen($ipv6_bin) == 16) { $pton = $ipv6_bin; } else { return false; } } } else if (preg_match('/^(?:\:(?:\:0{1,4}){0,4}\:|(?:0{1,4}\:){5})ffff\:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i', $ip, $matches)) { // IPv4 mapped IPv6 $octets = explode('.', $matches[1]); $pton = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff" . chr($octets[0]) . chr($octets[1]) . chr($octets[2]) . chr($octets[3]); } else { return false; } } $pton = str_pad($pton, 16, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00", STR_PAD_LEFT); return $pton; } /** * Verify PHP was compiled with IPv6 support. * * Some hosts appear to not have inet_ntop, and others appear to have inet_ntop but are unable to process IPv6 addresses. * * @return bool */ public static function has_ipv6() { return defined('AF_INET6'); } /** * Expands a compressed printable representation of an IPv6 address. * * @param string $ip * @return string */ public static function expand_ipv6_address($ip) { $hex = bin2hex(self::inet_pton($ip)); $ip = substr(preg_replace("/([a-f0-9]{4})/i", "$1:", $hex), 0, -1); return $ip; } /** * Returns whether or not the IP is a valid format. * * @param string $ip * @return bool */ public static function is_valid_ip($ip) { return filter_var($ip, FILTER_VALIDATE_IP) !== false; } /** * Returns whether or not the range is a valid CIDR range. * * @param string $range * @return bool */ public static function is_valid_cidr_range($range) { $components = explode('/', $range); if (count($components) != 2) { return false; } list($ip, $prefix) = $components; if (!self::is_valid_ip($ip)) { return false; } if (!preg_match('/^\d+$/', $prefix)) { return false; } if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { if ($prefix < 0 || $prefix > 32) { return false; } } else { if ($prefix < 1 || $prefix > 128) { return false; } } return true; } /** * Returns whether or not the IP is in the IPv6-mapped-IPv4 format. * * @param string $ip * @return bool */ public static function is_ipv6_mapped_ipv4($ip) { return preg_match('/^(?:\:(?:\:0{1,4}){0,4}\:|(?:0{1,4}\:){5})ffff\:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/i', $ip) > 0; } }login-security/classes/model/style.php000064400000001453147207020750014136 0ustar00registered) { wp_enqueue_style($this->handle); } else { wp_enqueue_style($this->handle, $this->source, $this->dependencies, $this->version); } } public function isEnqueued() { return wp_style_is($this->handle); } public function renderInline() { if (empty($this->source)) return; $url = esc_attr($this->getSourceUrl()); $linkTag = ""; ?> handle, $this->source, $this->dependencies, $this->version); return parent::register(); } }login-security/classes/model/text/javascript.php000064400000002236147207020750016130 0ustar00_javaScript = $javaScript; } public function __toString() { return $this->_javaScript; } }login-security/classes/model/text/html.php000064400000000755147207020750014732 0ustar00_html = $html; } public function __toString() { return $this->_html; } }login-security/classes/model/settings/db.php000064400000006534147207020750015230 0ustar00settings; if (!$allowOverwrite) { if ($this->_has_cached($key)) { return; } $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM `{$table}` WHERE `name` = %s", $key), ARRAY_A); if (is_array($row)) { return; } } if ($wpdb->query($wpdb->prepare("INSERT INTO `{$table}` (`name`, `value`, `autoload`) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`), `autoload` = VALUES(`autoload`)", $key, $value, $autoload)) !== false && $autoload != self::AUTOLOAD_NO) { $this->_update_cached($key, $value); do_action('wfls_settings_set', $key, $value); } } public function set_multiple($values) { foreach ($values as $key => $value) { if (is_array($value)) { $this->set($key, $value['value'], $value['autoload'], $value['allowOverwrite']); } else { $this->set($key, $value); } } } public function get($key, $default = false) { global $wpdb; if ($this->_has_cached($key)) { return $this->_cached_value($key); } $table = Controller_DB::shared()->settings; if (!($setting = $wpdb->get_row($wpdb->prepare("SELECT `name`, `value`, `autoload` FROM `{$table}` WHERE `name` = %s", $key)))) { return $default; } if ($setting->autoload != self::AUTOLOAD_NO) { $this->_update_cached($key, $setting->value); } return $setting->value; } public function remove($key) { global $wpdb; $table = Controller_DB::shared()->settings; $wpdb->query($wpdb->prepare("DELETE FROM `{$table}` WHERE `name` = %s", $key)); $this->_remove_cached($key); } private function _cached() { global $wpdb; $settings = wp_cache_get('allsettings', 'wordfence-ls'); if (!$settings) { $table = Controller_DB::shared()->settings; $suppress = $wpdb->suppress_errors(); $raw = $wpdb->get_results("SELECT `name`, `value` FROM `{$table}` WHERE `autoload` = 'yes'"); $wpdb->suppress_errors($suppress); $settings = array(); foreach ((array) $raw as $o) { $settings[$o->name] = $o->value; } wp_cache_add_non_persistent_groups('wordfence-ls'); wp_cache_add('allsettings', $settings, 'wordfence-ls'); } return $settings; } private function _update_cached($key, $value) { $settings = $this->_cached(); $settings[$key] = $value; wp_cache_set('allsettings', $settings, 'wordfence-ls'); } private function _remove_cached($key) { $settings = $this->_cached(); if (isset($settings[$key])) { unset($settings[$key]); wp_cache_set('allsettings', $settings, 'wordfence-ls'); } } private function _cached_value($key) { global $wpdb; $settings = $this->_cached(); if (isset($settings[$key])) { return $settings[$key]; } $table = Controller_DB::shared()->settings; $value = $wpdb->get_var($wpdb->prepare("SELECT `value` FROM `{$table}` WHERE name = %s", $key)); if ($value !== null) { $settings[$key] = $value; wp_cache_set('allsettings', $settings, 'wordfence-ls'); } return $value; } public function _has_cached($key) { $settings = $this->_cached(); return isset($settings[$key]); } }login-security/classes/model/settings/wpoptions.php000064400000002730147207020750016677 0ustar00_prefix = $prefix; } protected function _translate_key($key) { return strtolower(preg_replace('/[^a-z0-9]/i', '_', $key)); } public function set($key, $value, $autoload = self::AUTOLOAD_YES, $allowOverwrite = true) { $key = $this->_translate_key($this->_prefix . $key); if (!$allowOverwrite) { if (is_multisite()) { add_network_option(null, $key, $value); } else { add_option($key, $value, '', $autoload); } } else { if (is_multisite()) { update_network_option(null, $key, $value); } else { update_option($key, $value, $autoload); } } } public function set_multiple($values) { foreach ($values as $key => $value) { if (is_array($value)) { $this->set($key, $value['value'], $value['autoload'], $value['allowOverwrite']); } else { $this->set($key, $value); } } } public function get($key, $default = false) { $key = $this->_translate_key($this->_prefix . $key); if (is_multisite()) { $value = get_network_option($key, $default); } else { $value = get_option($key, $default); } return $value; } public function remove($key) { $key = $this->_translate_key($this->_prefix . $key); if (is_multisite()) { delete_network_option(null, $key); } else { delete_option($key); } } }login-security/classes/model/crypto/base2n.php000064400000022323147207020750015467 0ustar00= ($radix <<= 1) && $bitsPerCharacter < 8) { $bitsPerCharacter++; } $radix >>= 1; throw new \InvalidArgumentException( '$bitsPerCharacter can not be more than ' . $bitsPerCharacter . ' given $chars length of ' . $charLength . ' (max radix ' . $radix . ')'); } elseif ($bitsPerCharacter > 8) { // $bitsPerCharacter must not be greater than 8 throw new \InvalidArgumentException('$bitsPerCharacter can not be greater than 8'); } else { $radix = 1 << $bitsPerCharacter; } $this->_chars = $chars; $this->_bitsPerCharacter = $bitsPerCharacter; $this->_radix = $radix; $this->_rightPadFinalBits = $rightPadFinalBits; $this->_padFinalGroup = $padFinalGroup; $this->_padCharacter = $padCharacter[0]; $this->_caseSensitive = $caseSensitive; } /** * Encode a string * * @param string $rawString Binary data to encode * @return string */ public function encode($rawString) { // Unpack string into an array of bytes $bytes = unpack('C*', $rawString); $byteCount = count($bytes); $encodedString = ''; $byte = array_shift($bytes); $bitsRead = 0; $oldBits = 0; $chars = $this->_chars; $bitsPerCharacter = $this->_bitsPerCharacter; $rightPadFinalBits = $this->_rightPadFinalBits; $padFinalGroup = $this->_padFinalGroup; $padCharacter = $this->_padCharacter; $charsPerByte = 8 / $bitsPerCharacter; $encodedLength = $byteCount * $charsPerByte; // Generate encoded output; each loop produces one encoded character for ($c = 0; $c < $encodedLength; $c++) { // Get the bits needed for this encoded character if ($bitsRead + $bitsPerCharacter > 8) { // Not enough bits remain in this byte for the current character // Save the remaining bits before getting the next byte $oldBitCount = 8 - $bitsRead; $oldBits = $byte ^ ($byte >> $oldBitCount << $oldBitCount); $newBitCount = $bitsPerCharacter - $oldBitCount; if (!$bytes) { // Last bits; match final character and exit loop if ($rightPadFinalBits) $oldBits <<= $newBitCount; $encodedString .= $chars[$oldBits]; if ($padFinalGroup) { // Array of the lowest common multiples of $bitsPerCharacter and 8, divided by 8 $lcmMap = array(1 => 1, 2 => 1, 3 => 3, 4 => 1, 5 => 5, 6 => 3, 7 => 7, 8 => 1); $bytesPerGroup = $lcmMap[$bitsPerCharacter]; $pads = $bytesPerGroup * $charsPerByte - ceil((strlen($rawString) % $bytesPerGroup) * $charsPerByte); $encodedString .= str_repeat($padCharacter, $pads); } break; } // Get next byte $byte = array_shift($bytes); $bitsRead = 0; } else { $oldBitCount = 0; $newBitCount = $bitsPerCharacter; } // Read only the needed bits from this byte $bits = $byte >> 8 - ($bitsRead + ($newBitCount)); $bits ^= $bits >> $newBitCount << $newBitCount; $bitsRead += $newBitCount; if ($oldBitCount) { // Bits come from seperate bytes, add $oldBits to $bits $bits = ($oldBits << $newBitCount) | $bits; } $encodedString .= $chars[$bits]; } return $encodedString; } /** * Decode a string * * @param string $encodedString Data to decode * @param boolean $strict Returns NULL if $encodedString contains an undecodable character * @return string */ public function decode($encodedString, $strict = FALSE) { if (!$encodedString || !is_string($encodedString)) { // Empty string, nothing to decode return ''; } $chars = $this->_chars; $bitsPerCharacter = $this->_bitsPerCharacter; $radix = $this->_radix; $rightPadFinalBits = $this->_rightPadFinalBits; $padFinalGroup = $this->_padFinalGroup; $padCharacter = $this->_padCharacter; $caseSensitive = $this->_caseSensitive; // Get index of encoded characters if ($this->_charmap) { $charmap = $this->_charmap; } else { $charmap = array(); for ($i = 0; $i < $radix; $i++) { $charmap[$chars[$i]] = $i; } $this->_charmap = $charmap; } // The last encoded character is $encodedString[$lastNotatedIndex] $lastNotatedIndex = strlen($encodedString) - 1; // Remove trailing padding characters if ($padFinalGroup) { while ($encodedString[$lastNotatedIndex] === $padCharacter) { $encodedString = substr($encodedString, 0, $lastNotatedIndex); $lastNotatedIndex--; } } $rawString = ''; $byte = 0; $bitsWritten = 0; // Convert each encoded character to a series of unencoded bits for ($c = 0; $c <= $lastNotatedIndex; $c++) { if (!$caseSensitive && !isset($charmap[$encodedString[$c]])) { // Encoded character was not found; try other case if (isset($charmap[$cUpper = strtoupper($encodedString[$c])])) { $charmap[$encodedString[$c]] = $charmap[$cUpper]; } elseif (isset($charmap[$cLower = strtolower($encodedString[$c])])) { $charmap[$encodedString[$c]] = $charmap[$cLower]; } } if (isset($charmap[$encodedString[$c]])) { $bitsNeeded = 8 - $bitsWritten; $unusedBitCount = $bitsPerCharacter - $bitsNeeded; // Get the new bits ready if ($bitsNeeded > $bitsPerCharacter) { // New bits aren't enough to complete a byte; shift them left into position $newBits = $charmap[$encodedString[$c]] << $bitsNeeded - $bitsPerCharacter; $bitsWritten += $bitsPerCharacter; } elseif ($c !== $lastNotatedIndex || $rightPadFinalBits) { // Zero or more too many bits to complete a byte; shift right $newBits = $charmap[$encodedString[$c]] >> $unusedBitCount; $bitsWritten = 8; //$bitsWritten += $bitsNeeded; } else { // Final bits don't need to be shifted $newBits = $charmap[$encodedString[$c]]; $bitsWritten = 8; } $byte |= $newBits; if ($bitsWritten === 8 || $c === $lastNotatedIndex) { // Byte is ready to be written $rawString .= pack('C', $byte); if ($c !== $lastNotatedIndex) { // Start the next byte $bitsWritten = $unusedBitCount; $byte = ($charmap[$encodedString[$c]] ^ ($newBits << $unusedBitCount)) << 8 - $bitsWritten; } } } elseif ($strict) { // Unable to decode character; abort return NULL; } } return $rawString; } }login-security/classes/model/crypto/symmetric.php000064400000002444147207020750016333 0ustar00 base64_encode($encrypted), 'iv' => base64_encode($iv)); } return false; } /** * Returns the decrypted value of a payload encrypted by Model_Symmetric::encrypt * * @param array $encrypted * @return bool|string */ public static function decrypt($encrypted) { if (!Model_Crypto::has_required_crypto_functions()) { return false; } if (!isset($encrypted['data']) || !isset($encrypted['iv'])) { return false; } $symmetricKey = Model_Crypto::shared_symmetric_secret(); $iv = base64_decode($encrypted['iv']); $encrypted = base64_decode($encrypted['data']); $data = @openssl_decrypt($encrypted, 'aes-256-cbc', $symmetricKey, OPENSSL_RAW_DATA, $iv); return $data; } }login-security/classes/model/crypto/jwt.php000064400000006657147207020750015135 0ustar00_payload = $payload; $this->_expiration = $expiration; } public function __toString() { $payload = $this->_payload; if ($this->_expiration !== false) { $payload['_exp'] = $this->_expiration; } $key = Model_Crypto::shared_hash_secret(); $header = '{"alg":"HS256","typ":"JWT"}'; $body = self::base64url_encode($header) . '.' . self::base64url_encode(json_encode($payload)); $signature = hash_hmac('sha256', $body, $key, true); return $body . '.' . self::base64url_encode($signature); } public function __isset($key) { switch ($key) { case 'payload': case 'expiration': return true; } throw new \OutOfBoundsException('Invalid key: ' . $key); } public function __get($key) { switch ($key) { case 'payload': return $this->_payload; case 'expiration': return $this->_expiration; } throw new \OutOfBoundsException('Invalid key: ' . $key); } /** * Utility */ /** * Base64URL-encodes the given payload. This is identical to base64_encode except it substitutes characters * not safe for use in URLs. * * @param string $payload * @return string */ public static function base64url_encode($payload) { return self::base64url_convert_to(base64_encode($payload)); } public static function base64url_convert_to($base64) { $intermediate = rtrim($base64, '='); $intermediate = str_replace('+', '-', $intermediate); $intermediate = str_replace('/', '_', $intermediate); return $intermediate; } /** * Base64URL-decodes the given payload. This is identical to base64_encode except it allows for the characters * substituted by base64url_encode. * * @param string $payload * @return string */ public static function base64url_decode($payload) { return base64_decode(self::base64url_convert_from($payload)); } public static function base64url_convert_from($base64url) { $intermediate = str_replace('_', '/', $base64url); $intermediate = str_replace('-', '+', $intermediate); return $intermediate; } }login-security/classes/model/asset.php000064400000003611147207020750014113 0ustar00handle = $handle; $this->source = $source; $this->dependencies = $dependencies; $this->version = $version; } public function getSourceUrl() { if (empty($this->source)) return null; $url = $this->source; if (is_string($this->version)) $url = add_query_arg('ver', $this->version, $this->source); return $url; } public abstract function enqueue(); public abstract function isEnqueued(); public abstract function renderInline(); public function renderInlineIfNotEnqueued() { if (!$this->isEnqueued()) $this->renderInline(); } public function setRegistered() { $this->registered = true; return $this; } public function register() { return $this->setRegistered(); } public static function js($file) { return self::_pluginBaseURL() . 'js/' . self::_versionedFileName($file); } public static function css($file) { return self::_pluginBaseURL() . 'css/' . self::_versionedFileName($file); } public static function img($file) { return self::_pluginBaseURL() . 'img/' . $file; } protected static function _pluginBaseURL() { return plugins_url('', WORDFENCE_LS_FCPATH) . '/'; } protected static function _versionedFileName($subpath) { $version = WORDFENCE_LS_BUILD_NUMBER; if ($version != 'WORDFENCE_LS_BUILD_NUMBER' && preg_match('/^(.+?)(\.[^\.]+)$/', $subpath, $matches)) { $prefix = $matches[1]; $suffix = $matches[2]; return $prefix . '.' . $version . $suffix; } return $subpath; } public static function create($handle, $source = '', $dependencies = array(), $version = false) { return new static($handle, $source, $dependencies, $version); } }login-security/classes/utility/multisiteconfigurationextractor.php000064400000004216147207020750022144 0ustar00prefix = new Utility_MeasuredString($prefix); $this->suffix = new Utility_MeasuredString($suffix); $this->suffixOffset = -$this->suffix->length; } /** * Parses a `get_user_meta` result array into a more usable format. The input array will be something similar to * [ * 'wp_capabilities' => '...', * 'wp_3_capabilities' => '...', * 'wp_4_capabilities' => '...', * 'wp_10_capabilities' => '...', * ] * * This will return * [ * 1 => '...', * 3 => '...', * 4 => '...', * 10 => '...', * ] * * @param array $values * @return array */ private function parseBlogIds($values) { $parsed = array(); foreach ($values as $key => $value) { if (substr($key, $this->suffixOffset) === $this->suffix->string && strpos($key, (string) $this->prefix) === 0) { $blogId = substr($key, $this->prefix->length, strlen($key) - $this->prefix->length + $this->suffixOffset); if (empty($blogId)) { $parsed[1] = $value; } else if (substr($blogId, -1) === '_') { $parsed[(int) $blogId] = $value; } } } return $parsed; } /** * Filters $values, which is the resulting array from `$this->parseBlogIds` so it contains only the values for the * sites in $sites. * * @param array $values * @param array $sites * @return array */ private function filterValues($values, $sites) { $filtered = array(); foreach ($sites as $site) { $blogId = (int) $site->blog_id; $filtered[$blogId] = $values[$blogId]; } return $filtered; } /** * Processes a `get_user_meta` result array to re-key it so the keys are the numerical ID of all multisite blog IDs * in `$values` that are still in an active state. * * @param array $values * @return array */ public function extract($values) { $parsed = $this->parseBlogIds($values); if (empty($parsed)) return $parsed; $sites = Utility_Multisite::retrieve_active_sites(array_keys($parsed)); return $this->filterValues($parsed, $sites); } }login-security/classes/utility/baseconversion.php000064400000000641147207020750016417 0ustar00encode($data); } }login-security/classes/utility/number.php000064400000000671147207020750014672 0ustar00 $options)) !== false; } public static function isUnixTimestamp($value) { return self::isInteger($value, 0); } }login-security/classes/utility/nulllock.php000064400000000444147207020750015223 0ustar00string = $string; $this->length = strlen($string); } public function __toString() { return $this->string; } }login-security/classes/utility/sleep.php000064400000001145147207020750014507 0ustar00= 1) { sleep((int) $seconds); $seconds -= (int) $seconds; } if ($seconds > 0) { usleep((int) (1000000 * $seconds)); } } }login-security/classes/utility/multisite.php000064400000002262147207020750015417 0ustar00 '', /* WordPress core passes an empty string which appears to remove the result set limit */ 'update_site_meta_cache' => false, /* Defaults to true which is not desirable for this use case */ //Ignore archived/spam/deleted sites 'archived' => 0, 'spam' => 0, 'deleted' => 0 ); if ($blogIds !== null) { $args['site__in'] = $blogIds; } if (function_exists('get_sites')) { return get_sites($args); } global $wpdb; if ($blogIds !== null) { $blogIdsQuery = implode(',', wp_parse_id_list($args['site__in'])); return $wpdb->get_results("SELECT * FROM {$wpdb->blogs} WHERE blog_id IN ({$blogIdsQuery}) AND archived = 0 AND spam = 0 AND deleted = 0"); } return $wpdb->get_results("SELECT * FROM {$wpdb->blogs} WHERE archived = 0 AND spam = 0 AND deleted = 0"); } } login-security/classes/utility/url.php000064400000001261147207020750014200 0ustar00wpdb = $dbController->get_wpdb(); $this->table = $dbController->settings; $this->key = "lock:{$key}"; $this->timeout = self::resolveTimeout($timeout); } private static function resolveTimeout($timeout) { if ($timeout === null) $timeout = ini_get('max_execution_time'); $timeout = (int) $timeout; if ($timeout <= 0 || $timeout > self::MAX_TIMEOUT) return self::DEFAULT_TIMEOUT; return $timeout; } private function clearExpired($timestamp) { $this->wpdb->query($this->wpdb->prepare(<<table} WHERE name = %s AND value < %d SQL , $this->key, $timestamp)); } private function insert($expirationTimestamp) { $result = $this->wpdb->query($this->wpdb->prepare(<<table} (name, value, autoload) VALUES(%s, %d, 'no') SQL , $this->key, $expirationTimestamp)); return $result === 1; } public function acquire($delay = self::DEFAULT_DELAY) { $attempts = (int) ($this->timeout * 1000000 / $delay); for (; $attempts > 0; $attempts--) { $timestamp = time(); $this->clearExpired($timestamp); $expirationTimestamp = $timestamp + $this->timeout; $locked = $this->insert($expirationTimestamp); if ($locked) { $this->expirationTimestamp = $expirationTimestamp; return; } usleep($delay); } throw new RuntimeException("Failed to acquire lock {$this->key}"); } private function delete($expirationTimestamp) { $this->wpdb->delete( $this->table, array ( 'name' => $this->key, 'value' => $expirationTimestamp ), array ( '%s', '%d' ) ); } public function release() { if ($this->expirationTimestamp === null) return; $this->delete($this->expirationTimestamp); $this->expirationTimestamp = null; } }login-security/classes/utility/array.php000064400000001057147207020750014517 0ustar00 $value) { if ($index === $key) return $offset; $offset++; } return null; } public static function insertAfter(&$array, $targetKey, $key, $value) { $offset = self::findOffset($array, $targetKey); if ($offset === null) return false; $array = array_merge( array_slice($array, 0, $offset + 1), array( $key => $value ), array_slice($array, $offset + 1) ); return true; } }login-security/classes/controller/permissions.php000064400000033435147207020750016441 0ustar00_on_role_change(); if (is_multisite()) { //Super Admin automatically gets all capabilities, so we don't need to explicitly add them $this->_add_cap_multisite('administrator', self::CAP_ACTIVATE_2FA_SELF, $this->get_primary_sites()); } else { $this->_add_cap('administrator', self::CAP_ACTIVATE_2FA_SELF); $this->_add_cap('administrator', self::CAP_ACTIVATE_2FA_OTHERS); $this->_add_cap('administrator', self::CAP_MANAGE_SETTINGS); } } public function uninstall() { if (Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_DELETE_ON_DEACTIVATION)) { if (is_multisite()) { $sites = $this->get_sites(); foreach ($sites as $id) { switch_to_blog($id); wp_clear_scheduled_hook('wordfence_ls_role_sync_cron'); restore_current_blog(); } } } } public static function _init_actions() { add_action('wordfence_ls_role_sync_cron', array(Controller_Permissions::shared(), '_role_sync_cron')); } public function init() { global $wp_version; if (is_multisite()) { if (version_compare($wp_version, '5.1.0', '>=')) { add_action('wp_initialize_site', array($this, '_wp_initialize_site'), 99); } else { add_action('wpmu_new_blog', array($this, '_wpmu_new_blog'), 10, 5); } add_action('init', array($this, '_validate_role_sync_cron'), 1); } } /** * Syncs roles to the new multisite blog. * * @param $site_id * @param $user_id * @param $domain * @param $path * @param $network_id */ public function _wpmu_new_blog($site_id, $user_id, $domain, $path, $network_id) { $this->sync_roles($network_id, $site_id); } /** * Syncs roles to the new multisite blog. * * @param $new_site */ public function _wp_initialize_site($new_site) { $this->sync_roles($new_site->site_id, $new_site->blog_id); } /** * Creates the hourly cron (if needed) that handles syncing the roles/permissions for the current blog. Because crons * are specific to individual blogs on multisite rather than to the network itself, this will end up creating a cron * for every member blog of the multisite. * * If there is a new role change since the last sync, a one-off cron will be fired to sync it sooner than the normal * recurrence period. * * Multisite only. * */ public function _validate_role_sync_cron() { if (!wp_next_scheduled('wordfence_ls_role_sync_cron')) { wp_schedule_event(time(), 'hourly', 'wordfence_ls_role_sync_cron'); } else { $last_role_change = (int) get_site_option(self::SETTING_LAST_ROLE_CHANGE, 0); if ($last_role_change >= get_option(self::SETTING_LAST_ROLE_SYNC, 0)) { wp_schedule_single_event(time(), 'wordfence_ls_role_sync_cron'); //Force queue an update in case the normal cron is still a while out } } } /** * Handles syncing the roles/permissions for the current blog when the cron fires. */ public function _role_sync_cron() { $last_role_change = (int) get_site_option(self::SETTING_LAST_ROLE_CHANGE, 0); if ($last_role_change === 0) { $this->_on_role_change(); } if ($last_role_change >= get_option(self::SETTING_LAST_ROLE_SYNC, 0)) { $network_id = get_current_site()->id; $blog_id = get_current_blog_id(); $this->sync_roles($network_id, $blog_id); update_option(self::SETTING_LAST_ROLE_SYNC, time()); } } private function _on_role_change() { update_site_option(self::SETTING_LAST_ROLE_CHANGE, time()); } /** * Get the primary site ID for a given network */ private function get_primary_site_id($network_id) { global $wpdb; if(function_exists('get_network')){ $network=get_network($network_id); //TODO: Support multi-network throughout plugin return (int)$network->blog_id; } else{ return (int)$wpdb->get_var($wpdb->prepare("SELECT blogs.blog_id FROM {$wpdb->site} sites JOIN {$wpdb->blogs} blogs ON blogs.site_id=sites.id AND blogs.path=sites.path WHERE sites.id=%d", $network_id)); } } /** * Get all primary sites in a multi-network setup */ private function get_primary_sites() { global $wpdb; if(function_exists('get_networks')){ return array_map(function($network){ return $network->blog_id; }, get_networks()); } else{ return $wpdb->get_col("SELECT blogs.blog_id FROM {$wpdb->site} sites JOIN {$wpdb->blogs} blogs ON blogs.site_id=sites.id AND blogs.path=sites.path"); } } /** * Returns an array of all multisite `blog_id` values, optionally limiting the result to the subset between * ($from, $from + $count]. * * @param int $from * @param int $count * @return array */ private function get_sites($from = 0, $count = 0) { global $wpdb; if ($from === 0 && $count === 0) { return $wpdb->get_col("SELECT `blog_id` FROM `{$wpdb->blogs}` WHERE `deleted` = 0 ORDER BY blog_id "); } return $wpdb->get_col($wpdb->prepare("SELECT `blog_id` FROM `{$wpdb->blogs}` WHERE `deleted` = 0 AND blog_id > %d ORDER BY blog_id LIMIT %d", $from, $count)); } /** * Sync role capabilities from the default site to a newly added site * @param int $network_id the relevant network * @param int $site_id the newly added site(blog) */ private function sync_roles($network_id, $site_id){ if(array_key_exists($network_id, $this->network_roles)){ $current_roles=$this->network_roles[$network_id]; } else{ $current_roles=$this->_wp_roles($this->get_primary_site_id($network_id)); $this->network_roles[$network_id]=$current_roles; } $new_site_roles=$this->_wp_roles($site_id); $capabilities=array( self::CAP_ACTIVATE_2FA_SELF, self::CAP_ACTIVATE_2FA_OTHERS, self::CAP_MANAGE_SETTINGS ); foreach($current_roles->get_names() as $role_name=>$role_label){ if($new_site_roles->get_role($role_name)===null) $new_site_roles->add_role($role_name, $role_label); $role=$current_roles->get_role($role_name); foreach($capabilities as $cap){ if($role->has_cap($cap)){ $this->_add_cap_multisite($role_name, $cap, array($site_id)); } else{ $this->_remove_cap_multisite($role_name, $cap, array($site_id)); } } } } public function allow_2fa_self($role_name) { $this->_on_role_change(); if (is_multisite()) { return $this->_add_cap_multisite($role_name, self::CAP_ACTIVATE_2FA_SELF, $this->get_primary_sites()); } else { return $this->_add_cap($role_name, self::CAP_ACTIVATE_2FA_SELF); } } public function disallow_2fa_self($role_name) { $this->_on_role_change(); if (is_multisite()) { return $this->_remove_cap_multisite($role_name, self::CAP_ACTIVATE_2FA_SELF, $this->get_primary_sites()); } else { if ($role_name == 'administrator') { return true; } return $this->_remove_cap($role_name, self::CAP_ACTIVATE_2FA_SELF); } } public function can_manage_settings($user = false) { if ($user === false) { $user = wp_get_current_user(); } if (!($user instanceof \WP_User)) { return false; } return $user->has_cap(self::CAP_MANAGE_SETTINGS); } public function can_role_manage_settings($role) { if (is_string($role)) { $role = get_role($role); } if ($role) return $role->has_cap(self::CAP_MANAGE_SETTINGS); return false; } private function _wp_roles($site_id = null) { require(ABSPATH . 'wp-includes/version.php'); /** @var string $wp_version */ if (version_compare($wp_version, '4.9', '>=')) { return new \WP_Roles($site_id); } //\WP_Roles in WP < 4.9 initializes based on the current blog ID if (is_multisite()) { switch_to_blog($site_id); } $wp_roles = new \WP_Roles(); if (is_multisite()) { restore_current_blog(); } return $wp_roles; } private function _add_cap_multisite($role_name, $cap, $blog_ids=null) { if ($role_name === 'super-admin') return true; global $wpdb; $blogs = $blog_ids===null?$wpdb->get_col("SELECT `blog_id` FROM `{$wpdb->blogs}` WHERE `deleted` = 0"):$blog_ids; $added = false; foreach ($blogs as $id) { $wp_roles = $this->_wp_roles($id); switch_to_blog($id); $added = $this->_add_cap($role_name, $cap, $wp_roles) || $added; restore_current_blog(); } return $added; } private function _add_cap($role_name, $cap, $wp_roles = null) { if ($wp_roles === null) { $wp_roles = $this->_wp_roles(); } $role = $wp_roles->get_role($role_name); if ($role === null) { return false; } $wp_roles->add_cap($role_name, $cap); return true; } private function _remove_cap_multisite($role_name, $cap, $blog_ids=null) { if ($role_name === 'super-admin') return false; global $wpdb; $blogs = $blog_ids===null?$wpdb->get_col("SELECT `blog_id` FROM `{$wpdb->blogs}` WHERE `deleted` = 0"):$blog_ids; $removed = false; foreach ($blogs as $id) { $wp_roles = $this->_wp_roles($id); switch_to_blog($id); $removed = $this->_remove_cap($role_name, $cap, $wp_roles) || $removed; restore_current_blog(); } return $removed; } private function _remove_cap($role_name, $cap, $wp_roles = null) { if ($wp_roles === null) { $wp_roles = $this->_wp_roles(); } $role = $wp_roles->get_role($role_name); if ($role === null) { return false; } $wp_roles->remove_cap($role_name, $cap); return true; } /** * Loads the role capability info for the multisite blog IDs in `$includedSites` and appends it to * `$this->multisite_roles`. Role capability data that is already loaded will be skipped. * * @param array $includeSites An array of multisite blog IDs to load. */ private function _load_multisite_roles($includeSites) { global $wpdb; $needed = array_diff($includeSites, array_keys($this->multisite_roles)); if (empty($needed)) { return; } $suffix = "user_roles"; $queries = array(); foreach ($needed as $b) { $tables = $wpdb->tables('blog', true, $b); $queries[] = "SELECT CAST(option_name AS CHAR UNICODE) AS option_name, CAST(option_value AS CHAR UNICODE) AS option_value FROM {$tables['options']} WHERE option_name LIKE '%{$suffix}'"; } $chunks = array_chunk($queries, 50); $options = array(); foreach ($chunks as $c) { $rows = $wpdb->get_results(implode(' UNION ', $c), OBJECT_K); foreach ($rows as $row) { $options[$row->option_name] = $row->option_value; } } $extractor = new Utility_MultisiteConfigurationExtractor($wpdb->base_prefix, $suffix); foreach ($extractor->extract($options) as $site => $option) { $this->multisite_roles[$site] = maybe_unserialize($option); } } /** * Returns an array of multisite roles. This is guaranteed to include the multisite blogs in `$includeSites` but may * include others from earlier calls that are cached. * * @param array $includeSites An array for multisite blog IDs. * @return array */ public function get_multisite_roles($includeSites) { if ($this->multisite_roles === null) { $this->multisite_roles = array(); } $this->_load_multisite_roles($includeSites); return $this->multisite_roles; } /** * Returns the sites + roles that a user has on multisite. The structure of the returned array has the keys as the * individual site IDs and the associated value as an array of the user's capabilities on that site. * * @param WP_User $user * @return array */ public function get_multisite_roles_for_user($user) { global $wpdb; $roles = array(); $meta = get_user_meta($user->ID); if (is_array($meta)) { $extractor = new Utility_MultisiteConfigurationExtractor($wpdb->base_prefix, 'capabilities'); foreach ($extractor->extract($meta) as $site => $capabilities) { if (!is_array($capabilities)) { continue; } $capabilities = array_map('maybe_unserialize', $capabilities); $localRoles = array(); foreach ($capabilities as $entry) { foreach ($entry as $role => $state) { if ($state) $localRoles[$role] = true; } } $roles[$site] = array_keys($localRoles); } } return $roles; } public function get_all_roles($user) { global $wpdb; if (is_multisite()) { $roles = array(); if (is_super_admin($user->ID)) { $roles['super-admin'] = true; } foreach ($this->get_multisite_roles_for_user($user) as $site => $siteRoles) { foreach ($siteRoles as $role) { $roles[$role] = true; } } return array_keys($roles); } else { return $user->roles; } } public function does_user_have_multisite_capability($user, $capability) { $userRoles = $this->get_multisite_roles_for_user($user); if (in_array('super-admin', $userRoles)) { return true; } $blogRoles = $this->get_multisite_roles(array_keys($userRoles)); $blogs = get_blogs_of_user($user->ID); foreach ($blogs as $blogId => $blog) { $blogId = (int) $blogId; if (!array_key_exists($blogId, $userRoles) || !array_key_exists($blogId, $blogRoles)) { continue; } //Blog with ID `$blogId` should be ignored foreach ($userRoles[$blogId] as $userRole) { if (!array_key_exists($userRole, $blogRoles[$blogId]) || !array_key_exists('capabilities', $blogRoles[$blogId][$userRole])) { continue; } //Sanity check for needed keys, should not happen $capabilities = $blogRoles[$blogId][$userRole]['capabilities']; if (array_key_exists($capability, $capabilities) && $capabilities[$capability]) { return true; } } } return false; } }login-security/classes/controller/users.php000064400000111726147207020750015227 0ustar00_init_actions(); } /** * Imports the array of 2FA secrets. Users that do not currently exist or are disallowed from enabling 2FA are not imported. * * @param array $secrets An array of secrets in the format array( => array('secret' => , 'recovery' => , 'ctime' => , 'vtime' => , 'type' => ), ...) * @return int The number imported. */ public function import_2fa($secrets) { global $wpdb; $table = Controller_DB::shared()->secrets; $count = 0; foreach ($secrets as $id => $parameters) { $user = new \WP_User($id); if (!$user->exists() || !$this->can_activate_2fa($user) || $parameters['type'] != 'authenticator' || $this->has_2fa_active($user)) { continue; } $secret = Model_Compat::hex2bin($parameters['secret']); $recovery = Model_Compat::hex2bin($parameters['recovery']); $ctime = (int) $parameters['ctime']; $vtime = min((int) $parameters['vtime'], Controller_Time::time()); $type = $parameters['type']; $wpdb->query($wpdb->prepare("INSERT INTO `{$table}` (`user_id`, `secret`, `recovery`, `ctime`, `vtime`, `mode`) VALUES (%d, %s, %s, %d, %d, %s)", $user->ID, $secret, $recovery, $ctime, $vtime, $type)); $count++; } return $count; } public function admin_users() { //We should eventually allow for any user to be granted the manage capability, but we won't account for that now if (is_multisite()) { $logins = get_super_admins(); $users = array(); foreach ($logins as $l) { $user = new \WP_User(null, $l); if ($user->ID > 0) { $users[] = $user; } } return $users; } $query = new \WP_User_Query(http_build_query(array('role' => 'administrator', 'number' => -1))); return $query->get_results(); } public function get_users_by_role($role, $limit = -1) { if ($role === 'super-admin') { $superAdmins = array(); foreach(get_super_admins() as $username) { $superAdmins[] = new \WP_User($username); } return $superAdmins; } else { $query = new \WP_User_Query(http_build_query(array('role' => $role, 'number' => is_int($limit) ? $limit : -1))); return $query->get_results(); } } /** * Returns whether or not the user has a valid remembered device. * * @param \WP_User $user * @return bool */ public function has_remembered_2fa($user) { static $_cache = array(); if (isset($_cache[$user->ID])) { return $_cache[$user->ID]; } if (!Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_REMEMBER_DEVICE_ENABLED)) { return false; } $maxExpiration = \WordfenceLS\Controller_Time::time() + Controller_Settings::shared()->get_int(Controller_Settings::OPTION_REMEMBER_DEVICE_DURATION); $encrypted = Model_Symmetric::encrypt((string) $user->ID); if (!$encrypted) { //Can't generate cookie key due to host failure return false; } foreach ($_COOKIE as $name => $value) { if (!preg_match('/^wfls\-remembered\-(.+)$/', $name, $matches)) { continue; } $jwt = Model_JWT::decode_jwt($value); if (!$jwt || !isset($jwt->payload['iv'])) { continue; } if (\WordfenceLS\Controller_Time::time() > min($jwt->expiration, $maxExpiration)) { //Either JWT is expired or the remember period was shortened since generating it continue; } $data = Model_JWT::base64url_convert_from($matches[1]); $iv = $jwt->payload['iv']; $encrypted = array('data' => $data, 'iv' => $iv); $userID = (int) Model_Symmetric::decrypt($encrypted); if ($userID != 0 && $userID == $user->ID) { $_cache[$user->ID] = true; return true; } } $_cache[$user->ID] = false; return false; } /** * Sets the cookie needed to remember the 2FA status. * * @param \WP_User $user */ public function remember_2fa($user) { if (!Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_REMEMBER_DEVICE_ENABLED)) { return; } if ($this->has_remembered_2fa($user)) { return; } $encrypted = Model_Symmetric::encrypt((string) $user->ID); if (!$encrypted) { //Can't generate cookie key due to host failure return; } //Remove old cookies foreach ($_COOKIE as $name => $value) { if (!preg_match('/^wfls\-remembered\-(.+)$/', $name, $matches)) { continue; } setcookie($name, '', \WordfenceLS\Controller_Time::time() - 86400); } //Set the new one $expiration = \WordfenceLS\Controller_Time::time() + Controller_Settings::shared()->get_int(Controller_Settings::OPTION_REMEMBER_DEVICE_DURATION); $jwt = new Model_JWT(array('iv' => $encrypted['iv']), $expiration); $cookieName = 'wfls-remembered-' . Model_JWT::base64url_convert_to($encrypted['data']); $cookieValue = (string) $jwt; setcookie($cookieName, $cookieValue, $expiration, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true); } /** * Returns whether or not 2FA can be activated on the given user. * * @param \WP_User $user * @return bool */ public function can_activate_2fa($user) { if (is_multisite() && !is_super_admin($user->ID)) { return Controller_Permissions::shared()->does_user_have_multisite_capability($user, Controller_Permissions::CAP_ACTIVATE_2FA_SELF); } return user_can($user, Controller_Permissions::CAP_ACTIVATE_2FA_SELF); } /** * Returns whether or not any user has 2FA activated. * * @return bool */ public function any_2fa_active() { global $wpdb; $table = Controller_DB::shared()->secrets; return !!intval($wpdb->get_var("SELECT COUNT(*) FROM `{$table}`")); } /** * Returns whether or not the user has 2FA activated. * * @param \WP_User $user * @return bool */ public function has_2fa_active($user) { global $wpdb; $table = Controller_DB::shared()->secrets; return $this->can_activate_2fa($user) && !!intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$table}` WHERE `user_id` = %d", $user->ID))); } /** * Deactivates a user. * * @param \WP_User $user */ public function deactivate_2fa($user) { global $wpdb; $table = Controller_DB::shared()->secrets; $wpdb->query($wpdb->prepare("DELETE FROM `{$table}` WHERE `user_id` = %d", $user->ID)); /** * Fires when 2FA is disabled for a user. * * @since 1.1.13 * * @param \WP_User $user The user. */ do_action('wordfence_ls_2fa_deactivated', $user); } private function has_admin_with_2fa_active() { static $cache = null; if ($cache === null) { $activeIDs = $this->_user_ids_with_2fa_active(); foreach ($activeIDs as $id) { if (Controller_Permissions::shared()->can_manage_settings(new \WP_User($id))) { $cache = true; return $cache; } } $cache = false; } return $cache; } /** * Returns whether or not 2FA is required for the user regardless of activation status. 2FA is considered required * when the option to require it is enabled and there is at least one administrator with it active. * * @param \WP_User $user * @param bool &$gracePeriod * @param int &$requiredAt * @return bool */ public function requires_2fa($user, &$gracePeriod = false, &$requiredAt = null) { static $cache = array(); if (array_key_exists($user->ID, $cache)) { list($required, $gracePeriod, $requiredAt) = $cache[$user->ID]; return $required; } else { $gracePeriod = false; $requiredAt = null; $required = $this->does_user_role_require_2fa($user, $gracePeriod, $requiredAt); $cache[$user->ID] = array($required, $gracePeriod, $requiredAt); return $required; } } /** * Returns the number of recovery codes remaining for the user or null if the user does not have 2FA active. * * @param \WP_User $user * @return float|null */ public function recovery_code_count($user) { global $wpdb; $table = Controller_DB::shared()->secrets; $record = $wpdb->get_var($wpdb->prepare("SELECT `recovery` FROM `{$table}` WHERE `user_id` = %d", $user->ID)); if (!$record) { return null; } return floor(Model_Crypto::strlen($record) / self::RECOVERY_CODE_SIZE); } /** * Generates a new set of recovery codes and saves them to $user if provided. * * @param \WP_User|bool $user The user to save the codes to or false to just return codes. * @param int $count * @return array */ public function regenerate_recovery_codes($user = false, $count = self::RECOVERY_CODE_COUNT) { $codes = array(); for ($i = 0; $i < $count; $i++) { $c = \WordfenceLS\Model_Crypto::random_bytes(self::RECOVERY_CODE_SIZE); $codes[] = $c; } if ($user && Controller_Users::shared()->has_2fa_active($user)) { global $wpdb; $table = Controller_DB::shared()->secrets; $wpdb->query($wpdb->prepare("UPDATE `{$table}` SET `recovery` = %s WHERE `user_id` = %d", implode('', $codes), $user->ID)); } return $codes; } /** * Records the reCAPTCHA score for later display. * * This is not atomic, which means this can miscount on hits that overlap, but the overhead of being atomic is not * worth it for our use. * * @param \WP_User $user|null * @param float $score */ public function record_captcha_score($user, $score) { if (!Controller_CAPTCHA::shared()->enabled()) { return; } if ($user) { update_user_meta($user->ID, 'wfls-last-captcha-score', $score); } $stats = Controller_Settings::shared()->get_array(Controller_Settings::OPTION_CAPTCHA_STATS); $int_score = min(max((int) ($score * 10), 0), 10); $count = array_sum($stats['counts']); $stats['counts'][$int_score]++; $stats['avg'] = ($stats['avg'] * $count + $int_score) / ($count + 1); Controller_Settings::shared()->set_array(Controller_Settings::OPTION_CAPTCHA_STATS, $stats); } /** * Returns the active and inactive user counts. * * @return array */ public function user_counts() { if (is_multisite() && function_exists('get_user_count')) { $total_users = get_user_count(); } else { global $wpdb; $total_users = (int) $wpdb->get_var("SELECT COUNT(ID) as c FROM {$wpdb->users}"); } $active_users = $this->active_count(); return array('active_users' => $active_users, 'inactive_users' => max($total_users - $active_users, 0)); } public function detailed_user_counts($force = false) { global $wpdb; $blog_prefix = $wpdb->get_blog_prefix(); $wp_roles = new \WP_Roles(); $roles = $wp_roles->get_names(); $counts = array(); $groups = array('avail_roles' => 0, 'active_avail_roles' => 0); foreach ($groups as $group => $count) { $counts[$group] = array(); foreach ($roles as $role_key => $role_name) { $counts[$group][$role_key] = 0; } $counts[$group][self::TRUNCATED_ROLE_KEY] = 0; } $dbController = Controller_DB::shared(); if ($dbController->create_temporary_role_counts_table()) { $lock = new Utility_NullLock(); $role_counts_table = $dbController->role_counts_temporary; } else { $lock = new Utility_DatabaseLock($dbController, 'role-count-calculation'); $role_counts_table = $dbController->role_counts; } try { $lock->acquire(); if(!$force && Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_USER_COUNT_QUERY_STATE)) throw new RuntimeException('Previous user count query failed to completed successfully. User count queries are currently disabled'); Controller_Settings::shared()->set(Controller_Settings::OPTION_USER_COUNT_QUERY_STATE, true); $dbController->require_schema_version(2); $secrets_table = $dbController->secrets; $dbController->query("TRUNCATE {$role_counts_table}"); $dbController->query($wpdb->prepare(<<usermeta} um INNER JOIN {$wpdb->users} u ON u.ID = um.user_id LEFT JOIN {$secrets_table} s ON s.user_id = u.ID WHERE meta_key = %s ON DUPLICATE KEY UPDATE user_count = user_count + 1; SQL , "{$blog_prefix}capabilities")); $results = $wpdb->get_results(<<set(Controller_Settings::OPTION_USER_COUNT_QUERY_STATE, false); } catch (RuntimeException $e) { $lock->release(); //Finally is not supported in older PHP versions, so it is necessary to release the lock in two places return false; } $lock->release(); foreach ($results as $row) { $truncated_role = false; try { $row_roles = Utility_Serialization::unserialize($row->serialized_roles, array('allowed_classes' => false), 'is_array'); } catch (RuntimeException $e) { $row_roles = array(self::TRUNCATED_ROLE_KEY => true); $truncated_role = true; } foreach ($row_roles as $row_role => $state) { if ($state !== true || (!$truncated_role && !is_string($row_role))) continue; if (array_key_exists($row_role, $roles) || $row_role === self::TRUNCATED_ROLE_KEY) { foreach ($groups as $group => &$group_count) { if ($group === 'active_avail_roles' && $row->two_factor_inactive) continue; $counts[$group][$row_role] += $row->user_count; $group_count += $row->user_count; } } } } foreach ($roles as $role_key => $role_name) { if ($counts['avail_roles'][$role_key] === 0 && $counts['active_avail_roles'][$role_key] === 0) { unset($counts['avail_roles'][$role_key]); unset($counts['active_avail_roles'][$role_key]); } } // Separately add super admins for multisite if (is_multisite()) { $superAdmins = 0; $activeSuperAdmins = 0; foreach(get_super_admins() as $username) { $superAdmins++; $user = new \WP_User($username); if ($this->has_2fa_active($user)) { $activeSuperAdmins++; } } $counts['avail_roles']['super-admin'] = $superAdmins; $counts['active_avail_roles']['super-admin'] = $activeSuperAdmins; } $counts['total_users'] = $groups['avail_roles']; $counts['active_total_users'] = $groups['active_avail_roles']; return $counts; } /** * Returns the number of users with 2FA active. * * @return int */ public function active_count() { global $wpdb; $table = Controller_DB::shared()->secrets; return intval($wpdb->get_var("SELECT COUNT(*) FROM `{$table}`")); } /** * WP Filters/Actions */ protected function _init_actions() { add_action('deleted_user', array($this, '_deleted_user')); add_filter('manage_users_columns', array($this, '_manage_users_columns')); add_filter('manage_users_custom_column', array($this, '_manage_users_custom_column'), 10, 3); add_filter('manage_users_sortable_columns', array($this, '_manage_users_sortable_columns'), 10, 1); add_filter('users_list_table_query_args', array($this, '_users_list_table_query_args')); add_filter('user_row_actions', array($this, '_user_row_actions'), 10, 2); add_filter('views_users', array($this, '_views_users')); if (is_multisite()) { add_filter('manage_users-network_columns', array($this, '_manage_users_columns')); add_filter('manage_users-network_custom_column', array($this, '_manage_users_custom_column'), 10, 3); add_filter('manage_users-network_sortable_columns', array($this, '_manage_users_sortable_columns'), 10, 1); add_filter('ms_user_row_actions', array($this, '_user_row_actions'), 10, 2); add_filter('views_users-network', array($this, '_views_users')); } } public function _deleted_user($id) { $user = new \WP_User($id); if ($user instanceof \WP_User && !$user->exists()) { global $wpdb; $table = Controller_DB::shared()->secrets; $wpdb->query($wpdb->prepare("DELETE FROM `{$table}` WHERE `user_id` = %d", $id)); } } public function _manage_users_columns($columns = array()) { if (user_can(wp_get_current_user(), Controller_Permissions::CAP_ACTIVATE_2FA_OTHERS)) { $columns['wfls_2fa_status'] = esc_html__('2FA Status', 'wordfence'); } if (Controller_Settings::shared()->are_login_history_columns_enabled() && Controller_Permissions::shared()->can_manage_settings(wp_get_current_user())) { $columns['wfls_last_login'] = esc_html__('Last Login', 'wordfence'); if (Controller_CAPTCHA::shared()->enabled()) { $columns['wfls_last_captcha'] = esc_html__('Last CAPTCHA', 'wordfence'); } } return $columns; } public function _manage_users_custom_column($value = '', $column_name = '', $user_id = 0) { switch($column_name) { case 'wfls_2fa_status': $user = new \WP_User($user_id); $value = __('Not Allowed', 'wordfence'); if (Controller_Users::shared()->can_activate_2fa($user)) { $has2fa = Controller_Users::shared()->has_2fa_active($user); $requires2fa = $this->requires_2fa($user, $inGracePeriod); if ($has2fa) { $value = esc_html__('Active', 'wordfence'); } elseif ($inGracePeriod) { $value = wp_kses(__('Inactive(Grace Period)', 'wordfence'), array('small'=>array('class'=>array()))); } elseif (($requires2fa && !$has2fa)) { $value = wp_kses($inGracePeriod === null ? __('Locked Out(Grace Period Disabled)', 'wordfence') : __('Locked Out(Grace Period Exceeded)', 'wordfence'), array('small'=>array('class'=>array()))); } else { $value = esc_html__('Inactive', 'wordfence'); } } break; case 'wfls_last_login': $value = '-'; if (($last = get_user_meta($user_id, 'wfls-last-login', true)) && Utility_Number::isUnixTimestamp($last)) { $value = Controller_Time::format_local_time(get_option('date_format') . ' ' . get_option('time_format'), $last); } break; case 'wfls_last_captcha': $user = new \WP_User($user_id); $value = '-'; if (($last = get_user_meta($user_id, 'wfls-last-captcha-score', true))) { $value = number_format($last, 1); } break; } return $value; } public function _manage_users_sortable_columns($sortable_columns) { return array_merge($sortable_columns, array( 'wfls_last_login' => 'wfls-lastlogin', 'wfls_last_captcha' => 'wfls-lastcaptcha', )); } protected function _user_ids_with_2fa_active() { global $wpdb; $table = Controller_DB::shared()->secrets; return $wpdb->get_col("SELECT DISTINCT `user_id` FROM {$table}"); } public function _users_list_table_query_args($args) { if (isset($_REQUEST['wf2fa']) && preg_match('/^(?:in)?active$/i', $_REQUEST['wf2fa'])) { $mode = strtolower($_REQUEST['wf2fa']); if ($mode == 'active') { $args['include'] = $this->_user_ids_with_2fa_active(); } else if ($mode == 'inactive') { unset($args['include']); $args['exclude'] = $this->_user_ids_with_2fa_active(); } } if (isset($args['orderby'])) { if (is_string($args['orderby'])) { if ($args['orderby'] == 'wfls-lastlogin') { $args['meta_key'] = 'wfls-last-login'; $args['orderby'] = 'meta_value'; } else if ($args['orderby'] == 'wfls-lastcaptcha') { $args['meta_key'] = 'wfls-last-captcha-score'; $args['orderby'] = 'meta_value'; } } else { $has_one = false; if (array_key_exists('wfls-lastlogin', $args['orderby'])) { $args['meta_key'] = 'wfls-last-login'; $args['orderby']['meta_value'] = $args['orderby']['wfls-lastlogin']; unset($args['orderby']['wfls-lastlogin']); $has_one = true; } if (array_key_exists('wfls-lastcaptcha', $args['orderby'])) { if (!$has_one) { //We have to discard one if both are set to sort by because $meta_key can only be a single value rather than an array $args['meta_key'] = 'wfls-last-captcha-score'; $args['orderby']['meta_value'] = $args['orderby']['wfls-lastcaptcha']; } unset($args['orderby']['wfls-lastcaptcha']); $has_one = true; } if (in_array('wfls-lastlogin', $args['orderby'])) { if (!$has_one) { //We have to discard one if both are set to sort by because $meta_key can only be a single value rather than an array $args['meta_key'] = 'wfls-last-login'; $args['orderby'][] = 'meta_value'; } unset($args['orderby'][array_search('wfls-lastlogin', $args['orderby'])]); $has_one = true; } if (in_array('wfls-lastcaptcha', $args['orderby'])) { if (!$has_one) { //We have to discard one if both are set to sort by because $meta_key can only be a single value rather than an array $args['meta_key'] = 'wfls-last-captcha-score'; $args['orderby'][] = 'meta_value'; } unset($args['orderby'][array_search('wfls-lastcaptcha', $args['orderby'])]); $has_one = true; } } } return $args; } public function _user_row_actions($actions, $user) { //Format is 'view' => 'View' if (user_can(wp_get_current_user(), Controller_Permissions::CAP_ACTIVATE_2FA_OTHERS) && (Controller_Users::shared()->can_activate_2fa($user) || Controller_Users::shared()->has_2fa_active($user))) { $url = (is_multisite() ? network_admin_url('admin.php?page=WFLS&user=' . $user->ID) : admin_url('admin.php?page=WFLS&user=' . $user->ID)); $actions['wf2fa'] = '' . esc_html__('2FA', 'wordfence') . ''; } return $actions; } public function _views_users($views) { //Format is 'subscriber' => 'Subscriber (40,002)', include(ABSPATH . WPINC . '/version.php'); /** @var string $wp_version */ if (user_can(wp_get_current_user(), Controller_Permissions::CAP_ACTIVATE_2FA_OTHERS) && version_compare($wp_version, '4.4.0', '>=')) { $counts = $this->user_counts(); $views['all'] = str_replace(' class="current" aria-current="page"', '', $views['all']); $views['wfls-active'] = '' . esc_html__('2FA Active', 'wordfence') . ' (' . number_format($counts['active_users']) . ')'; $views['wfls-inactive'] = '' . esc_html__('2FA Inactive', 'wordfence') . ' (' . number_format($counts['inactive_users']) . ')'; } return $views; } private function get_grace_period_reset_time($user) { $time = get_user_option(self::META_KEY_GRACE_PERIOD_RESET, $user->ID); if (empty($time)) return null; return (int) $time; } public function get_grace_period_override($user) { $override = get_user_option(self::META_KEY_GRACE_PERIOD_OVERRIDE, $user->ID); if ($override === false) return null; return (int) $override; } private function does_user_role_require_2fa($user, &$inGracePeriod = null, &$requiredAt = null) { $is2faAdmin = Controller_Permissions::shared()->can_manage_settings($user); $userDate = self::get_grace_period_reset_time($user); if ($userDate === null) $userDate = self::get_registration_date($user); if ($is2faAdmin && !$this->get_grace_period_allowed_flag($user->ID)) { $gracePeriod = 0; $inGracePeriod = null; } else { $gracePeriod = self::get_grace_period_override($user); if ($gracePeriod === null) $gracePeriod = Controller_Settings::shared()->get_user_2fa_grace_period(); $gracePeriod *= self::SECONDS_PER_DAY; $inGracePeriod = false; } $now = time(); foreach (Controller_Permissions::shared()->get_all_roles($user) as $role) { $roleDate = Controller_Settings::shared()->get_required_2fa_role_activation_time($role); if ($roleDate === false) continue; $effectiveDate = max($userDate, $roleDate) + $gracePeriod; if ($requiredAt === null || $effectiveDate < $requiredAt) $requiredAt = $effectiveDate; if ($effectiveDate <= $now && (!$is2faAdmin || $this->has_admin_with_2fa_active())) { if ($inGracePeriod) $inGracePeriod = false; return true; } else if ($inGracePeriod !== null) { $inGracePeriod = true; } } return false; } private static function get_registration_date($user) { return strtotime($user->user_registered); } public function reset_2fa_grace_period($user, $override = null) { if (!$this->can_activate_2fa($user) || $this->has_2fa_active($user)) return false; update_user_option($user->ID, self::META_KEY_GRACE_PERIOD_RESET, time(), true); if ($override !== null) update_user_option($user->ID, self::META_KEY_GRACE_PERIOD_OVERRIDE, (int) $override, true); return true; } public function revoke_grace_period($user) { foreach(array( self::META_KEY_GRACE_PERIOD_RESET, self::META_KEY_GRACE_PERIOD_OVERRIDE, self::META_KEY_ALLOW_GRACE_PERIOD ) as $option) { delete_user_option($user->ID, $option, true); } } public function allow_grace_period($userId) { update_user_option($userId, self::META_KEY_ALLOW_GRACE_PERIOD, true, true); } public function get_grace_period_allowed_flag($userId) { return (bool) get_user_option(self::META_KEY_ALLOW_GRACE_PERIOD, $userId); } public function has_revokable_grace_period($user) { return $this->get_grace_period_allowed_flag($user->ID) || $this->get_grace_period_reset_time($user) !== null; } private function get_inactive_2fa_super_admins($gracePeriod = false) { $inactive = array(); foreach(get_super_admins() as $username) { $user = new \WP_User($username); if (!$this->has_2fa_active($user)) { $this->requires_2fa($user, $inGracePeriod, $requiredAt); if ($gracePeriod === null || $gracePeriod == $inGracePeriod) { $current = new \StdClass(); $current->user_id = $user->ID; $current->user_login = $username; $current->required_at = $requiredAt; $inactive[] = $current; } } } return $inactive; } private function generate_inactive_2fa_user_query($roleKey, $gracePeriod = null, $page = null, $perPage = null) { global $wpdb; $secondsPerDay = (int) self::SECONDS_PER_DAY; $gracePeriodSeconds = (int) (Controller_Settings::shared()->get_user_2fa_grace_period() * self::SECONDS_PER_DAY); $roleTime = (int) (Controller_Settings::shared()->get_required_2fa_role_activation_time($roleKey)); $siteId = get_current_blog_id(); $blogPrefix = $wpdb->get_blog_prefix($siteId); $usermeta = $wpdb->usermeta; $users = $wpdb->users; $secrets = Controller_DB::shared()->secrets; $admin = Controller_Permissions::shared()->can_role_manage_settings($roleKey); $parameters = array( self::META_KEY_GRACE_PERIOD_RESET, self::META_KEY_GRACE_PERIOD_OVERRIDE ); $gracePeriodClause = "IF(overrides.days IS NULL, $gracePeriodSeconds, overrides.days * $secondsPerDay)"; $registeredTimestampClause = "UNIX_TIMESTAMP(CONVERT_TZ($users.user_registered, '+00:00', @@time_zone))"; $now = time(); if ($admin) { $allowancesJoin = <<= 0 && $perPage > 0) $query .= " LIMIT $offset, $limit"; } $serializedRoleKey = serialize($roleKey); $roleMatch = '%' . (method_exists($wpdb, 'esc_like') ? $wpdb->esc_like($serializedRoleKey) : addcslashes($serializedRoleKey, '_%\\')). '%'; $parameters[] = $roleMatch; return $wpdb->prepare( $query.';', $parameters ); } public function get_inactive_2fa_users($roleKey, $gracePeriod = null, $page = null, $perPage = null, &$lastPage = null) { global $wpdb; if (is_multisite() && $roleKey === 'super-admin') { $superAdmins = $this->get_inactive_2fa_super_admins($gracePeriod); if ($page !== null && $perPage !== null) { $start = ($page - 1) * $perPage; $end = $start + $perPage; $lastPage = $end >= count($superAdmins); $superAdmins = array_slice($superAdmins, $start, $perPage); } return $superAdmins; } else { $query = $this->generate_inactive_2fa_user_query($roleKey, $gracePeriod, $page, $perPage); $results = $wpdb->get_results($query); if (count($results) > $perPage) { $lastPage = false; array_pop($results); } else { $lastPage = true; } return $results; } } private function get_verification_token_transient_key($hash) { return self::VERIFICATION_TOKEN_TRANSIENT_PREFIX . $hash; } private function load_verification_token($hash) { $key = $this->get_verification_token_transient_key($hash); $userId = get_transient($key); if ($userId === false) return null; return intval($userId); } private function load_verification_tokens($user) { $storedHashes = get_user_meta($user->ID, self::META_KEY_VERIFICATION_TOKENS, true); $validHashes = array(); if (is_array($storedHashes)) { foreach ($storedHashes as $hash) { $userId = $this->load_verification_token($hash); if ($userId === $user->ID) $validHashes[] = $hash; } } return $validHashes; } private function hash_verification_token($token) { return wp_hash($token); } public function generate_verification_token($user) { $token = Model_Crypto::random_bytes(self::VERIFICATION_TOKEN_BYTES); $hash = $this->hash_verification_token($token); $tokens = $this->load_verification_tokens($user); array_unshift($tokens, $hash); while (count($tokens) > self::VERIFICATION_TOKEN_LIMIT) { $excessHash = array_pop($tokens); delete_transient($this->get_verification_token_transient_key($excessHash)); } $key = $this->get_verification_token_transient_key($hash); set_transient($key, $user->ID, WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES * 60); update_user_meta($user->ID, self::META_KEY_VERIFICATION_TOKENS, $tokens); return base64_encode($token); } public function validate_verification_token($token, $user = null) { $hash = $this->hash_verification_token(base64_decode($token)); $userId = $this->load_verification_token($hash); return $userId !== null && ($user === null || $userId === $user->ID); } /** * Returns the key used to store a captcha score transient. * * @param string $hash * @return string */ private function get_captcha_score_transient_key($hash) { return self::CAPTCHA_SCORE_TRANSIENT_PREFIX . $hash; } /** * Attempts to look up a stored captcha score for the given hash and user. If found, returns the score. If not, * returns null. * * @param string $hash * @param \WP_User $user * @return float|false */ private function load_captcha_score($hash, $user) { $key = $this->get_captcha_score_transient_key($hash); $data = get_transient($key); if ($data === false) { return false; } if (!$user->exists() || $data['user'] !== $user->ID) { return false; } return floatval($data['score']); } /** * Deletes the stored captcha score if present for the given hash. * * @param string $hash */ private function clear_captcha_score($token, $user) { $hash = $this->hash_captcha_token($token); $key = $this->get_captcha_score_transient_key($hash); delete_transient($key); $storedHashes = get_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, true); $validHashes = array(); if (is_array($storedHashes)) { foreach ($storedHashes as $hash) { $storedScore = $this->load_captcha_score($hash, $user); if ($storedScore !== false) { $validHashes[] = $hash; } } } $validHashes = array_slice($validHashes, 0, self::CAPTCHA_SCORE_LIMIT); update_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, $validHashes); } /** * Hashes the captcha token for storage. * * @param string $token * @return string */ private function hash_captcha_token($token) { return wp_hash($token); } /** * Returns the cached score for the given captcha score and user if available. This action removes it from the cache * since the intent is for it only to be used for the initial login request to validate credentials + the follow-up * request either finalizing the login (no 2FA set) or with the 2FA token. * * $expired will be set to `true` if the reason for returning `false` is because the $token is recently expired. It * will be false when the $token is either uncached or has been expired long enough to be removed from the internal * list. * * @param string $token * @param \WP_User $user * @param bool $expired * @return float|false */ public function cached_captcha_score($token, $user, &$expired = false) { $hash = $this->hash_captcha_token($token); $score = $this->load_captcha_score($hash, $user); if ($score === false) { $storedHashes = get_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, true); if (is_array($storedHashes)) { $expired = in_array($hash, $storedHashes); } } $this->clear_captcha_score($token, $user); return $score; } /** * Caches the $token/$score pair for $user, automatically pruning its cached list to the maximum allowable count * * @param string $token * @param float|false $score * @param \WP_User $user */ public function cache_captcha_score($token, $score, $user) { if ($score === false) { return; } $storedHashes = get_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, true); $validHashes = array(); if (is_array($storedHashes)) { foreach ($storedHashes as $hash) { $storedScore = $this->load_captcha_score($hash, $user); if ($storedScore !== false) { $validHashes[] = $hash; } } } $hash = $this->hash_verification_token($token); array_unshift($validHashes, $hash); while (count($validHashes) > self::CAPTCHA_SCORE_LIMIT) { $excessHash = array_pop($validHashes); delete_transient($this->get_captcha_score_transient_key($excessHash)); } $key = $this->get_captcha_score_transient_key($hash); set_transient($key, array('user' => $user->ID, 'score' => $score), self::CAPTCHA_SCORE_CACHE_DURATION); update_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, $validHashes); } public function get_user_count() { global $wpdb; if (function_exists('get_user_count')) return get_user_count(); return $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->users}"); } public function has_large_user_base() { return $this->get_user_count() >= self::LARGE_USER_BASE_THRESHOLD; } public function should_force_user_counts() { return isset($_GET['wfls-show-user-counts']); } public function get_detailed_user_counts_if_enabled() { $force = $this->should_force_user_counts(); if ($this->has_large_user_base() && !$force) return null; return $this->detailed_user_counts($force); } }login-security/classes/controller/wordfencels.php000064400000146365147207020750016410 0ustar00_init_actions(); Controller_AJAX::shared()->init(); Controller_Users::shared()->init(); Controller_Time::shared()->init(); Controller_Permissions::shared()->init(); } protected function _init_actions() { register_activation_hook(WORDFENCE_LS_FCPATH, array($this, '_install_plugin')); register_deactivation_hook(WORDFENCE_LS_FCPATH, array($this, '_uninstall_plugin')); $versionInOptions = ((is_multisite() && function_exists('get_network_option')) ? get_network_option(null, self::VERSION_KEY, false) : get_option(self::VERSION_KEY, false)); if (!$versionInOptions || version_compare(WORDFENCE_LS_VERSION, $versionInOptions, '>')) { //Either there is no version in options or the version in options is greater and we need to run the upgrade $this->_install(); } if (!Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_ALLOW_XML_RPC)) { add_filter('xmlrpc_enabled', array($this, '_block_xml_rpc')); } add_action('admin_init', array($this, '_admin_init')); add_action('login_enqueue_scripts', array($this, '_login_enqueue_scripts')); add_filter('authenticate', array($this, '_authenticate'), 25, 3); add_action('set_logged_in_cookie', array($this, '_set_logged_in_cookie'), 25, 4); add_action('wp_login', array($this, '_record_login'), 999, 1); add_action('register_post', array($this, '_register_post'), 25, 3); add_filter('wp_login_errors', array($this, '_wp_login_errors'), 25, 3); if ($this->is_woocommerce_integration_enabled()) { $this->init_woocommerce_actions(); } add_action('user_new_form', array($this, '_user_new_form')); add_action('user_register', array($this, '_user_register')); $useSubmenu = WORDFENCE_LS_FROM_CORE; if (is_multisite() && !is_network_admin()) { $useSubmenu = false; } add_action('admin_menu', array($this, '_admin_menu'), $useSubmenu ? 55 : 10); if (is_multisite()) { add_action('network_admin_menu', array($this, '_admin_menu'), $useSubmenu ? 55 : 10); } add_action('admin_enqueue_scripts', array($this, '_admin_enqueue_scripts')); add_action('show_user_profile', array($this, '_edit_user_profile'), 0); //We can't add it to the password section directly -- priority 0 is as close as we can get add_action('edit_user_profile', array($this, '_edit_user_profile'), 0); add_action('init', array($this, '_wordpress_init')); if ($this->is_shortcode_enabled()) add_action('wp_enqueue_scripts', array($this, '_handle_shortcode_prerequisites')); Controller_Permissions::_init_actions(); } public function _wordpress_init() { if (!WORDFENCE_LS_FROM_CORE) load_plugin_textdomain('wordfence-login-security', false, WORDFENCE_LS_PATH . 'languages'); if ($this->is_shortcode_enabled()) add_shortcode(self::SHORTCODE_2FA_MANAGEMENT, array($this, '_handle_user_2fa_management_shortcode')); } private function init_woocommerce_actions() { add_action('woocommerce_before_customer_login_form', array($this, '_woocommerce_login_enqueue_scripts')); add_action('woocommerce_before_checkout_form', array($this, '_woocommerce_checkout_login_enqueue_scripts')); add_action('wp_loaded', array($this, '_handle_woocommerce_registration'), 10, 0); //Woocommerce uses priority 20 if ($this->is_woocommerce_account_integration_enabled()) { add_filter('woocommerce_account_menu_items', array($this, '_woocommerce_account_menu_items')); add_filter('woocommerce_account_wordfence-2fa_endpoint', array($this, '_woocommerce_account_menu_content')); add_filter('woocommerce_get_query_vars', array($this, '_woocommerce_get_query_vars')); add_action('wp_enqueue_scripts', array($this, '_woocommerce_account_enqueue_assets')); } } public function _admin_init() { if (WORDFENCE_LS_FROM_CORE) { \wfModuleController::shared()->addOptionIndex('wfls-option-enable-2fa-roles', __('Login Security: Enable 2FA for these roles', 'wordfence')); \wfModuleController::shared()->addOptionIndex('wfls-option-allow-remember', __('Login Security: Allow remembering device for 30 days', 'wordfence')); \wfModuleController::shared()->addOptionIndex('wfls-option-require-2fa-xml-rpc', __('Login Security: Require 2FA for XML-RPC call authentication', 'wordfence')); \wfModuleController::shared()->addOptionIndex('wfls-option-disable-xml-rpc', __('Login Security: Disable XML-RPC authentication', 'wordfence')); \wfModuleController::shared()->addOptionIndex('wfls-option-whitelist-2fa', __('Login Security: Allowlisted IP addresses that bypass 2FA and reCAPTCHA', 'wordfence')); \wfModuleController::shared()->addOptionIndex('wfls-option-enable-captcha', __('Login Security: Enable reCAPTCHA on the login and user registration pages', 'wordfence')); $title = __('Login Security Options', 'wordfence'); $description = __('Login Security options are available on the Login Security options page', 'wordfence'); $url = esc_url(network_admin_url('admin.php?page=WFLS#top#settings')); $link = __('Login Security Options', 'wordfence');; \wfModuleController::shared()->addOptionBlock(<<
{$title}
END ); } if (Controller_Permissions::shared()->can_manage_settings()) { if ((is_plugin_active('jetpack/jetpack.php') || (is_multisite() && is_plugin_active_for_network('jetpack/jetpack.php'))) && !Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_ALLOW_XML_RPC)) { if (is_multisite()) { add_action('network_admin_notices', array($this, '_jetpack_xml_rpc_notice')); } else { add_action('admin_notices', array($this, '_jetpack_xml_rpc_notice')); } } if (Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_CAPTCHA_TEST_MODE) && Controller_CAPTCHA::shared()->enabled()) { if (is_multisite()) { add_action('network_admin_notices', array($this, '_recaptcha_test_notice')); } else { add_action('admin_notices', array($this, '_recaptcha_test_notice')); } } if ($this->has_woocommerce() && !Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_ENABLE_WOOCOMMERCE_INTEGRATION)) { if (!Controller_Notices::shared()->is_persistent_notice_dismissed(get_current_user_id(), Controller_Notices::PERSISTENT_NOTICE_WOOCOMMERCE_INTEGRATION)) { Controller_Notices::shared()->register_persistent_notice(Controller_Notices::PERSISTENT_NOTICE_WOOCOMMERCE_INTEGRATION); add_action(is_multisite() ? 'network_admin_notices' : 'admin_notices', array($this, '_woocommerce_integration_notice')); } } } } /** * Notices */ public function _jetpack_xml_rpc_notice() { echo '

' . wp_kses(sprintf(__('XML-RPC authentication is disabled. Jetpack is currently active and requires XML-RPC authentication to work correctly. Manage Settings', 'wordfence'), esc_url(network_admin_url('admin.php?page=WFLS#top#settings'))), array('a'=>array('href'=>array()))) . '

'; } public function _recaptcha_test_notice() { echo '

' . wp_kses(sprintf(__('reCAPTCHA test mode is enabled. While enabled, login and registration requests will be checked for their score but will not be blocked if the score is below the minimum score. Manage Settings', 'wordfence'), esc_url(network_admin_url('admin.php?page=WFLS#top#settings'))), array('a'=>array('href'=>array()))) . '

'; } public function _woocommerce_integration_notice() { ?>

_install(); } public function _uninstall_plugin() { Controller_Time::shared()->uninstall(); Controller_Permissions::shared()->uninstall(); foreach (array(self::VERSION_KEY) as $opt) { if (is_multisite() && function_exists('delete_network_option')) { delete_network_option(null, $opt); } delete_option($opt); } if (Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_DELETE_ON_DEACTIVATION)) { Controller_DB::shared()->uninstall(); } $this->purge_rewrite_rules(); } protected function _install() { static $_runInstallCalled = false; if ($_runInstallCalled) { return; } $_runInstallCalled = true; if (function_exists('ignore_user_abort')) { @ignore_user_abort(true); } if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); } $previousVersion = ((is_multisite() && function_exists('get_network_option')) ? get_network_option(null, self::VERSION_KEY, '0.0.0') : get_option(self::VERSION_KEY, '0.0.0')); if (is_multisite() && function_exists('update_network_option')) { update_network_option(null, self::VERSION_KEY, WORDFENCE_LS_VERSION); //In case we have a fatal error we don't want to keep running install. } else { update_option(self::VERSION_KEY, WORDFENCE_LS_VERSION); //In case we have a fatal error we don't want to keep running install. } Controller_DB::shared()->install(); Controller_Settings::shared()->set_defaults(); if (\WordfenceLS\Controller_Time::time() > Controller_Settings::shared()->get_int(Controller_Settings::OPTION_LAST_SECRET_REFRESH) + 180 * 86400) { Model_Crypto::refresh_secrets(); } Controller_Time::shared()->install(); Controller_Permissions::shared()->install(); $this->purge_rewrite_rules(); } private function purge_rewrite_rules() { // This is usually done internally in WP_Rewrite::flush_rules, but is followed there by WP_Rewrite::wp_rewrite_rules which repopulates it. This should cause it to be repopulated on the next request. update_option('rewrite_rules', ''); } /** * In most cases, this will be done internally by WooCommerce since we are using the woocommerce_get_query_vars filter, but when toggling the option on our settings page we must still do this manually */ private function register_rewrite_endpoints() { add_rewrite_endpoint(self::WOOCOMMERCE_ENDPOINT, $this->is_woocommerce_account_integration_enabled() ? EP_PAGES : EP_NONE); } public function refresh_rewrite_rules() { $this->register_rewrite_endpoints(); flush_rewrite_rules(); } public function _block_xml_rpc() { /** * Fires just prior to blocking an XML-RPC request. After firing this action hook the XML-RPC request is blocked. * * @param int $source The source code of the block. */ do_action('wfls_xml_rpc_blocked', 2); return false; } private function has_woocommerce() { return class_exists('woocommerce'); } private function is_woocommerce_integration_enabled() { return Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_ENABLE_WOOCOMMERCE_INTEGRATION); } private function is_woocommerce_account_integration_enabled() { return $this->is_woocommerce_integration_enabled() && Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_ENABLE_WOOCOMMERCE_ACCOUNT_INTEGRATION); } private function is_shortcode_enabled() { return Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_ENABLE_SHORTCODE); } public function _woocommerce_login_enqueue_scripts() { wp_enqueue_style('dashicons'); $this->_login_enqueue_scripts(); } public function _woocommerce_checkout_login_enqueue_scripts() { /** * This is the same check used in WooCommerce to determine whether or not to display the checkout login form * @see templates/checkout/form-login.php in WooCommerce */ if ( is_user_logged_in() || 'no' === get_option( 'woocommerce_enable_checkout_login_reminder' ) ) { return; } $this->_woocommerce_login_enqueue_scripts(); } /** * Login Page */ public function _login_enqueue_scripts() { $useCAPTCHA = Controller_CAPTCHA::shared()->enabled(); if ($useCAPTCHA) { wp_enqueue_script('wordfence-ls-recaptcha', 'https://www.google.com/recaptcha/api.js?render=' . urlencode(Controller_Settings::shared()->get(Controller_Settings::OPTION_RECAPTCHA_SITE_KEY))); } if ($useCAPTCHA || Controller_Users::shared()->any_2fa_active()) { $this->validate_email_verification_token(null, $verification); Model_Script::create('wordfence-ls-login', Model_Asset::js('login.js'), array('jquery'), WORDFENCE_LS_VERSION) ->withTranslations(array( 'Message to Support' => __('Message to Support', 'wordfence'), 'Send' => __('Send', 'wordfence'), 'An error was encountered while trying to send the message. Please try again.' => __('An error was encountered while trying to send the message. Please try again.', 'wordfence'), 'ERROR: An error was encountered while trying to send the message. Please try again.' => wp_kses(__('ERROR: An error was encountered while trying to send the message. Please try again.', 'wordfence'), array('strong' => array())), 'Login failed with status code 403. Please contact the site administrator.' => __('Login failed with status code 403. Please contact the site administrator.', 'wordfence'), 'ERROR: Login failed with status code 403. Please contact the site administrator.' => wp_kses(__('ERROR: Login failed with status code 403. Please contact the site administrator.', 'wordfence'), array('strong' => array())), 'Login failed with status code 503. Please contact the site administrator.' => __('Login failed with status code 503. Please contact the site administrator.', 'wordfence'), 'ERROR: Login failed with status code 503. Please contact the site administrator.' => wp_kses(__('ERROR: Login failed with status code 503. Please contact the site administrator.', 'wordfence'), array('strong' => array())), 'Wordfence 2FA Code' => __('Wordfence 2FA Code', 'wordfence'), 'Remember for 30 days' => __('Remember for 30 days', 'wordfence'), 'Log In' => __('Log In', 'wordfence'), 'ERROR: An error was encountered while trying to authenticate. Please try again.' => wp_kses(__('ERROR: An error was encountered while trying to authenticate. Please try again.', 'wordfence'), array('strong' => array())), 'The Wordfence 2FA Code can be found within the authenticator app you used when first activating two-factor authentication. You may also use one of your recovery codes.' => __('The Wordfence 2FA Code can be found within the authenticator app you used when first activating two-factor authentication. You may also use one of your recovery codes.', 'wordfence') )) ->setTranslationObjectName('WFLS_LOGIN_TRANSLATIONS') ->enqueue(); wp_enqueue_style('wordfence-ls-login', Model_Asset::css('login.css'), array(), WORDFENCE_LS_VERSION); wp_localize_script('wordfence-ls-login', 'WFLSVars', array( 'ajaxurl' => Utility_URL::relative_admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('wp-ajax'), 'recaptchasitekey' => Controller_Settings::shared()->get(Controller_Settings::OPTION_RECAPTCHA_SITE_KEY), 'useCAPTCHA' => $useCAPTCHA, 'allowremember' => Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_REMEMBER_DEVICE_ENABLED), 'verification' => $verification, )); } } private function get_2fa_management_script_data() { return array( 'WFLSVars' => array( 'ajaxurl' => Utility_URL::relative_admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('wp-ajax'), 'modalTemplate' => Model_View::create('common/modal-prompt', array('title' => '${title}', 'message' => '${message}', 'primaryButton' => array('id' => 'wfls-generic-modal-close', 'label' => __('Close', 'wordfence'), 'link' => '#')))->render(), 'modalNoButtonsTemplate' => Model_View::create('common/modal-prompt', array('title' => '${title}', 'message' => '${message}'))->render(), 'tokenInvalidTemplate' => Model_View::create('common/modal-prompt', array('title' => '${title}', 'message' => '${message}', 'primaryButton' => array('id' => 'wfls-token-invalid-modal-reload', 'label' => __('Reload', 'wordfence'), 'link' => '#')))->render(), 'modalHTMLTemplate' => Model_View::create('common/modal-prompt', array('title' => '${title}', 'message' => '{{html message}}', 'primaryButton' => array('id' => 'wfls-generic-modal-close', 'label' => __('Close', 'wordfence'), 'link' => '#')))->render() ) ); } public function should_use_core_font_awesome_styles() { if ($this->use_core_font_awesome_styles === null) { $this->use_core_font_awesome_styles = wp_style_is('wordfence-font-awesome-style'); } return $this->use_core_font_awesome_styles; } private function get_2fa_management_assets($embedded = false) { $assets = array( Model_Script::create('wordfence-ls-jquery.qrcode', Model_Asset::js('jquery.qrcode.min.js'), array('jquery'), WORDFENCE_LS_VERSION), Model_Script::create('wordfence-ls-jquery.tmpl', Model_Asset::js('jquery.tmpl.min.js'), array('jquery'), WORDFENCE_LS_VERSION), Model_Script::create('wordfence-ls-jquery.colorbox', Model_Asset::js('jquery.colorbox.min.js'), array('jquery'), WORDFENCE_LS_VERSION) ); if (Controller_Permissions::shared()->can_manage_settings()) { $assets[] = Model_Style::create('wordfence-ls-jquery-ui-css', Model_Asset::css('jquery-ui.min.css'), array(), WORDFENCE_LS_VERSION); $assets[] = Model_Style::create('wordfence-ls-jquery-ui-css.structure', Model_Asset::css('jquery-ui.structure.min.css'), array(), WORDFENCE_LS_VERSION); $assets[] = Model_Style::create('wordfence-ls-jquery-ui-css.theme', Model_Asset::css('jquery-ui.theme.min.css'), array(), WORDFENCE_LS_VERSION); } $assets[] = Model_Script::create('wordfence-ls-admin', Model_Asset::js('admin.js'), array('jquery'), WORDFENCE_LS_VERSION) ->withTranslation('You have unsaved changes to your options. If you leave this page, those changes will be lost.', __('You have unsaved changes to your options. If you leave this page, those changes will be lost.', 'wordfence')) ->setTranslationObjectName('WFLS_ADMIN_TRANSLATIONS'); $registered = array( Model_Script::create('chart-js', Model_Asset::js('chart.umd.js'), array('jquery'), '4.2.1')->setRegistered(), Model_Script::create('wordfence-select2-js', Model_Asset::js('wfselect2.min.js'), array('jquery'), WORDFENCE_LS_VERSION)->setRegistered(), Model_Style::create('wordfence-select2-css', Model_Asset::css('wfselect2.min.css'), array(), WORDFENCE_LS_VERSION)->setRegistered() ); if (!WORDFENCE_LS_FROM_CORE && !$this->management_assets_registered) { foreach ($registered as $asset) $asset->register(); $this->management_assets_registered = true; } $assets = array_merge($assets, $registered); $assets[] = Model_Style::create('wordfence-ls-admin', Model_Asset::css('admin.css'), array(), WORDFENCE_LS_VERSION); $assets[] = Model_Style::create('wordfence-ls-colorbox', Model_Asset::css('colorbox.css'), array(), WORDFENCE_LS_VERSION); $assets[] = Model_Style::create('wordfence-ls-ionicons', Model_Asset::css('ionicons.css'), array(), WORDFENCE_LS_VERSION); if ($embedded) { $assets[] = Model_Style::create('dashicons'); $assets[] = Model_Style::create('wordfence-ls-embedded', Model_Asset::css('embedded.css'), array(), WORDFENCE_LS_VERSION); } if (!$this->should_use_core_font_awesome_styles()) { $assets[] = Model_Style::create('wordfence-ls-font-awesome', Model_Asset::css('font-awesome.css'), array(), WORDFENCE_LS_VERSION); } return $assets; } private function enqueue_2fa_management_assets($embedded = false) { if ($this->management_assets_enqueued) return; foreach ($this->get_2fa_management_assets($embedded) as $asset) $asset->enqueue(); foreach ($this->get_2fa_management_script_data() as $key => $data) wp_localize_script('wordfence-ls-admin', $key, $data); $this->management_assets_enqueued = true; } /** * Admin Pages */ public function _admin_enqueue_scripts($hookSuffix) { if (isset($_GET['page']) && $_GET['page'] == 'WFLS') { $this->enqueue_2fa_management_assets(); } else { wp_enqueue_style('wordfence-ls-admin-global', Model_Asset::css('admin-global.css'), array(), WORDFENCE_LS_VERSION); } if (Controller_Notices::shared()->has_notice(wp_get_current_user()) || in_array($hookSuffix, array('user-edit.php', 'user-new.php', 'profile.php'))) { wp_enqueue_script('wordfence-ls-admin-global', Model_Asset::js('admin-global.js'), array('jquery'), WORDFENCE_LS_VERSION); wp_localize_script('wordfence-ls-admin-global', 'GWFLSVars', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('wp-ajax'), )); } } public function _edit_user_profile($user) { if ($user->ID == get_current_user_id() || !current_user_can(Controller_Permissions::CAP_ACTIVATE_2FA_OTHERS)) { $manageURL = admin_url('admin.php?page=WFLS'); } else { $manageURL = admin_url('admin.php?page=WFLS&user=' . ((int) $user->ID)); } if (is_multisite() && is_super_admin()) { if ($user->ID == get_current_user_id()) { $manageURL = network_admin_url('admin.php?page=WFLS'); } else { $manageURL = network_admin_url('admin.php?page=WFLS&user=' . ((int) $user->ID)); } } $userAllowed2fa = Controller_Users::shared()->can_activate_2fa($user); $viewerIsUser = $user->ID == get_current_user_id(); $viewerCanManage2fa = current_user_can(Controller_Permissions::CAP_ACTIVATE_2FA_OTHERS); $requires2fa = Controller_Users::shared()->requires_2fa($user, $inGracePeriod, $requiredAt); $has2fa = Controller_Users::shared()->has_2fa_active($user); $lockedOut = $requires2fa && !$has2fa; $hasGracePeriod = Controller_Settings::shared()->get_user_2fa_grace_period() > 0; if ($userAllowed2fa && ($viewerIsUser || $viewerCanManage2fa)): ?>

:

has_2fa_active($user) ? esc_html__('Manage 2FA', 'wordfence') : esc_html__('Activate 2FA', 'wordfence')); ?>

:

$user, 'gracePeriod' => $inGracePeriod ))->render() ?> has_revokable_grace_period($user)): ?> $user ))->render() ?>

has_woocommerce()) return false; $nonceValue = ''; foreach (array('woocommerce-login-nonce', '_wpnonce') as $key) { if (array_key_exists($key, $_REQUEST)) { $nonceValue = $_REQUEST[$key]; break; } } return ( isset( $_POST['login'], $_POST['username'], $_POST['password'] ) && is_string($nonceValue) && wp_verify_nonce( $nonceValue, 'woocommerce-login' ) ); } public function _authenticate($user, $username, $password) { if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST && !Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_XMLRPC_ENABLED)) { //XML-RPC call and we're not enforcing 2FA on it return $user; } if (Controller_Whitelist::shared()->is_whitelisted(Model_Request::current()->ip())) { //Whitelisted, so we're not enforcing 2FA return $user; } $isLogin = !(defined('WORDFENCE_LS_AUTHENTICATION_CHECK') && WORDFENCE_LS_AUTHENTICATION_CHECK); //Checking for the purpose of prompting for 2FA, don't enforce it here $isCombinedCheck = (defined('WORDFENCE_LS_CHECKING_COMBINED') && WORDFENCE_LS_CHECKING_COMBINED); $combinedTwoFactor = false; /* * If we don't have a valid $user at this point, it means the $username/$password combo is invalid. We'll check * to see if the user has provided a combined password in the format ``, populating $user from * that if so. */ if (!defined('WORDFENCE_LS_CHECKING_COMBINED') && (!isset($_POST['wfls-token']) || !is_string($_POST['wfls-token'])) && (!is_object($user) || !($user instanceof \WP_User))) { //Compatibility with WF legacy 2FA $combinedTOTPRegex = '/((?:[0-9]{3}\s*){2})$/i'; $combinedRecoveryRegex = '/((?:[a-f0-9]{4}\s*){4})$/i'; if ($this->legacy_2fa_active()) { $combinedTOTPRegex = '/(? strlen($matches[1])) { $revisedPassword = substr($password, 0, strlen($password) - strlen($matches[1])); $code = $matches[1]; } } else if (preg_match($combinedRecoveryRegex, $password, $matches)) { //Possible recovery code if (strlen($password) > strlen($matches[1])) { $revisedPassword = substr($password, 0, strlen($password) - strlen($matches[1])); $code = $matches[1]; } } if (isset($revisedPassword)) { define('WORDFENCE_LS_CHECKING_COMBINED', true); //Avoid recursing into this block if (!defined('WORDFENCE_LS_AUTHENTICATION_CHECK')) { define('WORDFENCE_LS_AUTHENTICATION_CHECK', true); } $revisedUser = wp_authenticate($username, $revisedPassword); if (is_object($revisedUser) && ($revisedUser instanceof \WP_User) && Controller_TOTP::shared()->validate_2fa($revisedUser, $code, $isLogin)) { define('WORDFENCE_LS_COMBINED_IS_VALID', true); //This will cause the front-end to skip the 2FA prompt $user = $revisedUser; $combinedTwoFactor = true; } } } /* * CAPTCHA Check * * It will be enforced so long as: * * 1. It's enabled and keys are set. * 2. This is not an XML-RPC request. An XML-RPC request is de facto an automated request, so a CAPTCHA makes * no sense. * 3. A filter does not override it. This is to allow plugins with REST endpoints that handle authentication * themselves to opt out of the requirement. * 4. The user is not providing a combined credentials + 2FA authentication login request. * 5. The request is not a WooCommerce login while WC integration is disabled */ if (!$combinedTwoFactor && !$isCombinedCheck && !empty($username) && (!$this->_is_woocommerce_login() || Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_ENABLE_WOOCOMMERCE_INTEGRATION))) { //Login attempt, not just a wp-login.php page load $requireCAPTCHA = Controller_CAPTCHA::shared()->is_captcha_required(); $performVerification = false; $token = Controller_CAPTCHA::shared()->get_token(); if ($requireCAPTCHA && empty($token) && !Controller_CAPTCHA::shared()->test_mode()) { //No CAPTCHA token means forced additional verification (if neither 2FA nor test mode are active) $performVerification = true; } if (is_object($user) && $user instanceof \WP_User && $this->validate_email_verification_token($user)) { //Skip the CAPTCHA check if the email address was verified $requireCAPTCHA = false; $performVerification = false; //Reset token rate limit $identifier = sprintf('wfls-captcha-%d', $user->ID); $tokenBucket = new Model_TokenBucket('rate:' . $identifier, 3, 1 / (WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES * Model_TokenBucket::MINUTE)); //Maximum of three requests, refilling at a rate of one per token expiration period $tokenBucket->reset(); } $score = false; if ($requireCAPTCHA && !$performVerification) { $expired = false; if (is_object($user) && $user instanceof \WP_User) { $score = Controller_Users::shared()->cached_captcha_score($token, $user, $expired); } if ($score === false) { if ($expired) { return new \WP_Error('wfls_captcha_expired', wp_kses(__('CAPTCHA EXPIRED: The CAPTCHA verification for this login attempt has expired. Please try again.', 'wordfence'), array('strong'=>array()))); } $score = Controller_CAPTCHA::shared()->score($token); if ($score !== false && is_object($user) && $user instanceof \WP_User) { Controller_Users::shared()->cache_captcha_score($token, $score, $user); Controller_Users::shared()->record_captcha_score($user, $score); } } if ($score === false && !Controller_CAPTCHA::shared()->test_mode()) { //An invalid token will require additional verification (if test mode is not active) $performVerification = true; } } if ($requireCAPTCHA) { if ($performVerification || !Controller_CAPTCHA::shared()->is_human($score)) { if (is_object($user) && $user instanceof \WP_User) { $identifier = sprintf('wfls-captcha-%d', $user->ID); $tokenBucket = new Model_TokenBucket('rate:' . $identifier, 3, 1 / (WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES * Model_TokenBucket::MINUTE)); //Maximum of three requests, refilling at a rate of one per token expiration period if ($tokenBucket->consume(1)) { if ($this->has_woocommerce() && array_key_exists('woocommerce-login-nonce', $_POST)) { $loginUrl = get_permalink(get_option('woocommerce_myaccount_page_id')); } else { $loginUrl = wp_login_url(); } $verificationUrl = add_query_arg( array( 'wfls-email-verification' => rawurlencode(Controller_Users::shared()->generate_verification_token($user)) ), $loginUrl ); $view = new Model_View('email/login-verification', array( 'siteName' => get_bloginfo('name', 'raw'), 'verificationURL' => $verificationUrl, 'ip' => Model_Request::current()->ip(), 'canEnable2FA' => Controller_Users::shared()->can_activate_2fa($user), )); wp_mail($user->user_email, __('Login Verification Required', 'wordfence'), $view->render(), "Content-Type: text/html"); } } Utility_Sleep::sleep(Model_Crypto::random_int(0, 2000) / 1000); return new \WP_Error('wfls_captcha_verify', wp_kses(__('VERIFICATION REQUIRED: Additional verification is required for login. If there is a valid account for the provided login credentials, please check the email address associated with it for a verification link to continue logging in.', 'wordfence'), array('strong' => array()))); } } } if (!$combinedTwoFactor) { if ($isLogin && $user instanceof \WP_User) { if (Controller_Users::shared()->has_2fa_active($user)) { if (Controller_Users::shared()->has_remembered_2fa($user)) { return $user; } elseif (array_key_exists('wfls-token', $_POST)) { if (is_string($_POST['wfls-token']) && Controller_TOTP::shared()->validate_2fa($user, $_POST['wfls-token'])) { return $user; } else { return new \WP_Error('wfls_twofactor_failed', wp_kses(__('CODE INVALID: The 2FA code provided is either expired or invalid. Please try again.', 'wordfence'), array('strong'=>array()))); } } } $in2faGracePeriod = false; $time2faRequired = null; if (Controller_Users::shared()->has_2fa_active($user)) { $legacy2FAActive = Controller_WordfenceLS::shared()->legacy_2fa_active(); if ($legacy2FAActive) { return new \WP_Error('wfls_twofactor_required', wp_kses(__('CODE REQUIRED: Please enter your 2FA code immediately after your password in the same field.', 'wordfence'), array('strong'=>array()))); } return new \WP_Error('wfls_twofactor_required', wp_kses(__('CODE REQUIRED: Please provide your 2FA code when prompted.', 'wordfence'), array('strong'=>array()))); } else if (Controller_Users::shared()->requires_2fa($user, $in2faGracePeriod, $time2faRequired)) { return new \WP_Error('wfls_twofactor_blocked', wp_kses(__('LOGIN BLOCKED: 2FA is required to be active on your account. Please contact the site administrator.', 'wordfence'), array('strong'=>array()))); } else if ($in2faGracePeriod) { Controller_Notices::shared()->add_notice(Model_Notice::SEVERITY_CRITICAL, new Model_HTML(wp_kses(sprintf(__('You do not currently have two-factor authentication active on your account, which will be required beginning %s. Configure 2FA', 'wordfence'), Controller_Time::format_local_time('F j, Y g:i A', $time2faRequired), esc_url((is_multisite() && is_super_admin($user->ID)) ? network_admin_url('admin.php?page=WFLS') : admin_url('admin.php?page=WFLS'))), array('a'=>array('href'=>array())))), 'wfls-will-be-required', $user); } } } return $user; } public function _set_logged_in_cookie($logged_in_cookie, $expire, $expiration, $user_id) { $user = new \WP_User($user_id); if (Controller_Users::shared()->has_2fa_active($user) && isset($_POST['wfls-remember-device']) && $_POST['wfls-remember-device']) { Controller_Users::shared()->remember_2fa($user); } delete_user_meta($user_id, 'wfls-captcha-nonce'); } public function _record_login($user_login/*, $user -- we'd like to use the second parameter instead, but too many plugins call this hook and only provide one of the two required parameters*/) { $user = get_user_by('login', $user_login); if (is_object($user) && $user instanceof \WP_User && $user->exists()) { update_user_meta($user->ID, 'wfls-last-login', Controller_Time::time()); } } public function _register_post($sanitized_user_login, $user_email, $errors) { if (!empty($sanitized_user_login)) { $captchaResult = $this->process_registration_captcha_with_hooks(); if ($captchaResult !== true) { $errors->add($captchaResult['category'], $captchaResult['message']); } } } private function validate_email_verification_token($user = null, &$token = null) { $token = isset($_REQUEST['wfls-email-verification']) ? $_REQUEST['wfls-email-verification'] : null; if (empty($token)) return null; return is_string($token) && Controller_Users::shared()->validate_verification_token($token, $user); } /** * @param \WP_Error $errors * @param string $redirect_to * @return \WP_Error */ public function _wp_login_errors($errors, $redirect_to) { $has_errors = (method_exists($errors, 'has_errors') ? $errors->has_errors() : !empty($errors->errors)); //has_errors was added in WP 5.1 $emailVerificationTokenValid = $this->validate_email_verification_token(); if (!$has_errors && $emailVerificationTokenValid !== null) { if ($emailVerificationTokenValid) { $errors->add('wfls_email_verified', esc_html__('Email verification succeeded. Please continue logging in.', 'wordfence'), 'message'); } else { $errors->add('wfls_email_not_verified', esc_html__('Email verification invalid or expired. Please try again.', 'wordfence'), 'message'); } } return $errors; } public function legacy_2fa_active() { $wfLegacy2FAActive = false; if (class_exists('wfConfig') && \wfConfig::get('isPaid')) { $twoFactorUsers = \wfConfig::get_ser('twoFactorUsers', array()); if (is_array($twoFactorUsers) && count($twoFactorUsers) > 0) { foreach ($twoFactorUsers as $t) { if ($t[3] == 'activated') { $testUser = get_user_by('ID', $t[0]); if (is_object($testUser) && $testUser instanceof \WP_User && \wfUtils::isAdmin($testUser)) { $wfLegacy2FAActive = true; break; } } } } if ($wfLegacy2FAActive && class_exists('wfCredentialsController') && method_exists('wfCredentialsController', 'useLegacy2FA') && !\wfCredentialsController::useLegacy2FA()) { $wfLegacy2FAActive = false; } } return $wfLegacy2FAActive; } /** * Menu */ public function _admin_menu() { $user = wp_get_current_user(); if (Controller_Notices::shared()->has_notice($user)) { Controller_Users::shared()->requires_2fa($user, $gracePeriod); if (!$gracePeriod) { Controller_Notices::shared()->remove_notice(false, 'wfls-will-be-required', $user); } } Controller_Notices::shared()->enqueue_notices(); $useSubmenu = WORDFENCE_LS_FROM_CORE && current_user_can('activate_plugins'); if (is_multisite() && !is_network_admin()) { $useSubmenu = false; if (is_super_admin()) { return; } } if ($useSubmenu) { add_submenu_page('Wordfence', __('Login Security', 'wordfence'), __('Login Security', 'wordfence'), Controller_Permissions::CAP_ACTIVATE_2FA_SELF, 'WFLS', array($this, '_menu')); } else { add_menu_page(__('Login Security', 'wordfence'), __('Login Security', 'wordfence'), Controller_Permissions::CAP_ACTIVATE_2FA_SELF, 'WFLS', array($this, '_menu'), Model_Asset::img('menu.svg')); } } public function _menu() { $user = wp_get_current_user(); $administrator = false; $canEditUsers = false; if (Controller_Permissions::shared()->can_manage_settings($user)) { $administrator = true; } if (user_can($user, Controller_Permissions::CAP_ACTIVATE_2FA_OTHERS)) { $canEditUsers = true; if (isset($_GET['user'])) { $user = new \WP_User((int) $_GET['user']); if (!$user->exists()) { $user = wp_get_current_user(); } } } $sections = array(); if (isset($_GET['role']) && $canEditUsers) { $roleKey = $_GET['role']; $roles = new \WP_Roles(); $role = $roles->get_role($roleKey); $roleTitle = $roleKey === 'super-admin' ? __('Super Administrator', 'wordfence') : $roles->role_names[$roleKey]; $requiredAt = Controller_Settings::shared()->get_required_2fa_role_activation_time($roleKey); $states = array( 'grace_period' => array( 'title' => __('Grace Period', 'wordfence'), 'gracePeriod' => true ), 'locked_out' => array( 'title' => __('Locked Out', 'wordfence'), 'gracePeriod' => false ) ); foreach ($states as $key => $state) { $pageKey = "page_$key"; $page = isset($_GET[$pageKey]) ? max((int) $_GET[$pageKey], 1) : 1; $title = $state['title']; $lastPage = true; if ($requiredAt === false) $users = array(); else $users = Controller_Users::shared()->get_inactive_2fa_users($roleKey, $state['gracePeriod'], $page, self::USERS_PER_PAGE, $lastPage); $sections[] = array( 'tab' => new Model_Tab($key, $key, $title, $title), 'title' => new Model_Title($key, sprintf(__('Users without 2FA active (%s)', 'wordfence'), $title) . ' - ' . $roleTitle), 'content' => new Model_View('page/role', array( 'role' => $role, 'roleTitle' => $roleTitle, 'stateTitle' => $title, 'requiredAt' => $requiredAt, 'state' => $state, 'users' => $users, 'page' => $page, 'lastPage' => $lastPage, 'pageKey' => $pageKey, 'stateKey' => $key )), ); } } else { $sections[] = array( 'tab' => new Model_Tab('manage', 'manage', __('Two-Factor Authentication', 'wordfence'), __('Two-Factor Authentication', 'wordfence')), 'title' => new Model_Title('manage', __('Two-Factor Authentication', 'wordfence'), Controller_Support::supportURL(Controller_Support::ITEM_MODULE_LOGIN_SECURITY_2FA), new Model_HTML(wp_kses(__('Learn more about Two-Factor Authentication', 'wordfence'), array('span'=>array('class'=>array()))))), 'content' => new Model_View('page/manage', array( 'user' => $user, 'canEditUsers' => $canEditUsers, )), ); if ($administrator) { $sections[] = array( 'tab' => new Model_Tab('settings', 'settings', __('Settings', 'wordfence'), __('Settings', 'wordfence')), 'title' => new Model_Title('settings', __('Login Security Settings', 'wordfence'), Controller_Support::supportURL(Controller_Support::ITEM_MODULE_LOGIN_SECURITY), new Model_HTML(wp_kses(__('Learn more about Login Security', 'wordfence'), array('span'=>array('class'=>array()))))), 'content' => new Model_View('page/settings', array( 'hasWoocommerce' => $this->has_woocommerce() )), ); } } $view = new Model_View('page/page', array( 'sections' => $sections, )); echo $view->render(); } private function process_registration_captcha() { if (Controller_Whitelist::shared()->is_whitelisted(Model_Request::current()->ip())) { //Whitelisted, so we're not enforcing 2FA return true; } $captchaController = Controller_CAPTCHA::shared(); $requireCaptcha = $captchaController->is_captcha_required(); $token = $captchaController->get_token(); if ($requireCaptcha) { if ($token === null && !$captchaController->test_mode()) { return array( 'message' => wp_kses(__('REGISTRATION ATTEMPT BLOCKED: This site requires a security token created when the page loads for all registration attempts. Please ensure JavaScript is enabled and try again.', 'wordfence'), array('strong'=>array())), 'category' => 'wfls_captcha_required' ); } $score = $captchaController->score($token); if ($score === false && !$captchaController->test_mode()) { return array( 'message' => wp_kses(__('REGISTRATION ATTEMPT BLOCKED: The security token for the login attempt was invalid or expired. Please reload the page and try again.', 'wordfence'), array('strong'=>array())), 'category' => 'wfls_captcha_required' ); } Controller_Users::shared()->record_captcha_score(null, $score); if (!$captchaController->is_human($score)) { $encryptedIP = Model_Symmetric::encrypt(Model_Request::current()->ip()); $encryptedScore = Model_Symmetric::encrypt($score); $result = array( 'category' => 'wfls_registration_blocked' ); if ($encryptedIP && $encryptedScore && filter_var(get_site_option('admin_email'), FILTER_VALIDATE_EMAIL)) { $jwt = new Model_JWT(array('ip' => $encryptedIP, 'score' => $encryptedScore), Controller_Time::time() + 600); $result['message'] = wp_kses(sprintf(__('REGISTRATION BLOCKED: The registration request was blocked because it was flagged as spam. Please try again or contact the site owner for help.', 'wordfence'), esc_attr((string)$jwt)), array('strong'=>array(), 'a'=>array('href'=>array(), 'class'=>array(), 'data-token'=>array()))); } else { $result['message'] = wp_kses(__('REGISTRATION BLOCKED: The registration request was blocked because it was flagged as spam. Please try again or contact the site owner for help.', 'wordfence'), array('strong'=>array())); } return $result; } } return true; } /** * @param int $endpointType the type of endpoint being processed * The default value of 1 corresponds to a regular login * @see wordfence::wfsnEndpointType() */ private function process_registration_captcha_with_hooks($endpointType = 1) { $result = $this->process_registration_captcha(); if ($result !== true) { if ($result['category'] === 'wfls_registration_blocked') { /** * Fires just prior to blocking user registration due to a failed CAPTCHA. After firing this action hook * the registration attempt is blocked. * * @param int $source The source code of the block. */ do_action('wfls_registration_blocked', $endpointType); /** * Filters the message to show if registration is blocked due to a captcha rejection. * * @since 1.0.0 * * @param string $message The message to display, HTML allowed. */ $result['message'] = apply_filters('wfls_registration_blocked_message', $result['message']); } } return $result; } private function disable_woocommerce_registration($message) { if ($this->has_woocommerce()) { remove_action('wp_loaded', array('WC_Form_Handler', 'process_registration'), 20); wc_add_notice($message, 'error'); } } public function _handle_woocommerce_registration() { if ($this->has_woocommerce() && isset($_POST['register'], $_POST['email']) && (isset($_POST['_wpnonce']) || isset($_POST['woocommerce-register-nonce']))) { $captchaResult = $this->process_registration_captcha_with_hooks(); if ($captchaResult !== true) { $this->disable_woocommerce_registration($captchaResult['message']); } } } public function _user_new_form() { if (Controller_Settings::shared()->get_user_2fa_grace_period()) echo Model_View::create('user/grace-period-toggle', array())->render(); } public function _user_register($newUserId) { $creator = wp_get_current_user(); if (!Controller_Permissions::shared()->can_manage_settings($creator) || $creator->ID == $newUserId) return; if (isset($_POST['wfls-grace-period-toggle'])) Controller_Users::shared()->allow_grace_period($newUserId); } public function _woocommerce_account_menu_items($items) { if ($this->can_user_activate_2fa_self()) { $endpointId = self::WOOCOMMERCE_ENDPOINT; $label = __('Wordfence 2FA', 'wordfence'); if (!Utility_Array::insertAfter($items, 'edit-account', $endpointId, $label)) { $items[$endpointId] = $label; } } return $items; } public function _woocommerce_get_query_vars($query_vars) { $query_vars[self::WOOCOMMERCE_ENDPOINT] = self::WOOCOMMERCE_ENDPOINT; return $query_vars; } private function can_user_activate_2fa_self($user = null) { if ($user === null) $user = wp_get_current_user(); return user_can($user, Controller_Permissions::CAP_ACTIVATE_2FA_SELF); } private function render_embedded_user_2fa_management_interface($stacked = null) { $user = wp_get_current_user(); $stacked = $stacked === null ? Controller_Settings::shared()->should_stack_ui_columns() : $stacked; if ($this->can_user_activate_2fa_self($user)) { $assets = $this->management_assets_enqueued ? array() : $this->get_2fa_management_assets(true); $scriptData = $this->management_assets_enqueued ? array() : $this->get_2fa_management_script_data(); return Model_View::create( 'page/manage-embedded', array( 'user' => $user, 'stacked' => $stacked, 'assets' => $assets, 'scriptData' => $scriptData ) )->render(); } else { return Model_View::create('page/permission-denied')->render(); } } public function _woocommerce_account_menu_content() { echo $this->render_embedded_user_2fa_management_interface(); } private function does_current_page_include_shortcode($shortcode) { global $post; return $post instanceof \WP_Post && has_shortcode($post->post_content, $shortcode); } public function _woocommerce_account_enqueue_assets() { if (!$this->has_woocommerce()) return; if ($this->does_current_page_include_shortcode('woocommerce_my_account')) { wp_enqueue_style('wordfence-ls-woocommerce-account-styles', Model_Asset::css('woocommerce-account.css'), array(), WORDFENCE_LS_VERSION); $this->enqueue_2fa_management_assets(true); } } public function _handle_user_2fa_management_shortcode($attributes, $content = null, $shortcode = null) { $shortcode = $shortcode === null ? self::SHORTCODE_2FA_MANAGEMENT : $shortcode; $attributes = shortcode_atts( array( 'stacked' => Controller_Settings::shared()->should_stack_ui_columns() ? 'true' : 'false' ), $attributes, $shortcode ); $stacked = filter_var($attributes['stacked'], FILTER_VALIDATE_BOOLEAN); return $this->render_embedded_user_2fa_management_interface($stacked); } public function _handle_shortcode_prerequisites() { if ($this->does_current_page_include_shortcode(self::SHORTCODE_2FA_MANAGEMENT)) { if (!is_user_logged_in()) auth_redirect(); $this->enqueue_2fa_management_assets(true); } } }login-security/classes/controller/ajax.php000064400000063350147207020750015010 0ustar00_actions = array( 'authenticate' => array( 'handler' => array($this, '_ajax_authenticate_callback'), 'nopriv' => true, 'nonce' => false, 'permissions' => array(), //Format is 'permission' => 'error message' 'required_parameters' => array(), ), 'register_support' => array( 'handler' => array($this, '_ajax_register_support_callback'), 'nopriv' => true, 'nonce' => false, 'permissions' => array(), 'required_parameters' => array('wfls-message-nonce', 'wfls-message'), ), 'activate' => array( 'handler' => array($this, '_ajax_activate_callback'), 'permissions' => array(), 'required_parameters' => array('nonce', 'secret', 'recovery', 'code', 'user'), ), 'deactivate' => array( 'handler' => array($this, '_ajax_deactivate_callback'), 'permissions' => array(), 'required_parameters' => array('nonce', 'user'), ), 'regenerate' => array( 'handler' => array($this, '_ajax_regenerate_callback'), 'permissions' => array(), 'required_parameters' => array('nonce', 'user'), ), 'save_options' => array( 'handler' => array($this, '_ajax_save_options_callback'), 'permissions' => array(Controller_Permissions::CAP_MANAGE_SETTINGS => function() { return __('You do not have permission to change options.', 'wordfence'); }), //These are deliberately written as closures to be executed later so that WP doesn't load the translations too early, which can cause it not to pick up user-specific language settings 'required_parameters' => array('nonce', 'changes'), ), 'send_grace_period_notification' => array( 'handler' => array($this, '_ajax_send_grace_period_notification_callback'), 'permissions' => array(Controller_Permissions::CAP_MANAGE_SETTINGS => function() { return __('You do not have permission to send notifications.', 'wordfence'); }), 'required_parameters' => array('nonce', 'role', 'url'), ), 'update_ip_preview' => array( 'handler' => array($this, '_ajax_update_ip_preview_callback'), 'permissions' => array(Controller_Permissions::CAP_MANAGE_SETTINGS => function() { return __('You do not have permission to change options.', 'wordfence'); }), 'required_parameters' => array('nonce', 'ip_source', 'ip_source_trusted_proxies'), ), 'dismiss_notice' => array( 'handler' => array($this, '_ajax_dismiss_notice_callback'), 'permissions' => array(), 'required_parameters' => array('nonce', 'id'), ), 'reset_recaptcha_stats' => array( 'handler' => array($this, '_ajax_reset_recaptcha_stats_callback'), 'permissions' => array(Controller_Permissions::CAP_MANAGE_SETTINGS => function() { return __('You do not have permission to reset reCAPTCHA statistics.', 'wordfence'); }), 'required_parameters' => array('nonce'), ), 'reset_2fa_grace_period' => array ( 'handler' => array($this, '_ajax_reset_2fa_grace_period_callback'), 'permissions' => array(Controller_Permissions::CAP_MANAGE_SETTINGS => function() { return __('You do not have permission to reset the 2FA grace period.', 'wordfence'); }), 'required_parameters' => array('nonce', 'user_id') ), 'revoke_2fa_grace_period' => array ( 'handler' => array($this, '_ajax_revoke_2fa_grace_period_callback'), 'permissions' => array(Controller_Permissions::CAP_MANAGE_SETTINGS => function() { return __('You do not have permission to revoke the 2FA grace period.', 'wordfence'); }), 'required_parameters' => array('nonce', 'user_id') ), 'reset_ntp_failure_count' => array( 'handler' => array($this, '_ajax_reset_ntp_failure_count_callback'), 'permissions' => array(Controller_Permissions::CAP_MANAGE_SETTINGS => function() { return __('You do not have permission to reset the NTP failure count.', 'wordfence'); }), 'required_parameters' => array(), ), 'disable_ntp' => array( 'handler' => array($this, '_ajax_disable_ntp_callback'), 'permissions' => array(Controller_Permissions::CAP_MANAGE_SETTINGS => function() { return __('You do not have permission to disable NTP.', 'wordfence'); }), 'required_parameters' => array(), ), 'dismiss_persistent_notice' => array( 'handler' => array($this, '_ajax_dismiss_persistent_notice_callback'), 'permissions' => array(Controller_Permissions::CAP_MANAGE_SETTINGS => function() { return __('You do not have permission to dismiss this notice.', 'wordfence'); }), 'required_parameters' => array('nonce', 'notice_id') ) ); $this->_init_actions(); } public function _init_actions() { foreach ($this->_actions as $action => $parameters) { if (isset($parameters['nopriv']) && $parameters['nopriv']) { add_action('wp_ajax_nopriv_wordfence_ls_' . $action, array($this, '_ajax_handler')); } add_action('wp_ajax_wordfence_ls_' . $action, array($this, '_ajax_handler')); } } /** * This is a convenience function for sending a JSON response and ensuring that execution stops after sending * since wp_die() can be interrupted. * * @param $response * @param int|null $status_code */ public static function send_json($response, $status_code = null) { wp_send_json($response, $status_code); die(); } public function _ajax_handler() { $action = (isset($_POST['action']) && is_string($_POST['action']) && $_POST['action']) ? $_POST['action'] : $_GET['action']; if (preg_match('~wordfence_ls_([a-zA-Z_0-9]+)$~', $action, $matches)) { $action = $matches[1]; if (!isset($this->_actions[$action])) { self::send_json(array('error' => esc_html__('An unknown action was provided.', 'wordfence'))); } $parameters = $this->_actions[$action]; if (!empty($parameters['required_parameters'])) { foreach ($parameters['required_parameters'] as $k) { if (!isset($_POST[$k])) { self::send_json(array('error' => esc_html__('An expected parameter was not provided.', 'wordfence'))); } } } if (!isset($parameters['nonce']) || $parameters['nonce']) { $nonce = (isset($_POST['nonce']) && is_string($_POST['nonce']) && $_POST['nonce']) ? $_POST['nonce'] : $_GET['nonce']; if (!is_string($nonce) || !wp_verify_nonce($nonce, 'wp-ajax')) { self::send_json(array('error' => esc_html__('Your browser sent an invalid security token. Please try reloading this page.', 'wordfence'), 'tokenInvalid' => 1)); } } if (!empty($parameters['permissions'])) { $user = wp_get_current_user(); foreach ($parameters['permissions'] as $permission => $error) { if (!user_can($user, $permission)) { self::send_json(array('error' => $error())); } } } call_user_func($parameters['handler']); } } public function _ajax_authenticate_callback() { $credentialKeys = array( 'log' => 'pwd', 'username' => 'password' ); $username = null; $password = null; foreach ($credentialKeys as $usernameKey => $passwordKey) { if (array_key_exists($usernameKey, $_POST) && array_key_exists($passwordKey, $_POST) && is_string($_POST[$usernameKey]) && is_string($_POST[$passwordKey])) { $username = $_POST[$usernameKey]; $password = $_POST[$passwordKey]; break; } } if (empty($username) || empty($password)) { self::send_json(array('error' => wp_kses(sprintf(__('ERROR: A username and password must be provided. Lost your password?', 'wordfence'), wp_lostpassword_url()), array('strong'=>array(), 'a'=>array('href'=>array(), 'title'=>array()))))); } $legacy2FAActive = Controller_WordfenceLS::shared()->legacy_2fa_active(); if ($legacy2FAActive) { //Legacy 2FA is active, pass it on to the authenticate filter self::send_json(array('login' => 1)); } do_action_ref_array('wp_authenticate', array(&$username, &$password)); define('WORDFENCE_LS_AUTHENTICATION_CHECK', true); //Prevents our auth filter from recursing $user = wp_authenticate($username, $password); if (is_object($user) && ($user instanceof \WP_User)) { if (!Controller_Users::shared()->has_2fa_active($user) || Controller_Whitelist::shared()->is_whitelisted(Model_Request::current()->ip()) || Controller_Users::shared()->has_remembered_2fa($user) || defined('WORDFENCE_LS_COMBINED_IS_VALID')) { //Not enabled for this user, is whitelisted, has a valid remembered cookie, or has already provided a 2FA code via the password field pass the credentials on to the normal login flow self::send_json(array('login' => 1)); } self::send_json(array('login' => 1, 'two_factor_required' => true)); } else if (is_wp_error($user)) { $errors = array(); $messages = array(); $reset = false; foreach ($user->get_error_codes() as $code) { if ($code == 'invalid_username' || $code == 'invalid_email' || $code == 'incorrect_password' || $code == 'authentication_failed') { $errors[] = wp_kses(sprintf(__('ERROR: The username or password you entered is incorrect. Lost your password?', 'wordfence'), wp_lostpassword_url()), array('strong'=>array(), 'a'=>array('href'=>array(), 'title'=>array()))); } else { if ($code == 'wfls_twofactor_invalid') { $reset = true; } $severity = $user->get_error_data($code); foreach ($user->get_error_messages($code) as $error_message) { if ($severity == 'message') { $messages[] = $error_message; } else { $errors[] = $error_message; } } } } if (!empty($errors)) { $errors = implode('
', $errors); $errors = apply_filters('login_errors', $errors); self::send_json(array('error' => $errors, 'reset' => $reset)); } if (!empty($messages)) { $messages = implode('
', $messages); $messages = apply_filters('login_errors', $messages); self::send_json(array('message' => $messages, 'reset' => $reset)); } } self::send_json(array('error' => wp_kses(sprintf(__('ERROR: The username or password you entered is incorrect. Lost your password?', 'wordfence'), wp_lostpassword_url()), array('strong'=>array(), 'a'=>array('href'=>array(), 'title'=>array()))))); } public function _ajax_register_support_callback() { $email = null; if (array_key_exists('email', $_POST) && is_string($_POST['email'])) { $email = $_POST['email']; } else if (array_key_exists('user_email', $_POST) && is_string($_POST['user_email'])) { $email = $_POST['user_email']; } if ( $email === null || !isset($_POST['wfls-message']) || !is_string($_POST['wfls-message']) || !isset($_POST['wfls-message-nonce']) || !is_string($_POST['wfls-message-nonce'])) { self::send_json(array('error' => wp_kses(sprintf(__('ERROR: Unable to send message. Please refresh the page and try again.', 'wordfence')), array('strong'=>array())))); } $email = sanitize_email($email); $login = ''; if (array_key_exists('user_login', $_POST) && is_string($_POST['user_login'])) $login = sanitize_user($_POST['user_login']); $message = strip_tags($_POST['wfls-message']); $nonce = $_POST['wfls-message-nonce']; if ((isset($_POST['user_login']) && empty($login)) || empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL) || empty($message)) { self::send_json(array('error' => wp_kses(sprintf(__('ERROR: Unable to send message. Please refresh the page and try again.', 'wordfence')), array('strong'=>array())))); } $jwt = Model_JWT::decode_jwt($_POST['wfls-message-nonce']); if ($jwt && isset($jwt->payload['ip']) && isset($jwt->payload['score'])) { $decryptedIP = Model_Symmetric::decrypt($jwt->payload['ip']); $decryptedScore = Model_Symmetric::decrypt($jwt->payload['score']); if ($decryptedIP === false || $decryptedScore === false || Model_IP::inet_pton($decryptedIP) !== Model_IP::inet_pton(Model_Request::current()->ip())) { //JWT IP and the current request's IP don't match, refuse the message self::send_json(array('error' => wp_kses(sprintf(__('ERROR: Unable to send message. Please refresh the page and try again.', 'wordfence')), array('strong'=>array())))); } $identifier = bin2hex(Model_IP::inet_pton($decryptedIP)); $tokenBucket = new Model_TokenBucket('rate:' . $identifier, 2, 1 / (6 * Model_TokenBucket::HOUR)); //Maximum of two requests, refilling at a rate of one per six hours if (!$tokenBucket->consume(1)) { self::send_json(array('error' => wp_kses(sprintf(__('ERROR: Unable to send message. You have exceeded the maximum number of messages that may be sent at this time. Please try again later.', 'wordfence')), array('strong'=>array())))); } $email = array( 'to' => get_site_option('admin_email'), 'subject' => __('Blocked User Registration Contact Form', 'wordfence'), 'body' => sprintf(__("A visitor blocked from registration sent the following message.\n\n----------------------------------------\n\nIP: %s\nUsername: %s\nEmail: %s\nreCAPTCHA Score: %f\n\n----------------------------------------\n\n%s", 'wordfence'), $decryptedIP, $login, $email, $decryptedScore, $message), 'headers' => '', ); $success = wp_mail($email['to'], $email['subject'], $email['body'], $email['headers']); if ($success) { self::send_json(array('message' => wp_kses(sprintf(__('MESSAGE SENT: Your message was sent to the site owner.', 'wordfence')), array('strong'=>array())))); } self::send_json(array('error' => wp_kses(sprintf(__('ERROR: An error occurred while sending the message. Please try again.', 'wordfence')), array('strong'=>array())))); } self::send_json(array('error' => wp_kses(sprintf(__('ERROR: Unable to send message. Please refresh the page and try again.', 'wordfence')), array('strong'=>array())))); } public function _ajax_activate_callback() { $userID = (int) @$_POST['user']; $user = wp_get_current_user(); if ($user->ID != $userID) { if (!user_can($user, Controller_Permissions::CAP_ACTIVATE_2FA_OTHERS)) { self::send_json(array('error' => esc_html__('You do not have permission to activate the given user.', 'wordfence'))); } else { $user = new \WP_User($userID); if (!$user->exists()) { self::send_json(array('error' => esc_html__('The given user does not exist.', 'wordfence'))); } } } else if (!user_can($user, Controller_Permissions::CAP_ACTIVATE_2FA_SELF)) { self::send_json(array('error' => esc_html__('You do not have permission to activate 2FA.', 'wordfence'))); } if (Controller_Users::shared()->has_2fa_active($user)) { self::send_json(array('error' => esc_html__('The given user already has two-factor authentication active.', 'wordfence'))); } $matches = (isset($_POST['secret']) && isset($_POST['code']) && is_string($_POST['secret']) && is_string($_POST['code']) && Controller_TOTP::shared()->check_code($_POST['secret'], $_POST['code'])); if ($matches === false) { self::send_json(array('error' => esc_html__('The code provided does not match the expected value. Please verify that the time on your authenticator device is correct and that this server\'s time is correct.', 'wordfence'))); } Controller_TOTP::shared()->activate_2fa($user, $_POST['secret'], $_POST['recovery'], $matches); Controller_Notices::shared()->remove_notice(false, 'wfls-will-be-required', $user); self::send_json(array('activated' => 1, 'text' => sprintf(count($_POST['recovery']) == 1 ? esc_html__('%d unused recovery code remains. You may generate a new set by clicking below.', 'wordfence') : esc_html__('%d unused recovery codes remain. You may generate a new set by clicking below.', 'wordfence'), count($_POST['recovery'])))); } public function _ajax_deactivate_callback() { $userID = (int) @$_POST['user']; $user = wp_get_current_user(); if ($user->ID != $userID) { if (!user_can($user, Controller_Permissions::CAP_ACTIVATE_2FA_OTHERS)) { self::send_json(array('error' => esc_html__('You do not have permission to deactivate the given user.', 'wordfence'))); } else { $user = new \WP_User($userID); if (!$user->exists()) { self::send_json(array('error' => esc_html__('The user does not exist.', 'wordfence'))); } } } else if (!user_can($user, Controller_Permissions::CAP_ACTIVATE_2FA_SELF)) { self::send_json(array('error' => esc_html__('You do not have permission to deactivate 2FA.', 'wordfence'))); } if (!Controller_Users::shared()->has_2fa_active($user)) { self::send_json(array('error' => esc_html__('The user specified does not have two-factor authentication active.', 'wordfence'))); } Controller_Users::shared()->deactivate_2fa($user); self::send_json(array('deactivated' => 1)); } public function _ajax_regenerate_callback() { $userID = (int) @$_POST['user']; $user = wp_get_current_user(); if ($user->ID != $userID) { if (!user_can($user, Controller_Permissions::CAP_ACTIVATE_2FA_OTHERS)) { self::send_json(array('error' => esc_html__('You do not have permission to generate new recovery codes for the given user.', 'wordfence'))); } else { $user = new \WP_User($userID); if (!$user->exists()) { self::send_json(array('error' => esc_html__('The user does not exist.', 'wordfence'))); } } } else if (!user_can($user, Controller_Permissions::CAP_ACTIVATE_2FA_SELF)) { self::send_json(array('error' => esc_html__('You do not have permission to generate new recovery codes.', 'wordfence'))); } if (!Controller_Users::shared()->has_2fa_active($user)) { self::send_json(array('error' => esc_html__('The user specified does not have two-factor authentication active.', 'wordfence'))); } $codes = Controller_Users::shared()->regenerate_recovery_codes($user); self::send_json(array('regenerated' => 1, 'recovery' => array_map(function($r) { return implode(' ', str_split(bin2hex($r), 4)); }, $codes), 'text' => sprintf(count($codes) == 1 ? esc_html__('%d unused recovery code remains. You may generate a new set by clicking below.', 'wordfence') : esc_html__('%d unused recovery codes remain. You may generate a new set by clicking below.', 'wordfence'), count($codes)))); } public function _ajax_save_options_callback() { if (!empty($_POST['changes']) && is_string($_POST['changes']) && is_array($changes = json_decode(stripslashes($_POST['changes']), true))) { try { $errors = Controller_Settings::shared()->validate_multiple($changes); if ($errors !== true) { if (count($errors) == 1) { $e = array_shift($errors); self::send_json(array('error' => esc_html(sprintf(__('An error occurred while saving the configuration: %s', 'wordfence'), $e)))); } else if (count($errors) > 1) { $compoundMessage = array(); foreach ($errors as $e) { $compoundMessage[] = esc_html($e); } self::send_json(array( 'error' => wp_kses(sprintf(__('Errors occurred while saving the configuration: %s', 'wordfence'), '
  • ' . implode('
  • ', $compoundMessage) . '
'), array('ul'=>array(), 'li'=>array())), 'html' => true, )); } self::send_json(array( 'error' => esc_html__('Errors occurred while saving the configuration.', 'wordfence'), )); } Controller_Settings::shared()->set_multiple($changes); if (array_key_exists(Controller_Settings::OPTION_ENABLE_WOOCOMMERCE_ACCOUNT_INTEGRATION, $changes) || array_key_exists(Controller_Settings::OPTION_ENABLE_WOOCOMMERCE_INTEGRATION, $changes)) Controller_WordfenceLS::shared()->refresh_rewrite_rules(); $response = array('success' => true); return self::send_json($response); } catch (\Exception $e) { self::send_json(array( 'error' => $e->getMessage(), )); } } self::send_json(array( 'error' => esc_html__('No configuration changes were provided to save.', 'wordfence'), )); } public function _ajax_send_grace_period_notification_callback() { $notifyAll = isset($_POST['notify_all']); $users = Controller_Users::shared()->get_users_by_role($_POST['role'], $notifyAll ? null: self::MAX_USERS_TO_NOTIFY + 1); $url = $_POST['url']; if (!empty($url)) { $url = get_site_url(null, $url); if (filter_var($url, FILTER_VALIDATE_URL) === false) { self::send_json(array('error' => esc_html__('The specified URL is invalid.', 'wordfence'))); } } $userCount = count($users); if (!$notifyAll && $userCount > self::MAX_USERS_TO_NOTIFY) self::send_json(array('error' => esc_html(sprintf(__('More than %d users exist for the selected role. This notification is not designed to handle large groups of users. In such instances, using a different solution for notifying users of upcoming 2FA requirements is recommended.', 'wordfence'), self::MAX_USERS_TO_NOTIFY)), 'limit_exceeded' => true)); $sent = 0; foreach ($users as $user) { Controller_Users::shared()->requires_2fa($user, $inGracePeriod, $requiredAt); if ($inGracePeriod && !Controller_Users::shared()->has_2fa_active($user)) { $subject = sprintf(__('2FA will soon be required on %s', 'wordfence'), home_url()); $requiredDate = Controller_Time::format_local_time('F j, Y g:i A', $requiredAt); if (empty($url)) { $userUrl = (is_multisite() && is_super_admin($user->ID)) ? network_admin_url('admin.php?page=WFLS') : admin_url('admin.php?page=WFLS'); } else { $userUrl = $url; } $message = sprintf( __("

You do not currently have two-factor authentication active on your account, which will be required beginning %s.

Configure 2FA

", 'wordfence'), $requiredDate, htmlentities($userUrl) ); wp_mail($user->user_email, $subject, $message, array('Content-Type: text/html')); $sent++; } } if ($userCount == 0) { self::send_json(array('error' => esc_html__('No users currently exist with the selected role.', 'wordfence'))); } else if ($sent == 0) { self::send_json(array('confirmation' => esc_html__('All users with the selected role already have two-factor authentication activated or have been locked out.', 'wordfence'))); } else if ($sent == 1) { self::send_json(array('confirmation' => esc_html(sprintf(__('A reminder to activate two-factor authentication was sent to %d user.', 'wordfence'), $sent)))); } self::send_json(array('confirmation' => esc_html(sprintf(__('A reminder to activate two-factor authentication was sent to %d users.', 'wordfence'), $sent)))); } public function _ajax_update_ip_preview_callback() { $source = $_POST['ip_source']; $raw_proxies = $_POST['ip_source_trusted_proxies']; if (!is_string($source) || !is_string($raw_proxies)) { die(); } $valid = array(); $invalid = array(); $test = preg_split('/[\r\n,]+/', $raw_proxies); foreach ($test as $value) { if (strlen($value) > 0) { if (Model_IP::is_valid_ip($value) || Model_IP::is_valid_cidr_range($value)) { $valid[] = $value; } else { $invalid[] = $value; } } } $trusted_proxies = $valid; $preview = Model_Request::current()->detected_ip_preview($source, $trusted_proxies); $ip = Model_Request::current()->ip_for_field($source, $trusted_proxies); self::send_json(array('ip' => $ip[0], 'preview' => $preview)); } public function _ajax_dismiss_notice_callback() { Controller_Notices::shared()->remove_notice($_POST['id'], false, wp_get_current_user()); } public function _ajax_reset_recaptcha_stats_callback() { Controller_Settings::shared()->set_array(Controller_Settings::OPTION_CAPTCHA_STATS, array('counts' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'avg' => 0)); $response = array('success' => true); self::send_json($response); } public function _ajax_reset_2fa_grace_period_callback() { $userId = (int) $_POST['user_id']; $gracePeriodOverride = array_key_exists('grace_period_override', $_POST) ? (int) $_POST['grace_period_override'] : null; $user = get_userdata($userId); if ($user === false) self::send_json(array('error' => esc_html__('Invalid user specified', 'wordfence'))); if ($gracePeriodOverride < 0 || $gracePeriodOverride > Controller_Settings::MAX_REQUIRE_2FA_USER_GRACE_PERIOD) self::send_json(array('error' => esc_html__('Invalid grace period override', 'wordfence'))); $gracePeriodAllowed = Controller_Users::shared()->get_grace_period_allowed_flag($userId); if (!$gracePeriodAllowed) Controller_Users::shared()->allow_grace_period($userId); if (!Controller_Users::shared()->reset_2fa_grace_period($user, $gracePeriodOverride)) self::send_json(array('error' => esc_html__('Failed to reset grace period', 'wordfence'))); self::send_json(array('success' => true)); } public function _ajax_revoke_2fa_grace_period_callback() { $user = get_userdata((int) $_POST['user_id']); if ($user === false) self::send_json(array('error' => esc_html__('Invalid user specified', 'wordfence'))); Controller_Users::shared()->revoke_grace_period($user); self::send_json(array('success' => true)); } public function _ajax_reset_ntp_failure_count_callback() { Controller_Settings::shared()->reset_ntp_failure_count(); } public function _ajax_disable_ntp_callback() { Controller_Settings::shared()->disable_ntp_cron(); } public function _ajax_dismiss_persistent_notice_callback() { $userId = get_current_user_id(); $noticeId = $_POST['notice_id']; if ($userId !== 0 && Controller_Notices::shared()->dismiss_persistent_notice($userId, $noticeId)) self::send_json(array('success' => true)); self::send_json(array( 'error' => esc_html__('Unable to dismiss notice', 'wordfence') )); } }login-security/classes/controller/notices.php000064400000012462147207020750015527 0ustar00_notices($user); foreach ($notices as $id => $n) { if ($category !== false && isset($n['category']) && $n['category'] == $category) { //Same category overwrites previous entry unset($notices[$id]); } } $id = Model_Crypto::uuid(); $notices[$id] = array( 'severity' => $severity, 'messageHTML' => Model_HTML::esc_html($message), ); if ($category !== false) { $notices[$id]['category'] = $category; } $this->_save_notices($notices, $user); } /** * Removes a notice using one of two possible search methods: * * 1. If $id matches. $category is ignored but only notices for $user are checked. * 2. If $category matches. Only notices for $user are checked. * * @param bool|int $id * @param bool|string $category * @param bool|\WP_User $user */ public function remove_notice($id = false, $category = false, $user = false) { if ($id === false && $category === false) { return; } else if ($id !== false) { $category = false; } $notices = $this->_notices($user); foreach ($notices as $nid => $n) { if ($id == $nid) { //ID match unset($notices[$nid]); break; } else if ($id !== false) { continue; } if ($category !== false && isset($n['category']) && $category == $n['category']) { //Category match unset($notices[$nid]); } } $this->_save_notices($notices, $user); } /** * Returns whether or not a notice exists for the given user. * * @param bool|\WP_User $user * @return bool */ public function has_notice($user) { $notices = $this->_notices($user); return !!count($notices) || $this->has_persistent_notices(); } /** * Enqueues a user's notices. For administrators this also includes global notices. * * @return bool Whether any notices were enqueued. */ public function enqueue_notices() { $user = wp_get_current_user(); if ($user->ID == 0) { return false; } $added = false; $notices = array(); if (Controller_Permissions::shared()->can_manage_settings($user)) { $globalNotices = $this->_notices(false); $notices = array_merge($notices, $globalNotices); } $userNotices = $this->_notices($user); $notices = array_merge($notices, $userNotices); foreach ($notices as $nid => $n) { $notice = new Model_Notice($nid, $n['severity'], $n['messageHTML'], $n['category']); if (is_multisite()) { add_action('network_admin_notices', array($notice, 'display_notice')); } else { add_action('admin_notices', array($notice, 'display_notice')); } $added = true; } return $added; } /** * Utility */ /** * Returns the notices for a user if provided, otherwise the global notices. * * @param bool|\WP_User $user * @return array */ protected function _notices($user) { if ($user instanceof \WP_User) { $notices = get_user_meta($user->ID, self::USER_META_KEY, true); return array_filter((array) $notices); } return Controller_Settings::shared()->get_array(Controller_Settings::OPTION_GLOBAL_NOTICES); } /** * Saves the notices. * * @param array $notices * @param bool|\WP_User $user */ protected function _save_notices($notices, $user) { if ($user instanceof \WP_User) { update_user_meta($user->ID, self::USER_META_KEY, $notices); return; } Controller_Settings::shared()->set_array(Controller_Settings::OPTION_GLOBAL_NOTICES, $notices, true); } public function get_persistent_notice_ids() { return array( self::PERSISTENT_NOTICE_WOOCOMMERCE_INTEGRATION ); } private static function get_persistent_notice_dismiss_key($noticeId) { return self::PERSISTENT_NOTICE_DISMISS_PREFIX . $noticeId; } public function register_persistent_notice($noticeId) { $this->persistentNotices[] = $noticeId; } public function has_persistent_notices() { return count($this->persistentNotices) > 0; } public function dismiss_persistent_notice($userId, $noticeId) { if (!in_array($noticeId, $this->get_persistent_notice_ids(), true)) return false; update_user_option($userId, self::get_persistent_notice_dismiss_key($noticeId), true, true); return true; } public function is_persistent_notice_dismissed($userId, $noticeId) { return (bool) get_user_option(self::get_persistent_notice_dismiss_key($noticeId), $userId); } }login-security/classes/controller/settings.php000064400000061517147207020750015730 0ustar00_settingsStorage = $settingsStorage; $this->_migrate_admin_2fa_requirements_to_roles(); } public function set_defaults() { $this->_settingsStorage->set_multiple(array( self::OPTION_XMLRPC_ENABLED => array('value' => true, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_2FA_WHITELISTED => array('value' => '', 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_IP_SOURCE => array('value' => Model_Request::IP_SOURCE_AUTOMATIC, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_IP_TRUSTED_PROXIES => array('value' => '', 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_REQUIRE_2FA_ADMIN => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_REQUIRE_2FA_GRACE_PERIOD_ENABLED => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_REQUIRE_2FA_USER_GRACE_PERIOD => array('value' => self::DEFAULT_REQUIRE_2FA_USER_GRACE_PERIOD, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_GLOBAL_NOTICES => array('value' => '[]', 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_REMEMBER_DEVICE_ENABLED => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_REMEMBER_DEVICE_DURATION => array('value' => (30 * 86400), 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_ALLOW_XML_RPC => array('value' => true, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_ENABLE_AUTH_CAPTCHA => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_CAPTCHA_STATS => array('value' => '{"counts":[0,0,0,0,0,0,0,0,0,0,0],"avg":0}', 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_RECAPTCHA_THRESHOLD => array('value' => 0.5, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_LAST_SECRET_REFRESH => array('value' => 0, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_DELETE_ON_DEACTIVATION => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_ENABLE_WOOCOMMERCE_INTEGRATION => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_ENABLE_WOOCOMMERCE_ACCOUNT_INTEGRATION => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_ENABLE_SHORTCODE => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_ENABLE_LOGIN_HISTORY_COLUMNS => array('value' => true, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_STACK_UI_COLUMNS => array('value' => true, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_SCHEMA_VERSION => array('value' => 0, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_USER_COUNT_QUERY_STATE => array('value' => 0, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false), self::OPTION_DISABLE_TEMPORARY_TABLES => array('value' => 0, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false) )); } public function set($key, $value, $already_validated = false) { return $this->set_multiple(array($key => $value), $already_validated); } public function set_array($key, $value, $already_validated = false) { return $this->set_multiple(array($key => json_encode($value)), $already_validated); } public function set_multiple($changes, $already_validated = false) { if (!$already_validated && $this->validate_multiple($changes) !== true) { return false; } $changes = $this->clean_multiple($changes); $changes = $this->preprocess_multiple($changes); $this->_settingsStorage->set_multiple($changes); return true; } public function get($key, $default = false) { return $this->_settingsStorage->get($key, $default); } public function get_bool($key, $default = false) { return $this->_truthy_to_bool($this->get($key, $default)); } public function get_int($key, $default = 0) { return intval($this->get($key, $default)); } public function get_float($key, $default = 0.0) { return (float) $this->get($key, $default); } public function get_array($key, $default = array()) { $value = $this->get($key, null); if (is_string($value)) { $value = @json_decode($value, true); } else { $value = null; } return is_array($value) ? $value : $default; } public function remove($key) { $this->_settingsStorage->remove($key); } /** * Validates whether a user-entered setting value is acceptable. Returns true if valid or an error message if not. * * @param string $key * @param mixed $value * @return bool|string */ public function validate($key, $value) { switch ($key) { //Boolean case self::OPTION_XMLRPC_ENABLED: case self::OPTION_REQUIRE_2FA_ADMIN: case self::OPTION_REQUIRE_2FA_GRACE_PERIOD_ENABLED: case self::OPTION_REMEMBER_DEVICE_ENABLED: case self::OPTION_ALLOW_XML_RPC: case self::OPTION_ENABLE_AUTH_CAPTCHA: case self::OPTION_CAPTCHA_TEST_MODE: case self::OPTION_DISMISSED_FRESH_INSTALL_MODAL: case self::OPTION_DELETE_ON_DEACTIVATION: case self::OPTION_ENABLE_WOOCOMMERCE_INTEGRATION: case self::OPTION_ENABLE_WOOCOMMERCE_ACCOUNT_INTEGRATION: case self::OPTION_ENABLE_SHORTCODE: case self::OPTION_ENABLE_LOGIN_HISTORY_COLUMNS: case self::OPTION_STACK_UI_COLUMNS: case self::OPTION_USER_COUNT_QUERY_STATE: case self::OPTION_DISABLE_TEMPORARY_TABLES: return true; //Int case self::OPTION_LAST_SECRET_REFRESH: return is_numeric($value); //Left using is_numeric to prevent issues with existing values case self::OPTION_SCHEMA_VERSION: return Utility_Number::isInteger($value, 0); //Array case self::OPTION_GLOBAL_NOTICES: case self::OPTION_CAPTCHA_STATS: return preg_match('/^\[.*\]$/', $value) || preg_match('/^\{.*\}$/', $value); //Only a rough JSON validation //Special case self::OPTION_IP_TRUSTED_PROXIES: case self::OPTION_2FA_WHITELISTED: $parsed = array_filter(array_map(function($s) { return trim($s); }, preg_split('/[\r\n]/', $value))); foreach ($parsed as $entry) { if (!Controller_Whitelist::shared()->is_valid_range($entry)) { return sprintf(__('The IP/range %s is invalid.', 'wordfence'), esc_html($entry)); } } return true; case self::OPTION_IP_SOURCE: if (!in_array($value, array(Model_Request::IP_SOURCE_AUTOMATIC, Model_Request::IP_SOURCE_REMOTE_ADDR, Model_Request::IP_SOURCE_X_FORWARDED_FOR, Model_Request::IP_SOURCE_X_REAL_IP))) { return __('An invalid IP source was provided.', 'wordfence'); } return true; case self::OPTION_REQUIRE_2FA_GRACE_PERIOD: $gracePeriodEnd = strtotime($value); if ($gracePeriodEnd <= \WordfenceLS\Controller_Time::time()) { return __('The grace period end time must be in the future.', 'wordfence'); } return true; case self::OPTION_REMEMBER_DEVICE_DURATION: return is_numeric($value) && $value > 0; case self::OPTION_RECAPTCHA_THRESHOLD: return is_numeric($value) && $value > 0 && $value <= 1; case self::OPTION_RECAPTCHA_SITE_KEY: if (empty($value)) { return true; } $response = wp_remote_get('https://www.google.com/recaptcha/api.js?render=' . urlencode($value)); if (!is_wp_error($response)) { $status = wp_remote_retrieve_response_code($response); if ($status == 200) { return true; } $data = wp_remote_retrieve_body($response); if (strpos($data, 'grecaptcha') === false) { return __('Unable to validate the reCAPTCHA site key. Please check the key and try again.', 'wordfence'); } return true; } return sprintf(__('An error was encountered while validating the reCAPTCHA site key: %s', 'wordfence'), $response->get_error_message()); case self::OPTION_REQUIRE_2FA_USER_GRACE_PERIOD: return is_numeric($value) && $value >= 0 && $value <= self::MAX_REQUIRE_2FA_USER_GRACE_PERIOD; } return true; } public function validate_multiple($values) { $errors = array(); foreach ($values as $key => $value) { $status = $this->validate($key, $value); if ($status !== true) { $errors[$key] = $status; } } if (!empty($errors)) { return $errors; } return true; } /** * Cleans and normalizes a setting value for use in saving. * * @param string $key * @param mixed $value * @return mixed */ public function clean($key, $value) { switch ($key) { //Boolean case self::OPTION_XMLRPC_ENABLED: case self::OPTION_REQUIRE_2FA_ADMIN: case self::OPTION_REQUIRE_2FA_GRACE_PERIOD_ENABLED: case self::OPTION_REMEMBER_DEVICE_ENABLED: case self::OPTION_ALLOW_XML_RPC: case self::OPTION_ENABLE_AUTH_CAPTCHA: case self::OPTION_CAPTCHA_TEST_MODE: case self::OPTION_DISMISSED_FRESH_INSTALL_MODAL: case self::OPTION_DELETE_ON_DEACTIVATION: case self::OPTION_ENABLE_WOOCOMMERCE_INTEGRATION: case self::OPTION_ENABLE_WOOCOMMERCE_ACCOUNT_INTEGRATION: case self::OPTION_ENABLE_SHORTCODE; case self::OPTION_ENABLE_LOGIN_HISTORY_COLUMNS: case self::OPTION_STACK_UI_COLUMNS: case self::OPTION_USER_COUNT_QUERY_STATE: case self::OPTION_DISABLE_TEMPORARY_TABLES: return $this->_truthy_to_bool($value); //Int case self::OPTION_REMEMBER_DEVICE_DURATION: case self::OPTION_LAST_SECRET_REFRESH: case self::OPTION_REQUIRE_2FA_USER_GRACE_PERIOD: case self::OPTION_SCHEMA_VERSION: return (int) $value; //Float case self::OPTION_RECAPTCHA_THRESHOLD: return (float) $value; //Special case self::OPTION_IP_TRUSTED_PROXIES: case self::OPTION_2FA_WHITELISTED: $parsed = array_filter(array_map(function($s) { return trim($s); }, preg_split('/[\r\n]/', $value))); $cleaned = array(); foreach ($parsed as $item) { $cleaned[] = $this->_sanitize_ip_range($item); } return implode("\n", $cleaned); case self::OPTION_REQUIRE_2FA_GRACE_PERIOD: $dt = $this->_parse_local_time($value); return $dt->format('U'); case self::OPTION_RECAPTCHA_SITE_KEY: case self::OPTION_RECAPTCHA_SECRET: return trim($value); } return $value; } public function clean_multiple($changes) { $cleaned = array(); foreach ($changes as $key => $value) { $cleaned[$key] = $this->clean($key, $value); } return $cleaned; } private function get_required_2fa_role_key($role) { return implode('.', array(self::OPTION_PREFIX_REQUIRED_2FA_ROLE, $role)); } public function get_required_2fa_role_activation_time($role) { $time = $this->get_int($this->get_required_2fa_role_key($role), -1); if ($time < 0) return false; return $time; } public function get_user_2fa_grace_period() { return $this->get_int(self::OPTION_REQUIRE_2FA_USER_GRACE_PERIOD, self::DEFAULT_REQUIRE_2FA_USER_GRACE_PERIOD); } /** * Preprocesses the value, returning true if it was saved here (e.g., saved 2fa enabled by assigning a role * capability) or false if it is to be saved by the backing storage. * * @param string $key * @param mixed $value * @param array &$settings the array of settings to process, this function may append additional values from preprocessing * @return bool */ public function preprocess($key, $value, &$settings) { if (preg_match('/^enabled-roles\.(.+)$/', $key, $matches)) { //Enabled roles are stored as capabilities rather than in the settings storage $role = $matches[1]; if ($role === 'super-admin') { $roleValid = true; } else if (in_array($value, array(self::STATE_2FA_OPTIONAL, self::STATE_2FA_REQUIRED))) { $roleValid = Controller_Permissions::shared()->allow_2fa_self($role); } else { $roleValid = Controller_Permissions::shared()->disallow_2fa_self($role); } if (!in_array($value, array(self::STATE_2FA_OPTIONAL, self::STATE_2FA_REQUIRED))) { $value = self::STATE_2FA_DISABLED; } if ($roleValid) { $settings[$this->get_required_2fa_role_key($role)] = ($value === self::STATE_2FA_REQUIRED ? time() : -1); } /** * Fires when 2FA availability/required on a role changes. * * @since 1.1.13 * * @param string $role The name of the role. * @param string $state The state of 2FA on the role. */ do_action('wordfence_ls_changed_2fa_required', $role, $value); return true; } //Settings that will dispatch actions switch ($key) { case self::OPTION_XMLRPC_ENABLED: $before = $this->get($key); $after = $value; /** * Fires when the XML-RPC 2FA requirement changes. * * @since 1.1.13 * * @param bool $before The previous value. * @param bool $after The new value. */ do_action('wordfence_ls_xml_rpc_2fa_toggled', $before, $after); break; case self::OPTION_2FA_WHITELISTED: $before = $this->whitelisted_ips(); $after = explode("\n", $value); //Already cleaned here so just re-split /** * Fires when the whitelist changes. * * @since 1.1.13 * * @param string[] $before The previous value. * @param string[] $after The new value. */ do_action('wordfence_ls_updated_allowed_ips', $before, $after); break; case self::OPTION_IP_SOURCE: $before = $this->get($key); $after = $value; /** * Fires when the IP source changes. * * @since 1.1.13 * * @param string $before The previous value. * @param string $after The new value. */ do_action('wordfence_ls_changed_ip_source', $before, $after); break; case self::OPTION_IP_TRUSTED_PROXIES: $before = $this->trusted_proxies(); $after = explode("\n", $value); //Already cleaned here so just re-split /** * Fires when the trusted proxy list changes. * * @since 1.1.13 * * @param string[] $before The previous value. * @param string[] $after The new value. */ do_action('wordfence_ls_updated_trusted_proxies', $before, $after); break; case self::OPTION_REQUIRE_2FA_USER_GRACE_PERIOD: $before = $this->get($key); $after = $value; /** * Fires when the grace period changes. * * @since 1.1.13 * * @param int $before The previous value. * @param int $after The new value. */ do_action('wordfence_ls_changed_grace_period', $before, $after); break; case self::OPTION_ALLOW_XML_RPC: $before = $this->get($key); $after = $value; /** * Fires when the XML-RPC is enabled/disabled. * * @since 1.1.13 * * @param bool $before The previous value. * @param bool $after The new value. */ do_action('wordfence_ls_xml_rpc_enabled_toggled', $before, $after); break; case self::OPTION_ENABLE_AUTH_CAPTCHA: $before = $this->get($key); $after = $value; /** * Fires when the login captcha is enabled/disabled. * * @since 1.1.13 * * @param bool $before The previous value. * @param bool $after The new value. */ do_action('wordfence_ls_captcha_enabled_toggled', $before, $after); break; case self::OPTION_RECAPTCHA_THRESHOLD: $before = $this->get($key); $after = $value; /** * Fires when the reCAPTCHA threshold changes. * * @since 1.1.13 * * @param float $before The previous value. * @param float $after The new value. */ do_action('wordfence_ls_captcha_threshold_changed', $before, $after); break; case self::OPTION_ENABLE_WOOCOMMERCE_INTEGRATION: $before = $this->get($key); $after = $value; /** * Fires when WooCommerce integration is enabled/disabled. * * @since 1.1.13 * * @param bool $before The previous value. * @param bool $after The new value. */ do_action('wordfence_ls_woocommerce_enabled_toggled', $before, $after); break; case self::OPTION_CAPTCHA_TEST_MODE: $before = $this->get($key); $after = $value; /** * Fires when captcha test mode is enabled/disabled. * * @since 1.1.13 * * @param bool $before The previous value. * @param bool $after The new value. */ do_action('wordfence_ls_captcha_test_mode_toggled', $before, $after); break; } return false; } public function preprocess_multiple($changes) { $remaining = array(); foreach ($changes as $key => $value) { if (!$this->preprocess($key, $value, $remaining)) { $remaining[$key] = $value; } } return $remaining; } /** * Convenience */ /** * Returns a cleaned array containing the whitelist entries. * * @return array */ public function whitelisted_ips() { return array_filter(array_map(function($s) { return trim($s); }, preg_split('/[\r\n]/', $this->get(self::OPTION_2FA_WHITELISTED, '')))); } /** * Returns a cleaned array containing the trusted proxy entries. * * @return array */ public function trusted_proxies() { return array_filter(array_map(function($s) { return trim($s); }, preg_split('/[\r\n]/', $this->get(self::OPTION_IP_TRUSTED_PROXIES, '')))); } public function get_ntp_failure_count() { return $this->get_int(self::OPTION_NTP_FAILURE_COUNT, 0); } public function reset_ntp_failure_count() { $this->set(self::OPTION_NTP_FAILURE_COUNT, 0); } public function increment_ntp_failure_count() { $count = $this->get_ntp_failure_count(); if ($count < 0) return false; $count++; $this->set(self::OPTION_NTP_FAILURE_COUNT, $count); return $count; } public function is_ntp_disabled_via_constant() { return defined('WORDFENCE_LS_DISABLE_NTP') && WORDFENCE_LS_DISABLE_NTP; } public function is_ntp_enabled($requireOffset = true) { if ($this->is_ntp_cron_disabled()) return false; if ($this->get_bool(self::OPTION_USE_NTP, true)) { if ($requireOffset) { $offset = $this->get(self::OPTION_NTP_OFFSET, null); return $offset !== null && abs((int)$offset) <= Controller_TOTP::TIME_WINDOW_LENGTH; } else { return true; } } return false; } public function is_ntp_cron_disabled(&$failureCount = null) { if ($this->is_ntp_disabled_via_constant()) return true; $failureCount = $this->get_ntp_failure_count(); if ($failureCount >= Controller_Time::FAILURE_LIMIT) { return true; } else if ($failureCount < 0) { $failureCount = 0; return true; } return false; } public function disable_ntp_cron() { $this->set(self::OPTION_NTP_FAILURE_COUNT, -1); } public function are_login_history_columns_enabled() { return Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_ENABLE_LOGIN_HISTORY_COLUMNS, true); } public function should_stack_ui_columns() { return self::shared()->get_bool(Controller_Settings::OPTION_STACK_UI_COLUMNS, true); } /** * Utility */ /** * Translates a value to a boolean, correctly interpreting various textual representations. * * @param $value * @return bool */ protected function _truthy_to_bool($value) { if ($value === true || $value === false) { return $value; } if (is_numeric($value)) { return !!$value; } if (preg_match('/^(?:f(?:alse)?|no?|off)$/i', $value)) { return false; } else if (preg_match('/^(?:t(?:rue)?|y(?:es)?|on)$/i', $value)) { return true; } return !empty($value); } /** * Parses the given time string and returns its DateTime with the server's configured time zone. * * @param string $timestring * @return \DateTime */ protected function _parse_local_time($timestring) { $utc = new \DateTimeZone('UTC'); $tz = get_option('timezone_string'); if (!empty($tz)) { $tz = new \DateTimeZone($tz); return new \DateTime($timestring, $tz); } else { $gmt = get_option('gmt_offset'); if (!empty($gmt)) { if (PHP_VERSION_ID < 50510) { $timestamp = strtotime($timestring); $dtStr = gmdate("c", (int) ($timestamp + $gmt * 3600)); //Have to do it this way because of < PHP 5.5.10 return new \DateTime($dtStr, $utc); } else { $direction = ($gmt > 0 ? '+' : '-'); $gmt = abs($gmt); $h = (int) $gmt; $m = ($gmt - $h) * 60; $tz = new \DateTimeZone($direction . str_pad($h, 2, '0', STR_PAD_LEFT) . str_pad($m, 2, '0', STR_PAD_LEFT)); return new \DateTime($timestring, $tz); } } } return new \DateTime($timestring); } /** * Cleans a user-entered IP range of unnecessary characters and normalizes some glyphs. * * @param string $range * @return string */ protected function _sanitize_ip_range($range) { $range = preg_replace('/\s/', '', $range); //Strip whitespace $range = preg_replace('/[\\x{2013}-\\x{2015}]/u', '-', $range); //Non-hyphen dashes to hyphen $range = strtolower($range); if (preg_match('/^\d+-\d+$/', $range)) { //v5 32 bit int style format list($start, $end) = explode('-', $range); $start = long2ip($start); $end = long2ip($end); $range = "{$start}-{$end}"; } return $range; } private function _migrate_admin_2fa_requirements_to_roles() { if (!$this->get_bool(self::OPTION_REQUIRE_2FA_ADMIN)) return; $time = time(); if (is_multisite()) { $this->set($this->get_required_2fa_role_key('super-admin'), $time, true); } else { $roles = new \WP_Roles(); foreach ($roles->roles as $key => $data) { $role = $roles->get_role($key); if (Controller_Permissions::shared()->can_role_manage_settings($role) && Controller_Permissions::shared()->allow_2fa_self($role->name)) { $this->set($this->get_required_2fa_role_key($role->name), $time, true); } } } $this->remove(self::OPTION_REQUIRE_2FA_ADMIN); $this->remove(self::OPTION_REQUIRE_2FA_GRACE_PERIOD); $this->remove(self::OPTION_REQUIRE_2FA_GRACE_PERIOD_ENABLED); } public function reset_ntp_disabled_flag() { $this->remove(self::OPTION_USE_NTP); $this->remove(self::OPTION_NTP_OFFSET); $this->remove(self::OPTION_NTP_FAILURE_COUNT); } }login-security/classes/controller/captcha.php000064400000010122147207020750015455 0ustar00site_key(); $secret = $this->_secret(); return Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_ENABLE_AUTH_CAPTCHA) && !empty($key) && !empty($secret); } /** * Returns the public reCAPTCHA key if set. * * @return string|bool */ public function site_key() { return Controller_Settings::shared()->get(Controller_Settings::OPTION_RECAPTCHA_SITE_KEY); } /** * Returns the private reCAPTCHA secret if set. * * @return string|bool */ protected function _secret() { return Controller_Settings::shared()->get(Controller_Settings::OPTION_RECAPTCHA_SECRET); } /** * Returns the bot/human threshold for comparing the score against, defaulting to 0.5. * * @return float */ public function threshold() { return max(0.1, Controller_Settings::shared()->get_float(Controller_Settings::OPTION_RECAPTCHA_THRESHOLD, 0.5)); } /** * Determine whether or not test mode for reCAPTCHA is enabled * * @return bool */ public function test_mode() { return Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_CAPTCHA_TEST_MODE); } /** * Queries the reCAPTCHA endpoint with the given token, verifies the action matches, and returns the corresponding * score. If validation fails, false is returned. Any other failure (e.g., mangled response or connection dropped) returns 0.0. * * @param string $token * @param string $action * @param int $timeout * @return float|false */ public function score($token, $action = 'login', $timeout = 10) { try { $payload = array( 'secret' => $this->_secret(), 'response' => $token, 'remoteip' => Model_Request::current()->ip(), ); $response = wp_remote_post(self::RECAPTCHA_ENDPOINT, array( 'body' => $payload, 'headers' => array( 'Referer' => false, ), 'timeout' => $timeout, 'blocking' => true, )); if (!is_wp_error($response)) { $jsonResponse = wp_remote_retrieve_body($response); $decoded = @json_decode($jsonResponse, true); if (is_array($decoded) && isset($decoded['success'])) { if ($decoded['success']) { if (isset($decoded['score']) && isset($decoded['action']) && $decoded['action'] == $action) { return (float) $decoded['score']; } } return false; } } } catch (\Exception $e) { //Fall through } return 0.0; } /** * Returns true if the score is >= the threshold to be considered a human request. * * @param float $score * @return bool */ public function is_human($score) { if ($this->test_mode()) { return true; } $threshold = $this->threshold(); return ($score >= $threshold || abs($score - $threshold) < 0.0001); } /** * Check if the current request is an XML RPC request * @return bool */ private static function is_xml_rpc() { return defined('XMLRPC_REQUEST') && XMLRPC_REQUEST; } /** * Check if captcha is required for the current request * @return bool */ public function is_captcha_required() { $required = $this->enabled() && !self::is_xml_rpc(); return apply_filters('wordfence_ls_require_captcha', $required); } /** * Get the captcha token provided with the current request * @param string $key if specified, override the default token parameter * @return string|null the captcha token, if present, null otherwise */ public function get_token($key = 'wfls-captcha-token') { return (isset($_POST[$key]) && is_string($_POST[$key]) && !empty($_POST[$key]) ? $_POST[$key] : null); } }login-security/classes/controller/db.php000064400000011547147207020750014453 0ustar00base_prefix; } /** * Returns the table with the site (single site installations) or network (multisite) prefix added. * * @param string $table * @return string */ public static function network_table($table) { return self::network_prefix() . $table; } public function __get($key) { switch ($key) { case 'secrets': return self::network_table(self::TABLE_2FA_SECRETS); case 'settings': return self::network_table(self::TABLE_SETTINGS); case 'role_counts': return self::network_table(self::TABLE_ROLE_COUNTS); case 'role_counts_temporary': return self::network_table(self::TABLE_ROLE_COUNTS_TEMPORARY); } throw new \OutOfBoundsException('Unknown key: ' . $key); } public function install() { $this->_create_schema(); global $wpdb; $table = $this->secrets; $wpdb->query($wpdb->prepare("UPDATE `{$table}` SET `vtime` = LEAST(`vtime`, %d)", Controller_Time::time())); } public function uninstall() { $tables = array(self::TABLE_2FA_SECRETS, self::TABLE_SETTINGS, self::TABLE_ROLE_COUNTS); foreach ($tables as $table) { global $wpdb; $wpdb->query('DROP TABLE IF EXISTS `' . self::network_table($table) . '`'); } } private function create_table($name, $definition, $temporary = false) { global $wpdb; if (is_array($definition)) { foreach ($definition as $attempt) { if ($this->create_table($name, $attempt, $temporary)) return true; } return false; } else { return $wpdb->query('CREATE ' . ($temporary ? 'TEMPORARY ' : '') . 'TABLE IF NOT EXISTS `' . self::network_table($name) . '` ' . $definition); } } private function create_temporary_table($name, $definition) { if (Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_DISABLE_TEMPORARY_TABLES)) return false; if ($this->create_table($name, $definition, true)) return true; Controller_Settings::shared()->set(Controller_Settings::OPTION_DISABLE_TEMPORARY_TABLES, true); return false; } private function get_role_counts_table_definition($engine = null) { $engineClause = $engine === null ? '' : "ENGINE={$engine}"; return <<get_role_counts_table_definition('MEMORY'), $this->get_role_counts_table_definition('MyISAM'), $this->get_role_counts_table_definition() ); } protected function _create_schema() { $tables = array( self::TABLE_2FA_SECRETS => '( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `user_id` bigint(20) unsigned NOT NULL, `secret` tinyblob NOT NULL, `recovery` blob NOT NULL, `ctime` int(10) unsigned NOT NULL, `vtime` int(10) unsigned NOT NULL, `mode` enum(\'authenticator\') NOT NULL DEFAULT \'authenticator\', PRIMARY KEY (`id`), KEY `user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;', self::TABLE_SETTINGS => '( `name` varchar(191) NOT NULL DEFAULT \'\', `value` longblob, `autoload` enum(\'no\',\'yes\') NOT NULL DEFAULT \'yes\', PRIMARY KEY (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;', self::TABLE_ROLE_COUNTS => $this->get_role_counts_table_definition_options() ); foreach ($tables as $table => $def) { $this->create_table($table, $def); } Controller_Settings::shared()->set(Controller_Settings::OPTION_SCHEMA_VERSION, self::SCHEMA_VERSION); } public function require_schema_version($version) { $current = Controller_Settings::shared()->get_int(Controller_Settings::OPTION_SCHEMA_VERSION); if ($current < $version) { $this->install(); } } public function query($query) { global $wpdb; if ($wpdb->query($query) === false) throw new RuntimeException("Failed to execute query: {$query}"); } public function get_wpdb() { global $wpdb; return $wpdb; } public function create_temporary_role_counts_table() { return $this->create_temporary_table(self::TABLE_ROLE_COUNTS_TEMPORARY, $this->get_role_counts_table_definition_options()); } }login-security/classes/controller/whitelist.php000064400000022246147207020750016100 0ustar00_cachedStatus[$ipHash])) { return $this->_cachedStatus[$ipHash]; } $whitelist = Controller_Settings::shared()->whitelisted_ips(); foreach ($whitelist as $entry) { if ($this->ip_in_range($ip, $entry)) { $this->_cachedStatus[$ipHash] = true; return true; } } $this->_cachedStatus[$ipHash] = false; return false; } /** * Check if the supplied IP address is within the user supplied range. * * @param string $ip * @return bool */ public function ip_in_range($ip, $range) { if (strpos($range, '/') !== false) { //CIDR range -- 127.0.0.1/24 return $this->_cidr_contains_ip($range, $ip); } else if (strpos($range, '[') !== false) { //Bracketed range -- 127.0.0.[1-100] // IPv4 range if (strpos($range, '.') !== false && strpos($ip, '.') !== false) { // IPv4-mapped-IPv6 if (preg_match('/:ffff:([^:]+)$/i', $range, $matches)) { $range = $matches[1]; } if (preg_match('/:ffff:([^:]+)$/i', $ip, $matches)) { $ip = $matches[1]; } // Range check if (preg_match('/\[\d+\-\d+\]/', $range)) { $ipParts = explode('.', $ip); $whiteParts = explode('.', $range); $mismatch = false; if (count($whiteParts) != 4 || count($ipParts) != 4) { return false; } for ($i = 0; $i <= 3; $i++) { if (preg_match('/^\[(\d+)\-(\d+)\]$/', $whiteParts[$i], $m)) { if ($ipParts[$i] < $m[1] || $ipParts[$i] > $m[2]) { $mismatch = true; } } else if ($whiteParts[$i] != $ipParts[$i]) { $mismatch = true; } } if ($mismatch === false) { return true; // Is whitelisted because we did not get a mismatch } } else if ($range == $ip) { return true; } // IPv6 range } else if (strpos($range, ':') !== false && strpos($ip, ':') !== false) { $ip = strtolower(Model_IP::expand_ipv6_address($ip)); $range = strtolower($this->_expand_ipv6_range($range)); if (preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/i', $range)) { $IPparts = explode(':', $ip); $whiteParts = explode(':', $range); $mismatch = false; if (count($whiteParts) != 8 || count($IPparts) != 8) { return false; } for ($i = 0; $i <= 7; $i++) { if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]$/i', $whiteParts[$i], $m)) { $ip_group = hexdec($IPparts[$i]); $range_group_from = hexdec($m[1]); $range_group_to = hexdec($m[2]); if ($ip_group < $range_group_from || $ip_group > $range_group_to) { $mismatch = true; break; } } else if ($whiteParts[$i] != $IPparts[$i]) { $mismatch = true; break; } } if ($mismatch === false) { return true; // Is whitelisted because we did not get a mismatch } } else if ($range == $ip) { return true; } } } else if (strpos($range, '-') !== false) { //Linear range -- 127.0.0.1 - 127.0.1.100 list($ip1, $ip2) = explode('-', $range); $ip1N = Model_IP::inet_pton($ip1); $ip2N = Model_IP::inet_pton($ip2); $ipN = Model_IP::inet_pton($ip); return (strcmp($ip1N, $ipN) <= 0 && strcmp($ip2N, $ipN) >= 0); } else { //Treat as a literal IP $ip1 = Model_IP::inet_pton($range); $ip2 = Model_IP::inet_pton($ip); if ($ip1 !== false && $ip1 === $ip2) { return true; } } return false; } /** * Utility */ /** * Returns whether or not the CIDR-formatted subnet contains $ip. * * @param string $subnet * @param string $ip A human-readable IP. * @return bool */ protected function _cidr_contains_ip($subnet, $ip) { list($network, $prefix) = array_pad(explode('/', $subnet, 2), 2, null); if (filter_var($network, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { // If no prefix was supplied, 32 is implied for IPv4 if ($prefix === null) { $prefix = 32; } // Validate the IPv4 network prefix if ($prefix < 0 || $prefix > 32) { return false; } // Increase the IPv4 network prefix to work in the IPv6 address space $prefix += 96; } else { // If no prefix was supplied, 128 is implied for IPv6 if ($prefix === null) { $prefix = 128; } // Validate the IPv6 network prefix if ($prefix < 1 || $prefix > 128) { return false; } } $bin_network = Model_Crypto::substr(Model_IP::inet_pton($network), 0, ceil($prefix / 8)); $bin_ip = Model_Crypto::substr(Model_IP::inet_pton($ip), 0, ceil($prefix / 8)); if ($prefix % 8 != 0) { //Adjust the last relevant character to fit the mask length since the character's bits are split over it $pos = intval($prefix / 8); $adjustment = chr(((0xff << (8 - ($prefix % 8))) & 0xff)); $bin_network[$pos] = ($bin_network[$pos] & $adjustment); $bin_ip[$pos] = ($bin_ip[$pos] & $adjustment); } return ($bin_network === $bin_ip); } /** * Expands a compressed printable range representation of an IPv6 address. * * @param string $range * @return string */ protected function _expand_ipv6_range($range) { $colon_count = substr_count($range, ':'); $dbl_colon_count = substr_count($range, '::'); if ($dbl_colon_count > 1) { return false; } $dbl_colon_pos = strpos($range, '::'); if ($dbl_colon_pos !== false) { $range = str_replace('::', str_repeat(':0000', (($dbl_colon_pos === 0 || $dbl_colon_pos === strlen($range) - 2) ? 9 : 8) - $colon_count) . ':', $range); $range = trim($range, ':'); } $colon_count = substr_count($range, ':'); if ($colon_count != 7) { return false; } $groups = explode(':', $range); $expanded = ''; foreach ($groups as $group) { if (preg_match('/\[([a-f0-9]{1,4})\-([a-f0-9]{1,4})\]/i', $group, $matches)) { $expanded .= sprintf('[%s-%s]', str_pad(strtolower($matches[1]), 4, '0', STR_PAD_LEFT), str_pad(strtolower($matches[2]), 4, '0', STR_PAD_LEFT)) . ':'; } else if (preg_match('/[a-f0-9]{1,4}/i', $group)) { $expanded .= str_pad(strtolower($group), 4, '0', STR_PAD_LEFT) . ':'; } else { return false; } } return trim($expanded, ':'); } /** * @return bool */ public function is_valid_range($range) { return $this->_is_valid_cidr_range($range) || $this->_is_valid_bracketed_range($range) || $this->_is_valid_linear_range($range) || Model_IP::is_valid_ip($range); } protected function _is_valid_cidr_range($range) { //e.g., 192.0.2.1/24 if (preg_match('/[^0-9a-f:\/\.]/i', $range)) { return false; } $components = explode('/', $range); if (count($components) != 2) { return false; } list($ip, $prefix) = $components; if (!Model_IP::is_valid_ip($ip)) { return false; } if (!preg_match('/^\d+$/', $prefix)) { return false; } if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { if ($prefix < 0 || $prefix > 32) { return false; } } else { if ($prefix < 1 || $prefix > 128) { return false; } } return true; } protected function _is_valid_bracketed_range($range) { //e.g., 192.0.2.[1-10] if (preg_match('/[^0-9a-f:\.\[\]\-]/i', $range)) { return false; } if (strpos($range, '.') !== false) { //IPv4 if (preg_match_all('/(\d+)/', $range, $matches) > 0) { foreach ($matches[1] as $match) { $group = (int) $match; if ($group > 255 || $group < 0) { return false; } } } $group_regex = '([0-9]{1,3}|\[[0-9]{1,3}\-[0-9]{1,3}\])'; return preg_match('/^' . str_repeat("{$group_regex}\\.", 3) . $group_regex . '$/i', $range) > 0; } //IPv6 if (strpos($range, '::') !== false) { $range = $this->_expand_ipv6_range($range); } if (!$range) { return false; } $group_regex = '([a-f0-9]{1,4}|\[[a-f0-9]{1,4}\-[a-f0-9]{1,4}\])'; return preg_match('/^' . str_repeat($group_regex . ':', 7) . $group_regex . '$/i', $range) > 0; } protected function _is_valid_linear_range($range) { //e.g., 192.0.2.1-192.0.2.100 if (preg_match('/[^0-9a-f:\.\-]/i', $range)) { return false; } list($ip1, $ip2) = explode("-", $range); $ip1N = Model_IP::inet_pton($ip1); $ip2N = Model_IP::inet_pton($ip2); if ($ip1N === false || !Model_IP::is_valid_ip($ip1) || $ip2N === false || !Model_IP::is_valid_ip($ip2)) { return false; } return strcmp($ip1N, $ip2N) <= 0; } protected function _is_mixed_range($range) { //e.g., 192.0.2.1-2001:db8::ffff if (preg_match('/[^0-9a-f:\.\-]/i', $range)) { return false; } list($ip1, $ip2) = explode("-", $range); $ipv4Count = 0; $ipv4Count += filter_var($ip1, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false ? 1 : 0; $ipv4Count += filter_var($ip2, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false ? 1 : 0; $ipv6Count = 0; $ipv6Count += filter_var($ip1, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false ? 1 : 0; $ipv6Count += filter_var($ip2, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false ? 1 : 0; if ($ipv4Count != 2 && $ipv6Count != 2) { return true; } return false; } }login-security/classes/controller/cron.php000064400000000006147207020750015013 0ustar00secrets; $wpdb->query($wpdb->prepare("INSERT INTO `{$table}` (`user_id`, `secret`, `recovery`, `ctime`, `vtime`, `mode`) VALUES (%d, %s, %s, UNIX_TIMESTAMP(), %d, 'authenticator')", $user->ID, Model_Compat::hex2bin($secret), implode('', array_map(function($r) { return Model_Compat::hex2bin($r); }, $recovery)), $vtime)); /** * Fires when 2FA is enabled for a user. * * @since 1.1.13 * * @param \WP_User $user The user. */ do_action('wordfence_ls_2fa_activated', $user); } /** * Validates the 2FA (or recovery) code for the given user. This will return `null` if the user does not have 2FA * enabled. This check will mark the code as used, preventing its use again. * * @param \WP_User $user * @param string $code * @return bool|null Returns null if the user does not have 2FA enabled, false if the code is invalid, and true if valid. */ public function validate_2fa($user, $code, $update = true) { global $wpdb; $table = Controller_DB::shared()->secrets; $record = $wpdb->get_row($wpdb->prepare("SELECT * FROM `{$table}` WHERE `user_id` = %d FOR UPDATE", $user->ID), ARRAY_A); if (!$record) { return null; } if (preg_match('/^(?:[a-f0-9]{4}\s*){4}$/i', $code)) { //Recovery code $code = strtolower(preg_replace('/\s/i', '', $code)); $recoveryCodes = str_split(strtolower(bin2hex($record['recovery'])), 16); $index = array_search($code, $recoveryCodes); if ($index !== false) { if ($update) { unset($recoveryCodes[$index]); $updatedRecoveryCodes = implode('', $recoveryCodes); $wpdb->query($wpdb->prepare("UPDATE `{$table}` SET `recovery` = X%s WHERE `id` = %d", $updatedRecoveryCodes, $record['id'])); } $wpdb->query('COMMIT'); return true; } } else if (preg_match('/^(?:[0-9]{3}\s*){2}$/i', $code)) { //TOTP code $code = preg_replace('/\s/i', '', $code); $secret = bin2hex($record['secret']); $matches = $this->check_code($secret, $code, floor($record['vtime'] / self::TIME_WINDOW_LENGTH)); if ($matches !== false) { if ($update) { $wpdb->query($wpdb->prepare("UPDATE `{$table}` SET `vtime` = %d WHERE `id` = %d", $matches, $record['id'])); } $wpdb->query('COMMIT'); return true; } } $wpdb->query('ROLLBACK'); return false; } /** * Checks whether or not the code is valid for the given secret. If it is, it returns the time window (as a timestamp) * that matched. If no time windows are provided, it checks the current and one on each side. * * @param string $secret The secret as a hex string. * @param string $code The code. * @param null|int The last-used time window (as a timestamp). * @param null|array $windows An array of time windows or null to use the default. * @return bool|int The time window if matches, otherwise false. */ public function check_code($secret, $code, $previous = null, $windows = null) { $timeCode = floor(Controller_Time::time() / self::TIME_WINDOW_LENGTH); if ($windows === null) { $windows = array(); $validRange = array(-1, 1); //90 second range for authenticator $lowRange = $validRange[0]; $highRange = $validRange[1]; for ($i = 0; $i >= $lowRange; $i--) { $windows[] = $timeCode + $i; } for ($i = 1; $i <= $highRange; $i++) { $windows[] = $timeCode + $i; } } foreach ($windows as $w) { if ($previous !== null && $previous >= $w) { continue; } $expectedCode = $this->_generate_totp($secret, dechex($w)); if (hash_equals($expectedCode, $code)) { return $w * self::TIME_WINDOW_LENGTH; } } return false; } /** * Generates a TOTP value using the provided parameters. * * @param $key The key in hex. * @param $time The desired time code in hex. * @param int $digits The number of digits. * @return string The TOTP value. */ private function _generate_totp($key, $time, $digits = 6) { $time = Model_Compat::hex2bin(str_pad($time, 16, '0', STR_PAD_LEFT)); $key = Model_Compat::hex2bin($key); $hash = hash_hmac('sha1', $time, $key); $offset = hexdec(substr($hash, -2)) & 0xf; $intermediate = ( ((hexdec(substr($hash, $offset * 2, 2)) & 0x7f) << 24) | ((hexdec(substr($hash, ($offset + 1) * 2, 2)) & 0xff) << 16) | ((hexdec(substr($hash, ($offset + 2) * 2, 2)) & 0xff) << 8) | ((hexdec(substr($hash, ($offset + 3) * 2, 2)) & 0xff)) ); $otp = $intermediate % pow(10, $digits); return str_pad("{$otp}", $digits, '0', STR_PAD_LEFT); } }login-security/classes/controller/time.php000064400000014551147207020750015022 0ustar00reset_ntp_disabled_flag(); } public function uninstall() { wp_clear_scheduled_hook('wordfence_ls_ntp_cron'); Controller_Settings::shared()->reset_ntp_disabled_flag(); } public function init() { $this->_init_actions(); } public function _init_actions() { add_action('wordfence_ls_ntp_cron', array($this, '_wordfence_ls_ntp_cron')); } public function _wordfence_ls_ntp_cron() { if (Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_ALLOW_DISABLING_NTP) && Controller_Settings::shared()->is_ntp_cron_disabled()) return; $ntp = self::ntp_time(); $time = time(); if ($ntp === false) { $failureCount = Controller_Settings::shared()->increment_ntp_failure_count(); if ($failureCount >= self::FAILURE_LIMIT) { Controller_Settings::shared()->set(Controller_Settings::OPTION_USE_NTP, false); Controller_Settings::shared()->set(Controller_Settings::OPTION_NTP_OFFSET, 0); } } else { Controller_Settings::shared()->reset_ntp_failure_count(); Controller_Settings::shared()->set(Controller_Settings::OPTION_USE_NTP, true); Controller_Settings::shared()->set(Controller_Settings::OPTION_NTP_OFFSET, $ntp - $time); } Controller_Settings::shared()->set(Controller_Settings::OPTION_ALLOW_DISABLING_NTP, true); } /** * Returns the current UTC timestamp, offset as needed to reflect the time retrieved from an NTP request or (if * running in the complete plugin) offset as needed from the Wordfence server's true time. * * @param bool|int $time The timestamp to apply any offset to. If `false`, it will use the current timestamp. * @return int */ public static function time($time = false) { if ($time === false) { $time = time(); } $offset = 0; if (Controller_Settings::shared()->is_ntp_enabled()) { $offset = Controller_Settings::shared()->get_int(Controller_Settings::OPTION_NTP_OFFSET); } else if (WORDFENCE_LS_FROM_CORE) { $offset = \wfUtils::normalizedTime($time) - $time; } return $time + $offset; } /** * Returns the current timestamp from ntp.org using the NTP protocol. If unable to (e.g., UDP connections are blocked), * it will return false. * * @return bool|float */ public static function ntp_time() { $servers = array('0.pool.ntp.org', '1.pool.ntp.org', '2.pool.ntp.org', '3.pool.ntp.org'); //Header - RFC 5905, page 18 $header = '00'; //LI (leap indicator) - 2 bits: 00 for "no warning" $header .= sprintf('%03d', decbin(self::NTP_VERSION)); //VN (version number) - 3 bits: 011 for version 3 $header .= '011'; //Mode (association mode) - 3 bit: 011 for "client" $packet = chr(bindec($header)); $packet .= str_repeat("\x0", 39); foreach ($servers as $s) { $socket = @fsockopen('udp://' . $s, 123, $err_no, $err_str, 1); if ($socket) { stream_set_timeout($socket, 1); $remote_originate = microtime(true); $secondsNTP = ((int) $remote_originate) + self::NTP_EPOCH_CONVERT; $fractional = sprintf('%010d', round(($remote_originate - ((int) $remote_originate)) * 0x100000000)); $packed = pack('N', $secondsNTP) . pack('N', $fractional); if (@fwrite($socket, $packet . $packed)) { $response = fread($socket, 48); $local_transmitted = microtime(true); } @fclose($socket); if (isset($response) && Model_Crypto::strlen($response) == 48) { break; } } } if (isset($response) && Model_Crypto::strlen($response) == 48) { $longs = unpack("N12", $response); $remote_originate_seconds = sprintf('%u', $longs[7]) - self::NTP_EPOCH_CONVERT; $remote_received_seconds = sprintf('%u', $longs[9]) - self::NTP_EPOCH_CONVERT; $remote_transmitted_seconds = sprintf('%u', $longs[11]) - self::NTP_EPOCH_CONVERT; $remote_originate_fraction = sprintf('%u', $longs[8]) / 0x100000000; $remote_received_fraction = sprintf('%u', $longs[10]) / 0x100000000; $remote_transmitted_fraction = sprintf('%u', $longs[12]) / 0x100000000; $remote_originate = $remote_originate_seconds + $remote_originate_fraction; $remote_received = $remote_received_seconds + $remote_received_fraction; $remote_transmitted = $remote_transmitted_seconds + $remote_transmitted_fraction; $delay = (($local_transmitted - $remote_originate) / 2) - ($remote_transmitted - $remote_received); $ntp_time = $remote_transmitted - $delay; return $ntp_time; } return false; } /** * Formats and returns the given timestamp using the time zone set for the WordPress installation. * * @param string $format See the PHP docs on DateTime for the format options. * @param int|bool $timestamp Assumed to be in UTC. If false, defaults to the current timestamp. * @return string */ public static function format_local_time($format, $timestamp = false) { if ($timestamp === false) { $timestamp = self::time(); } $utc = new \DateTimeZone('UTC'); if (!function_exists('date_timestamp_set')) { $dtStr = gmdate("c", (int) $timestamp); //Have to do it this way because of PHP 5.2 $dt = new \DateTime($dtStr, $utc); } else { $dt = new \DateTime('now', $utc); $dt->setTimestamp($timestamp); } $tz = get_option('timezone_string'); if (!empty($tz)) { $dt->setTimezone(new \DateTimeZone($tz)); } else { $gmt = get_option('gmt_offset'); if (!empty($gmt)) { if (PHP_VERSION_ID < 50510) { $dtStr = gmdate("c", (int) ($timestamp + $gmt * 3600)); //Have to do it this way because of < PHP 5.5.10 $dt = new \DateTime($dtStr, $utc); } else { $direction = ($gmt > 0 ? '+' : '-'); $gmt = abs($gmt); $h = (int) $gmt; $m = ($gmt - $h) * 60; $dt->setTimezone(new \DateTimeZone($direction . str_pad($h, 2, '0', STR_PAD_LEFT) . str_pad($m, 2, '0', STR_PAD_LEFT))); } } } return $dt->format($format); } }login-security/classes/controller/support.php000064400000004212147207020750015571 0ustar00 RewriteEngine On RewriteCond %{REQUEST_URI} \.php$ RewriteRule .* - [F,L,NC] Require all denied Order deny,allow Deny from all login-security/img/loading_background.png000064400000000235147207020750014623 0ustar00PNG  IHDR((mdIDATXٱ 0 A#{CEAu$ PcpymrS[rs (P@ (P@ (U`/ɌzIENDB`login-security/img/loading.gif000064400000004761147207020750012415 0ustar00GIF89aర莎Ȝبvvvhhh!Created with ajaxload.info!! NETSCAPE2.0, $AeZ <䠒ÌQ46<A ßHa:ID0Fa\xG3! O:-RjTJ*  t ~" ds]  )t-"i;H>nQg]_* R3 GI? ˴v$ýj3!!, $0eZy0q PУW )";qX^D50 Ո%`rJ{ 1$ʈ!!, $@e6$Ơ` 3*=  P\"F`P-d5V"2|?n"!( )e4xyc?   3 #wyJ l% o^[b_0 V T[0m $4>'VZ c3$X%!!, $`e:D3 H0,'j0Qs L(2HMj#ȉB \Oi`u=YEVL=I  > suI WJm| \"_b0 BcV"d]*K1" H|@B?I4# S$-||!!, $4ea:D hI /K$W- 0(`3F=pf@tQ  {f~*yS*mg) enu E^Z^ g@ kw(b& -w#" xW"t ##%U$`to!!, $4ea:* 1v/Kdzk#  F Y" % E  Cb AI4$ (z:2 mI Ll## F##>F!! , $4ea:* 1v/KdVtKG227D"$)Qqp8 y l |~6zw2j# F " % VC ]6a$ Q :2 \  EF I&x "͓F4$]#x!! , $4eZi䠒J16e E,C\3 ^3[ S|?!;login-security/img/ui-icons_444444_256x240.png000064400000015520147207020750014401 0ustar00PNG  IHDREr@bKGDD< pHYsHHFk>mIDATx{leG}?gK$U!>TRu &H`oE*U.B RE*%B$EFdE)݈†>lB^PRU%9g^{}{=y~9Mn<.` cAb/@[VD03AX90N_B &>~> c;abDߎEQz'kMayԉ 6!:u::@RŤByDD'L-f]Sq!f SQ&S7MCr==3dJ{fZS0Ms:0K٦gʿ&HU=mc4i?UĔGU4hcQb]!hLW/  @pxh8~|AQf?ێ1f¸=uQGJHpPϠIw״m췥ԧ>2"WÓP&{nT:sfqH@.cI ~Ss+^|Bn29 dH]v-ˌ-meh>q&تg 9x#cn~!pxh84^/ o #Z@S^4 KZKPd9C@F[,a+]8vKqHlw9ק84KBץ|&#[\C`R!:F zC6)AT1wU.Iҗ!4ig3wE:q7n0uAmPy TK(5ͬlNæbTrwDV]te47 L[C0P&0++  @pxh84Y`OEZj,*-E)e{U, \uoV: `omKet_WOdW3Z Քrv|~^g`jycp*fb]M59.LNq+%ۯ0vccJEM5kk#I<x_ I#0Dw4xh84^/ ^c drDM{wQte{^`br./M:0գ*z`]|SB(e)h͎/Vܮ"aO!o ,u^Љ^=zt`WeCCM Qϱo0q%Uo?<@6HG/uAQ-_u||ִ*2lٍuG@ j !Br qL!*)h.V{3-!w$dP*/P[ t1l3hlyp0bgL} 3$%\/.)TwQ"uK7d+2! @pxh84'aY$mf6lB'.D Рx7SxtIe We}d0RW^ݢ0](U݋Tx|T|@v^Df׹Lb(2H0n8;_QO}D&3{YWw5*e˗?̖G :H iZӖ)/MP(:!/|B[i:Gf L`~:z$aa^ʭ|P- kbݞ<ەL&xJAABȼHvᑢ[ySəwNJuy %pw0_a"zx 3i1|= @%JWAm\ l>arV-PSוjA^&v&^WcqMz-&| ϜCʥxx}\ZW4Ig8YL1  aROQ.PU(>K&c.mO-M\8SY.'|5*܊B@֘A%Mo2:c[K93-tnNjBo2`*]9aZAq?Wկó)%0"_ϮqpyV!/hI.($]!rW+p P; 44E*0r"77`\*!s<@Zbn0͏me HEH]XSM~#a1&|VbYQE}e%ͦ@~NN>8o7*GBMR-U=-³K E+x p c\UWs5Wla?Lsf;2vy+o*m m-t8 W<,o̰8f=CH`⾿h)_}(%m'& ;(ݚ!{:^R@N ү"*<V敡5oh~q+`6IiI<%Z=^JE\`0]1DA^a0f(c %[Gxh84^/Ordh9M7L)-ӂ-?K(stբh-?:_XڧCr]8YB3|C| leXW_x!V8Ɗb8bQ7N4Jl!= D ?'a`^aq8:-YPx[o֝XQ"GִW} 1>cÚbYV 6=*ۿŻ)8hs/8'Ϳ,lEb\Ye.M6܈d9uJZĽj-l&8:s\Кty'hs[Gmh-mk2^SJe%u\j5 n|;଴δa[;Хˎp?|yRaC$_a/)NUNƍȄp~Ú/kn|J)/9Q:,K xR! B%]lq+|w ս_\GW)"3]aCJ%>|' _ƅDP*07³=VZEgV-L'@}2ƪ[P' rEm["vO# 5g$$?mƟ'zJ^SxHjg4D=4@F7# ъ~z'o To㩋b̓ddcBU%T9ҊD@.!+Nwuer>n-U$U`Iv%AQEd ;Ә:+a&XĎ y%k Gс "4pxh84^ @d6((Ԕ{C::[>H, ɴNVNhr.~NZb3+Q|,%t%n!作*7fg(m!f y"kI6Y/eZٳ+zg6W8ɦbQ%y8Ï2ŕ#b-tY͕8ynO{]!ټ*\ (p2.Hٙd.>Z(N &l2&k U&ף~.ו658A7}P:BlkU[8)j1$W !s\ QFn(qRK:'X@=$ x#|5:@]2Si.&W $` 8t3 =&[#ߥU3Wҳq .H{➇Z,ȣ"AH#珁W(+̱1Ɏ;8/<EE C+nU:|a ɔM|?LW1$~}W ,oVRl{7 ^;<,t[T"3SU9l˾H[yV&a@E&I?U7xG4c *i'xƨ" I3c1e\ӎe($ztnꅧHv.|mO9QFL&y Πg)5ޜlWv' 6zK,`]ǫ2@}Ϟ9=>4=O5͍sp 'UX⻹{J FalrF}c@`fQȎчxm܃ #<𶀆 @pd`.6Ip۞]A+"y Pns3oz7hzeDp'm&MwMh<Ǧ(I'G>W%QWVoHtMtjDU͕l n IwoO_5~YXw(Rm)'Dɸ5=@+~jy^`O`$!_U#!GB |V:ĵ>de=Ay<Շ*^_b T)}7 bPohhh~7rM/dFHGI>&Ql/L^b:גTDM9Q<*y =2~/?J,+%qqޗS?ܑ ^e]GSVpu^س~oBo l84^/  @!/ONGۮ2t]С8cݮ5z=7ˮg@/aE[y#3#z7L.| (cRcSmQ$6ץ{ Da{T_j84^/  @ñ`/_g "d`ɾ0 { Cj_9jDՋa~? dc[0*a,& 0&+ OB4!1~Ix>8c&G)jI=@5@v&J?QlÇ@Q?x^y 3%tEXtdate:create2015-03-11T14:59:12+00:000i%tEXtdate:modify2015-03-11T14:59:12+00:00AtEXtSoftwareAdobe ImageReadyqe<IENDB`login-security/img/ui-icons_cc0000_256x240.png000064400000010705147207020750014517 0ustar00PNG  IHDRIJPLTErYtRNS3P/"Uq@f`2 !<BHK Z#'1S,4j8E|)Q$ bJmߜGc?oh@^bKGDH pHYsHHFk>dIDATx] c۶H阒]Kd%٫뺮lmw]|pXm-}X:+iĆQV9\e'AtOS:72YsxMہB&z>nC@r@*aӝ%MFDDDDDDTߖH,ERUn ب\f͹:}4ᦋ{)n[ ̰E K Y Dۇ- +Kl=ӃL`љ|%n a N#5 (4?EDDDD\oWFfq;\E_,W!%zE!F¶. (USHQ0dw)T 8#p,xBK *xXEe K솎%mKX~sFE~tdcaI1Af4dHcGSB`0wev`"{ .GDDDD,dO6k"qkMefS_UKŌ&g~>n H}) LF%8()r![4统qQk0m[Le_70@>1 X0AZVcEV Ltk3EJ44ZﮊN`rt>`˥  AHBLH@cUq=jcM2sJCLiR NQ0=Yi-|4V ]]B^ޞ_H$<$ a=d@ (ZAp_}~s:N{DC>m^ƒS&, ;N&B} <_AB]HuN(B0{h1IKDs j' M8.ӫ1h3df}mq nU{Lo z \=?@ ((e|=ơ麄Ci1r<|OO; `HpQyzԈuZVƲ!)5mC2Lyg;֑RjWa@@VL&Wru=Z ̥=U5} 7;b(nP&sk48ͥ01U Wvk18dqTՌE]qH8 GF K'rOrŗ6"fpT^3c"nMم-/W=tJ,X){P Rm|K>mX8v5h<_{ꘀYF|&_G;&>^W⁃&K(81EB@F&;"L'wfwE-6o&/̫'Xe,>~ee|A=) dQ`}P[KN˂/~)O[dO=3El5'Y$?7mTzզ.\.` WE"""""v)V#]L;zqJr²[\-tҽ5 @ͷϟnT@+;cQhC*TڙAVr?f? Q1T`} Hk,{VZˋTϛ?I̯uQKLMe͆~qym09 S;j5 iQ]7k0UޭGkX3#lY_Цxj޶9`# M [z KuO_z˿Dܭ*kOJ(7n\e ITƨl/U߶uw.~;#r.8 o# 5Lh>1i pVM?/u70 X@L+M+{Fkt{ŧ890`. ĀCR+\/t R; TӲ]aL|efđ >ۣG|P`P8C1K՛A̍<2ۂKrl@L L 8@E>`nPNԍ,p EƆZF lÎ; F7Ȯ; swSz)g7{rsSgȋ(߄~AWytX$NVR_<6p.O8O[OdDk>_OO}JSdmV?W(_m j~=H IԁF>T/{*]IGJ@iqamNF|Q50+ ES8:v`p~vj:Bp96oys% |@H]+@t]Wk}}7FʮrAB\m-_2PY8xՎN.h~@+7z5t_//?0S>)zi0n/B`{DW#`Bo[,gFVЁpP߾C]Bz ,XXfԃA:H k7dZ9oc}o]0vd:R]0ve]刈jу| ? +(OǍ+ #ysߍnpFru<.HȺotM3h}߆P}˗vP}mǀ?WZ@}@@FDl%tEXtdate:create2015-03-11T14:59:12+00:000i%tEXtdate:modify2015-03-11T14:59:12+00:00AtEXtSoftwareAdobe ImageReadyqe<IENDB`login-security/img/menu.svg000064400000005776147207020750012005 0ustar00 login-security/img/lightbox-controls.png000064400000006236147207020750014477 0ustar00PNG  IHDR2*jtEXtSoftwareAdobe ImageReadyqe< @IDATx\kpU$3$L qwA,TPB@"D Ft-u]ZGŔDuԬ#@<bLOgfNusw۸ X g`±\ñ[ ~R eLXlH81`%Wd^E*^Ef*ɚ" *r]  '~"Jx|//[UJJm}v9},/!h~a^xr.{Fdʭk bݰ~C=ldҕIxSP>+_(_sYgYa\vW,VKE\|\Aμ0 0,le1gJ%$&cbc0e7&$l웒BvuuaZѹîޠ_m& z͛O|Z @[[=ڇi%xur|(ByK CYP\U5? եΝ;ۀic0:Ng_ #5R0y#Gy5d6]|/dB].gk׮ڷok:u*m6grrH:vJ!p}NeB*ĉOAb37 ݡCkkkՆujqp/!G>f͚Ib_&m޼f,F4kꫯ6ЏedeΝ;p8>RRUcc$RWz, A%Pha;iҤ#Q<^m7,B  fErfժUob0(|y' ] {^. 1&sr =00 G)wz޲JI94tvv".^˗UXXXrF]hZ߆P j+V .:y"h9A'ΊʠgF)_t7€o[w~P{?ňu]vM2jEͰ-U6mCh-o|}FS be: ǯD,*c# ^P\muV;~&f h<4R5`E p=~Q?mXCLb/A"(UD7=E*d4@I Q$yv%%^< \͇ؾƙ'Ԍ(/ Vp4X῔bQ?|@ 8F)xaSE =6Vayu!T_6DY؀|ydK3}H Ap\4w{{;X|\{fxG43*M& ,_.=riHx99Dn62mp^?U<>zS^l6Y+8Ӝ|qRzvtB9&$$h) Ʈ#fZ)rhoğ0oɠ]3Z-qQo8i`[rͼ!SCCuvv{ ׊nEy~ 7yוդO-\Iz)j4 Cah qIxI9jjrx9jR=VEɆ V9qWu67I!Wϥ#ɑq=lOOK2,o\]2 BET ̙sEnjzqa9;^dI+$I+LmYi ض 0!hK[=(9EuXZy_otƘct2WI"%;Jfz׃{ЧD}ɬY;SN[qO >em`)YYrLj޷kXdvfXG@_\ps͕i6DPMHq5ԌO`uggXFV䇭Mc_4kii!gΜy#$V+gϞ8*kTv̘1̙3PX6^W [֬OQ|[B =cPe0y^㥴(F\ .`III53bTߘ1cڋjF}Nj;Ə\{![noV^fvYYYpFF|:da"'6XpGkr#Rr#{GY !LjUTTpj3//0h4o!9l2{4/k[PP>"mRk:{d`Got;ߝf*cc_IR@ӷ<~uzklڴ~^kjjcXWWױ4q[|(E&N| ޟU^S5T1hS5rf{wr٭꺏> G566~f۳gEMdF+pm,2hw,Z|@lrGE lSݸqcڴi@6h_!'hQN%r>y"N$hПB:{$(Jd 8 ztv^8,|+ `!E8'N$l1}ƞbIDATx]]%uzfV^;lY03&)]P 'M@+ȋa k`J!&~H2ք F?!0_&`>u޹?U_3sSu|U]:!D@t 8"t@M;ljH=&Omk3FB8F;@* cOy=kWڅە 3ANI*vP-ast $Hp As21E,ځ ԪGE'ihe]iSǼjvqPr /Y e-ڦt R5wgf I;&ImJmqkۦw4BsȠWM &_ /1n;z_$ 8H`# pDH 8|3h2oF)e>}Hl>LBV-}uҾo|ڵ/l| "o]}HSwǗAs!803@j~yӇ 4h >@")J_9]L;njq (pDH 8"G$@'MqXi}"03]AS(~ @@-$M6 W3'79+OqOh\O< `xTOHp/dg4 "GlՈf+)CO'u*c'TMKh^< uWhzHʇڸq풁| vAM1|B2 t3XbhW rsnZLwpO~8 pđ 8"G$@# pCR~O xDsM* |{oWC;]Հl5)^^ #šNv97> tm aϿ_ k{bW%p@ݖC>۩`ͤqǽ;~>iCZX^[HfalsX'0M _I~9'}~{.@62,hPU@U൓)UCH _\f.*@/gN*C-{g7H4ɼkor?_59]bU/_kp)m/qToLZ8D88"G$@#`ܘ*s> ʧ轎I{`>@1%,`35"G # pDH1{Hg[0 ˖\6%Hqx,Oaflxt! b[WX7wNyZ.ZuCTk qh_|kj~Q@n2=A6-kϺ"-RgZPv \͹8f/-Kp[΃:%,AL&S&D'Ms>A' FUf` JvQ8]6"?_޽C$n\Ba#¬#,5D3fq# p,'?'޷:0+n֚^vx] f 45b{=6v=NWxG8£K3kt͒] -F⛛./復Ou{~#qRyB+xۂJP1 !%3/` >\^dG/N-^ZvP#Y"ho \[LugHz~]()`+8PS~3OY((My~;HĻ}x 9 2SwG 9M\f/A1n~p SN<p-@o.-t>0-[\_g@6 nYNa .B|9>.N&hD[/3 oL59G0>*Z;"x}Œ.QٟJ| LQG5c̍ >Iէ-}{s=5}  _<Գ{ɂWZgԔ5~c|- 8"G$@X,WE Zm}p-oCCmȹA'[69~ ӎP#0*)uQDvi)FF4dAntÐ˞lO6;R,:ElPǔ K:Dۄt).kL9AOqrџWԧlJߢ\&;߈2AK9DiZyQ#5۬|{MHD#JD\8)ju9|1~=O=\Vn~'/PX(,u'0MPŃmq | J~yL1ާ{W/?o ]}uܙ F&*S\=u3~NzߤYq6DR_/MwElJCǯq!Lw]*ԐjΛ O]DCBɡB&@_fd]zva{!_nmSQnoMkn[qP3.)#{>oCz!U&6~߃JeتI]Ӻe9Qa.%sj=;x{DŽy:8"G$@ sɠ|6zCm79V ^Vzk}4*HAɿ6ZtF..2>Ot-fV;W_ٞ,O >Z|ya~萻B ki#@u"ڥ! *7! f3DckTɰCk.sPש;C<}Ra0L;7Lw5@6JCDV#NXFa곮8y>B %:dH Ar'@+2oH:͢ Ӻ]r8C`W;ErV۸ruEP;3*'JcIzkRl `OW*}Cc |NY+UOr1\LSZ5):aPlNw"Cهf9K`>3Jghe|7IqN[ʥ^c7Mq=M*3+1qKʯ3!KfŠ6|#y >_&lʶ&1_TjW`iHCڥټ ,Z7֟Ԫl;w*QwyFϜhc14R6i6h~ P6S]*r3pC۴NB;G~gj!|hϔ}vQ{z,pQGD{~9 )ߛzn&@T>51 偠0I˫\!\rrh3>]Y7PtӴDO[5nA ] alT2A_&\An4'':aOHc 0  w޺{Gg3/R1T0+ޔ0Rm柅`o*~ _g uFjǟ=sxpE)VU9vuZO=j<_>o5@HPGx#;I 0Q#zG$@# pDZK*6Aa\n6v[K(Ѯ0zLI/jnЮ|y.4qE صẄ́xjq {Y>.]!a[!i.d&~RJ4`&@Sۥ sy7wphq= lefy y_Eŵ^ըwXQ" tloޡs9eSR~ڥ|;i6\/$ʗʿx8_t+xp+ި$@'q_Ǔ.t-o\}]m&-t-o}dC>rFO|Ӗw8KCH7V'J M֝`\ W#|{B ̪$y4,}F>nk-PWEAtH=jiY'W;S'QOQL7DO=@w=KFR™?ЛΕN8#(p̯/ b,?Pw%tEXtdate:create2015-03-11T14:59:12+00:000i%tEXtdate:modify2015-03-11T14:59:12+00:00AtEXtSoftwareAdobe ImageReadyqe<IENDB`login-security/img/ui-icons_555555_256x240.png000064400000015514147207020750014412 0ustar00PNG  IHDREr@bKGDUI pHYsHHFk>iIDATxleGu?wKֻI -Ti};T[-e$boE*Uh]@JD6_$)(j7 »QP zBTlU}n6ku}w+{}Μ;gL<=.` cAb;@zD03FX9:0N_B &>~> c;abD߈E.BH!6 Cu8tuI?!B *Oz.[aͲlB >h>M,4Ro 1zzf^ 4"Sj߱`沛u`M( ,7M { ƞi;~:)7qqF6g]$ױt pxh84^/  @ی2v،1c쩳nʾ~7 |wM~[O}f7@q?@# e.L3noF| Tel8iu{t`neӋOhmP&Lv5zވe]lMԧ7qX ] Wqw(BPpxh84^/ G8gJo}iA )s5~aJYTV1pTVP%تrO?ph *sKM,Gf -։Ckuu@FkmS0(b\/Chjgb)(; z-tVn R71%ݪ-`.i۠@/8PjY%؜Mźh[2 R h7Yo8j~10*Xt0Ua2^Ma%WW/  @pxh8ápihxѮB&=wwn-`r5YJǖ0V\)P>YD@k}Z@XxXY l]l;ul3e4ұp!bPdmgQO c:(); WA}(HWY\URP7Y,]`߬('u LdT˿sf) &)0(T}sLU4!V;Kjs Q:7ػ^{VJ {Ϸ_a*Ǫj.,TGy6 #v1f1i Dx4 ^/  @9l0٫\>Vo/]`Y`K,vL{|<<3Xߔ-ҮsS݂۹]=޴Rn֟6>0Mf{U*o0q%U dP"PX"]`C@f˫v: SE|Bd ZӪȰg7UABTM/V k*)h.V{3(lkl{IB%bj5pPAc*Lt5K|w 5Paļ~@mP @IK^[]REnWe@փ @pxh8OZòH ͖m2لNT]myA9wnI :Jm^NzuRtT}v/Ri&~Su "{=\2= ٿˬ#տɨl:yGA>7Cb ȚfJ_}t2ר/-_d3[FEWA"IӊRןm9|%\OC%lP=2kdz#[/\# /Wn2om+LєUy?>#E*3͔8>K>ap06D9@4 Ag6cl /  @ñ`"]iK{s D07ӕSzb!䬴[,'XI(cOmlm k#K#m~Pa&. )bmuLI fnQ! Lü#鷭mcȱDuBVVD`2GPkpAWNTPU(iJş6 ȗe\Նg=Nj6g t ޟ?[d>^Kߤ,An"hf h6T xXֵ$C a"D0jHMoTBfx^""#rl,ip7u, XFcDJM$"Ĺ}江Wo*B5V-U=-³ E+x`Z%y=s=a 3 ksN4lTBƸhf!NjHa?6B|^,/0rDF[5 !m` '$J~1ofDx;k 6(jPx)_4?Koq&[T:E9/J0D`RWg^V $Y|&yeᯈ9BR*ˆ ׄ/j&RE 21{a`7<* @px](Al=\ntÄҢ>)زoӄҿtZtG@ KtTnFKYfM~-L+K@a,*,OY` NKrw&}9$ߋFݑ[ @{z&f ͿӢ 49 q6ahx1ie^ bdM+@;z7m0:Oa<n-U8/U`^v%p wGJlyTAasS>ge?L^D_i\Sxu5_<tO0 PA𶀆 @p4O"qGA餦vGbQ przEss] 6&K_)ČZ/y!wr'!x!}E~7C~?Cn 1]C'I_2 ,J͞D\\Ig, 1*Q ǹx]~)LOk <"mkvsxb* PaBF+ߐQ!Fj4LqbSqfX֘e A5YkOد2?զ)[lSPҟ0K(JHAt&sUmo T&@oaqABf9bI7ge/P * uN0.p_{Hs|e:@-2Qi.&W $`8tf&mSz sߪ{t+ٸ9u_~qC~LWy\$;W)W]q0 `9!ٱbx=/p&z,jMZq-ѩ c Olsa=/NfxHrW XG5y 9,=yDng6-6yi(De3^o*DgrX}?'xN&b@iE < !i&[Ny`nen3Tgms[Il_D/IoYS4@osѶ _f`?Xqy.sYVm,G g3TG$ɨ(<#5ЌT?ljڦ1_PO ^Ԥ !Nzڱ^!4 tS/4=Ke͍3,rg 'U߫J Faq{i1 0 w ڸG <:Fx~?@m  @v@]]<ҟPj74"}u67s~WF$ Z.h~oxl⛈tIDATx{leG}?g l^CKd*M&6JRm붔MފRUd'Ѻ5@TʣB$EF"nDaWiSRI6Dyq^3s|s̙o~3g~3 >Go.4^dL21L$`l[~&+&щ@G`Vag'alNXb0t(P5RH&<ą-`r22)Kz,$[RpR su8r]aj#zTMS$\Oό3!ZPYo)D N#Yʦ2: )P}_US2L]a?szՙ/lljٛ<2ݎ:+g/  @pxh8z|aQf=ۊ1f¸;{ã)!)fB=&5]ӶߖfO}Ϭ(0oY03(Kdl1U+թLMz{D@>]D-omu ]ZV,ˌMmyhUzfi=@]S؊tTo6g%SB_qw)DPpxh84^/ _G8Ĺ{Ja bR(xhi@IzJҼy tsY@eeC뱅rjm܍C&[LM%m{e^3JM3@ӰX7mkA ".v2MuZfPy -!LUis_hxvv+3  @pxh84Y`>HԀEZn5{Dn{DY l]ysx-[LqmЭq `_XA*fLOtL17:0k SYB> /SA]CgݳX:p:L*YQNT\}="dLM 5-l} 0(TT|sLE4!;K~pm {˷aʜǢ.Jm.718(I : sdbSҞ^:S)!" @pxh84gW@~= &{K'jڻ ,#Vvh=L*}yLʼ=Ƌc6m^P?,LD1DV2S$_`U3 UfaQvFX"&!k֘D`:G㊐(qAOVPU j(iJlWqK li3 x T!sXD@s 9+y8u\mY"r7v֗u- v!H 4<@(B$"PdVZƜ wߠs0kB5h$]dpϪ_@0+4@"hOtckQi c%}TC5|jfJ򭼕da?L sf9[2Vq7; y6mNi  b-"?[fXhE!MLqRTDW*ZIhD$w{mÊ: ~fE㳔&GESdJ4ye]U/Vx06ȃ"XW Nj OFS gc>$C^xT @)>U.,-z)E}Zeg ttG@sKtTk$ZKyO~-Lk+@=2GXVX`3"SMlriߍR{wn''8SjoOI;tXa.0N7N. (O< ;vɤVyY5(aU_}h?Gx8񈦂XgMOz!V.qįi|LHL_F yUғ_J`B]L"tYeM܈d:uJZĵjmlC&8sךt﹅m6T469K) %HZߒ:.՚nz w> c^68#ͦ1mX֡C][5 : a2S-II\q#`pPe^b|~.?=-5OK?09Lx%8qIT d=\&'=n)X[8SDf‚<|'O!) 63Uag}osk;'Z9Z]HQ33uBn6BMf E-l2=)Hx V `Vj,&Jr0YeT]GxL뼪 ])is9NG^WiH6Ͻ `7"Hʬ)\ 1Rsv6Xe=Nc ` Td=az8M?UU^Wҟ<0WKHHAth֪q*SdI&FB8YbI7g=^D=;@qA,U`b]=oO˷1J1VR;@x՘2Ht!6ɽ\8\'S367tkl SPVأ~\N-(; mjksOfjcA=@9C'*B| eXBdR j oMs!F=zo*ikkf7빞끛Y^#i[.. e""ҿ~A~/#]@ R* XśsTG$ɨ(<#5ЌT?͉ym<[=(xZ}W#N awpQML`KqSFMRdSijg|۹I͡ Z}+c^ԄhE^UxG4c *IO&'5)1?sHf5[CP 2c+j]3eYgt,@+g-OJ`eLa1CYZn]3*~:g("褿[12n2kYL_|.My/y/ ({F$i=|KA,%}[қdFYg1iFUOQD>go]\zWbT=).xY"IUf8˂Z ,CHX|iz6)X0Ovث;J FalpF}b@`fQ={Ɂi <5Pŏ._px[@pxh82P~;GMtM-v@]ܠ.߰ >K94}6. /n:&"O'"'G>P%RWV~1KtMt1jDUlP%U |^ӿ]C` _i/{җVQdz16zfudO?P{f_?Azf_? o#j3URmUqmfh7sIb_.`8G.篘B5nJM47sIb4@4FXb)&PqFHGI>.Ql/LVb% ) .(?Vr 飸UUzX!e|Ul_6~yUY: )5WJ/?GpG~/_17lz DXǭpu}^kسAo Bo l84^/  @!OMG۶2t]ȡcݶ5z-o;3`c6FzAmǸm` cHdIDATx] c۶H阒]Kd%٫뺮lmw]|pXm-}X:+iĆQV9\e'AtOS:72YsxMہB&z>nC@r@*aӝ%MFDDDDDDTߖH,ERUn ب\f͹:}4ᦋ{)n[ ̰E K Y Dۇ- +Kl=ӃL`љ|%n a N#5 (4?EDDDD\oWFfq;\E_,W!%zE!F¶. (USHQ0dw)T 8#p,xBK *xXEe K솎%mKX~sFE~tdcaI1Af4dHcGSB`0wev`"{ .GDDDD,dO6k"qkMefS_UKŌ&g~>n H}) LF%8()r![4统qQk0m[Le_70@>1 X0AZVcEV Ltk3EJ44ZﮊN`rt>`˥  AHBLH@cUq=jcM2sJCLiR NQ0=Yi-|4V ]]B^ޞ_H$<$ a=d@ (ZAp_}~s:N{DC>m^ƒS&, ;N&B} <_AB]HuN(B0{h1IKDs j' M8.ӫ1h3df}mq nU{Lo z \=?@ ((e|=ơ麄Ci1r<|OO; `HpQyzԈuZVƲ!)5mC2Lyg;֑RjWa@@VL&Wru=Z ̥=U5} 7;b(nP&sk48ͥ01U Wvk18dqTՌE]qH8 GF K'rOrŗ6"fpT^3c"nMم-/W=tJ,X){P Rm|K>mX8v5h<_{ꘀYF|&_G;&>^W⁃&K(81EB@F&;"L'wfwE-6o&/̫'Xe,>~ee|A=) dQ`}P[KN˂/~)O[dO=3El5'Y$?7mTzզ.\.` WE"""""v)V#]L;zqJr²[\-tҽ5 @ͷϟnT@+;cQhC*TڙAVr?f? Q1T`} Hk,{VZˋTϛ?I̯uQKLMe͆~qym09 S;j5 iQ]7k0UޭGkX3#lY_Цxj޶9`# M [z KuO_z˿Dܭ*kOJ(7n\e ITƨl/U߶uw.~;#r.8 o# 5Lh>1i pVM?/u70 X@L+M+{Fkt{ŧ890`. ĀCR+\/t R; TӲ]aL|efđ >ۣG|P`P8C1K՛A̍<2ۂKrl@L L 8@E>`nPNԍ,p EƆZF lÎ; F7Ȯ; swSz)g7{rsSgȋ(߄~AWytX$NVR_<6p.O8O[OdDk>_OO}JSdmV?W(_m j~=H IԁF>T/{*]IGJ@iqamNF|Q50+ ES8:v`p~vj:Bp96oys% |@H]+@t]Wk}}7FʮrAB\m-_2PY8xՎN.h~@+7z5t_//?0S>)zi0n/B`{DW#`Bo[,gFVЁpP߾C]Bz ,XXfԃA:H k7dZ9oc}o]0vd:R]0ve]刈jу| ? +(OǍ+ #ysߍnpFru<.HȺotM3h}߆P}˗vP}mǀ?WZ@}@@FDl%tEXtdate:create2015-03-11T14:59:12+00:000i%tEXtdate:modify2015-03-11T14:59:12+00:00AtEXtSoftwareAdobe ImageReadyqe<IENDB`login-security/img/header.svg000064400000005776147207020750012271 0ustar00 login-security/views/onboarding/standalone-header.php000064400000003325147207020750017076 0ustar00

full Wordfence plugin includes all of the features in this plugin as well as a full-featured WordPress firewall, a security scanner, live traffic, and more. The standard installation includes a robust set of free features that can be upgraded via a Premium license key.', 'wordfence'), 'https://wordpress.org/plugins/wordfence/'); ?>

login-security/views/user/grace-period-toggle.php000064400000001020147207020750016162 0ustar00 login-security/views/common/revoke-grace-period.php000064400000003574147207020750016526 0ustar00
login-security/views/common/modal-prompt.php000064400000005317147207020750015304 0ustar00 , 'label' =>
login-security/views/email/login-verification.php000064400000002505147207020750016254 0ustar00 array())); ?>

' . esc_html__('Request Time:', 'wordfence') . ' ' . esc_html(\WordfenceLS\Controller_Time::format_local_time('F j, Y h:i:s A')); ?>
' . esc_html__('IP:', 'wordfence') . ' ' . esc_html($ip); ?>

will be valid for 15 minutes from the time it was sent. If you did not attempt this login, please change your password immediately.', 'wordfence'), array('b'=>array())); ?>

Verify and Log In', 'wordfence'), esc_url($verificationURL)), array('a' => array('href' => array()), 'strong' => array())); ?>login-security/views/page/page.php000064400000002732147207020750013227 0ustar00 Model_Tab instance, 'title' => Title instance, 'content' => HTML content). Required. */ ?>
can_manage_settings() && !\WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_DISMISSED_FRESH_INSTALL_MODAL) && !WORDFENCE_LS_FROM_CORE) { echo \WordfenceLS\Model_View::create('onboarding/standalone-header')->render(); } ?>
$tabs, ))->render(); ?>
$s['title'], ))->render(); echo $s['content']; ?>
login-security/views/page/manage.php000064400000013470147207020750013544 0ustar00ID == $user->ID) { $ownAccount = true; } $enabled = \WordfenceLS\Controller_Users::shared()->has_2fa_active($user); $requires2fa = \WordfenceLS\Controller_Users::shared()->requires_2fa($user, $inGracePeriod, $requiredAt); $lockedOut = $requires2fa && !$enabled; ?>

click here.', 'wordfence'), \WordfenceLS\Controller_Support::esc_supportURL(\WordfenceLS\Controller_Support::ITEM_MODULE_LOGIN_SECURITY_2FA)), array('a'=>array('href'=>array(), 'target'=>array(), 'rel'=>array()))); ?>

%s', 'wordfence'), get_avatar($user->ID, 16, '', $user->user_login), \WordfenceLS\Text\Model_HTML::esc_html($user->user_login) . ($ownAccount ? ' ' . __('(you)', 'wordfence') : '')), array('span'=>array('class'=>array()))); ?>
= 0 ? '+' . $offset : $offset); } ?> can_manage_settings()): ?>

UTC ()

is_ntp_enabled()) { echo esc_html__('Corrected Time (NTP):', 'wordfence') . ' ' . date('Y-m-d H:i:s', $correctedTime) . ' UTC (' . \WordfenceLS\Controller_Time::format_local_time('Y-m-d H:i:s', $correctedTime) . ' ' . $tz . ')
'; } else if (WORDFENCE_LS_FROM_CORE && $correctedTime != $time) { echo esc_html__('Corrected Time (WF):', 'wordfence') . ' ' . date('Y-m-d H:i:s', $correctedTime) . ' UTC (' . \WordfenceLS\Controller_Time::format_local_time('Y-m-d H:i:s', $correctedTime) . ' ' . $tz . ')
'; } ?> ip()); if (\WordfenceLS\Controller_Whitelist::shared()->is_whitelisted(\WordfenceLS\Model_Request::current()->ip())) { echo ' (' . esc_html__('allowlisted', 'wordfence') . ')'; } ?>

login-security/views/page/permission-denied.php000064400000000366147207020750015732 0ustar00

login-security/views/page/settings.php000064400000002743147207020750014155 0ustar00
\WordfenceLS\Controller_Users::shared()->get_detailed_user_counts_if_enabled(), ))->render(); ?>
$hasWoocommerce ))->render(); ?>
login-security/views/page/manage-embedded.php000064400000007501147207020750015271 0ustar00has_2fa_active($user); $requires2fa = \WordfenceLS\Controller_Users::shared()->requires_2fa($user, $inGracePeriod, $requiredAt); $lockedOut = $requires2fa && !$enabled; $containerClasses = 'wfls-flex-row ' . ($stacked ? 'wfls-flex-row-wrapped' : 'wfls-flex-row-wrappable wfls-flex-row-equal-heights'); $columnClasses = 'wfls-flex-row wfls-flex-item-xs-100 ' . ($stacked ? '' : 'wfls-flex-row-equal-heights'); ?> renderInlineIfNotEnqueued(); ?>
class="stacked" >

click here.', 'wordfence'), \WordfenceLS\Controller_Support::esc_supportURL(\WordfenceLS\Controller_Support::ITEM_MODULE_LOGIN_SECURITY_2FA)), array('a'=>array('href'=>array(), 'target'=>array(), 'rel'=>array()))); ?>

login-security/views/page/section-title.php000064400000002043147207020750015071 0ustar00

title); ?>

helpURL !== null && $title->helpLink !== null): ?> helpLink); ?>
login-security/views/page/tabbar.php000064400000001365147207020750013547 0ustar00
  • a; if (!preg_match('/^https?:\/\//i', $a)) { $a = '#top#' . urlencode($a); } ?>
  • tabTitle); ?>
login-security/views/page/role.php000064400000004243147207020750013253 0ustar00

()

User Required Date
user_login) ?> required_at): ?> required_at)) ?>
1): ?> "> ">
login-security/views/manage/activate.php000064400000017266147207020750014437 0ustar00get_user(); $recovery = $initializationData->get_recovery_codes(); ?>

    user_login) . "\r\n"; $recoveryCodeFileContents .= "\r\n" . sprintf(__('Each line of %d letters and numbers is a single recovery code, with optional spaces for readability. To use a recovery code, after entering your username and password, enter the code like "1234 5678 90AB CDEF" at the 2FA prompt. If your site has a custom login prompt and does not show a 2FA prompt, you can use the single-step method by entering your password and the code together in the Password field, like "mypassword1234 5678 90AB CDEF". Your recovery codes are:', 'wordfence'), \WordfenceLS\Model_Crypto::strlen($recovery[0]) * 2) . "\r\n\r\n"; foreach ($recovery as $c) { $hex = bin2hex($c); $blocks = str_split($hex, 4); echo '
  • ' . implode(' ', $blocks) . '
  • '; $recoveryCodeFileContents .= implode(' ', $blocks) . "\r\n"; } ?>


login-security/views/manage/regenerate.php000064400000013710147207020750014746 0ustar00

login-security/views/manage/grace-period.php000064400000004267147207020750015175 0ustar00ID == $user->ID) { $ownAccount = true; } $defaultGracePeriod = \WordfenceLS\Controller_Settings::shared()->get_user_2fa_grace_period(); $hasGracePeriod = $defaultGracePeriod > 0; ?>

%s', 'wordfence'), array('strong'=>array())), $requiredDateFormatted) : sprintf(wp_kses(__('Two-factor authentication will be required for user %s beginning %s.', 'wordfence'), array('strong'=>array())), esc_html($user->user_login), $requiredDateFormatted) ?>

has_revokable_grace_period($user)): ?> $user ))->render() ?>

$user, 'gracePeriod' => $gracePeriod, 'defaultGracePeriod' => $defaultGracePeriod ))->render() ?>
login-security/views/manage/deactivate.php000064400000007065147207020750014744 0ustar00ID == $user->ID) { $ownAccount = true; } ?>

%s. You may deactivate it by clicking the button below.', 'wordfence'), esc_html($user->user_login)), array('strong'=>array())); } ?>

login-security/views/manage/code.php000064400000003210147207020750013531 0ustar00

login-security/views/options/option-toggled-multiple.php000064400000005151147207020750017654 0ustar00 string