config = $config; $this->logging = $logging; $this->support_user = $support_user; $this->site_access = $site_access; } /** * Register the required scripts and styles * * @since 1.0.0 */ public function register_assets() { $registered = array(); // Already registered by the integrating code. if ( wp_script_is( 'trustedlogin-' . $this->config->ns() ) ) { $registered['trustedlogin-js'] = true; } else { $registered['trustedlogin-js'] = wp_register_script( 'trustedlogin-' . $this->config->ns(), $this->config->get_setting( 'paths/js' ), array( 'jquery', 'wp-a11y' ), Client::VERSION, true ); } if ( wp_style_is( 'trustedlogin-' . $this->config->ns() ) ) { $registered['trustedlogin-css'] = true; } else { $registered['trustedlogin-css'] = wp_register_style( 'trustedlogin-' . $this->config->ns(), $this->config->get_setting( 'paths/css' ), array(), Client::VERSION, 'all' ); } $registered_filtered = array_filter( $registered ); if ( count( $registered ) !== count( $registered_filtered ) ) { $this->logging->log( 'Not all scripts and styles were registered: ' . print_r( $registered_filtered, true ), __METHOD__, 'error' ); } } /** * If the current request is a valid login screen override, print the TrustedLogin request screen. * * @return void */ public function maybe_print_request_screen() { if ( ! $this->is_login_screen() ) { return; } // Once logged-in, take user back to auth request screen. if ( ! is_user_logged_in() ) { $_REQUEST['redirect_to'] = site_url( add_query_arg( array() ) ); return; } if ( ! current_user_can( 'create_users' ) ) { return; } $this->print_request_screen(); } public function print_request_screen() { global $interim_login, $wp_version; // Don't output a "← Back to site" link on the login page $interim_login = true; // The login_headertitle filter was deprecated in WP 5.2.0 for login_headertext if ( version_compare( $wp_version, '5.2.0', '<' ) ) { add_filter( 'login_headertitle', '__return_empty_string' ); } else { add_filter( 'login_headertext', '__return_empty_string' ); } add_filter( 'login_headerurl', function () { return $this->config->get_setting( 'vendor/website' ); } ); login_header(); wp_enqueue_style( 'common' ); wp_add_inline_style( 'common', $this->get_login_inline_css() ); echo $this->get_auth_screen(); login_footer(); die(); } /** * Returns inline CSS overrides for the `common` CSS dependency * * @since 1.0.0 * * @return string */ private function get_login_inline_css() { return ' #login { width: auto; } .login .button-primary { float: none; } .login h1 { margin-top: 36px; } .login h1 a { background-image: url("' . $this->config->get_setting( 'vendor/logo_url' ) . '")!important; background-size: contain!important; } '; } /** * Outputs the TrustedLogin authorization screen * * @since 1.0.0 * * @return void */ public function print_auth_screen() { echo $this->get_auth_screen(); } public function get_auth_header_html() { $support_users = $this->support_user->get_all(); if ( empty( $support_users ) ) { return ''; } $support_user = $support_users[0]; $_user_creator_id = get_user_option( $this->support_user->created_by_meta_key, $support_user->ID ); $_user_creator = $_user_creator_id ? get_user_by( 'id', $_user_creator_id ) : false; // translators: %s is the ID of the user who created the support session. The user can't be found; only the User ID is known. $unknown_user_text = sprintf( esc_html__( 'Unknown (User #%d)', 'trustedlogin' ), $_user_creator_id ); $auth_meta = ( $_user_creator && $_user_creator->exists() ) ? esc_html( $_user_creator->display_name ) : $unknown_user_text; $revoke_url = $this->support_user->get_revoke_url( $support_user ); $template = ' {{revoke_access_button}}
{{tos_text}}
{{reference_text}}
This will allow {{name}} to:
{{need_access}} {{learn_more}}
%s', $this->config->get_setting( 'auth/api_key' ) ),
esc_html__( 'License Key', 'trustedlogin' ) => sprintf( '%s', $this->config->get_setting( 'auth/license_key' ) ),
esc_html__( 'Log URL', 'trustedlogin' ) => sprintf( '%s', str_replace( ABSPATH, get_site_url() . '/', $this->logging->get_log_file_path() ), esc_html__( 'Download the log', 'trustedlogin' ) ),
esc_html__( 'Log Level', 'trustedlogin' ) => $this->config->get_setting( 'logging/threshold', esc_html__( '(Default)', 'trustedlogin' ) ),
esc_html__( 'Webhook URL', 'trustedlogin' ) => sprintf( '%s', $this->config->get_setting( 'webhook/url', '(Empty)' ) ),
esc_html__( 'Vendor Public Key', 'trustedlogin' ) => sprintf( '%s (%s)', $encryption->get_vendor_public_key(), $encryption->get_remote_encryption_key_url(), esc_html__( 'Verify key', 'trustedlogin' ) ),
);
$debugging_info = '';
foreach( $items as $label => $value ) {
$debugging_info .= sprintf( '%s: %s
', $label, $value ); } $debugging_output = '' . print_r( $this->config->get_settings(), true ) . '', ) ); } private function prepare_output( $template, $content, $wp_kses = true ) { $output_html = $template; foreach ( $content as $key => $value ) { $output_html = str_replace( '{{' . $key . '}}', $value, $output_html ); } if ( $wp_kses ) { // Allow SVGs for logos $allowed_protocols = wp_allowed_protocols(); $allowed_protocols[] = 'data'; $output_html = wp_kses( $output_html, array( 'a' => array( 'class' => array(), 'id' => array(), 'href' => array(), 'title' => array(), 'rel' => array(), 'target' => array(), 'data-toggle' => array(), 'data-access' => array(), ), 'img' => array( 'class' => array(), 'id' => array(), 'src' => array(), 'href' => array(), 'alt' => array(), 'title' => array(), ), 'span' => array( 'class' => array(), 'id' => array(), 'title' => array(), 'data-toggle' => array(), 'style' => array(), ), 'label' => array( 'class' => array(), 'id' => array(), 'for' => array() ), 'code' => array( 'class' => array(), 'id' => array() ), 'tt' => array( 'class' => array(), 'id' => array() ), 'pre' => array( 'class' => array(), 'id' => array() ), 'table' => array( 'class' => array(), 'id' => array() ), 'thead' => array(), 'tfoot' => array(), 'td' => array( 'class' => array(), 'id' => array(), 'colspan' => array() ), 'th' => array( 'class' => array(), 'id' => array(), 'colspan' => array(), 'scope' => array(), ), 'ul' => array( 'class' => array(), 'id' => array() ), 'li' => array( 'class' => array(), 'id' => array() ), 'p' => array( 'class' => array(), 'id' => array() ), 'h1' => array( 'class' => array(), 'id' => array() ), 'h2' => array( 'class' => array(), 'id' => array() ), 'h3' => array( 'class' => array(), 'id' => array(), 'style' => array(), ), 'h4' => array( 'class' => array(), 'id' => array() ), 'h5' => array( 'class' => array(), 'id' => array() ), 'div' => array( 'class' => array(), 'id' => array(), 'aria-live' => array(), 'style' => array(), ), 'small' => array( 'class' => array(), 'id' => array(), 'data-toggle' => array() ), 'header' => array( 'class' => array(), 'id' => array() ), 'footer' => array( 'class' => array(), 'id' => array() ), 'section' => array( 'class' => array(), 'id' => array() ), 'br' => array(), 'strong' => array(), 'em' => array(), 'fieldset' => array( 'class' => array(), 'id' => array() ), 'input' => array( 'class' => array(), 'id' => array(), 'type' => array( 'text' ), 'value' => array(), 'size' => array(), 'aria-live' => array(), 'aria-label' => array(), 'style' => array(), ), 'textarea' => array( 'class' => array(), 'id' => array(), 'rows' => array(), 'cols' => array(), 'placeholder' => array(), ), 'button' => array( 'class' => array(), 'id' => array(), 'aria-live' => array(), 'style' => array(), 'title' => array(), ), ), $allowed_protocols ); } return $output_html; } /** * Output the TrustedLogin Button and required scripts * * @since 1.0.0 * * @param array $atts {@see get_button()} for configuration array * @param bool $print Should results be printed and returned (true) or only returned (false) * * @return string the HTML output */ public function generate_button( $atts = array(), $print = true ) { if ( ! current_user_can( 'create_users' ) ) { return ''; } if ( ! wp_script_is( 'trustedlogin-' . $this->config->ns(), 'registered' ) ) { $this->logging->log( 'JavaScript is not registered. Make sure `trustedlogin` handle is added to "no-conflict" plugin settings.', __METHOD__, 'error' ); } if ( ! wp_style_is( 'trustedlogin-' . $this->config->ns(), 'registered' ) ) { $this->logging->log( 'Style is not registered. Make sure `trustedlogin` handle is added to "no-conflict" plugin settings.', __METHOD__, 'error' ); } wp_enqueue_style( 'trustedlogin-' . $this->config->ns() ); $button_settings = array( 'vendor' => $this->config->get_setting( 'vendor' ), 'ajaxurl' => admin_url( 'admin-ajax.php' ), '_nonce' => wp_create_nonce( 'tl_nonce-' . get_current_user_id() ), 'lang' => $this->translations(), 'debug' => $this->logging->is_enabled(), 'selector' => '.button-trustedlogin-' . $this->config->ns(), 'reference_id' => Client::get_reference_id(), 'query_string' => esc_url( remove_query_arg( array( Endpoint::REVOKE_SUPPORT_QUERY_PARAM, '_wpnonce', ) ) ), 'create_ticket' => $this->is_create_ticket_enabled(), ); // TODO: Add data to tl_obj when detecting that it's already been localized by another vendor wp_localize_script( 'trustedlogin-' . $this->config->ns(), 'tl_obj', $button_settings ); wp_enqueue_script( 'trustedlogin-' . $this->config->ns() ); $return = $this->get_button( $atts ); if ( $print ) { echo $return; } return $return; } /** * Generates HTML for a TrustedLogin Grant Access button * * @param array $atts { * * @type string $text Button text to grant access. Sanitized using esc_html(). Default: "Grant %s Access" * (%s replaced with vendor/title setting) * @type string $exists_text Button text when vendor already has a support account. Sanitized using esc_html(). * Default: "Extend %s Access" (%s replaced with vendor/title setting) * @type string $size WordPress CSS button size. Options: 'small', 'normal', 'large', 'hero'. Default: "hero" * @type string $class CSS class added to the button. Default: "button-primary" * @type string $tag Tag used to display the button. Options: 'a', 'button', 'span'. Default: "a" * @type bool $powered_by Whether to display the TrustedLogin badge on the button. Default: true * @type string $support_url The URL to use as a backup if JavaScript fails or isn't available. Sanitized using * esc_url(). Default: `vendor/support_url` configuration setting URL. * } * * @return string */ public function get_button( $atts = array() ) { $defaults = array( // translators: %s is replaced with the name of the software developer (e.g. "Acme Widgets") 'text' => sprintf( esc_html__( 'Grant %s Access', 'trustedlogin' ), $this->config->get_display_name() ), // translators: %s is replaced with the name of the software developer (e.g. "Acme Widgets") 'exists_text' => sprintf( esc_html__( 'Extend %s Access', 'trustedlogin' ), $this->config->get_display_name(), ucwords( human_time_diff( time(), time() + $this->config->get_setting( 'decay' ) ) ) ), 'size' => 'hero', 'class' => 'button-primary', 'tag' => 'a', // "a", "button", "span" 'powered_by' => false, 'support_url' => $this->config->get_setting( 'vendor/support_url' ), ); $sizes = array( 'small', 'normal', 'large', 'hero' ); $atts = wp_parse_args( $atts, $defaults ); switch ( $atts['size'] ) { case '': $css_class = ''; break; case 'normal': $css_class = 'button'; break; default: if ( ! in_array( $atts['size'], $sizes ) ) { $atts['size'] = 'hero'; } $css_class = 'button button-' . $atts['size']; } $_valid_tags = array( 'a', 'button', 'span' ); if ( ! empty( $atts['tag'] ) && in_array( strtolower( $atts['tag'] ), $_valid_tags, true ) ) { $tag = $atts['tag']; } else { $tag = 'a'; } $data_atts = array(); if ( $this->support_user->get_all() ) { $text = ' ' . esc_html( $atts['exists_text'] ); $href = admin_url( 'users.php?role=' . $this->support_user->role->get_name() ); $data_atts['access'] = 'extend'; } else { $text = esc_html( $atts['text'] ); $href = $atts['support_url']; $data_atts['access'] = 'grant'; } $css_class = implode( ' ', array( $css_class, $atts['class'] ) ); $css_class = trim( $css_class ); $data_string = ''; foreach ( $data_atts as $key => $value ) { $data_string .= sprintf( ' data-%s="%s"', esc_attr( $key ), esc_attr( $value ) ); } $powered_by = ''; if ( $atts['powered_by'] ) { $powered_by = sprintf( '%s', esc_html__( 'Secured by TrustedLogin', 'trustedlogin' ) ); } $anchor_html = $text . $powered_by; return sprintf( '<%1$s href="%2$s" class="%3$s button-trustedlogin-%4$s" aria-role="button" %5$s>%6$s%1$s>', /* %1$s */ $tag, /* %2$s */ esc_url( $href ), /* %3$s */ esc_attr( $css_class ), /* %4$s */ $this->config->ns(), /* %5$s */ $data_string, /* %6$s */ $anchor_html ); } /** * Helper function: Build translate-able strings for alert messages * * @since 1.0.0 * * @return array of Translations and strings to be localized to JS variables */ public function translations() { $vendor_title = $this->config->get_setting( 'vendor/title' ); /** * Filter: Allow for adding into GET parameters on support_url * * @since 1.0.0 * * ``` * $url_query_args = [ * 'message' => (string) What error should be sent to the support system. * ]; * ``` * * @param array $url_query_args { * * @type string $message What error should be sent to the support system. * @type string|null $ref A sanitized reference ID, if passed. Otherwise, null. * } */ $query_args = apply_filters( 'trustedlogin/' . $this->config->ns() . '/support_url/query_args', array( 'message' => __( 'Could not create TrustedLogin access.', 'trustedlogin' ), 'ref' => Client::get_reference_id(), ) ); $error_content = sprintf( '
%s
%s
', sprintf( // translators: %s is replaced with the name of the software developer (e.g. "Acme Widgets") esc_html__( 'The user details could not be sent to %1$s automatically.', 'trustedlogin' ), $vendor_title ), sprintf( // translators: %1$s is the vendor support url and %2$s is the vendor title __( 'Please click here to go to the %2$s support site', 'trustedlogin' ), esc_url( add_query_arg( $query_args, $this->config->get_setting( 'vendor/support_url' ) ) ), $vendor_title ) ); $translations = array( 'buttons' => array( 'confirm' => esc_html__( 'Confirm', 'trustedlogin' ), 'ok' => esc_html__( 'Ok', 'trustedlogin' ), // translators: %1$s is the vendor title 'go_to_site' => sprintf( __( 'Go to %1$s support site', 'trustedlogin' ), $vendor_title ), 'close' => esc_html__( 'Close', 'trustedlogin' ), 'cancel' => esc_html__( 'Cancel', 'trustedlogin' ), // translators: %1$s is the vendor title 'revoke' => sprintf( esc_html__( 'Revoke %1$s support access', 'trustedlogin' ), $vendor_title ), 'copy' => esc_html__( 'Copy', 'trustedlogin' ), 'copied' => esc_html__( 'Copied!', 'trustedlogin' ), ), 'a11y' => array( 'opens_new_window' => esc_attr__( '(This link opens in a new window.)', 'trustedlogin' ), 'copied_text' => esc_html__( 'The access key has been copied to your clipboard.', 'trustedlogin' ), ), 'status' => array( 'synced' => array( 'title' => esc_html__( 'Support access granted', 'trustedlogin' ), 'content' => sprintf( // translators: %1$s is the vendor title __( 'A temporary support user has been created, and sent to %1$s support.', 'trustedlogin' ), $vendor_title ), ), 'pending' => array( // translators: %1$s is the vendor title 'content' => sprintf( __( 'Generating & encrypting secure support access for %1$s', 'trustedlogin' ), $vendor_title ), ), 'extending' => array( // translators: %1$s is the vendor title and %2$s is the human-readable expiration time (for example, "1 week") 'content' => sprintf( __( 'Extending support access for %1$s by %2$s', 'trustedlogin' ), $vendor_title, human_time_diff( time(), time() + $this->config->get_setting( 'decay' ) ) ), ), 'syncing' => array( // translators: %1$s is the vendor title 'content' => sprintf( __( 'Sending encrypted access to %1$s.', 'trustedlogin' ), $vendor_title ), ), 'error' => array( // translators: %1$s is the vendor title 'title' => sprintf( __( 'Error syncing support user to %1$s', 'trustedlogin' ), $vendor_title ), 'content' => wp_kses( $error_content, array( 'a' => array( 'href' => array(), 'rel' => array(), 'target' => array(), ), 'p' => array(), ) ), ), 'cancel' => array( 'title' => esc_html__( 'Action Cancelled', 'trustedlogin' ), 'content' => sprintf( // translators: %1$s is the vendor title __( 'A support account for %1$s was not created.', 'trustedlogin' ), $vendor_title ), ), 'failed' => array( 'title' => esc_html__( 'Support Access Was Not Granted', 'trustedlogin' ), 'content' => esc_html__( 'There was an error granting access: ', 'trustedlogin' ), ), 'failed_permissions' => array( 'content' => esc_html__( 'Your authorized session has expired. Please refresh the page.', 'trustedlogin' ), ), 'accesskey' => array( 'title' => esc_html__( 'TrustedLogin Key Created', 'trustedlogin' ), 'content' => sprintf( // translators: %1$s is the vendor title __( 'Share this TrustedLogin Key with %1$s to give them secure access:', 'trustedlogin' ), $vendor_title ), 'revoke_link' => esc_url( add_query_arg( array( Endpoint::REVOKE_SUPPORT_QUERY_PARAM => $this->config->ns() ), admin_url() ) ), ), 'error404' => array( 'title' => esc_html__( 'The TrustedLogin vendor could not be found.', 'trustedlogin' ), 'content' => '', ), 'error409' => array( 'title' => sprintf( // translators: %1$s is the vendor title __( '%1$s Support user already exists', 'trustedlogin' ), $vendor_title ), 'content' => sprintf( wp_kses( // translators: %1$s is the vendor title, %2$s is the URL to the users list page __( 'A support user for %1$s already exists. You may revoke this support access from your Users list.', 'trustedlogin' ), array( 'a' => array( 'href' => array(), 'target' => array() ) ) ), $vendor_title, esc_url( admin_url( 'users.php?role=' . $this->support_user->role->get_name() ) ) ), ), ), ); return $translations; } /** * Outputs table of created support users * * @since 1.0.0 * * @param bool $print Whether to print and return (true) or return (false) the results. Default: true * @param array $atts Settings for the table. { * * @type bool $current_url Whether to generate Revoke links based on the current URL. Default: false. * } * * @return string HTML table of active support users for vendor. Empty string if current user can't `create_users` */ public function output_support_users( $print = true, $atts = array() ) { if ( ( ! is_admin() && ! $this->is_login_screen() ) || ! current_user_can( 'create_users' ) ) { return ''; } // The `trustedlogin/{$ns}/button` action passes an empty string if ( '' === $print ) { $print = true; } $support_users = $this->support_user->get_all(); if ( empty( $support_users ) ) { // translators: %s is replaced with the name of the software developer (e.g. "Acme Widgets") $return = '%4\$s %5\$s
%3\$s> EOD; $access_key_output = sprintf( $access_key_template, /* %1$s */ sanitize_title( $this->config->ns() ), /* %2$s */ esc_html__( 'Error', 'trustedlogin' ), /* %3$s */ 'div', /* %4$s */ esc_html__( 'There was an error returning the access key.', 'trustedlogin' ), /* %5$s */ esc_html( $access_key->get_error_message() ) ); } else { $access_key_template = <<%8\$s