Version Description
Download this release
Release Info
Developer | jipmoors |
Plugin | Yoast SEO |
Version | 7.0 |
Comparing to | |
See all releases |
Version 7.0
- admin/ajax.php +390 -0
- admin/ajax/class-recalculate-scores-ajax.php +119 -0
- admin/ajax/class-shortcode-filter.php +39 -0
- admin/ajax/class-yoast-dismissable-notice.php +80 -0
- admin/ajax/class-yoast-onpage-ajax.php +38 -0
- admin/ajax/class-yoast-plugin-conflict-ajax.php +105 -0
- admin/banner/class-admin-banner-renderer.php +48 -0
- admin/banner/class-admin-banner-sidebar-renderer.php +60 -0
- admin/banner/class-admin-banner-sidebar.php +388 -0
- admin/banner/class-admin-banner-spot-renderer.php +37 -0
- admin/banner/class-admin-banner-spot.php +118 -0
- admin/banner/class-admin-banner.php +87 -0
- admin/capabilities/class-abstract-capability-manager.php +82 -0
- admin/capabilities/class-capability-manager-factory.php +30 -0
- admin/capabilities/class-capability-manager-integration.php +113 -0
- admin/capabilities/class-capability-manager-vip.php +70 -0
- admin/capabilities/class-capability-manager-wp.php +48 -0
- admin/capabilities/class-capability-manager.php +35 -0
- admin/capabilities/class-capability-utils.php +52 -0
- admin/capabilities/class-register-capabilities.php +40 -0
- admin/class-admin-asset-dev-server-location.php +80 -0
- admin/class-admin-asset-location.php +20 -0
- admin/class-admin-asset-manager.php +493 -0
- admin/class-admin-asset-seo-location.php +95 -0
- admin/class-admin-help-panel.php +92 -0
- admin/class-admin-init.php +665 -0
- admin/class-admin-user-profile.php +86 -0
- admin/class-admin-utils.php +68 -0
- admin/class-admin.php +539 -0
- admin/class-asset.php +170 -0
- admin/class-bulk-description-editor-list-table.php +75 -0
- admin/class-bulk-editor-list-table.php +1019 -0
- admin/class-bulk-title-editor-list-table.php +85 -0
- admin/class-collector.php +46 -0
- admin/class-config.php +145 -0
- admin/class-cornerstone-field.php +55 -0
- admin/class-cornerstone.php +90 -0
- admin/class-customizer.php +253 -0
- admin/class-database-proxy.php +191 -0
- admin/class-export.php +285 -0
- admin/class-extension-manager.php +138 -0
- admin/class-extension.php +81 -0
- admin/class-extensions.php +106 -0
- admin/class-help-center-item.php +94 -0
- admin/class-help-center.php +254 -0
- admin/class-import-aioseo.php +107 -0
- admin/class-import-external.php +113 -0
- admin/class-import-jetpack.php +31 -0
- admin/class-import-seopressor.php +189 -0
- admin/class-import-ultimate-seo.php +54 -0
- admin/class-import-woothemes-seo.php +149 -0
- admin/class-import-wpseo.php +208 -0
- admin/class-import.php +205 -0
- admin/class-license-page-manager.php +209 -0
- admin/class-meta-columns.php +752 -0
- admin/class-meta-storage.php +109 -0
- admin/class-meta-table-accessible.php +101 -0
- admin/class-option-tab.php +89 -0
- admin/class-option-tabs-formatter.php +57 -0
- admin/class-option-tabs.php +105 -0
- admin/class-plugin-availability.php +342 -0
- admin/class-plugin-compatibility.php +107 -0
- admin/class-plugin-conflict.php +161 -0
- admin/class-premium-popup.php +97 -0
- admin/class-premium-upsell-admin-block.php +144 -0
- admin/class-primary-term-admin.php +232 -0
- admin/class-product-upsell-notice.php +201 -0
- admin/class-recalculate-scores.php +52 -0
- admin/class-remote-request.php +128 -0
- admin/class-social-admin.php +241 -0
- admin/class-social-facebook-form.php +273 -0
- admin/class-social-facebook.php +211 -0
- admin/class-suggested-plugins.php +157 -0
- admin/class-yoast-alerts.php +258 -0
- admin/class-yoast-columns.php +42 -0
- admin/class-yoast-dashboard-widget.php +153 -0
- admin/class-yoast-form.php +669 -0
- admin/class-yoast-notification-center.php +598 -0
- admin/class-yoast-notification.php +330 -0
- admin/class-yoast-plugin-conflict.php +334 -0
- admin/config-ui/class-configuration-components.php +71 -0
- admin/config-ui/class-configuration-endpoint.php +78 -0
- admin/config-ui/class-configuration-options-adapter.php +197 -0
- admin/config-ui/class-configuration-page.php +262 -0
- admin/config-ui/class-configuration-service.php +160 -0
- admin/config-ui/class-configuration-storage.php +198 -0
- admin/config-ui/class-configuration-structure.php +99 -0
- admin/config-ui/class-configuration-translations.php +53 -0
- admin/config-ui/components/class-component-configuration-choices.php +96 -0
- admin/config-ui/components/class-component-connect-google-search-console.php +149 -0
- admin/config-ui/components/class-component-mailchimp-signup.php +79 -0
- admin/config-ui/components/class-component-suggestions.php +106 -0
- admin/config-ui/components/interface-component.php +33 -0
- admin/config-ui/factories/class-factory-post-type.php +68 -0
- admin/config-ui/fields/class-field-choice-post-type.php +79 -0
- admin/config-ui/fields/class-field-choice.php +40 -0
- admin/config-ui/fields/class-field-company-logo.php +28 -0
- admin/config-ui/fields/class-field-company-name.php +28 -0
- admin/config-ui/fields/class-field-company-or-person.php +32 -0
- admin/config-ui/fields/class-field-configuration-choices.php +41 -0
- admin/config-ui/fields/class-field-connect-google-search-console.php +28 -0
- admin/config-ui/fields/class-field-environment.php +90 -0
- admin/config-ui/fields/class-field-google-search-console-intro.php +35 -0
- admin/config-ui/fields/class-field-mailchimp-signup.php +58 -0
- admin/config-ui/fields/class-field-multiple-authors.php +83 -0
- admin/config-ui/fields/class-field-person-name.php +28 -0
- admin/config-ui/fields/class-field-post-type-visibility.php +25 -0
- admin/config-ui/fields/class-field-profile-url-facebook.php +29 -0
- admin/config-ui/fields/class-field-profile-url-googleplus.php +29 -0
- admin/config-ui/fields/class-field-profile-url-instagram.php +29 -0
- admin/config-ui/fields/class-field-profile-url-linkedin.php +29 -0
- admin/config-ui/fields/class-field-profile-url-myspace.php +29 -0
- admin/config-ui/fields/class-field-profile-url-pinterest.php +29 -0
- admin/config-ui/fields/class-field-profile-url-twitter.php +28 -0
- admin/config-ui/fields/class-field-profile-url-youtube.php +29 -0
- admin/config-ui/fields/class-field-separator.php +43 -0
- admin/config-ui/fields/class-field-site-name.php +58 -0
- admin/config-ui/fields/class-field-site-type.php +37 -0
- admin/config-ui/fields/class-field-social-profiles-intro.php +31 -0
- admin/config-ui/fields/class-field-success-message.php +35 -0
- admin/config-ui/fields/class-field-suggestions.php +42 -0
- admin/config-ui/fields/class-field-title-intro.php +27 -0
- admin/config-ui/fields/class-field-upsell-configuration-service.php +43 -0
- admin/config-ui/fields/class-field-upsell-site-review.php +36 -0
- admin/config-ui/fields/class-field.php +134 -0
- admin/endpoints/class-endpoint-ryte.php +54 -0
- admin/endpoints/class-endpoint-statistics.php +54 -0
- admin/endpoints/class-endpoint.php +24 -0
- admin/filters/class-abstract-post-filter.php +178 -0
- admin/filters/class-cornerstone-filter.php +104 -0
- admin/formatter/class-metabox-formatter.php +203 -0
- admin/formatter/class-post-metabox-formatter.php +211 -0
- admin/formatter/class-term-metabox-formatter.php +136 -0
- admin/formatter/interface-metabox-formatter.php +18 -0
- admin/google_search_console/class-gsc-ajax.php +108 -0
- admin/google_search_console/class-gsc-bulk-action.php +96 -0
- admin/google_search_console/class-gsc-category-filters.php +199 -0
- admin/google_search_console/class-gsc-config.php +22 -0
- admin/google_search_console/class-gsc-count.php +227 -0
- admin/google_search_console/class-gsc-issue.php +89 -0
- admin/google_search_console/class-gsc-issues.php +175 -0
- admin/google_search_console/class-gsc-mapper.php +119 -0
- admin/google_search_console/class-gsc-marker.php +143 -0
- admin/google_search_console/class-gsc-modal.php +56 -0
- admin/google_search_console/class-gsc-platform-tabs.php +95 -0
- admin/google_search_console/class-gsc-service.php +193 -0
- admin/google_search_console/class-gsc-settings.php +102 -0
- admin/google_search_console/class-gsc-table.php +368 -0
- admin/google_search_console/class-gsc.php +311 -0
- admin/google_search_console/views/gsc-display.php +147 -0
- admin/google_search_console/views/gsc-redirect-nopremium.php +23 -0
- admin/import/class-import-aioseo-hooks.php +40 -0
- admin/import/class-import-hooks.php +89 -0
- admin/import/class-import-wpseo-hooks.php +40 -0
- admin/index.php +4 -0
- admin/interface-collection.php +18 -0
- admin/interface-installable.php +17 -0
- admin/links/class-link-cleanup-transient.php +33 -0
- admin/links/class-link-column-count.php +86 -0
- admin/links/class-link-columns.php +253 -0
- admin/links/class-link-compatibility-notifier.php +56 -0
- admin/links/class-link-content-processor.php +133 -0
- admin/links/class-link-extractor.php +46 -0
- admin/links/class-link-factory.php +81 -0
- admin/links/class-link-filter.php +55 -0
- admin/links/class-link-installer.php +50 -0
- admin/links/class-link-internal-lookup.php +23 -0
- admin/links/class-link-notifier.php +125 -0
- admin/links/class-link-query.php +158 -0
- admin/links/class-link-reindex-dashboard.php +222 -0
- admin/links/class-link-reindex-post-endpoint.php +54 -0
- admin/links/class-link-reindex-post-service.php +93 -0
- admin/links/class-link-storage.php +150 -0
- admin/links/class-link-table-accessible-notifier.php +54 -0
- admin/links/class-link-table-accessible.php +101 -0
- admin/links/class-link-type-classifier.php +93 -0
- admin/links/class-link-utils.php +39 -0
- admin/links/class-link-watcher-loader.php +23 -0
- admin/links/class-link-watcher.php +119 -0
- admin/links/class-link.php +62 -0
- admin/listeners/class-listener.php +17 -0
- admin/menu/class-admin-menu.php +270 -0
- admin/menu/class-menu.php +89 -0
- admin/menu/class-network-admin-menu.php +82 -0
- admin/menu/class-submenu-capability-normalize.php +39 -0
- admin/metabox/class-metabox-add-keyword-tab.php +67 -0
- admin/metabox/class-metabox-addon-section.php +37 -0
- admin/metabox/class-metabox-analysis-readability.php +37 -0
- admin/metabox/class-metabox-analysis-seo.php +37 -0
- admin/metabox/class-metabox-editor.php +65 -0
- admin/metabox/class-metabox-form-tab.php +117 -0
- admin/metabox/class-metabox-tab-section.php +147 -0
- admin/metabox/class-metabox.php +1184 -0
- admin/metabox/interface-metabox-analysis.php +31 -0
- admin/metabox/interface-metabox-section.php +20 -0
- admin/metabox/interface-metabox-tab.php +24 -0
- admin/notifiers/class-configuration-notifier.php +154 -0
- admin/onpage/class-onpage-option.php +117 -0
- admin/onpage/class-onpage-request.php +65 -0
- admin/onpage/class-onpage.php +237 -0
- admin/onpage/class-ryte-service.php +100 -0
- admin/pages/dashboard.php +65 -0
- admin/pages/licenses.php +15 -0
- admin/pages/metas.php +79 -0
- admin/pages/network.php +153 -0
- admin/pages/social.php +23 -0
- admin/pages/tools.php +84 -0
- admin/recalculate/class-recalculate-posts.php +149 -0
- admin/recalculate/class-recalculate-terms.php +149 -0
- admin/recalculate/class-recalculate.php +101 -0
- admin/roles/class-abstract-role-manager.php +143 -0
- admin/roles/class-register-roles.php +30 -0
- admin/roles/class-role-manager-factory.php +30 -0
- admin/roles/class-role-manager-vip.php +50 -0
- admin/roles/class-role-manager-wp.php +60 -0
- admin/roles/class-role-manager.php +41 -0
- admin/statistics/class-statistics-integration.php +28 -0
- admin/statistics/class-statistics-service.php +232 -0
- admin/taxonomy/class-taxonomy-columns.php +247 -0
- admin/taxonomy/class-taxonomy-content-fields.php +76 -0
- admin/taxonomy/class-taxonomy-fields-presenter.php +234 -0
- admin/taxonomy/class-taxonomy-fields.php +73 -0
- admin/taxonomy/class-taxonomy-metabox.php +417 -0
- admin/taxonomy/class-taxonomy-settings-fields.php +106 -0
- admin/taxonomy/class-taxonomy-social-fields.php +141 -0
- admin/taxonomy/class-taxonomy.php +344 -0
- admin/tracking/class-tracking-default-data.php +38 -0
- admin/tracking/class-tracking-plugin-data.php +58 -0
- admin/tracking/class-tracking-server-data.php +83 -0
- admin/tracking/class-tracking-theme-data.php +47 -0
- admin/tracking/class-tracking.php +93 -0
- admin/views/class-view-utils.php +103 -0
- admin/views/class-yoast-form-fieldset.php +168 -0
- admin/views/class-yoast-input-select.php +128 -0
- admin/views/form/fieldset.php +24 -0
- admin/views/form/select.php +24 -0
- admin/views/interface-yoast-form-element.php +17 -0
- admin/views/js-templates-primary-term.php +47 -0
- admin/views/licenses.php +271 -0
- admin/views/partial-alerts-errors.php +20 -0
- admin/views/partial-alerts-template.php +69 -0
- admin/views/partial-alerts-warnings.php +20 -0
- admin/views/tabs/dashboard/dashboard.php +51 -0
- admin/views/tabs/dashboard/features.php +154 -0
- admin/views/tabs/dashboard/site-analysis.php +21 -0
- admin/views/tabs/dashboard/webmaster-tools.php +67 -0
- admin/views/tabs/metas/archives.php +146 -0
- admin/views/tabs/metas/breadcrumbs.php +111 -0
- admin/views/tabs/metas/general.php +20 -0
- admin/views/tabs/metas/general/force-rewrite-title.php +23 -0
- admin/views/tabs/metas/general/homepage.php +46 -0
- admin/views/tabs/metas/general/knowledge-graph.php +41 -0
- admin/views/tabs/metas/general/title-separator.php +23 -0
- admin/views/tabs/metas/media.php +46 -0
- admin/views/tabs/metas/post-types.php +82 -0
- admin/views/tabs/metas/rss.php +69 -0
- admin/views/tabs/metas/taxonomies.php +85 -0
- admin/views/tabs/social/accounts.php +36 -0
- admin/views/tabs/social/facebook.php +82 -0
- admin/views/tabs/social/google.php +25 -0
- admin/views/tabs/social/pinterest.php +38 -0
- admin/views/tabs/social/twitterbox.php +28 -0
- admin/views/tabs/tool/import-seo.php +52 -0
- admin/views/tabs/tool/wpseo-export.php +34 -0
- admin/views/tabs/tool/wpseo-import.php +39 -0
- admin/views/tool-bulk-editor.php +86 -0
- admin/views/tool-file-editor.php +239 -0
- admin/views/tool-import-export.php +152 -0
- admin/views/user-profile.php +53 -0
- admin/watchers/class-slug-change-watcher.php +116 -0
- css/dist/admin-global-700-rtl.min.css +1 -0
- css/dist/admin-global-700.min.css +1 -0
- css/dist/adminbar-700-rtl.min.css +1 -0
- css/dist/adminbar-700.min.css +1 -0
- css/dist/alerts-700-rtl.min.css +1 -0
admin/ajax.php
ADDED
@@ -0,0 +1,390 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
7 |
+
header( 'Status: 403 Forbidden' );
|
8 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
9 |
+
exit();
|
10 |
+
}
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @todo this whole thing should probably be a proper class.
|
14 |
+
*/
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Convenience function to JSON encode and echo results and then die
|
18 |
+
*
|
19 |
+
* @param array $results Results array for encoding.
|
20 |
+
*/
|
21 |
+
function wpseo_ajax_json_echo_die( $results ) {
|
22 |
+
echo wp_json_encode( $results );
|
23 |
+
die();
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Function used from AJAX calls, takes it variables from $_POST, dies on exit.
|
28 |
+
*/
|
29 |
+
function wpseo_set_option() {
|
30 |
+
if ( ! current_user_can( 'manage_options' ) ) {
|
31 |
+
die( '-1' );
|
32 |
+
}
|
33 |
+
|
34 |
+
check_ajax_referer( 'wpseo-setoption' );
|
35 |
+
|
36 |
+
$option = sanitize_text_field( filter_input( INPUT_POST, 'option' ) );
|
37 |
+
if ( $option !== 'page_comments' ) {
|
38 |
+
die( '-1' );
|
39 |
+
}
|
40 |
+
|
41 |
+
update_option( $option, 0 );
|
42 |
+
die( '1' );
|
43 |
+
}
|
44 |
+
|
45 |
+
add_action( 'wp_ajax_wpseo_set_option', 'wpseo_set_option' );
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Since 3.2 Notifications are dismissed in the Notification Center.
|
49 |
+
*/
|
50 |
+
add_action( 'wp_ajax_yoast_dismiss_notification', array( 'Yoast_Notification_Center', 'ajax_dismiss_notification' ) );
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Function used to remove the admin notices for several purposes, dies on exit.
|
54 |
+
*/
|
55 |
+
function wpseo_set_ignore() {
|
56 |
+
if ( ! current_user_can( 'manage_options' ) ) {
|
57 |
+
die( '-1' );
|
58 |
+
}
|
59 |
+
|
60 |
+
check_ajax_referer( 'wpseo-ignore' );
|
61 |
+
|
62 |
+
$ignore_key = sanitize_text_field( filter_input( INPUT_POST, 'option' ) );
|
63 |
+
WPSEO_Options::set( 'ignore_' . $ignore_key, true );
|
64 |
+
|
65 |
+
die( '1' );
|
66 |
+
}
|
67 |
+
|
68 |
+
add_action( 'wp_ajax_wpseo_set_ignore', 'wpseo_set_ignore' );
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Hides the default tagline notice for a specific user.
|
72 |
+
*/
|
73 |
+
function wpseo_dismiss_tagline_notice() {
|
74 |
+
if ( ! current_user_can( 'manage_options' ) ) {
|
75 |
+
die( '-1' );
|
76 |
+
}
|
77 |
+
|
78 |
+
check_ajax_referer( 'wpseo-dismiss-tagline-notice' );
|
79 |
+
|
80 |
+
update_user_meta( get_current_user_id(), 'wpseo_seen_tagline_notice', 'seen' );
|
81 |
+
|
82 |
+
die( '1' );
|
83 |
+
}
|
84 |
+
|
85 |
+
add_action( 'wp_ajax_wpseo_dismiss_tagline_notice', 'wpseo_dismiss_tagline_notice' );
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Used in the editor to replace vars for the snippet preview
|
89 |
+
*/
|
90 |
+
function wpseo_ajax_replace_vars() {
|
91 |
+
global $post;
|
92 |
+
check_ajax_referer( 'wpseo-replace-vars' );
|
93 |
+
|
94 |
+
$post = get_post( intval( filter_input( INPUT_POST, 'post_id' ) ) );
|
95 |
+
global $wp_query;
|
96 |
+
$wp_query->queried_object = $post;
|
97 |
+
$wp_query->queried_object_id = $post->ID;
|
98 |
+
|
99 |
+
$omit = array( 'excerpt', 'excerpt_only', 'title' );
|
100 |
+
echo wpseo_replace_vars( stripslashes( filter_input( INPUT_POST, 'string' ) ), $post, $omit );
|
101 |
+
die;
|
102 |
+
}
|
103 |
+
|
104 |
+
add_action( 'wp_ajax_wpseo_replace_vars', 'wpseo_ajax_replace_vars' );
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Save an individual SEO title from the Bulk Editor.
|
108 |
+
*/
|
109 |
+
function wpseo_save_title() {
|
110 |
+
wpseo_save_what( 'title' );
|
111 |
+
}
|
112 |
+
|
113 |
+
add_action( 'wp_ajax_wpseo_save_title', 'wpseo_save_title' );
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Save an individual meta description from the Bulk Editor.
|
117 |
+
*/
|
118 |
+
function wpseo_save_description() {
|
119 |
+
wpseo_save_what( 'metadesc' );
|
120 |
+
}
|
121 |
+
|
122 |
+
add_action( 'wp_ajax_wpseo_save_metadesc', 'wpseo_save_description' );
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Save titles & descriptions
|
126 |
+
*
|
127 |
+
* @param string $what Type of item to save (title, description).
|
128 |
+
*/
|
129 |
+
function wpseo_save_what( $what ) {
|
130 |
+
check_ajax_referer( 'wpseo-bulk-editor' );
|
131 |
+
|
132 |
+
$new = filter_input( INPUT_POST, 'new_value' );
|
133 |
+
$post_id = intval( filter_input( INPUT_POST, 'wpseo_post_id' ) );
|
134 |
+
$original = filter_input( INPUT_POST, 'existing_value' );
|
135 |
+
|
136 |
+
$results = wpseo_upsert_new( $what, $post_id, $new, $original );
|
137 |
+
|
138 |
+
wpseo_ajax_json_echo_die( $results );
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Helper function to update a post's meta data, returning relevant information
|
143 |
+
* about the information updated and the results or the meta update.
|
144 |
+
*
|
145 |
+
* @param int $post_id Post ID.
|
146 |
+
* @param string $new_meta_value New meta value to record.
|
147 |
+
* @param string $orig_meta_value Original meta value.
|
148 |
+
* @param string $meta_key Meta key string.
|
149 |
+
* @param string $return_key Return key string to use in results.
|
150 |
+
*
|
151 |
+
* @return string
|
152 |
+
*/
|
153 |
+
function wpseo_upsert_meta( $post_id, $new_meta_value, $orig_meta_value, $meta_key, $return_key ) {
|
154 |
+
|
155 |
+
$post_id = intval( $post_id );
|
156 |
+
$sanitized_new_meta_value = wp_strip_all_tags( $new_meta_value );
|
157 |
+
$orig_meta_value = wp_strip_all_tags( $orig_meta_value );
|
158 |
+
|
159 |
+
$upsert_results = array(
|
160 |
+
'status' => 'success',
|
161 |
+
'post_id' => $post_id,
|
162 |
+
"new_{$return_key}" => $sanitized_new_meta_value,
|
163 |
+
"original_{$return_key}" => $orig_meta_value,
|
164 |
+
);
|
165 |
+
|
166 |
+
$the_post = get_post( $post_id );
|
167 |
+
if ( empty( $the_post ) ) {
|
168 |
+
|
169 |
+
$upsert_results['status'] = 'failure';
|
170 |
+
$upsert_results['results'] = __( 'Post doesn\'t exist.', 'wordpress-seo' );
|
171 |
+
|
172 |
+
return $upsert_results;
|
173 |
+
}
|
174 |
+
|
175 |
+
$post_type_object = get_post_type_object( $the_post->post_type );
|
176 |
+
if ( ! $post_type_object ) {
|
177 |
+
|
178 |
+
$upsert_results['status'] = 'failure';
|
179 |
+
$upsert_results['results'] = sprintf(
|
180 |
+
/* translators: %s expands to post type. */
|
181 |
+
__( 'Post has an invalid Post Type: %s.', 'wordpress-seo' ),
|
182 |
+
$the_post->post_type
|
183 |
+
);
|
184 |
+
|
185 |
+
return $upsert_results;
|
186 |
+
}
|
187 |
+
|
188 |
+
if ( ! current_user_can( $post_type_object->cap->edit_posts ) ) {
|
189 |
+
|
190 |
+
$upsert_results['status'] = 'failure';
|
191 |
+
$upsert_results['results'] = sprintf(
|
192 |
+
/* translators: %s expands to post type name. */
|
193 |
+
__( 'You can\'t edit %s.', 'wordpress-seo' ),
|
194 |
+
$post_type_object->label
|
195 |
+
);
|
196 |
+
|
197 |
+
return $upsert_results;
|
198 |
+
}
|
199 |
+
|
200 |
+
if ( ! current_user_can( $post_type_object->cap->edit_others_posts ) && (int) $the_post->post_author !== get_current_user_id() ) {
|
201 |
+
|
202 |
+
$upsert_results['status'] = 'failure';
|
203 |
+
$upsert_results['results'] = sprintf(
|
204 |
+
/* translators: %s expands to the name of a post type (plural). */
|
205 |
+
__( 'You can\'t edit %s that aren\'t yours.', 'wordpress-seo' ),
|
206 |
+
$post_type_object->label
|
207 |
+
);
|
208 |
+
|
209 |
+
return $upsert_results;
|
210 |
+
|
211 |
+
}
|
212 |
+
|
213 |
+
if ( $sanitized_new_meta_value === $orig_meta_value && $sanitized_new_meta_value !== $new_meta_value ) {
|
214 |
+
$upsert_results['status'] = 'failure';
|
215 |
+
$upsert_results['results'] = __( 'You have used HTML in your value which is not allowed.', 'wordpress-seo' );
|
216 |
+
|
217 |
+
return $upsert_results;
|
218 |
+
}
|
219 |
+
|
220 |
+
$res = update_post_meta( $post_id, $meta_key, $sanitized_new_meta_value );
|
221 |
+
|
222 |
+
$upsert_results['status'] = ( $res !== false ) ? 'success' : 'failure';
|
223 |
+
$upsert_results['results'] = $res;
|
224 |
+
|
225 |
+
return $upsert_results;
|
226 |
+
}
|
227 |
+
|
228 |
+
/**
|
229 |
+
* Save all titles sent from the Bulk Editor.
|
230 |
+
*/
|
231 |
+
function wpseo_save_all_titles() {
|
232 |
+
wpseo_save_all( 'title' );
|
233 |
+
}
|
234 |
+
|
235 |
+
add_action( 'wp_ajax_wpseo_save_all_titles', 'wpseo_save_all_titles' );
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Save all description sent from the Bulk Editor.
|
239 |
+
*/
|
240 |
+
function wpseo_save_all_descriptions() {
|
241 |
+
wpseo_save_all( 'metadesc' );
|
242 |
+
}
|
243 |
+
|
244 |
+
add_action( 'wp_ajax_wpseo_save_all_descriptions', 'wpseo_save_all_descriptions' );
|
245 |
+
|
246 |
+
/**
|
247 |
+
* Utility function to save values
|
248 |
+
*
|
249 |
+
* @param string $what Type of item so save.
|
250 |
+
*/
|
251 |
+
function wpseo_save_all( $what ) {
|
252 |
+
check_ajax_referer( 'wpseo-bulk-editor' );
|
253 |
+
|
254 |
+
// @todo the WPSEO Utils class can't filter arrays in POST yet.
|
255 |
+
$new_values = $_POST['items'];
|
256 |
+
$original_values = $_POST['existing_items'];
|
257 |
+
|
258 |
+
$results = array();
|
259 |
+
|
260 |
+
if ( is_array( $new_values ) && $new_values !== array() ) {
|
261 |
+
foreach ( $new_values as $post_id => $new_value ) {
|
262 |
+
$original_value = $original_values[ $post_id ];
|
263 |
+
$results[] = wpseo_upsert_new( $what, $post_id, $new_value, $original_value );
|
264 |
+
}
|
265 |
+
}
|
266 |
+
wpseo_ajax_json_echo_die( $results );
|
267 |
+
}
|
268 |
+
|
269 |
+
/**
|
270 |
+
* Insert a new value
|
271 |
+
*
|
272 |
+
* @param string $what Item type (such as title).
|
273 |
+
* @param int $post_id Post ID.
|
274 |
+
* @param string $new New value to record.
|
275 |
+
* @param string $original Original value.
|
276 |
+
*
|
277 |
+
* @return string
|
278 |
+
*/
|
279 |
+
function wpseo_upsert_new( $what, $post_id, $new, $original ) {
|
280 |
+
$meta_key = WPSEO_Meta::$meta_prefix . $what;
|
281 |
+
|
282 |
+
return wpseo_upsert_meta( $post_id, $new, $original, $meta_key, $what );
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
* Handles the posting of a new FB admin.
|
287 |
+
*/
|
288 |
+
function wpseo_add_fb_admin() {
|
289 |
+
check_ajax_referer( 'wpseo_fb_admin_nonce' );
|
290 |
+
|
291 |
+
if ( ! current_user_can( 'manage_options' ) ) {
|
292 |
+
die( '-1' );
|
293 |
+
}
|
294 |
+
|
295 |
+
$facebook_social = new Yoast_Social_Facebook();
|
296 |
+
|
297 |
+
wp_die( $facebook_social->add_admin( filter_input( INPUT_POST, 'admin_name' ), filter_input( INPUT_POST, 'admin_id' ) ) );
|
298 |
+
}
|
299 |
+
|
300 |
+
add_action( 'wp_ajax_wpseo_add_fb_admin', 'wpseo_add_fb_admin' );
|
301 |
+
|
302 |
+
/**
|
303 |
+
* Retrieves the keyword for the keyword doubles.
|
304 |
+
*/
|
305 |
+
function ajax_get_keyword_usage() {
|
306 |
+
$post_id = filter_input( INPUT_POST, 'post_id' );
|
307 |
+
$keyword = filter_input( INPUT_POST, 'keyword' );
|
308 |
+
|
309 |
+
if ( ! current_user_can( 'edit_post', $post_id ) ) {
|
310 |
+
die( '-1' );
|
311 |
+
}
|
312 |
+
|
313 |
+
wp_die(
|
314 |
+
wp_json_encode( WPSEO_Meta::keyword_usage( $keyword, $post_id ) )
|
315 |
+
);
|
316 |
+
}
|
317 |
+
|
318 |
+
add_action( 'wp_ajax_get_focus_keyword_usage', 'ajax_get_keyword_usage' );
|
319 |
+
|
320 |
+
/**
|
321 |
+
* Retrieves the keyword for the keyword doubles of the termpages.
|
322 |
+
*/
|
323 |
+
function ajax_get_term_keyword_usage() {
|
324 |
+
$post_id = filter_input( INPUT_POST, 'post_id' );
|
325 |
+
$keyword = filter_input( INPUT_POST, 'keyword' );
|
326 |
+
$taxonomy_name = filter_input( INPUT_POST, 'taxonomy' );
|
327 |
+
|
328 |
+
$taxonomy = get_taxonomy( $taxonomy_name );
|
329 |
+
|
330 |
+
if ( ! $taxonomy ) {
|
331 |
+
wp_die( 0 );
|
332 |
+
}
|
333 |
+
|
334 |
+
if ( ! current_user_can( $taxonomy->cap->edit_terms ) ) {
|
335 |
+
wp_die( -1 );
|
336 |
+
}
|
337 |
+
|
338 |
+
$usage = WPSEO_Taxonomy_Meta::get_keyword_usage( $keyword, $post_id, $taxonomy_name );
|
339 |
+
|
340 |
+
// Normalize the result so it it the same as the post keyword usage AJAX request.
|
341 |
+
$usage = $usage[ $keyword ];
|
342 |
+
|
343 |
+
wp_die(
|
344 |
+
wp_json_encode( $usage )
|
345 |
+
);
|
346 |
+
}
|
347 |
+
|
348 |
+
add_action( 'wp_ajax_get_term_keyword_usage', 'ajax_get_term_keyword_usage' );
|
349 |
+
|
350 |
+
// Crawl Issue Manager AJAX hooks.
|
351 |
+
new WPSEO_GSC_Ajax();
|
352 |
+
|
353 |
+
// SEO Score Recalculations.
|
354 |
+
new WPSEO_Recalculate_Scores_Ajax();
|
355 |
+
|
356 |
+
new Yoast_OnPage_Ajax();
|
357 |
+
|
358 |
+
new WPSEO_Shortcode_Filter();
|
359 |
+
|
360 |
+
new WPSEO_Taxonomy_Columns();
|
361 |
+
|
362 |
+
// Setting the notice for the recalculate the posts.
|
363 |
+
new Yoast_Dismissable_Notice_Ajax( 'recalculate', Yoast_Dismissable_Notice_Ajax::FOR_SITE );
|
364 |
+
|
365 |
+
/********************** DEPRECATED METHODS **********************/
|
366 |
+
|
367 |
+
|
368 |
+
/**
|
369 |
+
* Removes stopword from the sample permalink that is generated in an AJAX request
|
370 |
+
*
|
371 |
+
* @deprecated 6.3
|
372 |
+
* @codeCoverageIgnore
|
373 |
+
*/
|
374 |
+
function wpseo_remove_stopwords_sample_permalink() {
|
375 |
+
_deprecated_function( __FUNCTION__, 'WPSEO 6.3', 'This method is deprecated.' );
|
376 |
+
|
377 |
+
wpseo_ajax_json_echo_die( '' );
|
378 |
+
}
|
379 |
+
|
380 |
+
/**
|
381 |
+
* Function used to delete blocking files, dies on exit.
|
382 |
+
*
|
383 |
+
* @deprecated 7.0
|
384 |
+
* @codeCoverageIgnore
|
385 |
+
*/
|
386 |
+
function wpseo_kill_blocking_files() {
|
387 |
+
_deprecated_function( __FUNCTION__, 'WPSEO 7.0', 'This method is deprecated.' );
|
388 |
+
|
389 |
+
wpseo_ajax_json_echo_die( '' );
|
390 |
+
}
|
admin/ajax/class-recalculate-scores-ajax.php
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Ajax
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Recalculate_Scores
|
8 |
+
*
|
9 |
+
* This class handles the SEO score recalculation for all posts with a filled focus keyword
|
10 |
+
*/
|
11 |
+
class WPSEO_Recalculate_Scores_Ajax {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Initialize the AJAX hooks
|
15 |
+
*/
|
16 |
+
public function __construct() {
|
17 |
+
add_action( 'wp_ajax_wpseo_recalculate_scores', array( $this, 'recalculate_scores' ) );
|
18 |
+
add_action( 'wp_ajax_wpseo_update_score', array( $this, 'save_score' ) );
|
19 |
+
add_action( 'wp_ajax_wpseo_recalculate_total', array( $this, 'get_total' ) );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Get the totals for the posts and the terms.
|
24 |
+
*/
|
25 |
+
public function get_total() {
|
26 |
+
check_ajax_referer( 'wpseo_recalculate', 'nonce' );
|
27 |
+
|
28 |
+
wp_die(
|
29 |
+
wp_json_encode(
|
30 |
+
array(
|
31 |
+
'posts' => $this->calculate_posts(),
|
32 |
+
'terms' => $this->calculate_terms(),
|
33 |
+
)
|
34 |
+
)
|
35 |
+
);
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Start recalculation
|
40 |
+
*/
|
41 |
+
public function recalculate_scores() {
|
42 |
+
check_ajax_referer( 'wpseo_recalculate', 'nonce' );
|
43 |
+
|
44 |
+
$fetch_object = $this->get_fetch_object();
|
45 |
+
if ( ! empty( $fetch_object ) ) {
|
46 |
+
$paged = filter_input( INPUT_POST, 'paged', FILTER_VALIDATE_INT );
|
47 |
+
$response = $fetch_object->get_items_to_recalculate( $paged );
|
48 |
+
|
49 |
+
if ( ! empty( $response ) ) {
|
50 |
+
wp_die( wp_json_encode( $response ) );
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
wp_die( '' );
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Saves the new linkdex score for given post
|
59 |
+
*/
|
60 |
+
public function save_score() {
|
61 |
+
check_ajax_referer( 'wpseo_recalculate', 'nonce' );
|
62 |
+
|
63 |
+
$fetch_object = $this->get_fetch_object();
|
64 |
+
if ( ! empty( $fetch_object ) ) {
|
65 |
+
$scores = filter_input( INPUT_POST, 'scores', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
|
66 |
+
$fetch_object->save_scores( $scores );
|
67 |
+
}
|
68 |
+
|
69 |
+
wp_die();
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Returns the needed object for recalculating scores.
|
74 |
+
*
|
75 |
+
* @return WPSEO_Recalculate_Posts|WPSEO_Recalculate_Terms
|
76 |
+
*/
|
77 |
+
private function get_fetch_object() {
|
78 |
+
switch ( filter_input( INPUT_POST, 'type' ) ) {
|
79 |
+
case 'post':
|
80 |
+
return new WPSEO_Recalculate_Posts();
|
81 |
+
case 'term':
|
82 |
+
return new WPSEO_Recalculate_Terms();
|
83 |
+
}
|
84 |
+
|
85 |
+
return null;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Gets the total number of posts
|
90 |
+
*
|
91 |
+
* @return int
|
92 |
+
*/
|
93 |
+
private function calculate_posts() {
|
94 |
+
$count_posts_query = new WP_Query(
|
95 |
+
array(
|
96 |
+
'post_type' => 'any',
|
97 |
+
'meta_key' => '_yoast_wpseo_focuskw',
|
98 |
+
'posts_per_page' => 1,
|
99 |
+
'fields' => 'ids',
|
100 |
+
)
|
101 |
+
);
|
102 |
+
|
103 |
+
return $count_posts_query->found_posts;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Get the total number of terms
|
108 |
+
*
|
109 |
+
* @return int
|
110 |
+
*/
|
111 |
+
private function calculate_terms() {
|
112 |
+
$total = 0;
|
113 |
+
foreach ( get_taxonomies( array(), 'objects' ) as $taxonomy ) {
|
114 |
+
$total += wp_count_terms( $taxonomy->name, array( 'hide_empty' => false ) );
|
115 |
+
}
|
116 |
+
|
117 |
+
return $total;
|
118 |
+
}
|
119 |
+
}
|
admin/ajax/class-shortcode-filter.php
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Ajax
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Shortcode_Filter
|
8 |
+
*
|
9 |
+
* Used for parsing WP shortcodes with AJAX
|
10 |
+
*/
|
11 |
+
class WPSEO_Shortcode_Filter {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Initialize the AJAX hooks
|
15 |
+
*/
|
16 |
+
public function __construct() {
|
17 |
+
add_action( 'wp_ajax_wpseo_filter_shortcodes', array( $this, 'do_filter' ) );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Parse the shortcodes
|
22 |
+
*/
|
23 |
+
public function do_filter() {
|
24 |
+
check_ajax_referer( 'wpseo-filter-shortcodes', 'nonce' );
|
25 |
+
|
26 |
+
$shortcodes = filter_input( INPUT_POST, 'data', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
|
27 |
+
|
28 |
+
$parsed_shortcodes = array();
|
29 |
+
|
30 |
+
foreach ( $shortcodes as $shortcode ) {
|
31 |
+
$parsed_shortcodes[] = array(
|
32 |
+
'shortcode' => $shortcode,
|
33 |
+
'output' => do_shortcode( $shortcode ),
|
34 |
+
);
|
35 |
+
}
|
36 |
+
|
37 |
+
wp_die( wp_json_encode( $parsed_shortcodes ) );
|
38 |
+
}
|
39 |
+
}
|
admin/ajax/class-yoast-dismissable-notice.php
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\admin|ajax
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class will catch the request to dismiss the target notice (set by notice_name) and will store the dismiss status as an user meta
|
8 |
+
* in the database
|
9 |
+
*/
|
10 |
+
class Yoast_Dismissable_Notice_Ajax {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @var string Notice type toggle value for user notices.
|
14 |
+
*/
|
15 |
+
const FOR_USER = 'user_meta';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var string Notice type toggle value for network notices.
|
19 |
+
*/
|
20 |
+
const FOR_NETWORK = 'site_option';
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var string Notice type toggle value for site notices.
|
24 |
+
*/
|
25 |
+
const FOR_SITE = 'option';
|
26 |
+
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var string Name of the notice that will be dismissed.
|
30 |
+
*/
|
31 |
+
private $notice_name;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @var string
|
35 |
+
*/
|
36 |
+
private $notice_type;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Initialize the hooks for the AJAX request
|
40 |
+
*
|
41 |
+
* @param string $notice_name The name for the hook to catch the notice.
|
42 |
+
* @param string $notice_type The notice type.
|
43 |
+
*/
|
44 |
+
public function __construct( $notice_name, $notice_type = self::FOR_USER ) {
|
45 |
+
$this->notice_name = $notice_name;
|
46 |
+
$this->notice_type = $notice_type;
|
47 |
+
|
48 |
+
add_action( 'wp_ajax_wpseo_dismiss_' . $notice_name, array( $this, 'dismiss_notice' ) );
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Handles the dismiss notice request
|
53 |
+
*/
|
54 |
+
public function dismiss_notice() {
|
55 |
+
check_ajax_referer( 'wpseo-dismiss-' . $this->notice_name );
|
56 |
+
|
57 |
+
$this->save_dismissed();
|
58 |
+
|
59 |
+
wp_die( 'true' );
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Storing the dismissed value in the database. The target location is based on the set notification type.
|
64 |
+
*/
|
65 |
+
private function save_dismissed() {
|
66 |
+
if ( $this->notice_type === self::FOR_SITE ) {
|
67 |
+
update_option( 'wpseo_dismiss_' . $this->notice_name, 1 );
|
68 |
+
|
69 |
+
return;
|
70 |
+
}
|
71 |
+
|
72 |
+
if ( $this->notice_type === self::FOR_NETWORK ) {
|
73 |
+
update_site_option( 'wpseo_dismiss_' . $this->notice_name, 1 );
|
74 |
+
|
75 |
+
return;
|
76 |
+
}
|
77 |
+
|
78 |
+
update_user_meta( get_current_user_id(), 'wpseo_dismiss_' . $this->notice_name, 1 );
|
79 |
+
}
|
80 |
+
}
|
admin/ajax/class-yoast-onpage-ajax.php
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\admin|ajax
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Yoast_OnPage_Ajax
|
8 |
+
*
|
9 |
+
* This class will catch the request to dismiss the Ryte notice and will store
|
10 |
+
* the dismiss status as an user meta in the database.
|
11 |
+
*/
|
12 |
+
class Yoast_OnPage_Ajax {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Initialize the hooks for the AJAX request
|
16 |
+
*/
|
17 |
+
public function __construct() {
|
18 |
+
add_action( 'wp_ajax_wpseo_dismiss_onpageorg', array( $this, 'dismiss_notice' ) );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Handles the dismiss notice request
|
23 |
+
*/
|
24 |
+
public function dismiss_notice() {
|
25 |
+
check_ajax_referer( 'wpseo-dismiss-onpageorg' );
|
26 |
+
|
27 |
+
$this->save_dismissed();
|
28 |
+
|
29 |
+
wp_die( 'true' );
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Storing the dismissed value as an user option in the database
|
34 |
+
*/
|
35 |
+
private function save_dismissed() {
|
36 |
+
update_user_meta( get_current_user_id(), WPSEO_OnPage::USER_META_KEY, 1 );
|
37 |
+
}
|
38 |
+
}
|
admin/ajax/class-yoast-plugin-conflict-ajax.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\admin|ajax
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Yoast_Plugin_Conflict_Ajax
|
8 |
+
*/
|
9 |
+
class Yoast_Plugin_Conflict_Ajax {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string
|
13 |
+
*/
|
14 |
+
private $option_name = 'wpseo_dismissed_conflicts';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var array
|
18 |
+
*/
|
19 |
+
private $dismissed_conflicts = array();
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Initialize the hooks for the AJAX request
|
23 |
+
*/
|
24 |
+
public function __construct() {
|
25 |
+
add_action( 'wp_ajax_wpseo_dismiss_plugin_conflict', array( $this, 'dismiss_notice' ) );
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Handles the dismiss notice request
|
30 |
+
*/
|
31 |
+
public function dismiss_notice() {
|
32 |
+
check_ajax_referer( 'dismiss-plugin-conflict' );
|
33 |
+
|
34 |
+
$conflict_data = filter_input( INPUT_POST, 'data', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
|
35 |
+
|
36 |
+
$this->dismissed_conflicts = $this->get_dismissed_conflicts( $conflict_data['section'] );
|
37 |
+
|
38 |
+
$this->compare_plugins( $conflict_data['plugins'] );
|
39 |
+
|
40 |
+
$this->save_dismissed_conflicts( $conflict_data['section'] );
|
41 |
+
|
42 |
+
wp_die( 'true' );
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Getting the user option from the database
|
47 |
+
*
|
48 |
+
* @return bool|array
|
49 |
+
*/
|
50 |
+
private function get_dismissed_option() {
|
51 |
+
return get_user_meta( get_current_user_id(), $this->option_name, true );
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Getting the dismissed conflicts from the database
|
56 |
+
*
|
57 |
+
* @param string $plugin_section Type of conflict group (such as Open Graph or sitemap).
|
58 |
+
*
|
59 |
+
* @return array
|
60 |
+
*/
|
61 |
+
private function get_dismissed_conflicts( $plugin_section ) {
|
62 |
+
$dismissed_conflicts = $this->get_dismissed_option();
|
63 |
+
|
64 |
+
if ( is_array( $dismissed_conflicts ) && array_key_exists( $plugin_section, $dismissed_conflicts ) ) {
|
65 |
+
return $dismissed_conflicts[ $plugin_section ];
|
66 |
+
}
|
67 |
+
|
68 |
+
return array();
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Storing the conflicting plugins as an user option in the database
|
73 |
+
*
|
74 |
+
* @param string $plugin_section Plugin conflict type (such as Open Graph or sitemap).
|
75 |
+
*/
|
76 |
+
private function save_dismissed_conflicts( $plugin_section ) {
|
77 |
+
$dismissed_conflicts = $this->get_dismissed_option();
|
78 |
+
|
79 |
+
$dismissed_conflicts[ $plugin_section ] = $this->dismissed_conflicts;
|
80 |
+
|
81 |
+
update_user_meta( get_current_user_id(), $this->option_name, $dismissed_conflicts );
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Loop through the plugins to compare them with the already stored dismissed plugin conflicts
|
86 |
+
*
|
87 |
+
* @param array $posted_plugins Plugin set to check.
|
88 |
+
*/
|
89 |
+
public function compare_plugins( array $posted_plugins ) {
|
90 |
+
foreach ( $posted_plugins as $posted_plugin ) {
|
91 |
+
$this->compare_plugin( $posted_plugin );
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Check if plugin is already dismissed, if not store it in the array that will be saved later
|
97 |
+
*
|
98 |
+
* @param string $posted_plugin Plugin to check against dismissed conflicts.
|
99 |
+
*/
|
100 |
+
private function compare_plugin( $posted_plugin ) {
|
101 |
+
if ( ! in_array( $posted_plugin, $this->dismissed_conflicts, true ) ) {
|
102 |
+
$this->dismissed_conflicts[] = $posted_plugin;
|
103 |
+
}
|
104 |
+
}
|
105 |
+
}
|
admin/banner/class-admin-banner-renderer.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Banner
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the render object for generating the html for the given banner.
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Banner_Renderer {
|
10 |
+
|
11 |
+
/** @var string */
|
12 |
+
protected $base_path = '';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Renders the admin banner.
|
16 |
+
*
|
17 |
+
* @param WPSEO_Admin_Banner $banner The banner to render.
|
18 |
+
*
|
19 |
+
* @return string
|
20 |
+
*/
|
21 |
+
public function render( WPSEO_Admin_Banner $banner ) {
|
22 |
+
$output = '<a class="wpseo-banner__link" target="_blank" href="' . esc_url( $banner->get_url() ) . '">';
|
23 |
+
$output .= '<img class="wpseo-banner__image" width="' . esc_attr( $banner->get_width() ) . '" height="' . esc_attr( $banner->get_height() ) . '" src="' . esc_attr( $this->get_image_path( $banner->get_image() ) ) . '" alt="' . esc_attr( $banner->get_alt() ) . '"/>';
|
24 |
+
$output .= '</a>';
|
25 |
+
|
26 |
+
return $output;
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Sets the base path, where the images are located.
|
31 |
+
*
|
32 |
+
* @param string $base_path The image location.
|
33 |
+
*/
|
34 |
+
public function set_base_path( $base_path ) {
|
35 |
+
$this->base_path = $base_path;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Returns the full path for the image.
|
40 |
+
*
|
41 |
+
* @param string $image The image path.
|
42 |
+
*
|
43 |
+
* @return string
|
44 |
+
*/
|
45 |
+
protected function get_image_path( $image ) {
|
46 |
+
return rtrim( $this->base_path, '/' ) . '/' . ltrim( $image, '/' );
|
47 |
+
}
|
48 |
+
}
|
admin/banner/class-admin-banner-sidebar-renderer.php
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Banner
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the render object for generating the html for the banner sidebar
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Banner_Sidebar_Renderer {
|
10 |
+
|
11 |
+
/** @var WPSEO_Admin_Banner_Spot_Renderer */
|
12 |
+
protected $spot_renderer;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Sets the spot renderer.
|
16 |
+
*
|
17 |
+
* @param WPSEO_Admin_Banner_Spot_Renderer $spot_renderer The spot renderer that has to be used.
|
18 |
+
*/
|
19 |
+
public function __construct( WPSEO_Admin_Banner_Spot_Renderer $spot_renderer ) {
|
20 |
+
$this->spot_renderer = $spot_renderer;
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Renders the admin banner sidebar.
|
25 |
+
*
|
26 |
+
* @param WPSEO_Admin_Banner_Sidebar $banner_sidebar The sidebar to render.
|
27 |
+
*
|
28 |
+
* @return string
|
29 |
+
*/
|
30 |
+
public function render( WPSEO_Admin_Banner_Sidebar $banner_sidebar ) {
|
31 |
+
return sprintf( '
|
32 |
+
<div class="wpseo_content_cell" id="sidebar-container">
|
33 |
+
<div id="sidebar">
|
34 |
+
<div class="wpseo_content_cell_title yoast-sidebar__title ">
|
35 |
+
%1$s
|
36 |
+
</div>
|
37 |
+
%2$s
|
38 |
+
</div>
|
39 |
+
</div>',
|
40 |
+
$banner_sidebar->get_title(),
|
41 |
+
$this->render_banner_spots( $banner_sidebar->get_banner_spots() )
|
42 |
+
);
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Renders the admin banner spots.
|
47 |
+
*
|
48 |
+
* @param WPSEO_Admin_Banner_Spot[] $banner_spots The banner spots to render.
|
49 |
+
*
|
50 |
+
* @return string
|
51 |
+
*/
|
52 |
+
protected function render_banner_spots( array $banner_spots ) {
|
53 |
+
$return = '';
|
54 |
+
foreach ( $banner_spots as $banner_spot ) {
|
55 |
+
$return .= $this->spot_renderer->render( $banner_spot );
|
56 |
+
}
|
57 |
+
|
58 |
+
return $return;
|
59 |
+
}
|
60 |
+
}
|
admin/banner/class-admin-banner-sidebar.php
ADDED
@@ -0,0 +1,388 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Banner
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the render object for generating the html for the given banner.
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Banner_Sidebar {
|
10 |
+
|
11 |
+
/** @var string */
|
12 |
+
protected $title = '';
|
13 |
+
|
14 |
+
/** @var WPSEO_Admin_Banner_Spot[] */
|
15 |
+
protected $banner_spots = array();
|
16 |
+
|
17 |
+
/** @var WPSEO_Admin_Banner_Renderer */
|
18 |
+
protected $banner_renderer;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* WPSEO_Admin_Banner_Sidebar constructor.
|
22 |
+
*
|
23 |
+
* @param string $title The title for the sidebar.
|
24 |
+
* @param WPSEO_Admin_Banner_Renderer $banner_renderer The render class for banners.
|
25 |
+
*/
|
26 |
+
public function __construct( $title, WPSEO_Admin_Banner_Renderer $banner_renderer ) {
|
27 |
+
$this->title = $title;
|
28 |
+
$this->banner_renderer = $banner_renderer;
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Returns the set title.
|
33 |
+
*
|
34 |
+
* @return string
|
35 |
+
*/
|
36 |
+
public function get_title() {
|
37 |
+
return $this->title;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Initializes the banner sidebar by setting its banner spots.
|
42 |
+
*
|
43 |
+
* @param WPSEO_Features $features Class regarding WPSEO Features.
|
44 |
+
*/
|
45 |
+
public function initialize( WPSEO_Features $features ) {
|
46 |
+
if ( $features->is_free() ) {
|
47 |
+
$this->add_banner_spot( $this->get_premium_spot() );
|
48 |
+
}
|
49 |
+
|
50 |
+
$this->add_banner_spot( $this->get_services_spot() );
|
51 |
+
|
52 |
+
$extensions_spot = $this->get_extensions_spot( $this->get_active_extensions() );
|
53 |
+
if ( $extensions_spot->has_banners() ) {
|
54 |
+
$this->add_banner_spot( $extensions_spot );
|
55 |
+
}
|
56 |
+
|
57 |
+
$this->add_banner_spot( $this->get_courses_spot() );
|
58 |
+
$this->add_banner_spot( $this->get_remove_banner_spot() );
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Returns array with bannerspots.
|
63 |
+
*
|
64 |
+
* @return WPSEO_Admin_Banner_Spot[]
|
65 |
+
*/
|
66 |
+
public function get_banner_spots() {
|
67 |
+
return $this->banner_spots;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Adds a banner spot.
|
72 |
+
*
|
73 |
+
* @param WPSEO_Admin_Banner_Spot $spot The spot to add.
|
74 |
+
*/
|
75 |
+
protected function add_banner_spot( WPSEO_Admin_Banner_Spot $spot ) {
|
76 |
+
$this->banner_spots[] = $spot;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Returns the premium banner spot.
|
81 |
+
*
|
82 |
+
* @return WPSEO_Admin_Banner_Spot
|
83 |
+
*/
|
84 |
+
protected function get_premium_spot() {
|
85 |
+
$premium_spot = new WPSEO_Admin_Banner_Spot( '', $this->banner_renderer );
|
86 |
+
|
87 |
+
$premium_uri = WPSEO_Shortlinker::get( 'https://yoa.st/jj' );
|
88 |
+
|
89 |
+
$premium_spot->set_extra(
|
90 |
+
/* translators: %1$s expands to the plugin name */
|
91 |
+
'<h2>' . sprintf( __( 'Get %1$s', 'wordpress-seo' ), 'Yoast SEO Premium' ) . '</h2>' .
|
92 |
+
'<ul>' .
|
93 |
+
'<li><strong>' . __( 'Multiple keywords', 'wordpress-seo' ) . '</strong><br/>' . __( 'Increase your SEO reach', 'wordpress-seo' ) . '</li>' .
|
94 |
+
'<li><strong>' . __( 'No more dead links', 'wordpress-seo' ) . '</strong><br/>' . __( 'Easy redirect manager', 'wordpress-seo' ) . '</li>' .
|
95 |
+
'<li><strong>' . __( 'Internal linking suggestions', 'wordpress-seo' ) . '</strong><br/>' . __( 'Find related posts superfast', 'wordpress-seo' ) . '</li>' .
|
96 |
+
'<li><strong>' . __( 'Social media preview', 'wordpress-seo' ) . '</strong><br/>' . esc_html__( 'Facebook & Twitter', 'wordpress-seo' ) . '</li>' .
|
97 |
+
'<li><strong>' . __( '24/7 support', 'wordpress-seo' ) . '</strong></li>' .
|
98 |
+
'<li><strong>' . __( 'No ads!', 'wordpress-seo' ) . '</strong></li>' .
|
99 |
+
'</ul>' .
|
100 |
+
/* translators: %s expands to Yoast SEO Premium */
|
101 |
+
'<a id="wpseo-premium-button" class="button button-primary" href="' . $premium_uri . '" target="_blank">' . sprintf( __( 'Get %s now!', 'wordpress-seo' ), 'Yoast SEO Premium' ) . '</a><br/>'
|
102 |
+
);
|
103 |
+
|
104 |
+
/*
|
105 |
+
$premium_spot->set_description(
|
106 |
+
sprintf(
|
107 |
+
/* translators: %1$s expands to a link start tag to the Yoast plugin page, %2$s is the link closing tag * /
|
108 |
+
__( 'Want to get the most out of your SEO-strategy? %1$sGo premium!%2$s.', 'wordpress-seo' ),
|
109 |
+
'<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/ji' ) . '">',
|
110 |
+
'</a>'
|
111 |
+
)
|
112 |
+
);
|
113 |
+
/*
|
114 |
+
|
115 |
+
$premium_spot->add_banner(
|
116 |
+
new WPSEO_Admin_Banner(
|
117 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/jj' ),
|
118 |
+
'premium-seo.png',
|
119 |
+
261,
|
120 |
+
152,
|
121 |
+
sprintf(
|
122 |
+
/* translators: %1$s expands to Yoast SEO Premium. * /
|
123 |
+
__( 'Buy the %1$s plugin now and get access to extra features and 24/7 support!', 'wordpress-seo' ),
|
124 |
+
'Yoast SEO Premium'
|
125 |
+
)
|
126 |
+
)
|
127 |
+
);
|
128 |
+
*/
|
129 |
+
|
130 |
+
return $premium_spot;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Returns the services banner spot.
|
135 |
+
*
|
136 |
+
* @return WPSEO_Admin_Banner_Spot
|
137 |
+
*/
|
138 |
+
protected function get_services_spot() {
|
139 |
+
$service_spot = new WPSEO_Admin_Banner_Spot( __( 'Services', 'wordpress-seo' ), $this->banner_renderer );
|
140 |
+
|
141 |
+
$service_spot->set_description(
|
142 |
+
sprintf(
|
143 |
+
/* translators: %1$s expands to a link start tag to the Yoast Services page, %2$s to Yoast, %3$s is the link closing tag. */
|
144 |
+
__( 'Do you want to know how to improve your rankings? %1$sLet team %2$s help you!%3$s', 'wordpress-seo' ),
|
145 |
+
'<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/jk' ) . '">',
|
146 |
+
'Yoast',
|
147 |
+
'</a>'
|
148 |
+
)
|
149 |
+
);
|
150 |
+
|
151 |
+
$service_spot->add_banner(
|
152 |
+
new WPSEO_Admin_Banner(
|
153 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/jm' ),
|
154 |
+
'configuration-service.png',
|
155 |
+
261,
|
156 |
+
152,
|
157 |
+
sprintf(
|
158 |
+
/* translators: %1$s expands to Yoast SEO Premium. */
|
159 |
+
__( 'Let our experts set up your %1$s plugin!', 'wordpress-seo' ),
|
160 |
+
'Yoast SEO Premium'
|
161 |
+
)
|
162 |
+
)
|
163 |
+
);
|
164 |
+
|
165 |
+
/*
|
166 |
+
$service_spot->add_banner(
|
167 |
+
new WPSEO_Admin_Banner(
|
168 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/seo-care-banner' ),
|
169 |
+
'seo-care.png',
|
170 |
+
261,
|
171 |
+
152,
|
172 |
+
sprintf(
|
173 |
+
/* translators: %1$s expands to Yoast SEO Care. * /
|
174 |
+
__( 'Let us help you take care of the SEO of your website. Order %1$s now!', 'wordpress-seo' ),
|
175 |
+
'Yoast SEO Care'
|
176 |
+
)
|
177 |
+
)
|
178 |
+
);
|
179 |
+
*/
|
180 |
+
|
181 |
+
return $service_spot;
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Returns an array with the Yoast SEO extensions with the value true when they are active.
|
186 |
+
*
|
187 |
+
* @return array
|
188 |
+
*/
|
189 |
+
protected function get_active_extensions() {
|
190 |
+
return array(
|
191 |
+
'video' => class_exists( 'wpseo_Video_Sitemap' ),
|
192 |
+
'woocommerce' => class_exists( 'Woocommerce' ) && class_exists( 'Yoast_WooCommerce_SEO' ),
|
193 |
+
'news' => class_exists( 'WPSEO_News' ),
|
194 |
+
'local' => defined( 'WPSEO_LOCAL_VERSION' ),
|
195 |
+
);
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Returns the extensions banner spot.
|
200 |
+
*
|
201 |
+
* @param array $active_extensions The active extensions.
|
202 |
+
*
|
203 |
+
* @return WPSEO_Admin_Banner_Spot
|
204 |
+
*/
|
205 |
+
protected function get_extensions_spot( array $active_extensions ) {
|
206 |
+
$extension_spot = new WPSEO_Admin_Banner_Spot( __( 'Extensions', 'wordpress-seo' ), $this->banner_renderer );
|
207 |
+
|
208 |
+
$extension_spot->set_description(
|
209 |
+
sprintf(
|
210 |
+
/* translators: %1$s expands to a link start tag to the Yoast plugin page, %2$s is the link closing tag. */
|
211 |
+
__( 'Take your SEO to the next level and outrank your competition with our %1$sSEO plugins%2$s.', 'wordpress-seo' ),
|
212 |
+
'<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/jn' ) . '">',
|
213 |
+
'</a>'
|
214 |
+
)
|
215 |
+
);
|
216 |
+
|
217 |
+
if ( empty( $active_extensions['video'] ) ) {
|
218 |
+
$extension_spot->add_banner(
|
219 |
+
new WPSEO_Admin_Banner(
|
220 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/jo' ),
|
221 |
+
'video-seo.png',
|
222 |
+
261,
|
223 |
+
152,
|
224 |
+
sprintf(
|
225 |
+
/* translators: %1$s expands to Yoast Video SEO. */
|
226 |
+
__( 'Buy the %1$s plugin now and optimize your videos for video search results and social media!', 'wordpress-seo' ),
|
227 |
+
'Yoast Video SEO'
|
228 |
+
)
|
229 |
+
)
|
230 |
+
);
|
231 |
+
}
|
232 |
+
|
233 |
+
if ( empty( $active_extensions['woocommerce'] ) ) {
|
234 |
+
$extension_spot->add_banner(
|
235 |
+
new WPSEO_Admin_Banner(
|
236 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/jp' ),
|
237 |
+
'woocommerce-seo.png',
|
238 |
+
261,
|
239 |
+
152,
|
240 |
+
sprintf(
|
241 |
+
/* translators: %1$s expands to Yoast WooCommerce SEO. */
|
242 |
+
__( 'Buy the %1$s plugin now and optimize your shop today to improve your product promotion!', 'wordpress-seo' ),
|
243 |
+
'Yoast WooCommerce SEO'
|
244 |
+
)
|
245 |
+
)
|
246 |
+
);
|
247 |
+
}
|
248 |
+
|
249 |
+
if ( empty( $active_extensions['local'] ) ) {
|
250 |
+
$extension_spot->add_banner(
|
251 |
+
new WPSEO_Admin_Banner(
|
252 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/jq' ),
|
253 |
+
'local-seo.png', 261,
|
254 |
+
152,
|
255 |
+
sprintf(
|
256 |
+
/* translators: %1$s expands to Yoast Local SEO. */
|
257 |
+
__( 'Buy the %1$s plugin now to improve your site’s Local SEO and ranking in Google Maps!', 'wordpress-seo' ),
|
258 |
+
'Yoast Local SEO'
|
259 |
+
)
|
260 |
+
)
|
261 |
+
);
|
262 |
+
}
|
263 |
+
|
264 |
+
if ( empty( $active_extensions['news'] ) ) {
|
265 |
+
$extension_spot->add_banner(
|
266 |
+
new WPSEO_Admin_Banner(
|
267 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/jr' ),
|
268 |
+
'news-seo.png',
|
269 |
+
261,
|
270 |
+
152,
|
271 |
+
sprintf(
|
272 |
+
/* translators: %1$s expands to Yoast News SEO. */
|
273 |
+
__( 'Buy the %1$s plugin now and start optimizing to get your site featured in Google News!', 'wordpress-seo' ),
|
274 |
+
'Yoast News SEO'
|
275 |
+
)
|
276 |
+
)
|
277 |
+
);
|
278 |
+
}
|
279 |
+
|
280 |
+
return $extension_spot;
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Returns the courses banner spot.
|
285 |
+
*
|
286 |
+
* @return WPSEO_Admin_Banner_Spot
|
287 |
+
*/
|
288 |
+
protected function get_courses_spot() {
|
289 |
+
$courses_spot = new WPSEO_Admin_Banner_Spot( __( 'Courses', 'wordpress-seo' ), $this->banner_renderer );
|
290 |
+
|
291 |
+
$courses_spot->set_description(
|
292 |
+
sprintf(
|
293 |
+
/* translators: %1$s expands to a link start tag to the Yoast Services page, %2$s is the link closing tag. */
|
294 |
+
__( 'Do you want to get a grip on your own SEO-strategy? Learn all about it in one of %1$sour courses%2$s.', 'wordpress-seo' ),
|
295 |
+
'<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/jt' ) . '">',
|
296 |
+
'</a>'
|
297 |
+
)
|
298 |
+
);
|
299 |
+
|
300 |
+
$courses_spot->add_banner(
|
301 |
+
new WPSEO_Admin_Banner(
|
302 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/ju' ),
|
303 |
+
'basic-seo-training.png',
|
304 |
+
261,
|
305 |
+
152,
|
306 |
+
__( 'Take the online Basic SEO Training course and learn the fundamentals of SEO!', 'wordpress-seo' )
|
307 |
+
)
|
308 |
+
);
|
309 |
+
|
310 |
+
$courses_spot->add_banner(
|
311 |
+
new WPSEO_Admin_Banner(
|
312 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/jv' ),
|
313 |
+
'yoast-seo-for-wordpress-training.png',
|
314 |
+
261,
|
315 |
+
152,
|
316 |
+
sprintf(
|
317 |
+
/* translators: %1$s expands to Yoast SEO for WordPress Training, %2$s to Yoast SEO for WordPress. */
|
318 |
+
__( 'Take the %1$s course and become a certified %2$s expert!', 'wordpress-seo' ),
|
319 |
+
'Yoast SEO for WordPress Training',
|
320 |
+
'Yoast SEO for WordPress'
|
321 |
+
)
|
322 |
+
)
|
323 |
+
);
|
324 |
+
|
325 |
+
$courses_spot->add_banner(
|
326 |
+
new WPSEO_Admin_Banner(
|
327 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/jw' ),
|
328 |
+
'seo-copywriting-training.png',
|
329 |
+
261,
|
330 |
+
152,
|
331 |
+
__( 'Take the online SEO Copywriting Training course and learn how to write awesome copy that ranks!', 'wordpress-seo' )
|
332 |
+
)
|
333 |
+
);
|
334 |
+
|
335 |
+
$courses_spot->add_banner(
|
336 |
+
new WPSEO_Admin_Banner(
|
337 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/qy' ),
|
338 |
+
'site-structure-training.png',
|
339 |
+
261,
|
340 |
+
152,
|
341 |
+
__( 'Take the online Site Structure Training course and learn how to structure your website!', 'wordpress-seo' )
|
342 |
+
)
|
343 |
+
);
|
344 |
+
|
345 |
+
$courses_spot->add_banner(
|
346 |
+
new WPSEO_Admin_Banner(
|
347 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/jaa' ),
|
348 |
+
'technical-seo-training.png',
|
349 |
+
261,
|
350 |
+
152,
|
351 |
+
__( 'Take the online Technical SEO Training course and learn essential technical SEO-concepts!', 'wordpress-seo' )
|
352 |
+
)
|
353 |
+
);
|
354 |
+
|
355 |
+
$courses_spot->add_banner(
|
356 |
+
new WPSEO_Admin_Banner(
|
357 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/15h' ),
|
358 |
+
'structured-data-course.png',
|
359 |
+
261,
|
360 |
+
152,
|
361 |
+
__( 'Take the online Structured Data Training course and learn how to create rich snippets!', 'wordpress-seo' )
|
362 |
+
)
|
363 |
+
);
|
364 |
+
|
365 |
+
return $courses_spot;
|
366 |
+
}
|
367 |
+
|
368 |
+
/**
|
369 |
+
* Returns the remove banner spot.
|
370 |
+
*
|
371 |
+
* @return WPSEO_Admin_Banner_Spot
|
372 |
+
*/
|
373 |
+
protected function get_remove_banner_spot() {
|
374 |
+
|
375 |
+
$remove_banner_spot = new WPSEO_Admin_Banner_Spot(
|
376 |
+
__( 'Remove these ads?', 'wordpress-seo' )
|
377 |
+
);
|
378 |
+
|
379 |
+
$remove_banner_spot->set_description(
|
380 |
+
'<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/jy' ) . '">' .
|
381 |
+
/* translators: %1$s expands to Yoast SEO Premium */
|
382 |
+
sprintf( __( 'Upgrade to %1$s »', 'wordpress-seo' ), 'Yoast SEO Premium' ) .
|
383 |
+
'</a>'
|
384 |
+
);
|
385 |
+
|
386 |
+
return $remove_banner_spot;
|
387 |
+
}
|
388 |
+
}
|
admin/banner/class-admin-banner-spot-renderer.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Banner
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the render object for generating the html for the given banner spot.
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Banner_Spot_Renderer {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Renders the admin banner spot.
|
13 |
+
*
|
14 |
+
* @param WPSEO_Admin_Banner_Spot $banner_spot The spot to render.
|
15 |
+
*
|
16 |
+
* @return string
|
17 |
+
*/
|
18 |
+
public function render( WPSEO_Admin_Banner_Spot $banner_spot ) {
|
19 |
+
$output = '<div class="yoast-sidebar__spot">';
|
20 |
+
if ( $banner_spot->get_title() !== '' ) {
|
21 |
+
$output .= '<strong>' . $banner_spot->get_title() . '</strong>';
|
22 |
+
}
|
23 |
+
|
24 |
+
if ( $banner_spot->get_extra() !== '' ) {
|
25 |
+
$output .= $banner_spot->get_extra();
|
26 |
+
}
|
27 |
+
|
28 |
+
if ( $banner_spot->get_description() !== '' ) {
|
29 |
+
$output .= '<p>' . $banner_spot->get_description() . '</p>';
|
30 |
+
}
|
31 |
+
|
32 |
+
$output .= $banner_spot->render_banner();
|
33 |
+
$output .= '</div>';
|
34 |
+
|
35 |
+
return $output;
|
36 |
+
}
|
37 |
+
}
|
admin/banner/class-admin-banner-spot.php
ADDED
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Banner
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the an admin banner spot.
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Banner_Spot {
|
10 |
+
|
11 |
+
/** @var string */
|
12 |
+
private $title;
|
13 |
+
|
14 |
+
/** @var string */
|
15 |
+
private $description = '';
|
16 |
+
|
17 |
+
/** @var string */
|
18 |
+
private $extra = '';
|
19 |
+
|
20 |
+
/** @var WPSEO_Admin_Banner[] */
|
21 |
+
private $banners = array();
|
22 |
+
|
23 |
+
/**
|
24 |
+
* WPSEO_Admin_Banner_Spot constructor.
|
25 |
+
*
|
26 |
+
* @param string $title The title for the spot.
|
27 |
+
* @param WPSEO_Admin_Banner_Renderer $banner_renderer The renderer for the banner.
|
28 |
+
*/
|
29 |
+
public function __construct( $title, WPSEO_Admin_Banner_Renderer $banner_renderer = null ) {
|
30 |
+
$this->title = $title;
|
31 |
+
$this->banner_renderer = ( is_null( $banner_renderer ) ? new WPSEO_Admin_Banner_Renderer() : $banner_renderer );
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Returns the title.
|
36 |
+
*
|
37 |
+
* @return string
|
38 |
+
*/
|
39 |
+
public function get_title() {
|
40 |
+
return $this->title;
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Returns the description.
|
45 |
+
*
|
46 |
+
* @return string
|
47 |
+
*/
|
48 |
+
public function get_description() {
|
49 |
+
return $this->description;
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Returns the extra content.
|
54 |
+
*
|
55 |
+
* @return string
|
56 |
+
*/
|
57 |
+
public function get_extra() {
|
58 |
+
return $this->extra;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Sets the description
|
63 |
+
*
|
64 |
+
* @param string $description The description.
|
65 |
+
*/
|
66 |
+
public function set_description( $description ) {
|
67 |
+
$this->description = $description;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Sets the "extra"
|
72 |
+
*
|
73 |
+
* @param string $extra The "extra".
|
74 |
+
*/
|
75 |
+
public function set_extra( $extra ) {
|
76 |
+
$this->extra = $extra;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Adds an admin banner.
|
81 |
+
*
|
82 |
+
* @param WPSEO_Admin_Banner $banner The banner to add.
|
83 |
+
*/
|
84 |
+
public function add_banner( WPSEO_Admin_Banner $banner ) {
|
85 |
+
$this->banners[] = $banner;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Renders the banner.
|
90 |
+
*
|
91 |
+
* @return string
|
92 |
+
*/
|
93 |
+
public function render_banner() {
|
94 |
+
if ( ! $this->has_banners() ) {
|
95 |
+
return '';
|
96 |
+
}
|
97 |
+
|
98 |
+
return $this->banner_renderer->render( $this->get_random_banner() );
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Checks if there are any banners set.
|
103 |
+
*
|
104 |
+
* @return bool
|
105 |
+
*/
|
106 |
+
public function has_banners() {
|
107 |
+
return ! empty( $this->banners );
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Returns a random banner.
|
112 |
+
*
|
113 |
+
* @return null|WPSEO_Admin_Banner
|
114 |
+
*/
|
115 |
+
protected function get_random_banner() {
|
116 |
+
return $this->banners[ array_rand( $this->banners, 1 ) ];
|
117 |
+
}
|
118 |
+
}
|
admin/banner/class-admin-banner.php
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Banner
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents an admin banner.
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Banner {
|
10 |
+
|
11 |
+
/** @var string */
|
12 |
+
private $url;
|
13 |
+
|
14 |
+
/** @var string */
|
15 |
+
private $image;
|
16 |
+
|
17 |
+
/** @var integer */
|
18 |
+
private $width;
|
19 |
+
|
20 |
+
/** @var integer */
|
21 |
+
private $height;
|
22 |
+
|
23 |
+
/** @var string */
|
24 |
+
private $alt;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Sets the attributes for this object.
|
28 |
+
*
|
29 |
+
* @param string $url The URL where the banner links to.
|
30 |
+
* @param string $image The image filename.
|
31 |
+
* @param integer $width The width of the image.
|
32 |
+
* @param integer $height The height of the image.
|
33 |
+
* @param string $alt The alt text for the image.
|
34 |
+
*/
|
35 |
+
public function __construct( $url, $image, $width, $height, $alt = '' ) {
|
36 |
+
$this->url = $url;
|
37 |
+
$this->image = $image;
|
38 |
+
$this->alt = $alt;
|
39 |
+
$this->width = $width;
|
40 |
+
$this->height = $height;
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Returns the set url.
|
45 |
+
*
|
46 |
+
* @return string
|
47 |
+
*/
|
48 |
+
public function get_url() {
|
49 |
+
return $this->url;
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Returns the image.
|
54 |
+
*
|
55 |
+
* @return string
|
56 |
+
*/
|
57 |
+
public function get_image() {
|
58 |
+
return $this->image;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Returns the alt-text.
|
63 |
+
*
|
64 |
+
* @return string
|
65 |
+
*/
|
66 |
+
public function get_alt() {
|
67 |
+
return $this->alt;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Returns the width.
|
72 |
+
*
|
73 |
+
* @return string
|
74 |
+
*/
|
75 |
+
public function get_width() {
|
76 |
+
return $this->width;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Returns the height.
|
81 |
+
*
|
82 |
+
* @return string
|
83 |
+
*/
|
84 |
+
public function get_height() {
|
85 |
+
return $this->height;
|
86 |
+
}
|
87 |
+
}
|
admin/capabilities/class-abstract-capability-manager.php
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Capabilities
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Abstract Capability Manager shared code.
|
8 |
+
*/
|
9 |
+
abstract class WPSEO_Abstract_Capability_Manager implements WPSEO_Capability_Manager {
|
10 |
+
/** @var array Registered capabilities */
|
11 |
+
protected $capabilities = array();
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Registers a capability.
|
15 |
+
*
|
16 |
+
* @param string $capability Capability to register.
|
17 |
+
* @param array $roles Roles to add the capability to.
|
18 |
+
* @param bool $overwrite Optional. Use add or overwrite as registration method.
|
19 |
+
*/
|
20 |
+
public function register( $capability, array $roles, $overwrite = false ) {
|
21 |
+
if ( $overwrite || ! isset( $this->capabilities[ $capability ] ) ) {
|
22 |
+
$this->capabilities[ $capability ] = $roles;
|
23 |
+
|
24 |
+
return;
|
25 |
+
}
|
26 |
+
|
27 |
+
// Combine configurations.
|
28 |
+
$this->capabilities[ $capability ] = array_merge( $roles, $this->capabilities[ $capability ] );
|
29 |
+
|
30 |
+
// Remove doubles.
|
31 |
+
$this->capabilities[ $capability ] = array_unique( $this->capabilities[ $capability ] );
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Returns the list of registered capabilitities.
|
36 |
+
*
|
37 |
+
* @return string[] Registered capabilities.
|
38 |
+
*/
|
39 |
+
public function get_capabilities() {
|
40 |
+
return array_keys( $this->capabilities );
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Returns a list of WP_Role roles.
|
45 |
+
*
|
46 |
+
* The string array of role names are converted to actual WP_Role objects.
|
47 |
+
* These are needed to be able to use the API on them.
|
48 |
+
*
|
49 |
+
* @param array $roles Roles to retrieve the objects for.
|
50 |
+
*
|
51 |
+
* @return WP_Role[] List of WP_Role objects.
|
52 |
+
*/
|
53 |
+
protected function get_wp_roles( array $roles ) {
|
54 |
+
$wp_roles = array_map( 'get_role', $roles );
|
55 |
+
|
56 |
+
return array_filter( $wp_roles );
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Filter capability roles.
|
61 |
+
*
|
62 |
+
* @param string $capability Capability to filter roles for.
|
63 |
+
* @param array $roles List of roles which can be filtered.
|
64 |
+
*
|
65 |
+
* @return array Filtered list of roles for the capability.
|
66 |
+
*/
|
67 |
+
protected function filter_roles( $capability, array $roles ) {
|
68 |
+
/**
|
69 |
+
* Filter: Allow changing roles that a capability is added to.
|
70 |
+
*
|
71 |
+
* @api array $roles The default roles to be filtered.
|
72 |
+
*/
|
73 |
+
$filtered = apply_filters( $capability . '_roles', $roles );
|
74 |
+
|
75 |
+
// Make sure we have the expected type.
|
76 |
+
if ( ! is_array( $filtered ) ) {
|
77 |
+
return array();
|
78 |
+
}
|
79 |
+
|
80 |
+
return $filtered;
|
81 |
+
}
|
82 |
+
}
|
admin/capabilities/class-capability-manager-factory.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Capabilities
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Capability Manager Factory.
|
8 |
+
*/
|
9 |
+
class WPSEO_Capability_Manager_Factory {
|
10 |
+
/**
|
11 |
+
* Returns the Manager to use.
|
12 |
+
*
|
13 |
+
* @return WPSEO_Capability_Manager Manager to use.
|
14 |
+
*/
|
15 |
+
public static function get() {
|
16 |
+
static $manager = null;
|
17 |
+
|
18 |
+
if ( $manager === null ) {
|
19 |
+
if ( function_exists( 'wpcom_vip_add_role_caps' ) ) {
|
20 |
+
$manager = new WPSEO_Capability_Manager_VIP();
|
21 |
+
}
|
22 |
+
|
23 |
+
if ( ! function_exists( 'wpcom_vip_add_role_caps' ) ) {
|
24 |
+
$manager = new WPSEO_Capability_Manager_WP();
|
25 |
+
}
|
26 |
+
}
|
27 |
+
|
28 |
+
return $manager;
|
29 |
+
}
|
30 |
+
}
|
admin/capabilities/class-capability-manager-integration.php
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Capabilities
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Integrates Yoast SEO capabilities with third party role manager plugins.
|
8 |
+
*
|
9 |
+
* Integrates with: Members
|
10 |
+
* Integrates with: User Role Editor
|
11 |
+
*/
|
12 |
+
class WPSEO_Capability_Manager_Integration implements WPSEO_WordPress_Integration {
|
13 |
+
|
14 |
+
/** @var WPSEO_Capability_Manager Capability manager to use. */
|
15 |
+
public $manager;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* WPSEO_Capability_Manager_Integration constructor.
|
19 |
+
*
|
20 |
+
* @param WPSEO_Capability_Manager $manager The capability manager to use.
|
21 |
+
*/
|
22 |
+
public function __construct( WPSEO_Capability_Manager $manager ) {
|
23 |
+
$this->manager = $manager;
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Registers the hooks.
|
28 |
+
*
|
29 |
+
* @return void
|
30 |
+
*/
|
31 |
+
public function register_hooks() {
|
32 |
+
add_filter( 'members_get_capabilities', array( $this, 'get_capabilities' ) );
|
33 |
+
add_action( 'members_register_cap_groups', array( $this, 'action_members_register_cap_group' ) );
|
34 |
+
|
35 |
+
add_filter( 'ure_capabilities_groups_tree', array( $this, 'filter_ure_capabilities_groups_tree' ) );
|
36 |
+
add_filter( 'ure_custom_capability_groups', array( $this, 'filter_ure_custom_capability_groups' ), 10, 2 );
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Get the Yoast SEO capabilities.
|
41 |
+
* Optionally append them to an existing array.
|
42 |
+
*
|
43 |
+
* @param array $caps Optional existing capability list.
|
44 |
+
* @return array
|
45 |
+
*/
|
46 |
+
public function get_capabilities( array $caps = array() ) {
|
47 |
+
if ( ! did_action( 'wpseo_register_capabilities' ) ) {
|
48 |
+
do_action( 'wpseo_register_capabilities' );
|
49 |
+
}
|
50 |
+
|
51 |
+
return array_merge( $caps, $this->manager->get_capabilities() );
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Add capabilities to its own group in the Members plugin.
|
56 |
+
*
|
57 |
+
* @see members_register_cap_group()
|
58 |
+
*/
|
59 |
+
public function action_members_register_cap_group() {
|
60 |
+
if ( ! function_exists( 'members_register_cap_group' ) ) {
|
61 |
+
return;
|
62 |
+
}
|
63 |
+
// Register the yoast group.
|
64 |
+
members_register_cap_group( 'wordpress-seo',
|
65 |
+
array(
|
66 |
+
'label' => esc_html__( 'Yoast SEO', 'wordpress-seo' ),
|
67 |
+
'caps' => $this->get_capabilities(),
|
68 |
+
'icon' => 'dashicons-admin-plugins',
|
69 |
+
'diff_added' => true,
|
70 |
+
)
|
71 |
+
);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Adds Yoast SEO capability group in the User Role Editor plugin.
|
76 |
+
*
|
77 |
+
* @see URE_Capabilities_Groups_Manager::get_groups_tree()
|
78 |
+
*
|
79 |
+
* @param array $groups Current groups.
|
80 |
+
*
|
81 |
+
* @return array Filtered list of capabilty groups.
|
82 |
+
*/
|
83 |
+
public function filter_ure_capabilities_groups_tree( $groups = array() ) {
|
84 |
+
$groups = (array) $groups;
|
85 |
+
|
86 |
+
$groups['wordpress-seo'] = array(
|
87 |
+
'caption' => 'Yoast SEO',
|
88 |
+
'parent' => 'custom',
|
89 |
+
'level' => 3,
|
90 |
+
);
|
91 |
+
|
92 |
+
return $groups;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Adds capabilities to the Yoast SEO group in the User Role Editor plugin.
|
97 |
+
*
|
98 |
+
* @see URE_Capabilities_Groups_Manager::get_cap_groups()
|
99 |
+
*
|
100 |
+
* @param array $groups Current capability groups.
|
101 |
+
* @param string $cap_id Capability identifier.
|
102 |
+
*
|
103 |
+
* @return array List of filtered groups.
|
104 |
+
*/
|
105 |
+
public function filter_ure_custom_capability_groups( $groups = array(), $cap_id = '' ) {
|
106 |
+
if ( in_array( $cap_id, $this->get_capabilities(), true ) ) {
|
107 |
+
$groups = (array) $groups;
|
108 |
+
$groups[] = 'wordpress-seo';
|
109 |
+
}
|
110 |
+
|
111 |
+
return $groups;
|
112 |
+
}
|
113 |
+
}
|
admin/capabilities/class-capability-manager-vip.php
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Capabilities
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* VIP implementation of the Capability Manager.
|
8 |
+
*/
|
9 |
+
final class WPSEO_Capability_Manager_VIP extends WPSEO_Abstract_Capability_Manager {
|
10 |
+
/**
|
11 |
+
* Adds the registered capabilities to the system.
|
12 |
+
*
|
13 |
+
* @return void
|
14 |
+
*/
|
15 |
+
public function add() {
|
16 |
+
$role_capabilities = array();
|
17 |
+
foreach ( $this->capabilities as $capability => $roles ) {
|
18 |
+
$role_capabilities = $this->get_role_capabilities( $role_capabilities, $capability, $roles );
|
19 |
+
}
|
20 |
+
|
21 |
+
foreach ( $role_capabilities as $role => $capabilities ) {
|
22 |
+
wpcom_vip_add_role_caps( $role, $capabilities );
|
23 |
+
}
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Removes the registered capabilities from the system
|
28 |
+
*
|
29 |
+
* @return void
|
30 |
+
*/
|
31 |
+
public function remove() {
|
32 |
+
// Remove from any role it has been added to.
|
33 |
+
$roles = wp_roles()->get_names();
|
34 |
+
$roles = array_keys( $roles );
|
35 |
+
|
36 |
+
$role_capabilities = array();
|
37 |
+
foreach ( array_keys( $this->capabilities ) as $capability ) {
|
38 |
+
// Allow filtering of roles.
|
39 |
+
$role_capabilities = $this->get_role_capabilities( $role_capabilities, $capability, $roles );
|
40 |
+
}
|
41 |
+
|
42 |
+
foreach ( $role_capabilities as $role => $capabilities ) {
|
43 |
+
wpcom_vip_remove_role_caps( $role, $capabilities );
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Returns the roles which the capability is registered on.
|
49 |
+
*
|
50 |
+
* @param array $role_capabilities List of all roles with their capabilities.
|
51 |
+
* @param string $capability Capability to filter roles for.
|
52 |
+
* @param array $roles List of default roles.
|
53 |
+
*
|
54 |
+
* @return array List of capabilities.
|
55 |
+
*/
|
56 |
+
protected function get_role_capabilities( $role_capabilities, $capability, $roles ) {
|
57 |
+
// Allow filtering of roles.
|
58 |
+
$filtered_roles = $this->filter_roles( $capability, $roles );
|
59 |
+
|
60 |
+
foreach ( $filtered_roles as $role ) {
|
61 |
+
if ( ! isset( $add_role_caps[ $role ] ) ) {
|
62 |
+
$role_capabilities[ $role ] = array();
|
63 |
+
}
|
64 |
+
|
65 |
+
$role_capabilities[ $role ][] = $capability;
|
66 |
+
}
|
67 |
+
|
68 |
+
return $role_capabilities;
|
69 |
+
}
|
70 |
+
}
|
admin/capabilities/class-capability-manager-wp.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Capabilities
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Default WordPress capability manager implementation.
|
8 |
+
*/
|
9 |
+
final class WPSEO_Capability_Manager_WP extends WPSEO_Abstract_Capability_Manager {
|
10 |
+
/**
|
11 |
+
* Adds the capabilities to the roles.
|
12 |
+
*
|
13 |
+
* @return void
|
14 |
+
*/
|
15 |
+
public function add() {
|
16 |
+
foreach ( $this->capabilities as $capability => $roles ) {
|
17 |
+
$filtered_roles = $this->filter_roles( $capability, $roles );
|
18 |
+
|
19 |
+
$wp_roles = $this->get_wp_roles( $filtered_roles );
|
20 |
+
foreach ( $wp_roles as $wp_role ) {
|
21 |
+
$wp_role->add_cap( $capability );
|
22 |
+
}
|
23 |
+
}
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Unregisters the capabilities from the system.
|
28 |
+
*
|
29 |
+
* @return void
|
30 |
+
*/
|
31 |
+
public function remove() {
|
32 |
+
// Remove from any roles it has been added to.
|
33 |
+
$roles = wp_roles()->get_names();
|
34 |
+
$roles = array_keys( $roles );
|
35 |
+
|
36 |
+
foreach ( $this->capabilities as $capability => $_roles ) {
|
37 |
+
$registered_roles = array_unique( array_merge( $roles, $this->capabilities[ $capability ] ) );
|
38 |
+
|
39 |
+
// Allow filtering of roles.
|
40 |
+
$filtered_roles = $this->filter_roles( $capability, $registered_roles );
|
41 |
+
|
42 |
+
$wp_roles = $this->get_wp_roles( $filtered_roles );
|
43 |
+
foreach ( $wp_roles as $wp_role ) {
|
44 |
+
$wp_role->remove_cap( $capability );
|
45 |
+
}
|
46 |
+
}
|
47 |
+
}
|
48 |
+
}
|
admin/capabilities/class-capability-manager.php
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Capabilities
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Capability Manager interface.
|
8 |
+
*/
|
9 |
+
interface WPSEO_Capability_Manager {
|
10 |
+
/**
|
11 |
+
* Registers a capability.
|
12 |
+
*
|
13 |
+
* @param string $capability Capability to register.
|
14 |
+
* @param array $roles Roles to add the capability to.
|
15 |
+
* @param bool $overwrite Optional. Use add or overwrite as registration method.
|
16 |
+
*/
|
17 |
+
public function register( $capability, array $roles, $overwrite = false );
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Adds the registerd capabilities to the system.
|
21 |
+
*/
|
22 |
+
public function add();
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Removes the registered capabilities from the system.
|
26 |
+
*/
|
27 |
+
public function remove();
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Returns the list of registered capabilities.
|
31 |
+
*
|
32 |
+
* @return string[] List of registered capabilities.
|
33 |
+
*/
|
34 |
+
public function get_capabilities();
|
35 |
+
}
|
admin/capabilities/class-capability-utils.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Capabilities
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Capability Utils collection.
|
8 |
+
*/
|
9 |
+
class WPSEO_Capability_Utils {
|
10 |
+
/**
|
11 |
+
* Checks if the user has the proper capabilities.
|
12 |
+
*
|
13 |
+
* @param string $capability Capability to check.
|
14 |
+
*
|
15 |
+
* @return bool True if the user has the proper rights.
|
16 |
+
*/
|
17 |
+
public static function current_user_can( $capability ) {
|
18 |
+
if ( $capability === 'wpseo_manage_options' ) {
|
19 |
+
return self::has( $capability );
|
20 |
+
}
|
21 |
+
|
22 |
+
return self::has_any( array( 'wpseo_manage_options', $capability ) );
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Checks if the current user has at least one of the supplied capabilities.
|
27 |
+
*
|
28 |
+
* @param array $capabilities Capabilities to check against.
|
29 |
+
*
|
30 |
+
* @return bool True if the user has at least one capability.
|
31 |
+
*/
|
32 |
+
protected static function has_any( array $capabilities ) {
|
33 |
+
foreach ( $capabilities as $capability ) {
|
34 |
+
if ( self::has( $capability ) ) {
|
35 |
+
return true;
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
return false;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Checks if the user has a certain capability.
|
44 |
+
*
|
45 |
+
* @param string $capability Capability to check against.
|
46 |
+
*
|
47 |
+
* @return bool True if the user has the capability.
|
48 |
+
*/
|
49 |
+
protected static function has( $capability ) {
|
50 |
+
return current_user_can( $capability );
|
51 |
+
}
|
52 |
+
}
|
admin/capabilities/class-register-capabilities.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Capabilities
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Capabilities registration class.
|
8 |
+
*/
|
9 |
+
class WPSEO_Register_Capabilities implements WPSEO_WordPress_Integration {
|
10 |
+
/**
|
11 |
+
* Registers the hooks.
|
12 |
+
*
|
13 |
+
* @return void
|
14 |
+
*/
|
15 |
+
public function register_hooks() {
|
16 |
+
add_action( 'wpseo_register_capabilities', array( $this, 'register' ) );
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Registers the capabilities.
|
21 |
+
*
|
22 |
+
* @return void
|
23 |
+
*/
|
24 |
+
public function register() {
|
25 |
+
$manager = WPSEO_Capability_Manager_Factory::get();
|
26 |
+
|
27 |
+
$manager->register( 'wpseo_bulk_edit', array( 'editor', 'wpseo_editor', 'wpseo_manager' ) );
|
28 |
+
$manager->register( 'wpseo_edit_advanced_metadata', array( 'wpseo_editor', 'wpseo_manager' ) );
|
29 |
+
|
30 |
+
$manager->register( 'wpseo_manage_options', array( 'wpseo_manager' ) );
|
31 |
+
|
32 |
+
/*
|
33 |
+
* Respect MultiSite 'access' setting if set to 'super admins only'.
|
34 |
+
* This means that local admins do not get the `wpseo_manage_options` capability.
|
35 |
+
*/
|
36 |
+
if ( WPSEO_Options::get( 'access' ) !== 'superadmins' ) {
|
37 |
+
$manager->register( 'wpseo_manage_options', array( 'administrator' ) );
|
38 |
+
}
|
39 |
+
}
|
40 |
+
}
|
admin/class-admin-asset-dev-server-location.php
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Changes the asset paths to dev server paths.
|
8 |
+
*/
|
9 |
+
final class WPSEO_Admin_Asset_Dev_Server_Location implements WPSEO_Admin_Asset_Location {
|
10 |
+
const DEFAULT_URL = 'http://localhost:8080';
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @var array
|
14 |
+
*/
|
15 |
+
private static $dev_server_script = array(
|
16 |
+
'commons',
|
17 |
+
'configuration-wizard',
|
18 |
+
'wp-seo-dashboard-widget',
|
19 |
+
'wp-seo-help-center',
|
20 |
+
'wp-seo-metabox',
|
21 |
+
'wp-seo-post-scraper',
|
22 |
+
'wp-seo-term-scraper',
|
23 |
+
);
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var string
|
27 |
+
*/
|
28 |
+
private $url;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @param string $url Where the dev server is located.
|
32 |
+
*/
|
33 |
+
public function __construct( $url = null ) {
|
34 |
+
if ( $url === null ) {
|
35 |
+
$url = self::DEFAULT_URL;
|
36 |
+
}
|
37 |
+
|
38 |
+
$this->url = $url;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Determines the URL of the asset on the dev server.
|
43 |
+
*
|
44 |
+
* @param WPSEO_Admin_Asset $asset The asset to determine the URL for.
|
45 |
+
* @param string $type The type of asset. Usually JS or CSS.
|
46 |
+
*
|
47 |
+
* @return string The URL of the asset.
|
48 |
+
*/
|
49 |
+
public function get_url( WPSEO_Admin_Asset $asset, $type ) {
|
50 |
+
if ( WPSEO_Admin_Asset::TYPE_CSS === $type ) {
|
51 |
+
return $this->get_default_url( $asset, $type );
|
52 |
+
}
|
53 |
+
|
54 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
55 |
+
$flat_version = $asset_manager->flatten_version( WPSEO_VERSION );
|
56 |
+
$version_less_source = str_replace( '-' . $flat_version, '', $asset->get_src() );
|
57 |
+
|
58 |
+
if ( ! in_array( $version_less_source, self::$dev_server_script, true ) ) {
|
59 |
+
return $this->get_default_url( $asset, $type );
|
60 |
+
}
|
61 |
+
|
62 |
+
$path = sprintf( '%s%s.js', $asset->get_src(), $asset->get_suffix() );
|
63 |
+
|
64 |
+
return trailingslashit( $this->url ) . $path;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Determines the URL of the asset not using the dev server.
|
69 |
+
*
|
70 |
+
* @param WPSEO_Admin_Asset $asset The asset to determine the URL for.
|
71 |
+
* @param string $type The type of asset.
|
72 |
+
*
|
73 |
+
* @return string The URL of the asset file.
|
74 |
+
*/
|
75 |
+
public function get_default_url( WPSEO_Admin_Asset $asset, $type ) {
|
76 |
+
$default_location = new WPSEO_Admin_Asset_SEO_Location( WPSEO_FILE );
|
77 |
+
|
78 |
+
return $default_location->get_url( $asset, $type );
|
79 |
+
}
|
80 |
+
}
|
admin/class-admin-asset-location.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO|Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents a way to determine an assets location.
|
8 |
+
*/
|
9 |
+
interface WPSEO_Admin_Asset_Location {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Determines the URL of the asset on the dev server.
|
13 |
+
*
|
14 |
+
* @param WPSEO_Admin_Asset $asset The asset to determine the URL for.
|
15 |
+
* @param string $type The type of asset. Usually JS or CSS.
|
16 |
+
*
|
17 |
+
* @return string The URL of the asset.
|
18 |
+
*/
|
19 |
+
public function get_url( WPSEO_Admin_Asset $asset, $type );
|
20 |
+
}
|
admin/class-admin-asset-manager.php
ADDED
@@ -0,0 +1,493 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class registers all the necessary styles and scripts. Also has methods for the enqueing of scripts and styles. It automatically adds a prefix to the handle.
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Asset_Manager {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WPSEO_Admin_Asset_Location
|
13 |
+
*/
|
14 |
+
protected $asset_location;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Prefix for naming the assets.
|
18 |
+
*/
|
19 |
+
const PREFIX = 'yoast-seo-';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Constructs a manager of assets. Needs a location to know where to register assets at.
|
23 |
+
*
|
24 |
+
* @param WPSEO_Admin_Asset_Location $asset_location The provider of the asset location.
|
25 |
+
*/
|
26 |
+
public function __construct( WPSEO_Admin_Asset_Location $asset_location = null ) {
|
27 |
+
if ( $asset_location === null ) {
|
28 |
+
$asset_location = self::create_default_location();
|
29 |
+
}
|
30 |
+
|
31 |
+
$this->asset_location = $asset_location;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Enqueues scripts.
|
36 |
+
*
|
37 |
+
* @param string $script The name of the script to enqueue.
|
38 |
+
*/
|
39 |
+
public function enqueue_script( $script ) {
|
40 |
+
wp_enqueue_script( self::PREFIX . $script );
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Enqueues styles.
|
45 |
+
*
|
46 |
+
* @param string $style The name of the style to enqueue.
|
47 |
+
*/
|
48 |
+
public function enqueue_style( $style ) {
|
49 |
+
wp_enqueue_style( self::PREFIX . $style );
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Registers scripts based on it's parameters.
|
54 |
+
*
|
55 |
+
* @param WPSEO_Admin_Asset $script The script to register.
|
56 |
+
*/
|
57 |
+
public function register_script( WPSEO_Admin_Asset $script ) {
|
58 |
+
wp_register_script(
|
59 |
+
self::PREFIX . $script->get_name(),
|
60 |
+
$this->asset_location->get_url( $script, WPSEO_Admin_Asset::TYPE_JS ),
|
61 |
+
$script->get_deps(),
|
62 |
+
$script->get_version(),
|
63 |
+
$script->is_in_footer()
|
64 |
+
);
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Registers styles based on it's parameters.
|
69 |
+
*
|
70 |
+
* @param WPSEO_Admin_Asset $style The style to register.
|
71 |
+
*/
|
72 |
+
public function register_style( WPSEO_Admin_Asset $style ) {
|
73 |
+
wp_register_style(
|
74 |
+
self::PREFIX . $style->get_name(),
|
75 |
+
$this->asset_location->get_url( $style, WPSEO_Admin_Asset::TYPE_CSS ),
|
76 |
+
$style->get_deps(),
|
77 |
+
$style->get_version(),
|
78 |
+
$style->get_media()
|
79 |
+
);
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Calls the functions that register scripts and styles with the scripts and styles to be registered as arguments.
|
84 |
+
*/
|
85 |
+
public function register_assets() {
|
86 |
+
|
87 |
+
$user_locale = WPSEO_Utils::get_user_locale();
|
88 |
+
$language = WPSEO_Utils::get_language( $user_locale );
|
89 |
+
|
90 |
+
wp_register_script(
|
91 |
+
self::PREFIX . 'intl-polyfill',
|
92 |
+
sprintf( 'https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.%s', $language ),
|
93 |
+
array(),
|
94 |
+
WPSEO_VERSION
|
95 |
+
);
|
96 |
+
|
97 |
+
$this->register_scripts( $this->scripts_to_be_registered() );
|
98 |
+
$this->register_styles( $this->styles_to_be_registered() );
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Registers all the scripts passed to it.
|
103 |
+
*
|
104 |
+
* @param array $scripts The scripts passed to it.
|
105 |
+
*/
|
106 |
+
public function register_scripts( $scripts ) {
|
107 |
+
foreach ( $scripts as $script ) {
|
108 |
+
$script = new WPSEO_Admin_Asset( $script );
|
109 |
+
$this->register_script( $script );
|
110 |
+
}
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Registers all the styles it recieves.
|
115 |
+
*
|
116 |
+
* @param array $styles Styles that need to be registerd.
|
117 |
+
*/
|
118 |
+
public function register_styles( $styles ) {
|
119 |
+
foreach ( $styles as $style ) {
|
120 |
+
$style = new WPSEO_Admin_Asset( $style );
|
121 |
+
$this->register_style( $style );
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* A list of styles that shouldn't be registered but are needed in other locations in the plugin.
|
127 |
+
*
|
128 |
+
* @return array
|
129 |
+
*/
|
130 |
+
public function special_styles() {
|
131 |
+
$flat_version = $this->flatten_version( WPSEO_VERSION );
|
132 |
+
|
133 |
+
return array(
|
134 |
+
'inside-editor' => new WPSEO_Admin_Asset( array(
|
135 |
+
'name' => 'inside-editor',
|
136 |
+
'src' => 'inside-editor-' . $flat_version,
|
137 |
+
) ),
|
138 |
+
);
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Flattens a version number for use in a filename
|
143 |
+
*
|
144 |
+
* @param string $version The original version number.
|
145 |
+
* @return string The flattened version number.
|
146 |
+
*/
|
147 |
+
public function flatten_version( $version ) {
|
148 |
+
$parts = explode( '.', $version );
|
149 |
+
|
150 |
+
if ( count( $parts ) === 2 && preg_match( '/^\d+$/', $parts[1] ) === 1 ) {
|
151 |
+
$parts[] = '0';
|
152 |
+
}
|
153 |
+
|
154 |
+
return implode( '', $parts );
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Creates a default location object for use in the admin asset manager.
|
159 |
+
*
|
160 |
+
* @return WPSEO_Admin_Asset_Location The location to use in the asset manager.
|
161 |
+
*/
|
162 |
+
public static function create_default_location() {
|
163 |
+
if ( defined( 'YOAST_SEO_DEV_SERVER' ) && YOAST_SEO_DEV_SERVER ) {
|
164 |
+
$url = defined( 'YOAST_SEO_DEV_SERVER_URL' ) ? YOAST_SEO_DEV_SERVER_URL : WPSEO_Admin_Asset_Dev_Server_Location::DEFAULT_URL;
|
165 |
+
|
166 |
+
return new WPSEO_Admin_Asset_Dev_Server_Location( $url );
|
167 |
+
}
|
168 |
+
|
169 |
+
return new WPSEO_Admin_Asset_SEO_Location( WPSEO_FILE );
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Returns the scripts that need to be registered.
|
174 |
+
*
|
175 |
+
* @todo Data format is not self-documenting. Needs explanation inline. R.
|
176 |
+
*
|
177 |
+
* @return array scripts that need to be registered.
|
178 |
+
*/
|
179 |
+
private function scripts_to_be_registered() {
|
180 |
+
|
181 |
+
$select2_language = 'en';
|
182 |
+
$user_locale = WPSEO_Utils::get_user_locale();
|
183 |
+
$language = WPSEO_Utils::get_language( $user_locale );
|
184 |
+
|
185 |
+
if ( file_exists( WPSEO_PATH . "js/dist/select2/i18n/{$user_locale}.js" ) ) {
|
186 |
+
$select2_language = $user_locale; // Chinese and some others use full locale.
|
187 |
+
}
|
188 |
+
elseif ( file_exists( WPSEO_PATH . "js/dist/select2/i18n/{$language}.js" ) ) {
|
189 |
+
$select2_language = $language;
|
190 |
+
}
|
191 |
+
|
192 |
+
$flat_version = $this->flatten_version( WPSEO_VERSION );
|
193 |
+
|
194 |
+
return array(
|
195 |
+
array(
|
196 |
+
'name' => 'react-dependencies',
|
197 |
+
// Load webpack-commons for bundle support.
|
198 |
+
'src' => 'commons-' . $flat_version,
|
199 |
+
'deps' => array(
|
200 |
+
self::PREFIX . 'intl-polyfill',
|
201 |
+
self::PREFIX . 'babel-polyfill',
|
202 |
+
),
|
203 |
+
),
|
204 |
+
array(
|
205 |
+
'name' => 'help-center',
|
206 |
+
'src' => 'wp-seo-help-center-' . $flat_version,
|
207 |
+
'deps' => array(
|
208 |
+
'jquery',
|
209 |
+
self::PREFIX . 'react-dependencies',
|
210 |
+
),
|
211 |
+
),
|
212 |
+
array(
|
213 |
+
'name' => 'admin-script',
|
214 |
+
'src' => 'wp-seo-admin-' . $flat_version,
|
215 |
+
'deps' => array(
|
216 |
+
'jquery',
|
217 |
+
'jquery-ui-core',
|
218 |
+
'jquery-ui-progressbar',
|
219 |
+
self::PREFIX . 'select2',
|
220 |
+
self::PREFIX . 'select2-translations',
|
221 |
+
self::PREFIX . 'babel-polyfill',
|
222 |
+
),
|
223 |
+
),
|
224 |
+
array(
|
225 |
+
'name' => 'admin-media',
|
226 |
+
'src' => 'wp-seo-admin-media-' . $flat_version,
|
227 |
+
'deps' => array(
|
228 |
+
'jquery',
|
229 |
+
'jquery-ui-core',
|
230 |
+
self::PREFIX . 'babel-polyfill',
|
231 |
+
),
|
232 |
+
),
|
233 |
+
array(
|
234 |
+
'name' => 'bulk-editor',
|
235 |
+
'src' => 'wp-seo-bulk-editor-' . $flat_version,
|
236 |
+
'deps' => array( 'jquery', self::PREFIX . 'babel-polyfill' ),
|
237 |
+
),
|
238 |
+
array(
|
239 |
+
'name' => 'dismissible',
|
240 |
+
'src' => 'wp-seo-dismissible-' . $flat_version,
|
241 |
+
'deps' => array( 'jquery', self::PREFIX . 'babel-polyfill' ),
|
242 |
+
),
|
243 |
+
array(
|
244 |
+
'name' => 'admin-global-script',
|
245 |
+
'src' => 'wp-seo-admin-global-' . $flat_version,
|
246 |
+
'deps' => array( 'jquery', self::PREFIX . 'babel-polyfill' ),
|
247 |
+
),
|
248 |
+
array(
|
249 |
+
'name' => 'metabox',
|
250 |
+
'src' => 'wp-seo-metabox-' . $flat_version,
|
251 |
+
'deps' => array(
|
252 |
+
'jquery',
|
253 |
+
self::PREFIX . 'select2',
|
254 |
+
self::PREFIX . 'select2-translations',
|
255 |
+
self::PREFIX . 'react-dependencies',
|
256 |
+
),
|
257 |
+
'in_footer' => false,
|
258 |
+
),
|
259 |
+
array(
|
260 |
+
'name' => 'featured-image',
|
261 |
+
'src' => 'wp-seo-featured-image-' . $flat_version,
|
262 |
+
'deps' => array(
|
263 |
+
'jquery',
|
264 |
+
self::PREFIX . 'babel-polyfill',
|
265 |
+
),
|
266 |
+
),
|
267 |
+
array(
|
268 |
+
'name' => 'admin-gsc',
|
269 |
+
'src' => 'wp-seo-admin-gsc-' . $flat_version,
|
270 |
+
'deps' => array( self::PREFIX . 'babel-polyfill' ),
|
271 |
+
'in_footer' => false,
|
272 |
+
),
|
273 |
+
array(
|
274 |
+
'name' => 'post-scraper',
|
275 |
+
'src' => 'wp-seo-post-scraper-' . $flat_version,
|
276 |
+
'deps' => array(
|
277 |
+
self::PREFIX . 'replacevar-plugin',
|
278 |
+
self::PREFIX . 'shortcode-plugin',
|
279 |
+
'wp-util',
|
280 |
+
self::PREFIX . 'react-dependencies',
|
281 |
+
),
|
282 |
+
),
|
283 |
+
array(
|
284 |
+
'name' => 'term-scraper',
|
285 |
+
'src' => 'wp-seo-term-scraper-' . $flat_version,
|
286 |
+
'deps' => array(
|
287 |
+
self::PREFIX . 'replacevar-plugin',
|
288 |
+
self::PREFIX . 'react-dependencies',
|
289 |
+
),
|
290 |
+
),
|
291 |
+
array(
|
292 |
+
'name' => 'replacevar-plugin',
|
293 |
+
'src' => 'wp-seo-replacevar-plugin-' . $flat_version,
|
294 |
+
'deps' => array(
|
295 |
+
self::PREFIX . 'babel-polyfill',
|
296 |
+
),
|
297 |
+
),
|
298 |
+
array(
|
299 |
+
'name' => 'shortcode-plugin',
|
300 |
+
'src' => 'wp-seo-shortcode-plugin-' . $flat_version,
|
301 |
+
'deps' => array(
|
302 |
+
self::PREFIX . 'babel-polyfill',
|
303 |
+
),
|
304 |
+
),
|
305 |
+
array(
|
306 |
+
'name' => 'recalculate',
|
307 |
+
'src' => 'wp-seo-recalculate-' . $flat_version,
|
308 |
+
'deps' => array(
|
309 |
+
'jquery',
|
310 |
+
'jquery-ui-core',
|
311 |
+
'jquery-ui-progressbar',
|
312 |
+
self::PREFIX . 'babel-polyfill',
|
313 |
+
),
|
314 |
+
),
|
315 |
+
array(
|
316 |
+
'name' => 'primary-category',
|
317 |
+
'src' => 'wp-seo-metabox-category-' . $flat_version,
|
318 |
+
'deps' => array(
|
319 |
+
'jquery',
|
320 |
+
'wp-util',
|
321 |
+
self::PREFIX . 'babel-polyfill',
|
322 |
+
),
|
323 |
+
),
|
324 |
+
array(
|
325 |
+
'name' => 'select2',
|
326 |
+
'src' => 'select2/select2.full',
|
327 |
+
'suffix' => '.min',
|
328 |
+
'deps' => array(
|
329 |
+
'jquery',
|
330 |
+
),
|
331 |
+
'version' => '4.0.3',
|
332 |
+
),
|
333 |
+
array(
|
334 |
+
'name' => 'select2-translations',
|
335 |
+
'src' => 'select2/i18n/' . $select2_language,
|
336 |
+
'deps' => array(
|
337 |
+
'jquery',
|
338 |
+
self::PREFIX . 'select2',
|
339 |
+
),
|
340 |
+
'version' => '4.0.3',
|
341 |
+
'suffix' => '',
|
342 |
+
),
|
343 |
+
array(
|
344 |
+
'name' => 'configuration-wizard',
|
345 |
+
'src' => 'configuration-wizard-' . $flat_version,
|
346 |
+
'deps' => array(
|
347 |
+
'jquery',
|
348 |
+
self::PREFIX . 'react-dependencies',
|
349 |
+
),
|
350 |
+
),
|
351 |
+
// Register for backwards-compatiblity.
|
352 |
+
array(
|
353 |
+
'name' => 'polyfill',
|
354 |
+
'src' => 'wp-seo-babel-polyfill-' . $flat_version,
|
355 |
+
),
|
356 |
+
array(
|
357 |
+
'name' => 'babel-polyfill',
|
358 |
+
'src' => 'wp-seo-babel-polyfill-' . $flat_version,
|
359 |
+
),
|
360 |
+
array(
|
361 |
+
'name' => 'reindex-links',
|
362 |
+
'src' => 'wp-seo-reindex-links-' . $flat_version,
|
363 |
+
'deps' => array(
|
364 |
+
'jquery',
|
365 |
+
'jquery-ui-core',
|
366 |
+
'jquery-ui-progressbar',
|
367 |
+
),
|
368 |
+
),
|
369 |
+
array(
|
370 |
+
'name' => 'edit-page-script',
|
371 |
+
'src' => 'wp-seo-edit-page-' . $flat_version,
|
372 |
+
'deps' => array( 'jquery' ),
|
373 |
+
),
|
374 |
+
array(
|
375 |
+
'name' => 'quick-edit-handler',
|
376 |
+
'src' => 'wp-seo-quick-edit-handler-' . $flat_version,
|
377 |
+
'deps' => array( 'jquery' ),
|
378 |
+
'in_footer' => true,
|
379 |
+
),
|
380 |
+
array(
|
381 |
+
'name' => 'api',
|
382 |
+
'src' => 'wp-seo-api-' . $flat_version,
|
383 |
+
'deps' => array(
|
384 |
+
'wp-api',
|
385 |
+
'jquery',
|
386 |
+
),
|
387 |
+
),
|
388 |
+
array(
|
389 |
+
'name' => 'dashboard-widget',
|
390 |
+
'src' => 'wp-seo-dashboard-widget-' . $flat_version,
|
391 |
+
'deps' => array(
|
392 |
+
self::PREFIX . 'api',
|
393 |
+
'jquery',
|
394 |
+
self::PREFIX . 'react-dependencies',
|
395 |
+
),
|
396 |
+
),
|
397 |
+
array(
|
398 |
+
'name' => 'filter-explanation',
|
399 |
+
'src' => 'wp-seo-filter-explanation-' . $flat_version,
|
400 |
+
'deps' => array( 'jquery' ),
|
401 |
+
),
|
402 |
+
);
|
403 |
+
}
|
404 |
+
|
405 |
+
/**
|
406 |
+
* Returns the styles that need to be registered.
|
407 |
+
*
|
408 |
+
* @todo Data format is not self-documenting. Needs explanation inline. R.
|
409 |
+
*
|
410 |
+
* @return array styles that need to be registered.
|
411 |
+
*/
|
412 |
+
private function styles_to_be_registered() {
|
413 |
+
$flat_version = $this->flatten_version( WPSEO_VERSION );
|
414 |
+
|
415 |
+
return array(
|
416 |
+
array(
|
417 |
+
'name' => 'admin-css',
|
418 |
+
'src' => 'yst_plugin_tools-' . $flat_version,
|
419 |
+
'deps' => array( self::PREFIX . 'toggle-switch' ),
|
420 |
+
),
|
421 |
+
array(
|
422 |
+
'name' => 'toggle-switch',
|
423 |
+
'src' => 'toggle-switch-' . $flat_version,
|
424 |
+
),
|
425 |
+
array(
|
426 |
+
'name' => 'dismissible',
|
427 |
+
'src' => 'wpseo-dismissible-' . $flat_version,
|
428 |
+
),
|
429 |
+
array(
|
430 |
+
'name' => 'alerts',
|
431 |
+
'src' => 'alerts-' . $flat_version,
|
432 |
+
),
|
433 |
+
array(
|
434 |
+
'name' => 'edit-page',
|
435 |
+
'src' => 'edit-page-' . $flat_version,
|
436 |
+
),
|
437 |
+
array(
|
438 |
+
'name' => 'featured-image',
|
439 |
+
'src' => 'featured-image-' . $flat_version,
|
440 |
+
),
|
441 |
+
array(
|
442 |
+
'name' => 'metabox-css',
|
443 |
+
'src' => 'metabox-' . $flat_version,
|
444 |
+
'deps' => array(
|
445 |
+
self::PREFIX . 'select2',
|
446 |
+
),
|
447 |
+
),
|
448 |
+
array(
|
449 |
+
'name' => 'wp-dashboard',
|
450 |
+
'src' => 'dashboard-' . $flat_version,
|
451 |
+
),
|
452 |
+
array(
|
453 |
+
'name' => 'scoring',
|
454 |
+
'src' => 'yst_seo_score-' . $flat_version,
|
455 |
+
),
|
456 |
+
array(
|
457 |
+
'name' => 'snippet',
|
458 |
+
'src' => 'snippet-' . $flat_version,
|
459 |
+
),
|
460 |
+
array(
|
461 |
+
'name' => 'adminbar',
|
462 |
+
'src' => 'adminbar-' . $flat_version,
|
463 |
+
),
|
464 |
+
array(
|
465 |
+
'name' => 'primary-category',
|
466 |
+
'src' => 'metabox-primary-category-' . $flat_version,
|
467 |
+
),
|
468 |
+
array(
|
469 |
+
'name' => 'select2',
|
470 |
+
'src' => 'select2/select2',
|
471 |
+
'suffix' => '.min',
|
472 |
+
'version' => '4.0.1',
|
473 |
+
'rtl' => false,
|
474 |
+
),
|
475 |
+
array(
|
476 |
+
'name' => 'admin-global',
|
477 |
+
'src' => 'admin-global-' . $flat_version,
|
478 |
+
),
|
479 |
+
array(
|
480 |
+
'name' => 'yoast-components',
|
481 |
+
'src' => 'yoast-components-' . $flat_version,
|
482 |
+
),
|
483 |
+
array(
|
484 |
+
'name' => 'extensions',
|
485 |
+
'src' => 'yoast-extensions-' . $flat_version,
|
486 |
+
),
|
487 |
+
array(
|
488 |
+
'name' => 'filter-explanation',
|
489 |
+
'src' => 'filter-explanation-' . $flat_version,
|
490 |
+
),
|
491 |
+
);
|
492 |
+
}
|
493 |
+
}
|
admin/class-admin-asset-seo-location.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO|Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Determines the location of an asset within the SEO plugin.
|
8 |
+
*/
|
9 |
+
final class WPSEO_Admin_Asset_SEO_Location implements WPSEO_Admin_Asset_Location {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string
|
13 |
+
*/
|
14 |
+
protected $plugin_file;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* The plugin file to base the asset location upon.
|
18 |
+
*
|
19 |
+
* @param string $plugin_file The plugin file string.
|
20 |
+
*/
|
21 |
+
public function __construct( $plugin_file ) {
|
22 |
+
$this->plugin_file = $plugin_file;
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Determines the URL of the asset on the dev server.
|
27 |
+
*
|
28 |
+
* @param WPSEO_Admin_Asset $asset The asset to determine the URL for.
|
29 |
+
* @param string $type The type of asset. Usually JS or CSS.
|
30 |
+
*
|
31 |
+
* @return string The URL of the asset.
|
32 |
+
*/
|
33 |
+
public function get_url( WPSEO_Admin_Asset $asset, $type ) {
|
34 |
+
$path = $this->get_path( $asset, $type );
|
35 |
+
if ( empty( $path ) ) {
|
36 |
+
return '';
|
37 |
+
}
|
38 |
+
|
39 |
+
if ( YOAST_ENVIRONMENT !== 'development' && ! $asset->get_suffix() ) {
|
40 |
+
$plugin_path = plugin_dir_path( $this->plugin_file );
|
41 |
+
if ( ! file_exists( $plugin_path . $path ) ) {
|
42 |
+
|
43 |
+
// Give a notice to the user in the console (only once).
|
44 |
+
WPSEO_Utils::javascript_console_notification(
|
45 |
+
'Development Files',
|
46 |
+
sprintf(
|
47 |
+
/* translators: %1$s resolves to https://github.com/Yoast/wordpress-seo */
|
48 |
+
__( 'You are trying to load non-minified files. These are only available in our development package. Check out %1$s to see all the source files.', 'wordpress-seo' ),
|
49 |
+
'https://github.com/Yoast/wordpress-seo'
|
50 |
+
),
|
51 |
+
true
|
52 |
+
);
|
53 |
+
|
54 |
+
// Just load the .min file.
|
55 |
+
$path = $this->get_path( $asset, $type, '.min' );
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
return plugins_url( $path, $this->plugin_file );
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Determines the path relative to the plugin folder of an asset.
|
64 |
+
*
|
65 |
+
* @param WPSEO_Admin_Asset $asset The asset to determine the path
|
66 |
+
* for.
|
67 |
+
* @param string $type The type of asset.
|
68 |
+
* @param string|null $force_suffix The suffix to force, or null when
|
69 |
+
* to use the default suffix.
|
70 |
+
*
|
71 |
+
* @return string The path to the asset file.
|
72 |
+
*/
|
73 |
+
protected function get_path( WPSEO_Admin_Asset $asset, $type, $force_suffix = null ) {
|
74 |
+
$relative_path = '';
|
75 |
+
$rtl_suffix = '';
|
76 |
+
|
77 |
+
$suffix = ( $force_suffix === null ) ? $asset->get_suffix() : $force_suffix;
|
78 |
+
|
79 |
+
switch ( $type ) {
|
80 |
+
case WPSEO_Admin_Asset::TYPE_JS:
|
81 |
+
$relative_path = 'js/dist/' . $asset->get_src() . $suffix . '.js';
|
82 |
+
break;
|
83 |
+
|
84 |
+
case WPSEO_Admin_Asset::TYPE_CSS:
|
85 |
+
// Path and suffix for RTL stylesheets.
|
86 |
+
if ( function_exists( 'is_rtl' ) && is_rtl() && $asset->has_rtl() ) {
|
87 |
+
$rtl_suffix = '-rtl';
|
88 |
+
}
|
89 |
+
$relative_path = 'css/dist/' . $asset->get_src() . $rtl_suffix . $suffix . '.css';
|
90 |
+
break;
|
91 |
+
}
|
92 |
+
|
93 |
+
return $relative_path;
|
94 |
+
}
|
95 |
+
}
|
admin/class-admin-help-panel.php
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Generates the HTML for an inline Help Button and Panel.
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Help_Panel {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string
|
13 |
+
*/
|
14 |
+
private $id;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
private $help_button_text;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var string
|
23 |
+
*/
|
24 |
+
private $help_content;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
private $wrapper;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Constructor.
|
33 |
+
*
|
34 |
+
* @param string $id Unique identifier of the element the inline help refers to, used as an identifier in the html.
|
35 |
+
* @param string $help_button_text The Help Button text. Needs a properly escaped string.
|
36 |
+
* @param string $help_content The Help Panel content. Needs a properly escaped string (might contain HTML).
|
37 |
+
* @param string $wrapper Optional Whether to print out a container div element for the Help Panel, used for styling.
|
38 |
+
* Pass a `has-wrapper` value to print out the container. Default: no container.
|
39 |
+
*/
|
40 |
+
public function __construct( $id, $help_button_text, $help_content, $wrapper = '' ) {
|
41 |
+
$this->id = $id;
|
42 |
+
$this->help_button_text = $help_button_text;
|
43 |
+
$this->help_content = $help_content;
|
44 |
+
$this->wrapper = $wrapper;
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Returns the html for the Help Button.
|
49 |
+
*
|
50 |
+
* @return string
|
51 |
+
*/
|
52 |
+
public function get_button_html() {
|
53 |
+
|
54 |
+
if ( ! $this->id || ! $this->help_button_text || ! $this->help_content ) {
|
55 |
+
return '';
|
56 |
+
}
|
57 |
+
|
58 |
+
return sprintf(
|
59 |
+
' <button type="button" class="yoast_help yoast-help-button dashicons" id="%1$s-help-toggle" aria-expanded="false" aria-controls="%1$s-help"><span class="yoast-help-icon" aria-hidden="true"></span><span class="screen-reader-text">%2$s</span></button>',
|
60 |
+
esc_attr( $this->id ),
|
61 |
+
$this->help_button_text
|
62 |
+
);
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Returns the html for the Help Panel.
|
67 |
+
*
|
68 |
+
* @return string
|
69 |
+
*/
|
70 |
+
public function get_panel_html() {
|
71 |
+
|
72 |
+
if ( ! $this->id || ! $this->help_button_text || ! $this->help_content ) {
|
73 |
+
return '';
|
74 |
+
}
|
75 |
+
|
76 |
+
$wrapper_start = '';
|
77 |
+
$wrapper_end = '';
|
78 |
+
|
79 |
+
if ( 'has-wrapper' === $this->wrapper ) {
|
80 |
+
$wrapper_start = '<div class="yoast-seo-help-container">';
|
81 |
+
$wrapper_end = '</div>';
|
82 |
+
}
|
83 |
+
|
84 |
+
return sprintf(
|
85 |
+
'%1$s<p id="%2$s-help" class="yoast-help-panel">%3$s</p>%4$s',
|
86 |
+
$wrapper_start,
|
87 |
+
esc_attr( $this->id ),
|
88 |
+
$this->help_content,
|
89 |
+
$wrapper_end
|
90 |
+
);
|
91 |
+
}
|
92 |
+
}
|
admin/class-admin-init.php
ADDED
@@ -0,0 +1,665 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Performs the load on admin side.
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Init {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Holds the global `$pagenow` variable's value.
|
13 |
+
*
|
14 |
+
* @var string
|
15 |
+
*/
|
16 |
+
private $pagenow;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Holds the asset manager.
|
20 |
+
*
|
21 |
+
* @var WPSEO_Admin_Asset_Manager
|
22 |
+
*/
|
23 |
+
private $asset_manager;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Class constructor
|
27 |
+
*/
|
28 |
+
public function __construct() {
|
29 |
+
$GLOBALS['wpseo_admin'] = new WPSEO_Admin();
|
30 |
+
|
31 |
+
$this->pagenow = $GLOBALS['pagenow'];
|
32 |
+
|
33 |
+
$this->asset_manager = new WPSEO_Admin_Asset_Manager();
|
34 |
+
|
35 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_dismissible' ) );
|
36 |
+
add_action( 'admin_init', array( $this, 'tagline_notice' ), 15 );
|
37 |
+
add_action( 'admin_init', array( $this, 'blog_public_notice' ), 15 );
|
38 |
+
add_action( 'admin_init', array( $this, 'permalink_notice' ), 15 );
|
39 |
+
add_action( 'admin_init', array( $this, 'page_comments_notice' ), 15 );
|
40 |
+
add_action( 'admin_init', array( $this, 'ga_compatibility_notice' ), 15 );
|
41 |
+
add_action( 'admin_init', array( $this, 'yoast_plugin_compatibility_notification' ), 15 );
|
42 |
+
add_action( 'admin_init', array( $this, 'yoast_plugin_suggestions_notification' ), 15 );
|
43 |
+
add_action( 'admin_init', array( $this, 'recalculate_notice' ), 15 );
|
44 |
+
add_action( 'admin_init', array( $this->asset_manager, 'register_assets' ) );
|
45 |
+
add_action( 'admin_init', array( $this, 'show_hook_deprecation_warnings' ) );
|
46 |
+
add_action( 'admin_init', array( 'WPSEO_Plugin_Conflict', 'hook_check_for_plugin_conflicts' ) );
|
47 |
+
|
48 |
+
$this->load_meta_boxes();
|
49 |
+
$this->load_taxonomy_class();
|
50 |
+
$this->load_admin_page_class();
|
51 |
+
$this->load_admin_user_class();
|
52 |
+
$this->load_xml_sitemaps_admin();
|
53 |
+
$this->load_plugin_suggestions();
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Enqueue our styling for dismissible yoast notifications.
|
58 |
+
*/
|
59 |
+
public function enqueue_dismissible() {
|
60 |
+
$this->asset_manager->enqueue_style( 'dismissible' );
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Helper to verify if the current user has already seen the about page for the current version
|
65 |
+
*
|
66 |
+
* @return bool
|
67 |
+
*/
|
68 |
+
private function seen_about() {
|
69 |
+
$seen_about_version = substr( get_user_meta( get_current_user_id(), 'wpseo_seen_about_version', true ), 0, 3 );
|
70 |
+
$last_minor_version = substr( WPSEO_VERSION, 0, 3 );
|
71 |
+
|
72 |
+
return version_compare( $seen_about_version, $last_minor_version, '>=' );
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Notify about the default tagline if the user hasn't changed it
|
77 |
+
*/
|
78 |
+
public function tagline_notice() {
|
79 |
+
|
80 |
+
$current_url = ( is_ssl() ? 'https://' : 'http://' );
|
81 |
+
$current_url .= sanitize_text_field( $_SERVER['SERVER_NAME'] ) . sanitize_text_field( $_SERVER['REQUEST_URI'] );
|
82 |
+
$customize_url = add_query_arg( array(
|
83 |
+
'url' => urlencode( $current_url ),
|
84 |
+
), wp_customize_url() );
|
85 |
+
|
86 |
+
$info_message = sprintf(
|
87 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
88 |
+
__( 'You still have the default WordPress tagline, even an empty one is probably better. %1$sYou can fix this in the customizer%2$s.', 'wordpress-seo' ),
|
89 |
+
'<a href="' . esc_attr( $customize_url ) . '">',
|
90 |
+
'</a>'
|
91 |
+
);
|
92 |
+
|
93 |
+
$notification_options = array(
|
94 |
+
'type' => Yoast_Notification::ERROR,
|
95 |
+
'id' => 'wpseo-dismiss-tagline-notice',
|
96 |
+
'capabilities' => 'wpseo_manage_options',
|
97 |
+
);
|
98 |
+
|
99 |
+
$tagline_notification = new Yoast_Notification( $info_message, $notification_options );
|
100 |
+
|
101 |
+
$notification_center = Yoast_Notification_Center::get();
|
102 |
+
if ( $this->has_default_tagline() ) {
|
103 |
+
$notification_center->add_notification( $tagline_notification );
|
104 |
+
}
|
105 |
+
else {
|
106 |
+
$notification_center->remove_notification( $tagline_notification );
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Add an alert if the blog is not publicly visible
|
112 |
+
*/
|
113 |
+
public function blog_public_notice() {
|
114 |
+
|
115 |
+
$info_message = '<strong>' . __( 'Huge SEO Issue: You\'re blocking access to robots.', 'wordpress-seo' ) . '</strong> ';
|
116 |
+
$info_message .= sprintf(
|
117 |
+
/* translators: %1$s resolves to the opening tag of the link to the reading settings, %1$s resolves to the closing tag for the link */
|
118 |
+
__( 'You must %1$sgo to your Reading Settings%2$s and uncheck the box for Search Engine Visibility.', 'wordpress-seo' ),
|
119 |
+
'<a href="' . esc_url( admin_url( 'options-reading.php' ) ) . '">',
|
120 |
+
'</a>'
|
121 |
+
);
|
122 |
+
|
123 |
+
$notification_options = array(
|
124 |
+
'type' => Yoast_Notification::ERROR,
|
125 |
+
'id' => 'wpseo-dismiss-blog-public-notice',
|
126 |
+
'priority' => 1.0,
|
127 |
+
'capabilities' => 'wpseo_manage_options',
|
128 |
+
);
|
129 |
+
|
130 |
+
$notification = new Yoast_Notification( $info_message, $notification_options );
|
131 |
+
|
132 |
+
$notification_center = Yoast_Notification_Center::get();
|
133 |
+
if ( ! $this->is_blog_public() ) {
|
134 |
+
$notification_center->add_notification( $notification );
|
135 |
+
}
|
136 |
+
else {
|
137 |
+
$notification_center->remove_notification( $notification );
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Display notice to disable comment pagination
|
143 |
+
*/
|
144 |
+
public function page_comments_notice() {
|
145 |
+
|
146 |
+
$info_message = __( 'Paging comments is enabled, this is not needed in 999 out of 1000 cases, we recommend to disable it.', 'wordpress-seo' );
|
147 |
+
$info_message .= '<br/>';
|
148 |
+
|
149 |
+
$info_message .= sprintf(
|
150 |
+
/* translators: %1$s resolves to the opening tag of the link to the comment setting page, %2$s resolves to the closing tag of the link */
|
151 |
+
__( 'Simply uncheck the box before "Break comments into pages..." on the %1$sComment settings page%2$s.', 'wordpress-seo' ),
|
152 |
+
'<a href="' . esc_url( admin_url( 'options-discussion.php' ) ) . '">',
|
153 |
+
'</a>'
|
154 |
+
);
|
155 |
+
|
156 |
+
$notification_options = array(
|
157 |
+
'type' => Yoast_Notification::WARNING,
|
158 |
+
'id' => 'wpseo-dismiss-page_comments-notice',
|
159 |
+
'capabilities' => 'wpseo_manage_options',
|
160 |
+
);
|
161 |
+
|
162 |
+
$tagline_notification = new Yoast_Notification( $info_message, $notification_options );
|
163 |
+
|
164 |
+
$notification_center = Yoast_Notification_Center::get();
|
165 |
+
if ( $this->has_page_comments() ) {
|
166 |
+
$notification_center->add_notification( $tagline_notification );
|
167 |
+
}
|
168 |
+
else {
|
169 |
+
$notification_center->remove_notification( $tagline_notification );
|
170 |
+
}
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Returns whether or not the site has the default tagline
|
175 |
+
*
|
176 |
+
* @return bool
|
177 |
+
*/
|
178 |
+
public function has_default_tagline() {
|
179 |
+
$blog_description = get_bloginfo( 'description' );
|
180 |
+
$default_blog_description = 'Just another WordPress site';
|
181 |
+
|
182 |
+
// We are checking against the WordPress internal translation.
|
183 |
+
// @codingStandardsIgnoreLine
|
184 |
+
$translated_blog_description = __( 'Just another WordPress site' );
|
185 |
+
|
186 |
+
return $translated_blog_description === $blog_description || $default_blog_description === $blog_description;
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Show alert when the permalink doesn't contain %postname%
|
191 |
+
*/
|
192 |
+
public function permalink_notice() {
|
193 |
+
|
194 |
+
$info_message = __( 'You do not have your postname in the URL of your posts and pages, it is highly recommended that you do. Consider setting your permalink structure to <strong>/%postname%/</strong>.', 'wordpress-seo' );
|
195 |
+
$info_message .= '<br/>';
|
196 |
+
$info_message .= sprintf(
|
197 |
+
/* translators: %1$s resolves to the starting tag of the link to the permalink settings page, %2$s resolves to the closing tag of the link */
|
198 |
+
__( 'You can fix this on the %1$sPermalink settings page%2$s.', 'wordpress-seo' ),
|
199 |
+
'<a href="' . admin_url( 'options-permalink.php' ) . '">',
|
200 |
+
'</a>'
|
201 |
+
);
|
202 |
+
|
203 |
+
$notification_options = array(
|
204 |
+
'type' => Yoast_Notification::WARNING,
|
205 |
+
'id' => 'wpseo-dismiss-permalink-notice',
|
206 |
+
'capabilities' => 'wpseo_manage_options',
|
207 |
+
'priority' => 0.8,
|
208 |
+
);
|
209 |
+
|
210 |
+
$notification = new Yoast_Notification( $info_message, $notification_options );
|
211 |
+
|
212 |
+
$notification_center = Yoast_Notification_Center::get();
|
213 |
+
if ( ! $this->has_postname_in_permalink() ) {
|
214 |
+
$notification_center->add_notification( $notification );
|
215 |
+
}
|
216 |
+
else {
|
217 |
+
$notification_center->remove_notification( $notification );
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Are page comments enabled
|
223 |
+
*
|
224 |
+
* @return bool
|
225 |
+
*/
|
226 |
+
public function has_page_comments() {
|
227 |
+
return '1' === get_option( 'page_comments' );
|
228 |
+
}
|
229 |
+
|
230 |
+
/**
|
231 |
+
* Shows a notice to the user if they have Google Analytics for WordPress 5.4.3 installed because it causes an error
|
232 |
+
* on the google search console page.
|
233 |
+
*/
|
234 |
+
public function ga_compatibility_notice() {
|
235 |
+
|
236 |
+
$notification = $this->get_compatibility_notification();
|
237 |
+
$notification_center = Yoast_Notification_Center::get();
|
238 |
+
|
239 |
+
if ( defined( 'GAWP_VERSION' ) && '5.4.3' === GAWP_VERSION ) {
|
240 |
+
$notification_center->add_notification( $notification );
|
241 |
+
}
|
242 |
+
else {
|
243 |
+
$notification_center->remove_notification( $notification );
|
244 |
+
}
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Build compatibility problem notification
|
249 |
+
*
|
250 |
+
* @return Yoast_Notification
|
251 |
+
*/
|
252 |
+
private function get_compatibility_notification() {
|
253 |
+
$info_message = sprintf(
|
254 |
+
/* translators: %1$s expands to Yoast SEO, %2$s expands to 5.4.3, %3$s expands to Google Analytics by Yoast */
|
255 |
+
__( '%1$s detected you are using version %2$s of %3$s, please update to the latest version to prevent compatibility issues.', 'wordpress-seo' ),
|
256 |
+
'Yoast SEO',
|
257 |
+
'5.4.3',
|
258 |
+
'Google Analytics by Yoast'
|
259 |
+
);
|
260 |
+
|
261 |
+
return new Yoast_Notification(
|
262 |
+
$info_message,
|
263 |
+
array(
|
264 |
+
'id' => 'gawp-compatibility-notice',
|
265 |
+
'type' => Yoast_Notification::ERROR,
|
266 |
+
)
|
267 |
+
);
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* Determines whether a suggested plugins notification needs to be displayed.
|
272 |
+
*
|
273 |
+
* @return void
|
274 |
+
*/
|
275 |
+
public function yoast_plugin_suggestions_notification() {
|
276 |
+
$checker = new WPSEO_Plugin_Availability();
|
277 |
+
$notification_center = Yoast_Notification_Center::get();
|
278 |
+
|
279 |
+
// Get all Yoast plugins that have dependencies.
|
280 |
+
$plugins = $checker->get_plugins_with_dependencies();
|
281 |
+
|
282 |
+
foreach ( $plugins as $plugin_name => $plugin ) {
|
283 |
+
$dependency_names = $checker->get_dependency_names( $plugin );
|
284 |
+
$notification = $this->get_yoast_seo_suggested_plugins_notification( $plugin_name, $plugin, $dependency_names[0] );
|
285 |
+
|
286 |
+
if ( $checker->dependencies_are_satisfied( $plugin ) && ! $checker->is_installed( $plugin ) ) {
|
287 |
+
$notification_center->add_notification( $notification );
|
288 |
+
|
289 |
+
continue;
|
290 |
+
}
|
291 |
+
|
292 |
+
$notification_center->remove_notification( $notification );
|
293 |
+
}
|
294 |
+
}
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Build Yoast SEO suggested plugins notification.
|
298 |
+
*
|
299 |
+
* @param string $name The plugin name to use for the unique ID.
|
300 |
+
* @param array $plugin The plugin to retrieve the data from.
|
301 |
+
* @param string $dependency_name The name of the dependency.
|
302 |
+
*
|
303 |
+
* @return Yoast_Notification The notification containing the suggested plugin.
|
304 |
+
*/
|
305 |
+
private function get_yoast_seo_suggested_plugins_notification( $name, $plugin, $dependency_name ) {
|
306 |
+
$info_message = sprintf(
|
307 |
+
/* translators: %1$s expands to Yoast SEO, %2$s expands to the plugin version, %3$s expands to the plugin name */
|
308 |
+
__( '%1$s and %2$s can work together a lot better by adding a helper plugin. Please install %3$s to make your life better.', 'wordpress-seo' ),
|
309 |
+
'Yoast SEO',
|
310 |
+
$dependency_name,
|
311 |
+
sprintf( '<a href="%s">%s</a>', $plugin['url'], $plugin['title'] )
|
312 |
+
);
|
313 |
+
|
314 |
+
return new Yoast_Notification(
|
315 |
+
$info_message,
|
316 |
+
array(
|
317 |
+
'id' => 'wpseo-suggested-plugin-' . $name,
|
318 |
+
'type' => Yoast_Notification::WARNING,
|
319 |
+
)
|
320 |
+
);
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* Add an alert if outdated versions of Yoast SEO plugins are running.
|
325 |
+
*/
|
326 |
+
public function yoast_plugin_compatibility_notification() {
|
327 |
+
$compatibility_checker = new WPSEO_Plugin_Compatibility( WPSEO_VERSION );
|
328 |
+
$plugins = $compatibility_checker->get_installed_plugins_compatibility();
|
329 |
+
|
330 |
+
$notification_center = Yoast_Notification_Center::get();
|
331 |
+
|
332 |
+
foreach ( $plugins as $name => $plugin ) {
|
333 |
+
$type = ( $plugin['active'] ) ? Yoast_Notification::ERROR : Yoast_Notification::WARNING;
|
334 |
+
$notification = $this->get_yoast_seo_compatibility_notification( $name, $plugin, $type );
|
335 |
+
|
336 |
+
if ( $plugin['compatible'] === false ) {
|
337 |
+
$notification_center->add_notification( $notification );
|
338 |
+
|
339 |
+
continue;
|
340 |
+
}
|
341 |
+
|
342 |
+
$notification_center->remove_notification( $notification );
|
343 |
+
}
|
344 |
+
}
|
345 |
+
|
346 |
+
/**
|
347 |
+
* Build Yoast SEO compatibility problem notification
|
348 |
+
*
|
349 |
+
* @param string $name The plugin name to use for the unique ID.
|
350 |
+
* @param array $plugin The plugin to retrieve the data from.
|
351 |
+
* @param string $level The severity level to use for the notification.
|
352 |
+
*
|
353 |
+
* @return Yoast_Notification
|
354 |
+
*/
|
355 |
+
private function get_yoast_seo_compatibility_notification( $name, $plugin, $level = Yoast_Notification::WARNING ) {
|
356 |
+
$info_message = sprintf(
|
357 |
+
/* translators: %1$s expands to Yoast SEO, %2$s expands to the plugin version, %3$s expands to the plugin name */
|
358 |
+
__( '%1$s detected you are using version %2$s of %3$s, please update to the latest version to prevent compatibility issues.', 'wordpress-seo' ),
|
359 |
+
'Yoast SEO',
|
360 |
+
$plugin['version'],
|
361 |
+
$plugin['title']
|
362 |
+
);
|
363 |
+
|
364 |
+
return new Yoast_Notification(
|
365 |
+
$info_message,
|
366 |
+
array(
|
367 |
+
'id' => 'wpseo-outdated-yoast-seo-plugin-' . $name,
|
368 |
+
'type' => $level,
|
369 |
+
)
|
370 |
+
);
|
371 |
+
}
|
372 |
+
|
373 |
+
/**
|
374 |
+
* Shows the notice for recalculating the post. the Notice will only be shown if the user hasn't dismissed it before.
|
375 |
+
*/
|
376 |
+
public function recalculate_notice() {
|
377 |
+
// Just a return, because we want to temporary disable this notice (#3998 and #4532).
|
378 |
+
return;
|
379 |
+
|
380 |
+
if ( filter_input( INPUT_GET, 'recalculate' ) === '1' ) {
|
381 |
+
update_option( 'wpseo_dismiss_recalculate', '1' );
|
382 |
+
|
383 |
+
return;
|
384 |
+
}
|
385 |
+
|
386 |
+
if ( ! WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) {
|
387 |
+
return;
|
388 |
+
}
|
389 |
+
|
390 |
+
if ( $this->is_site_notice_dismissed( 'wpseo_dismiss_recalculate' ) ) {
|
391 |
+
return;
|
392 |
+
}
|
393 |
+
|
394 |
+
Yoast_Notification_Center::get()->add_notification(
|
395 |
+
new Yoast_Notification(
|
396 |
+
sprintf(
|
397 |
+
/* translators: 1: is a link to 'admin_url / admin.php?page=wpseo_tools&recalculate=1' 2: closing link tag */
|
398 |
+
__( 'We\'ve updated our SEO score algorithm. %1$sRecalculate the SEO scores%2$s for all posts and pages.', 'wordpress-seo' ),
|
399 |
+
'<a href="' . admin_url( 'admin.php?page=wpseo_tools&recalculate=1' ) . '">',
|
400 |
+
'</a>'
|
401 |
+
),
|
402 |
+
array(
|
403 |
+
'type' => 'updated yoast-dismissible',
|
404 |
+
'id' => 'wpseo-dismiss-recalculate',
|
405 |
+
'nonce' => wp_create_nonce( 'wpseo-dismiss-recalculate' ),
|
406 |
+
)
|
407 |
+
)
|
408 |
+
);
|
409 |
+
}
|
410 |
+
|
411 |
+
/**
|
412 |
+
* Check if the user has dismissed the given notice (by $notice_name)
|
413 |
+
*
|
414 |
+
* @param string $notice_name The name of the notice that might be dismissed.
|
415 |
+
*
|
416 |
+
* @return bool
|
417 |
+
*/
|
418 |
+
private function is_site_notice_dismissed( $notice_name ) {
|
419 |
+
return '1' === get_option( $notice_name, true );
|
420 |
+
}
|
421 |
+
|
422 |
+
/**
|
423 |
+
* Helper to verify if the user is currently visiting one of our admin pages.
|
424 |
+
*
|
425 |
+
* @return bool
|
426 |
+
*/
|
427 |
+
private function on_wpseo_admin_page() {
|
428 |
+
return 'admin.php' === $this->pagenow && strpos( filter_input( INPUT_GET, 'page' ), 'wpseo' ) === 0;
|
429 |
+
}
|
430 |
+
|
431 |
+
/**
|
432 |
+
* Determine whether we should load the meta box class and if so, load it.
|
433 |
+
*/
|
434 |
+
private function load_meta_boxes() {
|
435 |
+
|
436 |
+
$is_editor = WPSEO_Metabox::is_post_overview( $this->pagenow ) || WPSEO_Metabox::is_post_edit( $this->pagenow );
|
437 |
+
$is_inline_save = filter_input( INPUT_POST, 'action' ) === 'inline-save';
|
438 |
+
|
439 |
+
/**
|
440 |
+
* Filter: 'wpseo_always_register_metaboxes_on_admin' - Allow developers to change whether
|
441 |
+
* the WPSEO metaboxes are only registered on the typical pages (lean loading) or always
|
442 |
+
* registered when in admin.
|
443 |
+
*
|
444 |
+
* @api bool Whether to always register the metaboxes or not. Defaults to false.
|
445 |
+
*/
|
446 |
+
if ( $is_editor || $is_inline_save || apply_filters( 'wpseo_always_register_metaboxes_on_admin', false )
|
447 |
+
) {
|
448 |
+
$GLOBALS['wpseo_metabox'] = new WPSEO_Metabox();
|
449 |
+
$GLOBALS['wpseo_meta_columns'] = new WPSEO_Meta_Columns();
|
450 |
+
}
|
451 |
+
}
|
452 |
+
|
453 |
+
/**
|
454 |
+
* Determine if we should load our taxonomy edit class and if so, load it.
|
455 |
+
*/
|
456 |
+
private function load_taxonomy_class() {
|
457 |
+
if (
|
458 |
+
WPSEO_Taxonomy::is_term_edit( $this->pagenow )
|
459 |
+
|| WPSEO_Taxonomy::is_term_overview( $this->pagenow )
|
460 |
+
) {
|
461 |
+
new WPSEO_Taxonomy();
|
462 |
+
}
|
463 |
+
}
|
464 |
+
|
465 |
+
/**
|
466 |
+
* Determine if we should load our admin pages class and if so, load it.
|
467 |
+
*
|
468 |
+
* Loads admin page class for all admin pages starting with `wpseo_`.
|
469 |
+
*/
|
470 |
+
private function load_admin_user_class() {
|
471 |
+
if ( in_array( $this->pagenow, array( 'user-edit.php', 'profile.php' ), true )
|
472 |
+
&& current_user_can( 'edit_users' )
|
473 |
+
) {
|
474 |
+
new WPSEO_Admin_User_Profile();
|
475 |
+
}
|
476 |
+
}
|
477 |
+
|
478 |
+
/**
|
479 |
+
* Determine if we should load our admin pages class and if so, load it.
|
480 |
+
*
|
481 |
+
* Loads admin page class for all admin pages starting with `wpseo_`.
|
482 |
+
*/
|
483 |
+
private function load_admin_page_class() {
|
484 |
+
|
485 |
+
if ( $this->on_wpseo_admin_page() ) {
|
486 |
+
// For backwards compatabilty, this still needs a global, for now...
|
487 |
+
$GLOBALS['wpseo_admin_pages'] = new WPSEO_Admin_Pages();
|
488 |
+
|
489 |
+
// Only register the yoast i18n when the page is a Yoast SEO page.
|
490 |
+
if ( WPSEO_Utils::is_yoast_seo_free_page( filter_input( INPUT_GET, 'page' ) ) ) {
|
491 |
+
$this->register_i18n_promo_class();
|
492 |
+
$this->register_premium_upsell_admin_block();
|
493 |
+
}
|
494 |
+
}
|
495 |
+
}
|
496 |
+
|
497 |
+
/**
|
498 |
+
* Loads the plugin suggestions.
|
499 |
+
*/
|
500 |
+
private function load_plugin_suggestions() {
|
501 |
+
$suggestions = new WPSEO_Suggested_Plugins( new WPSEO_Plugin_Availability(), Yoast_Notification_Center::get() );
|
502 |
+
$suggestions->register_hooks();
|
503 |
+
}
|
504 |
+
|
505 |
+
/**
|
506 |
+
* Registers the Premium Upsell Admin Block.
|
507 |
+
*
|
508 |
+
* @return void
|
509 |
+
*/
|
510 |
+
private function register_premium_upsell_admin_block() {
|
511 |
+
if ( ! WPSEO_Utils::is_yoast_seo_premium() ) {
|
512 |
+
$upsell_block = new WPSEO_Premium_Upsell_Admin_Block( 'wpseo_admin_promo_footer' );
|
513 |
+
$upsell_block->register_hooks();
|
514 |
+
}
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* Registers the promotion class for our GlotPress instance, then creates a notification with the i18n promo.
|
519 |
+
*
|
520 |
+
* @link https://github.com/Yoast/i18n-module
|
521 |
+
*/
|
522 |
+
private function register_i18n_promo_class() {
|
523 |
+
// BC, because an older version of the i18n-module didn't have this class.
|
524 |
+
$i18n_module = new Yoast_I18n_WordPressOrg_v3(
|
525 |
+
array(
|
526 |
+
'textdomain' => 'wordpress-seo',
|
527 |
+
'plugin_name' => 'Yoast SEO',
|
528 |
+
'hook' => 'wpseo_admin_promo_footer',
|
529 |
+
), false
|
530 |
+
);
|
531 |
+
|
532 |
+
$message = $i18n_module->get_promo_message();
|
533 |
+
|
534 |
+
if ( $message !== '' ) {
|
535 |
+
$message .= $i18n_module->get_dismiss_i18n_message_button();
|
536 |
+
}
|
537 |
+
|
538 |
+
$notification_center = Yoast_Notification_Center::get();
|
539 |
+
|
540 |
+
$notification = new Yoast_Notification(
|
541 |
+
$message,
|
542 |
+
array(
|
543 |
+
'type' => Yoast_Notification::WARNING,
|
544 |
+
'id' => 'i18nModuleTranslationAssistance',
|
545 |
+
)
|
546 |
+
);
|
547 |
+
|
548 |
+
if ( $message ) {
|
549 |
+
$notification_center->add_notification( $notification );
|
550 |
+
|
551 |
+
return;
|
552 |
+
}
|
553 |
+
|
554 |
+
$notification_center->remove_notification( $notification );
|
555 |
+
}
|
556 |
+
|
557 |
+
/**
|
558 |
+
* See if we should start our XML Sitemaps Admin class
|
559 |
+
*/
|
560 |
+
private function load_xml_sitemaps_admin() {
|
561 |
+
if ( WPSEO_Options::get( 'enable_xml_sitemap', false ) ) {
|
562 |
+
new WPSEO_Sitemaps_Admin();
|
563 |
+
}
|
564 |
+
}
|
565 |
+
|
566 |
+
/**
|
567 |
+
* Check if the site is set to be publicly visible
|
568 |
+
*
|
569 |
+
* @return bool
|
570 |
+
*/
|
571 |
+
private function is_blog_public() {
|
572 |
+
return '1' === (string) get_option( 'blog_public' );
|
573 |
+
}
|
574 |
+
|
575 |
+
/**
|
576 |
+
* Shows deprecation warnings to the user if a plugin has registered a filter we have deprecated.
|
577 |
+
*/
|
578 |
+
public function show_hook_deprecation_warnings() {
|
579 |
+
global $wp_filter;
|
580 |
+
|
581 |
+
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
|
582 |
+
return false;
|
583 |
+
}
|
584 |
+
|
585 |
+
// WordPress hooks that have been deprecated since a Yoast SEO version.
|
586 |
+
$deprecated_filters = array(
|
587 |
+
'wpseo_metadesc_length' => array(
|
588 |
+
'version' => '3.0',
|
589 |
+
'alternative' => 'javascript',
|
590 |
+
),
|
591 |
+
'wpseo_metadesc_length_reason' => array(
|
592 |
+
'version' => '3.0',
|
593 |
+
'alternative' => 'javascript',
|
594 |
+
),
|
595 |
+
'wpseo_body_length_score' => array(
|
596 |
+
'version' => '3.0',
|
597 |
+
'alternative' => 'javascript',
|
598 |
+
),
|
599 |
+
'wpseo_linkdex_results' => array(
|
600 |
+
'version' => '3.0',
|
601 |
+
'alternative' => 'javascript',
|
602 |
+
),
|
603 |
+
'wpseo_snippet' => array(
|
604 |
+
'version' => '3.0',
|
605 |
+
'alternative' => 'javascript',
|
606 |
+
),
|
607 |
+
'wp_seo_get_bc_title' => array(
|
608 |
+
'version' => '5.8',
|
609 |
+
'alternative' => 'wpseo_breadcrumb_single_link_info',
|
610 |
+
),
|
611 |
+
'wpseo_metakey' => array(
|
612 |
+
'version' => '6.3',
|
613 |
+
'alternative' => null,
|
614 |
+
),
|
615 |
+
'wpseo_metakeywords' => array(
|
616 |
+
'version' => '6.3',
|
617 |
+
'alternative' => null,
|
618 |
+
),
|
619 |
+
'wpseo_stopwords' => array(
|
620 |
+
'version' => '7.0',
|
621 |
+
'alternative' => null,
|
622 |
+
),
|
623 |
+
'wpseo_redirect_orphan_attachment' => array(
|
624 |
+
'version' => '7.0',
|
625 |
+
'alternative' => null,
|
626 |
+
),
|
627 |
+
);
|
628 |
+
|
629 |
+
// Determine which filters have been registered.
|
630 |
+
$deprecated_notices = array_intersect(
|
631 |
+
array_keys( $deprecated_filters ),
|
632 |
+
array_keys( $wp_filter )
|
633 |
+
);
|
634 |
+
|
635 |
+
// Show notice for each deprecated filter or action that has been registered.
|
636 |
+
foreach ( $deprecated_notices as $deprecated_filter ) {
|
637 |
+
$deprecation_info = $deprecated_filters[ $deprecated_filter ];
|
638 |
+
_deprecated_hook(
|
639 |
+
$deprecated_filter,
|
640 |
+
'WPSEO ' . $deprecation_info['version'],
|
641 |
+
$deprecation_info['alternative']
|
642 |
+
);
|
643 |
+
}
|
644 |
+
}
|
645 |
+
|
646 |
+
/**
|
647 |
+
* Check if there is a dismiss notice action.
|
648 |
+
*
|
649 |
+
* @param string $notice_name The name of the notice to dismiss.
|
650 |
+
*
|
651 |
+
* @return bool
|
652 |
+
*/
|
653 |
+
private function dismiss_notice( $notice_name ) {
|
654 |
+
return filter_input( INPUT_GET, $notice_name ) === '1' && wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), $notice_name );
|
655 |
+
}
|
656 |
+
|
657 |
+
/**
|
658 |
+
* Check if the permalink uses %postname%
|
659 |
+
*
|
660 |
+
* @return bool
|
661 |
+
*/
|
662 |
+
private function has_postname_in_permalink() {
|
663 |
+
return ( false !== strpos( get_option( 'permalink_structure' ), '%postname%' ) );
|
664 |
+
}
|
665 |
+
}
|
admin/class-admin-user-profile.php
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
* @since 1.8.0
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Customizes user profile.
|
9 |
+
*/
|
10 |
+
class WPSEO_Admin_User_Profile {
|
11 |
+
/**
|
12 |
+
* Class constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
add_action( 'show_user_profile', array( $this, 'user_profile' ) );
|
16 |
+
add_action( 'edit_user_profile', array( $this, 'user_profile' ) );
|
17 |
+
add_action( 'personal_options_update', array( $this, 'process_user_option_update' ) );
|
18 |
+
add_action( 'edit_user_profile_update', array( $this, 'process_user_option_update' ) );
|
19 |
+
|
20 |
+
add_action( 'update_user_meta', array( $this, 'clear_author_sitemap_cache' ), 10, 3 );
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Clear author sitemap cache when settings are changed.
|
25 |
+
*
|
26 |
+
* @since 3.1
|
27 |
+
*
|
28 |
+
* @param int $meta_id The ID of the meta option changed.
|
29 |
+
* @param int $object_id The ID of the user.
|
30 |
+
* @param string $meta_key The key of the meta field changed.
|
31 |
+
*/
|
32 |
+
public function clear_author_sitemap_cache( $meta_id, $object_id, $meta_key ) {
|
33 |
+
if ( '_yoast_wpseo_profile_updated' === $meta_key ) {
|
34 |
+
WPSEO_Sitemaps_Cache::clear( array( 'author' ) );
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Filter POST variables.
|
40 |
+
*
|
41 |
+
* @param string $var_name Name of the variable to filter.
|
42 |
+
*
|
43 |
+
* @return mixed
|
44 |
+
*/
|
45 |
+
private function filter_input_post( $var_name ) {
|
46 |
+
$val = filter_input( INPUT_POST, $var_name );
|
47 |
+
if ( $val ) {
|
48 |
+
return WPSEO_Utils::sanitize_text_field( $val );
|
49 |
+
}
|
50 |
+
return '';
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Updates the user metas that (might) have been set on the user profile page.
|
55 |
+
*
|
56 |
+
* @param int $user_id User ID of the updated user.
|
57 |
+
*/
|
58 |
+
public function process_user_option_update( $user_id ) {
|
59 |
+
update_user_meta( $user_id, '_yoast_wpseo_profile_updated', time() );
|
60 |
+
|
61 |
+
$nonce_value = $this->filter_input_post( 'wpseo_nonce' );
|
62 |
+
|
63 |
+
if ( empty( $nonce_value ) ) { // Submit from alternate forms.
|
64 |
+
return;
|
65 |
+
}
|
66 |
+
|
67 |
+
check_admin_referer( 'wpseo_user_profile_update', 'wpseo_nonce' );
|
68 |
+
|
69 |
+
update_user_meta( $user_id, 'wpseo_title', $this->filter_input_post( 'wpseo_author_title' ) );
|
70 |
+
update_user_meta( $user_id, 'wpseo_metadesc', $this->filter_input_post( 'wpseo_author_metadesc' ) );
|
71 |
+
update_user_meta( $user_id, 'wpseo_noindex_author', $this->filter_input_post( 'wpseo_noindex_author' ) );
|
72 |
+
update_user_meta( $user_id, 'wpseo_content_analysis_disable', $this->filter_input_post( 'wpseo_content_analysis_disable' ) );
|
73 |
+
update_user_meta( $user_id, 'wpseo_keyword_analysis_disable', $this->filter_input_post( 'wpseo_keyword_analysis_disable' ) );
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Add the inputs needed for SEO values to the User Profile page.
|
78 |
+
*
|
79 |
+
* @param WP_User $user User instance to output for.
|
80 |
+
*/
|
81 |
+
public function user_profile( $user ) {
|
82 |
+
wp_nonce_field( 'wpseo_user_profile_update', 'wpseo_nonce' );
|
83 |
+
|
84 |
+
require_once WPSEO_PATH . 'admin/views/user-profile.php';
|
85 |
+
}
|
86 |
+
}
|
admin/class-admin-utils.php
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the utils for the admin.
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Utils {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Gets the install URL for the passed plugin slug.
|
13 |
+
*
|
14 |
+
* @param string $slug The slug to create an install link for.
|
15 |
+
*
|
16 |
+
* @return string The install URL. Empty string if the current user doesn't have the proper capabilities.
|
17 |
+
*/
|
18 |
+
public static function get_install_url( $slug ) {
|
19 |
+
if ( ! current_user_can( 'install_plugins' ) ) {
|
20 |
+
return '';
|
21 |
+
}
|
22 |
+
|
23 |
+
return wp_nonce_url(
|
24 |
+
self_admin_url( 'update.php?action=install-plugin&plugin=' . dirname( $slug ) ),
|
25 |
+
'install-plugin_' . dirname( $slug )
|
26 |
+
);
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Gets the activation URL for the passed plugin slug.
|
31 |
+
*
|
32 |
+
* @param string $slug The slug to create an activation link for.
|
33 |
+
*
|
34 |
+
* @return string The activation URL. Empty string if the current user doesn't have the proper capabilities.
|
35 |
+
*/
|
36 |
+
public static function get_activation_url( $slug ) {
|
37 |
+
if ( ! current_user_can( 'install_plugins' ) ) {
|
38 |
+
return '';
|
39 |
+
}
|
40 |
+
|
41 |
+
return wp_nonce_url(
|
42 |
+
self_admin_url( 'plugins.php?action=activate&plugin_status=all&paged=1&s&plugin=' . $slug ),
|
43 |
+
'activate-plugin_' . $slug
|
44 |
+
);
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Creates a link if the passed plugin is deemend a directly-installable plugin.
|
49 |
+
*
|
50 |
+
* @param array $plugin The plugin to create the link for.
|
51 |
+
*
|
52 |
+
* @return string The link to the plugin install. Returns the title if the plugin is deemed a Premium product.
|
53 |
+
*/
|
54 |
+
public static function get_install_link( $plugin ) {
|
55 |
+
$install_url = WPSEO_Admin_Utils::get_install_url( $plugin['slug'] );
|
56 |
+
|
57 |
+
if ( $install_url === '' || ( isset( $plugin['premium'] ) && $plugin['premium'] === true ) ) {
|
58 |
+
return $plugin['title'];
|
59 |
+
}
|
60 |
+
|
61 |
+
return sprintf(
|
62 |
+
'<a href="%s">%s</a>',
|
63 |
+
$install_url,
|
64 |
+
$plugin['title']
|
65 |
+
);
|
66 |
+
|
67 |
+
}
|
68 |
+
}
|
admin/class-admin.php
ADDED
@@ -0,0 +1,539 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class that holds most of the admin functionality for Yoast SEO.
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin {
|
10 |
+
|
11 |
+
/** The page identifier used in WordPress to register the admin page !DO NOT CHANGE THIS! */
|
12 |
+
const PAGE_IDENTIFIER = 'wpseo_dashboard';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Array of classes that add admin functionality.
|
16 |
+
*
|
17 |
+
* @var array
|
18 |
+
*/
|
19 |
+
protected $admin_features;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Class constructor.
|
23 |
+
*/
|
24 |
+
public function __construct() {
|
25 |
+
$integrations = array();
|
26 |
+
|
27 |
+
global $pagenow;
|
28 |
+
|
29 |
+
$wpseo_menu = new WPSEO_Menu();
|
30 |
+
$wpseo_menu->register_hooks();
|
31 |
+
|
32 |
+
if ( is_multisite() ) {
|
33 |
+
WPSEO_Options::maybe_set_multisite_defaults( false );
|
34 |
+
}
|
35 |
+
|
36 |
+
if ( WPSEO_Options::get( 'stripcategorybase' ) === true ) {
|
37 |
+
add_action( 'created_category', array( $this, 'schedule_rewrite_flush' ) );
|
38 |
+
add_action( 'edited_category', array( $this, 'schedule_rewrite_flush' ) );
|
39 |
+
add_action( 'delete_category', array( $this, 'schedule_rewrite_flush' ) );
|
40 |
+
}
|
41 |
+
|
42 |
+
$this->admin_features = array(
|
43 |
+
// Google Search Console.
|
44 |
+
'google_search_console' => new WPSEO_GSC(),
|
45 |
+
'dashboard_widget' => new Yoast_Dashboard_Widget(),
|
46 |
+
);
|
47 |
+
|
48 |
+
if ( WPSEO_Metabox::is_post_overview( $pagenow ) || WPSEO_Metabox::is_post_edit( $pagenow ) ) {
|
49 |
+
$this->admin_features['primary_category'] = new WPSEO_Primary_Term_Admin();
|
50 |
+
}
|
51 |
+
|
52 |
+
if ( filter_input( INPUT_GET, 'page' ) === 'wpseo_tools' && filter_input( INPUT_GET, 'tool' ) === null ) {
|
53 |
+
new WPSEO_Recalculate_Scores();
|
54 |
+
}
|
55 |
+
|
56 |
+
add_filter( 'plugin_action_links_' . WPSEO_BASENAME, array( $this, 'add_action_link' ), 10, 2 );
|
57 |
+
|
58 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'config_page_scripts' ) );
|
59 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_global_style' ) );
|
60 |
+
|
61 |
+
add_filter( 'user_contactmethods', array( $this, 'update_contactmethods' ), 10, 1 );
|
62 |
+
|
63 |
+
add_action( 'after_switch_theme', array( $this, 'switch_theme' ) );
|
64 |
+
add_action( 'switch_theme', array( $this, 'switch_theme' ) );
|
65 |
+
|
66 |
+
add_filter( 'set-screen-option', array( $this, 'save_bulk_edit_options' ), 10, 3 );
|
67 |
+
|
68 |
+
add_action( 'admin_init', array( 'WPSEO_Plugin_Conflict', 'hook_check_for_plugin_conflicts' ), 10, 1 );
|
69 |
+
add_action( 'admin_init', array( $this, 'import_plugin_hooks' ) );
|
70 |
+
|
71 |
+
add_action( 'admin_init', array( $this, 'map_manage_options_cap' ) );
|
72 |
+
|
73 |
+
WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'wpseo' );
|
74 |
+
WPSEO_Sitemaps_Cache::register_clear_on_option_update( 'home' );
|
75 |
+
|
76 |
+
if ( WPSEO_Utils::is_yoast_seo_page() ) {
|
77 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
78 |
+
}
|
79 |
+
|
80 |
+
if ( WPSEO_Utils::is_api_available() ) {
|
81 |
+
$configuration = new WPSEO_Configuration_Page();
|
82 |
+
$configuration->set_hooks();
|
83 |
+
$configuration->catch_configuration_request();
|
84 |
+
}
|
85 |
+
|
86 |
+
$this->set_upsell_notice();
|
87 |
+
|
88 |
+
$this->check_php_version();
|
89 |
+
$this->initialize_cornerstone_content();
|
90 |
+
|
91 |
+
$integrations[] = new WPSEO_Yoast_Columns();
|
92 |
+
$integrations[] = new WPSEO_License_Page_Manager();
|
93 |
+
$integrations[] = new WPSEO_Statistic_Integration();
|
94 |
+
$integrations[] = new WPSEO_Slug_Change_Watcher();
|
95 |
+
$integrations[] = new WPSEO_Capability_Manager_Integration( WPSEO_Capability_Manager_Factory::get() );
|
96 |
+
$integrations = array_merge( $integrations, $this->initialize_seo_links() );
|
97 |
+
|
98 |
+
/** @var WPSEO_WordPress_Integration $integration */
|
99 |
+
foreach ( $integrations as $integration ) {
|
100 |
+
$integration->register_hooks();
|
101 |
+
}
|
102 |
+
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Setting the hooks for importing data from other plugins.
|
107 |
+
*/
|
108 |
+
public function import_plugin_hooks() {
|
109 |
+
if ( current_user_can( $this->get_manage_options_cap() ) ) {
|
110 |
+
$plugin_imports = array(
|
111 |
+
'wpSEO' => new WPSEO_Import_WPSEO_Hooks(),
|
112 |
+
'aioseo' => new WPSEO_Import_AIOSEO_Hooks(),
|
113 |
+
);
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Schedules a rewrite flush to happen at shutdown.
|
119 |
+
*/
|
120 |
+
public function schedule_rewrite_flush() {
|
121 |
+
add_action( 'shutdown', 'flush_rewrite_rules' );
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Returns all the classes for the admin features.
|
126 |
+
*
|
127 |
+
* @return array
|
128 |
+
*/
|
129 |
+
public function get_admin_features() {
|
130 |
+
return $this->admin_features;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Register assets needed on admin pages.
|
135 |
+
*/
|
136 |
+
public function enqueue_assets() {
|
137 |
+
if ( 'wpseo_licenses' === filter_input( INPUT_GET, 'page' ) ) {
|
138 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
139 |
+
$asset_manager->enqueue_style( 'extensions' );
|
140 |
+
}
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Returns the manage_options capability.
|
145 |
+
*
|
146 |
+
* @return string The capability to use.
|
147 |
+
*/
|
148 |
+
public function get_manage_options_cap() {
|
149 |
+
/**
|
150 |
+
* Filter: 'wpseo_manage_options_capability' - Allow changing the capability users need to view the settings pages.
|
151 |
+
*
|
152 |
+
* @api string unsigned The capability.
|
153 |
+
*/
|
154 |
+
return apply_filters( 'wpseo_manage_options_capability', 'wpseo_manage_options' );
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Maps the manage_options cap on saving an options page to wpseo_manage_options.
|
159 |
+
*/
|
160 |
+
public function map_manage_options_cap() {
|
161 |
+
$option_page = ! empty( $_POST['option_page'] ) ? $_POST['option_page'] : ''; // WPCS: CSRF ok.
|
162 |
+
|
163 |
+
if ( strpos( $option_page, 'yoast_wpseo' ) === 0 ) {
|
164 |
+
add_filter( 'option_page_capability_' . $option_page, array( $this, 'get_manage_options_cap' ) );
|
165 |
+
}
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Adds the ability to choose how many posts are displayed per page
|
170 |
+
* on the bulk edit pages.
|
171 |
+
*/
|
172 |
+
public function bulk_edit_options() {
|
173 |
+
$option = 'per_page';
|
174 |
+
$args = array(
|
175 |
+
'label' => __( 'Posts', 'wordpress-seo' ),
|
176 |
+
'default' => 10,
|
177 |
+
'option' => 'wpseo_posts_per_page',
|
178 |
+
);
|
179 |
+
add_screen_option( $option, $args );
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Saves the posts per page limit for bulk edit pages.
|
184 |
+
*
|
185 |
+
* @param int $status Status value to pass through.
|
186 |
+
* @param string $option Option name.
|
187 |
+
* @param int $value Count value to check.
|
188 |
+
*
|
189 |
+
* @return int
|
190 |
+
*/
|
191 |
+
public function save_bulk_edit_options( $status, $option, $value ) {
|
192 |
+
if ( 'wpseo_posts_per_page' === $option && ( $value > 0 && $value < 1000 ) ) {
|
193 |
+
return $value;
|
194 |
+
}
|
195 |
+
|
196 |
+
return $status;
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Adds links to Premium Support and FAQ under the plugin in the plugin overview page.
|
201 |
+
*
|
202 |
+
* @staticvar string $this_plugin Holds the directory & filename for the plugin.
|
203 |
+
*
|
204 |
+
* @param array $links Array of links for the plugins, adapted when the current plugin is found.
|
205 |
+
* @param string $file The filename for the current plugin, which the filter loops through.
|
206 |
+
*
|
207 |
+
* @return array $links
|
208 |
+
*/
|
209 |
+
public function add_action_link( $links, $file ) {
|
210 |
+
if ( WPSEO_BASENAME === $file && WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) {
|
211 |
+
$settings_link = '<a href="' . esc_url( admin_url( 'admin.php?page=' . self::PAGE_IDENTIFIER ) ) . '">' . __( 'Settings', 'wordpress-seo' ) . '</a>';
|
212 |
+
array_unshift( $links, $settings_link );
|
213 |
+
}
|
214 |
+
|
215 |
+
if ( class_exists( 'WPSEO_Product_Premium' ) ) {
|
216 |
+
$product_premium = new WPSEO_Product_Premium();
|
217 |
+
$extension_manager = new WPSEO_Extension_Manager();
|
218 |
+
|
219 |
+
if ( $extension_manager->is_activated( $product_premium->get_slug() ) ) {
|
220 |
+
return $links;
|
221 |
+
}
|
222 |
+
}
|
223 |
+
|
224 |
+
// Add link to premium support landing page.
|
225 |
+
$premium_link = '<a href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/1yb' ) ) . '">' . __( 'Premium Support', 'wordpress-seo' ) . '</a>';
|
226 |
+
array_unshift( $links, $premium_link );
|
227 |
+
|
228 |
+
// Add link to docs.
|
229 |
+
$faq_link = '<a href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/1yc' ) ) . '">' . __( 'FAQ', 'wordpress-seo' ) . '</a>';
|
230 |
+
array_unshift( $links, $faq_link );
|
231 |
+
|
232 |
+
return $links;
|
233 |
+
}
|
234 |
+
|
235 |
+
/**
|
236 |
+
* Enqueues the (tiny) global JS needed for the plugin.
|
237 |
+
*/
|
238 |
+
public function config_page_scripts() {
|
239 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
240 |
+
$asset_manager->enqueue_script( 'admin-global-script' );
|
241 |
+
|
242 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-global-script', 'wpseoAdminGlobalL10n', $this->localize_admin_global_script() );
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Enqueues the (tiny) global stylesheet needed for the plugin.
|
247 |
+
*/
|
248 |
+
public function enqueue_global_style() {
|
249 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
250 |
+
$asset_manager->enqueue_style( 'admin-global' );
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Filter the $contactmethods array and add Facebook, Google+ and Twitter.
|
255 |
+
*
|
256 |
+
* These are used with the Facebook author, rel="author" and Twitter cards implementation.
|
257 |
+
*
|
258 |
+
* @param array $contactmethods Currently set contactmethods.
|
259 |
+
*
|
260 |
+
* @return array $contactmethods with added contactmethods.
|
261 |
+
*/
|
262 |
+
public function update_contactmethods( $contactmethods ) {
|
263 |
+
// Add Google+.
|
264 |
+
$contactmethods['googleplus'] = __( 'Google+', 'wordpress-seo' );
|
265 |
+
// Add Twitter.
|
266 |
+
$contactmethods['twitter'] = __( 'Twitter username (without @)', 'wordpress-seo' );
|
267 |
+
// Add Facebook.
|
268 |
+
$contactmethods['facebook'] = __( 'Facebook profile URL', 'wordpress-seo' );
|
269 |
+
|
270 |
+
return $contactmethods;
|
271 |
+
}
|
272 |
+
|
273 |
+
/**
|
274 |
+
* Log the updated timestamp for user profiles when theme is changed.
|
275 |
+
*/
|
276 |
+
public function switch_theme() {
|
277 |
+
$users = get_users( array( 'who' => 'authors' ) );
|
278 |
+
if ( is_array( $users ) && $users !== array() ) {
|
279 |
+
foreach ( $users as $user ) {
|
280 |
+
update_user_meta( $user->ID, '_yoast_wpseo_profile_updated', time() );
|
281 |
+
}
|
282 |
+
}
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
* Localization for the dismiss urls.
|
287 |
+
*
|
288 |
+
* @return array
|
289 |
+
*/
|
290 |
+
private function localize_admin_global_script() {
|
291 |
+
return array(
|
292 |
+
'dismiss_about_url' => $this->get_dismiss_url( 'wpseo-dismiss-about' ),
|
293 |
+
'dismiss_tagline_url' => $this->get_dismiss_url( 'wpseo-dismiss-tagline-notice' ),
|
294 |
+
'help_video_iframe_title' => __( 'Yoast SEO video tutorial', 'wordpress-seo' ),
|
295 |
+
'scrollable_table_hint' => __( 'Scroll to see the table content.', 'wordpress-seo' ),
|
296 |
+
);
|
297 |
+
}
|
298 |
+
|
299 |
+
/**
|
300 |
+
* Extending the current page URL with two params to be able to ignore the notice.
|
301 |
+
*
|
302 |
+
* @param string $dismiss_param The param used to dismiss the notification.
|
303 |
+
*
|
304 |
+
* @return string
|
305 |
+
*/
|
306 |
+
private function get_dismiss_url( $dismiss_param ) {
|
307 |
+
$arr_params = array(
|
308 |
+
$dismiss_param => '1',
|
309 |
+
'nonce' => wp_create_nonce( $dismiss_param ),
|
310 |
+
);
|
311 |
+
|
312 |
+
return esc_url( add_query_arg( $arr_params ) );
|
313 |
+
}
|
314 |
+
|
315 |
+
/**
|
316 |
+
* Sets the upsell notice.
|
317 |
+
*/
|
318 |
+
protected function set_upsell_notice() {
|
319 |
+
$upsell = new WPSEO_Product_Upsell_Notice();
|
320 |
+
$upsell->dismiss_notice_listener();
|
321 |
+
$upsell->initialize();
|
322 |
+
}
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Initializes Whip to show a notice for outdated PHP versions.
|
326 |
+
*/
|
327 |
+
protected function check_php_version() {
|
328 |
+
if ( ! current_user_can( 'manage_options' ) ) {
|
329 |
+
return;
|
330 |
+
}
|
331 |
+
|
332 |
+
if ( ! $this->on_dashboard_page() ) {
|
333 |
+
return;
|
334 |
+
}
|
335 |
+
|
336 |
+
whip_wp_check_versions( array(
|
337 |
+
'php' => '>=5.4',
|
338 |
+
) );
|
339 |
+
}
|
340 |
+
|
341 |
+
/**
|
342 |
+
* Whether we are on the admin dashboard page.
|
343 |
+
*
|
344 |
+
* @returns bool
|
345 |
+
*/
|
346 |
+
protected function on_dashboard_page() {
|
347 |
+
return 'index.php' === $GLOBALS['pagenow'];
|
348 |
+
}
|
349 |
+
|
350 |
+
/**
|
351 |
+
* Loads the cornerstone filter.
|
352 |
+
*/
|
353 |
+
protected function initialize_cornerstone_content() {
|
354 |
+
if ( ! WPSEO_Options::get( 'enable_cornerstone_content' ) ) {
|
355 |
+
return;
|
356 |
+
}
|
357 |
+
|
358 |
+
$cornerstone = new WPSEO_Cornerstone();
|
359 |
+
$cornerstone->register_hooks();
|
360 |
+
|
361 |
+
$cornerstone_filter = new WPSEO_Cornerstone_Filter();
|
362 |
+
$cornerstone_filter->register_hooks();
|
363 |
+
}
|
364 |
+
|
365 |
+
/**
|
366 |
+
* Initializes the seo link watcher.
|
367 |
+
*
|
368 |
+
* @returns WPSEO_WordPress_Integration[]
|
369 |
+
*/
|
370 |
+
protected function initialize_seo_links() {
|
371 |
+
$integrations = array();
|
372 |
+
|
373 |
+
$link_table_compatibility_notifier = new WPSEO_Link_Compatibility_Notifier();
|
374 |
+
$link_table_accessible_notifier = new WPSEO_Link_Table_Accessible_Notifier();
|
375 |
+
|
376 |
+
if ( ! WPSEO_Options::get( 'enable_text_link_counter' ) ) {
|
377 |
+
$link_table_compatibility_notifier->remove_notification();
|
378 |
+
|
379 |
+
return $integrations;
|
380 |
+
}
|
381 |
+
|
382 |
+
$integrations[] = new WPSEO_Link_Cleanup_Transient();
|
383 |
+
|
384 |
+
// Only use the link module for PHP 5.3 and higher and show a notice when version is wrong.
|
385 |
+
if ( version_compare( phpversion(), '5.3', '<' ) ) {
|
386 |
+
$link_table_compatibility_notifier->add_notification();
|
387 |
+
|
388 |
+
return $integrations;
|
389 |
+
}
|
390 |
+
|
391 |
+
$link_table_compatibility_notifier->remove_notification();
|
392 |
+
|
393 |
+
// When the table doesn't exists, just add the notification and return early.
|
394 |
+
if ( ! WPSEO_Link_Table_Accessible::is_accessible() ) {
|
395 |
+
WPSEO_Link_Table_Accessible::cleanup();
|
396 |
+
}
|
397 |
+
|
398 |
+
if ( ! WPSEO_Meta_Table_Accessible::is_accessible() ) {
|
399 |
+
WPSEO_Meta_Table_Accessible::cleanup();
|
400 |
+
}
|
401 |
+
|
402 |
+
if ( ! WPSEO_Link_Table_Accessible::is_accessible() || ! WPSEO_Meta_Table_Accessible::is_accessible() ) {
|
403 |
+
$link_table_accessible_notifier->add_notification();
|
404 |
+
|
405 |
+
return $integrations;
|
406 |
+
}
|
407 |
+
|
408 |
+
$link_table_accessible_notifier->remove_notification();
|
409 |
+
|
410 |
+
$integrations[] = new WPSEO_Link_Columns( new WPSEO_Meta_Storage() );
|
411 |
+
$integrations[] = new WPSEO_Link_Reindex_Dashboard();
|
412 |
+
$integrations[] = new WPSEO_Link_Notifier();
|
413 |
+
|
414 |
+
// Adds a filter to exclude the attachments from the link count.
|
415 |
+
add_filter( 'wpseo_link_count_post_types', array( 'WPSEO_Post_Type', 'filter_attachment_post_type' ) );
|
416 |
+
|
417 |
+
return $integrations;
|
418 |
+
}
|
419 |
+
|
420 |
+
/********************** DEPRECATED METHODS **********************/
|
421 |
+
|
422 |
+
// @codeCoverageIgnoreStart
|
423 |
+
/**
|
424 |
+
* Register the menu item and its sub menu's.
|
425 |
+
*
|
426 |
+
* @deprecated 5.5
|
427 |
+
*/
|
428 |
+
public function register_settings_page() {
|
429 |
+
_deprecated_function( __METHOD__, 'WPSEO 5.5.0' );
|
430 |
+
}
|
431 |
+
|
432 |
+
/**
|
433 |
+
* Register the settings page for the Network settings.
|
434 |
+
*
|
435 |
+
* @deprecated 5.5
|
436 |
+
*/
|
437 |
+
public function register_network_settings_page() {
|
438 |
+
_deprecated_function( __METHOD__, 'WPSEO 5.5.0' );
|
439 |
+
}
|
440 |
+
|
441 |
+
/**
|
442 |
+
* Load the form for a WPSEO admin page.
|
443 |
+
*
|
444 |
+
* @deprecated 5.5
|
445 |
+
*/
|
446 |
+
public function load_page() {
|
447 |
+
_deprecated_function( __METHOD__, 'WPSEO 5.5.0' );
|
448 |
+
}
|
449 |
+
|
450 |
+
/**
|
451 |
+
* Loads the form for the network configuration page.
|
452 |
+
*
|
453 |
+
* @deprecated 5.5
|
454 |
+
*/
|
455 |
+
public function network_config_page() {
|
456 |
+
_deprecated_function( __METHOD__, 'WPSEO 5.5.0' );
|
457 |
+
}
|
458 |
+
|
459 |
+
/**
|
460 |
+
* Filters all advanced settings pages from the given pages.
|
461 |
+
*
|
462 |
+
* @deprecated 5.5
|
463 |
+
* @param array $pages The pages to filter.
|
464 |
+
*/
|
465 |
+
public function filter_settings_pages( array $pages ) {
|
466 |
+
_deprecated_function( __METHOD__, 'WPSEO 5.5.0' );
|
467 |
+
}
|
468 |
+
|
469 |
+
/**
|
470 |
+
* Cleans stopwords out of the slug, if the slug hasn't been set yet.
|
471 |
+
*
|
472 |
+
* @deprecated 7.0
|
473 |
+
*
|
474 |
+
* @return void
|
475 |
+
*/
|
476 |
+
public function remove_stopwords_from_slug() {
|
477 |
+
_deprecated_function( __METHOD__, 'WPSEO 7.0' );
|
478 |
+
}
|
479 |
+
|
480 |
+
/**
|
481 |
+
* Filter the stopwords from the slug.
|
482 |
+
*
|
483 |
+
* @deprecated 7.0
|
484 |
+
*
|
485 |
+
* @return void
|
486 |
+
*/
|
487 |
+
public function filter_stopwords_from_slug() {
|
488 |
+
_deprecated_function( __METHOD__, 'WPSEO 7.0' );
|
489 |
+
}
|
490 |
+
|
491 |
+
/**
|
492 |
+
* Adds contextual help to the titles & metas page.
|
493 |
+
*
|
494 |
+
* @deprecated 5.6.0
|
495 |
+
*/
|
496 |
+
public function title_metas_help_tab() {
|
497 |
+
_deprecated_function( __METHOD__, '5.6.0' );
|
498 |
+
|
499 |
+
$screen = get_current_screen();
|
500 |
+
|
501 |
+
$screen->set_help_sidebar( '
|
502 |
+
<p><strong>' . __( 'For more information:', 'wordpress-seo' ) . '</strong></p>
|
503 |
+
<p><a target="_blank" href="https://yoast.com/wordpress-seo/#titles">' . __( 'Title optimization', 'wordpress-seo' ) . '</a></p>
|
504 |
+
<p><a target="_blank" href="https://yoast.com/google-page-title/">' . __( 'Why Google won\'t display the right page title', 'wordpress-seo' ) . '</a></p>'
|
505 |
+
);
|
506 |
+
|
507 |
+
$screen->add_help_tab(
|
508 |
+
array(
|
509 |
+
'id' => 'basic-help',
|
510 |
+
'title' => __( 'Template explanation', 'wordpress-seo' ),
|
511 |
+
'content' => "\n\t\t<h2>" . __( 'Template explanation', 'wordpress-seo' ) . "</h2>\n\t\t" . '<p>' .
|
512 |
+
sprintf(
|
513 |
+
/* translators: %1$s expands to Yoast SEO. */
|
514 |
+
__( 'The title & metas settings for %1$s are made up of variables that are replaced by specific values from the page when the page is displayed. The tabs on the left explain the available variables.', 'wordpress-seo' ),
|
515 |
+
'Yoast SEO'
|
516 |
+
) .
|
517 |
+
'</p><p>' . __( 'Note that not all variables can be used in every template.', 'wordpress-seo' ) . '</p>',
|
518 |
+
)
|
519 |
+
);
|
520 |
+
|
521 |
+
$screen->add_help_tab(
|
522 |
+
array(
|
523 |
+
'id' => 'title-vars',
|
524 |
+
'title' => __( 'Basic Variables', 'wordpress-seo' ),
|
525 |
+
'content' => "\n\t\t<h2>" . __( 'Basic Variables', 'wordpress-seo' ) . "</h2>\n\t\t" . WPSEO_Replace_Vars::get_basic_help_texts(),
|
526 |
+
)
|
527 |
+
);
|
528 |
+
|
529 |
+
$screen->add_help_tab(
|
530 |
+
array(
|
531 |
+
'id' => 'title-vars-advanced',
|
532 |
+
'title' => __( 'Advanced Variables', 'wordpress-seo' ),
|
533 |
+
'content' => "\n\t\t<h2>" . __( 'Advanced Variables', 'wordpress-seo' ) . "</h2>\n\t\t" . WPSEO_Replace_Vars::get_advanced_help_texts(),
|
534 |
+
)
|
535 |
+
);
|
536 |
+
}
|
537 |
+
|
538 |
+
// @codeCoverageIgnoreEnd
|
539 |
+
}
|
admin/class-asset.php
ADDED
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents a WPSEO asset
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Asset {
|
10 |
+
|
11 |
+
const TYPE_JS = 'js';
|
12 |
+
const TYPE_CSS = 'css';
|
13 |
+
|
14 |
+
const NAME = 'name';
|
15 |
+
const SRC = 'src';
|
16 |
+
const DEPS = 'deps';
|
17 |
+
const VERSION = 'version';
|
18 |
+
|
19 |
+
// Style specific.
|
20 |
+
const MEDIA = 'media';
|
21 |
+
const RTL = 'rtl';
|
22 |
+
|
23 |
+
// Script specific.
|
24 |
+
const IN_FOOTER = 'in_footer';
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
protected $name;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var string
|
33 |
+
*/
|
34 |
+
protected $src;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @var string|array
|
38 |
+
*/
|
39 |
+
protected $deps;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @var string
|
43 |
+
*/
|
44 |
+
protected $version;
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @var string
|
48 |
+
*/
|
49 |
+
protected $media;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @var boolean
|
53 |
+
*/
|
54 |
+
protected $in_footer;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* @var boolean
|
58 |
+
*/
|
59 |
+
protected $rtl;
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @var string
|
63 |
+
*/
|
64 |
+
protected $suffix;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @param array $args The arguments for this asset.
|
68 |
+
*
|
69 |
+
* @throws InvalidArgumentException Throws when no name or src has been provided.
|
70 |
+
*/
|
71 |
+
public function __construct( array $args ) {
|
72 |
+
if ( ! isset( $args['name'] ) ) {
|
73 |
+
throw new InvalidArgumentException( 'name is a required argument' );
|
74 |
+
}
|
75 |
+
|
76 |
+
if ( ! isset( $args['src'] ) ) {
|
77 |
+
throw new InvalidArgumentException( 'src is a required argument' );
|
78 |
+
}
|
79 |
+
|
80 |
+
$args = array_merge( array(
|
81 |
+
'deps' => array(),
|
82 |
+
'version' => WPSEO_VERSION,
|
83 |
+
'in_footer' => true,
|
84 |
+
'rtl' => true,
|
85 |
+
'media' => 'all',
|
86 |
+
'suffix' => WPSEO_CSSJS_SUFFIX,
|
87 |
+
), $args );
|
88 |
+
|
89 |
+
$this->name = $args['name'];
|
90 |
+
$this->src = $args['src'];
|
91 |
+
$this->deps = $args['deps'];
|
92 |
+
$this->version = $args['version'];
|
93 |
+
$this->media = $args['media'];
|
94 |
+
$this->in_footer = $args['in_footer'];
|
95 |
+
$this->rtl = $args['rtl'];
|
96 |
+
$this->suffix = $args['suffix'];
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* @return string
|
101 |
+
*/
|
102 |
+
public function get_name() {
|
103 |
+
return $this->name;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* @return string
|
108 |
+
*/
|
109 |
+
public function get_src() {
|
110 |
+
return $this->src;
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* @return array|string
|
115 |
+
*/
|
116 |
+
public function get_deps() {
|
117 |
+
return $this->deps;
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* @return string
|
122 |
+
*/
|
123 |
+
public function get_version() {
|
124 |
+
return $this->version;
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* @return string
|
129 |
+
*/
|
130 |
+
public function get_media() {
|
131 |
+
return $this->media;
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* @return boolean
|
136 |
+
*/
|
137 |
+
public function is_in_footer() {
|
138 |
+
return $this->in_footer;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* @return boolean
|
143 |
+
*/
|
144 |
+
public function has_rtl() {
|
145 |
+
return $this->rtl;
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* @return string
|
150 |
+
*/
|
151 |
+
public function get_suffix() {
|
152 |
+
return $this->suffix;
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Returns the full URL for this asset based on the path to the plugin file.
|
157 |
+
*
|
158 |
+
* @param string $type Type of asset.
|
159 |
+
* @param string $plugin_file Absolute path to the plugin file.
|
160 |
+
*
|
161 |
+
* @return string The full URL to the asset.
|
162 |
+
*/
|
163 |
+
public function get_url( $type, $plugin_file ) {
|
164 |
+
_deprecated_function( __CLASS__ . '::get_url', '6.2', 'WPSEO_Admin_Asset_SEO_Location::get_url' );
|
165 |
+
|
166 |
+
$asset_location = new WPSEO_Admin_Asset_SEO_Location( $plugin_file );
|
167 |
+
|
168 |
+
return $asset_location->get_url( $this, $type );
|
169 |
+
}
|
170 |
+
}
|
admin/class-bulk-description-editor-list-table.php
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Bulk Editor
|
4 |
+
* @since 1.5.0
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Implements table for bulk description editing.
|
9 |
+
*/
|
10 |
+
class WPSEO_Bulk_Description_List_Table extends WPSEO_Bulk_List_Table {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Current type for this class will be (meta) description.
|
14 |
+
*
|
15 |
+
* @var string
|
16 |
+
*/
|
17 |
+
protected $page_type = 'description';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Settings with are used in __construct
|
21 |
+
*
|
22 |
+
* @var array
|
23 |
+
*/
|
24 |
+
protected $settings = array(
|
25 |
+
'singular' => 'wpseo_bulk_description',
|
26 |
+
'plural' => 'wpseo_bulk_descriptions',
|
27 |
+
'ajax' => true,
|
28 |
+
);
|
29 |
+
|
30 |
+
/**
|
31 |
+
* The field in the database where meta field is saved.
|
32 |
+
*
|
33 |
+
* @var string
|
34 |
+
*/
|
35 |
+
protected $target_db_field = 'metadesc';
|
36 |
+
|
37 |
+
/**
|
38 |
+
* The columns shown on the table
|
39 |
+
*
|
40 |
+
* @return array
|
41 |
+
*/
|
42 |
+
public function get_columns() {
|
43 |
+
$columns = array(
|
44 |
+
'col_existing_yoast_seo_metadesc' => __( 'Existing Yoast Meta Description', 'wordpress-seo' ),
|
45 |
+
'col_new_yoast_seo_metadesc' => __( 'New Yoast Meta Description', 'wordpress-seo' ),
|
46 |
+
);
|
47 |
+
|
48 |
+
return $this->merge_columns( $columns );
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Parse the metadescription
|
53 |
+
*
|
54 |
+
* @param string $column_name Column name.
|
55 |
+
* @param object $record Data object.
|
56 |
+
* @param string $attributes HTML attributes.
|
57 |
+
*
|
58 |
+
* @return string
|
59 |
+
*/
|
60 |
+
protected function parse_page_specific_column( $column_name, $record, $attributes ) {
|
61 |
+
switch ( $column_name ) {
|
62 |
+
case 'col_new_yoast_seo_metadesc':
|
63 |
+
return sprintf(
|
64 |
+
'<textarea id="%1$s" name="%1$s" class="wpseo-new-metadesc" data-id="%2$s" aria-labelledby="col_new_yoast_seo_metadesc"></textarea>',
|
65 |
+
esc_attr( 'wpseo-new-metadesc-' . $record->ID ),
|
66 |
+
esc_attr( $record->ID )
|
67 |
+
);
|
68 |
+
|
69 |
+
case 'col_existing_yoast_seo_metadesc':
|
70 |
+
// @todo Inconsistent return/echo behavior R.
|
71 |
+
echo $this->parse_meta_data_field( $record->ID, $attributes );
|
72 |
+
break;
|
73 |
+
}
|
74 |
+
}
|
75 |
+
} /* End of class */
|
admin/class-bulk-editor-list-table.php
ADDED
@@ -0,0 +1,1019 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Bulk Editor
|
4 |
+
* @since 1.5.0
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Implements table for bulk editing.
|
9 |
+
*/
|
10 |
+
class WPSEO_Bulk_List_Table extends WP_List_Table {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* The nonce that was passed with the request
|
14 |
+
*
|
15 |
+
* @var string
|
16 |
+
*/
|
17 |
+
private $nonce;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Array of post types for which the current user has `edit_others_posts` capabilities.
|
21 |
+
*
|
22 |
+
* @var array
|
23 |
+
*/
|
24 |
+
private $all_posts;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Array of post types for which the current user has `edit_posts` capabilities, but not `edit_others_posts`.
|
28 |
+
*
|
29 |
+
* @var array
|
30 |
+
*/
|
31 |
+
private $own_posts;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Saves all the metadata into this array.
|
35 |
+
*
|
36 |
+
* @var array
|
37 |
+
*/
|
38 |
+
protected $meta_data = array();
|
39 |
+
|
40 |
+
/**
|
41 |
+
* The current requested page_url
|
42 |
+
*
|
43 |
+
* @var string
|
44 |
+
*/
|
45 |
+
private $request_url;
|
46 |
+
|
47 |
+
/**
|
48 |
+
* The current page (depending on $_GET['paged']) if current tab is for current page_type, else it will be 1
|
49 |
+
*
|
50 |
+
* @var integer
|
51 |
+
*/
|
52 |
+
private $current_page;
|
53 |
+
|
54 |
+
/**
|
55 |
+
* The current post filter, if is used (depending on $_GET['post_type_filter'])
|
56 |
+
*
|
57 |
+
* @var string
|
58 |
+
*/
|
59 |
+
private $current_filter;
|
60 |
+
|
61 |
+
/**
|
62 |
+
* The current post status, if is used (depending on $_GET['post_status'])
|
63 |
+
*
|
64 |
+
* @var string
|
65 |
+
*/
|
66 |
+
private $current_status;
|
67 |
+
|
68 |
+
/**
|
69 |
+
* The current sorting, if used (depending on $_GET['order'] and $_GET['orderby'])
|
70 |
+
*
|
71 |
+
* @var string
|
72 |
+
*/
|
73 |
+
private $current_order;
|
74 |
+
|
75 |
+
/**
|
76 |
+
* The page_type for current class instance (for example: title / description).
|
77 |
+
*
|
78 |
+
* @var string
|
79 |
+
*/
|
80 |
+
protected $page_type;
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Based on the page_type ($this->page_type) there will be constructed an url part, for subpages and
|
84 |
+
* navigation
|
85 |
+
*
|
86 |
+
* @var string
|
87 |
+
*/
|
88 |
+
protected $page_url;
|
89 |
+
|
90 |
+
/**
|
91 |
+
* The settings which will be used in the __construct.
|
92 |
+
*
|
93 |
+
* @var array
|
94 |
+
*/
|
95 |
+
protected $settings;
|
96 |
+
|
97 |
+
/**
|
98 |
+
* @var array
|
99 |
+
*/
|
100 |
+
protected $pagination = array();
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Class constructor
|
104 |
+
*/
|
105 |
+
public function __construct() {
|
106 |
+
parent::__construct( $this->settings );
|
107 |
+
|
108 |
+
$this->request_url = $_SERVER['REQUEST_URI'];
|
109 |
+
$this->current_page = ( ! empty( $_GET['paged'] ) ) ? $_GET['paged'] : 1;
|
110 |
+
$this->current_filter = ( ! empty( $_GET['post_type_filter'] ) ) ? $_GET['post_type_filter'] : 1;
|
111 |
+
$this->current_status = ( ! empty( $_GET['post_status'] ) ) ? $_GET['post_status'] : 1;
|
112 |
+
$this->current_order = array(
|
113 |
+
'order' => ( ! empty( $_GET['order'] ) ) ? $_GET['order'] : 'asc',
|
114 |
+
'orderby' => ( ! empty( $_GET['orderby'] ) ) ? $_GET['orderby'] : 'post_title',
|
115 |
+
);
|
116 |
+
|
117 |
+
$this->verify_nonce();
|
118 |
+
|
119 |
+
$this->nonce = wp_create_nonce( 'bulk-editor-table' );
|
120 |
+
$this->page_url = "&nonce={$this->nonce}&type={$this->page_type}#top#{$this->page_type}";
|
121 |
+
|
122 |
+
$this->populate_editable_post_types();
|
123 |
+
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Verifies nonce if additional parameters have been sent.
|
128 |
+
*
|
129 |
+
* Shows an error notification if the nonce check fails.
|
130 |
+
*/
|
131 |
+
private function verify_nonce() {
|
132 |
+
if ( $this->should_verify_nonce() && ! wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), 'bulk-editor-table' ) ) {
|
133 |
+
Yoast_Notification_Center::get()->add_notification(
|
134 |
+
new Yoast_Notification(
|
135 |
+
__( 'You are not allowed to access this page.', 'wordpress-seo' ),
|
136 |
+
array( 'type' => Yoast_Notification::ERROR )
|
137 |
+
)
|
138 |
+
);
|
139 |
+
Yoast_Notification_Center::get()->display_notifications();
|
140 |
+
die;
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Checks if additional parameters have been sent to determine if nonce should be checked or not.
|
146 |
+
*
|
147 |
+
* @return bool
|
148 |
+
*/
|
149 |
+
private function should_verify_nonce() {
|
150 |
+
$possible_params = array(
|
151 |
+
'type',
|
152 |
+
'paged',
|
153 |
+
'post_type_filter',
|
154 |
+
'post_status',
|
155 |
+
'order',
|
156 |
+
'orderby',
|
157 |
+
);
|
158 |
+
|
159 |
+
foreach ( $possible_params as $param_name ) {
|
160 |
+
if ( filter_input( INPUT_GET, $param_name ) ) {
|
161 |
+
return true;
|
162 |
+
}
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Prepares the data and renders the page.
|
168 |
+
*/
|
169 |
+
public function show_page() {
|
170 |
+
$this->prepare_page_navigation();
|
171 |
+
$this->prepare_items();
|
172 |
+
|
173 |
+
$this->views();
|
174 |
+
$this->display();
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Used in the constructor to build a reference list of post types the current user can edit.
|
179 |
+
*/
|
180 |
+
protected function populate_editable_post_types() {
|
181 |
+
$post_types = get_post_types(
|
182 |
+
array(
|
183 |
+
'public' => true,
|
184 |
+
'exclude_from_search' => false,
|
185 |
+
),
|
186 |
+
'object'
|
187 |
+
);
|
188 |
+
|
189 |
+
$this->all_posts = array();
|
190 |
+
$this->own_posts = array();
|
191 |
+
|
192 |
+
if ( is_array( $post_types ) && $post_types !== array() ) {
|
193 |
+
foreach ( $post_types as $post_type ) {
|
194 |
+
if ( ! current_user_can( $post_type->cap->edit_posts ) ) {
|
195 |
+
continue;
|
196 |
+
}
|
197 |
+
|
198 |
+
if ( current_user_can( $post_type->cap->edit_others_posts ) ) {
|
199 |
+
$this->all_posts[] = esc_sql( $post_type->name );
|
200 |
+
}
|
201 |
+
else {
|
202 |
+
$this->own_posts[] = esc_sql( $post_type->name );
|
203 |
+
}
|
204 |
+
}
|
205 |
+
}
|
206 |
+
}
|
207 |
+
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Will shown the navigation for the table like pagenavigation and pagefilter;
|
211 |
+
*
|
212 |
+
* @param string $which Table nav location (such as top).
|
213 |
+
*/
|
214 |
+
public function display_tablenav( $which ) {
|
215 |
+
$post_status = sanitize_text_field( filter_input( INPUT_GET, 'post_status' ) );
|
216 |
+
?>
|
217 |
+
<div class="tablenav <?php echo esc_attr( $which ); ?>">
|
218 |
+
|
219 |
+
<?php if ( 'top' === $which ) { ?>
|
220 |
+
<form id="posts-filter" action="" method="get">
|
221 |
+
<input type="hidden" name="nonce" value="<?php echo esc_attr( $this->nonce ); ?>"/>
|
222 |
+
<input type="hidden" name="page" value="wpseo_tools"/>
|
223 |
+
<input type="hidden" name="tool" value="bulk-editor"/>
|
224 |
+
<input type="hidden" name="type" value="<?php echo esc_attr( $this->page_type ); ?>"/>
|
225 |
+
<input type="hidden" name="orderby"
|
226 |
+
value="<?php echo esc_attr( filter_input( INPUT_GET, 'orderby' ) ); ?>"/>
|
227 |
+
<input type="hidden" name="order"
|
228 |
+
value="<?php echo esc_attr( filter_input( INPUT_GET, 'order' ) ); ?>"/>
|
229 |
+
<input type="hidden" name="post_type_filter"
|
230 |
+
value="<?php echo esc_attr( filter_input( INPUT_GET, 'post_type_filter' ) ); ?>"/>
|
231 |
+
<?php if ( ! empty( $post_status ) ) { ?>
|
232 |
+
<input type="hidden" name="post_status" value="<?php echo esc_attr( $post_status ); ?>"/>
|
233 |
+
<?php } ?>
|
234 |
+
<?php } ?>
|
235 |
+
|
236 |
+
<?php
|
237 |
+
$this->extra_tablenav( $which );
|
238 |
+
$this->pagination( $which );
|
239 |
+
?>
|
240 |
+
|
241 |
+
<br class="clear"/>
|
242 |
+
<?php if ( 'top' === $which ) { ?>
|
243 |
+
</form>
|
244 |
+
<?php } ?>
|
245 |
+
</div>
|
246 |
+
|
247 |
+
<?php
|
248 |
+
}
|
249 |
+
|
250 |
+
/**
|
251 |
+
* This function builds the base sql subquery used in this class.
|
252 |
+
*
|
253 |
+
* This function takes into account the post types in which the current user can
|
254 |
+
* edit all posts, and the ones the current user can only edit his/her own.
|
255 |
+
*
|
256 |
+
* @return string $subquery The subquery, which should always be used in $wpdb->prepare(), passing the current user_id in as the first parameter.
|
257 |
+
*/
|
258 |
+
public function get_base_subquery() {
|
259 |
+
global $wpdb;
|
260 |
+
|
261 |
+
$all_posts_string = "'" . implode( "', '", $this->all_posts ) . "'";
|
262 |
+
$own_posts_string = "'" . implode( "', '", $this->own_posts ) . "'";
|
263 |
+
|
264 |
+
$post_author = esc_sql( (int) get_current_user_id() );
|
265 |
+
|
266 |
+
$subquery = "(
|
267 |
+
SELECT *
|
268 |
+
FROM {$wpdb->posts}
|
269 |
+
WHERE post_type IN ({$all_posts_string})
|
270 |
+
UNION ALL
|
271 |
+
SELECT *
|
272 |
+
FROM {$wpdb->posts}
|
273 |
+
WHERE post_type IN ({$own_posts_string}) AND post_author = {$post_author}
|
274 |
+
) sub_base";
|
275 |
+
|
276 |
+
return $subquery;
|
277 |
+
}
|
278 |
+
|
279 |
+
|
280 |
+
/**
|
281 |
+
* @return array
|
282 |
+
*/
|
283 |
+
public function get_views() {
|
284 |
+
global $wpdb;
|
285 |
+
|
286 |
+
$status_links = array();
|
287 |
+
|
288 |
+
$states = get_post_stati( array( 'show_in_admin_all_list' => true ) );
|
289 |
+
$states = esc_sql( $states );
|
290 |
+
$all_states = "'" . implode( "', '", $states ) . "'";
|
291 |
+
|
292 |
+
$subquery = $this->get_base_subquery();
|
293 |
+
|
294 |
+
$total_posts = $wpdb->get_var(
|
295 |
+
"
|
296 |
+
SELECT COUNT(ID) FROM {$subquery}
|
297 |
+
WHERE post_status IN ({$all_states})
|
298 |
+
"
|
299 |
+
);
|
300 |
+
|
301 |
+
|
302 |
+
$post_status = filter_input( INPUT_GET, 'post_status' );
|
303 |
+
$class = empty( $post_status ) ? ' class="current"' : '';
|
304 |
+
$localized_text = sprintf(
|
305 |
+
/* translators: %s expands to the number of posts in localized format. */
|
306 |
+
_nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $total_posts, 'posts', 'wordpress-seo' ),
|
307 |
+
number_format_i18n( $total_posts )
|
308 |
+
);
|
309 |
+
|
310 |
+
$status_links['all'] = '<a href="' . esc_url( admin_url( 'admin.php?page=wpseo_tools&tool=bulk-editor' . $this->page_url ) ) . '"' . $class . '>' . $localized_text . '</a>';
|
311 |
+
|
312 |
+
$post_stati = get_post_stati( array( 'show_in_admin_all_list' => true ), 'objects' );
|
313 |
+
if ( is_array( $post_stati ) && $post_stati !== array() ) {
|
314 |
+
foreach ( $post_stati as $status ) {
|
315 |
+
|
316 |
+
$status_name = esc_sql( $status->name );
|
317 |
+
|
318 |
+
$total = (int) $wpdb->get_var(
|
319 |
+
$wpdb->prepare(
|
320 |
+
"
|
321 |
+
SELECT COUNT(ID) FROM {$subquery}
|
322 |
+
WHERE post_status = %s
|
323 |
+
",
|
324 |
+
$status_name
|
325 |
+
)
|
326 |
+
);
|
327 |
+
|
328 |
+
if ( $total === 0 ) {
|
329 |
+
continue;
|
330 |
+
}
|
331 |
+
|
332 |
+
$class = '';
|
333 |
+
if ( $status_name === $post_status ) {
|
334 |
+
$class = ' class="current"';
|
335 |
+
}
|
336 |
+
|
337 |
+
$status_links[ $status_name ] = '<a href="' . esc_url( add_query_arg( array( 'post_status' => $status_name ), admin_url( 'admin.php?page=wpseo_tools&tool=bulk-editor' . $this->page_url ) ) ) . '"' . $class . '>' . sprintf( translate_nooped_plural( $status->label_count, $total ), number_format_i18n( $total ) ) . '</a>';
|
338 |
+
}
|
339 |
+
}
|
340 |
+
unset( $post_stati, $status, $status_name, $total, $class );
|
341 |
+
|
342 |
+
$trashed_posts = $wpdb->get_var(
|
343 |
+
"
|
344 |
+
SELECT COUNT(ID) FROM {$subquery}
|
345 |
+
WHERE post_status IN ('trash')
|
346 |
+
"
|
347 |
+
);
|
348 |
+
|
349 |
+
$class = '';
|
350 |
+
if ( 'trash' === $post_status ) {
|
351 |
+
$class = 'class="current"';
|
352 |
+
}
|
353 |
+
|
354 |
+
$localized_text = sprintf(
|
355 |
+
/* translators: %s expands to the number of trashed posts in localized format. */
|
356 |
+
_nx( 'Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>', $trashed_posts, 'posts', 'wordpress-seo' ),
|
357 |
+
number_format_i18n( $trashed_posts )
|
358 |
+
);
|
359 |
+
|
360 |
+
$status_links['trash'] = '<a href="' . esc_url( admin_url( 'admin.php?page=wpseo_tools&tool=bulk-editor&post_status=trash' . $this->page_url ) ) . '"' . $class . '>' . $localized_text . '</a>';
|
361 |
+
|
362 |
+
return $status_links;
|
363 |
+
}
|
364 |
+
|
365 |
+
|
366 |
+
/**
|
367 |
+
* @param string $which Table nav location (such as top).
|
368 |
+
*/
|
369 |
+
public function extra_tablenav( $which ) {
|
370 |
+
|
371 |
+
if ( 'top' === $which ) {
|
372 |
+
$post_types = get_post_types(
|
373 |
+
array(
|
374 |
+
'public' => true,
|
375 |
+
'exclude_from_search' => false,
|
376 |
+
)
|
377 |
+
);
|
378 |
+
|
379 |
+
$instance_type = esc_attr( $this->page_type );
|
380 |
+
|
381 |
+
if ( is_array( $post_types ) && $post_types !== array() ) {
|
382 |
+
global $wpdb;
|
383 |
+
|
384 |
+
echo '<div class="alignleft actions">';
|
385 |
+
|
386 |
+
$post_types = esc_sql( $post_types );
|
387 |
+
$post_types = "'" . implode( "', '", $post_types ) . "'";
|
388 |
+
|
389 |
+
$states = get_post_stati( array( 'show_in_admin_all_list' => true ) );
|
390 |
+
$states['trash'] = 'trash';
|
391 |
+
$states = esc_sql( $states );
|
392 |
+
$all_states = "'" . implode( "', '", $states ) . "'";
|
393 |
+
|
394 |
+
$subquery = $this->get_base_subquery();
|
395 |
+
|
396 |
+
$post_types = $wpdb->get_results(
|
397 |
+
"
|
398 |
+
SELECT DISTINCT post_type FROM {$subquery}
|
399 |
+
WHERE post_status IN ({$all_states})
|
400 |
+
ORDER BY 'post_type' ASC
|
401 |
+
"
|
402 |
+
);
|
403 |
+
|
404 |
+
$post_type_filter = filter_input( INPUT_GET, 'post_type_filter' );
|
405 |
+
$selected = ( ! empty( $post_type_filter ) ) ? sanitize_text_field( $post_type_filter ) : '-1';
|
406 |
+
|
407 |
+
$options = '<option value="-1">' . esc_html__( 'Show All Post Types', 'wordpress-seo' ) . '</option>';
|
408 |
+
|
409 |
+
if ( is_array( $post_types ) && $post_types !== array() ) {
|
410 |
+
foreach ( $post_types as $post_type ) {
|
411 |
+
$obj = get_post_type_object( $post_type->post_type );
|
412 |
+
$options .= sprintf(
|
413 |
+
'<option value="%2$s" %3$s>%1$s</option>',
|
414 |
+
esc_html( $obj->labels->name ),
|
415 |
+
esc_attr( $post_type->post_type ),
|
416 |
+
selected( $selected, $post_type->post_type, false )
|
417 |
+
);
|
418 |
+
}
|
419 |
+
}
|
420 |
+
|
421 |
+
printf(
|
422 |
+
'<label for="%1$s" class="screen-reader-text">%2$s</label>',
|
423 |
+
esc_attr( 'post-type-filter-' . $instance_type ),
|
424 |
+
esc_html__( 'Filter by post type', 'wordpress-seo' )
|
425 |
+
);
|
426 |
+
printf(
|
427 |
+
'<select name="post_type_filter" id="%2$s">%1$s</select>',
|
428 |
+
$options,
|
429 |
+
esc_attr( 'post-type-filter-' . $instance_type )
|
430 |
+
);
|
431 |
+
|
432 |
+
submit_button( __( 'Filter', 'wordpress-seo' ), 'button', false, false, array( 'id' => 'post-query-submit' ) );
|
433 |
+
echo '</div>';
|
434 |
+
}
|
435 |
+
}
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
*
|
440 |
+
* @return array
|
441 |
+
*/
|
442 |
+
public function get_sortable_columns() {
|
443 |
+
return array(
|
444 |
+
'col_page_title' => array( 'post_title', true ),
|
445 |
+
'col_post_type' => array( 'post_type', false ),
|
446 |
+
'col_post_date' => array( 'post_date', false ),
|
447 |
+
);
|
448 |
+
}
|
449 |
+
|
450 |
+
/**
|
451 |
+
* Sets the correct pagenumber and pageurl for the navigation
|
452 |
+
*/
|
453 |
+
public function prepare_page_navigation() {
|
454 |
+
|
455 |
+
$request_url = $this->request_url . $this->page_url;
|
456 |
+
|
457 |
+
$current_page = $this->current_page;
|
458 |
+
$current_filter = $this->current_filter;
|
459 |
+
$current_status = $this->current_status;
|
460 |
+
$current_order = $this->current_order;
|
461 |
+
|
462 |
+
// If current type doesn't compare with objects page_type, than we have to unset some vars in the requested url (which will be use for internal table urls).
|
463 |
+
if ( $_GET['type'] !== $this->page_type ) {
|
464 |
+
$request_url = remove_query_arg( 'paged', $request_url ); // Page will be set with value 1 below.
|
465 |
+
$request_url = remove_query_arg( 'post_type_filter', $request_url );
|
466 |
+
$request_url = remove_query_arg( 'post_status', $request_url );
|
467 |
+
$request_url = remove_query_arg( 'orderby', $request_url );
|
468 |
+
$request_url = remove_query_arg( 'order', $request_url );
|
469 |
+
$request_url = add_query_arg( 'pages', 1, $request_url );
|
470 |
+
|
471 |
+
$current_page = 1;
|
472 |
+
$current_filter = '-1';
|
473 |
+
$current_status = '';
|
474 |
+
$current_order = array(
|
475 |
+
'orderby' => 'post_title',
|
476 |
+
'order' => 'asc',
|
477 |
+
);
|
478 |
+
}
|
479 |
+
|
480 |
+
$_SERVER['REQUEST_URI'] = $request_url;
|
481 |
+
|
482 |
+
$_GET['paged'] = $current_page;
|
483 |
+
$_REQUEST['paged'] = $current_page;
|
484 |
+
$_REQUEST['post_type_filter'] = $current_filter;
|
485 |
+
$_GET['post_type_filter'] = $current_filter;
|
486 |
+
$_GET['post_status'] = $current_status;
|
487 |
+
$_GET['orderby'] = $current_order['orderby'];
|
488 |
+
$_GET['order'] = $current_order['order'];
|
489 |
+
|
490 |
+
}
|
491 |
+
|
492 |
+
/**
|
493 |
+
* Preparing the requested pagerows and setting the needed variables
|
494 |
+
*/
|
495 |
+
public function prepare_items() {
|
496 |
+
|
497 |
+
$post_type_clause = $this->get_post_type_clause();
|
498 |
+
$all_states = $this->get_all_states();
|
499 |
+
$subquery = $this->get_base_subquery();
|
500 |
+
|
501 |
+
// Setting the column headers.
|
502 |
+
$this->set_column_headers();
|
503 |
+
|
504 |
+
// Count the total number of needed items and setting pagination given $total_items.
|
505 |
+
$total_items = $this->count_items( $subquery, $all_states, $post_type_clause );
|
506 |
+
$this->set_pagination( $total_items );
|
507 |
+
|
508 |
+
// Getting items given $query.
|
509 |
+
$query = $this->parse_item_query( $subquery, $all_states, $post_type_clause );
|
510 |
+
$this->get_items( $query );
|
511 |
+
|
512 |
+
// Get the metadata for the current items ($this->items).
|
513 |
+
$this->get_meta_data();
|
514 |
+
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* Getting the columns for first row
|
519 |
+
*
|
520 |
+
* @return array
|
521 |
+
*/
|
522 |
+
public function get_columns() {
|
523 |
+
return $this->merge_columns();
|
524 |
+
}
|
525 |
+
|
526 |
+
/**
|
527 |
+
* Setting the column headers
|
528 |
+
*/
|
529 |
+
protected function set_column_headers() {
|
530 |
+
$columns = $this->get_columns();
|
531 |
+
$hidden = array();
|
532 |
+
$sortable = $this->get_sortable_columns();
|
533 |
+
$this->_column_headers = array( $columns, $hidden, $sortable );
|
534 |
+
}
|
535 |
+
|
536 |
+
/**
|
537 |
+
* Counting total items
|
538 |
+
*
|
539 |
+
* @param string $subquery SQL FROM part.
|
540 |
+
* @param string $all_states SQL IN part.
|
541 |
+
* @param string $post_type_clause SQL post type part.
|
542 |
+
*
|
543 |
+
* @return mixed
|
544 |
+
*/
|
545 |
+
protected function count_items( $subquery, $all_states, $post_type_clause ) {
|
546 |
+
global $wpdb;
|
547 |
+
$total_items = $wpdb->get_var(
|
548 |
+
"
|
549 |
+
SELECT COUNT(ID)
|
550 |
+
FROM {$subquery}
|
551 |
+
WHERE post_status IN ({$all_states}) $post_type_clause
|
552 |
+
"
|
553 |
+
);
|
554 |
+
|
555 |
+
return $total_items;
|
556 |
+
}
|
557 |
+
|
558 |
+
/**
|
559 |
+
* Getting the post_type_clause filter
|
560 |
+
*
|
561 |
+
* @return string
|
562 |
+
*/
|
563 |
+
protected function get_post_type_clause() {
|
564 |
+
// Filter Block.
|
565 |
+
$post_types = null;
|
566 |
+
$post_type_clause = '';
|
567 |
+
$post_type_filter = filter_input( INPUT_GET, 'post_type_filter' );
|
568 |
+
|
569 |
+
if ( ! empty( $post_type_filter ) && get_post_type_object( sanitize_text_field( $post_type_filter ) ) ) {
|
570 |
+
$post_types = esc_sql( sanitize_text_field( $post_type_filter ) );
|
571 |
+
$post_type_clause = "AND post_type IN ('{$post_types}')";
|
572 |
+
}
|
573 |
+
|
574 |
+
return $post_type_clause;
|
575 |
+
}
|
576 |
+
|
577 |
+
/**
|
578 |
+
* Setting the pagination.
|
579 |
+
*
|
580 |
+
* Total items is the number of all visible items.
|
581 |
+
*
|
582 |
+
* @param int $total_items Total items counts.
|
583 |
+
*/
|
584 |
+
protected function set_pagination( $total_items ) {
|
585 |
+
|
586 |
+
// Calculate items per page.
|
587 |
+
$per_page = $this->get_items_per_page( 'wpseo_posts_per_page', 10 );
|
588 |
+
$paged = esc_sql( sanitize_text_field( filter_input( INPUT_GET, 'paged' ) ) );
|
589 |
+
|
590 |
+
if ( empty( $paged ) || ! is_numeric( $paged ) || $paged <= 0 ) {
|
591 |
+
$paged = 1;
|
592 |
+
}
|
593 |
+
|
594 |
+
$this->set_pagination_args(
|
595 |
+
array(
|
596 |
+
'total_items' => $total_items,
|
597 |
+
'total_pages' => ceil( $total_items / $per_page ),
|
598 |
+
'per_page' => $per_page,
|
599 |
+
)
|
600 |
+
);
|
601 |
+
|
602 |
+
$this->pagination = array(
|
603 |
+
'per_page' => $per_page,
|
604 |
+
'offset' => ( $paged - 1 ) * $per_page,
|
605 |
+
);
|
606 |
+
|
607 |
+
}
|
608 |
+
|
609 |
+
/**
|
610 |
+
* Parse the query to get items from database.
|
611 |
+
*
|
612 |
+
* Based on given parameters there will be parse a query which will get all the pages/posts and other post_types
|
613 |
+
* from the database.
|
614 |
+
*
|
615 |
+
* @param string $subquery SQL FROM part.
|
616 |
+
* @param string $all_states SQL IN part.
|
617 |
+
* @param string $post_type_clause SQL post type part.
|
618 |
+
*
|
619 |
+
* @return string
|
620 |
+
*/
|
621 |
+
protected function parse_item_query( $subquery, $all_states, $post_type_clause ) {
|
622 |
+
// Order By block.
|
623 |
+
$orderby = filter_input( INPUT_GET, 'orderby' );
|
624 |
+
|
625 |
+
$orderby = ! empty( $orderby ) ? esc_sql( sanitize_text_field( $orderby ) ) : 'post_title';
|
626 |
+
$orderby = $this->sanitize_orderby( $orderby );
|
627 |
+
|
628 |
+
// Order clause.
|
629 |
+
$order = filter_input( INPUT_GET, 'order' );
|
630 |
+
$order = ! empty( $order ) ? esc_sql( strtoupper( sanitize_text_field( $order ) ) ) : 'ASC';
|
631 |
+
$order = $this->sanitize_order( $order );
|
632 |
+
|
633 |
+
// Get all needed results.
|
634 |
+
$query = "
|
635 |
+
SELECT ID, post_title, post_type, post_status, post_modified, post_date
|
636 |
+
FROM {$subquery}
|
637 |
+
WHERE post_status IN ({$all_states}) $post_type_clause
|
638 |
+
ORDER BY {$orderby} {$order}
|
639 |
+
LIMIT %d,%d
|
640 |
+
";
|
641 |
+
|
642 |
+
return $query;
|
643 |
+
}
|
644 |
+
|
645 |
+
/**
|
646 |
+
* Heavily restricts the possible columns by which a user can order the table in the bulk editor, thereby preventing a possible CSRF vulnerability.
|
647 |
+
*
|
648 |
+
* @param string $orderby The column by which we want to order.
|
649 |
+
*
|
650 |
+
* @return string $orderby
|
651 |
+
*/
|
652 |
+
protected function sanitize_orderby( $orderby ) {
|
653 |
+
$valid_column_names = array(
|
654 |
+
'post_title',
|
655 |
+
'post_type',
|
656 |
+
'post_date',
|
657 |
+
);
|
658 |
+
|
659 |
+
if ( in_array( $orderby, $valid_column_names, true ) ) {
|
660 |
+
return $orderby;
|
661 |
+
}
|
662 |
+
|
663 |
+
return 'post_title';
|
664 |
+
}
|
665 |
+
|
666 |
+
/**
|
667 |
+
* Makes sure the order clause is always ASC or DESC for the bulk editor table, thereby preventing a possible CSRF vulnerability.
|
668 |
+
*
|
669 |
+
* @param string $order Whether we want to sort ascending or descending.
|
670 |
+
*
|
671 |
+
* @return string $order SQL order string (ASC, DESC).
|
672 |
+
*/
|
673 |
+
protected function sanitize_order( $order ) {
|
674 |
+
if ( in_array( strtoupper( $order ), array( 'ASC', 'DESC' ), true ) ) {
|
675 |
+
return $order;
|
676 |
+
}
|
677 |
+
|
678 |
+
return 'ASC';
|
679 |
+
}
|
680 |
+
|
681 |
+
/**
|
682 |
+
* Getting all the items.
|
683 |
+
*
|
684 |
+
* @param string $query SQL query to use.
|
685 |
+
*/
|
686 |
+
protected function get_items( $query ) {
|
687 |
+
global $wpdb;
|
688 |
+
|
689 |
+
$this->items = $wpdb->get_results(
|
690 |
+
$wpdb->prepare(
|
691 |
+
$query,
|
692 |
+
$this->pagination['offset'],
|
693 |
+
$this->pagination['per_page']
|
694 |
+
)
|
695 |
+
);
|
696 |
+
}
|
697 |
+
|
698 |
+
/**
|
699 |
+
* Getting all the states.
|
700 |
+
*
|
701 |
+
* @return string
|
702 |
+
*/
|
703 |
+
protected function get_all_states() {
|
704 |
+
$states = get_post_stati( array( 'show_in_admin_all_list' => true ) );
|
705 |
+
$states['trash'] = 'trash';
|
706 |
+
|
707 |
+
if ( ! empty( $_GET['post_status'] ) ) {
|
708 |
+
$requested_state = sanitize_text_field( $_GET['post_status'] );
|
709 |
+
if ( in_array( $requested_state, $states, true ) ) {
|
710 |
+
$states = array( $requested_state );
|
711 |
+
}
|
712 |
+
|
713 |
+
if ( $requested_state !== 'trash' ) {
|
714 |
+
unset( $states['trash'] );
|
715 |
+
}
|
716 |
+
}
|
717 |
+
|
718 |
+
$states = esc_sql( $states );
|
719 |
+
$all_states = "'" . implode( "', '", $states ) . "'";
|
720 |
+
|
721 |
+
return $all_states;
|
722 |
+
}
|
723 |
+
|
724 |
+
/**
|
725 |
+
* Based on $this->items and the defined columns, the table rows will be displayed.
|
726 |
+
*/
|
727 |
+
public function display_rows() {
|
728 |
+
|
729 |
+
$records = $this->items;
|
730 |
+
|
731 |
+
list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
|
732 |
+
|
733 |
+
if ( ( is_array( $records ) && $records !== array() ) && ( is_array( $columns ) && $columns !== array() ) ) {
|
734 |
+
|
735 |
+
foreach ( $records as $rec ) {
|
736 |
+
|
737 |
+
echo '<tr id="', esc_attr( 'record_' . $rec->ID ), '">';
|
738 |
+
|
739 |
+
foreach ( $columns as $column_name => $column_display_name ) {
|
740 |
+
|
741 |
+
$classes = '';
|
742 |
+
if ( $primary === $column_name ) {
|
743 |
+
$classes .= ' has-row-actions column-primary';
|
744 |
+
}
|
745 |
+
|
746 |
+
$attributes = $this->column_attributes( $column_name, $hidden, $classes, $column_display_name );
|
747 |
+
|
748 |
+
$column_value = $this->parse_column( $column_name, $rec );
|
749 |
+
|
750 |
+
if ( method_exists( $this, 'parse_page_specific_column' ) && empty( $column_value ) ) {
|
751 |
+
$column_value = $this->parse_page_specific_column( $column_name, $rec, $attributes );
|
752 |
+
}
|
753 |
+
|
754 |
+
if ( ! empty( $column_value ) ) {
|
755 |
+
printf( '<td %2$s>%1$s</td>', $column_value, $attributes );
|
756 |
+
}
|
757 |
+
}
|
758 |
+
|
759 |
+
echo '</tr>';
|
760 |
+
}
|
761 |
+
}
|
762 |
+
}
|
763 |
+
|
764 |
+
/**
|
765 |
+
* Getting the attributes for each table cell.
|
766 |
+
*
|
767 |
+
* @param string $column_name Column name string.
|
768 |
+
* @param array $hidden Set of hidden columns.
|
769 |
+
* @param string $classes Additional CSS classes.
|
770 |
+
* @param string $column_display_name Column display name string.
|
771 |
+
*
|
772 |
+
* @return string
|
773 |
+
*/
|
774 |
+
protected function column_attributes( $column_name, $hidden, $classes, $column_display_name ) {
|
775 |
+
|
776 |
+
$attributes = '';
|
777 |
+
$class = array( $column_name, "column-$column_name$classes" );
|
778 |
+
|
779 |
+
if ( in_array( $column_name, $hidden, true ) ) {
|
780 |
+
$class[] = 'hidden';
|
781 |
+
}
|
782 |
+
|
783 |
+
if ( ! empty( $class ) ) {
|
784 |
+
$attributes = 'class="' . implode( ' ', $class ) . '"';
|
785 |
+
}
|
786 |
+
|
787 |
+
$attributes .= ' data-colname="' . esc_attr( $column_display_name ) . '"';
|
788 |
+
|
789 |
+
return $attributes;
|
790 |
+
}
|
791 |
+
|
792 |
+
/**
|
793 |
+
* Parsing the title.
|
794 |
+
*
|
795 |
+
* @param WP_Post $rec Post object.
|
796 |
+
*
|
797 |
+
* @return string
|
798 |
+
*/
|
799 |
+
protected function parse_page_title_column( $rec ) {
|
800 |
+
|
801 |
+
$title = empty( $rec->post_title ) ? __( '(no title)', 'wordpress-seo' ) : $rec->post_title;
|
802 |
+
|
803 |
+
$return = sprintf( '<strong>%1$s</strong>', stripslashes( wp_strip_all_tags( $title ) ) );
|
804 |
+
|
805 |
+
$post_type_object = get_post_type_object( $rec->post_type );
|
806 |
+
$can_edit_post = current_user_can( $post_type_object->cap->edit_post, $rec->ID );
|
807 |
+
|
808 |
+
$actions = array();
|
809 |
+
|
810 |
+
if ( $can_edit_post && 'trash' !== $rec->post_status ) {
|
811 |
+
$actions['edit'] = sprintf(
|
812 |
+
'<a href="%s" aria-label="%s">%s</a>',
|
813 |
+
esc_url( get_edit_post_link( $rec->ID, true ) ),
|
814 |
+
/* translators: %s: post title */
|
815 |
+
esc_attr( sprintf( __( 'Edit “%s”', 'wordpress-seo' ), $title ) ),
|
816 |
+
__( 'Edit', 'wordpress-seo' )
|
817 |
+
);
|
818 |
+
}
|
819 |
+
|
820 |
+
if ( $post_type_object->public ) {
|
821 |
+
if ( in_array( $rec->post_status, array( 'pending', 'draft', 'future' ), true ) ) {
|
822 |
+
if ( $can_edit_post ) {
|
823 |
+
$actions['view'] = sprintf(
|
824 |
+
'<a href="%s" aria-label="%s">%s</a>',
|
825 |
+
esc_url( add_query_arg( 'preview', 'true', get_permalink( $rec->ID ) ) ),
|
826 |
+
/* translators: %s: post title */
|
827 |
+
esc_attr( sprintf( __( 'Preview “%s”', 'wordpress-seo' ), $title ) ),
|
828 |
+
__( 'Preview', 'wordpress-seo' )
|
829 |
+
);
|
830 |
+
}
|
831 |
+
}
|
832 |
+
elseif ( 'trash' !== $rec->post_status ) {
|
833 |
+
$actions['view'] = sprintf(
|
834 |
+
'<a href="%s" aria-label="%s" rel="bookmark">%s</a>',
|
835 |
+
esc_url( get_permalink( $rec->ID ) ),
|
836 |
+
/* translators: %s: post title */
|
837 |
+
esc_attr( sprintf( __( 'View “%s”', 'wordpress-seo' ), $title ) ),
|
838 |
+
__( 'View', 'wordpress-seo' )
|
839 |
+
);
|
840 |
+
}
|
841 |
+
}
|
842 |
+
|
843 |
+
$return .= $this->row_actions( $actions );
|
844 |
+
|
845 |
+
return $return;
|
846 |
+
|
847 |
+
}
|
848 |
+
|
849 |
+
/**
|
850 |
+
* Parsing the column based on the $column_name.
|
851 |
+
*
|
852 |
+
* @param string $column_name Column name.
|
853 |
+
* @param WP_Post $rec Post object.
|
854 |
+
*
|
855 |
+
* @return string
|
856 |
+
*/
|
857 |
+
protected function parse_column( $column_name, $rec ) {
|
858 |
+
|
859 |
+
static $date_format;
|
860 |
+
|
861 |
+
if ( ! isset( $date_format ) ) {
|
862 |
+
$date_format = get_option( 'date_format' );
|
863 |
+
}
|
864 |
+
|
865 |
+
switch ( $column_name ) {
|
866 |
+
case 'col_page_title':
|
867 |
+
$column_value = $this->parse_page_title_column( $rec );
|
868 |
+
break;
|
869 |
+
|
870 |
+
case 'col_page_slug':
|
871 |
+
$permalink = get_permalink( $rec->ID );
|
872 |
+
$display_slug = str_replace( get_bloginfo( 'url' ), '', $permalink );
|
873 |
+
$column_value = sprintf( '<a href="%2$s" target="_blank">%1$s</a>', stripslashes( rawurldecode( $display_slug ) ), esc_url( $permalink ) );
|
874 |
+
break;
|
875 |
+
|
876 |
+
case 'col_post_type':
|
877 |
+
$post_type = get_post_type_object( $rec->post_type );
|
878 |
+
$column_value = $post_type->labels->singular_name;
|
879 |
+
break;
|
880 |
+
|
881 |
+
case 'col_post_status':
|
882 |
+
$post_status = get_post_status_object( $rec->post_status );
|
883 |
+
$column_value = $post_status->label;
|
884 |
+
break;
|
885 |
+
|
886 |
+
case 'col_post_date':
|
887 |
+
$column_value = date_i18n( $date_format, strtotime( $rec->post_date ) );
|
888 |
+
break;
|
889 |
+
|
890 |
+
case 'col_row_action':
|
891 |
+
$column_value = sprintf(
|
892 |
+
'<a href="#" role="button" class="wpseo-save" data-id="%1$s">%2$s</a> <span aria-hidden="true">|</span> <a href="#" role="button" class="wpseo-save-all">%3$s</a>',
|
893 |
+
$rec->ID,
|
894 |
+
esc_html__( 'Save', 'wordpress-seo' ),
|
895 |
+
esc_html__( 'Save all', 'wordpress-seo' )
|
896 |
+
);
|
897 |
+
break;
|
898 |
+
}
|
899 |
+
|
900 |
+
if ( ! empty( $column_value ) ) {
|
901 |
+
return $column_value;
|
902 |
+
}
|
903 |
+
}
|
904 |
+
|
905 |
+
/**
|
906 |
+
* Parse the field where the existing meta-data value is displayed.
|
907 |
+
*
|
908 |
+
* @param integer $record_id Record ID.
|
909 |
+
* @param string $attributes HTML attributes.
|
910 |
+
* @param bool|array $values Optional values data array.
|
911 |
+
*
|
912 |
+
* @return string
|
913 |
+
*/
|
914 |
+
protected function parse_meta_data_field( $record_id, $attributes, $values = false ) {
|
915 |
+
|
916 |
+
// Fill meta data if exists in $this->meta_data.
|
917 |
+
$meta_data = ( ! empty( $this->meta_data[ $record_id ] ) ) ? $this->meta_data[ $record_id ] : array();
|
918 |
+
$meta_key = WPSEO_Meta::$meta_prefix . $this->target_db_field;
|
919 |
+
$meta_value = ( ! empty( $meta_data[ $meta_key ] ) ) ? $meta_data[ $meta_key ] : '';
|
920 |
+
|
921 |
+
if ( ! empty( $values ) ) {
|
922 |
+
$meta_value = $values[ $meta_value ];
|
923 |
+
}
|
924 |
+
|
925 |
+
return sprintf( '<td %2$s id="wpseo-existing-%4$s-%3$s">%1$s</td>', $meta_value, $attributes, $record_id, $this->target_db_field );
|
926 |
+
}
|
927 |
+
|
928 |
+
/**
|
929 |
+
* Method for setting the meta data, which belongs to the records that will be shown on the current page.
|
930 |
+
*
|
931 |
+
* This method will loop through the current items ($this->items) for getting the post_id. With this data
|
932 |
+
* ($needed_ids) the method will query the meta-data table for getting the title.
|
933 |
+
*/
|
934 |
+
protected function get_meta_data() {
|
935 |
+
|
936 |
+
$post_ids = $this->get_post_ids();
|
937 |
+
$meta_data = $this->get_meta_data_result( $post_ids );
|
938 |
+
|
939 |
+
$this->parse_meta_data( $meta_data );
|
940 |
+
|
941 |
+
// Little housekeeping.
|
942 |
+
unset( $post_ids, $meta_data );
|
943 |
+
|
944 |
+
}
|
945 |
+
|
946 |
+
/**
|
947 |
+
* Getting all post_ids from to $this->items.
|
948 |
+
*
|
949 |
+
* @return string
|
950 |
+
*/
|
951 |
+
protected function get_post_ids() {
|
952 |
+
$needed_ids = array();
|
953 |
+
foreach ( $this->items as $item ) {
|
954 |
+
$needed_ids[] = $item->ID;
|
955 |
+
}
|
956 |
+
|
957 |
+
$post_ids = "'" . implode( "', '", $needed_ids ) . "'";
|
958 |
+
|
959 |
+
return $post_ids;
|
960 |
+
}
|
961 |
+
|
962 |
+
/**
|
963 |
+
* Getting the meta_data from database.
|
964 |
+
*
|
965 |
+
* @param string $post_ids Post IDs string for SQL IN part.
|
966 |
+
*
|
967 |
+
* @return mixed
|
968 |
+
*/
|
969 |
+
protected function get_meta_data_result( $post_ids ) {
|
970 |
+
global $wpdb;
|
971 |
+
|
972 |
+
$meta_data = $wpdb->get_results(
|
973 |
+
"
|
974 |
+
SELECT *
|
975 |
+
FROM {$wpdb->postmeta}
|
976 |
+
WHERE post_id IN({$post_ids}) && meta_key = '" . WPSEO_Meta::$meta_prefix . $this->target_db_field . "'
|
977 |
+
"
|
978 |
+
);
|
979 |
+
|
980 |
+
return $meta_data;
|
981 |
+
}
|
982 |
+
|
983 |
+
/**
|
984 |
+
* Setting $this->meta_data.
|
985 |
+
*
|
986 |
+
* @param array $meta_data Meta data set.
|
987 |
+
*/
|
988 |
+
protected function parse_meta_data( $meta_data ) {
|
989 |
+
|
990 |
+
foreach ( $meta_data as $row ) {
|
991 |
+
$this->meta_data[ $row->post_id ][ $row->meta_key ] = $row->meta_value;
|
992 |
+
}
|
993 |
+
|
994 |
+
}
|
995 |
+
|
996 |
+
/**
|
997 |
+
* This method will merge general array with given parameter $columns.
|
998 |
+
*
|
999 |
+
* @param array $columns Optional columns set.
|
1000 |
+
*
|
1001 |
+
* @return array
|
1002 |
+
*/
|
1003 |
+
protected function merge_columns( $columns = array() ) {
|
1004 |
+
$columns = array_merge(
|
1005 |
+
array(
|
1006 |
+
'col_page_title' => __( 'WP Page Title', 'wordpress-seo' ),
|
1007 |
+
'col_post_type' => __( 'Post Type', 'wordpress-seo' ),
|
1008 |
+
'col_post_status' => __( 'Post Status', 'wordpress-seo' ),
|
1009 |
+
'col_post_date' => __( 'Publication date', 'wordpress-seo' ),
|
1010 |
+
'col_page_slug' => __( 'Page URL/Slug', 'wordpress-seo' ),
|
1011 |
+
),
|
1012 |
+
$columns
|
1013 |
+
);
|
1014 |
+
|
1015 |
+
$columns['col_row_action'] = __( 'Action', 'wordpress-seo' );
|
1016 |
+
|
1017 |
+
return $columns;
|
1018 |
+
}
|
1019 |
+
} /* End of class */
|
admin/class-bulk-title-editor-list-table.php
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Bulk Editor
|
4 |
+
* @since 1.5.0
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Implements table for bulk title editing.
|
9 |
+
*/
|
10 |
+
class WPSEO_Bulk_Title_Editor_List_Table extends WPSEO_Bulk_List_Table {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Current type for this class will be title
|
14 |
+
*
|
15 |
+
* @var string
|
16 |
+
*/
|
17 |
+
protected $page_type = 'title';
|
18 |
+
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Settings with are used in __construct
|
22 |
+
*
|
23 |
+
* @var array
|
24 |
+
*/
|
25 |
+
protected $settings = array(
|
26 |
+
'singular' => 'wpseo_bulk_title',
|
27 |
+
'plural' => 'wpseo_bulk_titles',
|
28 |
+
'ajax' => true,
|
29 |
+
);
|
30 |
+
|
31 |
+
/**
|
32 |
+
* The field in the database where meta field is saved.
|
33 |
+
*
|
34 |
+
* @var string
|
35 |
+
*/
|
36 |
+
protected $target_db_field = 'title';
|
37 |
+
|
38 |
+
/**
|
39 |
+
* The columns shown on the table
|
40 |
+
*
|
41 |
+
* @return array
|
42 |
+
*/
|
43 |
+
public function get_columns() {
|
44 |
+
|
45 |
+
$columns = array(
|
46 |
+
/* translators: %1$s expands to Yoast SEO */
|
47 |
+
'col_existing_yoast_seo_title' => sprintf( __( 'Existing %1$s Title', 'wordpress-seo' ), 'Yoast SEO' ),
|
48 |
+
/* translators: %1$s expands to Yoast SEO */
|
49 |
+
'col_new_yoast_seo_title' => sprintf( __( 'New %1$s Title', 'wordpress-seo' ), 'Yoast SEO' ),
|
50 |
+
);
|
51 |
+
|
52 |
+
return $this->merge_columns( $columns );
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Parse the title columns
|
57 |
+
*
|
58 |
+
* @param string $column_name Column name.
|
59 |
+
* @param object $record Data object.
|
60 |
+
* @param string $attributes HTML attributes.
|
61 |
+
*
|
62 |
+
* @return string
|
63 |
+
*/
|
64 |
+
protected function parse_page_specific_column( $column_name, $record, $attributes ) {
|
65 |
+
|
66 |
+
// Fill meta data if exists in $this->meta_data.
|
67 |
+
$meta_data = ( ! empty( $this->meta_data[ $record->ID ] ) ) ? $this->meta_data[ $record->ID ] : array();
|
68 |
+
|
69 |
+
switch ( $column_name ) {
|
70 |
+
case 'col_existing_yoast_seo_title':
|
71 |
+
// @todo Inconsistent echo/return behavior R.
|
72 |
+
echo $this->parse_meta_data_field( $record->ID, $attributes );
|
73 |
+
break;
|
74 |
+
|
75 |
+
case 'col_new_yoast_seo_title':
|
76 |
+
return sprintf(
|
77 |
+
'<input type="text" id="%1$s" name="%1$s" class="wpseo-new-title" data-id="%2$s" aria-labelledby="col_new_yoast_seo_title" />',
|
78 |
+
'wpseo-new-title-' . $record->ID,
|
79 |
+
$record->ID
|
80 |
+
);
|
81 |
+
}
|
82 |
+
|
83 |
+
unset( $meta_data );
|
84 |
+
}
|
85 |
+
} /* End of class */
|
admin/class-collector.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Collects the data from the added collection objects.
|
8 |
+
*/
|
9 |
+
class WPSEO_Collector {
|
10 |
+
|
11 |
+
/** @var WPSEO_Collection[] */
|
12 |
+
protected $collections = array();
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Adds a collection object to the collections.
|
16 |
+
*
|
17 |
+
* @param WPSEO_Collection $collection The collection object to add.
|
18 |
+
*/
|
19 |
+
public function add_collection( WPSEO_Collection $collection ) {
|
20 |
+
$this->collections[] = $collection;
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Collects the data from the collection objects.
|
25 |
+
*
|
26 |
+
* @return array The collected data.
|
27 |
+
*/
|
28 |
+
public function collect() {
|
29 |
+
$data = array();
|
30 |
+
|
31 |
+
foreach ( $this->collections as $collection ) {
|
32 |
+
$data = array_merge( $data, $collection->get() );
|
33 |
+
}
|
34 |
+
|
35 |
+
return $data;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Returns the collected data as a JSON encoded string.
|
40 |
+
*
|
41 |
+
* @return false|string The encode string.
|
42 |
+
*/
|
43 |
+
public function get_as_json() {
|
44 |
+
return wp_json_encode( $this->collect() );
|
45 |
+
}
|
46 |
+
}
|
admin/class-config.php
ADDED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Admin_Pages
|
8 |
+
*
|
9 |
+
* Class with functionality for the Yoast SEO admin pages.
|
10 |
+
*/
|
11 |
+
class WPSEO_Admin_Pages {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var string $currentoption The option in use for the current admin page.
|
15 |
+
*/
|
16 |
+
public $currentoption = 'wpseo';
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Holds the asset manager.
|
20 |
+
*
|
21 |
+
* @var WPSEO_Admin_Asset_Manager
|
22 |
+
*/
|
23 |
+
private $asset_manager;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Class constructor, which basically only hooks the init function on the init hook
|
27 |
+
*/
|
28 |
+
public function __construct() {
|
29 |
+
add_action( 'init', array( $this, 'init' ), 20 );
|
30 |
+
$this->asset_manager = new WPSEO_Admin_Asset_Manager();
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Make sure the needed scripts are loaded for admin pages
|
35 |
+
*/
|
36 |
+
public function init() {
|
37 |
+
if ( filter_input( INPUT_GET, 'wpseo_reset_defaults' ) && wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), 'wpseo_reset_defaults' ) && current_user_can( 'manage_options' ) ) {
|
38 |
+
WPSEO_Options::reset();
|
39 |
+
wp_redirect( admin_url( 'admin.php?page=' . WPSEO_Configuration_Page::PAGE_IDENTIFIER ) );
|
40 |
+
}
|
41 |
+
|
42 |
+
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
43 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'config_page_scripts' ) );
|
44 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'config_page_styles' ) );
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Run admin-specific actions.
|
49 |
+
*/
|
50 |
+
public function admin_init() {
|
51 |
+
|
52 |
+
$page = filter_input( INPUT_GET, 'page' );
|
53 |
+
$tool = filter_input( INPUT_GET, 'tool' );
|
54 |
+
$export_nonce = filter_input( INPUT_POST, WPSEO_Export::NONCE_NAME );
|
55 |
+
|
56 |
+
if ( 'wpseo_tools' === $page && 'import-export' === $tool && $export_nonce !== null ) {
|
57 |
+
$this->do_yoast_export();
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Loads the required styles for the config page.
|
63 |
+
*/
|
64 |
+
public function config_page_styles() {
|
65 |
+
wp_enqueue_style( 'dashboard' );
|
66 |
+
wp_enqueue_style( 'thickbox' );
|
67 |
+
wp_enqueue_style( 'global' );
|
68 |
+
wp_enqueue_style( 'wp-admin' );
|
69 |
+
$this->asset_manager->enqueue_style( 'select2' );
|
70 |
+
|
71 |
+
$this->asset_manager->enqueue_style( 'admin-css' );
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Loads the required scripts for the config page.
|
76 |
+
*/
|
77 |
+
public function config_page_scripts() {
|
78 |
+
$this->asset_manager->enqueue_script( 'admin-script' );
|
79 |
+
$this->asset_manager->enqueue_script( 'help-center' );
|
80 |
+
|
81 |
+
wp_enqueue_script( 'dashboard' );
|
82 |
+
wp_enqueue_script( 'thickbox' );
|
83 |
+
|
84 |
+
$page = filter_input( INPUT_GET, 'page' );
|
85 |
+
|
86 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-script', 'wpseoSelect2Locale', WPSEO_Utils::get_language( WPSEO_Utils::get_user_locale() ) );
|
87 |
+
|
88 |
+
if ( in_array( $page, array( 'wpseo_social', WPSEO_Admin::PAGE_IDENTIFIER, 'wpseo_titles' ), true ) ) {
|
89 |
+
wp_enqueue_media();
|
90 |
+
|
91 |
+
$this->asset_manager->enqueue_script( 'admin-media' );
|
92 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-media', 'wpseoMediaL10n', $this->localize_media_script() );
|
93 |
+
}
|
94 |
+
|
95 |
+
if ( 'wpseo_tools' === $page ) {
|
96 |
+
$this->enqueue_tools_scripts();
|
97 |
+
}
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Pass some variables to js for upload module.
|
102 |
+
*
|
103 |
+
* @return array
|
104 |
+
*/
|
105 |
+
public function localize_media_script() {
|
106 |
+
return array(
|
107 |
+
'choose_image' => __( 'Use Image', 'wordpress-seo' ),
|
108 |
+
);
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Enqueues and handles all the tool dependencies.
|
113 |
+
*/
|
114 |
+
private function enqueue_tools_scripts() {
|
115 |
+
$tool = filter_input( INPUT_GET, 'tool' );
|
116 |
+
|
117 |
+
if ( empty( $tool ) ) {
|
118 |
+
$this->asset_manager->enqueue_script( 'yoast-seo' );
|
119 |
+
}
|
120 |
+
|
121 |
+
if ( 'bulk-editor' === $tool ) {
|
122 |
+
$this->asset_manager->enqueue_script( 'bulk-editor' );
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Runs the yoast exporter class to possibly init the file download.
|
128 |
+
*/
|
129 |
+
private function do_yoast_export() {
|
130 |
+
check_admin_referer( WPSEO_Export::NONCE_ACTION, WPSEO_Export::NONCE_NAME );
|
131 |
+
|
132 |
+
if ( ! WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) {
|
133 |
+
return;
|
134 |
+
}
|
135 |
+
|
136 |
+
$wpseo_post = filter_input( INPUT_POST, 'wpseo' );
|
137 |
+
$include_taxonomy = ! empty( $wpseo_post['include_taxonomy'] );
|
138 |
+
$export = new WPSEO_Export( $include_taxonomy );
|
139 |
+
|
140 |
+
if ( $export->has_error() ) {
|
141 |
+
add_action( 'admin_notices', array( $export, 'set_error_hook' ) );
|
142 |
+
|
143 |
+
}
|
144 |
+
}
|
145 |
+
} /* End of class */
|
admin/class-cornerstone-field.php
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Adds a checkbox to the focus keyword section.
|
8 |
+
*/
|
9 |
+
class WPSEO_Cornerstone_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns a label with a checkbox in it. Make it possible to mark the page as cornerstone content.
|
13 |
+
*
|
14 |
+
* @param WP_POST $post The post object.
|
15 |
+
*
|
16 |
+
* @return string The HTML to show.
|
17 |
+
*/
|
18 |
+
public function get_html( $post ) {
|
19 |
+
|
20 |
+
$post_types = apply_filters( 'wpseo_cornerstone_post_types', WPSEO_Post_Type::get_accessible_post_types() );
|
21 |
+
if ( ! is_array( $post_types ) || ! isset( $post_types[ get_post_type( $post ) ] ) ) {
|
22 |
+
return '';
|
23 |
+
}
|
24 |
+
|
25 |
+
$return = '';
|
26 |
+
$return .= sprintf(
|
27 |
+
'<input id="%1$s" class="wpseo-cornerstone-checkbox" type="checkbox" value="1" name="%1$s" %2$s/>',
|
28 |
+
WPSEO_Cornerstone::META_NAME,
|
29 |
+
checked( $this->get_meta_value( $post->ID ), '1', false )
|
30 |
+
);
|
31 |
+
|
32 |
+
$return .= sprintf( '<label for="%1$s">', WPSEO_Cornerstone::META_NAME );
|
33 |
+
|
34 |
+
$return .= sprintf(
|
35 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
36 |
+
__( 'This article is %1$scornerstone content%2$s', 'wordpress-seo' ),
|
37 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/metabox-help-cornerstone' ) . '" target="_blank">',
|
38 |
+
'</a>'
|
39 |
+
);
|
40 |
+
$return .= '</label>';
|
41 |
+
|
42 |
+
return $return;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Gets the meta value from the database.
|
47 |
+
*
|
48 |
+
* @param int $post_id The post id to get the meta value for.
|
49 |
+
*
|
50 |
+
* @return null|string The meta value from the database.
|
51 |
+
*/
|
52 |
+
protected function get_meta_value( $post_id ) {
|
53 |
+
return get_post_meta( $post_id, WPSEO_Cornerstone::META_NAME, true );
|
54 |
+
}
|
55 |
+
}
|
admin/class-cornerstone.php
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the yoast cornerstone content.
|
8 |
+
*/
|
9 |
+
class WPSEO_Cornerstone {
|
10 |
+
|
11 |
+
const META_NAME = '_yst_is_cornerstone';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Registers the hooks.
|
15 |
+
*
|
16 |
+
* @return void
|
17 |
+
*/
|
18 |
+
public function register_hooks() {
|
19 |
+
global $pagenow;
|
20 |
+
|
21 |
+
if ( ! $this->page_contains_cornerstone_content_field( $pagenow ) ) {
|
22 |
+
return;
|
23 |
+
}
|
24 |
+
|
25 |
+
add_action( 'save_post', array( $this, 'save_meta_value' ) );
|
26 |
+
add_filter( 'wpseo_cornerstone_post_types', array( 'WPSEO_Post_Type', 'filter_attachment_post_type' ) );
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Saves the meta value to the database.
|
31 |
+
*
|
32 |
+
* @param int $post_id The post id to save the meta value for.
|
33 |
+
*
|
34 |
+
* @return void
|
35 |
+
*/
|
36 |
+
public function save_meta_value( $post_id ) {
|
37 |
+
$is_cornerstone_content = $this->is_cornerstone_content();
|
38 |
+
|
39 |
+
if ( $is_cornerstone_content ) {
|
40 |
+
$this->update_meta( $post_id, $is_cornerstone_content );
|
41 |
+
|
42 |
+
return;
|
43 |
+
}
|
44 |
+
|
45 |
+
$this->delete_meta( $post_id );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Returns the result of the cornerstone content checkbox.
|
50 |
+
*
|
51 |
+
* @return bool True when checkbox is checked.
|
52 |
+
*/
|
53 |
+
protected function is_cornerstone_content() {
|
54 |
+
return filter_input( INPUT_POST, self::META_NAME ) === '1';
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Checks if the current page matches one of the pages that contains the cornerstone content field.
|
59 |
+
*
|
60 |
+
* @param string $page The page to check.
|
61 |
+
*
|
62 |
+
* @return bool True when the page contains the cornerstone content field.
|
63 |
+
*/
|
64 |
+
protected function page_contains_cornerstone_content_field( $page ) {
|
65 |
+
return WPSEO_Metabox::is_post_edit( $page );
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Updates the cornerstone content post meta with the given cornerstone content value.
|
70 |
+
*
|
71 |
+
* @param int $post_id The post id to save the meta value for.
|
72 |
+
* @param bool $is_cornerstone_content Whether or not the post should be considered to be cornerstone content.
|
73 |
+
*
|
74 |
+
* @return void
|
75 |
+
*/
|
76 |
+
protected function update_meta( $post_id, $is_cornerstone_content ) {
|
77 |
+
update_post_meta( $post_id, self::META_NAME, $is_cornerstone_content );
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Deletes the cornerstone content post meta for the given post id.
|
82 |
+
*
|
83 |
+
* @param int $post_id The post id to delete the cornerstone content meta value for..
|
84 |
+
*
|
85 |
+
* @return void
|
86 |
+
*/
|
87 |
+
protected function delete_meta( $post_id ) {
|
88 |
+
delete_post_meta( $post_id, self::META_NAME );
|
89 |
+
}
|
90 |
+
}
|
admin/class-customizer.php
ADDED
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Customizer
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class with functionality to support WP SEO settings in WordPress Customizer.
|
8 |
+
*/
|
9 |
+
class WPSEO_Customizer {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WP_Customize_Manager
|
13 |
+
*/
|
14 |
+
protected $wp_customize;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Construct Method.
|
18 |
+
*/
|
19 |
+
public function __construct() {
|
20 |
+
add_action( 'customize_register', array( $this, 'wpseo_customize_register' ) );
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Function to support WordPress Customizer
|
25 |
+
*
|
26 |
+
* @param WP_Customize_Manager $wp_customize Manager class instance.
|
27 |
+
*/
|
28 |
+
public function wpseo_customize_register( $wp_customize ) {
|
29 |
+
if ( ! WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) {
|
30 |
+
return;
|
31 |
+
}
|
32 |
+
|
33 |
+
$this->wp_customize = $wp_customize;
|
34 |
+
|
35 |
+
$this->breadcrumbs_section();
|
36 |
+
$this->breadcrumbs_blog_remove_setting();
|
37 |
+
$this->breadcrumbs_separator_setting();
|
38 |
+
$this->breadcrumbs_home_setting();
|
39 |
+
$this->breadcrumbs_prefix_setting();
|
40 |
+
$this->breadcrumbs_archiveprefix_setting();
|
41 |
+
$this->breadcrumbs_searchprefix_setting();
|
42 |
+
$this->breadcrumbs_404_setting();
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Add the breadcrumbs section to the customizer
|
47 |
+
*/
|
48 |
+
private function breadcrumbs_section() {
|
49 |
+
$this->wp_customize->add_section(
|
50 |
+
'wpseo_breadcrumbs_customizer_section', array(
|
51 |
+
/* translators: %s is the name of the plugin */
|
52 |
+
'title' => sprintf( __( '%s Breadcrumbs', 'wordpress-seo' ), 'Yoast SEO' ),
|
53 |
+
'priority' => 999,
|
54 |
+
'active_callback' => array( $this, 'breadcrumbs_active_callback' ),
|
55 |
+
)
|
56 |
+
);
|
57 |
+
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Returns whether or not the breadcrumbs are active
|
62 |
+
*
|
63 |
+
* @return bool
|
64 |
+
*/
|
65 |
+
public function breadcrumbs_active_callback() {
|
66 |
+
return true === ( current_theme_supports( 'yoast-seo-breadcrumbs' ) || WPSEO_Options::get( 'breadcrumbs-enable' ) );
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Adds the breadcrumbs remove blog checkbox
|
71 |
+
*/
|
72 |
+
private function breadcrumbs_blog_remove_setting() {
|
73 |
+
$this->wp_customize->add_setting(
|
74 |
+
'wpseo_titles[breadcrumbs-blog-remove]', array(
|
75 |
+
'default' => '',
|
76 |
+
'type' => 'option',
|
77 |
+
'transport' => 'refresh',
|
78 |
+
)
|
79 |
+
);
|
80 |
+
|
81 |
+
$this->wp_customize->add_control(
|
82 |
+
new WP_Customize_Control(
|
83 |
+
$this->wp_customize, 'wpseo-breadcrumbs-blog-remove', array(
|
84 |
+
'label' => __( 'Remove blog page from breadcrumbs', 'wordpress-seo' ),
|
85 |
+
'type' => 'checkbox',
|
86 |
+
'section' => 'wpseo_breadcrumbs_customizer_section',
|
87 |
+
'settings' => 'wpseo_titles[breadcrumbs-blog-remove]',
|
88 |
+
'context' => '',
|
89 |
+
'active_callback' => array( $this, 'breadcrumbs_blog_remove_active_cb' ),
|
90 |
+
)
|
91 |
+
)
|
92 |
+
);
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Returns whether or not to show the breadcrumbs blog remove option
|
97 |
+
*
|
98 |
+
* @return bool
|
99 |
+
*/
|
100 |
+
public function breadcrumbs_blog_remove_active_cb() {
|
101 |
+
return 'page' === get_option( 'show_on_front' );
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Adds the breadcrumbs separator text field
|
106 |
+
*/
|
107 |
+
private function breadcrumbs_separator_setting() {
|
108 |
+
$this->wp_customize->add_setting(
|
109 |
+
'wpseo_titles[breadcrumbs-sep]', array(
|
110 |
+
'default' => '',
|
111 |
+
'type' => 'option',
|
112 |
+
'transport' => 'refresh',
|
113 |
+
)
|
114 |
+
);
|
115 |
+
|
116 |
+
$this->wp_customize->add_control(
|
117 |
+
new WP_Customize_Control(
|
118 |
+
$this->wp_customize, 'wpseo-breadcrumbs-separator', array(
|
119 |
+
'label' => __( 'Breadcrumbs separator:', 'wordpress-seo' ),
|
120 |
+
'type' => 'text',
|
121 |
+
'section' => 'wpseo_breadcrumbs_customizer_section',
|
122 |
+
'settings' => 'wpseo_titles[breadcrumbs-sep]',
|
123 |
+
'context' => '',
|
124 |
+
)
|
125 |
+
)
|
126 |
+
);
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Adds the breadcrumbs home anchor text field
|
131 |
+
*/
|
132 |
+
private function breadcrumbs_home_setting() {
|
133 |
+
$this->wp_customize->add_setting(
|
134 |
+
'wpseo_titles[breadcrumbs-home]', array(
|
135 |
+
'default' => '',
|
136 |
+
'type' => 'option',
|
137 |
+
'transport' => 'refresh',
|
138 |
+
)
|
139 |
+
);
|
140 |
+
|
141 |
+
$this->wp_customize->add_control(
|
142 |
+
new WP_Customize_Control(
|
143 |
+
$this->wp_customize, 'wpseo-breadcrumbs-home', array(
|
144 |
+
'label' => __( 'Anchor text for the homepage:', 'wordpress-seo' ),
|
145 |
+
'type' => 'text',
|
146 |
+
'section' => 'wpseo_breadcrumbs_customizer_section',
|
147 |
+
'settings' => 'wpseo_titles[breadcrumbs-home]',
|
148 |
+
'context' => '',
|
149 |
+
)
|
150 |
+
)
|
151 |
+
);
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Adds the breadcrumbs prefix text field
|
156 |
+
*/
|
157 |
+
private function breadcrumbs_prefix_setting() {
|
158 |
+
$this->wp_customize->add_setting(
|
159 |
+
'wpseo_titles[breadcrumbs-prefix]', array(
|
160 |
+
'default' => '',
|
161 |
+
'type' => 'option',
|
162 |
+
'transport' => 'refresh',
|
163 |
+
)
|
164 |
+
);
|
165 |
+
|
166 |
+
$this->wp_customize->add_control(
|
167 |
+
new WP_Customize_Control(
|
168 |
+
$this->wp_customize, 'wpseo-breadcrumbs-prefix', array(
|
169 |
+
'label' => __( 'Prefix for breadcrumbs:', 'wordpress-seo' ),
|
170 |
+
'type' => 'text',
|
171 |
+
'section' => 'wpseo_breadcrumbs_customizer_section',
|
172 |
+
'settings' => 'wpseo_titles[breadcrumbs-prefix]',
|
173 |
+
'context' => '',
|
174 |
+
)
|
175 |
+
)
|
176 |
+
);
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Adds the breadcrumbs archive prefix text field
|
181 |
+
*/
|
182 |
+
private function breadcrumbs_archiveprefix_setting() {
|
183 |
+
$this->wp_customize->add_setting(
|
184 |
+
'wpseo_titles[breadcrumbs-archiveprefix]', array(
|
185 |
+
'default' => '',
|
186 |
+
'type' => 'option',
|
187 |
+
'transport' => 'refresh',
|
188 |
+
)
|
189 |
+
);
|
190 |
+
|
191 |
+
$this->wp_customize->add_control(
|
192 |
+
new WP_Customize_Control(
|
193 |
+
$this->wp_customize, 'wpseo-breadcrumbs-archiveprefix', array(
|
194 |
+
'label' => __( 'Prefix for archive pages:', 'wordpress-seo' ),
|
195 |
+
'type' => 'text',
|
196 |
+
'section' => 'wpseo_breadcrumbs_customizer_section',
|
197 |
+
'settings' => 'wpseo_titles[breadcrumbs-archiveprefix]',
|
198 |
+
'context' => '',
|
199 |
+
)
|
200 |
+
)
|
201 |
+
);
|
202 |
+
}
|
203 |
+
|
204 |
+
/**
|
205 |
+
* Adds the breadcrumbs search prefix text field
|
206 |
+
*/
|
207 |
+
private function breadcrumbs_searchprefix_setting() {
|
208 |
+
$this->wp_customize->add_setting(
|
209 |
+
'wpseo_titles[breadcrumbs-searchprefix]', array(
|
210 |
+
'default' => '',
|
211 |
+
'type' => 'option',
|
212 |
+
'transport' => 'refresh',
|
213 |
+
)
|
214 |
+
);
|
215 |
+
|
216 |
+
$this->wp_customize->add_control(
|
217 |
+
new WP_Customize_Control(
|
218 |
+
$this->wp_customize, 'wpseo-breadcrumbs-searchprefix', array(
|
219 |
+
'label' => __( 'Prefix for search result pages:', 'wordpress-seo' ),
|
220 |
+
'type' => 'text',
|
221 |
+
'section' => 'wpseo_breadcrumbs_customizer_section',
|
222 |
+
'settings' => 'wpseo_titles[breadcrumbs-searchprefix]',
|
223 |
+
'context' => '',
|
224 |
+
)
|
225 |
+
)
|
226 |
+
);
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* Adds the breadcrumb 404 prefix text field
|
231 |
+
*/
|
232 |
+
private function breadcrumbs_404_setting() {
|
233 |
+
$this->wp_customize->add_setting(
|
234 |
+
'wpseo_titles[breadcrumbs-404crumb]', array(
|
235 |
+
'default' => '',
|
236 |
+
'type' => 'option',
|
237 |
+
'transport' => 'refresh',
|
238 |
+
)
|
239 |
+
);
|
240 |
+
|
241 |
+
$this->wp_customize->add_control(
|
242 |
+
new WP_Customize_Control(
|
243 |
+
$this->wp_customize, 'wpseo-breadcrumbs-404crumb', array(
|
244 |
+
'label' => __( 'Breadcrumb for 404 pages:', 'wordpress-seo' ),
|
245 |
+
'type' => 'text',
|
246 |
+
'section' => 'wpseo_breadcrumbs_customizer_section',
|
247 |
+
'settings' => 'wpseo_titles[breadcrumbs-404crumb]',
|
248 |
+
'context' => '',
|
249 |
+
)
|
250 |
+
)
|
251 |
+
);
|
252 |
+
}
|
253 |
+
}
|
admin/class-database-proxy.php
ADDED
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the proxy for communicating with the database
|
8 |
+
*/
|
9 |
+
class WPSEO_Database_Proxy {
|
10 |
+
|
11 |
+
/** @var string */
|
12 |
+
protected $table_name;
|
13 |
+
|
14 |
+
/** @var bool */
|
15 |
+
protected $suppress_errors = true;
|
16 |
+
|
17 |
+
/** @var bool */
|
18 |
+
protected $last_suppressed_state;
|
19 |
+
|
20 |
+
/** @var wpdb */
|
21 |
+
protected $database;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Sets the class attributes.
|
25 |
+
*
|
26 |
+
* @param wpdb $database The database object.
|
27 |
+
* @param string $table_name The table name that is represented.
|
28 |
+
* @param bool $suppress_errors Should the errors be suppressed.
|
29 |
+
*/
|
30 |
+
public function __construct( $database, $table_name, $suppress_errors = true ) {
|
31 |
+
$this->table_name = $table_name;
|
32 |
+
$this->suppress_errors = (bool) $suppress_errors;
|
33 |
+
$this->database = $database;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Inserts data into the database.
|
38 |
+
*
|
39 |
+
* @param array $data Data to insert.
|
40 |
+
* @param null $format Formats for the data.
|
41 |
+
*
|
42 |
+
* @return false|int Total amount of inserted rows or false on error.
|
43 |
+
*/
|
44 |
+
public function insert( array $data, $format = null ) {
|
45 |
+
$this->pre_execution();
|
46 |
+
|
47 |
+
$result = $this->database->insert( $this->get_table_name(), $data, $format );
|
48 |
+
|
49 |
+
$this->post_execution();
|
50 |
+
|
51 |
+
return $result;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Updates data in the database.
|
56 |
+
*
|
57 |
+
* @param array $data Data to update on the table.
|
58 |
+
* @param array $where Where condition as key => value array.
|
59 |
+
* @param null $format Optional. data prepare format.
|
60 |
+
* @param null $where_format Optional. Where prepare format.
|
61 |
+
*
|
62 |
+
* @return false|int False when the update request is invalid, int on number of rows changed.
|
63 |
+
*/
|
64 |
+
public function update( array $data, array $where, $format = null, $where_format = null ) {
|
65 |
+
$this->pre_execution();
|
66 |
+
|
67 |
+
$result = $this->database->update( $this->get_table_name(), $data, $where, $format, $where_format );
|
68 |
+
|
69 |
+
$this->post_execution();
|
70 |
+
|
71 |
+
return $result;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Upserts data in the database.
|
76 |
+
*
|
77 |
+
* Tries to insert the data first, if this fails an update is attempted.
|
78 |
+
*
|
79 |
+
* @param array $data Data to update on the table.
|
80 |
+
* @param array $where Where condition as key => value array.
|
81 |
+
* @param null $format Optional. data prepare format.
|
82 |
+
* @param null $where_format Optional. Where prepare format.
|
83 |
+
*
|
84 |
+
* @return false|int False when the upsert request is invalid, int on number of rows changed.
|
85 |
+
*/
|
86 |
+
public function upsert( array $data, array $where, $format = null, $where_format = null ) {
|
87 |
+
$result = $this->insert( $data, $format );
|
88 |
+
|
89 |
+
if ( false === $result ) {
|
90 |
+
$result = $this->update( $data, $where, $format, $where_format );
|
91 |
+
}
|
92 |
+
|
93 |
+
return $result;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Deletes a record from the database.
|
98 |
+
*
|
99 |
+
* @param array $where Where clauses for the query.
|
100 |
+
* @param null|array $format Formats for the data.
|
101 |
+
*
|
102 |
+
* @return false|int
|
103 |
+
*/
|
104 |
+
public function delete( array $where, $format = null ) {
|
105 |
+
$this->pre_execution();
|
106 |
+
|
107 |
+
$result = $this->database->delete( $this->get_table_name(), $where, $format );
|
108 |
+
|
109 |
+
$this->post_execution();
|
110 |
+
|
111 |
+
return $result;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Executes the given query and returns the results.
|
116 |
+
*
|
117 |
+
* @param string $query The query to execute.
|
118 |
+
*
|
119 |
+
* @return array|null|object The resultset
|
120 |
+
*/
|
121 |
+
public function get_results( $query ) {
|
122 |
+
$this->pre_execution();
|
123 |
+
|
124 |
+
$results = $this->database->get_results( $query );
|
125 |
+
|
126 |
+
$this->post_execution();
|
127 |
+
|
128 |
+
return $results;
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Creates a table to the database.
|
133 |
+
*
|
134 |
+
* @param array $columns The columns to create.
|
135 |
+
* @param array $indexes The indexes to use.
|
136 |
+
*
|
137 |
+
* @return bool True when creation is successful.
|
138 |
+
*/
|
139 |
+
public function create_table( array $columns, array $indexes = array() ) {
|
140 |
+
$create_table = sprintf( '
|
141 |
+
CREATE TABLE IF NOT EXISTS %1$s ( %2$s ) %3$s',
|
142 |
+
$this->get_table_name(),
|
143 |
+
implode( ',', array_merge( $columns, $indexes ) ),
|
144 |
+
$this->database->get_charset_collate()
|
145 |
+
);
|
146 |
+
|
147 |
+
$this->pre_execution();
|
148 |
+
|
149 |
+
$is_created = (bool) $this->database->query( $create_table );
|
150 |
+
|
151 |
+
$this->post_execution();
|
152 |
+
|
153 |
+
return $is_created;
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* Checks if there is an error.
|
158 |
+
*
|
159 |
+
* @return bool Returns true when there is an error.
|
160 |
+
*/
|
161 |
+
public function has_error() {
|
162 |
+
return ( $this->database->last_error !== '' );
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Executed before a query will be ran.
|
167 |
+
*/
|
168 |
+
protected function pre_execution() {
|
169 |
+
if ( $this->suppress_errors ) {
|
170 |
+
$this->last_suppressed_state = $this->database->suppress_errors();
|
171 |
+
}
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Executed after a query has been ran.
|
176 |
+
*/
|
177 |
+
protected function post_execution() {
|
178 |
+
if ( $this->suppress_errors ) {
|
179 |
+
$this->database->suppress_errors( $this->last_suppressed_state );
|
180 |
+
}
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Returns the set table name.
|
185 |
+
*
|
186 |
+
* @return string
|
187 |
+
*/
|
188 |
+
protected function get_table_name() {
|
189 |
+
return $this->table_name;
|
190 |
+
}
|
191 |
+
}
|
admin/class-export.php
ADDED
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Export
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Export
|
8 |
+
*
|
9 |
+
* Class with functionality to export the WP SEO settings
|
10 |
+
*/
|
11 |
+
class WPSEO_Export {
|
12 |
+
|
13 |
+
const ZIP_FILENAME = 'yoast-seo-settings-export.zip';
|
14 |
+
const INI_FILENAME = 'settings.ini';
|
15 |
+
|
16 |
+
const NONCE_ACTION = 'wpseo_export';
|
17 |
+
const NONCE_NAME = 'wpseo_export_nonce';
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @var string
|
21 |
+
*/
|
22 |
+
private $export = '';
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @var string
|
26 |
+
*/
|
27 |
+
private $error = '';
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @var string
|
31 |
+
*/
|
32 |
+
public $export_zip_url = '';
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @var boolean
|
36 |
+
*/
|
37 |
+
public $success;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Whether or not the export will include taxonomy metadata
|
41 |
+
*
|
42 |
+
* @var boolean
|
43 |
+
*/
|
44 |
+
private $include_taxonomy;
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @var array
|
48 |
+
*/
|
49 |
+
private $dir = array();
|
50 |
+
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Class constructor
|
54 |
+
*
|
55 |
+
* @param boolean $include_taxonomy Whether to include the taxonomy metadata the plugin creates.
|
56 |
+
*/
|
57 |
+
public function __construct( $include_taxonomy = false ) {
|
58 |
+
$this->include_taxonomy = $include_taxonomy;
|
59 |
+
$this->dir = wp_upload_dir();
|
60 |
+
|
61 |
+
$this->export_settings();
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Returns true when the property error has a value.
|
66 |
+
*
|
67 |
+
* @return bool
|
68 |
+
*/
|
69 |
+
public function has_error() {
|
70 |
+
return ( $this->error !== '' );
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Sets the error hook, to display the error to the user.
|
75 |
+
*/
|
76 |
+
public function set_error_hook() {
|
77 |
+
/* translators: %1$s expands to Yoast SEO */
|
78 |
+
$message = sprintf( __( 'Error creating %1$s export: ', 'wordpress-seo' ), 'Yoast SEO' ) . $this->error;
|
79 |
+
|
80 |
+
printf(
|
81 |
+
'<div class="notice notice-error"><p>%1$s</p></div>',
|
82 |
+
$message
|
83 |
+
);
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Exports the current site's WP SEO settings.
|
88 |
+
*/
|
89 |
+
private function export_settings() {
|
90 |
+
|
91 |
+
$this->export_header();
|
92 |
+
|
93 |
+
foreach ( WPSEO_Options::get_option_names() as $opt_group ) {
|
94 |
+
$this->write_opt_group( $opt_group );
|
95 |
+
}
|
96 |
+
|
97 |
+
$this->taxonomy_metadata();
|
98 |
+
|
99 |
+
if ( ! $this->write_settings_file() ) {
|
100 |
+
$this->error = __( 'Could not write settings to file.', 'wordpress-seo' );
|
101 |
+
|
102 |
+
return;
|
103 |
+
}
|
104 |
+
|
105 |
+
if ( $this->zip_file() ) {
|
106 |
+
// Just exit, because there is a download being served.
|
107 |
+
exit;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Writes the header of the export file.
|
113 |
+
*/
|
114 |
+
private function export_header() {
|
115 |
+
$header = sprintf(
|
116 |
+
/* translators: %1$s expands to Yoast SEO, %2$s expands to Yoast.com */
|
117 |
+
esc_html__( 'This is a settings export file for the %1$s plugin by %2$s', 'wordpress-seo' ),
|
118 |
+
'Yoast SEO',
|
119 |
+
'Yoast.com'
|
120 |
+
);
|
121 |
+
$this->write_line( '; ' . $header . ' - ' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/1yd' ) ) );
|
122 |
+
if ( $this->include_taxonomy ) {
|
123 |
+
$this->write_line( '; ' . __( 'This export includes taxonomy metadata', 'wordpress-seo' ) );
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Writes a line to the export
|
129 |
+
*
|
130 |
+
* @param string $line Line string.
|
131 |
+
* @param boolean $newline_first Boolean flag whether to prepend with new line.
|
132 |
+
*/
|
133 |
+
private function write_line( $line, $newline_first = false ) {
|
134 |
+
if ( $newline_first ) {
|
135 |
+
$this->export .= PHP_EOL;
|
136 |
+
}
|
137 |
+
$this->export .= $line . PHP_EOL;
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Writes an entire option group to the export
|
142 |
+
*
|
143 |
+
* @param string $opt_group Option group name.
|
144 |
+
*/
|
145 |
+
private function write_opt_group( $opt_group ) {
|
146 |
+
|
147 |
+
$this->write_line( '[' . $opt_group . ']', true );
|
148 |
+
|
149 |
+
$options = get_option( $opt_group );
|
150 |
+
|
151 |
+
if ( ! is_array( $options ) ) {
|
152 |
+
return;
|
153 |
+
}
|
154 |
+
|
155 |
+
foreach ( $options as $key => $elem ) {
|
156 |
+
if ( is_array( $elem ) ) {
|
157 |
+
$count = count( $elem );
|
158 |
+
for ( $i = 0; $i < $count; $i ++ ) {
|
159 |
+
$this->write_setting( $key . '[]', $elem[ $i ] );
|
160 |
+
}
|
161 |
+
}
|
162 |
+
else {
|
163 |
+
$this->write_setting( $key, $elem );
|
164 |
+
}
|
165 |
+
}
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Writes a settings line to the export
|
170 |
+
*
|
171 |
+
* @param string $key Key string.
|
172 |
+
* @param string $val Value string.
|
173 |
+
*/
|
174 |
+
private function write_setting( $key, $val ) {
|
175 |
+
if ( is_string( $val ) ) {
|
176 |
+
$val = '"' . $val . '"';
|
177 |
+
}
|
178 |
+
$this->write_line( $key . ' = ' . $val );
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* Adds the taxonomy meta data if there is any
|
183 |
+
*/
|
184 |
+
private function taxonomy_metadata() {
|
185 |
+
if ( $this->include_taxonomy ) {
|
186 |
+
$taxonomy_meta = get_option( 'wpseo_taxonomy_meta' );
|
187 |
+
if ( is_array( $taxonomy_meta ) ) {
|
188 |
+
$this->write_line( '[wpseo_taxonomy_meta]', true );
|
189 |
+
$this->write_setting( 'wpseo_taxonomy_meta', urlencode( wp_json_encode( $taxonomy_meta ) ) );
|
190 |
+
}
|
191 |
+
else {
|
192 |
+
$this->write_line( '; ' . __( 'No taxonomy metadata found', 'wordpress-seo' ), true );
|
193 |
+
}
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
/**
|
198 |
+
* Writes the settings to our temporary settings.ini file
|
199 |
+
*
|
200 |
+
* @return boolean unsigned
|
201 |
+
*/
|
202 |
+
private function write_settings_file() {
|
203 |
+
$handle = fopen( $this->dir['path'] . '/' . self::INI_FILENAME, 'w' );
|
204 |
+
if ( ! $handle ) {
|
205 |
+
return false;
|
206 |
+
}
|
207 |
+
|
208 |
+
$res = fwrite( $handle, $this->export );
|
209 |
+
if ( ! $res ) {
|
210 |
+
return false;
|
211 |
+
}
|
212 |
+
|
213 |
+
fclose( $handle );
|
214 |
+
|
215 |
+
return true;
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* Zips the settings ini file
|
220 |
+
*
|
221 |
+
* @return bool|null
|
222 |
+
*/
|
223 |
+
private function zip_file() {
|
224 |
+
$is_zip_created = $this->create_zip();
|
225 |
+
|
226 |
+
// The settings.ini isn't needed, because it's in the zipfile.
|
227 |
+
$this->remove_settings_ini();
|
228 |
+
|
229 |
+
if ( ! $is_zip_created ) {
|
230 |
+
$this->error = __( 'Could not zip settings-file.', 'wordpress-seo' );
|
231 |
+
|
232 |
+
return false;
|
233 |
+
}
|
234 |
+
|
235 |
+
$this->serve_settings_export();
|
236 |
+
$this->remove_zip();
|
237 |
+
|
238 |
+
return true;
|
239 |
+
}
|
240 |
+
|
241 |
+
/**
|
242 |
+
* Creates the zipfile and returns true if it created successful.
|
243 |
+
*
|
244 |
+
* @return bool
|
245 |
+
*/
|
246 |
+
private function create_zip() {
|
247 |
+
chdir( $this->dir['path'] );
|
248 |
+
$zip = new PclZip( './' . self::ZIP_FILENAME );
|
249 |
+
if ( 0 === $zip->create( './' . self::INI_FILENAME ) ) {
|
250 |
+
return false;
|
251 |
+
}
|
252 |
+
|
253 |
+
return file_exists( self::ZIP_FILENAME );
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Downloads the zip file.
|
258 |
+
*/
|
259 |
+
private function serve_settings_export() {
|
260 |
+
// Clean any content that has been already output. For example by other plugins or faulty PHP files.
|
261 |
+
if ( ob_get_contents() ) {
|
262 |
+
ob_clean();
|
263 |
+
}
|
264 |
+
header( 'Content-Type: application/octet-stream; charset=utf-8' );
|
265 |
+
header( 'Content-Transfer-Encoding: Binary' );
|
266 |
+
header( 'Content-Disposition: attachment; filename=' . self::ZIP_FILENAME );
|
267 |
+
header( 'Content-Length: ' . filesize( self::ZIP_FILENAME ) );
|
268 |
+
|
269 |
+
readfile( self::ZIP_FILENAME );
|
270 |
+
}
|
271 |
+
|
272 |
+
/**
|
273 |
+
* Removes the settings ini file.
|
274 |
+
*/
|
275 |
+
private function remove_settings_ini() {
|
276 |
+
unlink( './' . self::INI_FILENAME );
|
277 |
+
}
|
278 |
+
|
279 |
+
/**
|
280 |
+
* Removes the files because they are already downloaded.
|
281 |
+
*/
|
282 |
+
private function remove_zip() {
|
283 |
+
unlink( './' . self::ZIP_FILENAME );
|
284 |
+
}
|
285 |
+
}
|
admin/class-extension-manager.php
ADDED
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the class that contains the available extensions for Yoast SEO.
|
8 |
+
*/
|
9 |
+
class WPSEO_Extension_Manager {
|
10 |
+
|
11 |
+
/** The transient key to save the cache in */
|
12 |
+
const TRANSIENT_CACHE_KEY = 'wpseo_license_active_extensions';
|
13 |
+
|
14 |
+
/** @var WPSEO_Extension[] */
|
15 |
+
protected $extensions = array();
|
16 |
+
|
17 |
+
/** @var array List of active plugins */
|
18 |
+
static protected $active_extensions;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Adds an extension to the manager.
|
22 |
+
*
|
23 |
+
* @param string $extension_name The extension name.
|
24 |
+
* @param WPSEO_Extension $extension The extension value object.
|
25 |
+
*
|
26 |
+
* @return void
|
27 |
+
*/
|
28 |
+
public function add( $extension_name, WPSEO_Extension $extension = null ) {
|
29 |
+
$this->extensions[ $extension_name ] = $extension;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Removes an extension from the manager.
|
34 |
+
*
|
35 |
+
* @param string $extension_name The name of the extension to remove.
|
36 |
+
*
|
37 |
+
* @return void
|
38 |
+
*/
|
39 |
+
public function remove( $extension_name ) {
|
40 |
+
if ( array_key_exists( $extension_name, $this->extensions ) ) {
|
41 |
+
unset( $this->extensions[ $extension_name ] );
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Returns the extension for the given extension name.
|
47 |
+
*
|
48 |
+
* @param string $extension_name The name of the extension to get.
|
49 |
+
*
|
50 |
+
* @return null|WPSEO_Extension The extension object or null when it doesn't exist.
|
51 |
+
*/
|
52 |
+
public function get( $extension_name ) {
|
53 |
+
if ( array_key_exists( $extension_name, $this->extensions ) ) {
|
54 |
+
return $this->extensions[ $extension_name ];
|
55 |
+
}
|
56 |
+
|
57 |
+
return null;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Returns all set extension.
|
62 |
+
*
|
63 |
+
* @return WPSEO_Extension[] Array with the extensions.
|
64 |
+
*/
|
65 |
+
public function get_all() {
|
66 |
+
return $this->extensions;
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Checks if the plugin is activated within My Yoast.
|
71 |
+
*
|
72 |
+
* @param string $extension_name The extension name to check.
|
73 |
+
*
|
74 |
+
* @return bool True when the plugin is activated.
|
75 |
+
*/
|
76 |
+
public function is_activated( $extension_name ) {
|
77 |
+
if ( self::$active_extensions === null ) {
|
78 |
+
// Force re-check on license & dashboard pages.
|
79 |
+
$current_page = $this->get_current_page();
|
80 |
+
|
81 |
+
// Check whether the licenses are valid or whether we need to show notifications.
|
82 |
+
$exclude_cache = ( $current_page === 'wpseo_licenses' || $current_page === 'wpseo_dashboard' );
|
83 |
+
|
84 |
+
// Fetch transient data on any other page.
|
85 |
+
if ( ! $exclude_cache ) {
|
86 |
+
self::$active_extensions = $this->get_cached_extensions();
|
87 |
+
}
|
88 |
+
|
89 |
+
// If the active extensions is still NULL, we need to set it.
|
90 |
+
if ( ! is_array( self::$active_extensions ) ) {
|
91 |
+
self::$active_extensions = $this->retrieve_active_extensions();
|
92 |
+
|
93 |
+
$this->set_cached_extensions( self::$active_extensions );
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
return in_array( $extension_name, self::$active_extensions, true );
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Retrieves the active extensions via an external request.
|
102 |
+
*
|
103 |
+
* @return array Array containing the active extensions.
|
104 |
+
*/
|
105 |
+
protected function retrieve_active_extensions() {
|
106 |
+
return (array) apply_filters( 'yoast-active-extensions', array() );
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Returns the current page.
|
111 |
+
*
|
112 |
+
* @return string The current page.
|
113 |
+
*/
|
114 |
+
protected function get_current_page() {
|
115 |
+
return filter_input( INPUT_GET, 'page' );
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Gets a cached list of active extensions.
|
120 |
+
*
|
121 |
+
* @return boolean|array The cached extensions.
|
122 |
+
*/
|
123 |
+
protected function get_cached_extensions() {
|
124 |
+
return get_transient( self::TRANSIENT_CACHE_KEY );
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Sets the active extensions transient for the set duration.
|
129 |
+
*
|
130 |
+
* @param array $extensions The extensions to add.
|
131 |
+
* @param int $duration The duration that the list of extensions needs to remain cached.
|
132 |
+
*
|
133 |
+
* @return void
|
134 |
+
*/
|
135 |
+
protected function set_cached_extensions( $extensions, $duration = DAY_IN_SECONDS ) {
|
136 |
+
set_transient( self::TRANSIENT_CACHE_KEY, $extensions, $duration );
|
137 |
+
}
|
138 |
+
}
|
admin/class-extension.php
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the values for a single Yoast Premium extension plugin.
|
8 |
+
*/
|
9 |
+
class WPSEO_Extension {
|
10 |
+
|
11 |
+
/** @var array */
|
12 |
+
protected $config = array();
|
13 |
+
|
14 |
+
/**
|
15 |
+
* WPSEO_Extension constructor.
|
16 |
+
*
|
17 |
+
* @param array $config The config to use.
|
18 |
+
*/
|
19 |
+
public function __construct( array $config ) {
|
20 |
+
$this->config = $config;
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Returns the product title.
|
25 |
+
*
|
26 |
+
* @return string The set title.
|
27 |
+
*/
|
28 |
+
public function get_title() {
|
29 |
+
return $this->config['title'];
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Returns URL to the page where the product can be bought.
|
34 |
+
*
|
35 |
+
* @return string The buy url.
|
36 |
+
*/
|
37 |
+
public function get_buy_url() {
|
38 |
+
return $this->config['buyUrl'];
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Returns URL to the page with more info.
|
43 |
+
*
|
44 |
+
* @return string The url to the info page.
|
45 |
+
*/
|
46 |
+
public function get_info_url() {
|
47 |
+
return $this->config['infoUrl'];
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Returns the image.
|
52 |
+
*
|
53 |
+
* @return string The image.
|
54 |
+
*/
|
55 |
+
public function get_image() {
|
56 |
+
return $this->config['image'];
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Returns the buy button value if set, otherwise fallback to the title.
|
61 |
+
*
|
62 |
+
* @return string The buy button.
|
63 |
+
*/
|
64 |
+
public function get_buy_button() {
|
65 |
+
if ( isset( $this->config['buy_button'] ) ) {
|
66 |
+
return $this->config['buy_button'];
|
67 |
+
}
|
68 |
+
|
69 |
+
return $this->get_title();
|
70 |
+
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Returns the benefits.
|
75 |
+
*
|
76 |
+
* @return array The array with benefits.
|
77 |
+
*/
|
78 |
+
public function get_benefits() {
|
79 |
+
return $this->config['benefits'];
|
80 |
+
}
|
81 |
+
}
|
admin/class-extensions.php
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the class that contains the list of possible extensions for Yoast SEO.
|
8 |
+
*/
|
9 |
+
class WPSEO_Extensions {
|
10 |
+
|
11 |
+
/** @var array Array with the Yoast extensions */
|
12 |
+
protected $extensions = array(
|
13 |
+
'Yoast SEO Premium' => array(
|
14 |
+
'slug' => 'yoast-seo-premium',
|
15 |
+
'identifier' => 'wordpress-seo-premium',
|
16 |
+
'classname' => 'WPSEO_Premium',
|
17 |
+
),
|
18 |
+
'News SEO' => array(
|
19 |
+
'slug' => 'news-seo',
|
20 |
+
'identifier' => 'wpseo-news',
|
21 |
+
'classname' => 'WPSEO_News',
|
22 |
+
),
|
23 |
+
'Yoast WooCommerce SEO' => array(
|
24 |
+
'slug' => 'woocommerce-yoast-seo',
|
25 |
+
'identifier' => 'wpseo-woocommerce',
|
26 |
+
'classname' => 'Yoast_WooCommerce_SEO',
|
27 |
+
),
|
28 |
+
'Video SEO' => array(
|
29 |
+
'slug' => 'video-seo-for-wordpress',
|
30 |
+
'identifier' => 'wpseo-video',
|
31 |
+
'classname' => 'WPSEO_Video_Sitemap',
|
32 |
+
),
|
33 |
+
'Local SEO' => array(
|
34 |
+
'slug' => 'local-seo-for-wordpress',
|
35 |
+
'identifier' => 'wpseo-local',
|
36 |
+
'classname' => 'WPSEO_Local_Core',
|
37 |
+
),
|
38 |
+
'Local SEO for WooCommerce' => array(
|
39 |
+
'slug' => 'local-seo-for-woocommerce',
|
40 |
+
'identifier' => 'wpseo-local-woocommerce',
|
41 |
+
'classname' => 'WPSEO_Local_WooCommerce',
|
42 |
+
),
|
43 |
+
);
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Returns the set extensions.
|
47 |
+
*
|
48 |
+
* @return array All the extension names.
|
49 |
+
*/
|
50 |
+
public function get() {
|
51 |
+
return array_keys( $this->extensions );
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Checks if the extension is valid.
|
56 |
+
*
|
57 |
+
* @param string $extension The extension to get the name for.
|
58 |
+
*
|
59 |
+
* @return bool Returns true when valid.
|
60 |
+
*/
|
61 |
+
public function is_valid( $extension ) {
|
62 |
+
$extensions = new WPSEO_Extension_Manager();
|
63 |
+
return $extensions->is_activated( $this->extensions[ $extension ]['identifier'] );
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Invalidates the extension by removing its option.
|
68 |
+
*
|
69 |
+
* @param string $extension The extension to invalidate.
|
70 |
+
*/
|
71 |
+
public function invalidate( $extension ) {
|
72 |
+
/*
|
73 |
+
* Make sure we clear the current site and multisite options.
|
74 |
+
*
|
75 |
+
* Because plugins can be site-activated or multi-site activated we need to clear
|
76 |
+
* all possible options.
|
77 |
+
*
|
78 |
+
* If we knew here that the extension in question was network activated
|
79 |
+
* we could do this a lot more easily.
|
80 |
+
*/
|
81 |
+
delete_option( $this->get_option_name( $extension ) );
|
82 |
+
delete_site_option( $this->get_option_name( $extension ) );
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Checks if the plugin has been installed.
|
87 |
+
*
|
88 |
+
* @param string $extension The name of the plugin to check.
|
89 |
+
*
|
90 |
+
* @return bool Returns true when installed.
|
91 |
+
*/
|
92 |
+
public function is_installed( $extension ) {
|
93 |
+
return class_exists( $this->extensions[ $extension ]['classname'] );
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Converts the extension to the required option name.
|
98 |
+
*
|
99 |
+
* @param string $extension The extension name to convert.
|
100 |
+
*
|
101 |
+
* @return string Returns the option name.
|
102 |
+
*/
|
103 |
+
protected function get_option_name( $extension ) {
|
104 |
+
return sanitize_title_with_dashes( $this->extensions[ $extension ]['slug'] . '_', null, 'save' ) . 'license';
|
105 |
+
}
|
106 |
+
}
|
admin/class-help-center-item.php
ADDED
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Options\Tabs
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Help_Center_Item
|
8 |
+
*/
|
9 |
+
class WPSEO_Help_Center_Item {
|
10 |
+
|
11 |
+
/** @var string Identifier for this tab. */
|
12 |
+
private $identifier;
|
13 |
+
|
14 |
+
/** @var string Label to display. */
|
15 |
+
private $label;
|
16 |
+
|
17 |
+
/** @var string The dashicon classname to display in front of the label. */
|
18 |
+
private $dashicon;
|
19 |
+
|
20 |
+
/** @var array Optional arguments. */
|
21 |
+
private $args = array();
|
22 |
+
|
23 |
+
/**
|
24 |
+
* WPSEO_Help_Center_Item constructor.
|
25 |
+
*
|
26 |
+
* @param string $identifier Unique identifier for this tab.
|
27 |
+
* @param string $label Label to display.
|
28 |
+
* @param array $args Optional. Settings for this tab.
|
29 |
+
* @param string $dashicon Optional. The classname of the dahsicon to put in front of the label.
|
30 |
+
*/
|
31 |
+
public function __construct( $identifier, $label, $args = array(), $dashicon = '' ) {
|
32 |
+
$this->identifier = $identifier;
|
33 |
+
$this->label = $label;
|
34 |
+
$this->dashicon = $dashicon;
|
35 |
+
$this->args = $args;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Get the label.
|
40 |
+
*
|
41 |
+
* @return string
|
42 |
+
*/
|
43 |
+
public function get_label() {
|
44 |
+
return $this->label;
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Get the identifier.
|
49 |
+
*
|
50 |
+
* @return string
|
51 |
+
*/
|
52 |
+
public function get_identifier() {
|
53 |
+
return $this->identifier;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Get the dashicon.
|
58 |
+
*
|
59 |
+
* @return string
|
60 |
+
*/
|
61 |
+
public function get_dashicon() {
|
62 |
+
return $this->dashicon;
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Get the content of this tab.
|
67 |
+
*
|
68 |
+
* @return mixed|string
|
69 |
+
*/
|
70 |
+
public function get_content() {
|
71 |
+
if ( ! empty( $this->args['content'] ) ) {
|
72 |
+
return $this->args['content'];
|
73 |
+
}
|
74 |
+
|
75 |
+
if ( ! empty( $this->args['callback'] ) ) {
|
76 |
+
return call_user_func_array( $this->args['callback'], array( $this ) );
|
77 |
+
}
|
78 |
+
|
79 |
+
if ( ! empty( $this->args['view'] ) ) {
|
80 |
+
$view = $this->args['view'];
|
81 |
+
if ( substr( $view, - 4 ) === '.php' ) {
|
82 |
+
$view = substr( $view, 0, - 4 );
|
83 |
+
}
|
84 |
+
|
85 |
+
if ( ! empty( $this->args['view_arguments'] ) ) {
|
86 |
+
extract( $this->args['view_arguments'] );
|
87 |
+
}
|
88 |
+
|
89 |
+
include WPSEO_PATH . 'admin/views/' . $view . '.php';
|
90 |
+
}
|
91 |
+
|
92 |
+
return '';
|
93 |
+
}
|
94 |
+
}
|
admin/class-help-center.php
ADDED
@@ -0,0 +1,254 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Help_Center
|
8 |
+
*/
|
9 |
+
class WPSEO_Help_Center {
|
10 |
+
/** @var WPSEO_Option_Tab[] $tab */
|
11 |
+
private $tabs;
|
12 |
+
|
13 |
+
/** @var string Mount point in the HTML */
|
14 |
+
private $identifier = 'yoast-help-center-container';
|
15 |
+
|
16 |
+
/** @var array Additional help center items */
|
17 |
+
protected $help_center_items = array();
|
18 |
+
|
19 |
+
/** @var bool Show premium support tab */
|
20 |
+
protected $premium_support;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* WPSEO_Help_Center constructor.
|
24 |
+
*
|
25 |
+
* @param string $unused Backwards compatible argument.
|
26 |
+
* @param WPSEO_Option_Tabs|WPSEO_Option_Tab $option_tabs Currently displayed tabs.
|
27 |
+
* @param boolean $premium_support Show premium support tab.
|
28 |
+
*/
|
29 |
+
public function __construct( $unused, $option_tabs, $premium_support = false ) {
|
30 |
+
$this->premium_support = $premium_support;
|
31 |
+
|
32 |
+
$tabs = new WPSEO_Option_Tabs( '' );
|
33 |
+
|
34 |
+
if ( $option_tabs instanceof WPSEO_Option_Tabs ) {
|
35 |
+
$tabs = $option_tabs;
|
36 |
+
}
|
37 |
+
|
38 |
+
if ( $option_tabs instanceof WPSEO_Option_Tab ) {
|
39 |
+
$tabs = new WPSEO_Option_Tabs( '', $option_tabs->get_name() );
|
40 |
+
$tabs->add_tab( $option_tabs );
|
41 |
+
}
|
42 |
+
|
43 |
+
$this->tabs = $tabs;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Localize data required by the help center component.
|
48 |
+
*/
|
49 |
+
public function localize_data() {
|
50 |
+
$this->add_contact_support_item();
|
51 |
+
$this->enqueue_localized_data( $this->format_data( $this->tabs->get_tabs() ) );
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Format the required data for localized script.
|
56 |
+
*
|
57 |
+
* @param WPSEO_Option_Tab[] $tabs Yoast admin pages navigational tabs.
|
58 |
+
*
|
59 |
+
* @return array Associative array containing data for help center component.
|
60 |
+
*/
|
61 |
+
protected function format_data( array $tabs ) {
|
62 |
+
$formatted_data = array( 'tabs' => array() );
|
63 |
+
|
64 |
+
foreach ( $tabs as $tab ) {
|
65 |
+
$formatted_data['tabs'][ $tab->get_name() ] = array(
|
66 |
+
'label' => $tab->get_label(),
|
67 |
+
'videoUrl' => $tab->get_video_url(),
|
68 |
+
'id' => $tab->get_name(),
|
69 |
+
);
|
70 |
+
}
|
71 |
+
|
72 |
+
$active_tab = $this->tabs->get_active_tab();
|
73 |
+
$active_tab = ( null === $active_tab ) ? $tabs[0] : $active_tab;
|
74 |
+
|
75 |
+
$formatted_data['mountId'] = $this->identifier;
|
76 |
+
$formatted_data['initialTab'] = $active_tab->get_name();
|
77 |
+
|
78 |
+
$is_premium = WPSEO_Utils::is_yoast_seo_premium();
|
79 |
+
|
80 |
+
// Will translate to either empty string or "1" in localised script.
|
81 |
+
$formatted_data['isPremium'] = $is_premium;
|
82 |
+
$formatted_data['pluginVersion'] = WPSEO_VERSION;
|
83 |
+
|
84 |
+
// Open HelpScout on activating this tab ID.
|
85 |
+
$formatted_data['shouldDisplayContactForm'] = $this->premium_support;
|
86 |
+
|
87 |
+
$formatted_data['translations'] = self::get_translated_texts();
|
88 |
+
|
89 |
+
$formatted_data['videoDescriptions'] = array(
|
90 |
+
array(
|
91 |
+
'title' => __( 'Need some help?', 'wordpress-seo' ),
|
92 |
+
'description' => __( 'Go Premium and our experts will be there for you to answer any questions you might have about the setup and use of the plugin.', 'wordpress-seo' ),
|
93 |
+
'link' => 'https://yoa.st/seo-premium-vt?utm_content=' . WPSEO_VERSION,
|
94 |
+
'linkText' => __( 'Get Yoast SEO Premium now »', 'wordpress-seo' ),
|
95 |
+
),
|
96 |
+
array(
|
97 |
+
'title' => __( 'Want to be a Yoast SEO Expert?', 'wordpress-seo' ),
|
98 |
+
'description' => __( 'Follow our Yoast SEO for WordPress training and become a certified Yoast SEO Expert!', 'wordpress-seo' ),
|
99 |
+
'link' => 'https://yoa.st/wordpress-training-vt?utm_content=' . WPSEO_VERSION,
|
100 |
+
'linkText' => __( 'Enroll in the Yoast SEO for WordPress training »', 'wordpress-seo' ),
|
101 |
+
),
|
102 |
+
);
|
103 |
+
|
104 |
+
$formatted_data['contactSupportParagraphs'] = array(
|
105 |
+
array(
|
106 |
+
'image' => array(
|
107 |
+
'src' => esc_url( plugin_dir_url( WPSEO_FILE ) . 'images/support-team.svg' ),
|
108 |
+
'width' => 100,
|
109 |
+
'height' => 100,
|
110 |
+
'alt' => '',
|
111 |
+
),
|
112 |
+
'content' => null,
|
113 |
+
),
|
114 |
+
array(
|
115 |
+
'image' => null,
|
116 |
+
'content' => __( 'If you have a problem that you can\'t solve with our video tutorials or knowledge base, you can send a message to our support team. They can be reached 24/7.', 'wordpress-seo' ),
|
117 |
+
),
|
118 |
+
array(
|
119 |
+
'image' => null,
|
120 |
+
'content' => __( 'Support requests you create here are sent directly into our support system, which is secured with 256 bit SSL, so communication is 100% secure.', 'wordpress-seo' ),
|
121 |
+
),
|
122 |
+
);
|
123 |
+
|
124 |
+
$formatted_data['extraTabs'] = $this->get_extra_tabs();
|
125 |
+
|
126 |
+
return $formatted_data;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Get additional tabs for the help center component.
|
131 |
+
*
|
132 |
+
* @return array Additional help center tabs.
|
133 |
+
*/
|
134 |
+
protected function get_extra_tabs() {
|
135 |
+
$help_center_items = apply_filters( 'wpseo_help_center_items', $this->help_center_items );
|
136 |
+
|
137 |
+
return array_map( array( $this, 'format_helpcenter_tab' ), $help_center_items );
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Convert WPSEO_Help_Center_Item into help center format.
|
142 |
+
*
|
143 |
+
* @param WPSEO_Help_Center_Item $item The item to convert.
|
144 |
+
*
|
145 |
+
* @return array Formatted item.
|
146 |
+
*/
|
147 |
+
protected function format_helpcenter_tab( WPSEO_Help_Center_Item $item ) {
|
148 |
+
return array(
|
149 |
+
'identifier' => $item->get_identifier(),
|
150 |
+
'label' => $item->get_label(),
|
151 |
+
'content' => $item->get_content(),
|
152 |
+
);
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Enqueue localized script for help center component.
|
157 |
+
*
|
158 |
+
* @param array $data Data to localize.
|
159 |
+
*/
|
160 |
+
protected function enqueue_localized_data( $data ) {
|
161 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'help-center', 'wpseoHelpCenterData', $data );
|
162 |
+
}
|
163 |
+
|
164 |
+
/**
|
165 |
+
* Outputs the help center div.
|
166 |
+
*/
|
167 |
+
public function mount() {
|
168 |
+
echo '<div id="' . esc_attr( $this->identifier ) . '">' . esc_html__( 'Loading help center.', 'wordpress-seo' ) . '</div>';
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Add the contact support help center item to the help center.
|
173 |
+
*/
|
174 |
+
private function add_contact_support_item() {
|
175 |
+
/* translators: %s: expands to 'Yoast SEO Premium'. */
|
176 |
+
$popup_title = sprintf( __( 'Email support is a %s feature', 'wordpress-seo' ), 'Yoast SEO Premium' );
|
177 |
+
$popup_content = '<p class="yoast-measure">' . __( 'Go Premium and our experts will be there for you to answer any questions you might have about the set-up and use of the plug-in!', 'wordpress-seo' ) . '</p>';
|
178 |
+
/* translators: %1$s: expands to 'Yoast SEO Premium'. */
|
179 |
+
$popup_content .= '<p>' . sprintf( __( 'Other benefits of %1$s for you:', 'wordpress-seo' ), 'Yoast SEO Premium' ) . '</p>';
|
180 |
+
$popup_content .= '<ul class="wpseo-premium-advantages-list">';
|
181 |
+
$popup_content .= '<li>' . sprintf(
|
182 |
+
// We don't use strong text here, but we do use it in the "Add keyword" popup, this is just to have the same translatable strings.
|
183 |
+
/* translators: %1$s expands to a 'strong' start tag, %2$s to a 'strong' end tag. */
|
184 |
+
__( '%1$sNo more dead links%2$s: easy redirect manager', 'wordpress-seo' ), '', ''
|
185 |
+
) . '</li>';
|
186 |
+
$popup_content .= '<li>' . __( 'Superfast internal links suggestions', 'wordpress-seo' ) . '</li>';
|
187 |
+
$popup_content .= '<li>' . sprintf(
|
188 |
+
// We don't use strong text here, but we do use it in the "Add keyword" popup, this is just to have the same translatable strings.
|
189 |
+
/* translators: %1$s expands to a 'strong' start tag, %2$s to a 'strong' end tag. */
|
190 |
+
__( '%1$sSocial media preview%2$s: Facebook & Twitter', 'wordpress-seo' ), '', ''
|
191 |
+
) . '</li>';
|
192 |
+
$popup_content .= '<li>' . __( '24/7 support', 'wordpress-seo' ) . '</li>';
|
193 |
+
$popup_content .= '<li>' . __( 'No ads!', 'wordpress-seo' ) . '</li>';
|
194 |
+
$popup_content .= '</ul>';
|
195 |
+
|
196 |
+
$premium_popup = new WPSEO_Premium_Popup( 'contact-support', 'h2', $popup_title, $popup_content, WPSEO_Shortlinker::get( 'https://yoa.st/contact-support' ) );
|
197 |
+
$contact_support_help_center_item = new WPSEO_Help_Center_Item(
|
198 |
+
'contact-support',
|
199 |
+
__( 'Get support', 'wordpress-seo' ),
|
200 |
+
array( 'content' => $premium_popup->get_premium_message( false ) ),
|
201 |
+
'dashicons-email-alt'
|
202 |
+
);
|
203 |
+
|
204 |
+
$this->help_center_items[] = $contact_support_help_center_item;
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Pass text variables to js for the help center JS module.
|
209 |
+
*
|
210 |
+
* %s is replaced with <code>%s</code> and replaced again in the javascript with the actual variable.
|
211 |
+
*
|
212 |
+
* @return array Translated text strings for the help center.
|
213 |
+
*/
|
214 |
+
public static function get_translated_texts() {
|
215 |
+
// Esc_html is not needed because React already handles HTML in the (translations of) these strings.
|
216 |
+
return array(
|
217 |
+
'locale' => WPSEO_Utils::get_user_locale(),
|
218 |
+
'videoTutorial' => __( 'Video tutorial', 'wordpress-seo' ),
|
219 |
+
'knowledgeBase' => __( 'Knowledge base', 'wordpress-seo' ),
|
220 |
+
'getSupport' => __( 'Get support', 'wordpress-seo' ),
|
221 |
+
'algoliaSearcher.loadingPlaceholder' => __( 'Loading...', 'wordpress-seo' ),
|
222 |
+
'algoliaSearcher.errorMessage' => __( 'Something went wrong. Please try again later.', 'wordpress-seo' ),
|
223 |
+
'searchBar.headingText' => __( 'Search the Yoast Knowledge Base for answers to your questions:', 'wordpress-seo' ),
|
224 |
+
'searchBar.placeholderText' => __( 'Type here to search...', 'wordpress-seo' ),
|
225 |
+
'searchBar.buttonText' => __( 'Search', 'wordpress-seo' ),
|
226 |
+
'searchResultDetail.openButton' => __( 'View in KB', 'wordpress-seo' ),
|
227 |
+
'searchResultDetail.openButtonLabel' => __( 'Open the knowledge base article in a new window or read it in the iframe below', 'wordpress-seo' ),
|
228 |
+
'searchResultDetail.backButton' => __( 'Go back', 'wordpress-seo' ),
|
229 |
+
'searchResultDetail.backButtonLabel' => __( 'Go back to the search results', 'wordpress-seo' ),
|
230 |
+
'searchResultDetail.iframeTitle' => __( 'Knowledge base article', 'wordpress-seo' ),
|
231 |
+
'searchResultDetail.searchResult' => __( 'Search result', 'wordpress-seo' ),
|
232 |
+
'searchResult.noResultsText' => __( 'No results found.', 'wordpress-seo' ),
|
233 |
+
'searchResult.foundResultsText' => sprintf(
|
234 |
+
/* translators: %s expands to the number of results found . */
|
235 |
+
__( 'Number of results found: %s', 'wordpress-seo' ),
|
236 |
+
'{ resultsCount }'
|
237 |
+
),
|
238 |
+
'searchResult.searchResultsHeading' => __( 'Search results', 'wordpress-seo' ),
|
239 |
+
'a11yNotice.opensInNewTab' => __( '(Opens in a new browser tab)', 'wordpress-seo' ),
|
240 |
+
'contactSupport.button' => __( 'New support request', 'wordpress-seo' ),
|
241 |
+
'helpCenter.buttonText' => __( 'Need help?', 'wordpress-seo' ),
|
242 |
+
);
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* Outputs the help center.
|
247 |
+
*
|
248 |
+
* @deprecated 5.6
|
249 |
+
*/
|
250 |
+
public function output_help_center() {
|
251 |
+
_deprecated_function( 'WPSEO_Help_Center::output_help_center', 'WPSEO 5.6.0', 'WPSEO_Help_Center::mount()' );
|
252 |
+
$this->mount();
|
253 |
+
}
|
254 |
+
}
|
admin/class-import-aioseo.php
ADDED
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Import\External
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class with functionality to import Yoast SEO settings from All In One SEO.
|
8 |
+
*/
|
9 |
+
class WPSEO_Import_AIOSEO extends WPSEO_Import_External {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Holds the AOIOSEO options
|
13 |
+
*
|
14 |
+
* @var array
|
15 |
+
*/
|
16 |
+
private $aioseo_options;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Import All In One SEO settings
|
20 |
+
*
|
21 |
+
* @param boolean $replace Boolean replace switch.
|
22 |
+
*/
|
23 |
+
public function __construct( $replace = false ) {
|
24 |
+
parent::__construct( $replace );
|
25 |
+
|
26 |
+
$this->aioseo_options = get_option( 'aioseop_options' );
|
27 |
+
|
28 |
+
$this->success = true;
|
29 |
+
$this->import_metas();
|
30 |
+
$this->import_ga();
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Import All In One SEO meta values.
|
35 |
+
*/
|
36 |
+
private function import_metas() {
|
37 |
+
WPSEO_Meta::replace_meta( '_aioseop_description', WPSEO_Meta::$meta_prefix . 'metadesc', $this->replace );
|
38 |
+
WPSEO_Meta::replace_meta( '_aioseop_keywords', WPSEO_Meta::$meta_prefix . 'metakeywords', $this->replace );
|
39 |
+
WPSEO_Meta::replace_meta( '_aioseop_title', WPSEO_Meta::$meta_prefix . 'title', $this->replace );
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Import the Google Analytics settings.
|
44 |
+
*
|
45 |
+
* These values are used in Google Analytics for WordPress by MonsterInsights and will be converted in the plugin
|
46 |
+
* to usable settings when a user installs the Google Analytics plugin for the first time.
|
47 |
+
*/
|
48 |
+
private function import_ga() {
|
49 |
+
if ( ! isset( $this->aioseo_options['aiosp_google_analytics_id'] ) ) {
|
50 |
+
$this->set_msg( sprintf(
|
51 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
52 |
+
__( 'All in One SEO data successfully imported. Would you like to %1$sdisable the All in One SEO plugin%2$s?', 'wordpress-seo' ),
|
53 |
+
'<a href="' . esc_url( admin_url( 'admin.php?page=wpseo_tools&tool=import-export&deactivate_aioseo=1#top#import-seo' ) ) . '">',
|
54 |
+
'</a>'
|
55 |
+
) );
|
56 |
+
|
57 |
+
return;
|
58 |
+
}
|
59 |
+
|
60 |
+
if ( get_option( 'yst_ga' ) === false ) {
|
61 |
+
update_option( 'yst_ga', $this->determine_ga_settings() );
|
62 |
+
}
|
63 |
+
|
64 |
+
$plugin_install_nonce = wp_create_nonce( 'install-plugin_google-analytics-for-wordpress' ); // Use the old name because that's the WordPress.org repo.
|
65 |
+
|
66 |
+
$this->set_msg( sprintf(
|
67 |
+
/* translators: 1,2: link open tag; 3: link close tag. */
|
68 |
+
__( 'All in One SEO data successfully imported. Would you like to %1$sdisable the All in One SEO plugin%3$s? You\'ve had Google Analytics enabled in All in One SEO, would you like to install the %2$sGoogle Analytics plugin%3$s?', 'wordpress-seo' ),
|
69 |
+
'<a href="' . esc_url( admin_url( 'admin.php?page=wpseo_tools&tool=import-export&deactivate_aioseo=1#top#import-seo' ) ) . '">',
|
70 |
+
'<a href="' . esc_url( admin_url( 'update.php?action=install-plugin&plugin=google-analytics-for-wordpress&_wpnonce=' . $plugin_install_nonce ) ) . '">',
|
71 |
+
'</a>'
|
72 |
+
) );
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Determine the appropriate GA settings for this site.
|
77 |
+
*
|
78 |
+
* @return array $ga_settings The imported settings.
|
79 |
+
*/
|
80 |
+
private function determine_ga_settings() {
|
81 |
+
$ga_universal = 0;
|
82 |
+
if ( $this->aioseo_options['aiosp_ga_use_universal_analytics'] === 'on' ) {
|
83 |
+
$ga_universal = 1;
|
84 |
+
}
|
85 |
+
|
86 |
+
$ga_track_outbound = 0;
|
87 |
+
if ( $this->aioseo_options['aiosp_ga_track_outbound_links'] === 'on' ) {
|
88 |
+
$ga_track_outbound = 1;
|
89 |
+
}
|
90 |
+
|
91 |
+
$ga_anonymize_ip = 0;
|
92 |
+
if ( $this->aioseo_options['aiosp_ga_anonymize_ip'] === 'on' ) {
|
93 |
+
$ga_anonymize_ip = 1;
|
94 |
+
}
|
95 |
+
|
96 |
+
return array(
|
97 |
+
'ga_general' => array(
|
98 |
+
'manual_ua_code' => (int) 1,
|
99 |
+
'manual_ua_code_field' => $this->aioseo_options['aiosp_google_analytics_id'],
|
100 |
+
'enable_universal' => $ga_universal,
|
101 |
+
'track_outbound' => $ga_track_outbound,
|
102 |
+
'ignore_users' => (array) $this->aioseo_options['aiosp_ga_exclude_users'],
|
103 |
+
'anonymize_ips' => (int) $ga_anonymize_ip,
|
104 |
+
),
|
105 |
+
);
|
106 |
+
}
|
107 |
+
}
|
admin/class-import-external.php
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Import\External
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Import_External
|
8 |
+
*
|
9 |
+
* Class with functionality to import Yoast SEO settings from other plugins
|
10 |
+
*/
|
11 |
+
class WPSEO_Import_External {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Whether or not to delete old data.
|
15 |
+
*
|
16 |
+
* @var boolean
|
17 |
+
*/
|
18 |
+
protected $replace;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Message about the import status.
|
22 |
+
*
|
23 |
+
* @var string
|
24 |
+
*/
|
25 |
+
public $msg = '';
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Whether import has been successful.
|
29 |
+
*
|
30 |
+
* @var bool
|
31 |
+
*/
|
32 |
+
public $success = false;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Import class constructor.
|
36 |
+
*
|
37 |
+
* @param boolean $replace Boolean replace switch.
|
38 |
+
*/
|
39 |
+
public function __construct( $replace = false ) {
|
40 |
+
$this->replace = $replace;
|
41 |
+
|
42 |
+
WPSEO_Options::initialize();
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Convenience function to set import message
|
47 |
+
*
|
48 |
+
* @param string $msg Message string.
|
49 |
+
*/
|
50 |
+
protected function set_msg( $msg ) {
|
51 |
+
if ( ! empty( $this->msg ) ) {
|
52 |
+
$this->msg .= PHP_EOL;
|
53 |
+
}
|
54 |
+
$this->msg .= $msg;
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Deletes an option depending on the class replace state
|
59 |
+
*
|
60 |
+
* @param string $option Option key.
|
61 |
+
*/
|
62 |
+
protected function perhaps_delete( $option ) {
|
63 |
+
if ( $this->replace ) {
|
64 |
+
delete_option( $option );
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Import HeadSpace SEO settings
|
70 |
+
*/
|
71 |
+
public function import_headspace() {
|
72 |
+
global $wpdb;
|
73 |
+
|
74 |
+
WPSEO_Meta::replace_meta( '_headspace_description', WPSEO_Meta::$meta_prefix . 'metadesc', $this->replace );
|
75 |
+
WPSEO_Meta::replace_meta( '_headspace_keywords', WPSEO_Meta::$meta_prefix . 'metakeywords', $this->replace );
|
76 |
+
WPSEO_Meta::replace_meta( '_headspace_page_title', WPSEO_Meta::$meta_prefix . 'title', $this->replace );
|
77 |
+
|
78 |
+
/**
|
79 |
+
* @todo [JRF => whomever] verify how headspace sets these metas ( 'noindex', 'nofollow', 'noarchive', 'noodp', 'noydir' )
|
80 |
+
* and if the values saved are concurrent with the ones we use (i.e. 0/1/2)
|
81 |
+
*/
|
82 |
+
WPSEO_Meta::replace_meta( '_headspace_noindex', WPSEO_Meta::$meta_prefix . 'meta-robots-noindex', $this->replace );
|
83 |
+
WPSEO_Meta::replace_meta( '_headspace_nofollow', WPSEO_Meta::$meta_prefix . 'meta-robots-nofollow', $this->replace );
|
84 |
+
|
85 |
+
/*
|
86 |
+
* @todo - [JRF => whomever] check if this can be done more efficiently by querying only the meta table
|
87 |
+
* possibly directly changing it using concat on the existing values
|
88 |
+
*/
|
89 |
+
$posts = $wpdb->get_results( "SELECT ID FROM $wpdb->posts" );
|
90 |
+
if ( is_array( $posts ) && $posts !== array() ) {
|
91 |
+
foreach ( $posts as $post ) {
|
92 |
+
$custom = get_post_custom( $post->ID );
|
93 |
+
$robotsmeta_adv = '';
|
94 |
+
if ( isset( $custom['_headspace_noarchive'] ) ) {
|
95 |
+
$robotsmeta_adv .= 'noarchive,';
|
96 |
+
}
|
97 |
+
$robotsmeta_adv = preg_replace( '`,$`', '', $robotsmeta_adv );
|
98 |
+
WPSEO_Meta::set_value( 'meta-robots-adv', $robotsmeta_adv, $post->ID );
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
if ( $this->replace ) {
|
103 |
+
// We no longer use noydir, but we remove the meta key as it's unneeded.
|
104 |
+
$hs_meta = array( 'noarchive', 'noodp', 'noydir' );
|
105 |
+
foreach ( $hs_meta as $meta ) {
|
106 |
+
delete_post_meta_by_key( '_headspace_' . $meta );
|
107 |
+
}
|
108 |
+
unset( $hs_meta, $meta );
|
109 |
+
}
|
110 |
+
$this->success = true;
|
111 |
+
$this->set_msg( __( 'HeadSpace2 data successfully imported', 'wordpress-seo' ) );
|
112 |
+
}
|
113 |
+
}
|
admin/class-import-jetpack.php
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Import\External
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Import_Jetpack_SEO
|
8 |
+
*
|
9 |
+
* Class with functionality to import Yoast SEO settings from Jetpack Advanced SEO
|
10 |
+
*/
|
11 |
+
class WPSEO_Import_Jetpack_SEO extends WPSEO_Import_External {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Import Jetpack Advanced SEO settings
|
15 |
+
*
|
16 |
+
* @param boolean $replace Boolean replace switch.
|
17 |
+
*/
|
18 |
+
public function __construct( $replace = false ) {
|
19 |
+
parent::__construct( $replace );
|
20 |
+
|
21 |
+
$this->success = true;
|
22 |
+
$this->import_metas();
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Import All In One SEO meta values
|
27 |
+
*/
|
28 |
+
private function import_metas() {
|
29 |
+
WPSEO_Meta::replace_meta( 'advanced_seo_description', WPSEO_Meta::$meta_prefix . 'metadesc', $this->replace );
|
30 |
+
}
|
31 |
+
}
|
admin/class-import-seopressor.php
ADDED
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Import\External
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Import_SEOPressor
|
8 |
+
*
|
9 |
+
* Class with functionality to import Yoast SEO settings from SEOpressor.
|
10 |
+
*/
|
11 |
+
class WPSEO_Import_SEOPressor extends WPSEO_Import_External {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Imports the SEOpressor settings.
|
15 |
+
*
|
16 |
+
* @param boolean $replace Boolean replace switch.
|
17 |
+
*/
|
18 |
+
public function __construct( $replace = false ) {
|
19 |
+
parent::__construct( $replace );
|
20 |
+
|
21 |
+
$this->import_post_metas();
|
22 |
+
|
23 |
+
$this->success = true;
|
24 |
+
$this->set_msg( __( 'SEOpressor data successfully imported.', 'wordpress-seo' ) );
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Imports the post meta values to Yoast SEO.
|
29 |
+
*
|
30 |
+
* @return void
|
31 |
+
*/
|
32 |
+
private function import_post_metas() {
|
33 |
+
// Query for all the posts that have an _seop_settings meta set.
|
34 |
+
$query_posts = new WP_Query( 'post_type=any&meta_key=_seop_settings&order=ASC&fields=ids&nopaging=true' );
|
35 |
+
if ( ! empty( $query_posts->posts ) ) {
|
36 |
+
foreach ( array_values( $query_posts->posts ) as $post_id ) {
|
37 |
+
$this->import_post_focus_keywords( $post_id );
|
38 |
+
$this->import_seopressor_post_settings( $post_id );
|
39 |
+
$this->seopressor_post_cleanup( $post_id );
|
40 |
+
}
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Imports the data. SEOpressor stores most of the data in one post array, this loops over it.
|
46 |
+
*
|
47 |
+
* @param int $post_id Post ID.
|
48 |
+
*
|
49 |
+
* @return void
|
50 |
+
*/
|
51 |
+
private function import_seopressor_post_settings( $post_id ) {
|
52 |
+
$settings = get_post_meta( $post_id, '_seop_settings', true );
|
53 |
+
|
54 |
+
foreach (
|
55 |
+
array(
|
56 |
+
'fb_description' => 'opengraph-description',
|
57 |
+
'fb_title' => 'opengraph-title',
|
58 |
+
'fb_type' => 'og_type',
|
59 |
+
'fb_img' => 'opengraph-image',
|
60 |
+
'meta_title' => 'title',
|
61 |
+
'meta_description' => 'metadesc',
|
62 |
+
'meta_canonical' => 'canonical',
|
63 |
+
'tw_description' => 'twitter-description',
|
64 |
+
'tw_title' => 'twitter-title',
|
65 |
+
'tw_image' => 'twitter-image',
|
66 |
+
) as $seopressor_key => $yoast_key ) {
|
67 |
+
$this->import_meta_helper( $seopressor_key, $yoast_key, $settings, $post_id );
|
68 |
+
}
|
69 |
+
|
70 |
+
$this->import_post_robots( $settings['meta_rules'], $post_id );
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Represents the Helper function to store the meta value should it be set in SEOPressor's settings.
|
75 |
+
*
|
76 |
+
* @param string $seo_pressor_key The key in the SEOPressor array.
|
77 |
+
* @param string $yoast_key The identifier we use in our meta settings.
|
78 |
+
* @param array $seopressor_settings The array of settings for this post in SEOpressor.
|
79 |
+
* @param int $post_id The post ID.
|
80 |
+
*
|
81 |
+
* @return void
|
82 |
+
*/
|
83 |
+
private function import_meta_helper( $seo_pressor_key, $yoast_key, $seopressor_settings, $post_id ) {
|
84 |
+
if ( ! empty( $seopressor_settings[ $seo_pressor_key ] ) ) {
|
85 |
+
WPSEO_Meta::set_value( $yoast_key, $seopressor_settings[ $seo_pressor_key ], $post_id );
|
86 |
+
}
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Imports the focus keywords, and stores them for later use.
|
91 |
+
*
|
92 |
+
* @param integer $post_id Post ID.
|
93 |
+
*
|
94 |
+
* @return void
|
95 |
+
*/
|
96 |
+
private function import_post_focus_keywords( $post_id ) {
|
97 |
+
// Import the focus keyword.
|
98 |
+
$focuskw = trim( get_post_meta( $post_id, '_seop_kw_1', true ) );
|
99 |
+
WPSEO_Meta::set_value( 'focuskw', $focuskw, $post_id );
|
100 |
+
|
101 |
+
// Import additional focus keywords for use in premium.
|
102 |
+
$focuskw2 = trim( get_post_meta( $post_id, '_seop_kw_2', true ) );
|
103 |
+
$focuskw3 = trim( get_post_meta( $post_id, '_seop_kw_3', true ) );
|
104 |
+
|
105 |
+
$focus_keywords = array();
|
106 |
+
if ( ! empty( $focuskw2 ) ) {
|
107 |
+
$focus_keywords[] = $focuskw2;
|
108 |
+
}
|
109 |
+
if ( ! empty( $focuskw3 ) ) {
|
110 |
+
$focus_keywords[] = $focuskw3;
|
111 |
+
}
|
112 |
+
|
113 |
+
if ( $focus_keywords !== array() ) {
|
114 |
+
WPSEO_Meta::set_value( 'focuskeywords', wp_json_encode( $focus_keywords ), $post_id );
|
115 |
+
}
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Retrieves the SEOpressor robot value and map this to Yoast SEO values.
|
120 |
+
*
|
121 |
+
* @param string $meta_rules The meta rules taken from the SEOpressor settings array.
|
122 |
+
* @param integer $post_id The post id of the current post.
|
123 |
+
*
|
124 |
+
* @return void
|
125 |
+
*/
|
126 |
+
private function import_post_robots( $meta_rules, $post_id ) {
|
127 |
+
$seopressor_robots = explode( '#|#|#', $meta_rules );
|
128 |
+
|
129 |
+
$robot_value = $this->get_robot_value( $seopressor_robots );
|
130 |
+
|
131 |
+
// Saving the new meta values for Yoast SEO.
|
132 |
+
WPSEO_Meta::set_value( 'meta-robots-noindex', $robot_value['index'], $post_id );
|
133 |
+
WPSEO_Meta::set_value( 'meta-robots-nofollow', $robot_value['follow'], $post_id );
|
134 |
+
WPSEO_Meta::set_value( 'meta-robots-adv', $robot_value['advanced'], $post_id );
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Gets the robot config by given SEOpressor robots value.
|
139 |
+
*
|
140 |
+
* @param array $seopressor_robots The value in SEOpressor that needs to be converted to the Yoast format.
|
141 |
+
*
|
142 |
+
* @return array The robots values in Yoast format.
|
143 |
+
*/
|
144 |
+
private function get_robot_value( $seopressor_robots ) {
|
145 |
+
$return = array(
|
146 |
+
'index' => 2,
|
147 |
+
'follow' => 0,
|
148 |
+
'advanced' => '',
|
149 |
+
);
|
150 |
+
|
151 |
+
if ( in_array( 'noindex', $seopressor_robots, true ) ) {
|
152 |
+
$return['index'] = 1;
|
153 |
+
}
|
154 |
+
if ( in_array( 'nofollow', $seopressor_robots, true ) ) {
|
155 |
+
$return['follow'] = 0;
|
156 |
+
}
|
157 |
+
foreach ( array( 'noarchive', 'nosnippet', 'noimageindex' ) as $needle ) {
|
158 |
+
if ( in_array( $needle, $seopressor_robots, true ) ) {
|
159 |
+
$return['advanced'] .= $needle . ',';
|
160 |
+
}
|
161 |
+
}
|
162 |
+
$return['advanced'] = rtrim( $return['advanced'], ',' );
|
163 |
+
|
164 |
+
return $return;
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Removes all the post meta fields SEOpressor creates.
|
169 |
+
*
|
170 |
+
* @param integer $post_id Post ID.
|
171 |
+
*
|
172 |
+
* @return void
|
173 |
+
*/
|
174 |
+
private function seopressor_post_cleanup( $post_id ) {
|
175 |
+
if ( ! $this->replace ) {
|
176 |
+
return;
|
177 |
+
}
|
178 |
+
|
179 |
+
// If we get to replace the data, let's do some proper cleanup.
|
180 |
+
global $wpdb;
|
181 |
+
$query = $wpdb->prepare(
|
182 |
+
"DELETE FROM $wpdb->postmeta
|
183 |
+
WHERE post_id = %d AND meta_key LIKE %s",
|
184 |
+
$post_id,
|
185 |
+
'_seop_%'
|
186 |
+
);
|
187 |
+
$wpdb->query( $query );
|
188 |
+
}
|
189 |
+
}
|
admin/class-import-ultimate-seo.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Import\External
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class with functionality to import Yoast SEO settings from Ultimate SEO.
|
8 |
+
*/
|
9 |
+
class WPSEO_Import_Ultimate_SEO extends WPSEO_Import_External {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Constructs the import SEO Ultimate settings.
|
13 |
+
*
|
14 |
+
* @param boolean $replace Boolean replace switch.
|
15 |
+
*/
|
16 |
+
public function __construct( $replace = false ) {
|
17 |
+
parent::__construct( $replace );
|
18 |
+
|
19 |
+
$this->import_metas();
|
20 |
+
$this->cleanup();
|
21 |
+
|
22 |
+
$this->success = true;
|
23 |
+
$this->set_msg( __( 'SEO Ultimate data successfully imported.', 'wordpress-seo' ) );
|
24 |
+
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Imports the Ultimate SEO meta values.
|
29 |
+
*
|
30 |
+
* @returns void
|
31 |
+
*/
|
32 |
+
private function import_metas() {
|
33 |
+
WPSEO_Meta::replace_meta( '_su_description', WPSEO_Meta::$meta_prefix . 'metadesc', $this->replace );
|
34 |
+
WPSEO_Meta::replace_meta( '_su_meta_robots_nofollow', WPSEO_Meta::$meta_prefix . 'meta-robots-nofollow', $this->replace );
|
35 |
+
WPSEO_Meta::replace_meta( '_su_meta_robots_noindex', WPSEO_Meta::$meta_prefix . 'meta-robots-nofollow', $this->replace );
|
36 |
+
WPSEO_Meta::replace_meta( '_su_og_title', WPSEO_Meta::$meta_prefix . 'opengraph-title', $this->replace );
|
37 |
+
WPSEO_Meta::replace_meta( '_su_og_description', WPSEO_Meta::$meta_prefix . 'opengraph-description', $this->replace );
|
38 |
+
WPSEO_Meta::replace_meta( '_su_og_image', WPSEO_Meta::$meta_prefix . 'opengraph-image', $this->replace );
|
39 |
+
WPSEO_Meta::replace_meta( '_su_title', WPSEO_Meta::$meta_prefix . 'title', $this->replace );
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Removes all leftover SEO ultimate data from the database.
|
44 |
+
*
|
45 |
+
* @return void
|
46 |
+
*/
|
47 |
+
private function cleanup() {
|
48 |
+
if ( ! $this->replace ) {
|
49 |
+
return;
|
50 |
+
}
|
51 |
+
global $wpdb;
|
52 |
+
$wpdb->query( "DELETE FROM {$wpdb->prefix}postmeta WHERE meta_key LIKE '_su_%'" );
|
53 |
+
}
|
54 |
+
}
|
admin/class-import-woothemes-seo.php
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Import\External
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Import_WooThemes_SEO
|
8 |
+
*
|
9 |
+
* Class with functionality to import Yoast SEO settings from WooThemes SEO
|
10 |
+
*/
|
11 |
+
class WPSEO_Import_WooThemes_SEO extends WPSEO_Import_External {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Class constructor
|
15 |
+
*
|
16 |
+
* @param boolean $replace Boolean replace switch.
|
17 |
+
*/
|
18 |
+
public function __construct( $replace = false ) {
|
19 |
+
parent::__construct( $replace );
|
20 |
+
|
21 |
+
$this->success = true;
|
22 |
+
$this->import_home();
|
23 |
+
$this->import_option( 'seo_woo_single_layout', 'post' );
|
24 |
+
$this->import_option( 'seo_woo_page_layout', 'page' );
|
25 |
+
$this->import_archive_option();
|
26 |
+
$this->import_custom_values( 'seo_woo_meta_home_desc', 'metadesc-home-wpseo' );
|
27 |
+
$this->import_custom_values( 'seo_woo_meta_home_key', 'metakey-home-wpseo' );
|
28 |
+
$this->import_metas();
|
29 |
+
|
30 |
+
update_option( 'wpseo_titles', $this->options );
|
31 |
+
|
32 |
+
$this->set_msg( __( 'WooThemes SEO framework settings & data successfully imported.', 'wordpress-seo' ) );
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Holds the WPSEO Title Options
|
37 |
+
*
|
38 |
+
* @var array
|
39 |
+
*/
|
40 |
+
private $options;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Import options.
|
44 |
+
*
|
45 |
+
* @param string $option Option key.
|
46 |
+
* @param string $post_type Post type name to import for.
|
47 |
+
*/
|
48 |
+
private function import_option( $option, $post_type ) {
|
49 |
+
switch ( get_option( $option ) ) {
|
50 |
+
case 'a':
|
51 |
+
$this->options[ 'title-' . $post_type ] = '%%title%% %%sep%% %%sitename%%';
|
52 |
+
break;
|
53 |
+
case 'b':
|
54 |
+
$this->options[ 'title-' . $post_type ] = '%%title%%';
|
55 |
+
break;
|
56 |
+
case 'c':
|
57 |
+
$this->options[ 'title-' . $post_type ] = '%%sitename%% %%sep%% %%title%%';
|
58 |
+
break;
|
59 |
+
case 'd':
|
60 |
+
$this->options[ 'title-' . $post_type ] = '%%title%% %%sep%% %%sitedesc%%';
|
61 |
+
break;
|
62 |
+
case 'e':
|
63 |
+
$this->options[ 'title-' . $post_type ] = '%%sitename%% %%sep%% %%title%% %%sep%% %%sitedesc%%';
|
64 |
+
break;
|
65 |
+
}
|
66 |
+
$this->perhaps_delete( $option );
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Import the archive layout for all taxonomies
|
71 |
+
*/
|
72 |
+
private function import_archive_option() {
|
73 |
+
$reinstate_replace = false;
|
74 |
+
if ( $this->replace ) {
|
75 |
+
$this->replace = false;
|
76 |
+
$reinstate_replace = true;
|
77 |
+
}
|
78 |
+
$taxonomies = get_taxonomies( array( 'public' => true ), 'names' );
|
79 |
+
if ( is_array( $taxonomies ) && $taxonomies !== array() ) {
|
80 |
+
foreach ( $taxonomies as $tax ) {
|
81 |
+
$this->import_option( 'seo_woo_archive_layout', 'tax-' . $tax );
|
82 |
+
}
|
83 |
+
}
|
84 |
+
if ( $reinstate_replace ) {
|
85 |
+
$this->replace = true;
|
86 |
+
$this->perhaps_delete( 'seo_woo_archive_layout' );
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Import custom descriptions and meta keys
|
92 |
+
*
|
93 |
+
* @param string $option Option key.
|
94 |
+
* @param string $key Internal key to import over.
|
95 |
+
*/
|
96 |
+
private function import_custom_values( $option, $key ) {
|
97 |
+
// Import the custom homepage description.
|
98 |
+
if ( 'c' === get_option( $option ) ) {
|
99 |
+
$this->options[ $key ] = get_option( $option . '_custom' );
|
100 |
+
}
|
101 |
+
$this->perhaps_delete( $option );
|
102 |
+
$this->perhaps_delete( $option . '_custom' );
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Imports the WooThemes SEO homepage settings
|
107 |
+
*/
|
108 |
+
private function import_home() {
|
109 |
+
switch ( get_option( 'seo_woo_home_layout' ) ) {
|
110 |
+
case 'a':
|
111 |
+
$this->options['title-home-wpseo'] = '%%sitename%% %%sep%% %%sitedesc%%';
|
112 |
+
break;
|
113 |
+
case 'b':
|
114 |
+
$this->options['title-home-wpseo'] = '%%sitename%% ' . get_option( 'seo_woo_paged_var' ) . ' %%pagenum%%';
|
115 |
+
break;
|
116 |
+
case 'c':
|
117 |
+
$this->options['title-home-wpseo'] = '%%sitedesc%%';
|
118 |
+
break;
|
119 |
+
}
|
120 |
+
$this->perhaps_delete( 'seo_woo_home_layout' );
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Import meta values if they're applicable
|
125 |
+
*/
|
126 |
+
private function import_metas() {
|
127 |
+
WPSEO_Meta::replace_meta( 'seo_follow', WPSEO_Meta::$meta_prefix . 'meta-robots-nofollow', $this->replace );
|
128 |
+
WPSEO_Meta::replace_meta( 'seo_noindex', WPSEO_Meta::$meta_prefix . 'meta-robots-noindex', $this->replace );
|
129 |
+
|
130 |
+
// If WooSEO is set to use the Woo titles, import those.
|
131 |
+
if ( 'true' == get_option( 'seo_woo_wp_title' ) ) {
|
132 |
+
WPSEO_Meta::replace_meta( 'seo_title', WPSEO_Meta::$meta_prefix . 'title', $this->replace );
|
133 |
+
}
|
134 |
+
|
135 |
+
// If WooSEO is set to use the Woo meta descriptions, import those.
|
136 |
+
if ( 'b' === get_option( 'seo_woo_meta_single_desc' ) ) {
|
137 |
+
WPSEO_Meta::replace_meta( 'seo_description', WPSEO_Meta::$meta_prefix . 'metadesc', $this->replace );
|
138 |
+
}
|
139 |
+
|
140 |
+
// If WooSEO is set to use the Woo meta keywords, import those.
|
141 |
+
if ( 'b' === get_option( 'seo_woo_meta_single_key' ) ) {
|
142 |
+
WPSEO_Meta::replace_meta( 'seo_keywords', WPSEO_Meta::$meta_prefix . 'metakeywords', $this->replace );
|
143 |
+
}
|
144 |
+
|
145 |
+
foreach ( array( 'seo_woo_wp_title', 'seo_woo_meta_single_desc', 'seo_woo_meta_single_key' ) as $option ) {
|
146 |
+
$this->perhaps_delete( $option );
|
147 |
+
}
|
148 |
+
}
|
149 |
+
}
|
admin/class-import-wpseo.php
ADDED
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Import\External
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Import_WPSEO
|
8 |
+
*
|
9 |
+
* Class with functionality to import Yoast SEO settings from wpSEO
|
10 |
+
*/
|
11 |
+
class WPSEO_Import_WPSEO extends WPSEO_Import_External {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Import wpSEO settings
|
15 |
+
*
|
16 |
+
* @param boolean $replace Boolean replace switch.
|
17 |
+
*/
|
18 |
+
public function __construct( $replace = false ) {
|
19 |
+
parent::__construct( $replace );
|
20 |
+
|
21 |
+
$this->import_post_metas();
|
22 |
+
$this->import_taxonomy_metas();
|
23 |
+
|
24 |
+
$this->success = true;
|
25 |
+
$this->set_msg(
|
26 |
+
sprintf(
|
27 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
28 |
+
__( 'wpSEO data successfully imported. Would you like to %1$sdisable the wpSEO plugin%2$s?', 'wordpress-seo' ),
|
29 |
+
'<a href="' . esc_url( admin_url( 'admin.php?page=wpseo_tools&tool=import-export&deactivate_wpseo=1#top#import-seo' ) ) . '">',
|
30 |
+
'</a>'
|
31 |
+
)
|
32 |
+
);
|
33 |
+
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Import the post meta values to Yoast SEO by replacing the wpSEO fields by Yoast SEO fields
|
38 |
+
*/
|
39 |
+
private function import_post_metas() {
|
40 |
+
WPSEO_Meta::replace_meta( '_wpseo_edit_title', WPSEO_Meta::$meta_prefix . 'title', $this->replace );
|
41 |
+
WPSEO_Meta::replace_meta( '_wpseo_edit_description', WPSEO_Meta::$meta_prefix . 'metadesc', $this->replace );
|
42 |
+
WPSEO_Meta::replace_meta( '_wpseo_edit_keywords', WPSEO_Meta::$meta_prefix . 'keywords', $this->replace );
|
43 |
+
WPSEO_Meta::replace_meta( '_wpseo_edit_canonical', WPSEO_Meta::$meta_prefix . 'canonical', $this->replace );
|
44 |
+
|
45 |
+
$this->import_post_robots();
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Importing the robot values from WPSEO plugin. These have to be converted to the Yoast format.
|
50 |
+
*/
|
51 |
+
private function import_post_robots() {
|
52 |
+
$query_posts = new WP_Query( 'post_type=any&meta_key=_wpseo_edit_robots&order=ASC&fields=ids&nopaging=true' );
|
53 |
+
|
54 |
+
if ( ! empty( $query_posts->posts ) ) {
|
55 |
+
foreach ( array_values( $query_posts->posts ) as $post_id ) {
|
56 |
+
$this->import_post_robot( $post_id );
|
57 |
+
}
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Getting the wpSEO robot value and map this to Yoast SEO values.
|
63 |
+
*
|
64 |
+
* @param integer $post_id The post id of the current post.
|
65 |
+
*/
|
66 |
+
private function import_post_robot( $post_id ) {
|
67 |
+
$wpseo_robots = get_post_meta( $post_id, '_wpseo_edit_robots', true );
|
68 |
+
$robot_value = $this->get_robot_value( $wpseo_robots );
|
69 |
+
|
70 |
+
// Saving the new meta values for Yoast SEO.
|
71 |
+
WPSEO_Meta::set_value( $robot_value['index'], 'meta-robots-noindex', $post_id );
|
72 |
+
WPSEO_Meta::set_value( $robot_value['follow'], 'meta-robots-nofollow', $post_id );
|
73 |
+
|
74 |
+
$this->delete_post_robot( $post_id );
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Delete the wpSEO robot values, because they aren't needed anymore.
|
79 |
+
*
|
80 |
+
* @param integer $post_id The post id of the current post.
|
81 |
+
*/
|
82 |
+
private function delete_post_robot( $post_id ) {
|
83 |
+
if ( $this->replace ) {
|
84 |
+
delete_post_meta( $post_id, '_wpseo_edit_robots' );
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Import the taxonomy metas from wpSEO
|
90 |
+
*/
|
91 |
+
private function import_taxonomy_metas() {
|
92 |
+
$terms = get_terms( get_taxonomies(), array( 'hide_empty' => false ) );
|
93 |
+
$tax_meta = get_option( 'wpseo_taxonomy_meta' );
|
94 |
+
|
95 |
+
foreach ( $terms as $term ) {
|
96 |
+
$this->import_taxonomy_description( $tax_meta, $term->taxonomy, $term->term_id );
|
97 |
+
$this->import_taxonomy_robots( $tax_meta, $term->taxonomy, $term->term_id );
|
98 |
+
$this->delete_taxonomy_metas( $term->taxonomy, $term->term_id );
|
99 |
+
}
|
100 |
+
|
101 |
+
update_option( 'wpseo_taxonomy_meta', $tax_meta );
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Import the meta description to Yoast SEO
|
106 |
+
*
|
107 |
+
* @param array $tax_meta The array with the current metadata.
|
108 |
+
* @param string $taxonomy String with the name of the taxonomy.
|
109 |
+
* @param string $term_id The ID of the current term.
|
110 |
+
*/
|
111 |
+
private function import_taxonomy_description( & $tax_meta, $taxonomy, $term_id ) {
|
112 |
+
$description = get_option( 'wpseo_' . $taxonomy . '_' . $term_id, false );
|
113 |
+
if ( $description !== false ) {
|
114 |
+
// Import description.
|
115 |
+
$tax_meta[ $taxonomy ][ $term_id ]['wpseo_desc'] = $description;
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Import the robot value to Yoast SEO
|
121 |
+
*
|
122 |
+
* @param array $tax_meta The array with the current metadata.
|
123 |
+
* @param string $taxonomy String with the name of the taxonomy.
|
124 |
+
* @param string $term_id The ID of the current term.
|
125 |
+
*/
|
126 |
+
private function import_taxonomy_robots( & $tax_meta, $taxonomy, $term_id ) {
|
127 |
+
$wpseo_robots = get_option( 'wpseo_' . $taxonomy . '_' . $term_id . '_robots', false );
|
128 |
+
if ( $wpseo_robots !== false ) {
|
129 |
+
// The value 1, 2 and 6 are the index values in wpSEO.
|
130 |
+
$new_robot_value = ( in_array( (int) $wpseo_robots, array( 1, 2, 6 ), true ) ) ? 'index' : 'noindex';
|
131 |
+
|
132 |
+
$tax_meta[ $taxonomy ][ $term_id ]['wpseo_noindex'] = $new_robot_value;
|
133 |
+
}
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Delete the wpSEO taxonomy meta data.
|
138 |
+
*
|
139 |
+
* @param string $taxonomy String with the name of the taxonomy.
|
140 |
+
* @param string $term_id The ID of the current term.
|
141 |
+
*/
|
142 |
+
private function delete_taxonomy_metas( $taxonomy, $term_id ) {
|
143 |
+
if ( $this->replace ) {
|
144 |
+
delete_option( 'wpseo_' . $taxonomy . '_' . $term_id );
|
145 |
+
delete_option( 'wpseo_' . $taxonomy . '_' . $term_id . '_robots' );
|
146 |
+
}
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Getting the robot config by given wpSEO robots value.
|
151 |
+
*
|
152 |
+
* @param string $wpseo_robots The value in wpSEO that needs to be converted to the Yoast format.
|
153 |
+
*
|
154 |
+
* @return array
|
155 |
+
*/
|
156 |
+
private function get_robot_value( $wpseo_robots ) {
|
157 |
+
static $robot_values;
|
158 |
+
|
159 |
+
if ( $robot_values === null ) {
|
160 |
+
/**
|
161 |
+
* The values 1 - 6 are the configured values from wpSEO. This array will map the values of wpSEO to our values.
|
162 |
+
*
|
163 |
+
* There are some double array like 1-6 and 3-4. The reason is they only set the index value. The follow value is
|
164 |
+
* the default we use in the cases there isn't a follow value present.
|
165 |
+
*
|
166 |
+
* @var array
|
167 |
+
*/
|
168 |
+
$robot_values = array(
|
169 |
+
// In wpSEO: index, follow.
|
170 |
+
1 => array(
|
171 |
+
'index' => 2,
|
172 |
+
'follow' => 0,
|
173 |
+
),
|
174 |
+
// In wpSEO: index, nofollow.
|
175 |
+
2 => array(
|
176 |
+
'index' => 2,
|
177 |
+
'follow' => 1,
|
178 |
+
),
|
179 |
+
// In wpSEO: noindex.
|
180 |
+
3 => array(
|
181 |
+
'index' => 1,
|
182 |
+
'follow' => 0,
|
183 |
+
),
|
184 |
+
// In wpSEO: noindex, follow.
|
185 |
+
4 => array(
|
186 |
+
'index' => 1,
|
187 |
+
'follow' => 0,
|
188 |
+
),
|
189 |
+
// In wpSEO: noindex, nofollow.
|
190 |
+
5 => array(
|
191 |
+
'index' => 1,
|
192 |
+
'follow' => 1,
|
193 |
+
),
|
194 |
+
// In wpSEO: index.
|
195 |
+
6 => array(
|
196 |
+
'index' => 2,
|
197 |
+
'follow' => 0,
|
198 |
+
),
|
199 |
+
);
|
200 |
+
}
|
201 |
+
|
202 |
+
if ( array_key_exists( $wpseo_robots, $robot_values ) ) {
|
203 |
+
return $robot_values[ $wpseo_robots ];
|
204 |
+
}
|
205 |
+
|
206 |
+
return $robot_values[1];
|
207 |
+
}
|
208 |
+
}
|
admin/class-import.php
ADDED
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Import
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Import
|
8 |
+
*
|
9 |
+
* Class with functionality to import the Yoast SEO settings
|
10 |
+
*/
|
11 |
+
class WPSEO_Import {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Message about the import
|
15 |
+
*
|
16 |
+
* @var string
|
17 |
+
*/
|
18 |
+
public $msg = '';
|
19 |
+
|
20 |
+
/** @var bool $success If import was a success flag. */
|
21 |
+
public $success = false;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var array
|
25 |
+
*/
|
26 |
+
private $file;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var string
|
30 |
+
*/
|
31 |
+
private $filename;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @var string
|
35 |
+
*/
|
36 |
+
private $old_wpseo_version = null;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @var string
|
40 |
+
*/
|
41 |
+
private $path;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* @var array
|
45 |
+
*/
|
46 |
+
private $upload_dir;
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Class constructor
|
50 |
+
*/
|
51 |
+
public function __construct() {
|
52 |
+
if ( ! $this->handle_upload() ) {
|
53 |
+
return;
|
54 |
+
}
|
55 |
+
|
56 |
+
$this->determine_path();
|
57 |
+
|
58 |
+
if ( ! $this->unzip_file() ) {
|
59 |
+
$this->clean_up();
|
60 |
+
|
61 |
+
return;
|
62 |
+
}
|
63 |
+
|
64 |
+
$this->parse_options();
|
65 |
+
|
66 |
+
$this->clean_up();
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Handle the file upload
|
71 |
+
*
|
72 |
+
* @return boolean
|
73 |
+
*/
|
74 |
+
private function handle_upload() {
|
75 |
+
$overrides = array( 'mimes' => array( 'zip' => 'application/zip' ) ); // Explicitly allow zip in multisite.
|
76 |
+
$this->file = wp_handle_upload( $_FILES['settings_import_file'], $overrides );
|
77 |
+
|
78 |
+
if ( is_wp_error( $this->file ) ) {
|
79 |
+
$this->msg = __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . $this->file->get_error_message();
|
80 |
+
|
81 |
+
return false;
|
82 |
+
}
|
83 |
+
|
84 |
+
if ( is_array( $this->file ) && isset( $this->file['error'] ) ) {
|
85 |
+
$this->msg = __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . $this->file['error'];
|
86 |
+
|
87 |
+
return false;
|
88 |
+
}
|
89 |
+
|
90 |
+
if ( ! isset( $this->file['file'] ) ) {
|
91 |
+
$this->msg = __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . __( 'Upload failed.', 'wordpress-seo' );
|
92 |
+
|
93 |
+
return false;
|
94 |
+
}
|
95 |
+
|
96 |
+
return true;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Determine the path to the import file
|
101 |
+
*/
|
102 |
+
private function determine_path() {
|
103 |
+
$this->upload_dir = wp_upload_dir();
|
104 |
+
|
105 |
+
if ( ! defined( 'DIRECTORY_SEPARATOR' ) ) {
|
106 |
+
define( 'DIRECTORY_SEPARATOR', '/' );
|
107 |
+
}
|
108 |
+
$this->path = $this->upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'wpseo-import' . DIRECTORY_SEPARATOR;
|
109 |
+
|
110 |
+
if ( ! isset( $GLOBALS['wp_filesystem'] ) || ! is_object( $GLOBALS['wp_filesystem'] ) ) {
|
111 |
+
WP_Filesystem();
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Unzip the file
|
117 |
+
*
|
118 |
+
* @return boolean
|
119 |
+
*/
|
120 |
+
private function unzip_file() {
|
121 |
+
$unzipped = unzip_file( $this->file['file'], $this->path );
|
122 |
+
$msg_base = __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ';
|
123 |
+
|
124 |
+
if ( is_wp_error( $unzipped ) ) {
|
125 |
+
/* translators: %s expands to an error message. */
|
126 |
+
$this->msg = $msg_base . sprintf( __( 'Unzipping failed with error "%s".', 'wordpress-seo' ), $unzipped->get_error_message() );
|
127 |
+
|
128 |
+
return false;
|
129 |
+
}
|
130 |
+
|
131 |
+
$this->filename = $this->path . 'settings.ini';
|
132 |
+
if ( ! is_file( $this->filename ) || ! is_readable( $this->filename ) ) {
|
133 |
+
$this->msg = $msg_base . __( 'Unzipping failed - file settings.ini not found.', 'wordpress-seo' );
|
134 |
+
|
135 |
+
return false;
|
136 |
+
}
|
137 |
+
|
138 |
+
return true;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Parse the option file
|
143 |
+
*/
|
144 |
+
private function parse_options() {
|
145 |
+
/*
|
146 |
+
* Implemented INI_SCANNER_RAW to make sure variables aren't parsed.
|
147 |
+
*
|
148 |
+
* http://php.net/manual/en/function.parse-ini-file.php#99943
|
149 |
+
*/
|
150 |
+
$options = parse_ini_file( $this->filename, true, INI_SCANNER_RAW );
|
151 |
+
|
152 |
+
if ( is_array( $options ) && $options !== array() ) {
|
153 |
+
if ( isset( $options['wpseo']['version'] ) && $options['wpseo']['version'] !== '' ) {
|
154 |
+
$this->old_wpseo_version = $options['wpseo']['version'];
|
155 |
+
}
|
156 |
+
foreach ( $options as $name => $opt_group ) {
|
157 |
+
$this->parse_option_group( $name, $opt_group, $options );
|
158 |
+
}
|
159 |
+
$this->msg = __( 'Settings successfully imported.', 'wordpress-seo' );
|
160 |
+
$this->success = true;
|
161 |
+
}
|
162 |
+
else {
|
163 |
+
$this->msg = __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . __( 'No settings found in file.', 'wordpress-seo' );
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Parse the option group and import it
|
169 |
+
*
|
170 |
+
* @param string $name Name string.
|
171 |
+
* @param array $opt_group Option group data.
|
172 |
+
* @param array $options Options data.
|
173 |
+
*/
|
174 |
+
private function parse_option_group( $name, $opt_group, $options ) {
|
175 |
+
if ( $name === 'wpseo_taxonomy_meta' ) {
|
176 |
+
$opt_group = json_decode( urldecode( $opt_group['wpseo_taxonomy_meta'] ), true );
|
177 |
+
}
|
178 |
+
|
179 |
+
// Make sure that the imported options are cleaned/converted on import.
|
180 |
+
$option_instance = WPSEO_Options::get_option_instance( $name );
|
181 |
+
if ( is_object( $option_instance ) && method_exists( $option_instance, 'import' ) ) {
|
182 |
+
$option_instance->import( $opt_group, $this->old_wpseo_version, $options );
|
183 |
+
}
|
184 |
+
elseif ( WP_DEBUG === true || ( defined( 'WPSEO_DEBUG' ) && WPSEO_DEBUG === true ) ) {
|
185 |
+
/* translators: %s expands to the name of an outdated setting. */
|
186 |
+
$this->msg = sprintf( __( 'Setting "%s" is no longer used and has been discarded.', 'wordpress-seo' ), $name );
|
187 |
+
}
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Remove the files
|
192 |
+
*/
|
193 |
+
private function clean_up() {
|
194 |
+
if ( file_exists( $this->filename ) && is_writable( $this->filename ) ) {
|
195 |
+
unlink( $this->filename );
|
196 |
+
}
|
197 |
+
if ( ! empty( $this->file['file'] ) && file_exists( $this->file['file'] ) && is_writable( $this->file['file'] ) ) {
|
198 |
+
unlink( $this->file['file'] );
|
199 |
+
}
|
200 |
+
if ( file_exists( $this->path ) && is_writable( $this->path ) ) {
|
201 |
+
$wp_file = new WP_Filesystem_Direct( $this->path );
|
202 |
+
$wp_file->rmdir( $this->path, true );
|
203 |
+
}
|
204 |
+
}
|
205 |
+
}
|
admin/class-license-page-manager.php
ADDED
@@ -0,0 +1,209 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the values for a single Yoast Premium extension plugin.
|
8 |
+
*/
|
9 |
+
class WPSEO_License_Page_Manager implements WPSEO_WordPress_Integration {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string Version number for License Page Manager.
|
13 |
+
*/
|
14 |
+
const VERSION_LEGACY = '1';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string Version number for License Page Manager.
|
18 |
+
*/
|
19 |
+
const VERSION_BACKWARDS_COMPATIBILITY = '2';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Registers all hooks to WordPress.
|
23 |
+
*/
|
24 |
+
public function register_hooks() {
|
25 |
+
add_filter( 'http_response', array( $this, 'handle_response' ), 10, 3 );
|
26 |
+
|
27 |
+
if ( $this->get_version() === self::VERSION_BACKWARDS_COMPATIBILITY ) {
|
28 |
+
add_filter( 'yoast-license-valid', '__return_true' );
|
29 |
+
add_filter( 'yoast-show-license-notice', '__return_false' );
|
30 |
+
add_action( 'admin_init', array( $this, 'validate_extensions' ), 15 );
|
31 |
+
}
|
32 |
+
else {
|
33 |
+
add_action( 'admin_init', array( $this, 'remove_faulty_notifications' ), 15 );
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Validates the extensions and show a notice for the invalid extensions.
|
39 |
+
*/
|
40 |
+
public function validate_extensions() {
|
41 |
+
|
42 |
+
if ( filter_input( INPUT_GET, 'page' ) === WPSEO_Admin::PAGE_IDENTIFIER ) {
|
43 |
+
/**
|
44 |
+
* Filter: 'yoast-active-extensions' - Collects all active extensions. This hook is implemented in the
|
45 |
+
* license manager.
|
46 |
+
*
|
47 |
+
* @api array $extensions The array with extensions.
|
48 |
+
*/
|
49 |
+
apply_filters( 'yoast-active-extensions', array() );
|
50 |
+
}
|
51 |
+
|
52 |
+
$extension_list = new WPSEO_Extensions();
|
53 |
+
$extensions = $extension_list->get();
|
54 |
+
|
55 |
+
$notification_center = Yoast_Notification_Center::get();
|
56 |
+
|
57 |
+
foreach ( $extensions as $product_name ) {
|
58 |
+
$notification = $this->create_notification( $product_name );
|
59 |
+
|
60 |
+
// Add a notification when the installed plugin isn't activated in My Yoast.
|
61 |
+
if ( $extension_list->is_installed( $product_name ) && ! $extension_list->is_valid( $product_name ) ) {
|
62 |
+
$notification_center->add_notification( $notification );
|
63 |
+
|
64 |
+
continue;
|
65 |
+
}
|
66 |
+
|
67 |
+
$notification_center->remove_notification( $notification );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Removes the faulty set notifications.
|
73 |
+
*/
|
74 |
+
public function remove_faulty_notifications() {
|
75 |
+
$extension_list = new WPSEO_Extensions();
|
76 |
+
$extensions = $extension_list->get();
|
77 |
+
|
78 |
+
$notification_center = Yoast_Notification_Center::get();
|
79 |
+
|
80 |
+
foreach ( $extensions as $product_name ) {
|
81 |
+
$notification = $this->create_notification( $product_name );
|
82 |
+
$notification_center->remove_notification( $notification );
|
83 |
+
}
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Handles the response.
|
88 |
+
*
|
89 |
+
* @param array $response HTTP response.
|
90 |
+
* @param array $request_arguments HTTP request arguments. Unused.
|
91 |
+
* @param string $url The request URL.
|
92 |
+
*
|
93 |
+
* @return array The response array.
|
94 |
+
*/
|
95 |
+
public function handle_response( array $response, $request_arguments, $url ) {
|
96 |
+
$response_code = wp_remote_retrieve_response_code( $response );
|
97 |
+
|
98 |
+
if ( $response_code === 200 && $this->is_expected_endpoint( $url ) ) {
|
99 |
+
$response_data = $this->parse_response( $response );
|
100 |
+
$this->detect_version( $response_data );
|
101 |
+
}
|
102 |
+
|
103 |
+
return $response;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Returns the license page to use based on the version number.
|
108 |
+
*
|
109 |
+
* @return string The page to use.
|
110 |
+
*/
|
111 |
+
public function get_license_page() {
|
112 |
+
return 'licenses';
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Returns the version number of the license server.
|
117 |
+
*
|
118 |
+
* @return int The version number
|
119 |
+
*/
|
120 |
+
protected function get_version() {
|
121 |
+
return get_option( $this->get_option_name(), self::VERSION_BACKWARDS_COMPATIBILITY );
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Returns the option name.
|
126 |
+
*
|
127 |
+
* @return string The option name.
|
128 |
+
*/
|
129 |
+
protected function get_option_name() {
|
130 |
+
return 'wpseo_license_server_version';
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Sets the version when there is a value in the response.
|
135 |
+
*
|
136 |
+
* @param array $response The response to extract the version from.
|
137 |
+
*/
|
138 |
+
protected function detect_version( $response ) {
|
139 |
+
if ( ! empty( $response['serverVersion'] ) ) {
|
140 |
+
$this->set_version( $response['serverVersion'] );
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Sets the version.
|
146 |
+
*
|
147 |
+
* @param string $server_version The version number to save.
|
148 |
+
*/
|
149 |
+
protected function set_version( $server_version ) {
|
150 |
+
update_option( $this->get_option_name(), $server_version );
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Parses the response by getting its body and do a unserialize of it.
|
155 |
+
*
|
156 |
+
* @param array $response The response to parse.
|
157 |
+
*
|
158 |
+
* @return mixed|string|false The parsed response.
|
159 |
+
*/
|
160 |
+
protected function parse_response( $response ) {
|
161 |
+
$response = json_decode( wp_remote_retrieve_body( $response ), true );
|
162 |
+
$response = maybe_unserialize( $response );
|
163 |
+
|
164 |
+
return $response;
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Checks if the given url matches the expected endpoint.
|
169 |
+
*
|
170 |
+
* @param string $url The url to check.
|
171 |
+
*
|
172 |
+
* @return bool True when url matches the endpoint.
|
173 |
+
*/
|
174 |
+
protected function is_expected_endpoint( $url ) {
|
175 |
+
$url_parts = wp_parse_url( $url );
|
176 |
+
|
177 |
+
$is_yoast_com = ( in_array( $url_parts['host'], array( 'yoast.com', 'my.yoast.com' ), true ) );
|
178 |
+
$is_edd_api = ( isset( $url_parts['path'] ) && $url_parts['path'] === '/edd-sl-api' );
|
179 |
+
|
180 |
+
return $is_yoast_com && $is_edd_api;
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Creates an instance of Yoast_Notification
|
185 |
+
*
|
186 |
+
* @param string $product_name The product to create the notification for.
|
187 |
+
*
|
188 |
+
* @return Yoast_Notification The created notification.
|
189 |
+
*/
|
190 |
+
protected function create_notification( $product_name ) {
|
191 |
+
$notification_options = array(
|
192 |
+
'type' => Yoast_Notification::ERROR,
|
193 |
+
'id' => 'wpseo-dismiss-' . sanitize_title_with_dashes( $product_name, null, 'save' ),
|
194 |
+
'capabilities' => 'wpseo_manage_options',
|
195 |
+
);
|
196 |
+
|
197 |
+
$notification = new Yoast_Notification(
|
198 |
+
sprintf(
|
199 |
+
/* translators: %1$s expands to the product name. %2$s expands to a link to My Yoast */
|
200 |
+
__( 'You are not receiving updates or support! Fix this problem by adding this site and enabling %1$s for it in %2$s.', 'wordpress-seo' ),
|
201 |
+
$product_name,
|
202 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/13j' ) . '" target="_blank">My Yoast</a>'
|
203 |
+
),
|
204 |
+
$notification_options
|
205 |
+
);
|
206 |
+
|
207 |
+
return $notification;
|
208 |
+
}
|
209 |
+
}
|
admin/class-meta-columns.php
ADDED
@@ -0,0 +1,752 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Meta_Columns
|
8 |
+
*/
|
9 |
+
class WPSEO_Meta_Columns {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WPSEO_Metabox_Analysis_SEO
|
13 |
+
*/
|
14 |
+
private $analysis_seo;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var WPSEO_Metabox_Analysis_Readability
|
18 |
+
*/
|
19 |
+
private $analysis_readability;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* When page analysis is enabled, just initialize the hooks.
|
23 |
+
*/
|
24 |
+
public function __construct() {
|
25 |
+
if ( apply_filters( 'wpseo_use_page_analysis', true ) === true ) {
|
26 |
+
add_action( 'admin_init', array( $this, 'setup_hooks' ) );
|
27 |
+
}
|
28 |
+
|
29 |
+
$this->analysis_seo = new WPSEO_Metabox_Analysis_SEO();
|
30 |
+
$this->analysis_readability = new WPSEO_Metabox_Analysis_Readability();
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Sets up up the hooks.
|
35 |
+
*/
|
36 |
+
public function setup_hooks() {
|
37 |
+
$this->set_post_type_hooks();
|
38 |
+
|
39 |
+
if ( $this->analysis_seo->is_enabled() ) {
|
40 |
+
add_action( 'restrict_manage_posts', array( $this, 'posts_filter_dropdown' ) );
|
41 |
+
}
|
42 |
+
|
43 |
+
if ( $this->analysis_readability->is_enabled() ) {
|
44 |
+
add_action( 'restrict_manage_posts', array( $this, 'posts_filter_dropdown_readability' ) );
|
45 |
+
}
|
46 |
+
|
47 |
+
add_filter( 'request', array( $this, 'column_sort_orderby' ) );
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Adds the column headings for the SEO plugin for edit posts / pages overview.
|
52 |
+
*
|
53 |
+
* @param array $columns Already existing columns.
|
54 |
+
*
|
55 |
+
* @return array Array containing the column headings.
|
56 |
+
*/
|
57 |
+
public function column_heading( $columns ) {
|
58 |
+
if ( $this->display_metabox() === false ) {
|
59 |
+
return $columns;
|
60 |
+
}
|
61 |
+
|
62 |
+
$added_columns = array();
|
63 |
+
|
64 |
+
if ( $this->analysis_seo->is_enabled() ) {
|
65 |
+
$added_columns['wpseo-score'] = '<span class="yoast-tooltip yoast-tooltip-n yoast-tooltip-alt" data-label="' . esc_attr__( 'SEO score', 'wordpress-seo' ) . '"><span class="yoast-column-seo-score yoast-column-header-has-tooltip"><span class="screen-reader-text">' . __( 'SEO score', 'wordpress-seo' ) . '</span></span></span>';
|
66 |
+
}
|
67 |
+
|
68 |
+
if ( $this->analysis_readability->is_enabled() ) {
|
69 |
+
$added_columns['wpseo-score-readability'] = '<span class="yoast-tooltip yoast-tooltip-n yoast-tooltip-alt" data-label="' . esc_attr__( 'Readability score', 'wordpress-seo' ) . '"><span class="yoast-column-readability yoast-column-header-has-tooltip"><span class="screen-reader-text">' . __( 'Readability score', 'wordpress-seo' ) . '</span></span></span>';
|
70 |
+
}
|
71 |
+
|
72 |
+
$added_columns['wpseo-title'] = __( 'SEO Title', 'wordpress-seo' );
|
73 |
+
$added_columns['wpseo-metadesc'] = __( 'Meta Desc.', 'wordpress-seo' );
|
74 |
+
|
75 |
+
if ( $this->analysis_seo->is_enabled() ) {
|
76 |
+
$added_columns['wpseo-focuskw'] = __( 'Focus KW', 'wordpress-seo' );
|
77 |
+
}
|
78 |
+
|
79 |
+
return array_merge( $columns, $added_columns );
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Displays the column content for the given column.
|
84 |
+
*
|
85 |
+
* @param string $column_name Column to display the content for.
|
86 |
+
* @param int $post_id Post to display the column content for.
|
87 |
+
*/
|
88 |
+
public function column_content( $column_name, $post_id ) {
|
89 |
+
if ( $this->display_metabox() === false ) {
|
90 |
+
return;
|
91 |
+
}
|
92 |
+
|
93 |
+
switch ( $column_name ) {
|
94 |
+
case 'wpseo-score':
|
95 |
+
echo $this->parse_column_score( $post_id );
|
96 |
+
return;
|
97 |
+
|
98 |
+
case 'wpseo-score-readability':
|
99 |
+
echo $this->parse_column_score_readability( $post_id );
|
100 |
+
return;
|
101 |
+
|
102 |
+
case 'wpseo-title':
|
103 |
+
$post = get_post( $post_id, ARRAY_A );
|
104 |
+
$title = wpseo_replace_vars( $this->page_title( $post_id ), $post );
|
105 |
+
$title = apply_filters( 'wpseo_title', $title );
|
106 |
+
|
107 |
+
echo esc_html( $title );
|
108 |
+
return;
|
109 |
+
|
110 |
+
case 'wpseo-metadesc':
|
111 |
+
$post = get_post( $post_id, ARRAY_A );
|
112 |
+
$metadesc_val = wpseo_replace_vars( WPSEO_Meta::get_value( 'metadesc', $post_id ), $post );
|
113 |
+
$metadesc_val = apply_filters( 'wpseo_metadesc', $metadesc_val );
|
114 |
+
|
115 |
+
if ( '' === $metadesc_val ) {
|
116 |
+
echo '<span aria-hidden="true">—</span><span class="screen-reader-text">',
|
117 |
+
esc_html__( 'Meta description not set.', 'wordpress-seo' ),
|
118 |
+
'</span>';
|
119 |
+
return;
|
120 |
+
}
|
121 |
+
|
122 |
+
echo esc_html( $metadesc_val );
|
123 |
+
return;
|
124 |
+
|
125 |
+
case 'wpseo-focuskw':
|
126 |
+
$focuskw_val = WPSEO_Meta::get_value( 'focuskw', $post_id );
|
127 |
+
|
128 |
+
if ( '' === $focuskw_val ) {
|
129 |
+
echo '<span aria-hidden="true">—</span><span class="screen-reader-text">',
|
130 |
+
esc_html__( 'Focus keyword not set.', 'wordpress-seo' ),
|
131 |
+
'</span>';
|
132 |
+
return;
|
133 |
+
}
|
134 |
+
|
135 |
+
echo esc_html( $focuskw_val );
|
136 |
+
return;
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Indicates which of the SEO columns are sortable.
|
142 |
+
*
|
143 |
+
* @param array $columns Appended with their orderby variable.
|
144 |
+
*
|
145 |
+
* @return array Array containing the sortable columns.
|
146 |
+
*/
|
147 |
+
public function column_sort( $columns ) {
|
148 |
+
if ( $this->display_metabox() === false ) {
|
149 |
+
return $columns;
|
150 |
+
}
|
151 |
+
|
152 |
+
$columns['wpseo-metadesc'] = 'wpseo-metadesc';
|
153 |
+
|
154 |
+
if ( $this->analysis_seo->is_enabled() ) {
|
155 |
+
$columns['wpseo-focuskw'] = 'wpseo-focuskw';
|
156 |
+
}
|
157 |
+
|
158 |
+
return $columns;
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Hides the SEO title, meta description and focus keyword columns if the user hasn't chosen which columns to hide.
|
163 |
+
*
|
164 |
+
* @param array|false $result The hidden columns.
|
165 |
+
* @param string $option The option name used to set which columns should be hidden.
|
166 |
+
* @param WP_User $user The User.
|
167 |
+
*
|
168 |
+
* @return array $result Array containing the columns to hide.
|
169 |
+
*/
|
170 |
+
public function column_hidden( $result, $option, $user ) {
|
171 |
+
global $wpdb;
|
172 |
+
|
173 |
+
if ( $user->has_prop( $wpdb->get_blog_prefix() . $option ) || $user->has_prop( $option ) ) {
|
174 |
+
return $result;
|
175 |
+
}
|
176 |
+
|
177 |
+
if ( ! is_array( $result ) ) {
|
178 |
+
$result = array();
|
179 |
+
}
|
180 |
+
|
181 |
+
array_push( $result, 'wpseo-title', 'wpseo-metadesc' );
|
182 |
+
|
183 |
+
if ( $this->analysis_seo->is_enabled() ) {
|
184 |
+
array_push( $result, 'wpseo-focuskw' );
|
185 |
+
}
|
186 |
+
|
187 |
+
return $result;
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Adds a dropdown that allows filtering on the posts SEO Quality.
|
192 |
+
*/
|
193 |
+
public function posts_filter_dropdown() {
|
194 |
+
if ( ! $this->can_display_filter() ) {
|
195 |
+
return;
|
196 |
+
}
|
197 |
+
|
198 |
+
$ranks = WPSEO_Rank::get_all_ranks();
|
199 |
+
|
200 |
+
echo '<label class="screen-reader-text" for="wpseo-filter">' . esc_html__( 'Filter by SEO Score', 'wordpress-seo' ) . '</label>';
|
201 |
+
echo '<select name="seo_filter" id="wpseo-filter">';
|
202 |
+
|
203 |
+
echo $this->generate_option( '', __( 'All SEO Scores', 'wordpress-seo' ) );
|
204 |
+
|
205 |
+
foreach ( $ranks as $rank ) {
|
206 |
+
$selected = selected( $this->get_current_seo_filter(), $rank->get_rank(), false );
|
207 |
+
|
208 |
+
echo $this->generate_option( $rank->get_rank(), $rank->get_drop_down_label(), $selected );
|
209 |
+
}
|
210 |
+
|
211 |
+
echo '</select>';
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Adds a dropdown that allows filtering on the posts Readability Quality.
|
216 |
+
*
|
217 |
+
* @return void
|
218 |
+
*/
|
219 |
+
public function posts_filter_dropdown_readability() {
|
220 |
+
if ( ! $this->can_display_filter() ) {
|
221 |
+
return;
|
222 |
+
}
|
223 |
+
|
224 |
+
$ranks = WPSEO_Rank::get_all_readability_ranks();
|
225 |
+
|
226 |
+
echo '<label class="screen-reader-text" for="wpseo-readability-filter">' . esc_html__( 'Filter by Readability Score', 'wordpress-seo' ) . '</label>';
|
227 |
+
echo '<select name="readability_filter" id="wpseo-readability-filter">';
|
228 |
+
|
229 |
+
echo $this->generate_option( '', __( 'All Readability Scores', 'wordpress-seo' ) );
|
230 |
+
|
231 |
+
foreach ( $ranks as $rank ) {
|
232 |
+
$selected = selected( $this->get_current_readability_filter(), $rank->get_rank(), false );
|
233 |
+
|
234 |
+
echo $this->generate_option( $rank->get_rank(), $rank->get_drop_down_readability_labels(), $selected );
|
235 |
+
}
|
236 |
+
|
237 |
+
echo '</select>';
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Generates an <option> element.
|
242 |
+
*
|
243 |
+
* @param string $value The option's value.
|
244 |
+
* @param string $label The option's label.
|
245 |
+
* @param string $selected HTML selected attribute for an option.
|
246 |
+
*
|
247 |
+
* @return string The generated <option> element.
|
248 |
+
*/
|
249 |
+
protected function generate_option( $value, $label, $selected = '' ) {
|
250 |
+
return '<option ' . $selected . ' value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>';
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* Determines the SEO score filter to be later used in the meta query, based on the passed SEO filter.
|
255 |
+
*
|
256 |
+
* @param string $seo_filter The SEO filter to use to determine what further filter to apply.
|
257 |
+
*
|
258 |
+
* @return array The SEO score filter.
|
259 |
+
*/
|
260 |
+
protected function determine_seo_filters( $seo_filter ) {
|
261 |
+
if ( $seo_filter === WPSEO_Rank::NO_FOCUS ) {
|
262 |
+
return $this->create_no_focus_keyword_filter();
|
263 |
+
}
|
264 |
+
|
265 |
+
if ( $seo_filter === WPSEO_Rank::NO_INDEX ) {
|
266 |
+
return $this->create_no_index_filter();
|
267 |
+
}
|
268 |
+
|
269 |
+
$rank = new WPSEO_Rank( $seo_filter );
|
270 |
+
|
271 |
+
return $this->create_seo_score_filter( $rank->get_starting_score(), $rank->get_end_score() );
|
272 |
+
}
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Determines the Readabilty score filter to the meta query, based on the passed Readabilty filter.
|
276 |
+
*
|
277 |
+
* @param string $readability_filter The Readability filter to use to determine what further filter to apply.
|
278 |
+
*
|
279 |
+
* @return array The Readability score filter.
|
280 |
+
*/
|
281 |
+
protected function determine_readability_filters( $readability_filter ) {
|
282 |
+
$rank = new WPSEO_Rank( $readability_filter );
|
283 |
+
|
284 |
+
return $this->create_readability_score_filter( $rank->get_starting_score(), $rank->get_end_score() );
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* Creates a keyword filter for the meta query, based on the passed Keyword filter.
|
289 |
+
*
|
290 |
+
* @param string $keyword_filter The keyword filter to use.
|
291 |
+
*
|
292 |
+
* @return array The keyword filter.
|
293 |
+
*/
|
294 |
+
protected function get_keyword_filter( $keyword_filter ) {
|
295 |
+
return array(
|
296 |
+
'post_type' => get_query_var( 'post_type', 'post' ),
|
297 |
+
'meta_key' => WPSEO_Meta::$meta_prefix . 'focuskw',
|
298 |
+
'meta_value' => sanitize_text_field( $keyword_filter ),
|
299 |
+
);
|
300 |
+
}
|
301 |
+
|
302 |
+
/**
|
303 |
+
* Determines whether the passed filter is considered to be valid.
|
304 |
+
*
|
305 |
+
* @param mixed $filter The filter to check against.
|
306 |
+
*
|
307 |
+
* @return bool Whether or not the filter is considered valid.
|
308 |
+
*/
|
309 |
+
protected function is_valid_filter( $filter ) {
|
310 |
+
return ! empty( $filter ) && is_string( $filter );
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Collects the filters and merges them into a single array.
|
315 |
+
*
|
316 |
+
* @return array Array containing all the applicable filters.
|
317 |
+
*/
|
318 |
+
protected function collect_filters() {
|
319 |
+
$active_filters = array();
|
320 |
+
|
321 |
+
$seo_filter = $this->get_current_seo_filter();
|
322 |
+
$readability_filter = $this->get_current_readability_filter();
|
323 |
+
$current_keyword_filter = $this->get_current_keyword_filter();
|
324 |
+
|
325 |
+
if ( $this->is_valid_filter( $seo_filter ) ) {
|
326 |
+
$active_filters = array_merge(
|
327 |
+
$active_filters,
|
328 |
+
$this->determine_seo_filters( $seo_filter )
|
329 |
+
);
|
330 |
+
}
|
331 |
+
|
332 |
+
if ( $this->is_valid_filter( $readability_filter ) ) {
|
333 |
+
$active_filters = array_merge(
|
334 |
+
$active_filters,
|
335 |
+
$this->determine_readability_filters( $readability_filter )
|
336 |
+
);
|
337 |
+
}
|
338 |
+
|
339 |
+
if ( $this->is_valid_filter( $current_keyword_filter ) ) {
|
340 |
+
$active_filters = array_merge(
|
341 |
+
$active_filters,
|
342 |
+
$this->get_keyword_filter( $current_keyword_filter )
|
343 |
+
);
|
344 |
+
}
|
345 |
+
|
346 |
+
return $active_filters;
|
347 |
+
}
|
348 |
+
|
349 |
+
/**
|
350 |
+
* Modify the query based on the filters that are being passed.
|
351 |
+
*
|
352 |
+
* @param array $vars Query variables that need to be modified based on the filters.
|
353 |
+
*
|
354 |
+
* @return array Array containing the meta query to use for filtering the posts overview.
|
355 |
+
*/
|
356 |
+
public function column_sort_orderby( $vars ) {
|
357 |
+
$collected_filters = $this->collect_filters();
|
358 |
+
|
359 |
+
if ( isset( $vars['orderby'] ) ) {
|
360 |
+
$vars = array_merge( $vars, $this->filter_order_by( $vars['orderby'] ) );
|
361 |
+
}
|
362 |
+
|
363 |
+
return $this->build_filter_query( $vars, $collected_filters );
|
364 |
+
}
|
365 |
+
|
366 |
+
/**
|
367 |
+
* Retrieves the meta robots query values to be used within the meta query.
|
368 |
+
*
|
369 |
+
* @return array Array containing the query parameters regarding meta robots.
|
370 |
+
*/
|
371 |
+
protected function get_meta_robots_query_values() {
|
372 |
+
return array(
|
373 |
+
'relation' => 'OR',
|
374 |
+
array(
|
375 |
+
'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
|
376 |
+
'compare' => 'NOT EXISTS',
|
377 |
+
),
|
378 |
+
array(
|
379 |
+
'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
|
380 |
+
'value' => '1',
|
381 |
+
'compare' => '!=',
|
382 |
+
),
|
383 |
+
);
|
384 |
+
}
|
385 |
+
|
386 |
+
/**
|
387 |
+
* Determines the score filters to be used. If more than one is passed, it created an AND statement for the query.
|
388 |
+
*
|
389 |
+
* @param array $score_filters Array containing the score filters.
|
390 |
+
*
|
391 |
+
* @return array Array containing the score filters that need to be applied to the meta query.
|
392 |
+
*/
|
393 |
+
protected function determine_score_filters( $score_filters ) {
|
394 |
+
if ( count( $score_filters ) > 1 ) {
|
395 |
+
return array_merge( array( 'relation' => 'AND' ), $score_filters );
|
396 |
+
}
|
397 |
+
|
398 |
+
return $score_filters;
|
399 |
+
}
|
400 |
+
|
401 |
+
/**
|
402 |
+
* Retrieves the post type from the $_GET variable.
|
403 |
+
*
|
404 |
+
* @return string The current post type.
|
405 |
+
*/
|
406 |
+
public function get_current_post_type() {
|
407 |
+
return filter_input( INPUT_GET, 'post_type' );
|
408 |
+
}
|
409 |
+
|
410 |
+
/**
|
411 |
+
* Retrieves the SEO filter from the $_GET variable.
|
412 |
+
*
|
413 |
+
* @return string The current post type.
|
414 |
+
*/
|
415 |
+
public function get_current_seo_filter() {
|
416 |
+
return filter_input( INPUT_GET, 'seo_filter' );
|
417 |
+
}
|
418 |
+
|
419 |
+
/**
|
420 |
+
* Retrieves the Readability filter from the $_GET variable.
|
421 |
+
*
|
422 |
+
* @return string The current post type.
|
423 |
+
*/
|
424 |
+
public function get_current_readability_filter() {
|
425 |
+
return filter_input( INPUT_GET, 'readability_filter' );
|
426 |
+
}
|
427 |
+
|
428 |
+
/**
|
429 |
+
* Retrieves the keyword filter from the $_GET variable.
|
430 |
+
*
|
431 |
+
* @return string The current post type.
|
432 |
+
*/
|
433 |
+
public function get_current_keyword_filter() {
|
434 |
+
return filter_input( INPUT_GET, 'seo_kw_filter' );
|
435 |
+
}
|
436 |
+
|
437 |
+
/**
|
438 |
+
* Uses the vars to create a complete filter query that can later be executed to filter out posts.
|
439 |
+
*
|
440 |
+
* @param array $vars Array containing the variables that will be used in the meta query.
|
441 |
+
* @param array $filters Array containing the filters that we need to apply in the meta query.
|
442 |
+
*
|
443 |
+
* @return array Array containing the complete filter query.
|
444 |
+
*/
|
445 |
+
protected function build_filter_query( $vars, $filters ) {
|
446 |
+
// If no filters were applied, just return everything.
|
447 |
+
if ( count( $filters ) === 0 ) {
|
448 |
+
return $vars;
|
449 |
+
}
|
450 |
+
|
451 |
+
$result = array( 'meta_query' => array() );
|
452 |
+
$result['meta_query'] = array_merge( $result['meta_query'], array( $this->determine_score_filters( $filters ) ) );
|
453 |
+
|
454 |
+
$current_seo_filter = $this->get_current_seo_filter();
|
455 |
+
|
456 |
+
// This only applies for the SEO score filter because it can because the SEO score can be altered by the no-index option.
|
457 |
+
if ( $this->is_valid_filter( $current_seo_filter ) && ! in_array( $current_seo_filter, array( WPSEO_Rank::NO_INDEX, WPSEO_Rank::NO_FOCUS ), true ) ) {
|
458 |
+
$result['meta_query'] = array_merge( $result['meta_query'], array( $this->get_meta_robots_query_values() ) );
|
459 |
+
}
|
460 |
+
|
461 |
+
return array_merge( $vars, $result );
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Creates a Readability score filter.
|
466 |
+
*
|
467 |
+
* @param number $low The lower boundary of the score.
|
468 |
+
* @param number $high The higher boundary of the score.
|
469 |
+
*
|
470 |
+
* @return array The Readability Score filter.
|
471 |
+
*/
|
472 |
+
protected function create_readability_score_filter( $low, $high ) {
|
473 |
+
return array(
|
474 |
+
array(
|
475 |
+
'key' => WPSEO_Meta::$meta_prefix . 'content_score',
|
476 |
+
'value' => array( $low, $high ),
|
477 |
+
'type' => 'numeric',
|
478 |
+
'compare' => 'BETWEEN',
|
479 |
+
),
|
480 |
+
);
|
481 |
+
}
|
482 |
+
|
483 |
+
/**
|
484 |
+
* Creates an SEO score filter.
|
485 |
+
*
|
486 |
+
* @param number $low The lower boundary of the score.
|
487 |
+
* @param number $high The higher boundary of the score.
|
488 |
+
*
|
489 |
+
* @return array The SEO score filter.
|
490 |
+
*/
|
491 |
+
protected function create_seo_score_filter( $low, $high ) {
|
492 |
+
return array(
|
493 |
+
array(
|
494 |
+
'key' => WPSEO_Meta::$meta_prefix . 'linkdex',
|
495 |
+
'value' => array( $low, $high ),
|
496 |
+
'type' => 'numeric',
|
497 |
+
'compare' => 'BETWEEN',
|
498 |
+
),
|
499 |
+
);
|
500 |
+
}
|
501 |
+
|
502 |
+
/**
|
503 |
+
* Creates a filter to retrieve posts that were set to no-index.
|
504 |
+
*
|
505 |
+
* @return array Array containin the no-index filter.
|
506 |
+
*/
|
507 |
+
protected function create_no_index_filter() {
|
508 |
+
return array(
|
509 |
+
array(
|
510 |
+
'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
|
511 |
+
'value' => '1',
|
512 |
+
'compare' => '=',
|
513 |
+
),
|
514 |
+
);
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* Creates a filter to retrieve posts that have no keyword set.
|
519 |
+
*
|
520 |
+
* @return array Array containing the no focus keyword filter.
|
521 |
+
*/
|
522 |
+
protected function create_no_focus_keyword_filter() {
|
523 |
+
return array(
|
524 |
+
array(
|
525 |
+
'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
|
526 |
+
'value' => 'needs-a-value-anyway',
|
527 |
+
'compare' => 'NOT EXISTS',
|
528 |
+
),
|
529 |
+
array(
|
530 |
+
'key' => WPSEO_Meta::$meta_prefix . 'linkdex',
|
531 |
+
'value' => 'needs-a-value-anyway',
|
532 |
+
'compare' => 'NOT EXISTS',
|
533 |
+
),
|
534 |
+
);
|
535 |
+
}
|
536 |
+
|
537 |
+
/**
|
538 |
+
* Determines whether a particular post_id is of an indexable post type.
|
539 |
+
*
|
540 |
+
* @param string $post_id The post ID to check.
|
541 |
+
*
|
542 |
+
* @return bool Whether or not it is indexable.
|
543 |
+
*/
|
544 |
+
protected function is_indexable( $post_id ) {
|
545 |
+
if ( ! empty( $post_id ) && ! $this->uses_default_indexing( $post_id ) ) {
|
546 |
+
return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '2';
|
547 |
+
}
|
548 |
+
|
549 |
+
$post = get_post( $post_id );
|
550 |
+
|
551 |
+
if ( is_object( $post ) ) {
|
552 |
+
// If the option is false, this means we want to index it.
|
553 |
+
return WPSEO_Options::get( 'noindex-' . $post->post_type, false ) === false;
|
554 |
+
}
|
555 |
+
|
556 |
+
return true;
|
557 |
+
}
|
558 |
+
|
559 |
+
/**
|
560 |
+
* Determines whether the given post ID uses the default indexing settings.
|
561 |
+
*
|
562 |
+
* @param integer $post_id The post ID to check.
|
563 |
+
*
|
564 |
+
* @return bool Whether or not the default indexing is being used for the post.
|
565 |
+
*/
|
566 |
+
protected function uses_default_indexing( $post_id ) {
|
567 |
+
return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '0';
|
568 |
+
}
|
569 |
+
|
570 |
+
/**
|
571 |
+
* Returns filters when $order_by is matched in the if-statement.
|
572 |
+
*
|
573 |
+
* @param string $order_by The ID of the column by which to order the posts.
|
574 |
+
*
|
575 |
+
* @return array Array containing the order filters.
|
576 |
+
*/
|
577 |
+
private function filter_order_by( $order_by ) {
|
578 |
+
switch ( $order_by ) {
|
579 |
+
case 'wpseo-metadesc':
|
580 |
+
return array(
|
581 |
+
'meta_key' => WPSEO_Meta::$meta_prefix . 'metadesc',
|
582 |
+
'orderby' => 'meta_value',
|
583 |
+
);
|
584 |
+
|
585 |
+
case 'wpseo-focuskw':
|
586 |
+
return array(
|
587 |
+
'meta_key' => WPSEO_Meta::$meta_prefix . 'focuskw',
|
588 |
+
'orderby' => 'meta_value',
|
589 |
+
);
|
590 |
+
}
|
591 |
+
|
592 |
+
return array();
|
593 |
+
}
|
594 |
+
|
595 |
+
/**
|
596 |
+
* Parses the score column.
|
597 |
+
*
|
598 |
+
* @param integer $post_id The ID of the post for which to show the score.
|
599 |
+
*
|
600 |
+
* @return string The HTML for the SEO score indicator.
|
601 |
+
*/
|
602 |
+
private function parse_column_score( $post_id ) {
|
603 |
+
if ( ! $this->is_indexable( $post_id ) ) {
|
604 |
+
$rank = new WPSEO_Rank( WPSEO_Rank::NO_INDEX );
|
605 |
+
$title = __( 'Post is set to noindex.', 'wordpress-seo' );
|
606 |
+
|
607 |
+
WPSEO_Meta::set_value( 'linkdex', 0, $post_id );
|
608 |
+
|
609 |
+
return $this->render_score_indicator( $rank, $title );
|
610 |
+
}
|
611 |
+
|
612 |
+
if ( WPSEO_Meta::get_value( 'focuskw', $post_id ) === '' ) {
|
613 |
+
$rank = new WPSEO_Rank( WPSEO_Rank::NO_FOCUS );
|
614 |
+
$title = __( 'Focus keyword not set.', 'wordpress-seo' );
|
615 |
+
|
616 |
+
return $this->render_score_indicator( $rank, $title );
|
617 |
+
}
|
618 |
+
|
619 |
+
$score = (int) WPSEO_Meta::get_value( 'linkdex', $post_id );
|
620 |
+
$rank = WPSEO_Rank::from_numeric_score( $score );
|
621 |
+
$title = $rank->get_label();
|
622 |
+
|
623 |
+
return $this->render_score_indicator( $rank, $title );
|
624 |
+
}
|
625 |
+
|
626 |
+
/**
|
627 |
+
* Parsing the readability score column.
|
628 |
+
*
|
629 |
+
* @param int $post_id The ID of the post for which to show the readability score.
|
630 |
+
*
|
631 |
+
* @return string The HTML for the readability score indicator.
|
632 |
+
*/
|
633 |
+
private function parse_column_score_readability( $post_id ) {
|
634 |
+
$score = (int) WPSEO_Meta::get_value( 'content_score', $post_id );
|
635 |
+
$rank = WPSEO_Rank::from_numeric_score( $score );
|
636 |
+
|
637 |
+
return $this->render_score_indicator( $rank );
|
638 |
+
}
|
639 |
+
|
640 |
+
/**
|
641 |
+
* Sets up the hooks for the post_types.
|
642 |
+
*/
|
643 |
+
private function set_post_type_hooks() {
|
644 |
+
$post_types = WPSEO_Post_Type::get_accessible_post_types();
|
645 |
+
|
646 |
+
if ( ! is_array( $post_types ) || $post_types === array() ) {
|
647 |
+
return;
|
648 |
+
}
|
649 |
+
|
650 |
+
foreach ( $post_types as $post_type ) {
|
651 |
+
if ( $this->display_metabox( $post_type ) === false ) {
|
652 |
+
continue;
|
653 |
+
}
|
654 |
+
|
655 |
+
add_filter( 'manage_' . $post_type . '_posts_columns', array( $this, 'column_heading' ), 10, 1 );
|
656 |
+
add_action( 'manage_' . $post_type . '_posts_custom_column', array( $this, 'column_content' ), 10, 2 );
|
657 |
+
add_action( 'manage_edit-' . $post_type . '_sortable_columns', array( $this, 'column_sort' ), 10, 2 );
|
658 |
+
|
659 |
+
/*
|
660 |
+
* Use the `get_user_option_{$option}` filter to change the output of the get_user_option
|
661 |
+
* function for the `manage{$screen}columnshidden` option, which is based on the current
|
662 |
+
* admin screen. The admin screen we want to target is the `edit-{$post_type}` screen.
|
663 |
+
*/
|
664 |
+
$filter = sprintf( 'get_user_option_%s', sprintf( 'manage%scolumnshidden', 'edit-' . $post_type ) );
|
665 |
+
|
666 |
+
add_filter( $filter, array( $this, 'column_hidden' ), 10, 3 );
|
667 |
+
}
|
668 |
+
|
669 |
+
unset( $post_type );
|
670 |
+
}
|
671 |
+
|
672 |
+
/**
|
673 |
+
* Wraps the WPSEO_Metabox check to determine whether the metabox should be displayed either by
|
674 |
+
* choice of the admin or because the post type is not a public post type.
|
675 |
+
*
|
676 |
+
* @since 7.0
|
677 |
+
*
|
678 |
+
* @param string $post_type Optional. The post type to test, defaults to the current post post_type.
|
679 |
+
*
|
680 |
+
* @return bool Whether or not the meta box (and associated columns etc) should be hidden.
|
681 |
+
*/
|
682 |
+
private function display_metabox( $post_type = null ) {
|
683 |
+
$current_post_type = sanitize_text_field( $this->get_current_post_type() );
|
684 |
+
|
685 |
+
if ( ! isset( $post_type ) && ! empty( $current_post_type ) ) {
|
686 |
+
$post_type = $current_post_type;
|
687 |
+
}
|
688 |
+
|
689 |
+
return WPSEO_Utils::is_metabox_active( $post_type, 'post_type' );
|
690 |
+
}
|
691 |
+
|
692 |
+
/**
|
693 |
+
* Retrieve the page title.
|
694 |
+
*
|
695 |
+
* @param int $post_id Post to retrieve the title for.
|
696 |
+
*
|
697 |
+
* @return string
|
698 |
+
*/
|
699 |
+
private function page_title( $post_id ) {
|
700 |
+
$fixed_title = WPSEO_Meta::get_value( 'title', $post_id );
|
701 |
+
if ( $fixed_title !== '' ) {
|
702 |
+
return $fixed_title;
|
703 |
+
}
|
704 |
+
|
705 |
+
$post = get_post( $post_id );
|
706 |
+
|
707 |
+
if ( is_object( $post ) && WPSEO_Options::get( 'title-' . $post->post_type, '' ) !== '' ) {
|
708 |
+
$title_template = WPSEO_Options::get( 'title-' . $post->post_type );
|
709 |
+
$title_template = str_replace( ' %%page%% ', ' ', $title_template );
|
710 |
+
|
711 |
+
return wpseo_replace_vars( $title_template, $post );
|
712 |
+
}
|
713 |
+
|
714 |
+
return wpseo_replace_vars( '%%title%%', $post );
|
715 |
+
}
|
716 |
+
|
717 |
+
/**
|
718 |
+
* @param WPSEO_Rank $rank The rank this indicator should have.
|
719 |
+
* @param string $title Optional. The title for this rank, defaults to the title of the rank.
|
720 |
+
*
|
721 |
+
* @return string The HTML for a score indicator.
|
722 |
+
*/
|
723 |
+
private function render_score_indicator( $rank, $title = '' ) {
|
724 |
+
if ( empty( $title ) ) {
|
725 |
+
$title = $rank->get_label();
|
726 |
+
}
|
727 |
+
|
728 |
+
return '<div aria-hidden="true" title="' . esc_attr( $title ) . '" class="wpseo-score-icon ' . esc_attr( $rank->get_css_class() ) . '"></div><span class="screen-reader-text">' . $title . '</span>';
|
729 |
+
}
|
730 |
+
|
731 |
+
/**
|
732 |
+
* Determines whether or not filter dropdowns should be displayed.
|
733 |
+
*
|
734 |
+
* @return bool Whether or the current page can display the filter drop downs.
|
735 |
+
*/
|
736 |
+
public function can_display_filter() {
|
737 |
+
if ( $GLOBALS['pagenow'] === 'upload.php' ) {
|
738 |
+
return false;
|
739 |
+
}
|
740 |
+
|
741 |
+
if ( $this->display_metabox() === false ) {
|
742 |
+
return false;
|
743 |
+
}
|
744 |
+
|
745 |
+
$screen = get_current_screen();
|
746 |
+
if ( null === $screen ) {
|
747 |
+
return false;
|
748 |
+
}
|
749 |
+
|
750 |
+
return WPSEO_Post_Type::is_post_type_accessible( $screen->post_type );
|
751 |
+
}
|
752 |
+
}
|
admin/class-meta-storage.php
ADDED
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the link count storage.
|
8 |
+
*/
|
9 |
+
class WPSEO_Meta_Storage implements WPSEO_Installable {
|
10 |
+
|
11 |
+
const TABLE_NAME = 'yoast_seo_meta';
|
12 |
+
|
13 |
+
/** @var WPSEO_Database_Proxy */
|
14 |
+
protected $database_proxy;
|
15 |
+
|
16 |
+
/** @var null|string */
|
17 |
+
protected $table_prefix;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Sets the table prefix.
|
21 |
+
*
|
22 |
+
* @param string $table_prefix Optional. The prefix to use for the table.
|
23 |
+
*/
|
24 |
+
public function __construct( $table_prefix = null ) {
|
25 |
+
if ( null === $table_prefix ) {
|
26 |
+
$table_prefix = $GLOBALS['wpdb']->get_blog_prefix();
|
27 |
+
}
|
28 |
+
|
29 |
+
$this->table_prefix = $table_prefix;
|
30 |
+
$this->database_proxy = new WPSEO_Database_Proxy( $GLOBALS['wpdb'], $this->get_table_name(), true );
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Returns the table name to use.
|
35 |
+
*
|
36 |
+
* @return string The table name.
|
37 |
+
*/
|
38 |
+
public function get_table_name() {
|
39 |
+
return $this->table_prefix . self::TABLE_NAME;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Creates the database table.
|
44 |
+
*
|
45 |
+
* @return boolean True if the table was created, false if something went wrong.
|
46 |
+
*/
|
47 |
+
public function install() {
|
48 |
+
return $this->database_proxy->create_table(
|
49 |
+
array(
|
50 |
+
'object_id bigint(20) UNSIGNED NOT NULL',
|
51 |
+
'internal_link_count int(10) UNSIGNED NULL DEFAULT NULL',
|
52 |
+
'incoming_link_count int(10) UNSIGNED NULL DEFAULT NULL',
|
53 |
+
),
|
54 |
+
array(
|
55 |
+
'UNIQUE KEY object_id (object_id)',
|
56 |
+
)
|
57 |
+
);
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Saves the link count to the database.
|
62 |
+
*
|
63 |
+
* @param int $meta_id The id to save the link count for.
|
64 |
+
* @param array $meta_data The total amount of links.
|
65 |
+
*/
|
66 |
+
public function save_meta_data( $meta_id, array $meta_data ) {
|
67 |
+
$where = array( 'object_id' => $meta_id );
|
68 |
+
|
69 |
+
$saved = $this->database_proxy->upsert(
|
70 |
+
array_merge( $where, $meta_data ),
|
71 |
+
$where
|
72 |
+
);
|
73 |
+
|
74 |
+
if ( $saved === false ) {
|
75 |
+
WPSEO_Meta_Table_Accessible::set_inaccessible();
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Updates the incoming link count
|
81 |
+
*
|
82 |
+
* @param array $post_ids The posts to update the incoming link count for.
|
83 |
+
* @param WPSEO_Link_Storage $storage The link storage object.
|
84 |
+
*/
|
85 |
+
public function update_incoming_link_count( array $post_ids, WPSEO_Link_Storage $storage ) {
|
86 |
+
global $wpdb;
|
87 |
+
|
88 |
+
$query = $wpdb->prepare( '
|
89 |
+
SELECT COUNT( id ) AS incoming, target_post_id AS post_id
|
90 |
+
FROM ' . $storage->get_table_name() . '
|
91 |
+
WHERE target_post_id IN(' . implode( ',', array_fill( 0, count( $post_ids ), '%d' ) ) . ')
|
92 |
+
GROUP BY target_post_id',
|
93 |
+
$post_ids
|
94 |
+
);
|
95 |
+
|
96 |
+
$results = $wpdb->get_results( $query );
|
97 |
+
|
98 |
+
$post_ids_non_zero = array();
|
99 |
+
foreach ( $results as $result ) {
|
100 |
+
$this->save_meta_data( $result->post_id, array( 'incoming_link_count' => $result->incoming ) );
|
101 |
+
$post_ids_non_zero[] = $result->post_id;
|
102 |
+
}
|
103 |
+
|
104 |
+
$post_ids_zero = array_diff( $post_ids, $post_ids_non_zero );
|
105 |
+
foreach ( $post_ids_zero as $post_id ) {
|
106 |
+
$this->save_meta_data( $post_id, array( 'incoming_link_count' => 0 ) );
|
107 |
+
}
|
108 |
+
}
|
109 |
+
}
|
admin/class-meta-table-accessible.php
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the state of the table being accessible.
|
8 |
+
*/
|
9 |
+
class WPSEO_Meta_Table_Accessible {
|
10 |
+
|
11 |
+
const ACCESSIBLE = '0';
|
12 |
+
const INACCESSBILE = '1';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Checks if the given table name exists.
|
16 |
+
*
|
17 |
+
* @return bool True when table is accessible.
|
18 |
+
*/
|
19 |
+
public static function is_accessible() {
|
20 |
+
$value = get_transient( self::transient_name() );
|
21 |
+
|
22 |
+
// If the value is not set, check the table.
|
23 |
+
if ( false === $value ) {
|
24 |
+
return self::check_table();
|
25 |
+
}
|
26 |
+
|
27 |
+
return $value === self::ACCESSIBLE;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Sets the transient value to 1, to indicate the table is not accessible.
|
32 |
+
*
|
33 |
+
* @return void
|
34 |
+
*/
|
35 |
+
public static function set_inaccessible() {
|
36 |
+
set_transient( self::transient_name(), self::INACCESSBILE, HOUR_IN_SECONDS );
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Removes the transient.
|
41 |
+
*
|
42 |
+
* @return void
|
43 |
+
*/
|
44 |
+
public static function cleanup() {
|
45 |
+
delete_transient( self::transient_name() );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Sets the transient value to 0, to indicate the table is accessible.
|
50 |
+
*
|
51 |
+
* @return void
|
52 |
+
*/
|
53 |
+
protected static function set_accessible() {
|
54 |
+
/*
|
55 |
+
* Prefer to set a 0 timeout, but if the timeout was set before WordPress will not delete the transient
|
56 |
+
* correctly when overridden with a zero value.
|
57 |
+
*
|
58 |
+
* Setting a YEAR_IN_SECONDS instead.
|
59 |
+
*/
|
60 |
+
set_transient( self::transient_name(), self::ACCESSIBLE, YEAR_IN_SECONDS );
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Checks if the table exists if not, set the transient to indicate the inaccessible table.
|
65 |
+
*
|
66 |
+
* @return bool True if table is accessible.
|
67 |
+
*/
|
68 |
+
protected static function check_table() {
|
69 |
+
global $wpdb;
|
70 |
+
|
71 |
+
$storage = new WPSEO_Meta_Storage();
|
72 |
+
if ( $wpdb->get_var( 'SHOW TABLES LIKE "' . $storage->get_table_name() . '"' ) !== $storage->get_table_name() ) {
|
73 |
+
self::set_inaccessible();
|
74 |
+
return false;
|
75 |
+
}
|
76 |
+
|
77 |
+
self::set_accessible();
|
78 |
+
return true;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Returns the name of the transient.
|
83 |
+
*
|
84 |
+
* @return string The name of the transient to use.
|
85 |
+
*/
|
86 |
+
protected static function transient_name() {
|
87 |
+
return 'wpseo_meta_table_inaccessible';
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Checks if the table exists if not, set the transient to indicate the inaccessible table.
|
92 |
+
*
|
93 |
+
* @deprecated 6.0
|
94 |
+
*
|
95 |
+
* @return bool True if table is accessible.
|
96 |
+
*/
|
97 |
+
public static function check_table_is_accessible() {
|
98 |
+
_deprecated_function( __FUNCTION__, '6.0', __CLASS__ . '::is_accessible' );
|
99 |
+
return self::is_accessible();
|
100 |
+
}
|
101 |
+
}
|
admin/class-option-tab.php
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Options\Tabs
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Option_Tab
|
8 |
+
*/
|
9 |
+
class WPSEO_Option_Tab {
|
10 |
+
|
11 |
+
/** @var string Name of the tab */
|
12 |
+
private $name;
|
13 |
+
|
14 |
+
/** @var string Label of the tab */
|
15 |
+
private $label;
|
16 |
+
|
17 |
+
/** @var array Optional arguments */
|
18 |
+
private $arguments;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* WPSEO_Option_Tab constructor.
|
22 |
+
*
|
23 |
+
* @param string $name Name of the tab.
|
24 |
+
* @param string $label Localized label of the tab.
|
25 |
+
* @param array $arguments Optional arguments.
|
26 |
+
*/
|
27 |
+
public function __construct( $name, $label, array $arguments = array() ) {
|
28 |
+
$this->name = sanitize_title( $name );
|
29 |
+
$this->label = $label;
|
30 |
+
$this->arguments = $arguments;
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Gets the name.
|
35 |
+
*
|
36 |
+
* @return string The name.
|
37 |
+
*/
|
38 |
+
public function get_name() {
|
39 |
+
return $this->name;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Gets the label.
|
44 |
+
*
|
45 |
+
* @return string The label.
|
46 |
+
*/
|
47 |
+
public function get_label() {
|
48 |
+
return $this->label;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Gets the video URL.
|
53 |
+
*
|
54 |
+
* @return string The video url.
|
55 |
+
*/
|
56 |
+
public function get_video_url() {
|
57 |
+
return $this->get_argument( 'video_url' );
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Retrieves whether the tab needs a save button.
|
62 |
+
*
|
63 |
+
* @return bool True whether the tabs needs a save button.
|
64 |
+
*/
|
65 |
+
public function has_save_button() {
|
66 |
+
return (bool) $this->get_argument( 'save_button', true );
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Gets the option group.
|
71 |
+
*
|
72 |
+
* @return string The option group.
|
73 |
+
*/
|
74 |
+
public function get_opt_group() {
|
75 |
+
return $this->get_argument( 'opt_group' );
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Retrieves the variable from the supplied arguments.
|
80 |
+
*
|
81 |
+
* @param string $variable Variable to retrieve.
|
82 |
+
* @param string|mixed $default Default to use when variable not found.
|
83 |
+
*
|
84 |
+
* @return mixed|string The retrieved variable.
|
85 |
+
*/
|
86 |
+
protected function get_argument( $variable, $default = '' ) {
|
87 |
+
return array_key_exists( $variable, $this->arguments ) ? $this->arguments[ $variable ] : $default;
|
88 |
+
}
|
89 |
+
}
|
admin/class-option-tabs-formatter.php
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Options\Tabs
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Option_Tabs_Formatter
|
8 |
+
*/
|
9 |
+
class WPSEO_Option_Tabs_Formatter {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @param WPSEO_Option_Tabs $option_tabs Option Tabs to get base from.
|
13 |
+
* @param WPSEO_Option_Tab $tab Tab to get name from.
|
14 |
+
*
|
15 |
+
* @return string
|
16 |
+
*/
|
17 |
+
public function get_tab_view( WPSEO_Option_Tabs $option_tabs, WPSEO_Option_Tab $tab ) {
|
18 |
+
return WPSEO_PATH . 'admin/views/tabs/' . $option_tabs->get_base() . '/' . $tab->get_name() . '.php';
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @param WPSEO_Option_Tabs $option_tabs Option Tabs to get tabs from.
|
23 |
+
*/
|
24 |
+
public function run( WPSEO_Option_Tabs $option_tabs ) {
|
25 |
+
|
26 |
+
echo '<h2 class="nav-tab-wrapper" id="wpseo-tabs">';
|
27 |
+
foreach ( $option_tabs->get_tabs() as $tab ) {
|
28 |
+
printf(
|
29 |
+
'<a class="nav-tab" id="%1$s" href="%2$s">%3$s</a>',
|
30 |
+
esc_attr( $tab->get_name() . '-tab' ),
|
31 |
+
esc_url( '#top#' . $tab->get_name() ),
|
32 |
+
esc_html( $tab->get_label() )
|
33 |
+
);
|
34 |
+
}
|
35 |
+
echo '</h2>';
|
36 |
+
|
37 |
+
$help_center = new WPSEO_Help_Center( '', $option_tabs, WPSEO_Utils::is_yoast_seo_premium() );
|
38 |
+
$help_center->localize_data();
|
39 |
+
$help_center->mount();
|
40 |
+
|
41 |
+
foreach ( $option_tabs->get_tabs() as $tab ) {
|
42 |
+
$identifier = $tab->get_name();
|
43 |
+
|
44 |
+
$class = 'wpseotab ' . ( $tab->has_save_button() ? 'save' : 'nosave' );
|
45 |
+
printf( '<div id="%1$s" class="%2$s">', esc_attr( $identifier ), esc_attr( $class ) );
|
46 |
+
|
47 |
+
// Output the settings view for all tabs.
|
48 |
+
$tab_view = $this->get_tab_view( $option_tabs, $tab );
|
49 |
+
if ( is_file( $tab_view ) ) {
|
50 |
+
$yform = Yoast_Form::get_instance();
|
51 |
+
require_once $tab_view;
|
52 |
+
}
|
53 |
+
|
54 |
+
echo '</div>';
|
55 |
+
}
|
56 |
+
}
|
57 |
+
}
|
admin/class-option-tabs.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Options\Tabs
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Option_Tabs
|
8 |
+
*/
|
9 |
+
class WPSEO_Option_Tabs {
|
10 |
+
|
11 |
+
/** @var string Tabs base */
|
12 |
+
private $base;
|
13 |
+
|
14 |
+
/** @var array The tabs in this group */
|
15 |
+
private $tabs = array();
|
16 |
+
|
17 |
+
/** @var string Name of the active tab */
|
18 |
+
private $active_tab = '';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* WPSEO_Option_Tabs constructor.
|
22 |
+
*
|
23 |
+
* @param string $base Base of the tabs.
|
24 |
+
* @param string $active_tab Currently active tab.
|
25 |
+
*/
|
26 |
+
public function __construct( $base, $active_tab = '' ) {
|
27 |
+
$this->base = sanitize_title( $base );
|
28 |
+
|
29 |
+
$tab = filter_input( INPUT_GET, 'tab' );
|
30 |
+
$this->active_tab = empty( $tab ) ? $active_tab : $tab;
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Get the base
|
35 |
+
*
|
36 |
+
* @return string
|
37 |
+
*/
|
38 |
+
public function get_base() {
|
39 |
+
return $this->base;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Add a tab
|
44 |
+
*
|
45 |
+
* @param WPSEO_Option_Tab $tab Tab to add.
|
46 |
+
*
|
47 |
+
* @return $this
|
48 |
+
*/
|
49 |
+
public function add_tab( WPSEO_Option_Tab $tab ) {
|
50 |
+
$this->tabs[] = $tab;
|
51 |
+
|
52 |
+
return $this;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Get active tab
|
57 |
+
*
|
58 |
+
* @return null|WPSEO_Option_Tab Get the active tab.
|
59 |
+
*/
|
60 |
+
public function get_active_tab() {
|
61 |
+
if ( empty( $this->active_tab ) ) {
|
62 |
+
return null;
|
63 |
+
}
|
64 |
+
|
65 |
+
$active_tabs = array_filter( $this->tabs, array( $this, 'is_active_tab' ) );
|
66 |
+
if ( ! empty( $active_tabs ) ) {
|
67 |
+
$active_tabs = array_values( $active_tabs );
|
68 |
+
if ( count( $active_tabs ) === 1 ) {
|
69 |
+
return $active_tabs[0];
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
return null;
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Is the tab the active tab
|
78 |
+
*
|
79 |
+
* @param WPSEO_Option_Tab $tab Tab to check for active tab.
|
80 |
+
*
|
81 |
+
* @return bool
|
82 |
+
*/
|
83 |
+
public function is_active_tab( WPSEO_Option_Tab $tab ) {
|
84 |
+
return ( $tab->get_name() === $this->active_tab );
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Get all tabs
|
89 |
+
*
|
90 |
+
* @return WPSEO_Option_Tab[]
|
91 |
+
*/
|
92 |
+
public function get_tabs() {
|
93 |
+
return $this->tabs;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Display the tabs
|
98 |
+
*
|
99 |
+
* @param Yoast_Form $yform Yoast Form needed in the views.
|
100 |
+
*/
|
101 |
+
public function display( Yoast_Form $yform ) {
|
102 |
+
$formatter = new WPSEO_Option_Tabs_Formatter();
|
103 |
+
$formatter->run( $this, $yform );
|
104 |
+
}
|
105 |
+
}
|
admin/class-plugin-availability.php
ADDED
@@ -0,0 +1,342 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Plugin_Availability
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Plugin_Availability
|
8 |
+
*/
|
9 |
+
class WPSEO_Plugin_Availability {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var array
|
13 |
+
*/
|
14 |
+
protected $plugins = array();
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Registers the plugins so we can access them.
|
18 |
+
*/
|
19 |
+
public function register() {
|
20 |
+
$this->register_yoast_plugins();
|
21 |
+
$this->register_yoast_plugins_status();
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Registers all the available Yoast SEO plugins.
|
26 |
+
*/
|
27 |
+
protected function register_yoast_plugins() {
|
28 |
+
$this->plugins = array(
|
29 |
+
'yoast-seo-premium' => array(
|
30 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/1y7' ),
|
31 |
+
'title' => 'Yoast SEO Premium',
|
32 |
+
'description' => sprintf(
|
33 |
+
/* translators: %1$s expands to Yoast SEO */
|
34 |
+
__( 'The premium version of %1$s with more features & support.', 'wordpress-seo' ),
|
35 |
+
'Yoast SEO'
|
36 |
+
),
|
37 |
+
'installed' => false,
|
38 |
+
'slug' => 'wordpress-seo-premium/wp-seo-premium.php',
|
39 |
+
'version_sync' => true,
|
40 |
+
'premium' => true,
|
41 |
+
),
|
42 |
+
|
43 |
+
'video-seo-for-wordpress-seo-by-yoast' => array(
|
44 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/1y8' ),
|
45 |
+
'title' => 'Video SEO',
|
46 |
+
'description' => __( 'Optimize your videos to show them off in search results and get more clicks!', 'wordpress-seo' ),
|
47 |
+
'installed' => false,
|
48 |
+
'slug' => 'wpseo-video/video-seo.php',
|
49 |
+
'version_sync' => true,
|
50 |
+
'premium' => true,
|
51 |
+
),
|
52 |
+
|
53 |
+
'yoast-news-seo' => array(
|
54 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/1y9' ),
|
55 |
+
'title' => 'News SEO',
|
56 |
+
'description' => __( 'Are you in Google News? Increase your traffic from Google News by optimizing for it!', 'wordpress-seo' ),
|
57 |
+
'installed' => false,
|
58 |
+
'slug' => 'wpseo-news/wpseo-news.php',
|
59 |
+
'version_sync' => true,
|
60 |
+
'premium' => true,
|
61 |
+
),
|
62 |
+
|
63 |
+
'local-seo-for-yoast-seo' => array(
|
64 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/1ya' ),
|
65 |
+
'title' => 'Local SEO',
|
66 |
+
'description' => __( 'Rank better locally and in Google Maps, without breaking a sweat!', 'wordpress-seo' ),
|
67 |
+
'installed' => false,
|
68 |
+
'slug' => 'wordpress-seo-local/local-seo.php',
|
69 |
+
'version_sync' => true,
|
70 |
+
'premium' => true,
|
71 |
+
),
|
72 |
+
|
73 |
+
'yoast-woocommerce-seo' => array(
|
74 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/1o0' ),
|
75 |
+
'title' => 'Yoast WooCommerce SEO',
|
76 |
+
'description' => sprintf(
|
77 |
+
/* translators: %1$s expands to Yoast SEO */
|
78 |
+
__( 'Seamlessly integrate WooCommerce with %1$s and get extra features!', 'wordpress-seo' ),
|
79 |
+
'Yoast SEO'
|
80 |
+
),
|
81 |
+
'_dependencies' => array(
|
82 |
+
'WooCommerce' => array(
|
83 |
+
'slug' => 'woocommerce/woocommerce.php',
|
84 |
+
),
|
85 |
+
),
|
86 |
+
'installed' => false,
|
87 |
+
'slug' => 'wpseo-woocommerce/wpseo-woocommerce.php',
|
88 |
+
'version_sync' => true,
|
89 |
+
'premium' => true,
|
90 |
+
),
|
91 |
+
|
92 |
+
'yoast-acf-analysis' => array(
|
93 |
+
'url' => 'https://wordpress.org/plugins/acf-content-analysis-for-yoast-seo/',
|
94 |
+
'title' => 'ACF Content Analysis for Yoast SEO',
|
95 |
+
'description' => sprintf(
|
96 |
+
/* translators: %1$s expands to Yoast SEO, %2$s expands to Advanced Custom Fields */
|
97 |
+
__( 'Seamlessly integrate %2$s with %1$s for the content analysis!', 'wordpress-seo' ),
|
98 |
+
'Yoast SEO',
|
99 |
+
'Advanced Custom Fields'
|
100 |
+
),
|
101 |
+
'installed' => false,
|
102 |
+
'slug' => 'acf-content-analysis-for-yoast-seo/yoast-acf-analysis.php',
|
103 |
+
'_dependencies' => array(
|
104 |
+
'Advanced Custom Fields' => array(
|
105 |
+
'slug' => 'advanced-custom-fields/acf.php',
|
106 |
+
),
|
107 |
+
),
|
108 |
+
'version_sync' => false,
|
109 |
+
),
|
110 |
+
|
111 |
+
'yoastseo-amp' => array(
|
112 |
+
'url' => 'https://wordpress.org/plugins/glue-for-yoast-seo-amp/',
|
113 |
+
'title' => 'Yoast SEO AMP Glue',
|
114 |
+
'description' => sprintf(
|
115 |
+
/* translators: %1$s expands to Yoast SEO */
|
116 |
+
__( 'Seamlessly integrate %1$s into your AMP pages!', 'wordpress-seo' ), 'Yoast SEO'
|
117 |
+
),
|
118 |
+
'installed' => false,
|
119 |
+
'slug' => 'glue-for-yoast-seo-amp/yoastseo-amp.php',
|
120 |
+
'_dependencies' => array(
|
121 |
+
'AMP' => array(
|
122 |
+
'slug' => 'amp/amp.php',
|
123 |
+
),
|
124 |
+
),
|
125 |
+
'version_sync' => false,
|
126 |
+
),
|
127 |
+
);
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Sets certain plugin properties based on WordPress' status.
|
132 |
+
*/
|
133 |
+
protected function register_yoast_plugins_status() {
|
134 |
+
|
135 |
+
foreach ( $this->plugins as $name => $plugin ) {
|
136 |
+
|
137 |
+
$plugin_slug = $plugin['slug'];
|
138 |
+
$plugin_path = WP_PLUGIN_DIR . '/' . $plugin_slug;
|
139 |
+
|
140 |
+
if ( file_exists( $plugin_path ) ) {
|
141 |
+
$plugin_data = get_plugin_data( $plugin_path, false, false );
|
142 |
+
$this->plugins[ $name ]['installed'] = true;
|
143 |
+
$this->plugins[ $name ]['version'] = $plugin_data['Version'];
|
144 |
+
$this->plugins[ $name ]['active'] = is_plugin_active( $plugin_slug );
|
145 |
+
}
|
146 |
+
}
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Checks whether or not a plugin is known within the Yoast SEO collection.
|
151 |
+
*
|
152 |
+
* @param {string} $plugin The plugin to search for.
|
153 |
+
*
|
154 |
+
* @return bool Whether or not the plugin is exists.
|
155 |
+
*/
|
156 |
+
protected function plugin_exists( $plugin ) {
|
157 |
+
return isset( $this->plugins[ $plugin ] );
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Gets all the possibly available plugins.
|
162 |
+
*
|
163 |
+
* @return array Array containing the information about the plugins.
|
164 |
+
*/
|
165 |
+
public function get_plugins() {
|
166 |
+
return $this->plugins;
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Gets a specific plugin. Returns an empty array if it cannot be found.
|
171 |
+
*
|
172 |
+
* @param {string} $plugin The plugin to search for.
|
173 |
+
*
|
174 |
+
* @return array The plugin properties.
|
175 |
+
*/
|
176 |
+
public function get_plugin( $plugin ) {
|
177 |
+
if ( ! $this->plugin_exists( $plugin ) ) {
|
178 |
+
return array();
|
179 |
+
}
|
180 |
+
|
181 |
+
return $this->plugins[ $plugin ];
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Gets the version of the plugin.
|
186 |
+
*
|
187 |
+
* @param {string} $plugin The plugin to search for.
|
188 |
+
*
|
189 |
+
* @return string The version associated with the plugin.
|
190 |
+
*/
|
191 |
+
public function get_version( $plugin ) {
|
192 |
+
if ( ! isset( $plugin['version'] ) ) {
|
193 |
+
return '';
|
194 |
+
}
|
195 |
+
|
196 |
+
return $plugin['version'];
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Checks if there are dependencies available for the plugin.
|
201 |
+
*
|
202 |
+
* @param {string} $plugin The plugin to search for.
|
203 |
+
*
|
204 |
+
* @return bool Whether or not there is a dependency present.
|
205 |
+
*/
|
206 |
+
public function has_dependencies( $plugin ) {
|
207 |
+
return ( isset( $plugin['_dependencies'] ) && ! empty( $plugin['_dependencies'] ) );
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Gets the dependencies for the plugin.
|
212 |
+
*
|
213 |
+
* @param {string} $plugin The plugin to search for.
|
214 |
+
*
|
215 |
+
* @return array Array containing all the dependencies associated with the plugin.
|
216 |
+
*/
|
217 |
+
public function get_dependencies( $plugin ) {
|
218 |
+
if ( ! $this->has_dependencies( $plugin ) ) {
|
219 |
+
return array();
|
220 |
+
}
|
221 |
+
|
222 |
+
return $plugin['_dependencies'];
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Checks if all dependencies are satisfied.
|
227 |
+
*
|
228 |
+
* @param {string} $plugin The plugin to search for.
|
229 |
+
*
|
230 |
+
* @return bool Whether or not the dependencies are satisfied.
|
231 |
+
*/
|
232 |
+
public function dependencies_are_satisfied( $plugin ) {
|
233 |
+
if ( ! $this->has_dependencies( $plugin ) ) {
|
234 |
+
return true;
|
235 |
+
}
|
236 |
+
|
237 |
+
$dependencies = $this->get_dependencies( $plugin );
|
238 |
+
$installed_dependencies = array_filter( $dependencies, array( $this, 'is_dependency_available' ) );
|
239 |
+
|
240 |
+
return count( $installed_dependencies ) === count( $dependencies );
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Checks whether or not one of the plugins is properly installed and usable.
|
245 |
+
*
|
246 |
+
* @param {string} $plugin The plugin to search for.
|
247 |
+
*
|
248 |
+
* @return bool Whether or not the plugin is properly installed.
|
249 |
+
*/
|
250 |
+
public function is_installed( $plugin ) {
|
251 |
+
if ( empty( $plugin ) ) {
|
252 |
+
return false;
|
253 |
+
}
|
254 |
+
|
255 |
+
return $this->is_available( $plugin );
|
256 |
+
}
|
257 |
+
|
258 |
+
/**
|
259 |
+
* Gets all installed plugins.
|
260 |
+
*
|
261 |
+
* @return array The installed plugins.
|
262 |
+
*/
|
263 |
+
public function get_installed_plugins() {
|
264 |
+
$installed = array();
|
265 |
+
|
266 |
+
foreach ( $this->plugins as $plugin_key => $plugin ) {
|
267 |
+
if ( $this->is_installed( $plugin ) ) {
|
268 |
+
$installed[ $plugin_key ] = $plugin;
|
269 |
+
}
|
270 |
+
}
|
271 |
+
|
272 |
+
return $installed;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Checks for the availability of the plugin.
|
277 |
+
*
|
278 |
+
* @param {string} $plugin The plugin to search for.
|
279 |
+
*
|
280 |
+
* @return bool Whether or not the plugin is available.
|
281 |
+
*/
|
282 |
+
public function is_available( $plugin ) {
|
283 |
+
return isset( $plugin['installed'] ) && $plugin['installed'] === true;
|
284 |
+
}
|
285 |
+
|
286 |
+
/**
|
287 |
+
* Checks whether a dependency is available.
|
288 |
+
*
|
289 |
+
* @param {string} $dependency The dependency to look for.
|
290 |
+
*
|
291 |
+
* @return bool Whether or not the dependency is available.
|
292 |
+
*/
|
293 |
+
public function is_dependency_available( $dependency ) {
|
294 |
+
return in_array( $dependency['slug'], array_keys( get_plugins() ), true );
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* Gets the names of the dependencies.
|
299 |
+
*
|
300 |
+
* @param array $plugin The plugin to get the dependency names from.
|
301 |
+
*
|
302 |
+
* @return array Array containing the names of the associated dependencies.
|
303 |
+
*/
|
304 |
+
public function get_dependency_names( $plugin ) {
|
305 |
+
if ( ! $this->has_dependencies( $plugin ) ) {
|
306 |
+
return array();
|
307 |
+
}
|
308 |
+
|
309 |
+
return array_keys( $plugin['_dependencies'] );
|
310 |
+
}
|
311 |
+
|
312 |
+
/**
|
313 |
+
* Gets an array of plugins that have defined dependencies.
|
314 |
+
*
|
315 |
+
* @return array Array of the plugins that have dependencies.
|
316 |
+
*/
|
317 |
+
public function get_plugins_with_dependencies() {
|
318 |
+
return array_filter( $this->plugins, array( $this, 'has_dependencies' ) );
|
319 |
+
}
|
320 |
+
|
321 |
+
/**
|
322 |
+
* Determines whether or not a plugin is active.
|
323 |
+
*
|
324 |
+
* @param string $plugin The plugin slug to check.
|
325 |
+
*
|
326 |
+
* @return bool Whether or not the plugin is active.
|
327 |
+
*/
|
328 |
+
public function is_active( $plugin ) {
|
329 |
+
return is_plugin_active( $plugin );
|
330 |
+
}
|
331 |
+
|
332 |
+
/**
|
333 |
+
* Determines whether or not a plugin is a Premium product.
|
334 |
+
*
|
335 |
+
* @param array $plugin The plugin to check.
|
336 |
+
*
|
337 |
+
* @return bool Whether or not the plugin is a Premium product.
|
338 |
+
*/
|
339 |
+
public function is_premium( $plugin ) {
|
340 |
+
return isset( $plugin['premium'] ) && $plugin['premium'] === true;
|
341 |
+
}
|
342 |
+
}
|
admin/class-plugin-compatibility.php
ADDED
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Plugin_Compatibility
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Plugin_Compatibility
|
8 |
+
*/
|
9 |
+
class WPSEO_Plugin_Compatibility {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string
|
13 |
+
*/
|
14 |
+
protected $current_wpseo_version;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var WPSEO_Plugin_Availability
|
18 |
+
*/
|
19 |
+
protected $availability_checker;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var array
|
23 |
+
*/
|
24 |
+
protected $installed_plugins;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* WPSEO_Plugin_Compatibility constructor.
|
28 |
+
*
|
29 |
+
* @param string $version The version to check against.
|
30 |
+
* @param null|class $availability_checker The checker to use.
|
31 |
+
*/
|
32 |
+
public function __construct( $version, $availability_checker = null ) {
|
33 |
+
// We trim off the patch version, as this shouldn't break the comparison.
|
34 |
+
$this->current_wpseo_version = $this->get_major_minor_version( $version );
|
35 |
+
$this->availability_checker = $this->retrieve_availability_checker( $availability_checker );
|
36 |
+
$this->installed_plugins = $this->availability_checker->get_installed_plugins();
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Retrieves the availability checker.
|
41 |
+
*
|
42 |
+
* @param null|object $checker The checker to set.
|
43 |
+
*
|
44 |
+
* @return WPSEO_Plugin_Availability The checker to use.
|
45 |
+
*/
|
46 |
+
private function retrieve_availability_checker( $checker ) {
|
47 |
+
if ( is_null( $checker ) || ! is_object( $checker ) ) {
|
48 |
+
$checker = new WPSEO_Plugin_Availability();
|
49 |
+
$checker->register();
|
50 |
+
}
|
51 |
+
|
52 |
+
return $checker;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Wraps the availability checker's get_installed_plugins method.
|
57 |
+
*
|
58 |
+
* @return array Array containing all the installed plugins.
|
59 |
+
*/
|
60 |
+
public function get_installed_plugins() {
|
61 |
+
return $this->installed_plugins;
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Creates a list of installed plugins and whether or not they are compatible.
|
66 |
+
*
|
67 |
+
* @return array Array containing the installed plugins and compatibility.
|
68 |
+
*/
|
69 |
+
public function get_installed_plugins_compatibility() {
|
70 |
+
foreach ( $this->installed_plugins as $key => $plugin ) {
|
71 |
+
|
72 |
+
$this->installed_plugins[ $key ]['compatible'] = $this->is_compatible( $key );
|
73 |
+
}
|
74 |
+
|
75 |
+
return $this->installed_plugins;
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Checks whether or not a plugin is compatible.
|
80 |
+
*
|
81 |
+
* @param string $plugin The plugin to look for and match.
|
82 |
+
*
|
83 |
+
* @return bool Whether or not the plugin is compatible.
|
84 |
+
*/
|
85 |
+
public function is_compatible( $plugin ) {
|
86 |
+
$plugin = $this->availability_checker->get_plugin( $plugin );
|
87 |
+
|
88 |
+
// If we are not syncing versions, we are always compatible.
|
89 |
+
if ( ! isset( $plugin['version_sync'] ) || $plugin['version_sync'] !== true ) {
|
90 |
+
return true;
|
91 |
+
}
|
92 |
+
|
93 |
+
$plugin_version = $this->availability_checker->get_version( $plugin );
|
94 |
+
return $this->get_major_minor_version( $plugin_version ) === $this->current_wpseo_version;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Gets the major/minor version of the plugin for easier comparing.
|
99 |
+
*
|
100 |
+
* @param string $version The version to trim.
|
101 |
+
*
|
102 |
+
* @return string The major/minor version of the plugin.
|
103 |
+
*/
|
104 |
+
protected function get_major_minor_version( $version ) {
|
105 |
+
return substr( $version, 0, 3 );
|
106 |
+
}
|
107 |
+
}
|
admin/class-plugin-conflict.php
ADDED
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
* @since 1.7.0
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Contains list of conflicting plugins.
|
9 |
+
*/
|
10 |
+
class WPSEO_Plugin_Conflict extends Yoast_Plugin_Conflict {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* The plugins must be grouped per section.
|
14 |
+
*
|
15 |
+
* It's possible to check for each section if there are conflicting plugin
|
16 |
+
*
|
17 |
+
* @var array
|
18 |
+
*/
|
19 |
+
protected $plugins = array(
|
20 |
+
// The plugin which are writing OG metadata.
|
21 |
+
'open_graph' => array(
|
22 |
+
'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
|
23 |
+
// 2 Click Social Media Buttons.
|
24 |
+
'add-link-to-facebook/add-link-to-facebook.php', // Add Link to Facebook.
|
25 |
+
'add-meta-tags/add-meta-tags.php', // Add Meta Tags.
|
26 |
+
'easy-facebook-share-thumbnails/esft.php', // Easy Facebook Share Thumbnail.
|
27 |
+
'facebook/facebook.php', // Facebook (official plugin).
|
28 |
+
'facebook-awd/AWD_facebook.php', // Facebook AWD All in one.
|
29 |
+
'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
|
30 |
+
// Facebook Featured Image & OG Meta Tags.
|
31 |
+
'facebook-meta-tags/facebook-metatags.php', // Facebook Meta Tags.
|
32 |
+
'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
|
33 |
+
// Facebook Open Graph Meta Tags for WordPress.
|
34 |
+
'facebook-revised-open-graph-meta-tag/index.php', // Facebook Revised Open Graph Meta Tag.
|
35 |
+
'facebook-thumb-fixer/_facebook-thumb-fixer.php', // Facebook Thumb Fixer.
|
36 |
+
'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
|
37 |
+
// Fedmich's Facebook Open Graph Meta.
|
38 |
+
'network-publisher/networkpub.php', // Network Publisher.
|
39 |
+
'nextgen-facebook/nextgen-facebook.php', // NextGEN Facebook OG.
|
40 |
+
'opengraph/opengraph.php', // Open Graph.
|
41 |
+
'open-graph-protocol-framework/open-graph-protocol-framework.php',
|
42 |
+
// Open Graph Protocol Framework.
|
43 |
+
'seo-facebook-comments/seofacebook.php', // SEO Facebook Comments.
|
44 |
+
'seo-ultimate/seo-ultimate.php', // SEO Ultimate.
|
45 |
+
'sexybookmarks/sexy-bookmarks.php', // Shareaholic.
|
46 |
+
'shareaholic/sexy-bookmarks.php', // Shareaholic.
|
47 |
+
'sharepress/sharepress.php', // SharePress.
|
48 |
+
'simple-facebook-connect/sfc.php', // Simple Facebook Connect.
|
49 |
+
'social-discussions/social-discussions.php', // Social Discussions.
|
50 |
+
'social-sharing-toolkit/social_sharing_toolkit.php', // Social Sharing Toolkit.
|
51 |
+
'socialize/socialize.php', // Socialize.
|
52 |
+
'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
|
53 |
+
// Tweet, Like, Google +1 and Share.
|
54 |
+
'wordbooker/wordbooker.php', // Wordbooker.
|
55 |
+
'wpsso/wpsso.php', // WordPress Social Sharing Optimization.
|
56 |
+
'wp-caregiver/wp-caregiver.php', // WP Caregiver.
|
57 |
+
'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
|
58 |
+
// WP Facebook Like Send & Open Graph Meta.
|
59 |
+
'wp-facebook-open-graph-protocol/wp-facebook-ogp.php', // WP Facebook Open Graph protocol.
|
60 |
+
'wp-ogp/wp-ogp.php', // WP-OGP.
|
61 |
+
'zoltonorg-social-plugin/zosp.php', // Zolton.org Social Plugin.
|
62 |
+
),
|
63 |
+
'xml_sitemaps' => array(
|
64 |
+
'google-sitemap-plugin/google-sitemap-plugin.php',
|
65 |
+
// Google Sitemap (BestWebSoft).
|
66 |
+
'xml-sitemaps/xml-sitemaps.php',
|
67 |
+
// XML Sitemaps (Denis de Bernardy and Mike Koepke).
|
68 |
+
'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
|
69 |
+
// Better WordPress Google XML Sitemaps (Khang Minh).
|
70 |
+
'google-sitemap-generator/sitemap.php',
|
71 |
+
// Google XML Sitemaps (Arne Brachhold).
|
72 |
+
'xml-sitemap-feed/xml-sitemap.php',
|
73 |
+
// XML Sitemap & Google News feeds (RavanH).
|
74 |
+
'google-monthly-xml-sitemap/monthly-xml-sitemap.php',
|
75 |
+
// Google Monthly XML Sitemap (Andrea Pernici).
|
76 |
+
'simple-google-sitemap-xml/simple-google-sitemap-xml.php',
|
77 |
+
// Simple Google Sitemap XML (iTx Technologies).
|
78 |
+
'another-simple-xml-sitemap/another-simple-xml-sitemap.php',
|
79 |
+
// Another Simple XML Sitemap.
|
80 |
+
'xml-maps/google-sitemap.php',
|
81 |
+
// Xml Sitemap (Jason Martens).
|
82 |
+
'google-xml-sitemap-generator-by-anton-dachauer/adachauer-google-xml-sitemap.php',
|
83 |
+
// Google XML Sitemap Generator by Anton Dachauer (Anton Dachauer).
|
84 |
+
'wp-xml-sitemap/wp-xml-sitemap.php',
|
85 |
+
// WP XML Sitemap (Team Vivacity).
|
86 |
+
'sitemap-generator-for-webmasters/sitemap.php',
|
87 |
+
// Sitemap Generator for Webmasters (iwebslogtech).
|
88 |
+
'xml-sitemap-xml-sitemapcouk/xmls.php',
|
89 |
+
// XML Sitemap - XML-Sitemap.co.uk (Simon Hancox).
|
90 |
+
'sewn-in-xml-sitemap/sewn-xml-sitemap.php',
|
91 |
+
// Sewn In XML Sitemap (jcow).
|
92 |
+
'rps-sitemap-generator/rps-sitemap-generator.php',
|
93 |
+
// RPS Sitemap Generator (redpixelstudios).
|
94 |
+
),
|
95 |
+
'cloaking' => array(
|
96 |
+
'rs-head-cleaner/rs-head-cleaner.php',
|
97 |
+
// RS Head Cleaner Plus https://wordpress.org/plugins/rs-head-cleaner/.
|
98 |
+
'rs-head-cleaner-lite/rs-head-cleaner-lite.php',
|
99 |
+
// RS Head Cleaner Lite https://wordpress.org/plugins/rs-head-cleaner-lite/.
|
100 |
+
),
|
101 |
+
);
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Overrides instance to set with this class as class
|
105 |
+
*
|
106 |
+
* @param string $class_name Optional class name.
|
107 |
+
*
|
108 |
+
* @return Yoast_Plugin_Conflict
|
109 |
+
*/
|
110 |
+
public static function get_instance( $class_name = __CLASS__ ) {
|
111 |
+
return parent::get_instance( $class_name );
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* After activating any plugin, this method will be executed by a hook.
|
116 |
+
*
|
117 |
+
* If the activated plugin is conflicting with ours a notice will be shown.
|
118 |
+
*
|
119 |
+
* @param string|bool $plugin Optional plugin basename to check.
|
120 |
+
*/
|
121 |
+
public static function hook_check_for_plugin_conflicts( $plugin = false ) {
|
122 |
+
|
123 |
+
// The instance of itself.
|
124 |
+
$instance = self::get_instance();
|
125 |
+
|
126 |
+
// Only add plugin as active plugin if $plugin isn't false.
|
127 |
+
if ( $plugin && is_string( $plugin ) ) {
|
128 |
+
// Because it's just activated.
|
129 |
+
$instance->add_active_plugin( $instance->find_plugin_category( $plugin ), $plugin );
|
130 |
+
}
|
131 |
+
|
132 |
+
$plugin_sections = array();
|
133 |
+
|
134 |
+
// Only check for open graph problems when they are enabled.
|
135 |
+
if ( WPSEO_Options::get( 'opengraph' ) ) {
|
136 |
+
/* translators: %1$s expands to Yoast SEO, %2%s: 'Facebook' plugin name of possibly conflicting plugin with regard to creating OpenGraph output. */
|
137 |
+
$plugin_sections['open_graph'] = __( 'Both %1$s and %2$s create OpenGraph output, which might make Facebook, Twitter, LinkedIn and other social networks use the wrong texts and images when your pages are being shared.', 'wordpress-seo' )
|
138 |
+
. '<br/><br/>'
|
139 |
+
. '<a class="button" href="' . admin_url( 'admin.php?page=wpseo_social#top#facebook' ) . '">'
|
140 |
+
/* translators: %1$s expands to Yoast SEO. */
|
141 |
+
. sprintf( __( 'Configure %1$s\'s OpenGraph settings', 'wordpress-seo' ), 'Yoast SEO' )
|
142 |
+
. '</a>';
|
143 |
+
}
|
144 |
+
|
145 |
+
// Only check for XML conflicts if sitemaps are enabled.
|
146 |
+
if ( WPSEO_Options::get( 'enable_xml_sitemap' ) ) {
|
147 |
+
/* 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. */
|
148 |
+
$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' )
|
149 |
+
. '<br/><br/>'
|
150 |
+
. '<a class="button" href="' . admin_url( 'admin.php?page=wpseo_dashboard#top#features' ) . '">'
|
151 |
+
/* translators: %1$s expands to Yoast SEO. */
|
152 |
+
. sprintf( __( 'Toggle %1$s\'s XML Sitemap', 'wordpress-seo' ), 'Yoast SEO' )
|
153 |
+
. '</a>';
|
154 |
+
}
|
155 |
+
|
156 |
+
/* 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. */
|
157 |
+
$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' );
|
158 |
+
|
159 |
+
$instance->check_plugin_conflicts( $plugin_sections );
|
160 |
+
}
|
161 |
+
}
|
admin/class-premium-popup.php
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Premium_popup
|
8 |
+
*/
|
9 |
+
class WPSEO_Premium_Popup {
|
10 |
+
/**
|
11 |
+
* An unique identifier for the popup
|
12 |
+
*
|
13 |
+
* @var string
|
14 |
+
*/
|
15 |
+
private $identifier = '';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* The heading level of the title of the popup.
|
19 |
+
*
|
20 |
+
* @var String
|
21 |
+
*/
|
22 |
+
private $heading_level = '';
|
23 |
+
|
24 |
+
/**
|
25 |
+
* The title of the popup
|
26 |
+
*
|
27 |
+
* @var String
|
28 |
+
*/
|
29 |
+
private $title = '';
|
30 |
+
|
31 |
+
/**
|
32 |
+
* The content of the popup
|
33 |
+
*
|
34 |
+
* @var String
|
35 |
+
*/
|
36 |
+
private $content = '';
|
37 |
+
|
38 |
+
/**
|
39 |
+
* The URL for where the button should link to.
|
40 |
+
*
|
41 |
+
* @var String
|
42 |
+
*/
|
43 |
+
private $url = '';
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Wpseo_Premium_Popup constructor.
|
47 |
+
*
|
48 |
+
* @param String $identifier An unique identifier for the popup.
|
49 |
+
* @param String $heading_level The heading level for the title of the popup.
|
50 |
+
* @param String $title The title of the popup.
|
51 |
+
* @param String $content The content of the popup.
|
52 |
+
* @param String $url The URL for where the button should link to.
|
53 |
+
*/
|
54 |
+
public function __construct( $identifier, $heading_level, $title, $content, $url ) {
|
55 |
+
$this->identifier = $identifier;
|
56 |
+
$this->heading_level = $heading_level;
|
57 |
+
$this->title = $title;
|
58 |
+
$this->content = $content;
|
59 |
+
$this->url = $url;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Returns the premium popup as an HTML string.
|
64 |
+
*
|
65 |
+
* @param bool $popup Show this message as a popup show it straight away.
|
66 |
+
*
|
67 |
+
* @return string
|
68 |
+
*/
|
69 |
+
public function get_premium_message( $popup = true ) {
|
70 |
+
// Don't show in Premium.
|
71 |
+
if ( defined( 'WPSEO_PREMIUM_FILE' ) ) {
|
72 |
+
return '';
|
73 |
+
}
|
74 |
+
|
75 |
+
$assets_uri = trailingslashit( plugin_dir_url( WPSEO_FILE ) );
|
76 |
+
|
77 |
+
/* translators: %s expands to Yoast SEO Premium */
|
78 |
+
$cta_text = sprintf( __( 'Get %s now!', 'wordpress-seo' ), 'Yoast SEO Premium' );
|
79 |
+
$classes = '';
|
80 |
+
if ( $popup ) {
|
81 |
+
$classes = ' hidden';
|
82 |
+
}
|
83 |
+
$micro_copy = __( '1 year free updates and upgrades included!', 'wordpress-seo' );
|
84 |
+
|
85 |
+
$popup = <<<EO_POPUP
|
86 |
+
<div id="wpseo-{$this->identifier}-popup" class="wpseo-premium-popup wp-clearfix$classes">
|
87 |
+
<img class="alignright wpseo-premium-popup-icon" src="{$assets_uri}images/Yoast_SEO_Icon.svg" width="150" height="150" alt="Yoast SEO"/>
|
88 |
+
<{$this->heading_level} id="wpseo-contact-support-popup-title" class="wpseo-premium-popup-title">{$this->title}</{$this->heading_level}>
|
89 |
+
{$this->content}
|
90 |
+
<a id="wpseo-{$this->identifier}-popup-button" class="button button-primary" href="{$this->url}" target="_blank" rel="noreferrer noopener">{$cta_text}</a><br/>
|
91 |
+
<small>{$micro_copy}</small>
|
92 |
+
</div>
|
93 |
+
EO_POPUP;
|
94 |
+
|
95 |
+
return $popup;
|
96 |
+
}
|
97 |
+
}
|
admin/class-premium-upsell-admin-block.php
ADDED
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Premium_Upsell_Admin_Block
|
8 |
+
*/
|
9 |
+
class WPSEO_Premium_Upsell_Admin_Block {
|
10 |
+
/** @var string Hook to display the block on. */
|
11 |
+
protected $hook;
|
12 |
+
|
13 |
+
/** @var string Identifier to use in the dismissal functionality. */
|
14 |
+
protected $identifier = 'premium_upsell_admin_block';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Registers which hook the block will be displayed on.
|
18 |
+
*
|
19 |
+
* @param string $hook Hook to display the block on.
|
20 |
+
*/
|
21 |
+
public function __construct( $hook ) {
|
22 |
+
$this->hook = $hook;
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Registers WordPress hooks.
|
27 |
+
*
|
28 |
+
* @return void
|
29 |
+
*/
|
30 |
+
public function register_hooks() {
|
31 |
+
if ( ! $this->is_hidden() ) {
|
32 |
+
add_action( $this->hook, array( $this, 'render' ) );
|
33 |
+
}
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Renders the upsell block.
|
38 |
+
*
|
39 |
+
* @return void
|
40 |
+
*/
|
41 |
+
public function render() {
|
42 |
+
$url = WPSEO_Shortlinker::get( 'https://yoa.st/17h' );
|
43 |
+
|
44 |
+
$arguments = array(
|
45 |
+
'<strong>' . esc_html__( 'Multiple keywords', 'wordpress-seo' ) . '</strong>: ' . esc_html__( 'Increase your SEO reach', 'wordpress-seo' ),
|
46 |
+
'<strong>' . esc_html__( 'No more dead links', 'wordpress-seo' ) . '</strong>: ' . esc_html__( 'Easy redirect manager', 'wordpress-seo' ),
|
47 |
+
'<strong>' . esc_html__( 'Superfast internal linking suggestions', 'wordpress-seo' ) . '</strong>',
|
48 |
+
'<strong>' . esc_html__( 'Social media preview', 'wordpress-seo' ) . '</strong>: ' . esc_html__( 'Facebook & Twitter', 'wordpress-seo' ),
|
49 |
+
'<strong>' . esc_html__( '24/7 support', 'wordpress-seo' ) . '</strong>',
|
50 |
+
'<strong>' . esc_html__( 'No ads!', 'wordpress-seo' ) . '</strong>',
|
51 |
+
);
|
52 |
+
|
53 |
+
$arguments_html = implode( '', array_map( array( $this, 'get_argument_html' ), $arguments ) );
|
54 |
+
|
55 |
+
$class = $this->get_html_class();
|
56 |
+
|
57 |
+
/* translators: %s expands to "Yoast SEO Premium". */
|
58 |
+
$dismiss_msg = sprintf( __( 'Dismiss %s upgrade motivation', 'wordpress-seo' ), 'Yoast SEO Premium' );
|
59 |
+
/* translators: %s expands to "Yoast SEO Premium". */
|
60 |
+
$upgrade_msg = sprintf( __( 'Find out why you should upgrade to %s »', 'wordpress-seo' ), 'Yoast SEO Premium' );
|
61 |
+
|
62 |
+
echo '<div class="' . esc_attr( $class ) . '">';
|
63 |
+
printf(
|
64 |
+
'<a href="%1$s" style="" class="alignright %2$s" aria-label="%3$s">X</a>',
|
65 |
+
esc_url( add_query_arg( array( $this->get_query_variable_name() => 1 ) ) ),
|
66 |
+
esc_attr( $class . '--close' ),
|
67 |
+
esc_attr( $dismiss_msg )
|
68 |
+
);
|
69 |
+
|
70 |
+
echo '<div>';
|
71 |
+
echo '<h2 class="' . esc_attr( $class . '--header' ) . '">' . esc_html__( 'Go premium!', 'wordpress-seo' ) . '</h2>';
|
72 |
+
echo '<ul class="' . esc_attr( $class . '--motivation' ) . '">' . $arguments_html . '</ul>';
|
73 |
+
|
74 |
+
echo '<p><a href="' . esc_url( $url ) . '" target="_blank">' . esc_html( $upgrade_msg ) . '</a><br />';
|
75 |
+
echo '</div>';
|
76 |
+
|
77 |
+
echo '</div>';
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Formats the argument to a HTML list item.
|
82 |
+
*
|
83 |
+
* @param string $argument The argument to format.
|
84 |
+
*
|
85 |
+
* @return string Formatted argument in HTML.
|
86 |
+
*/
|
87 |
+
protected function get_argument_html( $argument ) {
|
88 |
+
$class = $this->get_html_class();
|
89 |
+
|
90 |
+
return sprintf(
|
91 |
+
'<li><div class="%1$s">%2$s</div></li>',
|
92 |
+
esc_attr( $class . '--argument' ),
|
93 |
+
$argument
|
94 |
+
);
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Checks if the block is hidden by the user.
|
99 |
+
*
|
100 |
+
* @return bool False when it should be shown, True if it should be hidden.
|
101 |
+
*/
|
102 |
+
protected function is_hidden() {
|
103 |
+
$transient_name = $this->get_option_name();
|
104 |
+
|
105 |
+
$hide = (bool) get_user_option( $transient_name );
|
106 |
+
if ( ! $hide ) {
|
107 |
+
$query_variable_name = $this->get_query_variable_name();
|
108 |
+
if ( filter_input( INPUT_GET, $query_variable_name, FILTER_VALIDATE_INT ) === 1 ) {
|
109 |
+
// No expiration time, so this would normally not expire, but it wouldn't be copied to other sites etc.
|
110 |
+
update_user_option( get_current_user_id(), $transient_name, true );
|
111 |
+
$hide = true;
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
return $hide;
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Retrieves the option name to use.
|
120 |
+
*
|
121 |
+
* @return string The name of the option to save the data in.
|
122 |
+
*/
|
123 |
+
protected function get_option_name() {
|
124 |
+
return 'yoast_promo_hide_' . $this->identifier;
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Retrieves the query variable to use for dismissing the block.
|
129 |
+
*
|
130 |
+
* @return string The name of the query variable to use.
|
131 |
+
*/
|
132 |
+
protected function get_query_variable_name() {
|
133 |
+
return 'yoast_promo_hide_' . $this->identifier;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Returns the HTML base class to use.
|
138 |
+
*
|
139 |
+
* @return string The HTML base class.
|
140 |
+
*/
|
141 |
+
protected function get_html_class() {
|
142 |
+
return 'yoast_' . $this->identifier;
|
143 |
+
}
|
144 |
+
}
|
admin/class-primary-term-admin.php
ADDED
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Adds the UI to change the primary term for a post
|
8 |
+
*/
|
9 |
+
class WPSEO_Primary_Term_Admin {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
add_action( 'admin_footer', array( $this, 'wp_footer' ), 10 );
|
16 |
+
|
17 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
18 |
+
|
19 |
+
add_action( 'save_post', array( $this, 'save_primary_terms' ) );
|
20 |
+
|
21 |
+
$primary_term = new WPSEO_Frontend_Primary_Category();
|
22 |
+
$primary_term->register_hooks();
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Get the current post ID.
|
27 |
+
*
|
28 |
+
* @return integer The post ID.
|
29 |
+
*/
|
30 |
+
protected function get_current_id() {
|
31 |
+
$post_id = filter_input( INPUT_GET, 'post', FILTER_SANITIZE_NUMBER_INT );
|
32 |
+
if ( empty( $post_id ) && isset( $GLOBALS['post_ID'] ) ) {
|
33 |
+
$post_id = filter_var( $GLOBALS['post_ID'], FILTER_SANITIZE_NUMBER_INT );
|
34 |
+
}
|
35 |
+
|
36 |
+
return $post_id;
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Add primary term templates
|
41 |
+
*/
|
42 |
+
public function wp_footer() {
|
43 |
+
$taxonomies = $this->get_primary_term_taxonomies();
|
44 |
+
|
45 |
+
if ( ! empty( $taxonomies ) ) {
|
46 |
+
$this->include_js_templates();
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Enqueues all the assets needed for the primary term interface
|
52 |
+
*
|
53 |
+
* @return void
|
54 |
+
*/
|
55 |
+
public function enqueue_assets() {
|
56 |
+
global $pagenow;
|
57 |
+
|
58 |
+
if ( ! WPSEO_Metabox::is_post_edit( $pagenow ) ) {
|
59 |
+
return;
|
60 |
+
}
|
61 |
+
|
62 |
+
$taxonomies = $this->get_primary_term_taxonomies();
|
63 |
+
|
64 |
+
// Only enqueue if there are taxonomies that need a primary term.
|
65 |
+
if ( empty( $taxonomies ) ) {
|
66 |
+
return;
|
67 |
+
}
|
68 |
+
|
69 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
70 |
+
$asset_manager->enqueue_style( 'primary-category' );
|
71 |
+
$asset_manager->enqueue_script( 'primary-category' );
|
72 |
+
|
73 |
+
$taxonomies = array_map( array( $this, 'map_taxonomies_for_js' ), $taxonomies );
|
74 |
+
|
75 |
+
$data = array(
|
76 |
+
'taxonomies' => $taxonomies,
|
77 |
+
);
|
78 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'primary-category', 'wpseoPrimaryCategoryL10n', $data );
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Saves all selected primary terms
|
83 |
+
*
|
84 |
+
* @param int $post_id Post ID to save primary terms for.
|
85 |
+
*/
|
86 |
+
public function save_primary_terms( $post_id ) {
|
87 |
+
// Bail if this is a multisite installation and the site has been switched.
|
88 |
+
if ( is_multisite() && ms_is_switched() ) {
|
89 |
+
return;
|
90 |
+
}
|
91 |
+
|
92 |
+
$taxonomies = $this->get_primary_term_taxonomies( $post_id );
|
93 |
+
|
94 |
+
foreach ( $taxonomies as $taxonomy ) {
|
95 |
+
$this->save_primary_term( $post_id, $taxonomy );
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* /**
|
101 |
+
* Get the id of the primary term
|
102 |
+
*
|
103 |
+
* @param string $taxonomy_name Taxonomy name for the term.
|
104 |
+
*
|
105 |
+
* @return int primary term id
|
106 |
+
*/
|
107 |
+
protected function get_primary_term( $taxonomy_name ) {
|
108 |
+
$primary_term = new WPSEO_Primary_Term( $taxonomy_name, $this->get_current_id() );
|
109 |
+
|
110 |
+
return $primary_term->get_primary_term();
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Returns all the taxonomies for which the primary term selection is enabled
|
115 |
+
*
|
116 |
+
* @param int $post_id Default current post ID.
|
117 |
+
* @return array
|
118 |
+
*/
|
119 |
+
protected function get_primary_term_taxonomies( $post_id = null ) {
|
120 |
+
|
121 |
+
if ( null === $post_id ) {
|
122 |
+
$post_id = $this->get_current_id();
|
123 |
+
}
|
124 |
+
|
125 |
+
$taxonomies = wp_cache_get( 'primary_term_taxonomies_' . $post_id, 'wpseo' );
|
126 |
+
if ( false !== $taxonomies ) {
|
127 |
+
return $taxonomies;
|
128 |
+
}
|
129 |
+
|
130 |
+
$taxonomies = $this->generate_primary_term_taxonomies( $post_id );
|
131 |
+
|
132 |
+
wp_cache_set( 'primary_term_taxonomies_' . $post_id, $taxonomies, 'wpseo' );
|
133 |
+
|
134 |
+
return $taxonomies;
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Include templates file
|
139 |
+
*/
|
140 |
+
protected function include_js_templates() {
|
141 |
+
include_once WPSEO_PATH . 'admin/views/js-templates-primary-term.php';
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Save the primary term for a specific taxonomy
|
146 |
+
*
|
147 |
+
* @param int $post_id Post ID to save primary term for.
|
148 |
+
* @param WP_Term $taxonomy Taxonomy to save primary term for.
|
149 |
+
*/
|
150 |
+
protected function save_primary_term( $post_id, $taxonomy ) {
|
151 |
+
$primary_term = filter_input( INPUT_POST, WPSEO_Meta::$form_prefix . 'primary_' . $taxonomy->name . '_term', FILTER_SANITIZE_NUMBER_INT );
|
152 |
+
|
153 |
+
// We accept an empty string here because we need to save that if no terms are selected.
|
154 |
+
if ( null !== $primary_term && check_admin_referer( 'save-primary-term', WPSEO_Meta::$form_prefix . 'primary_' . $taxonomy->name . '_nonce' ) ) {
|
155 |
+
$primary_term_object = new WPSEO_Primary_Term( $taxonomy->name, $post_id );
|
156 |
+
$primary_term_object->set_primary_term( $primary_term );
|
157 |
+
}
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Generate the primary term taxonomies.
|
162 |
+
*
|
163 |
+
* @param int $post_id ID of the post.
|
164 |
+
*
|
165 |
+
* @return array
|
166 |
+
*/
|
167 |
+
protected function generate_primary_term_taxonomies( $post_id ) {
|
168 |
+
$post_type = get_post_type( $post_id );
|
169 |
+
$all_taxonomies = get_object_taxonomies( $post_type, 'objects' );
|
170 |
+
$all_taxonomies = array_filter( $all_taxonomies, array( $this, 'filter_hierarchical_taxonomies' ) );
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Filters which taxonomies for which the user can choose the primary term.
|
174 |
+
*
|
175 |
+
* @api array $taxonomies An array of taxonomy objects that are primary_term enabled.
|
176 |
+
*
|
177 |
+
* @param string $post_type The post type for which to filter the taxonomies.
|
178 |
+
* @param array $all_taxonomies All taxonomies for this post types, even ones that don't have primary term
|
179 |
+
* enabled.
|
180 |
+
*/
|
181 |
+
$taxonomies = (array) apply_filters( 'wpseo_primary_term_taxonomies', $all_taxonomies, $post_type, $all_taxonomies );
|
182 |
+
|
183 |
+
return $taxonomies;
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Returns an array suitable for use in the javascript
|
188 |
+
*
|
189 |
+
* @param stdClass $taxonomy The taxonomy to map.
|
190 |
+
*
|
191 |
+
* @return array
|
192 |
+
*/
|
193 |
+
private function map_taxonomies_for_js( $taxonomy ) {
|
194 |
+
$primary_term = $this->get_primary_term( $taxonomy->name );
|
195 |
+
|
196 |
+
if ( empty( $primary_term ) ) {
|
197 |
+
$primary_term = '';
|
198 |
+
}
|
199 |
+
|
200 |
+
return array(
|
201 |
+
'title' => $taxonomy->labels->singular_name,
|
202 |
+
'name' => $taxonomy->name,
|
203 |
+
'primary' => $primary_term,
|
204 |
+
'terms' => array_map( array( $this, 'map_terms_for_js' ), get_terms( $taxonomy->name ) ),
|
205 |
+
);
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* Returns an array suitable for use in the javascript
|
210 |
+
*
|
211 |
+
* @param stdClass $term The term to map.
|
212 |
+
*
|
213 |
+
* @return array
|
214 |
+
*/
|
215 |
+
private function map_terms_for_js( $term ) {
|
216 |
+
return array(
|
217 |
+
'id' => $term->term_id,
|
218 |
+
'name' => $term->name,
|
219 |
+
);
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Returns whether or not a taxonomy is hierarchical
|
224 |
+
*
|
225 |
+
* @param stdClass $taxonomy Taxonomy object.
|
226 |
+
*
|
227 |
+
* @return bool
|
228 |
+
*/
|
229 |
+
private function filter_hierarchical_taxonomies( $taxonomy ) {
|
230 |
+
return (bool) $taxonomy->hierarchical;
|
231 |
+
}
|
232 |
+
}
|
admin/class-product-upsell-notice.php
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the upsell notice.
|
8 |
+
*/
|
9 |
+
class WPSEO_Product_Upsell_Notice {
|
10 |
+
|
11 |
+
const USER_META_DISMISSED = 'wpseo-remove-upsell-notice';
|
12 |
+
|
13 |
+
const OPTION_NAME = 'wpseo';
|
14 |
+
|
15 |
+
/** @var array */
|
16 |
+
protected $options;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Sets the options, because they always have to be there on instance.
|
20 |
+
*/
|
21 |
+
public function __construct() {
|
22 |
+
$this->options = $this->get_options();
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Checks if the notice should be added or removed.
|
27 |
+
*/
|
28 |
+
public function initialize() {
|
29 |
+
if ( $this->is_notice_dismissed() ) {
|
30 |
+
$this->remove_notification();
|
31 |
+
|
32 |
+
return;
|
33 |
+
}
|
34 |
+
|
35 |
+
if ( $this->should_add_notification() ) {
|
36 |
+
$this->add_notification();
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Sets the upgrade notice.
|
42 |
+
*/
|
43 |
+
public function set_upgrade_notice() {
|
44 |
+
|
45 |
+
if ( $this->has_first_activated_on() ) {
|
46 |
+
return;
|
47 |
+
}
|
48 |
+
|
49 |
+
$this->set_first_activated_on();
|
50 |
+
$this->add_notification();
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Listener for the upsell notice.
|
55 |
+
*/
|
56 |
+
public function dismiss_notice_listener() {
|
57 |
+
if ( filter_input( INPUT_GET, 'yoast_dismiss' ) !== 'upsell' ) {
|
58 |
+
return;
|
59 |
+
}
|
60 |
+
|
61 |
+
$this->dismiss_notice();
|
62 |
+
|
63 |
+
wp_redirect( admin_url( 'admin.php?page=wpseo_dashboard' ) );
|
64 |
+
exit;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* When the notice should be shown.
|
69 |
+
*
|
70 |
+
* @return bool
|
71 |
+
*/
|
72 |
+
protected function should_add_notification() {
|
73 |
+
return ( $this->options['first_activated_on'] < strtotime( '-2weeks' ) );
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Checks if the options has a first activated on date value.
|
78 |
+
*/
|
79 |
+
protected function has_first_activated_on() {
|
80 |
+
return $this->options['first_activated_on'] !== false;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Sets the first activated on.
|
85 |
+
*/
|
86 |
+
protected function set_first_activated_on() {
|
87 |
+
$this->options['first_activated_on'] = strtotime( '-2weeks' );
|
88 |
+
|
89 |
+
$this->save_options();
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Adds a notification to the notification center.
|
94 |
+
*/
|
95 |
+
protected function add_notification() {
|
96 |
+
$notification_center = Yoast_Notification_Center::get();
|
97 |
+
$notification_center->add_notification( $this->get_notification() );
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Adds a notification to the notification center.
|
102 |
+
*/
|
103 |
+
protected function remove_notification() {
|
104 |
+
$notification_center = Yoast_Notification_Center::get();
|
105 |
+
$notification_center->remove_notification( $this->get_notification() );
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Returns a premium upsell section if using the free plugin.
|
110 |
+
*
|
111 |
+
* @return string
|
112 |
+
*/
|
113 |
+
protected function get_premium_upsell_section() {
|
114 |
+
$features = new WPSEO_Features();
|
115 |
+
if ( $features->is_free() ) {
|
116 |
+
return sprintf(
|
117 |
+
/* translators: %1$s expands anchor to premium plugin page, %2$s expands to </a> */
|
118 |
+
__( '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 keywords. It also comes with 24/7 personal support.', 'wordpress-seo' ),
|
119 |
+
"<a href='" . WPSEO_Shortlinker::get( 'https://yoa.st/premium-notification' ) . "'>",
|
120 |
+
'</a>'
|
121 |
+
);
|
122 |
+
}
|
123 |
+
|
124 |
+
return '';
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Gets the notification value.
|
129 |
+
*
|
130 |
+
* @return Yoast_Notification
|
131 |
+
*/
|
132 |
+
protected function get_notification() {
|
133 |
+
$message = sprintf(
|
134 |
+
/* 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. */
|
135 |
+
__( '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' ),
|
136 |
+
'Yoast SEO',
|
137 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/rate-yoast-seo' ) . '">',
|
138 |
+
'</a>'
|
139 |
+
) . "\n\n";
|
140 |
+
|
141 |
+
$message .= sprintf(
|
142 |
+
/* translators: %1$s is a link start tag to the bugreport guidelines on the Yoast knowledge base, %2$s is the link closing tag. */
|
143 |
+
__( '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' ),
|
144 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/bugreport' ) . '">',
|
145 |
+
'</a>'
|
146 |
+
) . "\n\n";
|
147 |
+
|
148 |
+
$message .= $this->get_premium_upsell_section() . "\n\n";
|
149 |
+
|
150 |
+
$message .= sprintf(
|
151 |
+
/* translators: %1$s is the notification dismissal link start tag, %2$s is the link closing tag. */
|
152 |
+
__( '%1$sPlease don\'t show me this notification anymore%2$s', 'wordpress-seo' ),
|
153 |
+
'<a class="button" href="' . admin_url( '?page=' . WPSEO_Admin::PAGE_IDENTIFIER . '&yoast_dismiss=upsell' ) . '">',
|
154 |
+
'</a>'
|
155 |
+
);
|
156 |
+
|
157 |
+
$notification = new Yoast_Notification(
|
158 |
+
$message,
|
159 |
+
array(
|
160 |
+
'type' => Yoast_Notification::WARNING,
|
161 |
+
'id' => 'wpseo-upsell-notice',
|
162 |
+
'capabilities' => 'wpseo_manage_options',
|
163 |
+
'priority' => 0.8,
|
164 |
+
)
|
165 |
+
);
|
166 |
+
|
167 |
+
return $notification;
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Dismisses the notice.
|
172 |
+
*
|
173 |
+
* @return string
|
174 |
+
*/
|
175 |
+
protected function is_notice_dismissed() {
|
176 |
+
return get_user_meta( get_current_user_id(), self::USER_META_DISMISSED, true ) === '1';
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Dismisses the notice.
|
181 |
+
*/
|
182 |
+
protected function dismiss_notice() {
|
183 |
+
update_user_meta( get_current_user_id(), self::USER_META_DISMISSED, true );
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Returns the set options
|
188 |
+
*
|
189 |
+
* @return mixed|void
|
190 |
+
*/
|
191 |
+
protected function get_options() {
|
192 |
+
return get_option( self::OPTION_NAME );
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Saves the options to the database.
|
197 |
+
*/
|
198 |
+
protected function save_options() {
|
199 |
+
update_option( self::OPTION_NAME, $this->options );
|
200 |
+
}
|
201 |
+
}
|
admin/class-recalculate-scores.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Recalculate_Scores
|
8 |
+
*
|
9 |
+
* This class handles the SEO score recalculation for all posts with a filled focus keyword
|
10 |
+
*/
|
11 |
+
class WPSEO_Recalculate_Scores {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Constructing the object by modalbox, the localization and the totals.
|
15 |
+
*/
|
16 |
+
public function __construct() {
|
17 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'recalculate_assets' ) );
|
18 |
+
add_action( 'admin_footer', array( $this, 'modal_box' ), 20 );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Run the localize script.
|
23 |
+
*/
|
24 |
+
public function recalculate_assets() {
|
25 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
26 |
+
$asset_manager->enqueue_script( 'recalculate' );
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Initialize the modal box to be displayed when needed.
|
31 |
+
*/
|
32 |
+
public function modal_box() {
|
33 |
+
// Adding the thickbox.
|
34 |
+
add_thickbox();
|
35 |
+
|
36 |
+
$progress = sprintf(
|
37 |
+
/* translators: 1: expands to a <span> containing the number of posts recalculated. 2: expands to a <strong> containing the total number of posts. */
|
38 |
+
esc_html__( '%1$s of %2$s done.', 'wordpress-seo' ),
|
39 |
+
'<span id="wpseo_count">0</span>',
|
40 |
+
'<strong id="wpseo_count_total">0</strong>'
|
41 |
+
);
|
42 |
+
|
43 |
+
?>
|
44 |
+
<div id="wpseo_recalculate" class="hidden">
|
45 |
+
<p><?php esc_html_e( 'Recalculating SEO scores for all pieces of content with a focus keyword.', 'wordpress-seo' ); ?></p>
|
46 |
+
|
47 |
+
<div id="wpseo_progressbar"></div>
|
48 |
+
<p><?php echo $progress; ?></p>
|
49 |
+
</div>
|
50 |
+
<?php
|
51 |
+
}
|
52 |
+
}
|
admin/class-remote-request.php
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class handles a post request being send to a given endpoint.
|
8 |
+
*/
|
9 |
+
class WPSEO_Remote_Request {
|
10 |
+
|
11 |
+
const METHOD_POST = 'post';
|
12 |
+
const METHOD_GET = 'get';
|
13 |
+
|
14 |
+
/** @var string */
|
15 |
+
protected $endpoint = '';
|
16 |
+
|
17 |
+
/** @var array */
|
18 |
+
protected $args = array(
|
19 |
+
'blocking' => false,
|
20 |
+
'sslverify' => false,
|
21 |
+
'timeout' => 2,
|
22 |
+
);
|
23 |
+
|
24 |
+
/** @var WP_Error|null */
|
25 |
+
protected $response_error;
|
26 |
+
|
27 |
+
/** @var mixed */
|
28 |
+
protected $response_body;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Sets the endpoint and arguments.
|
32 |
+
*
|
33 |
+
* @param string $endpoint The endpoint to send the request to.
|
34 |
+
* @param array $args The arguments to use in this request.
|
35 |
+
*/
|
36 |
+
public function __construct( $endpoint, array $args = array() ) {
|
37 |
+
$this->endpoint = $endpoint;
|
38 |
+
$this->args = wp_parse_args( $this->args, $args );
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Sets the request body.
|
43 |
+
*
|
44 |
+
* @param mixed $body The body to set.
|
45 |
+
*/
|
46 |
+
public function set_body( $body ) {
|
47 |
+
$this->args['body'] = $body;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Sends the data to the given endpoint.
|
52 |
+
*
|
53 |
+
* @param string $method The type of request to send.
|
54 |
+
*
|
55 |
+
* @return bool True when sending data has been successful.
|
56 |
+
*/
|
57 |
+
public function send( $method = self::METHOD_POST ) {
|
58 |
+
switch ( $method ) {
|
59 |
+
case self::METHOD_POST:
|
60 |
+
$response = $this->post();
|
61 |
+
break;
|
62 |
+
case self::METHOD_GET:
|
63 |
+
$response = $this->get();
|
64 |
+
break;
|
65 |
+
default:
|
66 |
+
/* translators: %1$s expands to the request method */
|
67 |
+
$response = new WP_Error( 1, sprintf( __( 'Request method %1$s is not valid.', 'wordpress-seo' ), $method ) );
|
68 |
+
break;
|
69 |
+
}
|
70 |
+
|
71 |
+
return $this->process_response( $response );
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Returns the value of the response error.
|
76 |
+
*
|
77 |
+
* @return null|WP_Error The response error.
|
78 |
+
*/
|
79 |
+
public function get_response_error() {
|
80 |
+
return $this->response_error;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Returns the response body.
|
85 |
+
*
|
86 |
+
* @return mixed The response body.
|
87 |
+
*/
|
88 |
+
public function get_response_body() {
|
89 |
+
return $this->response_body;
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Processes the given response.
|
94 |
+
*
|
95 |
+
* @param mixed $response The response to process.
|
96 |
+
*
|
97 |
+
* @return bool True when response is valid.
|
98 |
+
*/
|
99 |
+
protected function process_response( $response ) {
|
100 |
+
if ( $response instanceof WP_Error ) {
|
101 |
+
$this->response_error = $response;
|
102 |
+
|
103 |
+
return false;
|
104 |
+
}
|
105 |
+
|
106 |
+
$this->response_body = wp_remote_retrieve_body( $response );
|
107 |
+
|
108 |
+
return ( wp_remote_retrieve_response_code( $response ) === 200 );
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Performs a post request to the specified endpoint with set arguments.
|
113 |
+
*
|
114 |
+
* @return WP_Error|array The response or WP_Error on failure.
|
115 |
+
*/
|
116 |
+
protected function post() {
|
117 |
+
return wp_remote_post( $this->endpoint, $this->args );
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Performs a post request to the specified endpoint with set arguments.
|
122 |
+
*
|
123 |
+
* @return WP_Error|array The response or WP_Error on failure.
|
124 |
+
*/
|
125 |
+
protected function get() {
|
126 |
+
return wp_remote_get( $this->endpoint, $this->args );
|
127 |
+
}
|
128 |
+
}
|
admin/class-social-admin.php
ADDED
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class adds the Social tab to the Yoast SEO metabox and makes sure the settings are saved.
|
8 |
+
*/
|
9 |
+
class WPSEO_Social_Admin extends WPSEO_Metabox {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Class constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
self::translate_meta_boxes();
|
16 |
+
add_filter( 'wpseo_save_metaboxes', array( $this, 'save_meta_boxes' ), 10, 1 );
|
17 |
+
add_action( 'wpseo_save_compare_data', array( $this, 'og_data_compare' ), 10, 1 );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Translate text strings for use in the meta box.
|
22 |
+
*
|
23 |
+
* IMPORTANT: if you want to add a new string (option) somewhere, make sure you add that array key to
|
24 |
+
* the main meta box definition array in the class WPSEO_Meta() as well!!!!
|
25 |
+
*/
|
26 |
+
public static function translate_meta_boxes() {
|
27 |
+
/* translators: %s expands to the social network's name. */
|
28 |
+
$title_text = __( 'If you don\'t want to use the post title for sharing the post on %s but instead want another title there, write it here.', 'wordpress-seo' );
|
29 |
+
|
30 |
+
/* translators: %s expands to the social network's name. */
|
31 |
+
$description_text = __( 'If you don\'t want to use the meta description for sharing the post on %s but want another description there, write it here.', 'wordpress-seo' );
|
32 |
+
|
33 |
+
/* translators: %s expands to the social network's name. */
|
34 |
+
$image_text = __( 'If you want to override the image used on %s for this post, upload / choose an image or add the URL here.', 'wordpress-seo' );
|
35 |
+
|
36 |
+
/* translators: %1$s expands to the social network, %2$s to the recommended image size. */
|
37 |
+
$image_size_text = __( 'The recommended image size for %1$s is %2$s pixels.', 'wordpress-seo' );
|
38 |
+
|
39 |
+
$social_networks = array(
|
40 |
+
'opengraph' => __( 'Facebook', 'wordpress-seo' ),
|
41 |
+
'twitter' => __( 'Twitter', 'wordpress-seo' ),
|
42 |
+
);
|
43 |
+
|
44 |
+
// Source: https://blog.bufferapp.com/ideal-image-sizes-social-media-posts.
|
45 |
+
$recommended_image_sizes = array(
|
46 |
+
/* translators: %1$s expands to the image recommended width, %2$s to its height. */
|
47 |
+
'opengraph' => sprintf( __( '%1$s by %2$s', 'wordpress-seo' ), '1200', '630' ),
|
48 |
+
// Source: https://developers.facebook.com/docs/sharing/best-practices#images.
|
49 |
+
/* translators: %1$s expands to the image recommended width, %2$s to its height. */
|
50 |
+
'twitter' => sprintf( __( '%1$s by %2$s', 'wordpress-seo' ), '1024', '512' ),
|
51 |
+
);
|
52 |
+
|
53 |
+
foreach ( $social_networks as $network => $label ) {
|
54 |
+
if ( true === WPSEO_Options::get( $network, false ) ) {
|
55 |
+
/* translators: %s expands to the name of a social network. */
|
56 |
+
self::$meta_fields['social'][ $network . '-title' ]['title'] = sprintf( __( '%s Title', 'wordpress-seo' ), $label );
|
57 |
+
self::$meta_fields['social'][ $network . '-title' ]['description'] = sprintf( $title_text, $label );
|
58 |
+
|
59 |
+
/* translators: %s expands to the name of a social network. */
|
60 |
+
self::$meta_fields['social'][ $network . '-description' ]['title'] = sprintf( __( '%s Description', 'wordpress-seo' ), $label );
|
61 |
+
self::$meta_fields['social'][ $network . '-description' ]['description'] = sprintf( $description_text, $label );
|
62 |
+
|
63 |
+
/* translators: %s expands to the name of a social network. */
|
64 |
+
self::$meta_fields['social'][ $network . '-image' ]['title'] = sprintf( __( '%s Image', 'wordpress-seo' ), $label );
|
65 |
+
self::$meta_fields['social'][ $network . '-image' ]['description'] = sprintf( $image_text, $label ) . ' ' . sprintf( $image_size_text, $label, $recommended_image_sizes[ $network ] );
|
66 |
+
}
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Returns the metabox section for the social settings.
|
72 |
+
*
|
73 |
+
* @return WPSEO_Metabox_Tab_Section
|
74 |
+
*/
|
75 |
+
public function get_meta_section() {
|
76 |
+
$tabs = array();
|
77 |
+
$social_meta_fields = $this->get_meta_field_defs( 'social' );
|
78 |
+
$single = true;
|
79 |
+
|
80 |
+
$opengraph = WPSEO_Options::get( 'opengraph' );
|
81 |
+
$twitter = WPSEO_Options::get( 'twitter' );
|
82 |
+
|
83 |
+
if ( $opengraph === true && $twitter === true ) {
|
84 |
+
$single = null;
|
85 |
+
}
|
86 |
+
|
87 |
+
if ( $opengraph === true ) {
|
88 |
+
$tabs[] = new WPSEO_Metabox_Form_Tab(
|
89 |
+
'facebook',
|
90 |
+
$this->get_social_tab_content( 'opengraph', $social_meta_fields ),
|
91 |
+
'<span class="screen-reader-text">' . __( 'Facebook / Open Graph metadata', 'wordpress-seo' ) . '</span><span class="dashicons dashicons-facebook-alt"></span>',
|
92 |
+
array(
|
93 |
+
'link_aria_label' => __( 'Facebook / Open Graph metadata', 'wordpress-seo' ),
|
94 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-se',
|
95 |
+
'single' => $single,
|
96 |
+
)
|
97 |
+
);
|
98 |
+
}
|
99 |
+
|
100 |
+
if ( $twitter === true ) {
|
101 |
+
$tabs[] = new WPSEO_Metabox_Form_Tab(
|
102 |
+
'twitter',
|
103 |
+
$this->get_social_tab_content( 'twitter', $social_meta_fields ),
|
104 |
+
'<span class="screen-reader-text">' . __( 'Twitter metadata', 'wordpress-seo' ) . '</span><span class="dashicons dashicons-twitter"></span>',
|
105 |
+
array(
|
106 |
+
'link_aria_label' => __( 'Twitter metadata', 'wordpress-seo' ),
|
107 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-se',
|
108 |
+
'single' => $single,
|
109 |
+
)
|
110 |
+
);
|
111 |
+
}
|
112 |
+
|
113 |
+
return new WPSEO_Metabox_Tab_Section(
|
114 |
+
'social',
|
115 |
+
'<span class="screen-reader-text">' . __( 'Social', 'wordpress-seo' ) . '</span><span class="dashicons dashicons-share"></span>',
|
116 |
+
$tabs,
|
117 |
+
array(
|
118 |
+
'link_aria_label' => __( 'Social', 'wordpress-seo' ),
|
119 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-e',
|
120 |
+
)
|
121 |
+
);
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Generates the html for a social settings tab for one of the supported social media.
|
126 |
+
*
|
127 |
+
* @param string $medium Medium. Can be 'opengraph' or 'twitter'.
|
128 |
+
* @param array $meta_field_defs The social meta field definitions.
|
129 |
+
*
|
130 |
+
* @return string
|
131 |
+
*/
|
132 |
+
private function get_social_tab_content( $medium, $meta_field_defs ) {
|
133 |
+
$field_names = array(
|
134 |
+
$medium . '-title',
|
135 |
+
$medium . '-description',
|
136 |
+
$medium . '-image',
|
137 |
+
);
|
138 |
+
|
139 |
+
$tab_content = $this->get_premium_notice( $medium );
|
140 |
+
|
141 |
+
foreach ( $field_names as $field_name ) {
|
142 |
+
$tab_content .= $this->do_meta_box( $meta_field_defs[ $field_name ], $field_name );
|
143 |
+
}
|
144 |
+
|
145 |
+
return $tab_content;
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Returns the Upgrade to Premium notice.
|
150 |
+
*
|
151 |
+
* @param string $network The social network.
|
152 |
+
*
|
153 |
+
* @return string The notice HTML on the free version, empty string on premium.
|
154 |
+
*/
|
155 |
+
public function get_premium_notice( $network ) {
|
156 |
+
$features = new WPSEO_Features();
|
157 |
+
if ( $features->is_premium() ) {
|
158 |
+
return '';
|
159 |
+
}
|
160 |
+
|
161 |
+
$network_name = __( 'Facebook', 'wordpress-seo' );
|
162 |
+
|
163 |
+
if ( 'twitter' === $network ) {
|
164 |
+
$network_name = __( 'Twitter', 'wordpress-seo' );
|
165 |
+
}
|
166 |
+
|
167 |
+
return sprintf(
|
168 |
+
'<div class="notice inline yoast-notice yoast-notice-go-premium">
|
169 |
+
<p>%1$s</p>
|
170 |
+
<p><a href="%2$s" target="_blank">%3$s</a></p>
|
171 |
+
</div>',
|
172 |
+
sprintf(
|
173 |
+
/* translators: %1$s expands to the social network's name, %2$s to Yoast SEO Premium. */
|
174 |
+
esc_html__( 'Do you want to preview what it will look like if people share this post on %1$s? You can, with %2$s.', 'wordpress-seo' ),
|
175 |
+
esc_html( $network_name ),
|
176 |
+
'<strong>Yoast SEO Premium</strong>'
|
177 |
+
),
|
178 |
+
esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/179' ) ),
|
179 |
+
sprintf(
|
180 |
+
/* translators: %s expands to Yoast SEO Premium. */
|
181 |
+
esc_html__( 'Find out why you should upgrade to %s', 'wordpress-seo' ),
|
182 |
+
'Yoast SEO Premium'
|
183 |
+
)
|
184 |
+
);
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Filter over the meta boxes to save, this function adds the Social meta boxes.
|
189 |
+
*
|
190 |
+
* @param array $field_defs Array of metaboxes to save.
|
191 |
+
*
|
192 |
+
* @return array
|
193 |
+
*/
|
194 |
+
public function save_meta_boxes( $field_defs ) {
|
195 |
+
return array_merge( $field_defs, $this->get_meta_field_defs( 'social' ) );
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* This method will compare opengraph fields with the posted values.
|
200 |
+
*
|
201 |
+
* When fields are changed, the facebook cache will be purge.
|
202 |
+
*
|
203 |
+
* @param WP_Post $post Post instance.
|
204 |
+
*/
|
205 |
+
public function og_data_compare( $post ) {
|
206 |
+
|
207 |
+
// Check if post data is available, if post_id is set and if original post_status is publish.
|
208 |
+
// @codingStandardsIgnoreStart
|
209 |
+
if (
|
210 |
+
! empty( $_POST ) && ! empty( $post->ID ) && $post->post_status === 'publish' &&
|
211 |
+
isset( $_POST['original_post_status'] ) && $_POST['original_post_status'] === 'publish'
|
212 |
+
) {
|
213 |
+
// @codingStandardsIgnoreEnd
|
214 |
+
|
215 |
+
$fields_to_compare = array(
|
216 |
+
'opengraph-title',
|
217 |
+
'opengraph-description',
|
218 |
+
'opengraph-image',
|
219 |
+
);
|
220 |
+
|
221 |
+
$reset_facebook_cache = false;
|
222 |
+
|
223 |
+
foreach ( $fields_to_compare as $field_to_compare ) {
|
224 |
+
$old_value = self::get_value( $field_to_compare, $post->ID );
|
225 |
+
$new_value = self::get_post_value( self::$form_prefix . $field_to_compare );
|
226 |
+
|
227 |
+
if ( $old_value !== $new_value ) {
|
228 |
+
$reset_facebook_cache = true;
|
229 |
+
break;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
unset( $field_to_compare, $old_value, $new_value );
|
233 |
+
|
234 |
+
if ( $reset_facebook_cache ) {
|
235 |
+
wp_remote_get(
|
236 |
+
'https://graph.facebook.com/?id=' . get_permalink( $post->ID ) . '&scrape=true&method=post'
|
237 |
+
);
|
238 |
+
}
|
239 |
+
}
|
240 |
+
}
|
241 |
+
} /* End of class */
|
admin/class-social-facebook-form.php
ADDED
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO
|
4 |
+
* @subpackage Admin
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* This will display the HTML for the facebook insights part
|
9 |
+
*/
|
10 |
+
class Yoast_Social_Facebook_Form {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @var array - The FB admins
|
14 |
+
*/
|
15 |
+
private $fb_admins;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var array - The repository for the buttons that will be shown
|
19 |
+
*/
|
20 |
+
private $buttons = array();
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var string - The URL to link to
|
24 |
+
*/
|
25 |
+
private $admin_url = 'admin.php?page=wpseo_social';
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Setting the FB admins option and call the methods to display everything
|
29 |
+
*/
|
30 |
+
public function __construct() {
|
31 |
+
$this->fb_admins = WPSEO_Options::get( 'fb_admins', array() );
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Returns the output-property
|
36 |
+
*/
|
37 |
+
public function show_form() {
|
38 |
+
$this
|
39 |
+
->form_head()
|
40 |
+
->manage_user_admin()
|
41 |
+
->form_thickbox()
|
42 |
+
->show_buttons()
|
43 |
+
->manage_app_as_admin();
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Parses the admin_link
|
48 |
+
*
|
49 |
+
* @param string $admin_id Facebook admin ID string.
|
50 |
+
* @param array $admin Admin data array.
|
51 |
+
* @param string|bool $nonce Optional nonce string.
|
52 |
+
*
|
53 |
+
* @return string
|
54 |
+
*/
|
55 |
+
public function get_admin_link( $admin_id, $admin, $nonce = false ) {
|
56 |
+
if ( $nonce === false ) {
|
57 |
+
$nonce = $this->get_delete_nonce();
|
58 |
+
}
|
59 |
+
|
60 |
+
$return = '<li><a target="_blank" href="' . esc_url( $admin['link'] ) . '">' . esc_html( $admin['name'] ) . '</a>';
|
61 |
+
$return .= ' - <strong><a href="' . esc_url( $this->admin_delete_link( $admin_id, $nonce ) ) . '">X</a></strong></li>';
|
62 |
+
|
63 |
+
return $return;
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* SHow the top of the social insights part of the page
|
68 |
+
*
|
69 |
+
* @return $this
|
70 |
+
*/
|
71 |
+
private function form_head() {
|
72 |
+
echo '<h2>' . esc_html__( 'Facebook Insights and Admins', 'wordpress-seo' ) . '</h2>';
|
73 |
+
echo '<p>', sprintf(
|
74 |
+
/* translators: %1$s and %2$s expand to a link to Facebook Insights */
|
75 |
+
esc_html__( 'To be able to access %1$sFacebook Insights%2$s for your site, you need to specify a Facebook Admin. This can be a user. If you have an app for your site, you could use that as well.', 'wordpress-seo' ),
|
76 |
+
'<a target="_blank" href="https://www.facebook.com/insights">',
|
77 |
+
'</a>'
|
78 |
+
);
|
79 |
+
echo ' ';
|
80 |
+
printf(
|
81 |
+
/* translators: %1$s and %2$s expand to a link to the Yoast Knowledge Base */
|
82 |
+
esc_html__( 'More info can be found %1$son our knowledge base%2$s.', 'wordpress-seo' ),
|
83 |
+
'<a target="_blank" href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/facebook-insights' ) ) . '">',
|
84 |
+
'</a>'
|
85 |
+
);
|
86 |
+
echo '</p>';
|
87 |
+
|
88 |
+
return $this;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Show the form inside the thickbox
|
93 |
+
*/
|
94 |
+
private function form_thickbox() {
|
95 |
+
// Adding the thickbox.
|
96 |
+
add_thickbox();
|
97 |
+
|
98 |
+
echo '<div id="add_facebook_admin" class="hidden">';
|
99 |
+
echo "<div class='form-wrap wpseo_content_wrapper wpseo-add-fb-admin-form-wrap'>";
|
100 |
+
echo '<p>';
|
101 |
+
printf(
|
102 |
+
/* translators: %1$s and %2$s expand to a link to Facebook Insights */
|
103 |
+
esc_html__( 'To be able to access %1$sFacebook Insights%2$s, you need to add a user here. The name is used for reference only, the ID is used for verification.', 'wordpress-seo' ),
|
104 |
+
'<a target="_blank" href="https://www.facebook.com/insights">',
|
105 |
+
'</a>'
|
106 |
+
);
|
107 |
+
echo '</p>';
|
108 |
+
echo '<p>';
|
109 |
+
printf(
|
110 |
+
/* translators: %1$s and %2$s expand to a link to the Yoast Knowledge Base */
|
111 |
+
esc_html__( 'If you don\'t know where to find the needed ID, see %1$sthis knowledge base article%2$s.', 'wordpress-seo' ),
|
112 |
+
'<a target="_blank" href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/facebook-insights' ) ) . '">',
|
113 |
+
'</a>'
|
114 |
+
);
|
115 |
+
echo '</p>';
|
116 |
+
echo '<div class="form-field form-required">';
|
117 |
+
echo '<label for="fb_admin_name">' . esc_html__( 'Admin\'s name:', 'wordpress-seo' ) . '</label>';
|
118 |
+
echo '<input type="text" id="fb_admin_name" name="fb_admin_name" value="" maxlength="255" />';
|
119 |
+
echo '</div>';
|
120 |
+
echo '<div class="form-field form-required">';
|
121 |
+
echo '<label for="fb_admin_id">' . esc_html__( 'Admin\'s Facebook user ID:', 'wordpress-seo' ) . '</label>';
|
122 |
+
echo '<input type="text" id="fb_admin_id" name="fb_admin_id" value="" maxlength="255" />';
|
123 |
+
echo '</div>';
|
124 |
+
echo "<p class='submit'>";
|
125 |
+
echo '<input type="hidden" name="fb_admin_nonce" value="' . esc_attr( wp_create_nonce( 'wpseo_fb_admin_nonce' ) ) . '" />';
|
126 |
+
echo '<input type="submit" value="' . esc_attr__( 'Add Facebook admin', 'wordpress-seo' ) . '" class="button button-primary" onclick="javascript:wpseo_add_fb_admin();" />';
|
127 |
+
echo '</p>';
|
128 |
+
echo '</div>';
|
129 |
+
echo '</div>';
|
130 |
+
|
131 |
+
return $this;
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Display the buttons to add an admin or add another admin from Facebook and display the admin that has been added already.
|
136 |
+
*
|
137 |
+
* @return $this
|
138 |
+
*/
|
139 |
+
private function manage_user_admin() {
|
140 |
+
$button_text = __( 'Add Facebook admin', 'wordpress-seo' );
|
141 |
+
$nonce = false;
|
142 |
+
$class_attr = ' class="hidden"';
|
143 |
+
|
144 |
+
if ( $this->has_fb_admins() ) {
|
145 |
+
$nonce = $this->get_delete_nonce();
|
146 |
+
$button_text = __( 'Add Another Facebook Admin', 'wordpress-seo' );
|
147 |
+
$class_attr = '';
|
148 |
+
}
|
149 |
+
|
150 |
+
echo "<div id='connected_fb_admins'{$class_attr}>";
|
151 |
+
echo '<p>' . esc_html__( 'Currently connected Facebook admins:', 'wordpress-seo' ) . '</p>';
|
152 |
+
echo '<ul id="user_admin">';
|
153 |
+
$this->show_user_admins( $nonce );
|
154 |
+
echo '</ul>';
|
155 |
+
echo '</div>';
|
156 |
+
|
157 |
+
unset( $nonce );
|
158 |
+
|
159 |
+
$this->add_button(
|
160 |
+
array(
|
161 |
+
'url' => '#TB_inline?width=600&height=350&inlineId=add_facebook_admin',
|
162 |
+
'value' => $button_text,
|
163 |
+
'class' => 'thickbox',
|
164 |
+
'title' => $button_text,
|
165 |
+
)
|
166 |
+
);
|
167 |
+
|
168 |
+
return $this;
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Show input field to set a facebook apps as an admin
|
173 |
+
*
|
174 |
+
* @return $this
|
175 |
+
*/
|
176 |
+
private function manage_app_as_admin() {
|
177 |
+
echo '<div class="clear"></div><br />';
|
178 |
+
Yoast_Form::get_instance()->textinput( 'fbadminapp', __( 'Facebook App ID', 'wordpress-seo' ) );
|
179 |
+
|
180 |
+
return $this;
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Loop through the fb-admins to parse the output for them
|
185 |
+
*
|
186 |
+
* @param string $nonce Nonce string.
|
187 |
+
*/
|
188 |
+
private function show_user_admins( $nonce ) {
|
189 |
+
foreach ( $this->fb_admins as $admin_id => $admin ) {
|
190 |
+
echo $this->get_admin_link( $admin_id, $admin, $nonce );
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Parsing the link that directs to the admin removal
|
196 |
+
*
|
197 |
+
* @param string $admin_id Facebook admin ID.
|
198 |
+
* @param string $nonce Nonce string.
|
199 |
+
*
|
200 |
+
* @return string
|
201 |
+
*/
|
202 |
+
private function admin_delete_link( $admin_id, $nonce ) {
|
203 |
+
return add_query_arg(
|
204 |
+
array(
|
205 |
+
'delfbadmin' => esc_attr( $admin_id ),
|
206 |
+
'nonce' => $nonce,
|
207 |
+
),
|
208 |
+
admin_url( $this->admin_url . '#top#facebook' )
|
209 |
+
);
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Adding a button to the button property
|
214 |
+
*
|
215 |
+
* @param array $args Arguments data array.
|
216 |
+
*/
|
217 |
+
private function add_button( $args ) {
|
218 |
+
$args = wp_parse_args(
|
219 |
+
$args,
|
220 |
+
array(
|
221 |
+
'url' => '',
|
222 |
+
'value' => '',
|
223 |
+
'class' => '',
|
224 |
+
'id' => '',
|
225 |
+
'title' => '',
|
226 |
+
|
227 |
+
)
|
228 |
+
);
|
229 |
+
|
230 |
+
$this->buttons[] = '<a title="' . esc_attr( $args['title'] ) . '" id="' . esc_attr( $args['id'] ) . '" class="button ' . esc_attr( $args['class'] ) . '" href="' . esc_url( $args['url'] ) . '">' . esc_html( $args['value'] ) . '</a>';
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Showing the buttons
|
235 |
+
*/
|
236 |
+
private function show_buttons() {
|
237 |
+
if ( $this->has_fb_admins() ) {
|
238 |
+
$this->add_button(
|
239 |
+
array(
|
240 |
+
'url' => add_query_arg( array(
|
241 |
+
'nonce' => wp_create_nonce( 'fbclearall' ),
|
242 |
+
'fbclearall' => 'true',
|
243 |
+
), admin_url( $this->admin_url . '#top#facebook' ) ),
|
244 |
+
'value' => __( 'Clear all Facebook Data', 'wordpress-seo' ),
|
245 |
+
)
|
246 |
+
);
|
247 |
+
}
|
248 |
+
|
249 |
+
if ( is_array( $this->buttons ) && $this->buttons !== array() ) {
|
250 |
+
echo '<p class="fb-buttons">' . implode( '', $this->buttons ) . '</p>';
|
251 |
+
}
|
252 |
+
|
253 |
+
return $this;
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Check if the clear button should be displayed. This is based on the set options.
|
258 |
+
*
|
259 |
+
* @return bool When fb admins is a valid array.
|
260 |
+
*/
|
261 |
+
private function has_fb_admins() {
|
262 |
+
return is_array( $this->fb_admins ) && $this->fb_admins !== array();
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* Creates nonce for removal link
|
267 |
+
*
|
268 |
+
* @return mixed
|
269 |
+
*/
|
270 |
+
private function get_delete_nonce() {
|
271 |
+
return wp_create_nonce( 'delfbadmin' );
|
272 |
+
}
|
273 |
+
}
|
admin/class-social-facebook.php
ADDED
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO
|
4 |
+
* @subpackage Admin
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* The Facebook insights class, this will add some listeners to fetch GET params
|
9 |
+
*/
|
10 |
+
class Yoast_Social_Facebook {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @var Yoast_Social_Facebook_Form
|
14 |
+
*/
|
15 |
+
private $form;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Setting the options and define the listener to fetch $_GET values
|
19 |
+
*/
|
20 |
+
public function __construct() {
|
21 |
+
$this->get_listener();
|
22 |
+
|
23 |
+
$this->form = new Yoast_Social_Facebook_Form();
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Returns the output from the form class
|
28 |
+
*/
|
29 |
+
public function show_form() {
|
30 |
+
$this->form->show_form();
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Adding a new admin
|
35 |
+
*
|
36 |
+
* @param string $admin_name Name string.
|
37 |
+
* @param string $admin_id ID string.
|
38 |
+
*
|
39 |
+
* @return string
|
40 |
+
*/
|
41 |
+
public function add_admin( $admin_name, $admin_id ) {
|
42 |
+
$success = 0;
|
43 |
+
|
44 |
+
// If one of the fields is empty.
|
45 |
+
if ( empty( $admin_name ) || empty( $admin_id ) ) {
|
46 |
+
$response_body = $this->get_response_body( 'not_present' );
|
47 |
+
}
|
48 |
+
else {
|
49 |
+
$admin_id = $this->parse_admin_id( $admin_id );
|
50 |
+
$option = WPSEO_Options::get( 'fb_admins' );
|
51 |
+
|
52 |
+
if ( ! isset( $option[ $admin_id ] ) ) {
|
53 |
+
$name = sanitize_text_field( urldecode( $admin_name ) );
|
54 |
+
$admin_id = sanitize_text_field( $admin_id );
|
55 |
+
|
56 |
+
if ( preg_match( '/[0-9]+?/', $admin_id ) && preg_match( '/[\w\s]+?/', $name ) ) {
|
57 |
+
$option[ $admin_id ]['name'] = $name;
|
58 |
+
$option[ $admin_id ]['link'] = urldecode( 'http://www.facebook.com/' . $admin_id );
|
59 |
+
WPSEO_Options::set( 'fb_admins', $option );
|
60 |
+
|
61 |
+
$success = 1;
|
62 |
+
$response_body = $this->form->get_admin_link( $admin_id, $option[ $admin_id ] );
|
63 |
+
}
|
64 |
+
else {
|
65 |
+
$response_body = $this->get_response_body( 'invalid_format' );
|
66 |
+
}
|
67 |
+
}
|
68 |
+
else {
|
69 |
+
$response_body = $this->get_response_body( 'already_exists' );
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
return wp_json_encode(
|
74 |
+
array(
|
75 |
+
'success' => $success,
|
76 |
+
'html' => $response_body,
|
77 |
+
)
|
78 |
+
);
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Fetches the id if the full meta tag or a full url was given
|
83 |
+
*
|
84 |
+
* @param string $admin_id Admin ID input string to process.
|
85 |
+
*
|
86 |
+
* @return string
|
87 |
+
*/
|
88 |
+
private function parse_admin_id( $admin_id ) {
|
89 |
+
if ( preg_match( '/^\<meta property\=\"fb:admins\" content\=\"(\d+?)\"/', $admin_id, $matches_full_meta ) ) {
|
90 |
+
return $matches_full_meta[1];
|
91 |
+
}
|
92 |
+
|
93 |
+
return trim( wp_parse_url( $admin_id, PHP_URL_PATH ), '/' );
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Returns a different response body depending on the response type
|
98 |
+
*
|
99 |
+
* @param string $type Type string.
|
100 |
+
*
|
101 |
+
* @return string
|
102 |
+
*/
|
103 |
+
private function get_response_body( $type ) {
|
104 |
+
switch ( $type ) {
|
105 |
+
case 'not_present':
|
106 |
+
$return = "<p class='notice-error notice'><span>" . __( 'Please make sure both fields are filled.', 'wordpress-seo' ) . '</span></p>';
|
107 |
+
break;
|
108 |
+
case 'invalid_format':
|
109 |
+
$return = "<p class='notice-error notice'><span>" . __( 'Your input contains invalid characters. Please make sure both fields are filled in correctly.', 'wordpress-seo' ) . '</span></p>';
|
110 |
+
break;
|
111 |
+
case 'already_exists':
|
112 |
+
$return = "<p class='notice-error notice'><span>" . __( 'This Facebook user has already been added as an admin.', 'wordpress-seo' ) . '</span></p>';
|
113 |
+
break;
|
114 |
+
default:
|
115 |
+
$return = '';
|
116 |
+
break;
|
117 |
+
}
|
118 |
+
|
119 |
+
return $return;
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* This method will hook into the defined get params
|
124 |
+
*/
|
125 |
+
private function get_listener() {
|
126 |
+
$delfbadmin = filter_input( INPUT_GET, 'delfbadmin' );
|
127 |
+
if ( ! empty( $delfbadmin ) ) {
|
128 |
+
$this->delete_admin( $delfbadmin );
|
129 |
+
}
|
130 |
+
elseif ( filter_input( INPUT_GET, 'fbclearall' ) ) {
|
131 |
+
$this->clear_all();
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Deletes the admin from the options
|
137 |
+
*
|
138 |
+
* @param string $delfbadmin Facebook admin ID.
|
139 |
+
*/
|
140 |
+
private function delete_admin( $delfbadmin ) {
|
141 |
+
$this->verify_nonce( 'delfbadmin' );
|
142 |
+
|
143 |
+
$admin_id = sanitize_text_field( $delfbadmin );
|
144 |
+
$option = WPSEO_Options::get( 'fb_admins' );
|
145 |
+
|
146 |
+
if ( isset( $option[ $admin_id ] ) ) {
|
147 |
+
$fbadmin = $option[ $admin_id ]['name'];
|
148 |
+
unset( $option[ $admin_id ][ $admin_id ] );
|
149 |
+
WPSEO_Options::set( 'fb_admins', $option );
|
150 |
+
|
151 |
+
/* translators: %s expands to the username of the removed Facebook admin. */
|
152 |
+
$this->success_notice( sprintf( __( 'Successfully removed admin %s', 'wordpress-seo' ), $fbadmin ) );
|
153 |
+
|
154 |
+
unset( $fbadmin );
|
155 |
+
}
|
156 |
+
|
157 |
+
unset( $admin_id );
|
158 |
+
|
159 |
+
// Clean up the referrer url for later use.
|
160 |
+
if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
|
161 |
+
$this->cleanup_referrer_url( 'nonce', 'delfbadmin' );
|
162 |
+
}
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Clear all the facebook that has been set already
|
167 |
+
*/
|
168 |
+
private function clear_all() {
|
169 |
+
$this->verify_nonce( 'fbclearall' );
|
170 |
+
|
171 |
+
// Reset to defaults, don't unset as otherwise the old values will be retained.
|
172 |
+
WPSEO_Options::set( 'fb_admins', WPSEO_Options::get_default( 'wpseo_social', 'fb_admins' ) );
|
173 |
+
|
174 |
+
$this->success_notice( __( 'Successfully cleared all Facebook Data', 'wordpress-seo' ) );
|
175 |
+
|
176 |
+
// Clean up the referrer url for later use.
|
177 |
+
if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
|
178 |
+
$this->cleanup_referrer_url( 'nonce', 'fbclearall' );
|
179 |
+
}
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Clean up the request_uri. The given params are the params that will be removed from the URL
|
184 |
+
*/
|
185 |
+
private function cleanup_referrer_url() {
|
186 |
+
$_SERVER['REQUEST_URI'] = remove_query_arg(
|
187 |
+
func_get_args(),
|
188 |
+
sanitize_text_field( $_SERVER['REQUEST_URI'] )
|
189 |
+
);
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* When something is going well, show a success notice
|
194 |
+
*
|
195 |
+
* @param string $notice_text Message string.
|
196 |
+
*/
|
197 |
+
private function success_notice( $notice_text ) {
|
198 |
+
add_settings_error( 'yoast_wpseo_social_options', 'success', $notice_text, 'updated' );
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Verify the nonce from the URL with the saved nonce
|
203 |
+
*
|
204 |
+
* @param string $nonce_name Nonce name string.
|
205 |
+
*/
|
206 |
+
private function verify_nonce( $nonce_name ) {
|
207 |
+
if ( wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), $nonce_name ) !== 1 ) {
|
208 |
+
die( "I don't think that's really nice of you!." );
|
209 |
+
}
|
210 |
+
}
|
211 |
+
}
|
admin/class-suggested-plugins.php
ADDED
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Suggested_Plugins
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Suggested_Plugins
|
8 |
+
*/
|
9 |
+
class WPSEO_Suggested_Plugins implements WPSEO_WordPress_Integration {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WPSEO_Plugin_Availability
|
13 |
+
*/
|
14 |
+
protected $availability_checker;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var Yoast_Notification_Center
|
18 |
+
*/
|
19 |
+
protected $notification_center;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* WPSEO_Suggested_Plugins constructor.
|
23 |
+
*
|
24 |
+
* @param WPSEO_Plugin_Availability $availability_checker The availability checker to use.
|
25 |
+
* @param Yoast_Notification_Center $notification_center The notification center to add notifications to.
|
26 |
+
*/
|
27 |
+
public function __construct( WPSEO_Plugin_Availability $availability_checker, Yoast_Notification_Center $notification_center ) {
|
28 |
+
$this->availability_checker = $availability_checker;
|
29 |
+
$this->notification_center = $notification_center;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Registers all hooks to WordPress.
|
34 |
+
*
|
35 |
+
* @return void
|
36 |
+
*/
|
37 |
+
public function register_hooks() {
|
38 |
+
add_action( 'admin_init', array( $this->availability_checker, 'register' ) );
|
39 |
+
add_action( 'admin_init', array( $this, 'add_notifications' ) );
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Adds notifications (when necessary).
|
44 |
+
*
|
45 |
+
* @return void
|
46 |
+
*/
|
47 |
+
public function add_notifications() {
|
48 |
+
$checker = $this->availability_checker;
|
49 |
+
|
50 |
+
// Get all Yoast plugins that have dependencies.
|
51 |
+
$plugins = $checker->get_plugins_with_dependencies();
|
52 |
+
|
53 |
+
foreach ( $plugins as $plugin_name => $plugin ) {
|
54 |
+
if ( ! $checker->dependencies_are_satisfied( $plugin ) ) {
|
55 |
+
continue;
|
56 |
+
}
|
57 |
+
|
58 |
+
$dependency_names = $checker->get_dependency_names( $plugin );
|
59 |
+
$notification = $this->get_yoast_seo_suggested_plugins_notification( $plugin_name, $plugin, $dependency_names[0] );
|
60 |
+
|
61 |
+
if ( ! $checker->is_installed( $plugin ) || ! $checker->is_active( $plugin['slug'] ) ) {
|
62 |
+
$this->notification_center->add_notification( $notification );
|
63 |
+
|
64 |
+
continue;
|
65 |
+
}
|
66 |
+
|
67 |
+
$this->notification_center->remove_notification( $notification );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Build Yoast SEO suggested plugins notification.
|
73 |
+
*
|
74 |
+
* @param string $name The plugin name to use for the unique ID.
|
75 |
+
* @param array $plugin The plugin to retrieve the data from.
|
76 |
+
* @param string $dependency_name The name of the dependency.
|
77 |
+
*
|
78 |
+
* @return Yoast_Notification The notification containing the suggested plugin.
|
79 |
+
*/
|
80 |
+
protected function get_yoast_seo_suggested_plugins_notification( $name, $plugin, $dependency_name ) {
|
81 |
+
$message = $this->create_install_suggested_plugin_message( $plugin, $dependency_name );
|
82 |
+
|
83 |
+
if ( $this->availability_checker->is_installed( $plugin ) && ! $this->availability_checker->is_active( $plugin['slug'] ) ) {
|
84 |
+
$message = $this->create_activate_suggested_plugin_message( $plugin, $dependency_name );
|
85 |
+
}
|
86 |
+
|
87 |
+
return new Yoast_Notification(
|
88 |
+
$message,
|
89 |
+
array(
|
90 |
+
'id' => 'wpseo-suggested-plugin-' . $name,
|
91 |
+
'type' => Yoast_Notification::WARNING,
|
92 |
+
'capabilities' => array( 'install_plugins' ),
|
93 |
+
)
|
94 |
+
);
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Creates a message to suggest the installation of a particular plugin.
|
99 |
+
*
|
100 |
+
* @param array $suggested_plugin The suggested plugin.
|
101 |
+
* @param array $third_party_plugin The third party plugin that we have a suggested plugin for.
|
102 |
+
*
|
103 |
+
* @return string The install suggested plugin message.
|
104 |
+
*/
|
105 |
+
protected function create_install_suggested_plugin_message( $suggested_plugin, $third_party_plugin ) {
|
106 |
+
/* translators: %1$s expands to Yoast SEO, %2$s expands to the dependency name, %3$s expands to the install link, %4$s expands to the more info link. */
|
107 |
+
$message = __( '%1$s and %2$s can work together a lot better by adding a helper plugin. Please install %3$s to make your life better. %4$s.', 'wordpress-seo' );
|
108 |
+
$install_link = WPSEO_Admin_Utils::get_install_link( $suggested_plugin );
|
109 |
+
|
110 |
+
return sprintf(
|
111 |
+
$message,
|
112 |
+
'Yoast SEO',
|
113 |
+
$third_party_plugin,
|
114 |
+
$install_link,
|
115 |
+
$this->create_more_information_link( $suggested_plugin['url'], $suggested_plugin['title'] )
|
116 |
+
);
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Creates a more information link that directs the user to WordPress.org Plugin repository.
|
121 |
+
*
|
122 |
+
* @param string $url The URL to the plugin's page.
|
123 |
+
* @param string $name The name of the plugin.
|
124 |
+
*
|
125 |
+
* @return string The more information link.
|
126 |
+
*/
|
127 |
+
protected function create_more_information_link( $url, $name ) {
|
128 |
+
return sprintf(
|
129 |
+
'<a href="%s" aria-label="%s" target="_blank" rel="noopener noreferrer">%s</a>',
|
130 |
+
$url,
|
131 |
+
/* translators: %1$s expands to the dependency name. */
|
132 |
+
sprintf( __( 'More information about %1$s', 'wordpress-seo' ), $name ),
|
133 |
+
__( 'More information', 'wordpress-seo' )
|
134 |
+
);
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Creates a message to suggest the activation of a particular plugin.
|
139 |
+
*
|
140 |
+
* @param array $suggested_plugin The suggested plugin.
|
141 |
+
* @param array $third_party_plugin The third party plugin that we have a suggested plugin for.
|
142 |
+
*
|
143 |
+
* @return string The activate suggested plugin message.
|
144 |
+
*/
|
145 |
+
protected function create_activate_suggested_plugin_message( $suggested_plugin, $third_party_plugin ) {
|
146 |
+
/* translators: %1$s expands to Yoast SEO, %2$s expands to the dependency name, %3$s expands to activation link. */
|
147 |
+
$message = __( '%1$s and %2$s can work together a lot better by adding a helper plugin. Please activate %3$s to make your life better.', 'wordpress-seo' );
|
148 |
+
$activation_url = WPSEO_Admin_Utils::get_activation_url( $suggested_plugin['slug'] );
|
149 |
+
|
150 |
+
return sprintf(
|
151 |
+
$message,
|
152 |
+
'Yoast SEO',
|
153 |
+
$third_party_plugin,
|
154 |
+
sprintf( '<a href="%s">%s</a>', $activation_url, $suggested_plugin['title'] )
|
155 |
+
);
|
156 |
+
}
|
157 |
+
}
|
admin/class-yoast-alerts.php
ADDED
@@ -0,0 +1,258 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Notifications
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Yoast_Alerts
|
8 |
+
*/
|
9 |
+
class Yoast_Alerts {
|
10 |
+
|
11 |
+
const ADMIN_PAGE = 'wpseo_dashboard';
|
12 |
+
|
13 |
+
/** @var int Total notifications count */
|
14 |
+
private static $notification_count = 0;
|
15 |
+
|
16 |
+
/** @var array All error notifications */
|
17 |
+
private static $errors = array();
|
18 |
+
/** @var array Active errors */
|
19 |
+
private static $active_errors = array();
|
20 |
+
/** @var array Dismissed errors */
|
21 |
+
private static $dismissed_errors = array();
|
22 |
+
|
23 |
+
/** @var array All warning notifications */
|
24 |
+
private static $warnings = array();
|
25 |
+
/** @var array Active warnings */
|
26 |
+
private static $active_warnings = array();
|
27 |
+
/** @var array Dismissed warnings */
|
28 |
+
private static $dismissed_warnings = array();
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Yoast_Alerts constructor.
|
32 |
+
*/
|
33 |
+
public function __construct() {
|
34 |
+
|
35 |
+
$this->add_hooks();
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Add hooks
|
40 |
+
*/
|
41 |
+
private function add_hooks() {
|
42 |
+
|
43 |
+
$page = filter_input( INPUT_GET, 'page' );
|
44 |
+
if ( self::ADMIN_PAGE === $page ) {
|
45 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
46 |
+
}
|
47 |
+
|
48 |
+
// Needed for adminbar and Alerts page.
|
49 |
+
add_action( 'admin_init', array( __CLASS__, 'collect_alerts' ), 99 );
|
50 |
+
|
51 |
+
// Add AJAX hooks.
|
52 |
+
add_action( 'wp_ajax_yoast_dismiss_alert', array( $this, 'ajax_dismiss_alert' ) );
|
53 |
+
add_action( 'wp_ajax_yoast_restore_alert', array( $this, 'ajax_restore_alert' ) );
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Enqueue assets
|
58 |
+
*/
|
59 |
+
public function enqueue_assets() {
|
60 |
+
|
61 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
62 |
+
$asset_manager->enqueue_style( 'alerts' );
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Handle ajax request to dismiss an alert
|
67 |
+
*/
|
68 |
+
public function ajax_dismiss_alert() {
|
69 |
+
|
70 |
+
$notification = $this->get_notification_from_ajax_request();
|
71 |
+
if ( $notification ) {
|
72 |
+
$notification_center = Yoast_Notification_Center::get();
|
73 |
+
$notification_center->maybe_dismiss_notification( $notification );
|
74 |
+
|
75 |
+
$this->output_ajax_response( $notification->get_type() );
|
76 |
+
}
|
77 |
+
|
78 |
+
wp_die();
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Handle ajax request to restore an alert
|
83 |
+
*/
|
84 |
+
public function ajax_restore_alert() {
|
85 |
+
|
86 |
+
$notification = $this->get_notification_from_ajax_request();
|
87 |
+
if ( $notification ) {
|
88 |
+
delete_user_meta( get_current_user_id(), $notification->get_dismissal_key() );
|
89 |
+
|
90 |
+
$this->output_ajax_response( $notification->get_type() );
|
91 |
+
}
|
92 |
+
|
93 |
+
wp_die();
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Create AJAX response data
|
98 |
+
*
|
99 |
+
* @param string $type Alert type.
|
100 |
+
*/
|
101 |
+
private function output_ajax_response( $type ) {
|
102 |
+
|
103 |
+
$html = $this->get_view_html( $type );
|
104 |
+
echo wp_json_encode(
|
105 |
+
array(
|
106 |
+
'html' => $html,
|
107 |
+
'total' => self::get_active_alert_count(),
|
108 |
+
)
|
109 |
+
);
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Get the HTML to return in the AJAX request
|
114 |
+
*
|
115 |
+
* @param string $type Alert type.
|
116 |
+
*
|
117 |
+
* @return bool|string
|
118 |
+
*/
|
119 |
+
private function get_view_html( $type ) {
|
120 |
+
|
121 |
+
switch ( $type ) {
|
122 |
+
case 'error':
|
123 |
+
$view = 'errors';
|
124 |
+
break;
|
125 |
+
|
126 |
+
case 'warning':
|
127 |
+
default:
|
128 |
+
$view = 'warnings';
|
129 |
+
break;
|
130 |
+
}
|
131 |
+
|
132 |
+
// Re-collect alerts.
|
133 |
+
self::collect_alerts();
|
134 |
+
|
135 |
+
/** @noinspection PhpUnusedLocalVariableInspection */
|
136 |
+
$alerts_data = self::get_template_variables();
|
137 |
+
|
138 |
+
ob_start();
|
139 |
+
include WPSEO_PATH . 'admin/views/partial-alerts-' . $view . '.php';
|
140 |
+
$html = ob_get_clean();
|
141 |
+
|
142 |
+
return $html;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Extract the Yoast Notification from the AJAX request
|
147 |
+
*
|
148 |
+
* @return null|Yoast_Notification
|
149 |
+
*/
|
150 |
+
private function get_notification_from_ajax_request() {
|
151 |
+
|
152 |
+
$notification_center = Yoast_Notification_Center::get();
|
153 |
+
$notification_id = filter_input( INPUT_POST, 'notification' );
|
154 |
+
|
155 |
+
return $notification_center->get_notification_by_id( $notification_id );
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* Show the alerts overview page
|
160 |
+
*/
|
161 |
+
public static function show_overview_page() {
|
162 |
+
|
163 |
+
/** @noinspection PhpUnusedLocalVariableInspection */
|
164 |
+
$alerts_data = self::get_template_variables();
|
165 |
+
|
166 |
+
include WPSEO_PATH . 'admin/views/alerts-dashboard.php';
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Collect the alerts and group them together
|
171 |
+
*/
|
172 |
+
public static function collect_alerts() {
|
173 |
+
|
174 |
+
$notification_center = Yoast_Notification_Center::get();
|
175 |
+
|
176 |
+
$notifications = $notification_center->get_sorted_notifications();
|
177 |
+
self::$notification_count = count( $notifications );
|
178 |
+
|
179 |
+
self::$errors = array_filter( $notifications, array( __CLASS__, 'filter_error_alerts' ) );
|
180 |
+
self::$dismissed_errors = array_filter( self::$errors, array( __CLASS__, 'filter_dismissed_alerts' ) );
|
181 |
+
self::$active_errors = array_diff( self::$errors, self::$dismissed_errors );
|
182 |
+
|
183 |
+
self::$warnings = array_filter( $notifications, array( __CLASS__, 'filter_warning_alerts' ) );
|
184 |
+
self::$dismissed_warnings = array_filter( self::$warnings, array( __CLASS__, 'filter_dismissed_alerts' ) );
|
185 |
+
self::$active_warnings = array_diff( self::$warnings, self::$dismissed_warnings );
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Get the variables needed in the views
|
190 |
+
*
|
191 |
+
* @return array
|
192 |
+
*/
|
193 |
+
public static function get_template_variables() {
|
194 |
+
|
195 |
+
return array(
|
196 |
+
'metrics' => array(
|
197 |
+
'total' => self::$notification_count,
|
198 |
+
'active' => self::get_active_alert_count(),
|
199 |
+
'errors' => count( self::$errors ),
|
200 |
+
'warnings' => count( self::$warnings ),
|
201 |
+
),
|
202 |
+
'errors' => array(
|
203 |
+
'dismissed' => self::$dismissed_errors,
|
204 |
+
'active' => self::$active_errors,
|
205 |
+
),
|
206 |
+
'warnings' => array(
|
207 |
+
'dismissed' => self::$dismissed_warnings,
|
208 |
+
'active' => self::$active_warnings,
|
209 |
+
),
|
210 |
+
);
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Get the number of active alerts
|
215 |
+
*
|
216 |
+
* @return int
|
217 |
+
*/
|
218 |
+
public static function get_active_alert_count() {
|
219 |
+
|
220 |
+
return ( count( self::$active_errors ) + count( self::$active_warnings ) );
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* Filter out any non-errors
|
225 |
+
*
|
226 |
+
* @param Yoast_Notification $notification Notification to test.
|
227 |
+
*
|
228 |
+
* @return bool
|
229 |
+
*/
|
230 |
+
private static function filter_error_alerts( Yoast_Notification $notification ) {
|
231 |
+
|
232 |
+
return $notification->get_type() === 'error';
|
233 |
+
}
|
234 |
+
|
235 |
+
/**
|
236 |
+
* Filter out any non-warnings
|
237 |
+
*
|
238 |
+
* @param Yoast_Notification $notification Notification to test.
|
239 |
+
*
|
240 |
+
* @return bool
|
241 |
+
*/
|
242 |
+
private static function filter_warning_alerts( Yoast_Notification $notification ) {
|
243 |
+
|
244 |
+
return $notification->get_type() !== 'error';
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Filter out any dismissed notifications
|
249 |
+
*
|
250 |
+
* @param Yoast_Notification $notification Notification to test.
|
251 |
+
*
|
252 |
+
* @return bool
|
253 |
+
*/
|
254 |
+
private static function filter_dismissed_alerts( Yoast_Notification $notification ) {
|
255 |
+
|
256 |
+
return Yoast_Notification_Center::is_notification_dismissed( $notification );
|
257 |
+
}
|
258 |
+
}
|
admin/class-yoast-columns.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the yoast columns.
|
8 |
+
*/
|
9 |
+
class WPSEO_Yoast_Columns implements WPSEO_WordPress_Integration {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Registers all hooks to WordPress
|
13 |
+
*/
|
14 |
+
public function register_hooks() {
|
15 |
+
add_action( 'load-edit.php', array( $this, 'add_help_tab' ) );
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Adds the help tab to the help center for current screen.
|
20 |
+
*/
|
21 |
+
public function add_help_tab() {
|
22 |
+
$screen = get_current_screen();
|
23 |
+
$screen->add_help_tab(
|
24 |
+
array(
|
25 |
+
/* translators: %s expands to Yoast */
|
26 |
+
'title' => sprintf( __( '%s Columns', 'wordpress-seo' ), 'Yoast' ),
|
27 |
+
'id' => 'yst-columns',
|
28 |
+
'content' => sprintf(
|
29 |
+
/* translators: %1$s: Yoast SEO, %2$s: Link to article about content analysis, %3$s: Anchor closing, %4$s: Link to article about text links, %5$s: Emphasis open tag, %6$s: Emphasis close tag */
|
30 |
+
'<p>' . __( '%1$s adds several columns to this page. We\'ve written an article about %2$show to use the SEO score and Readability score%3$s. The links columns show the number of articles on this site linking %5$sto%6$s this article and the number of URLs linked %5$sfrom%6$s this article. Learn more about %4$show to use these features to improve your internal linking%3$s, which greatly enhances your SEO.', 'wordpress-seo' ) . '</p>',
|
31 |
+
'Yoast SEO',
|
32 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/16p' ) . '">',
|
33 |
+
'</a>',
|
34 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/16q' ) . '">',
|
35 |
+
'<em>',
|
36 |
+
'</em>'
|
37 |
+
),
|
38 |
+
'priority' => 15,
|
39 |
+
)
|
40 |
+
);
|
41 |
+
}
|
42 |
+
}
|
admin/class-yoast-dashboard-widget.php
ADDED
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class to change or add WordPress dashboard widgets
|
8 |
+
*/
|
9 |
+
class Yoast_Dashboard_Widget {
|
10 |
+
|
11 |
+
const CACHE_TRANSIENT_KEY = 'wpseo-dashboard-totals';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var WPSEO_Admin_Asset_Manager
|
15 |
+
*/
|
16 |
+
protected $asset_manager;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var WPSEO_Statistics
|
20 |
+
*/
|
21 |
+
protected $statistics;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @param WPSEO_Statistics $statistics The statistics class to retrieve statistics from.
|
25 |
+
*/
|
26 |
+
public function __construct( WPSEO_Statistics $statistics = null ) {
|
27 |
+
if ( null === $statistics ) {
|
28 |
+
$statistics = new WPSEO_Statistics();
|
29 |
+
}
|
30 |
+
|
31 |
+
$this->statistics = $statistics;
|
32 |
+
$this->asset_manager = new WPSEO_Admin_Asset_Manager();
|
33 |
+
|
34 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_dashboard_assets' ) );
|
35 |
+
add_action( 'admin_init', array( $this, 'queue_dashboard_widget' ) );
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Adds the dashboard widget if it should be shown.
|
40 |
+
*
|
41 |
+
* @return void
|
42 |
+
*/
|
43 |
+
public function queue_dashboard_widget() {
|
44 |
+
if ( $this->show_widget() ) {
|
45 |
+
add_action( 'wp_dashboard_setup', array( $this, 'add_dashboard_widget' ) );
|
46 |
+
}
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Adds dashboard widget to WordPress
|
51 |
+
*/
|
52 |
+
public function add_dashboard_widget() {
|
53 |
+
add_filter( 'postbox_classes_dashboard_wpseo-dashboard-overview', array( $this, 'wpseo_dashboard_overview_class' ) );
|
54 |
+
wp_add_dashboard_widget(
|
55 |
+
'wpseo-dashboard-overview',
|
56 |
+
/* translators: %s is the plugin name */
|
57 |
+
sprintf( __( '%s Posts Overview', 'wordpress-seo' ), 'Yoast SEO' ),
|
58 |
+
array( $this, 'display_dashboard_widget' )
|
59 |
+
);
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Adds CSS classes to the dashboard widget.
|
64 |
+
*
|
65 |
+
* @param array $classes An array of postbox CSS classes.
|
66 |
+
*
|
67 |
+
* @return array
|
68 |
+
*/
|
69 |
+
public function wpseo_dashboard_overview_class( $classes ) {
|
70 |
+
$classes[] = 'yoast wpseo-dashboard-overview';
|
71 |
+
return $classes;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Displays the dashboard widget.
|
76 |
+
*/
|
77 |
+
public function display_dashboard_widget() {
|
78 |
+
echo '<div id="yoast-seo-dashboard-widget"></div>';
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Enqueues stylesheet for the dashboard if the current page is the dashboard.
|
83 |
+
*/
|
84 |
+
public function enqueue_dashboard_stylesheets() {
|
85 |
+
_deprecated_function( __METHOD__, 'WPSEO 5.5', 'This method is deprecated, please use the <code>enqueue_dashboard_assets</code> method.' );
|
86 |
+
|
87 |
+
if ( ! $this->is_dashboard_screen() ) {
|
88 |
+
return;
|
89 |
+
}
|
90 |
+
|
91 |
+
$this->asset_manager->enqueue_style( 'wp-dashboard' );
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Enqueues assets for the dashboard if the current page is the dashboard.
|
96 |
+
*/
|
97 |
+
public function enqueue_dashboard_assets() {
|
98 |
+
if ( ! $this->is_dashboard_screen() ) {
|
99 |
+
return;
|
100 |
+
}
|
101 |
+
|
102 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'dashboard-widget', 'wpseoDashboardWidgetL10n', $this->localize_dashboard_script() );
|
103 |
+
$this->asset_manager->enqueue_script( 'dashboard-widget' );
|
104 |
+
$this->asset_manager->enqueue_style( 'wp-dashboard' );
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Translates strings used in the dashboard widget.
|
109 |
+
*
|
110 |
+
* @return array The translated strings.
|
111 |
+
*/
|
112 |
+
public function localize_dashboard_script() {
|
113 |
+
return array(
|
114 |
+
'feed_header' => sprintf(
|
115 |
+
/* translators: %1$s resolves to Yoast.com */
|
116 |
+
__( 'Latest blogposts on %1$s', 'wordpress-seo' ),
|
117 |
+
'Yoast.com'
|
118 |
+
),
|
119 |
+
'feed_footer' => __( 'Read more like this on our SEO blog', 'wordpress-seo' ),
|
120 |
+
'ryte_header' => sprintf(
|
121 |
+
/* translators: %1$s expands to Ryte. */
|
122 |
+
__( 'Indexability check by %1$s', 'wordpress-seo' ),
|
123 |
+
'Ryte'
|
124 |
+
),
|
125 |
+
'ryte_fetch' => __( 'Fetch the current status', 'wordpress-seo' ),
|
126 |
+
'ryte_analyze' => __( 'Analyze entire site', 'wordpress-seo' ),
|
127 |
+
'ryte_fetch_url' => esc_attr( add_query_arg( 'wpseo-redo-onpage', '1' ) ) . '#wpseo-dashboard-overview',
|
128 |
+
'ryte_landing_url' => WPSEO_Shortlinker::get( 'https://yoa.st/rytelp' ),
|
129 |
+
);
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Checks if the current screen is the dashboard screen.
|
134 |
+
*
|
135 |
+
* @return bool Whether or not this is the dashboard screen.
|
136 |
+
*/
|
137 |
+
private function is_dashboard_screen() {
|
138 |
+
$current_screen = get_current_screen();
|
139 |
+
|
140 |
+
return ( $current_screen instanceof WP_Screen && $current_screen->id === 'dashboard' );
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Returns true when the dashboard widget should be shown.
|
145 |
+
*
|
146 |
+
* @return bool
|
147 |
+
*/
|
148 |
+
private function show_widget() {
|
149 |
+
$analysis_seo = new WPSEO_Metabox_Analysis_SEO();
|
150 |
+
|
151 |
+
return $analysis_seo->is_enabled() && current_user_can( 'edit_posts' );
|
152 |
+
}
|
153 |
+
}
|
admin/class-yoast-form.php
ADDED
@@ -0,0 +1,669 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Admin form class.
|
8 |
+
*
|
9 |
+
* @since 2.0
|
10 |
+
*/
|
11 |
+
class Yoast_Form {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var object Instance of this class
|
15 |
+
* @since 2.0
|
16 |
+
*/
|
17 |
+
public static $instance;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @var string
|
21 |
+
* @since 2.0
|
22 |
+
*/
|
23 |
+
public $option_name;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var array
|
27 |
+
* @since 2.0
|
28 |
+
*/
|
29 |
+
public $options;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Get the singleton instance of this class
|
33 |
+
*
|
34 |
+
* @since 2.0
|
35 |
+
*
|
36 |
+
* @return Yoast_Form
|
37 |
+
*/
|
38 |
+
public static function get_instance() {
|
39 |
+
if ( ! ( self::$instance instanceof self ) ) {
|
40 |
+
self::$instance = new self();
|
41 |
+
}
|
42 |
+
|
43 |
+
return self::$instance;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Generates the header for admin pages
|
48 |
+
*
|
49 |
+
* @since 2.0
|
50 |
+
*
|
51 |
+
* @param bool $form Whether or not the form start tag should be included.
|
52 |
+
* @param string $option The short name of the option to use for the current page.
|
53 |
+
* @param bool $contains_files Whether the form should allow for file uploads.
|
54 |
+
* @param bool $option_long_name Group name of the option.
|
55 |
+
*/
|
56 |
+
public function admin_header( $form = true, $option = 'wpseo', $contains_files = false, $option_long_name = false ) {
|
57 |
+
if ( ! $option_long_name ) {
|
58 |
+
$option_long_name = WPSEO_Options::get_group_name( $option );
|
59 |
+
}
|
60 |
+
?>
|
61 |
+
<div class="wrap yoast wpseo-admin-page <?php echo esc_attr( 'page-' . $option ); ?>">
|
62 |
+
<?php
|
63 |
+
/**
|
64 |
+
* Display the updated/error messages
|
65 |
+
* Only needed as our settings page is not under options, otherwise it will automatically be included
|
66 |
+
*
|
67 |
+
* @see settings_errors()
|
68 |
+
*/
|
69 |
+
require_once ABSPATH . 'wp-admin/options-head.php';
|
70 |
+
?>
|
71 |
+
<h1 id="wpseo-title"><?php echo esc_html( get_admin_page_title() ); ?></h1>
|
72 |
+
<div class="wpseo_content_wrapper">
|
73 |
+
<div class="wpseo_content_cell" id="wpseo_content_top">
|
74 |
+
<?php
|
75 |
+
if ( $form === true ) {
|
76 |
+
$enctype = ( $contains_files ) ? ' enctype="multipart/form-data"' : '';
|
77 |
+
echo '<form action="' . esc_url( admin_url( 'options.php' ) ) . '" method="post" id="wpseo-conf"' . $enctype . ' accept-charset="' . esc_attr( get_bloginfo( 'charset' ) ) . '">';
|
78 |
+
settings_fields( $option_long_name );
|
79 |
+
}
|
80 |
+
$this->set_option( $option );
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Set the option used in output for form elements
|
85 |
+
*
|
86 |
+
* @since 2.0
|
87 |
+
*
|
88 |
+
* @param string $option_name Option key.
|
89 |
+
*/
|
90 |
+
public function set_option( $option_name ) {
|
91 |
+
$this->option_name = $option_name;
|
92 |
+
$this->options = $this->get_option();
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Sets a value in the options.
|
97 |
+
*
|
98 |
+
* @since 5.4
|
99 |
+
*
|
100 |
+
* @param string $key The key of the option to set.
|
101 |
+
* @param mixed $value The value to set the option to.
|
102 |
+
* @param bool $overwrite Whether to overwrite existing options. Default is false.
|
103 |
+
*/
|
104 |
+
public function set_options_value( $key, $value, $overwrite = false ) {
|
105 |
+
if ( $overwrite || ! array_key_exists( $key, $this->options ) ) {
|
106 |
+
$this->options[ $key ] = $value;
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Retrieve options based on whether we're on multisite or not.
|
112 |
+
*
|
113 |
+
* @since 1.2.4
|
114 |
+
* @since 2.0 Moved to this class.
|
115 |
+
*
|
116 |
+
* @return array
|
117 |
+
*/
|
118 |
+
private function get_option() {
|
119 |
+
if ( is_network_admin() ) {
|
120 |
+
return get_site_option( $this->option_name );
|
121 |
+
}
|
122 |
+
|
123 |
+
return get_option( $this->option_name );
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Generates the footer for admin pages
|
128 |
+
*
|
129 |
+
* @since 2.0
|
130 |
+
*
|
131 |
+
* @param bool $submit Whether or not a submit button and form end tag should be shown.
|
132 |
+
* @param bool $show_sidebar Whether or not to show the banner sidebar - used by premium plugins to disable it.
|
133 |
+
*/
|
134 |
+
public function admin_footer( $submit = true, $show_sidebar = true ) {
|
135 |
+
if ( $submit ) {
|
136 |
+
submit_button( __( 'Save changes', 'wordpress-seo' ) );
|
137 |
+
|
138 |
+
echo '
|
139 |
+
</form>';
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* Apply general admin_footer hooks
|
144 |
+
*/
|
145 |
+
do_action( 'wpseo_admin_footer' );
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Run possibly set actions to add for example an i18n box
|
149 |
+
*/
|
150 |
+
do_action( 'wpseo_admin_promo_footer' );
|
151 |
+
|
152 |
+
echo '
|
153 |
+
</div><!-- end of div wpseo_content_top -->';
|
154 |
+
|
155 |
+
if ( $show_sidebar ) {
|
156 |
+
$this->admin_sidebar();
|
157 |
+
}
|
158 |
+
|
159 |
+
echo '</div><!-- end of div wpseo_content_wrapper -->';
|
160 |
+
|
161 |
+
|
162 |
+
if ( ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) ) {
|
163 |
+
$xdebug = ( extension_loaded( 'xdebug' ) ? true : false );
|
164 |
+
echo '
|
165 |
+
<div id="wpseo-debug-info" class="yoast-container">
|
166 |
+
|
167 |
+
<h2>' . esc_html__( 'Debug Information', 'wordpress-seo' ) . '</h2>
|
168 |
+
<div>
|
169 |
+
<h3 class="wpseo-debug-heading">' . esc_html__( 'Current option:', 'wordpress-seo' ) . ' <span class="wpseo-debug">' . esc_html( $this->option_name ) . '</span></h3>
|
170 |
+
' . ( ( $xdebug ) ? '' : '<pre>' );
|
171 |
+
var_dump( $this->get_option() );
|
172 |
+
echo '
|
173 |
+
' . ( ( $xdebug ) ? '' : '</pre>' ) . '
|
174 |
+
</div>
|
175 |
+
</div>';
|
176 |
+
}
|
177 |
+
|
178 |
+
echo '
|
179 |
+
</div><!-- end of wrap -->';
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Generates the sidebar for admin pages.
|
184 |
+
*
|
185 |
+
* @since 2.0
|
186 |
+
*/
|
187 |
+
public function admin_sidebar() {
|
188 |
+
|
189 |
+
// No banners in Premium.
|
190 |
+
if ( class_exists( 'WPSEO_Product_Premium' ) ) {
|
191 |
+
$product_premium = new WPSEO_Product_Premium();
|
192 |
+
$extension_manager = new WPSEO_Extension_Manager();
|
193 |
+
|
194 |
+
if ( $extension_manager->is_activated( $product_premium->get_slug() ) ) {
|
195 |
+
return;
|
196 |
+
}
|
197 |
+
}
|
198 |
+
|
199 |
+
$sidebar_renderer = new WPSEO_Admin_Banner_Sidebar_Renderer( new WPSEO_Admin_Banner_Spot_Renderer() );
|
200 |
+
|
201 |
+
$banner_renderer = new WPSEO_Admin_Banner_Renderer();
|
202 |
+
$banner_renderer->set_base_path( plugins_url( 'images/banner/', WPSEO_FILE ) );
|
203 |
+
|
204 |
+
/* translators: %1$s expands to "Yoast". */
|
205 |
+
$sidebar = new WPSEO_Admin_Banner_Sidebar( sprintf( __( '%1s recommendations for you', 'wordpress-seo' ), 'Yoast' ), $banner_renderer );
|
206 |
+
$sidebar->initialize( new WPSEO_Features() );
|
207 |
+
|
208 |
+
echo $sidebar_renderer->render( $sidebar );
|
209 |
+
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Output a label element
|
214 |
+
*
|
215 |
+
* @since 2.0
|
216 |
+
*
|
217 |
+
* @param string $text Label text string.
|
218 |
+
* @param array $attr HTML attributes set.
|
219 |
+
*/
|
220 |
+
public function label( $text, $attr ) {
|
221 |
+
$attr = wp_parse_args( $attr, array(
|
222 |
+
'class' => 'checkbox',
|
223 |
+
'close' => true,
|
224 |
+
'for' => '',
|
225 |
+
)
|
226 |
+
);
|
227 |
+
echo "<label class='" . esc_attr( $attr['class'] ) . "' for='" . esc_attr( $attr['for'] ) . "'>$text";
|
228 |
+
if ( $attr['close'] ) {
|
229 |
+
echo '</label>';
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Output a legend element.
|
235 |
+
*
|
236 |
+
* @since 3.4
|
237 |
+
*
|
238 |
+
* @param string $text Legend text string.
|
239 |
+
* @param array $attr HTML attributes set.
|
240 |
+
*/
|
241 |
+
public function legend( $text, $attr ) {
|
242 |
+
$attr = wp_parse_args( $attr, array(
|
243 |
+
'id' => '',
|
244 |
+
'class' => '',
|
245 |
+
)
|
246 |
+
);
|
247 |
+
|
248 |
+
$id = ( '' === $attr['id'] ) ? '' : ' id="' . esc_attr( $attr['id'] ) . '"';
|
249 |
+
echo '<legend class="yoast-form-legend ' . esc_attr( $attr['class'] ) . '"' . $id . '>' . $text . '</legend>';
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Create a Checkbox input field.
|
254 |
+
*
|
255 |
+
* @since 2.0
|
256 |
+
*
|
257 |
+
* @param string $var The variable within the option to create the checkbox for.
|
258 |
+
* @param string $label The label to show for the variable.
|
259 |
+
* @param bool $label_left Whether the label should be left (true) or right (false).
|
260 |
+
*/
|
261 |
+
public function checkbox( $var, $label, $label_left = false ) {
|
262 |
+
if ( ! isset( $this->options[ $var ] ) ) {
|
263 |
+
$this->options[ $var ] = false;
|
264 |
+
}
|
265 |
+
|
266 |
+
if ( $this->options[ $var ] === true ) {
|
267 |
+
$this->options[ $var ] = 'on';
|
268 |
+
}
|
269 |
+
|
270 |
+
$class = '';
|
271 |
+
if ( $label_left !== false ) {
|
272 |
+
if ( ! empty( $label_left ) ) {
|
273 |
+
$label_left .= ':';
|
274 |
+
}
|
275 |
+
$this->label( $label_left, array( 'for' => $var ) );
|
276 |
+
}
|
277 |
+
else {
|
278 |
+
$class = 'double';
|
279 |
+
}
|
280 |
+
|
281 |
+
echo '<input class="checkbox ', esc_attr( $class ), '" type="checkbox" id="', esc_attr( $var ), '" name="', esc_attr( $this->option_name ), '[', esc_attr( $var ), ']" value="on"', checked( $this->options[ $var ], 'on', false ), '/>';
|
282 |
+
|
283 |
+
if ( ! empty( $label ) ) {
|
284 |
+
$this->label( $label, array( 'for' => $var ) );
|
285 |
+
}
|
286 |
+
|
287 |
+
echo '<br class="clear" />';
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Create a light switch input field using a single checkbox.
|
292 |
+
*
|
293 |
+
* @since 3.1
|
294 |
+
*
|
295 |
+
* @param string $var The variable within the option to create the checkbox for.
|
296 |
+
* @param string $label The label element text for the checkbox.
|
297 |
+
* @param array $buttons Array of two visual labels for the buttons (defaults Disabled/Enabled).
|
298 |
+
* @param boolean $reverse Reverse order of buttons (default true).
|
299 |
+
* @param string $help Inline Help that will be printed out before the visible toggles text.
|
300 |
+
*/
|
301 |
+
public function light_switch( $var, $label, $buttons = array(), $reverse = true, $help = '' ) {
|
302 |
+
|
303 |
+
if ( ! isset( $this->options[ $var ] ) ) {
|
304 |
+
$this->options[ $var ] = false;
|
305 |
+
}
|
306 |
+
|
307 |
+
if ( $this->options[ $var ] === true ) {
|
308 |
+
$this->options[ $var ] = 'on';
|
309 |
+
}
|
310 |
+
|
311 |
+
$class = 'switch-light switch-candy switch-yoast-seo';
|
312 |
+
$aria_labelledby = esc_attr( $var ) . '-label';
|
313 |
+
|
314 |
+
if ( $reverse ) {
|
315 |
+
$class .= ' switch-yoast-seo-reverse';
|
316 |
+
}
|
317 |
+
|
318 |
+
if ( empty( $buttons ) ) {
|
319 |
+
$buttons = array( __( 'Disabled', 'wordpress-seo' ), __( 'Enabled', 'wordpress-seo' ) );
|
320 |
+
}
|
321 |
+
|
322 |
+
list( $off_button, $on_button ) = $buttons;
|
323 |
+
|
324 |
+
$help_class = '';
|
325 |
+
$screen_reader_text_class = '';
|
326 |
+
|
327 |
+
$help_class = ! empty( $help ) ? ' switch-container__has-help' : '';
|
328 |
+
|
329 |
+
echo "<div class='switch-container$help_class'>",
|
330 |
+
"<span class='switch-light-visual-label'>{$label}</span>" . $help,
|
331 |
+
'<label class="', $class, '"><b class="switch-yoast-seo-jaws-a11y"> </b>',
|
332 |
+
'<input type="checkbox" aria-labelledby="', $aria_labelledby, '" id="', esc_attr( $var ), '" name="', esc_attr( $this->option_name ), '[', esc_attr( $var ), ']" value="on"', checked( $this->options[ $var ], 'on', false ), '/>',
|
333 |
+
"<b class='label-text screen-reader-text' id='{$aria_labelledby}'>{$label}</b>",
|
334 |
+
'<span aria-hidden="true">
|
335 |
+
<span>', esc_html( $off_button ) ,'</span>
|
336 |
+
<span>', esc_html( $on_button ) ,'</span>
|
337 |
+
<a></a>
|
338 |
+
</span>
|
339 |
+
</label><div class="clear"></div></div>';
|
340 |
+
}
|
341 |
+
|
342 |
+
/**
|
343 |
+
* Create a Text input field.
|
344 |
+
*
|
345 |
+
* @since 2.0
|
346 |
+
* @since 2.1 Introduced the `$attr` parameter.
|
347 |
+
*
|
348 |
+
* @param string $var The variable within the option to create the text input field for.
|
349 |
+
* @param string $label The label to show for the variable.
|
350 |
+
* @param array|string $attr Extra class to add to the input field.
|
351 |
+
*/
|
352 |
+
public function textinput( $var, $label, $attr = array() ) {
|
353 |
+
if ( ! is_array( $attr ) ) {
|
354 |
+
$attr = array(
|
355 |
+
'class' => $attr,
|
356 |
+
);
|
357 |
+
}
|
358 |
+
$attr = wp_parse_args( $attr, array(
|
359 |
+
'placeholder' => '',
|
360 |
+
'class' => '',
|
361 |
+
) );
|
362 |
+
$val = ( isset( $this->options[ $var ] ) ) ? $this->options[ $var ] : '';
|
363 |
+
|
364 |
+
$this->label(
|
365 |
+
$label . ':',
|
366 |
+
array(
|
367 |
+
'for' => $var,
|
368 |
+
'class' => 'textinput',
|
369 |
+
)
|
370 |
+
);
|
371 |
+
echo '<input class="textinput ' . esc_attr( $attr['class'] ) . ' " placeholder="' . esc_attr( $attr['placeholder'] ) . '" type="text" id="', esc_attr( $var ), '" name="', esc_attr( $this->option_name ), '[', esc_attr( $var ), ']" value="', esc_attr( $val ), '"/>', '<br class="clear" />';
|
372 |
+
}
|
373 |
+
|
374 |
+
/**
|
375 |
+
* Create a textarea.
|
376 |
+
*
|
377 |
+
* @since 2.0
|
378 |
+
*
|
379 |
+
* @param string $var The variable within the option to create the textarea for.
|
380 |
+
* @param string $label The label to show for the variable.
|
381 |
+
* @param array $attr The CSS class to assign to the textarea.
|
382 |
+
*/
|
383 |
+
public function textarea( $var, $label, $attr = array() ) {
|
384 |
+
if ( ! is_array( $attr ) ) {
|
385 |
+
$attr = array(
|
386 |
+
'class' => $attr,
|
387 |
+
);
|
388 |
+
}
|
389 |
+
$attr = wp_parse_args( $attr, array(
|
390 |
+
'cols' => '',
|
391 |
+
'rows' => '',
|
392 |
+
'class' => '',
|
393 |
+
) );
|
394 |
+
$val = ( isset( $this->options[ $var ] ) ) ? $this->options[ $var ] : '';
|
395 |
+
|
396 |
+
$this->label(
|
397 |
+
$label . ':',
|
398 |
+
array(
|
399 |
+
'for' => $var,
|
400 |
+
'class' => 'textinput',
|
401 |
+
)
|
402 |
+
);
|
403 |
+
echo '<textarea cols="' . esc_attr( $attr['cols'] ) . '" rows="' . esc_attr( $attr['rows'] ) . '" class="textinput ' . esc_attr( $attr['class'] ) . '" id="' . esc_attr( $var ) . '" name="' . esc_attr( $this->option_name ) . '[' . esc_attr( $var ) . ']">' . esc_textarea( $val ) . '</textarea><br class="clear" />';
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Create a hidden input field.
|
408 |
+
*
|
409 |
+
* @since 2.0
|
410 |
+
*
|
411 |
+
* @param string $var The variable within the option to create the hidden input for.
|
412 |
+
* @param string $id The ID of the element.
|
413 |
+
*/
|
414 |
+
public function hidden( $var, $id = '' ) {
|
415 |
+
$val = ( isset( $this->options[ $var ] ) ) ? $this->options[ $var ] : '';
|
416 |
+
if ( is_bool( $val ) ) {
|
417 |
+
$val = ( $val === true ) ? 'true' : 'false';
|
418 |
+
}
|
419 |
+
|
420 |
+
if ( '' === $id ) {
|
421 |
+
$id = 'hidden_' . $var;
|
422 |
+
}
|
423 |
+
|
424 |
+
echo '<input type="hidden" id="' . esc_attr( $id ) . '" name="' . esc_attr( $this->option_name ) . '[' . esc_attr( $var ) . ']" value="' . esc_attr( $val ) . '"/>';
|
425 |
+
}
|
426 |
+
|
427 |
+
/**
|
428 |
+
* Create a Select Box.
|
429 |
+
*
|
430 |
+
* @since 2.0
|
431 |
+
*
|
432 |
+
* @param string $field_name The variable within the option to create the select for.
|
433 |
+
* @param string $label The label to show for the variable.
|
434 |
+
* @param array $select_options The select options to choose from.
|
435 |
+
*/
|
436 |
+
public function select( $field_name, $label, array $select_options ) {
|
437 |
+
|
438 |
+
if ( empty( $select_options ) ) {
|
439 |
+
return;
|
440 |
+
}
|
441 |
+
|
442 |
+
$this->label(
|
443 |
+
$label . ':',
|
444 |
+
array(
|
445 |
+
'for' => $field_name,
|
446 |
+
'class' => 'select',
|
447 |
+
)
|
448 |
+
);
|
449 |
+
|
450 |
+
$select_name = esc_attr( $this->option_name ) . '[' . esc_attr( $field_name ) . ']';
|
451 |
+
$active_option = ( isset( $this->options[ $field_name ] ) ) ? $this->options[ $field_name ] : '';
|
452 |
+
|
453 |
+
$select = new Yoast_Input_Select( $field_name, $select_name, $select_options, $active_option );
|
454 |
+
$select->add_attribute( 'class', 'select' );
|
455 |
+
$select->output_html();
|
456 |
+
|
457 |
+
echo '<br class="clear"/>';
|
458 |
+
}
|
459 |
+
|
460 |
+
/**
|
461 |
+
* Create a File upload field.
|
462 |
+
*
|
463 |
+
* @since 2.0
|
464 |
+
*
|
465 |
+
* @param string $var The variable within the option to create the file upload field for.
|
466 |
+
* @param string $label The label to show for the variable.
|
467 |
+
*/
|
468 |
+
public function file_upload( $var, $label ) {
|
469 |
+
$val = '';
|
470 |
+
if ( isset( $this->options[ $var ] ) && is_array( $this->options[ $var ] ) ) {
|
471 |
+
$val = $this->options[ $var ]['url'];
|
472 |
+
}
|
473 |
+
|
474 |
+
$var_esc = esc_attr( $var );
|
475 |
+
$this->label(
|
476 |
+
$label . ':',
|
477 |
+
array(
|
478 |
+
'for' => $var,
|
479 |
+
'class' => 'select',
|
480 |
+
)
|
481 |
+
);
|
482 |
+
echo '<input type="file" value="' . esc_attr( $val ) . '" class="textinput" name="' . esc_attr( $this->option_name ) . '[' . $var_esc . ']" id="' . $var_esc . '"/>';
|
483 |
+
|
484 |
+
// Need to save separate array items in hidden inputs, because empty file inputs type will be deleted by settings API.
|
485 |
+
if ( ! empty( $this->options[ $var ] ) ) {
|
486 |
+
$this->hidden( 'file', $this->option_name . '_file' );
|
487 |
+
$this->hidden( 'url', $this->option_name . '_url' );
|
488 |
+
$this->hidden( 'type', $this->option_name . '_type' );
|
489 |
+
}
|
490 |
+
echo '<br class="clear"/>';
|
491 |
+
}
|
492 |
+
|
493 |
+
/**
|
494 |
+
* Media input
|
495 |
+
*
|
496 |
+
* @since 2.0
|
497 |
+
*
|
498 |
+
* @param string $var Option name.
|
499 |
+
* @param string $label Label message.
|
500 |
+
*/
|
501 |
+
public function media_input( $var, $label ) {
|
502 |
+
$val = '';
|
503 |
+
if ( isset( $this->options[ $var ] ) ) {
|
504 |
+
$val = $this->options[ $var ];
|
505 |
+
}
|
506 |
+
|
507 |
+
$var_esc = esc_attr( $var );
|
508 |
+
|
509 |
+
$this->label(
|
510 |
+
$label . ':',
|
511 |
+
array(
|
512 |
+
'for' => 'wpseo_' . $var,
|
513 |
+
'class' => 'select',
|
514 |
+
)
|
515 |
+
);
|
516 |
+
echo '<input class="textinput" id="wpseo_', $var_esc, '" type="text" size="36" name="', esc_attr( $this->option_name ), '[', $var_esc, ']" value="', esc_attr( $val ), '" />';
|
517 |
+
echo '<input id="wpseo_', $var_esc, '_button" class="wpseo_image_upload_button button" type="button" value="', esc_attr__( 'Upload Image', 'wordpress-seo' ), '" />';
|
518 |
+
echo '<br class="clear"/>';
|
519 |
+
}
|
520 |
+
|
521 |
+
/**
|
522 |
+
* Create a Radio input field.
|
523 |
+
*
|
524 |
+
* @since 2.0
|
525 |
+
*
|
526 |
+
* @param string $var The variable within the option to create the radio button for.
|
527 |
+
* @param array $values The radio options to choose from.
|
528 |
+
* @param string $legend Optional. The legend to show for the field set, if any.
|
529 |
+
* @param array $legend_attr Optional. The attributes for the legend, if any.
|
530 |
+
*/
|
531 |
+
public function radio( $var, $values, $legend = '', $legend_attr = array() ) {
|
532 |
+
if ( ! is_array( $values ) || $values === array() ) {
|
533 |
+
return;
|
534 |
+
}
|
535 |
+
if ( ! isset( $this->options[ $var ] ) ) {
|
536 |
+
$this->options[ $var ] = false;
|
537 |
+
}
|
538 |
+
|
539 |
+
$var_esc = esc_attr( $var );
|
540 |
+
|
541 |
+
echo '<fieldset class="yoast-form-fieldset wpseo_radio_block" id="' . $var_esc . '">';
|
542 |
+
|
543 |
+
if ( is_string( $legend ) && '' !== $legend ) {
|
544 |
+
|
545 |
+
$legend_attr = wp_parse_args( $legend_attr, array(
|
546 |
+
'id' => '',
|
547 |
+
'class' => 'radiogroup',
|
548 |
+
) );
|
549 |
+
|
550 |
+
$this->legend( $legend, $legend_attr );
|
551 |
+
}
|
552 |
+
|
553 |
+
foreach ( $values as $key => $value ) {
|
554 |
+
$key_esc = esc_attr( $key );
|
555 |
+
echo '<input type="radio" class="radio" id="' . $var_esc . '-' . $key_esc . '" name="' . esc_attr( $this->option_name ) . '[' . $var_esc . ']" value="' . $key_esc . '" ' . checked( $this->options[ $var ], $key_esc, false ) . ' />';
|
556 |
+
$this->label(
|
557 |
+
$value,
|
558 |
+
array(
|
559 |
+
'for' => $var_esc . '-' . $key_esc,
|
560 |
+
'class' => 'radio',
|
561 |
+
)
|
562 |
+
);
|
563 |
+
}
|
564 |
+
echo '</fieldset>';
|
565 |
+
}
|
566 |
+
|
567 |
+
|
568 |
+
/**
|
569 |
+
* Create a toggle switch input field using two radio buttons.
|
570 |
+
*
|
571 |
+
* @since 3.1
|
572 |
+
*
|
573 |
+
* @param string $var The variable within the option to create the radio buttons for.
|
574 |
+
* @param array $values Associative array of on/off keys and their values to be used as
|
575 |
+
* the label elements text for the radio buttons. Optionally, each
|
576 |
+
* value can be an array of visible label text and screen reader text.
|
577 |
+
* @param string $label The visual label for the radio buttons group, used as the fieldset legend.
|
578 |
+
* @param string $help Inline Help that will be printed out before the visible toggles text.
|
579 |
+
*/
|
580 |
+
public function toggle_switch( $var, $values, $label, $help = '' ) {
|
581 |
+
if ( ! is_array( $values ) || $values === array() ) {
|
582 |
+
return;
|
583 |
+
}
|
584 |
+
if ( ! isset( $this->options[ $var ] ) ) {
|
585 |
+
$this->options[ $var ] = false;
|
586 |
+
}
|
587 |
+
if ( $this->options[ $var ] === true ) {
|
588 |
+
$this->options[ $var ] = 'on';
|
589 |
+
}
|
590 |
+
if ( $this->options[ $var ] === false ) {
|
591 |
+
$this->options[ $var ] = 'off';
|
592 |
+
}
|
593 |
+
|
594 |
+
$help_class = ! empty( $help ) ? ' switch-container__has-help' : '';
|
595 |
+
|
596 |
+
$var_esc = esc_attr( $var );
|
597 |
+
|
598 |
+
printf( '<div class="%s">', esc_attr( 'switch-container' . $help_class ) );
|
599 |
+
echo '<fieldset id="', $var_esc, '" class="fieldset-switch-toggle"><legend>', $label, '</legend>', $help,
|
600 |
+
'<div class="switch-toggle switch-candy switch-yoast-seo">';
|
601 |
+
|
602 |
+
foreach ( $values as $key => $value ) {
|
603 |
+
$screen_reader_text = '';
|
604 |
+
$screen_reader_text_html = '';
|
605 |
+
|
606 |
+
if ( is_array( $value ) ) {
|
607 |
+
$screen_reader_text = $value['screen_reader_text'];
|
608 |
+
$screen_reader_text_html = '<span class="screen-reader-text"> ' . esc_html( $screen_reader_text ) . '</span>';
|
609 |
+
$value = $value['text'];
|
610 |
+
}
|
611 |
+
|
612 |
+
$key_esc = esc_attr( $key );
|
613 |
+
$for = $var_esc . '-' . $key_esc;
|
614 |
+
echo '<input type="radio" id="' . $for . '" name="' . esc_attr( $this->option_name ) . '[' . $var_esc . ']" value="' . $key_esc . '" ' . checked( $this->options[ $var ], $key_esc, false ) . ' />',
|
615 |
+
'<label for="', $for, '">', esc_html( $value ), $screen_reader_text_html,'</label>';
|
616 |
+
}
|
617 |
+
|
618 |
+
echo '<a></a></div></fieldset><div class="clear"></div></div>' . "\n\n";
|
619 |
+
}
|
620 |
+
|
621 |
+
/**
|
622 |
+
* Creates a toggle switch to define whether an indexable should be indexed or not.
|
623 |
+
*
|
624 |
+
* @param string $var The variable within the option to create the radio buttons for.
|
625 |
+
* @param string $label The visual label for the radio buttons group, used as the fieldset legend.
|
626 |
+
* @param string $help Inline Help that will be printed out before the visible toggles text.
|
627 |
+
*
|
628 |
+
* @return void
|
629 |
+
*/
|
630 |
+
public function index_switch( $var, $label, $help = '' ) {
|
631 |
+
$index_switch_values = array(
|
632 |
+
'off' => __( 'Yes', 'wordpress-seo' ),
|
633 |
+
'on' => __( 'No', 'wordpress-seo' ),
|
634 |
+
);
|
635 |
+
|
636 |
+
$this->toggle_switch(
|
637 |
+
$var,
|
638 |
+
$index_switch_values,
|
639 |
+
sprintf(
|
640 |
+
/* translators: %s expands to an indexable object's name, like a post type or taxonomy */
|
641 |
+
esc_html__( 'Show %s in search results?', 'wordpress-seo' ),
|
642 |
+
'<strong>' . esc_html( $label ) . '</strong>'
|
643 |
+
),
|
644 |
+
$help
|
645 |
+
);
|
646 |
+
}
|
647 |
+
|
648 |
+
/**
|
649 |
+
* Creates a toggle switch to show hide certain options.
|
650 |
+
*
|
651 |
+
* @param string $var The variable within the option to create the radio buttons for.
|
652 |
+
* @param string $label The visual label for the radio buttons group, used as the fieldset legend.
|
653 |
+
* @param bool $inverse_keys Whether or not the option keys need to be inverted to support older functions.
|
654 |
+
* @param string $help Inline Help that will be printed out before the visible toggles text.
|
655 |
+
*
|
656 |
+
* @return void
|
657 |
+
*/
|
658 |
+
public function show_hide_switch( $var, $label, $inverse_keys = false, $help = '' ) {
|
659 |
+
$on_key = ( $inverse_keys ) ? 'off' : 'on';
|
660 |
+
$off_key = ( $inverse_keys ) ? 'on' : 'off';
|
661 |
+
|
662 |
+
$show_hide_switch = array(
|
663 |
+
$on_key => __( 'Show', 'wordpress-seo' ),
|
664 |
+
$off_key => __( 'Hide', 'wordpress-seo' ),
|
665 |
+
);
|
666 |
+
|
667 |
+
$this->toggle_switch( $var, $show_hide_switch, $label, $help );
|
668 |
+
}
|
669 |
+
}
|
admin/class-yoast-notification-center.php
ADDED
@@ -0,0 +1,598 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Notifications
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Handles notifications storage and display.
|
8 |
+
*/
|
9 |
+
class Yoast_Notification_Center {
|
10 |
+
|
11 |
+
/** Option name to store notifications on */
|
12 |
+
const STORAGE_KEY = 'yoast_notifications';
|
13 |
+
|
14 |
+
/** @var \Yoast_Notification_Center The singleton instance of this object */
|
15 |
+
private static $instance = null;
|
16 |
+
|
17 |
+
/** @var $notifications Yoast_Notification[] */
|
18 |
+
private $notifications = array();
|
19 |
+
|
20 |
+
/** @var array Notifications there are newly added */
|
21 |
+
private $new = array();
|
22 |
+
|
23 |
+
/** @var array Notifications that were resolved this execution */
|
24 |
+
private $resolved = 0;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Construct
|
28 |
+
*/
|
29 |
+
private function __construct() {
|
30 |
+
|
31 |
+
$this->retrieve_notifications_from_storage();
|
32 |
+
|
33 |
+
add_action( 'all_admin_notices', array( $this, 'display_notifications' ) );
|
34 |
+
|
35 |
+
add_action( 'wp_ajax_yoast_get_notifications', array( $this, 'ajax_get_notifications' ) );
|
36 |
+
|
37 |
+
add_action( 'wpseo_deactivate', array( $this, 'deactivate_hook' ) );
|
38 |
+
add_action( 'shutdown', array( $this, 'update_storage' ) );
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Singleton getter
|
43 |
+
*
|
44 |
+
* @return Yoast_Notification_Center
|
45 |
+
*/
|
46 |
+
public static function get() {
|
47 |
+
|
48 |
+
if ( null === self::$instance ) {
|
49 |
+
self::$instance = new self();
|
50 |
+
}
|
51 |
+
|
52 |
+
return self::$instance;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Dismiss a notification
|
57 |
+
*/
|
58 |
+
public static function ajax_dismiss_notification() {
|
59 |
+
|
60 |
+
$notification_center = self::get();
|
61 |
+
|
62 |
+
$notification_id = filter_input( INPUT_POST, 'notification' );
|
63 |
+
if ( empty( $notification_id ) ) {
|
64 |
+
die( '-1' );
|
65 |
+
}
|
66 |
+
|
67 |
+
$notification = $notification_center->get_notification_by_id( $notification_id );
|
68 |
+
if ( false === ( $notification instanceof Yoast_Notification ) ) {
|
69 |
+
|
70 |
+
// Permit legacy.
|
71 |
+
$notification = new Yoast_Notification( '', array(
|
72 |
+
'id' => $notification_id,
|
73 |
+
'dismissal_key' => $notification_id,
|
74 |
+
) );
|
75 |
+
}
|
76 |
+
|
77 |
+
if ( $notification_center->maybe_dismiss_notification( $notification ) ) {
|
78 |
+
die( '1' );
|
79 |
+
}
|
80 |
+
|
81 |
+
die( '-1' );
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Check if the user has dismissed a notification
|
86 |
+
*
|
87 |
+
* @param Yoast_Notification $notification The notification to check for dismissal.
|
88 |
+
* @param null|int $user_id User ID to check on.
|
89 |
+
*
|
90 |
+
* @return bool
|
91 |
+
*/
|
92 |
+
public static function is_notification_dismissed( Yoast_Notification $notification, $user_id = null ) {
|
93 |
+
|
94 |
+
$user_id = ( ! is_null( $user_id ) ? $user_id : get_current_user_id() );
|
95 |
+
$dismissal_key = $notification->get_dismissal_key();
|
96 |
+
|
97 |
+
$current_value = get_user_meta( $user_id, $dismissal_key, true );
|
98 |
+
|
99 |
+
return ! empty( $current_value );
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Check if the nofitication is being dismissed
|
104 |
+
*
|
105 |
+
* @param string|Yoast_Notification $notification Notification to check dismissal of.
|
106 |
+
* @param string $meta_value Value to set the meta value to if dismissed.
|
107 |
+
*
|
108 |
+
* @return bool True if dismissed.
|
109 |
+
*/
|
110 |
+
public static function maybe_dismiss_notification( Yoast_Notification $notification, $meta_value = 'seen' ) {
|
111 |
+
|
112 |
+
// Only persistent notifications are dismissible.
|
113 |
+
if ( ! $notification->is_persistent() ) {
|
114 |
+
return false;
|
115 |
+
}
|
116 |
+
|
117 |
+
// If notification is already dismissed, we're done.
|
118 |
+
if ( self::is_notification_dismissed( $notification ) ) {
|
119 |
+
return true;
|
120 |
+
}
|
121 |
+
|
122 |
+
$dismissal_key = $notification->get_dismissal_key();
|
123 |
+
$notification_id = $notification->get_id();
|
124 |
+
|
125 |
+
$is_dismissing = ( $dismissal_key === self::get_user_input( 'notification' ) );
|
126 |
+
if ( ! $is_dismissing ) {
|
127 |
+
$is_dismissing = ( $notification_id === self::get_user_input( 'notification' ) );
|
128 |
+
}
|
129 |
+
|
130 |
+
// Fallback to ?dismissal_key=1&nonce=bla when JavaScript fails.
|
131 |
+
if ( ! $is_dismissing ) {
|
132 |
+
$is_dismissing = ( '1' === self::get_user_input( $dismissal_key ) );
|
133 |
+
}
|
134 |
+
|
135 |
+
if ( ! $is_dismissing ) {
|
136 |
+
return false;
|
137 |
+
}
|
138 |
+
|
139 |
+
$user_nonce = self::get_user_input( 'nonce' );
|
140 |
+
if ( false === wp_verify_nonce( $user_nonce, $notification_id ) ) {
|
141 |
+
return false;
|
142 |
+
}
|
143 |
+
|
144 |
+
return self::dismiss_notification( $notification, $meta_value );
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Clear dismissal information for the specified Notification
|
149 |
+
*
|
150 |
+
* When a cause is resolved, the next time it is present we want to show
|
151 |
+
* the message again.
|
152 |
+
*
|
153 |
+
* @param string|Yoast_Notification $notification Notification to clear the dismissal of.
|
154 |
+
*
|
155 |
+
* @return bool
|
156 |
+
*/
|
157 |
+
public function clear_dismissal( $notification ) {
|
158 |
+
|
159 |
+
if ( $notification instanceof Yoast_Notification ) {
|
160 |
+
$dismissal_key = $notification->get_dismissal_key();
|
161 |
+
}
|
162 |
+
|
163 |
+
if ( is_string( $notification ) ) {
|
164 |
+
$dismissal_key = $notification;
|
165 |
+
}
|
166 |
+
|
167 |
+
if ( empty( $dismissal_key ) ) {
|
168 |
+
return false;
|
169 |
+
}
|
170 |
+
|
171 |
+
// Remove notification dismissal for all users.
|
172 |
+
$deleted = delete_metadata( 'user', 0, $dismissal_key, '', true );
|
173 |
+
|
174 |
+
return $deleted;
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Add notification to the cookie
|
179 |
+
*
|
180 |
+
* @param Yoast_Notification $notification Notification object instance.
|
181 |
+
*/
|
182 |
+
public function add_notification( Yoast_Notification $notification ) {
|
183 |
+
|
184 |
+
// Don't add if the user can't see it.
|
185 |
+
if ( ! $notification->display_for_current_user() ) {
|
186 |
+
return;
|
187 |
+
}
|
188 |
+
|
189 |
+
$notification_id = $notification->get_id();
|
190 |
+
|
191 |
+
// Empty notifications are always added.
|
192 |
+
if ( $notification_id !== '' ) {
|
193 |
+
|
194 |
+
// If notification ID exists in notifications, don't add again.
|
195 |
+
$present_notification = $this->get_notification_by_id( $notification_id );
|
196 |
+
if ( ! is_null( $present_notification ) ) {
|
197 |
+
$this->remove_notification( $present_notification, false );
|
198 |
+
}
|
199 |
+
|
200 |
+
if ( is_null( $present_notification ) ) {
|
201 |
+
$this->new[] = $notification_id;
|
202 |
+
}
|
203 |
+
}
|
204 |
+
|
205 |
+
// Add to list.
|
206 |
+
$this->notifications[] = $notification;
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Get the notification by ID
|
211 |
+
*
|
212 |
+
* @param string $notification_id The ID of the notification to search for.
|
213 |
+
*
|
214 |
+
* @return null|Yoast_Notification
|
215 |
+
*/
|
216 |
+
public function get_notification_by_id( $notification_id ) {
|
217 |
+
|
218 |
+
foreach ( $this->notifications as & $notification ) {
|
219 |
+
if ( $notification_id === $notification->get_id() ) {
|
220 |
+
return $notification;
|
221 |
+
}
|
222 |
+
}
|
223 |
+
|
224 |
+
return null;
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Display the notifications
|
229 |
+
*
|
230 |
+
* @param bool $echo_as_json True when notifications should be printed directly.
|
231 |
+
*
|
232 |
+
* @return void
|
233 |
+
*/
|
234 |
+
public function display_notifications( $echo_as_json = false ) {
|
235 |
+
|
236 |
+
// Never display notifications for network admin.
|
237 |
+
if ( function_exists( 'is_network_admin' ) && is_network_admin() ) {
|
238 |
+
return;
|
239 |
+
}
|
240 |
+
|
241 |
+
$sorted_notifications = $this->get_sorted_notifications();
|
242 |
+
$notifications = array_filter( $sorted_notifications, array( $this, 'is_notification_persistent' ) );
|
243 |
+
|
244 |
+
if ( empty( $notifications ) ) {
|
245 |
+
return;
|
246 |
+
}
|
247 |
+
|
248 |
+
array_walk( $notifications, array( $this, 'remove_notification' ) );
|
249 |
+
|
250 |
+
if ( $echo_as_json ) {
|
251 |
+
$notification_json = array();
|
252 |
+
foreach ( $notifications as $notification ) {
|
253 |
+
$notification_json[] = $notification->render();
|
254 |
+
}
|
255 |
+
|
256 |
+
echo json_encode( $notification_json );
|
257 |
+
|
258 |
+
return;
|
259 |
+
}
|
260 |
+
|
261 |
+
foreach ( $notifications as $notification ) {
|
262 |
+
echo $notification;
|
263 |
+
}
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* Remove notification after it has been displayed
|
268 |
+
*
|
269 |
+
* @param Yoast_Notification $notification Notification to remove.
|
270 |
+
* @param bool $resolve Resolve as fixed.
|
271 |
+
*/
|
272 |
+
public function remove_notification( Yoast_Notification $notification, $resolve = true ) {
|
273 |
+
|
274 |
+
$index = false;
|
275 |
+
|
276 |
+
// Match persistent Notifications by ID, non persistent by item in the array.
|
277 |
+
if ( $notification->is_persistent() ) {
|
278 |
+
foreach ( $this->notifications as $current_index => $present_notification ) {
|
279 |
+
if ( $present_notification->get_id() === $notification->get_id() ) {
|
280 |
+
$index = $current_index;
|
281 |
+
break;
|
282 |
+
}
|
283 |
+
}
|
284 |
+
}
|
285 |
+
else {
|
286 |
+
$index = array_search( $notification, $this->notifications, true );
|
287 |
+
}
|
288 |
+
|
289 |
+
if ( false === $index ) {
|
290 |
+
return;
|
291 |
+
}
|
292 |
+
|
293 |
+
if ( $notification->is_persistent() && $resolve ) {
|
294 |
+
$this->resolved++;
|
295 |
+
$this->clear_dismissal( $notification );
|
296 |
+
}
|
297 |
+
|
298 |
+
unset( $this->notifications[ $index ] );
|
299 |
+
$this->notifications = array_values( $this->notifications );
|
300 |
+
}
|
301 |
+
|
302 |
+
/**
|
303 |
+
* Get the notification count
|
304 |
+
*
|
305 |
+
* @param bool $dismissed Count dismissed notifications.
|
306 |
+
*
|
307 |
+
* @return int Number of notifications
|
308 |
+
*/
|
309 |
+
public function get_notification_count( $dismissed = false ) {
|
310 |
+
|
311 |
+
$notifications = $this->get_notifications();
|
312 |
+
$notifications = array_filter( $notifications, array( $this, 'filter_persistent_notifications' ) );
|
313 |
+
|
314 |
+
if ( ! $dismissed ) {
|
315 |
+
$notifications = array_filter( $notifications, array( $this, 'filter_dismissed_notifications' ) );
|
316 |
+
}
|
317 |
+
|
318 |
+
return count( $notifications );
|
319 |
+
}
|
320 |
+
|
321 |
+
/**
|
322 |
+
* Get the number of notifications resolved this execution
|
323 |
+
*
|
324 |
+
* These notifications have been resolved and should be counted when active again.
|
325 |
+
*
|
326 |
+
* @return int
|
327 |
+
*/
|
328 |
+
public function get_resolved_notification_count() {
|
329 |
+
|
330 |
+
return $this->resolved;
|
331 |
+
}
|
332 |
+
|
333 |
+
/**
|
334 |
+
* Return the notifications sorted on type and priority
|
335 |
+
*
|
336 |
+
* @return array|Yoast_Notification[] Sorted Notifications
|
337 |
+
*/
|
338 |
+
public function get_sorted_notifications() {
|
339 |
+
|
340 |
+
$notifications = $this->get_notifications();
|
341 |
+
if ( empty( $notifications ) ) {
|
342 |
+
return array();
|
343 |
+
}
|
344 |
+
|
345 |
+
// Sort by severity, error first.
|
346 |
+
usort( $notifications, array( $this, 'sort_notifications' ) );
|
347 |
+
|
348 |
+
return $notifications;
|
349 |
+
}
|
350 |
+
|
351 |
+
/**
|
352 |
+
* AJAX display notifications
|
353 |
+
*/
|
354 |
+
public function ajax_get_notifications() {
|
355 |
+
$echo = filter_input( INPUT_POST, 'version' ) === '2';
|
356 |
+
|
357 |
+
// Display the notices.
|
358 |
+
$this->display_notifications( $echo );
|
359 |
+
|
360 |
+
// AJAX die.
|
361 |
+
exit;
|
362 |
+
}
|
363 |
+
|
364 |
+
/**
|
365 |
+
* Remove storage when the plugin is deactivated
|
366 |
+
*/
|
367 |
+
public function deactivate_hook() {
|
368 |
+
|
369 |
+
$this->clear_notifications();
|
370 |
+
}
|
371 |
+
|
372 |
+
/**
|
373 |
+
* Save persistent notifications to storage
|
374 |
+
*
|
375 |
+
* We need to be able to retrieve these so they can be dismissed at any time during the execution.
|
376 |
+
*
|
377 |
+
* @since 3.2
|
378 |
+
*
|
379 |
+
* @return void
|
380 |
+
*/
|
381 |
+
public function update_storage() {
|
382 |
+
|
383 |
+
$notifications = $this->get_notifications();
|
384 |
+
|
385 |
+
/**
|
386 |
+
* Filter: 'yoast_notifications_before_storage' - Allows developer to filter notifications before saving them.
|
387 |
+
*
|
388 |
+
* @api Yoast_Notification[] $notifications
|
389 |
+
*/
|
390 |
+
$notifications = apply_filters( 'yoast_notifications_before_storage', $notifications );
|
391 |
+
|
392 |
+
// No notifications to store, clear storage.
|
393 |
+
if ( empty( $notifications ) ) {
|
394 |
+
$this->remove_storage();
|
395 |
+
|
396 |
+
return;
|
397 |
+
}
|
398 |
+
|
399 |
+
$notifications = array_map( array( $this, 'notification_to_array' ), $notifications );
|
400 |
+
|
401 |
+
// Save the notifications to the storage.
|
402 |
+
update_user_option( get_current_user_id(), self::STORAGE_KEY, $notifications );
|
403 |
+
}
|
404 |
+
|
405 |
+
/**
|
406 |
+
* Provide a way to verify present notifications
|
407 |
+
*
|
408 |
+
* @return array|Yoast_Notification[] Registered notifications.
|
409 |
+
*/
|
410 |
+
public function get_notifications() {
|
411 |
+
|
412 |
+
return $this->notifications;
|
413 |
+
}
|
414 |
+
|
415 |
+
/**
|
416 |
+
* Get newly added notifications
|
417 |
+
*
|
418 |
+
* @return array
|
419 |
+
*/
|
420 |
+
public function get_new_notifications() {
|
421 |
+
|
422 |
+
return array_map( array( $this, 'get_notification_by_id' ), $this->new );
|
423 |
+
}
|
424 |
+
|
425 |
+
/**
|
426 |
+
* Get information from the User input
|
427 |
+
*
|
428 |
+
* @param string $key Key to retrieve.
|
429 |
+
*
|
430 |
+
* @return mixed value of key if set.
|
431 |
+
*/
|
432 |
+
private static function get_user_input( $key ) {
|
433 |
+
|
434 |
+
$filter_input_type = INPUT_GET;
|
435 |
+
if ( 'POST' === strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
|
436 |
+
$filter_input_type = INPUT_POST;
|
437 |
+
}
|
438 |
+
|
439 |
+
return filter_input( $filter_input_type, $key );
|
440 |
+
}
|
441 |
+
|
442 |
+
/**
|
443 |
+
* Retrieve the notifications from storage
|
444 |
+
*
|
445 |
+
* @return array Yoast_Notification[] Notifications
|
446 |
+
*/
|
447 |
+
private function retrieve_notifications_from_storage() {
|
448 |
+
|
449 |
+
$stored_notifications = get_user_option( self::STORAGE_KEY, get_current_user_id() );
|
450 |
+
|
451 |
+
// Check if notifications are stored.
|
452 |
+
if ( empty( $stored_notifications ) ) {
|
453 |
+
return;
|
454 |
+
}
|
455 |
+
|
456 |
+
if ( is_array( $stored_notifications ) ) {
|
457 |
+
$notifications = array_map( array( $this, 'array_to_notification' ), $stored_notifications );
|
458 |
+
// Apply array_values to ensure we get a 0-indexed array.
|
459 |
+
$notifications = array_values( array_filter( $notifications, array( $this, 'filter_notification_current_user' ) ) );
|
460 |
+
|
461 |
+
$this->notifications = $notifications;
|
462 |
+
}
|
463 |
+
}
|
464 |
+
|
465 |
+
/**
|
466 |
+
* Sort on type then priority
|
467 |
+
*
|
468 |
+
* @param Yoast_Notification $a Compare with B.
|
469 |
+
* @param Yoast_Notification $b Compare with A.
|
470 |
+
*
|
471 |
+
* @return int 1, 0 or -1 for sorting offset.
|
472 |
+
*/
|
473 |
+
private function sort_notifications( Yoast_Notification $a, Yoast_Notification $b ) {
|
474 |
+
|
475 |
+
$a_type = $a->get_type();
|
476 |
+
$b_type = $b->get_type();
|
477 |
+
|
478 |
+
if ( $a_type === $b_type ) {
|
479 |
+
return WPSEO_Utils::calc( $b->get_priority(), 'compare', $a->get_priority() );
|
480 |
+
}
|
481 |
+
|
482 |
+
if ( 'error' === $a_type ) {
|
483 |
+
return -1;
|
484 |
+
}
|
485 |
+
|
486 |
+
if ( 'error' === $b_type ) {
|
487 |
+
return 1;
|
488 |
+
}
|
489 |
+
|
490 |
+
return 0;
|
491 |
+
}
|
492 |
+
|
493 |
+
/**
|
494 |
+
* Dismiss the notification
|
495 |
+
*
|
496 |
+
* @param Yoast_Notification $notification Notification to dismiss.
|
497 |
+
* @param string $meta_value Value to save in the dismissal.
|
498 |
+
*
|
499 |
+
* @return bool
|
500 |
+
*/
|
501 |
+
private static function dismiss_notification( Yoast_Notification $notification, $meta_value = 'seen' ) {
|
502 |
+
// Dismiss notification.
|
503 |
+
return ( false !== update_user_meta( get_current_user_id(), $notification->get_dismissal_key(), $meta_value ) );
|
504 |
+
}
|
505 |
+
|
506 |
+
/**
|
507 |
+
* Remove all notifications from storage
|
508 |
+
*/
|
509 |
+
private function remove_storage() {
|
510 |
+
|
511 |
+
delete_user_option( get_current_user_id(), self::STORAGE_KEY );
|
512 |
+
}
|
513 |
+
|
514 |
+
/**
|
515 |
+
* Clear local stored notifications
|
516 |
+
*/
|
517 |
+
private function clear_notifications() {
|
518 |
+
|
519 |
+
$this->notifications = array();
|
520 |
+
}
|
521 |
+
|
522 |
+
/**
|
523 |
+
* Filter out non-persistent notifications.
|
524 |
+
*
|
525 |
+
* @param Yoast_Notification $notification Notification to test for persistent.
|
526 |
+
*
|
527 |
+
* @since 3.2
|
528 |
+
*
|
529 |
+
* @return bool
|
530 |
+
*/
|
531 |
+
private function filter_persistent_notifications( Yoast_Notification $notification ) {
|
532 |
+
|
533 |
+
return $notification->is_persistent();
|
534 |
+
}
|
535 |
+
|
536 |
+
/**
|
537 |
+
* Filter out dismissed notifications
|
538 |
+
*
|
539 |
+
* @param Yoast_Notification $notification Notification to check.
|
540 |
+
*
|
541 |
+
* @return bool
|
542 |
+
*/
|
543 |
+
private function filter_dismissed_notifications( Yoast_Notification $notification ) {
|
544 |
+
|
545 |
+
return ! $this->maybe_dismiss_notification( $notification );
|
546 |
+
}
|
547 |
+
|
548 |
+
/**
|
549 |
+
* Convert Notification to array representation
|
550 |
+
*
|
551 |
+
* @param Yoast_Notification $notification Notification to convert.
|
552 |
+
*
|
553 |
+
* @since 3.2
|
554 |
+
*
|
555 |
+
* @return array
|
556 |
+
*/
|
557 |
+
private function notification_to_array( Yoast_Notification $notification ) {
|
558 |
+
|
559 |
+
return $notification->to_array();
|
560 |
+
}
|
561 |
+
|
562 |
+
/**
|
563 |
+
* Convert stored array to Notification.
|
564 |
+
*
|
565 |
+
* @param array $notification_data Array to convert to Notification.
|
566 |
+
*
|
567 |
+
* @return Yoast_Notification
|
568 |
+
*/
|
569 |
+
private function array_to_notification( $notification_data ) {
|
570 |
+
|
571 |
+
return new Yoast_Notification(
|
572 |
+
$notification_data['message'],
|
573 |
+
$notification_data['options']
|
574 |
+
);
|
575 |
+
}
|
576 |
+
|
577 |
+
/**
|
578 |
+
* Filter notifications that should not be displayed for the current user
|
579 |
+
*
|
580 |
+
* @param Yoast_Notification $notification Notification to test.
|
581 |
+
*
|
582 |
+
* @return bool
|
583 |
+
*/
|
584 |
+
private function filter_notification_current_user( Yoast_Notification $notification ) {
|
585 |
+
return $notification->display_for_current_user();
|
586 |
+
}
|
587 |
+
|
588 |
+
/**
|
589 |
+
* Checks if given notification is persistent.
|
590 |
+
*
|
591 |
+
* @param Yoast_Notification $notification The notification to check.
|
592 |
+
*
|
593 |
+
* @return bool True when notification is not persistent.
|
594 |
+
*/
|
595 |
+
private function is_notification_persistent( Yoast_Notification $notification ) {
|
596 |
+
return ! $notification->is_persistent();
|
597 |
+
}
|
598 |
+
}
|
admin/class-yoast-notification.php
ADDED
@@ -0,0 +1,330 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Notifications
|
4 |
+
* @since 1.5.3
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Implements individual notification.
|
9 |
+
*/
|
10 |
+
class Yoast_Notification {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @var string Type of capability check.
|
14 |
+
*/
|
15 |
+
const MATCH_ALL = 'all';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var string Type of capability check.
|
19 |
+
*/
|
20 |
+
const MATCH_ANY = 'any';
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var string Notification type.
|
24 |
+
*/
|
25 |
+
const ERROR = 'error';
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @var string Notification type.
|
29 |
+
*/
|
30 |
+
const WARNING = 'warning';
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @var string Notification type.
|
34 |
+
*/
|
35 |
+
const UPDATED = 'updated';
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Contains optional arguments:
|
39 |
+
*
|
40 |
+
* - type: The notification type, i.e. 'updated' or 'error'
|
41 |
+
* - id: The ID of the notification
|
42 |
+
* - nonce: Security nonce to use in case of dismissible notice.
|
43 |
+
* - priority: From 0 to 1, determines the order of Notifications.
|
44 |
+
* - dismissal_key: Option name to save dismissal information in, ID will be used if not supplied.
|
45 |
+
* - capabilities: Capabilities that a user must have for this Notification to show.
|
46 |
+
* - capability_check: How to check capability pass: all or any.
|
47 |
+
* - wpseo_page_only: Only display on wpseo page or on every page.
|
48 |
+
*
|
49 |
+
* @var array Options of this Notification.
|
50 |
+
*/
|
51 |
+
private $options = array();
|
52 |
+
|
53 |
+
/** @var array Contains default values for the optional arguments */
|
54 |
+
private $defaults = array(
|
55 |
+
'type' => self::UPDATED,
|
56 |
+
'id' => '',
|
57 |
+
'nonce' => null,
|
58 |
+
'priority' => 0.5,
|
59 |
+
'data_json' => array(),
|
60 |
+
'dismissal_key' => null,
|
61 |
+
'capabilities' => array(),
|
62 |
+
'capability_check' => self::MATCH_ALL,
|
63 |
+
);
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Notification class constructor.
|
67 |
+
*
|
68 |
+
* @param string $message Message string.
|
69 |
+
* @param array $options Set of options.
|
70 |
+
*/
|
71 |
+
public function __construct( $message, $options = array() ) {
|
72 |
+
$this->message = $message;
|
73 |
+
$this->options = $this->normalize_options( $options );
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Retrieve notification ID string.
|
78 |
+
*
|
79 |
+
* @return string
|
80 |
+
*/
|
81 |
+
public function get_id() {
|
82 |
+
return $this->options['id'];
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Retrieve nonce identifier.
|
87 |
+
*
|
88 |
+
* @return null|string Nonce for this Notification.
|
89 |
+
*/
|
90 |
+
public function get_nonce() {
|
91 |
+
if ( $this->options['id'] && empty( $this->options['nonce'] ) ) {
|
92 |
+
$this->options['nonce'] = wp_create_nonce( $this->options['id'] );
|
93 |
+
}
|
94 |
+
|
95 |
+
return $this->options['nonce'];
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Make sure the nonce is up to date
|
100 |
+
*/
|
101 |
+
public function refresh_nonce() {
|
102 |
+
if ( $this->options['id'] ) {
|
103 |
+
$this->options['nonce'] = wp_create_nonce( $this->options['id'] );
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Get the type of the notification
|
109 |
+
*
|
110 |
+
* @return string
|
111 |
+
*/
|
112 |
+
public function get_type() {
|
113 |
+
return $this->options['type'];
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Priority of the notification
|
118 |
+
*
|
119 |
+
* Relative to the type.
|
120 |
+
*
|
121 |
+
* @return float Returns the priority between 0 and 1.
|
122 |
+
*/
|
123 |
+
public function get_priority() {
|
124 |
+
return $this->options['priority'];
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Get the User Meta key to check for dismissal of notification
|
129 |
+
*
|
130 |
+
* @return string User Meta Option key that registers dismissal.
|
131 |
+
*/
|
132 |
+
public function get_dismissal_key() {
|
133 |
+
if ( empty( $this->options['dismissal_key'] ) ) {
|
134 |
+
return $this->options['id'];
|
135 |
+
}
|
136 |
+
|
137 |
+
return $this->options['dismissal_key'];
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Is this Notification persistent
|
142 |
+
*
|
143 |
+
* @return bool True if persistent, False if fire and forget.
|
144 |
+
*/
|
145 |
+
public function is_persistent() {
|
146 |
+
$id = $this->get_id();
|
147 |
+
|
148 |
+
return ! empty( $id );
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Check if the notification is relevant for the current user
|
153 |
+
*
|
154 |
+
* @return bool True if a user needs to see this Notification, False if not.
|
155 |
+
*/
|
156 |
+
public function display_for_current_user() {
|
157 |
+
// If the notification is for the current page only, always show.
|
158 |
+
if ( ! $this->is_persistent() ) {
|
159 |
+
return true;
|
160 |
+
}
|
161 |
+
|
162 |
+
// If the current user doesn't match capabilities.
|
163 |
+
return $this->match_capabilities();
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Does the current user match required capabilities
|
168 |
+
*
|
169 |
+
* @return bool
|
170 |
+
*/
|
171 |
+
public function match_capabilities() {
|
172 |
+
// Super Admin can do anything.
|
173 |
+
if ( is_multisite() && is_super_admin() ) {
|
174 |
+
return true;
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Filter capabilities that enable the displaying of this notification.
|
179 |
+
*
|
180 |
+
* @since 3.2
|
181 |
+
*
|
182 |
+
* @param array $capabilities The capabilities that must be present for this Notification.
|
183 |
+
* @param Yoast_Notification $notification The notification object.
|
184 |
+
*
|
185 |
+
* @return array of capabilities or empty for no restrictions.
|
186 |
+
*/
|
187 |
+
$capabilities = apply_filters( 'wpseo_notification_capabilities', $this->options['capabilities'], $this );
|
188 |
+
|
189 |
+
// Should be an array.
|
190 |
+
if ( ! is_array( $capabilities ) ) {
|
191 |
+
$capabilities = (array) $capabilities;
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Filter capability check to enable all or any capabilities.
|
196 |
+
*
|
197 |
+
* @since 3.2
|
198 |
+
*
|
199 |
+
* @param string $capability_check The type of check that will be used to determine if an capability is present.
|
200 |
+
* @param Yoast_Notification $notification The notification object.
|
201 |
+
*
|
202 |
+
* @return string self::MATCH_ALL or self::MATCH_ANY.
|
203 |
+
*/
|
204 |
+
$capability_check = apply_filters( 'wpseo_notification_capability_check', $this->options['capability_check'], $this );
|
205 |
+
|
206 |
+
if ( ! in_array( $capability_check, array( self::MATCH_ALL, self::MATCH_ANY ), true ) ) {
|
207 |
+
$capability_check = self::MATCH_ALL;
|
208 |
+
}
|
209 |
+
|
210 |
+
if ( ! empty( $capabilities ) ) {
|
211 |
+
|
212 |
+
$has_capabilities = array_filter( $capabilities, array( $this, 'has_capability' ) );
|
213 |
+
|
214 |
+
switch ( $capability_check ) {
|
215 |
+
case self::MATCH_ALL:
|
216 |
+
return $has_capabilities === $capabilities;
|
217 |
+
case self::MATCH_ANY:
|
218 |
+
return ! empty( $has_capabilities );
|
219 |
+
}
|
220 |
+
}
|
221 |
+
|
222 |
+
return true;
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Array filter function to find matched capabilities
|
227 |
+
*
|
228 |
+
* @param string $capability Capability to test.
|
229 |
+
*
|
230 |
+
* @return bool
|
231 |
+
*/
|
232 |
+
private function has_capability( $capability ) {
|
233 |
+
return current_user_can( $capability );
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Return the object properties as an array
|
238 |
+
*
|
239 |
+
* @return array
|
240 |
+
*/
|
241 |
+
public function to_array() {
|
242 |
+
return array(
|
243 |
+
'message' => $this->message,
|
244 |
+
'options' => $this->options,
|
245 |
+
);
|
246 |
+
}
|
247 |
+
|
248 |
+
/**
|
249 |
+
* Adds string (view) behaviour to the Notification
|
250 |
+
*
|
251 |
+
* @return string
|
252 |
+
*/
|
253 |
+
public function __toString() {
|
254 |
+
return $this->render();
|
255 |
+
}
|
256 |
+
|
257 |
+
/**
|
258 |
+
* Renders the notification as a string.
|
259 |
+
*
|
260 |
+
* @return string The rendered notification.
|
261 |
+
*/
|
262 |
+
public function render() {
|
263 |
+
$attributes = array();
|
264 |
+
|
265 |
+
// Default notification classes.
|
266 |
+
$classes = array(
|
267 |
+
'yoast-alert',
|
268 |
+
);
|
269 |
+
|
270 |
+
// Maintain WordPress visualisation of alerts when they are not persistent.
|
271 |
+
if ( ! $this->is_persistent() ) {
|
272 |
+
$classes[] = 'notice';
|
273 |
+
$classes[] = $this->get_type();
|
274 |
+
}
|
275 |
+
|
276 |
+
if ( ! empty( $classes ) ) {
|
277 |
+
$attributes['class'] = implode( ' ', $classes );
|
278 |
+
}
|
279 |
+
|
280 |
+
// Combined attribute key and value into a string.
|
281 |
+
array_walk( $attributes, array( $this, 'parse_attributes' ) );
|
282 |
+
|
283 |
+
// Build the output DIV.
|
284 |
+
return '<div ' . implode( ' ', $attributes ) . '>' . wpautop( $this->message ) . '</div>' . PHP_EOL;
|
285 |
+
}
|
286 |
+
|
287 |
+
/**
|
288 |
+
* Get the JSON if provided
|
289 |
+
*
|
290 |
+
* @return false|string
|
291 |
+
*/
|
292 |
+
public function get_json() {
|
293 |
+
if ( empty( $this->options['data_json'] ) ) {
|
294 |
+
return '';
|
295 |
+
}
|
296 |
+
|
297 |
+
return wp_json_encode( $this->options['data_json'] );
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* Make sure we only have values that we can work with
|
302 |
+
*
|
303 |
+
* @param array $options Options to normalize.
|
304 |
+
*
|
305 |
+
* @return array
|
306 |
+
*/
|
307 |
+
private function normalize_options( $options ) {
|
308 |
+
$options = wp_parse_args( $options, $this->defaults );
|
309 |
+
|
310 |
+
// Should not exceed 0 or 1.
|
311 |
+
$options['priority'] = min( 1, max( 0, $options['priority'] ) );
|
312 |
+
|
313 |
+
// Set default capabilities when not supplied.
|
314 |
+
if ( empty( $options['capabilities'] ) || array() === $options['capabilities'] ) {
|
315 |
+
$options['capabilities'] = array( 'wpseo_manage_options' );
|
316 |
+
}
|
317 |
+
|
318 |
+
return $options;
|
319 |
+
}
|
320 |
+
|
321 |
+
/**
|
322 |
+
* Format HTML element attributes
|
323 |
+
*
|
324 |
+
* @param string $value Attribute value.
|
325 |
+
* @param string $key Attribute name.
|
326 |
+
*/
|
327 |
+
private function parse_attributes( & $value, $key ) {
|
328 |
+
$value = sprintf( '%s="%s"', $key, esc_attr( $value ) );
|
329 |
+
}
|
330 |
+
}
|
admin/class-yoast-plugin-conflict.php
ADDED
@@ -0,0 +1,334 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
* @since 1.7.0
|
5 |
+
*/
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Base class for handling plugin conflicts.
|
9 |
+
*/
|
10 |
+
class Yoast_Plugin_Conflict {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* The plugins must be grouped per section.
|
14 |
+
*
|
15 |
+
* It's possible to check for each section if there are conflicting plugin
|
16 |
+
*
|
17 |
+
* @var array
|
18 |
+
*/
|
19 |
+
protected $plugins = array();
|
20 |
+
|
21 |
+
/**
|
22 |
+
* All the current active plugins will be stored in this private var
|
23 |
+
*
|
24 |
+
* @var array
|
25 |
+
*/
|
26 |
+
protected $all_active_plugins = array();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* After searching for active plugins that are in $this->plugins the active plugins will be stored in this
|
30 |
+
* property
|
31 |
+
*
|
32 |
+
* @var array
|
33 |
+
*/
|
34 |
+
protected $active_plugins = array();
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Property for holding instance of itself
|
38 |
+
*
|
39 |
+
* @var Yoast_Plugin_Conflict
|
40 |
+
*/
|
41 |
+
protected static $instance;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* For the use of singleton pattern. Create instance of itself and return his instance
|
45 |
+
*
|
46 |
+
* @param string $class_name Give the classname to initialize. If classname is false (empty) it will use it's own __CLASS__.
|
47 |
+
*
|
48 |
+
* @return Yoast_Plugin_Conflict
|
49 |
+
*/
|
50 |
+
public static function get_instance( $class_name = '' ) {
|
51 |
+
|
52 |
+
if ( is_null( self::$instance ) ) {
|
53 |
+
if ( ! is_string( $class_name ) || $class_name === '' ) {
|
54 |
+
$class_name = __CLASS__;
|
55 |
+
}
|
56 |
+
|
57 |
+
self::$instance = new $class_name();
|
58 |
+
}
|
59 |
+
|
60 |
+
return self::$instance;
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Setting instance, all active plugins and search for active plugins
|
65 |
+
*
|
66 |
+
* Protected constructor to prevent creating a new instance of the
|
67 |
+
* *Singleton* via the `new` operator from outside of this class.
|
68 |
+
*/
|
69 |
+
protected function __construct() {
|
70 |
+
// Set active plugins.
|
71 |
+
$this->all_active_plugins = get_option( 'active_plugins' );
|
72 |
+
|
73 |
+
if ( filter_input( INPUT_GET, 'action' ) === 'deactivate' ) {
|
74 |
+
$this->remove_deactivated_plugin();
|
75 |
+
}
|
76 |
+
|
77 |
+
// Search for active plugins.
|
78 |
+
$this->search_active_plugins();
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Check if there are conflicting plugins for given $plugin_section
|
83 |
+
*
|
84 |
+
* @param string $plugin_section Type of plugin conflict (such as Open Graph or sitemap).
|
85 |
+
*
|
86 |
+
* @return bool
|
87 |
+
*/
|
88 |
+
public function check_for_conflicts( $plugin_section ) {
|
89 |
+
|
90 |
+
static $sections_checked;
|
91 |
+
|
92 |
+
if ( $sections_checked === null ) {
|
93 |
+
$sections_checked = array();
|
94 |
+
}
|
95 |
+
|
96 |
+
if ( ! in_array( $plugin_section, $sections_checked, true ) ) {
|
97 |
+
$sections_checked[] = $plugin_section;
|
98 |
+
$has_conflicts = ( ! empty( $this->active_plugins[ $plugin_section ] ) );
|
99 |
+
|
100 |
+
return $has_conflicts;
|
101 |
+
}
|
102 |
+
|
103 |
+
return false;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Getting all the conflicting plugins and return them as a string.
|
108 |
+
*
|
109 |
+
* This method will loop through all conflicting plugins to get the details of each plugin. The plugin name
|
110 |
+
* will be taken from the details to parse a comma separated string, which can be use for by example a notice
|
111 |
+
*
|
112 |
+
* @param string $plugin_section Plugin conflict type (such as Open Graph or sitemap).
|
113 |
+
*
|
114 |
+
* @return string
|
115 |
+
*/
|
116 |
+
public function get_conflicting_plugins_as_string( $plugin_section ) {
|
117 |
+
if ( ! function_exists( 'get_plugin_data' ) ) {
|
118 |
+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
119 |
+
}
|
120 |
+
|
121 |
+
// Getting the active plugins by given section.
|
122 |
+
$plugins = $this->active_plugins[ $plugin_section ];
|
123 |
+
|
124 |
+
$plugin_names = array();
|
125 |
+
foreach ( $plugins as $plugin ) {
|
126 |
+
$name = WPSEO_Utils::get_plugin_name( $plugin );
|
127 |
+
if ( ! empty( $name ) ) {
|
128 |
+
$plugin_names[] = '<em>' . $name . '</em>';
|
129 |
+
}
|
130 |
+
}
|
131 |
+
unset( $plugins, $plugin );
|
132 |
+
|
133 |
+
if ( ! empty( $plugin_names ) ) {
|
134 |
+
return implode( ' & ', $plugin_names );
|
135 |
+
}
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Checks for given $plugin_sections for conflicts
|
140 |
+
*
|
141 |
+
* @param array $plugin_sections Set of sections.
|
142 |
+
*/
|
143 |
+
public function check_plugin_conflicts( $plugin_sections ) {
|
144 |
+
foreach ( $plugin_sections as $plugin_section => $readable_plugin_section ) {
|
145 |
+
// Check for conflicting plugins and show error if there are conflicts.
|
146 |
+
if ( $this->check_for_conflicts( $plugin_section ) ) {
|
147 |
+
$this->set_error( $plugin_section, $readable_plugin_section );
|
148 |
+
}
|
149 |
+
}
|
150 |
+
|
151 |
+
// List of all active sections.
|
152 |
+
$sections = array_keys( $plugin_sections );
|
153 |
+
// List of all sections.
|
154 |
+
$all_plugin_sections = array_keys( $this->plugins );
|
155 |
+
|
156 |
+
/*
|
157 |
+
* Get all sections that are inactive.
|
158 |
+
* These plugins need to be cleared.
|
159 |
+
*
|
160 |
+
* This happens when Sitemaps or OpenGraph implementations toggle active/disabled.
|
161 |
+
*/
|
162 |
+
$inactive_sections = array_diff( $all_plugin_sections, $sections );
|
163 |
+
if ( ! empty( $inactive_sections ) ) {
|
164 |
+
foreach ( $inactive_sections as $section ) {
|
165 |
+
array_walk( $this->plugins[ $section ], array( $this, 'clear_error' ) );
|
166 |
+
}
|
167 |
+
}
|
168 |
+
|
169 |
+
// For active sections clear errors for inactive plugins.
|
170 |
+
foreach ( $sections as $section ) {
|
171 |
+
// By default clear errors for all plugins of the section.
|
172 |
+
$inactive_plugins = $this->plugins[ $section ];
|
173 |
+
|
174 |
+
// If there are active plugins, filter them from being cleared.
|
175 |
+
if ( isset( $this->active_plugins[ $section ] ) ) {
|
176 |
+
$inactive_plugins = array_diff( $this->plugins[ $section ], $this->active_plugins[ $section ] );
|
177 |
+
}
|
178 |
+
|
179 |
+
array_walk( $inactive_plugins, array( $this, 'clear_error' ) );
|
180 |
+
}
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Setting an error on the screen
|
185 |
+
*
|
186 |
+
* @param string $plugin_section Type of conflict group (such as Open Graph or sitemap).
|
187 |
+
* @param string $readable_plugin_section This is the value for the translation.
|
188 |
+
*/
|
189 |
+
protected function set_error( $plugin_section, $readable_plugin_section ) {
|
190 |
+
|
191 |
+
$notification_center = Yoast_Notification_Center::get();
|
192 |
+
|
193 |
+
foreach ( $this->active_plugins[ $plugin_section ] as $plugin_file ) {
|
194 |
+
|
195 |
+
$plugin_name = WPSEO_Utils::get_plugin_name( $plugin_file );
|
196 |
+
|
197 |
+
$error_message = '';
|
198 |
+
/* translators: %1$s: 'Facebook & Open Graph' plugin name(s) of possibly conflicting plugin(s), %2$s to Yoast SEO */
|
199 |
+
$error_message .= '<p>' . sprintf( __( 'The %1$s plugin might cause issues when used in conjunction with %2$s.', 'wordpress-seo' ), '<em>' . $plugin_name . '</em>', 'Yoast SEO' ) . '</p>';
|
200 |
+
$error_message .= '<p>' . sprintf( $readable_plugin_section, 'Yoast SEO', $plugin_name ) . '</p>';
|
201 |
+
|
202 |
+
/* translators: %s: 'Facebook' plugin name of possibly conflicting plugin */
|
203 |
+
$error_message .= '<a class="button button-primary" href="' . wp_nonce_url( 'plugins.php?action=deactivate&plugin=' . $plugin_file . '&plugin_status=all', 'deactivate-plugin_' . $plugin_file ) . '">' . sprintf( __( 'Deactivate %s', 'wordpress-seo' ), WPSEO_Utils::get_plugin_name( $plugin_file ) ) . '</a> ';
|
204 |
+
|
205 |
+
$identifier = $this->get_notification_identifier( $plugin_file );
|
206 |
+
|
207 |
+
// Add the message to the notifications center.
|
208 |
+
$notification_center->add_notification(
|
209 |
+
new Yoast_Notification(
|
210 |
+
$error_message,
|
211 |
+
array(
|
212 |
+
'type' => Yoast_Notification::ERROR,
|
213 |
+
'id' => 'wpseo-conflict-' . $identifier,
|
214 |
+
)
|
215 |
+
)
|
216 |
+
);
|
217 |
+
}
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* Clear the notification for a plugin
|
222 |
+
*
|
223 |
+
* @param string $plugin_file Clear the optional notification for this plugin.
|
224 |
+
*/
|
225 |
+
public function clear_error( $plugin_file ) {
|
226 |
+
$identifier = $this->get_notification_identifier( $plugin_file );
|
227 |
+
|
228 |
+
$notification_center = Yoast_Notification_Center::get();
|
229 |
+
$notification = $notification_center->get_notification_by_id( 'wpseo-conflict-' . $identifier );
|
230 |
+
|
231 |
+
if ( $notification ) {
|
232 |
+
$notification_center->remove_notification( $notification );
|
233 |
+
}
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Loop through the $this->plugins to check if one of the plugins is active.
|
238 |
+
*
|
239 |
+
* This method will store the active plugins in $this->active_plugins.
|
240 |
+
*/
|
241 |
+
protected function search_active_plugins() {
|
242 |
+
foreach ( $this->plugins as $plugin_section => $plugins ) {
|
243 |
+
$this->check_plugins_active( $plugins, $plugin_section );
|
244 |
+
}
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Loop through plugins and check if each plugin is active
|
249 |
+
*
|
250 |
+
* @param array $plugins Set of plugins.
|
251 |
+
* @param string $plugin_section Type of conflict group (such as Open Graph or sitemap).
|
252 |
+
*/
|
253 |
+
protected function check_plugins_active( $plugins, $plugin_section ) {
|
254 |
+
foreach ( $plugins as $plugin ) {
|
255 |
+
if ( $this->check_plugin_is_active( $plugin ) ) {
|
256 |
+
$this->add_active_plugin( $plugin_section, $plugin );
|
257 |
+
}
|
258 |
+
}
|
259 |
+
}
|
260 |
+
|
261 |
+
|
262 |
+
/**
|
263 |
+
* Check if given plugin exists in array with all_active_plugins
|
264 |
+
*
|
265 |
+
* @param string $plugin Plugin basename string.
|
266 |
+
*
|
267 |
+
* @return bool
|
268 |
+
*/
|
269 |
+
protected function check_plugin_is_active( $plugin ) {
|
270 |
+
return in_array( $plugin, $this->all_active_plugins, true );
|
271 |
+
}
|
272 |
+
|
273 |
+
/**
|
274 |
+
* Add plugin to the list of active plugins.
|
275 |
+
*
|
276 |
+
* This method will check first if key $plugin_section exists, if not it will create an empty array
|
277 |
+
* If $plugin itself doesn't exist it will be added.
|
278 |
+
*
|
279 |
+
* @param string $plugin_section Type of conflict group (such as Open Graph or sitemap).
|
280 |
+
* @param string $plugin Plugin basename string.
|
281 |
+
*/
|
282 |
+
protected function add_active_plugin( $plugin_section, $plugin ) {
|
283 |
+
|
284 |
+
if ( ! array_key_exists( $plugin_section, $this->active_plugins ) ) {
|
285 |
+
$this->active_plugins[ $plugin_section ] = array();
|
286 |
+
}
|
287 |
+
|
288 |
+
if ( ! in_array( $plugin, $this->active_plugins[ $plugin_section ], true ) ) {
|
289 |
+
$this->active_plugins[ $plugin_section ][] = $plugin;
|
290 |
+
}
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* Search in $this->plugins for the given $plugin
|
295 |
+
*
|
296 |
+
* If there is a result it will return the plugin category
|
297 |
+
*
|
298 |
+
* @param string $plugin Plugin basename string.
|
299 |
+
*
|
300 |
+
* @return int|string
|
301 |
+
*/
|
302 |
+
protected function find_plugin_category( $plugin ) {
|
303 |
+
|
304 |
+
foreach ( $this->plugins as $plugin_section => $plugins ) {
|
305 |
+
if ( in_array( $plugin, $plugins, true ) ) {
|
306 |
+
return $plugin_section;
|
307 |
+
}
|
308 |
+
}
|
309 |
+
|
310 |
+
}
|
311 |
+
|
312 |
+
/**
|
313 |
+
* When being in the deactivation process the currently deactivated plugin has to be removed.
|
314 |
+
*/
|
315 |
+
private function remove_deactivated_plugin() {
|
316 |
+
$deactivated_plugin = filter_input( INPUT_GET, 'plugin' );
|
317 |
+
$key_to_remove = array_search( $deactivated_plugin, $this->all_active_plugins, true );
|
318 |
+
|
319 |
+
if ( $key_to_remove !== false ) {
|
320 |
+
unset( $this->all_active_plugins[ $key_to_remove ] );
|
321 |
+
}
|
322 |
+
}
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Get the identifier from the plugin file
|
326 |
+
*
|
327 |
+
* @param string $plugin_file Plugin file to get Identifier from.
|
328 |
+
*
|
329 |
+
* @return string
|
330 |
+
*/
|
331 |
+
private function get_notification_identifier( $plugin_file ) {
|
332 |
+
return md5( $plugin_file );
|
333 |
+
}
|
334 |
+
}
|
admin/config-ui/class-configuration-components.php
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Configuration_Components
|
8 |
+
*/
|
9 |
+
class WPSEO_Configuration_Components {
|
10 |
+
|
11 |
+
/** @var WPSEO_Config_Component[] List of registered components */
|
12 |
+
protected $components = array();
|
13 |
+
|
14 |
+
/** @var WPSEO_Configuration_Options_Adapter Adapter */
|
15 |
+
protected $adapter;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Add default components.
|
19 |
+
*/
|
20 |
+
public function initialize() {
|
21 |
+
$this->add_component( new WPSEO_Config_Component_Connect_Google_Search_Console() );
|
22 |
+
$this->add_component( new WPSEO_Config_Component_Mailchimp_Signup() );
|
23 |
+
$this->add_component( new WPSEO_Config_Component_Configuration_Choices() );
|
24 |
+
$this->add_component( new WPSEO_Config_Component_Suggestions() );
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Add a component
|
29 |
+
*
|
30 |
+
* @param WPSEO_Config_Component $component Component to add.
|
31 |
+
*/
|
32 |
+
public function add_component( WPSEO_Config_Component $component ) {
|
33 |
+
$this->components[] = $component;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Sets the storage to use.
|
38 |
+
*
|
39 |
+
* @param WPSEO_Configuration_Storage $storage Storage to use.
|
40 |
+
*/
|
41 |
+
public function set_storage( WPSEO_Configuration_Storage $storage ) {
|
42 |
+
$this->set_adapter( $storage->get_adapter() );
|
43 |
+
|
44 |
+
foreach ( $this->components as $component ) {
|
45 |
+
$storage->add_field( $component->get_field() );
|
46 |
+
}
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Sets the adapter to use.
|
51 |
+
*
|
52 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to use.
|
53 |
+
*/
|
54 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
55 |
+
$this->adapter = $adapter;
|
56 |
+
|
57 |
+
foreach ( $this->components as $component ) {
|
58 |
+
$adapter->add_custom_lookup(
|
59 |
+
$component->get_field()->get_identifier(),
|
60 |
+
array(
|
61 |
+
$component,
|
62 |
+
'get_data',
|
63 |
+
),
|
64 |
+
array(
|
65 |
+
$component,
|
66 |
+
'set_data',
|
67 |
+
)
|
68 |
+
);
|
69 |
+
}
|
70 |
+
}
|
71 |
+
}
|
admin/config-ui/class-configuration-endpoint.php
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Configuration_Endpoint
|
8 |
+
*/
|
9 |
+
class WPSEO_Configuration_Endpoint {
|
10 |
+
|
11 |
+
const REST_NAMESPACE = 'yoast/v1';
|
12 |
+
const ENDPOINT_RETRIEVE = 'configurator';
|
13 |
+
const ENDPOINT_STORE = 'configurator';
|
14 |
+
|
15 |
+
const CAPABILITY_RETRIEVE = 'wpseo_manage_options';
|
16 |
+
const CAPABILITY_STORE = 'wpseo_manage_options';
|
17 |
+
|
18 |
+
/** @var WPSEO_Configuration_Service Service to use */
|
19 |
+
protected $service;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Sets the service to use.
|
23 |
+
*
|
24 |
+
* @param WPSEO_Configuration_Service $service Service to use.
|
25 |
+
*/
|
26 |
+
public function set_service( WPSEO_Configuration_Service $service ) {
|
27 |
+
$this->service = $service;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Register REST routes.
|
32 |
+
*/
|
33 |
+
public function register() {
|
34 |
+
// Register fetch config.
|
35 |
+
register_rest_route( self::REST_NAMESPACE, self::ENDPOINT_RETRIEVE, array(
|
36 |
+
'methods' => 'GET',
|
37 |
+
'callback' => array(
|
38 |
+
$this->service,
|
39 |
+
'get_configuration',
|
40 |
+
),
|
41 |
+
'permission_callback' => array(
|
42 |
+
$this,
|
43 |
+
'can_retrieve_data',
|
44 |
+
),
|
45 |
+
) );
|
46 |
+
|
47 |
+
// Register save changes.
|
48 |
+
register_rest_route( self::REST_NAMESPACE, self::ENDPOINT_STORE, array(
|
49 |
+
'methods' => 'POST',
|
50 |
+
'callback' => array(
|
51 |
+
$this->service,
|
52 |
+
'set_configuration',
|
53 |
+
),
|
54 |
+
'permission_callback' => array(
|
55 |
+
$this,
|
56 |
+
'can_save_data',
|
57 |
+
),
|
58 |
+
) );
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Permission callback implementation
|
63 |
+
*
|
64 |
+
* @return bool
|
65 |
+
*/
|
66 |
+
public function can_retrieve_data() {
|
67 |
+
return current_user_can( self::CAPABILITY_RETRIEVE );
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Permission callback implementation
|
72 |
+
*
|
73 |
+
* @return bool
|
74 |
+
*/
|
75 |
+
public function can_save_data() {
|
76 |
+
return current_user_can( self::CAPABILITY_STORE );
|
77 |
+
}
|
78 |
+
}
|
admin/config-ui/class-configuration-options-adapter.php
ADDED
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Configuration_Options_Adapter
|
8 |
+
*
|
9 |
+
* Convert Configuration settings to WPSEO Options
|
10 |
+
*
|
11 |
+
* @since 3.6
|
12 |
+
*/
|
13 |
+
class WPSEO_Configuration_Options_Adapter {
|
14 |
+
|
15 |
+
const OPTION_TYPE_WORDPRESS = 'wordpress';
|
16 |
+
const OPTION_TYPE_YOAST = 'yoast';
|
17 |
+
const OPTION_TYPE_CUSTOM = 'custom';
|
18 |
+
|
19 |
+
/** @var array List of registered lookups */
|
20 |
+
protected $lookup = array();
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Add a lookup for a WordPress native option
|
24 |
+
*
|
25 |
+
* @param string $class_name Class to bind to an option.
|
26 |
+
* @param string $option Option name to use.
|
27 |
+
*
|
28 |
+
* @throws InvalidArgumentException Thrown when invalid input is provided.
|
29 |
+
*/
|
30 |
+
public function add_wordpress_lookup( $class_name, $option ) {
|
31 |
+
|
32 |
+
if ( ! is_string( $option ) ) {
|
33 |
+
throw new InvalidArgumentException( 'WordPress option must be a string.' );
|
34 |
+
}
|
35 |
+
|
36 |
+
$this->add_lookup( $class_name, self::OPTION_TYPE_WORDPRESS, $option );
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Add a lookup for a Yoast option
|
41 |
+
*
|
42 |
+
* @param string $class_name Class to bind to the lookup.
|
43 |
+
* @param string $key Key in the option group to bind to.
|
44 |
+
*
|
45 |
+
* @throws InvalidArgumentException Thrown when invalid input is provided.
|
46 |
+
*/
|
47 |
+
public function add_option_lookup( $class_name, $key ) {
|
48 |
+
|
49 |
+
$test = WPSEO_Options::get( $key );
|
50 |
+
if ( is_null( $test ) ) {
|
51 |
+
/* translators: %1$s resolves to the option name passed to the lookup registration */
|
52 |
+
throw new InvalidArgumentException( sprintf( __( 'Yoast option %1$s not found.', 'wordpress-seo' ), $key ) );
|
53 |
+
}
|
54 |
+
|
55 |
+
$this->add_lookup( $class_name, self::OPTION_TYPE_YOAST, $key );
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Add a lookup for a Yoast option
|
60 |
+
*
|
61 |
+
* @param string $class_name Class to bind to the lookup.
|
62 |
+
* @param string $option Option group to use.
|
63 |
+
* @param string $key Key in the option group to bind to.
|
64 |
+
*
|
65 |
+
* @deprecated 7.0
|
66 |
+
*
|
67 |
+
* @throws InvalidArgumentException Thrown when invalid input is provided.
|
68 |
+
*/
|
69 |
+
public function add_yoast_lookup( $class_name, $option, $key ) {
|
70 |
+
_deprecated_function( __METHOD__, 'WPSEO 7.0', 'WPSEO_Configuration_Options_Adapter::add_option_lookup' );
|
71 |
+
$this->add_option_lookup( $class_name, $key );
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Add a lookup for a custom implementation
|
76 |
+
*
|
77 |
+
* @param string $class_name Class to bind to the lookup.
|
78 |
+
* @param callable $callback_get Callback to retrieve data.
|
79 |
+
* @param callable $callback_set Callback to save data.
|
80 |
+
*
|
81 |
+
* @throws InvalidArgumentException Thrown when invalid input is provided.
|
82 |
+
*/
|
83 |
+
public function add_custom_lookup( $class_name, $callback_get, $callback_set ) {
|
84 |
+
|
85 |
+
if ( ! is_callable( $callback_get ) || ! is_callable( $callback_set ) ) {
|
86 |
+
throw new InvalidArgumentException( 'Custom option must be callable.' );
|
87 |
+
}
|
88 |
+
|
89 |
+
$this->add_lookup( $class_name, self::OPTION_TYPE_CUSTOM, array(
|
90 |
+
$callback_get,
|
91 |
+
$callback_set,
|
92 |
+
) );
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Add a field lookup.
|
97 |
+
*
|
98 |
+
* @param string $class_name Class to add lookup for.
|
99 |
+
* @param string $type Type of lookup.
|
100 |
+
* @param string|array $option Implementation of the lookup.
|
101 |
+
*
|
102 |
+
* @throws Exception Thrown when invalid input is provided.
|
103 |
+
*/
|
104 |
+
protected function add_lookup( $class_name, $type, $option ) {
|
105 |
+
$this->lookup[ $class_name ] = array(
|
106 |
+
'type' => $type,
|
107 |
+
'option' => $option,
|
108 |
+
);
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Get the data for the provided field
|
113 |
+
*
|
114 |
+
* @param WPSEO_Config_Field $field Field to get data for.
|
115 |
+
*
|
116 |
+
* @return mixed
|
117 |
+
*/
|
118 |
+
public function get( WPSEO_Config_Field $field ) {
|
119 |
+
$identifier = $field->get_identifier();
|
120 |
+
|
121 |
+
// Lookup option and retrieve value.
|
122 |
+
$type = $this->get_option_type( $identifier );
|
123 |
+
$option = $this->get_option( $identifier );
|
124 |
+
|
125 |
+
switch ( $type ) {
|
126 |
+
case self::OPTION_TYPE_WORDPRESS:
|
127 |
+
return get_option( $option );
|
128 |
+
|
129 |
+
case self::OPTION_TYPE_YOAST:
|
130 |
+
return WPSEO_Options::get( $option );
|
131 |
+
|
132 |
+
case self::OPTION_TYPE_CUSTOM:
|
133 |
+
return call_user_func( $option[0] );
|
134 |
+
}
|
135 |
+
|
136 |
+
return null;
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Save data from a field
|
141 |
+
*
|
142 |
+
* @param WPSEO_Config_Field $field Field to use for lookup.
|
143 |
+
* @param mixed $value Value to save to the lookup of the field.
|
144 |
+
*
|
145 |
+
* @return bool
|
146 |
+
*/
|
147 |
+
public function set( WPSEO_Config_Field $field, $value ) {
|
148 |
+
$identifier = $field->get_identifier();
|
149 |
+
|
150 |
+
// Lookup option and retrieve value.
|
151 |
+
$type = $this->get_option_type( $identifier );
|
152 |
+
$option = $this->get_option( $identifier );
|
153 |
+
|
154 |
+
switch ( $type ) {
|
155 |
+
case self::OPTION_TYPE_WORDPRESS:
|
156 |
+
return update_option( $option, $value );
|
157 |
+
|
158 |
+
case self::OPTION_TYPE_YOAST:
|
159 |
+
return WPSEO_Options::set( $option, $value );
|
160 |
+
|
161 |
+
case self::OPTION_TYPE_CUSTOM:
|
162 |
+
return call_user_func( $option[1], $value );
|
163 |
+
}
|
164 |
+
|
165 |
+
return false;
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Get the lookup type for a specific class
|
170 |
+
*
|
171 |
+
* @param string $class_name Class to get the type of.
|
172 |
+
*
|
173 |
+
* @return null|string
|
174 |
+
*/
|
175 |
+
protected function get_option_type( $class_name ) {
|
176 |
+
if ( ! isset( $this->lookup[ $class_name ] ) ) {
|
177 |
+
return null;
|
178 |
+
}
|
179 |
+
|
180 |
+
return $this->lookup[ $class_name ]['type'];
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Get the option for a specific class
|
185 |
+
*
|
186 |
+
* @param string $class_name Class to get the option of.
|
187 |
+
*
|
188 |
+
* @return null|string|array
|
189 |
+
*/
|
190 |
+
protected function get_option( $class_name ) {
|
191 |
+
if ( ! isset( $this->lookup[ $class_name ] ) ) {
|
192 |
+
return null;
|
193 |
+
}
|
194 |
+
|
195 |
+
return $this->lookup[ $class_name ]['option'];
|
196 |
+
}
|
197 |
+
}
|
admin/config-ui/class-configuration-page.php
ADDED
@@ -0,0 +1,262 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @class WPSEO_Configuration_Wizard Loads the Yoast configuration wizard.
|
8 |
+
*/
|
9 |
+
class WPSEO_Configuration_Page {
|
10 |
+
|
11 |
+
const PAGE_IDENTIFIER = 'wpseo_configurator';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Sets the hooks when the user has enought rights and is on the right page.
|
15 |
+
*/
|
16 |
+
public function set_hooks() {
|
17 |
+
if ( ! ( $this->is_config_page() && current_user_can( WPSEO_Configuration_Endpoint::CAPABILITY_RETRIEVE ) ) ) {
|
18 |
+
return;
|
19 |
+
}
|
20 |
+
|
21 |
+
if ( $this->should_add_notification() ) {
|
22 |
+
$this->add_notification();
|
23 |
+
}
|
24 |
+
|
25 |
+
// Register the page for the wizard.
|
26 |
+
add_action( 'admin_menu', array( $this, 'add_wizard_page' ) );
|
27 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
28 |
+
add_action( 'admin_init', array( $this, 'render_wizard_page' ) );
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Check if the configuration is finished. If so, just remove the notification.
|
33 |
+
*/
|
34 |
+
public function catch_configuration_request() {
|
35 |
+
$configuration_page = filter_input( INPUT_GET, 'configuration' );
|
36 |
+
$page = filter_input( INPUT_GET, 'page' );
|
37 |
+
|
38 |
+
if ( ! ( $configuration_page === 'finished' && ( $page === WPSEO_Admin::PAGE_IDENTIFIER ) ) ) {
|
39 |
+
return;
|
40 |
+
}
|
41 |
+
|
42 |
+
$this->remove_notification();
|
43 |
+
$this->remove_notification_option();
|
44 |
+
|
45 |
+
wp_redirect( admin_url( 'admin.php?page=' . WPSEO_Admin::PAGE_IDENTIFIER ) );
|
46 |
+
exit;
|
47 |
+
}
|
48 |
+
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Registers the page for the wizard.
|
52 |
+
*/
|
53 |
+
public function add_wizard_page() {
|
54 |
+
add_dashboard_page( '', '', 'wpseo_manage_options', self::PAGE_IDENTIFIER, '' );
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Renders the wizard page and exits to prevent the WordPress UI from loading.
|
59 |
+
*/
|
60 |
+
public function render_wizard_page() {
|
61 |
+
$this->show_wizard();
|
62 |
+
exit;
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Enqueues the assets needed for the wizard.
|
67 |
+
*/
|
68 |
+
public function enqueue_assets() {
|
69 |
+
wp_enqueue_media();
|
70 |
+
|
71 |
+
/*
|
72 |
+
* Print the `forms.css` WP stylesheet before any Yoast style, this way
|
73 |
+
* it's easier to override selectors with the same specificity later.
|
74 |
+
*/
|
75 |
+
wp_enqueue_style( 'forms' );
|
76 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
77 |
+
$asset_manager->register_assets();
|
78 |
+
$asset_manager->enqueue_script( 'configuration-wizard' );
|
79 |
+
$asset_manager->enqueue_style( 'yoast-components' );
|
80 |
+
|
81 |
+
$config = $this->get_config();
|
82 |
+
|
83 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'configuration-wizard', 'yoastWizardConfig', $config );
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Setup Wizard Header.
|
88 |
+
*/
|
89 |
+
public function show_wizard() {
|
90 |
+
$this->enqueue_assets();
|
91 |
+
$dashboard_url = admin_url( '/admin.php?page=wpseo_dashboard' );
|
92 |
+
?>
|
93 |
+
<!DOCTYPE html>
|
94 |
+
<!--[if IE 9]>
|
95 |
+
<html class="ie9" <?php language_attributes(); ?> >
|
96 |
+
<![endif]-->
|
97 |
+
<!--[if !(IE 9) ]><!-->
|
98 |
+
<html <?php language_attributes(); ?>>
|
99 |
+
<!--<![endif]-->
|
100 |
+
<head>
|
101 |
+
<meta name="viewport" content="width=device-width"/>
|
102 |
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
103 |
+
<title><?php
|
104 |
+
printf(
|
105 |
+
/* translators: %s expands to Yoast SEO. */
|
106 |
+
esc_html__( '%s › Configuration Wizard', 'wordpress-seo' ),
|
107 |
+
'Yoast SEO' );
|
108 |
+
?></title>
|
109 |
+
<?php
|
110 |
+
wp_print_head_scripts();
|
111 |
+
wp_print_styles( 'yoast-seo-yoast-components' );
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Is called before the closing </head> tag in the Yoast Configuration wizard.
|
115 |
+
*
|
116 |
+
* Allows users to add their own scripts or styles.
|
117 |
+
*
|
118 |
+
* @since 4.0
|
119 |
+
*/
|
120 |
+
do_action( 'wpseo_configuration_wizard_head' );
|
121 |
+
?>
|
122 |
+
</head>
|
123 |
+
<body class="wp-admin wp-core-ui">
|
124 |
+
<div id="wizard"></div>
|
125 |
+
<div role="contentinfo" class="yoast-wizard-return-link-container">
|
126 |
+
<a class="button yoast-wizard-return-link" href="<?php echo esc_url( $dashboard_url ); ?>">
|
127 |
+
<span aria-hidden="true" class="dashicons dashicons-no"></span>
|
128 |
+
<?php
|
129 |
+
esc_html_e( 'Close wizard', 'wordpress-seo' );
|
130 |
+
?>
|
131 |
+
</a>
|
132 |
+
</div>
|
133 |
+
<?php
|
134 |
+
wp_print_media_templates();
|
135 |
+
wp_print_footer_scripts();
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Is called before the closing </body> tag in the Yoast Configuration wizard.
|
139 |
+
*
|
140 |
+
* Allows users to add their own scripts.
|
141 |
+
*
|
142 |
+
* @since 4.0
|
143 |
+
*/
|
144 |
+
do_action( 'wpseo_configuration_wizard_footer' );
|
145 |
+
|
146 |
+
wp_print_scripts( 'yoast-seo-configuration-wizard' );
|
147 |
+
?>
|
148 |
+
</body>
|
149 |
+
</html>
|
150 |
+
<?php
|
151 |
+
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* Get the API config for the wizard.
|
156 |
+
*
|
157 |
+
* @return array The API endpoint config.
|
158 |
+
*/
|
159 |
+
public function get_config() {
|
160 |
+
$service = new WPSEO_GSC_Service();
|
161 |
+
$config = array(
|
162 |
+
'namespace' => WPSEO_Configuration_Endpoint::REST_NAMESPACE,
|
163 |
+
'endpoint_retrieve' => WPSEO_Configuration_Endpoint::ENDPOINT_RETRIEVE,
|
164 |
+
'endpoint_store' => WPSEO_Configuration_Endpoint::ENDPOINT_STORE,
|
165 |
+
'nonce' => wp_create_nonce( 'wp_rest' ),
|
166 |
+
'root' => esc_url_raw( rest_url() ),
|
167 |
+
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
168 |
+
'finishUrl' => admin_url( 'admin.php?page=wpseo_dashboard&configuration=finished' ),
|
169 |
+
'gscAuthURL' => $service->get_client()->createAuthUrl(),
|
170 |
+
'gscProfiles' => $service->get_sites(),
|
171 |
+
'gscNonce' => wp_create_nonce( 'wpseo-gsc-ajax-security' ),
|
172 |
+
);
|
173 |
+
|
174 |
+
return $config;
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Checks if the current page is the configuration page.
|
179 |
+
*
|
180 |
+
* @return bool
|
181 |
+
*/
|
182 |
+
protected function is_config_page() {
|
183 |
+
return ( filter_input( INPUT_GET, 'page' ) === self::PAGE_IDENTIFIER );
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Returns the translations necessary for the configuration wizard.
|
188 |
+
*
|
189 |
+
* @deprecated 4.9
|
190 |
+
*
|
191 |
+
* @returns array The translations for the configuration wizard.
|
192 |
+
*/
|
193 |
+
public function get_translations() {
|
194 |
+
_deprecated_function( __METHOD__, 'WPSEO 4.9', 'WPSEO_' );
|
195 |
+
|
196 |
+
$translations = new WPSEO_Configuration_Translations( WPSEO_Utils::get_user_locale() );
|
197 |
+
|
198 |
+
return $translations->retrieve();
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Adds a notification to the notification center.
|
203 |
+
*/
|
204 |
+
private function add_notification() {
|
205 |
+
$notification_center = Yoast_Notification_Center::get();
|
206 |
+
$notification_center->add_notification( self::get_notification() );
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Removes the notification from the notification center.
|
211 |
+
*/
|
212 |
+
private function remove_notification() {
|
213 |
+
$notification_center = Yoast_Notification_Center::get();
|
214 |
+
$notification_center->remove_notification( self::get_notification() );
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* Gets the notification.
|
219 |
+
*
|
220 |
+
* @return Yoast_Notification
|
221 |
+
*/
|
222 |
+
private static function get_notification() {
|
223 |
+
$message = __( 'The configuration wizard helps you to easily configure your site to have the optimal SEO settings.', 'wordpress-seo' );
|
224 |
+
$message .= '<br/>';
|
225 |
+
$message .= sprintf(
|
226 |
+
/* translators: %1$s resolves to Yoast SEO, %2$s resolves to the starting tag of the link to the wizard, %3$s resolves to the closing link tag */
|
227 |
+
__( 'We have detected that you have not finished this wizard yet, so we recommend you to %2$sstart the configuration wizard to configure %1$s%3$s.', 'wordpress-seo' ),
|
228 |
+
'Yoast SEO',
|
229 |
+
'<a href="' . admin_url( '?page=' . self::PAGE_IDENTIFIER ) . '">',
|
230 |
+
'</a>'
|
231 |
+
);
|
232 |
+
|
233 |
+
$notification = new Yoast_Notification(
|
234 |
+
$message,
|
235 |
+
array(
|
236 |
+
'type' => Yoast_Notification::WARNING,
|
237 |
+
'id' => 'wpseo-dismiss-onboarding-notice',
|
238 |
+
'capabilities' => 'wpseo_manage_options',
|
239 |
+
'priority' => 0.8,
|
240 |
+
)
|
241 |
+
);
|
242 |
+
|
243 |
+
return $notification;
|
244 |
+
}
|
245 |
+
|
246 |
+
/**
|
247 |
+
* When the notice should be shown.
|
248 |
+
*
|
249 |
+
* @return bool
|
250 |
+
*/
|
251 |
+
private function should_add_notification() {
|
252 |
+
return ( WPSEO_Options::get( 'show_onboarding_notice' ) === true );
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Remove the options that triggers the notice for the configuration wizard.
|
257 |
+
*/
|
258 |
+
private function remove_notification_option() {
|
259 |
+
WPSEO_Options::set( 'show_onboarding_notice', false );
|
260 |
+
}
|
261 |
+
|
262 |
+
}
|
admin/config-ui/class-configuration-service.php
ADDED
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Configuration_Service
|
8 |
+
*/
|
9 |
+
class WPSEO_Configuration_Service {
|
10 |
+
|
11 |
+
/** @var WPSEO_Configuration_Structure */
|
12 |
+
protected $structure;
|
13 |
+
|
14 |
+
/** @var WPSEO_Configuration_Components */
|
15 |
+
protected $components;
|
16 |
+
|
17 |
+
/** @var WPSEO_Configuration_Storage */
|
18 |
+
protected $storage;
|
19 |
+
|
20 |
+
/** @var WPSEO_Configuration_Endpoint */
|
21 |
+
protected $endpoint;
|
22 |
+
|
23 |
+
/** @var WPSEO_Configuration_Options_Adapter */
|
24 |
+
protected $adapter;
|
25 |
+
|
26 |
+
/** @var WPSEO_Configuration_Translations */
|
27 |
+
protected $translations;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Hook into the REST API and switch language.
|
31 |
+
*/
|
32 |
+
public function initialize() {
|
33 |
+
$this->set_default_providers();
|
34 |
+
$this->endpoint->register();
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Set default handlers
|
39 |
+
*/
|
40 |
+
public function set_default_providers() {
|
41 |
+
$this->set_storage( new WPSEO_Configuration_Storage() );
|
42 |
+
$this->set_options_adapter( new WPSEO_Configuration_Options_Adapter() );
|
43 |
+
$this->set_components( new WPSEO_Configuration_Components() );
|
44 |
+
$this->set_endpoint( new WPSEO_Configuration_Endpoint() );
|
45 |
+
$this->set_structure( new WPSEO_Configuration_Structure() );
|
46 |
+
$this->set_translations( new WPSEO_Configuration_Translations( WPSEO_Utils::get_user_locale() ) );
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Set storage handler
|
51 |
+
*
|
52 |
+
* @param WPSEO_Configuration_Storage $storage Storage handler to use.
|
53 |
+
*/
|
54 |
+
public function set_storage( WPSEO_Configuration_Storage $storage ) {
|
55 |
+
$this->storage = $storage;
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Set endpoint handler
|
60 |
+
*
|
61 |
+
* @param WPSEO_Configuration_Endpoint $endpoint Endpoint implementation to use.
|
62 |
+
*/
|
63 |
+
public function set_endpoint( WPSEO_Configuration_Endpoint $endpoint ) {
|
64 |
+
$this->endpoint = $endpoint;
|
65 |
+
$this->endpoint->set_service( $this );
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Set the options adapter
|
70 |
+
*
|
71 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to use.
|
72 |
+
*/
|
73 |
+
public function set_options_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
74 |
+
$this->adapter = $adapter;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Set components provider
|
79 |
+
*
|
80 |
+
* @param WPSEO_Configuration_Components $components Component provider to use.
|
81 |
+
*/
|
82 |
+
public function set_components( WPSEO_Configuration_Components $components ) {
|
83 |
+
$this->components = $components;
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Set structure provider
|
88 |
+
*
|
89 |
+
* @param WPSEO_Configuration_Structure $structure Structure provider to use.
|
90 |
+
*/
|
91 |
+
public function set_structure( WPSEO_Configuration_Structure $structure ) {
|
92 |
+
$this->structure = $structure;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Sets the translations object.
|
97 |
+
*
|
98 |
+
* @param WPSEO_Configuration_Translations $translations The translations object.
|
99 |
+
*/
|
100 |
+
public function set_translations( WPSEO_Configuration_Translations $translations ) {
|
101 |
+
$this->translations = $translations;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Populate the configuration
|
106 |
+
*/
|
107 |
+
protected function populate_configuration() {
|
108 |
+
// Switch to the user locale with fallback to the site locale.
|
109 |
+
if ( function_exists( 'switch_to_locale' ) ) {
|
110 |
+
switch_to_locale( WPSEO_Utils::get_user_locale() );
|
111 |
+
}
|
112 |
+
|
113 |
+
// Make sure we have our translations available.
|
114 |
+
wpseo_load_textdomain();
|
115 |
+
|
116 |
+
$this->structure->initialize();
|
117 |
+
|
118 |
+
$this->storage->set_adapter( $this->adapter );
|
119 |
+
$this->storage->add_default_fields();
|
120 |
+
|
121 |
+
$this->components->initialize();
|
122 |
+
$this->components->set_storage( $this->storage );
|
123 |
+
|
124 |
+
// @todo: check if this is really needed, since the switch happens only in the API.
|
125 |
+
if ( function_exists( 'restore_current_locale' ) ) {
|
126 |
+
restore_current_locale();
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Used by endpoint to retrieve configuration
|
132 |
+
*
|
133 |
+
* @return array List of settings.
|
134 |
+
*/
|
135 |
+
public function get_configuration() {
|
136 |
+
$this->populate_configuration();
|
137 |
+
$fields = $this->storage->retrieve();
|
138 |
+
$steps = $this->structure->retrieve();
|
139 |
+
$translations = $this->translations->retrieve();
|
140 |
+
|
141 |
+
return array(
|
142 |
+
'fields' => $fields,
|
143 |
+
'steps' => $steps,
|
144 |
+
'translations' => $translations,
|
145 |
+
);
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Used by endpoint to store changes
|
150 |
+
*
|
151 |
+
* @param WP_REST_Request $request Request from the REST API.
|
152 |
+
*
|
153 |
+
* @return array List of feedback per option if saving succeeded.
|
154 |
+
*/
|
155 |
+
public function set_configuration( WP_REST_Request $request ) {
|
156 |
+
$this->populate_configuration();
|
157 |
+
|
158 |
+
return $this->storage->store( $request->get_json_params() );
|
159 |
+
}
|
160 |
+
}
|
admin/config-ui/class-configuration-storage.php
ADDED
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Configuration_Storage
|
8 |
+
*/
|
9 |
+
class WPSEO_Configuration_Storage {
|
10 |
+
|
11 |
+
/** @var WPSEO_Configuration_Options_Adapter */
|
12 |
+
protected $adapter;
|
13 |
+
|
14 |
+
/** @var array WPSEO_Config_Field */
|
15 |
+
protected $fields = array();
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Add default fields
|
19 |
+
*/
|
20 |
+
public function add_default_fields() {
|
21 |
+
$fields = array(
|
22 |
+
new WPSEO_Config_Field_Upsell_Configuration_Service(),
|
23 |
+
new WPSEO_Config_Field_Upsell_Site_Review(),
|
24 |
+
new WPSEO_Config_Field_Success_Message(),
|
25 |
+
new WPSEO_Config_Field_Mailchimp_Signup(),
|
26 |
+
new WPSEO_Config_Field_Environment(),
|
27 |
+
new WPSEO_Config_Field_Site_Type(),
|
28 |
+
new WPSEO_Config_Field_Multiple_Authors(),
|
29 |
+
new WPSEO_Config_Field_Title_Intro(),
|
30 |
+
new WPSEO_Config_Field_Site_Name(),
|
31 |
+
new WPSEO_Config_Field_Separator(),
|
32 |
+
new WPSEO_Config_Field_Google_Search_Console_Intro(),
|
33 |
+
new WPSEO_Config_Field_Social_Profiles_Intro(),
|
34 |
+
new WPSEO_Config_Field_Profile_URL_Facebook(),
|
35 |
+
new WPSEO_Config_Field_Profile_URL_Twitter(),
|
36 |
+
new WPSEO_Config_Field_Profile_URL_Instagram(),
|
37 |
+
new WPSEO_Config_Field_Profile_URL_LinkedIn(),
|
38 |
+
new WPSEO_Config_Field_Profile_URL_MySpace(),
|
39 |
+
new WPSEO_Config_Field_Profile_URL_Pinterest(),
|
40 |
+
new WPSEO_Config_Field_Profile_URL_YouTube(),
|
41 |
+
new WPSEO_Config_Field_Profile_URL_GooglePlus(),
|
42 |
+
new WPSEO_Config_Field_Company_Or_Person(),
|
43 |
+
new WPSEO_Config_Field_Company_Name(),
|
44 |
+
new WPSEO_Config_Field_Company_Logo(),
|
45 |
+
new WPSEO_Config_Field_Person_Name(),
|
46 |
+
new WPSEO_Config_Field_Post_Type_Visibility(),
|
47 |
+
);
|
48 |
+
|
49 |
+
$post_type_factory = new WPSEO_Config_Factory_Post_Type();
|
50 |
+
$fields = array_merge( $fields, $post_type_factory->get_fields() );
|
51 |
+
|
52 |
+
foreach ( $fields as $field ) {
|
53 |
+
$this->add_field( $field );
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Allow for field injections
|
59 |
+
*
|
60 |
+
* @param WPSEO_Config_Field $field Field to add to the stack.
|
61 |
+
*/
|
62 |
+
public function add_field( WPSEO_Config_Field $field ) {
|
63 |
+
$this->fields[] = $field;
|
64 |
+
|
65 |
+
if ( isset( $this->adapter ) ) {
|
66 |
+
$field->set_adapter( $this->adapter );
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Set the adapter to use
|
72 |
+
*
|
73 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to use.
|
74 |
+
*/
|
75 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
76 |
+
$this->adapter = $adapter;
|
77 |
+
|
78 |
+
foreach ( $this->fields as $field ) {
|
79 |
+
$field->set_adapter( $this->adapter );
|
80 |
+
}
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Retrieve the current adapter
|
85 |
+
*
|
86 |
+
* @return WPSEO_Configuration_Options_Adapter
|
87 |
+
*/
|
88 |
+
public function get_adapter() {
|
89 |
+
return $this->adapter;
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Retrieve the registered fields
|
94 |
+
*
|
95 |
+
* @returns array List of settings.
|
96 |
+
*/
|
97 |
+
public function retrieve() {
|
98 |
+
$output = array();
|
99 |
+
|
100 |
+
/** @var WPSEO_Config_Field $field */
|
101 |
+
foreach ( $this->fields as $field ) {
|
102 |
+
|
103 |
+
$build = $field->to_array();
|
104 |
+
|
105 |
+
$data = $this->get_field_data( $field );
|
106 |
+
if ( ! is_null( $data ) ) {
|
107 |
+
$build['data'] = $data;
|
108 |
+
}
|
109 |
+
|
110 |
+
$output[ $field->get_identifier() ] = $build;
|
111 |
+
}
|
112 |
+
|
113 |
+
return $output;
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Save the data
|
118 |
+
*
|
119 |
+
* @param array $data_to_store Data provided by the API which needs to be processed for saving.
|
120 |
+
*
|
121 |
+
* @return string Results
|
122 |
+
*/
|
123 |
+
public function store( $data_to_store ) {
|
124 |
+
$output = array();
|
125 |
+
|
126 |
+
/** @var WPSEO_Config_Field $field */
|
127 |
+
foreach ( $this->fields as $field ) {
|
128 |
+
|
129 |
+
$field_identifier = $field->get_identifier();
|
130 |
+
|
131 |
+
if ( ! array_key_exists( $field_identifier, $data_to_store ) ) {
|
132 |
+
continue;
|
133 |
+
}
|
134 |
+
|
135 |
+
$field_data = array();
|
136 |
+
if ( isset( $data_to_store[ $field_identifier ] ) ) {
|
137 |
+
$field_data = $data_to_store[ $field_identifier ];
|
138 |
+
}
|
139 |
+
|
140 |
+
$result = $this->adapter->set( $field, $field_data );
|
141 |
+
|
142 |
+
$build = array(
|
143 |
+
'result' => $result,
|
144 |
+
);
|
145 |
+
|
146 |
+
// Set current data to object to be displayed.
|
147 |
+
$data = $this->get_field_data( $field );
|
148 |
+
if ( ! is_null( $data ) ) {
|
149 |
+
$build['data'] = $data;
|
150 |
+
}
|
151 |
+
|
152 |
+
$output[ $field_identifier ] = $build;
|
153 |
+
}
|
154 |
+
|
155 |
+
return $output;
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* Filter out null input values
|
160 |
+
*
|
161 |
+
* @param mixed $input Input to test against.
|
162 |
+
*
|
163 |
+
* @return bool
|
164 |
+
*/
|
165 |
+
protected function is_not_null( $input ) {
|
166 |
+
return ! is_null( $input );
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Get data from a specific field
|
171 |
+
*
|
172 |
+
* @param WPSEO_Config_Field $field Field to get data for.
|
173 |
+
*
|
174 |
+
* @return array|mixed
|
175 |
+
*/
|
176 |
+
protected function get_field_data( WPSEO_Config_Field $field ) {
|
177 |
+
$data = $this->adapter->get( $field );
|
178 |
+
|
179 |
+
if ( is_array( $data ) ) {
|
180 |
+
$defaults = $field->get_data();
|
181 |
+
|
182 |
+
// Remove 'null' values from input.
|
183 |
+
$data = array_filter( $data, array( $this, 'is_not_null' ) );
|
184 |
+
|
185 |
+
// Merge defaults with data.
|
186 |
+
$data = array_merge( $defaults, $data );
|
187 |
+
}
|
188 |
+
|
189 |
+
if ( is_null( $data ) ) {
|
190 |
+
// Get default if no data was set.
|
191 |
+
$data = $field->get_data();
|
192 |
+
|
193 |
+
return $data;
|
194 |
+
}
|
195 |
+
|
196 |
+
return $data;
|
197 |
+
}
|
198 |
+
}
|
admin/config-ui/class-configuration-structure.php
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Configuration_Structure
|
8 |
+
*/
|
9 |
+
class WPSEO_Configuration_Structure {
|
10 |
+
|
11 |
+
/** @var array Registered steps */
|
12 |
+
protected $steps = array();
|
13 |
+
|
14 |
+
/**
|
15 |
+
* WPSEO_Configuration_Structure constructor.
|
16 |
+
*/
|
17 |
+
public function initialize() {
|
18 |
+
$this->add_step( 'intro', __( 'Welcome!', 'wordpress-seo' ), array(
|
19 |
+
'configurationChoices',
|
20 |
+
), false, true );
|
21 |
+
|
22 |
+
$this->add_step( 'environment_type', __( 'Environment', 'wordpress-seo' ), array( 'environment_type' ) );
|
23 |
+
$this->add_step( 'siteType', __( 'Site type', 'wordpress-seo' ), array( 'siteType' ) );
|
24 |
+
$this->add_step( 'publishingEntity', __( 'Company or person', 'wordpress-seo' ), array(
|
25 |
+
'publishingEntity',
|
26 |
+
'publishingEntityType',
|
27 |
+
'publishingEntityCompanyName',
|
28 |
+
'publishingEntityCompanyLogo',
|
29 |
+
'publishingEntityPersonName',
|
30 |
+
) );
|
31 |
+
$this->add_step( 'profileUrls', __( 'Social profiles', 'wordpress-seo' ), array(
|
32 |
+
'socialProfilesIntro',
|
33 |
+
'profileUrlFacebook',
|
34 |
+
'profileUrlTwitter',
|
35 |
+
'profileUrlInstagram',
|
36 |
+
'profileUrlLinkedIn',
|
37 |
+
'profileUrlMySpace',
|
38 |
+
'profileUrlPinterest',
|
39 |
+
'profileUrlYouTube',
|
40 |
+
'profileUrlGooglePlus',
|
41 |
+
) );
|
42 |
+
|
43 |
+
$fields = array( 'postTypeVisibility' );
|
44 |
+
|
45 |
+
$post_type_factory = new WPSEO_Config_Factory_Post_Type();
|
46 |
+
foreach ( $post_type_factory->get_fields() as $post_type_field ) {
|
47 |
+
$fields[] = $post_type_field->get_identifier();
|
48 |
+
}
|
49 |
+
$this->add_step( 'postTypeVisibility', __( 'Search engine visibility', 'wordpress-seo' ), $fields );
|
50 |
+
|
51 |
+
$this->add_step( 'multipleAuthors', __( 'Multiple authors', 'wordpress-seo' ), array( 'multipleAuthors' ) );
|
52 |
+
$this->add_step( 'connectGoogleSearchConsole', __( 'Google Search Console', 'wordpress-seo' ), array(
|
53 |
+
'googleSearchConsoleIntro',
|
54 |
+
'connectGoogleSearchConsole',
|
55 |
+
) );
|
56 |
+
$this->add_step( 'titleTemplate', __( 'Title settings', 'wordpress-seo' ), array(
|
57 |
+
'titleIntro',
|
58 |
+
'siteName',
|
59 |
+
'separator',
|
60 |
+
) );
|
61 |
+
|
62 |
+
$this->add_step( 'newsletter', __( 'Newsletter', 'wordpress-seo' ), array(
|
63 |
+
'mailchimpSignup',
|
64 |
+
), true, true );
|
65 |
+
$this->add_step( 'suggestions', __( 'You might like', 'wordpress-seo' ), array(
|
66 |
+
'suggestions',
|
67 |
+
), true, true );
|
68 |
+
$this->add_step( 'success', __( 'Success!', 'wordpress-seo' ), array(
|
69 |
+
'successMessage',
|
70 |
+
), true, true );
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Add a step to the structure
|
75 |
+
*
|
76 |
+
* @param string $identifier Identifier for this step.
|
77 |
+
* @param string $title Title to display for this step.
|
78 |
+
* @param array $fields Fields to use on the step.
|
79 |
+
* @param bool $navigation Show navigation buttons.
|
80 |
+
* @param bool $full_width Wheter the step content is full width or not.
|
81 |
+
*/
|
82 |
+
protected function add_step( $identifier, $title, $fields, $navigation = true, $full_width = false ) {
|
83 |
+
$this->steps[ $identifier ] = array(
|
84 |
+
'title' => $title,
|
85 |
+
'fields' => $fields,
|
86 |
+
'hideNavigation' => ! (bool) $navigation,
|
87 |
+
'fullWidth' => $full_width,
|
88 |
+
);
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Retrieve the registered steps
|
93 |
+
*
|
94 |
+
* @return array
|
95 |
+
*/
|
96 |
+
public function retrieve() {
|
97 |
+
return $this->steps;
|
98 |
+
}
|
99 |
+
}
|
admin/config-ui/class-configuration-translations.php
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Configuration_Structure
|
8 |
+
*/
|
9 |
+
class WPSEO_Configuration_Translations {
|
10 |
+
|
11 |
+
/** @var array Registered steps */
|
12 |
+
protected $translations = array();
|
13 |
+
|
14 |
+
/** @var string The locale */
|
15 |
+
protected $locale;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Sets the translations based on the file.
|
19 |
+
*
|
20 |
+
* @param string $locale The locale to retreive the translations for.
|
21 |
+
*/
|
22 |
+
public function __construct( $locale ) {
|
23 |
+
$this->locale = $locale;
|
24 |
+
$this->translations = $this->get_translations_from_file();
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Retrieve the translations
|
29 |
+
*
|
30 |
+
* @return array
|
31 |
+
*/
|
32 |
+
public function retrieve() {
|
33 |
+
return $this->translations;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Retrieves the translations from the JSON-file.
|
38 |
+
*
|
39 |
+
* @return array Array with the translations.
|
40 |
+
*/
|
41 |
+
protected function get_translations_from_file() {
|
42 |
+
|
43 |
+
$file = plugin_dir_path( WPSEO_FILE ) . 'languages/yoast-components-' . $this->locale . '.json';
|
44 |
+
if ( file_exists( $file ) ) {
|
45 |
+
$file = file_get_contents( $file );
|
46 |
+
if ( is_string( $file ) && $file !== '' ) {
|
47 |
+
return json_decode( $file, true );
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
return array();
|
52 |
+
}
|
53 |
+
}
|
admin/config-ui/components/class-component-configuration-choices.php
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the configuration choices component.
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Component_Configuration_Choices implements WPSEO_Config_Component {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Gets the component identifier.
|
13 |
+
*
|
14 |
+
* @return string
|
15 |
+
*/
|
16 |
+
public function get_identifier() {
|
17 |
+
return 'ConfigurationChoices';
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Gets the field.
|
22 |
+
*
|
23 |
+
* @return WPSEO_Config_Field
|
24 |
+
*/
|
25 |
+
public function get_field() {
|
26 |
+
$field = new WPSEO_Config_Field_Configuration_Choices();
|
27 |
+
|
28 |
+
$field->set_property( 'label', sprintf(
|
29 |
+
/* translators: %s expands to 'Yoast SEO'. */
|
30 |
+
__( 'Please choose the %s configuration of your liking:', 'wordpress-seo' ), 'Yoast SEO' )
|
31 |
+
);
|
32 |
+
|
33 |
+
$field->add_choice(
|
34 |
+
sprintf(
|
35 |
+
/* translators: %s expands to 'Yoast SEO'. */
|
36 |
+
__( 'Configure %s in a few steps', 'wordpress-seo' ),
|
37 |
+
'Yoast SEO'
|
38 |
+
),
|
39 |
+
sprintf(
|
40 |
+
/* translators: %1$s expands to 'Yoast SEO'. */
|
41 |
+
__( 'Welcome to the %1$s configuration wizard. In a few simple steps we\'ll help you configure your SEO settings to match your website\'s needs! %1$s will take care of all the technical optimizations your site needs.', 'wordpress-seo' ),
|
42 |
+
'Yoast SEO'
|
43 |
+
),
|
44 |
+
array(
|
45 |
+
'type' => 'primary',
|
46 |
+
'label' => sprintf(
|
47 |
+
/* translators: %s expands to 'Yoast SEO'. */
|
48 |
+
__( 'Configure %s', 'wordpress-seo' ), 'Yoast SEO'
|
49 |
+
),
|
50 |
+
'action' => 'nextStep',
|
51 |
+
),
|
52 |
+
plugin_dir_url( WPSEO_FILE ) . '/images/Yoast_SEO_Icon.svg'
|
53 |
+
);
|
54 |
+
$field->add_choice(
|
55 |
+
sprintf(
|
56 |
+
/* translators: %s expands to 'Yoast SEO'. */
|
57 |
+
__( 'Let us set up %s for you', 'wordpress-seo' ), 'Yoast SEO'
|
58 |
+
),
|
59 |
+
sprintf(
|
60 |
+
/* translators: %1$s expands to 'Yoast SEO', %2$s expands to 'Yoast SEO Premium'. */
|
61 |
+
__( 'While we strive to make setting up %1$s as easy as possible, we understand it can still be daunting. If you would rather have us set up %1$s for you (and get a copy of %2$s in the process), order a %1$s configuration service and sit back while we configure your site.', 'wordpress-seo' ),
|
62 |
+
'Yoast SEO',
|
63 |
+
'Yoast SEO Premium'
|
64 |
+
),
|
65 |
+
array(
|
66 |
+
'type' => 'secondary',
|
67 |
+
'label' => __( 'Configuration service', 'wordpress-seo' ),
|
68 |
+
'action' => 'followURL',
|
69 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/wizard-configuration-upsell' ),
|
70 |
+
),
|
71 |
+
plugin_dir_url( WPSEO_FILE ) . 'images/yoast-configuration-icon.svg'
|
72 |
+
);
|
73 |
+
|
74 |
+
return $field;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Get the data for the field.
|
79 |
+
*
|
80 |
+
* @return array
|
81 |
+
*/
|
82 |
+
public function get_data() {
|
83 |
+
return array();
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Save data
|
88 |
+
*
|
89 |
+
* @param array $data Data containing changes.
|
90 |
+
*
|
91 |
+
* @return bool
|
92 |
+
*/
|
93 |
+
public function set_data( $data ) {
|
94 |
+
return true;
|
95 |
+
}
|
96 |
+
}
|
admin/config-ui/components/class-component-connect-google-search-console.php
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Component_Connect_Google_Search_Console
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Component_Connect_Google_Search_Console implements WPSEO_Config_Component {
|
10 |
+
|
11 |
+
const OPTION_ACCESS_TOKEN = 'wpseo-gsc-access_token';
|
12 |
+
const OPTION_REFRESH_TOKEN = 'wpseo-gsc-refresh_token';
|
13 |
+
|
14 |
+
|
15 |
+
/** @var WPSEO_GSC_Service Service to use */
|
16 |
+
protected $gsc_service;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* WPSEO_Config_Component_Connect_Google_Search_Console constructor.
|
20 |
+
*/
|
21 |
+
public function __construct() {
|
22 |
+
$this->gsc_service = new WPSEO_GSC_Service( $this->get_profile() );
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Set the Google Search Console service.
|
27 |
+
*
|
28 |
+
* @param WPSEO_GSC_Service $service Set service to use.
|
29 |
+
*/
|
30 |
+
public function set_gsc_service( WPSEO_GSC_Service $service ) {
|
31 |
+
$this->gsc_service = $service;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Gets the component identifier.
|
36 |
+
*
|
37 |
+
* @return string
|
38 |
+
*/
|
39 |
+
public function get_identifier() {
|
40 |
+
return 'ConnectGoogleSearchConsole';
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Gets the field.
|
45 |
+
*
|
46 |
+
* @return WPSEO_Config_Field
|
47 |
+
*/
|
48 |
+
public function get_field() {
|
49 |
+
return new WPSEO_Config_Field_Connect_Google_Search_Console();
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Get the data for the field.
|
54 |
+
*
|
55 |
+
* @return mixed
|
56 |
+
*/
|
57 |
+
public function get_data() {
|
58 |
+
|
59 |
+
$data = array(
|
60 |
+
'profileList' => $this->get_profilelist(),
|
61 |
+
'profile' => $this->get_profile(),
|
62 |
+
'hasAccessToken' => $this->hasAccessToken(),
|
63 |
+
);
|
64 |
+
|
65 |
+
return $data;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Save data
|
70 |
+
*
|
71 |
+
* @param array $data Data containing changes.
|
72 |
+
*
|
73 |
+
* @return mixed
|
74 |
+
*/
|
75 |
+
public function set_data( $data ) {
|
76 |
+
|
77 |
+
$current_data = $this->get_data();
|
78 |
+
|
79 |
+
$this->handle_profile_change( $current_data, $data );
|
80 |
+
|
81 |
+
// Save profile.
|
82 |
+
$has_saved = update_option(
|
83 |
+
WPSEO_GSC::OPTION_WPSEO_GSC,
|
84 |
+
array( 'profile' => $data['profile'] )
|
85 |
+
);
|
86 |
+
|
87 |
+
// Collect results to return to the configurator.
|
88 |
+
$results = array(
|
89 |
+
'profile' => $has_saved,
|
90 |
+
);
|
91 |
+
|
92 |
+
return $results;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Remove issues when the profile has changed
|
97 |
+
*
|
98 |
+
* @param array $current_data Saved data before changes.
|
99 |
+
* @param array $data Data after changes.
|
100 |
+
*/
|
101 |
+
protected function handle_profile_change( $current_data, $data ) {
|
102 |
+
// If the profile has been changed, remove issues.
|
103 |
+
if ( $current_data['profile'] === $data['profile'] ) {
|
104 |
+
return;
|
105 |
+
}
|
106 |
+
|
107 |
+
$this->reload_issues();
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Get the current GSC profile
|
112 |
+
*
|
113 |
+
* @return string
|
114 |
+
*/
|
115 |
+
protected function get_profile() {
|
116 |
+
return WPSEO_GSC_Settings::get_profile();
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Reload GSC issues
|
121 |
+
*/
|
122 |
+
protected function reload_issues() {
|
123 |
+
WPSEO_GSC_Settings::reload_issues();
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Gets a list with the profiles.
|
128 |
+
*
|
129 |
+
* @return array
|
130 |
+
*/
|
131 |
+
protected function get_profilelist() {
|
132 |
+
$profiles = array();
|
133 |
+
$sites = $this->gsc_service->get_sites();
|
134 |
+
foreach ( $sites as $site_key => $site_value ) {
|
135 |
+
$profiles[ untrailingslashit( $site_key ) ] = untrailingslashit( $site_value );
|
136 |
+
}
|
137 |
+
|
138 |
+
return $profiles;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Checks if there is an access token. If so, there is a connection.
|
143 |
+
*
|
144 |
+
* @return bool
|
145 |
+
*/
|
146 |
+
private function hasAccessToken() {
|
147 |
+
return ( null !== $this->gsc_service->get_client()->getAccessToken() );
|
148 |
+
}
|
149 |
+
}
|
admin/config-ui/components/class-component-mailchimp-signup.php
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the mailchimp signup components.
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Component_Mailchimp_Signup implements WPSEO_Config_Component {
|
10 |
+
|
11 |
+
const META_NAME = 'wpseo-has-mailchimp-signup';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Gets the component identifier.
|
15 |
+
*
|
16 |
+
* @return string
|
17 |
+
*/
|
18 |
+
public function get_identifier() {
|
19 |
+
return 'MailchimpSignup';
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Gets the field.
|
24 |
+
*
|
25 |
+
* @return WPSEO_Config_Field
|
26 |
+
*/
|
27 |
+
public function get_field() {
|
28 |
+
return new WPSEO_Config_Field_Mailchimp_Signup();
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Get the data for the field.
|
33 |
+
*
|
34 |
+
* @return mixed
|
35 |
+
*/
|
36 |
+
public function get_data() {
|
37 |
+
$data = array(
|
38 |
+
'hasSignup' => $this->has_mailchimp_signup(),
|
39 |
+
);
|
40 |
+
|
41 |
+
return $data;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Save data
|
46 |
+
*
|
47 |
+
* @param array $data Data containing changes.
|
48 |
+
*
|
49 |
+
* @return mixed
|
50 |
+
*/
|
51 |
+
public function set_data( $data ) {
|
52 |
+
|
53 |
+
$has_saved = false;
|
54 |
+
if ( ! empty( $data['hasSignup'] ) ) {
|
55 |
+
// Saves the user meta.
|
56 |
+
update_user_meta( get_current_user_id(), self::META_NAME, true );
|
57 |
+
|
58 |
+
$has_saved = ( $data['hasSignup'] === $this->has_mailchimp_signup() );
|
59 |
+
}
|
60 |
+
|
61 |
+
// Collect results to return to the configurator.
|
62 |
+
$results = array(
|
63 |
+
'hasSignup' => $has_saved,
|
64 |
+
);
|
65 |
+
|
66 |
+
return $results;
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Checks if the user has entered his email for mailchimp already.
|
71 |
+
*
|
72 |
+
* @return bool
|
73 |
+
*/
|
74 |
+
protected function has_mailchimp_signup() {
|
75 |
+
$user_meta = get_user_meta( get_current_user_id(), self::META_NAME, true );
|
76 |
+
|
77 |
+
return ( ! empty( $user_meta ) );
|
78 |
+
}
|
79 |
+
}
|
admin/config-ui/components/class-component-suggestions.php
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the configuration suggestions component.
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Component_Suggestions implements WPSEO_Config_Component {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Gets the component identifier.
|
13 |
+
*
|
14 |
+
* @return string
|
15 |
+
*/
|
16 |
+
public function get_identifier() {
|
17 |
+
return 'Suggestions';
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Gets the field.
|
22 |
+
*
|
23 |
+
* @return WPSEO_Config_Field
|
24 |
+
*/
|
25 |
+
public function get_field() {
|
26 |
+
$field = new WPSEO_Config_Field_Suggestions();
|
27 |
+
|
28 |
+
// Only show Premium upsell when we are not inside a Premium install.
|
29 |
+
if ( ! WPSEO_Utils::is_yoast_seo_premium() ) {
|
30 |
+
$field->add_suggestion(
|
31 |
+
/* translators: %s resolves to Yoast SEO Premium */
|
32 |
+
sprintf( __( 'Outrank the competition with %s', 'wordpress-seo' ), 'Yoast SEO Premium' ),
|
33 |
+
/* translators: %1$s resolves to Yoast SEO Premium */
|
34 |
+
sprintf( __( 'Do you want to outrank your competition? %1$s gives you awesome additional features that\'ll help you to set up your SEO strategy like a professional. Use the multiple focus keywords functionality, the redirect manager and our internal linking tool. %1$s will also give you access to premium support.', 'wordpress-seo' ), 'Yoast SEO Premium' ),
|
35 |
+
array(
|
36 |
+
'label' => __( 'Upgrade to Premium', 'wordpress-seo' ),
|
37 |
+
'type' => 'primary',
|
38 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/wizard-suggestion-premium' ),
|
39 |
+
),
|
40 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/video-yoast-seo-premium' )
|
41 |
+
);
|
42 |
+
}
|
43 |
+
|
44 |
+
$field->add_suggestion(
|
45 |
+
__( 'Learn how to write copy that ranks', 'wordpress-seo' ),
|
46 |
+
/* translators: %1$s resolves to SEO copywriting training */
|
47 |
+
sprintf( __( 'Do you want to learn how to write content that generates traffic? Check out our %1$s. We will help you to write awesome copy that will rank in the search engines. The %1$s covers all the main steps in SEO copywriting: from keyword research to publishing.', 'wordpress-seo' ), 'SEO copywriting training' ),
|
48 |
+
array(
|
49 |
+
'label' => 'SEO copywriting training',
|
50 |
+
'type' => 'link',
|
51 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/configuration-wizard-copywrite-course-link' ),
|
52 |
+
),
|
53 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/video-course-copywriting' )
|
54 |
+
);
|
55 |
+
|
56 |
+
$field->add_suggestion(
|
57 |
+
/* translators: %1$s resolves to Yoast SEO, %2$s resolves to Yoast SEO plugin training */
|
58 |
+
sprintf( __( 'Get the most out of %1$s with the %2$s', 'wordpress-seo' ), 'Yoast SEO', 'Yoast SEO plugin training' ),
|
59 |
+
/* translators: %1$s resolves to Yoast SEO */
|
60 |
+
sprintf( __( 'Do you want to know all the ins and outs of the %1$s plugin? Do you want to learn all about our advanced settings? Want to be able to really get the most out of the %1$s plugin? Check out our %1$s plugin training and start outranking the competition!', 'wordpress-seo' ), 'Yoast SEO' ),
|
61 |
+
array(
|
62 |
+
'label' => 'Yoast SEO plugin training',
|
63 |
+
'type' => 'link',
|
64 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/wizard-suggestion-plugin-course' ),
|
65 |
+
),
|
66 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/video-plugin-course' )
|
67 |
+
);
|
68 |
+
|
69 |
+
// When we are running in Yoast SEO Premium and don't have Local SEO installed, show Local SEO as suggestion.
|
70 |
+
if ( WPSEO_Utils::is_yoast_seo_premium() && ! defined( 'WPSEO_LOCAL_FILE' ) ) {
|
71 |
+
$field->add_suggestion(
|
72 |
+
sprintf( __( 'Attract more customers near you', 'wordpress-seo' ), 'Yoast SEO', 'Yoast SEO plugin training' ),
|
73 |
+
/* translators: %1$s resolves to Local SEO */
|
74 |
+
sprintf( __( 'If you want to outrank the competition in a specific town or region, check out our %1$s plugin! You’ll be able to easily insert Google maps, opening hours, contact information and a store locator. Besides that %1$s helps you to improve the usability of your contact page.', 'wordpress-seo' ), 'Local SEO' ),
|
75 |
+
array(
|
76 |
+
'label' => 'Local SEO',
|
77 |
+
'type' => 'link',
|
78 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/wizard-suggestion-localseo' ),
|
79 |
+
),
|
80 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/video-localseo' )
|
81 |
+
);
|
82 |
+
}
|
83 |
+
|
84 |
+
return $field;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Get the data for the field.
|
89 |
+
*
|
90 |
+
* @return array
|
91 |
+
*/
|
92 |
+
public function get_data() {
|
93 |
+
return array();
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Save data
|
98 |
+
*
|
99 |
+
* @param array $data Data containing changes.
|
100 |
+
*
|
101 |
+
* @return bool
|
102 |
+
*/
|
103 |
+
public function set_data( $data ) {
|
104 |
+
return true;
|
105 |
+
}
|
106 |
+
}
|
admin/config-ui/components/interface-component.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Config Component interface
|
8 |
+
*/
|
9 |
+
interface WPSEO_Config_Component {
|
10 |
+
/**
|
11 |
+
* @return string
|
12 |
+
*/
|
13 |
+
public function get_identifier();
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @return mixed
|
17 |
+
*/
|
18 |
+
public function get_data();
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Save changes
|
22 |
+
*
|
23 |
+
* @param array $data Data provided by the API.
|
24 |
+
*
|
25 |
+
* @return mixed
|
26 |
+
*/
|
27 |
+
public function set_data( $data );
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @return WPSEO_Config_Field
|
31 |
+
*/
|
32 |
+
public function get_field();
|
33 |
+
}
|
admin/config-ui/factories/class-factory-post-type.php
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Configurator
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Factory_Post_Type
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Factory_Post_Type {
|
10 |
+
|
11 |
+
/** @var WPSEO_Config_Field_Choice_Post_Type[] List of fields */
|
12 |
+
protected static $fields = array();
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @return WPSEO_Config_Field_Choice_Post_Type[] List of fields.
|
16 |
+
*/
|
17 |
+
public function get_fields() {
|
18 |
+
|
19 |
+
if ( empty( self::$fields ) ) {
|
20 |
+
|
21 |
+
$fields = array();
|
22 |
+
|
23 |
+
// WPSEO_Post_type::get_accessible_post_types() should *not* be used to get a similar experience from the settings.
|
24 |
+
$post_types = get_post_types( array( 'public' => true ), 'objects' );
|
25 |
+
$post_types = WPSEO_Post_Type::filter_attachment_post_type( $post_types );
|
26 |
+
if ( ! empty( $post_types ) ) {
|
27 |
+
foreach ( $post_types as $post_type => $post_type_object ) {
|
28 |
+
$label = $this->decode_html_entities( $post_type_object->label );
|
29 |
+
$field = new WPSEO_Config_Field_Choice_Post_Type( $post_type, $label );
|
30 |
+
|
31 |
+
$this->add_custom_properties( $post_type, $field );
|
32 |
+
|
33 |
+
$fields[] = $field;
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
self::$fields = $fields;
|
38 |
+
}
|
39 |
+
|
40 |
+
return self::$fields;
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Add custom properties for specific post types
|
45 |
+
*
|
46 |
+
* @param string $post_type Post type of field that is being added.
|
47 |
+
* @param WPSEO_Config_Field $field Field that corresponds to the post type.
|
48 |
+
*/
|
49 |
+
private function add_custom_properties( $post_type, $field ) {
|
50 |
+
if ( 'attachment' === $post_type ) {
|
51 |
+
$field->set_property( 'explanation', __( 'WordPress automatically generates an URL for each media item in the library. Enabling this will allow for google to index the generated URL.', 'wordpress-seo' ) );
|
52 |
+
}
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Replaces the HTML entity with it's actual symbol.
|
57 |
+
*
|
58 |
+
* Because we do not not know what consequences it will have if we convert every HTML entity,
|
59 |
+
* we will only replace the characters that we have known problems with in text's.
|
60 |
+
*
|
61 |
+
* @param string $text The text to decode.
|
62 |
+
*
|
63 |
+
* @return string String with decoded HTML entities.
|
64 |
+
*/
|
65 |
+
private function decode_html_entities( $text ) {
|
66 |
+
return str_replace( ''', '’', $text );
|
67 |
+
}
|
68 |
+
}
|
admin/config-ui/fields/class-field-choice-post-type.php
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Choice_Post_Type
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Choice_Post_Type extends WPSEO_Config_Field_Choice {
|
10 |
+
|
11 |
+
/** @var string Post type */
|
12 |
+
protected $post_type;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* WPSEO_Config_Field_Choice_Post_Type constructor.
|
16 |
+
*
|
17 |
+
* @param string $post_type The post type to add.
|
18 |
+
* @param string $label Label to show (translated post type).
|
19 |
+
*/
|
20 |
+
public function __construct( $post_type, $label ) {
|
21 |
+
parent::__construct( 'postType' . ucfirst( $post_type ) );
|
22 |
+
|
23 |
+
$this->post_type = $post_type;
|
24 |
+
|
25 |
+
/* Translators: %1$s expands to the name of the post type. The options given to the user are "visible" and "hidden" */
|
26 |
+
$this->set_property( 'label', sprintf( __( 'Search engines should show "%1$s" in search results:', 'wordpress-seo' ), $label ) );
|
27 |
+
|
28 |
+
$this->add_choice( 'true', __( 'Yes', 'wordpress-seo' ) );
|
29 |
+
$this->add_choice( 'false', __( 'No', 'wordpress-seo' ) );
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Set adapter
|
34 |
+
*
|
35 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
36 |
+
*/
|
37 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
38 |
+
$adapter->add_custom_lookup(
|
39 |
+
$this->get_identifier(),
|
40 |
+
array( $this, 'get_data' ),
|
41 |
+
array( $this, 'set_data' )
|
42 |
+
);
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Get the post type of this field.
|
47 |
+
*
|
48 |
+
* @return string Post type.
|
49 |
+
*/
|
50 |
+
public function get_post_type() {
|
51 |
+
return $this->post_type;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* @return bool
|
56 |
+
*/
|
57 |
+
public function get_data() {
|
58 |
+
$key = 'noindex-' . $this->get_post_type();
|
59 |
+
|
60 |
+
if ( WPSEO_Options::get( $key, false ) === false ) {
|
61 |
+
return 'true';
|
62 |
+
}
|
63 |
+
|
64 |
+
return 'false';
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Set new data
|
69 |
+
*
|
70 |
+
* @param string $visible Visible (true) or hidden (false).
|
71 |
+
*
|
72 |
+
* @return bool
|
73 |
+
*/
|
74 |
+
public function set_data( $visible ) {
|
75 |
+
$post_type = $this->get_post_type();
|
76 |
+
|
77 |
+
return WPSEO_Options::set( 'noindex-' . $post_type, ( $visible === 'false' ) );
|
78 |
+
}
|
79 |
+
}
|
admin/config-ui/fields/class-field-choice.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Choice
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Choice extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Choice constructor.
|
13 |
+
*
|
14 |
+
* @param string $field Field name to use.
|
15 |
+
*/
|
16 |
+
public function __construct( $field ) {
|
17 |
+
parent::__construct( $field, 'Choice' );
|
18 |
+
|
19 |
+
$this->properties['choices'] = array();
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Add a choice to the properties
|
24 |
+
*
|
25 |
+
* @param string $value Value op the option.
|
26 |
+
* @param string $label Label to display for the value.
|
27 |
+
* @param string $screen_reader_text Optional. Screenreader text to use.
|
28 |
+
*/
|
29 |
+
public function add_choice( $value, $label, $screen_reader_text = '' ) {
|
30 |
+
$choice = array(
|
31 |
+
'label' => $label,
|
32 |
+
);
|
33 |
+
|
34 |
+
if ( $screen_reader_text ) {
|
35 |
+
$choice['screenReaderText'] = $screen_reader_text;
|
36 |
+
}
|
37 |
+
|
38 |
+
$this->properties['choices'][ $value ] = $choice;
|
39 |
+
}
|
40 |
+
}
|
admin/config-ui/fields/class-field-company-logo.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Configurator
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Company_Logo
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Company_Logo extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Company_Logo constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'publishingEntityCompanyLogo', 'MediaUpload' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'Provide an image of the company logo', 'wordpress-seo' ) );
|
18 |
+
|
19 |
+
$this->set_requires( 'publishingEntityType', 'company' );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
24 |
+
*/
|
25 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
26 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'company_logo' );
|
27 |
+
}
|
28 |
+
}
|
admin/config-ui/fields/class-field-company-name.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Configurator
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Company_Name
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Company_Name extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Company_Name constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'publishingEntityCompanyName', 'Input' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'The name of the company', 'wordpress-seo' ) );
|
18 |
+
|
19 |
+
$this->set_requires( 'publishingEntityType', 'company' );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
24 |
+
*/
|
25 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
26 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'company_name' );
|
27 |
+
}
|
28 |
+
}
|
admin/config-ui/fields/class-field-company-or-person.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Configurator
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Company_Or_Person
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Company_Or_Person extends WPSEO_Config_Field_Choice {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Company_Or_Person constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'publishingEntityType' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'Does your site represent a person or company?', 'wordpress-seo' ) );
|
18 |
+
|
19 |
+
$this->set_property( 'description', __( 'This information will be used in Google\'s Knowledge Graph Card, the big
|
20 |
+
block of information you see on the right side of the search results.', 'wordpress-seo' ) );
|
21 |
+
|
22 |
+
$this->add_choice( 'company', __( 'Company', 'wordpress-seo' ) );
|
23 |
+
$this->add_choice( 'person', __( 'Person', 'wordpress-seo' ) );
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
28 |
+
*/
|
29 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
30 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'company_or_person' );
|
31 |
+
}
|
32 |
+
}
|
admin/config-ui/fields/class-field-configuration-choices.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Holds the choices for the Configuration method to be chosen in the first step of the wizard
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Configuration_Choices extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Choice constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'configurationChoices', 'ConfigurationChoices' );
|
16 |
+
|
17 |
+
$this->properties['choices'] = array();
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Adds a choice to the properties
|
22 |
+
*
|
23 |
+
* @param string $title The title of the choice.
|
24 |
+
* @param string $copy The text explaining the choice.
|
25 |
+
* @param array $button The button details.
|
26 |
+
* @param null|string $image The image accompanying the choice.
|
27 |
+
*/
|
28 |
+
public function add_choice( $title, $copy, $button, $image = null ) {
|
29 |
+
$choice = array(
|
30 |
+
'title' => $title,
|
31 |
+
'copy' => $copy,
|
32 |
+
'button' => $button,
|
33 |
+
);
|
34 |
+
|
35 |
+
if ( ! empty( $image ) ) {
|
36 |
+
$choice['image'] = $image;
|
37 |
+
}
|
38 |
+
|
39 |
+
$this->properties['choices'][] = $choice;
|
40 |
+
}
|
41 |
+
}
|
admin/config-ui/fields/class-field-connect-google-search-console.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Connect_Google_Search_Console
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Connect_Google_Search_Console extends WPSEO_Config_Field {
|
10 |
+
/**
|
11 |
+
* WPSEO_Config_Field_Connect_Google_Search_Console constructor.
|
12 |
+
*/
|
13 |
+
public function __construct() {
|
14 |
+
parent::__construct( 'connectGoogleSearchConsole', 'ConnectGoogleSearchConsole' );
|
15 |
+
}
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Get the data
|
19 |
+
*
|
20 |
+
* @return array
|
21 |
+
*/
|
22 |
+
public function get_data() {
|
23 |
+
return array(
|
24 |
+
'profile' => '',
|
25 |
+
'profileList' => '',
|
26 |
+
);
|
27 |
+
}
|
28 |
+
}
|
admin/config-ui/fields/class-field-environment.php
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Environment
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Environment extends WPSEO_Config_Field_Choice {
|
10 |
+
/**
|
11 |
+
* WPSEO_Config_Field_Environment constructor.
|
12 |
+
*/
|
13 |
+
public function __construct() {
|
14 |
+
parent::__construct( 'environment_type' );
|
15 |
+
|
16 |
+
$this->set_property( 'label', __( 'Please specify if your site is under construction or already active.', 'wordpress-seo' ) );
|
17 |
+
|
18 |
+
$this->set_property( 'description', __( 'Choose under construction if you want to keep the site out of the index
|
19 |
+
of search engines. Don\'t forget to activate it once you\'re ready to
|
20 |
+
publish your site.', 'wordpress-seo' ) );
|
21 |
+
|
22 |
+
$this->add_choice( 'production', __( 'Option A: My site is live and ready to be indexed', 'wordpress-seo' ) );
|
23 |
+
$this->add_choice( 'staging', __( 'Option B: My site is under construction and should not be indexed', 'wordpress-seo' ) );
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Set adapter
|
28 |
+
*
|
29 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
30 |
+
*/
|
31 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
32 |
+
$adapter->add_custom_lookup(
|
33 |
+
$this->get_identifier(),
|
34 |
+
array( $this, 'get_data' ),
|
35 |
+
array( $this, 'set_data' )
|
36 |
+
);
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Gets the option that is set for this field.
|
41 |
+
*
|
42 |
+
* @return string The value for the environment_type wpseo option.
|
43 |
+
*/
|
44 |
+
public function get_data() {
|
45 |
+
return WPSEO_Options::get( 'environment_type' );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Set new data.
|
50 |
+
*
|
51 |
+
* @param string $environment_type The site's environment type.
|
52 |
+
*
|
53 |
+
* @return bool Returns whether the value is successfully set.
|
54 |
+
*/
|
55 |
+
public function set_data( $environment_type ) {
|
56 |
+
$return = true;
|
57 |
+
if ( $this->get_data() !== $environment_type ) {
|
58 |
+
$return = WPSEO_Options::set( 'environment_type', $environment_type );
|
59 |
+
if ( ! $this->set_indexation( $environment_type ) ) {
|
60 |
+
return false;
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
return $return;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Set the WordPress Search Engine Visibility option based on the environment type.
|
69 |
+
*
|
70 |
+
* @param string $environment_type The environment the site is running in.
|
71 |
+
*
|
72 |
+
* @return bool Returns if the options is set successfully.
|
73 |
+
*/
|
74 |
+
protected function set_indexation( $environment_type ) {
|
75 |
+
$new_blog_public_value = 0;
|
76 |
+
$current_blog_public_value = get_option( 'blog_public' );
|
77 |
+
|
78 |
+
if ( $environment_type === 'production' ) {
|
79 |
+
$new_blog_public_value = 1;
|
80 |
+
}
|
81 |
+
|
82 |
+
if ( $current_blog_public_value !== $new_blog_public_value ) {
|
83 |
+
update_option( 'blog_public', $new_blog_public_value );
|
84 |
+
|
85 |
+
return true;
|
86 |
+
}
|
87 |
+
|
88 |
+
return ( get_option( 'blog_public' ) === $new_blog_public_value );
|
89 |
+
}
|
90 |
+
}
|
admin/config-ui/fields/class-field-google-search-console-intro.php
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Google_Search_Console_Intro
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Google_Search_Console_Intro extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Social_Profiles_Intro constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'googleSearchConsoleIntro', 'HTML' );
|
16 |
+
|
17 |
+
$html = sprintf(
|
18 |
+
/* translators: %1$s is the plugin name, %2$s is a link start tag to a Yoast help page, %3$s is the link closing tag. */
|
19 |
+
__( '%1$s integrates with Google Search Console, a must-have tool for site owners.
|
20 |
+
It provides you with information about the health of your site.
|
21 |
+
Don\'t have a Google account or is your site not activated yet?
|
22 |
+
Find out %2$show to connect Google Search Console to your site.%3$s',
|
23 |
+
'wordpress-seo' ),
|
24 |
+
'Yoast SEO',
|
25 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/1ex' ) . '">',
|
26 |
+
'</a>' );
|
27 |
+
|
28 |
+
$disclaimer = __( 'Note: we don\'t store your data in any way and don\'t have full access to your account.
|
29 |
+
Your privacy is safe with us.', 'wordpress-seo' );
|
30 |
+
|
31 |
+
$html = '<p>' . $html . '</p><small>' . esc_html( $disclaimer ) . '</small>';
|
32 |
+
|
33 |
+
$this->set_property( 'html', $html );
|
34 |
+
}
|
35 |
+
}
|
admin/config-ui/fields/class-field-mailchimp-signup.php
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Mailchimp_Signup
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Mailchimp_Signup extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Mailchimp_Signup constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'mailchimpSignup', 'MailchimpSignup' );
|
16 |
+
|
17 |
+
$current_user = wp_get_current_user();
|
18 |
+
$user_email = ( $current_user->ID > 0 ) ? $current_user->user_email : '';
|
19 |
+
|
20 |
+
$this->set_property( 'title', __( 'Stay up-to-date', 'wordpress-seo' ) );
|
21 |
+
$this->set_property(
|
22 |
+
'label',
|
23 |
+
sprintf(
|
24 |
+
/* translators: %s expands to Yoast SEO. */
|
25 |
+
__( 'Your %1$s plugin is now set up. SEO, however, is subject to constant change. Sign up for our newsletter if you would like to keep up-to-date regarding %1$s, other plugins by Yoast and important news in the world of SEO.', 'wordpress-seo' ),
|
26 |
+
'Yoast SEO'
|
27 |
+
)
|
28 |
+
);
|
29 |
+
|
30 |
+
$this->set_property( 'decoration', plugin_dir_url( WPSEO_FILE ) . 'images/newsletter-collage.png' );
|
31 |
+
|
32 |
+
$this->set_property( 'mailchimpActionUrl', 'https://yoast.us1.list-manage.com/subscribe/post-json?u=ffa93edfe21752c921f860358&id=972f1c9122' );
|
33 |
+
$this->set_property( 'currentUserEmail', $user_email );
|
34 |
+
$this->set_property( 'userName', trim( $current_user->user_firstname . ' ' . $current_user->user_lastname ) );
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Get the data
|
39 |
+
*
|
40 |
+
* @return array
|
41 |
+
*/
|
42 |
+
public function get_data() {
|
43 |
+
return array(
|
44 |
+
'hasSignup' => $this->has_mailchimp_signup(),
|
45 |
+
);
|
46 |
+
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Checks if the user has entered his email for mailchimp already.
|
51 |
+
*
|
52 |
+
* @return bool
|
53 |
+
*/
|
54 |
+
protected function has_mailchimp_signup() {
|
55 |
+
$user_meta = get_user_meta( get_current_user_id(), WPSEO_Config_Component_Mailchimp_Signup::META_NAME, true );
|
56 |
+
return ( ! empty( $user_meta ) );
|
57 |
+
}
|
58 |
+
}
|
admin/config-ui/fields/class-field-multiple-authors.php
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Multiple_Authors
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Multiple_Authors extends WPSEO_Config_Field_Choice {
|
10 |
+
/**
|
11 |
+
* WPSEO_Config_Field_Multiple_Authors constructor.
|
12 |
+
*/
|
13 |
+
public function __construct() {
|
14 |
+
parent::__construct( 'multipleAuthors' );
|
15 |
+
|
16 |
+
$this->set_property( 'label', __( 'Does, or will, your site have multiple authors?', 'wordpress-seo' ) );
|
17 |
+
|
18 |
+
$this->set_property( 'description', __( 'If you choose no, your author archives will be deactivated to prevent
|
19 |
+
duplicate content issues.', 'wordpress-seo' ) );
|
20 |
+
|
21 |
+
$this->add_choice( 'yes', __( 'Yes', 'wordpress-seo' ) );
|
22 |
+
$this->add_choice( 'no', __( 'No', 'wordpress-seo' ) );
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Set adapter.
|
27 |
+
*
|
28 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
29 |
+
*/
|
30 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
31 |
+
$adapter->add_custom_lookup(
|
32 |
+
$this->get_identifier(),
|
33 |
+
array( $this, 'get_data' ),
|
34 |
+
array( $this, 'set_data' )
|
35 |
+
);
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Get the data from the stored options.
|
40 |
+
*
|
41 |
+
* @return null|string
|
42 |
+
*/
|
43 |
+
public function get_data() {
|
44 |
+
|
45 |
+
if ( WPSEO_Options::get( 'has_multiple_authors', false ) ) {
|
46 |
+
$value = WPSEO_Options::get( 'has_multiple_authors' );
|
47 |
+
}
|
48 |
+
|
49 |
+
if ( ! isset( $value ) || is_null( $value ) ) {
|
50 |
+
// If there are more than one users with level > 1 default to multiple authors.
|
51 |
+
$users = get_users( array(
|
52 |
+
'fields' => 'IDs',
|
53 |
+
'who' => 'authors',
|
54 |
+
) );
|
55 |
+
|
56 |
+
$value = count( $users ) > 1;
|
57 |
+
}
|
58 |
+
|
59 |
+
return ( $value ) ? 'yes' : 'no';
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Set the data in the options.
|
64 |
+
*
|
65 |
+
* @param {string} $data The data to set for the field.
|
66 |
+
*
|
67 |
+
* @return bool Returns true or false for successful storing the data.
|
68 |
+
*/
|
69 |
+
public function set_data( $data ) {
|
70 |
+
$value = ( $data === 'yes' );
|
71 |
+
|
72 |
+
// Set multiple authors option.
|
73 |
+
$result_multiple_authors = WPSEO_Options::set( 'has_multiple_authors', $value );
|
74 |
+
|
75 |
+
/*
|
76 |
+
* Set disable author archives option. When multiple authors is set to true,
|
77 |
+
* the disable author option has to be false. Because of this the $value is inversed.
|
78 |
+
*/
|
79 |
+
$result_author_archives = WPSEO_Options::set( 'disable-author', ! $value );
|
80 |
+
|
81 |
+
return ( $result_multiple_authors === true && $result_author_archives === true );
|
82 |
+
}
|
83 |
+
}
|
admin/config-ui/fields/class-field-person-name.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Configurator
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Person_Name
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Person_Name extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Company_Or_Person constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'publishingEntityPersonName', 'Input' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'The name of the person', 'wordpress-seo' ) );
|
18 |
+
|
19 |
+
$this->set_requires( 'publishingEntityType', 'person' );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
24 |
+
*/
|
25 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
26 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'person_name' );
|
27 |
+
}
|
28 |
+
}
|
admin/config-ui/fields/class-field-post-type-visibility.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Post_Type_Visibility
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Post_Type_Visibility extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Post_Type_Visibility constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'postTypeVisibility', 'HTML' );
|
16 |
+
|
17 |
+
$copy = __( 'Please specify what content types you would like to appear in search engines.
|
18 |
+
If you do not know the differences between these, it\'s best to choose the
|
19 |
+
default settings.', 'wordpress-seo' );
|
20 |
+
|
21 |
+
$html = '<p>' . esc_html( $copy ) . '</p><br/>';
|
22 |
+
|
23 |
+
$this->set_property( 'html', $html );
|
24 |
+
}
|
25 |
+
}
|
admin/config-ui/fields/class-field-profile-url-facebook.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Profile_URL_Facebook
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Profile_URL_Facebook extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Profile_URL_Facebook constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'profileUrlFacebook', 'Input' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'Facebook Page URL', 'wordpress-seo' ) );
|
18 |
+
$this->set_property( 'pattern', '^https:\/\/www\.facebook\.com\/([^/]+)\/$' );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Set adapter
|
23 |
+
*
|
24 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
25 |
+
*/
|
26 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
27 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'facebook_site' );
|
28 |
+
}
|
29 |
+
}
|
admin/config-ui/fields/class-field-profile-url-googleplus.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Profile_URL_GooglePlus
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Profile_URL_GooglePlus extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Profile_URL_GooglePlus constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'profileUrlGooglePlus', 'Input' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'Google+ URL', 'wordpress-seo' ) );
|
18 |
+
$this->set_property( 'pattern', '^https:\/\/plus\.google\.com\/([^/]+)$' );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Set adapter
|
23 |
+
*
|
24 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
25 |
+
*/
|
26 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
27 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'google_plus_url' );
|
28 |
+
}
|
29 |
+
}
|
admin/config-ui/fields/class-field-profile-url-instagram.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Profile_URL_Instagram
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Profile_URL_Instagram extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Profile_URL_Instagram constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'profileUrlInstagram', 'Input' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'Instagram URL', 'wordpress-seo' ) );
|
18 |
+
$this->set_property( 'pattern', '^https:\/\/www\.instagram\.com\/([^/]+)\/$' );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Set adapter
|
23 |
+
*
|
24 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
25 |
+
*/
|
26 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
27 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'instagram_url' );
|
28 |
+
}
|
29 |
+
}
|
admin/config-ui/fields/class-field-profile-url-linkedin.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Profile_URL_LinkedIn
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Profile_URL_LinkedIn extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Profile_URL_LinkedIn constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'profileUrlLinkedIn', 'Input' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'LinkedIn URL', 'wordpress-seo' ) );
|
18 |
+
$this->set_property( 'pattern', '^https:\/\/www\.linkedin\.com\/in\/([^/]+)$' );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Set adapter
|
23 |
+
*
|
24 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
25 |
+
*/
|
26 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
27 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'linkedin_url' );
|
28 |
+
}
|
29 |
+
}
|
admin/config-ui/fields/class-field-profile-url-myspace.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Profile_URL_MySpace
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Profile_URL_MySpace extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Profile_URL_MySpace constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'profileUrlMySpace', 'Input' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'MySpace URL', 'wordpress-seo' ) );
|
18 |
+
$this->set_property( 'pattern', '^https:\/\/myspace\.com\/([^/]+)\/$' );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Set adapter
|
23 |
+
*
|
24 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
25 |
+
*/
|
26 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
27 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'myspace_url' );
|
28 |
+
}
|
29 |
+
}
|
admin/config-ui/fields/class-field-profile-url-pinterest.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Profile_URL_Pinterest
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Profile_URL_Pinterest extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Profile_URL_Pinterest constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'profileUrlPinterest', 'Input' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'Pinterest URL', 'wordpress-seo' ) );
|
18 |
+
$this->set_property( 'pattern', '^https:\/\/www\.pinterest\.com\/([^/]+)\/$' );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Set adapter
|
23 |
+
*
|
24 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
25 |
+
*/
|
26 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
27 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'pinterest_url' );
|
28 |
+
}
|
29 |
+
}
|
admin/config-ui/fields/class-field-profile-url-twitter.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Profile_URL_Twitter
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Profile_URL_Twitter extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Profile_URL_Twitter constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'profileUrlTwitter', 'Input' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'Twitter Username', 'wordpress-seo' ) );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Set adapter
|
22 |
+
*
|
23 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
24 |
+
*/
|
25 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
26 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'twitter_site' );
|
27 |
+
}
|
28 |
+
}
|
admin/config-ui/fields/class-field-profile-url-youtube.php
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Profile_URL_YouTube
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Profile_URL_YouTube extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Profile_URL_YouTube constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'profileUrlYouTube', 'Input' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'YouTube URL', 'wordpress-seo' ) );
|
18 |
+
$this->set_property( 'pattern', '^https:\/\/www\.youtube\.com\/([^/]+)$' );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Set adapter
|
23 |
+
*
|
24 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
25 |
+
*/
|
26 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
27 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'youtube_url' );
|
28 |
+
}
|
29 |
+
}
|
admin/config-ui/fields/class-field-separator.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Separator
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Separator extends WPSEO_Config_Field_Choice {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Separator constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'separator' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'Title Separator', 'wordpress-seo' ) );
|
18 |
+
$this->set_property( 'explanation', __( 'Choose the symbol to use as your title separator. This will display, for instance, between your post title and site name. Symbols are shown in the size they\'ll appear in the search results.', 'wordpress-seo' ) );
|
19 |
+
|
20 |
+
$this->add_choice( 'sc-dash', '-', __( 'Dash', 'wordpress-seo' ) );
|
21 |
+
$this->add_choice( 'sc-ndash', '–', __( 'En dash', 'wordpress-seo' ) );
|
22 |
+
$this->add_choice( 'sc-mdash', '—', __( 'Em dash', 'wordpress-seo' ) );
|
23 |
+
$this->add_choice( 'sc-middot', '·', __( 'Middle dot', 'wordpress-seo' ) );
|
24 |
+
$this->add_choice( 'sc-bull', '•', __( 'Bullet', 'wordpress-seo' ) );
|
25 |
+
$this->add_choice( 'sc-star', '*', __( 'Asterisk', 'wordpress-seo' ) );
|
26 |
+
$this->add_choice( 'sc-smstar', '⋆', __( 'Low asterisk', 'wordpress-seo' ) );
|
27 |
+
$this->add_choice( 'sc-pipe', '|', __( 'Vertical bar', 'wordpress-seo' ) );
|
28 |
+
$this->add_choice( 'sc-tilde', '~', __( 'Small tilde', 'wordpress-seo' ) );
|
29 |
+
$this->add_choice( 'sc-laquo', '«', __( 'Left angle quotation mark', 'wordpress-seo' ) );
|
30 |
+
$this->add_choice( 'sc-raquo', '»', __( 'Right angle quotation mark', 'wordpress-seo' ) );
|
31 |
+
$this->add_choice( 'sc-lt', '<', __( 'Less than sign', 'wordpress-seo' ) );
|
32 |
+
$this->add_choice( 'sc-gt', '>', __( 'Greater than sign', 'wordpress-seo' ) );
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Set adapter
|
37 |
+
*
|
38 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
39 |
+
*/
|
40 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
41 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'separator' );
|
42 |
+
}
|
43 |
+
}
|
admin/config-ui/fields/class-field-site-name.php
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Site_Name
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Site_Name extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Site_Name constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'siteName', 'Input' );
|
16 |
+
|
17 |
+
$this->set_property( 'label', __( 'Website name', 'wordpress-seo' ) );
|
18 |
+
|
19 |
+
$this->set_property( 'explanation', __( 'Google shows your website\'s name in the search results, if you want to change it, you can do that here.', 'wordpress-seo' ) );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Set adapter
|
24 |
+
*
|
25 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
26 |
+
*/
|
27 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
28 |
+
$adapter->add_custom_lookup(
|
29 |
+
$this->get_identifier(),
|
30 |
+
array( $this, 'get_data' ),
|
31 |
+
array( $this, 'set_data' )
|
32 |
+
);
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Get the data from the stored options.
|
37 |
+
*
|
38 |
+
* @return null|string
|
39 |
+
*/
|
40 |
+
public function get_data() {
|
41 |
+
if ( WPSEO_Options::get( 'website_name', false ) ) {
|
42 |
+
return WPSEO_Options::get( 'website_name' );
|
43 |
+
}
|
44 |
+
|
45 |
+
return get_bloginfo( 'name' );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Set the data in the options.
|
50 |
+
*
|
51 |
+
* @param {string} $data The data to set for the field.
|
52 |
+
*
|
53 |
+
* @return bool Returns true or false for successful storing the data.
|
54 |
+
*/
|
55 |
+
public function set_data( $data ) {
|
56 |
+
return WPSEO_Options::set( 'website_name', $data );
|
57 |
+
}
|
58 |
+
}
|
admin/config-ui/fields/class-field-site-type.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Site_Type
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Site_Type extends WPSEO_Config_Field_Choice {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Site_Type constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'siteType' );
|
16 |
+
|
17 |
+
/* translators: %1$s resolves to the home_url of the blog. */
|
18 |
+
$this->set_property( 'label', sprintf( __( 'What does the site %1$s represent?', 'wordpress-seo' ), get_home_url() ) );
|
19 |
+
|
20 |
+
$this->add_choice( 'blog', __( 'A blog', 'wordpress-seo' ) );
|
21 |
+
$this->add_choice( 'shop', __( 'An online shop', 'wordpress-seo' ) );
|
22 |
+
$this->add_choice( 'news', __( 'A news channel', 'wordpress-seo' ) );
|
23 |
+
$this->add_choice( 'smallBusiness', __( 'A small offline business', 'wordpress-seo' ) );
|
24 |
+
$this->add_choice( 'corporate', __( 'A corporation', 'wordpress-seo' ) );
|
25 |
+
$this->add_choice( 'portfolio', __( 'A portfolio', 'wordpress-seo' ) );
|
26 |
+
$this->add_choice( 'other', __( 'Something else', 'wordpress-seo' ) );
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Set adapter
|
31 |
+
*
|
32 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
33 |
+
*/
|
34 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
35 |
+
$adapter->add_option_lookup( $this->get_identifier(), 'site_type' );
|
36 |
+
}
|
37 |
+
}
|
admin/config-ui/fields/class-field-social-profiles-intro.php
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Social_Profiles_Intro
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Social_Profiles_Intro extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Social_Profiles_Intro constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'socialProfilesIntro', 'HTML' );
|
16 |
+
|
17 |
+
$intro_text = sprintf(
|
18 |
+
/* translators: %1$s is the plugin name, %2$s is a link opening tag, %3$s is a link closing tag. */
|
19 |
+
__( '%1$s can tell search engines about your social media profiles.
|
20 |
+
These will be used in Google\'s Knowledge Graph. There are additional
|
21 |
+
sharing options in the %1$s Social settings. %2$sRead more%3$s about these options.', 'wordpress-seo' ),
|
22 |
+
'Yoast SEO',
|
23 |
+
'<a target="_blank" rel="noopener noreferrer" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/1ey' ) . '">',
|
24 |
+
'</a>'
|
25 |
+
);
|
26 |
+
|
27 |
+
$html = '<p>' . $intro_text . '</p>';
|
28 |
+
|
29 |
+
$this->set_property( 'html', $html );
|
30 |
+
}
|
31 |
+
}
|
admin/config-ui/fields/class-field-success-message.php
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Success_Message
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Success_Message extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Success_Message constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'successMessage', 'FinalStep' );
|
16 |
+
|
17 |
+
$success_message = sprintf(
|
18 |
+
/* translators: %1$s expands to Yoast SEO. */
|
19 |
+
__( '%1$s will now take care of all the needed technical optimization of your site. To really improve your site\'s performance in the search results, it\'s important to start creating content that ranks well for keywords you care about. Check out this video in which we explain how to use the %1$s metabox when you edit posts or pages.', 'wordpress-seo' ),
|
20 |
+
'Yoast SEO'
|
21 |
+
);
|
22 |
+
|
23 |
+
$this->set_property( 'title', __( 'You\'ve done it!', 'wordpress-seo' ) );
|
24 |
+
$this->set_property( 'message', $success_message );
|
25 |
+
$this->set_property( 'video', array(
|
26 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/metabox-screencast' ),
|
27 |
+
'title' => sprintf(
|
28 |
+
/* translators: %1$s expands to Yoast SEO. */
|
29 |
+
__( '%1$s video tutorial', 'wordpress-seo' ),
|
30 |
+
'Yoast SEO'
|
31 |
+
),
|
32 |
+
)
|
33 |
+
);
|
34 |
+
}
|
35 |
+
}
|
admin/config-ui/fields/class-field-suggestions.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Holds the suggestions for the 'You might also like' page in the wizard
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Suggestions extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Suggestions constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'suggestions', 'Suggestions' );
|
16 |
+
|
17 |
+
$this->properties['suggestions'] = array();
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Adds a suggestion to the properties
|
22 |
+
*
|
23 |
+
* @param string $title The title of the choice.
|
24 |
+
* @param string $copy The text explaining the choice.
|
25 |
+
* @param array $button The button details.
|
26 |
+
* @param null|string $video The video accompanying the choice.
|
27 |
+
*/
|
28 |
+
public function add_suggestion( $title, $copy, $button, $video = null ) {
|
29 |
+
$suggestion = array(
|
30 |
+
'title' => $title,
|
31 |
+
'copy' => $copy,
|
32 |
+
'button' => $button,
|
33 |
+
);
|
34 |
+
|
35 |
+
if ( ! empty( $video ) ) {
|
36 |
+
$suggestion['video'] = $video;
|
37 |
+
}
|
38 |
+
|
39 |
+
$this->properties['suggestions'][] = $suggestion;
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
admin/config-ui/fields/class-field-title-intro.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Title_Intro
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Title_Intro extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Social_Profiles_Intro constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'titleIntro', 'HTML' );
|
16 |
+
|
17 |
+
$html = __( 'On this page, you can change the name of your site and choose which
|
18 |
+
separator to use. The separator will display, for instance, between your
|
19 |
+
post title and site name. Symbols are shown in the size they\'ll appear in
|
20 |
+
the search results. Choose the one that fits your brand best or takes up
|
21 |
+
the least space in the snippets.', 'wordpress-seo' );
|
22 |
+
|
23 |
+
$html = '<p>' . esc_html( $html ) . '</p>';
|
24 |
+
|
25 |
+
$this->set_property( 'html', $html );
|
26 |
+
}
|
27 |
+
}
|
admin/config-ui/fields/class-field-upsell-configuration-service.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Upsell_Configuration_Service
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Upsell_Configuration_Service extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Upsell_Configuration_Service constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'upsellConfigurationService', 'HTML' );
|
16 |
+
|
17 |
+
$intro_text = sprintf(
|
18 |
+
/* translators: %1$s expands to Yoast SEO. */
|
19 |
+
__( 'Welcome to the %1$s configuration wizard. In a few simple steps we\'ll help you configure your SEO settings to match your website\'s needs!', 'wordpress-seo' ),
|
20 |
+
'Yoast SEO'
|
21 |
+
);
|
22 |
+
|
23 |
+
$upsell_text = sprintf(
|
24 |
+
/* Translators: %1$s expands to Yoast SEO, %2$s expands to Yoast SEO Premium, %3$s opens the link, %4$s closes the link. */
|
25 |
+
__( 'While we strive to make setting up %1$s as easy as possible, we understand it can be daunting. If you’d rather have us set up %1$s for you (and get a copy of %2$s in the process), order our %3$s%1$s configuration service%4$s here!', 'wordpress-seo' ),
|
26 |
+
'Yoast SEO',
|
27 |
+
'Yoast SEO Premium',
|
28 |
+
'<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/configuration-package' ) . '">',
|
29 |
+
'</a>'
|
30 |
+
);
|
31 |
+
|
32 |
+
$html = '<p>' . esc_html( $intro_text ) . '</p>';
|
33 |
+
$html .= '<p><em>' . wp_kses( $upsell_text, array(
|
34 |
+
'a' => array(
|
35 |
+
'target' => array( '_blank' ),
|
36 |
+
'href' => array(),
|
37 |
+
),
|
38 |
+
) ) . '</em></p>';
|
39 |
+
|
40 |
+
|
41 |
+
$this->set_property( 'html', $html );
|
42 |
+
}
|
43 |
+
}
|
admin/config-ui/fields/class-field-upsell-site-review.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field_Upsell_Site_Review
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field_Upsell_Site_Review extends WPSEO_Config_Field {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* WPSEO_Config_Field_Upsell_Site_Review constructor.
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
parent::__construct( 'upsellSiteReview', 'HTML' );
|
16 |
+
|
17 |
+
$upsell_text = sprintf(
|
18 |
+
/* translators: %1$s will be a link to a review explanation page. Text between %2$s and %3$s will be a link to an SEO copywriting course page. */
|
19 |
+
__( 'If you want more help creating awesome content, check out our %2$sSEO copywriting course%3$s. Do you want to know all about the features of the plugin, consider doing our %1$s!', 'wordpress-seo' ),
|
20 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/yoastseotraining' ) . '" target="_blank">Yoast SEO for WordPress training</a>',
|
21 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/configuration-wizard-copywrite-course-link' ) . '" target="_blank">',
|
22 |
+
'</a>'
|
23 |
+
);
|
24 |
+
|
25 |
+
$html = '<p>' .
|
26 |
+
wp_kses( $upsell_text, array(
|
27 |
+
'a' => array(
|
28 |
+
'href' => array(),
|
29 |
+
'target' => array( '_blank' ),
|
30 |
+
),
|
31 |
+
) ) .
|
32 |
+
'</p>';
|
33 |
+
|
34 |
+
$this->set_property( 'html', $html );
|
35 |
+
}
|
36 |
+
}
|
admin/config-ui/fields/class-field.php
ADDED
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\ConfigurationUI
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Config_Field
|
8 |
+
*/
|
9 |
+
class WPSEO_Config_Field {
|
10 |
+
/** @var string Field name */
|
11 |
+
protected $field;
|
12 |
+
|
13 |
+
/** @var string Component to use */
|
14 |
+
protected $component;
|
15 |
+
|
16 |
+
/** @var array Properties of this field */
|
17 |
+
protected $properties = array();
|
18 |
+
|
19 |
+
/** @var array Field requirements */
|
20 |
+
protected $requires = array();
|
21 |
+
|
22 |
+
/** @var array|mixed Value of this field */
|
23 |
+
protected $data = array();
|
24 |
+
|
25 |
+
/**
|
26 |
+
* WPSEO_Config_Field constructor.
|
27 |
+
*
|
28 |
+
* @param string $field The field name.
|
29 |
+
* @param string $component The component to use.
|
30 |
+
*/
|
31 |
+
public function __construct( $field, $component ) {
|
32 |
+
$this->field = $field;
|
33 |
+
$this->component = $component;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Get the identifier
|
38 |
+
*
|
39 |
+
* @return string
|
40 |
+
*/
|
41 |
+
public function get_identifier() {
|
42 |
+
return $this->field;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Get the component
|
47 |
+
*
|
48 |
+
* @return string
|
49 |
+
*/
|
50 |
+
public function get_component() {
|
51 |
+
return $this->component;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Set a property value
|
56 |
+
*
|
57 |
+
* @param string $name Property to set.
|
58 |
+
* @param mixed $value Value to apply.
|
59 |
+
*/
|
60 |
+
public function set_property( $name, $value ) {
|
61 |
+
$this->properties[ $name ] = $value;
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Get all the properties
|
66 |
+
*
|
67 |
+
* @return array
|
68 |
+
*/
|
69 |
+
public function get_properties() {
|
70 |
+
return $this->properties;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Get the data
|
75 |
+
*
|
76 |
+
* @return mixed
|
77 |
+
*/
|
78 |
+
public function get_data() {
|
79 |
+
return $this->data;
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Array representation of this object.
|
84 |
+
*
|
85 |
+
* @return array
|
86 |
+
*/
|
87 |
+
public function to_array() {
|
88 |
+
$output = array(
|
89 |
+
'componentName' => $this->get_component(),
|
90 |
+
);
|
91 |
+
|
92 |
+
$properties = $this->get_properties();
|
93 |
+
if ( $properties ) {
|
94 |
+
$output['properties'] = $properties;
|
95 |
+
}
|
96 |
+
|
97 |
+
$requires = $this->get_requires();
|
98 |
+
if ( ! empty( $requires ) ) {
|
99 |
+
$output['requires'] = $requires;
|
100 |
+
}
|
101 |
+
|
102 |
+
return $output;
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Set the adapter to use
|
107 |
+
*
|
108 |
+
* @param WPSEO_Configuration_Options_Adapter $adapter Adapter to register lookup on.
|
109 |
+
*/
|
110 |
+
public function set_adapter( WPSEO_Configuration_Options_Adapter $adapter ) {
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Requires another field to have a certain value.
|
115 |
+
*
|
116 |
+
* @param string $field Field to check for a certain value.
|
117 |
+
* @param mixed $value Value of the field.
|
118 |
+
*/
|
119 |
+
public function set_requires( $field, $value ) {
|
120 |
+
$this->requires = array(
|
121 |
+
'field' => $field,
|
122 |
+
'value' => $value,
|
123 |
+
);
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Get the required field settings (if present)
|
128 |
+
*
|
129 |
+
* @return array
|
130 |
+
*/
|
131 |
+
public function get_requires() {
|
132 |
+
return $this->requires;
|
133 |
+
}
|
134 |
+
}
|
admin/endpoints/class-endpoint-ryte.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\OnPage
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents an implementation of the WPSEO_Endpoint interface to register one or multiple endpoints.
|
8 |
+
*/
|
9 |
+
class WPSEO_Endpoint_Ryte implements WPSEO_Endpoint {
|
10 |
+
|
11 |
+
const REST_NAMESPACE = 'yoast/v1';
|
12 |
+
const ENDPOINT_RETRIEVE = 'ryte';
|
13 |
+
|
14 |
+
const CAPABILITY_RETRIEVE = 'manage_options';
|
15 |
+
|
16 |
+
/** @var WPSEO_Ryte_Service Service to use */
|
17 |
+
protected $service;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Constructs the WPSEO_Endpoint_Ryte class and sets the service to use.
|
21 |
+
*
|
22 |
+
* @param WPSEO_Ryte_Service $service Service to use.
|
23 |
+
*/
|
24 |
+
public function __construct( WPSEO_Ryte_Service $service ) {
|
25 |
+
$this->service = $service;
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Registers the REST routes that are available on the endpoint.
|
30 |
+
*/
|
31 |
+
public function register() {
|
32 |
+
// Register fetch config.
|
33 |
+
register_rest_route( self::REST_NAMESPACE, self::ENDPOINT_RETRIEVE, array(
|
34 |
+
'methods' => 'GET',
|
35 |
+
'callback' => array(
|
36 |
+
$this->service,
|
37 |
+
'get_statistics',
|
38 |
+
),
|
39 |
+
'permission_callback' => array(
|
40 |
+
$this,
|
41 |
+
'can_retrieve_data',
|
42 |
+
),
|
43 |
+
) );
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Determines whether or not data can be retrieved for the registered endpoints.
|
48 |
+
*
|
49 |
+
* @return bool Whether or not data can be retrieved.
|
50 |
+
*/
|
51 |
+
public function can_retrieve_data() {
|
52 |
+
return current_user_can( self::CAPABILITY_RETRIEVE );
|
53 |
+
}
|
54 |
+
}
|
admin/endpoints/class-endpoint-statistics.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Statistics
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents an implementation of the WPSEO_Endpoint interface to register one or multiple endpoints.
|
8 |
+
*/
|
9 |
+
class WPSEO_Endpoint_Statistics implements WPSEO_Endpoint {
|
10 |
+
|
11 |
+
const REST_NAMESPACE = 'yoast/v1';
|
12 |
+
const ENDPOINT_RETRIEVE = 'statistics';
|
13 |
+
|
14 |
+
const CAPABILITY_RETRIEVE = 'read';
|
15 |
+
|
16 |
+
/** @var WPSEO_Statistics_Service Service to use */
|
17 |
+
protected $service;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Constructs the WPSEO_Endpoint_Statistics class and sets the service to use.
|
21 |
+
*
|
22 |
+
* @param WPSEO_Statistics_Service $service Service to use.
|
23 |
+
*/
|
24 |
+
public function __construct( WPSEO_Statistics_Service $service ) {
|
25 |
+
$this->service = $service;
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Registers the REST routes that are available on the endpoint.
|
30 |
+
*/
|
31 |
+
public function register() {
|
32 |
+
// Register fetch config.
|
33 |
+
register_rest_route( self::REST_NAMESPACE, self::ENDPOINT_RETRIEVE, array(
|
34 |
+
'methods' => 'GET',
|
35 |
+
'callback' => array(
|
36 |
+
$this->service,
|
37 |
+
'get_statistics',
|
38 |
+
),
|
39 |
+
'permission_callback' => array(
|
40 |
+
$this,
|
41 |
+
'can_retrieve_data',
|
42 |
+
),
|
43 |
+
) );
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Determines whether or not data can be retrieved for the registered endpoints.
|
48 |
+
*
|
49 |
+
* @return bool Whether or not data can be retrieved.
|
50 |
+
*/
|
51 |
+
public function can_retrieve_data() {
|
52 |
+
return current_user_can( self::CAPABILITY_RETRIEVE );
|
53 |
+
}
|
54 |
+
}
|
admin/endpoints/class-endpoint.php
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Endpoints
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Dictates the required methods for an Endpoint implementation.
|
8 |
+
*/
|
9 |
+
interface WPSEO_Endpoint {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Registers the routes for the endpoints.
|
13 |
+
*
|
14 |
+
* @return void
|
15 |
+
*/
|
16 |
+
public function register();
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Determines whether or not data can be retrieved for the registered endpoints.
|
20 |
+
*
|
21 |
+
* @return bool Whether or not data can be retrieved.
|
22 |
+
*/
|
23 |
+
public function can_retrieve_data();
|
24 |
+
}
|
admin/filters/class-abstract-post-filter.php
ADDED
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Filters
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Abstract_Post_Filter
|
8 |
+
*/
|
9 |
+
abstract class WPSEO_Abstract_Post_Filter implements WPSEO_WordPress_Integration {
|
10 |
+
|
11 |
+
const FILTER_QUERY_ARG = 'yoast_filter';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Modify the query based on the FILTER_QUERY_ARG variable in $_GET
|
15 |
+
*
|
16 |
+
* @param string $where Query variables.
|
17 |
+
*
|
18 |
+
* @return string The modified query.
|
19 |
+
*/
|
20 |
+
abstract public function filter_posts( $where );
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Returns the query value this filter uses.
|
24 |
+
*
|
25 |
+
* @return string The query value this filter uses.
|
26 |
+
*/
|
27 |
+
abstract public function get_query_val();
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Returns the total number of posts that match this filter.
|
31 |
+
*
|
32 |
+
* @return int The total number of posts that match this filter.
|
33 |
+
*/
|
34 |
+
abstract protected function get_post_total();
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Returns the label for this filter.
|
38 |
+
*
|
39 |
+
* @return string The label for this filter.
|
40 |
+
*/
|
41 |
+
abstract protected function get_label();
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Registers the hooks.
|
45 |
+
*/
|
46 |
+
public function register_hooks() {
|
47 |
+
add_action( 'admin_init', array( $this, 'add_filter_links' ), 11 );
|
48 |
+
|
49 |
+
add_filter( 'posts_where', array( $this, 'filter_posts' ) );
|
50 |
+
|
51 |
+
if ( $this->is_filter_active() ) {
|
52 |
+
add_action( 'restrict_manage_posts', array( $this, 'render_hidden_input' ) );
|
53 |
+
}
|
54 |
+
|
55 |
+
if ( $this->is_filter_active() && $this->get_explanation() !== null ) {
|
56 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_explanation_assets' ) );
|
57 |
+
}
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Adds the filter links to the view_edit screens to give the user a filter link.
|
62 |
+
*
|
63 |
+
* @return void
|
64 |
+
*/
|
65 |
+
public function add_filter_links() {
|
66 |
+
foreach ( $this->get_post_types() as $post_type ) {
|
67 |
+
add_filter( 'views_edit-' . $post_type, array( $this, 'add_filter_link' ) );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Enqueues the necessary assets to display a filter explanation.
|
73 |
+
*
|
74 |
+
* @return void
|
75 |
+
*/
|
76 |
+
public function enqueue_explanation_assets() {
|
77 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
78 |
+
$asset_manager->enqueue_script( 'filter-explanation' );
|
79 |
+
$asset_manager->enqueue_style( 'filter-explanation' );
|
80 |
+
wp_localize_script(
|
81 |
+
WPSEO_Admin_Asset_Manager::PREFIX . 'filter-explanation',
|
82 |
+
'yoastFilterExplanation',
|
83 |
+
array( 'text' => $this->get_explanation() )
|
84 |
+
);
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Adds a filter link to the views.
|
89 |
+
*
|
90 |
+
* @param array $views Array with the views.
|
91 |
+
*
|
92 |
+
* @return array Array of views including the added view.
|
93 |
+
*/
|
94 |
+
public function add_filter_link( array $views ) {
|
95 |
+
$views[ 'yoast_' . $this->get_query_val() ] = sprintf(
|
96 |
+
'<a href="%1$s"%2$s>%3$s</a> (%4$s)',
|
97 |
+
esc_url( $this->get_filter_url() ),
|
98 |
+
( $this->is_filter_active() ) ? ' class="current" aria-current="page"' : '',
|
99 |
+
$this->get_label(),
|
100 |
+
$this->get_post_total()
|
101 |
+
);
|
102 |
+
|
103 |
+
return $views;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Returns a text explaining this filter. Null if no explanation is necessary.
|
108 |
+
*
|
109 |
+
* @return string|null The explanation or null.
|
110 |
+
*/
|
111 |
+
protected function get_explanation() {
|
112 |
+
return null;
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Renders a hidden input to preserve this filter's state when using sub-filters.
|
117 |
+
*
|
118 |
+
* @return void
|
119 |
+
*/
|
120 |
+
public function render_hidden_input() {
|
121 |
+
echo '<input type="hidden" name="' . esc_attr( self::FILTER_QUERY_ARG ) . '" value="' . esc_attr( $this->get_query_val() ) . '">';
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Returns an url to edit.php with post_type and this filter as the query arguments.
|
126 |
+
*
|
127 |
+
* @return string The url to activate this filter.
|
128 |
+
*/
|
129 |
+
protected function get_filter_url() {
|
130 |
+
return add_query_arg( array(
|
131 |
+
self::FILTER_QUERY_ARG => $this->get_query_val(),
|
132 |
+
'post_type' => $this->get_current_post_type(),
|
133 |
+
), 'edit.php' );
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Returns true when the filter is active.
|
138 |
+
*
|
139 |
+
* @return bool Whether or not the filter is active.
|
140 |
+
*/
|
141 |
+
protected function is_filter_active() {
|
142 |
+
return ( $this->is_supported_post_type( $this->get_current_post_type() )
|
143 |
+
&& filter_input( INPUT_GET, self::FILTER_QUERY_ARG ) === $this->get_query_val() );
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Returns the current post type.
|
148 |
+
*
|
149 |
+
* @return string The current post type.
|
150 |
+
*/
|
151 |
+
protected function get_current_post_type() {
|
152 |
+
return filter_input(
|
153 |
+
INPUT_GET, 'post_type', FILTER_DEFAULT, array(
|
154 |
+
'options' => array( 'default' => 'post' ),
|
155 |
+
)
|
156 |
+
);
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Returns the post types to which this filter should be added.
|
161 |
+
*
|
162 |
+
* @return array The post types to which this filter should be added.
|
163 |
+
*/
|
164 |
+
protected function get_post_types() {
|
165 |
+
return WPSEO_Post_Type::get_accessible_post_types();
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Checks if the post type is supported.
|
170 |
+
*
|
171 |
+
* @param string $post_type Post type to check against.
|
172 |
+
*
|
173 |
+
* @return bool True when it is supported.
|
174 |
+
*/
|
175 |
+
protected function is_supported_post_type( $post_type ) {
|
176 |
+
return in_array( $post_type, $this->get_post_types(), true );
|
177 |
+
}
|
178 |
+
}
|
admin/filters/class-cornerstone-filter.php
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Registers the filter for filtering posts by cornerstone content.
|
8 |
+
*/
|
9 |
+
class WPSEO_Cornerstone_Filter extends WPSEO_Abstract_Post_Filter {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns the query value this filter uses.
|
13 |
+
*
|
14 |
+
* @return {string} The query value this filter uses.
|
15 |
+
*/
|
16 |
+
public function get_query_val() {
|
17 |
+
return 'cornerstone';
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Modify the query based on the seo_filter variable in $_GET
|
22 |
+
*
|
23 |
+
* @param string $where Query variables.
|
24 |
+
*
|
25 |
+
* @return string The modified query.
|
26 |
+
*/
|
27 |
+
public function filter_posts( $where ) {
|
28 |
+
if ( $this->is_filter_active() ) {
|
29 |
+
global $wpdb;
|
30 |
+
|
31 |
+
$where .= sprintf(
|
32 |
+
' AND ' . $wpdb->posts . '.ID IN( SELECT post_id FROM ' . $wpdb->postmeta . ' WHERE meta_key = "%s" AND meta_value = "1" ) ',
|
33 |
+
WPSEO_Cornerstone::META_NAME
|
34 |
+
);
|
35 |
+
}
|
36 |
+
|
37 |
+
return $where;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Returns the label for this filter.
|
42 |
+
*
|
43 |
+
* @return string The label for this filter.
|
44 |
+
*/
|
45 |
+
protected function get_label() {
|
46 |
+
return __( 'Cornerstone content', 'wordpress-seo' );
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Returns a text explaining this filter.
|
51 |
+
*
|
52 |
+
* @return string The explanation.
|
53 |
+
*/
|
54 |
+
protected function get_explanation() {
|
55 |
+
$post_type_object = get_post_type_object( $this->get_current_post_type() );
|
56 |
+
|
57 |
+
if ( $post_type_object === null ) {
|
58 |
+
return null;
|
59 |
+
}
|
60 |
+
|
61 |
+
return sprintf(
|
62 |
+
/* translators: %1$s expands to the posttype label, %2$s expands anchor to blog post about cornerstone content, %3$s expands to </a> */
|
63 |
+
__( 'Mark the most important %1$s as \'cornerstone content\' to improve your site structure. %2$sLearn more about cornerstone content%3$s.', 'wordpress-seo' ),
|
64 |
+
strtolower( $post_type_object->labels->name ),
|
65 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/1i9' ) . '" target="_blank">',
|
66 |
+
'</a>'
|
67 |
+
);
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Returns the total amount of articles marked as cornerstone content.
|
72 |
+
*
|
73 |
+
* @return integer
|
74 |
+
*/
|
75 |
+
protected function get_post_total() {
|
76 |
+
global $wpdb;
|
77 |
+
|
78 |
+
return (int) $wpdb->get_var(
|
79 |
+
$wpdb->prepare( '
|
80 |
+
SELECT COUNT( 1 )
|
81 |
+
FROM ' . $wpdb->postmeta . '
|
82 |
+
WHERE post_id IN( SELECT ID FROM ' . $wpdb->posts . ' WHERE post_type = %s ) &&
|
83 |
+
meta_value = "1" AND meta_key = %s
|
84 |
+
',
|
85 |
+
$this->get_current_post_type(),
|
86 |
+
WPSEO_Cornerstone::META_NAME
|
87 |
+
)
|
88 |
+
);
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Returns the post types to which this filter should be added.
|
93 |
+
*
|
94 |
+
* @return array The post types to which this filter should be added.
|
95 |
+
*/
|
96 |
+
protected function get_post_types() {
|
97 |
+
$post_types = apply_filters( 'wpseo_cornerstone_post_types', parent::get_post_types() );
|
98 |
+
if ( ! is_array( $post_types ) ) {
|
99 |
+
return array();
|
100 |
+
}
|
101 |
+
|
102 |
+
return $post_types;
|
103 |
+
}
|
104 |
+
}
|
admin/formatter/class-metabox-formatter.php
ADDED
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Formatter
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class forces needed methods for the metabox localization
|
8 |
+
*/
|
9 |
+
class WPSEO_Metabox_Formatter {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WPSEO_Metabox_Formatter_Interface Object that provides formatted values.
|
13 |
+
*/
|
14 |
+
private $formatter;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Setting the formatter property.
|
18 |
+
*
|
19 |
+
* @param WPSEO_Metabox_Formatter_Interface $formatter Object that provides the formatted values.
|
20 |
+
*/
|
21 |
+
public function __construct( WPSEO_Metabox_Formatter_Interface $formatter ) {
|
22 |
+
$this->formatter = $formatter;
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Returns the values
|
27 |
+
*
|
28 |
+
* @return array
|
29 |
+
*/
|
30 |
+
public function get_values() {
|
31 |
+
$defaults = $this->get_defaults();
|
32 |
+
$values = $this->formatter->get_values();
|
33 |
+
|
34 |
+
return ( $values + $defaults );
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Returns array with all the values always needed by a scraper object
|
39 |
+
*
|
40 |
+
* @return array Default settings for the metabox.
|
41 |
+
*/
|
42 |
+
private function get_defaults() {
|
43 |
+
$analysis_seo = new WPSEO_Metabox_Analysis_SEO();
|
44 |
+
$analysis_readability = new WPSEO_Metabox_Analysis_Readability();
|
45 |
+
|
46 |
+
return array(
|
47 |
+
'language' => WPSEO_Language_Utils::get_site_language_name(),
|
48 |
+
'settings_link' => $this->get_settings_link(),
|
49 |
+
'search_url' => '',
|
50 |
+
'post_edit_url' => '',
|
51 |
+
'base_url' => '',
|
52 |
+
'contentTab' => __( 'Readability', 'wordpress-seo' ),
|
53 |
+
'keywordTab' => __( 'Keyword:', 'wordpress-seo' ),
|
54 |
+
'enterFocusKeyword' => __( 'Enter your focus keyword', 'wordpress-seo' ),
|
55 |
+
'removeKeyword' => __( 'Remove keyword', 'wordpress-seo' ),
|
56 |
+
'contentLocale' => get_locale(),
|
57 |
+
'userLocale' => WPSEO_Utils::get_user_locale(),
|
58 |
+
'translations' => $this->get_translations(),
|
59 |
+
'keyword_usage' => array(),
|
60 |
+
'title_template' => '',
|
61 |
+
'metadesc_template' => '',
|
62 |
+
'contentAnalysisActive' => $analysis_readability->is_enabled() ? 1 : 0,
|
63 |
+
'keywordAnalysisActive' => $analysis_seo->is_enabled() ? 1 : 0,
|
64 |
+
'intl' => $this->get_content_analysis_component_translations(),
|
65 |
+
/**
|
66 |
+
* Filter to determine if the markers should be enabled or not.
|
67 |
+
*
|
68 |
+
* @param bool $showMarkers Should the markers being enabled. Default = true.
|
69 |
+
*/
|
70 |
+
'show_markers' => apply_filters( 'wpseo_enable_assessment_markers', true ),
|
71 |
+
'publish_box' => array(
|
72 |
+
'labels' => array(
|
73 |
+
'content' => array(
|
74 |
+
'na' => sprintf(
|
75 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag. */
|
76 |
+
__( 'Readability: %1$sNot available%2$s', 'wordpress-seo' ),
|
77 |
+
'<strong>',
|
78 |
+
'</strong>'
|
79 |
+
),
|
80 |
+
'bad' => sprintf(
|
81 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag. */
|
82 |
+
__( 'Readability: %1$sNeeds improvement%2$s', 'wordpress-seo' ),
|
83 |
+
'<strong>',
|
84 |
+
'</strong>'
|
85 |
+
),
|
86 |
+
'ok' => sprintf(
|
87 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag. */
|
88 |
+
__( 'Readability: %1$sOK%2$s', 'wordpress-seo' ),
|
89 |
+
'<strong>',
|
90 |
+
'</strong>'
|
91 |
+
),
|
92 |
+
'good' => sprintf(
|
93 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag. */
|
94 |
+
__( 'Readability: %1$sGood%2$s', 'wordpress-seo' ),
|
95 |
+
'<strong>',
|
96 |
+
'</strong>'
|
97 |
+
),
|
98 |
+
),
|
99 |
+
'keyword' => array(
|
100 |
+
'na' => sprintf(
|
101 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag. */
|
102 |
+
__( 'SEO: %1$sNot available%2$s', 'wordpress-seo' ),
|
103 |
+
'<strong>',
|
104 |
+
'</strong>'
|
105 |
+
),
|
106 |
+
'bad' => sprintf(
|
107 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag. */
|
108 |
+
__( 'SEO: %1$sNeeds improvement%2$s', 'wordpress-seo' ),
|
109 |
+
'<strong>',
|
110 |
+
'</strong>'
|
111 |
+
),
|
112 |
+
'ok' => sprintf(
|
113 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag. */
|
114 |
+
__( 'SEO: %1$sOK%2$s', 'wordpress-seo' ),
|
115 |
+
'<strong>',
|
116 |
+
'</strong>'
|
117 |
+
),
|
118 |
+
'good' => sprintf(
|
119 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag. */
|
120 |
+
__( 'SEO: %1$sGood%2$s', 'wordpress-seo' ),
|
121 |
+
'<strong>',
|
122 |
+
'</strong>'
|
123 |
+
),
|
124 |
+
),
|
125 |
+
),
|
126 |
+
),
|
127 |
+
'markdownEnabled' => $this->is_markdown_enabled(),
|
128 |
+
);
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Returns a link to the settings page, if the user has the right capabilities.
|
133 |
+
* Returns an empty string otherwise.
|
134 |
+
*
|
135 |
+
* @return string The settings link.
|
136 |
+
*/
|
137 |
+
private function get_settings_link() {
|
138 |
+
if ( current_user_can( 'manage_options' ) ) {
|
139 |
+
return admin_url( 'options-general.php' );
|
140 |
+
}
|
141 |
+
|
142 |
+
return '';
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Returns required yoast-component translations.
|
147 |
+
*
|
148 |
+
* @return array
|
149 |
+
*/
|
150 |
+
private function get_content_analysis_component_translations() {
|
151 |
+
// Esc_html is not needed because React already handles HTML in the (translations of) these strings.
|
152 |
+
return array(
|
153 |
+
'locale' => WPSEO_Utils::get_user_locale(),
|
154 |
+
'content-analysis.language-notice-link' => __( 'Change language', 'wordpress-seo' ),
|
155 |
+
'content-analysis.errors' => __( 'Errors', 'wordpress-seo' ),
|
156 |
+
'content-analysis.problems' => __( 'Problems', 'wordpress-seo' ),
|
157 |
+
'content-analysis.improvements' => __( 'Improvements', 'wordpress-seo' ),
|
158 |
+
'content-analysis.considerations' => __( 'Considerations', 'wordpress-seo' ),
|
159 |
+
'content-analysis.good' => __( 'Good results', 'wordpress-seo' ),
|
160 |
+
'content-analysis.language-notice' => __( 'Your site language is set to {language}.', 'wordpress-seo' ),
|
161 |
+
'content-analysis.language-notice-contact-admin' => __( 'Your site language is set to {language}. If this is not correct, contact your site administrator.', 'wordpress-seo' ),
|
162 |
+
'content-analysis.highlight' => __( 'Highlight this result in the text', 'wordpress-seo' ),
|
163 |
+
'content-analysis.nohighlight' => __( 'Remove highlight from the text', 'wordpress-seo' ),
|
164 |
+
'content-analysis.disabledButton' => __( 'Marks are disabled in current view', 'wordpress-seo' ),
|
165 |
+
'a11yNotice.opensInNewTab' => __( '(Opens in a new browser tab)', 'wordpress-seo' ),
|
166 |
+
);
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Returns Jed compatible YoastSEO.js translations.
|
171 |
+
*
|
172 |
+
* @return array
|
173 |
+
*/
|
174 |
+
private function get_translations() {
|
175 |
+
$locale = WPSEO_Utils::get_user_locale();
|
176 |
+
|
177 |
+
$file = plugin_dir_path( WPSEO_FILE ) . 'languages/wordpress-seo-' . $locale . '.json';
|
178 |
+
if ( file_exists( $file ) ) {
|
179 |
+
$file = file_get_contents( $file );
|
180 |
+
if ( is_string( $file ) && $file !== '' ) {
|
181 |
+
return json_decode( $file, true );
|
182 |
+
}
|
183 |
+
}
|
184 |
+
|
185 |
+
return array();
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Checks if Jetpack's markdown module is enabled.
|
190 |
+
* Can be extended to work with other plugins that parse markdown in the content.
|
191 |
+
*
|
192 |
+
* @return boolean
|
193 |
+
*/
|
194 |
+
private function is_markdown_enabled() {
|
195 |
+
if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_active_modules' ) ) {
|
196 |
+
$active_modules = Jetpack::get_active_modules();
|
197 |
+
|
198 |
+
return in_array( 'markdown', $active_modules, true );
|
199 |
+
}
|
200 |
+
|
201 |
+
return false;
|
202 |
+
}
|
203 |
+
}
|
admin/formatter/class-post-metabox-formatter.php
ADDED
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Formatter
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class provides data for the post metabox by return its values for localization
|
8 |
+
*/
|
9 |
+
class WPSEO_Post_Metabox_Formatter implements WPSEO_Metabox_Formatter_Interface {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WP_Post
|
13 |
+
*/
|
14 |
+
private $post;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string The permalink to follow.
|
18 |
+
*/
|
19 |
+
private $permalink;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Constructor.
|
23 |
+
*
|
24 |
+
* @param WP_Post|array $post Post object.
|
25 |
+
* @param array $options Title options to use.
|
26 |
+
* @param string $structure The permalink to follow.
|
27 |
+
*/
|
28 |
+
public function __construct( $post, array $options, $structure ) {
|
29 |
+
$this->post = $post;
|
30 |
+
$this->permalink = $structure;
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Returns the translated values.
|
35 |
+
*
|
36 |
+
* @return array
|
37 |
+
*/
|
38 |
+
public function get_values() {
|
39 |
+
$values = array(
|
40 |
+
'search_url' => $this->search_url(),
|
41 |
+
'post_edit_url' => $this->edit_url(),
|
42 |
+
'base_url' => $this->base_url_for_js(),
|
43 |
+
'metaDescriptionDate' => '',
|
44 |
+
);
|
45 |
+
|
46 |
+
if ( $this->post instanceof WP_Post ) {
|
47 |
+
$values_to_set = array(
|
48 |
+
'keyword_usage' => $this->get_focus_keyword_usage(),
|
49 |
+
'title_template' => $this->get_title_template(),
|
50 |
+
'metadesc_template' => $this->get_metadesc_template(),
|
51 |
+
'metaDescriptionDate' => $this->get_metadesc_date(),
|
52 |
+
);
|
53 |
+
|
54 |
+
$values = ( $values_to_set + $values );
|
55 |
+
}
|
56 |
+
|
57 |
+
return $values;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Returns the url to search for keyword for the post
|
62 |
+
*
|
63 |
+
* @return string
|
64 |
+
*/
|
65 |
+
private function search_url() {
|
66 |
+
return admin_url( 'edit.php?seo_kw_filter={keyword}' );
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Returns the url to edit the taxonomy
|
71 |
+
*
|
72 |
+
* @return string
|
73 |
+
*/
|
74 |
+
private function edit_url() {
|
75 |
+
return admin_url( 'post.php?post={id}&action=edit' );
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Returns a base URL for use in the JS, takes permalink structure into account
|
80 |
+
*
|
81 |
+
* @return string
|
82 |
+
*/
|
83 |
+
private function base_url_for_js() {
|
84 |
+
global $pagenow;
|
85 |
+
|
86 |
+
// The default base is the home_url.
|
87 |
+
$base_url = home_url( '/', null );
|
88 |
+
|
89 |
+
if ( 'post-new.php' === $pagenow ) {
|
90 |
+
return $base_url;
|
91 |
+
}
|
92 |
+
|
93 |
+
// If %postname% is the last tag, just strip it and use that as a base.
|
94 |
+
if ( 1 === preg_match( '#%postname%/?$#', $this->permalink ) ) {
|
95 |
+
$base_url = preg_replace( '#%postname%/?$#', '', $this->permalink );
|
96 |
+
}
|
97 |
+
|
98 |
+
return $base_url;
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Counts the number of given keywords used for other posts other than the given post_id.
|
103 |
+
*
|
104 |
+
* @return array The keyword and the associated posts that use it.
|
105 |
+
*/
|
106 |
+
private function get_focus_keyword_usage() {
|
107 |
+
$keyword = WPSEO_Meta::get_value( 'focuskw', $this->post->ID );
|
108 |
+
$usage = array( $keyword => $this->get_keyword_usage_for_current_post( $keyword ) );
|
109 |
+
|
110 |
+
if ( WPSEO_Utils::is_yoast_seo_premium() ) {
|
111 |
+
return $this->get_premium_keywords( $usage );
|
112 |
+
}
|
113 |
+
|
114 |
+
return $usage;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Retrieves the additional keywords from Premium, that are associated with the post.
|
119 |
+
*
|
120 |
+
* @param array $usage The original keyword usage for the main keyword.
|
121 |
+
*
|
122 |
+
* @return array The keyword usage, including the additional keywords.
|
123 |
+
*/
|
124 |
+
protected function get_premium_keywords( $usage ) {
|
125 |
+
$additional_keywords = json_decode( WPSEO_Meta::get_value( 'focuskeywords', $this->post->ID ), true );
|
126 |
+
|
127 |
+
if ( empty( $additional_keywords ) ) {
|
128 |
+
return $usage;
|
129 |
+
}
|
130 |
+
|
131 |
+
foreach ( $additional_keywords as $additional_keyword ) {
|
132 |
+
$keyword = $additional_keyword['keyword'];
|
133 |
+
|
134 |
+
$usage[ $keyword ] = $this->get_keyword_usage_for_current_post( $keyword );
|
135 |
+
}
|
136 |
+
|
137 |
+
return $usage;
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Gets the keyword usage for the current post and the specified keyword.
|
142 |
+
*
|
143 |
+
* @param string $keyword The keyword to check the usage of.
|
144 |
+
*
|
145 |
+
* @return array The post IDs which use the passed keyword.
|
146 |
+
*/
|
147 |
+
protected function get_keyword_usage_for_current_post( $keyword ) {
|
148 |
+
return WPSEO_Meta::keyword_usage( $keyword, $this->post->ID );
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Retrieves the title template.
|
153 |
+
*
|
154 |
+
* @return string
|
155 |
+
*/
|
156 |
+
private function get_title_template() {
|
157 |
+
return $this->get_template( 'title' );
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Retrieves the metadesc template.
|
162 |
+
*
|
163 |
+
* @return string
|
164 |
+
*/
|
165 |
+
private function get_metadesc_template() {
|
166 |
+
return $this->get_template( 'metadesc' );
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Retrieves a template.
|
171 |
+
*
|
172 |
+
* @param String $template_option_name The name of the option in which the template you want to get is saved.
|
173 |
+
*
|
174 |
+
* @return string
|
175 |
+
*/
|
176 |
+
private function get_template( $template_option_name ) {
|
177 |
+
$needed_option = $template_option_name . '-' . $this->post->post_type;
|
178 |
+
|
179 |
+
if ( WPSEO_Options::get( $needed_option, '' ) !== '' ) {
|
180 |
+
return WPSEO_Options::get( $needed_option );
|
181 |
+
}
|
182 |
+
|
183 |
+
return '';
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Determines the date to be displayed in the snippet preview
|
188 |
+
*
|
189 |
+
* @return string
|
190 |
+
*/
|
191 |
+
private function get_metadesc_date() {
|
192 |
+
$date = '';
|
193 |
+
|
194 |
+
if ( $this->is_show_date_enabled() ) {
|
195 |
+
$date = date_i18n( 'M j, Y', mysql2date( 'U', $this->post->post_date ) );
|
196 |
+
}
|
197 |
+
|
198 |
+
return $date;
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Returns whether or not showing the date in the snippet preview is enabled.
|
203 |
+
*
|
204 |
+
* @return bool
|
205 |
+
*/
|
206 |
+
private function is_show_date_enabled() {
|
207 |
+
$key = sprintf( 'showdate-%s', $this->post->post_type );
|
208 |
+
|
209 |
+
return WPSEO_Options::get( $key, true );
|
210 |
+
}
|
211 |
+
}
|
admin/formatter/class-term-metabox-formatter.php
ADDED
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Formatter
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class provides data for the term metabox by return its values for localization
|
8 |
+
*/
|
9 |
+
class WPSEO_Term_Metabox_Formatter implements WPSEO_Metabox_Formatter_Interface {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WP_Term|stdClass
|
13 |
+
*/
|
14 |
+
private $term;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var stdClass
|
18 |
+
*/
|
19 |
+
private $taxonomy;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var array Array with the WPSEO_Titles options.
|
23 |
+
*/
|
24 |
+
protected $options;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* WPSEO_Taxonomy_Scraper constructor.
|
28 |
+
*
|
29 |
+
* @param stdClass $taxonomy Taxonomy.
|
30 |
+
* @param WP_Term|stdClass $term Term.
|
31 |
+
*/
|
32 |
+
public function __construct( $taxonomy, $term ) {
|
33 |
+
$this->term = $term;
|
34 |
+
$this->taxonomy = $taxonomy;
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Returns the translated values.
|
39 |
+
*
|
40 |
+
* @return array
|
41 |
+
*/
|
42 |
+
public function get_values() {
|
43 |
+
$values = array();
|
44 |
+
|
45 |
+
// 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 kw doubles.
|
46 |
+
if ( is_object( $this->term ) && property_exists( $this->term, 'taxonomy' ) ) {
|
47 |
+
$values = array(
|
48 |
+
'search_url' => $this->search_url(),
|
49 |
+
'post_edit_url' => $this->edit_url(),
|
50 |
+
'base_url' => $this->base_url_for_js(),
|
51 |
+
'taxonomy' => $this->term->taxonomy,
|
52 |
+
'keyword_usage' => $this->get_focus_keyword_usage(),
|
53 |
+
'title_template' => $this->get_title_template(),
|
54 |
+
'metadesc_template' => $this->get_metadesc_template(),
|
55 |
+
);
|
56 |
+
}
|
57 |
+
|
58 |
+
return $values;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Returns the url to search for keyword for the taxonomy
|
63 |
+
*
|
64 |
+
* @return string
|
65 |
+
*/
|
66 |
+
private function search_url() {
|
67 |
+
return admin_url( 'edit-tags.php?taxonomy=' . $this->term->taxonomy . '&seo_kw_filter={keyword}' );
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Returns the url to edit the taxonomy
|
72 |
+
*
|
73 |
+
* @return string
|
74 |
+
*/
|
75 |
+
private function edit_url() {
|
76 |
+
global $wp_version;
|
77 |
+
$script_filename = version_compare( $wp_version, '4.5', '<' ) ? 'edit-tags' : 'term';
|
78 |
+
return admin_url( $script_filename . '.php?action=edit&taxonomy=' . $this->term->taxonomy . '&tag_ID={id}' );
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Returns a base URL for use in the JS, takes permalink structure into account
|
83 |
+
*
|
84 |
+
* @return string
|
85 |
+
*/
|
86 |
+
private function base_url_for_js() {
|
87 |
+
|
88 |
+
$base_url = home_url( '/', null );
|
89 |
+
if ( ! WPSEO_Options::get( 'stripcategorybase', false ) ) {
|
90 |
+
$base_url = trailingslashit( $base_url . $this->taxonomy->rewrite['slug'] );
|
91 |
+
}
|
92 |
+
|
93 |
+
return $base_url;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Counting the number of given keyword used for other term than given term_id
|
98 |
+
*
|
99 |
+
* @return array
|
100 |
+
*/
|
101 |
+
private function get_focus_keyword_usage() {
|
102 |
+
$focuskw = WPSEO_Taxonomy_Meta::get_term_meta( $this->term, $this->term->taxonomy, 'focuskw' );
|
103 |
+
|
104 |
+
return WPSEO_Taxonomy_Meta::get_keyword_usage( $focuskw, $this->term->term_id, $this->term->taxonomy );
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Retrieves the title template.
|
109 |
+
*
|
110 |
+
* @return string
|
111 |
+
*/
|
112 |
+
private function get_title_template() {
|
113 |
+
return $this->get_template( 'title' );
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Retrieves the metadesc template.
|
118 |
+
*
|
119 |
+
* @return string
|
120 |
+
*/
|
121 |
+
private function get_metadesc_template() {
|
122 |
+
return $this->get_template( 'metadesc' );
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Retrieves a template.
|
127 |
+
*
|
128 |
+
* @param String $template_option_name The name of the option in which the template you want to get is saved.
|
129 |
+
*
|
130 |
+
* @return string
|
131 |
+
*/
|
132 |
+
private function get_template( $template_option_name ) {
|
133 |
+
$needed_option = $template_option_name . '-tax-' . $this->term->taxonomy;
|
134 |
+
return WPSEO_Options::get( $needed_option, '' );
|
135 |
+
}
|
136 |
+
}
|
admin/formatter/interface-metabox-formatter.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Formatter
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Interface to force get_values
|
8 |
+
*/
|
9 |
+
interface WPSEO_Metabox_Formatter_Interface {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns formatter values.
|
13 |
+
*
|
14 |
+
* @return array
|
15 |
+
*/
|
16 |
+
public function get_values();
|
17 |
+
|
18 |
+
}
|
admin/google_search_console/class-gsc-ajax.php
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Ajax
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Ajax {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Setting the AJAX hooks for GSC
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
add_action( 'wp_ajax_wpseo_mark_fixed_crawl_issue', array( $this, 'ajax_mark_as_fixed' ) );
|
16 |
+
add_action( 'wp_ajax_wpseo_dismiss_gsc', array( $this, 'dismiss_notice' ) );
|
17 |
+
add_action( 'wp_ajax_wpseo_save_auth_code', array( $this, 'save_auth_code' ) );
|
18 |
+
add_action( 'wp_ajax_wpseo_clear_auth_code', array( $this, 'clear_auth_code' ) );
|
19 |
+
add_action( 'wp_ajax_wpseo_get_profiles', array( $this, 'get_profiles' ) );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* This method will be access by an AJAX request and will mark an issue as fixed.
|
24 |
+
*
|
25 |
+
* First it will do a request to the Google API
|
26 |
+
*/
|
27 |
+
public function ajax_mark_as_fixed() {
|
28 |
+
if ( $this->valid_nonce() ) {
|
29 |
+
$marker = new WPSEO_GSC_Marker( filter_input( INPUT_POST, 'url' ) );
|
30 |
+
|
31 |
+
wp_die( $marker->get_response() );
|
32 |
+
}
|
33 |
+
|
34 |
+
wp_die( 'false' );
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Handle the AJAX request and dismiss the GSC notice
|
39 |
+
*/
|
40 |
+
public function dismiss_notice() {
|
41 |
+
check_ajax_referer( 'dismiss-gsc-notice' );
|
42 |
+
|
43 |
+
update_user_meta( get_current_user_id(), 'wpseo_dismissed_gsc_notice', true );
|
44 |
+
|
45 |
+
wp_die( 'true' );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Saves the authorization code.
|
50 |
+
*/
|
51 |
+
public function save_auth_code() {
|
52 |
+
if ( ! $this->valid_nonce() ) {
|
53 |
+
wp_die( '0' );
|
54 |
+
}
|
55 |
+
|
56 |
+
// Validate the authorization.
|
57 |
+
$service = $this->get_service();
|
58 |
+
$authorization_code = filter_input( INPUT_POST, 'authorization' );
|
59 |
+
$is_authorization_valid = WPSEO_GSC_Settings::validate_authorization( $authorization_code, $service->get_client() );
|
60 |
+
if ( ! $is_authorization_valid ) {
|
61 |
+
wp_die( '0' );
|
62 |
+
}
|
63 |
+
|
64 |
+
$this->get_profiles();
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Clears all authorization data.
|
69 |
+
*/
|
70 |
+
public function clear_auth_code() {
|
71 |
+
if ( ! $this->valid_nonce() ) {
|
72 |
+
wp_die( '0' );
|
73 |
+
}
|
74 |
+
|
75 |
+
$service = $this->get_service();
|
76 |
+
|
77 |
+
WPSEO_GSC_Settings::clear_data( $service );
|
78 |
+
|
79 |
+
$this->get_profiles();
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Check if posted nonce is valid and return true if it is
|
84 |
+
*
|
85 |
+
* @return mixed
|
86 |
+
*/
|
87 |
+
private function valid_nonce() {
|
88 |
+
return wp_verify_nonce( filter_input( INPUT_POST, 'ajax_nonce' ), 'wpseo-gsc-ajax-security' );
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Returns an instance of the Google Search Console service.
|
93 |
+
*
|
94 |
+
* @return WPSEO_GSC_Service
|
95 |
+
*/
|
96 |
+
private function get_service() {
|
97 |
+
return new WPSEO_GSC_Service();
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Prints a JSON encoded string with the current profile config.
|
102 |
+
*/
|
103 |
+
private function get_profiles() {
|
104 |
+
$component = new WPSEO_Config_Component_Connect_Google_Search_Console();
|
105 |
+
|
106 |
+
wp_die( wp_json_encode( $component->get_data() ) );
|
107 |
+
}
|
108 |
+
}
|
admin/google_search_console/class-gsc-bulk-action.php
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Bulk_Action
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Bulk_Action {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Setting the listener on the bulk action post
|
13 |
+
*/
|
14 |
+
public function __construct() {
|
15 |
+
if ( wp_verify_nonce( filter_input( INPUT_POST, 'wpseo_gsc_nonce' ), 'wpseo_gsc_nonce' ) ) {
|
16 |
+
$this->handle_bulk_action();
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Handles the bulk action when there is an action posted
|
22 |
+
*/
|
23 |
+
private function handle_bulk_action() {
|
24 |
+
$bulk_action = $this->determine_bulk_action();
|
25 |
+
if ( $bulk_action !== false ) {
|
26 |
+
$this->run_bulk_action( $bulk_action, $this->posted_issues() );
|
27 |
+
|
28 |
+
wp_redirect( filter_input( INPUT_POST, '_wp_http_referer' ) );
|
29 |
+
exit;
|
30 |
+
}
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Determine which bulk action is selected and return that value
|
35 |
+
*
|
36 |
+
* @return string|bool
|
37 |
+
*/
|
38 |
+
private function determine_bulk_action() {
|
39 |
+
$action_inputs = array(
|
40 |
+
'action', // Bulk action select above the table.
|
41 |
+
'action2', // Bulk action select below the table.
|
42 |
+
);
|
43 |
+
|
44 |
+
foreach ( $action_inputs as $action_name ) {
|
45 |
+
$action = filter_input( INPUT_POST, $action_name );
|
46 |
+
if ( ! empty( $action ) && $action !== '-1' ) {
|
47 |
+
return $action;
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
return false;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Get the posted issues and return them
|
56 |
+
*
|
57 |
+
* @return array
|
58 |
+
*/
|
59 |
+
private function posted_issues() {
|
60 |
+
$issues = filter_input( INPUT_POST, 'wpseo_crawl_issues', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
|
61 |
+
if ( ! empty( $issues ) ) {
|
62 |
+
return $issues;
|
63 |
+
}
|
64 |
+
|
65 |
+
// Fallback if issues are empty.
|
66 |
+
return array();
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Runs the bulk action
|
71 |
+
*
|
72 |
+
* @param string $bulk_action Action type.
|
73 |
+
* @param array $issues Set of issues to apply to.
|
74 |
+
*/
|
75 |
+
private function run_bulk_action( $bulk_action, $issues ) {
|
76 |
+
switch ( $bulk_action ) {
|
77 |
+
case 'mark_as_fixed':
|
78 |
+
array_map( array( $this, 'action_mark_as_fixed' ), $issues );
|
79 |
+
|
80 |
+
break;
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Marks the issue as fixed
|
86 |
+
*
|
87 |
+
* @param string $issue Issue URL.
|
88 |
+
*
|
89 |
+
* @return string
|
90 |
+
*/
|
91 |
+
private function action_mark_as_fixed( $issue ) {
|
92 |
+
new WPSEO_GSC_Marker( $issue );
|
93 |
+
|
94 |
+
return $issue;
|
95 |
+
}
|
96 |
+
}
|
admin/google_search_console/class-gsc-category-filters.php
ADDED
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Category_Filters
|
8 |
+
*
|
9 |
+
* This class will get all category counts from the options and will parse the filter links that are displayed above
|
10 |
+
* the crawl issue tables.
|
11 |
+
*/
|
12 |
+
class WPSEO_GSC_Category_Filters {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* The counts per category
|
16 |
+
*
|
17 |
+
* @var array
|
18 |
+
*/
|
19 |
+
private $category_counts = array();
|
20 |
+
|
21 |
+
/**
|
22 |
+
* All the possible filters
|
23 |
+
*
|
24 |
+
* @var array
|
25 |
+
*/
|
26 |
+
private $filter_values = array();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* The current category
|
30 |
+
*
|
31 |
+
* @var string
|
32 |
+
*/
|
33 |
+
private $category;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Constructing this object
|
37 |
+
*
|
38 |
+
* Setting the hook to create the issues categories as the links
|
39 |
+
*
|
40 |
+
* @param array $platform_counts Set of issue counts by platform.
|
41 |
+
*/
|
42 |
+
public function __construct( array $platform_counts ) {
|
43 |
+
if ( ! empty( $platform_counts ) ) {
|
44 |
+
$this->set_counts( $platform_counts );
|
45 |
+
}
|
46 |
+
|
47 |
+
// Setting the filter values.
|
48 |
+
$this->set_filter_values();
|
49 |
+
|
50 |
+
$this->category = $this->get_current_category();
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Returns the value of the current category
|
55 |
+
*
|
56 |
+
* @return mixed|string
|
57 |
+
*/
|
58 |
+
public function get_category() {
|
59 |
+
return $this->category;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Returns the current filters as an array
|
64 |
+
*
|
65 |
+
* Only return categories with more than 0 issues
|
66 |
+
*
|
67 |
+
* @return array
|
68 |
+
*/
|
69 |
+
public function as_array() {
|
70 |
+
$new_views = array();
|
71 |
+
|
72 |
+
foreach ( $this->category_counts as $category_name => $category ) {
|
73 |
+
$new_views[] = $this->create_view_link( $category_name, $category['count'] );
|
74 |
+
}
|
75 |
+
|
76 |
+
return $new_views;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Getting the current view
|
81 |
+
*/
|
82 |
+
private function get_current_category() {
|
83 |
+
$current_category = filter_input( INPUT_GET, 'category' );
|
84 |
+
if ( ! empty( $current_category ) ) {
|
85 |
+
return $current_category;
|
86 |
+
}
|
87 |
+
|
88 |
+
// Just prevent redirect loops.
|
89 |
+
if ( ! empty( $this->category_counts ) ) {
|
90 |
+
$current_category = 'not_found';
|
91 |
+
if ( empty( $this->category_counts[ $current_category ] ) ) {
|
92 |
+
$current_category = key( $this->category_counts );
|
93 |
+
}
|
94 |
+
|
95 |
+
// Just redirect to set the category.
|
96 |
+
wp_redirect( add_query_arg( 'category', $current_category ) );
|
97 |
+
exit;
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Setting the view counts based on the saved data. The info will be used to display the category filters
|
103 |
+
*
|
104 |
+
* @param array $platform_counts Set of counts by platform.
|
105 |
+
*/
|
106 |
+
private function set_counts( array $platform_counts ) {
|
107 |
+
$this->category_counts = $this->parse_counts( $platform_counts );
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Setting the values for the filter
|
112 |
+
*/
|
113 |
+
private function set_filter_values() {
|
114 |
+
$this->set_filter_value( 'access_denied', __( 'Access denied', 'wordpress-seo' ), __( 'Server requires authentication or is blocking Googlebot from accessing the site.', 'wordpress-seo' ), __( 'Show information about errors in category \'Access Denied\'', 'wordpress-seo' ) );
|
115 |
+
$this->set_filter_value( 'faulty_redirects', __( 'Faulty redirects', 'wordpress-seo' ) );
|
116 |
+
$this->set_filter_value( 'not_followed', __( 'Not followed', 'wordpress-seo' ) );
|
117 |
+
$this->set_filter_value( 'not_found', __( 'Not found', 'wordpress-seo' ), __( 'URL points to a non-existent page.', 'wordpress-seo' ), __( 'Show information about errors in category \'Not Found\'', 'wordpress-seo' ) );
|
118 |
+
$this->set_filter_value( 'other', __( 'Other', 'wordpress-seo' ), __( 'Google was unable to crawl this URL due to an undetermined issue.', 'wordpress-seo' ), __( 'Show information about errors in category \'Other\'', 'wordpress-seo' ) );
|
119 |
+
/* Translators: %1$s: expands to '<code>robots.txt</code>'. */
|
120 |
+
$this->set_filter_value( 'roboted', __( 'Blocked', 'wordpress-seo' ), sprintf( __( 'Googlebot could access your site, but certain URLs are blocked for Googlebot in your %1$s file. This block could either be for all Googlebots or even specifically for Googlebot-mobile.', 'wordpress-seo' ), '<code>robots.txt</code>' ), __( 'Show information about errors in category \'Blocked\'', 'wordpress-seo' ) );
|
121 |
+
$this->set_filter_value( 'server_error', __( 'Server Error', 'wordpress-seo' ), __( 'Request timed out or site is blocking Google.', 'wordpress-seo' ), __( 'Show information about errors in category \'Server\'', 'wordpress-seo' ) );
|
122 |
+
$this->set_filter_value( 'soft_404', __( 'Soft 404', 'wordpress-seo' ), __( "The target URL doesn't exist, but your server is not returning a 404 (file not found) error.", 'wordpress-seo' ), __( 'Show information about errors in category \'Soft 404\'', 'wordpress-seo' ) );
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Add new filter value to the filter_values
|
127 |
+
*
|
128 |
+
* @param string $key Filter key.
|
129 |
+
* @param string $value Filter value.
|
130 |
+
* @param string $description Optional description string.
|
131 |
+
* @param string $help_button_text Optional help button text.
|
132 |
+
*/
|
133 |
+
private function set_filter_value( $key, $value, $description = '', $help_button_text = '' ) {
|
134 |
+
$this->filter_values[ $key ] = array(
|
135 |
+
'value' => $value,
|
136 |
+
'description' => $description,
|
137 |
+
'help-button' => $help_button_text,
|
138 |
+
);
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Creates a filter link
|
143 |
+
*
|
144 |
+
* @param string $category Issue type.
|
145 |
+
* @param integer $count Count for the type.
|
146 |
+
*
|
147 |
+
* @return string
|
148 |
+
*/
|
149 |
+
private function create_view_link( $category, $count ) {
|
150 |
+
$href = add_query_arg(
|
151 |
+
array(
|
152 |
+
'category' => $category,
|
153 |
+
'paged' => 1,
|
154 |
+
)
|
155 |
+
);
|
156 |
+
|
157 |
+
$class = 'gsc_category';
|
158 |
+
|
159 |
+
if ( $this->category === $category ) {
|
160 |
+
$class .= ' current';
|
161 |
+
}
|
162 |
+
|
163 |
+
$help_button = '';
|
164 |
+
$help_panel = '';
|
165 |
+
if ( $this->filter_values[ $category ]['description'] !== '' ) {
|
166 |
+
$help = new WPSEO_Admin_Help_Panel( $category, $this->filter_values[ $category ]['help-button'], $this->filter_values[ $category ]['description'], 'has-wrapper' );
|
167 |
+
$help_button = $help->get_button_html();
|
168 |
+
$help_panel = $help->get_panel_html();
|
169 |
+
}
|
170 |
+
|
171 |
+
return sprintf(
|
172 |
+
'<a href="%1$s" class="%2$s">%3$s</a> (<span id="gsc_count_%4$s">%5$s</span>) %6$s %7$s',
|
173 |
+
esc_attr( $href ),
|
174 |
+
$class,
|
175 |
+
$this->filter_values[ $category ]['value'],
|
176 |
+
$category,
|
177 |
+
$count,
|
178 |
+
$help_button,
|
179 |
+
$help_panel
|
180 |
+
);
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Parsing the category counts. When there are 0 issues for a specific category, just remove that one from the array
|
185 |
+
*
|
186 |
+
* @param array $category_counts Set of counts for categories.
|
187 |
+
*
|
188 |
+
* @return mixed
|
189 |
+
*/
|
190 |
+
private function parse_counts( $category_counts ) {
|
191 |
+
foreach ( $category_counts as $category_name => $category ) {
|
192 |
+
if ( $category['count'] === '0' ) {
|
193 |
+
unset( $category_counts[ $category_name ] );
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
return $category_counts;
|
198 |
+
}
|
199 |
+
}
|
admin/google_search_console/class-gsc-config.php
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Config
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Config {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var array
|
13 |
+
*/
|
14 |
+
public static $gsc = array(
|
15 |
+
'application_name' => 'Yoast SEO',
|
16 |
+
'client_id' => '395430892738-ushj8aced0cji2j4bkq6bda6felaigb9.apps.googleusercontent.com',
|
17 |
+
'client_secret' => 'c2kYgOwMhk1emWxQ3NaA8wOi',
|
18 |
+
'redirect_uri' => 'urn:ietf:wg:oauth:2.0:oob',
|
19 |
+
'scopes' => array( 'https://www.googleapis.com/auth/webmasters' ),
|
20 |
+
);
|
21 |
+
|
22 |
+
}
|
admin/google_search_console/class-gsc-count.php
ADDED
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Count
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Count {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string The name of the option containing the last checked timestamp.
|
13 |
+
*/
|
14 |
+
const OPTION_CI_LAST_FETCH = 'wpseo_gsc_last_fetch';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string The option name where the issues counts are saved.
|
18 |
+
*/
|
19 |
+
const OPTION_CI_COUNTS = 'wpseo_gsc_issues_counts';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var WPSEO_GSC_Service
|
23 |
+
*/
|
24 |
+
private $service;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Holder for the fetched issues from GSC
|
28 |
+
*
|
29 |
+
* @var array
|
30 |
+
*/
|
31 |
+
private $issues = array();
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Fetching the counts
|
35 |
+
*
|
36 |
+
* @param WPSEO_GSC_Service $service Service class instance.
|
37 |
+
*/
|
38 |
+
public function __construct( WPSEO_GSC_Service $service ) {
|
39 |
+
$this->service = $service;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Getting the counts for given platform and return them as an array
|
44 |
+
*
|
45 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
46 |
+
*
|
47 |
+
* @return array
|
48 |
+
*/
|
49 |
+
public function get_platform_counts( $platform ) {
|
50 |
+
$counts = $this->get_counts();
|
51 |
+
if ( array_key_exists( $platform, $counts ) ) {
|
52 |
+
return $counts[ $platform ];
|
53 |
+
}
|
54 |
+
|
55 |
+
return array();
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Return the fetched issues
|
60 |
+
*
|
61 |
+
* @return array
|
62 |
+
*/
|
63 |
+
public function get_issues() {
|
64 |
+
return $this->issues;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Listing the issues an gives them back as fetched issues
|
69 |
+
*
|
70 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
71 |
+
* @param string $category Issue category.
|
72 |
+
*/
|
73 |
+
public function list_issues( $platform, $category ) {
|
74 |
+
$counts = $this->get_counts();
|
75 |
+
|
76 |
+
if ( array_key_exists( $platform, $counts ) ) {
|
77 |
+
$counts[ $platform ] = $this->list_category_issues( $counts[ $platform ], $platform, $category );
|
78 |
+
|
79 |
+
// Write the new counts value.
|
80 |
+
$this->set_counts( $counts );
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Getting the counts for given platform and category.
|
86 |
+
*
|
87 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
88 |
+
* @param string $category Issue type.
|
89 |
+
*
|
90 |
+
* @return integer
|
91 |
+
*/
|
92 |
+
public function get_issue_count( $platform, $category ) {
|
93 |
+
$counts = $this->get_counts();
|
94 |
+
|
95 |
+
if ( ! empty( $counts[ $platform ][ $category ]['count'] ) ) {
|
96 |
+
return $counts[ $platform ][ $category ]['count'];
|
97 |
+
}
|
98 |
+
|
99 |
+
return 0;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Update the count of the issues
|
104 |
+
*
|
105 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
106 |
+
* @param string $category Issue type.
|
107 |
+
* @param integer $new_count Updated count.
|
108 |
+
*/
|
109 |
+
public function update_issue_count( $platform, $category, $new_count ) {
|
110 |
+
$counts = $this->get_counts();
|
111 |
+
|
112 |
+
if ( ! empty( $counts[ $platform ][ $category ] ) && is_array( $counts[ $platform ][ $category ] ) ) {
|
113 |
+
$counts[ $platform ][ $category ]['count'] = $new_count;
|
114 |
+
}
|
115 |
+
|
116 |
+
$this->set_counts( $counts );
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Fetching the counts from the GSC API
|
121 |
+
*/
|
122 |
+
public function fetch_counts() {
|
123 |
+
if ( WPSEO_GSC_Settings::get_profile() && $this->get_last_fetch() <= strtotime( '-12 hours' ) ) {
|
124 |
+
// Remove the timestamp.
|
125 |
+
$this->remove_last_fetch();
|
126 |
+
|
127 |
+
// Getting the counts and parse them.
|
128 |
+
$counts = $this->parse_counts( $this->service->get_crawl_issue_counts() );
|
129 |
+
|
130 |
+
// Fetching the counts by setting an option.
|
131 |
+
$this->set_counts( $counts );
|
132 |
+
|
133 |
+
// Saving the current timestamp.
|
134 |
+
$this->save_last_fetch();
|
135 |
+
}
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Parsing the received counts from the API and map the keys to plugin friendly values
|
140 |
+
*
|
141 |
+
* @param array $fetched_counts Set of retrieved counts.
|
142 |
+
*
|
143 |
+
* @return array
|
144 |
+
*/
|
145 |
+
private function parse_counts( array $fetched_counts ) {
|
146 |
+
$counts = array();
|
147 |
+
foreach ( $fetched_counts as $platform_name => $categories ) {
|
148 |
+
$new_platform = WPSEO_GSC_Mapper::platform_from_api( $platform_name );
|
149 |
+
|
150 |
+
foreach ( $categories as $category_name => $category ) {
|
151 |
+
$new_category = WPSEO_GSC_Mapper::category_from_api( $category_name );
|
152 |
+
|
153 |
+
$counts[ $new_platform ][ $new_category ] = $category;
|
154 |
+
}
|
155 |
+
}
|
156 |
+
|
157 |
+
return $counts;
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Listing the issues for current category.
|
162 |
+
*
|
163 |
+
* @param array $counts Set of counts.
|
164 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
165 |
+
* @param string $category Issue type.
|
166 |
+
*
|
167 |
+
* @return array
|
168 |
+
*/
|
169 |
+
private function list_category_issues( array $counts, $platform, $category ) {
|
170 |
+
// When the issues have to be fetched.
|
171 |
+
if ( array_key_exists( $category, $counts ) && $counts[ $category ]['count'] > 0 && $counts[ $category ]['last_fetch'] <= strtotime( '-12 hours' ) ) {
|
172 |
+
$issues = $this->service->fetch_category_issues( WPSEO_GSC_Mapper::platform_to_api( $platform ), WPSEO_GSC_Mapper::category_to_api( $category ) );
|
173 |
+
if ( ! empty( $issues ) ) {
|
174 |
+
$this->issues = $issues;
|
175 |
+
}
|
176 |
+
|
177 |
+
// Be sure the total count is correct.
|
178 |
+
$counts[ $category ]['count'] = count( $this->issues );
|
179 |
+
|
180 |
+
// Set last fetch.
|
181 |
+
$counts[ $category ]['last_fetch'] = time();
|
182 |
+
}
|
183 |
+
|
184 |
+
return $counts;
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Getting the counts from the options
|
189 |
+
*
|
190 |
+
* @return array
|
191 |
+
*/
|
192 |
+
private function get_counts() {
|
193 |
+
return get_option( self::OPTION_CI_COUNTS, array() );
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Fetching the counts from the service and store them in an option
|
198 |
+
*
|
199 |
+
* @param array $counts Set of counts.
|
200 |
+
*/
|
201 |
+
private function set_counts( array $counts ) {
|
202 |
+
update_option( self::OPTION_CI_COUNTS, $counts );
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Store the timestamp of when crawl errors were saved the last time.
|
207 |
+
*/
|
208 |
+
private function save_last_fetch() {
|
209 |
+
add_option( self::OPTION_CI_LAST_FETCH, time(), '', 'no' );
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Remove the last checked option
|
214 |
+
*/
|
215 |
+
private function remove_last_fetch() {
|
216 |
+
delete_option( self::OPTION_CI_LAST_FETCH );
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Get the timestamp of when the crawl errors were last saved
|
221 |
+
*
|
222 |
+
* @return int
|
223 |
+
*/
|
224 |
+
private function get_last_fetch() {
|
225 |
+
return get_option( self::OPTION_CI_LAST_FETCH, 0 );
|
226 |
+
}
|
227 |
+
}
|
admin/google_search_console/class-gsc-issue.php
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Issue
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Issue {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string
|
13 |
+
*/
|
14 |
+
private $url;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var DateTime
|
18 |
+
*/
|
19 |
+
private $first_detected;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var DateTime
|
23 |
+
*/
|
24 |
+
private $last_crawled;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
private $response_code;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Search Console issue class constructor.
|
33 |
+
*
|
34 |
+
* @param string $url URL of the issue.
|
35 |
+
* @param DateTime $first_detected Time of first discovery.
|
36 |
+
* @param DateTime $last_crawled Time of last crawl.
|
37 |
+
* @param string $response_code HTTP response code.
|
38 |
+
*/
|
39 |
+
public function __construct( $url, DateTime $first_detected, DateTime $last_crawled, $response_code ) {
|
40 |
+
$this->url = $url;
|
41 |
+
$this->first_detected = $first_detected;
|
42 |
+
$this->last_crawled = $last_crawled;
|
43 |
+
$this->response_code = $response_code;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Put the class properties in array
|
48 |
+
*
|
49 |
+
* @return array
|
50 |
+
*/
|
51 |
+
public function to_array() {
|
52 |
+
return array(
|
53 |
+
'url' => $this->url,
|
54 |
+
'first_detected' => $this->to_date_format( $this->first_detected ),
|
55 |
+
'first_detected_raw' => $this->to_timestamp( $this->first_detected ),
|
56 |
+
'last_crawled' => $this->to_date_format( $this->last_crawled ),
|
57 |
+
'last_crawled_raw' => $this->to_timestamp( $this->last_crawled ),
|
58 |
+
'response_code' => $this->response_code,
|
59 |
+
);
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Converting the date to a date format
|
64 |
+
*
|
65 |
+
* @param DateTime $date_to_convert Date instance.
|
66 |
+
* @param string $format Format string.
|
67 |
+
*
|
68 |
+
* @return string
|
69 |
+
*/
|
70 |
+
private function to_date_format( DateTime $date_to_convert, $format = '' ) {
|
71 |
+
|
72 |
+
if ( empty( $format ) ) {
|
73 |
+
$format = get_option( 'date_format' );
|
74 |
+
}
|
75 |
+
|
76 |
+
return date_i18n( $format, $date_to_convert->format( 'U' ) );
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Converting the date to a timestamp
|
81 |
+
*
|
82 |
+
* @param DateTime $date_to_convert Date object instance.
|
83 |
+
*
|
84 |
+
* @return string
|
85 |
+
*/
|
86 |
+
private function to_timestamp( DateTime $date_to_convert ) {
|
87 |
+
return $date_to_convert->format( 'U' );
|
88 |
+
}
|
89 |
+
}
|
admin/google_search_console/class-gsc-issues.php
ADDED
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Issues
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Issues {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string
|
13 |
+
*/
|
14 |
+
private $option_name = '';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* List of all current issues to compare with received issues
|
18 |
+
*
|
19 |
+
* @var array
|
20 |
+
*/
|
21 |
+
private $current_issues = array();
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Holder for all the issues
|
25 |
+
*
|
26 |
+
* @var array
|
27 |
+
*/
|
28 |
+
private $issues = array();
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Setting up the properties and fetching the current issues
|
32 |
+
*
|
33 |
+
* @param string $platform Platform type (desktop, mobile, feature phone).
|
34 |
+
* @param string $category Issues category.
|
35 |
+
* @param array|bool $fetched_issues Optional set of issues.
|
36 |
+
*/
|
37 |
+
public function __construct( $platform, $category, $fetched_issues = false ) {
|
38 |
+
$this->option_name = strtolower( 'wpseo-gsc-issues-' . $platform . '-' . $category );
|
39 |
+
$this->issues = $this->get_issues();
|
40 |
+
|
41 |
+
if ( ! empty( $fetched_issues ) && is_array( $fetched_issues ) ) {
|
42 |
+
$this->save_fetched_issues( $fetched_issues );
|
43 |
+
}
|
44 |
+
}
|
45 |
+
/**
|
46 |
+
* Getting the issues from the options.
|
47 |
+
*
|
48 |
+
* @return array
|
49 |
+
*/
|
50 |
+
public function get_issues() {
|
51 |
+
return get_option( $this->option_name, array() );
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Deleting the issue from the issues
|
56 |
+
*
|
57 |
+
* @param string $url URL to delete issues for.
|
58 |
+
*
|
59 |
+
* @return bool
|
60 |
+
*/
|
61 |
+
public function delete_issue( $url ) {
|
62 |
+
$target_issue = $this->get_issue_by_url( $url );
|
63 |
+
if ( $target_issue !== false ) {
|
64 |
+
unset( $this->issues[ $target_issue ] );
|
65 |
+
|
66 |
+
$this->save_issues( $this->issues );
|
67 |
+
|
68 |
+
return true;
|
69 |
+
}
|
70 |
+
|
71 |
+
return false;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Fetching the issues for current category and compare them with the already existing issues.
|
76 |
+
*
|
77 |
+
* @param array $fetched_issues Set of retrieved issues.
|
78 |
+
*/
|
79 |
+
private function save_fetched_issues( array $fetched_issues ) {
|
80 |
+
$this->set_current_issues();
|
81 |
+
|
82 |
+
$crawl_issues = $this->get_issues();
|
83 |
+
|
84 |
+
// Walk through the issues to do the comparison.
|
85 |
+
foreach ( $fetched_issues as $issue ) {
|
86 |
+
$this->issue_compare( $crawl_issues, $issue );
|
87 |
+
}
|
88 |
+
|
89 |
+
$this->save_issues( $crawl_issues );
|
90 |
+
|
91 |
+
// Refresh the value of $this->issues.
|
92 |
+
$this->issues = $this->get_issues();
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Comparing the issue with the list of current existing issues
|
97 |
+
*
|
98 |
+
* @param array $crawl_issues Set of issues by reference.
|
99 |
+
* @param stdClass $issue Issue object to check against the list.
|
100 |
+
*/
|
101 |
+
private function issue_compare( &$crawl_issues, $issue ) {
|
102 |
+
$issue->pageUrl = WPSEO_Utils::format_url( (string) $issue->pageUrl );
|
103 |
+
|
104 |
+
if ( ! in_array( $issue->pageUrl, $this->current_issues, true ) ) {
|
105 |
+
array_push(
|
106 |
+
$crawl_issues,
|
107 |
+
$this->get_issue( $this->create_issue( $issue ) )
|
108 |
+
);
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* The fetched issue from the API will be parsed as an WPSEO_Crawl_Issue object. After initializing the issue as an
|
114 |
+
* object, the object will be returned
|
115 |
+
*
|
116 |
+
* @param stdClass $issue Issue data object.
|
117 |
+
*
|
118 |
+
* @return WPSEO_GSC_Issue
|
119 |
+
*/
|
120 |
+
private function create_issue( $issue ) {
|
121 |
+
return new WPSEO_GSC_Issue(
|
122 |
+
$issue->pageUrl,
|
123 |
+
new DateTime( (string) $issue->first_detected ),
|
124 |
+
new DateTime( (string) $issue->last_crawled ),
|
125 |
+
(string) ( ! empty( $issue->responseCode ) ) ? $issue->responseCode : null
|
126 |
+
);
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Returns the crawl issue as an array.
|
131 |
+
*
|
132 |
+
* @param WPSEO_GSC_Issue $crawl_issue Issue object instance.
|
133 |
+
*
|
134 |
+
* @return array()
|
135 |
+
*/
|
136 |
+
private function get_issue( WPSEO_GSC_Issue $crawl_issue ) {
|
137 |
+
return $crawl_issue->to_array();
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Saving the issues to the options. The target option is base on current platform and category.
|
142 |
+
*
|
143 |
+
* @param array $issues Set of issues.
|
144 |
+
*/
|
145 |
+
private function save_issues( array $issues ) {
|
146 |
+
update_option( $this->option_name, $issues, false );
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Getting the issues from the options and get only the URL out of it. This is because there will be a comparison
|
151 |
+
* with the issues from the API.
|
152 |
+
*/
|
153 |
+
private function set_current_issues() {
|
154 |
+
if ( ! empty( $this->issues ) ) {
|
155 |
+
$this->current_issues = wp_list_pluck( $this->issues, 'url' );
|
156 |
+
}
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Search in the issues for the given $url
|
161 |
+
*
|
162 |
+
* @param string $url Issue URL to search for.
|
163 |
+
*
|
164 |
+
* @return int|string
|
165 |
+
*/
|
166 |
+
private function get_issue_by_url( $url ) {
|
167 |
+
foreach ( $this->issues as $key => $issue ) {
|
168 |
+
if ( $url === $issue['url'] ) {
|
169 |
+
return $key;
|
170 |
+
}
|
171 |
+
}
|
172 |
+
|
173 |
+
return false;
|
174 |
+
}
|
175 |
+
}
|
admin/google_search_console/class-gsc-mapper.php
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Mapper
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Mapper {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* The platforms which can be mapped.
|
13 |
+
*
|
14 |
+
* @var array
|
15 |
+
*/
|
16 |
+
private static $platforms = array(
|
17 |
+
'web' => 'web',
|
18 |
+
'mobile' => 'mobile',
|
19 |
+
'smartphone_only' => 'smartphoneOnly',
|
20 |
+
'settings' => 'settings', // This one is basicly not a platform, but a tab.
|
21 |
+
);
|
22 |
+
|
23 |
+
/**
|
24 |
+
* The categories which can be mapped
|
25 |
+
*
|
26 |
+
* @var array
|
27 |
+
*/
|
28 |
+
private static $categories = array(
|
29 |
+
'access_denied' => 'authPermissions',
|
30 |
+
'faulty_redirects' => 'manyToOneRedirect',
|
31 |
+
'not_followed' => 'notFollowed',
|
32 |
+
'not_found' => 'notFound',
|
33 |
+
'other' => 'other',
|
34 |
+
'roboted' => 'roboted',
|
35 |
+
'server_error' => 'serverError',
|
36 |
+
'soft_404' => 'soft404',
|
37 |
+
);
|
38 |
+
|
39 |
+
/**
|
40 |
+
* If there is no platform, just get the first key out of the array and redirect to it.
|
41 |
+
*
|
42 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
43 |
+
*
|
44 |
+
* @return mixed
|
45 |
+
*/
|
46 |
+
public static function get_current_platform( $platform ) {
|
47 |
+
$current_platform = filter_input( INPUT_GET, $platform );
|
48 |
+
if ( ! empty( $current_platform ) ) {
|
49 |
+
return $current_platform;
|
50 |
+
}
|
51 |
+
|
52 |
+
wp_redirect( add_query_arg( $platform, key( self::$platforms ) ) );
|
53 |
+
exit;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Mapping the platform
|
58 |
+
*
|
59 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
60 |
+
*
|
61 |
+
* @return mixed
|
62 |
+
*/
|
63 |
+
public static function platform_to_api( $platform ) {
|
64 |
+
if ( ! empty( $platform ) && array_key_exists( $platform, self::$platforms ) ) {
|
65 |
+
return self::$platforms[ $platform ];
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Mapping the given platform by value and return its key
|
71 |
+
*
|
72 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
73 |
+
*
|
74 |
+
* @return string
|
75 |
+
*/
|
76 |
+
public static function platform_from_api( $platform ) {
|
77 |
+
if ( ! empty( $platform ) ) {
|
78 |
+
$platform = array_search( $platform, self::$platforms, true );
|
79 |
+
if ( $platform !== false ) {
|
80 |
+
return $platform;
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
return $platform;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Mapping the given category by searching for its key.
|
89 |
+
*
|
90 |
+
* @param string $category Issue type.
|
91 |
+
*
|
92 |
+
* @return mixed
|
93 |
+
*/
|
94 |
+
public static function category_to_api( $category ) {
|
95 |
+
if ( ! empty( $category ) && array_key_exists( $category, self::$categories ) ) {
|
96 |
+
return self::$categories[ $category ];
|
97 |
+
}
|
98 |
+
|
99 |
+
return $category;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Mapping the given category by value and return its key
|
104 |
+
*
|
105 |
+
* @param string $category Issue type.
|
106 |
+
*
|
107 |
+
* @return string
|
108 |
+
*/
|
109 |
+
public static function category_from_api( $category ) {
|
110 |
+
if ( ! empty( $category ) ) {
|
111 |
+
$category = array_search( $category, self::$categories, true );
|
112 |
+
if ( $category !== false ) {
|
113 |
+
return $category;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
return $category;
|
118 |
+
}
|
119 |
+
}
|
admin/google_search_console/class-gsc-marker.php
ADDED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Marker
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Marker {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WPSEO_GSC_Issues
|
13 |
+
*/
|
14 |
+
private $crawl_issues;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
private $url = '';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var string
|
23 |
+
*/
|
24 |
+
private $platform;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
private $category;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var string
|
33 |
+
*/
|
34 |
+
private $result;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Setting up the needed API libs and return the result
|
38 |
+
*
|
39 |
+
* If param URL is given, the request is performed by a bulk action
|
40 |
+
*
|
41 |
+
* @param string $url Optional URL.
|
42 |
+
*/
|
43 |
+
public function __construct( $url = '' ) {
|
44 |
+
$this->url = $url;
|
45 |
+
$this->result = $this->get_result();
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Getting the response for the AJAX request
|
50 |
+
*
|
51 |
+
* @return string
|
52 |
+
*/
|
53 |
+
public function get_response() {
|
54 |
+
return $this->result;
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Setting the result, this method will check if current
|
59 |
+
*
|
60 |
+
* @return string
|
61 |
+
*/
|
62 |
+
private function get_result() {
|
63 |
+
if ( $this->can_be_marked_as_fixed() ) {
|
64 |
+
$service = new WPSEO_GSC_Service( WPSEO_GSC_Settings::get_profile() );
|
65 |
+
|
66 |
+
if ( $this->set_crawl_issues() && $this->send_mark_as_fixed( $service ) && $this->delete_crawl_issue() ) {
|
67 |
+
$this->update_issue_count( $service );
|
68 |
+
|
69 |
+
return 'true';
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
return 'false';
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Check if request is valid by verifying the posted nonce and return the URL if this one is set
|
78 |
+
*
|
79 |
+
* @return bool|string
|
80 |
+
*/
|
81 |
+
private function can_be_marked_as_fixed() {
|
82 |
+
if ( $this->url !== '' ) {
|
83 |
+
return $this->url;
|
84 |
+
}
|
85 |
+
|
86 |
+
return false;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Storing the data belonging to the current issue, this data is needed in the 'mark as fixed' flow
|
91 |
+
*
|
92 |
+
* @return bool
|
93 |
+
*/
|
94 |
+
private function set_crawl_issues() {
|
95 |
+
$this->platform = filter_input( INPUT_POST, 'platform' );
|
96 |
+
$this->category = filter_input( INPUT_POST, 'category' );
|
97 |
+
if ( $this->platform && $this->category ) {
|
98 |
+
$this->crawl_issues = new WPSEO_GSC_Issues( $this->platform, $this->category );
|
99 |
+
|
100 |
+
return true;
|
101 |
+
}
|
102 |
+
|
103 |
+
return false;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Sending a request to the Google Search Console API to let them know we marked an issue as fixed.
|
108 |
+
*
|
109 |
+
* @param WPSEO_GSC_Service $service Service object instance.
|
110 |
+
*
|
111 |
+
* @return bool
|
112 |
+
*/
|
113 |
+
private function send_mark_as_fixed( WPSEO_GSC_Service $service ) {
|
114 |
+
return $service->mark_as_fixed( $this->url, $this->platform, $this->category );
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Delete the crawl issue from the database
|
119 |
+
*
|
120 |
+
* @return bool
|
121 |
+
*/
|
122 |
+
private function delete_crawl_issue() {
|
123 |
+
return $this->crawl_issues->delete_issue( $this->url );
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Getting the counts for current platform - category combination and update the score of it.
|
128 |
+
*
|
129 |
+
* @param WPSEO_GSC_Service $service Service object instance.
|
130 |
+
*/
|
131 |
+
private function update_issue_count( WPSEO_GSC_Service $service ) {
|
132 |
+
$counts = new WPSEO_GSC_Count( $service );
|
133 |
+
|
134 |
+
// Get the issues.
|
135 |
+
$total_issues = $counts->get_issue_count( $this->platform, $this->category );
|
136 |
+
|
137 |
+
// Lower the current count with 1.
|
138 |
+
$total_issues = ( $total_issues - 1 );
|
139 |
+
|
140 |
+
// And update the count.
|
141 |
+
$counts->update_issue_count( $this->platform, $this->category, $total_issues );
|
142 |
+
}
|
143 |
+
}
|
admin/google_search_console/class-gsc-modal.php
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the Google Search Console modal.
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Modal {
|
10 |
+
|
11 |
+
/** @var string */
|
12 |
+
protected $view;
|
13 |
+
|
14 |
+
/** @var int */
|
15 |
+
protected $height;
|
16 |
+
|
17 |
+
/** @var array */
|
18 |
+
protected $view_vars;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Sets the required attributes for this object.
|
22 |
+
*
|
23 |
+
* @param string $view The file with the view content.
|
24 |
+
* @param int $height The height that the modal will get.
|
25 |
+
* @param array $view_vars The attributes to use in the view.
|
26 |
+
*/
|
27 |
+
public function __construct( $view, $height, array $view_vars = array() ) {
|
28 |
+
$this->view = $view;
|
29 |
+
$this->height = $height;
|
30 |
+
$this->view_vars = $view_vars;
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Returns the height of the modal.
|
35 |
+
*
|
36 |
+
* @return int The set height.
|
37 |
+
*/
|
38 |
+
public function get_height() {
|
39 |
+
return $this->height;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Loads the view of the modal.
|
44 |
+
*
|
45 |
+
* @param string $unique_id An unique identifier for the modal.
|
46 |
+
*/
|
47 |
+
public function load_view( $unique_id ) {
|
48 |
+
extract( $this->view_vars );
|
49 |
+
|
50 |
+
echo '<div id="' . esc_attr( 'redirect-' . $unique_id ) . '" class="hidden">';
|
51 |
+
echo '<div class="form-wrap wpseo_content_wrapper">';
|
52 |
+
require $this->view;
|
53 |
+
echo '</div>';
|
54 |
+
echo '</div>';
|
55 |
+
}
|
56 |
+
}
|
admin/google_search_console/class-gsc-platform-tabs.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Platform_Tabs
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Platform_Tabs {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string
|
13 |
+
*/
|
14 |
+
private $current_tab;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Return the tabs as a string
|
18 |
+
*
|
19 |
+
* @return string
|
20 |
+
*/
|
21 |
+
public function __toString() {
|
22 |
+
return $this->platform_tabs();
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Getting the current_tab
|
27 |
+
*
|
28 |
+
* @return string
|
29 |
+
*/
|
30 |
+
public function current_tab() {
|
31 |
+
return $this->current_tab;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Loops through the array with all the platforms and convert it into an array
|
36 |
+
*
|
37 |
+
* @return string
|
38 |
+
*/
|
39 |
+
private function platform_tabs() {
|
40 |
+
$tabs = array( 'settings' => __( 'Settings', 'wordpress-seo' ) );
|
41 |
+
|
42 |
+
$platforms = array(
|
43 |
+
'web' => __( 'Desktop', 'wordpress-seo' ),
|
44 |
+
'smartphone_only' => __( 'Smartphone', 'wordpress-seo' ),
|
45 |
+
'mobile' => __( 'Feature phone', 'wordpress-seo' ),
|
46 |
+
);
|
47 |
+
|
48 |
+
if ( WPSEO_GSC_Settings::get_profile() !== '' ) {
|
49 |
+
$tabs = array_merge( $platforms, $tabs );
|
50 |
+
}
|
51 |
+
|
52 |
+
$admin_link = admin_url( 'admin.php?page=wpseo_search_console&tab=' );
|
53 |
+
|
54 |
+
$this->set_current_tab( $tabs );
|
55 |
+
|
56 |
+
$return = '';
|
57 |
+
|
58 |
+
foreach ( $tabs as $platform_target => $platform_value ) {
|
59 |
+
$return .= $this->platform_tab( $platform_target, $platform_value, $admin_link );
|
60 |
+
}
|
61 |
+
|
62 |
+
return $return;
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Setting the current tab
|
67 |
+
*
|
68 |
+
* @param array $platforms Set of platforms (desktop, mobile, feature phone).
|
69 |
+
*/
|
70 |
+
private function set_current_tab( array $platforms ) {
|
71 |
+
$this->current_tab = key( $platforms );
|
72 |
+
$current_platform = filter_input( INPUT_GET, 'tab' );
|
73 |
+
if ( ! empty( $current_platform ) && isset( $platforms[ $current_platform ] ) ) {
|
74 |
+
$this->current_tab = $current_platform;
|
75 |
+
}
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Parses the tab
|
80 |
+
*
|
81 |
+
* @param string $platform_target Platform (desktop, mobile, feature phone).
|
82 |
+
* @param string $platform_value Link anchor.
|
83 |
+
* @param string $admin_link Link URL admin base.
|
84 |
+
*
|
85 |
+
* @return string
|
86 |
+
*/
|
87 |
+
private function platform_tab( $platform_target, $platform_value, $admin_link ) {
|
88 |
+
$active = '';
|
89 |
+
if ( $this->current_tab === $platform_target ) {
|
90 |
+
$active = ' nav-tab-active';
|
91 |
+
}
|
92 |
+
|
93 |
+
return '<a class="nav-tab' . esc_attr( $active ) . '" id="' . esc_attr( $platform_target . '-tab' ) . '" href="' . esc_url( $admin_link . $platform_target ) . '">' . esc_html( $platform_value ) . '</a>';
|
94 |
+
}
|
95 |
+
}
|
admin/google_search_console/class-gsc-service.php
ADDED
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Service
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Service {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var Yoast_Api_Google_Client
|
13 |
+
*/
|
14 |
+
private $client;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
private $profile;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Search Console service constructor.
|
23 |
+
*
|
24 |
+
* @param string $profile Profile name.
|
25 |
+
*/
|
26 |
+
public function __construct( $profile = '' ) {
|
27 |
+
$this->profile = $profile;
|
28 |
+
|
29 |
+
$this->set_client();
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Returns the client
|
34 |
+
*
|
35 |
+
* @return Yoast_Api_Google_Client
|
36 |
+
*/
|
37 |
+
public function get_client() {
|
38 |
+
return $this->client;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Removes the option and calls the clients clear_data method to clear that one as well
|
43 |
+
*/
|
44 |
+
public function clear_data() {
|
45 |
+
// Clear client data.
|
46 |
+
$this->client->clear_data();
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Get all sites that are registered in the GSC panel
|
51 |
+
*
|
52 |
+
* @return array
|
53 |
+
*/
|
54 |
+
public function get_sites() {
|
55 |
+
$sites = array();
|
56 |
+
|
57 |
+
$response_json = $this->client->do_request( 'sites', true );
|
58 |
+
|
59 |
+
// Do list sites request.
|
60 |
+
if ( ! empty( $response_json->siteEntry ) ) {
|
61 |
+
foreach ( $response_json->siteEntry as $entry ) {
|
62 |
+
$sites[ str_ireplace( 'sites/', '', (string) $entry->siteUrl ) ] = (string) $entry->siteUrl;
|
63 |
+
}
|
64 |
+
|
65 |
+
// Sorting the retrieved sites.
|
66 |
+
asort( $sites );
|
67 |
+
}
|
68 |
+
|
69 |
+
return $sites;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Get crawl issues
|
74 |
+
*
|
75 |
+
* @return array
|
76 |
+
*/
|
77 |
+
public function get_crawl_issue_counts() {
|
78 |
+
// Setup crawl error list.
|
79 |
+
$crawl_error_counts = $this->get_crawl_error_counts( $this->profile );
|
80 |
+
|
81 |
+
$return = array();
|
82 |
+
// Ignore coding standards for object properties.
|
83 |
+
if ( ! empty( $crawl_error_counts->countPerTypes ) ) {
|
84 |
+
foreach ( $crawl_error_counts->countPerTypes as $category ) {
|
85 |
+
$return[ $category->platform ][ $category->category ] = array(
|
86 |
+
'count' => $category->entries[0]->count,
|
87 |
+
'last_fetch' => null,
|
88 |
+
);
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
return $return;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Sending request to mark issue as fixed
|
97 |
+
*
|
98 |
+
* @param string $url Issue URL.
|
99 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
100 |
+
* @param string $category Issue type.
|
101 |
+
*
|
102 |
+
* @return bool
|
103 |
+
*/
|
104 |
+
public function mark_as_fixed( $url, $platform, $category ) {
|
105 |
+
$response = $this->client->do_request( 'sites/' . urlencode( $this->profile ) . '/urlCrawlErrorsSamples/' . urlencode( ltrim( $url, '/' ) ) . '?category=' . WPSEO_GSC_Mapper::category_to_api( $category ) . '&platform=' . WPSEO_GSC_Mapper::platform_to_api( $platform ) . '', false, 'DELETE' );
|
106 |
+
return ( $response->getResponseHttpCode() === 204 );
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Fetching the issues from the GSC API
|
111 |
+
*
|
112 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
113 |
+
* @param string $category Issue type.
|
114 |
+
*
|
115 |
+
* @return mixed
|
116 |
+
*/
|
117 |
+
public function fetch_category_issues( $platform, $category ) {
|
118 |
+
$issues = $this->client->do_request(
|
119 |
+
'sites/' . urlencode( $this->profile ) . '/urlCrawlErrorsSamples?category=' . $category . '&platform=' . $platform,
|
120 |
+
true
|
121 |
+
);
|
122 |
+
|
123 |
+
if ( ! empty( $issues->urlCrawlErrorSample ) ) {
|
124 |
+
return $issues->urlCrawlErrorSample;
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Setting the GSC client
|
130 |
+
*/
|
131 |
+
private function set_client() {
|
132 |
+
try {
|
133 |
+
new Yoast_Api_Libs( '2.0' );
|
134 |
+
}
|
135 |
+
catch ( Exception $exception ) {
|
136 |
+
if ( $exception->getMessage() === 'required_version' ) {
|
137 |
+
$this->incompatible_api_libs(
|
138 |
+
__( 'Yoast plugins share some code between them to make your site faster. As a result of that, we need all Yoast plugins to be up to date. We\'ve detected this isn\'t the case, so please update the Yoast plugins that aren\'t up to date yet.', 'wordpress-seo' )
|
139 |
+
);
|
140 |
+
}
|
141 |
+
}
|
142 |
+
|
143 |
+
if ( class_exists( 'Yoast_Api_Google_Client' ) === false ) {
|
144 |
+
$this->incompatible_api_libs(
|
145 |
+
sprintf(
|
146 |
+
/* translators: %1$s expands to Yoast SEO, %2$s expands to Google Analytics by Yoast */
|
147 |
+
__(
|
148 |
+
'%1$s detected you’re using a version of %2$s which is not compatible with %1$s. Please update %2$s to the latest version to use this feature.',
|
149 |
+
'wordpress-seo'
|
150 |
+
),
|
151 |
+
'Yoast SEO',
|
152 |
+
'Google Analytics by Yoast'
|
153 |
+
)
|
154 |
+
);
|
155 |
+
|
156 |
+
wp_redirect( admin_url( 'admin.php?page=' . WPSEO_Admin::PAGE_IDENTIFIER ) );
|
157 |
+
exit;
|
158 |
+
}
|
159 |
+
|
160 |
+
$this->client = new Yoast_Api_Google_Client( WPSEO_GSC_Config::$gsc, 'wpseo-gsc', 'https://www.googleapis.com/webmasters/v3/' );
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Adding notice that the api libs has the wrong version
|
165 |
+
*
|
166 |
+
* @param string $notice Message string.
|
167 |
+
*/
|
168 |
+
private function incompatible_api_libs( $notice ) {
|
169 |
+
Yoast_Notification_Center::get()->add_notification(
|
170 |
+
new Yoast_Notification( $notice, array( 'type' => Yoast_Notification::ERROR ) )
|
171 |
+
);
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Getting the crawl error counts
|
176 |
+
*
|
177 |
+
* @param string $profile Profile name string.
|
178 |
+
*
|
179 |
+
* @return object|bool
|
180 |
+
*/
|
181 |
+
private function get_crawl_error_counts( $profile ) {
|
182 |
+
$crawl_error_counts = $this->client->do_request(
|
183 |
+
'sites/' . urlencode( $profile ) . '/urlCrawlErrorsCounts/query',
|
184 |
+
true
|
185 |
+
);
|
186 |
+
|
187 |
+
if ( ! empty( $crawl_error_counts ) ) {
|
188 |
+
return $crawl_error_counts;
|
189 |
+
}
|
190 |
+
|
191 |
+
return false;
|
192 |
+
}
|
193 |
+
}
|
admin/google_search_console/class-gsc-settings.php
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC_Settings
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC_Settings {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Clear all data from the database
|
13 |
+
*
|
14 |
+
* @param WPSEO_GSC_Service $service Service class instance.
|
15 |
+
*/
|
16 |
+
public static function clear_data( WPSEO_GSC_Service $service ) {
|
17 |
+
// Remove issue and issue counts.
|
18 |
+
self::remove();
|
19 |
+
|
20 |
+
// Removes the GSC options.
|
21 |
+
self::remove_gsc_option();
|
22 |
+
|
23 |
+
// Clear the service data.
|
24 |
+
$service->clear_data();
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Reloading all the issues
|
29 |
+
*/
|
30 |
+
public static function reload_issues() {
|
31 |
+
// Remove issue and issue counts.
|
32 |
+
self::remove();
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* When authorization is successful return true, otherwise false
|
37 |
+
*
|
38 |
+
* @param string $authorization_code Code to validate.
|
39 |
+
* @param Yoast_Api_Google_Client $client Client object instance.
|
40 |
+
*
|
41 |
+
* @return bool
|
42 |
+
*/
|
43 |
+
public static function validate_authorization( $authorization_code, Yoast_Api_Google_Client $client ) {
|
44 |
+
return ( $authorization_code !== '' && $client->authenticate_client( $authorization_code ) );
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Get the GSC profile
|
49 |
+
*
|
50 |
+
* @return string
|
51 |
+
*/
|
52 |
+
public static function get_profile() {
|
53 |
+
// Get option.
|
54 |
+
$option = get_option( WPSEO_GSC::OPTION_WPSEO_GSC, array( 'profile' => '' ) );
|
55 |
+
|
56 |
+
// Set the profile.
|
57 |
+
$profile = '';
|
58 |
+
if ( ! empty( $option['profile'] ) ) {
|
59 |
+
$profile = $option['profile'];
|
60 |
+
}
|
61 |
+
|
62 |
+
// Return the profile.
|
63 |
+
return trim( $profile, '/' );
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Removes the issue counts and all the issues from the options
|
68 |
+
*/
|
69 |
+
private static function remove() {
|
70 |
+
// Remove the issue counts from the options.
|
71 |
+
self::remove_issue_counts();
|
72 |
+
|
73 |
+
// Removing all issues from the database.
|
74 |
+
self::remove_issues();
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Remove the issue counts
|
79 |
+
*/
|
80 |
+
private static function remove_issue_counts() {
|
81 |
+
// Remove the options which are holding the counts.
|
82 |
+
delete_option( WPSEO_GSC_Count::OPTION_CI_COUNTS );
|
83 |
+
delete_option( WPSEO_GSC_Count::OPTION_CI_LAST_FETCH );
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Delete the issues and their meta data from the database
|
88 |
+
*/
|
89 |
+
private static function remove_issues() {
|
90 |
+
global $wpdb;
|
91 |
+
|
92 |
+
// Remove local crawl issues by running a delete query.
|
93 |
+
$wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE 'wpseo-gsc-issues-%'" );
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Removes the options for GSC
|
98 |
+
*/
|
99 |
+
private static function remove_gsc_option() {
|
100 |
+
delete_option( WPSEO_GSC::OPTION_WPSEO_GSC );
|
101 |
+
}
|
102 |
+
}
|
admin/google_search_console/class-gsc-table.php
ADDED
@@ -0,0 +1,368 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! class_exists( 'WP_List_Table' ) ) {
|
7 |
+
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
|
8 |
+
}
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class WPSEO_GSC_Table
|
12 |
+
*/
|
13 |
+
class WPSEO_GSC_Table extends WP_List_Table {
|
14 |
+
|
15 |
+
const FREE_MODAL_HEIGHT = 140;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var string
|
19 |
+
*/
|
20 |
+
private $search_string;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var array
|
24 |
+
*/
|
25 |
+
protected $_column_headers;
|
26 |
+
|
27 |
+
/**
|
28 |
+
* The category that is displayed
|
29 |
+
*
|
30 |
+
* @var mixed|string
|
31 |
+
*/
|
32 |
+
private $current_view;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @var integer
|
36 |
+
*/
|
37 |
+
private $per_page = 50;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* @var integer
|
41 |
+
*/
|
42 |
+
private $current_page = 1;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Search Console table class constructor (subclasses list table).
|
46 |
+
*
|
47 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
48 |
+
* @param string $category Type of the issues.
|
49 |
+
* @param array $items Set of the issues to display.
|
50 |
+
*/
|
51 |
+
public function __construct( $platform, $category, array $items ) {
|
52 |
+
parent::__construct();
|
53 |
+
|
54 |
+
// Adding the thickbox.
|
55 |
+
add_thickbox();
|
56 |
+
|
57 |
+
// Set search string.
|
58 |
+
$search_string = filter_input( INPUT_GET, 's' );
|
59 |
+
|
60 |
+
if ( $search_string !== '' ) {
|
61 |
+
$this->search_string = $search_string;
|
62 |
+
}
|
63 |
+
|
64 |
+
$this->current_view = $category;
|
65 |
+
|
66 |
+
// Set the crawl issue source.
|
67 |
+
$this->show_fields( $platform );
|
68 |
+
|
69 |
+
$this->items = $items;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Getting the screen id from this table
|
74 |
+
*
|
75 |
+
* @return string
|
76 |
+
*/
|
77 |
+
public function get_screen_id() {
|
78 |
+
return $this->screen->id;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Setup the table variables, fetch the items from the database, search, sort and format the items.
|
83 |
+
*/
|
84 |
+
public function prepare_items() {
|
85 |
+
// Get variables needed for pagination.
|
86 |
+
$this->per_page = $this->get_items_per_page( 'errors_per_page', $this->per_page );
|
87 |
+
$paged = filter_input( INPUT_GET, 'paged' );
|
88 |
+
$this->current_page = intval( ( ! empty( $paged ) ) ? $paged : 1 );
|
89 |
+
|
90 |
+
$this->setup_columns();
|
91 |
+
$this->views();
|
92 |
+
$this->parse_items();
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Set the table columns
|
97 |
+
*
|
98 |
+
* @return array
|
99 |
+
*/
|
100 |
+
public function get_columns() {
|
101 |
+
$columns = array(
|
102 |
+
'cb' => '<input type="checkbox" />',
|
103 |
+
'url' => __( 'URL', 'wordpress-seo' ),
|
104 |
+
'last_crawled' => __( 'Last crawled', 'wordpress-seo' ),
|
105 |
+
'first_detected' => __( 'First detected', 'wordpress-seo' ),
|
106 |
+
'response_code' => __( 'Response code', 'wordpress-seo' ),
|
107 |
+
);
|
108 |
+
|
109 |
+
return $columns;
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Return the columns that are sortable
|
114 |
+
*
|
115 |
+
* @return array
|
116 |
+
*/
|
117 |
+
protected function get_sortable_columns() {
|
118 |
+
$sortable_columns = array(
|
119 |
+
'url' => array( 'url', false ),
|
120 |
+
'last_crawled' => array( 'last_crawled', false ),
|
121 |
+
'first_detected' => array( 'first_detected', false ),
|
122 |
+
'response_code' => array( 'response_code', false ),
|
123 |
+
);
|
124 |
+
|
125 |
+
return $sortable_columns;
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Return available bulk actions
|
130 |
+
*
|
131 |
+
* @return array
|
132 |
+
*/
|
133 |
+
protected function get_bulk_actions() {
|
134 |
+
return array(
|
135 |
+
'mark_as_fixed' => __( 'Mark as fixed', 'wordpress-seo' ),
|
136 |
+
);
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Default method to display a column
|
141 |
+
*
|
142 |
+
* @param array $item Data array.
|
143 |
+
* @param string $column_name Column name key.
|
144 |
+
*
|
145 |
+
* @return mixed
|
146 |
+
*/
|
147 |
+
protected function column_default( $item, $column_name ) {
|
148 |
+
return $item[ $column_name ];
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Checkbox column
|
153 |
+
*
|
154 |
+
* @param array $item Item data array.
|
155 |
+
*
|
156 |
+
* @return string
|
157 |
+
*/
|
158 |
+
protected function column_cb( $item ) {
|
159 |
+
return sprintf(
|
160 |
+
'<input type="checkbox" name="wpseo_crawl_issues[]" id="cb-%1$s" value="%2$s" /><label for="cb-%1$s" class="screen-reader-text">%3$s</label>',
|
161 |
+
md5( $item['url'] ),
|
162 |
+
$item['url'],
|
163 |
+
__( 'Select redirect', 'wordpress-seo' )
|
164 |
+
);
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Formatting the output of the column last crawled into a dateformat
|
169 |
+
*
|
170 |
+
* @param array $item Item data array.
|
171 |
+
*
|
172 |
+
* @return string
|
173 |
+
*/
|
174 |
+
protected function column_last_crawled( $item ) {
|
175 |
+
return date_i18n( get_option( 'date_format' ), strtotime( $item['last_crawled'] ) );
|
176 |
+
}
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Formatting the output of the column first detected into a dateformat
|
180 |
+
*
|
181 |
+
* @param array $item Item data array.
|
182 |
+
*
|
183 |
+
* @return string
|
184 |
+
*/
|
185 |
+
protected function column_first_detected( $item ) {
|
186 |
+
return date_i18n( get_option( 'date_format' ), strtotime( $item['first_detected'] ) );
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* URL column
|
191 |
+
*
|
192 |
+
* @param array $item Item data array.
|
193 |
+
*
|
194 |
+
* @return string
|
195 |
+
*/
|
196 |
+
protected function column_url( $item ) {
|
197 |
+
$actions = array();
|
198 |
+
|
199 |
+
if ( $this->can_create_redirect() ) {
|
200 |
+
/** Gets the modal box */
|
201 |
+
$modal = $this->get_modal_box( $item['url'] );
|
202 |
+
$modal->load_view( md5( $item['url'] ) );
|
203 |
+
|
204 |
+
$actions['create_redirect'] = '<a href="#TB_inline?width=600&height=' . $modal->get_height() . '&inlineId=redirect-' . md5( $item['url'] ) . '" class="thickbox wpseo-open-gsc-redirect-modal aria-button-if-js">' . __( 'Create redirect', 'wordpress-seo' ) . '</a>';
|
205 |
+
}
|
206 |
+
|
207 |
+
$actions['view'] = '<a href="' . home_url( $item['url'] ) . '" target="_blank">' . __( 'View', 'wordpress-seo' ) . '</a>';
|
208 |
+
$actions['markasfixed'] = '<a href="javascript:wpseoMarkAsFixed(\'' . urlencode( $item['url'] ) . '\');">' . __( 'Mark as fixed', 'wordpress-seo' ) . '</a>';
|
209 |
+
|
210 |
+
return sprintf(
|
211 |
+
'<span class="value">%1$s</span> %2$s',
|
212 |
+
$item['url'],
|
213 |
+
$this->row_actions( $actions )
|
214 |
+
);
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* Running the setup of the columns
|
219 |
+
*/
|
220 |
+
private function setup_columns() {
|
221 |
+
$this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Check if the current category allow creating redirects
|
226 |
+
*
|
227 |
+
* @return bool
|
228 |
+
*/
|
229 |
+
private function can_create_redirect() {
|
230 |
+
return in_array( $this->current_view, array( 'soft_404', 'not_found', 'access_denied' ), true );
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Setting the table navigation
|
235 |
+
*
|
236 |
+
* @param int $total_items Total number of items.
|
237 |
+
* @param int $posts_per_page Number of items per page.
|
238 |
+
*/
|
239 |
+
private function set_pagination( $total_items, $posts_per_page ) {
|
240 |
+
$this->set_pagination_args( array(
|
241 |
+
'total_items' => $total_items,
|
242 |
+
'total_pages' => ceil( ( $total_items / $posts_per_page ) ),
|
243 |
+
'per_page' => $posts_per_page,
|
244 |
+
) );
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Setting the items
|
249 |
+
*/
|
250 |
+
private function parse_items() {
|
251 |
+
if ( is_array( $this->items ) && count( $this->items ) > 0 ) {
|
252 |
+
if ( ! empty( $this->search_string ) ) {
|
253 |
+
$this->do_search();
|
254 |
+
}
|
255 |
+
|
256 |
+
$this->set_pagination( count( $this->items ), $this->per_page );
|
257 |
+
|
258 |
+
$this->sort_items();
|
259 |
+
$this->paginate_items();
|
260 |
+
}
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Search through the items
|
265 |
+
*/
|
266 |
+
private function do_search() {
|
267 |
+
$results = array();
|
268 |
+
|
269 |
+
foreach ( $this->items as $item ) {
|
270 |
+
foreach ( $item as $value ) {
|
271 |
+
if ( stristr( $value, $this->search_string ) !== false ) {
|
272 |
+
$results[] = $item;
|
273 |
+
continue;
|
274 |
+
}
|
275 |
+
}
|
276 |
+
}
|
277 |
+
|
278 |
+
$this->items = $results;
|
279 |
+
}
|
280 |
+
|
281 |
+
/**
|
282 |
+
* Running the pagination
|
283 |
+
*/
|
284 |
+
private function paginate_items() {
|
285 |
+
// Setting the starting point. If starting point is below 1, overwrite it with value 0, otherwise it will be sliced of at the back.
|
286 |
+
$slice_start = ( $this->current_page - 1 );
|
287 |
+
if ( $slice_start < 0 ) {
|
288 |
+
$slice_start = 0;
|
289 |
+
}
|
290 |
+
|
291 |
+
// Apply 'pagination'.
|
292 |
+
$this->items = array_slice( $this->items, ( $slice_start * $this->per_page ), $this->per_page );
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* Sort the items by callback
|
297 |
+
*/
|
298 |
+
private function sort_items() {
|
299 |
+
// Sort the results.
|
300 |
+
usort( $this->items, array( $this, 'do_reorder' ) );
|
301 |
+
}
|
302 |
+
|
303 |
+
/**
|
304 |
+
* Doing the sorting of the issues
|
305 |
+
*
|
306 |
+
* @param array $a First data set for comparison.
|
307 |
+
* @param array $b Second data set for comparison.
|
308 |
+
*
|
309 |
+
* @return int
|
310 |
+
*/
|
311 |
+
private function do_reorder( $a, $b ) {
|
312 |
+
$orderby = filter_input( INPUT_GET, 'orderby' );
|
313 |
+
$order = filter_input( INPUT_GET, 'order' );
|
314 |
+
|
315 |
+
// If no sort, default to title.
|
316 |
+
$orderby = ( ! empty( $orderby ) ) ? $orderby : 'url';
|
317 |
+
|
318 |
+
// If no order, default to asc.
|
319 |
+
$order = ( ! empty( $order ) ) ? $order : 'asc';
|
320 |
+
|
321 |
+
// When there is a raw field of it, sort by this field.
|
322 |
+
if ( array_key_exists( $orderby . '_raw', $a ) && array_key_exists( $orderby . '_raw', $b ) ) {
|
323 |
+
$orderby = $orderby . '_raw';
|
324 |
+
}
|
325 |
+
|
326 |
+
// Determine sort order.
|
327 |
+
$result = strcmp( $a[ $orderby ], $b[ $orderby ] );
|
328 |
+
|
329 |
+
// Send final sort direction to usort.
|
330 |
+
return ( $order === 'asc' ) ? $result : ( - $result );
|
331 |
+
}
|
332 |
+
|
333 |
+
/**
|
334 |
+
* Checks if premium is loaded, if not the nopremium modal will be shown. Otherwise it will load the premium one.
|
335 |
+
*
|
336 |
+
* @param string $url URL string.
|
337 |
+
*
|
338 |
+
* @return WPSEO_GSC_Modal Instance of the GSC modal.
|
339 |
+
*/
|
340 |
+
private function get_modal_box( $url ) {
|
341 |
+
if ( defined( 'WPSEO_PREMIUM_FILE' ) && class_exists( 'WPSEO_Premium_GSC_Modal' ) ) {
|
342 |
+
static $premium_modal;
|
343 |
+
|
344 |
+
if ( ! $premium_modal ) {
|
345 |
+
$premium_modal = new WPSEO_Premium_GSC_Modal();
|
346 |
+
}
|
347 |
+
|
348 |
+
return $premium_modal->show( $url );
|
349 |
+
}
|
350 |
+
|
351 |
+
return new WPSEO_GSC_Modal(
|
352 |
+
dirname( __FILE__ ) . '/views/gsc-redirect-nopremium.php',
|
353 |
+
self::FREE_MODAL_HEIGHT,
|
354 |
+
array( 'url' => $url )
|
355 |
+
);
|
356 |
+
}
|
357 |
+
|
358 |
+
/**
|
359 |
+
* Showing the hidden fields used by the AJAX requests
|
360 |
+
*
|
361 |
+
* @param string $platform Platform (desktop, mobile, feature phone).
|
362 |
+
*/
|
363 |
+
private function show_fields( $platform ) {
|
364 |
+
echo '<input type="hidden" name="wpseo_gsc_nonce" value="' . esc_attr( wp_create_nonce( 'wpseo_gsc_nonce' ) ) . '" />';
|
365 |
+
echo '<input id="field_platform" type="hidden" name="platform" value="' . esc_attr( $platform ) . '" />';
|
366 |
+
echo '<input id="field_category" type="hidden" name="category" value="' . esc_attr( $this->current_view ) . '" />';
|
367 |
+
}
|
368 |
+
}
|
admin/google_search_console/class-gsc.php
ADDED
@@ -0,0 +1,311 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\admin|google_search_console
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_GSC
|
8 |
+
*/
|
9 |
+
class WPSEO_GSC {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* The option where data will be stored
|
13 |
+
*/
|
14 |
+
const OPTION_WPSEO_GSC = 'wpseo-gsc';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var WPSEO_GSC_Service
|
18 |
+
*/
|
19 |
+
private $service;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var WPSEO_GSC_Category_Filters
|
23 |
+
*/
|
24 |
+
protected $category_filter;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var WPSEO_GSC_Issues
|
28 |
+
*/
|
29 |
+
protected $issue_fetch;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var string current platform
|
33 |
+
*/
|
34 |
+
private $platform;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @var string current category
|
38 |
+
*/
|
39 |
+
private $category;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Constructor for the page class. This will initialize all GSC related stuff
|
43 |
+
*/
|
44 |
+
public function __construct() {
|
45 |
+
add_action( 'init', array( $this, 'init' ) );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Run init logic.
|
50 |
+
*/
|
51 |
+
public function init() {
|
52 |
+
|
53 |
+
// Setting the screen option.
|
54 |
+
if ( filter_input( INPUT_GET, 'page' ) === 'wpseo_search_console' ) {
|
55 |
+
|
56 |
+
if ( filter_input( INPUT_GET, 'tab' ) !== 'settings' && WPSEO_GSC_Settings::get_profile() === '' ) {
|
57 |
+
wp_redirect( add_query_arg( 'tab', 'settings' ) );
|
58 |
+
exit;
|
59 |
+
}
|
60 |
+
|
61 |
+
$this->set_hooks();
|
62 |
+
$this->set_dependencies();
|
63 |
+
$this->request_handler();
|
64 |
+
}
|
65 |
+
|
66 |
+
add_action( 'admin_init', array( $this, 'register_gsc_notification' ) );
|
67 |
+
add_action( 'admin_init', array( $this, 'register_settings' ) );
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* If the Google Search Console has no credentials, add a notification for the user to give him a heads up. This message is dismissable.
|
72 |
+
*/
|
73 |
+
public function register_gsc_notification() {
|
74 |
+
|
75 |
+
$notification = $this->get_profile_notification();
|
76 |
+
$notification_center = Yoast_Notification_Center::get();
|
77 |
+
|
78 |
+
if ( WPSEO_GSC_Settings::get_profile() === '' ) {
|
79 |
+
$notification_center->add_notification( $notification );
|
80 |
+
}
|
81 |
+
else {
|
82 |
+
$notification_center->remove_notification( $notification );
|
83 |
+
}
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Builds the notification used when GSC is not connected to a profile
|
88 |
+
*
|
89 |
+
* @return Yoast_Notification
|
90 |
+
*/
|
91 |
+
private function get_profile_notification() {
|
92 |
+
return new Yoast_Notification(
|
93 |
+
sprintf(
|
94 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
95 |
+
__( 'Don\'t miss your crawl errors: %1$sconnect with Google Search Console here%2$s.', 'wordpress-seo' ),
|
96 |
+
'<a href="' . admin_url( 'admin.php?page=wpseo_search_console&tab=settings' ) . '">',
|
97 |
+
'</a>'
|
98 |
+
),
|
99 |
+
array(
|
100 |
+
'type' => Yoast_Notification::WARNING,
|
101 |
+
'id' => 'wpseo-dismiss-gsc',
|
102 |
+
'capabilities' => 'wpseo_manage_options',
|
103 |
+
)
|
104 |
+
);
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Be sure the settings will be registered, so data can be stored
|
109 |
+
*/
|
110 |
+
public function register_settings() {
|
111 |
+
register_setting( 'yoast_wpseo_gsc_options', self::OPTION_WPSEO_GSC );
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Function that outputs the redirect page
|
116 |
+
*/
|
117 |
+
public function display() {
|
118 |
+
require_once WPSEO_PATH . 'admin/google_search_console/views/gsc-display.php';
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Display the table
|
123 |
+
*/
|
124 |
+
public function display_table() {
|
125 |
+
// The list table.
|
126 |
+
$list_table = new WPSEO_GSC_Table( $this->platform, $this->category, $this->issue_fetch->get_issues() );
|
127 |
+
|
128 |
+
// Adding filter to display the category filters.
|
129 |
+
add_filter( 'views_' . $list_table->get_screen_id(), array( $this->category_filter, 'as_array' ) );
|
130 |
+
|
131 |
+
// Preparing and displaying the table.
|
132 |
+
$list_table->prepare_items();
|
133 |
+
$list_table->search_box( __( 'Search', 'wordpress-seo' ), 'wpseo-crawl-issues-search' );
|
134 |
+
$list_table->display();
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Load the admin redirects scripts
|
139 |
+
*/
|
140 |
+
public function page_scripts() {
|
141 |
+
|
142 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
143 |
+
$asset_manager->enqueue_script( 'admin-gsc' );
|
144 |
+
$asset_manager->enqueue_style( 'metabox-css' );
|
145 |
+
add_screen_option( 'per_page', array(
|
146 |
+
'label' => __( 'Crawl errors per page', 'wordpress-seo' ),
|
147 |
+
'default' => 50,
|
148 |
+
'option' => 'errors_per_page',
|
149 |
+
) );
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Set the screen options
|
154 |
+
*
|
155 |
+
* @param string $status Status string.
|
156 |
+
* @param string $option Option key.
|
157 |
+
* @param string $value Value to return.
|
158 |
+
*
|
159 |
+
* @return mixed
|
160 |
+
*/
|
161 |
+
public function set_screen_option( $status, $option, $value ) {
|
162 |
+
if ( 'errors_per_page' === $option ) {
|
163 |
+
return $value;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Setting the hooks to be load on page request
|
169 |
+
*/
|
170 |
+
private function set_hooks() {
|
171 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'page_scripts' ) );
|
172 |
+
add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 11, 3 );
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Handles the POST and GET requests
|
177 |
+
*/
|
178 |
+
private function request_handler() {
|
179 |
+
|
180 |
+
// List the table search post to a get.
|
181 |
+
$this->list_table_search_post_to_get();
|
182 |
+
|
183 |
+
// Catch the authorization code POST.
|
184 |
+
$this->catch_authentication_post();
|
185 |
+
|
186 |
+
// Is there a reset post than we will remove the posts and data.
|
187 |
+
if ( filter_input( INPUT_GET, 'gsc_reset' ) ) {
|
188 |
+
// Clear the google data.
|
189 |
+
WPSEO_GSC_Settings::clear_data( $this->service );
|
190 |
+
|
191 |
+
// Adding notification to the notification center.
|
192 |
+
/* Translators: %1$s: expands to Google Search Console. */
|
193 |
+
$this->add_notification( sprintf( __( 'The %1$s data has been removed. You will have to reauthenticate if you want to retrieve the data again.', 'wordpress-seo' ), 'Google Search Console' ), Yoast_Notification::UPDATED );
|
194 |
+
|
195 |
+
// Directly output the notifications.
|
196 |
+
wp_redirect( remove_query_arg( 'gsc_reset' ) );
|
197 |
+
exit;
|
198 |
+
}
|
199 |
+
|
200 |
+
// Reloads al the issues.
|
201 |
+
if ( wp_verify_nonce( filter_input( INPUT_POST, 'reload-crawl-issues-nonce' ), 'reload-crawl-issues' ) && filter_input( INPUT_POST, 'reload-crawl-issues' ) ) {
|
202 |
+
// Reloading all the issues.
|
203 |
+
WPSEO_GSC_Settings::reload_issues();
|
204 |
+
|
205 |
+
// Adding the notification.
|
206 |
+
$this->add_notification( __( 'The issues have been successfully reloaded!', 'wordpress-seo' ), Yoast_Notification::UPDATED );
|
207 |
+
|
208 |
+
// Directly output the notifications.
|
209 |
+
Yoast_Notification_Center::get()->display_notifications();
|
210 |
+
}
|
211 |
+
|
212 |
+
// Catch bulk action request.
|
213 |
+
new WPSEO_GSC_Bulk_Action();
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Catch the redirects search post and redirect it to a search get
|
218 |
+
*/
|
219 |
+
private function list_table_search_post_to_get() {
|
220 |
+
$search_string = filter_input( INPUT_POST, 's' );
|
221 |
+
|
222 |
+
if ( $search_string === null ) {
|
223 |
+
return;
|
224 |
+
}
|
225 |
+
|
226 |
+
// When there is nothing being search and there is no search param in the url, break this method.
|
227 |
+
if ( $search_string === '' && filter_input( INPUT_GET, 's' ) === null ) {
|
228 |
+
return;
|
229 |
+
}
|
230 |
+
|
231 |
+
$url = ( $search_string !== '' ) ? add_query_arg( 's', $search_string ) : remove_query_arg( 's' );
|
232 |
+
|
233 |
+
// Do the redirect.
|
234 |
+
wp_redirect( $url );
|
235 |
+
exit;
|
236 |
+
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Catch the authentication post
|
241 |
+
*/
|
242 |
+
private function catch_authentication_post() {
|
243 |
+
$gsc_values = filter_input( INPUT_POST, 'gsc', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
|
244 |
+
// Catch the authorization code POST.
|
245 |
+
if ( ! empty( $gsc_values['authorization_code'] ) && wp_verify_nonce( $gsc_values['gsc_nonce'], 'wpseo-gsc_nonce' ) ) {
|
246 |
+
if ( ! WPSEO_GSC_Settings::validate_authorization( trim( $gsc_values['authorization_code'] ), $this->service->get_client() ) ) {
|
247 |
+
$this->add_notification( __( 'Incorrect Google Authorization Code.', 'wordpress-seo' ), Yoast_Notification::ERROR );
|
248 |
+
}
|
249 |
+
|
250 |
+
// Redirect user to prevent a post resubmission which causes an oauth error.
|
251 |
+
wp_redirect( admin_url( 'admin.php' ) . '?page=' . esc_attr( filter_input( INPUT_GET, 'page' ) ) . '&tab=settings' );
|
252 |
+
exit;
|
253 |
+
}
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Adding notification to the yoast notification center
|
258 |
+
*
|
259 |
+
* @param string $message Message string.
|
260 |
+
* @param string $type Message type.
|
261 |
+
*/
|
262 |
+
private function add_notification( $message, $type ) {
|
263 |
+
Yoast_Notification_Center::get()->add_notification(
|
264 |
+
new Yoast_Notification( $message, array( 'type' => $type ) )
|
265 |
+
);
|
266 |
+
}
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Setting dependencies which will be used one this page
|
270 |
+
*/
|
271 |
+
private function set_dependencies() {
|
272 |
+
// Setting the service object.
|
273 |
+
$this->service = new WPSEO_GSC_Service( WPSEO_GSC_Settings::get_profile() );
|
274 |
+
|
275 |
+
// Setting the platform.
|
276 |
+
$this->platform = WPSEO_GSC_Mapper::get_current_platform( 'tab' );
|
277 |
+
|
278 |
+
// Loading the issue counter.
|
279 |
+
$issue_count = new WPSEO_GSC_Count( $this->service );
|
280 |
+
$issue_count->fetch_counts();
|
281 |
+
|
282 |
+
// Loading the category filters.
|
283 |
+
$this->category_filter = new WPSEO_GSC_Category_Filters( $issue_count->get_platform_counts( $this->platform ) );
|
284 |
+
|
285 |
+
// Setting the current category.
|
286 |
+
$this->category = $this->category_filter->get_category();
|
287 |
+
|
288 |
+
// Listing the issues.
|
289 |
+
$issue_count->list_issues( $this->platform, $this->category );
|
290 |
+
|
291 |
+
// Fetching the issues.
|
292 |
+
$this->issue_fetch = new WPSEO_GSC_Issues( $this->platform, $this->category, $issue_count->get_issues() );
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* Setting the tab help on top of the screen
|
297 |
+
*/
|
298 |
+
public function set_help() {
|
299 |
+
$screen = get_current_screen();
|
300 |
+
|
301 |
+
$screen->add_help_tab(
|
302 |
+
array(
|
303 |
+
'id' => 'basic-help',
|
304 |
+
'title' => __( 'Issue categories', 'wordpress-seo' ),
|
305 |
+
'content' => '<p><strong>' . __( 'Desktop', 'wordpress-seo' ) . '</strong><br />' . __( 'Errors that occurred when your site was crawled by Googlebot.', 'wordpress-seo' ) . '</p>'
|
306 |
+
. '<p><strong>' . __( 'Smartphone', 'wordpress-seo' ) . '</strong><br />' . __( 'Errors that occurred only when your site was crawled by Googlebot-Mobile (errors didn\'t appear for desktop).', 'wordpress-seo' ) . '</p>'
|
307 |
+
. '<p><strong>' . __( 'Feature phone', 'wordpress-seo' ) . '</strong><br />' . __( 'Errors that only occurred when your site was crawled by Googlebot for feature phones (errors didn\'t appear for desktop).', 'wordpress-seo' ) . '</p>',
|
308 |
+
)
|
309 |
+
);
|
310 |
+
}
|
311 |
+
}
|
admin/google_search_console/views/gsc-display.php
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*/
|
5 |
+
|
6 |
+
// Admin header.
|
7 |
+
Yoast_Form::get_instance()->admin_header( false, 'wpseo-gsc', false, 'yoast_wpseo_gsc_options' );
|
8 |
+
|
9 |
+
$platform_tabs = new WPSEO_GSC_Platform_Tabs();
|
10 |
+
|
11 |
+
if ( defined( 'WP_DEBUG' ) && WP_DEBUG && WPSEO_GSC_Settings::get_profile() !== '' ) { ?>
|
12 |
+
<form action="" method="post" class="wpseo-gsc-reload-crawl-issues-form">
|
13 |
+
<input type='hidden' name='reload-crawl-issues-nonce' value='<?php echo esc_attr( wp_create_nonce( 'reload-crawl-issues' ) ); ?>' />
|
14 |
+
<input type="submit" name="reload-crawl-issues" id="reload-crawl-issue" class="button button-primary alignright"
|
15 |
+
value="<?php esc_attr_e( 'Reload crawl issues', 'wordpress-seo' ); ?>">
|
16 |
+
</form>
|
17 |
+
<?php } ?>
|
18 |
+
|
19 |
+
<h2 class="nav-tab-wrapper" id="wpseo-tabs">
|
20 |
+
<?php echo $platform_tabs; ?>
|
21 |
+
</h2>
|
22 |
+
|
23 |
+
<?php
|
24 |
+
|
25 |
+
// Video explains about the options when connected only.
|
26 |
+
if ( null !== $this->service->get_client()->getAccessToken() ) {
|
27 |
+
$video_url = WPSEO_Shortlinker::get( 'https://yoa.st/screencast-search-console' );
|
28 |
+
}
|
29 |
+
else {
|
30 |
+
$video_url = WPSEO_Shortlinker::get( 'https://yoa.st/screencast-connect-search-console' );
|
31 |
+
}
|
32 |
+
|
33 |
+
$tab = new WPSEO_Option_Tab( 'GSC', __( 'Google Search Console', 'wordpress-seo' ), array( 'video_url' => $video_url ) );
|
34 |
+
$gsc_help_center = new WPSEO_Help_Center( 'google-search-console', $tab, WPSEO_Utils::is_yoast_seo_premium() );
|
35 |
+
$gsc_help_center->localize_data();
|
36 |
+
$gsc_help_center->mount();
|
37 |
+
|
38 |
+
switch ( $platform_tabs->current_tab() ) {
|
39 |
+
case 'settings':
|
40 |
+
// Check if there is an access token.
|
41 |
+
if ( null === $this->service->get_client()->getAccessToken() ) {
|
42 |
+
// Print auth screen.
|
43 |
+
echo '<p>';
|
44 |
+
printf(
|
45 |
+
/* Translators: %1$s: expands to Yoast SEO, %2$s expands to Google Search Console. */
|
46 |
+
esc_html__( 'To allow %1$s to fetch your %2$s information, please enter your Google Authorization Code. Clicking the button below will open a new window.', 'wordpress-seo' ),
|
47 |
+
'Yoast SEO',
|
48 |
+
'Google Search Console'
|
49 |
+
);
|
50 |
+
echo "</p>\n";
|
51 |
+
echo '<input type="hidden" id="gsc_auth_url" value="', esc_url( $this->service->get_client()->createAuthUrl() ) , '" />';
|
52 |
+
echo "<button type='button' id='gsc_auth_code' class='button'>" , esc_html__( 'Get Google Authorization Code', 'wordpress-seo' ) ,"</button>\n";
|
53 |
+
|
54 |
+
echo '<p id="gsc-enter-code-label">' . esc_html__( 'Enter your Google Authorization Code and press the Authenticate button.', 'wordpress-seo' ) . "</p>\n";
|
55 |
+
echo "<form action='" . esc_url( admin_url( 'admin.php?page=wpseo_search_console&tab=settings' ) ) . "' method='post'>\n";
|
56 |
+
echo "<input type='text' name='gsc[authorization_code]' value='' class='textinput' aria-labelledby='gsc-enter-code-label' />";
|
57 |
+
echo "<input type='hidden' name='gsc[gsc_nonce]' value='" . esc_attr( wp_create_nonce( 'wpseo-gsc_nonce' ) ) . "' />";
|
58 |
+
echo "<input type='submit' name='gsc[Submit]' value='" . esc_attr__( 'Authenticate', 'wordpress-seo' ) . "' class='button button-primary' />";
|
59 |
+
echo "</form>\n";
|
60 |
+
}
|
61 |
+
else {
|
62 |
+
$reset_button = '<a class="button" href="' . esc_url( add_query_arg( 'gsc_reset', 1 ) ) . '">' . esc_html__( 'Reauthenticate with Google ', 'wordpress-seo' ) . '</a>';
|
63 |
+
echo '<h3>', esc_html__( 'Current profile', 'wordpress-seo' ), '</h3>';
|
64 |
+
$profile = WPSEO_GSC_Settings::get_profile();
|
65 |
+
if ( $profile !== '' ) {
|
66 |
+
echo '<p>';
|
67 |
+
echo $profile;
|
68 |
+
echo '</p>';
|
69 |
+
|
70 |
+
echo '<p>';
|
71 |
+
echo $reset_button;
|
72 |
+
echo '</p>';
|
73 |
+
|
74 |
+
}
|
75 |
+
else {
|
76 |
+
echo "<form action='" . esc_url( admin_url( 'options.php' ) ) . "' method='post'>";
|
77 |
+
|
78 |
+
settings_fields( 'yoast_wpseo_gsc_options' );
|
79 |
+
Yoast_Form::get_instance()->set_option( 'wpseo-gsc' );
|
80 |
+
|
81 |
+
echo '<p>';
|
82 |
+
$profiles = $this->service->get_sites();
|
83 |
+
if ( ! empty( $profiles ) ) {
|
84 |
+
$show_save = true;
|
85 |
+
echo Yoast_Form::get_instance()->select( 'profile', __( 'Profile', 'wordpress-seo' ), $profiles );
|
86 |
+
}
|
87 |
+
else {
|
88 |
+
$show_save = false;
|
89 |
+
esc_html_e( 'There were no profiles found', 'wordpress-seo' );
|
90 |
+
}
|
91 |
+
echo '</p>';
|
92 |
+
|
93 |
+
echo '<p>';
|
94 |
+
|
95 |
+
if ( $show_save ) {
|
96 |
+
echo '<input type="submit" name="submit" id="submit" class="button button-primary wpseo-gsc-save-profile" value="' . esc_attr__( 'Save Profile', 'wordpress-seo' ) . '" /> ' . __( 'or', 'wordpress-seo' ) , ' ';
|
97 |
+
}
|
98 |
+
echo $reset_button;
|
99 |
+
echo '</p>';
|
100 |
+
echo '</form>';
|
101 |
+
}
|
102 |
+
}
|
103 |
+
break;
|
104 |
+
|
105 |
+
default:
|
106 |
+
$form_action_url = add_query_arg( 'page', esc_attr( filter_input( INPUT_GET, 'page' ) ) );
|
107 |
+
|
108 |
+
get_current_screen()->set_screen_reader_content( array(
|
109 |
+
// There are no views links in this screen, so no need for the views heading.
|
110 |
+
'heading_views' => null,
|
111 |
+
'heading_pagination' => __( 'Crawl issues list navigation', 'wordpress-seo' ),
|
112 |
+
'heading_list' => __( 'Crawl issues list', 'wordpress-seo' ),
|
113 |
+
) );
|
114 |
+
|
115 |
+
// Open <form>.
|
116 |
+
echo "<form id='wpseo-crawl-issues-table-form' action='" . esc_url( $form_action_url ) . "' method='post'>\n";
|
117 |
+
|
118 |
+
// AJAX nonce.
|
119 |
+
echo "<input type='hidden' class='wpseo-gsc-ajax-security' value='" . esc_attr( wp_create_nonce( 'wpseo-gsc-ajax-security' ) ) . "' />\n";
|
120 |
+
|
121 |
+
$this->display_table();
|
122 |
+
|
123 |
+
// Close <form>.
|
124 |
+
echo "</form>\n";
|
125 |
+
|
126 |
+
break;
|
127 |
+
}
|
128 |
+
?>
|
129 |
+
<?php
|
130 |
+
// Add link to Knowledge Base article about crawl issues.
|
131 |
+
echo '<p>';
|
132 |
+
|
133 |
+
printf(
|
134 |
+
/* translators: %1$s expands anchor to knowledge base article, %2$s expands to </a> */
|
135 |
+
esc_html__( 'Please refer to %1$sour article about how to connect your website to Google Search Console%2$s if you need assistance.', 'wordpress-seo' ),
|
136 |
+
'<a href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/1zy' ) ) . '" target="_blank" rel="noopener noreferrer">',
|
137 |
+
'</a>'
|
138 |
+
);
|
139 |
+
|
140 |
+
echo '</p>';
|
141 |
+
?>
|
142 |
+
|
143 |
+
<br class="clear" />
|
144 |
+
<?php
|
145 |
+
|
146 |
+
// Admin footer.
|
147 |
+
Yoast_Form::get_instance()->admin_footer( false );
|
admin/google_search_console/views/gsc-redirect-nopremium.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin|Google_Search_Console
|
4 |
+
*
|
5 |
+
* This is the view for the modal box that appears when premium isn't loaded.
|
6 |
+
*/
|
7 |
+
|
8 |
+
echo '<h1 class="wpseo-redirect-url-title">';
|
9 |
+
printf(
|
10 |
+
/* Translators: %s: expands to Yoast SEO Premium */
|
11 |
+
esc_html__( 'Creating redirects is a %s feature', 'wordpress-seo' ),
|
12 |
+
'Yoast SEO Premium'
|
13 |
+
);
|
14 |
+
echo '</h1>';
|
15 |
+
echo '<p>';
|
16 |
+
printf(
|
17 |
+
/* Translators: %1$s: expands to 'Yoast SEO Premium', %2$s: links to Yoast SEO Premium plugin page. */
|
18 |
+
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' ),
|
19 |
+
'Yoast SEO Premium',
|
20 |
+
'<a href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/redirects' ) ) . '" target="_blank">yoast.com</a>'
|
21 |
+
);
|
22 |
+
echo '</p>';
|
23 |
+
echo '<button type="button" class="button wpseo-redirect-close">' . esc_html__( 'Close', 'wordpress-seo' ) . '</button>';
|
admin/import/class-import-aioseo-hooks.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Import
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Setting the hooks for importing the data the All-In-One-SEO plugin
|
8 |
+
*/
|
9 |
+
class WPSEO_Import_AIOSEO_Hooks extends WPSEO_Import_Hooks {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string The main plugin file.
|
13 |
+
*/
|
14 |
+
protected $plugin_file = 'all-in-one-seo-pack/all_in_one_seo_pack.php';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string The GET parameter for deactivating the plugin.
|
18 |
+
*/
|
19 |
+
protected $deactivation_listener = 'deactivate_aioseo';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Throw a notice to import settings.
|
23 |
+
*
|
24 |
+
* @since 3.0
|
25 |
+
*/
|
26 |
+
public function show_import_settings_notice() {
|
27 |
+
$url = add_query_arg( array( '_wpnonce' => wp_create_nonce( 'wpseo-import' ) ), admin_url( 'admin.php?page=wpseo_tools&tool=import-export&import=1&importaioseo=1#top#import-seo' ) );
|
28 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
29 |
+
echo '<div class="error"><p>', sprintf( esc_html__( 'The plugin All-In-One-SEO has been detected. Do you want to %1$simport its settings%2$s?', 'wordpress-seo' ), sprintf( '<a href="%s">', esc_url( $url ) ), '</a>' ), '</p></div>';
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Throw a notice to inform the user that the plugin has been deactivated
|
34 |
+
*
|
35 |
+
* @since 3.0
|
36 |
+
*/
|
37 |
+
public function show_deactivate_notice() {
|
38 |
+
echo '<div class="updated"><p>', esc_html__( 'All-In-One-SEO has been deactivated', 'wordpress-seo' ), '</p></div>';
|
39 |
+
}
|
40 |
+
}
|
admin/import/class-import-hooks.php
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Import
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Abstract object for handling the importing and deactivating of the plugin
|
8 |
+
*/
|
9 |
+
abstract class WPSEO_Import_Hooks {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string The main plugin file.
|
13 |
+
*/
|
14 |
+
protected $plugin_file;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string The GET parameter for running the import.
|
18 |
+
*/
|
19 |
+
protected $import_listener;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var string The GET parameter for deactivating the plugin.
|
23 |
+
*/
|
24 |
+
protected $deactivation_listener;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Throw a notice to import settings.
|
28 |
+
*
|
29 |
+
* @since 3.0
|
30 |
+
*/
|
31 |
+
abstract public function show_import_settings_notice();
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Throw a notice to inform the user that the plugin has been deactivated
|
35 |
+
*
|
36 |
+
* @since 3.0
|
37 |
+
*/
|
38 |
+
abstract public function show_deactivate_notice();
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Adding the hooks to show import/deactivate message when needed.
|
42 |
+
*/
|
43 |
+
public function __construct() {
|
44 |
+
if ( $this->is_active() ) {
|
45 |
+
$this->show_import_message();
|
46 |
+
$this->show_deactivate_message();
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Handle deactivation & import of the data data
|
52 |
+
*
|
53 |
+
* @since 3.0
|
54 |
+
*/
|
55 |
+
public function show_import_message() {
|
56 |
+
if ( filter_input( INPUT_GET, 'tool' ) !== 'import-export' ) {
|
57 |
+
add_action( 'admin_notices', array( $this, 'show_import_settings_notice' ) );
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Handle deactivation of the plugin
|
63 |
+
*
|
64 |
+
* @since 3.0
|
65 |
+
*/
|
66 |
+
public function show_deactivate_message() {
|
67 |
+
if ( filter_input( INPUT_GET, $this->deactivation_listener ) === '1' ) {
|
68 |
+
// Deactivate AIO.
|
69 |
+
deactivate_plugins( $this->plugin_file );
|
70 |
+
|
71 |
+
// Show notice that aioseo has been deactivated.
|
72 |
+
add_action( 'admin_notices', array( $this, 'show_deactivate_notice' ) );
|
73 |
+
|
74 |
+
// Clean up the referrer url for later use.
|
75 |
+
if ( isset( $_SERVER['REQUEST_URI'] ) ) {
|
76 |
+
$_SERVER['REQUEST_URI'] = remove_query_arg( array( $this->deactivation_listener ), sanitize_text_field( $_SERVER['REQUEST_URI'] ) );
|
77 |
+
}
|
78 |
+
}
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Check if the plugin is active.
|
83 |
+
*
|
84 |
+
* @return bool
|
85 |
+
*/
|
86 |
+
protected function is_active() {
|
87 |
+
return is_plugin_active( $this->plugin_file );
|
88 |
+
}
|
89 |
+
}
|
admin/import/class-import-wpseo-hooks.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Import
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Setting the hooks for importing the data the wpSEO plugin
|
8 |
+
*/
|
9 |
+
class WPSEO_Import_WPSEO_Hooks extends WPSEO_Import_Hooks {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string The main plugin file.
|
13 |
+
*/
|
14 |
+
protected $plugin_file = 'wpseo/wpseo.php';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string The GET parameter for deactivating the plugin.
|
18 |
+
*/
|
19 |
+
protected $deactivation_listener = 'deactivate_wpseo';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Throw a notice to import wpSEO.
|
23 |
+
*
|
24 |
+
* @since 3.0
|
25 |
+
*/
|
26 |
+
public function show_import_settings_notice() {
|
27 |
+
$url = add_query_arg( array( '_wpnonce' => wp_create_nonce( 'wpseo-import' ) ), admin_url( 'admin.php?page=wpseo_tools&tool=import-export&import=1&importwpseo=1#top#import-seo' ) );
|
28 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
29 |
+
echo '<div class="error"><p>', sprintf( esc_html__( 'The plugin wpSEO has been detected. Do you want to %1$simport its settings%2$s?', 'wordpress-seo' ), sprintf( '<a href="%s">', esc_url( $url ) ), '</a>' ), '</p></div>';
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Throw a notice to inform the user wpSEO has been deactivated
|
34 |
+
*
|
35 |
+
* @since 3.0
|
36 |
+
*/
|
37 |
+
public function show_deactivate_notice() {
|
38 |
+
echo '<div class="updated"><p>', esc_html__( 'wpSEO has been deactivated', 'wordpress-seo' ), '</p></div>';
|
39 |
+
}
|
40 |
+
}
|
admin/index.php
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Nothing to see here.
|
4 |
+
*/
|
admin/interface-collection.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Interface that represents a collection.
|
8 |
+
*/
|
9 |
+
interface WPSEO_Collection {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns the collection data.
|
13 |
+
*
|
14 |
+
* @return array The collection data.
|
15 |
+
*/
|
16 |
+
public function get();
|
17 |
+
|
18 |
+
}
|
admin/interface-installable.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the interface for an installable object.
|
8 |
+
*/
|
9 |
+
interface WPSEO_Installable {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Runs the installation routine.
|
13 |
+
*
|
14 |
+
* @return void
|
15 |
+
*/
|
16 |
+
public function install();
|
17 |
+
}
|
admin/links/class-link-cleanup-transient.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the cleanup logic when the text link counter features has been disabled.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Cleanup_Transient implements WPSEO_WordPress_Integration {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Registers the hooks.
|
13 |
+
*/
|
14 |
+
public function register_hooks() {
|
15 |
+
add_action( 'update_option_wpseo', array( $this, 'remove_transients_on_updated_option' ), 10, 2 );
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Removes the transient when the option is updated.
|
20 |
+
*
|
21 |
+
* @param mixed $old_value The old value.
|
22 |
+
* @param mixed $value The new value.
|
23 |
+
*
|
24 |
+
* @return void
|
25 |
+
*/
|
26 |
+
public function remove_transients_on_updated_option( $old_value, $value ) {
|
27 |
+
$option_name = 'enable_text_link_counter';
|
28 |
+
if ( $value[ $option_name ] === false && $old_value[ $option_name ] !== $value[ $option_name ] ) {
|
29 |
+
WPSEO_Link_Table_Accessible::cleanup();
|
30 |
+
WPSEO_Meta_Table_Accessible::cleanup();
|
31 |
+
}
|
32 |
+
}
|
33 |
+
}
|
admin/links/class-link-column-count.php
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the link column count. This class contains the count for each post id on the current page .
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Column_Count {
|
10 |
+
|
11 |
+
/** @var array */
|
12 |
+
protected $count = array();
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Sets the counts for the set target field.
|
16 |
+
*
|
17 |
+
* @param array $post_ids The posts to get the count for.
|
18 |
+
*/
|
19 |
+
public function set( $post_ids ) {
|
20 |
+
if ( empty( $post_ids ) ) {
|
21 |
+
return;
|
22 |
+
}
|
23 |
+
|
24 |
+
$this->count = $this->get_results( $post_ids );
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Gets the link count for given post id.
|
29 |
+
*
|
30 |
+
* @param int $post_id The post id.
|
31 |
+
* @param string $target_field The field to show.
|
32 |
+
*
|
33 |
+
* @return int|null The total amount of links or null if the target field
|
34 |
+
* does not exist for the given post id.
|
35 |
+
*/
|
36 |
+
public function get( $post_id, $target_field = 'internal_link_count' ) {
|
37 |
+
if ( array_key_exists( $post_id, $this->count ) && array_key_exists( $target_field, $this->count[ $post_id ] ) ) {
|
38 |
+
return $this->count[ $post_id ][ $target_field ];
|
39 |
+
}
|
40 |
+
|
41 |
+
return null;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Gets the link count for the given post ids.
|
46 |
+
*
|
47 |
+
* @param array $post_ids Array with post_ids.
|
48 |
+
*
|
49 |
+
* @return array
|
50 |
+
*/
|
51 |
+
protected function get_results( $post_ids ) {
|
52 |
+
global $wpdb;
|
53 |
+
|
54 |
+
$storage = new WPSEO_Meta_Storage();
|
55 |
+
|
56 |
+
$results = $wpdb->get_results(
|
57 |
+
$wpdb->prepare( '
|
58 |
+
SELECT internal_link_count, incoming_link_count, object_id
|
59 |
+
FROM ' . $storage->get_table_name() . '
|
60 |
+
WHERE object_id IN (' . implode( ',', array_fill( 0, count( $post_ids ), '%d' ) ) . ')',
|
61 |
+
$post_ids
|
62 |
+
),
|
63 |
+
ARRAY_A
|
64 |
+
);
|
65 |
+
|
66 |
+
$output = array();
|
67 |
+
foreach ( $results as $result ) {
|
68 |
+
$output[ (int) $result['object_id'] ] = array(
|
69 |
+
'internal_link_count' => $result['internal_link_count'],
|
70 |
+
'incoming_link_count' => (int) $result['incoming_link_count'],
|
71 |
+
);
|
72 |
+
}
|
73 |
+
|
74 |
+
// Set unfound items to zero.
|
75 |
+
foreach ( $post_ids as $post_id ) {
|
76 |
+
if ( ! array_key_exists( $post_id, $output ) ) {
|
77 |
+
$output[ $post_id ] = array(
|
78 |
+
'internal_link_count' => null,
|
79 |
+
'incoming_link_count' => 0,
|
80 |
+
);
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
return $output;
|
85 |
+
}
|
86 |
+
}
|
admin/links/class-link-columns.php
ADDED
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the link columns. This class will add and handle the link columns.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Columns {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string Partial column name.
|
13 |
+
*/
|
14 |
+
const COLUMN_LINKED = 'linked';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string Partial column name.
|
18 |
+
*/
|
19 |
+
const COLUMN_LINKS = 'links';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var WPSEO_Link_Column_Count
|
23 |
+
*/
|
24 |
+
protected $link_count;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var WPSEO_Meta_Storage Storage to use.
|
28 |
+
*/
|
29 |
+
protected $storage;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var array List of public post types.
|
33 |
+
*/
|
34 |
+
protected $public_post_types = array();
|
35 |
+
|
36 |
+
/**
|
37 |
+
* WPSEO_Link_Columns constructor.
|
38 |
+
*
|
39 |
+
* @param WPSEO_Meta_Storage $storage The storage object to use.
|
40 |
+
*/
|
41 |
+
public function __construct( WPSEO_Meta_Storage $storage ) {
|
42 |
+
$this->storage = $storage;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Registers the hooks.
|
47 |
+
*/
|
48 |
+
public function register_hooks() {
|
49 |
+
global $pagenow;
|
50 |
+
$is_ajax_request = defined( 'DOING_AJAX' ) && DOING_AJAX;
|
51 |
+
|
52 |
+
if ( ! WPSEO_Metabox::is_post_overview( $pagenow ) && ! $is_ajax_request ) {
|
53 |
+
return;
|
54 |
+
}
|
55 |
+
|
56 |
+
// Exit when either table is not present or accessible.
|
57 |
+
if ( ! WPSEO_Link_Table_Accessible::is_accessible() || ! WPSEO_Meta_Table_Accessible::is_accessible() ) {
|
58 |
+
return;
|
59 |
+
}
|
60 |
+
|
61 |
+
if ( $is_ajax_request ) {
|
62 |
+
add_action( 'admin_init', array( $this, 'set_count_objects' ) );
|
63 |
+
}
|
64 |
+
|
65 |
+
// Hook into tablenav to calculate links and linked.
|
66 |
+
add_action( 'manage_posts_extra_tablenav', array( $this, 'count_objects' ) );
|
67 |
+
|
68 |
+
add_filter( 'posts_clauses', array( $this, 'order_by_links' ), 1, 2 );
|
69 |
+
add_filter( 'posts_clauses', array( $this, 'order_by_linked' ), 1, 2 );
|
70 |
+
|
71 |
+
add_filter( 'admin_init', array( $this, 'register_init_hooks' ) );
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Register hooks that require to be registered after `init`.
|
76 |
+
*/
|
77 |
+
public function register_init_hooks() {
|
78 |
+
$this->public_post_types = apply_filters( 'wpseo_link_count_post_types', WPSEO_Post_Type::get_accessible_post_types() );
|
79 |
+
|
80 |
+
if ( is_array( $this->public_post_types ) && $this->public_post_types !== array() ) {
|
81 |
+
array_walk( $this->public_post_types, array( $this, 'set_post_type_hooks' ) );
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Modifies the query pieces to allow ordering column by links to post.
|
87 |
+
*
|
88 |
+
* @param array $pieces Array of Query pieces.
|
89 |
+
* @param \WP_Query $query The Query on which to apply.
|
90 |
+
*
|
91 |
+
* @return array
|
92 |
+
*/
|
93 |
+
public function order_by_links( $pieces, $query ) {
|
94 |
+
if ( 'wpseo-' . self::COLUMN_LINKS !== $query->get( 'orderby' ) ) {
|
95 |
+
return $pieces;
|
96 |
+
}
|
97 |
+
|
98 |
+
return $this->build_sort_query_pieces( $pieces, $query, 'internal_link_count' );
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Modifies the query pieces to allow ordering column by links to post.
|
103 |
+
*
|
104 |
+
* @param array $pieces Array of Query pieces.
|
105 |
+
* @param \WP_Query $query The Query on which to apply.
|
106 |
+
*
|
107 |
+
* @return array
|
108 |
+
*/
|
109 |
+
public function order_by_linked( $pieces, $query ) {
|
110 |
+
if ( 'wpseo-' . self::COLUMN_LINKED !== $query->get( 'orderby' ) ) {
|
111 |
+
return $pieces;
|
112 |
+
}
|
113 |
+
|
114 |
+
return $this->build_sort_query_pieces( $pieces, $query, 'incoming_link_count' );
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Builds the pieces for a sorting query.
|
119 |
+
*
|
120 |
+
* @param array $pieces Array of Query pieces.
|
121 |
+
* @param \WP_Query $query The Query on which to apply.
|
122 |
+
* @param string $field The field in the table to JOIN on.
|
123 |
+
*
|
124 |
+
* @return array Modified Query pieces.
|
125 |
+
*/
|
126 |
+
protected function build_sort_query_pieces( $pieces, $query, $field ) {
|
127 |
+
global $wpdb;
|
128 |
+
|
129 |
+
// We only want our code to run in the main WP query.
|
130 |
+
if ( ! $query->is_main_query() ) {
|
131 |
+
return $pieces;
|
132 |
+
}
|
133 |
+
|
134 |
+
// Get the order query variable - ASC or DESC.
|
135 |
+
$order = strtoupper( $query->get( 'order' ) );
|
136 |
+
|
137 |
+
// Make sure the order setting qualifies. If not, set default as ASC.
|
138 |
+
if ( ! in_array( $order, array( 'ASC', 'DESC' ), true ) ) {
|
139 |
+
$order = 'ASC';
|
140 |
+
}
|
141 |
+
|
142 |
+
$table = $this->storage->get_table_name();
|
143 |
+
|
144 |
+
$pieces['join'] .= " LEFT JOIN $table AS yst_links ON yst_links.object_id = {$wpdb->posts}.ID ";
|
145 |
+
$pieces['orderby'] = "{$field} $order, FIELD( {$wpdb->posts}.post_status, 'publish' ) $order, {$pieces['orderby']}";
|
146 |
+
|
147 |
+
return $pieces;
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Sets the hooks for each post type.
|
152 |
+
*
|
153 |
+
* @param string $post_type The post type.
|
154 |
+
*/
|
155 |
+
public function set_post_type_hooks( $post_type ) {
|
156 |
+
add_filter( 'manage_' . $post_type . '_posts_columns', array( $this, 'add_post_columns' ) );
|
157 |
+
add_action( 'manage_' . $post_type . '_posts_custom_column', array( $this, 'column_content' ), 10, 2 );
|
158 |
+
add_filter( 'manage_edit-' . $post_type . '_sortable_columns', array( $this, 'column_sort' ) );
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Adds the columns for the post overview.
|
163 |
+
*
|
164 |
+
* @param array $columns Array with columns.
|
165 |
+
*
|
166 |
+
* @return array The extended array with columns.
|
167 |
+
*/
|
168 |
+
public function add_post_columns( array $columns ) {
|
169 |
+
$columns[ 'wpseo-' . self::COLUMN_LINKS ] = '<span class="yoast-linked-to yoast-column-header-has-tooltip" data-label="' . esc_attr__( 'Number of internal links in this post. See "Yoast Columns" text in the help tab for more info.', 'wordpress-seo' ) . '"><span class="screen-reader-text">' . __( '# links in post', 'wordpress-seo' ) . '</span></span>';
|
170 |
+
|
171 |
+
if ( ! WPSEO_Link_Query::has_unprocessed_posts( $this->public_post_types ) ) {
|
172 |
+
$columns[ 'wpseo-' . self::COLUMN_LINKED ] = '<span class="yoast-linked-from yoast-column-header-has-tooltip" data-label="' . esc_attr__( 'Number of internal links linking to this post. See "Yoast Columns" text in the help tab for more info.', 'wordpress-seo' ) . '"><span class="screen-reader-text">' . __( '# internal links to', 'wordpress-seo' ) . '</span></span>';
|
173 |
+
}
|
174 |
+
|
175 |
+
return $columns;
|
176 |
+
}
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Makes sure we calculate all values in one query.
|
180 |
+
*
|
181 |
+
* @param string $target Extra table navigation location which is triggered.
|
182 |
+
*/
|
183 |
+
public function count_objects( $target ) {
|
184 |
+
if ( 'top' === $target ) {
|
185 |
+
$this->set_count_objects();
|
186 |
+
}
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Sets the objects to use for the count.
|
191 |
+
*/
|
192 |
+
public function set_count_objects() {
|
193 |
+
global $wp_query;
|
194 |
+
|
195 |
+
$posts = $wp_query->get_posts();
|
196 |
+
$post_ids = array();
|
197 |
+
|
198 |
+
// Post lists return a list of objects.
|
199 |
+
if ( isset( $posts[0] ) && is_object( $posts[0] ) ) {
|
200 |
+
$post_ids = wp_list_pluck( $posts, 'ID' );
|
201 |
+
}
|
202 |
+
elseif ( ! empty( $posts ) ) {
|
203 |
+
// Page list returns an array of post IDs.
|
204 |
+
$post_ids = array_keys( $posts );
|
205 |
+
}
|
206 |
+
|
207 |
+
$post_ids = WPSEO_Link_Query::filter_unprocessed_posts( $post_ids );
|
208 |
+
|
209 |
+
$links = new WPSEO_Link_Column_Count();
|
210 |
+
$links->set( $post_ids );
|
211 |
+
|
212 |
+
$this->link_count = $links;
|
213 |
+
}
|
214 |
+
|
215 |
+
/**
|
216 |
+
* Displays the column content for the given column
|
217 |
+
*
|
218 |
+
* @param string $column_name Column to display the content for.
|
219 |
+
* @param int $post_id Post to display the column content for.
|
220 |
+
*/
|
221 |
+
public function column_content( $column_name, $post_id ) {
|
222 |
+
$link_count = null;
|
223 |
+
|
224 |
+
switch ( $column_name ) {
|
225 |
+
case 'wpseo-' . self::COLUMN_LINKS:
|
226 |
+
$link_count = $this->link_count->get( $post_id, 'internal_link_count' );
|
227 |
+
break;
|
228 |
+
case 'wpseo-' . self::COLUMN_LINKED:
|
229 |
+
if ( get_post_status( $post_id ) === 'publish' ) {
|
230 |
+
$link_count = $this->link_count->get( $post_id, 'incoming_link_count' );
|
231 |
+
}
|
232 |
+
break;
|
233 |
+
}
|
234 |
+
|
235 |
+
if ( isset( $link_count ) ) {
|
236 |
+
echo (int) $link_count;
|
237 |
+
}
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Sets the sortable columns.
|
242 |
+
*
|
243 |
+
* @param array $columns Array with sortable columns.
|
244 |
+
*
|
245 |
+
* @return array The extended array with sortable columns.
|
246 |
+
*/
|
247 |
+
public function column_sort( array $columns ) {
|
248 |
+
$columns[ 'wpseo-' . self::COLUMN_LINKS ] = 'wpseo-' . self::COLUMN_LINKS;
|
249 |
+
$columns[ 'wpseo-' . self::COLUMN_LINKED ] = 'wpseo-' . self::COLUMN_LINKED;
|
250 |
+
|
251 |
+
return $columns;
|
252 |
+
}
|
253 |
+
}
|
admin/links/class-link-compatibility-notifier.php
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents compatibility with php version 5.3.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Compatibility_Notifier {
|
10 |
+
|
11 |
+
const NOTIFICATION_ID = 'wpseo-links-compatibility';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Adds the notification to the notification center.
|
15 |
+
*/
|
16 |
+
public function add_notification() {
|
17 |
+
Yoast_Notification_Center::get()->add_notification( $this->get_notification() );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Removes the notification from the notification center.
|
22 |
+
*/
|
23 |
+
public function remove_notification() {
|
24 |
+
Yoast_Notification_Center::get()->remove_notification( $this->get_notification() );
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Returns the notification when the version is incompatible
|
29 |
+
*
|
30 |
+
* @return Yoast_Notification The notification.
|
31 |
+
*/
|
32 |
+
protected function get_notification() {
|
33 |
+
return new Yoast_Notification(
|
34 |
+
sprintf(
|
35 |
+
/* translators: %1$s: Yoast SEO. %2$s: Version number of Yoast SEO. %3$s: PHP version %4$s: The current PHP versione. %5$s link to knowledge base article about solving PHP issue. %6$s: is anchor closing. */
|
36 |
+
__(
|
37 |
+
'The <strong>Text link counter</strong> feature (introduced in %1$s %2$s) is currently disabled. For this feature to work %1$s requires at least PHP version %3$s. We have detected PHP version %4$s on this website.
|
38 |
+
Please read the following %5$sknowledge base article%6$s to find out how to resolve this problem.',
|
39 |
+
'wordpress-seo'
|
40 |
+
),
|
41 |
+
'Yoast SEO',
|
42 |
+
'5.0',
|
43 |
+
'5.3',
|
44 |
+
phpversion(),
|
45 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/16f' ) . '" target="_blank">',
|
46 |
+
'</a>'
|
47 |
+
),
|
48 |
+
array(
|
49 |
+
'type' => Yoast_Notification::WARNING,
|
50 |
+
'id' => self::NOTIFICATION_ID,
|
51 |
+
'capabilities' => 'wpseo_manage_options',
|
52 |
+
'priority' => 0.8,
|
53 |
+
)
|
54 |
+
);
|
55 |
+
}
|
56 |
+
}
|
admin/links/class-link-content-processor.php
ADDED
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the content processor. It will extract links from the content and
|
8 |
+
* saves them for the given post id.
|
9 |
+
*/
|
10 |
+
class WPSEO_Link_Content_Processor {
|
11 |
+
|
12 |
+
/** @var WPSEO_Link_Storage */
|
13 |
+
protected $storage;
|
14 |
+
|
15 |
+
/** @var WPSEO_Meta_Storage */
|
16 |
+
private $count_storage;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Sets an instance of a storage object.
|
20 |
+
*
|
21 |
+
* @param WPSEO_Link_Storage $storage The storage object to use.
|
22 |
+
* @param WPSEO_Meta_Storage $count_storage The storage object for the link
|
23 |
+
* counts.
|
24 |
+
*/
|
25 |
+
public function __construct( WPSEO_Link_Storage $storage, WPSEO_Meta_Storage $count_storage ) {
|
26 |
+
$this->storage = $storage;
|
27 |
+
$this->count_storage = $count_storage;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Process the content for the given post id.
|
32 |
+
*
|
33 |
+
* @param int $post_id The post id.
|
34 |
+
* @param string $content The content to process.
|
35 |
+
*/
|
36 |
+
public function process( $post_id, $content ) {
|
37 |
+
$link_extractor = new WPSEO_Link_Extractor( $content );
|
38 |
+
$link_processor = new WPSEO_Link_Factory(
|
39 |
+
new WPSEO_Link_Type_Classifier( home_url() ),
|
40 |
+
new WPSEO_Link_Internal_Lookup(),
|
41 |
+
new WPSEO_Link_Filter( get_permalink( $post_id ) )
|
42 |
+
);
|
43 |
+
|
44 |
+
$extracted_links = $link_extractor->extract();
|
45 |
+
$links = $link_processor->build( $extracted_links );
|
46 |
+
|
47 |
+
$internal_links = array_filter( $links, array( $this, 'filter_internal_link' ) );
|
48 |
+
|
49 |
+
$stored_links = $this->get_stored_internal_links( $post_id );
|
50 |
+
|
51 |
+
$this->storage->cleanup( $post_id );
|
52 |
+
$this->storage->save_links( $post_id, $links );
|
53 |
+
|
54 |
+
$this->update_link_counts( $post_id, count( $internal_links ), array_merge( $stored_links, $internal_links ) );
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Updates the link counts for the post and referenced posts.
|
59 |
+
*
|
60 |
+
* @param int $post_id Post to update link counts for.
|
61 |
+
* @param int|null $count Number of internal links.
|
62 |
+
* @param array $links Links to process for incoming link count update.
|
63 |
+
*/
|
64 |
+
public function update_link_counts( $post_id, $count, array $links ) {
|
65 |
+
$this->store_internal_link_count( $post_id, $count );
|
66 |
+
$this->update_incoming_links( $post_id, $links );
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Retrieves the stored internal links for the supplied post.
|
71 |
+
*
|
72 |
+
* @param int $post_id The post to fetch links for.
|
73 |
+
*
|
74 |
+
* @return WPSEO_Link[] List of internal links connected to the post.
|
75 |
+
*/
|
76 |
+
public function get_stored_internal_links( $post_id ) {
|
77 |
+
$links = $this->storage->get_links( $post_id );
|
78 |
+
return array_filter( $links, array( $this, 'filter_internal_link' ) );
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Filters on INTERNAL links.
|
83 |
+
*
|
84 |
+
* @param WPSEO_Link $link Link to test type of.
|
85 |
+
*
|
86 |
+
* @return bool True for internal link, false for external link.
|
87 |
+
*/
|
88 |
+
protected function filter_internal_link( WPSEO_Link $link ) {
|
89 |
+
return $link->get_type() === WPSEO_Link::TYPE_INTERNAL;
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Stores the total links for the post.
|
94 |
+
*
|
95 |
+
* @param int $post_id The post id.
|
96 |
+
* @param int $internal_link_count Total amount of links in the post.
|
97 |
+
*
|
98 |
+
* @return void
|
99 |
+
*/
|
100 |
+
protected function store_internal_link_count( $post_id, $internal_link_count ) {
|
101 |
+
$this->count_storage->save_meta_data( $post_id, array( 'internal_link_count' => $internal_link_count ) );
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Updates the incoming link count.
|
106 |
+
*
|
107 |
+
* @param int $post_id Post which is processed, this needs to be recalculated too.
|
108 |
+
* @param WPSEO_Link[] $links Links to update the incoming link count of.
|
109 |
+
*
|
110 |
+
* @return void
|
111 |
+
*/
|
112 |
+
protected function update_incoming_links( $post_id, $links ) {
|
113 |
+
$post_ids = $this->get_internal_post_ids( $links );
|
114 |
+
$post_ids = array_merge( array( $post_id ), $post_ids );
|
115 |
+
$this->count_storage->update_incoming_link_count( $post_ids, $this->storage );
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Extract the post IDs from the links.
|
120 |
+
*
|
121 |
+
* @param WPSEO_Link[] $links Links to update the incoming link count of.
|
122 |
+
*
|
123 |
+
* @return int[] List of post IDs.
|
124 |
+
*/
|
125 |
+
protected function get_internal_post_ids( $links ) {
|
126 |
+
$post_ids = array();
|
127 |
+
foreach ( $links as $link ) {
|
128 |
+
$post_ids[] = $link->get_target_post_id();
|
129 |
+
}
|
130 |
+
|
131 |
+
return array_filter( $post_ids );
|
132 |
+
}
|
133 |
+
}
|
admin/links/class-link-extractor.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the link extractor.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Extractor {
|
10 |
+
|
11 |
+
/** @var string */
|
12 |
+
protected $content;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Sets the content.
|
16 |
+
*
|
17 |
+
* @param string $content The content to extract the links from.
|
18 |
+
*/
|
19 |
+
public function __construct( $content ) {
|
20 |
+
$this->content = $content;
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Extracts the hrefs from the content and returns them as an array.
|
25 |
+
*
|
26 |
+
* @return array All the extracted links
|
27 |
+
*/
|
28 |
+
public function extract() {
|
29 |
+
$links = array();
|
30 |
+
|
31 |
+
if ( strpos( $this->content, 'href' ) === false ) {
|
32 |
+
return $links;
|
33 |
+
}
|
34 |
+
|
35 |
+
$regexp = '<a\s[^>]*href=("??)([^" >]*?)\\1[^>]*>';
|
36 |
+
|
37 |
+
// Used modifiers iU to match case insensitive and make greedy quantifiers lazy.
|
38 |
+
if ( preg_match_all( "/$regexp/iU", $this->content, $matches, PREG_SET_ORDER ) ) {
|
39 |
+
foreach ( $matches as $match ) {
|
40 |
+
$links[] = trim( $match[2], "'" );
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
return $links;
|
45 |
+
}
|
46 |
+
}
|
admin/links/class-link-factory.php
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the conversion from array with string links into WPSEO_Link objects.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Factory {
|
10 |
+
|
11 |
+
/** @var WPSEO_Link_Type_Classifier */
|
12 |
+
protected $classifier;
|
13 |
+
|
14 |
+
/** @var WPSEO_Link_Internal_Lookup */
|
15 |
+
|
16 |
+
protected $internal_lookup;
|
17 |
+
|
18 |
+
/** @var WPSEO_Link_Filter */
|
19 |
+
protected $filter;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Sets the dependencies for this object.
|
23 |
+
*
|
24 |
+
* @param WPSEO_Link_Type_Classifier $classifier The classifier to use.
|
25 |
+
* @param WPSEO_Link_Internal_Lookup $internal_lookup The internal lookup to use.
|
26 |
+
* @param WPSEO_Link_Filter $filter The link filter.
|
27 |
+
*/
|
28 |
+
public function __construct( WPSEO_Link_Type_Classifier $classifier, WPSEO_Link_Internal_Lookup $internal_lookup, WPSEO_Link_Filter $filter ) {
|
29 |
+
$this->classifier = $classifier;
|
30 |
+
$this->internal_lookup = $internal_lookup;
|
31 |
+
$this->filter = $filter;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Formats an array of links to WPSEO_Link object.
|
36 |
+
*
|
37 |
+
* @param array $extracted_links The links for format.
|
38 |
+
*
|
39 |
+
* @return WPSEO_Link[] The formatted links.
|
40 |
+
*/
|
41 |
+
public function build( array $extracted_links ) {
|
42 |
+
$extracted_links = array_map( array( $this, 'build_link' ), $extracted_links );
|
43 |
+
$filtered_links = array_filter( $extracted_links, array(
|
44 |
+
$this->filter,
|
45 |
+
'internal_link_with_fragment_filter',
|
46 |
+
) );
|
47 |
+
|
48 |
+
return $filtered_links;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Builds the link.
|
53 |
+
*
|
54 |
+
* @param string $link The link to build.
|
55 |
+
*
|
56 |
+
* @return WPSEO_Link The built link.
|
57 |
+
*/
|
58 |
+
public function build_link( $link ) {
|
59 |
+
$link_type = $this->classifier->classify( $link );
|
60 |
+
|
61 |
+
$target_post_id = 0;
|
62 |
+
if ( $link_type === WPSEO_Link::TYPE_INTERNAL ) {
|
63 |
+
$target_post_id = $this->internal_lookup->lookup( $link );
|
64 |
+
}
|
65 |
+
|
66 |
+
return self::get_link( $link, $target_post_id, $link_type );
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Returns the link object.
|
71 |
+
*
|
72 |
+
* @param string $url The URL of the link.
|
73 |
+
* @param int $target_post_id The target post ID.
|
74 |
+
* @param string $type The link type.
|
75 |
+
*
|
76 |
+
* @return WPSEO_Link Generated link object.
|
77 |
+
*/
|
78 |
+
public static function get_link( $url, $target_post_id, $type ) {
|
79 |
+
return new WPSEO_Link( $url, $target_post_id, $type );
|
80 |
+
}
|
81 |
+
}
|
admin/links/class-link-filter.php
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the filter for filtering links
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Filter {
|
10 |
+
|
11 |
+
/** @var string|null */
|
12 |
+
protected $current_page_path;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Sets the current page path
|
16 |
+
*
|
17 |
+
* @param string $current_page The current page.
|
18 |
+
*/
|
19 |
+
public function __construct( $current_page = '' ) {
|
20 |
+
$this->current_page_path = untrailingslashit( WPSEO_Link_Utils::get_url_part( $current_page, 'path' ) );
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Filters all internal links that contains an fragment in the URL.
|
25 |
+
*
|
26 |
+
* @param WPSEO_Link $link The link that might be filtered.
|
27 |
+
*
|
28 |
+
* @return bool False when url contains a fragment.
|
29 |
+
*/
|
30 |
+
public function internal_link_with_fragment_filter( WPSEO_Link $link ) {
|
31 |
+
// When the type is external.
|
32 |
+
if ( $link->get_type() === WPSEO_Link::TYPE_EXTERNAL ) {
|
33 |
+
return true;
|
34 |
+
}
|
35 |
+
|
36 |
+
$url_parts = wp_parse_url( $link->get_url() );
|
37 |
+
|
38 |
+
if ( isset( $url_parts['path'] ) ) {
|
39 |
+
return ! $this->is_current_page( untrailingslashit( $url_parts['path'] ) );
|
40 |
+
}
|
41 |
+
|
42 |
+
return ( ! isset( $url_parts['fragment'] ) && ! isset( $url_parts['query'] ) );
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Is the url path the same as the current page path.
|
47 |
+
*
|
48 |
+
* @param string $url_path The url path.
|
49 |
+
*
|
50 |
+
* @return bool True when path is equal to the current page path.
|
51 |
+
*/
|
52 |
+
protected function is_current_page( $url_path ) {
|
53 |
+
return ( ! empty( $url_path ) && $url_path === $this->current_page_path );
|
54 |
+
}
|
55 |
+
}
|
admin/links/class-link-installer.php
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents installer for the link module.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Installer {
|
10 |
+
|
11 |
+
/** @var WPSEO_Installable[] */
|
12 |
+
protected $installables = array();
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Sets the installables.
|
16 |
+
*/
|
17 |
+
public function __construct() {
|
18 |
+
$this->installables = array(
|
19 |
+
new WPSEO_Link_Storage(),
|
20 |
+
new WPSEO_Meta_Storage(),
|
21 |
+
);
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Runs the installation of the link module.
|
26 |
+
*/
|
27 |
+
public function install() {
|
28 |
+
foreach ( $this->get_installables() as $installable ) {
|
29 |
+
$installable->install();
|
30 |
+
}
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Adds a installable object to the installer.
|
35 |
+
*
|
36 |
+
* @param WPSEO_Installable $installable The installable object.
|
37 |
+
*/
|
38 |
+
public function add_installable( WPSEO_Installable $installable ) {
|
39 |
+
$this->installables[] = $installable;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Returns the installable objects.
|
44 |
+
*
|
45 |
+
* @return WPSEO_Installable[] The installables to use.
|
46 |
+
*/
|
47 |
+
protected function get_installables() {
|
48 |
+
return $this->installables;
|
49 |
+
}
|
50 |
+
}
|
admin/links/class-link-internal-lookup.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the internal link lookup. This class tries get the postid for a given internal link.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Internal_Lookup {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Gets a post id for the given link for the given type. If type is outbound it returns 0 as post id.
|
13 |
+
*
|
14 |
+
* @param string $link The link to populate.
|
15 |
+
*
|
16 |
+
* @return int The post id belongs to given link if link is internal.
|
17 |
+
*/
|
18 |
+
public function lookup( $link ) {
|
19 |
+
// @codingStandardsIgnoreStart
|
20 |
+
return url_to_postid( $link );
|
21 |
+
// @codingStandardsIgnoreEnd
|
22 |
+
}
|
23 |
+
}
|
admin/links/class-link-notifier.php
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Premium
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Repressents the notifier for adding link indexing notification to the dashboard.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Notifier {
|
10 |
+
|
11 |
+
const NOTIFICATION_ID = 'wpseo-reindex-links';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Registers all hooks to WordPress
|
15 |
+
*/
|
16 |
+
public function register_hooks() {
|
17 |
+
if ( filter_input( INPUT_GET, 'page' ) === 'wpseo_dashboard' ) {
|
18 |
+
add_action( 'admin_init', array( $this, 'cleanup_notification' ) );
|
19 |
+
}
|
20 |
+
|
21 |
+
if ( ! wp_next_scheduled( self::NOTIFICATION_ID ) ) {
|
22 |
+
wp_schedule_event( time(), 'daily', self::NOTIFICATION_ID );
|
23 |
+
}
|
24 |
+
|
25 |
+
add_action( self::NOTIFICATION_ID, array( $this, 'manage_notification' ) );
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Removes the notification when it is set and the amount of unindexed items is lower than the threshold.
|
30 |
+
*/
|
31 |
+
public function cleanup_notification() {
|
32 |
+
if ( ! $this->has_notification() || $this->requires_notification() ) {
|
33 |
+
return;
|
34 |
+
}
|
35 |
+
|
36 |
+
$this->remove_notification( $this->get_notification() );
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Adds the notification when it isn't set already and the amount of unindexed items is greater than the set.
|
41 |
+
* threshold.
|
42 |
+
*/
|
43 |
+
public function manage_notification() {
|
44 |
+
if ( $this->has_notification() || ! $this->requires_notification() ) {
|
45 |
+
return;
|
46 |
+
}
|
47 |
+
|
48 |
+
$this->add_notification( $this->get_notification() );
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Checks if the notification has been set already.
|
53 |
+
*
|
54 |
+
* @return bool True when there is a notification.
|
55 |
+
*/
|
56 |
+
public function has_notification() {
|
57 |
+
$notification = Yoast_Notification_Center::get()->get_notification_by_id( self::NOTIFICATION_ID );
|
58 |
+
|
59 |
+
return $notification instanceof Yoast_Notification;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Adds a notification to the notification center.
|
64 |
+
*
|
65 |
+
* @param Yoast_Notification $notification The notification to add.
|
66 |
+
*/
|
67 |
+
protected function add_notification( Yoast_Notification $notification ) {
|
68 |
+
Yoast_Notification_Center::get()->add_notification( $notification );
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Removes the notification from the notification center.
|
73 |
+
*
|
74 |
+
* @param Yoast_Notification $notification The notification to remove.
|
75 |
+
*/
|
76 |
+
protected function remove_notification( Yoast_Notification $notification ) {
|
77 |
+
Yoast_Notification_Center::get()->remove_notification( $notification );
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Returns an instance of the notification.
|
82 |
+
*
|
83 |
+
* @return Yoast_Notification The notification to show.
|
84 |
+
*/
|
85 |
+
protected function get_notification() {
|
86 |
+
return new Yoast_Notification(
|
87 |
+
sprintf(
|
88 |
+
/* translators: 1: link to yoast.com post about internal linking suggestion. 2: is anchor closing. 3: button to the recalculation option. 4: closing button */
|
89 |
+
__(
|
90 |
+
'To make sure all the links in your texts are counted, we need to analyze all your texts.
|
91 |
+
All you have to do is press the following button and we\'ll go through all your texts for you.
|
92 |
+
|
93 |
+
%3$sCount links%4$s
|
94 |
+
|
95 |
+
The Text link counter feature provides insights in how many links are found in your text and how many links are referring to your text. This is very helpful when you are improving your %1$sinternal linking%2$s.',
|
96 |
+
'wordpress-seo'
|
97 |
+
),
|
98 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/15m' ) . '" target="_blank">',
|
99 |
+
'</a>',
|
100 |
+
'<button type="button" id="noticeRunLinkIndex" class="button">',
|
101 |
+
'</button>'
|
102 |
+
),
|
103 |
+
array(
|
104 |
+
'type' => Yoast_Notification::WARNING,
|
105 |
+
'id' => self::NOTIFICATION_ID,
|
106 |
+
'capabilities' => 'wpseo_manage_options',
|
107 |
+
'priority' => 0.8,
|
108 |
+
)
|
109 |
+
);
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Checks if the unindexed threshold is exceeded.
|
114 |
+
*
|
115 |
+
* @return bool True when the threshold is exceeded.
|
116 |
+
*/
|
117 |
+
protected function requires_notification() {
|
118 |
+
$post_types = apply_filters( 'wpseo_link_count_post_types', WPSEO_Post_Type::get_accessible_post_types() );
|
119 |
+
if ( ! is_array( $post_types ) ) {
|
120 |
+
return false;
|
121 |
+
}
|
122 |
+
|
123 |
+
return WPSEO_Link_Query::has_unprocessed_posts( $post_types );
|
124 |
+
}
|
125 |
+
}
|
admin/links/class-link-query.php
ADDED
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Database helper class.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Query {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Determine if there are any unprocessed public posts.
|
13 |
+
*
|
14 |
+
* @param array $post_types List of post types to check with.
|
15 |
+
*
|
16 |
+
* @return bool True if unprocessed posts are found, false if none are found.
|
17 |
+
*/
|
18 |
+
public static function has_unprocessed_posts( array $post_types ) {
|
19 |
+
global $wpdb;
|
20 |
+
|
21 |
+
if ( empty( $post_types ) ) {
|
22 |
+
return false;
|
23 |
+
}
|
24 |
+
|
25 |
+
$post_types = self::format_post_types( $post_types );
|
26 |
+
$count_table = self::get_count_table_name();
|
27 |
+
|
28 |
+
// Get any object which has not got the processed meta key.
|
29 |
+
$query = '
|
30 |
+
SELECT ID
|
31 |
+
FROM ' . $wpdb->posts . ' AS posts
|
32 |
+
LEFT JOIN ' . $count_table . ' AS yoast_meta
|
33 |
+
ON yoast_meta.object_id = posts.ID
|
34 |
+
WHERE posts.post_status = "publish"
|
35 |
+
AND posts.post_type IN ( ' . $post_types . ' )
|
36 |
+
AND yoast_meta.internal_link_count IS NULL
|
37 |
+
LIMIT 1';
|
38 |
+
|
39 |
+
// If anything is found, we have unprocessed posts.
|
40 |
+
$results = $wpdb->get_var( $query );
|
41 |
+
|
42 |
+
return ! empty( $results );
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Filter out posts that have not been processed yet.
|
47 |
+
*
|
48 |
+
* @param array $post_ids Post IDs to filter.
|
49 |
+
*
|
50 |
+
* @return array
|
51 |
+
*/
|
52 |
+
public static function filter_unprocessed_posts( array $post_ids ) {
|
53 |
+
global $wpdb;
|
54 |
+
|
55 |
+
$post_ids = array_filter( $post_ids );
|
56 |
+
if ( empty( $post_ids ) || array() === $post_ids ) {
|
57 |
+
return $post_ids;
|
58 |
+
}
|
59 |
+
|
60 |
+
$count_table = self::get_count_table_name();
|
61 |
+
|
62 |
+
$query = '
|
63 |
+
SELECT object_id
|
64 |
+
FROM ' . $count_table . '
|
65 |
+
WHERE object_id IN ( ' . implode( ',', $post_ids ) . ' )
|
66 |
+
';
|
67 |
+
|
68 |
+
$results = $wpdb->get_results( $query, ARRAY_A );
|
69 |
+
|
70 |
+
return array_map( 'intval', wp_list_pluck( $results, 'object_id' ) );
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Returns a limited set of unindexed posts.
|
75 |
+
*
|
76 |
+
* @param array $post_types The post type.
|
77 |
+
* @param int $limit The limit for the resultset.
|
78 |
+
*
|
79 |
+
* @return array|null|object The set of unindexed posts.
|
80 |
+
*/
|
81 |
+
public static function get_unprocessed_posts( array $post_types, $limit = 5 ) {
|
82 |
+
global $wpdb;
|
83 |
+
|
84 |
+
$count_table = self::get_count_table_name();
|
85 |
+
$post_types = self::format_post_types( $post_types );
|
86 |
+
|
87 |
+
// @codingStandardsIgnoreStart
|
88 |
+
$query = 'SELECT posts.ID, posts.post_content
|
89 |
+
FROM ' . $wpdb->posts . ' AS posts
|
90 |
+
LEFT JOIN ' . $count_table . ' AS yoast_meta
|
91 |
+
ON yoast_meta.object_id = posts.ID
|
92 |
+
WHERE posts.post_status = "publish"
|
93 |
+
AND posts.post_type IN ( ' . $post_types . ' )
|
94 |
+
AND yoast_meta.internal_link_count IS NULL
|
95 |
+
LIMIT %d
|
96 |
+
';
|
97 |
+
// @codingStandardsIgnoreEnd
|
98 |
+
|
99 |
+
return $wpdb->get_results(
|
100 |
+
$wpdb->prepare( $query, $limit )
|
101 |
+
);
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Returns the total amount of unindexed posts for given post type.
|
106 |
+
*
|
107 |
+
* @param array $post_types The post types.
|
108 |
+
*
|
109 |
+
* @return int The total of unindexed posts.
|
110 |
+
*/
|
111 |
+
public static function get_unprocessed_count( array $post_types ) {
|
112 |
+
global $wpdb;
|
113 |
+
|
114 |
+
if ( empty( $post_types ) ) {
|
115 |
+
return 0;
|
116 |
+
}
|
117 |
+
|
118 |
+
$count_table = self::get_count_table_name();
|
119 |
+
$post_types = self::format_post_types( $post_types );
|
120 |
+
|
121 |
+
// @codingStandardsIgnoreStart
|
122 |
+
$query = '
|
123 |
+
SELECT COUNT( posts.ID )
|
124 |
+
FROM ' . $wpdb->posts . ' AS posts
|
125 |
+
LEFT JOIN ' . $count_table . ' AS yoast_meta
|
126 |
+
ON yoast_meta.object_id = posts.ID
|
127 |
+
WHERE posts.post_status = "publish"
|
128 |
+
AND posts.post_type IN ( ' . $post_types . ' )
|
129 |
+
AND yoast_meta.internal_link_count IS NULL';
|
130 |
+
// @codingStandardsIgnoreEnd
|
131 |
+
|
132 |
+
return (int) $wpdb->get_var( $query );
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Returns the table name where the counts are stored.
|
137 |
+
*
|
138 |
+
* @return string
|
139 |
+
*/
|
140 |
+
protected static function get_count_table_name() {
|
141 |
+
$storage = new WPSEO_Meta_Storage();
|
142 |
+
return $storage->get_table_name();
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Formats an array with post types as an SQL string.
|
147 |
+
*
|
148 |
+
* @param array $post_types The post types to format.
|
149 |
+
*
|
150 |
+
* @return string Post types formatted for use in SQL statement.
|
151 |
+
*/
|
152 |
+
protected static function format_post_types( array $post_types ) {
|
153 |
+
$sanitized_post_types = array_map( 'esc_sql', $post_types );
|
154 |
+
$post_types = sprintf( '"%s"', implode( '", "', $sanitized_post_types ) );
|
155 |
+
|
156 |
+
return $post_types;
|
157 |
+
}
|
158 |
+
}
|
admin/links/class-link-reindex-dashboard.php
ADDED
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links\Reindex
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Handles the reindexing of links interface in the Dashboard.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Reindex_Dashboard {
|
10 |
+
/** @var array Public post types to scan for unprocessed items */
|
11 |
+
protected $public_post_types = array();
|
12 |
+
|
13 |
+
/** @var int Number of unprocessed items */
|
14 |
+
protected $unprocessed = 0;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Registers all hooks to WordPress.
|
18 |
+
*
|
19 |
+
* @return void
|
20 |
+
*/
|
21 |
+
public function register_hooks() {
|
22 |
+
if ( ! $this->is_dashboard_page() ) {
|
23 |
+
return;
|
24 |
+
}
|
25 |
+
|
26 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'calculate_unprocessed' ), 9 );
|
27 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ), 10 );
|
28 |
+
|
29 |
+
add_action( 'admin_footer', array( $this, 'modal_box' ), 20 );
|
30 |
+
|
31 |
+
add_action( 'wpseo_tools_overview_list_items', array( $this, 'show_tools_overview_item' ), 10 );
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Calculates the number of unprocessed items per post type.
|
36 |
+
*
|
37 |
+
* @return void
|
38 |
+
*/
|
39 |
+
public function calculate_unprocessed() {
|
40 |
+
$this->public_post_types = apply_filters( 'wpseo_link_count_post_types', WPSEO_Post_Type::get_accessible_post_types() );
|
41 |
+
|
42 |
+
if ( is_array( $this->public_post_types ) && $this->public_post_types !== array() ) {
|
43 |
+
$this->unprocessed = WPSEO_Link_Query::get_unprocessed_count( $this->public_post_types );
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Adds an item to the tools page overview list.
|
49 |
+
*
|
50 |
+
* @return void
|
51 |
+
*/
|
52 |
+
public function show_tools_overview_item() {
|
53 |
+
echo '<li>';
|
54 |
+
echo '<strong>' . esc_html__( 'Text link counter', 'wordpress-seo' ) . '</strong><br/>';
|
55 |
+
|
56 |
+
if ( ! $this->has_unprocessed() ) {
|
57 |
+
echo $this->message_already_indexed();
|
58 |
+
}
|
59 |
+
|
60 |
+
if ( $this->has_unprocessed() ) {
|
61 |
+
printf( '<span id="reindexLinks">%s</span>', $this->message_start_indexing() );
|
62 |
+
}
|
63 |
+
|
64 |
+
echo '</li>';
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Add the indexing interface for links to the dashboard.
|
69 |
+
*
|
70 |
+
* @deprecated 7.0
|
71 |
+
*
|
72 |
+
* @return void
|
73 |
+
*/
|
74 |
+
public function add_link_index_interface() {
|
75 |
+
_deprecated_function( __METHOD__, 'WPSEO 7.0' );
|
76 |
+
|
77 |
+
$html = '';
|
78 |
+
$html .= '<h2>' . esc_html__( 'Text link counter', 'wordpress-seo' ) . '</h2>';
|
79 |
+
$html .= '<p>' . sprintf(
|
80 |
+
/* translators: 1: link to yoast.com post about internal linking suggestion. 4: is Yoast.com 3: is anchor closing. */
|
81 |
+
__( 'The links in all your public texts need to be counted. This will provide insights of which texts need more links to them. If you want to know more about the why and how of internal linking, check out %1$sthe article about internal linking on %2$s%3$s.', 'wordpress-seo' ),
|
82 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/15n' ) . '" target="_blank">',
|
83 |
+
'Yoast.com',
|
84 |
+
'</a>'
|
85 |
+
) . '</p>';
|
86 |
+
|
87 |
+
if ( ! $this->has_unprocessed() ) {
|
88 |
+
$html .= '<p>' . $this->message_already_indexed() . '</p>';
|
89 |
+
}
|
90 |
+
|
91 |
+
if ( $this->has_unprocessed() ) {
|
92 |
+
$html .= '<p id="reindexLinks">' . $this->message_start_indexing() . '</p>';
|
93 |
+
}
|
94 |
+
|
95 |
+
$html .= '<br />';
|
96 |
+
|
97 |
+
echo $html;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Generates the model box.
|
102 |
+
*
|
103 |
+
* @return void
|
104 |
+
*/
|
105 |
+
public function modal_box() {
|
106 |
+
if ( ! $this->is_dashboard_page() ) {
|
107 |
+
return;
|
108 |
+
}
|
109 |
+
|
110 |
+
// Adding the thickbox.
|
111 |
+
add_thickbox();
|
112 |
+
|
113 |
+
$blocks = array();
|
114 |
+
|
115 |
+
if ( ! $this->has_unprocessed() ) {
|
116 |
+
$inner_text = sprintf( '<p>%s</p>',
|
117 |
+
esc_html__( 'All your texts are already counted, there is no need to count them again.', 'wordpress-seo' )
|
118 |
+
);
|
119 |
+
}
|
120 |
+
|
121 |
+
if ( $this->has_unprocessed() ) {
|
122 |
+
$progress = sprintf(
|
123 |
+
/* translators: 1: expands to a <span> containing the number of items recalculated. 2: expands to a <strong> containing the total number of items. */
|
124 |
+
__( 'Text %1$s of %2$s processed.', 'wordpress-seo' ),
|
125 |
+
'<span id="wpseo_count_index_links">0</span>',
|
126 |
+
sprintf( '<strong id="wpseo_count_total">%d</strong>', $this->get_unprocessed_count() )
|
127 |
+
);
|
128 |
+
|
129 |
+
$inner_text = '<div id="wpseo_index_links_progressbar" class="wpseo-progressbar"></div>';
|
130 |
+
$inner_text .= sprintf( '<p>%s</p>', $progress );
|
131 |
+
}
|
132 |
+
|
133 |
+
$blocks[] = sprintf( '<div><p>%s</p>%s</div>',
|
134 |
+
esc_html__( 'Counting links in your texts', 'wordpress-seo' ),
|
135 |
+
$inner_text
|
136 |
+
);
|
137 |
+
?>
|
138 |
+
<div id="wpseo_index_links_wrapper" class="hidden">
|
139 |
+
<?php echo implode( '<hr />', $blocks ); ?>
|
140 |
+
<button onclick="tb_remove();" type="button"
|
141 |
+
class="button"><?php esc_html_e( 'Stop counting', 'wordpress-seo' ); ?></button>
|
142 |
+
</div>
|
143 |
+
<?php
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Enqueues site wide analysis script
|
148 |
+
*
|
149 |
+
* @return void
|
150 |
+
*/
|
151 |
+
public function enqueue() {
|
152 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
153 |
+
$asset_manager->enqueue_script( 'reindex-links' );
|
154 |
+
|
155 |
+
$data = array(
|
156 |
+
'amount' => $this->get_unprocessed_count(),
|
157 |
+
'restApi' => array(
|
158 |
+
'root' => esc_url_raw( rest_url() ),
|
159 |
+
'endpoint' => WPSEO_Link_Reindex_Post_Endpoint::REST_NAMESPACE . '/' . WPSEO_Link_Reindex_Post_Endpoint::ENDPOINT_QUERY,
|
160 |
+
'nonce' => wp_create_nonce( 'wp_rest' ),
|
161 |
+
),
|
162 |
+
'message' => array(
|
163 |
+
'indexingCompleted' => $this->message_already_indexed(),
|
164 |
+
),
|
165 |
+
'l10n' => array(
|
166 |
+
'calculationInProgress' => __( 'Calculation in progress...', 'wordpress-seo' ),
|
167 |
+
'calculationCompleted' => __( 'Calculation completed.', 'wordpress-seo' ),
|
168 |
+
),
|
169 |
+
);
|
170 |
+
|
171 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'reindex-links', 'yoastReindexLinksData', array( 'data' => $data ) );
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Checks if the current page is the dashboard page.
|
176 |
+
*
|
177 |
+
* @return bool True when current page is the dashboard page.
|
178 |
+
*/
|
179 |
+
protected function is_dashboard_page() {
|
180 |
+
return ( filter_input( INPUT_GET, 'page' ) === 'wpseo_tools' );
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Retrieves the string to display when everything has been indexed.
|
185 |
+
*
|
186 |
+
* @return string The message to show when everything has been indexed.
|
187 |
+
*/
|
188 |
+
public function message_already_indexed() {
|
189 |
+
return '<span class="wpseo-checkmark-ok-icon"></span>' . esc_html__( 'Good job! All the links in your texts have been counted.', 'wordpress-seo' );
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* Returns if there are unprocessed items
|
194 |
+
*
|
195 |
+
* @return bool True if there are unprocessed items.
|
196 |
+
*/
|
197 |
+
public function has_unprocessed() {
|
198 |
+
return $this->unprocessed > 0;
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Returns the number of unprocessed items.
|
203 |
+
*
|
204 |
+
* @return int Number of unprocessed items.
|
205 |
+
*/
|
206 |
+
public function get_unprocessed_count() {
|
207 |
+
return $this->unprocessed;
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Retrieves the message to show starting indexation.
|
212 |
+
*
|
213 |
+
* @return string The message.
|
214 |
+
*/
|
215 |
+
public function message_start_indexing() {
|
216 |
+
return sprintf(
|
217 |
+
'<a id="openLinkIndexing" href="#TB_inline?width=600&height=%1$s&inlineId=wpseo_index_links_wrapper" title="%2$s" class="btn button yoast-js-index-links yoast-js-calculate-index-links--all thickbox">%2$s</a>',
|
218 |
+
175,
|
219 |
+
esc_attr__( 'Count links in your texts', 'wordpress-seo' )
|
220 |
+
);
|
221 |
+
}
|
222 |
+
}
|
admin/links/class-link-reindex-post-endpoint.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links\Reindex
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Link_Reindex_Post_Endpoint
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Reindex_Post_Endpoint {
|
10 |
+
|
11 |
+
const REST_NAMESPACE = 'yoast/v1';
|
12 |
+
const ENDPOINT_QUERY = 'reindex_posts';
|
13 |
+
|
14 |
+
const CAPABILITY_RETRIEVE = 'edit_posts';
|
15 |
+
|
16 |
+
/** @var WPSEO_Link_Reindex_Post_Service */
|
17 |
+
protected $service;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* WPSEO_Link_Reindex_Post_Endpoint constructor.
|
21 |
+
*
|
22 |
+
* @param WPSEO_Link_Reindex_Post_Service $service The service to handle the requests to the endpoint.
|
23 |
+
*/
|
24 |
+
public function __construct( WPSEO_Link_Reindex_Post_Service $service ) {
|
25 |
+
$this->service = $service;
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Register the REST endpoint to WordPress.
|
30 |
+
*/
|
31 |
+
public function register() {
|
32 |
+
register_rest_route( self::REST_NAMESPACE, self::ENDPOINT_QUERY, array(
|
33 |
+
'methods' => 'GET',
|
34 |
+
'callback' => array(
|
35 |
+
$this->service,
|
36 |
+
'reindex',
|
37 |
+
),
|
38 |
+
'permission_callback' => array(
|
39 |
+
$this,
|
40 |
+
'can_retrieve_data',
|
41 |
+
),
|
42 |
+
) );
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Determines if the current user is allowed to use this endpoint.
|
47 |
+
*
|
48 |
+
* @return bool
|
49 |
+
*/
|
50 |
+
public function can_retrieve_data() {
|
51 |
+
return current_user_can( self::CAPABILITY_RETRIEVE );
|
52 |
+
}
|
53 |
+
}
|
54 |
+
|
admin/links/class-link-reindex-post-service.php
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links\Reindex
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Link_Reindex_Post_Service
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Reindex_Post_Service {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Reindexes the unprocessed posts by REST request.
|
13 |
+
*
|
14 |
+
* @return WP_REST_Response The response object.
|
15 |
+
*/
|
16 |
+
public function reindex() {
|
17 |
+
return new WP_REST_Response( $this->process_posts() );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Returns the posts.
|
22 |
+
*
|
23 |
+
* @return int The total amount of unprocessed posts.
|
24 |
+
*/
|
25 |
+
protected function process_posts() {
|
26 |
+
if ( ! $this->is_processable() ) {
|
27 |
+
return 0;
|
28 |
+
}
|
29 |
+
|
30 |
+
$posts = $this->get_unprocessed_posts();
|
31 |
+
array_walk( $posts, array( $this, 'process_post' ) );
|
32 |
+
|
33 |
+
return count( $posts );
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Returns all unprocessed posts.
|
38 |
+
*
|
39 |
+
* @return array The unprocessed posts.
|
40 |
+
*/
|
41 |
+
protected function get_unprocessed_posts() {
|
42 |
+
$post_types = apply_filters( 'wpseo_link_count_post_types', WPSEO_Post_Type::get_accessible_post_types() );
|
43 |
+
if ( ! is_array( $post_types ) ) {
|
44 |
+
return array();
|
45 |
+
}
|
46 |
+
return WPSEO_Link_Query::get_unprocessed_posts( $post_types );
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Checks if the required tables are accessible.
|
51 |
+
*
|
52 |
+
* @return bool True when the tables are accessible.
|
53 |
+
*/
|
54 |
+
protected function is_processable() {
|
55 |
+
return WPSEO_Link_Table_Accessible::is_accessible() && WPSEO_Meta_Table_Accessible::is_accessible();
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Processes the post.
|
60 |
+
*
|
61 |
+
* @param stdObject $post The post to process.
|
62 |
+
*
|
63 |
+
* @return void
|
64 |
+
*/
|
65 |
+
protected function process_post( $post ) {
|
66 |
+
// Some plugins might output data, so let's buffer this to prevent wrong responses.
|
67 |
+
ob_start();
|
68 |
+
|
69 |
+
// Apply the filters to have the same content as shown on the frontend.
|
70 |
+
$content = apply_filters( 'the_content', $post->post_content );
|
71 |
+
$content = str_replace( ']]>', ']]>', $content );
|
72 |
+
|
73 |
+
ob_end_clean();
|
74 |
+
|
75 |
+
$content_processor = $this->get_content_processor();
|
76 |
+
$content_processor->process( $post->ID, $content );
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Returns an instance of the content processor.
|
81 |
+
*
|
82 |
+
* @return WPSEO_Link_Content_Processor The instance of the link content processor.
|
83 |
+
*/
|
84 |
+
protected function get_content_processor() {
|
85 |
+
static $content_processor;
|
86 |
+
|
87 |
+
if ( $content_processor === null ) {
|
88 |
+
$content_processor = new WPSEO_Link_Content_Processor( new WPSEO_Link_Storage(), new WPSEO_Meta_Storage() );
|
89 |
+
}
|
90 |
+
|
91 |
+
return $content_processor;
|
92 |
+
}
|
93 |
+
}
|
admin/links/class-link-storage.php
ADDED
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the storage of an seo link.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Storage implements WPSEO_Installable {
|
10 |
+
|
11 |
+
const TABLE_NAME = 'yoast_seo_links';
|
12 |
+
|
13 |
+
/** @var WPSEO_Database_Proxy */
|
14 |
+
protected $database_proxy;
|
15 |
+
|
16 |
+
/** @var null|string */
|
17 |
+
protected $table_prefix;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Sets the table prefix.
|
21 |
+
*
|
22 |
+
* @param string $table_prefix Optional. The prefix to use for the table.
|
23 |
+
*/
|
24 |
+
public function __construct( $table_prefix = null ) {
|
25 |
+
if ( null === $table_prefix ) {
|
26 |
+
$table_prefix = $GLOBALS['wpdb']->get_blog_prefix();
|
27 |
+
}
|
28 |
+
|
29 |
+
$this->table_prefix = $table_prefix;
|
30 |
+
$this->database_proxy = new WPSEO_Database_Proxy( $GLOBALS['wpdb'], $this->get_table_name(), true );
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Returns the table name to use.
|
35 |
+
*
|
36 |
+
* @return string The table name.
|
37 |
+
*/
|
38 |
+
public function get_table_name() {
|
39 |
+
return $this->table_prefix . self::TABLE_NAME;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Creates the database table.
|
44 |
+
*
|
45 |
+
* @return boolean True if the table was created, false if something went wrong.
|
46 |
+
*/
|
47 |
+
public function install() {
|
48 |
+
return $this->database_proxy->create_table(
|
49 |
+
array(
|
50 |
+
'id bigint(20) unsigned NOT NULL AUTO_INCREMENT',
|
51 |
+
'url varchar(255) NOT NULL',
|
52 |
+
'post_id bigint(20) unsigned NOT NULL',
|
53 |
+
'target_post_id bigint(20) unsigned NOT NULL',
|
54 |
+
'type VARCHAR(8) NOT NULL',
|
55 |
+
),
|
56 |
+
array(
|
57 |
+
'PRIMARY KEY (id)',
|
58 |
+
'KEY link_direction (post_id, type)',
|
59 |
+
)
|
60 |
+
);
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Returns an array of links from the database.
|
65 |
+
*
|
66 |
+
* @param int $post_id The post to get the links for.
|
67 |
+
*
|
68 |
+
* @return WPSEO_Link[] The links connected to the post.
|
69 |
+
*/
|
70 |
+
public function get_links( $post_id ) {
|
71 |
+
global $wpdb;
|
72 |
+
|
73 |
+
$results = $this->database_proxy->get_results(
|
74 |
+
$wpdb->prepare( '
|
75 |
+
SELECT url, post_id, target_post_id, type
|
76 |
+
FROM ' . $this->get_table_name() . '
|
77 |
+
WHERE post_id = %d',
|
78 |
+
$post_id
|
79 |
+
)
|
80 |
+
);
|
81 |
+
|
82 |
+
if ( $this->database_proxy->has_error() ) {
|
83 |
+
WPSEO_Link_Table_Accessible::set_inaccessible();
|
84 |
+
}
|
85 |
+
|
86 |
+
$links = array();
|
87 |
+
foreach ( $results as $link ) {
|
88 |
+
$links[] = WPSEO_Link_Factory::get_link( $link->url, $link->target_post_id, $link->type );
|
89 |
+
}
|
90 |
+
|
91 |
+
return $links;
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Walks the given links to save them.
|
96 |
+
*
|
97 |
+
* @param integer $post_id The post id to save.
|
98 |
+
* @param WPSEO_Link[] $links The link to save.
|
99 |
+
*
|
100 |
+
* @return void
|
101 |
+
*/
|
102 |
+
public function save_links( $post_id, array $links ) {
|
103 |
+
array_walk( $links, array( $this, 'save_link' ), $post_id );
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Removes all records for given post_id.
|
108 |
+
*
|
109 |
+
* @param int $post_id The post_id to remove the records for.
|
110 |
+
*
|
111 |
+
* @return int|false The number of rows updated, or false on error.
|
112 |
+
*/
|
113 |
+
public function cleanup( $post_id ) {
|
114 |
+
$is_deleted = $this->database_proxy->delete(
|
115 |
+
array( 'post_id' => $post_id ),
|
116 |
+
array( '%d' )
|
117 |
+
);
|
118 |
+
|
119 |
+
if ( $is_deleted === false ) {
|
120 |
+
WPSEO_Link_Table_Accessible::set_inaccessible();
|
121 |
+
}
|
122 |
+
|
123 |
+
return $is_deleted;
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Inserts the link into the database.
|
128 |
+
*
|
129 |
+
* @param WPSEO_Link $link The link to save.
|
130 |
+
* @param int $link_key The link key. Unused.
|
131 |
+
* @param int $post_id The post id to save the link for.
|
132 |
+
*
|
133 |
+
* @return void
|
134 |
+
*/
|
135 |
+
protected function save_link( WPSEO_Link $link, $link_key, $post_id ) {
|
136 |
+
$inserted = $this->database_proxy->insert(
|
137 |
+
array(
|
138 |
+
'url' => $link->get_url(),
|
139 |
+
'post_id' => $post_id,
|
140 |
+
'target_post_id' => $link->get_target_post_id(),
|
141 |
+
'type' => $link->get_type(),
|
142 |
+
),
|
143 |
+
array( '%s', '%d', '%d', '%s' )
|
144 |
+
);
|
145 |
+
|
146 |
+
if ( $inserted === false ) {
|
147 |
+
WPSEO_Link_Table_Accessible::set_inaccessible();
|
148 |
+
}
|
149 |
+
}
|
150 |
+
}
|
admin/links/class-link-table-accessible-notifier.php
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the notice when the table is not accessible.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Table_Accessible_Notifier {
|
10 |
+
|
11 |
+
const NOTIFICATION_ID = 'wpseo-links-table-not-accessible';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Adds the notification to the notification center.
|
15 |
+
*/
|
16 |
+
public function add_notification() {
|
17 |
+
Yoast_Notification_Center::get()->add_notification( $this->get_notification() );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Removes the notification from the notification center.
|
22 |
+
*/
|
23 |
+
public function remove_notification() {
|
24 |
+
Yoast_Notification_Center::get()->remove_notification( $this->get_notification() );
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Returns the notification when the table is not accessible.
|
29 |
+
*
|
30 |
+
* @return Yoast_Notification The notification.
|
31 |
+
*/
|
32 |
+
protected function get_notification() {
|
33 |
+
return new Yoast_Notification(
|
34 |
+
sprintf(
|
35 |
+
/* translators: %1$s: Yoast SEO. %2$s: Version number of Yoast SEO. %3$s: link to knowledge base article about solving table issue. %4$s: is anchor closing. */
|
36 |
+
__(
|
37 |
+
'The <strong>Text link counter</strong> feature (introduced in %1$s %2$s) is currently disabled. For this feature to work %1$s needs to create a table in your database. We were unable to create this table automatically.
|
38 |
+
Please read the following %3$sknowledge base article%4$s to find out how to resolve this problem.',
|
39 |
+
'wordpress-seo'
|
40 |
+
),
|
41 |
+
'Yoast SEO',
|
42 |
+
'5.0',
|
43 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/15o' ) . '">',
|
44 |
+
'</a>'
|
45 |
+
),
|
46 |
+
array(
|
47 |
+
'type' => Yoast_Notification::WARNING,
|
48 |
+
'id' => self::NOTIFICATION_ID,
|
49 |
+
'capabilities' => 'wpseo_manage_options',
|
50 |
+
'priority' => 0.8,
|
51 |
+
)
|
52 |
+
);
|
53 |
+
}
|
54 |
+
}
|
admin/links/class-link-table-accessible.php
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the state of the table being accessible.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Table_Accessible {
|
10 |
+
|
11 |
+
const ACCESSIBLE = '0';
|
12 |
+
const INACCESSBILE = '1';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Checks if the given table name exists.
|
16 |
+
*
|
17 |
+
* @return bool True when table is accessible.
|
18 |
+
*/
|
19 |
+
public static function is_accessible() {
|
20 |
+
$value = get_transient( self::transient_name() );
|
21 |
+
|
22 |
+
// If the value is not set, check the table.
|
23 |
+
if ( false === $value ) {
|
24 |
+
return self::check_table();
|
25 |
+
}
|
26 |
+
|
27 |
+
return $value === self::ACCESSIBLE;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Sets the transient value to 1, to indicate the table is not accessible.
|
32 |
+
*
|
33 |
+
* @return void
|
34 |
+
*/
|
35 |
+
public static function set_inaccessible() {
|
36 |
+
set_transient( self::transient_name(), self::INACCESSBILE, HOUR_IN_SECONDS );
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Removes the transient.
|
41 |
+
*
|
42 |
+
* @return void
|
43 |
+
*/
|
44 |
+
public static function cleanup() {
|
45 |
+
delete_transient( self::transient_name() );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Sets the transient value to 0, to indicate the table is accessible.
|
50 |
+
*
|
51 |
+
* @return void
|
52 |
+
*/
|
53 |
+
protected static function set_accessible() {
|
54 |
+
/*
|
55 |
+
* Prefer to set a 0 timeout, but if the timeout was set before WordPress will not delete the transient
|
56 |
+
* correctly when overridden with a zero value.
|
57 |
+
*
|
58 |
+
* Setting a YEAR_IN_SECONDS instead.
|
59 |
+
*/
|
60 |
+
set_transient( self::transient_name(), self::ACCESSIBLE, YEAR_IN_SECONDS );
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Checks if the table exists if not, set the transient to indicate the inaccessible table.
|
65 |
+
*
|
66 |
+
* @return bool True if table is accessible.
|
67 |
+
*/
|
68 |
+
protected static function check_table() {
|
69 |
+
global $wpdb;
|
70 |
+
|
71 |
+
$storage = new WPSEO_Link_Storage();
|
72 |
+
if ( $wpdb->get_var( 'SHOW TABLES LIKE "' . $storage->get_table_name() . '"' ) !== $storage->get_table_name() ) {
|
73 |
+
self::set_inaccessible();
|
74 |
+
return false;
|
75 |
+
}
|
76 |
+
|
77 |
+
self::set_accessible();
|
78 |
+
return true;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Returns the name of the transient.
|
83 |
+
*
|
84 |
+
* @return string The name of the transient to use.
|
85 |
+
*/
|
86 |
+
protected static function transient_name() {
|
87 |
+
return 'wpseo_link_table_inaccessible';
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Checks if the table exists if not, set the transient to indicate the inaccessible table.
|
92 |
+
*
|
93 |
+
* @deprecated 6.0
|
94 |
+
*
|
95 |
+
* @return bool True if table is accessible.
|
96 |
+
*/
|
97 |
+
public static function check_table_is_accessible() {
|
98 |
+
_deprecated_function( __FUNCTION__, '6.0', __CLASS__ . '::is_accessible' );
|
99 |
+
return self::is_accessible();
|
100 |
+
}
|
101 |
+
}
|
admin/links/class-link-type-classifier.php
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the classifier for a link. Determines of a link is an outbound or internal one.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Type_Classifier {
|
10 |
+
|
11 |
+
/** @var string */
|
12 |
+
protected $base_host = '';
|
13 |
+
|
14 |
+
/** @var string */
|
15 |
+
protected $base_path = '';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Constructor setting the base url
|
19 |
+
*
|
20 |
+
* @param string $base_url The base url to set.
|
21 |
+
*/
|
22 |
+
public function __construct( $base_url ) {
|
23 |
+
|
24 |
+
$this->base_host = WPSEO_Link_Utils::get_url_part( $base_url, 'host' );
|
25 |
+
|
26 |
+
$base_path = WPSEO_Link_Utils::get_url_part( $base_url, 'path' );
|
27 |
+
if ( $base_path ) {
|
28 |
+
$this->base_path = trailingslashit( $base_path );
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Determines if the given link is an outbound or an internal link.
|
34 |
+
*
|
35 |
+
* @param string $link The link to classify.
|
36 |
+
*
|
37 |
+
* @return string Returns outbound or internal.
|
38 |
+
*/
|
39 |
+
public function classify( $link ) {
|
40 |
+
$url_parts = wp_parse_url( $link );
|
41 |
+
|
42 |
+
// Because parse_url may return false.
|
43 |
+
if ( ! is_array( $url_parts ) ) {
|
44 |
+
$url_parts = array();
|
45 |
+
}
|
46 |
+
|
47 |
+
if ( $this->contains_protocol( $url_parts ) && $this->is_external_link( $url_parts ) ) {
|
48 |
+
return WPSEO_Link::TYPE_EXTERNAL;
|
49 |
+
}
|
50 |
+
|
51 |
+
return WPSEO_Link::TYPE_INTERNAL;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Returns true when the link starts with https:// or http://
|
56 |
+
*
|
57 |
+
* @param array $url_parts The url parts to use.
|
58 |
+
*
|
59 |
+
* @return bool True if the url starts with a protocol.
|
60 |
+
*/
|
61 |
+
protected function contains_protocol( array $url_parts ) {
|
62 |
+
return isset( $url_parts['scheme'] ) && $url_parts['scheme'] !== null;
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Checks if the link contains the home_url. Returns true if this isn't the case.
|
67 |
+
*
|
68 |
+
* @param array $url_parts The url parts to use.
|
69 |
+
*
|
70 |
+
* @return bool True when the link doesn't contain the home url.
|
71 |
+
*/
|
72 |
+
protected function is_external_link( array $url_parts ) {
|
73 |
+
if ( isset( $url_parts['scheme'] ) && ! in_array( $url_parts['scheme'], array( 'http', 'https' ), true ) ) {
|
74 |
+
return true;
|
75 |
+
}
|
76 |
+
// When the base host is equal to the host.
|
77 |
+
if ( isset( $url_parts['host'] ) && $url_parts['host'] !== $this->base_host ) {
|
78 |
+
return true;
|
79 |
+
}
|
80 |
+
|
81 |
+
// There is no base path.
|
82 |
+
if ( empty( $this->base_path ) ) {
|
83 |
+
return false;
|
84 |
+
}
|
85 |
+
|
86 |
+
// When there is a path.
|
87 |
+
if ( isset( $url_parts['path'] ) ) {
|
88 |
+
return ( strpos( $url_parts['path'], $this->base_path ) === false );
|
89 |
+
}
|
90 |
+
|
91 |
+
return true;
|
92 |
+
}
|
93 |
+
}
|
admin/links/class-link-utils.php
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the utils for the links module.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Utils {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns all the supported public post types.
|
13 |
+
*
|
14 |
+
* @return array The supported public post types.
|
15 |
+
*/
|
16 |
+
public static function get_public_post_types() {
|
17 |
+
_deprecated_function( __METHOD__, '5.9', 'WPSEO_Post_Type::get_accessible_post_types' );
|
18 |
+
|
19 |
+
return WPSEO_Post_Type::filter_attachment_post_type( WPSEO_Post_Type::get_accessible_post_types() );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Returns the value that is part of the given url.
|
24 |
+
*
|
25 |
+
* @param string $url The url to parse.
|
26 |
+
* @param string $part The url part to use.
|
27 |
+
*
|
28 |
+
* @return string The value of the url part.
|
29 |
+
*/
|
30 |
+
public static function get_url_part( $url, $part ) {
|
31 |
+
$url_parts = wp_parse_url( $url );
|
32 |
+
|
33 |
+
if ( isset( $url_parts[ $part ] ) ) {
|
34 |
+
return $url_parts[ $part ];
|
35 |
+
}
|
36 |
+
|
37 |
+
return '';
|
38 |
+
}
|
39 |
+
}
|
admin/links/class-link-watcher-loader.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the loader for link watcher.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Watcher_Loader {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Loads the link watcher.
|
13 |
+
*
|
14 |
+
* @return void
|
15 |
+
*/
|
16 |
+
public function load() {
|
17 |
+
$storage = new WPSEO_Link_Storage();
|
18 |
+
$count_storage = new WPSEO_Meta_Storage();
|
19 |
+
$content_processor = new WPSEO_Link_Content_Processor( $storage, $count_storage );
|
20 |
+
$link_watcher = new WPSEO_Link_Watcher( $content_processor );
|
21 |
+
$link_watcher->register_hooks();
|
22 |
+
}
|
23 |
+
}
|
admin/links/class-link-watcher.php
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the link watcher. This class will watch for the save_post hook being called.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link_Watcher {
|
10 |
+
|
11 |
+
/** @var WPSEO_Link_Content_Processor */
|
12 |
+
protected $content_processor;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* WPSEO_Link_Watcher constructor.
|
16 |
+
*
|
17 |
+
* @param WPSEO_Link_Content_Processor $content_processor The processor to use.
|
18 |
+
*/
|
19 |
+
public function __construct( WPSEO_Link_Content_Processor $content_processor ) {
|
20 |
+
$this->content_processor = $content_processor;
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Registers the hooks.
|
25 |
+
*
|
26 |
+
* @returns void
|
27 |
+
*/
|
28 |
+
public function register_hooks() {
|
29 |
+
add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );
|
30 |
+
add_action( 'delete_post', array( $this, 'delete_post' ) );
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Saves the links that are used in the post.
|
35 |
+
*
|
36 |
+
* @param int $post_id The post id to.
|
37 |
+
* @param WP_Post $post The post object.
|
38 |
+
*
|
39 |
+
* @return void
|
40 |
+
*/
|
41 |
+
public function save_post( $post_id, WP_Post $post ) {
|
42 |
+
if ( ! WPSEO_Link_Table_Accessible::is_accessible() || ! WPSEO_Meta_Table_Accessible::is_accessible() ) {
|
43 |
+
return;
|
44 |
+
}
|
45 |
+
|
46 |
+
// When the post is a revision.
|
47 |
+
if ( wp_is_post_revision( $post->ID ) ) {
|
48 |
+
return;
|
49 |
+
}
|
50 |
+
|
51 |
+
// When the post status is auto-draft.
|
52 |
+
if ( $post->post_status === 'auto-draft' ) {
|
53 |
+
return;
|
54 |
+
}
|
55 |
+
|
56 |
+
// When the post isn't processable, just remove the saved links.
|
57 |
+
if ( ! $this->is_processable( $post_id ) ) {
|
58 |
+
return;
|
59 |
+
}
|
60 |
+
|
61 |
+
$this->process( $post_id, $post->post_content );
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Removes the seo links when the post is deleted.
|
66 |
+
*
|
67 |
+
* @param int $post_id The post id.
|
68 |
+
*
|
69 |
+
* @return void
|
70 |
+
*/
|
71 |
+
public function delete_post( $post_id ) {
|
72 |
+
if ( ! WPSEO_Link_Table_Accessible::is_accessible() || ! WPSEO_Meta_Table_Accessible::is_accessible() ) {
|
73 |
+
return;
|
74 |
+
}
|
75 |
+
|
76 |
+
// Fetch links to update related linked objects.
|
77 |
+
$links = $this->content_processor->get_stored_internal_links( $post_id );
|
78 |
+
|
79 |
+
// Update the storage, remove all links for this post.
|
80 |
+
$storage = new WPSEO_Link_Storage();
|
81 |
+
$storage->cleanup( $post_id );
|
82 |
+
|
83 |
+
// Update link counts for object and referenced links.
|
84 |
+
$this->content_processor->update_link_counts( $post_id, 0, $links );
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Checks if the post is processable.
|
89 |
+
*
|
90 |
+
* @param int $post_id The post id.
|
91 |
+
*
|
92 |
+
* @return bool True when the post is processable.
|
93 |
+
*/
|
94 |
+
protected function is_processable( $post_id ) {
|
95 |
+
/*
|
96 |
+
* Do not use the `wpseo_link_count_post_types` because we want to always count the links,
|
97 |
+
* even if we don't show them.
|
98 |
+
*/
|
99 |
+
$post_types = WPSEO_Post_Type::get_accessible_post_types();
|
100 |
+
|
101 |
+
return isset( $post_types[ get_post_type( $post_id ) ] );
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Processes the content for the given post id.
|
106 |
+
*
|
107 |
+
* @param int $post_id The post id to process.
|
108 |
+
* @param string $content The content to process.
|
109 |
+
*
|
110 |
+
* @return void
|
111 |
+
*/
|
112 |
+
private function process( $post_id, $content ) {
|
113 |
+
// Apply the filters to have the same content as shown on the frontend.
|
114 |
+
$content = apply_filters( 'the_content', $content );
|
115 |
+
$content = str_replace( ']]>', ']]>', $content );
|
116 |
+
|
117 |
+
$this->content_processor->process( $post_id, $content );
|
118 |
+
}
|
119 |
+
}
|
admin/links/class-link.php
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Links
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents an seo link.
|
8 |
+
*/
|
9 |
+
class WPSEO_Link {
|
10 |
+
|
11 |
+
const TYPE_EXTERNAL = 'external';
|
12 |
+
const TYPE_INTERNAL = 'internal';
|
13 |
+
|
14 |
+
/** @var string */
|
15 |
+
protected $url;
|
16 |
+
|
17 |
+
/** @var int */
|
18 |
+
protected $target_post_id;
|
19 |
+
|
20 |
+
/** @var string */
|
21 |
+
protected $type;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Sets the properties for the object.
|
25 |
+
*
|
26 |
+
* @param string $url The url.
|
27 |
+
* @param int $target_post_id ID to the post where the link refers to.
|
28 |
+
* @param string $type The url type: internal or outbound.
|
29 |
+
*/
|
30 |
+
public function __construct( $url, $target_post_id, $type ) {
|
31 |
+
$this->url = $url;
|
32 |
+
$this->target_post_id = $target_post_id;
|
33 |
+
$this->type = $type;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Returns the set URL.
|
38 |
+
*
|
39 |
+
* @return string The set url.
|
40 |
+
*/
|
41 |
+
public function get_url() {
|
42 |
+
return $this->url;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Returns the set target post id.
|
47 |
+
*
|
48 |
+
* @return int The set target post id.
|
49 |
+
*/
|
50 |
+
public function get_target_post_id() {
|
51 |
+
return (int) $this->target_post_id;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Return the set link type.
|
56 |
+
*
|
57 |
+
* @return string The set link type.
|
58 |
+
*/
|
59 |
+
public function get_type() {
|
60 |
+
return $this->type;
|
61 |
+
}
|
62 |
+
}
|
admin/listeners/class-listener.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Listeners
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Dictates the required methods for a Listener implementation.
|
8 |
+
*/
|
9 |
+
interface WPSEO_Listener {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Listens to an argument in the request URL and triggers an action.
|
13 |
+
*
|
14 |
+
* @return void
|
15 |
+
*/
|
16 |
+
public function listen();
|
17 |
+
}
|
admin/menu/class-admin-menu.php
ADDED
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Menu
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Registers the admin menu on the left of the admin area.
|
8 |
+
*/
|
9 |
+
class WPSEO_Admin_Menu implements WPSEO_WordPress_Integration {
|
10 |
+
/** @var WPSEO_Menu Menu */
|
11 |
+
protected $menu;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Constructs the Admin Menu.
|
15 |
+
*
|
16 |
+
* @param WPSEO_Menu $menu Menu to use.
|
17 |
+
*/
|
18 |
+
public function __construct( WPSEO_Menu $menu ) {
|
19 |
+
$this->menu = $menu;
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Registers all hooks to WordPress.
|
24 |
+
*
|
25 |
+
* @return void
|
26 |
+
*/
|
27 |
+
public function register_hooks() {
|
28 |
+
// Needs the lower than default priority so other plugins can hook underneath it without issue.
|
29 |
+
add_action( 'admin_menu', array( $this, 'register_settings_page' ), 5 );
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Registers the menu item submenus.
|
34 |
+
*/
|
35 |
+
public function register_settings_page() {
|
36 |
+
$can_manage_options = WPSEO_Capability_Utils::current_user_can( $this->get_manage_capability() );
|
37 |
+
|
38 |
+
if ( $can_manage_options ) {
|
39 |
+
/*
|
40 |
+
* The current user has the capability to control anything.
|
41 |
+
* This means that all submenus and dashboard can be shown.
|
42 |
+
*/
|
43 |
+
global $admin_page_hooks;
|
44 |
+
|
45 |
+
add_menu_page(
|
46 |
+
'Yoast SEO: ' . __( 'Dashboard', 'wordpress-seo' ),
|
47 |
+
__( 'SEO', 'wordpress-seo' ) . ' ' . $this->get_notification_counter(),
|
48 |
+
$this->get_manage_capability(),
|
49 |
+
$this->menu->get_page_identifier(),
|
50 |
+
$this->get_admin_page_callback(),
|
51 |
+
WPSEO_Utils::get_icon_svg(),
|
52 |
+
'99.31337'
|
53 |
+
);
|
54 |
+
|
55 |
+
$admin_page_hooks[ $this->menu->get_page_identifier() ] = 'seo'; // Wipe notification bits from hooks. R.
|
56 |
+
}
|
57 |
+
|
58 |
+
// Get all submenu pages.
|
59 |
+
$submenu_pages = $this->get_submenu_pages();
|
60 |
+
|
61 |
+
// Add submenu items to the main menu if possible.
|
62 |
+
if ( $can_manage_options ) {
|
63 |
+
$this->register_submenu_pages( $submenu_pages );
|
64 |
+
}
|
65 |
+
|
66 |
+
/*
|
67 |
+
* If the user does not have the general manage options capability,
|
68 |
+
* we need to make sure the desired sub-item can be reached.
|
69 |
+
*/
|
70 |
+
if ( ! $can_manage_options ) {
|
71 |
+
$this->register_menu_pages( $submenu_pages );
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Registers submenu pages as menu pages.
|
77 |
+
*
|
78 |
+
* @param array $submenu_pages List of submenu pages.
|
79 |
+
*/
|
80 |
+
protected function register_menu_pages( $submenu_pages ) {
|
81 |
+
if ( ! is_array( $submenu_pages ) || $submenu_pages === array() ) {
|
82 |
+
return;
|
83 |
+
}
|
84 |
+
|
85 |
+
// Loop through submenu pages and add them.
|
86 |
+
foreach ( $submenu_pages as $submenu_page ) {
|
87 |
+
if ( $submenu_page[3] === $this->get_manage_capability() ) {
|
88 |
+
continue;
|
89 |
+
}
|
90 |
+
|
91 |
+
// Register submenu as menu page.
|
92 |
+
add_menu_page(
|
93 |
+
'Yoast SEO: ' . $submenu_page[2],
|
94 |
+
$submenu_page[2],
|
95 |
+
$submenu_page[3],
|
96 |
+
$submenu_page[4],
|
97 |
+
$submenu_page[5],
|
98 |
+
WPSEO_Utils::get_icon_svg(),
|
99 |
+
'99.31337'
|
100 |
+
);
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Returns the list of registered submenu pages.
|
106 |
+
*
|
107 |
+
* @return array List of registered submenu pages.
|
108 |
+
*/
|
109 |
+
protected function get_submenu_pages() {
|
110 |
+
global $wpseo_admin;
|
111 |
+
|
112 |
+
/** WPSEO_Admin $wpseo_admin */
|
113 |
+
$admin_features = $wpseo_admin->get_admin_features();
|
114 |
+
|
115 |
+
// Submenu pages.
|
116 |
+
$submenu_pages = array(
|
117 |
+
$this->get_submenu_page( __( 'General', 'wordpress-seo' ), $this->menu->get_page_identifier() ),
|
118 |
+
$this->get_submenu_page( __( 'Search Appearance', 'wordpress-seo' ), 'wpseo_titles' ),
|
119 |
+
$this->get_submenu_page(
|
120 |
+
__( 'Search Console', 'wordpress-seo' ),
|
121 |
+
'wpseo_search_console',
|
122 |
+
array( $admin_features['google_search_console'], 'display' ),
|
123 |
+
array( array( $admin_features['google_search_console'], 'set_help' ) )
|
124 |
+
),
|
125 |
+
$this->get_submenu_page( __( 'Social', 'wordpress-seo' ), 'wpseo_social' ),
|
126 |
+
$this->get_submenu_page( __( 'Tools', 'wordpress-seo' ), 'wpseo_tools' ),
|
127 |
+
$this->get_submenu_page( $this->get_license_page_title(), 'wpseo_licenses' ),
|
128 |
+
);
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Filter: 'wpseo_submenu_pages' - Collects all submenus that need to be shown.
|
132 |
+
*
|
133 |
+
* @api array $submenu_pages List with all submenu pages.
|
134 |
+
*/
|
135 |
+
return (array) apply_filters( 'wpseo_submenu_pages', $submenu_pages );
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Creates a submenu formatted array.
|
140 |
+
*
|
141 |
+
* @param string $page_title Page title to use.
|
142 |
+
* @param string $page_slug Page slug to use.
|
143 |
+
* @param callable $callback Optional. Callback which handles the page request.
|
144 |
+
* @param callable[] $hook Optional. Hook to trigger when the page is registered.
|
145 |
+
*
|
146 |
+
* @return array Formatted submenu.
|
147 |
+
*/
|
148 |
+
protected function get_submenu_page( $page_title, $page_slug, $callback = null, $hook = null ) {
|
149 |
+
if ( $callback === null ) {
|
150 |
+
$callback = $this->get_admin_page_callback();
|
151 |
+
}
|
152 |
+
|
153 |
+
return array(
|
154 |
+
$this->menu->get_page_identifier(),
|
155 |
+
'',
|
156 |
+
$page_title,
|
157 |
+
$this->get_manage_capability(),
|
158 |
+
$page_slug,
|
159 |
+
$callback,
|
160 |
+
$hook,
|
161 |
+
);
|
162 |
+
}
|
163 |
+
|
164 |
+
/**
|
165 |
+
* Registers the submenu pages.
|
166 |
+
*
|
167 |
+
* This is only done when the user has the `wpseo_manage_options` capability,
|
168 |
+
* thus all capabilities can be set to this capability.
|
169 |
+
*
|
170 |
+
* @param array $submenu_pages List of submenu pages to register.
|
171 |
+
*
|
172 |
+
* @return void
|
173 |
+
*/
|
174 |
+
protected function register_submenu_pages( $submenu_pages ) {
|
175 |
+
if ( ! is_array( $submenu_pages ) || $submenu_pages === array() ) {
|
176 |
+
return;
|
177 |
+
}
|
178 |
+
|
179 |
+
// Loop through submenu pages and add them.
|
180 |
+
foreach ( $submenu_pages as $submenu_page ) {
|
181 |
+
$page_title = $submenu_page[2];
|
182 |
+
|
183 |
+
// We cannot use $submenu_page[1] because add-ons define that, so hard-code this value.
|
184 |
+
if ( $submenu_page[4] === 'wpseo_licenses' ) {
|
185 |
+
$page_title = $this->get_license_page_title();
|
186 |
+
}
|
187 |
+
|
188 |
+
$page_title .= ' - Yoast SEO';
|
189 |
+
|
190 |
+
/*
|
191 |
+
* Add submenu page.
|
192 |
+
*
|
193 |
+
* If we don't register this on `wpseo_manage_options`, admin users with only this capability
|
194 |
+
* will not be able to see the submenus which are configured with something else,
|
195 |
+
* thus all submenu pages are registered with the `wpseo_manage_options` capability here.
|
196 |
+
*/
|
197 |
+
$admin_page = add_submenu_page( $submenu_page[0], $page_title, $submenu_page[2], $this->get_manage_capability(), $submenu_page[4], $submenu_page[5] );
|
198 |
+
|
199 |
+
// Check if we need to hook.
|
200 |
+
if ( isset( $submenu_page[6] ) && ( is_array( $submenu_page[6] ) && $submenu_page[6] !== array() ) ) {
|
201 |
+
foreach ( $submenu_page[6] as $submenu_page_action ) {
|
202 |
+
add_action( 'load-' . $admin_page, $submenu_page_action );
|
203 |
+
}
|
204 |
+
}
|
205 |
+
}
|
206 |
+
|
207 |
+
// Use WordPress global $submenu to directly access it's properties.
|
208 |
+
global $submenu;
|
209 |
+
if ( isset( $submenu[ $this->menu->get_page_identifier() ] ) && WPSEO_Capability_Utils::current_user_can( $this->get_manage_capability() ) ) {
|
210 |
+
$submenu[ $this->menu->get_page_identifier() ][0][0] = __( 'General', 'wordpress-seo' );
|
211 |
+
}
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Returns the notification count in HTML format.
|
216 |
+
*
|
217 |
+
* @return string The notification count in HTML format.
|
218 |
+
*/
|
219 |
+
protected function get_notification_counter() {
|
220 |
+
$notification_center = Yoast_Notification_Center::get();
|
221 |
+
$notification_count = $notification_center->get_notification_count();
|
222 |
+
|
223 |
+
// Add main page.
|
224 |
+
/* translators: %s: number of notifications */
|
225 |
+
$notifications = sprintf( _n( '%s notification', '%s notifications', $notification_count, 'wordpress-seo' ), number_format_i18n( $notification_count ) );
|
226 |
+
|
227 |
+
$counter = sprintf( '<span class="update-plugins count-%1$d"><span class="plugin-count" aria-hidden="true">%1$d</span><span class="screen-reader-text">%2$s</span></span>', $notification_count, $notifications );
|
228 |
+
|
229 |
+
return $counter;
|
230 |
+
}
|
231 |
+
|
232 |
+
/**
|
233 |
+
* Returns the capability that is required to manage all options.
|
234 |
+
*
|
235 |
+
* @return string Capability to check against.
|
236 |
+
*/
|
237 |
+
protected function get_manage_capability() {
|
238 |
+
/**
|
239 |
+
* Filter: 'wpseo_manage_options_capability' - Allow changing the capability users need to view the settings pages
|
240 |
+
*
|
241 |
+
* @deprecated 5.5
|
242 |
+
* @api string unsigned The capability
|
243 |
+
*/
|
244 |
+
return apply_filters_deprecated( 'wpseo_manage_options_capability', array( 'wpseo_manage_options' ), 'WPSEO 5.5.0', false, 'Use the introduced wpseo_manage_options capability instead.' );
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Returns the page handler callback.
|
249 |
+
*
|
250 |
+
* @return array Callback page handler.
|
251 |
+
*/
|
252 |
+
protected function get_admin_page_callback() {
|
253 |
+
return array( $this->menu, 'load_page' );
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Returns the page title to use for the licenses page.
|
258 |
+
*
|
259 |
+
* @return string The title for the license page.
|
260 |
+
*/
|
261 |
+
protected function get_license_page_title() {
|
262 |
+
static $title = null;
|
263 |
+
|
264 |
+
if ( $title === null ) {
|
265 |
+
$title = __( 'Premium', 'wordpress-seo' );
|
266 |
+
}
|
267 |
+
|
268 |
+
return $title;
|
269 |
+
}
|
270 |
+
}
|
admin/menu/class-menu.php
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Menu
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Registers the regular admin menu and network admin menu implementations.
|
8 |
+
*/
|
9 |
+
class WPSEO_Menu implements WPSEO_WordPress_Integration {
|
10 |
+
/** The page identifier used in WordPress to register the admin page !DO NOT CHANGE THIS! */
|
11 |
+
const PAGE_IDENTIFIER = 'wpseo_dashboard';
|
12 |
+
|
13 |
+
/** @var array List of classes that add admin functionality. */
|
14 |
+
protected $admin_features;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Registers all hooks to WordPress.
|
18 |
+
*
|
19 |
+
* @return void
|
20 |
+
*/
|
21 |
+
public function register_hooks() {
|
22 |
+
$admin_menu = new WPSEO_Admin_Menu( $this );
|
23 |
+
$admin_menu->register_hooks();
|
24 |
+
|
25 |
+
$network_admin_menu = new WPSEO_Network_Admin_Menu( $this );
|
26 |
+
$network_admin_menu->register_hooks();
|
27 |
+
|
28 |
+
$capability_normalizer = new WPSEO_Submenu_Capability_Normalize();
|
29 |
+
$capability_normalizer->register_hooks();
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Returns the main menu page identifier.
|
34 |
+
*
|
35 |
+
* @return string Page identifier to use.
|
36 |
+
*/
|
37 |
+
public function get_page_identifier() {
|
38 |
+
return self::PAGE_IDENTIFIER;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Loads the requested admin settings page.
|
43 |
+
*
|
44 |
+
* @return void
|
45 |
+
*/
|
46 |
+
public function load_page() {
|
47 |
+
$page = filter_input( INPUT_GET, 'page' );
|
48 |
+
$this->show_page( $page );
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Shows an admin settings page.
|
53 |
+
*
|
54 |
+
* @param string $page Page to display.
|
55 |
+
*
|
56 |
+
* @return void
|
57 |
+
*/
|
58 |
+
protected function show_page( $page ) {
|
59 |
+
switch ( $page ) {
|
60 |
+
case 'wpseo_tools':
|
61 |
+
require_once WPSEO_PATH . 'admin/pages/tools.php';
|
62 |
+
break;
|
63 |
+
|
64 |
+
case 'wpseo_titles':
|
65 |
+
require_once WPSEO_PATH . 'admin/pages/metas.php';
|
66 |
+
break;
|
67 |
+
|
68 |
+
case 'wpseo_social':
|
69 |
+
require_once WPSEO_PATH . 'admin/pages/social.php';
|
70 |
+
break;
|
71 |
+
|
72 |
+
case 'wpseo_licenses':
|
73 |
+
require_once WPSEO_PATH . 'admin/pages/licenses.php';
|
74 |
+
break;
|
75 |
+
|
76 |
+
case 'wpseo_files':
|
77 |
+
require_once WPSEO_PATH . 'admin/views/tool-file-editor.php';
|
78 |
+
break;
|
79 |
+
|
80 |
+
case 'wpseo_configurator':
|
81 |
+
require_once WPSEO_PATH . 'admin/config-ui/class-configuration-page.php';
|
82 |
+
break;
|
83 |
+
|
84 |
+
default:
|
85 |
+
require_once WPSEO_PATH . 'admin/pages/dashboard.php';
|
86 |
+
break;
|
87 |
+
}
|
88 |
+
}
|
89 |
+
}
|
admin/menu/class-network-admin-menu.php
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Menu
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Network Admin Menu handler.
|
8 |
+
*/
|
9 |
+
class WPSEO_Network_Admin_Menu implements WPSEO_WordPress_Integration {
|
10 |
+
/** @var WPSEO_Menu Menu */
|
11 |
+
protected $menu;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* WPSEO_Network_Admin_Menu constructor.
|
15 |
+
*
|
16 |
+
* @param WPSEO_Menu $menu Menu to use.
|
17 |
+
*/
|
18 |
+
public function __construct( WPSEO_Menu $menu ) {
|
19 |
+
$this->menu = $menu;
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Registers all hooks to WordPress.
|
24 |
+
*
|
25 |
+
* @return void
|
26 |
+
*/
|
27 |
+
public function register_hooks() {
|
28 |
+
// Needs the lower than default priority so other plugins can hook underneath it without issue.
|
29 |
+
add_action( 'network_admin_menu', array( $this, 'register_settings_page' ), 5 );
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Register the settings page for the Network settings.
|
34 |
+
*
|
35 |
+
* @return void
|
36 |
+
*/
|
37 |
+
public function register_settings_page() {
|
38 |
+
if ( ! WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ) ) {
|
39 |
+
return;
|
40 |
+
}
|
41 |
+
|
42 |
+
$page_callback = array( $this->menu, 'load_page' );
|
43 |
+
|
44 |
+
add_menu_page(
|
45 |
+
'Yoast SEO: ' . __( 'MultiSite Settings', 'wordpress-seo' ),
|
46 |
+
__( 'SEO', 'wordpress-seo' ),
|
47 |
+
'delete_users',
|
48 |
+
$this->menu->get_page_identifier(),
|
49 |
+
array( $this, 'network_config_page' ),
|
50 |
+
WPSEO_Utils::get_icon_svg()
|
51 |
+
);
|
52 |
+
|
53 |
+
if ( WPSEO_Utils::allow_system_file_edit() === true ) {
|
54 |
+
add_submenu_page(
|
55 |
+
$this->menu->get_page_identifier(),
|
56 |
+
'Yoast SEO: ' . __( 'Edit Files', 'wordpress-seo' ),
|
57 |
+
__( 'Edit Files', 'wordpress-seo' ),
|
58 |
+
'delete_users', 'wpseo_files',
|
59 |
+
$page_callback
|
60 |
+
);
|
61 |
+
}
|
62 |
+
|
63 |
+
// Add Extension submenu page.
|
64 |
+
add_submenu_page(
|
65 |
+
$this->menu->get_page_identifier(),
|
66 |
+
'Yoast SEO: ' . __( 'Extensions', 'wordpress-seo' ),
|
67 |
+
__( 'Extensions', 'wordpress-seo' ),
|
68 |
+
'delete_users',
|
69 |
+
'wpseo_licenses',
|
70 |
+
$page_callback
|
71 |
+
);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Loads the form for the network configuration page.
|
76 |
+
*
|
77 |
+
* @return void
|
78 |
+
*/
|
79 |
+
public function network_config_page() {
|
80 |
+
require_once WPSEO_PATH . 'admin/pages/network.php';
|
81 |
+
}
|
82 |
+
}
|
admin/menu/class-submenu-capability-normalize.php
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Menu
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Normalize submenu capabilities to `wpseo_manage_options`.
|
8 |
+
*/
|
9 |
+
class WPSEO_Submenu_Capability_Normalize implements WPSEO_WordPress_Integration {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Registers all hooks to WordPress.
|
13 |
+
*
|
14 |
+
* @return void
|
15 |
+
*/
|
16 |
+
public function register_hooks() {
|
17 |
+
add_filter( 'wpseo_submenu_pages', array( $this, 'normalize_submenus_capability' ) );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Normalizes any `manage_options` to `wpseo_manage_options`.
|
22 |
+
*
|
23 |
+
* This is needed as the module plugins are not updated with the new capabilities directly,
|
24 |
+
* but they should not be shown as main menu items.
|
25 |
+
*
|
26 |
+
* @param array $submenu_pages List of subpages to convert.
|
27 |
+
*
|
28 |
+
* @return array Converted subpages.
|
29 |
+
*/
|
30 |
+
public function normalize_submenus_capability( $submenu_pages ) {
|
31 |
+
foreach ( $submenu_pages as $index => $submenu_page ) {
|
32 |
+
if ( $submenu_page[3] === 'manage_options' ) {
|
33 |
+
$submenu_pages[ $index ][3] = 'wpseo_manage_options';
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
return $submenu_pages;
|
38 |
+
}
|
39 |
+
}
|
admin/metabox/class-metabox-add-keyword-tab.php
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Metabox
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Tab to add a keyword to analyze
|
8 |
+
*/
|
9 |
+
class WPSEO_Metabox_Add_Keyword_Tab implements WPSEO_Metabox_Tab {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns a button because a link is inappropriate here
|
13 |
+
*
|
14 |
+
* @return string
|
15 |
+
*/
|
16 |
+
public function link() {
|
17 |
+
|
18 |
+
// Ensure thickbox is enqueued.
|
19 |
+
add_thickbox();
|
20 |
+
|
21 |
+
ob_start();
|
22 |
+
?>
|
23 |
+
<li class="wpseo-tab-add-keyword">
|
24 |
+
<button type="button" class="wpseo-add-keyword button button-link">
|
25 |
+
<span class="wpseo-add-keyword-plus" aria-hidden="true">+</span>
|
26 |
+
<?php esc_html_e( 'Add keyword', 'wordpress-seo' ); ?>
|
27 |
+
</button>
|
28 |
+
</li>
|
29 |
+
|
30 |
+
<?php
|
31 |
+
$popup_title = __( 'Want to add more than one keyword?', 'wordpress-seo' );
|
32 |
+
/* translators: %1$s expands to a 'Yoast SEO Premium' text linked to the yoast.com website. */
|
33 |
+
$popup_content = '<p>' . sprintf( __( 'Great news: you can, with %1$s!', 'wordpress-seo' ),
|
34 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/pe-premium-page' ) . '">Yoast SEO Premium</a>'
|
35 |
+
) . '</p>';
|
36 |
+
$popup_content .= '<p>' . sprintf(
|
37 |
+
/* translators: %s expands to 'Yoast SEO Premium'. */
|
38 |
+
__( 'Other benefits of %s for you:', 'wordpress-seo' ), 'Yoast SEO Premium'
|
39 |
+
) . '</p>';
|
40 |
+
$popup_content .= '<ul>';
|
41 |
+
$popup_content .= '<li>' . sprintf(
|
42 |
+
/* translators: %1$s expands to a 'strong' start tag, %2$s to a 'strong' end tag. */
|
43 |
+
__( '%1$sNo more dead links%2$s: easy redirect manager', 'wordpress-seo' ), '<strong>', '</strong>'
|
44 |
+
) . '</li>';
|
45 |
+
$popup_content .= '<li><strong>' . __( 'Superfast internal links suggestions', 'wordpress-seo' ) . '</strong></li>';
|
46 |
+
$popup_content .= '<li>' . sprintf(
|
47 |
+
/* translators: %1$s expands to a 'strong' start tag, %2$s to a 'strong' end tag. */
|
48 |
+
__( '%1$sSocial media preview%2$s: Facebook & Twitter', 'wordpress-seo' ), '<strong>', '</strong>'
|
49 |
+
) . '</li>';
|
50 |
+
$popup_content .= '<li><strong>' . __( '24/7 support', 'wordpress-seo' ) . '</strong></li>';
|
51 |
+
$popup_content .= '<li><strong>' . __( 'No ads!', 'wordpress-seo' ) . '</strong></li>';
|
52 |
+
$popup_content .= '</ul>';
|
53 |
+
$premium_popup = new WPSEO_Premium_Popup( 'add-keyword', 'h1', $popup_title, $popup_content, WPSEO_Shortlinker::get( 'https://yoa.st/add-keywords-popup' ) );
|
54 |
+
echo $premium_popup->get_premium_message();
|
55 |
+
|
56 |
+
return ob_get_clean();
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Returns an empty string because this tab has no content
|
61 |
+
*
|
62 |
+
* @return string
|
63 |
+
*/
|
64 |
+
public function content() {
|
65 |
+
return '';
|
66 |
+
}
|
67 |
+
}
|
admin/metabox/class-metabox-addon-section.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Generates and displays a section containing metabox tabs that have been added by other plugins through the
|
8 |
+
* `wpseo_tab_header` and `wpseo_tab_content` actions.
|
9 |
+
*/
|
10 |
+
class WPSEO_Metabox_Addon_Tab_Section extends WPSEO_Metabox_Tab_Section {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Applies the actions for adding a tab to the metabox.
|
14 |
+
*/
|
15 |
+
public function display_content() {
|
16 |
+
?>
|
17 |
+
<div id="wpseo-meta-section-addons" class="wpseo-meta-section">
|
18 |
+
<div class="wpseo-metabox-tabs-div">
|
19 |
+
<ul class="wpseo-metabox-tabs">
|
20 |
+
<?php do_action( 'wpseo_tab_header' ); ?>
|
21 |
+
</ul>
|
22 |
+
</div>
|
23 |
+
<?php do_action( 'wpseo_tab_content' ); ?>
|
24 |
+
</div>
|
25 |
+
<?php
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* `WPSEO_Metabox_Addon_Section` always has "tabs", represented by registered actions. If this is not the case,
|
30 |
+
* it should not be instantiated.
|
31 |
+
*
|
32 |
+
* @return bool
|
33 |
+
*/
|
34 |
+
protected function has_tabs() {
|
35 |
+
return true;
|
36 |
+
}
|
37 |
+
}
|
admin/metabox/class-metabox-analysis-readability.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Metabox
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the readability analysis
|
8 |
+
*/
|
9 |
+
class WPSEO_Metabox_Analysis_Readability implements WPSEO_Metabox_Analysis {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Whether this analysis is enabled.
|
13 |
+
*
|
14 |
+
* @return bool Whether or not this analysis is enabled.
|
15 |
+
*/
|
16 |
+
public function is_enabled() {
|
17 |
+
return $this->is_globally_enabled() && $this->is_user_enabled();
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Whether or not this analysis is enabled by the user.
|
22 |
+
*
|
23 |
+
* @return bool Whether or not this analysis is enabled by the user.
|
24 |
+
*/
|
25 |
+
public function is_user_enabled() {
|
26 |
+
return ! get_the_author_meta( 'wpseo_content_analysis_disable', get_current_user_id() );
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Whether or not this analysis is enabled globally.
|
31 |
+
*
|
32 |
+
* @return bool Whether or not this analysis is enabled globally.
|
33 |
+
*/
|
34 |
+
public function is_globally_enabled() {
|
35 |
+
return WPSEO_Options::get( 'content_analysis_active', true );
|
36 |
+
}
|
37 |
+
}
|
admin/metabox/class-metabox-analysis-seo.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Metabox
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the SEO analysis
|
8 |
+
*/
|
9 |
+
class WPSEO_Metabox_Analysis_SEO implements WPSEO_Metabox_Analysis {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Whether this analysis is enabled.
|
13 |
+
*
|
14 |
+
* @return bool Whether or not this analysis is enabled.
|
15 |
+
*/
|
16 |
+
public function is_enabled() {
|
17 |
+
return $this->is_globally_enabled() && $this->is_user_enabled();
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Whether or not this analysis is enabled by the user.
|
22 |
+
*
|
23 |
+
* @return bool Whether or not this analysis is enabled by the user.
|
24 |
+
*/
|
25 |
+
public function is_user_enabled() {
|
26 |
+
return ! get_the_author_meta( 'wpseo_keyword_analysis_disable', get_current_user_id() );
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Whether or not this analysis is enabled globally.
|
31 |
+
*
|
32 |
+
* @return bool Whether or not this analysis is enabled globally.
|
33 |
+
*/
|
34 |
+
public function is_globally_enabled() {
|
35 |
+
return WPSEO_Options::get( 'keyword_analysis_active', true );
|
36 |
+
}
|
37 |
+
}
|
admin/metabox/class-metabox-editor.php
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Metabox
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Handles all things with the metabox in combination with the WordPress editor.
|
8 |
+
*/
|
9 |
+
class WPSEO_Metabox_Editor {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Registers hooks to WordPress
|
13 |
+
*/
|
14 |
+
public function register_hooks() {
|
15 |
+
add_filter( 'mce_css', array( $this, 'add_css_inside_editor' ) );
|
16 |
+
add_filter( 'tiny_mce_before_init', array( $this, 'add_custom_element' ) );
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Adds our inside the editor CSS file to the list of CSS files to be loaded inside the editor.
|
21 |
+
*
|
22 |
+
* @param string $css_files The CSS files that WordPress wants to load inside the editor.
|
23 |
+
* @return string The CSS files WordPress wants to load and our CSS file.
|
24 |
+
*/
|
25 |
+
public function add_css_inside_editor( $css_files ) {
|
26 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
27 |
+
$styles = $asset_manager->special_styles();
|
28 |
+
/** @var WPSEO_Admin_Asset $inside_editor */
|
29 |
+
$inside_editor = $styles['inside-editor'];
|
30 |
+
|
31 |
+
$asset_location = new WPSEO_Admin_Asset_SEO_Location( WPSEO_FILE );
|
32 |
+
$url = $asset_location->get_url( $inside_editor, WPSEO_Admin_Asset::TYPE_CSS );
|
33 |
+
|
34 |
+
if ( '' === $css_files ) {
|
35 |
+
$css_files = $url;
|
36 |
+
}
|
37 |
+
else {
|
38 |
+
$css_files .= ',' . $url;
|
39 |
+
}
|
40 |
+
|
41 |
+
return $css_files;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Adds a custom element to the tinyMCE editor that we need for marking the content.
|
46 |
+
*
|
47 |
+
* @param array $tinymce_config The tinyMCE config as configured by WordPress.
|
48 |
+
*
|
49 |
+
* @return array The new tinyMCE config with our added custom elements.
|
50 |
+
*/
|
51 |
+
public function add_custom_element( $tinymce_config ) {
|
52 |
+
if ( ! empty( $tinymce_config['custom_elements'] ) ) {
|
53 |
+
$custom_elements = $tinymce_config['custom_elements'];
|
54 |
+
|
55 |
+
$custom_elements .= ',~yoastmark';
|
56 |
+
}
|
57 |
+
else {
|
58 |
+
$custom_elements = '~yoastmark';
|
59 |
+
}
|
60 |
+
|
61 |
+
$tinymce_config['custom_elements'] = $custom_elements;
|
62 |
+
|
63 |
+
return $tinymce_config;
|
64 |
+
}
|
65 |
+
}
|
admin/metabox/class-metabox-form-tab.php
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Generates the HTML for a metabox tab.
|
8 |
+
*/
|
9 |
+
class WPSEO_Metabox_Form_Tab implements WPSEO_Metabox_Tab {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string
|
13 |
+
*/
|
14 |
+
private $name;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
private $content;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var string
|
23 |
+
*/
|
24 |
+
private $link_content;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
private $tab_class;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var string
|
33 |
+
*/
|
34 |
+
private $link_class;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @var string
|
38 |
+
*/
|
39 |
+
private $link_title;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @var string
|
43 |
+
*/
|
44 |
+
private $link_aria_label;
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @var boolean
|
48 |
+
*/
|
49 |
+
private $single;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Constructor.
|
53 |
+
*
|
54 |
+
* @param string $name The name of the tab, used as an identifier in the html.
|
55 |
+
* @param string $content The tab content.
|
56 |
+
* @param string $link_content The text content of the tab link.
|
57 |
+
* @param array $options Optional link attributes.
|
58 |
+
*/
|
59 |
+
public function __construct( $name, $content, $link_content, array $options = array() ) {
|
60 |
+
$default_options = array(
|
61 |
+
'tab_class' => '',
|
62 |
+
'link_class' => '',
|
63 |
+
'link_title' => '',
|
64 |
+
'link_aria_label' => '',
|
65 |
+
'single' => false,
|
66 |
+
);
|
67 |
+
|
68 |
+
$options = array_merge( $default_options, $options );
|
69 |
+
|
70 |
+
$this->name = $name;
|
71 |
+
$this->content = $content;
|
72 |
+
$this->link_content = $link_content;
|
73 |
+
$this->tab_class = $options['tab_class'];
|
74 |
+
$this->link_class = $options['link_class'];
|
75 |
+
$this->link_title = $options['link_title'];
|
76 |
+
$this->link_aria_label = $options['link_aria_label'];
|
77 |
+
$this->single = $options['single'];
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Returns the html for the tab link.
|
82 |
+
*
|
83 |
+
* @return string
|
84 |
+
*/
|
85 |
+
public function link() {
|
86 |
+
|
87 |
+
$html = '<li class="%1$s%2$s"><a class="wpseo_tablink%3$s" href="#wpseo_%1$s"%4$s%5$s>%6$s</a></li>';
|
88 |
+
|
89 |
+
if ( $this->single ) {
|
90 |
+
$html = '<li class="%1$s%2$s"><span class="wpseo_tablink%3$s"%4$s%5$s>%6$s</span></li>';
|
91 |
+
}
|
92 |
+
|
93 |
+
return sprintf(
|
94 |
+
$html,
|
95 |
+
esc_attr( $this->name ),
|
96 |
+
( '' !== $this->tab_class ) ? ' ' . esc_attr( $this->tab_class ) : '',
|
97 |
+
( '' !== $this->link_class ) ? ' ' . esc_attr( $this->link_class ) : '',
|
98 |
+
( '' !== $this->link_title ) ? ' title="' . esc_attr( $this->link_title ) . '"' : '',
|
99 |
+
( '' !== $this->link_aria_label ) ? ' aria-label="' . esc_attr( $this->link_aria_label ) . '"' : '',
|
100 |
+
$this->link_content
|
101 |
+
);
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Returns the html for the tab content.
|
106 |
+
*
|
107 |
+
* @return string
|
108 |
+
*/
|
109 |
+
public function content() {
|
110 |
+
return sprintf(
|
111 |
+
'<div id="%1$s" class="wpseotab %2$s">%3$s</div>',
|
112 |
+
esc_attr( 'wpseo_' . $this->name ),
|
113 |
+
esc_attr( $this->name ),
|
114 |
+
$this->content
|
115 |
+
);
|
116 |
+
}
|
117 |
+
}
|
admin/metabox/class-metabox-tab-section.php
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Generates and displays the HTML for a metabox section.
|
8 |
+
*/
|
9 |
+
class WPSEO_Metabox_Tab_Section implements WPSEO_Metabox_Section {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WPSEO_Metabox_Tab[]
|
13 |
+
*/
|
14 |
+
public $tabs = array();
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
public $name;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var string
|
23 |
+
*/
|
24 |
+
private $link_content;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
private $link_title;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var string
|
33 |
+
*/
|
34 |
+
private $link_class;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @var string
|
38 |
+
*/
|
39 |
+
private $link_aria_label;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Constructor.
|
43 |
+
*
|
44 |
+
* @param string $name The name of the section, used as an identifier in the html. Can only contain URL safe characters.
|
45 |
+
* @param string $link_content The text content of the section link.
|
46 |
+
* @param array $tabs The metabox tabs (`WPSEO_Metabox_Tabs[]`) to be included in the section.
|
47 |
+
* @param array $options Optional link attributes.
|
48 |
+
*/
|
49 |
+
public function __construct( $name, $link_content, array $tabs = array(), array $options = array() ) {
|
50 |
+
$default_options = array(
|
51 |
+
'link_title' => '',
|
52 |
+
'link_class' => '',
|
53 |
+
'link_aria_label' => '',
|
54 |
+
);
|
55 |
+
|
56 |
+
$options = array_merge( $default_options, $options );
|
57 |
+
|
58 |
+
$this->name = $name;
|
59 |
+
foreach ( $tabs as $tab ) {
|
60 |
+
$this->add_tab( $tab );
|
61 |
+
}
|
62 |
+
$this->link_content = $link_content;
|
63 |
+
$this->link_title = $options['link_title'];
|
64 |
+
$this->link_class = $options['link_class'];
|
65 |
+
$this->link_aria_label = $options['link_aria_label'];
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Outputs the section link if any tab has been added.
|
70 |
+
*/
|
71 |
+
public function display_link() {
|
72 |
+
if ( $this->has_tabs() ) {
|
73 |
+
printf(
|
74 |
+
'<li><a href="#wpseo-meta-section-%1$s" class="wpseo-meta-section-link %2$s"%3$s%4$s>%5$s</a></li>',
|
75 |
+
esc_attr( $this->name ),
|
76 |
+
esc_attr( $this->link_class ),
|
77 |
+
( '' !== $this->link_title ) ? ' title="' . esc_attr( $this->link_title ) . '"' : '',
|
78 |
+
( '' !== $this->link_aria_label ) ? ' aria-label="' . esc_attr( $this->link_aria_label ) . '"' : '',
|
79 |
+
$this->link_content
|
80 |
+
);
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Outputs the section content if any tab has been added.
|
86 |
+
*/
|
87 |
+
public function display_content() {
|
88 |
+
if ( $this->has_tabs() ) {
|
89 |
+
$html = '<div id="%1$s" class="wpseo-meta-section">';
|
90 |
+
$html .= '<div class="wpseo-metabox-tabs-div">';
|
91 |
+
$html .= '<ul class="wpseo-metabox-tabs %2$s">%3$s</ul>%4$s';
|
92 |
+
$html .= '</div></div>';
|
93 |
+
|
94 |
+
printf(
|
95 |
+
$html,
|
96 |
+
esc_attr( 'wpseo-meta-section-' . $this->name ),
|
97 |
+
esc_attr( 'wpseo-metabox-tab-' . $this->name ),
|
98 |
+
$this->tab_links(),
|
99 |
+
$this->tab_content()
|
100 |
+
);
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Add a `WPSEO_Metabox_Tab` object to the tabs.
|
106 |
+
*
|
107 |
+
* @param WPSEO_Metabox_Tab $tab Tab to add.
|
108 |
+
*/
|
109 |
+
public function add_tab( WPSEO_Metabox_Tab $tab ) {
|
110 |
+
$this->tabs[] = $tab;
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Checks if any tabs have been added to the section.
|
115 |
+
*
|
116 |
+
* @return bool
|
117 |
+
*/
|
118 |
+
protected function has_tabs() {
|
119 |
+
return ! empty( $this->tabs );
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Concatenates all tabs' links into one html string.
|
124 |
+
*
|
125 |
+
* @return string
|
126 |
+
*/
|
127 |
+
private function tab_links() {
|
128 |
+
$links = '';
|
129 |
+
foreach ( $this->tabs as $tab ) {
|
130 |
+
$links .= $tab->link();
|
131 |
+
}
|
132 |
+
return $links;
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Concatenates all tabs' content into one html string.
|
137 |
+
*
|
138 |
+
* @return string
|
139 |
+
*/
|
140 |
+
private function tab_content() {
|
141 |
+
$content = '';
|
142 |
+
foreach ( $this->tabs as $tab ) {
|
143 |
+
$content .= $tab->content();
|
144 |
+
}
|
145 |
+
return $content;
|
146 |
+
}
|
147 |
+
}
|
admin/metabox/class-metabox.php
ADDED
@@ -0,0 +1,1184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class generates the metabox on the edit post / page as well as contains all page analysis functionality.
|
8 |
+
*/
|
9 |
+
class WPSEO_Metabox extends WPSEO_Meta {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WPSEO_Social_Admin
|
13 |
+
*/
|
14 |
+
protected $social_admin;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var WPSEO_Metabox_Analysis_SEO
|
18 |
+
*/
|
19 |
+
protected $analysis_seo;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var WPSEO_Metabox_Analysis_Readability
|
23 |
+
*/
|
24 |
+
protected $analysis_readability;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Class constructor.
|
28 |
+
*/
|
29 |
+
public function __construct() {
|
30 |
+
add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) );
|
31 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
|
32 |
+
add_action( 'wp_insert_post', array( $this, 'save_postdata' ) );
|
33 |
+
add_action( 'edit_attachment', array( $this, 'save_postdata' ) );
|
34 |
+
add_action( 'add_attachment', array( $this, 'save_postdata' ) );
|
35 |
+
add_action( 'post_submitbox_start', array( $this, 'publish_box' ) );
|
36 |
+
add_action( 'admin_init', array( $this, 'setup_page_analysis' ) );
|
37 |
+
add_action( 'admin_init', array( $this, 'translate_meta_boxes' ) );
|
38 |
+
add_action( 'admin_footer', array( $this, 'template_keyword_tab' ) );
|
39 |
+
add_action( 'admin_footer', array( $this, 'template_generic_tab' ) );
|
40 |
+
|
41 |
+
// Check if one of the social settings is checked in the options, if so, initialize the social_admin object.
|
42 |
+
if ( WPSEO_Options::get( 'opengraph', false ) || WPSEO_Options::get( 'twitter', false ) ) {
|
43 |
+
$this->social_admin = new WPSEO_Social_Admin();
|
44 |
+
}
|
45 |
+
|
46 |
+
$this->editor = new WPSEO_Metabox_Editor();
|
47 |
+
$this->editor->register_hooks();
|
48 |
+
|
49 |
+
$this->analysis_seo = new WPSEO_Metabox_Analysis_SEO();
|
50 |
+
$this->analysis_readability = new WPSEO_Metabox_Analysis_Readability();
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Translate text strings for use in the meta box.
|
55 |
+
*
|
56 |
+
* IMPORTANT: if you want to add a new string (option) somewhere, make sure you add that array key to
|
57 |
+
* the main meta box definition array in the class WPSEO_Meta() as well!!!!
|
58 |
+
*/
|
59 |
+
public static function translate_meta_boxes() {
|
60 |
+
self::$meta_fields['general']['snippetpreview']['title'] = __( 'Snippet editor', 'wordpress-seo' );
|
61 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
62 |
+
self::$meta_fields['general']['snippetpreview']['help'] = sprintf( __( 'This is a rendering of what this post might look like in Google\'s search results. %1$sLearn more about the Snippet Preview%2$s.', 'wordpress-seo' ), '<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/snippet-preview' ) . '">', '</a>' );
|
63 |
+
self::$meta_fields['general']['snippetpreview']['help-button'] = __( 'Show information about the snippet editor', 'wordpress-seo' );
|
64 |
+
|
65 |
+
self::$meta_fields['general']['pageanalysis']['title'] = __( 'Analysis', 'wordpress-seo' );
|
66 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
67 |
+
self::$meta_fields['general']['pageanalysis']['help'] = sprintf( __( 'This is the content analysis, a collection of content checks that analyze the content of your page. %1$sLearn more about the Content Analysis Tool%2$s.', 'wordpress-seo' ), '<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/content-analysis' ) . '">', '</a>' );
|
68 |
+
self::$meta_fields['general']['pageanalysis']['help-button'] = __( 'Show information about the content analysis', 'wordpress-seo' );
|
69 |
+
|
70 |
+
self::$meta_fields['general']['focuskw_text_input']['title'] = __( 'Focus keyword', 'wordpress-seo' );
|
71 |
+
self::$meta_fields['general']['focuskw_text_input']['label'] = __( 'Enter a focus keyword', 'wordpress-seo' );
|
72 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
73 |
+
self::$meta_fields['general']['focuskw_text_input']['help'] = sprintf( __( 'Pick the main keyword or keyphrase that this post/page is about. %1$sLearn more about the Focus Keyword%2$s.', 'wordpress-seo' ), '<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/focus-keyword' ) . '">', '</a>' );
|
74 |
+
self::$meta_fields['general']['focuskw_text_input']['help-button'] = __( 'Show information about the focus keyword', 'wordpress-seo' );
|
75 |
+
|
76 |
+
self::$meta_fields['general']['title']['title'] = __( 'SEO title', 'wordpress-seo' );
|
77 |
+
|
78 |
+
self::$meta_fields['general']['metadesc']['title'] = __( 'Meta description', 'wordpress-seo' );
|
79 |
+
|
80 |
+
/* translators: %s expands to the post type name. */
|
81 |
+
self::$meta_fields['advanced']['meta-robots-noindex']['title'] = __( 'Allow search engines to show this %s in search results?', 'wordpress-seo' );
|
82 |
+
if ( '0' === (string) get_option( 'blog_public' ) ) {
|
83 |
+
self::$meta_fields['advanced']['meta-robots-noindex']['description'] = '<p class="error-message">' . __( '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' ) . '</p>';
|
84 |
+
}
|
85 |
+
/* translators: %1$s expands to Yes or No, %2$s expands to the post type name.*/
|
86 |
+
self::$meta_fields['advanced']['meta-robots-noindex']['options']['0'] = __( 'Default for %2$s, currently: %1$s', 'wordpress-seo' );
|
87 |
+
self::$meta_fields['advanced']['meta-robots-noindex']['options']['2'] = __( 'Yes', 'wordpress-seo' );
|
88 |
+
self::$meta_fields['advanced']['meta-robots-noindex']['options']['1'] = __( 'No', 'wordpress-seo' );
|
89 |
+
|
90 |
+
/* translators: %1$s expands to the post type name.*/
|
91 |
+
self::$meta_fields['advanced']['meta-robots-nofollow']['title'] = __( 'Should search engines follow links on this %1$s?', 'wordpress-seo' );
|
92 |
+
self::$meta_fields['advanced']['meta-robots-nofollow']['options']['0'] = __( 'Yes', 'wordpress-seo' );
|
93 |
+
self::$meta_fields['advanced']['meta-robots-nofollow']['options']['1'] = __( 'No', 'wordpress-seo' );
|
94 |
+
|
95 |
+
self::$meta_fields['advanced']['meta-robots-adv']['title'] = __( 'Meta robots advanced', 'wordpress-seo' );
|
96 |
+
self::$meta_fields['advanced']['meta-robots-adv']['description'] = __( 'Advanced <code>meta</code> robots settings for this page.', 'wordpress-seo' );
|
97 |
+
/* translators: %s expands to the advanced robots settings default as set in the site-wide settings.*/
|
98 |
+
self::$meta_fields['advanced']['meta-robots-adv']['options']['-'] = __( 'Site-wide default: %s', 'wordpress-seo' );
|
99 |
+
self::$meta_fields['advanced']['meta-robots-adv']['options']['none'] = __( 'None', 'wordpress-seo' );
|
100 |
+
self::$meta_fields['advanced']['meta-robots-adv']['options']['noimageindex'] = __( 'No Image Index', 'wordpress-seo' );
|
101 |
+
self::$meta_fields['advanced']['meta-robots-adv']['options']['noarchive'] = __( 'No Archive', 'wordpress-seo' );
|
102 |
+
self::$meta_fields['advanced']['meta-robots-adv']['options']['nosnippet'] = __( 'No Snippet', 'wordpress-seo' );
|
103 |
+
|
104 |
+
self::$meta_fields['advanced']['bctitle']['title'] = __( 'Breadcrumbs Title', 'wordpress-seo' );
|
105 |
+
self::$meta_fields['advanced']['bctitle']['description'] = __( 'Title to use for this page in breadcrumb paths', 'wordpress-seo' );
|
106 |
+
|
107 |
+
self::$meta_fields['advanced']['canonical']['title'] = __( 'Canonical URL', 'wordpress-seo' );
|
108 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
109 |
+
self::$meta_fields['advanced']['canonical']['description'] = sprintf( __( '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' ), '<a target="_blank" href="http://googlewebmastercentral.blogspot.com/2009/12/handling-legitimate-cross-domain.html">', '</a>' );
|
110 |
+
|
111 |
+
self::$meta_fields['advanced']['redirect']['title'] = __( '301 Redirect', 'wordpress-seo' );
|
112 |
+
self::$meta_fields['advanced']['redirect']['description'] = __( 'The URL that this page should redirect to.', 'wordpress-seo' );
|
113 |
+
|
114 |
+
do_action( 'wpseo_tab_translate' );
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Determines whether the metabox should be shown for the passed identifier.
|
119 |
+
*
|
120 |
+
* By default the check is done for post types, but can also be used for taxonomies.
|
121 |
+
*
|
122 |
+
* @param string|null $identifier The identifier to check.
|
123 |
+
* @param string $type The type of object to check. Defaults to post_type.
|
124 |
+
*
|
125 |
+
* @return bool Whether or not the metabox should be displayed.
|
126 |
+
*/
|
127 |
+
public function display_metabox( $identifier = null, $type = 'post_type' ) {
|
128 |
+
return WPSEO_Utils::is_metabox_active( $identifier, $type );
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Sets up all the functionality related to the prominence of the page analysis functionality.
|
133 |
+
*/
|
134 |
+
public function setup_page_analysis() {
|
135 |
+
if ( apply_filters( 'wpseo_use_page_analysis', true ) === true ) {
|
136 |
+
add_action( 'post_submitbox_start', array( $this, 'publish_box' ) );
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Outputs the page analysis score in the Publish Box.
|
142 |
+
*/
|
143 |
+
public function publish_box() {
|
144 |
+
if ( $this->display_metabox() === false ) {
|
145 |
+
return;
|
146 |
+
}
|
147 |
+
|
148 |
+
$post = $this->get_metabox_post();
|
149 |
+
|
150 |
+
if ( self::get_value( 'meta-robots-noindex', $post->ID ) === '1' ) {
|
151 |
+
$score_label = 'noindex';
|
152 |
+
$title = __( 'Post is set to noindex.', 'wordpress-seo' );
|
153 |
+
$score_title = $title;
|
154 |
+
}
|
155 |
+
else {
|
156 |
+
$score = self::get_value( 'linkdex', $post->ID );
|
157 |
+
if ( $score === '' ) {
|
158 |
+
$score_label = 'na';
|
159 |
+
$title = __( 'No focus keyword set.', 'wordpress-seo' );
|
160 |
+
}
|
161 |
+
else {
|
162 |
+
$score_label = WPSEO_Utils::translate_score( $score );
|
163 |
+
}
|
164 |
+
|
165 |
+
$score_title = WPSEO_Utils::translate_score( $score, false );
|
166 |
+
|
167 |
+
if ( ! isset( $title ) ) {
|
168 |
+
$title = $score_title;
|
169 |
+
}
|
170 |
+
}
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Adds the Yoast SEO meta box to the edit boxes in the edit post, page,
|
175 |
+
* attachment, and custom post types pages.
|
176 |
+
*
|
177 |
+
* @return void
|
178 |
+
*/
|
179 |
+
public function add_meta_box() {
|
180 |
+
$post_types = WPSEO_Post_Type::get_accessible_post_types();
|
181 |
+
|
182 |
+
if ( ! is_array( $post_types ) || $post_types === array() ) {
|
183 |
+
return;
|
184 |
+
}
|
185 |
+
|
186 |
+
foreach ( $post_types as $post_type ) {
|
187 |
+
if ( $this->display_metabox( $post_type ) === false ) {
|
188 |
+
continue;
|
189 |
+
}
|
190 |
+
|
191 |
+
$product_title = 'Yoast SEO';
|
192 |
+
|
193 |
+
if ( file_exists( WPSEO_PATH . 'premium/' ) ) {
|
194 |
+
$product_title .= ' Premium';
|
195 |
+
}
|
196 |
+
|
197 |
+
if ( get_current_screen() !== null ) {
|
198 |
+
$screen_id = get_current_screen()->id;
|
199 |
+
add_filter( "postbox_classes_{$screen_id}_wpseo_meta", array( $this, 'wpseo_metabox_class' ) );
|
200 |
+
}
|
201 |
+
|
202 |
+
add_meta_box( 'wpseo_meta', $product_title, array(
|
203 |
+
$this,
|
204 |
+
'meta_box',
|
205 |
+
), $post_type, 'normal', apply_filters( 'wpseo_metabox_prio', 'high' ) );
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Adds CSS classes to the meta box.
|
211 |
+
*
|
212 |
+
* @param array $classes An array of postbox CSS classes.
|
213 |
+
*
|
214 |
+
* @return array List of classes that will be applied to the editbox container.
|
215 |
+
*/
|
216 |
+
public function wpseo_metabox_class( $classes ) {
|
217 |
+
$classes[] = 'yoast wpseo-metabox';
|
218 |
+
|
219 |
+
return $classes;
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Pass variables to js for use with the post-scraper.
|
224 |
+
*
|
225 |
+
* @return array
|
226 |
+
*/
|
227 |
+
public function localize_post_scraper_script() {
|
228 |
+
$post = $this->get_metabox_post();
|
229 |
+
$permalink = '';
|
230 |
+
|
231 |
+
if ( is_object( $post ) ) {
|
232 |
+
$permalink = get_sample_permalink( $post->ID );
|
233 |
+
$permalink = $permalink[0];
|
234 |
+
}
|
235 |
+
|
236 |
+
$post_formatter = new WPSEO_Metabox_Formatter(
|
237 |
+
new WPSEO_Post_Metabox_Formatter( $post, array(), $permalink )
|
238 |
+
);
|
239 |
+
|
240 |
+
return $post_formatter->get_values();
|
241 |
+
}
|
242 |
+
|
243 |
+
/**
|
244 |
+
* Pass some variables to js for replacing variables.
|
245 |
+
*/
|
246 |
+
public function localize_replace_vars_script() {
|
247 |
+
return array(
|
248 |
+
'no_parent_text' => __( '(no parent)', 'wordpress-seo' ),
|
249 |
+
'replace_vars' => $this->get_replace_vars(),
|
250 |
+
'scope' => $this->determine_scope(),
|
251 |
+
);
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* Determines the scope based on the post type.
|
256 |
+
* This can be used by the replacevar plugin to determine if a replacement needs to be executed.
|
257 |
+
*
|
258 |
+
* @return string String decribing the current scope.
|
259 |
+
*/
|
260 |
+
private function determine_scope() {
|
261 |
+
$post_type = get_post_type( $this->get_metabox_post() );
|
262 |
+
|
263 |
+
if ( $post_type === 'page' ) {
|
264 |
+
return 'page';
|
265 |
+
}
|
266 |
+
|
267 |
+
return 'post';
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* Pass some variables to js for the edit / post page overview, snippet preview, etc.
|
272 |
+
*
|
273 |
+
* @return array
|
274 |
+
*/
|
275 |
+
public function localize_shortcode_plugin_script() {
|
276 |
+
return array(
|
277 |
+
'wpseo_filter_shortcodes_nonce' => wp_create_nonce( 'wpseo-filter-shortcodes' ),
|
278 |
+
'wpseo_shortcode_tags' => $this->get_valid_shortcode_tags(),
|
279 |
+
);
|
280 |
+
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Output a tab in the Yoast SEO Metabox.
|
284 |
+
*
|
285 |
+
* @param string $id CSS ID of the tab.
|
286 |
+
* @param string $heading Heading for the tab.
|
287 |
+
* @param string $content Content of the tab. This content should be escaped.
|
288 |
+
*/
|
289 |
+
public function do_tab( $id, $heading, $content ) {
|
290 |
+
?>
|
291 |
+
<div id="<?php echo esc_attr( 'wpseo_' . $id ); ?>" class="wpseotab <?php echo esc_attr( $id ); ?>">
|
292 |
+
<?php echo $content; ?>
|
293 |
+
</div>
|
294 |
+
<?php
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* Output the meta box.
|
299 |
+
*/
|
300 |
+
public function meta_box() {
|
301 |
+
$content_sections = $this->get_content_sections();
|
302 |
+
|
303 |
+
$helpcenter_tab = new WPSEO_Option_Tab( 'metabox', __( 'Meta box', 'wordpress-seo' ),
|
304 |
+
array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/metabox-screencast' ) ) );
|
305 |
+
|
306 |
+
$help_center = new WPSEO_Help_Center( '', $helpcenter_tab, WPSEO_Utils::is_yoast_seo_premium() );
|
307 |
+
$help_center->localize_data();
|
308 |
+
$help_center->mount();
|
309 |
+
|
310 |
+
if ( ! defined( 'WPSEO_PREMIUM_FILE' ) ) {
|
311 |
+
echo $this->get_buy_premium_link();
|
312 |
+
}
|
313 |
+
|
314 |
+
echo '<div class="wpseo-metabox-content">';
|
315 |
+
echo '<div class="wpseo-metabox-sidebar"><ul>';
|
316 |
+
|
317 |
+
foreach ( $content_sections as $content_section ) {
|
318 |
+
if ( $content_section->name === 'premium' ) {
|
319 |
+
continue;
|
320 |
+
}
|
321 |
+
|
322 |
+
$content_section->display_link();
|
323 |
+
}
|
324 |
+
|
325 |
+
echo '</ul></div>';
|
326 |
+
|
327 |
+
foreach ( $content_sections as $content_section ) {
|
328 |
+
$content_section->display_content();
|
329 |
+
}
|
330 |
+
|
331 |
+
echo '</div>';
|
332 |
+
}
|
333 |
+
|
334 |
+
/**
|
335 |
+
* Returns the relevant metabox sections for the current view.
|
336 |
+
*
|
337 |
+
* @return WPSEO_Metabox_Section[]
|
338 |
+
*/
|
339 |
+
private function get_content_sections() {
|
340 |
+
$content_sections = array( $this->get_content_meta_section() );
|
341 |
+
|
342 |
+
// Check if social_admin is an instance of WPSEO_Social_Admin.
|
343 |
+
if ( $this->social_admin instanceof WPSEO_Social_Admin ) {
|
344 |
+
$content_sections[] = $this->social_admin->get_meta_section();
|
345 |
+
}
|
346 |
+
|
347 |
+
if ( WPSEO_Capability_Utils::current_user_can( 'wpseo_edit_advanced_metadata' ) || WPSEO_Options::get( 'disableadvanced_meta' ) === false ) {
|
348 |
+
$content_sections[] = $this->get_advanced_meta_section();
|
349 |
+
}
|
350 |
+
|
351 |
+
if ( ! defined( 'WPSEO_PREMIUM_FILE' ) ) {
|
352 |
+
$content_sections[] = $this->get_buy_premium_section();
|
353 |
+
}
|
354 |
+
|
355 |
+
if ( has_action( 'wpseo_tab_header' ) || has_action( 'wpseo_tab_content' ) ) {
|
356 |
+
$content_sections[] = $this->get_addons_meta_section();
|
357 |
+
}
|
358 |
+
|
359 |
+
return $content_sections;
|
360 |
+
}
|
361 |
+
|
362 |
+
/**
|
363 |
+
* Returns the metabox section for the content analysis.
|
364 |
+
*
|
365 |
+
* @return WPSEO_Metabox_Section
|
366 |
+
*/
|
367 |
+
private function get_content_meta_section() {
|
368 |
+
$content = $this->get_tab_content( 'general' );
|
369 |
+
|
370 |
+
$tabs = array();
|
371 |
+
|
372 |
+
$tabs[] = new WPSEO_Metabox_Form_Tab(
|
373 |
+
'content',
|
374 |
+
$content,
|
375 |
+
'',
|
376 |
+
array(
|
377 |
+
'tab_class' => 'yoast-seo__remove-tab',
|
378 |
+
)
|
379 |
+
);
|
380 |
+
|
381 |
+
$tabs[] = new WPSEO_Metabox_Add_Keyword_Tab();
|
382 |
+
|
383 |
+
return new WPSEO_Metabox_Tab_Section(
|
384 |
+
'content',
|
385 |
+
'<span class="screen-reader-text">' . __( 'Content optimization', 'wordpress-seo' ) . '</span><span class="yst-traffic-light-container">' . $this->traffic_light_svg() . '</span>',
|
386 |
+
$tabs,
|
387 |
+
array(
|
388 |
+
'link_aria_label' => __( 'Content optimization', 'wordpress-seo' ),
|
389 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-e',
|
390 |
+
)
|
391 |
+
);
|
392 |
+
}
|
393 |
+
|
394 |
+
/**
|
395 |
+
* Returns the metabox section for the advanced settings.
|
396 |
+
*
|
397 |
+
* @return WPSEO_Metabox_Section
|
398 |
+
*/
|
399 |
+
private function get_advanced_meta_section() {
|
400 |
+
$content = $this->get_tab_content( 'advanced' );
|
401 |
+
|
402 |
+
$tab = new WPSEO_Metabox_Form_Tab(
|
403 |
+
'advanced',
|
404 |
+
$content,
|
405 |
+
__( 'Advanced', 'wordpress-seo' ),
|
406 |
+
array(
|
407 |
+
'single' => true,
|
408 |
+
)
|
409 |
+
);
|
410 |
+
|
411 |
+
return new WPSEO_Metabox_Tab_Section(
|
412 |
+
'advanced',
|
413 |
+
'<span class="screen-reader-text">' . __( 'Advanced', 'wordpress-seo' ) . '</span><span class="dashicons dashicons-admin-generic"></span>',
|
414 |
+
array( $tab ),
|
415 |
+
array(
|
416 |
+
'link_aria_label' => __( 'Advanced', 'wordpress-seo' ),
|
417 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-e',
|
418 |
+
)
|
419 |
+
);
|
420 |
+
}
|
421 |
+
|
422 |
+
/**
|
423 |
+
* Returns a link to activate the Buy Premium tab.
|
424 |
+
*
|
425 |
+
* @return string
|
426 |
+
*/
|
427 |
+
private function get_buy_premium_link() {
|
428 |
+
return sprintf( "<div class='%s'><a href='#wpseo-meta-section-premium' class='wpseo-meta-section-link'><span class='dashicons dashicons-star-filled wpseo-buy-premium'></span>%s</a></div>",
|
429 |
+
'wpseo-metabox-buy-premium',
|
430 |
+
__( 'Go Premium', 'wordpress-seo' )
|
431 |
+
);
|
432 |
+
}
|
433 |
+
|
434 |
+
/**
|
435 |
+
* Returns the metabox section for the Premium section.
|
436 |
+
*
|
437 |
+
* @return WPSEO_Metabox_Section
|
438 |
+
*/
|
439 |
+
private function get_buy_premium_section() {
|
440 |
+
$content = sprintf( "<div class='wpseo-premium-description'>
|
441 |
+
%s
|
442 |
+
<ul class='wpseo-premium-advantages-list'>
|
443 |
+
<li>
|
444 |
+
<strong>%s</strong> - %s
|
445 |
+
</li>
|
446 |
+
<li>
|
447 |
+
<strong>%s</strong> - %s
|
448 |
+
</li>
|
449 |
+
<li>
|
450 |
+
<strong>%s</strong> - %s
|
451 |
+
</li>
|
452 |
+
<li>
|
453 |
+
<strong>%s</strong> - %s
|
454 |
+
</li>
|
455 |
+
</ul>
|
456 |
+
|
457 |
+
<a target='_blank' id='wpseo-buy-premium-popup-button' class='button button-buy-premium wpseo-metabox-go-to' href='%s'>
|
458 |
+
%s
|
459 |
+
</a>
|
460 |
+
|
461 |
+
<p><a target='_blank' class='wpseo-metabox-go-to' href='%s'>%s</a></p>
|
462 |
+
</div>",
|
463 |
+
/* translators: %1$s expands to Yoast SEO Premium. */
|
464 |
+
sprintf( __( 'You\'re not getting the benefits of %1$s yet. If you had %1$s, you could use its awesome features:', 'wordpress-seo' ), 'Yoast SEO Premium' ),
|
465 |
+
__( 'Redirect manager', 'wordpress-seo' ),
|
466 |
+
__( 'Create and manage redirects within your WordPress install.', 'wordpress-seo' ),
|
467 |
+
__( 'Multiple focus keywords', 'wordpress-seo' ),
|
468 |
+
__( 'Optimize a single post for up to 5 keywords.', 'wordpress-seo' ),
|
469 |
+
__( 'Social Previews', 'wordpress-seo' ),
|
470 |
+
__( 'Check what your Facebook or Twitter post will look like.', 'wordpress-seo' ),
|
471 |
+
__( 'Premium support', 'wordpress-seo' ),
|
472 |
+
__( 'Gain access to our 24/7 support team.', 'wordpress-seo' ),
|
473 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/pe-buy-premium' ),
|
474 |
+
/* translators: %s expands to Yoast SEO Premium. */
|
475 |
+
sprintf( __( 'Get %s now!', 'wordpress-seo' ), 'Yoast SEO Premium' ),
|
476 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/pe-premium-page' ),
|
477 |
+
__( 'More info', 'wordpress-seo' )
|
478 |
+
);
|
479 |
+
|
480 |
+
$tab = new WPSEO_Metabox_Form_Tab(
|
481 |
+
'premium',
|
482 |
+
$content,
|
483 |
+
'Yoast SEO Premium',
|
484 |
+
array(
|
485 |
+
'single' => true,
|
486 |
+
)
|
487 |
+
);
|
488 |
+
|
489 |
+
return new WPSEO_Metabox_Tab_Section(
|
490 |
+
'premium',
|
491 |
+
'<span class="dashicons dashicons-star-filled wpseo-buy-premium"></span>',
|
492 |
+
array( $tab ),
|
493 |
+
array(
|
494 |
+
'link_aria_label' => 'Yoast SEO Premium',
|
495 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-e',
|
496 |
+
)
|
497 |
+
);
|
498 |
+
}
|
499 |
+
|
500 |
+
/**
|
501 |
+
* Returns a metabox section dedicated to hosting metabox tabs that have been added by other plugins through the
|
502 |
+
* `wpseo_tab_header` and `wpseo_tab_content` actions.
|
503 |
+
*
|
504 |
+
* @return WPSEO_Metabox_Section
|
505 |
+
*/
|
506 |
+
private function get_addons_meta_section() {
|
507 |
+
return new WPSEO_Metabox_Addon_Tab_Section(
|
508 |
+
'addons',
|
509 |
+
'<span class="screen-reader-text">' . __( 'Add-ons', 'wordpress-seo' ) . '</span><span class="dashicons dashicons-admin-plugins"></span>',
|
510 |
+
array(),
|
511 |
+
array(
|
512 |
+
'link_aria_label' => __( 'Add-ons', 'wordpress-seo' ),
|
513 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-e',
|
514 |
+
)
|
515 |
+
);
|
516 |
+
}
|
517 |
+
|
518 |
+
/**
|
519 |
+
* Gets the contents for the metabox tab.
|
520 |
+
*
|
521 |
+
* @param string $tab_name Tab for which to retrieve the field definitions.
|
522 |
+
*
|
523 |
+
* @return string
|
524 |
+
*/
|
525 |
+
private function get_tab_content( $tab_name ) {
|
526 |
+
$content = '';
|
527 |
+
foreach ( $this->get_meta_field_defs( $tab_name ) as $key => $meta_field ) {
|
528 |
+
$content .= $this->do_meta_box( $meta_field, $key );
|
529 |
+
}
|
530 |
+
unset( $key, $meta_field );
|
531 |
+
|
532 |
+
return $content;
|
533 |
+
}
|
534 |
+
|
535 |
+
/**
|
536 |
+
* Adds a line in the meta box.
|
537 |
+
*
|
538 |
+
* @todo [JRF] Check if $class is added appropriately everywhere.
|
539 |
+
*
|
540 |
+
* @param array $meta_field_def Contains the vars based on which output is generated.
|
541 |
+
* @param string $key Internal key (without prefix).
|
542 |
+
*
|
543 |
+
* @return string
|
544 |
+
*/
|
545 |
+
public function do_meta_box( $meta_field_def, $key = '' ) {
|
546 |
+
$content = '';
|
547 |
+
$esc_form_key = esc_attr( self::$form_prefix . $key );
|
548 |
+
$meta_value = self::get_value( $key, $this->get_metabox_post()->ID );
|
549 |
+
|
550 |
+
$class = '';
|
551 |
+
if ( isset( $meta_field_def['class'] ) && $meta_field_def['class'] !== '' ) {
|
552 |
+
$class = ' ' . $meta_field_def['class'];
|
553 |
+
}
|
554 |
+
|
555 |
+
$placeholder = '';
|
556 |
+
if ( isset( $meta_field_def['placeholder'] ) && $meta_field_def['placeholder'] !== '' ) {
|
557 |
+
$placeholder = $meta_field_def['placeholder'];
|
558 |
+
}
|
559 |
+
|
560 |
+
$aria_describedby = '';
|
561 |
+
$description = '';
|
562 |
+
if ( isset( $meta_field_def['description'] ) ) {
|
563 |
+
$aria_describedby = ' aria-describedby="' . $esc_form_key . '-desc"';
|
564 |
+
$description = '<p id="' . $esc_form_key . '-desc" class="yoast-metabox__description">' . $meta_field_def['description'] . '</p>';
|
565 |
+
}
|
566 |
+
|
567 |
+
switch ( $meta_field_def['type'] ) {
|
568 |
+
case 'pageanalysis':
|
569 |
+
if ( WPSEO_Options::get( 'content_analysis_active' ) === false && WPSEO_Options::get( 'keyword_analysis_active' ) === false ) {
|
570 |
+
break;
|
571 |
+
}
|
572 |
+
|
573 |
+
$content .= '<div id="pageanalysis">';
|
574 |
+
$content .= '<section class="yoast-section" id="wpseo-pageanalysis-section">';
|
575 |
+
$content .= '<h3 class="yoast-section__heading yoast-section__heading-icon yoast-section__heading-icon-list">' . __( 'Analysis', 'wordpress-seo' ) . '</h3>';
|
576 |
+
$content .= '<div id="wpseo-pageanalysis"></div>';
|
577 |
+
$content .= '<div id="yoast-seo-content-analysis"></div>';
|
578 |
+
$content .= '</section>';
|
579 |
+
$content .= '</div>';
|
580 |
+
break;
|
581 |
+
case 'snippetpreview':
|
582 |
+
$content .= '<div id="wpseosnippet" class="wpseosnippet"></div>';
|
583 |
+
break;
|
584 |
+
case 'focuskeyword':
|
585 |
+
if ( $placeholder !== '' ) {
|
586 |
+
$placeholder = ' placeholder="' . esc_attr( $placeholder ) . '"';
|
587 |
+
}
|
588 |
+
|
589 |
+
$content .= '<div id="wpseofocuskeyword">';
|
590 |
+
$content .= '<section class="yoast-section" id="wpseo-focuskeyword-section">';
|
591 |
+
$content .= '<h3 class="yoast-section__heading yoast-section__heading-icon yoast-section__heading-icon-key">' . esc_html( $meta_field_def['title'] ) . '</h3>';
|
592 |
+
$content .= '<label for="' . $esc_form_key . '" class="screen-reader-text">' . esc_html( $meta_field_def['label'] ) . '</label>';
|
593 |
+
$content .= '<input type="text"' . $placeholder . ' id="' . $esc_form_key . '" autocomplete="off" name="' . $esc_form_key . '" value="' . esc_attr( $meta_value ) . '" class="large-text' . $class . '"/>';
|
594 |
+
|
595 |
+
if ( WPSEO_Options::get( 'enable_cornerstone_content', false ) ) {
|
596 |
+
$cornerstone_field = new WPSEO_Cornerstone_Field();
|
597 |
+
|
598 |
+
$content .= $cornerstone_field->get_html( $this->get_metabox_post() );
|
599 |
+
}
|
600 |
+
$content .= '</section>';
|
601 |
+
$content .= '</div>';
|
602 |
+
break;
|
603 |
+
case 'text':
|
604 |
+
$ac = '';
|
605 |
+
if ( isset( $meta_field_def['autocomplete'] ) && $meta_field_def['autocomplete'] === false ) {
|
606 |
+
$ac = 'autocomplete="off" ';
|
607 |
+
}
|
608 |
+
if ( $placeholder !== '' ) {
|
609 |
+
$placeholder = ' placeholder="' . esc_attr( $placeholder ) . '"';
|
610 |
+
}
|
611 |
+
$content .= '<input type="text"' . $placeholder . ' id="' . $esc_form_key . '" ' . $ac . 'name="' . $esc_form_key . '" value="' . esc_attr( $meta_value ) . '" class="large-text' . $class . '"' . $aria_describedby . '/>';
|
612 |
+
break;
|
613 |
+
|
614 |
+
case 'textarea':
|
615 |
+
$rows = 3;
|
616 |
+
if ( isset( $meta_field_def['rows'] ) && $meta_field_def['rows'] > 0 ) {
|
617 |
+
$rows = $meta_field_def['rows'];
|
618 |
+
}
|
619 |
+
$content .= '<textarea class="large-text' . $class . '" rows="' . esc_attr( $rows ) . '" id="' . $esc_form_key . '" name="' . $esc_form_key . '"' . $aria_describedby . '>' . esc_textarea( $meta_value ) . '</textarea>';
|
620 |
+
break;
|
621 |
+
|
622 |
+
case 'hidden':
|
623 |
+
$content .= '<input type="hidden" id="' . $esc_form_key . '" name="' . $esc_form_key . '" value="' . esc_attr( $meta_value ) . '"/>' . "\n";
|
624 |
+
break;
|
625 |
+
case 'select':
|
626 |
+
if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== array() ) {
|
627 |
+
$content .= '<select name="' . $esc_form_key . '" id="' . $esc_form_key . '" class="yoast' . $class . '">';
|
628 |
+
foreach ( $meta_field_def['options'] as $val => $option ) {
|
629 |
+
$selected = selected( $meta_value, $val, false );
|
630 |
+
$content .= '<option ' . $selected . ' value="' . esc_attr( $val ) . '">' . esc_html( $option ) . '</option>';
|
631 |
+
}
|
632 |
+
unset( $val, $option, $selected );
|
633 |
+
$content .= '</select>';
|
634 |
+
}
|
635 |
+
break;
|
636 |
+
|
637 |
+
case 'multiselect':
|
638 |
+
if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== array() ) {
|
639 |
+
|
640 |
+
// Set $meta_value as $selected_arr.
|
641 |
+
$selected_arr = $meta_value;
|
642 |
+
|
643 |
+
// If the multiselect field is 'meta-robots-adv' we should explode on ,.
|
644 |
+
if ( 'meta-robots-adv' === $key ) {
|
645 |
+
$selected_arr = explode( ',', $meta_value );
|
646 |
+
}
|
647 |
+
|
648 |
+
if ( ! is_array( $selected_arr ) ) {
|
649 |
+
$selected_arr = (array) $selected_arr;
|
650 |
+
}
|
651 |
+
|
652 |
+
$options_count = count( $meta_field_def['options'] );
|
653 |
+
|
654 |
+
// This select now uses Select2.
|
655 |
+
$content .= '<select multiple="multiple" size="' . esc_attr( $options_count ) . '" name="' . $esc_form_key . '[]" id="' . $esc_form_key . '" class="yoast' . $class . '"' . $aria_describedby . '>';
|
656 |
+
foreach ( $meta_field_def['options'] as $val => $option ) {
|
657 |
+
$selected = '';
|
658 |
+
if ( in_array( $val, $selected_arr ) ) {
|
659 |
+
$selected = ' selected="selected"';
|
660 |
+
}
|
661 |
+
$content .= '<option ' . $selected . ' value="' . esc_attr( $val ) . '">' . esc_html( $option ) . '</option>';
|
662 |
+
}
|
663 |
+
$content .= '</select>';
|
664 |
+
unset( $val, $option, $selected, $selected_arr, $options_count );
|
665 |
+
}
|
666 |
+
break;
|
667 |
+
|
668 |
+
case 'checkbox':
|
669 |
+
$checked = checked( $meta_value, 'on', false );
|
670 |
+
$expl = ( isset( $meta_field_def['expl'] ) ) ? esc_html( $meta_field_def['expl'] ) : '';
|
671 |
+
$content .= '<input type="checkbox" id="' . $esc_form_key . '" name="' . $esc_form_key . '" ' . $checked . ' value="on" class="yoast' . $class . '"' . $aria_describedby . '/> <label for="' . $esc_form_key . '">' . $expl . '</label>';
|
672 |
+
unset( $checked, $expl );
|
673 |
+
break;
|
674 |
+
|
675 |
+
case 'radio':
|
676 |
+
if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== array() ) {
|
677 |
+
foreach ( $meta_field_def['options'] as $val => $option ) {
|
678 |
+
$checked = checked( $meta_value, $val, false );
|
679 |
+
$content .= '<input type="radio" ' . $checked . ' id="' . $esc_form_key . '_' . esc_attr( $val ) . '" name="' . $esc_form_key . '" value="' . esc_attr( $val ) . '"/> <label for="' . $esc_form_key . '_' . esc_attr( $val ) . '">' . esc_html( $option ) . '</label> ';
|
680 |
+
}
|
681 |
+
unset( $val, $option, $checked );
|
682 |
+
}
|
683 |
+
break;
|
684 |
+
|
685 |
+
case 'upload':
|
686 |
+
$content .= '<input id="' . $esc_form_key . '" type="text" size="36" class="' . $class . '" name="' . $esc_form_key . '" value="' . esc_attr( $meta_value ) . '"' . $aria_describedby . ' />';
|
687 |
+
$content .= '<input id="' . $esc_form_key . '_button" class="wpseo_image_upload_button button" type="button" value="' . esc_attr__( 'Upload Image', 'wordpress-seo' ) . '" />';
|
688 |
+
break;
|
689 |
+
}
|
690 |
+
|
691 |
+
$html = '';
|
692 |
+
if ( $content === '' ) {
|
693 |
+
$content = apply_filters( 'wpseo_do_meta_box_field_' . $key, $content, $meta_value, $esc_form_key, $meta_field_def, $key );
|
694 |
+
}
|
695 |
+
|
696 |
+
if ( $content !== '' ) {
|
697 |
+
|
698 |
+
$title = esc_html( $meta_field_def['title'] );
|
699 |
+
|
700 |
+
// By default, use the field title as a label element.
|
701 |
+
$label = '<label for="' . $esc_form_key . '">' . $title . '</label>';
|
702 |
+
|
703 |
+
// Set the inline help and help panel, if any.
|
704 |
+
$help_button = '';
|
705 |
+
$help_panel = '';
|
706 |
+
if ( isset( $meta_field_def['help'] ) && $meta_field_def['help'] !== '' ) {
|
707 |
+
$help = new WPSEO_Admin_Help_Panel( $key, $meta_field_def['help-button'], $meta_field_def['help'] );
|
708 |
+
$help_button = $help->get_button_html();
|
709 |
+
$help_panel = $help->get_panel_html();
|
710 |
+
}
|
711 |
+
|
712 |
+
// If it's a set of radio buttons, output proper fieldset and legend.
|
713 |
+
if ( 'radio' === $meta_field_def['type'] ) {
|
714 |
+
return '<fieldset><legend>' . $title . '</legend>' . $help_button . $help_panel . $content . $description . '</fieldset>';
|
715 |
+
}
|
716 |
+
|
717 |
+
// If it's a single checkbox, ignore the title.
|
718 |
+
if ( 'checkbox' === $meta_field_def['type'] ) {
|
719 |
+
$label = '';
|
720 |
+
}
|
721 |
+
|
722 |
+
// Special meta box sections such as the Snippet Preview, the Analysis, etc.
|
723 |
+
if ( in_array( $meta_field_def['type'], array(
|
724 |
+
'snippetpreview',
|
725 |
+
'pageanalysis',
|
726 |
+
'focuskeyword',
|
727 |
+
), true )
|
728 |
+
) {
|
729 |
+
return $this->create_content_box( $content, $meta_field_def['type'], $help_button, $help_panel );
|
730 |
+
}
|
731 |
+
|
732 |
+
// Other meta box content or form fields.
|
733 |
+
if ( $meta_field_def['type'] === 'hidden' ) {
|
734 |
+
$html = $content;
|
735 |
+
}
|
736 |
+
else {
|
737 |
+
$html = $label . $help_button . $help_panel . $content . $description;
|
738 |
+
}
|
739 |
+
}
|
740 |
+
|
741 |
+
return $html;
|
742 |
+
}
|
743 |
+
|
744 |
+
/**
|
745 |
+
* Creates a sections specific row.
|
746 |
+
*
|
747 |
+
* @param string $content The content to show.
|
748 |
+
* @param string $hidden_help_name Escaped form key name.
|
749 |
+
* @param string $help_button The help button.
|
750 |
+
* @param string $help_panel The help text.
|
751 |
+
*
|
752 |
+
* @return string
|
753 |
+
*/
|
754 |
+
private function create_content_box( $content, $hidden_help_name, $help_button, $help_panel ) {
|
755 |
+
$html = $content;
|
756 |
+
$html .= '<div class="wpseo_hidden" id="help-yoast-' . $hidden_help_name . '">' . $help_button . $help_panel . '</div>';
|
757 |
+
|
758 |
+
return $html;
|
759 |
+
}
|
760 |
+
|
761 |
+
/**
|
762 |
+
* Save the WP SEO metadata for posts.
|
763 |
+
*
|
764 |
+
* {@internal $_POST parameters are validated via sanitize_post_meta().}}
|
765 |
+
*
|
766 |
+
* @param int $post_id Post ID.
|
767 |
+
*
|
768 |
+
* @return bool|void Boolean false if invalid save post request.
|
769 |
+
*/
|
770 |
+
public function save_postdata( $post_id ) {
|
771 |
+
// Bail if this is a multisite installation and the site has been switched.
|
772 |
+
if ( is_multisite() && ms_is_switched() ) {
|
773 |
+
return false;
|
774 |
+
}
|
775 |
+
|
776 |
+
if ( $post_id === null ) {
|
777 |
+
return false;
|
778 |
+
}
|
779 |
+
|
780 |
+
if ( wp_is_post_revision( $post_id ) ) {
|
781 |
+
$post_id = wp_is_post_revision( $post_id );
|
782 |
+
}
|
783 |
+
|
784 |
+
/**
|
785 |
+
* Determine we're not accidentally updating a different post.
|
786 |
+
* We can't use filter_input here as the ID isn't available at this point, other than in the $_POST data.
|
787 |
+
*/
|
788 |
+
// @codingStandardsIgnoreStart
|
789 |
+
if ( ! isset( $_POST['ID'] ) || $post_id !== (int) $_POST['ID'] ) {
|
790 |
+
return false;
|
791 |
+
}
|
792 |
+
// @codingStandardsIgnoreEnd
|
793 |
+
|
794 |
+
clean_post_cache( $post_id );
|
795 |
+
$post = get_post( $post_id );
|
796 |
+
|
797 |
+
if ( ! is_object( $post ) ) {
|
798 |
+
// Non-existent post.
|
799 |
+
return false;
|
800 |
+
}
|
801 |
+
|
802 |
+
do_action( 'wpseo_save_compare_data', $post );
|
803 |
+
|
804 |
+
$meta_boxes = apply_filters( 'wpseo_save_metaboxes', array() );
|
805 |
+
$meta_boxes = array_merge( $meta_boxes, $this->get_meta_field_defs( 'general', $post->post_type ), $this->get_meta_field_defs( 'advanced' ) );
|
806 |
+
|
807 |
+
foreach ( $meta_boxes as $key => $meta_box ) {
|
808 |
+
|
809 |
+
// If analysis is disabled remove that analysis score value from the DB.
|
810 |
+
if ( $this->is_meta_value_disabled( $key ) ) {
|
811 |
+
self::delete( $key, $post_id );
|
812 |
+
continue;
|
813 |
+
}
|
814 |
+
|
815 |
+
$data = null;
|
816 |
+
if ( 'checkbox' === $meta_box['type'] ) {
|
817 |
+
// @codingStandardsIgnoreLine
|
818 |
+
$data = isset( $_POST[ self::$form_prefix . $key ] ) ? 'on' : 'off';
|
819 |
+
}
|
820 |
+
else {
|
821 |
+
// @codingStandardsIgnoreLine
|
822 |
+
if ( isset( $_POST[ self::$form_prefix . $key ] ) ) {
|
823 |
+
// @codingStandardsIgnoreLine
|
824 |
+
$data = $_POST[ self::$form_prefix . $key ];
|
825 |
+
}
|
826 |
+
}
|
827 |
+
if ( isset( $data ) ) {
|
828 |
+
self::set_value( $key, $data, $post_id );
|
829 |
+
}
|
830 |
+
}
|
831 |
+
|
832 |
+
do_action( 'wpseo_saved_postdata' );
|
833 |
+
}
|
834 |
+
|
835 |
+
/**
|
836 |
+
* Determines if the given meta value key is disabled.
|
837 |
+
*
|
838 |
+
* @param string $key The key of the meta value.
|
839 |
+
*
|
840 |
+
* @return bool Whether the given meta value key is disabled.
|
841 |
+
*/
|
842 |
+
public function is_meta_value_disabled( $key ) {
|
843 |
+
if ( 'linkdex' === $key && ! $this->analysis_seo->is_enabled() ) {
|
844 |
+
return true;
|
845 |
+
}
|
846 |
+
|
847 |
+
if ( 'content_score' === $key && ! $this->analysis_readability->is_enabled() ) {
|
848 |
+
return true;
|
849 |
+
}
|
850 |
+
|
851 |
+
return false;
|
852 |
+
}
|
853 |
+
|
854 |
+
/**
|
855 |
+
* Enqueues all the needed JS and CSS.
|
856 |
+
*
|
857 |
+
* @todo [JRF => whomever] Create css/metabox-mp6.css file and add it to the below allowed colors array when done.
|
858 |
+
*/
|
859 |
+
public function enqueue() {
|
860 |
+
global $pagenow;
|
861 |
+
|
862 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
863 |
+
|
864 |
+
$is_editor = self::is_post_overview( $pagenow ) || self::is_post_edit( $pagenow );
|
865 |
+
|
866 |
+
/* Filter 'wpseo_always_register_metaboxes_on_admin' documented in wpseo-main.php */
|
867 |
+
if ( ( $is_editor === false && apply_filters( 'wpseo_always_register_metaboxes_on_admin', false ) === false ) || $this->display_metabox() === false ) {
|
868 |
+
return;
|
869 |
+
}
|
870 |
+
|
871 |
+
if ( self::is_post_overview( $pagenow ) ) {
|
872 |
+
$asset_manager->enqueue_style( 'edit-page' );
|
873 |
+
$asset_manager->enqueue_script( 'edit-page-script' );
|
874 |
+
}
|
875 |
+
else {
|
876 |
+
|
877 |
+
if ( get_queried_object_id() !== 0 ) {
|
878 |
+
wp_enqueue_media( array( 'post' => get_queried_object_id() ) ); // Enqueue files needed for upload functionality.
|
879 |
+
}
|
880 |
+
|
881 |
+
$asset_manager->enqueue_style( 'metabox-css' );
|
882 |
+
$asset_manager->enqueue_style( 'scoring' );
|
883 |
+
$asset_manager->enqueue_style( 'snippet' );
|
884 |
+
$asset_manager->enqueue_style( 'select2' );
|
885 |
+
|
886 |
+
$asset_manager->enqueue_script( 'metabox' );
|
887 |
+
$asset_manager->enqueue_script( 'help-center' );
|
888 |
+
$asset_manager->enqueue_script( 'admin-media' );
|
889 |
+
|
890 |
+
$asset_manager->enqueue_script( 'post-scraper' );
|
891 |
+
$asset_manager->enqueue_script( 'replacevar-plugin' );
|
892 |
+
$asset_manager->enqueue_script( 'shortcode-plugin' );
|
893 |
+
|
894 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-media', 'wpseoMediaL10n', $this->localize_media_script() );
|
895 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'post-scraper', 'wpseoPostScraperL10n', $this->localize_post_scraper_script() );
|
896 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'replacevar-plugin', 'wpseoReplaceVarsL10n', $this->localize_replace_vars_script() );
|
897 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'shortcode-plugin', 'wpseoShortcodePluginL10n', $this->localize_shortcode_plugin_script() );
|
898 |
+
|
899 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'metabox', 'wpseoAdminL10n', WPSEO_Help_Center::get_translated_texts() );
|
900 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'metabox', 'wpseoSelect2Locale', WPSEO_Utils::get_language( WPSEO_Utils::get_user_locale() ) );
|
901 |
+
|
902 |
+
if ( post_type_supports( get_post_type(), 'thumbnail' ) ) {
|
903 |
+
$asset_manager->enqueue_style( 'featured-image' );
|
904 |
+
|
905 |
+
$asset_manager->enqueue_script( 'featured-image' );
|
906 |
+
|
907 |
+
$featured_image_l10 = array( 'featured_image_notice' => __( 'SEO issue: The featured image should be at least 200 by 200 pixels to be picked up by Facebook and other social media sites.', 'wordpress-seo' ) );
|
908 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'metabox', 'wpseoFeaturedImageL10n', $featured_image_l10 );
|
909 |
+
}
|
910 |
+
}
|
911 |
+
}
|
912 |
+
|
913 |
+
/**
|
914 |
+
* Pass some variables to js for upload module.
|
915 |
+
*
|
916 |
+
* @return array
|
917 |
+
*/
|
918 |
+
public function localize_media_script() {
|
919 |
+
return array(
|
920 |
+
'choose_image' => __( 'Use Image', 'wordpress-seo' ),
|
921 |
+
);
|
922 |
+
}
|
923 |
+
|
924 |
+
/**
|
925 |
+
* Returns post in metabox context.
|
926 |
+
*
|
927 |
+
* @returns WP_Post|array
|
928 |
+
*/
|
929 |
+
protected function get_metabox_post() {
|
930 |
+
$post = filter_input( INPUT_GET, 'post' );
|
931 |
+
if ( ! empty( $post ) ) {
|
932 |
+
$post_id = (int) WPSEO_Utils::validate_int( $post );
|
933 |
+
|
934 |
+
return get_post( $post_id );
|
935 |
+
}
|
936 |
+
|
937 |
+
|
938 |
+
if ( isset( $GLOBALS['post'] ) ) {
|
939 |
+
return $GLOBALS['post'];
|
940 |
+
}
|
941 |
+
|
942 |
+
return array();
|
943 |
+
}
|
944 |
+
|
945 |
+
|
946 |
+
/**
|
947 |
+
* Returns an array with shortcode tags for all registered shortcodes.
|
948 |
+
*
|
949 |
+
* @return array
|
950 |
+
*/
|
951 |
+
private function get_valid_shortcode_tags() {
|
952 |
+
$shortcode_tags = array();
|
953 |
+
|
954 |
+
foreach ( $GLOBALS['shortcode_tags'] as $tag => $description ) {
|
955 |
+
array_push( $shortcode_tags, $tag );
|
956 |
+
}
|
957 |
+
|
958 |
+
return $shortcode_tags;
|
959 |
+
}
|
960 |
+
|
961 |
+
/**
|
962 |
+
* Prepares the replace vars for localization.
|
963 |
+
*
|
964 |
+
* @return array replace vars
|
965 |
+
*/
|
966 |
+
private function get_replace_vars() {
|
967 |
+
$post = $this->get_metabox_post();
|
968 |
+
|
969 |
+
$cached_replacement_vars = array();
|
970 |
+
|
971 |
+
$vars_to_cache = array(
|
972 |
+
'date',
|
973 |
+
'id',
|
974 |
+
'sitename',
|
975 |
+
'sitedesc',
|
976 |
+
'sep',
|
977 |
+
'page',
|
978 |
+
'currenttime',
|
979 |
+
'currentdate',
|
980 |
+
'currentday',
|
981 |
+
'currentmonth',
|
982 |
+
'currentyear',
|
983 |
+
);
|
984 |
+
|
985 |
+
foreach ( $vars_to_cache as $var ) {
|
986 |
+
$cached_replacement_vars[ $var ] = wpseo_replace_vars( '%%' . $var . '%%', $post );
|
987 |
+
}
|
988 |
+
|
989 |
+
// Merge custom replace variables with the WordPress ones.
|
990 |
+
return array_merge( $cached_replacement_vars, $this->get_custom_replace_vars( $post ) );
|
991 |
+
}
|
992 |
+
|
993 |
+
/**
|
994 |
+
* Gets the custom replace variables for custom taxonomies and fields.
|
995 |
+
*
|
996 |
+
* @param WP_Post $post The post to check for custom taxonomies and fields.
|
997 |
+
*
|
998 |
+
* @return array Array containing all the replacement variables.
|
999 |
+
*/
|
1000 |
+
private function get_custom_replace_vars( $post ) {
|
1001 |
+
return array(
|
1002 |
+
'custom_fields' => $this->get_custom_fields_replace_vars( $post ),
|
1003 |
+
'custom_taxonomies' => $this->get_custom_taxonomies_replace_vars( $post ),
|
1004 |
+
);
|
1005 |
+
}
|
1006 |
+
|
1007 |
+
/**
|
1008 |
+
* Gets the custom replace variables for custom taxonomies.
|
1009 |
+
*
|
1010 |
+
* @param WP_Post $post The post to check for custom taxonomies.
|
1011 |
+
*
|
1012 |
+
* @return array Array containing all the replacement variables.
|
1013 |
+
*/
|
1014 |
+
private function get_custom_taxonomies_replace_vars( $post ) {
|
1015 |
+
$taxonomies = get_object_taxonomies( $post, 'objects' );
|
1016 |
+
$custom_replace_vars = array();
|
1017 |
+
|
1018 |
+
foreach ( $taxonomies as $taxonomy_name => $taxonomy ) {
|
1019 |
+
|
1020 |
+
if ( is_string( $taxonomy ) ) { // If attachment, see https://core.trac.wordpress.org/ticket/37368 .
|
1021 |
+
$taxonomy_name = $taxonomy;
|
1022 |
+
$taxonomy = get_taxonomy( $taxonomy_name );
|
1023 |
+
}
|
1024 |
+
|
1025 |
+
if ( $taxonomy->_builtin && $taxonomy->public ) {
|
1026 |
+
continue;
|
1027 |
+
}
|
1028 |
+
|
1029 |
+
$custom_replace_vars[ $taxonomy_name ] = array(
|
1030 |
+
'name' => $taxonomy->name,
|
1031 |
+
'description' => $taxonomy->description,
|
1032 |
+
);
|
1033 |
+
}
|
1034 |
+
|
1035 |
+
return $custom_replace_vars;
|
1036 |
+
}
|
1037 |
+
|
1038 |
+
/**
|
1039 |
+
* Gets the custom replace variables for custom fields.
|
1040 |
+
*
|
1041 |
+
* @param WP_Post $post The post to check for custom fields.
|
1042 |
+
*
|
1043 |
+
* @return array Array containing all the replacement variables.
|
1044 |
+
*/
|
1045 |
+
private function get_custom_fields_replace_vars( $post ) {
|
1046 |
+
$custom_replace_vars = array();
|
1047 |
+
|
1048 |
+
// If no post object is passed, return the empty custom_replace_vars array.
|
1049 |
+
if ( ! is_object( $post ) ) {
|
1050 |
+
return $custom_replace_vars;
|
1051 |
+
}
|
1052 |
+
|
1053 |
+
$custom_fields = get_post_custom( $post->ID );
|
1054 |
+
|
1055 |
+
foreach ( $custom_fields as $custom_field_name => $custom_field ) {
|
1056 |
+
if ( substr( $custom_field_name, 0, 1 ) === '_' ) {
|
1057 |
+
continue;
|
1058 |
+
}
|
1059 |
+
|
1060 |
+
$custom_replace_vars[ $custom_field_name ] = $custom_field[0];
|
1061 |
+
}
|
1062 |
+
|
1063 |
+
return $custom_replace_vars;
|
1064 |
+
}
|
1065 |
+
|
1066 |
+
|
1067 |
+
/**
|
1068 |
+
* Return the SVG for the traffic light in the metabox.
|
1069 |
+
*/
|
1070 |
+
public function traffic_light_svg() {
|
1071 |
+
return <<<SVG
|
1072 |
+
<svg class="yst-traffic-light init" version="1.1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
|
1073 |
+
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
1074 |
+
x="0px" y="0px" viewBox="0 0 30 47" enable-background="new 0 0 30 47" xml:space="preserve">
|
1075 |
+
<g id="BG_1_">
|
1076 |
+
</g>
|
1077 |
+
<g id="traffic_light">
|
1078 |
+
<g>
|
1079 |
+
<g>
|
1080 |
+
<g>
|
1081 |
+
<path fill="#5B2942" d="M22,0H8C3.6,0,0,3.6,0,7.9v31.1C0,43.4,3.6,47,8,47h14c4.4,0,8-3.6,8-7.9V7.9C30,3.6,26.4,0,22,0z
|
1082 |
+
M27.5,38.8c0,3.1-2.6,5.7-5.8,5.7H8.3c-3.2,0-5.8-2.5-5.8-5.7V8.3c0-1.5,0.6-2.9,1.7-4c1.1-1,2.5-1.6,4.1-1.6h13.4
|
1083 |
+
c1.5,0,3,0.6,4.1,1.6c1.1,1.1,1.7,2.5,1.7,4V38.8z"/>
|
1084 |
+
</g>
|
1085 |
+
<g class="traffic-light-color traffic-light-red">
|
1086 |
+
<ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
|
1087 |
+
<ellipse fill="#DC3232" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
|
1088 |
+
<ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
|
1089 |
+
</g>
|
1090 |
+
<g class="traffic-light-color traffic-light-orange">
|
1091 |
+
<ellipse fill="#F49A00" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
|
1092 |
+
<ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
|
1093 |
+
<ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
|
1094 |
+
</g>
|
1095 |
+
<g class="traffic-light-color traffic-light-green">
|
1096 |
+
<ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
|
1097 |
+
<ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
|
1098 |
+
<ellipse fill="#63B22B" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
|
1099 |
+
</g>
|
1100 |
+
<g class="traffic-light-color traffic-light-empty">
|
1101 |
+
<ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
|
1102 |
+
<ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
|
1103 |
+
<ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
|
1104 |
+
</g>
|
1105 |
+
<g class="traffic-light-color traffic-light-init">
|
1106 |
+
<ellipse fill="#5B2942" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
|
1107 |
+
<ellipse fill="#5B2942" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
|
1108 |
+
<ellipse fill="#5B2942" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
|
1109 |
+
</g>
|
1110 |
+
</g>
|
1111 |
+
</g>
|
1112 |
+
</g>
|
1113 |
+
</svg>
|
1114 |
+
SVG;
|
1115 |
+
|
1116 |
+
}
|
1117 |
+
|
1118 |
+
/**
|
1119 |
+
* Generic tab.
|
1120 |
+
*/
|
1121 |
+
public function template_generic_tab() {
|
1122 |
+
// This template belongs to the post scraper so don't echo it if it isn't enqueued.
|
1123 |
+
if ( ! wp_script_is( WPSEO_Admin_Asset_Manager::PREFIX . 'post-scraper' ) ) {
|
1124 |
+
return;
|
1125 |
+
}
|
1126 |
+
|
1127 |
+
echo '<script type="text/html" id="tmpl-generic_tab">
|
1128 |
+
<li class="<# if ( data.classes ) { #>{{data.classes}}<# } #><# if ( data.active ) { #> active<# } #>">
|
1129 |
+
<a class="wpseo_tablink" href="#wpseo_generic" data-score="{{data.score}}">
|
1130 |
+
<span class="wpseo-score-icon {{data.score}}"></span>
|
1131 |
+
<span class="wpseo-tab-prefix">{{data.prefix}}</span>
|
1132 |
+
<span class="wpseo-tab-label">{{data.label}}</span>
|
1133 |
+
<span class="screen-reader-text wpseo-generic-tab-textual-score">{{data.scoreText}}</span>
|
1134 |
+
</a>
|
1135 |
+
<# if ( data.hideable ) { #>
|
1136 |
+
<button type="button" class="remove-tab" aria-label="{{data.removeLabel}}"><span>x</span></button>
|
1137 |
+
<# } #>
|
1138 |
+
</li>
|
1139 |
+
</script>';
|
1140 |
+
}
|
1141 |
+
|
1142 |
+
/**
|
1143 |
+
* Keyword tab for enabling analysis of multiple keywords.
|
1144 |
+
*/
|
1145 |
+
public function template_keyword_tab() {
|
1146 |
+
// This template belongs to the post scraper so don't echo it if it isn't enqueued.
|
1147 |
+
if ( ! wp_script_is( WPSEO_Admin_Asset_Manager::PREFIX . 'post-scraper' ) ) {
|
1148 |
+
return;
|
1149 |
+
}
|
1150 |
+
|
1151 |
+
echo '<script type="text/html" id="tmpl-keyword_tab">
|
1152 |
+
<li class="<# if ( data.classes ) { #>{{data.classes}}<# } #><# if ( data.active ) { #> active<# } #>">
|
1153 |
+
<a class="wpseo_tablink" href="#wpseo_content" data-keyword="{{data.keyword}}" data-score="{{data.score}}">
|
1154 |
+
<span class="wpseo-score-icon {{data.score}}"></span>
|
1155 |
+
<span class="wpseo-tab-prefix">{{data.prefix}}</span>
|
1156 |
+
<em class="wpseo-keyword">{{data.label}}</em>
|
1157 |
+
<span class="screen-reader-text wpseo-keyword-tab-textual-score">{{data.scoreText}}</span>
|
1158 |
+
</a>
|
1159 |
+
<# if ( data.hideable ) { #>
|
1160 |
+
<button type="button" class="remove-keyword" aria-label="{{data.removeLabel}}"><span>x</span></button>
|
1161 |
+
<# } #>
|
1162 |
+
</li>
|
1163 |
+
</script>';
|
1164 |
+
}
|
1165 |
+
|
1166 |
+
/**
|
1167 |
+
* @param string $page The page to check for the post overview page.
|
1168 |
+
*
|
1169 |
+
* @return bool Whether or not the given page is the post overview page.
|
1170 |
+
*/
|
1171 |
+
public static function is_post_overview( $page ) {
|
1172 |
+
return 'edit.php' === $page;
|
1173 |
+
}
|
1174 |
+
|
1175 |
+
/**
|
1176 |
+
* @param string $page The page to check for the post edit page.
|
1177 |
+
*
|
1178 |
+
* @return bool Whether or not the given page is the post edit page.
|
1179 |
+
*/
|
1180 |
+
public static function is_post_edit( $page ) {
|
1181 |
+
return 'post.php' === $page
|
1182 |
+
|| 'post-new.php' === $page;
|
1183 |
+
}
|
1184 |
+
}
|
admin/metabox/interface-metabox-analysis.php
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Metabox
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Describes an interface for an analysis that can either be enabled or disabled
|
8 |
+
*/
|
9 |
+
interface WPSEO_Metabox_Analysis {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Whether this analysis is enabled.
|
13 |
+
*
|
14 |
+
* @return bool Whether or not this analysis is enabled.
|
15 |
+
*/
|
16 |
+
public function is_enabled();
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Whether or not this analysis is enabled by the user.
|
20 |
+
*
|
21 |
+
* @return bool Whether or not this analysis is enabled by the user.
|
22 |
+
*/
|
23 |
+
public function is_user_enabled();
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Whether or not this analysis is enabled globally.
|
27 |
+
*
|
28 |
+
* @return bool Whether or not this analysis is enabled globally.
|
29 |
+
*/
|
30 |
+
public function is_globally_enabled();
|
31 |
+
}
|
admin/metabox/interface-metabox-section.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Generates and displays the HTML for a metabox section.
|
8 |
+
*/
|
9 |
+
interface WPSEO_Metabox_Section {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Outputs the section link.
|
13 |
+
*/
|
14 |
+
public function display_link();
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Outputs the section content.
|
18 |
+
*/
|
19 |
+
public function display_content();
|
20 |
+
}
|
admin/metabox/interface-metabox-tab.php
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Generates the HTML for a metabox tab.
|
8 |
+
*/
|
9 |
+
interface WPSEO_Metabox_Tab {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns the html for the tab link.
|
13 |
+
*
|
14 |
+
* @return string
|
15 |
+
*/
|
16 |
+
public function link();
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Returns the html for the tab content.
|
20 |
+
*
|
21 |
+
* @return string
|
22 |
+
*/
|
23 |
+
public function content();
|
24 |
+
}
|
admin/notifiers/class-configuration-notifier.php
ADDED
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Notifiers
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the logic for showing the notification.
|
8 |
+
*/
|
9 |
+
class WPSEO_Configuration_Notifier implements WPSEO_Listener {
|
10 |
+
const META_NAME = 'wpseo-dismiss-configuration-notice';
|
11 |
+
const META_VALUE = 'yes';
|
12 |
+
/** @var bool */
|
13 |
+
protected $show_notification;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Constructs the object by setting the show notification property based the given options.
|
17 |
+
*/
|
18 |
+
public function __construct() {
|
19 |
+
$this->show_notification = WPSEO_Options::get( 'show_onboarding_notice', false );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Returns the content of the notification.
|
24 |
+
*
|
25 |
+
* @return string A string with the notification HTML, or empty string when no notification is needed.
|
26 |
+
*/
|
27 |
+
public function notify() {
|
28 |
+
if ( ! $this->show_notification() ) {
|
29 |
+
return $this->re_run_notification();
|
30 |
+
}
|
31 |
+
|
32 |
+
return $this->first_time_notification();
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Listens to an argument in the request URL. When triggered just set the notification to dismissed.
|
37 |
+
*
|
38 |
+
* @return void
|
39 |
+
*/
|
40 |
+
public function listen() {
|
41 |
+
if ( ! $this->show_notification() || ! $this->dismissal_is_triggered() ) {
|
42 |
+
return;
|
43 |
+
}
|
44 |
+
|
45 |
+
$this->set_dismissed();
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Checks if the dismissal should be triggered.
|
50 |
+
*
|
51 |
+
* @return bool True when action has been triggered.
|
52 |
+
*/
|
53 |
+
protected function dismissal_is_triggered() {
|
54 |
+
return filter_input( INPUT_GET, 'dismiss_get_started' ) === '1';
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Checks if the current user has dismissed the notification.
|
59 |
+
*
|
60 |
+
* @return bool True when the notification has been dismissed.
|
61 |
+
*/
|
62 |
+
protected function is_dismissed() {
|
63 |
+
return get_user_meta( get_current_user_id(), self::META_NAME, true ) === self::META_VALUE;
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Sets the dismissed state for the current user.
|
68 |
+
*
|
69 |
+
* @return void
|
70 |
+
*/
|
71 |
+
protected function set_dismissed() {
|
72 |
+
update_user_meta( get_current_user_id(), self::META_NAME, self::META_VALUE );
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Checks if the notification should be shown.
|
77 |
+
*
|
78 |
+
* @return bool True when notification should be shown.
|
79 |
+
*/
|
80 |
+
protected function show_notification() {
|
81 |
+
return $this->show_notification && ! $this->is_dismissed();
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* Returns the notification to re-run the config wizard.
|
86 |
+
*
|
87 |
+
* @return string The notification.
|
88 |
+
*/
|
89 |
+
private function re_run_notification() {
|
90 |
+
$content = sprintf(
|
91 |
+
/* translators: %1$s expands to Yoast SEO, %2$s is a link start tag to the Onboarding Wizard, %3$s is the link closing tag. */
|
92 |
+
esc_html__( 'Want to make sure your %1$s settings are still OK? %2$sOpen the configuration wizard again%3$s to validate them.', 'wordpress-seo' ),
|
93 |
+
'Yoast SEO',
|
94 |
+
'<a href="' . esc_url( admin_url( 'admin.php?page=' . WPSEO_Configuration_Page::PAGE_IDENTIFIER ) ) . '">',
|
95 |
+
'</a>'
|
96 |
+
);
|
97 |
+
|
98 |
+
return $this->notification( __( 'Check SEO configuration', 'wordpress-seo' ), $content );
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Returns the notification to start the config wizard for the first time.
|
103 |
+
*
|
104 |
+
* @return string The notification.
|
105 |
+
*/
|
106 |
+
private function first_time_notification() {
|
107 |
+
$content = sprintf(
|
108 |
+
/* translators: %1$s expands to Yoast SEO, %2$s is a link start tag to the Onboarding Wizard, %3$s is the link closing tag. */
|
109 |
+
esc_html__( 'Get started quickly with the %1$s %2$sconfiguration wizard%3$s!', 'wordpress-seo' ),
|
110 |
+
'Yoast SEO',
|
111 |
+
'<a href="' . esc_url( admin_url( 'admin.php?page=' . WPSEO_Configuration_Page::PAGE_IDENTIFIER ) ) . '">',
|
112 |
+
'</a>'
|
113 |
+
);
|
114 |
+
|
115 |
+
return $this->notification( __( 'First-time SEO configuration', 'wordpress-seo' ), $content, true );
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Returns a styled notification.
|
120 |
+
*
|
121 |
+
* @param string $title Title for the notification.
|
122 |
+
* @param string $content Content for the notification.
|
123 |
+
* @param bool $show_dismissal Whether to show the dismiss button or not.
|
124 |
+
*
|
125 |
+
* @return string The styled notification.
|
126 |
+
*/
|
127 |
+
private function notification( $title, $content, $show_dismissal = false ) {
|
128 |
+
$notification = '<div class="yoast-container yoast-container__configuration-wizard">';
|
129 |
+
$notification .= sprintf(
|
130 |
+
'<img src="%1$s" height="%2$s" width="%3$d" />',
|
131 |
+
esc_url( plugin_dir_url( WPSEO_FILE ) . 'images/new-to-configuration-notice.svg' ),
|
132 |
+
60,
|
133 |
+
60
|
134 |
+
);
|
135 |
+
$notification .= '<div class="yoast-container__configuration-wizard--content">';
|
136 |
+
$notification .= '<h3>' . esc_html( $title ) . '</h3>';
|
137 |
+
|
138 |
+
$notification .= '<p>';
|
139 |
+
$notification .= $content;
|
140 |
+
$notification .= '</p>';
|
141 |
+
|
142 |
+
$notification .= '</div>';
|
143 |
+
if ( $show_dismissal ) {
|
144 |
+
$notification .= sprintf(
|
145 |
+
'<a href="%1$s" style="" class="button dismiss yoast-container__configuration-wizard--dismiss"><span class="screen-reader-text">%2$s</span><span class="dashicons dashicons-no-alt"></span></a>',
|
146 |
+
esc_url( admin_url( 'admin.php?page=wpseo_dashboard&dismiss_get_started=1' ) ),
|
147 |
+
esc_html__( 'Dismiss this item.', 'wordpress-seo' )
|
148 |
+
);
|
149 |
+
}
|
150 |
+
$notification .= '</div>';
|
151 |
+
|
152 |
+
return $notification;
|
153 |
+
}
|
154 |
+
}
|
admin/onpage/class-onpage-option.php
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class handles the data for the option where the Ryte data is stored.
|
8 |
+
*/
|
9 |
+
class WPSEO_OnPage_Option {
|
10 |
+
|
11 |
+
const NOT_FETCHED = 99;
|
12 |
+
const IS_INDEXABLE = 1;
|
13 |
+
const IS_NOT_INDEXABLE = 0;
|
14 |
+
const CANNOT_FETCH = -1;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* The name of the option where data will be stored
|
18 |
+
*/
|
19 |
+
const OPTION_NAME = 'wpseo_onpage';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* The key of the status in the option
|
23 |
+
*/
|
24 |
+
const STATUS = 'status';
|
25 |
+
|
26 |
+
/**
|
27 |
+
* The key of the last fetch date in the option.
|
28 |
+
*/
|
29 |
+
const LAST_FETCH = 'last_fetch';
|
30 |
+
|
31 |
+
/**
|
32 |
+
* The limit for fetching the status manually.
|
33 |
+
*/
|
34 |
+
const FETCH_LIMIT = 15;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @var array The Ryte option stored in the database.
|
38 |
+
*/
|
39 |
+
private $onpage_option;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Setting the object by setting the properties
|
43 |
+
*/
|
44 |
+
public function __construct() {
|
45 |
+
$this->onpage_option = $this->get_option();
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Getting the status from the option.
|
50 |
+
*
|
51 |
+
* @return string
|
52 |
+
*/
|
53 |
+
public function get_status() {
|
54 |
+
if ( array_key_exists( self::STATUS, $this->onpage_option ) ) {
|
55 |
+
return $this->onpage_option[ self::STATUS ];
|
56 |
+
}
|
57 |
+
|
58 |
+
return self::CANNOT_FETCH;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Saving the status to the options.
|
63 |
+
*
|
64 |
+
* @param string $status The status to save.
|
65 |
+
*/
|
66 |
+
public function set_status( $status ) {
|
67 |
+
$this->onpage_option[ self::STATUS ] = $status;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Saving the last fetch timestamp to the options.
|
72 |
+
*
|
73 |
+
* @param integer $timestamp Timestamp with the new value.
|
74 |
+
*/
|
75 |
+
public function set_last_fetch( $timestamp ) {
|
76 |
+
$this->onpage_option[ self::LAST_FETCH ] = $timestamp;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Check if the last fetch is within the time of 60 minutes
|
81 |
+
*
|
82 |
+
* @return bool
|
83 |
+
*/
|
84 |
+
public function should_be_fetched() {
|
85 |
+
return ( ( time() - $this->onpage_option[ self::LAST_FETCH ] ) > self::FETCH_LIMIT );
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Saving the option with the current data
|
90 |
+
*/
|
91 |
+
public function save_option() {
|
92 |
+
update_option( self::OPTION_NAME, $this->onpage_option );
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Returns the value of the onpage_enabled status
|
97 |
+
*
|
98 |
+
* @return bool
|
99 |
+
*/
|
100 |
+
public function is_enabled() {
|
101 |
+
return WPSEO_Options::get( 'onpage_indexability' );
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Getting the option with the Ryte data.
|
106 |
+
*
|
107 |
+
* @return array
|
108 |
+
*/
|
109 |
+
private function get_option() {
|
110 |
+
$default = array(
|
111 |
+
self::STATUS => self::NOT_FETCHED,
|
112 |
+
self::LAST_FETCH => 0,
|
113 |
+
);
|
114 |
+
|
115 |
+
return get_option( self::OPTION_NAME, $default );
|
116 |
+
}
|
117 |
+
}
|
admin/onpage/class-onpage-request.php
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class will fetch a new status from Ryte and if it's necessary it will
|
8 |
+
* notify the site admin by email and remove the current meta value to hide the
|
9 |
+
* notice for all admin users.
|
10 |
+
*/
|
11 |
+
class WPSEO_OnPage_Request {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var string The endpoint where the request will be send to.
|
15 |
+
*/
|
16 |
+
private $onpage_endpoint = 'https://indexability.yoast.onpage.org/';
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Doing the remote get and returns the body
|
20 |
+
*
|
21 |
+
* @param string $target_url The home url.
|
22 |
+
* @param array $parameters Array of extra parameters to send to Ryte.
|
23 |
+
*
|
24 |
+
* @return array
|
25 |
+
* @throws Exception The error message that can be used to show to the user.
|
26 |
+
*/
|
27 |
+
protected function get_remote( $target_url, $parameters = array() ) {
|
28 |
+
$parameters = array_merge( array(
|
29 |
+
'url' => $target_url,
|
30 |
+
'wp_version' => $GLOBALS['wp_version'],
|
31 |
+
'yseo_version' => WPSEO_VERSION,
|
32 |
+
), $parameters );
|
33 |
+
|
34 |
+
$url = add_query_arg( $parameters, $this->onpage_endpoint );
|
35 |
+
|
36 |
+
$response = wp_remote_get( $url );
|
37 |
+
$response_code = wp_remote_retrieve_response_code( $response );
|
38 |
+
|
39 |
+
// When the request is successful, the response code will be 200.
|
40 |
+
if ( $response_code === 200 ) {
|
41 |
+
$response_body = wp_remote_retrieve_body( $response );
|
42 |
+
|
43 |
+
return json_decode( $response_body, true );
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Sending a request to Ryte to check if the $home_url is indexable.
|
49 |
+
*
|
50 |
+
* @param string $target_url The URL that will be send to the API.
|
51 |
+
* @param array $parameters Array of extra parameters to send to Ryte.
|
52 |
+
*
|
53 |
+
* @return array
|
54 |
+
*/
|
55 |
+
public function do_request( $target_url, $parameters = array() ) {
|
56 |
+
$json_body = $this->get_remote( $target_url, $parameters );
|
57 |
+
|
58 |
+
// Ryte recognized a redirect, fetch the data of that URL by calling this method with the value from Ryte.
|
59 |
+
if ( ! empty( $json_body['passes_juice_to'] ) ) {
|
60 |
+
return $this->do_request( $json_body['passes_juice_to'], $parameters );
|
61 |
+
}
|
62 |
+
|
63 |
+
return $json_body;
|
64 |
+
}
|
65 |
+
}
|
admin/onpage/class-onpage.php
ADDED
@@ -0,0 +1,237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Handle the request for getting the Ryte status.
|
8 |
+
*/
|
9 |
+
class WPSEO_OnPage {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* The name of the user meta key for storing the dismissed status.
|
13 |
+
*/
|
14 |
+
const USER_META_KEY = 'wpseo_dismiss_onpage';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var WPSEO_OnPage_Option The Ryte option class.
|
18 |
+
*/
|
19 |
+
private $onpage_option;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var boolean Is the request started by pressing the fetch button.
|
23 |
+
*/
|
24 |
+
private $is_manual_request = false;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Constructing the object
|
28 |
+
*/
|
29 |
+
public function __construct() {
|
30 |
+
// We never want to fetch on AJAX request because doing a remote request is really slow.
|
31 |
+
if ( ! ( defined( 'DOING_AJAX' ) && DOING_AJAX === true ) ) {
|
32 |
+
$this->onpage_option = new WPSEO_OnPage_Option();
|
33 |
+
|
34 |
+
if ( $this->onpage_option->is_enabled() ) {
|
35 |
+
$this->set_hooks();
|
36 |
+
$this->catch_redo_listener();
|
37 |
+
}
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* The hooks to run on plugin activation
|
43 |
+
*/
|
44 |
+
public function activate_hooks() {
|
45 |
+
$this->set_cron();
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Adding a weekly schedule to the schedules array
|
50 |
+
*
|
51 |
+
* @param array $schedules Array with schedules.
|
52 |
+
*
|
53 |
+
* @return array
|
54 |
+
*/
|
55 |
+
public function add_weekly_schedule( array $schedules ) {
|
56 |
+
$schedules['weekly'] = array(
|
57 |
+
'interval' => WEEK_IN_SECONDS,
|
58 |
+
'display' => __( 'Once Weekly', 'wordpress-seo' ),
|
59 |
+
);
|
60 |
+
|
61 |
+
return $schedules;
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Fetching the data from Ryte.
|
66 |
+
*
|
67 |
+
* @return bool
|
68 |
+
*/
|
69 |
+
public function fetch_from_onpage() {
|
70 |
+
if ( $this->onpage_option->should_be_fetched() ) {
|
71 |
+
$new_status = $this->request_indexability();
|
72 |
+
if ( false !== $new_status ) {
|
73 |
+
|
74 |
+
// Updates the timestamp in the option.
|
75 |
+
$this->onpage_option->set_last_fetch( time() );
|
76 |
+
|
77 |
+
// The currently indexability status.
|
78 |
+
$old_status = $this->onpage_option->get_status();
|
79 |
+
|
80 |
+
// Saving the new status.
|
81 |
+
$this->onpage_option->set_status( $new_status );
|
82 |
+
|
83 |
+
// Saving the option.
|
84 |
+
$this->onpage_option->save_option();
|
85 |
+
|
86 |
+
// Check if the status has been changed.
|
87 |
+
if ( $old_status !== $new_status && $new_status !== WPSEO_OnPage_Option::CANNOT_FETCH ) {
|
88 |
+
$this->notify_admins();
|
89 |
+
}
|
90 |
+
|
91 |
+
return true;
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
return false;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Show a notice when the website is not indexable
|
100 |
+
*/
|
101 |
+
public function show_notice() {
|
102 |
+
|
103 |
+
$notification = $this->get_indexability_notification();
|
104 |
+
$notification_center = Yoast_Notification_Center::get();
|
105 |
+
|
106 |
+
if ( $this->should_show_notice() ) {
|
107 |
+
$notification_center->add_notification( $notification );
|
108 |
+
|
109 |
+
return;
|
110 |
+
}
|
111 |
+
|
112 |
+
$notification_center->remove_notification( $notification );
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Builds the indexability notification
|
117 |
+
*
|
118 |
+
* @return Yoast_Notification
|
119 |
+
*/
|
120 |
+
private function get_indexability_notification() {
|
121 |
+
$notice = sprintf(
|
122 |
+
/* translators: 1: opens a link to a related knowledge base article. 2: closes the link */
|
123 |
+
__( '%1$sYour homepage cannot be indexed by search engines%2$s. This is very bad for SEO and should be fixed.', 'wordpress-seo' ),
|
124 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/onpageindexerror' ) . '" target="_blank">',
|
125 |
+
'</a>'
|
126 |
+
);
|
127 |
+
|
128 |
+
return new Yoast_Notification(
|
129 |
+
$notice,
|
130 |
+
array(
|
131 |
+
'type' => Yoast_Notification::ERROR,
|
132 |
+
'id' => 'wpseo-dismiss-onpageorg',
|
133 |
+
'capabilities' => 'wpseo_manage_options',
|
134 |
+
)
|
135 |
+
);
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Send a request to Ryte to get the indexability.
|
140 |
+
*
|
141 |
+
* @return int(0)|int(1)|false
|
142 |
+
*/
|
143 |
+
protected function request_indexability() {
|
144 |
+
$parameters = array();
|
145 |
+
if ( $this->wordfence_protection_enabled() ) {
|
146 |
+
$parameters['wf_strict'] = 1;
|
147 |
+
}
|
148 |
+
|
149 |
+
$request = new WPSEO_OnPage_Request();
|
150 |
+
$response = $request->do_request( get_option( 'home' ), $parameters );
|
151 |
+
|
152 |
+
if ( isset( $response['is_indexable'] ) ) {
|
153 |
+
return (int) $response['is_indexable'];
|
154 |
+
}
|
155 |
+
|
156 |
+
return WPSEO_OnPage_Option::CANNOT_FETCH;
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Should the notice being given?
|
161 |
+
*
|
162 |
+
* @return bool
|
163 |
+
*/
|
164 |
+
protected function should_show_notice() {
|
165 |
+
// If development mode is on or the blog is not public, just don't show this notice.
|
166 |
+
if ( WPSEO_Utils::is_development_mode() || ( '0' === get_option( 'blog_public' ) ) ) {
|
167 |
+
return false;
|
168 |
+
}
|
169 |
+
|
170 |
+
return $this->onpage_option->get_status() === WPSEO_OnPage_Option::IS_NOT_INDEXABLE;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Notify the admins
|
175 |
+
*/
|
176 |
+
protected function notify_admins() {
|
177 |
+
/*
|
178 |
+
* Let's start showing the notices to all admins by removing the hide-notice meta data for each admin resulting
|
179 |
+
* in popping up the notice again.
|
180 |
+
*/
|
181 |
+
delete_metadata( 'user', 0, WPSEO_OnPage::USER_META_KEY, '', true );
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Setting up the hooks.
|
186 |
+
*/
|
187 |
+
private function set_hooks() {
|
188 |
+
// Schedule cronjob when it doesn't exists on activation.
|
189 |
+
register_activation_hook( WPSEO_FILE, array( $this, 'activate_hooks' ) );
|
190 |
+
|
191 |
+
// Add weekly schedule to the cron job schedules.
|
192 |
+
add_filter( 'cron_schedules', array( $this, 'add_weekly_schedule' ) );
|
193 |
+
|
194 |
+
// Adding admin notice if necessary.
|
195 |
+
add_filter( 'admin_init', array( $this, 'show_notice' ) );
|
196 |
+
|
197 |
+
// Setting the action for the Ryte fetch.
|
198 |
+
add_action( 'wpseo_onpage_fetch', array( $this, 'fetch_from_onpage' ) );
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Setting the cronjob to get the new indexibility status.
|
203 |
+
*/
|
204 |
+
private function set_cron() {
|
205 |
+
if ( ! wp_next_scheduled( 'wpseo_onpage_fetch' ) ) {
|
206 |
+
wp_schedule_event( time(), 'weekly', 'wpseo_onpage_fetch' );
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Redo the fetch request for Ryte.
|
212 |
+
*/
|
213 |
+
private function catch_redo_listener() {
|
214 |
+
if ( filter_input( INPUT_GET, 'wpseo-redo-onpage' ) === '1' ) {
|
215 |
+
$this->is_manual_request = true;
|
216 |
+
|
217 |
+
add_action( 'admin_init', array( $this, 'fetch_from_onpage' ) );
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
/**
|
222 |
+
* Checks if WordFence protects the site against 'fake' Google crawlers.
|
223 |
+
*
|
224 |
+
* @return boolean
|
225 |
+
*/
|
226 |
+
private function wordfence_protection_enabled() {
|
227 |
+
if ( ! class_exists( 'wfConfig' ) ) {
|
228 |
+
return false;
|
229 |
+
}
|
230 |
+
|
231 |
+
if ( ! method_exists( 'wfConfig', 'get' ) ) {
|
232 |
+
return false;
|
233 |
+
}
|
234 |
+
|
235 |
+
return (bool) wfConfig::get( 'blockFakeBots' );
|
236 |
+
}
|
237 |
+
}
|
admin/onpage/class-ryte-service.php
ADDED
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\OnPage
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the service to be used by the WPSEO_Endpoint_Ryte endpoint.
|
8 |
+
*/
|
9 |
+
class WPSEO_Ryte_Service {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WPSEO_OnPage_Option
|
13 |
+
*/
|
14 |
+
protected $option;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Constructs the WPSEO_Ryte_Service class.
|
18 |
+
*
|
19 |
+
* @param WPSEO_OnPage_Option $option The option to retrieve data from.
|
20 |
+
*/
|
21 |
+
public function __construct( WPSEO_OnPage_Option $option ) {
|
22 |
+
$this->option = $option;
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Fetches statistics via REST request.
|
27 |
+
*
|
28 |
+
* @return WP_REST_Response The response object.
|
29 |
+
*/
|
30 |
+
public function get_statistics() {
|
31 |
+
$result = false;
|
32 |
+
|
33 |
+
if ( $this->option->is_enabled() ) {
|
34 |
+
$result = $this->get_score( $this->option->get_status(), $this->option->should_be_fetched() );
|
35 |
+
}
|
36 |
+
|
37 |
+
return new WP_REST_Response( array( 'ryte' => $result ) );
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Returns an the results of the Ryte option based on the passed status.
|
42 |
+
*
|
43 |
+
* @param string $status The option's status.
|
44 |
+
* @param bool $fetch Whether or not the data should be fetched.
|
45 |
+
*
|
46 |
+
* @return array The results, contains a score and label.
|
47 |
+
*/
|
48 |
+
private function get_score( $status, $fetch = false ) {
|
49 |
+
if ( $status === WPSEO_OnPage_Option::IS_INDEXABLE ) {
|
50 |
+
return array(
|
51 |
+
'score' => 'good',
|
52 |
+
'label' => __( 'Your homepage can be indexed by search engines.', 'wordpress-seo' ),
|
53 |
+
'can_fetch' => $fetch,
|
54 |
+
);
|
55 |
+
}
|
56 |
+
|
57 |
+
if ( $status === WPSEO_OnPage_Option::IS_NOT_INDEXABLE ) {
|
58 |
+
return array(
|
59 |
+
'score' => 'bad',
|
60 |
+
'label' => sprintf(
|
61 |
+
/* translators: %1$s: opens a link to a related knowledge base article. %2$s: closes the link. */
|
62 |
+
__( '%1$sYour homepage cannot be indexed by search engines%2$s. This is very bad for SEO and should be fixed.', 'wordpress-seo' ),
|
63 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/onpageindexerror' ) . '" target="_blank">',
|
64 |
+
'</a>'
|
65 |
+
),
|
66 |
+
'can_fetch' => $fetch,
|
67 |
+
);
|
68 |
+
}
|
69 |
+
|
70 |
+
if ( $status === WPSEO_OnPage_Option::CANNOT_FETCH ) {
|
71 |
+
return array(
|
72 |
+
'score' => 'na',
|
73 |
+
'label' => sprintf(
|
74 |
+
/* translators: %1$s: opens a link to a related knowledge base article, %2$s: expands to Yoast SEO, %3$s: closes the link, %4$s: expands to Ryte. */
|
75 |
+
__( '%1$s%2$s has not been able to fetch your site\'s indexability status%3$s from %4$s', 'wordpress-seo' ),
|
76 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/onpagerequestfailed' ) . '" target="_blank">',
|
77 |
+
'Yoast SEO',
|
78 |
+
'</a>',
|
79 |
+
'Ryte'
|
80 |
+
),
|
81 |
+
'can_fetch' => $fetch,
|
82 |
+
);
|
83 |
+
}
|
84 |
+
|
85 |
+
if ( $status === WPSEO_OnPage_Option::NOT_FETCHED ) {
|
86 |
+
return array(
|
87 |
+
'score' => 'na',
|
88 |
+
'label' => esc_html( sprintf(
|
89 |
+
/* translators: %1$s: expands to Yoast SEO, %2$s: expands to Ryte. */
|
90 |
+
__( '%1$s has not fetched your site\'s indexability status yet from %2$s', 'wordpress-seo' ),
|
91 |
+
'Yoast SEO',
|
92 |
+
'Ryte'
|
93 |
+
) ),
|
94 |
+
'can_fetch' => $fetch,
|
95 |
+
);
|
96 |
+
}
|
97 |
+
|
98 |
+
return array();
|
99 |
+
}
|
100 |
+
}
|
admin/pages/dashboard.php
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
7 |
+
header( 'Status: 403 Forbidden' );
|
8 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
9 |
+
exit();
|
10 |
+
}
|
11 |
+
|
12 |
+
if ( filter_input( INPUT_GET, 'intro' ) ) {
|
13 |
+
update_user_meta( get_current_user_id(), 'wpseo_seen_about_version', WPSEO_VERSION );
|
14 |
+
require WPSEO_PATH . 'admin/views/about.php';
|
15 |
+
|
16 |
+
return;
|
17 |
+
}
|
18 |
+
|
19 |
+
if ( isset( $_GET['allow_tracking'] ) && check_admin_referer( 'wpseo_activate_tracking', 'nonce' ) ) {
|
20 |
+
WPSEO_Options::set( 'yoast_tracking', ( $_GET['allow_tracking'] === 'yes' ) );
|
21 |
+
|
22 |
+
if ( isset( $_SERVER['HTTP_REFERER'] ) ) {
|
23 |
+
wp_safe_redirect( $_SERVER['HTTP_REFERER'], 307 );
|
24 |
+
exit;
|
25 |
+
}
|
26 |
+
}
|
27 |
+
|
28 |
+
$yform = Yoast_Form::get_instance();
|
29 |
+
$yform->admin_header( true, 'wpseo' );
|
30 |
+
|
31 |
+
do_action( 'wpseo_all_admin_notices' );
|
32 |
+
|
33 |
+
$tabs = new WPSEO_Option_Tabs( 'dashboard' );
|
34 |
+
$tabs->add_tab(
|
35 |
+
new WPSEO_Option_Tab(
|
36 |
+
'dashboard',
|
37 |
+
__( 'Dashboard', 'wordpress-seo' ),
|
38 |
+
array(
|
39 |
+
'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-notification-center' ),
|
40 |
+
'save_button' => false,
|
41 |
+
)
|
42 |
+
)
|
43 |
+
);
|
44 |
+
$tabs->add_tab(
|
45 |
+
new WPSEO_Option_Tab(
|
46 |
+
'features',
|
47 |
+
__( 'Features', 'wordpress-seo' ),
|
48 |
+
array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-features' ) )
|
49 |
+
)
|
50 |
+
);
|
51 |
+
$tabs->add_tab(
|
52 |
+
new WPSEO_Option_Tab(
|
53 |
+
'webmaster-tools',
|
54 |
+
__( 'Webmaster tools', 'wordpress-seo' ),
|
55 |
+
array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-general-search-console' ) )
|
56 |
+
)
|
57 |
+
);
|
58 |
+
|
59 |
+
do_action( 'wpseo_settings_tabs_dashboard', $tabs );
|
60 |
+
|
61 |
+
$tabs->display( $yform );
|
62 |
+
|
63 |
+
do_action( 'wpseo_dashboard' );
|
64 |
+
|
65 |
+
$yform->admin_footer();
|
admin/pages/licenses.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
* @since 1.5.0
|
5 |
+
*/
|
6 |
+
|
7 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
8 |
+
header( 'Status: 403 Forbidden' );
|
9 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
10 |
+
exit();
|
11 |
+
}
|
12 |
+
|
13 |
+
$license_page_manager = new WPSEO_License_Page_Manager();
|
14 |
+
$licenses_page = $license_page_manager->get_license_page();
|
15 |
+
require WPSEO_PATH . 'admin/views/' . $licenses_page . '.php';
|
admin/pages/metas.php
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
7 |
+
header( 'Status: 403 Forbidden' );
|
8 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
9 |
+
exit();
|
10 |
+
}
|
11 |
+
|
12 |
+
add_filter( 'wpseo_help_center_items', 'yoast_add_meta_options_help_center_tabs' );
|
13 |
+
|
14 |
+
$yform = Yoast_Form::get_instance();
|
15 |
+
$yform->admin_header( true, 'wpseo_titles' );
|
16 |
+
|
17 |
+
$tabs = new WPSEO_Option_Tabs( 'metas' );
|
18 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'general', __( 'General', 'wordpress-seo' ), array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-metas' ) ) ) );
|
19 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'post-types', __( 'Content Types', 'wordpress-seo' ), array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-metas-post-types' ) ) ) );
|
20 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'media', __( 'Media', 'wordpress-seo' ) ) );
|
21 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'taxonomies', __( 'Taxonomies', 'wordpress-seo' ), array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-metas-taxonomies' ) ) ) );
|
22 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'archives', __( 'Archives', 'wordpress-seo' ), array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-metas-archives' ) ) ) );
|
23 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'breadcrumbs', __( 'Breadcrumbs', 'wordpress-seo' ), array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-breadcrumbs' ) ) ) );
|
24 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'rss', __( 'RSS', 'wordpress-seo' ), array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-rss' ) ) ) );
|
25 |
+
$tabs->display( $yform );
|
26 |
+
|
27 |
+
$yform->admin_footer();
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Adds help tabs.
|
31 |
+
*
|
32 |
+
* @param array $tabs Current help center tabs.
|
33 |
+
*
|
34 |
+
* @return array List containing all the additional tabs.
|
35 |
+
*/
|
36 |
+
function yoast_add_meta_options_help_center_tabs( $tabs ) {
|
37 |
+
|
38 |
+
$tabs[] = new WPSEO_Help_Center_Item(
|
39 |
+
'template-variables',
|
40 |
+
__( 'Template explanation', 'wordpress-seo' ),
|
41 |
+
array( 'content' => wpseo_add_template_variables_helpcenter() )
|
42 |
+
);
|
43 |
+
|
44 |
+
return $tabs;
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Adds template variables to the help center.
|
49 |
+
*
|
50 |
+
* @return string The content for the template variables tab.
|
51 |
+
*/
|
52 |
+
function wpseo_add_template_variables_helpcenter() {
|
53 |
+
$explanation = sprintf(
|
54 |
+
/* translators: %1$s expands to Yoast SEO. */
|
55 |
+
__( 'The search appearance settings for %1$s are made up of variables that are replaced by specific values from the page when the page is displayed. The table below contains a list of the available variables.', 'wordpress-seo' ),
|
56 |
+
'Yoast SEO'
|
57 |
+
);
|
58 |
+
|
59 |
+
$output_explanation = sprintf(
|
60 |
+
'<h2>%s</h2><p>%s</p><p>%s</p>',
|
61 |
+
esc_html( __( 'Template explanation', 'wordpress-seo' ) ),
|
62 |
+
esc_html( $explanation ),
|
63 |
+
esc_html( __( 'Note that not all variables can be used in every template.', 'wordpress-seo' ) )
|
64 |
+
);
|
65 |
+
|
66 |
+
$output_basic = sprintf(
|
67 |
+
'<h2>%s</h2>%s',
|
68 |
+
esc_html( __( 'Basic Variables', 'wordpress-seo' ) ),
|
69 |
+
WPSEO_Replace_Vars::get_basic_help_texts()
|
70 |
+
);
|
71 |
+
|
72 |
+
$output_advanced = sprintf(
|
73 |
+
'<h2>%s</h2>%s',
|
74 |
+
esc_html( __( 'Advanced Variables', 'wordpress-seo' ) ),
|
75 |
+
WPSEO_Replace_Vars::get_advanced_help_texts()
|
76 |
+
);
|
77 |
+
|
78 |
+
return $output_explanation . $output_basic . $output_advanced;
|
79 |
+
}
|
admin/pages/network.php
ADDED
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
7 |
+
header( 'Status: 403 Forbidden' );
|
8 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
9 |
+
exit();
|
10 |
+
}
|
11 |
+
|
12 |
+
$yform = Yoast_Form::get_instance();
|
13 |
+
|
14 |
+
$options = get_site_option( 'wpseo_ms' );
|
15 |
+
|
16 |
+
if ( isset( $_POST['wpseo_submit'] ) ) {
|
17 |
+
check_admin_referer( 'wpseo-network-settings' );
|
18 |
+
|
19 |
+
foreach ( array( 'access', 'defaultblog' ) as $opt ) {
|
20 |
+
$options[ $opt ] = $_POST['wpseo_ms'][ $opt ];
|
21 |
+
}
|
22 |
+
unset( $opt );
|
23 |
+
WPSEO_Options::update_site_option( 'wpseo_ms', $options );
|
24 |
+
add_settings_error( 'wpseo_ms', 'settings_updated', __( 'Settings Updated.', 'wordpress-seo' ), 'updated' );
|
25 |
+
}
|
26 |
+
|
27 |
+
if ( isset( $_POST['wpseo_restore_blog'] ) ) {
|
28 |
+
check_admin_referer( 'wpseo-network-restore' );
|
29 |
+
if ( isset( $_POST['wpseo_ms']['restoreblog'] ) && is_numeric( $_POST['wpseo_ms']['restoreblog'] ) ) {
|
30 |
+
$restoreblog = (int) WPSEO_Utils::validate_int( $_POST['wpseo_ms']['restoreblog'] );
|
31 |
+
$blog = get_blog_details( $restoreblog );
|
32 |
+
|
33 |
+
if ( $blog ) {
|
34 |
+
WPSEO_Options::reset_ms_blog( $restoreblog );
|
35 |
+
/* translators: %s expands to the name of a blog within a multi-site network. */
|
36 |
+
add_settings_error( 'wpseo_ms', 'settings_updated', sprintf( __( '%s restored to default SEO settings.', 'wordpress-seo' ), esc_html( $blog->blogname ) ), 'updated' );
|
37 |
+
}
|
38 |
+
else {
|
39 |
+
/* translators: %s expands to the ID of a blog within a multi-site network. */
|
40 |
+
add_settings_error( 'wpseo_ms', 'settings_updated', sprintf( __( 'Blog %s not found.', 'wordpress-seo' ), esc_html( $restoreblog ) ), 'error' );
|
41 |
+
}
|
42 |
+
unset( $restoreblog, $blog );
|
43 |
+
}
|
44 |
+
}
|
45 |
+
|
46 |
+
/* Set up selectbox dropdowns for smaller networks (usability) */
|
47 |
+
$use_dropdown = true;
|
48 |
+
if ( get_blog_count() > 100 ) {
|
49 |
+
$use_dropdown = false;
|
50 |
+
}
|
51 |
+
else {
|
52 |
+
|
53 |
+
$sites = array_map( 'get_object_vars', get_sites( array( 'deleted' => 0 ) ) );
|
54 |
+
|
55 |
+
if ( is_array( $sites ) && $sites !== array() ) {
|
56 |
+
$dropdown_input = array(
|
57 |
+
'-' => __( 'None', 'wordpress-seo' ),
|
58 |
+
);
|
59 |
+
|
60 |
+
foreach ( $sites as $site ) {
|
61 |
+
$dropdown_input[ $site['blog_id'] ] = $site['blog_id'] . ': ' . $site['domain'];
|
62 |
+
|
63 |
+
$blog_states = array();
|
64 |
+
if ( $site['public'] === '1' ) {
|
65 |
+
$blog_states[] = __( 'public', 'wordpress-seo' );
|
66 |
+
}
|
67 |
+
if ( $site['archived'] === '1' ) {
|
68 |
+
$blog_states[] = __( 'archived', 'wordpress-seo' );
|
69 |
+
}
|
70 |
+
if ( $site['mature'] === '1' ) {
|
71 |
+
$blog_states[] = __( 'mature', 'wordpress-seo' );
|
72 |
+
}
|
73 |
+
if ( $site['spam'] === '1' ) {
|
74 |
+
$blog_states[] = __( 'spam', 'wordpress-seo' );
|
75 |
+
}
|
76 |
+
if ( $blog_states !== array() ) {
|
77 |
+
$dropdown_input[ $site['blog_id'] ] .= ' [' . implode( ', ', $blog_states ) . ']';
|
78 |
+
}
|
79 |
+
}
|
80 |
+
unset( $site, $blog_states );
|
81 |
+
}
|
82 |
+
else {
|
83 |
+
$use_dropdown = false;
|
84 |
+
}
|
85 |
+
unset( $sites );
|
86 |
+
}
|
87 |
+
|
88 |
+
$yform->admin_header( false, 'wpseo_ms' );
|
89 |
+
|
90 |
+
echo '<h2>', esc_html__( 'MultiSite Settings', 'wordpress-seo' ), '</h2>';
|
91 |
+
echo '<form method="post" accept-charset="', esc_attr( get_bloginfo( 'charset' ) ), '">';
|
92 |
+
wp_nonce_field( 'wpseo-network-settings', '_wpnonce', true, true );
|
93 |
+
|
94 |
+
/* {@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.}} */
|
95 |
+
$yform->select(
|
96 |
+
'access',
|
97 |
+
/* translators: %1$s expands to Yoast SEO */
|
98 |
+
sprintf( __( 'Who should have access to the %1$s settings', 'wordpress-seo' ), 'Yoast SEO' ),
|
99 |
+
array(
|
100 |
+
'admin' => __( 'Site Admins (default)', 'wordpress-seo' ),
|
101 |
+
'superadmin' => __( 'Super Admins only', 'wordpress-seo' ),
|
102 |
+
),
|
103 |
+
'wpseo_ms'
|
104 |
+
);
|
105 |
+
|
106 |
+
if ( $use_dropdown === true ) {
|
107 |
+
$yform->select(
|
108 |
+
'defaultblog',
|
109 |
+
__( 'New sites in the network inherit their SEO settings from this site', 'wordpress-seo' ),
|
110 |
+
$dropdown_input,
|
111 |
+
'wpseo_ms'
|
112 |
+
);
|
113 |
+
echo '<p>' . 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' ) . '</p>';
|
114 |
+
}
|
115 |
+
else {
|
116 |
+
$yform->textinput( 'defaultblog', __( 'New sites in the network inherit their SEO settings from this site', 'wordpress-seo' ), 'wpseo_ms' );
|
117 |
+
echo '<p>';
|
118 |
+
printf(
|
119 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
120 |
+
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' ),
|
121 |
+
'<a href="' . esc_url( network_admin_url( 'sites.php' ) ) . '">',
|
122 |
+
'</a>'
|
123 |
+
);
|
124 |
+
echo '</p>';
|
125 |
+
}
|
126 |
+
echo '<p><strong>' . esc_html__( 'Take note:', 'wordpress-seo' ) . '</strong> ' . 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 blogs.', 'wordpress-seo' ) . '</p>';
|
127 |
+
|
128 |
+
|
129 |
+
echo '<input type="submit" name="wpseo_submit" class="button button-primary" value="' . esc_attr__( 'Save MultiSite Settings', 'wordpress-seo' ) . '"/>';
|
130 |
+
echo '</form>';
|
131 |
+
|
132 |
+
echo '<h2>' . esc_html__( 'Restore site to default settings', 'wordpress-seo' ) . '</h2>';
|
133 |
+
echo '<form method="post" accept-charset="' . esc_attr( get_bloginfo( 'charset' ) ) . '">';
|
134 |
+
wp_nonce_field( 'wpseo-network-restore', '_wpnonce', true, true );
|
135 |
+
echo '<p>' . esc_html__( 'Using this form you can reset a site to the default SEO settings.', 'wordpress-seo' ) . '</p>';
|
136 |
+
|
137 |
+
if ( $use_dropdown === true ) {
|
138 |
+
unset( $dropdown_input['-'] );
|
139 |
+
$yform->select(
|
140 |
+
'restoreblog',
|
141 |
+
__( 'Site ID', 'wordpress-seo' ),
|
142 |
+
$dropdown_input,
|
143 |
+
'wpseo_ms'
|
144 |
+
);
|
145 |
+
}
|
146 |
+
else {
|
147 |
+
$yform->textinput( 'restoreblog', __( 'Blog ID', 'wordpress-seo' ), 'wpseo_ms' );
|
148 |
+
}
|
149 |
+
|
150 |
+
echo '<input type="submit" name="wpseo_restore_blog" value="' . esc_attr__( 'Restore site to defaults', 'wordpress-seo' ) . '" class="button"/>';
|
151 |
+
echo '</form>';
|
152 |
+
|
153 |
+
$yform->admin_footer( false );
|
admin/pages/social.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
7 |
+
header( 'Status: 403 Forbidden' );
|
8 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
9 |
+
exit();
|
10 |
+
}
|
11 |
+
|
12 |
+
$yform = Yoast_Form::get_instance();
|
13 |
+
$yform->admin_header( true, 'wpseo_social' );
|
14 |
+
|
15 |
+
$tabs = new WPSEO_Option_Tabs( 'social' );
|
16 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'accounts', __( 'Accounts', 'wordpress-seo' ), array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-social-accounts' ) ) ) );
|
17 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'facebook', __( 'Facebook', 'wordpress-seo' ), array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-social-facebook' ) ) ) );
|
18 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'twitterbox', __( 'Twitter', 'wordpress-seo' ), array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-social-twitter' ) ) ) );
|
19 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'pinterest', __( 'Pinterest', 'wordpress-seo' ), array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-social-pinterest' ) ) ) );
|
20 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'google', __( 'Google+', 'wordpress-seo' ), array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-social-google' ) ) ) );
|
21 |
+
$tabs->display( $yform );
|
22 |
+
|
23 |
+
$yform->admin_footer();
|
admin/pages/tools.php
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
7 |
+
header( 'Status: 403 Forbidden' );
|
8 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
9 |
+
exit();
|
10 |
+
}
|
11 |
+
|
12 |
+
$tool_page = (string) filter_input( INPUT_GET, 'tool' );
|
13 |
+
|
14 |
+
$yform = Yoast_Form::get_instance();
|
15 |
+
$yform->admin_header( false );
|
16 |
+
|
17 |
+
if ( '' === $tool_page ) {
|
18 |
+
|
19 |
+
$tools = array();
|
20 |
+
|
21 |
+
$tools['import-export'] = array(
|
22 |
+
'title' => __( 'Import and Export', 'wordpress-seo' ),
|
23 |
+
'desc' => __( 'Import settings from other SEO plugins and export your settings for re-use on (another) blog.', 'wordpress-seo' ),
|
24 |
+
);
|
25 |
+
|
26 |
+
if ( WPSEO_Utils::allow_system_file_edit() === true && ! is_multisite() ) {
|
27 |
+
$tools['file-editor'] = array(
|
28 |
+
'title' => __( 'File editor', 'wordpress-seo' ),
|
29 |
+
'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' ),
|
30 |
+
);
|
31 |
+
}
|
32 |
+
|
33 |
+
$tools['bulk-editor'] = array(
|
34 |
+
'title' => __( 'Bulk editor', 'wordpress-seo' ),
|
35 |
+
'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' ),
|
36 |
+
);
|
37 |
+
|
38 |
+
echo '<p>';
|
39 |
+
printf(
|
40 |
+
/* translators: %1$s expands to Yoast SEO */
|
41 |
+
esc_html__( '%1$s comes with some very powerful built-in tools:', 'wordpress-seo' ),
|
42 |
+
'Yoast SEO'
|
43 |
+
);
|
44 |
+
echo '</p>';
|
45 |
+
|
46 |
+
echo '<ul class="ul-disc">';
|
47 |
+
|
48 |
+
$admin_url = admin_url( 'admin.php?page=wpseo_tools' );
|
49 |
+
|
50 |
+
foreach ( $tools as $slug => $tool ) {
|
51 |
+
$href = ( ! empty( $tool['href'] ) ) ? $admin_url . $tool['href'] : add_query_arg( array( 'tool' => $slug ), $admin_url );
|
52 |
+
$attr = ( ! empty( $tool['attr'] ) ) ? $tool['attr'] : '';
|
53 |
+
|
54 |
+
echo '<li>';
|
55 |
+
echo '<strong><a href="', esc_url( $href ), '" ', $attr , '>', esc_html( $tool['title'] ), '</a></strong><br/>';
|
56 |
+
echo $tool['desc'];
|
57 |
+
echo '</li>';
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Action: 'wpseo_tools_overview_list_items' - Hook to add additional tools to the overview.
|
62 |
+
*/
|
63 |
+
do_action( 'wpseo_tools_overview_list_items' );
|
64 |
+
|
65 |
+
echo '</ul>';
|
66 |
+
|
67 |
+
echo '<input type="hidden" id="wpseo_recalculate_nonce" name="wpseo_recalculate_nonce" value="' . esc_attr( wp_create_nonce( 'wpseo_recalculate' ) ) . '" />';
|
68 |
+
|
69 |
+
}
|
70 |
+
else {
|
71 |
+
echo '<a href="', esc_url( admin_url( 'admin.php?page=wpseo_tools' ) ), '">', esc_html__( '« Back to Tools page', 'wordpress-seo' ), '</a>';
|
72 |
+
|
73 |
+
$tool_pages = array( 'bulk-editor', 'import-export' );
|
74 |
+
|
75 |
+
if ( WPSEO_Utils::allow_system_file_edit() === true && ! is_multisite() ) {
|
76 |
+
$tool_pages[] = 'file-editor';
|
77 |
+
}
|
78 |
+
|
79 |
+
if ( in_array( $tool_page, $tool_pages, true ) ) {
|
80 |
+
require_once WPSEO_PATH . 'admin/views/tool-' . $tool_page . '.php';
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
$yform->admin_footer( false );
|
admin/recalculate/class-recalculate-posts.php
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class handles the calculation of the SEO score for all posts with a filled focus keyword
|
8 |
+
*/
|
9 |
+
class WPSEO_Recalculate_Posts extends WPSEO_Recalculate {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Save the scores.
|
13 |
+
*
|
14 |
+
* @param array $scores The scores for the posts.
|
15 |
+
*/
|
16 |
+
public function save_scores( array $scores ) {
|
17 |
+
foreach ( $scores as $score ) {
|
18 |
+
$this->save_score( $score );
|
19 |
+
}
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Save the score.
|
24 |
+
*
|
25 |
+
* @param array $score The score to save.
|
26 |
+
*/
|
27 |
+
protected function save_score( array $score ) {
|
28 |
+
WPSEO_Meta::set_value( 'linkdex', $score['score'], $score['item_id'] );
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Get the posts from the database by doing a WP_Query.
|
33 |
+
*
|
34 |
+
* @param integer $paged The page.
|
35 |
+
*
|
36 |
+
* @return string
|
37 |
+
*/
|
38 |
+
protected function get_items( $paged ) {
|
39 |
+
$items_per_page = max( 1, $this->items_per_page );
|
40 |
+
$post_query = new WP_Query(
|
41 |
+
array(
|
42 |
+
'post_type' => 'any',
|
43 |
+
'meta_key' => '_yoast_wpseo_focuskw',
|
44 |
+
'posts_per_page' => $items_per_page,
|
45 |
+
'paged' => $paged,
|
46 |
+
)
|
47 |
+
);
|
48 |
+
|
49 |
+
return $post_query->get_posts();
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Map the posts to a response array
|
54 |
+
*
|
55 |
+
* @param WP_Post $item The post for which to build the analyzer data.
|
56 |
+
*
|
57 |
+
* @return array
|
58 |
+
*/
|
59 |
+
protected function item_to_response( $item ) {
|
60 |
+
$focus_keyword = WPSEO_Meta::get_value( 'focuskw', $item->ID );
|
61 |
+
|
62 |
+
$content = $item->post_content;
|
63 |
+
|
64 |
+
// Check if there's a featured image.
|
65 |
+
$content .= $this->add_featured_image( $item );
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Filter the post content for use in the SEO score recalculation.
|
69 |
+
*
|
70 |
+
* @param string $content Content of the post. Modify to reflect front-end content.
|
71 |
+
* @param WP_Post $item The Post object the content comes from.
|
72 |
+
*/
|
73 |
+
$content = apply_filters( 'wpseo_post_content_for_recalculation', $content, $item );
|
74 |
+
|
75 |
+
// Apply shortcodes.
|
76 |
+
$content = do_shortcode( $content );
|
77 |
+
|
78 |
+
return array(
|
79 |
+
'post_id' => $item->ID,
|
80 |
+
'text' => $content,
|
81 |
+
'keyword' => $focus_keyword,
|
82 |
+
'url' => urldecode( $item->post_name ),
|
83 |
+
'pageTitle' => apply_filters( 'wpseo_title', wpseo_replace_vars( $this->get_title( $item->ID, $item->post_type ), $item ) ),
|
84 |
+
'meta' => apply_filters( 'wpseo_metadesc', wpseo_replace_vars( $this->get_meta_description( $item->ID, $item->post_type ), $item ) ),
|
85 |
+
'keyword_usage' => array(
|
86 |
+
$focus_keyword => WPSEO_Meta::keyword_usage( $focus_keyword, $item->ID ),
|
87 |
+
),
|
88 |
+
);
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Get the title for given post
|
93 |
+
*
|
94 |
+
* @param integer $post_id The ID of the post for which to get the title.
|
95 |
+
* @param string $post_type The post type.
|
96 |
+
*
|
97 |
+
* @return mixed|string
|
98 |
+
*/
|
99 |
+
private function get_title( $post_id, $post_type ) {
|
100 |
+
$title = WPSEO_Meta::get_value( 'title', $post_id );
|
101 |
+
if ( '' !== $title ) {
|
102 |
+
return $title;
|
103 |
+
}
|
104 |
+
|
105 |
+
$default_from_options = $this->default_from_options( 'title-tax', $post_type );
|
106 |
+
if ( false !== $default_from_options ) {
|
107 |
+
return str_replace( ' %%page%% ', ' ', $default_from_options );
|
108 |
+
}
|
109 |
+
|
110 |
+
return '%%title%%';
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Get the meta description for given post
|
115 |
+
*
|
116 |
+
* @param integer $post_id The ID of the post for which to get the meta description.
|
117 |
+
* @param string $post_type The post type.
|
118 |
+
*
|
119 |
+
* @return bool|string
|
120 |
+
*/
|
121 |
+
private function get_meta_description( $post_id, $post_type ) {
|
122 |
+
$meta_description = WPSEO_Meta::get_value( 'metadesc', $post_id );
|
123 |
+
if ( '' !== $meta_description ) {
|
124 |
+
return $meta_description;
|
125 |
+
}
|
126 |
+
|
127 |
+
$default_from_options = $this->default_from_options( 'metadesc', $post_type );
|
128 |
+
if ( false !== $default_from_options ) {
|
129 |
+
return $default_from_options;
|
130 |
+
}
|
131 |
+
|
132 |
+
return '';
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Retrieves the associated featured image if there is one present.
|
137 |
+
*
|
138 |
+
* @param WP_Post $item The post item to check for a featured image.
|
139 |
+
*
|
140 |
+
* @return string The image string.
|
141 |
+
*/
|
142 |
+
private function add_featured_image( $item ) {
|
143 |
+
if ( ! has_post_thumbnail( $item->ID ) ) {
|
144 |
+
return '';
|
145 |
+
}
|
146 |
+
|
147 |
+
return ' ' . get_the_post_thumbnail( $item->ID );
|
148 |
+
}
|
149 |
+
}
|
admin/recalculate/class-recalculate-terms.php
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class handles the calculation of the SEO score for all terms
|
8 |
+
*/
|
9 |
+
class WPSEO_Recalculate_Terms extends WPSEO_Recalculate {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Save the scores.
|
13 |
+
*
|
14 |
+
* @param array $scores The scores to save.
|
15 |
+
*/
|
16 |
+
public function save_scores( array $scores ) {
|
17 |
+
|
18 |
+
$tax_meta = get_option( 'wpseo_taxonomy_meta' );
|
19 |
+
|
20 |
+
foreach ( $scores as $score ) {
|
21 |
+
$tax_meta[ $score['taxonomy'] ][ $score['item_id'] ]['wpseo_linkdex'] = $score['score'];
|
22 |
+
}
|
23 |
+
|
24 |
+
update_option( 'wpseo_taxonomy_meta', $tax_meta );
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Save the score.
|
29 |
+
*
|
30 |
+
* @param array $score The score to save.
|
31 |
+
*/
|
32 |
+
protected function save_score( array $score ) {
|
33 |
+
WPSEO_Meta::set_value( 'linkdex', $score['score'], $score['item_id'] );
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Get the terms from the database by doing a WP_Query.
|
38 |
+
*
|
39 |
+
* @param integer $paged The page.
|
40 |
+
*
|
41 |
+
* @return array
|
42 |
+
*/
|
43 |
+
protected function get_items( $paged ) {
|
44 |
+
$items_per_page = max( 1, $this->items_per_page );
|
45 |
+
|
46 |
+
return get_terms(
|
47 |
+
get_taxonomies(),
|
48 |
+
array(
|
49 |
+
'hide_empty' => false,
|
50 |
+
'number' => $items_per_page,
|
51 |
+
'offset' => $items_per_page * abs( $paged - 1 ),
|
52 |
+
)
|
53 |
+
);
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Convert the given term into a analyzable object.
|
58 |
+
*
|
59 |
+
* @param mixed $item The term for which to build the analyzer data.
|
60 |
+
*
|
61 |
+
* @return array
|
62 |
+
*/
|
63 |
+
protected function item_to_response( $item ) {
|
64 |
+
$focus_keyword = $this->get_focus_keyword( $item );
|
65 |
+
$title = str_replace( ' %%page%% ', ' ', $this->get_title( $item ) );
|
66 |
+
$meta = $this->get_meta_description( $item );
|
67 |
+
|
68 |
+
$description = $item->description;
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Filter the term description for recalculation.
|
72 |
+
*
|
73 |
+
* @param string $description Content of the term. Modify to reflect front-end content.
|
74 |
+
* @oaram WP_Term $item The term the description comes from.
|
75 |
+
*/
|
76 |
+
$description = apply_filters( 'wpseo_term_description_for_recalculation', $description, $item );
|
77 |
+
|
78 |
+
return array(
|
79 |
+
'term_id' => $item->term_id,
|
80 |
+
'taxonomy' => $item->taxonomy,
|
81 |
+
'text' => $description,
|
82 |
+
'keyword' => $focus_keyword,
|
83 |
+
'url' => urldecode( $item->slug ),
|
84 |
+
'pageTitle' => apply_filters( 'wpseo_title', wpseo_replace_vars( $title, $item, array( 'page' ) ) ),
|
85 |
+
'meta' => apply_filters( 'wpseo_metadesc', wpseo_replace_vars( $meta, $item ) ),
|
86 |
+
'keyword_usage' => array(
|
87 |
+
$focus_keyword => WPSEO_Taxonomy_Meta::get_keyword_usage( $focus_keyword, $item->term_id, $item->taxonomy ),
|
88 |
+
),
|
89 |
+
);
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Gets the focus keyword for the term
|
94 |
+
*
|
95 |
+
* @param stdClass|WP_Term $term Term to determine the keyword for.
|
96 |
+
*
|
97 |
+
* @return bool|string
|
98 |
+
*/
|
99 |
+
private function get_focus_keyword( $term ) {
|
100 |
+
$focus_keyword = WPSEO_Taxonomy_Meta::get_term_meta( 'focuskw', $term->term_id, $term->taxonomy );
|
101 |
+
if ( ! empty( $focus_keyword ) ) {
|
102 |
+
return $focus_keyword;
|
103 |
+
}
|
104 |
+
|
105 |
+
return $term->name;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Get the title for given term
|
110 |
+
*
|
111 |
+
* @param stdClass|WP_Term $term The term object.
|
112 |
+
*
|
113 |
+
* @return mixed|string
|
114 |
+
*/
|
115 |
+
private function get_title( $term ) {
|
116 |
+
$title = WPSEO_Taxonomy_Meta::get_term_meta( $term->term_id, $term->taxonomy, 'title' );
|
117 |
+
if ( '' !== $title ) {
|
118 |
+
return $title;
|
119 |
+
}
|
120 |
+
|
121 |
+
$default_from_options = $this->default_from_options( 'title-tax', $term->taxonomy );
|
122 |
+
if ( false !== $default_from_options ) {
|
123 |
+
return $default_from_options;
|
124 |
+
}
|
125 |
+
|
126 |
+
return '%%title%%';
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Get the meta description for given post
|
131 |
+
*
|
132 |
+
* @param stdClass|WP_Term $term The term object.
|
133 |
+
*
|
134 |
+
* @return bool|string
|
135 |
+
*/
|
136 |
+
private function get_meta_description( $term ) {
|
137 |
+
$meta_description = WPSEO_Taxonomy_Meta::get_term_meta( $term->term_id, $term->taxonomy, 'desc' );
|
138 |
+
if ( '' !== $meta_description ) {
|
139 |
+
return $meta_description;
|
140 |
+
}
|
141 |
+
|
142 |
+
$default_from_options = $this->default_from_options( 'metadesc-tax', $term->taxonomy );
|
143 |
+
if ( false !== $default_from_options ) {
|
144 |
+
return $default_from_options;
|
145 |
+
}
|
146 |
+
|
147 |
+
return '';
|
148 |
+
}
|
149 |
+
}
|
admin/recalculate/class-recalculate.php
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Abstract class to force methods in recalculate classes.
|
8 |
+
*/
|
9 |
+
abstract class WPSEO_Recalculate {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var int
|
13 |
+
*/
|
14 |
+
protected $items_per_page = 20;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Saves the array with scores to the database.
|
18 |
+
*
|
19 |
+
* @param array $scores Array with the score for each item.
|
20 |
+
*/
|
21 |
+
abstract public function save_scores( array $scores );
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Gets the items and parses it to an response
|
25 |
+
*
|
26 |
+
* @param integer $paged The current page number.
|
27 |
+
*
|
28 |
+
* @return string
|
29 |
+
*/
|
30 |
+
abstract protected function get_items( $paged );
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Maps the items to an array for the response
|
34 |
+
*
|
35 |
+
* @param mixed $item Object with data to parse.
|
36 |
+
*
|
37 |
+
* @return array
|
38 |
+
*/
|
39 |
+
abstract protected function item_to_response( $item );
|
40 |
+
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Gets the items to recalculate
|
44 |
+
*
|
45 |
+
* @param int $paged The current page number.
|
46 |
+
*
|
47 |
+
* @return array Items that can be recalculated.
|
48 |
+
*/
|
49 |
+
public function get_items_to_recalculate( $paged ) {
|
50 |
+
$return = array();
|
51 |
+
|
52 |
+
$paged = abs( $paged );
|
53 |
+
|
54 |
+
$items = $this->get_items( $paged );
|
55 |
+
|
56 |
+
$return['items'] = $this->parse_items( $items );
|
57 |
+
$return['total_items'] = count( $items );
|
58 |
+
|
59 |
+
if ( $return['total_items'] >= $this->items_per_page ) {
|
60 |
+
$return['next_page'] = ( $paged + 1 );
|
61 |
+
}
|
62 |
+
|
63 |
+
return $return;
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Parse the posts|terms with the value we need
|
68 |
+
*
|
69 |
+
* @param array $items The items to parse.
|
70 |
+
*
|
71 |
+
* @return array
|
72 |
+
*/
|
73 |
+
protected function parse_items( array $items ) {
|
74 |
+
$return = array();
|
75 |
+
foreach ( $items as $item ) {
|
76 |
+
$response = $this->item_to_response( $item );
|
77 |
+
if ( ! empty( $response ) ) {
|
78 |
+
$return[] = $response;
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
return $return;
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Get default from the options for given field
|
87 |
+
*
|
88 |
+
* @param string $field The field for which to get the default options.
|
89 |
+
* @param string $suffix The post type.
|
90 |
+
*
|
91 |
+
* @return bool|string
|
92 |
+
*/
|
93 |
+
protected function default_from_options( $field, $suffix ) {
|
94 |
+
$target_option_field = $field . '-' . $suffix;
|
95 |
+
if ( '' !== WPSEO_Options::get( $target_option_field, '' ) ) {
|
96 |
+
return WPSEO_Options::get( $target_option_field );
|
97 |
+
}
|
98 |
+
|
99 |
+
return false;
|
100 |
+
}
|
101 |
+
}
|
admin/roles/class-abstract-role-manager.php
ADDED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Roles
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Abstract Role Manager template.
|
8 |
+
*/
|
9 |
+
abstract class WPSEO_Abstract_Role_Manager implements WPSEO_Role_Manager {
|
10 |
+
/** @var array Registered roles. */
|
11 |
+
protected $roles = array();
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Registers a role.
|
15 |
+
*
|
16 |
+
* @param string $role Role to register.
|
17 |
+
* @param string $display_name Display name to use.
|
18 |
+
* @param null|string $template Optional. Role to base the new role on.
|
19 |
+
*
|
20 |
+
* @return void
|
21 |
+
*/
|
22 |
+
public function register( $role, $display_name, $template = null ) {
|
23 |
+
$this->roles[ $role ] =
|
24 |
+
(object) array(
|
25 |
+
'display_name' => $display_name,
|
26 |
+
'template' => $template,
|
27 |
+
);
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Returns the list of registered roles.
|
32 |
+
*
|
33 |
+
* @return string[] List or registered roles.
|
34 |
+
*/
|
35 |
+
public function get_roles() {
|
36 |
+
return array_keys( $this->roles );
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Adds the registered roles.
|
41 |
+
*
|
42 |
+
* @return void
|
43 |
+
*/
|
44 |
+
public function add() {
|
45 |
+
foreach ( $this->roles as $role => $data ) {
|
46 |
+
$capabilities = $this->get_capabilities( $data->template );
|
47 |
+
$capabilities = $this->filter_existing_capabilties( $role, $capabilities );
|
48 |
+
|
49 |
+
$this->add_role( $role, $data->display_name, $capabilities );
|
50 |
+
}
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Removes the registered roles.
|
55 |
+
*
|
56 |
+
* @return void
|
57 |
+
*/
|
58 |
+
public function remove() {
|
59 |
+
$roles = array_keys( $this->roles );
|
60 |
+
array_map( array( $this, 'remove_role' ), $roles );
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Returns the capabilities for the specified role.
|
65 |
+
*
|
66 |
+
* @param string $role Role to fetch capabilities from.
|
67 |
+
*
|
68 |
+
* @return array List of capabilities.
|
69 |
+
*/
|
70 |
+
protected function get_capabilities( $role ) {
|
71 |
+
if ( ! is_string( $role ) || empty( $role ) ) {
|
72 |
+
return array();
|
73 |
+
}
|
74 |
+
|
75 |
+
$wp_role = get_role( $role );
|
76 |
+
if ( ! $wp_role ) {
|
77 |
+
return array();
|
78 |
+
}
|
79 |
+
|
80 |
+
return $wp_role->capabilities;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Returns true if the capability exists on the role.
|
85 |
+
*
|
86 |
+
* @param WP_Role $role Role to check capability against.
|
87 |
+
* @param string $capability Capability to check.
|
88 |
+
*
|
89 |
+
* @return bool True if the capability is defined for the role.
|
90 |
+
*/
|
91 |
+
protected function capability_exists( WP_Role $role, $capability ) {
|
92 |
+
return ! array_key_exists( $capability, $role->capabilities );
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Filters out capabilities that are already set for the role.
|
97 |
+
*
|
98 |
+
* This makes sure we don't override configurations that have been previously set.
|
99 |
+
*
|
100 |
+
* @param string $role The role to check against.
|
101 |
+
* @param array $capabilities The capabilities that should be set.
|
102 |
+
*
|
103 |
+
* @return array Capabilties that can be safely set.
|
104 |
+
*/
|
105 |
+
protected function filter_existing_capabilties( $role, array $capabilities ) {
|
106 |
+
if ( $capabilities === array() ) {
|
107 |
+
return $capabilities;
|
108 |
+
}
|
109 |
+
|
110 |
+
$wp_role = get_role( $role );
|
111 |
+
if ( ! $wp_role ) {
|
112 |
+
return $capabilities;
|
113 |
+
}
|
114 |
+
|
115 |
+
foreach ( $capabilities as $capability => $grant ) {
|
116 |
+
if ( $this->capability_exists( $wp_role, $capability ) ) {
|
117 |
+
unset( $capabilities[ $capability ] );
|
118 |
+
}
|
119 |
+
}
|
120 |
+
|
121 |
+
return $capabilities;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Adds a role to the system.
|
126 |
+
*
|
127 |
+
* @param string $role Role to add.
|
128 |
+
* @param string $display_name Name to display for the role.
|
129 |
+
* @param array $capabilities Capabilities to add to the role.
|
130 |
+
*
|
131 |
+
* @return void
|
132 |
+
*/
|
133 |
+
abstract protected function add_role( $role, $display_name, array $capabilities = array() );
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Removes a role from the system
|
137 |
+
*
|
138 |
+
* @param string $role Role to remove.
|
139 |
+
*
|
140 |
+
* @return void
|
141 |
+
*/
|
142 |
+
abstract protected function remove_role( $role );
|
143 |
+
}
|
admin/roles/class-register-roles.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Roles
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Role registration class.
|
8 |
+
*/
|
9 |
+
class WPSEO_Register_Roles implements WPSEO_WordPress_Integration {
|
10 |
+
/**
|
11 |
+
* Adds hooks.
|
12 |
+
*
|
13 |
+
* @return void
|
14 |
+
*/
|
15 |
+
public function register_hooks() {
|
16 |
+
add_action( 'wpseo_register_roles', array( $this, 'register' ) );
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Registers the roles.
|
21 |
+
*
|
22 |
+
* @return void
|
23 |
+
*/
|
24 |
+
public function register() {
|
25 |
+
$role_manager = WPSEO_Role_Manager_Factory::get();
|
26 |
+
|
27 |
+
$role_manager->register( 'wpseo_manager', 'SEO Manager', 'editor' );
|
28 |
+
$role_manager->register( 'wpseo_editor', 'SEO Editor', 'editor' );
|
29 |
+
}
|
30 |
+
}
|
admin/roles/class-role-manager-factory.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Roles
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Role Manager Factory.
|
8 |
+
*/
|
9 |
+
class WPSEO_Role_Manager_Factory {
|
10 |
+
/**
|
11 |
+
* Retrieves the Role manager to use.
|
12 |
+
*
|
13 |
+
* @return WPSEO_Role_Manager
|
14 |
+
*/
|
15 |
+
public static function get() {
|
16 |
+
static $manager = null;
|
17 |
+
|
18 |
+
if ( $manager === null ) {
|
19 |
+
if ( function_exists( 'wpcom_vip_add_role' ) ) {
|
20 |
+
$manager = new WPSEO_Role_Manager_VIP();
|
21 |
+
}
|
22 |
+
|
23 |
+
if ( ! function_exists( 'wpcom_vip_add_role' ) ) {
|
24 |
+
$manager = new WPSEO_Role_Manager_WP();
|
25 |
+
}
|
26 |
+
}
|
27 |
+
|
28 |
+
return $manager;
|
29 |
+
}
|
30 |
+
}
|
admin/roles/class-role-manager-vip.php
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Roles
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* VIP implementation of the Role Manager.
|
8 |
+
*/
|
9 |
+
final class WPSEO_Role_Manager_VIP extends WPSEO_Abstract_Role_Manager {
|
10 |
+
/**
|
11 |
+
* Adds a role to the system.
|
12 |
+
*
|
13 |
+
* @param string $role Role to add.
|
14 |
+
* @param string $display_name Name to display for the role.
|
15 |
+
* @param array $capabilities Capabilities to add to the role.
|
16 |
+
*
|
17 |
+
* @return void
|
18 |
+
*/
|
19 |
+
protected function add_role( $role, $display_name, array $capabilities = array() ) {
|
20 |
+
$enabled_capabilities = array();
|
21 |
+
$disabled_capabilities = array();
|
22 |
+
|
23 |
+
// Build lists of enabled and disabled capabilities.
|
24 |
+
foreach ( $capabilities as $capability => $grant ) {
|
25 |
+
if ( $grant ) {
|
26 |
+
$enabled_capabilities[] = $capability;
|
27 |
+
}
|
28 |
+
|
29 |
+
if ( ! $grant ) {
|
30 |
+
$disabled_capabilities[] = $capability;
|
31 |
+
}
|
32 |
+
}
|
33 |
+
|
34 |
+
wpcom_vip_add_role( $role, $display_name, $enabled_capabilities );
|
35 |
+
if ( $disabled_capabilities !== array() ) {
|
36 |
+
wpcom_vip_remove_role_caps( $role, $disabled_capabilities );
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Removes a role from the system.
|
42 |
+
*
|
43 |
+
* @param string $role Role to remove.
|
44 |
+
*
|
45 |
+
* @return void
|
46 |
+
*/
|
47 |
+
protected function remove_role( $role ) {
|
48 |
+
remove_role( $role );
|
49 |
+
}
|
50 |
+
}
|
admin/roles/class-role-manager-wp.php
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Roles
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* WordPress' default implementation of the Role Manager.
|
8 |
+
*/
|
9 |
+
final class WPSEO_Role_Manager_WP extends WPSEO_Abstract_Role_Manager {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Adds a role to the system.
|
13 |
+
*
|
14 |
+
* @param string $role Role to add.
|
15 |
+
* @param string $display_name Name to display for the role.
|
16 |
+
* @param array $capabilities Capabilities to add to the role.
|
17 |
+
*
|
18 |
+
* @return void
|
19 |
+
*/
|
20 |
+
protected function add_role( $role, $display_name, array $capabilities = array() ) {
|
21 |
+
$wp_role = get_role( $role );
|
22 |
+
if ( $wp_role ) {
|
23 |
+
foreach ( $capabilities as $capability => $grant ) {
|
24 |
+
$wp_role->add_cap( $capability, $grant );
|
25 |
+
}
|
26 |
+
|
27 |
+
return;
|
28 |
+
}
|
29 |
+
|
30 |
+
// @codingStandardsIgnoreLine
|
31 |
+
add_role( $role, $display_name, $capabilities );
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Removes a role from the system.
|
36 |
+
*
|
37 |
+
* @param string $role Role to remove.
|
38 |
+
*
|
39 |
+
* @return void
|
40 |
+
*/
|
41 |
+
protected function remove_role( $role ) {
|
42 |
+
remove_role( $role );
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Formats the capabilities to the required format.
|
47 |
+
*
|
48 |
+
* @param array $capabilities Capabilities to format.
|
49 |
+
* @param bool $enabled Whether these capabilities should be enabled or not.
|
50 |
+
*
|
51 |
+
* @return array Formatted capabilities.
|
52 |
+
*/
|
53 |
+
protected function format_capabilities( array $capabilities, $enabled = true ) {
|
54 |
+
// Flip keys and values.
|
55 |
+
$capabilities = array_flip( $capabilities );
|
56 |
+
|
57 |
+
// Set all values to $enabled.
|
58 |
+
return array_fill_keys( array_keys( $capabilities ), $enabled );
|
59 |
+
}
|
60 |
+
}
|
admin/roles/class-role-manager.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Roles
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Role Manager interface.
|
8 |
+
*/
|
9 |
+
interface WPSEO_Role_Manager {
|
10 |
+
/**
|
11 |
+
* Registers a role.
|
12 |
+
*
|
13 |
+
* @param string $role Role to register.
|
14 |
+
* @param string $display_name Display name to use.
|
15 |
+
* @param null|string $template Optional. Role to base the new role on.
|
16 |
+
*
|
17 |
+
* @return void
|
18 |
+
*/
|
19 |
+
public function register( $role, $display_name, $template = null );
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Adds the registered roles.
|
23 |
+
*
|
24 |
+
* @return void
|
25 |
+
*/
|
26 |
+
public function add();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Removes the registered roles.
|
30 |
+
*
|
31 |
+
* @return void
|
32 |
+
*/
|
33 |
+
public function remove();
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Returns the list of registered roles.
|
37 |
+
*
|
38 |
+
* @return string[] List or registered roles.
|
39 |
+
*/
|
40 |
+
public function get_roles();
|
41 |
+
}
|
admin/statistics/class-statistics-integration.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Statistics
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Statistic_Integration
|
8 |
+
*/
|
9 |
+
class WPSEO_Statistic_Integration implements WPSEO_WordPress_Integration {
|
10 |
+
/**
|
11 |
+
* Adds hooks to clear the cache.
|
12 |
+
*
|
13 |
+
* @return void
|
14 |
+
*/
|
15 |
+
public function register_hooks() {
|
16 |
+
add_action( 'wp_insert_post', array( $this, 'clear_cache' ) );
|
17 |
+
add_action( 'delete_post', array( $this, 'clear_cache' ) );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Clears the dashboard widget items cache.
|
22 |
+
*
|
23 |
+
* @return void
|
24 |
+
*/
|
25 |
+
public function clear_cache() {
|
26 |
+
delete_transient( WPSEO_Statistics_Service::CACHE_TRANSIENT_KEY );
|
27 |
+
}
|
28 |
+
}
|
admin/statistics/class-statistics-service.php
ADDED
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Statistics
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Statistics_Service
|
8 |
+
*/
|
9 |
+
class WPSEO_Statistics_Service {
|
10 |
+
|
11 |
+
const CACHE_TRANSIENT_KEY = 'wpseo-statistics-totals';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var WPSEO_Statistics
|
15 |
+
*/
|
16 |
+
protected $statistics;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var string[]
|
20 |
+
*/
|
21 |
+
protected $labels;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* WPSEO_Statistics_Service contructor.
|
25 |
+
*
|
26 |
+
* @param WPSEO_Statistics $statistics The statistics class to retrieve statistics from.
|
27 |
+
*/
|
28 |
+
public function __construct( WPSEO_Statistics $statistics ) {
|
29 |
+
$this->statistics = $statistics;
|
30 |
+
$this->labels = $this->labels();
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Fetches statistics by REST request.
|
35 |
+
*
|
36 |
+
* @return WP_REST_Response The response object.
|
37 |
+
*/
|
38 |
+
public function get_statistics() {
|
39 |
+
$statistics = $this->statistic_items();
|
40 |
+
|
41 |
+
$data = array(
|
42 |
+
'header' => $this->get_header_from_statistics( $statistics ),
|
43 |
+
'seo_scores' => $statistics['scores'],
|
44 |
+
);
|
45 |
+
|
46 |
+
return new WP_REST_Response( $data );
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Gets a header summarizing the given statistics results.
|
51 |
+
*
|
52 |
+
* @param array $statistics The statistics results.
|
53 |
+
*
|
54 |
+
* @return string The header summing up the statistics results.
|
55 |
+
*/
|
56 |
+
private function get_header_from_statistics( array $statistics ) {
|
57 |
+
// Personal interpretation to allow release, should be looked at later.
|
58 |
+
if ( $statistics['division'] === false ) {
|
59 |
+
return __( 'You don\'t have any published posts, your SEO scores will appear here once you make your first post!', 'wordpress-seo' );
|
60 |
+
}
|
61 |
+
|
62 |
+
if ( $statistics['division']['good'] > 0.66 ) {
|
63 |
+
return __( 'Hey, your SEO is doing pretty well! Check out the stats:', 'wordpress-seo' );
|
64 |
+
}
|
65 |
+
|
66 |
+
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' );
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* An array representing items to be added to the At a Glance dashboard widget
|
71 |
+
*
|
72 |
+
* @return array The statistics for the current user.
|
73 |
+
*/
|
74 |
+
private function statistic_items() {
|
75 |
+
$transient = $this->get_transient();
|
76 |
+
$user_id = get_current_user_id();
|
77 |
+
|
78 |
+
if ( isset( $transient[ $user_id ] ) ) {
|
79 |
+
return $transient[ $user_id ];
|
80 |
+
}
|
81 |
+
|
82 |
+
return $this->set_statistic_items_for_user( $transient, $user_id );
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Gets the statistics transient value. Returns array if transient wasn't set.
|
87 |
+
*
|
88 |
+
* @return array|mixed Returns the transient or an empty array if the transient doesn't exist.
|
89 |
+
*/
|
90 |
+
private function get_transient() {
|
91 |
+
$transient = get_transient( self::CACHE_TRANSIENT_KEY );
|
92 |
+
|
93 |
+
if ( $transient === false ) {
|
94 |
+
return array();
|
95 |
+
}
|
96 |
+
|
97 |
+
return $transient;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Set the statistics transient cache for a specific user
|
102 |
+
*
|
103 |
+
* @param array $transient The current stored transient with the cached data.
|
104 |
+
* @param int $user The user's ID to assign the retrieved values to.
|
105 |
+
*
|
106 |
+
* @return array The statistics transient for the user.
|
107 |
+
*/
|
108 |
+
private function set_statistic_items_for_user( $transient, $user ) {
|
109 |
+
$scores = $this->get_seo_scores_with_post_count();
|
110 |
+
$division = $this->get_seo_score_division( $scores );
|
111 |
+
|
112 |
+
$transient[ $user ] = array(
|
113 |
+
// Use array_values because array_filter may return non-zero indexed arrays.
|
114 |
+
'scores' => array_values( array_filter( $scores, array( $this, 'filter_items' ) ) ),
|
115 |
+
'division' => $division,
|
116 |
+
);
|
117 |
+
|
118 |
+
set_transient( self::CACHE_TRANSIENT_KEY, $transient, DAY_IN_SECONDS );
|
119 |
+
|
120 |
+
return $transient[ $user ];
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Gets the division of SEO scores.
|
125 |
+
*
|
126 |
+
* @param array $scores The SEO scores.
|
127 |
+
*
|
128 |
+
* @return array|bool The division of SEO scores, false if there are no posts.
|
129 |
+
*/
|
130 |
+
private function get_seo_score_division( array $scores ) {
|
131 |
+
$total = 0;
|
132 |
+
$division = array();
|
133 |
+
|
134 |
+
foreach ( $scores as $score ) {
|
135 |
+
$total += $score['count'];
|
136 |
+
}
|
137 |
+
|
138 |
+
if ( $total === 0 ) {
|
139 |
+
return false;
|
140 |
+
}
|
141 |
+
|
142 |
+
foreach ( $scores as $score ) {
|
143 |
+
$division[ $score['seo_rank'] ] = ( $score['count'] / $total );
|
144 |
+
}
|
145 |
+
|
146 |
+
return $division;
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Get all SEO ranks and data associated with them.
|
151 |
+
*
|
152 |
+
* @return array An array of SEO scores and associated data.
|
153 |
+
*/
|
154 |
+
private function get_seo_scores_with_post_count() {
|
155 |
+
$ranks = WPSEO_Rank::get_all_ranks();
|
156 |
+
|
157 |
+
return array_map( array( $this, 'map_rank_to_widget' ), $ranks );
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Converts a rank to data usable in the dashboard widget.
|
162 |
+
*
|
163 |
+
* @param WPSEO_Rank $rank The rank to map.
|
164 |
+
*
|
165 |
+
* @return array The mapped rank.
|
166 |
+
*/
|
167 |
+
private function map_rank_to_widget( WPSEO_Rank $rank ) {
|
168 |
+
return array(
|
169 |
+
'seo_rank' => $rank->get_rank(),
|
170 |
+
'label' => $this->get_label_for_rank( $rank ),
|
171 |
+
'count' => $this->statistics->get_post_count( $rank ),
|
172 |
+
'link' => $this->get_link_for_rank( $rank ),
|
173 |
+
);
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Returns a dashboard widget label to use for a certain rank.
|
178 |
+
*
|
179 |
+
* @param WPSEO_Rank $rank The rank to return a label for.
|
180 |
+
*
|
181 |
+
* @return string The label for the rank.
|
182 |
+
*/
|
183 |
+
private function get_label_for_rank( WPSEO_Rank $rank ) {
|
184 |
+
return $this->labels[ $rank->get_rank() ];
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Determines the labels for the various scoring ranks that are known within Yoast SEO.
|
189 |
+
*
|
190 |
+
* @return array Array containing the translateable labels.
|
191 |
+
*/
|
192 |
+
private function labels() {
|
193 |
+
return array(
|
194 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag */
|
195 |
+
WPSEO_Rank::NO_FOCUS => sprintf( __( 'Posts %1$swithout%2$s a focus keyword', 'wordpress-seo' ), '<strong>', '</strong>' ),
|
196 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag */
|
197 |
+
WPSEO_Rank::BAD => sprintf( __( 'Posts with a %1$sneeds improvement%2$s SEO score', 'wordpress-seo' ), '<strong>', '</strong>' ),
|
198 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag */
|
199 |
+
WPSEO_Rank::OK => sprintf( __( 'Posts with an %1$sOK%2$s SEO score', 'wordpress-seo' ), '<strong>', '</strong>' ),
|
200 |
+
/* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag */
|
201 |
+
WPSEO_Rank::GOOD => sprintf( __( 'Posts with a %1$sgood%2$s SEO score', 'wordpress-seo' ), '<strong>', '</strong>' ),
|
202 |
+
/* translators: %s expands to <span lang="en">noindex</span> */
|
203 |
+
WPSEO_Rank::NO_INDEX => sprintf( __( 'Posts that are set to “%s”', 'wordpress-seo' ), '<span lang="en">noindex</span>' ),
|
204 |
+
);
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Filter items if they have a count of zero.
|
209 |
+
*
|
210 |
+
* @param array $item The item to potentially filter out.
|
211 |
+
*
|
212 |
+
* @return bool Whether or not the count is zero.
|
213 |
+
*/
|
214 |
+
private function filter_items( $item ) {
|
215 |
+
return $item['count'] !== 0;
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* Returns a link for the overview of posts of a certain rank.
|
220 |
+
*
|
221 |
+
* @param WPSEO_Rank $rank The rank to return a link for.
|
222 |
+
*
|
223 |
+
* @return string The link that shows an overview of posts with that rank.
|
224 |
+
*/
|
225 |
+
private function get_link_for_rank( WPSEO_Rank $rank ) {
|
226 |
+
if ( current_user_can( 'edit_others_posts' ) === false ) {
|
227 |
+
return esc_url( admin_url( 'edit.php?post_status=publish&post_type=post&seo_filter=' . $rank->get_rank() . '&author=' . get_current_user_id() ) );
|
228 |
+
}
|
229 |
+
|
230 |
+
return esc_url( admin_url( 'edit.php?post_status=publish&post_type=post&seo_filter=' . $rank->get_rank() ) );
|
231 |
+
}
|
232 |
+
}
|
admin/taxonomy/class-taxonomy-columns.php
ADDED
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class adds columns to the taxonomy table.
|
8 |
+
*/
|
9 |
+
class WPSEO_Taxonomy_Columns {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WPSEO_Metabox_Analysis_SEO
|
13 |
+
*/
|
14 |
+
private $analysis_seo;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var WPSEO_Metabox_Analysis_Readability
|
18 |
+
*/
|
19 |
+
private $analysis_readability;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var string The current taxonomy
|
23 |
+
*/
|
24 |
+
private $taxonomy;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* WPSEO_Taxonomy_Columns constructor.
|
28 |
+
*/
|
29 |
+
public function __construct() {
|
30 |
+
|
31 |
+
$this->taxonomy = $this->get_taxonomy();
|
32 |
+
|
33 |
+
if ( ! empty( $this->taxonomy ) ) {
|
34 |
+
add_filter( 'manage_edit-' . $this->taxonomy . '_columns', array( $this, 'add_columns' ) );
|
35 |
+
add_filter( 'manage_' . $this->taxonomy . '_custom_column', array( $this, 'parse_column' ), 10, 3 );
|
36 |
+
}
|
37 |
+
|
38 |
+
$this->analysis_seo = new WPSEO_Metabox_Analysis_SEO();
|
39 |
+
$this->analysis_readability = new WPSEO_Metabox_Analysis_Readability();
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Adds an SEO score column to the terms table, right after the description column.
|
44 |
+
*
|
45 |
+
* @param array $columns Current set columns.
|
46 |
+
*
|
47 |
+
* @return array
|
48 |
+
*/
|
49 |
+
public function add_columns( array $columns ) {
|
50 |
+
if ( $this->display_metabox( $this->taxonomy ) === false ) {
|
51 |
+
return $columns;
|
52 |
+
}
|
53 |
+
|
54 |
+
$new_columns = array();
|
55 |
+
|
56 |
+
foreach ( $columns as $column_name => $column_value ) {
|
57 |
+
$new_columns[ $column_name ] = $column_value;
|
58 |
+
|
59 |
+
if ( $column_name === 'description' && $this->analysis_seo->is_enabled() ) {
|
60 |
+
$new_columns['wpseo-score'] = '<span class="yoast-tooltip yoast-tooltip-n yoast-tooltip-alt" data-label="' . esc_attr__( 'SEO score', 'wordpress-seo' ) . '"><span class="yoast-column-seo-score yoast-column-header-has-tooltip"><span class="screen-reader-text">' . __( 'SEO score', 'wordpress-seo' ) . '</span></span></span>';
|
61 |
+
}
|
62 |
+
|
63 |
+
if ( $column_name === 'description' && $this->analysis_readability->is_enabled() ) {
|
64 |
+
$new_columns['wpseo-score-readability'] = '<span class="yoast-tooltip yoast-tooltip-n yoast-tooltip-alt" data-label="' . esc_attr__( 'Readability score', 'wordpress-seo' ) . '"><span class="yoast-column-readability yoast-column-header-has-tooltip"><span class="screen-reader-text">' . __( 'Readability score', 'wordpress-seo' ) . '</span></span></span>';
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
return $new_columns;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Parses the column.
|
73 |
+
*
|
74 |
+
* @param string $content The current content of the column.
|
75 |
+
* @param string $column_name The name of the column.
|
76 |
+
* @param integer $term_id ID of requested taxonomy.
|
77 |
+
*
|
78 |
+
* @return string
|
79 |
+
*/
|
80 |
+
public function parse_column( $content, $column_name, $term_id ) {
|
81 |
+
|
82 |
+
switch ( $column_name ) {
|
83 |
+
case 'wpseo-score':
|
84 |
+
return $this->get_score_value( $term_id );
|
85 |
+
|
86 |
+
case 'wpseo-score-readability':
|
87 |
+
return $this->get_score_readability_value( $term_id );
|
88 |
+
}
|
89 |
+
|
90 |
+
return $content;
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Retrieves the taxonomy from the $_GET variable.
|
95 |
+
*
|
96 |
+
* @return string The current taxonomy.
|
97 |
+
*/
|
98 |
+
public function get_current_taxonomy() {
|
99 |
+
return filter_input( $this->get_taxonomy_input_type(), 'taxonomy' );
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Returns the posted/get taxonomy value if it is set.
|
104 |
+
*
|
105 |
+
* @return string|null
|
106 |
+
*/
|
107 |
+
private function get_taxonomy() {
|
108 |
+
if ( defined( 'DOING_AJAX' ) && DOING_AJAX === true ) {
|
109 |
+
return FILTER_INPUT( INPUT_POST, 'taxonomy' );
|
110 |
+
}
|
111 |
+
|
112 |
+
return FILTER_INPUT( INPUT_GET, 'taxonomy' );
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Parses the value for the score column.
|
117 |
+
*
|
118 |
+
* @param integer $term_id ID of requested term.
|
119 |
+
*
|
120 |
+
* @return string
|
121 |
+
*/
|
122 |
+
private function get_score_value( $term_id ) {
|
123 |
+
$term = get_term( $term_id, $this->taxonomy );
|
124 |
+
|
125 |
+
// When the term isn't indexable.
|
126 |
+
if ( ! $this->is_indexable( $term ) ) {
|
127 |
+
return $this->create_score_icon(
|
128 |
+
new WPSEO_Rank( WPSEO_Rank::NO_INDEX ),
|
129 |
+
__( 'Term is set to noindex.', 'wordpress-seo' )
|
130 |
+
);
|
131 |
+
}
|
132 |
+
|
133 |
+
// When there is a focus key word.
|
134 |
+
$focus_keyword = $this->get_focus_keyword( $term );
|
135 |
+
$score = (int) WPSEO_Taxonomy_Meta::get_term_meta( $term_id, $this->taxonomy, 'linkdex' );
|
136 |
+
$rank = WPSEO_Rank::from_numeric_score( $score );
|
137 |
+
|
138 |
+
return $this->create_score_icon( $rank, $rank->get_label() );
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Parses the value for the readability score column.
|
143 |
+
*
|
144 |
+
* @param int $term_id ID of the requested term.
|
145 |
+
*
|
146 |
+
* @return string The HTML for the readability score indicator.
|
147 |
+
*/
|
148 |
+
private function get_score_readability_value( $term_id ) {
|
149 |
+
$score = (int) WPSEO_Taxonomy_Meta::get_term_meta( $term_id, $this->taxonomy, 'content_score' );
|
150 |
+
$rank = WPSEO_Rank::from_numeric_score( $score );
|
151 |
+
|
152 |
+
return $this->create_score_icon( $rank );
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Creates an icon by the given values.
|
157 |
+
*
|
158 |
+
* @param WPSEO_Rank $rank The ranking object.
|
159 |
+
* @param string $title Optional. The title to show. Defaults to the rank label.
|
160 |
+
*
|
161 |
+
* @return string The HTML for a score icon.
|
162 |
+
*/
|
163 |
+
private function create_score_icon( WPSEO_Rank $rank, $title = '' ) {
|
164 |
+
if ( empty( $title ) ) {
|
165 |
+
$title = $rank->get_label();
|
166 |
+
}
|
167 |
+
|
168 |
+
return '<div aria-hidden="true" title="' . esc_attr( $title ) . '" class="wpseo-score-icon ' . esc_attr( $rank->get_css_class() ) . '"></div><span class="screen-reader-text">' . $title . '</span>';
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Check if the taxonomy is indexable.
|
173 |
+
*
|
174 |
+
* @param mixed $term The current term.
|
175 |
+
*
|
176 |
+
* @return bool Whether or not the term is indexable.
|
177 |
+
*/
|
178 |
+
private function is_indexable( $term ) {
|
179 |
+
// When the no_index value is not empty and not default, check if its value is index.
|
180 |
+
$no_index = WPSEO_Taxonomy_Meta::get_term_meta( $term->term_id, $this->taxonomy, 'noindex' );
|
181 |
+
|
182 |
+
// Check if the default for taxonomy is empty (this will be index).
|
183 |
+
if ( ! empty( $no_index ) && $no_index !== 'default' ) {
|
184 |
+
return ( $no_index === 'index' );
|
185 |
+
}
|
186 |
+
|
187 |
+
if ( is_object( $term ) ) {
|
188 |
+
$no_index_key = 'noindex-tax-' . $term->taxonomy;
|
189 |
+
|
190 |
+
// If the option is false, this means we want to index it.
|
191 |
+
return WPSEO_Options::get( $no_index_key, false ) === false;
|
192 |
+
}
|
193 |
+
|
194 |
+
return true;
|
195 |
+
}
|
196 |
+
|
197 |
+
/**
|
198 |
+
* Returns the focus keyword if this is set, otherwise it will give the term name.
|
199 |
+
*
|
200 |
+
* @param stdClass|WP_Term $term The current term.
|
201 |
+
*
|
202 |
+
* @return string
|
203 |
+
*/
|
204 |
+
private function get_focus_keyword( $term ) {
|
205 |
+
$focus_keyword = WPSEO_Taxonomy_Meta::get_term_meta( 'focuskw', $term->term_id, $term->taxonomy );
|
206 |
+
if ( $focus_keyword !== false ) {
|
207 |
+
return $focus_keyword;
|
208 |
+
}
|
209 |
+
|
210 |
+
return $term->name;
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Checks if a taxonomy is being added via a POST method. If not, it defaults to a GET request.
|
215 |
+
*
|
216 |
+
* @return int
|
217 |
+
*/
|
218 |
+
private function get_taxonomy_input_type() {
|
219 |
+
if ( ! empty( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'POST' ) {
|
220 |
+
return INPUT_POST;
|
221 |
+
}
|
222 |
+
|
223 |
+
return INPUT_GET;
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* Wraps the WPSEO_Metabox check to determine whether the metabox should be displayed either by
|
228 |
+
* choice of the admin or because the taxonomy is not public.
|
229 |
+
*
|
230 |
+
* @since 7.0
|
231 |
+
*
|
232 |
+
* @param string $taxonomy Optional. The taxonomy to test, defaults to the current taxonomy.
|
233 |
+
*
|
234 |
+
* @return bool Whether or not the meta box (and associated columns etc) should be hidden.
|
235 |
+
*/
|
236 |
+
private function display_metabox( $taxonomy = null ) {
|
237 |
+
$current_taxonomy = sanitize_text_field( $this->get_current_taxonomy() );
|
238 |
+
|
239 |
+
if ( ! isset( $taxonomy ) && ! empty( $current_taxonomy ) ) {
|
240 |
+
$taxonomy = $current_taxonomy;
|
241 |
+
}
|
242 |
+
|
243 |
+
return WPSEO_Utils::is_metabox_active( $taxonomy, 'taxonomy' );
|
244 |
+
}
|
245 |
+
|
246 |
+
|
247 |
+
}
|
admin/taxonomy/class-taxonomy-content-fields.php
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class parses all the values for the general tab in the Yoast SEO settings metabox
|
8 |
+
*/
|
9 |
+
class WPSEO_Taxonomy_Content_Fields extends WPSEO_Taxonomy_Fields {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns array with the fields for the general tab
|
13 |
+
*
|
14 |
+
* @return array
|
15 |
+
*/
|
16 |
+
public function get() {
|
17 |
+
$fields = array(
|
18 |
+
'snippet' => $this->get_field_config(
|
19 |
+
__( 'Snippet editor', 'wordpress-seo' ),
|
20 |
+
'',
|
21 |
+
'snippetpreview',
|
22 |
+
array(
|
23 |
+
'help-button' => __( 'Show information about the snippet editor', 'wordpress-seo' ),
|
24 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
25 |
+
'help' => sprintf( __( 'This is a rendering of what this post might look like in Google\'s search results. %1$sLearn more about the Snippet Preview%2$s.', 'wordpress-seo' ), '<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/snippet-preview' ) . '">', '</a>' ),
|
26 |
+
)
|
27 |
+
),
|
28 |
+
'focuskw' => $this->get_field_config(
|
29 |
+
__( 'Focus keyword', 'wordpress-seo' ),
|
30 |
+
'',
|
31 |
+
'focuskeyword',
|
32 |
+
array(
|
33 |
+
'help-button' => __( 'Show information about the focus keyword', 'wordpress-seo' ),
|
34 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
35 |
+
'help' => sprintf( __( 'Pick the main keyword or keyphrase that this post/page is about. %1$sLearn more about the Focus Keyword%2$s.', 'wordpress-seo' ), '<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/focus-keyword' ) . '">', '</a>' ),
|
36 |
+
)
|
37 |
+
),
|
38 |
+
'analysis' => $this->get_field_config(
|
39 |
+
__( 'Analysis', 'wordpress-seo' ),
|
40 |
+
'',
|
41 |
+
'pageanalysis',
|
42 |
+
array(
|
43 |
+
'help-button' => __( 'Show information about the content analysis', 'wordpress-seo' ),
|
44 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
45 |
+
'help' => sprintf( __( 'This is the content analysis, a collection of content checks that analyze the content of your page. %1$sLearn more about the Content Analysis Tool%2$s.', 'wordpress-seo' ), '<a target="_blank" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/content-analysis' ) . '">', '</a>' ),
|
46 |
+
)
|
47 |
+
),
|
48 |
+
'title' => $this->get_field_config(
|
49 |
+
'',
|
50 |
+
'',
|
51 |
+
'hidden',
|
52 |
+
''
|
53 |
+
),
|
54 |
+
'desc' => $this->get_field_config(
|
55 |
+
'',
|
56 |
+
'',
|
57 |
+
'hidden',
|
58 |
+
''
|
59 |
+
),
|
60 |
+
'linkdex' => $this->get_field_config(
|
61 |
+
'',
|
62 |
+
'',
|
63 |
+
'hidden',
|
64 |
+
''
|
65 |
+
),
|
66 |
+
'content_score' => $this->get_field_config(
|
67 |
+
'',
|
68 |
+
'',
|
69 |
+
'hidden',
|
70 |
+
''
|
71 |
+
),
|
72 |
+
);
|
73 |
+
|
74 |
+
return $this->filter_hidden_fields( $fields );
|
75 |
+
}
|
76 |
+
}
|
admin/taxonomy/class-taxonomy-fields-presenter.php
ADDED
@@ -0,0 +1,234 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Taxonomy_Presenter
|
8 |
+
*/
|
9 |
+
class WPSEO_Taxonomy_Fields_Presenter {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* The taxonomy meta data for the current term
|
13 |
+
*
|
14 |
+
* @var array
|
15 |
+
*/
|
16 |
+
private $tax_meta;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @param stdClass $term The current term.
|
20 |
+
*/
|
21 |
+
public function __construct( $term ) {
|
22 |
+
$this->tax_meta = WPSEO_Taxonomy_Meta::get_term_meta( (int) $term->term_id, $term->taxonomy );
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Displaying the form fields
|
27 |
+
*
|
28 |
+
* @param array $fields Array with the fields that will be displayed.
|
29 |
+
*/
|
30 |
+
public function html( array $fields ) {
|
31 |
+
$content = '';
|
32 |
+
foreach ( $fields as $field_name => $field_configuration ) {
|
33 |
+
$content .= $this->form_row( 'wpseo_' . $field_name, $field_configuration );
|
34 |
+
}
|
35 |
+
return $content;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Create a row in the form table.
|
40 |
+
*
|
41 |
+
* @param string $field_name Variable the row controls.
|
42 |
+
* @param array $field_configuration Array with the field configuration.
|
43 |
+
*/
|
44 |
+
private function form_row( $field_name, array $field_configuration ) {
|
45 |
+
$esc_field_name = esc_attr( $field_name );
|
46 |
+
|
47 |
+
$options = (array) $field_configuration['options'];
|
48 |
+
|
49 |
+
if ( ! empty( $field_configuration['description'] ) ) {
|
50 |
+
$options['description'] = $field_configuration['description'];
|
51 |
+
}
|
52 |
+
|
53 |
+
$label = $this->get_label( $field_configuration['label'], $esc_field_name );
|
54 |
+
$field = $this->get_field( $field_configuration['type'], $esc_field_name, $this->get_field_value( $field_name ), $options );
|
55 |
+
$help_content = isset( $field_configuration['options']['help'] ) ? $field_configuration['options']['help'] : '';
|
56 |
+
$help_button_text = isset( $field_configuration['options']['help-button'] ) ? $field_configuration['options']['help-button'] : '';
|
57 |
+
$help = new WPSEO_Admin_Help_Panel( $field_name, $help_button_text, $help_content );
|
58 |
+
|
59 |
+
if ( in_array( $field_configuration['type'], array( 'focuskeyword', 'pageanalysis', 'snippetpreview' ), true ) ) {
|
60 |
+
return $this->parse_section_row( $field, $field_configuration['type'], $help );
|
61 |
+
}
|
62 |
+
|
63 |
+
return $this->parse_row( $label, $help, $field );
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Generates the html for the given field config.
|
68 |
+
*
|
69 |
+
* @param string $field_type The fieldtype, e.g: text, checkbox, etc.
|
70 |
+
* @param string $field_name The name of the field.
|
71 |
+
* @param string $field_value The value of the field.
|
72 |
+
* @param array $options Array with additional options.
|
73 |
+
*
|
74 |
+
* @return string
|
75 |
+
*/
|
76 |
+
private function get_field( $field_type, $field_name, $field_value, array $options ) {
|
77 |
+
|
78 |
+
$class = $this->get_class( $options );
|
79 |
+
$field = '';
|
80 |
+
$description = '';
|
81 |
+
$aria_describedby = '';
|
82 |
+
|
83 |
+
if ( ! empty( $options['description'] ) ) {
|
84 |
+
$aria_describedby = ' aria-describedby="' . $field_name . '-desc"';
|
85 |
+
$description = '<p id="' . $field_name . '-desc" class="yoast-metabox__description">' . $options['description'] . '</p>';
|
86 |
+
}
|
87 |
+
|
88 |
+
switch ( $field_type ) {
|
89 |
+
case 'div':
|
90 |
+
$field .= '<div id="' . $field_name . '"></div>';
|
91 |
+
break;
|
92 |
+
|
93 |
+
case 'snippetpreview':
|
94 |
+
$field .= '<div id="wpseosnippet" class="wpseosnippet"></div>';
|
95 |
+
break;
|
96 |
+
case 'pageanalysis':
|
97 |
+
if ( WPSEO_Options::get( 'content_analysis_active', true ) === false && WPSEO_Options::get( 'keyword_analysis_active', true ) === false ) {
|
98 |
+
break;
|
99 |
+
}
|
100 |
+
|
101 |
+
$field .= '<div id="pageanalysis">';
|
102 |
+
$field .= '<section class="yoast-section" id="wpseo-pageanalysis-section">';
|
103 |
+
$field .= '<h3 class="yoast-section__heading yoast-section__heading-icon yoast-section__heading-icon-list">' . __( 'Analysis', 'wordpress-seo' ) . '</h3>';
|
104 |
+
$field .= '<div id="wpseo_analysis"></div>';
|
105 |
+
$field .= '</section>';
|
106 |
+
$field .= '</div>';
|
107 |
+
break;
|
108 |
+
case 'focuskeyword':
|
109 |
+
$field .= '<div id="wpseofocuskeyword">';
|
110 |
+
$field .= '<section class="yoast-section" id="wpseo-focuskeyword-section">';
|
111 |
+
$field .= '<h3 class="yoast-section__heading yoast-section__heading-icon yoast-section__heading-icon-key">' . __( 'Focus keyword', 'wordpress-seo' ) . '</h3>';
|
112 |
+
$field .= '<label for="' . $field_name . '" class="screen-reader-text">' . __( 'Enter a focus keyword', 'wordpress-seo' ) . '</label>';
|
113 |
+
$field .= '<input type="text" id="' . $field_name . '" autocomplete="off" name="' . $field_name . '" value="' . esc_attr( $field_value ) . '" class="large-text' . $class . '"/><br />';
|
114 |
+
$field .= '</section>';
|
115 |
+
$field .= '</div>';
|
116 |
+
break;
|
117 |
+
case 'text':
|
118 |
+
$field .= '<input name="' . $field_name . '" id="' . $field_name . '" ' . $class . ' type="text" value="' . esc_attr( $field_value ) . '" size="40"' . $aria_describedby . '/>';
|
119 |
+
break;
|
120 |
+
case 'checkbox':
|
121 |
+
$field .= '<input name="' . $field_name . '" id="' . $field_name . '" type="checkbox" ' . checked( $field_value ) . $aria_describedby . '/>';
|
122 |
+
break;
|
123 |
+
case 'textarea':
|
124 |
+
$rows = 3;
|
125 |
+
if ( ! empty( $options['rows'] ) ) {
|
126 |
+
$rows = $options['rows'];
|
127 |
+
}
|
128 |
+
$field .= '<textarea class="large-text" rows="' . esc_attr( $rows ) . '" id="' . $field_name . '" name="' . $field_name . '"' . $aria_describedby . '>' . esc_textarea( $field_value ) . '</textarea>';
|
129 |
+
break;
|
130 |
+
case 'upload':
|
131 |
+
$field .= '<input id="' . $field_name . '" type="text" size="36" name="' . $field_name . '" value="' . esc_attr( $field_value ) . '"' . $aria_describedby . ' />';
|
132 |
+
$field .= '<input id="' . $field_name . '_button" class="wpseo_image_upload_button button" type="button" value="' . esc_attr__( 'Upload Image', 'wordpress-seo' ) . '" />';
|
133 |
+
break;
|
134 |
+
case 'select':
|
135 |
+
if ( is_array( $options ) && $options !== array() ) {
|
136 |
+
$field .= '<select name="' . $field_name . '" id="' . $field_name . '"' . $aria_describedby . '>';
|
137 |
+
|
138 |
+
$select_options = ( array_key_exists( 'options', $options ) ) ? $options['options'] : $options;
|
139 |
+
|
140 |
+
foreach ( $select_options as $option => $option_label ) {
|
141 |
+
$selected = selected( $option, $field_value, false );
|
142 |
+
$field .= '<option ' . $selected . ' value="' . esc_attr( $option ) . '">' . esc_html( $option_label ) . '</option>';
|
143 |
+
}
|
144 |
+
unset( $option, $option_label, $selected );
|
145 |
+
|
146 |
+
$field .= '</select>';
|
147 |
+
}
|
148 |
+
break;
|
149 |
+
case 'hidden':
|
150 |
+
$field .= '<input name="' . $field_name . '" id="hidden_' . $field_name . '" type="hidden" value="' . esc_attr( $field_value ) . '" />';
|
151 |
+
break;
|
152 |
+
}
|
153 |
+
|
154 |
+
return $field . $description;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Getting the value for given field_name
|
159 |
+
*
|
160 |
+
* @param string $field_name The fieldname to get the value for.
|
161 |
+
*
|
162 |
+
* @return string
|
163 |
+
*/
|
164 |
+
private function get_field_value( $field_name ) {
|
165 |
+
if ( isset( $this->tax_meta[ $field_name ] ) && $this->tax_meta[ $field_name ] !== '' ) {
|
166 |
+
return $this->tax_meta[ $field_name ];
|
167 |
+
}
|
168 |
+
|
169 |
+
return '';
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Getting the class attributes if $options contains a class key
|
174 |
+
*
|
175 |
+
* @param array $options The array with field options.
|
176 |
+
*
|
177 |
+
* @return string
|
178 |
+
*/
|
179 |
+
private function get_class( array $options ) {
|
180 |
+
if ( ! empty( $options['class'] ) ) {
|
181 |
+
return ' class="' . esc_attr( $options['class'] ) . '"';
|
182 |
+
}
|
183 |
+
|
184 |
+
return '';
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Getting the label HTML
|
189 |
+
*
|
190 |
+
* @param string $label The label value.
|
191 |
+
* @param string $field_name The target field.
|
192 |
+
*
|
193 |
+
* @return string
|
194 |
+
*/
|
195 |
+
private function get_label( $label, $field_name ) {
|
196 |
+
if ( $label !== '' ) {
|
197 |
+
return '<label for="' . $field_name . '">' . esc_html( $label ) . '</label>';
|
198 |
+
}
|
199 |
+
|
200 |
+
return '';
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Returns the HTML for the row which contains label, help and the field.
|
205 |
+
*
|
206 |
+
* @param string $label The html for the label if there was a label set.
|
207 |
+
* @param WPSEO_Admin_Help_Panel $help The help panel to render in this row.
|
208 |
+
* @param string $field The html for the field.
|
209 |
+
*
|
210 |
+
* @return string
|
211 |
+
*/
|
212 |
+
private function parse_row( $label, WPSEO_Admin_Help_Panel $help, $field ) {
|
213 |
+
if ( $label !== '' || $help !== '' ) {
|
214 |
+
return $label . $help->get_button_html() . $help->get_panel_html() . $field;
|
215 |
+
}
|
216 |
+
|
217 |
+
return $field;
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* Creates a sections specific row.
|
222 |
+
*
|
223 |
+
* @param string $content The content to show.
|
224 |
+
* @param string $esc_form_key Escaped form key name.
|
225 |
+
* @param WPSEO_Admin_Help_Panel $help The help button.
|
226 |
+
*
|
227 |
+
* @return string
|
228 |
+
*/
|
229 |
+
private function parse_section_row( $content, $esc_form_key, WPSEO_Admin_Help_Panel $help ) {
|
230 |
+
$html = $content;
|
231 |
+
$html .= '<div class="wpseo_hidden" id="help-yoast-' . $esc_form_key . '">' . $help->get_button_html() . $help->get_panel_html() . '</div>';
|
232 |
+
return $html;
|
233 |
+
}
|
234 |
+
}
|
admin/taxonomy/class-taxonomy-fields.php
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WPSEO_Taxonomy_Tab
|
8 |
+
*
|
9 |
+
* Contains the basics for each class extending this one.
|
10 |
+
*/
|
11 |
+
abstract class WPSEO_Taxonomy_Fields {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* The current term data
|
15 |
+
*
|
16 |
+
* @var stdClass
|
17 |
+
*/
|
18 |
+
protected $term;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Setting the class properties
|
22 |
+
*
|
23 |
+
* @param stdClass $term The current term.
|
24 |
+
*/
|
25 |
+
public function __construct( $term ) {
|
26 |
+
$this->term = $term;
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* This method should return the fields
|
31 |
+
*
|
32 |
+
* @return array
|
33 |
+
*/
|
34 |
+
abstract public function get();
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Returns array with the field data
|
38 |
+
*
|
39 |
+
* @param string $label The label displayed before the field.
|
40 |
+
* @param string $description Description which will explain the field.
|
41 |
+
* @param string $type The field type, for example: input, select.
|
42 |
+
* @param string|array $options Optional. Array with additional options.
|
43 |
+
* @param bool $hide Should the field be hidden.
|
44 |
+
*
|
45 |
+
* @return array
|
46 |
+
*/
|
47 |
+
protected function get_field_config( $label, $description, $type = 'text', $options = '', $hide = false ) {
|
48 |
+
return array(
|
49 |
+
'label' => $label,
|
50 |
+
'description' => $description,
|
51 |
+
'type' => $type,
|
52 |
+
'options' => $options,
|
53 |
+
'hide' => $hide,
|
54 |
+
);
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Filter the hidden fields.
|
59 |
+
*
|
60 |
+
* @param array $fields Array with the form fields that has will be filtered.
|
61 |
+
*
|
62 |
+
* @return array
|
63 |
+
*/
|
64 |
+
protected function filter_hidden_fields( array $fields ) {
|
65 |
+
foreach ( $fields as $field_name => $field_options ) {
|
66 |
+
if ( ! empty( $field_options['hide'] ) ) {
|
67 |
+
unset( $fields[ $field_name ] );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
return $fields;
|
72 |
+
}
|
73 |
+
}
|
admin/taxonomy/class-taxonomy-metabox.php
ADDED
@@ -0,0 +1,417 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class generates the metabox on the edit term page.
|
8 |
+
*/
|
9 |
+
class WPSEO_Taxonomy_Metabox {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WP_Term
|
13 |
+
*/
|
14 |
+
private $term;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
private $taxonomy;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var WPSEO_Taxonomy_Fields_Presenter
|
23 |
+
*/
|
24 |
+
private $taxonomy_tab_content;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var WPSEO_Taxonomy_Social_Fields
|
28 |
+
*/
|
29 |
+
private $taxonomy_social_fields;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var WPSEO_Social_Admin
|
33 |
+
*/
|
34 |
+
private $social_admin;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* The constructor.
|
38 |
+
*
|
39 |
+
* @param string $taxonomy The taxonomy.
|
40 |
+
* @param stdClass $term The term.
|
41 |
+
*/
|
42 |
+
public function __construct( $taxonomy, $term ) {
|
43 |
+
$this->term = $term;
|
44 |
+
$this->taxonomy = $taxonomy;
|
45 |
+
$this->taxonomy_tab_content = new WPSEO_Taxonomy_Fields_Presenter( $this->term );
|
46 |
+
|
47 |
+
add_action( 'admin_footer', array( $this, 'template_generic_tab' ) );
|
48 |
+
add_action( 'admin_footer', array( $this, 'template_keyword_tab' ) );
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Shows the Yoast SEO metabox for the term.
|
53 |
+
*/
|
54 |
+
public function display() {
|
55 |
+
|
56 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
57 |
+
$asset_manager->enqueue_script( 'help-center' );
|
58 |
+
|
59 |
+
$content_sections = $this->get_content_sections();
|
60 |
+
|
61 |
+
$product_title = 'Yoast SEO';
|
62 |
+
if ( file_exists( WPSEO_PATH . 'premium/' ) ) {
|
63 |
+
$product_title .= ' Premium';
|
64 |
+
}
|
65 |
+
|
66 |
+
printf( '<div id="wpseo_meta" class="postbox yoast wpseo-taxonomy-metabox-postbox"><h2><span>%1$s</span></h2>', $product_title );
|
67 |
+
|
68 |
+
echo '<div class="inside">';
|
69 |
+
|
70 |
+
$helpcenter_tab = new WPSEO_Option_Tab( 'tax-metabox', __( 'Meta box', 'wordpress-seo' ),
|
71 |
+
array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/metabox-taxonomy-screencast' ) ) );
|
72 |
+
|
73 |
+
$helpcenter = new WPSEO_Help_Center( 'tax-metabox', $helpcenter_tab, WPSEO_Utils::is_yoast_seo_premium() );
|
74 |
+
$helpcenter->localize_data();
|
75 |
+
$helpcenter->mount();
|
76 |
+
|
77 |
+
echo '<div id="taxonomy_overall"></div>';
|
78 |
+
|
79 |
+
if ( ! defined( 'WPSEO_PREMIUM_FILE' ) ) {
|
80 |
+
echo $this->get_buy_premium_link();
|
81 |
+
}
|
82 |
+
|
83 |
+
echo '<div class="wpseo-metabox-sidebar"><ul>';
|
84 |
+
|
85 |
+
foreach ( $content_sections as $content_section ) {
|
86 |
+
if ( $content_section->name === 'premium' ) {
|
87 |
+
continue;
|
88 |
+
}
|
89 |
+
|
90 |
+
$content_section->display_link();
|
91 |
+
}
|
92 |
+
|
93 |
+
echo '</ul></div>';
|
94 |
+
|
95 |
+
foreach ( $content_sections as $content_section ) {
|
96 |
+
$content_section->display_content();
|
97 |
+
}
|
98 |
+
echo '</div></div>';
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Returns the relevant metabox sections for the current view.
|
103 |
+
*
|
104 |
+
* @return WPSEO_Metabox_Section[]
|
105 |
+
*/
|
106 |
+
private function get_content_sections() {
|
107 |
+
$content_sections = array(
|
108 |
+
$this->get_content_meta_section(),
|
109 |
+
$this->get_social_meta_section(),
|
110 |
+
$this->get_settings_meta_section(),
|
111 |
+
);
|
112 |
+
|
113 |
+
if ( ! defined( 'WPSEO_PREMIUM_FILE' ) ) {
|
114 |
+
$content_sections[] = $this->get_buy_premium_section();
|
115 |
+
}
|
116 |
+
|
117 |
+
return $content_sections;
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Returns the metabox section for the content analysis.
|
122 |
+
*
|
123 |
+
* @return WPSEO_Metabox_Section
|
124 |
+
*/
|
125 |
+
private function get_content_meta_section() {
|
126 |
+
$taxonomy_content_fields = new WPSEO_Taxonomy_Content_Fields( $this->term );
|
127 |
+
$content = $this->taxonomy_tab_content->html( $taxonomy_content_fields->get( $this->term ) );
|
128 |
+
|
129 |
+
$tab = new WPSEO_Metabox_Form_Tab(
|
130 |
+
'content',
|
131 |
+
$content,
|
132 |
+
'',
|
133 |
+
array(
|
134 |
+
'tab_class' => 'yoast-seo__remove-tab',
|
135 |
+
)
|
136 |
+
);
|
137 |
+
|
138 |
+
return new WPSEO_Metabox_Tab_Section(
|
139 |
+
'content',
|
140 |
+
'<span class="screen-reader-text">' . __( 'Content optimization', 'wordpress-seo' ) . '</span><span class="yst-traffic-light-container">' . $this->traffic_light_svg() . '</span>',
|
141 |
+
array( $tab ),
|
142 |
+
array(
|
143 |
+
'link_aria_label' => __( 'Content optimization', 'wordpress-seo' ),
|
144 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-e',
|
145 |
+
)
|
146 |
+
);
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Returns the metabox section for the settings.
|
151 |
+
*
|
152 |
+
* @return WPSEO_Metabox_Section
|
153 |
+
*/
|
154 |
+
private function get_settings_meta_section() {
|
155 |
+
$taxonomy_settings_fields = new WPSEO_Taxonomy_Settings_Fields( $this->term );
|
156 |
+
$content = $this->taxonomy_tab_content->html( $taxonomy_settings_fields->get() );
|
157 |
+
|
158 |
+
$tab = new WPSEO_Metabox_Form_Tab(
|
159 |
+
'settings',
|
160 |
+
$content,
|
161 |
+
__( 'Settings', 'wordpress-seo' ),
|
162 |
+
array(
|
163 |
+
'single' => true,
|
164 |
+
)
|
165 |
+
);
|
166 |
+
|
167 |
+
return new WPSEO_Metabox_Tab_Section(
|
168 |
+
'settings',
|
169 |
+
'<span class="screen-reader-text">' . __( 'Settings', 'wordpress-seo' ) . '</span><span class="dashicons dashicons-admin-generic"></span>',
|
170 |
+
array( $tab ),
|
171 |
+
array(
|
172 |
+
'link_aria_label' => __( 'Settings', 'wordpress-seo' ),
|
173 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-e',
|
174 |
+
)
|
175 |
+
);
|
176 |
+
}
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Returns the metabox section for the social settings.
|
180 |
+
*
|
181 |
+
* @return WPSEO_Metabox_Section
|
182 |
+
*/
|
183 |
+
private function get_social_meta_section() {
|
184 |
+
$this->taxonomy_social_fields = new WPSEO_Taxonomy_Social_Fields( $this->term );
|
185 |
+
$this->social_admin = new WPSEO_Social_Admin();
|
186 |
+
|
187 |
+
$tabs = array();
|
188 |
+
$tabs[] = $this->create_tab( 'facebook', 'opengraph', 'facebook-alt', __( 'Facebook / Open Graph metadata', 'wordpress-seo' ) );
|
189 |
+
$tabs[] = $this->create_tab( 'twitter', 'twitter', 'twitter', __( 'Twitter metadata', 'wordpress-seo' ) );
|
190 |
+
|
191 |
+
return new WPSEO_Metabox_Tab_Section(
|
192 |
+
'social',
|
193 |
+
'<span class="screen-reader-text">' . __( 'Social', 'wordpress-seo' ) . '</span><span class="dashicons dashicons-share"></span>',
|
194 |
+
$tabs,
|
195 |
+
array(
|
196 |
+
'link_aria_label' => __( 'Social', 'wordpress-seo' ),
|
197 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-e',
|
198 |
+
)
|
199 |
+
);
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Creates a social network tab.
|
204 |
+
*
|
205 |
+
* @param string $name The name of the tab.
|
206 |
+
* @param string $network The network of the tab.
|
207 |
+
* @param string $icon The icon for the tab.
|
208 |
+
* @param string $label The label for the tab.
|
209 |
+
*
|
210 |
+
* @return WPSEO_Metabox_Form_Tab|bool
|
211 |
+
*/
|
212 |
+
private function create_tab( $name, $network, $icon, $label ) {
|
213 |
+
if ( WPSEO_Options::get( $network ) !== true ) {
|
214 |
+
return false;
|
215 |
+
}
|
216 |
+
|
217 |
+
$meta_fields = $this->taxonomy_social_fields->get_by_network( $network );
|
218 |
+
|
219 |
+
$tab_settings = new WPSEO_Metabox_Form_Tab(
|
220 |
+
$name,
|
221 |
+
$this->social_admin->get_premium_notice( $network ) . $this->taxonomy_tab_content->html( $meta_fields ),
|
222 |
+
'<span class="screen-reader-text">' . $label . '</span><span class="dashicons dashicons-' . $icon . '"></span>',
|
223 |
+
array(
|
224 |
+
'link_aria_label' => $label,
|
225 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-se',
|
226 |
+
'single' => $this->has_single_social_tab(),
|
227 |
+
)
|
228 |
+
);
|
229 |
+
|
230 |
+
return $tab_settings;
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Determine whether we only show one social network or two.
|
235 |
+
*
|
236 |
+
* @return bool
|
237 |
+
*/
|
238 |
+
private function has_single_social_tab() {
|
239 |
+
return ( WPSEO_Options::get( 'opengraph' ) === false || WPSEO_Options::get( 'twitter' ) === false );
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Returns a link to activate the Buy Premium tab.
|
244 |
+
*
|
245 |
+
* @return string
|
246 |
+
*/
|
247 |
+
private function get_buy_premium_link() {
|
248 |
+
return sprintf( "<div class='%s'><a href='#wpseo-meta-section-premium' class='wpseo-meta-section-link'><span class='dashicons dashicons-star-filled wpseo-buy-premium'></span>%s</a></div>",
|
249 |
+
'wpseo-metabox-buy-premium',
|
250 |
+
__( 'Go Premium', 'wordpress-seo' )
|
251 |
+
);
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* Returns the metabox section for the Premium section..
|
256 |
+
*
|
257 |
+
* @return WPSEO_Metabox_Section
|
258 |
+
*/
|
259 |
+
private function get_buy_premium_section() {
|
260 |
+
$content = sprintf( "<div class='wpseo-premium-description'>
|
261 |
+
%s
|
262 |
+
<ul class='wpseo-premium-advantages-list'>
|
263 |
+
<li>
|
264 |
+
<strong>%s</strong> - %s
|
265 |
+
</li>
|
266 |
+
<li>
|
267 |
+
<strong>%s</strong> - %s
|
268 |
+
</li>
|
269 |
+
<li>
|
270 |
+
<strong>%s</strong> - %s
|
271 |
+
</li>
|
272 |
+
<li>
|
273 |
+
<strong>%s</strong> - %s
|
274 |
+
</li>
|
275 |
+
</ul>
|
276 |
+
|
277 |
+
<a target='_blank' id='wpseo-buy-premium-popup-button' class='button button-buy-premium wpseo-metabox-go-to' href='%s'>
|
278 |
+
%s
|
279 |
+
</a>
|
280 |
+
|
281 |
+
<p><a target='_blank' class='wpseo-metabox-go-to' href='%s'>%s</a></p>
|
282 |
+
</div>",
|
283 |
+
/* translators: %1$s expands to Yoast SEO Premium. */
|
284 |
+
sprintf( __( 'You\'re not getting the benefits of %1$s yet. If you had %1$s, you could use its awesome features:', 'wordpress-seo' ), 'Yoast SEO Premium' ),
|
285 |
+
__( 'Redirect manager', 'wordpress-seo' ),
|
286 |
+
__( 'Create and manage redirects within your WordPress install.', 'wordpress-seo' ),
|
287 |
+
__( 'Multiple focus keywords', 'wordpress-seo' ),
|
288 |
+
__( 'Optimize a single post for up to 5 keywords.', 'wordpress-seo' ),
|
289 |
+
__( 'Social Previews', 'wordpress-seo' ),
|
290 |
+
__( 'Check what your Facebook or Twitter post will look like.', 'wordpress-seo' ),
|
291 |
+
__( 'Premium support', 'wordpress-seo' ),
|
292 |
+
__( 'Gain access to our 24/7 support team.', 'wordpress-seo' ),
|
293 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/pe-buy-premium' ),
|
294 |
+
/* translators: %s expands to Yoast SEO Premium. */
|
295 |
+
sprintf( __( 'Get %s now!', 'wordpress-seo' ), 'Yoast SEO Premium' ),
|
296 |
+
WPSEO_Shortlinker::get( 'https://yoa.st/pe-premium-page' ),
|
297 |
+
__( 'More info', 'wordpress-seo' )
|
298 |
+
);
|
299 |
+
|
300 |
+
$tab = new WPSEO_Metabox_Form_Tab(
|
301 |
+
'premium',
|
302 |
+
$content,
|
303 |
+
'Yoast SEO Premium',
|
304 |
+
array(
|
305 |
+
'single' => true,
|
306 |
+
)
|
307 |
+
);
|
308 |
+
|
309 |
+
return new WPSEO_Metabox_Tab_Section(
|
310 |
+
'premium',
|
311 |
+
'<span class="dashicons dashicons-star-filled wpseo-buy-premium"></span>',
|
312 |
+
array( $tab ),
|
313 |
+
array(
|
314 |
+
'link_aria_label' => 'Yoast SEO Premium',
|
315 |
+
'link_class' => 'yoast-tooltip yoast-tooltip-e',
|
316 |
+
)
|
317 |
+
);
|
318 |
+
}
|
319 |
+
|
320 |
+
/**
|
321 |
+
* Return the SVG for the traffic light in the metabox.
|
322 |
+
*/
|
323 |
+
public function traffic_light_svg() {
|
324 |
+
return <<<SVG
|
325 |
+
<svg class="yst-traffic-light init" version="1.1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
|
326 |
+
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
327 |
+
x="0px" y="0px" viewBox="0 0 30 47" enable-background="new 0 0 30 47" xml:space="preserve">
|
328 |
+
<g id="BG_1_">
|
329 |
+
</g>
|
330 |
+
<g id="traffic_light">
|
331 |
+
<g>
|
332 |
+
<g>
|
333 |
+
<g>
|
334 |
+
<path fill="#5B2942" d="M22,0H8C3.6,0,0,3.6,0,7.9v31.1C0,43.4,3.6,47,8,47h14c4.4,0,8-3.6,8-7.9V7.9C30,3.6,26.4,0,22,0z
|
335 |
+
M27.5,38.8c0,3.1-2.6,5.7-5.8,5.7H8.3c-3.2,0-5.8-2.5-5.8-5.7V8.3c0-1.5,0.6-2.9,1.7-4c1.1-1,2.5-1.6,4.1-1.6h13.4
|
336 |
+
c1.5,0,3,0.6,4.1,1.6c1.1,1.1,1.7,2.5,1.7,4V38.8z"/>
|
337 |
+
</g>
|
338 |
+
<g class="traffic-light-color traffic-light-red">
|
339 |
+
<ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
|
340 |
+
<ellipse fill="#DC3232" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
|
341 |
+
<ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
|
342 |
+
</g>
|
343 |
+
<g class="traffic-light-color traffic-light-orange">
|
344 |
+
<ellipse fill="#F49A00" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
|
345 |
+
<ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
|
346 |
+
<ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
|
347 |
+
</g>
|
348 |
+
<g class="traffic-light-color traffic-light-green">
|
349 |
+
<ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
|
350 |
+
<ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
|
351 |
+
<ellipse fill="#63B22B" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
|
352 |
+
</g>
|
353 |
+
<g class="traffic-light-color traffic-light-empty">
|
354 |
+
<ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
|
355 |
+
<ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
|
356 |
+
<ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
|
357 |
+
</g>
|
358 |
+
<g class="traffic-light-color traffic-light-init">
|
359 |
+
<ellipse fill="#5B2942" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
|
360 |
+
<ellipse fill="#5B2942" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
|
361 |
+
<ellipse fill="#5B2942" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
|
362 |
+
</g>
|
363 |
+
</g>
|
364 |
+
</g>
|
365 |
+
</g>
|
366 |
+
</svg>
|
367 |
+
SVG;
|
368 |
+
}
|
369 |
+
|
370 |
+
/**
|
371 |
+
* Generic tab.
|
372 |
+
*/
|
373 |
+
public function template_generic_tab() {
|
374 |
+
// This template belongs to the post scraper so don't echo it if it isn't enqueued.
|
375 |
+
if ( ! wp_script_is( WPSEO_Admin_Asset_Manager::PREFIX . 'term-scraper' ) ) {
|
376 |
+
return;
|
377 |
+
}
|
378 |
+
|
379 |
+
echo '<script type="text/html" id="tmpl-generic_tab">
|
380 |
+
<li class="<# if ( data.classes ) { #>{{data.classes}}<# } #><# if ( data.active ) { #> active<# } #>">
|
381 |
+
<a class="wpseo_tablink" href="#wpseo_generic" data-score="{{data.score}}">
|
382 |
+
<span class="wpseo-score-icon {{data.score}}"></span>
|
383 |
+
<span class="wpseo-tab-prefix">{{data.prefix}}</span>
|
384 |
+
<span class="wpseo-tab-label">{{data.label}}</span>
|
385 |
+
<span class="screen-reader-text wpseo-generic-tab-textual-score">{{data.scoreText}}</span>
|
386 |
+
</a>
|
387 |
+
<# if ( data.hideable ) { #>
|
388 |
+
<button type="button" class="remove-tab" aria-label="{{data.removeLabel}}"><span>x</span></button>
|
389 |
+
<# } #>
|
390 |
+
</li>
|
391 |
+
</script>';
|
392 |
+
}
|
393 |
+
|
394 |
+
/**
|
395 |
+
* Keyword tab for enabling analysis of multiple keywords.
|
396 |
+
*/
|
397 |
+
public function template_keyword_tab() {
|
398 |
+
// This template belongs to the term scraper so don't echo it if it isn't enqueued.
|
399 |
+
if ( ! wp_script_is( WPSEO_Admin_Asset_Manager::PREFIX . 'term-scraper' ) ) {
|
400 |
+
return;
|
401 |
+
}
|
402 |
+
|
403 |
+
echo '<script type="text/html" id="tmpl-keyword_tab">
|
404 |
+
<li class="<# if ( data.classes ) { #>{{data.classes}}<# } #><# if ( data.active ) { #> active<# } #>">
|
405 |
+
<a class="wpseo_tablink" href="#wpseo_content" data-keyword="{{data.keyword}}" data-score="{{data.score}}">
|
406 |
+
<span class="wpseo-score-icon {{data.score}}"></span>
|
407 |
+
<span class="wpseo-tab-prefix">{{data.prefix}}</span>
|
408 |
+
<em class="wpseo-keyword">{{data.label}}</em>
|
409 |
+
<span class="screen-reader-text wpseo-keyword-tab-textual-score">{{data.scoreText}}</span>
|
410 |
+
</a>
|
411 |
+
<# if ( data.hideable ) { #>
|
412 |
+
<button type="button" class="remove-keyword" aria-label="{{data.removeLabel}}"><span>x</span></button>
|
413 |
+
<# } #>
|
414 |
+
</li>
|
415 |
+
</script>';
|
416 |
+
}
|
417 |
+
}
|
admin/taxonomy/class-taxonomy-settings-fields.php
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class parses all the values for the general tab in the Yoast SEO settings metabox
|
8 |
+
*/
|
9 |
+
class WPSEO_Taxonomy_Settings_Fields extends WPSEO_Taxonomy_Fields {
|
10 |
+
/**
|
11 |
+
* @var array Options array for the no-index options, including translated labels
|
12 |
+
*/
|
13 |
+
private $no_index_options = array();
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param stdClass $term The currenct taxonomy.
|
17 |
+
*/
|
18 |
+
public function __construct( $term ) {
|
19 |
+
parent::__construct( $term );
|
20 |
+
$this->translate_meta_options();
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Returns array with the fields for the General tab.
|
25 |
+
*
|
26 |
+
* @return array Fields to be used on the General tab.
|
27 |
+
*/
|
28 |
+
public function get() {
|
29 |
+
$labels = $this->get_taxonomy_labels();
|
30 |
+
$fields = array(
|
31 |
+
'noindex' => $this->get_field_config(
|
32 |
+
esc_html( sprintf( __( 'Allow search engines to show this %s in search results?', 'wordpress-seo' ), $labels->singular_name ) ),
|
33 |
+
'',
|
34 |
+
'select',
|
35 |
+
$this->get_noindex_options()
|
36 |
+
),
|
37 |
+
'bctitle' => $this->get_field_config(
|
38 |
+
__( 'Breadcrumbs Title', 'wordpress-seo' ),
|
39 |
+
esc_html__( 'The Breadcrumbs Title is used in the breadcrumbs where this taxonomy appears.', 'wordpress-seo' ),
|
40 |
+
'text',
|
41 |
+
'',
|
42 |
+
( WPSEO_Options::get( 'breadcrumbs-enable' ) !== true )
|
43 |
+
),
|
44 |
+
'canonical' => $this->get_field_config(
|
45 |
+
__( 'Canonical URL', 'wordpress-seo' ),
|
46 |
+
esc_html__( 'The canonical link is shown on the archive page for this term.', 'wordpress-seo' )
|
47 |
+
),
|
48 |
+
);
|
49 |
+
|
50 |
+
return $this->filter_hidden_fields( $fields );
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Translate options text strings for use in the select fields
|
55 |
+
*
|
56 |
+
* {@internal IMPORTANT: if you want to add a new string (option) somewhere, make sure you add
|
57 |
+
* that array key to the main options definition array in the class WPSEO_Taxonomy_Meta() as well!!!!}}
|
58 |
+
*/
|
59 |
+
private function translate_meta_options() {
|
60 |
+
$this->no_index_options = WPSEO_Taxonomy_Meta::$no_index_options;
|
61 |
+
|
62 |
+
/* translators: %1$s expands to the taxonomy name %2$s expands to the current taxonomy index value */
|
63 |
+
$this->no_index_options['default'] = __( '%2$s (current default for %1$s)', 'wordpress-seo' );
|
64 |
+
$this->no_index_options['index'] = __( 'Yes', 'wordpress-seo' );
|
65 |
+
$this->no_index_options['noindex'] = __( 'No', 'wordpress-seo' );
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Getting the data for the noindex fields
|
70 |
+
*
|
71 |
+
* @return array Array containing the no_index options.
|
72 |
+
*/
|
73 |
+
private function get_noindex_options() {
|
74 |
+
$labels = $this->get_taxonomy_labels();
|
75 |
+
$noindex_options['options'] = $this->no_index_options;
|
76 |
+
$noindex_options['options']['default'] = sprintf( $noindex_options['options']['default'], $labels->name, $this->get_robot_index() );
|
77 |
+
|
78 |
+
return $noindex_options;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Retrieve the taxonomies plural for use in sentences.
|
83 |
+
*
|
84 |
+
* @return object Object containing the taxonomy's labels.
|
85 |
+
*/
|
86 |
+
private function get_taxonomy_labels() {
|
87 |
+
$taxonomy = get_taxonomy( $this->term->taxonomy );
|
88 |
+
|
89 |
+
return $taxonomy->labels;
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Returns the current robot index value for the taxonomy
|
94 |
+
*
|
95 |
+
* @return string
|
96 |
+
*/
|
97 |
+
private function get_robot_index() {
|
98 |
+
$robot_index = $this->no_index_options['index'];
|
99 |
+
$index_option = 'noindex-tax-' . $this->term->taxonomy;
|
100 |
+
if ( WPSEO_Options::get( $index_option, false ) ) {
|
101 |
+
$robot_index = $this->no_index_options['noindex'];
|
102 |
+
}
|
103 |
+
|
104 |
+
return $robot_index;
|
105 |
+
}
|
106 |
+
}
|
admin/taxonomy/class-taxonomy-social-fields.php
ADDED
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class parses all the values for the social tab in the Yoast SEO settings metabox
|
8 |
+
*/
|
9 |
+
class WPSEO_Taxonomy_Social_Fields extends WPSEO_Taxonomy_Fields {
|
10 |
+
/** @var array List of social networks */
|
11 |
+
protected $networks;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Setting the class properties
|
15 |
+
*
|
16 |
+
* @param stdClass|WP_Term $term The current taxonomy.
|
17 |
+
*/
|
18 |
+
public function __construct( $term ) {
|
19 |
+
parent::__construct( $term );
|
20 |
+
|
21 |
+
$this->networks = $this->get_social_networks();
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* When this method returns false, the social tab in the meta box will be hidden
|
26 |
+
*
|
27 |
+
* @return bool
|
28 |
+
*/
|
29 |
+
public function show_social() {
|
30 |
+
return ( WPSEO_Options::get( 'opengraph', false ) || WPSEO_Options::get( 'twitter', false ) );
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Gets the social meta fields by social network for the taxonomy.
|
35 |
+
*
|
36 |
+
* @param string $network The social network for which to fetch the fields.
|
37 |
+
*
|
38 |
+
* @return array
|
39 |
+
*/
|
40 |
+
public function get_by_network( $network ) {
|
41 |
+
$settings = $this->networks[ $network ];
|
42 |
+
|
43 |
+
return array(
|
44 |
+
$settings['network'] . '-title' => $this->get_field_config(
|
45 |
+
/* translators: %s expands to the social network name */
|
46 |
+
sprintf( __( '%s Title', 'wordpress-seo' ), $settings['label'] ),
|
47 |
+
/* translators: %1$s expands to the social network name */
|
48 |
+
sprintf( esc_html__( 'If you don\'t want to use the title for sharing on %1$s but instead want another title there, write it here.', 'wordpress-seo' ), $settings['label'] ),
|
49 |
+
'text',
|
50 |
+
array( 'class' => 'large-text' )
|
51 |
+
),
|
52 |
+
$settings['network'] . '-description' => $this->get_field_config(
|
53 |
+
/* translators: %s expands to the social network name */
|
54 |
+
sprintf( __( '%s Description', 'wordpress-seo' ), $settings['label'] ),
|
55 |
+
/* translators: %1$s expands to the social network name */
|
56 |
+
sprintf( esc_html__( 'If you don\'t want to use the meta description for sharing on %1$s but want another description there, write it here.', 'wordpress-seo' ), $settings['label'] ),
|
57 |
+
'textarea'
|
58 |
+
),
|
59 |
+
$settings['network'] . '-image' => $this->get_field_config(
|
60 |
+
/* translators: %s expands to the social network name */
|
61 |
+
sprintf( __( '%s Image', 'wordpress-seo' ), $settings['label'] ),
|
62 |
+
/* translators: %1$s expands to the social network name */
|
63 |
+
sprintf( esc_html__( 'If you want to use an image for sharing on %1$s, you can upload / choose an image or add the image URL here.', 'wordpress-seo' ), $settings['label'] ) . '<br />' .
|
64 |
+
/* translators: %1$s expands to the social network name, %2$s expands to the image size */
|
65 |
+
sprintf( __( 'The recommended image size for %1$s is %2$s pixels.', 'wordpress-seo' ), $settings['label'], $settings['size'] ),
|
66 |
+
'upload'
|
67 |
+
),
|
68 |
+
);
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Returning the fields for the social media tab
|
73 |
+
*
|
74 |
+
* @return array
|
75 |
+
*/
|
76 |
+
public function get() {
|
77 |
+
$fields = array();
|
78 |
+
foreach ( $this->networks as $option => $settings ) {
|
79 |
+
$fields_to_push = $this->get_by_network( $option );
|
80 |
+
|
81 |
+
$fields = array_merge( $fields, $fields_to_push );
|
82 |
+
}
|
83 |
+
|
84 |
+
return $this->filter_hidden_fields( $fields );
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Getting array with the social networks
|
89 |
+
*
|
90 |
+
* @return array
|
91 |
+
*/
|
92 |
+
private function get_social_networks() {
|
93 |
+
$social_networks = array(
|
94 |
+
// Source: https://developers.facebook.com/docs/sharing/best-practices#images.
|
95 |
+
'opengraph' => $this->social_network( 'opengraph', __( 'Facebook', 'wordpress-seo' ), sprintf(
|
96 |
+
/* translators: %1$s expands to the image recommended width, %2$s to its height. */
|
97 |
+
__( '%1$s by %2$s', 'wordpress-seo' ), '1200', '630'
|
98 |
+
) ),
|
99 |
+
'twitter' => $this->social_network( 'twitter', __( 'Twitter', 'wordpress-seo' ), sprintf(
|
100 |
+
/* translators: %1$s expands to the image recommended width, %2$s to its height. */
|
101 |
+
__( '%1$s by %2$s', 'wordpress-seo' ), '1024', '512'
|
102 |
+
) ),
|
103 |
+
);
|
104 |
+
|
105 |
+
return $this->filter_social_networks( $social_networks );
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Returns array with the config fields for the social network
|
110 |
+
*
|
111 |
+
* @param string $network The name of the social network.
|
112 |
+
* @param string $label The label for the social network.
|
113 |
+
* @param string $image_size The image dimensions.
|
114 |
+
*
|
115 |
+
* @return array
|
116 |
+
*/
|
117 |
+
private function social_network( $network, $label, $image_size ) {
|
118 |
+
return array(
|
119 |
+
'network' => $network,
|
120 |
+
'label' => $label,
|
121 |
+
'size' => $image_size,
|
122 |
+
);
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Filter the social networks which are disabled in the configuration
|
127 |
+
*
|
128 |
+
* @param array $social_networks Array with the social networks that have to be filtered.
|
129 |
+
*
|
130 |
+
* @return array
|
131 |
+
*/
|
132 |
+
private function filter_social_networks( array $social_networks ) {
|
133 |
+
foreach ( $social_networks as $social_network => $settings ) {
|
134 |
+
if ( WPSEO_Options::get( $social_network, false ) === false ) {
|
135 |
+
unset( $social_networks[ $social_network ] );
|
136 |
+
}
|
137 |
+
}
|
138 |
+
|
139 |
+
return $social_networks;
|
140 |
+
}
|
141 |
+
}
|
admin/taxonomy/class-taxonomy.php
ADDED
@@ -0,0 +1,344 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class that handles the edit boxes on taxonomy edit pages.
|
8 |
+
*/
|
9 |
+
class WPSEO_Taxonomy {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* The current active taxonomy.
|
13 |
+
*
|
14 |
+
* @var string
|
15 |
+
*/
|
16 |
+
private $taxonomy = '';
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var WPSEO_Metabox_Analysis_SEO
|
20 |
+
*/
|
21 |
+
private $analysis_seo;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var WPSEO_Metabox_Analysis_Readability
|
25 |
+
*/
|
26 |
+
private $analysis_readability;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Class constructor.
|
30 |
+
*/
|
31 |
+
public function __construct() {
|
32 |
+
$this->taxonomy = $this->get_taxonomy();
|
33 |
+
|
34 |
+
add_action( 'edit_term', array( $this, 'update_term' ), 99, 3 );
|
35 |
+
add_action( 'init', array( $this, 'custom_category_descriptions_allow_html' ) );
|
36 |
+
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
37 |
+
|
38 |
+
if ( self::is_term_overview( $GLOBALS['pagenow'] ) ) {
|
39 |
+
new WPSEO_Taxonomy_Columns();
|
40 |
+
}
|
41 |
+
$this->analysis_seo = new WPSEO_Metabox_Analysis_SEO();
|
42 |
+
$this->analysis_readability = new WPSEO_Metabox_Analysis_Readability();
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Add hooks late enough for taxonomy object to be available for checks.
|
47 |
+
*/
|
48 |
+
public function admin_init() {
|
49 |
+
|
50 |
+
$taxonomy = get_taxonomy( $this->taxonomy );
|
51 |
+
|
52 |
+
if ( empty( $taxonomy ) || empty( $taxonomy->public ) || ! $this->show_metabox() ) {
|
53 |
+
return;
|
54 |
+
}
|
55 |
+
|
56 |
+
$this->insert_description_field_editor();
|
57 |
+
|
58 |
+
add_filter( 'category_description', array( $this, 'custom_category_descriptions_add_shortcode_support' ) );
|
59 |
+
|
60 |
+
add_action( sanitize_text_field( $this->taxonomy ) . '_edit_form', array( $this, 'term_metabox' ), 90, 1 );
|
61 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Show the SEO inputs for term.
|
66 |
+
*
|
67 |
+
* @param stdClass|WP_Term $term Term to show the edit boxes for.
|
68 |
+
*/
|
69 |
+
public function term_metabox( $term ) {
|
70 |
+
$metabox = new WPSEO_Taxonomy_Metabox( $this->taxonomy, $term );
|
71 |
+
$metabox->display();
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Queue assets for taxonomy screens.
|
76 |
+
*
|
77 |
+
* @since 1.5.0
|
78 |
+
*/
|
79 |
+
public function admin_enqueue_scripts() {
|
80 |
+
$pagenow = $GLOBALS['pagenow'];
|
81 |
+
|
82 |
+
if ( ! ( self::is_term_edit( $pagenow ) || self::is_term_overview( $pagenow ) ) ) {
|
83 |
+
return;
|
84 |
+
}
|
85 |
+
|
86 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
87 |
+
$asset_manager->enqueue_style( 'scoring' );
|
88 |
+
|
89 |
+
|
90 |
+
$tag_id = filter_input( INPUT_GET, 'tag_ID' );
|
91 |
+
if (
|
92 |
+
self::is_term_edit( $pagenow ) &&
|
93 |
+
! empty( $tag_id ) // After we drop support for <4.5 this can be removed.
|
94 |
+
) {
|
95 |
+
wp_enqueue_media(); // Enqueue files needed for upload functionality.
|
96 |
+
|
97 |
+
$asset_manager->enqueue_style( 'metabox-css' );
|
98 |
+
$asset_manager->enqueue_style( 'snippet' );
|
99 |
+
$asset_manager->enqueue_style( 'scoring' );
|
100 |
+
$asset_manager->enqueue_script( 'metabox' );
|
101 |
+
$asset_manager->enqueue_script( 'term-scraper' );
|
102 |
+
|
103 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'term-scraper', 'wpseoTermScraperL10n', $this->localize_term_scraper_script() );
|
104 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'replacevar-plugin', 'wpseoReplaceVarsL10n', $this->localize_replace_vars_script() );
|
105 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'metabox', 'wpseoSelect2Locale', WPSEO_Utils::get_language( WPSEO_Utils::get_user_locale() ) );
|
106 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'metabox', 'wpseoAdminL10n', WPSEO_Help_Center::get_translated_texts() );
|
107 |
+
|
108 |
+
$asset_manager->enqueue_script( 'admin-media' );
|
109 |
+
|
110 |
+
wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-media', 'wpseoMediaL10n', array(
|
111 |
+
'choose_image' => __( 'Use Image', 'wordpress-seo' ),
|
112 |
+
) );
|
113 |
+
}
|
114 |
+
|
115 |
+
if ( self::is_term_overview( $pagenow ) ) {
|
116 |
+
$asset_manager->enqueue_script( 'edit-page-script' );
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* Update the taxonomy meta data on save.
|
122 |
+
*
|
123 |
+
* @param int $term_id ID of the term to save data for.
|
124 |
+
* @param int $tt_id The taxonomy_term_id for the term.
|
125 |
+
* @param string $taxonomy The taxonomy the term belongs to.
|
126 |
+
*/
|
127 |
+
public function update_term( $term_id, $tt_id, $taxonomy ) {
|
128 |
+
/* Create post array with only our values. */
|
129 |
+
$new_meta_data = array();
|
130 |
+
foreach ( WPSEO_Taxonomy_Meta::$defaults_per_term as $key => $default ) {
|
131 |
+
$posted_value = filter_input( INPUT_POST, $key );
|
132 |
+
if ( isset( $posted_value ) && $posted_value !== false ) {
|
133 |
+
$new_meta_data[ $key ] = $posted_value;
|
134 |
+
}
|
135 |
+
|
136 |
+
// If analysis is disabled remove that analysis score value from the DB.
|
137 |
+
if ( $this->is_meta_value_disabled( $key ) ) {
|
138 |
+
$new_meta_data[ $key ] = '';
|
139 |
+
}
|
140 |
+
}
|
141 |
+
unset( $key, $default );
|
142 |
+
|
143 |
+
// Saving the values.
|
144 |
+
WPSEO_Taxonomy_Meta::set_values( $term_id, $taxonomy, $new_meta_data );
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Determines if the given meta value key is disabled.
|
149 |
+
*
|
150 |
+
* @param string $key The key of the meta value.
|
151 |
+
* @return bool Whether the given meta value key is disabled.
|
152 |
+
*/
|
153 |
+
public function is_meta_value_disabled( $key ) {
|
154 |
+
if ( 'wpseo_linkdex' === $key && ! $this->analysis_seo->is_enabled() ) {
|
155 |
+
return true;
|
156 |
+
}
|
157 |
+
|
158 |
+
if ( 'wpseo_content_score' === $key && ! $this->analysis_readability->is_enabled() ) {
|
159 |
+
return true;
|
160 |
+
}
|
161 |
+
|
162 |
+
return false;
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Allows HTML in descriptions.
|
167 |
+
*/
|
168 |
+
public function custom_category_descriptions_allow_html() {
|
169 |
+
$filters = array(
|
170 |
+
'pre_term_description',
|
171 |
+
'pre_link_description',
|
172 |
+
'pre_link_notes',
|
173 |
+
'pre_user_description',
|
174 |
+
);
|
175 |
+
|
176 |
+
foreach ( $filters as $filter ) {
|
177 |
+
remove_filter( $filter, 'wp_filter_kses' );
|
178 |
+
}
|
179 |
+
remove_filter( 'term_description', 'wp_kses_data' );
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Output the WordPress editor.
|
184 |
+
*/
|
185 |
+
public function custom_category_description_editor() {
|
186 |
+
wp_editor( '', 'description' );
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Adds shortcode support to category descriptions.
|
191 |
+
*
|
192 |
+
* @param string $desc String to add shortcodes in.
|
193 |
+
*
|
194 |
+
* @return string
|
195 |
+
*/
|
196 |
+
public function custom_category_descriptions_add_shortcode_support( $desc ) {
|
197 |
+
// Wrap in output buffering to prevent shortcodes that echo stuff instead of return from breaking things.
|
198 |
+
ob_start();
|
199 |
+
$desc = do_shortcode( $desc );
|
200 |
+
ob_end_clean();
|
201 |
+
|
202 |
+
return $desc;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Pass variables to js for use with the term-scraper.
|
207 |
+
*
|
208 |
+
* @return array
|
209 |
+
*/
|
210 |
+
public function localize_term_scraper_script() {
|
211 |
+
$term_id = filter_input( INPUT_GET, 'tag_ID' );
|
212 |
+
$term = get_term_by( 'id', $term_id, $this->get_taxonomy() );
|
213 |
+
$taxonomy = get_taxonomy( $term->taxonomy );
|
214 |
+
|
215 |
+
$term_formatter = new WPSEO_Metabox_Formatter(
|
216 |
+
new WPSEO_Term_Metabox_Formatter( $taxonomy, $term )
|
217 |
+
);
|
218 |
+
|
219 |
+
return $term_formatter->get_values();
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Pass some variables to js for replacing variables.
|
224 |
+
*/
|
225 |
+
public function localize_replace_vars_script() {
|
226 |
+
return array(
|
227 |
+
'no_parent_text' => __( '(no parent)', 'wordpress-seo' ),
|
228 |
+
'replace_vars' => $this->get_replace_vars(),
|
229 |
+
'scope' => $this->determine_scope(),
|
230 |
+
);
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Determines the scope based on the current taxonomy.
|
235 |
+
* This can be used by the replacevar plugin to determine if a replacement needs to be executed.
|
236 |
+
*
|
237 |
+
* @return string String decribing the current scope.
|
238 |
+
*/
|
239 |
+
private function determine_scope() {
|
240 |
+
$taxonomy = $this->get_taxonomy();
|
241 |
+
|
242 |
+
if ( $taxonomy === 'category' ) {
|
243 |
+
return 'category';
|
244 |
+
}
|
245 |
+
|
246 |
+
if ( $taxonomy === 'post_tag' ) {
|
247 |
+
return 'tag';
|
248 |
+
}
|
249 |
+
|
250 |
+
return 'term';
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* @param string $page The string to check for the term overview page.
|
255 |
+
*
|
256 |
+
* @return bool
|
257 |
+
*/
|
258 |
+
public static function is_term_overview( $page ) {
|
259 |
+
return 'edit-tags.php' === $page;
|
260 |
+
}
|
261 |
+
|
262 |
+
/**
|
263 |
+
* @param string $page The string to check for the term edit page.
|
264 |
+
*
|
265 |
+
* @return bool
|
266 |
+
*/
|
267 |
+
public static function is_term_edit( $page ) {
|
268 |
+
return 'term.php' === $page;
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* Retrieves a template.
|
273 |
+
* Check if metabox for current taxonomy should be displayed.
|
274 |
+
*
|
275 |
+
* @return bool
|
276 |
+
*/
|
277 |
+
private function show_metabox() {
|
278 |
+
$option_key = 'display-metabox-tax-' . $this->taxonomy;
|
279 |
+
|
280 |
+
return WPSEO_Options::get( $option_key );
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Getting the taxonomy from the URL.
|
285 |
+
*
|
286 |
+
* @return string
|
287 |
+
*/
|
288 |
+
private function get_taxonomy() {
|
289 |
+
return filter_input( INPUT_GET, 'taxonomy', FILTER_DEFAULT, array( 'options' => array( 'default' => '' ) ) );
|
290 |
+
}
|
291 |
+
|
292 |
+
/**
|
293 |
+
* Prepares the replace vars for localization.
|
294 |
+
*
|
295 |
+
* @return array replace vars.
|
296 |
+
*/
|
297 |
+
private function get_replace_vars() {
|
298 |
+
$term_id = filter_input( INPUT_GET, 'tag_ID' );
|
299 |
+
$term = get_term_by( 'id', $term_id, $this->get_taxonomy() );
|
300 |
+
|
301 |
+
$cached_replacement_vars = array();
|
302 |
+
|
303 |
+
$vars_to_cache = array(
|
304 |
+
'date',
|
305 |
+
'id',
|
306 |
+
'sitename',
|
307 |
+
'sitedesc',
|
308 |
+
'sep',
|
309 |
+
'page',
|
310 |
+
'currenttime',
|
311 |
+
'currentdate',
|
312 |
+
'currentday',
|
313 |
+
'currentmonth',
|
314 |
+
'currentyear',
|
315 |
+
'term_title',
|
316 |
+
'term_description',
|
317 |
+
'category_description',
|
318 |
+
'tag_description',
|
319 |
+
'searchphrase',
|
320 |
+
);
|
321 |
+
|
322 |
+
foreach ( $vars_to_cache as $var ) {
|
323 |
+
$cached_replacement_vars[ $var ] = wpseo_replace_vars( '%%' . $var . '%%', $term );
|
324 |
+
}
|
325 |
+
|
326 |
+
return $cached_replacement_vars;
|
327 |
+
}
|
328 |
+
|
329 |
+
/**
|
330 |
+
* Adds custom category description editor.
|
331 |
+
* Needs a hook that runs before the description field. Prior to WP version 4.5 we need to use edit_form as
|
332 |
+
* term_edit_form_top was introduced in WP 4.5. This can be removed after <4.5 is no longer supported.
|
333 |
+
*
|
334 |
+
* @return {void}
|
335 |
+
*/
|
336 |
+
private function insert_description_field_editor() {
|
337 |
+
if ( version_compare( $GLOBALS['wp_version'], '4.5', '<' ) ) {
|
338 |
+
add_action( "{$this->taxonomy}_edit_form", array( $this, 'custom_category_description_editor' ) );
|
339 |
+
return;
|
340 |
+
}
|
341 |
+
|
342 |
+
add_action( "{$this->taxonomy}_term_edit_form_top", array( $this, 'custom_category_description_editor' ) );
|
343 |
+
}
|
344 |
+
}
|
admin/tracking/class-tracking-default-data.php
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Tracking
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the default data.
|
8 |
+
*/
|
9 |
+
class WPSEO_Tracking_Default_Data implements WPSEO_Collection {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns the collection data.
|
13 |
+
*
|
14 |
+
* @return array The collection data.
|
15 |
+
*/
|
16 |
+
public function get() {
|
17 |
+
return array(
|
18 |
+
'siteTitle' => get_option( 'blogname' ),
|
19 |
+
'@timestamp' => (int) date( 'Uv' ),
|
20 |
+
'wpVersion' => $this->get_wordpress_version(),
|
21 |
+
'homeURL' => home_url(),
|
22 |
+
'adminURL' => admin_url(),
|
23 |
+
'isMultisite' => is_multisite(),
|
24 |
+
'siteLanguage' => get_bloginfo( 'language' ),
|
25 |
+
);
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Returns the WordPress version.
|
30 |
+
*
|
31 |
+
* @return string The version.
|
32 |
+
*/
|
33 |
+
protected function get_wordpress_version() {
|
34 |
+
global $wp_version;
|
35 |
+
|
36 |
+
return $wp_version;
|
37 |
+
}
|
38 |
+
}
|
admin/tracking/class-tracking-plugin-data.php
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Tracking
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the plugin data.
|
8 |
+
*/
|
9 |
+
class WPSEO_Tracking_Plugin_Data implements WPSEO_Collection {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns the collection data.
|
13 |
+
*
|
14 |
+
* @return array The collection data.
|
15 |
+
*/
|
16 |
+
public function get() {
|
17 |
+
return array(
|
18 |
+
'plugins' => $this->get_plugin_data(),
|
19 |
+
);
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Returns all plugins.
|
24 |
+
*
|
25 |
+
* @return array The formatted plugins.
|
26 |
+
*/
|
27 |
+
protected function get_plugin_data() {
|
28 |
+
|
29 |
+
if ( ! function_exists( 'get_plugin_data' ) ) {
|
30 |
+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
31 |
+
}
|
32 |
+
|
33 |
+
$plugins = wp_get_active_and_valid_plugins();
|
34 |
+
$plugins = array_map( 'get_plugin_data', $plugins );
|
35 |
+
$plugins = array_map( array( $this, 'format_plugin' ), $plugins );
|
36 |
+
|
37 |
+
return $plugins;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Formats the plugin array.
|
42 |
+
*
|
43 |
+
* @param array $plugin The plugin details.
|
44 |
+
*
|
45 |
+
* @return array The formatted array.
|
46 |
+
*/
|
47 |
+
protected function format_plugin( array $plugin ) {
|
48 |
+
return array(
|
49 |
+
'name' => $plugin['Name'],
|
50 |
+
'url' => $plugin['PluginURI'],
|
51 |
+
'version' => $plugin['Version'],
|
52 |
+
'author' => array(
|
53 |
+
'name' => wp_strip_all_tags( $plugin['Author'], true ),
|
54 |
+
'url' => $plugin['AuthorURI'],
|
55 |
+
),
|
56 |
+
);
|
57 |
+
}
|
58 |
+
}
|
admin/tracking/class-tracking-server-data.php
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Tracking
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the server data.
|
8 |
+
*/
|
9 |
+
class WPSEO_Tracking_Server_Data implements WPSEO_Collection {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns the collection data.
|
13 |
+
*
|
14 |
+
* @return array The collection data.
|
15 |
+
*/
|
16 |
+
public function get() {
|
17 |
+
return array(
|
18 |
+
'server' => $this->get_server_data(),
|
19 |
+
);
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Returns the values with server details.
|
24 |
+
*
|
25 |
+
* @return array Array with the value.
|
26 |
+
*/
|
27 |
+
protected function get_server_data() {
|
28 |
+
$server_data = array();
|
29 |
+
|
30 |
+
// Validate if the server address is a valid IP-address.
|
31 |
+
$ipaddress = filter_input( INPUT_SERVER, 'SERVER_ADDR', FILTER_VALIDATE_IP );
|
32 |
+
if ( $ipaddress ) {
|
33 |
+
$server_data['ip'] = $ipaddress;
|
34 |
+
$server_data['Hostname'] = gethostbyaddr( $ipaddress );
|
35 |
+
}
|
36 |
+
|
37 |
+
$server_data['os'] = php_uname( 's r' );
|
38 |
+
$server_data['PhpVersion'] = PHP_VERSION;
|
39 |
+
$server_data['CurlVersion'] = $this->get_curl_info();
|
40 |
+
$server_data['PhpExtensions'] = $this->get_php_extensions();
|
41 |
+
|
42 |
+
return $server_data;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Returns details about the curl version.
|
47 |
+
*
|
48 |
+
* @return array|null The curl info. Or null when curl isn't available..
|
49 |
+
*/
|
50 |
+
protected function get_curl_info() {
|
51 |
+
if ( ! function_exists( 'curl_version' ) ) {
|
52 |
+
return null;
|
53 |
+
}
|
54 |
+
|
55 |
+
$curl = curl_version();
|
56 |
+
|
57 |
+
$ssl_support = true;
|
58 |
+
if ( ! $curl['features'] && CURL_VERSION_SSL ) {
|
59 |
+
$ssl_support = false;
|
60 |
+
}
|
61 |
+
|
62 |
+
return array(
|
63 |
+
'version' => $curl['version'],
|
64 |
+
'sslSupport' => $ssl_support,
|
65 |
+
);
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Returns a list with php extensions.
|
70 |
+
*
|
71 |
+
* @return array Returns the state of the php extensions.
|
72 |
+
*/
|
73 |
+
protected function get_php_extensions() {
|
74 |
+
return array(
|
75 |
+
'imagick' => extension_loaded( 'imagick' ),
|
76 |
+
'filter' => extension_loaded( 'filter' ),
|
77 |
+
'bcmath' => extension_loaded( 'bcmath' ),
|
78 |
+
'modXml' => extension_loaded( 'modXml' ),
|
79 |
+
'pcre' => extension_loaded( 'pcre' ),
|
80 |
+
'xml' => extension_loaded( 'xml' ),
|
81 |
+
);
|
82 |
+
}
|
83 |
+
}
|
admin/tracking/class-tracking-theme-data.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Tracking
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Represents the theme data.
|
8 |
+
*/
|
9 |
+
class WPSEO_Tracking_Theme_Data implements WPSEO_Collection {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Returns the collection data.
|
13 |
+
*
|
14 |
+
* @return array The collection data.
|
15 |
+
*/
|
16 |
+
public function get() {
|
17 |
+
$theme = wp_get_theme();
|
18 |
+
|
19 |
+
return array(
|
20 |
+
'theme' => array(
|
21 |
+
'name' => $theme->get( 'Name' ),
|
22 |
+
'url' => $theme->get( 'ThemeURI' ),
|
23 |
+
'version' => $theme->get( 'Version' ),
|
24 |
+
'author' => array(
|
25 |
+
'name' => $theme->get( 'Author' ),
|
26 |
+
'url' => $theme->get( 'AuthorURI' ),
|
27 |
+
),
|
28 |
+
'parentTheme' => $this->get_parent_theme( $theme ),
|
29 |
+
),
|
30 |
+
);
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Returns the name of the parent theme.
|
35 |
+
*
|
36 |
+
* @param WP_Theme $theme The theme object.
|
37 |
+
*
|
38 |
+
* @return null|string The name of the parent theme or null.
|
39 |
+
*/
|
40 |
+
private function get_parent_theme( WP_Theme $theme ) {
|
41 |
+
if ( is_child_theme() ) {
|
42 |
+
return $theme->get( 'Template' );
|
43 |
+
}
|
44 |
+
|
45 |
+
return null;
|
46 |
+
}
|
47 |
+
}
|
admin/tracking/class-tracking.php
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Tracking
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* This class handles the tracking routine.
|
8 |
+
*/
|
9 |
+
class WPSEO_Tracking {
|
10 |
+
|
11 |
+
/** @var string */
|
12 |
+
protected $option_name = 'wpseo_tracking_last_request';
|
13 |
+
|
14 |
+
/** @var int */
|
15 |
+
protected $threshold = 0;
|
16 |
+
|
17 |
+
/** @var string */
|
18 |
+
protected $endpoint = '';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Constructor setting the treshhold..
|
22 |
+
*
|
23 |
+
* @param string $endpoint The endpoint to send the data to.
|
24 |
+
* @param int $threshold The limit for the option.
|
25 |
+
*/
|
26 |
+
public function __construct( $endpoint, $threshold ) {
|
27 |
+
$this->endpoint = $endpoint;
|
28 |
+
$this->threshold = $threshold;
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Registers all hooks to WordPress
|
33 |
+
*/
|
34 |
+
public function send() {
|
35 |
+
|
36 |
+
$current_time = time();
|
37 |
+
if ( ! $this->should_send_tracking( $current_time ) ) {
|
38 |
+
return;
|
39 |
+
}
|
40 |
+
|
41 |
+
$collector = $this->get_collector();
|
42 |
+
|
43 |
+
$request = new WPSEO_Remote_Request( $this->endpoint );
|
44 |
+
$request->set_body( $collector->get_as_json() );
|
45 |
+
$request->send();
|
46 |
+
|
47 |
+
update_option( $this->option_name, $current_time, 'yes' );
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Returns true when last tracking data was send more than two weeks ago.
|
52 |
+
*
|
53 |
+
* @param int $current_time The current timestamp.
|
54 |
+
*
|
55 |
+
* @return bool True when tracking data should be send.
|
56 |
+
*/
|
57 |
+
protected function should_send_tracking( $current_time ) {
|
58 |
+
$last_time = get_option( $this->option_name );
|
59 |
+
|
60 |
+
// When there is no data being set.
|
61 |
+
if ( ! $last_time ) {
|
62 |
+
return true;
|
63 |
+
}
|
64 |
+
|
65 |
+
return $this->exceeds_treshhold( $current_time - $last_time );
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Checks if the given amount of seconds exceeds the set threshold.
|
70 |
+
*
|
71 |
+
* @param int $seconds The amount of seconds to check.
|
72 |
+
*
|
73 |
+
* @return bool True when seconds is bigger than threshold.
|
74 |
+
*/
|
75 |
+
protected function exceeds_treshhold( $seconds ) {
|
76 |
+
return ( $seconds > $this->threshold );
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Returns the collector for collecting the data.
|
81 |
+
*
|
82 |
+
* @return WPSEO_Collector The instance of the collector.
|
83 |
+
*/
|
84 |
+
protected function get_collector() {
|
85 |
+
$collector = new WPSEO_Collector();
|
86 |
+
$collector->add_collection( new WPSEO_Tracking_Default_Data() );
|
87 |
+
$collector->add_collection( new WPSEO_Tracking_Server_Data() );
|
88 |
+
$collector->add_collection( new WPSEO_Tracking_Theme_Data() );
|
89 |
+
$collector->add_collection( new WPSEO_Tracking_Plugin_Data() );
|
90 |
+
|
91 |
+
return $collector;
|
92 |
+
}
|
93 |
+
}
|
admin/views/class-view-utils.php
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Yoast_View_Utils
|
8 |
+
*/
|
9 |
+
class Yoast_View_Utils {
|
10 |
+
/** @var Yoast_Form Form to use. */
|
11 |
+
protected $form;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Yoast_View_Utils constructor.
|
15 |
+
*/
|
16 |
+
public function __construct() {
|
17 |
+
$this->form = Yoast_Form::get_instance();
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Shows the search results help question mark and help section.
|
22 |
+
*
|
23 |
+
* Used for all the Help sections for indexable objects like post types, taxonomies, or archives.
|
24 |
+
*
|
25 |
+
* @param string|object $post_type The post type to show the search results help for.
|
26 |
+
* @param string $help_text_switch Switch the help text to one that's more appropriate
|
27 |
+
* for the indexable object type the help section is for.
|
28 |
+
*
|
29 |
+
* @return object The help panel instance.
|
30 |
+
*/
|
31 |
+
public function search_results_setting_help( $post_type, $help_text_switch = '' ) {
|
32 |
+
if ( ! is_object( $post_type ) ) {
|
33 |
+
$post_type = get_post_type_object( $post_type );
|
34 |
+
}
|
35 |
+
|
36 |
+
/* translators: 1: expands to an indexable object's name, like a post type or taxonomy; 2: expands to <code>noindex</code>; 3: link open tag; 4: link close tag. */
|
37 |
+
$help_text = esc_html__( 'Not showing %1$s in the search results technically means those will have a %2$s robots meta and will be excluded from XML sitemaps. %3$sMore info on the search results settings%4$s.', 'wordpress-seo' );
|
38 |
+
|
39 |
+
if ( $help_text_switch === 'archive' ) {
|
40 |
+
/* translators: 1: expands to an indexable object's name, like a post type or taxonomy; 2: expands to <code>noindex</code>; 3: link open tag; 4: link close tag. */
|
41 |
+
$help_text = esc_html__( 'Not showing the archive for %1$s in the search results technically means those will have a %2$s robots meta and will be excluded from XML sitemaps. %3$sMore info on the search results settings%4$s.', 'wordpress-seo' );
|
42 |
+
}
|
43 |
+
|
44 |
+
$help_panel = new WPSEO_Admin_Help_Panel(
|
45 |
+
// Sometimes the same post type is used more than once in the same page, we need a unique ID though.
|
46 |
+
uniqid( 'noindex-' . $post_type->name ),
|
47 |
+
esc_html__( 'Help on this search results setting', 'wordpress-seo' ),
|
48 |
+
sprintf(
|
49 |
+
$help_text,
|
50 |
+
$post_type->labels->name,
|
51 |
+
'<code>noindex</code>',
|
52 |
+
'<a href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/show-x' ) ) . '" target="_blank" rel="noopener noreferrer">',
|
53 |
+
'</a>'
|
54 |
+
)
|
55 |
+
);
|
56 |
+
|
57 |
+
return $help_panel;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Shows the search appearance settings for a post type.
|
62 |
+
*
|
63 |
+
* @param string|object $post_type The post type to show the search appearance settings for.
|
64 |
+
*
|
65 |
+
* @return void
|
66 |
+
*/
|
67 |
+
public function show_post_type_settings( $post_type ) {
|
68 |
+
if ( ! is_object( $post_type ) ) {
|
69 |
+
$post_type = get_post_type_object( $post_type );
|
70 |
+
}
|
71 |
+
|
72 |
+
$show_post_type_help = $this->search_results_setting_help( $post_type );
|
73 |
+
|
74 |
+
$this->form->index_switch(
|
75 |
+
'noindex-' . $post_type->name,
|
76 |
+
$post_type->labels->name,
|
77 |
+
$show_post_type_help->get_button_html() . $show_post_type_help->get_panel_html()
|
78 |
+
);
|
79 |
+
|
80 |
+
$this->form->textinput(
|
81 |
+
'title-' . $post_type->name,
|
82 |
+
__( 'Title template', 'wordpress-seo' ),
|
83 |
+
'template posttype-template'
|
84 |
+
);
|
85 |
+
|
86 |
+
$this->form->textarea(
|
87 |
+
'metadesc-' . $post_type->name,
|
88 |
+
__( 'Meta description template', 'wordpress-seo' ),
|
89 |
+
array( 'class' => 'template posttype-template' )
|
90 |
+
);
|
91 |
+
|
92 |
+
$this->form->show_hide_switch(
|
93 |
+
'showdate-' . $post_type->name,
|
94 |
+
__( 'Date in Snippet Preview', 'wordpress-seo' )
|
95 |
+
);
|
96 |
+
|
97 |
+
$this->form->show_hide_switch(
|
98 |
+
'display-metabox-pt-' . $post_type->name,
|
99 |
+
/* translators: %1$s expands to Yoast SEO */
|
100 |
+
sprintf( __( '%1$s Meta Box', 'wordpress-seo' ), 'Yoast SEO' )
|
101 |
+
);
|
102 |
+
}
|
103 |
+
}
|
admin/views/class-yoast-form-fieldset.php
ADDED
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Generate the HTML for a form fieldset to wrap grouped form elements.
|
8 |
+
*/
|
9 |
+
class Yoast_Form_Fieldset implements Yoast_Form_Element {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string The fieldset ID.
|
13 |
+
*/
|
14 |
+
private $id;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var array The fieldset HTML default attributes.
|
18 |
+
*/
|
19 |
+
private $attributes = array(
|
20 |
+
'class' => 'yoast-form-fieldset',
|
21 |
+
);
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var string The grouped form elements for the fieldset.
|
25 |
+
*/
|
26 |
+
private $content;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var array The fieldset legend HTML default attributes.
|
30 |
+
*/
|
31 |
+
private $legend_attributes = array(
|
32 |
+
'class' => 'yoast-form-legend',
|
33 |
+
);
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var string A translatable string for the fieldset legend content.
|
37 |
+
*/
|
38 |
+
private $legend_content;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Constructor.
|
42 |
+
*
|
43 |
+
* @param string $id ID for the fieldset.
|
44 |
+
* @param string $legend_content The translatable legend text.
|
45 |
+
* @param string $content The grouped form elements for the fieldset.
|
46 |
+
*/
|
47 |
+
public function __construct( $id, $legend_content, $content ) {
|
48 |
+
$this->id = $id;
|
49 |
+
$this->legend_content = $legend_content;
|
50 |
+
$this->content = $content;
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Render the view.
|
55 |
+
*/
|
56 |
+
public function render_view() {
|
57 |
+
/*
|
58 |
+
* Extract because we want values accessible via variables for later use
|
59 |
+
* in the view instead of accessing them as an array.
|
60 |
+
*/
|
61 |
+
extract( $this->get_parts() );
|
62 |
+
|
63 |
+
require WPSEO_PATH . 'admin/views/form/fieldset.php';
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Start output buffering to catch the form elements to wrap in the fieldset.
|
68 |
+
*/
|
69 |
+
public function start() {
|
70 |
+
ob_start();
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Return output buffering with the form elements to wrap in the fieldset.
|
75 |
+
*/
|
76 |
+
public function end() {
|
77 |
+
$this->content = ob_get_clean();
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Return the rendered view.
|
82 |
+
*
|
83 |
+
* @return string
|
84 |
+
*/
|
85 |
+
public function get_html() {
|
86 |
+
ob_start();
|
87 |
+
$this->render_view();
|
88 |
+
return ob_get_clean();
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Output the rendered view.
|
93 |
+
*/
|
94 |
+
public function html() {
|
95 |
+
echo $this->get_html();
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Add attributes to the fieldset default attributes.
|
100 |
+
*
|
101 |
+
* @param array $attributes Array of attributes names and values to merge with the defaults.
|
102 |
+
*/
|
103 |
+
public function add_attributes( $attributes ) {
|
104 |
+
$this->attributes = wp_parse_args( $attributes, $this->attributes );
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Add attributes to the fieldset legend default attributes.
|
109 |
+
*
|
110 |
+
* @param array $attributes Array of attributes names and values to merge with the defaults.
|
111 |
+
*/
|
112 |
+
public function legend_add_attributes( $attributes ) {
|
113 |
+
$this->legend_attributes = wp_parse_args( $attributes, $this->legend_attributes );
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Visually hide the fieldset legend but keep it available to assistive technologies.
|
118 |
+
*/
|
119 |
+
public function legend_hide() {
|
120 |
+
$this->legend_attributes = wp_parse_args(
|
121 |
+
array( 'class' => 'screen-reader-text' ),
|
122 |
+
$this->legend_attributes
|
123 |
+
);
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Return the set of attributes and content for the fieldset.
|
128 |
+
*
|
129 |
+
* @return array
|
130 |
+
*/
|
131 |
+
private function get_parts() {
|
132 |
+
return array(
|
133 |
+
'id' => $this->id,
|
134 |
+
'attributes' => $this->get_attributes_html( $this->attributes ),
|
135 |
+
'legend_content' => $this->legend_content,
|
136 |
+
'legend_attributes' => $this->get_attributes_html( $this->legend_attributes ),
|
137 |
+
'content' => $this->content,
|
138 |
+
);
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Return HTML formatted attributes as a string, when there are attributes set.
|
143 |
+
*
|
144 |
+
* @param array $attributes Fieldset or legend attributes.
|
145 |
+
*
|
146 |
+
* @return string A space separated list of HTML formatted attributes or empty string.
|
147 |
+
*/
|
148 |
+
private function get_attributes_html( $attributes ) {
|
149 |
+
if ( ! empty( $attributes ) ) {
|
150 |
+
array_walk( $attributes, array( $this, 'parse_attribute' ) );
|
151 |
+
|
152 |
+
// Use an initial space as `implode()` adds a space only between array elements.
|
153 |
+
return ' ' . implode( ' ', $attributes );
|
154 |
+
}
|
155 |
+
|
156 |
+
return '';
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Escape and format an attribute as an HTML attribute.
|
161 |
+
*
|
162 |
+
* @param string $value The value of the attribute.
|
163 |
+
* @param string $attribute The attribute to look for.
|
164 |
+
*/
|
165 |
+
private function parse_attribute( & $value, $attribute ) {
|
166 |
+
$value = sprintf( '%s="%s"', esc_html( $attribute ), esc_attr( $value ) );
|
167 |
+
}
|
168 |
+
}
|
admin/views/class-yoast-input-select.php
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class for generating a html select.
|
8 |
+
*/
|
9 |
+
class Yoast_Input_Select {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string
|
13 |
+
*/
|
14 |
+
private $select_id;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
private $select_name;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var array
|
23 |
+
*/
|
24 |
+
private $select_attributes = array();
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var array Array with the options to parse.
|
28 |
+
*/
|
29 |
+
private $select_options;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @var string The current selected option.
|
33 |
+
*/
|
34 |
+
private $selected_option;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Constructor.
|
38 |
+
*
|
39 |
+
* @param string $select_id ID for the select.
|
40 |
+
* @param string $select_name Name for the select.
|
41 |
+
* @param array $select_options Array with the options to parse.
|
42 |
+
* @param string $selected_option The current selected option.
|
43 |
+
*/
|
44 |
+
public function __construct( $select_id, $select_name, array $select_options, $selected_option ) {
|
45 |
+
$this->select_id = $select_id;
|
46 |
+
$this->select_name = $select_name;
|
47 |
+
$this->select_options = $select_options;
|
48 |
+
$this->selected_option = $selected_option;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Print the rendered view.
|
53 |
+
*/
|
54 |
+
public function output_html() {
|
55 |
+
// Extract it, because we want each value accessible via a variable instead of accessing it as an array.
|
56 |
+
extract( $this->get_select_values() );
|
57 |
+
|
58 |
+
require WPSEO_PATH . 'admin/views/form/select.php';
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Return the rendered view
|
63 |
+
*
|
64 |
+
* @return string
|
65 |
+
*/
|
66 |
+
public function get_html() {
|
67 |
+
ob_start();
|
68 |
+
|
69 |
+
$this->output_html();
|
70 |
+
|
71 |
+
$rendered_output = ob_get_contents();
|
72 |
+
ob_end_clean();
|
73 |
+
|
74 |
+
return $rendered_output;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Add an attribute to the attributes property
|
79 |
+
*
|
80 |
+
* @param string $attribute The name of the attribute to add.
|
81 |
+
* @param string $value The value of the attribute.
|
82 |
+
*/
|
83 |
+
public function add_attribute( $attribute, $value ) {
|
84 |
+
$this->select_attributes[ $attribute ] = $value;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Return the set fields for the select
|
89 |
+
*
|
90 |
+
* @return array
|
91 |
+
*/
|
92 |
+
private function get_select_values() {
|
93 |
+
return array(
|
94 |
+
'id' => $this->select_id,
|
95 |
+
'name' => $this->select_name,
|
96 |
+
'attributes' => $this->get_attributes(),
|
97 |
+
'options' => $this->select_options,
|
98 |
+
'selected' => $this->selected_option,
|
99 |
+
);
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Return the attribute string, when there are attributes set.
|
104 |
+
*
|
105 |
+
* @return string
|
106 |
+
*/
|
107 |
+
private function get_attributes() {
|
108 |
+
$attributes = $this->select_attributes;
|
109 |
+
|
110 |
+
if ( ! empty( $attributes ) ) {
|
111 |
+
array_walk( $attributes, array( $this, 'parse_attribute' ) );
|
112 |
+
|
113 |
+
return implode( ' ', $attributes ) . ' ';
|
114 |
+
}
|
115 |
+
|
116 |
+
return '';
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Get an attribute from the attributes.
|
121 |
+
*
|
122 |
+
* @param string $value The value of the attribute.
|
123 |
+
* @param string $attribute The attribute to look for.
|
124 |
+
*/
|
125 |
+
private function parse_attribute( & $value, $attribute ) {
|
126 |
+
$value = sprintf( '%s="%s"', esc_html( $attribute ), esc_attr( $value ) );
|
127 |
+
}
|
128 |
+
}
|
admin/views/form/fieldset.php
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
7 |
+
header( 'Status: 403 Forbidden' );
|
8 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
9 |
+
exit();
|
10 |
+
}
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @var string $id ID attribute for the fieldset.
|
14 |
+
* @var string $attributes Additional attributes for the fieldset.
|
15 |
+
* @var string $legend_attributes Additional attributes for the legend.
|
16 |
+
* @var string $legend_content The legend text.
|
17 |
+
* @var string $content The fieldset content, i.e. a set of logically grouped form controls.
|
18 |
+
*/
|
19 |
+
?>
|
20 |
+
|
21 |
+
<fieldset id="<?php echo esc_attr( $id ); ?>"<?php echo $attributes; ?>>
|
22 |
+
<legend id="<?php echo esc_attr( $id . '-legend' ); ?>"<?php echo $legend_attributes; ?>><?php echo esc_html( $legend_content ); ?></legend>
|
23 |
+
<?php echo $content; ?>
|
24 |
+
</fieldset>
|
admin/views/form/select.php
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
7 |
+
header( 'Status: 403 Forbidden' );
|
8 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
9 |
+
exit();
|
10 |
+
}
|
11 |
+
|
12 |
+
/**
|
13 |
+
* @var string $attributes Additional attributes for the select
|
14 |
+
* @var string $name Value for the select name attribute.
|
15 |
+
* @var string $id ID attribute for the select.
|
16 |
+
* @var array $options Array with the options to show.
|
17 |
+
* @var string $selected The current set options.
|
18 |
+
*/
|
19 |
+
?>
|
20 |
+
<select <?php echo $attributes; ?>name="<?php echo esc_attr( $name ); ?>" id="<?php echo esc_attr( $id ); ?>">
|
21 |
+
<?php foreach ( $options as $option_attribute_value => $option_html_value ) : ?>
|
22 |
+
<option value="<?php echo esc_attr( $option_attribute_value ); ?>"<?php echo selected( $selected, $option_attribute_value, false ); ?>><?php echo esc_html( $option_html_value ); ?></option>
|
23 |
+
<?php endforeach; ?>
|
24 |
+
</select>
|
admin/views/interface-yoast-form-element.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Generate the HTML for a form element.
|
8 |
+
*/
|
9 |
+
interface Yoast_Form_Element {
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Return the HTML for the form element.
|
13 |
+
*
|
14 |
+
* @return string
|
15 |
+
*/
|
16 |
+
public function get_html();
|
17 |
+
}
|
admin/views/js-templates-primary-term.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
7 |
+
header( 'Status: 403 Forbidden' );
|
8 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
9 |
+
exit();
|
10 |
+
}
|
11 |
+
?>
|
12 |
+
|
13 |
+
<script type="text/html" id="tmpl-primary-term-input">
|
14 |
+
<input type="hidden" class="yoast-wpseo-primary-term"
|
15 |
+
id="yoast-wpseo-primary-{{data.taxonomy.name}}"
|
16 |
+
name="<?php echo esc_attr( WPSEO_Meta::$form_prefix ); ?>primary_{{data.taxonomy.name}}_term"
|
17 |
+
value="{{data.taxonomy.primary}}">
|
18 |
+
|
19 |
+
<?php wp_nonce_field( 'save-primary-term', WPSEO_Meta::$form_prefix . 'primary_{{data.taxonomy.name}}_nonce' ); ?>
|
20 |
+
</script>
|
21 |
+
|
22 |
+
<script type="text/html" id="tmpl-primary-term-ui">
|
23 |
+
<?php
|
24 |
+
printf(
|
25 |
+
'<button type="button" class="wpseo-make-primary-term" aria-label="%1$s">%2$s</button>',
|
26 |
+
esc_attr( sprintf(
|
27 |
+
/* translators: accessibility text. %1$s expands to the term title, %2$s to the taxonomy title. */
|
28 |
+
__( 'Make %1$s primary %2$s', 'wordpress-seo' ),
|
29 |
+
'{{data.term}}',
|
30 |
+
'{{data.taxonomy.title}}'
|
31 |
+
) ),
|
32 |
+
esc_html__( 'Make primary', 'wordpress-seo' )
|
33 |
+
);
|
34 |
+
?>
|
35 |
+
|
36 |
+
<span class="wpseo-is-primary-term" aria-hidden="true"><?php esc_html_e( 'Primary', 'wordpress-seo' ); ?></span>
|
37 |
+
</script>
|
38 |
+
|
39 |
+
<script type="text/html" id="tmpl-primary-term-screen-reader">
|
40 |
+
<span class="screen-reader-text wpseo-primary-category-label"><?php
|
41 |
+
printf(
|
42 |
+
/* translators: %s is the taxonomy title. This will be shown to screenreaders */
|
43 |
+
'(' . esc_html__( 'Primary %s', 'wordpress-seo' ) . ')',
|
44 |
+
'{{data.taxonomy.title}}'
|
45 |
+
);
|
46 |
+
?></span>
|
47 |
+
</script>
|
admin/views/licenses.php
ADDED
@@ -0,0 +1,271 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
* @since 5.1
|
5 |
+
*/
|
6 |
+
|
7 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
8 |
+
header( 'Status: 403 Forbidden' );
|
9 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
10 |
+
exit();
|
11 |
+
}
|
12 |
+
|
13 |
+
$extension_list = new WPSEO_Extensions();
|
14 |
+
$extensions = $extension_list->get();
|
15 |
+
|
16 |
+
// First invalidate all licenses.
|
17 |
+
array_map( array( $extension_list, 'invalidate' ), $extensions );
|
18 |
+
|
19 |
+
$extensions = new WPSEO_Extension_Manager();
|
20 |
+
|
21 |
+
$extensions->add(
|
22 |
+
'wordpress-seo-premium',
|
23 |
+
new WPSEO_Extension(
|
24 |
+
array(
|
25 |
+
'url' => WPSEO_Shortlinker::get( 'https://yoa.st/pe-premium-page' ),
|
26 |
+
'title' => 'Yoast SEO Premium',
|
27 |
+
/* translators: %1$s expands to Yoast SEO */
|
28 |
+
'desc' => sprintf( __( 'The premium version of %1$s with more features & support.', 'wordpress-seo' ), 'Yoast SEO' ),
|
29 |
+
'image' => plugins_url( 'images/extensions-premium-ribbon.png?v=' . WPSEO_VERSION, WPSEO_FILE ),
|
30 |
+
'benefits' => array(),
|
31 |
+
)
|
32 |
+
)
|
33 |
+
);
|
34 |
+
|
35 |
+
$extensions->add(
|
36 |
+
'wpseo-video',
|
37 |
+
new WPSEO_Extension(
|
38 |
+
array(
|
39 |
+
'buyUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zx/' ),
|
40 |
+
'infoUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zw/' ),
|
41 |
+
'title' => 'Video SEO',
|
42 |
+
'desc' => __( 'Optimize your videos to show them off in search results and get more clicks!', 'wordpress-seo' ),
|
43 |
+
'image' => plugins_url( 'images/extensions-video.png?v=' . WPSEO_VERSION, WPSEO_FILE ),
|
44 |
+
'benefits' => array(
|
45 |
+
__( 'Show your videos in Google Videos', 'wordpress-seo' ),
|
46 |
+
__( 'Enhance the experience of sharing posts with videos', 'wordpress-seo' ),
|
47 |
+
__( 'Make videos responsive through enabling fitvids.js', 'wordpress-seo' ),
|
48 |
+
),
|
49 |
+
)
|
50 |
+
)
|
51 |
+
);
|
52 |
+
|
53 |
+
$extensions->add(
|
54 |
+
'wpseo-news',
|
55 |
+
new WPSEO_Extension(
|
56 |
+
array(
|
57 |
+
'buyUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zv/' ),
|
58 |
+
'infoUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zu/' ),
|
59 |
+
'title' => 'News SEO',
|
60 |
+
'desc' => __( 'Are you in Google News? Increase your traffic from Google News by optimizing for it!', 'wordpress-seo' ),
|
61 |
+
'image' => plugins_url( 'images/extensions-news.png?v=' . WPSEO_VERSION, WPSEO_FILE ),
|
62 |
+
'benefits' => array(
|
63 |
+
__( 'Optimize your site for Google News', 'wordpress-seo' ),
|
64 |
+
__( 'Immediately pings Google on the publication of a new post', 'wordpress-seo' ),
|
65 |
+
__( 'Creates XML News Sitemaps', 'wordpress-seo' ),
|
66 |
+
),
|
67 |
+
)
|
68 |
+
)
|
69 |
+
);
|
70 |
+
|
71 |
+
if ( ! defined( 'WPSEO_LOCAL_WOOCOMMERCE_VERSION' ) ) {
|
72 |
+
$extensions->add(
|
73 |
+
'wpseo-local',
|
74 |
+
new WPSEO_Extension(
|
75 |
+
array(
|
76 |
+
'buyUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zt' ),
|
77 |
+
'infoUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zs' ),
|
78 |
+
'title' => 'Local SEO',
|
79 |
+
'desc' => __( 'Rank better locally and in Google Maps, without breaking a sweat!', 'wordpress-seo' ),
|
80 |
+
'image' => plugins_url( 'images/extensions-local.png?v=' . WPSEO_VERSION, WPSEO_FILE ),
|
81 |
+
'benefits' => array(
|
82 |
+
__( 'Get found by potential clients', 'wordpress-seo' ),
|
83 |
+
__( 'Easily insert Google Maps, a store locator, opening hours and more', 'wordpress-seo' ),
|
84 |
+
__( 'Improve the usability of your contact page', 'wordpress-seo' ),
|
85 |
+
),
|
86 |
+
)
|
87 |
+
)
|
88 |
+
);
|
89 |
+
}
|
90 |
+
|
91 |
+
$extensions->add(
|
92 |
+
'wpseo-local-woocommerce',
|
93 |
+
new WPSEO_Extension(
|
94 |
+
array(
|
95 |
+
'buyUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/272' ),
|
96 |
+
'infoUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/273' ),
|
97 |
+
'title' => 'Local SEO for WooCommerce',
|
98 |
+
'desc' => __( 'Rank better locally and in Google Maps, without breaking a sweat!', 'wordpress-seo' ),
|
99 |
+
'image' => plugins_url( 'images/extensions-local-for-woocommerce.png?v=' . WPSEO_VERSION, WPSEO_FILE ),
|
100 |
+
'benefits' => array(
|
101 |
+
esc_html__( 'Get better search results in local search', 'wordpress-seo' ),
|
102 |
+
esc_html__( 'Enhance your contact pages with Google Maps, opening hours and a store locator', 'wordpress-seo' ),
|
103 |
+
/* translators: %1$s expands to WooCommerce */
|
104 |
+
sprintf( esc_html__( 'Allow customers to pick up their %s order locally', 'wordpress-seo' ), 'WooCommerce' ),
|
105 |
+
),
|
106 |
+
)
|
107 |
+
)
|
108 |
+
);
|
109 |
+
|
110 |
+
// Add Yoast WooCommerce SEO when WooCommerce is active.
|
111 |
+
if ( class_exists( 'Woocommerce' ) ) {
|
112 |
+
$extensions->add(
|
113 |
+
'wpseo-woocommerce',
|
114 |
+
new WPSEO_Extension(
|
115 |
+
array(
|
116 |
+
'buyUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zr' ),
|
117 |
+
'infoUrl' => WPSEO_Shortlinker::get( 'https://yoa.st/zq' ),
|
118 |
+
'title' => 'Yoast WooCommerce SEO',
|
119 |
+
/* translators: %1$s expands to Yoast SEO */
|
120 |
+
'desc' => sprintf( __( 'Seamlessly integrate WooCommerce with %1$s and get extra features!', 'wordpress-seo' ), 'Yoast SEO' ),
|
121 |
+
'image' => plugins_url( 'images/extensions-woo.png?v=' . WPSEO_VERSION, WPSEO_FILE ),
|
122 |
+
'benefits' => array(
|
123 |
+
sprintf( __( 'Improve sharing on Pinterest', 'wordpress-seo' ) ),
|
124 |
+
|
125 |
+
/* translators: %1$s expands to Yoast, %2$s expands to WooCommerce */
|
126 |
+
sprintf( __( 'Use %1$s breadcrumbs instead of %2$s ones', 'wordpress-seo' ), 'Yoast', 'WooCommerce' ),
|
127 |
+
|
128 |
+
/* translators: %1$s expands to Yoast SEO, %2$s expands to WooCommerce */
|
129 |
+
sprintf( __( 'A seamless integration between %1$s and %2$s', 'wordpress-seo' ), 'Yoast SEO', 'WooCommerce' ),
|
130 |
+
),
|
131 |
+
'buy_button' => 'WooCommerce SEO',
|
132 |
+
)
|
133 |
+
)
|
134 |
+
);
|
135 |
+
}
|
136 |
+
|
137 |
+
/* translators: %1$s expands to Yoast SEO. */
|
138 |
+
$wpseo_extensions_header = sprintf( __( '%1$s Extensions', 'wordpress-seo' ), 'Yoast SEO' );
|
139 |
+
|
140 |
+
?>
|
141 |
+
|
142 |
+
<div class="wrap yoast wpseo_table_page">
|
143 |
+
|
144 |
+
<h1 id="wpseo-title" class="yoast-h1"><?php echo esc_html( $wpseo_extensions_header ); ?></h1>
|
145 |
+
|
146 |
+
<div id="extensions">
|
147 |
+
<section class="yoast-seo-premium-extension">
|
148 |
+
<?php
|
149 |
+
$extension = $extensions->get( 'wordpress-seo-premium' );
|
150 |
+
$extensions->remove( 'wordpress-seo-premium' );
|
151 |
+
?>
|
152 |
+
<h2>
|
153 |
+
<?php
|
154 |
+
printf(
|
155 |
+
/* translators: %1$s expands to Yoast SEO Premium */
|
156 |
+
esc_html__( '%1$s, take your optimization to the next level!', 'wordpress-seo' ),
|
157 |
+
'<span class="yoast-heading-highlight">' . $extension->get_title() . '</span>'
|
158 |
+
);
|
159 |
+
?>
|
160 |
+
</h2>
|
161 |
+
|
162 |
+
<ul class="yoast-seo-premium-benefits yoast-list--usp">
|
163 |
+
<li class="yoast-seo-premium-benefits__item">
|
164 |
+
<span class="yoast-seo-premium-benefits__title"><?php esc_html_e( 'Redirect manager', 'wordpress-seo' ); ?></span>
|
165 |
+
<span class="yoast-seo-premium-benefits__description"><?php esc_html_e( 'create and manage redirects from within your WordPress install.', 'wordpress-seo' ); ?></span>
|
166 |
+
</li>
|
167 |
+
<li class="yoast-seo-premium-benefits__item">
|
168 |
+
<span class="yoast-seo-premium-benefits__title"><?php esc_html_e( 'Multiple focus keywords', 'wordpress-seo' ); ?></span>
|
169 |
+
<span class="yoast-seo-premium-benefits__description"><?php esc_html_e( 'optimize a single post for up to 5 keywords.', 'wordpress-seo' ); ?></span>
|
170 |
+
</li>
|
171 |
+
<li class="yoast-seo-premium-benefits__item">
|
172 |
+
<span class="yoast-seo-premium-benefits__title"><?php esc_html_e( 'Social previews', 'wordpress-seo' ); ?></span>
|
173 |
+
<span class="yoast-seo-premium-benefits__description"><?php esc_html_e( 'check what your Facebook or Twitter post will look like.', 'wordpress-seo' ); ?></span>
|
174 |
+
</li>
|
175 |
+
<li class="yoast-seo-premium-benefits__item">
|
176 |
+
<span class="yoast-seo-premium-benefits__title"><?php esc_html_e( 'Premium support', 'wordpress-seo' ); ?></span>
|
177 |
+
<span class="yoast-seo-premium-benefits__description"><?php esc_html_e( 'gain access to our 24/7 support team.', 'wordpress-seo' ); ?></span>
|
178 |
+
</li>
|
179 |
+
</ul>
|
180 |
+
|
181 |
+
<?php if ( $extension_list->is_installed( $extension->get_title() ) ) : ?>
|
182 |
+
<div class="yoast-button yoast-button--noarrow yoast-button--extension yoast-button--extension-installed"><?php esc_html_e( 'Installed', 'wordpress-seo' ); ?></div>
|
183 |
+
|
184 |
+
<?php if ( $extensions->is_activated( 'wordpress-seo-premium' ) ) : ?>
|
185 |
+
<div class="yoast-button yoast-button--noarrow yoast-button--extension yoast-button--extension-activated"><?php esc_html_e( 'Activated', 'wordpress-seo' ); ?></div>
|
186 |
+
<a target="_blank" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/13k' ); ?>" class="yoast-link--license"><?php esc_html_e( 'Manage your subscription on My Yoast', 'wordpress-seo' ); ?></a>
|
187 |
+
<?php else : ?>
|
188 |
+
<div class="yoast-button yoast-button--noarrow yoast-button--extension yoast-button--extension-not-activated"><?php esc_html_e( 'Not activated', 'wordpress-seo' ); ?></div>
|
189 |
+
<a target="_blank" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/13i' ); ?>" class="yoast-link--license"><?php esc_html_e( 'Activate your site on My Yoast', 'wordpress-seo' ); ?></a>
|
190 |
+
<?php endif; ?>
|
191 |
+
</a>
|
192 |
+
|
193 |
+
<?php else : ?>
|
194 |
+
|
195 |
+
<a target="_blank" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/zz' ); ?>" class="yoast-button yoast-button--noarrow yoast-button-go-to yoast-button--extension yoast-button--extension-buy">
|
196 |
+
<?php
|
197 |
+
/* translators: $1$s expands to Yoast SEO Premium */
|
198 |
+
printf( __( 'Buy %1$s', 'wordpress-seo' ), $extension->get_title() );
|
199 |
+
?></a>
|
200 |
+
|
201 |
+
<a target="_blank" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/zy' ); ?>" class="yoast-link--more-info"><?php
|
202 |
+
printf(
|
203 |
+
/* translators: Text between %1$s and %2$s will only be shown to screen readers. %3$s expands to the product name. */
|
204 |
+
__( 'More information %1$sabout %3$s%2$s', 'wordpress-seo' ),
|
205 |
+
'<span class="screen-reader-text">',
|
206 |
+
'</span>',
|
207 |
+
$extension->get_title() );
|
208 |
+
?></a>
|
209 |
+
<?php endif; ?>
|
210 |
+
|
211 |
+
<p><small class="yoast-money-back-guarantee"><?php esc_html_e( 'Comes with our 30-day no questions asked money back guarantee', 'wordpress-seo' ); ?></small></p>
|
212 |
+
</section>
|
213 |
+
|
214 |
+
<hr class="yoast-hr" aria-hidden="true" />
|
215 |
+
|
216 |
+
<section class="yoast-promo-extensions">
|
217 |
+
<h2><?php
|
218 |
+
/* translators: %1$s expands to Yoast SEO */
|
219 |
+
$yoast_seo_extensions = sprintf( __( '%1$s extensions', 'wordpress-seo' ), 'Yoast SEO' );
|
220 |
+
|
221 |
+
$yoast_seo_extensions = '<span class="yoast-heading-highlight">' . $yoast_seo_extensions . '</span>';
|
222 |
+
|
223 |
+
/* translators: %1$s expands to Yoast SEO extensions */
|
224 |
+
printf( __( '%1$s to optimize your site even further', 'wordpress-seo' ), $yoast_seo_extensions );
|
225 |
+
?></h2>
|
226 |
+
|
227 |
+
<?php foreach ( $extensions->get_all() as $id => $extension ) : ?>
|
228 |
+
<section class="yoast-promoblock secondary yoast-promo-extension">
|
229 |
+
<img alt="" width="280" height="147" src="<?php echo esc_attr( $extension->get_image() ); ?>" />
|
230 |
+
<h3><?php echo esc_html( $extension->get_title() ); ?></h3>
|
231 |
+
|
232 |
+
<ul class="yoast-list--usp">
|
233 |
+
<?php foreach ( $extension->get_benefits() as $benefit ) : ?>
|
234 |
+
<li><?php echo esc_html( $benefit ); ?></li>
|
235 |
+
<?php endforeach; ?>
|
236 |
+
</ul>
|
237 |
+
|
238 |
+
<div class="yoast-button-container">
|
239 |
+
<?php if ( $extension_list->is_installed( $extension->get_title() ) ) : ?>
|
240 |
+
<div class="yoast-button yoast-button--noarrow yoast-button--extension yoast-button--extension-installed"><?php esc_html_e( 'Installed', 'wordpress-seo' ); ?></div>
|
241 |
+
|
242 |
+
<?php if ( $extensions->is_activated( $id ) ) : ?>
|
243 |
+
<div class="yoast-button yoast-button--noarrow yoast-button--extension yoast-button--extension-activated"><?php esc_html_e( 'Activated', 'wordpress-seo' ); ?></div>
|
244 |
+
<a target="_blank" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/13k' ); ?>" class="yoast-link--license"><?php esc_html_e( 'Manage your subscription on My Yoast', 'wordpress-seo' ); ?></a>
|
245 |
+
<?php else : ?>
|
246 |
+
<div class="yoast-button yoast-button--noarrow yoast-button--extension yoast-button--extension-not-activated"><?php esc_html_e( 'Not activated', 'wordpress-seo' ); ?></div>
|
247 |
+
<a target="_blank" href="<?php WPSEO_Shortlinker::show( 'https://yoa.st/13i' ); ?>" class="yoast-link--license"><?php esc_html_e( 'Activate your site on My Yoast', 'wordpress-seo' ); ?></a>
|
248 |
+
<?php endif; ?>
|
249 |
+
<?php else : ?>
|
250 |
+
<a target="_blank" class="yoast-button yoast-button--noarrow yoast-button-go-to yoast-button--extension yoast-button--extension-buy" href="<?php echo esc_url( $extension->get_buy_url() ); ?>">
|
251 |
+
<?php /* translators: %s expands to the product name */ ?>
|
252 |
+
<?php printf( __( 'Buy %s', 'wordpress-seo' ), $extension->get_buy_button() ); ?>
|
253 |
+
</a>
|
254 |
+
|
255 |
+
<a target="_blank" class="yoast-link--more-info" href="<?php echo esc_url( $extension->get_info_url() ); ?>"><?php
|
256 |
+
printf(
|
257 |
+
/* translators: Text between %1$s and %2$s will only be shown to screen readers. %3$s expands to the product name. */
|
258 |
+
__( 'More information %1$sabout %3$s%2$s', 'wordpress-seo' ),
|
259 |
+
'<span class="screen-reader-text">',
|
260 |
+
'</span>',
|
261 |
+
$extension->get_title() );
|
262 |
+
?>
|
263 |
+
</a>
|
264 |
+
<?php endif; ?>
|
265 |
+
</div>
|
266 |
+
</section>
|
267 |
+
<?php endforeach; ?>
|
268 |
+
</section>
|
269 |
+
</div>
|
270 |
+
|
271 |
+
</div>
|
admin/views/partial-alerts-errors.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
$type = 'alerts';
|
7 |
+
$dashicon = 'warning';
|
8 |
+
|
9 |
+
$i18n_title = __( 'Problems', 'wordpress-seo' );
|
10 |
+
$i18n_issues = __( 'We have detected the following issues that affect the SEO of your site.', 'wordpress-seo' );
|
11 |
+
$i18n_no_issues = __( 'Good job! We could detect no serious SEO problems.', 'wordpress-seo' );
|
12 |
+
$i18n_muted_issues_title = __( 'Muted problems:', 'wordpress-seo' );
|
13 |
+
|
14 |
+
$active_total = count( $alerts_data['errors']['active'] );
|
15 |
+
$total = $alerts_data['metrics']['errors'];
|
16 |
+
|
17 |
+
$active = $alerts_data['errors']['active'];
|
18 |
+
$dismissed = $alerts_data['errors']['dismissed'];
|
19 |
+
|
20 |
+
require WPSEO_PATH . 'admin/views/partial-alerts-template.php';
|
admin/views/partial-alerts-template.php
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! function_exists( '_yoast_display_alerts' ) ) {
|
7 |
+
/**
|
8 |
+
* Create the alert HTML with restore/dismiss button
|
9 |
+
*
|
10 |
+
* @param array $list List of alerts.
|
11 |
+
* @param string $status Status of the alerts (active/dismissed).
|
12 |
+
*/
|
13 |
+
function _yoast_display_alerts( $list, $status ) {
|
14 |
+
foreach ( $list as $notification ) {
|
15 |
+
|
16 |
+
switch ( $status ) {
|
17 |
+
case 'active':
|
18 |
+
$button = sprintf( '<button type="button" class="button dismiss"><span class="screen-reader-text">%1$s</span><span class="dashicons dashicons-no-alt"></span></button>', esc_html__( 'Dismiss this item.', 'wordpress-seo' ) );
|
19 |
+
break;
|
20 |
+
|
21 |
+
case 'dismissed':
|
22 |
+
$button = sprintf( '<button type="button" class="button restore"><span class="screen-reader-text">%1$s</span><span class="dashicons dashicons-hidden"></span></button>', esc_html__( 'Restore this item.', 'wordpress-seo' ) );
|
23 |
+
break;
|
24 |
+
}
|
25 |
+
|
26 |
+
printf(
|
27 |
+
'<div class="yoast-alert-holder" id="%1$s" data-nonce="%2$s" data-json="%3$s">%4$s%5$s</div>',
|
28 |
+
esc_attr( $notification->get_id() ),
|
29 |
+
esc_attr( $notification->get_nonce() ),
|
30 |
+
esc_attr( $notification->get_json() ),
|
31 |
+
$notification,
|
32 |
+
$button
|
33 |
+
);
|
34 |
+
}
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
$wpseo_i18n_summary = $i18n_issues;
|
39 |
+
if ( ! $active ) {
|
40 |
+
$dashicon = 'yes';
|
41 |
+
$wpseo_i18n_summary = $i18n_no_issues;
|
42 |
+
}
|
43 |
+
|
44 |
+
?>
|
45 |
+
<h3><span class="dashicons <?php echo esc_attr( 'dashicons-' . $dashicon ); ?>"></span> <?php echo esc_html( $i18n_title ); ?> (<?php echo (int) $active_total; ?>)</h3>
|
46 |
+
|
47 |
+
<div id="<?php echo esc_attr( 'yoast-' . $type ); ?>">
|
48 |
+
|
49 |
+
<?php if ( $total ) : ?>
|
50 |
+
<p><?php echo esc_html( $wpseo_i18n_summary ); ?></p>
|
51 |
+
|
52 |
+
<div class="container" id="<?php echo esc_attr( 'yoast-' . $type . '-active' ); ?>">
|
53 |
+
<?php _yoast_display_alerts( $active, 'active' ); ?>
|
54 |
+
</div>
|
55 |
+
|
56 |
+
<?php if ( $dismissed ) : ?>
|
57 |
+
<h4 class="yoast-muted-title"><?php echo esc_html( $i18n_muted_issues_title ); ?></h4>
|
58 |
+
<?php endif; ?>
|
59 |
+
|
60 |
+
<div class="container" id="<?php echo esc_attr( 'yoast-' . $type . '-dismissed' ); ?>">
|
61 |
+
<?php _yoast_display_alerts( $dismissed, 'dismissed' ); ?>
|
62 |
+
</div>
|
63 |
+
|
64 |
+
<?php else : ?>
|
65 |
+
|
66 |
+
<p><?php echo esc_html( $i18n_no_issues ); ?></p>
|
67 |
+
|
68 |
+
<?php endif; ?>
|
69 |
+
</div>
|
admin/views/partial-alerts-warnings.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
$type = 'warnings';
|
7 |
+
$dashicon = 'flag';
|
8 |
+
|
9 |
+
$i18n_title = __( 'Notifications', 'wordpress-seo' );
|
10 |
+
$i18n_issues = '';
|
11 |
+
$i18n_no_issues = __( 'No new notifications.', 'wordpress-seo' );
|
12 |
+
$i18n_muted_issues_title = __( 'Muted notifications:', 'wordpress-seo' );
|
13 |
+
|
14 |
+
$active_total = count( $alerts_data['warnings']['active'] );
|
15 |
+
$total = $alerts_data['metrics']['warnings'];
|
16 |
+
|
17 |
+
$active = $alerts_data['warnings']['active'];
|
18 |
+
$dismissed = $alerts_data['warnings']['dismissed'];
|
19 |
+
|
20 |
+
require WPSEO_PATH . 'admin/views/partial-alerts-template.php';
|
admin/views/tabs/dashboard/dashboard.php
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/** @noinspection PhpUnusedLocalVariableInspection */
|
7 |
+
$alerts_data = Yoast_Alerts::get_template_variables();
|
8 |
+
|
9 |
+
$notifier = new WPSEO_Configuration_Notifier();
|
10 |
+
$notifier->listen();
|
11 |
+
|
12 |
+
$wpseo_contributors_phrase = sprintf(
|
13 |
+
/* translators: %1$s expands to Yoast SEO */
|
14 |
+
__( 'See who contributed to %1$s.', 'wordpress-seo' ),
|
15 |
+
'Yoast SEO'
|
16 |
+
);
|
17 |
+
|
18 |
+
?>
|
19 |
+
|
20 |
+
<div class="tab-block">
|
21 |
+
<div class="yoast-alerts">
|
22 |
+
|
23 |
+
<?php echo $notifier->notify(); ?>
|
24 |
+
|
25 |
+
<div class="yoast-container yoast-container__alert">
|
26 |
+
<?php require WPSEO_PATH . 'admin/views/partial-alerts-errors.php'; ?>
|
27 |
+
</div>
|
28 |
+
|
29 |
+
<div class="yoast-container yoast-container__warning">
|
30 |
+
<?php require WPSEO_PATH . 'admin/views/partial-alerts-warnings.php'; ?>
|
31 |
+
</div>
|
32 |
+
|
33 |
+
</div>
|
34 |
+
</div>
|
35 |
+
|
36 |
+
<div class="tab-block">
|
37 |
+
<h3><?php esc_html_e( 'Credits', 'wordpress-seo' ); ?></h3>
|
38 |
+
<p>
|
39 |
+
<span class="dashicons dashicons-groups"></span>
|
40 |
+
<a href="<?php WPSEO_Shortlinker::show( 'http://yoa.st/yoast-seo-credits' ); ?>"><?php echo esc_html( $wpseo_contributors_phrase ); ?></a>
|
41 |
+
</p>
|
42 |
+
</div>
|
43 |
+
|
44 |
+
<?php
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Action: 'wpseo_internal_linking' - Hook to add the internal linking analyze interface to the interface.
|
48 |
+
*
|
49 |
+
* @deprecated 7.0
|
50 |
+
*/
|
51 |
+
do_action_deprecated( 'wpseo_internal_linking', array(), 'WPSEO 7.0' );
|
admin/views/tabs/dashboard/features.php
ADDED
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
$xml_sitemap_extra = false;
|
17 |
+
if ( WPSEO_Options::get( 'enable_xml_sitemap' ) ) {
|
18 |
+
$xml_sitemap_extra = '<a href="' . WPSEO_Sitemaps_Router::get_base_url( 'sitemap_index.xml' ) . '" target="_blank">' . __( 'See the XML sitemap.', 'wordpress-seo' ) . '</a>';
|
19 |
+
}
|
20 |
+
$feature_toggles = array(
|
21 |
+
(object) array(
|
22 |
+
'name' => __( 'SEO analysis', 'wordpress-seo' ),
|
23 |
+
'setting' => 'keyword_analysis_active',
|
24 |
+
'label' => __( 'The SEO analysis offers suggestions to improve the SEO of your text.', 'wordpress-seo' ),
|
25 |
+
'read_more_label' => __( 'Learn how the SEO analysis can help you rank.', 'wordpress-seo' ),
|
26 |
+
'read_more_url' => 'https://yoa.st/2ak',
|
27 |
+
'order' => 10,
|
28 |
+
),
|
29 |
+
(object) array(
|
30 |
+
'name' => __( 'Readability analysis', 'wordpress-seo' ),
|
31 |
+
'setting' => 'content_analysis_active',
|
32 |
+
'label' => __( 'The readability analysis offers suggestions to improve the structure and style of your text.', 'wordpress-seo' ),
|
33 |
+
'read_more_label' => __( 'Discover why readability is important for SEO.', 'wordpress-seo' ),
|
34 |
+
'read_more_url' => 'https://yoa.st/2ao',
|
35 |
+
'order' => 20,
|
36 |
+
),
|
37 |
+
(object) array(
|
38 |
+
'name' => __( 'Cornerstone content', 'wordpress-seo' ),
|
39 |
+
'setting' => 'enable_cornerstone_content',
|
40 |
+
'label' => __( 'The cornerstone content feature lets you to mark and filter cornerstone content on your website.', 'wordpress-seo' ),
|
41 |
+
'read_more_label' => __( 'Find out how cornerstone content can help you improve your site structure.', 'wordpress-seo' ),
|
42 |
+
'read_more_url' => 'https://yoa.st/dashboard-help-cornerstone',
|
43 |
+
'order' => 30,
|
44 |
+
),
|
45 |
+
(object) array(
|
46 |
+
'name' => __( 'Text link counter', 'wordpress-seo' ),
|
47 |
+
'setting' => 'enable_text_link_counter',
|
48 |
+
'label' => __( 'The text link counter helps you improve your site structure.', 'wordpress-seo' ),
|
49 |
+
'read_more_label' => __( 'Find out how the text link counter can enhance your SEO.', 'wordpress-seo' ),
|
50 |
+
'read_more_url' => 'https://yoa.st/2aj',
|
51 |
+
'order' => 40,
|
52 |
+
),
|
53 |
+
(object) array(
|
54 |
+
'name' => __( 'XML Sitemaps', 'wordpress-seo' ),
|
55 |
+
'setting' => 'enable_xml_sitemap',
|
56 |
+
/* translators: %s expands to Yoast SEO */
|
57 |
+
'label' => sprintf( __( 'Enable the XML sitemaps that %s generates.', 'wordpress-seo' ), 'Yoast SEO' ),
|
58 |
+
'read_more_label' => __( 'Read why XML Sitemaps are important for your site.', 'wordpress-seo' ),
|
59 |
+
'read_more_url' => 'https://yoa.st/2a-',
|
60 |
+
'extra' => $xml_sitemap_extra,
|
61 |
+
'order' => 60,
|
62 |
+
),
|
63 |
+
(object) array(
|
64 |
+
/* translators: %s expands to Ryte. */
|
65 |
+
'name' => sprintf( __( '%s integration', 'wordpress-seo' ), 'Ryte' ),
|
66 |
+
'setting' => 'onpage_indexability',
|
67 |
+
/* translators: %1$s expands to Ryte. */
|
68 |
+
'label' => sprintf( __( '%1$s can check daily if your site is still indexable by search engines and will notify you when this is not the case.', 'wordpress-seo' ), 'Ryte' ),
|
69 |
+
/* translators: %s expands to Ryte. */
|
70 |
+
'read_more_label' => sprintf( __( 'Read more about how %s works.', 'wordpress-seo' ), 'Ryte ' ),
|
71 |
+
'read_more_url' => 'https://yoa.st/2an',
|
72 |
+
'order' => 70,
|
73 |
+
),
|
74 |
+
(object) array(
|
75 |
+
'name' => __( 'Admin bar menu', 'wordpress-seo' ),
|
76 |
+
'setting' => 'enable_admin_bar_menu',
|
77 |
+
/* translators: %1$s expands to Yoast SEO */
|
78 |
+
'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' ),
|
79 |
+
'order' => 80,
|
80 |
+
),
|
81 |
+
(object) array(
|
82 |
+
'name' => __( 'Security: no advanced settings for authors', 'wordpress-seo' ),
|
83 |
+
'setting' => 'disableadvanced_meta',
|
84 |
+
'label' => sprintf(
|
85 |
+
/* translators: %1$s expands to Yoast SEO, %2$s expands to the translated version of "Off" */
|
86 |
+
__( 'The advanced section of the %1$s meta box allows a user to remove posts from the search results or change the canonical. 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' ),
|
87 |
+
'Yoast SEO',
|
88 |
+
__( 'Off', 'wordpress-seo' )
|
89 |
+
),
|
90 |
+
'order' => 90,
|
91 |
+
),
|
92 |
+
);
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Filter to add feature toggles from add-ons.
|
96 |
+
*
|
97 |
+
* @param array $feature_toggles Array with feature toggle objects where each object should have a `name`, `setting` and `label` property.
|
98 |
+
*/
|
99 |
+
$feature_toggles = apply_filters( 'wpseo_feature_toggles', $feature_toggles );
|
100 |
+
|
101 |
+
?>
|
102 |
+
<h2><?php esc_html_e( 'Features', 'wordpress-seo' ); ?></h2>
|
103 |
+
<div style="max-width:600px">
|
104 |
+
<?php echo esc_html( sprintf(
|
105 |
+
/* translators: %1$s expands to Yoast SEO */
|
106 |
+
__( '%1$s comes with a lot of features. You can enable / disable some of them below. Clicking the question mark gives more information about the feature.', 'wordpress-seo' ),
|
107 |
+
'Yoast SEO'
|
108 |
+
) ) ?>
|
109 |
+
<?php
|
110 |
+
|
111 |
+
/**
|
112 |
+
* Simple sorting function used for usort straight below.
|
113 |
+
*
|
114 |
+
* @param object $feature_a Feature A.
|
115 |
+
* @param object $feature_b Feature B.
|
116 |
+
*
|
117 |
+
* @return bool Whether order for feature A is bigger than for feature B.
|
118 |
+
*/
|
119 |
+
function wpseo_cmp_order( $feature_a, $feature_b ) {
|
120 |
+
return ( $feature_a->order > $feature_b->order );
|
121 |
+
}
|
122 |
+
|
123 |
+
usort( $feature_toggles, 'wpseo_cmp_order' );
|
124 |
+
|
125 |
+
foreach ( $feature_toggles as $feature ) {
|
126 |
+
$help_text = esc_html( $feature->label );
|
127 |
+
if ( ! empty( $feature->extra ) ) {
|
128 |
+
$help_text .= ' ' . $feature->extra;
|
129 |
+
}
|
130 |
+
if ( ! empty( $feature->read_more_label ) ) {
|
131 |
+
$help_text .= ' ' . sprintf( '<a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a>', WPSEO_Shortlinker::get( $feature->read_more_url ), esc_html( $feature->read_more_label ) );
|
132 |
+
}
|
133 |
+
|
134 |
+
$feature_help = new WPSEO_Admin_Help_Panel(
|
135 |
+
$feature->setting,
|
136 |
+
/* translators: %s expands to a feature's name */
|
137 |
+
sprintf( __( 'Help on: %s', 'wordpress-seo' ), $feature->name ),
|
138 |
+
$help_text
|
139 |
+
);
|
140 |
+
|
141 |
+
$yform->toggle_switch(
|
142 |
+
$feature->setting,
|
143 |
+
array(
|
144 |
+
'on' => __( 'On', 'wordpress-seo' ),
|
145 |
+
'off' => __( 'Off', 'wordpress-seo' ),
|
146 |
+
),
|
147 |
+
'<strong>' . $feature->name . $feature_help->get_button_html() . '</strong>' . $feature_help->get_panel_html()
|
148 |
+
);
|
149 |
+
}
|
150 |
+
?>
|
151 |
+
</div>
|
152 |
+
<?php
|
153 |
+
// Required to prevent our settings framework from saving the default because the field isn't explicitly set when saving the Dashboard page.
|
154 |
+
$yform->hidden( 'show_onboarding_notice', 'wpseo_show_onboarding_notice' );
|
admin/views/tabs/dashboard/site-analysis.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Fires when displaying the site wide analysis tab.
|
18 |
+
*
|
19 |
+
* @param Yoast_Form $yform The yoast form object.
|
20 |
+
*/
|
21 |
+
do_action( 'wpseo_settings_tab_site_analysis', $yform );
|
admin/views/tabs/dashboard/webmaster-tools.php
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
$webmaster_tools_help = new WPSEO_Admin_Help_Panel(
|
17 |
+
'dashboard-webmaster-tools',
|
18 |
+
esc_html__( 'Learn more about the Webmaster Tools verification', 'wordpress-seo' ),
|
19 |
+
esc_html__( 'You can use the boxes below to verify with the different Webmaster Tools. This feature will add a verification meta tag on your home page. Follow the links to the different Webmaster Tools and look for instructions for the meta tag verification method to get the verification code. If your site is already verified, you can just forget about these.', 'wordpress-seo' ),
|
20 |
+
'has-wrapper'
|
21 |
+
);
|
22 |
+
|
23 |
+
echo '<h2 class="help-button-inline">' . esc_html__( 'Webmaster Tools verification', 'wordpress-seo' ) . $webmaster_tools_help->get_button_html() . '</h2>';
|
24 |
+
echo $webmaster_tools_help->get_panel_html();
|
25 |
+
|
26 |
+
$msverify_link = 'https://www.bing.com/toolbox/webmaster/#/Dashboard/?url=' .
|
27 |
+
rawurlencode( str_replace( 'http://', '', get_bloginfo( 'url' ) ) );
|
28 |
+
|
29 |
+
$googleverify_link = add_query_arg(
|
30 |
+
array(
|
31 |
+
'hl' => 'en',
|
32 |
+
'tid' => 'alternate',
|
33 |
+
'siteUrl' => rawurlencode( get_bloginfo( 'url' ) ) . '/',
|
34 |
+
),
|
35 |
+
'https://www.google.com/webmasters/verification/verification'
|
36 |
+
);
|
37 |
+
|
38 |
+
|
39 |
+
$yform->textinput( 'msverify', __( 'Bing verification code', 'wordpress-seo' ) );
|
40 |
+
echo '<p class="desc label">';
|
41 |
+
printf(
|
42 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
43 |
+
esc_html__( 'Get your Bing verification code in %1$sBing Webmaster Tools%2$s.', 'wordpress-seo' ),
|
44 |
+
'<a target="_blank" href="' . esc_url( $msverify_link ) . '" rel="noopener noreferrer">',
|
45 |
+
'</a>'
|
46 |
+
);
|
47 |
+
echo '</p>';
|
48 |
+
|
49 |
+
$yform->textinput( 'googleverify', __( 'Google verification code', 'wordpress-seo' ) );
|
50 |
+
echo '<p class="desc label">';
|
51 |
+
printf(
|
52 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
53 |
+
esc_html__( 'Get your Google verification code in %1$sGoogle Search Console%2$s.', 'wordpress-seo' ),
|
54 |
+
'<a target="_blank" href="' . esc_url( $googleverify_link ) . '" rel="noopener noreferrer">',
|
55 |
+
'</a>'
|
56 |
+
);
|
57 |
+
echo '</p>';
|
58 |
+
|
59 |
+
$yform->textinput( 'yandexverify', __( 'Yandex verification code', 'wordpress-seo' ) );
|
60 |
+
echo '<p class="desc label">';
|
61 |
+
printf(
|
62 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
63 |
+
esc_html__( 'Get your Yandex verification code in %1$sYandex Webmaster Tools%2$s.', 'wordpress-seo' ),
|
64 |
+
'<a target="_blank" href="' . esc_url( 'https://webmaster.yandex.com/sites/add/' ) . '" rel="noopener noreferrer">',
|
65 |
+
'</a>'
|
66 |
+
);
|
67 |
+
echo '</p>';
|
admin/views/tabs/metas/archives.php
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
$archives_help_01 = sprintf(
|
17 |
+
/* translators: %1$s / %2$s: links to an article about duplicate content on yoast.com */
|
18 |
+
esc_html__( 'If you\'re running a one author blog, the author archive will be exactly the same as your homepage. This is what\'s called a %1$sduplicate content problem%2$s.', 'wordpress-seo' ),
|
19 |
+
'<a href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/duplicate-content' ) ) . '">',
|
20 |
+
'</a>'
|
21 |
+
);
|
22 |
+
|
23 |
+
$archives_help_02 = sprintf(
|
24 |
+
/* translators: %s expands to <code>noindex, follow</code> */
|
25 |
+
esc_html__( 'If this is the case on your site, you can choose to either disable it (which makes it redirect to the homepage), or to add %s to it so it doesn\'t show up in the search results.', 'wordpress-seo' ),
|
26 |
+
'<code>noindex,follow</code>'
|
27 |
+
);
|
28 |
+
|
29 |
+
$archives_help_03 = esc_html__( 'Note that links to archives might be still output by your theme and you would need to remove them separately.', 'wordpress-seo' );
|
30 |
+
|
31 |
+
$archives_help_04 = esc_html__( 'Date-based archives could in some cases also be seen as duplicate content.', 'wordpress-seo' );
|
32 |
+
|
33 |
+
$archives_help = new WPSEO_Admin_Help_Panel(
|
34 |
+
'search-appearance-archives',
|
35 |
+
__( 'Learn more about the archives setting', 'wordpress-seo' ),
|
36 |
+
$archives_help_01 . ' ' . $archives_help_02 . ' ' . $archives_help_03 . ' ' . $archives_help_04,
|
37 |
+
'has-wrapper'
|
38 |
+
);
|
39 |
+
|
40 |
+
echo '<p class="help-button-inline"><strong>' . esc_html__( 'Archives settings help', 'wordpress-seo' ) . $archives_help->get_button_html() . '</strong><p>';
|
41 |
+
echo $archives_help->get_panel_html();
|
42 |
+
|
43 |
+
echo '<div class="tab-block" id="author-archives-titles-metas">';
|
44 |
+
echo '<h2>' . esc_html__( 'Author archives settings', 'wordpress-seo' ) . '</h2>';
|
45 |
+
$yform->toggle_switch( 'disable-author', array(
|
46 |
+
'off' => __( 'Enabled', 'wordpress-seo' ),
|
47 |
+
'on' => __( 'Disabled', 'wordpress-seo' ),
|
48 |
+
), __( 'Author archives', 'wordpress-seo' ) );
|
49 |
+
|
50 |
+
echo "<div id='author-archives-titles-metas-content' class='archives-titles-metas-content'>";
|
51 |
+
|
52 |
+
$author_archives_help = new WPSEO_Admin_Help_Panel(
|
53 |
+
'noindex-author-wpseo',
|
54 |
+
esc_html__( 'Help on the author archives search results setting', 'wordpress-seo' ),
|
55 |
+
sprintf(
|
56 |
+
/* translators: 1: expands to <code>noindex</code>; 2: link open tag; 3: link close tag. */
|
57 |
+
esc_html__( 'Not showing the archive for authors in the search results technically means those will have a %1$s robots meta and will be excluded from XML sitemaps. %2$sMore info on the search results settings%3$s.', 'wordpress-seo' ),
|
58 |
+
'<code>noindex</code>',
|
59 |
+
'<a href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/show-x' ) ) . '" target="_blank" rel="noopener noreferrer">',
|
60 |
+
'</a>'
|
61 |
+
)
|
62 |
+
);
|
63 |
+
|
64 |
+
$yform->index_switch(
|
65 |
+
'noindex-author-wpseo',
|
66 |
+
__( 'author archives', 'wordpress-seo' ),
|
67 |
+
$author_archives_help->get_button_html() . $author_archives_help->get_panel_html()
|
68 |
+
);
|
69 |
+
|
70 |
+
$author_archives_no_posts_help = new WPSEO_Admin_Help_Panel(
|
71 |
+
'noindex-author-noposts-wpseo',
|
72 |
+
esc_html__( 'Help on the authors without posts archive search results setting', 'wordpress-seo' ),
|
73 |
+
sprintf(
|
74 |
+
/* translators: 1: expands to <code>noindex</code>; 2: link open tag; 3: link close tag. */
|
75 |
+
esc_html__( 'Not showing the archives for authors without posts in the search results technically means those will have a %1$s robots meta and will be excluded from XML sitemaps. %2$sMore info on the search results settings%3$s.', 'wordpress-seo' ),
|
76 |
+
'<code>noindex</code>',
|
77 |
+
'<a href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/show-x' ) ) . '" target="_blank" rel="noopener noreferrer">',
|
78 |
+
'</a>'
|
79 |
+
)
|
80 |
+
);
|
81 |
+
|
82 |
+
$yform->index_switch(
|
83 |
+
'noindex-author-noposts-wpseo',
|
84 |
+
__( 'archives for authors without posts', 'wordpress-seo' ),
|
85 |
+
$author_archives_no_posts_help->get_button_html() . $author_archives_no_posts_help->get_panel_html()
|
86 |
+
);
|
87 |
+
|
88 |
+
$yform->textinput( 'title-author-wpseo', __( 'Title template', 'wordpress-seo' ), 'template author-template' );
|
89 |
+
$yform->textarea( 'metadesc-author-wpseo', __( 'Meta description template', 'wordpress-seo' ), array( 'class' => 'template author-template' ) );
|
90 |
+
echo '</div>';
|
91 |
+
echo '</div>';
|
92 |
+
|
93 |
+
echo '<div class="tab-block" id="date-archives-titles-metas">';
|
94 |
+
echo '<h2>' . esc_html__( 'Date archives settings', 'wordpress-seo' ) . '</h2>';
|
95 |
+
$yform->toggle_switch( 'disable-date', array(
|
96 |
+
'off' => __( 'Enabled', 'wordpress-seo' ),
|
97 |
+
'on' => __( 'Disabled', 'wordpress-seo' ),
|
98 |
+
), __( 'Date archives', 'wordpress-seo' ) );
|
99 |
+
|
100 |
+
echo "<div id='date-archives-titles-metas-content' class='archives-titles-metas-content'>";
|
101 |
+
|
102 |
+
$date_archives_help = new WPSEO_Admin_Help_Panel(
|
103 |
+
'noindex-archive-wpseo',
|
104 |
+
esc_html__( 'Help on the date archives search results setting', 'wordpress-seo' ),
|
105 |
+
sprintf(
|
106 |
+
/* translators: 1: expands to <code>noindex</code>; 2: link open tag; 3: link close tag. */
|
107 |
+
esc_html__( 'Not showing the date archives in the search results technically means those will have a %1$s robots meta and will be excluded from XML sitemaps. %2$sMore info on the search results settings%3$s.', 'wordpress-seo' ),
|
108 |
+
'<code>noindex</code>',
|
109 |
+
'<a href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/show-x' ) ) . '" target="_blank" rel="noopener noreferrer">',
|
110 |
+
'</a>'
|
111 |
+
)
|
112 |
+
);
|
113 |
+
|
114 |
+
$yform->index_switch(
|
115 |
+
'noindex-archive-wpseo',
|
116 |
+
__( 'date archives', 'wordpress-seo' ),
|
117 |
+
$date_archives_help->get_button_html() . $date_archives_help->get_panel_html()
|
118 |
+
);
|
119 |
+
|
120 |
+
$yform->textinput( 'title-archive-wpseo', __( 'Title template', 'wordpress-seo' ), 'template date-template' );
|
121 |
+
$yform->textarea( 'metadesc-archive-wpseo', __( 'Meta description template', 'wordpress-seo' ), array( 'class' => 'template date-template' ) );
|
122 |
+
echo '</div>';
|
123 |
+
echo '</div>';
|
124 |
+
|
125 |
+
$spcia_pages_help = new WPSEO_Admin_Help_Panel(
|
126 |
+
'search-appearance-special-pages',
|
127 |
+
__( 'Learn more about the special pages setting', 'wordpress-seo' ),
|
128 |
+
sprintf(
|
129 |
+
/* translators: %s expands to <code>noindex, follow</code>. */
|
130 |
+
__( 'These pages will be %s by default, so they will never show up in search results.', 'wordpress-seo' ),
|
131 |
+
'<code>noindex, follow</code>'
|
132 |
+
),
|
133 |
+
'has-wrapper'
|
134 |
+
);
|
135 |
+
|
136 |
+
echo '<div class="tab-block" id="special-pages-titles-metas">';
|
137 |
+
echo '<h2 class="help-button-inline">' . esc_html__( 'Special Pages', 'wordpress-seo' ) . $spcia_pages_help->get_button_html() . '</h2>';
|
138 |
+
echo $spcia_pages_help->get_panel_html();
|
139 |
+
|
140 |
+
echo '<p><strong>' . esc_html__( 'Search pages', 'wordpress-seo' ) . '</strong><br/>';
|
141 |
+
$yform->textinput( 'title-search-wpseo', __( 'Title template', 'wordpress-seo' ), 'template search-template' );
|
142 |
+
echo '</p>';
|
143 |
+
echo '<p><strong>' . esc_html__( '404 pages', 'wordpress-seo' ) . '</strong><br/>';
|
144 |
+
$yform->textinput( 'title-404-wpseo', __( 'Title template', 'wordpress-seo' ), 'template error404-template' );
|
145 |
+
echo '</p>';
|
146 |
+
echo '</div>';
|
admin/views/tabs/metas/breadcrumbs.php
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
echo '<h2>' . esc_html__( 'Breadcrumbs settings', 'wordpress-seo' ) . '</h2>';
|
17 |
+
|
18 |
+
if ( ! current_theme_supports( 'yoast-seo-breadcrumbs' ) ) {
|
19 |
+
$yform->light_switch( 'breadcrumbs-enable', __( 'Enable Breadcrumbs', 'wordpress-seo' ) );
|
20 |
+
echo '<br/>';
|
21 |
+
}
|
22 |
+
|
23 |
+
echo '<div id="breadcrumbsinfo">';
|
24 |
+
|
25 |
+
$yform->textinput( 'breadcrumbs-sep', __( 'Separator between breadcrumbs', 'wordpress-seo' ) );
|
26 |
+
$yform->textinput( 'breadcrumbs-home', __( 'Anchor text for the Homepage', 'wordpress-seo' ) );
|
27 |
+
$yform->textinput( 'breadcrumbs-prefix', __( 'Prefix for the breadcrumb path', 'wordpress-seo' ) );
|
28 |
+
$yform->textinput( 'breadcrumbs-archiveprefix', __( 'Prefix for Archive breadcrumbs', 'wordpress-seo' ) );
|
29 |
+
$yform->textinput( 'breadcrumbs-searchprefix', __( 'Prefix for Search Page breadcrumbs', 'wordpress-seo' ) );
|
30 |
+
$yform->textinput( 'breadcrumbs-404crumb', __( 'Breadcrumb for 404 Page', 'wordpress-seo' ) );
|
31 |
+
|
32 |
+
echo '<br/>';
|
33 |
+
|
34 |
+
if ( get_option( 'show_on_front' ) === 'page' && get_option( 'page_for_posts' ) > 0 ) {
|
35 |
+
$yform->show_hide_switch( 'breadcrumbs-blog-remove', __( 'Show Blog page', 'wordpress-seo' ) );
|
36 |
+
}
|
37 |
+
|
38 |
+
$yform->toggle_switch( 'breadcrumbs-boldlast', array(
|
39 |
+
'on' => __( 'Bold', 'wordpress-seo' ),
|
40 |
+
'off' => __( 'Regular', 'wordpress-seo' ),
|
41 |
+
), __( 'Bold the last page', 'wordpress-seo' ) );
|
42 |
+
|
43 |
+
echo '<br/><br/>';
|
44 |
+
|
45 |
+
/*
|
46 |
+
* WPSEO_Post_Type::get_accessible_post_types() should *not* be used here.
|
47 |
+
* Even posts that are not indexed, should be able to get breadcrumbs for accessibility/usability.
|
48 |
+
*/
|
49 |
+
$post_types = get_post_types( array( 'public' => true ), 'objects' );
|
50 |
+
if ( is_array( $post_types ) && $post_types !== array() ) {
|
51 |
+
echo '<h2>' . esc_html__( 'Taxonomy to show in breadcrumbs for content types', 'wordpress-seo' ) . '</h2>';
|
52 |
+
foreach ( $post_types as $pt ) {
|
53 |
+
$taxonomies = get_object_taxonomies( $pt->name, 'objects' );
|
54 |
+
if ( is_array( $taxonomies ) && $taxonomies !== array() ) {
|
55 |
+
$values = array( 0 => __( 'None', 'wordpress-seo' ) );
|
56 |
+
foreach ( $taxonomies as $tax ) {
|
57 |
+
$values[ $tax->name ] = $tax->labels->singular_name;
|
58 |
+
}
|
59 |
+
$yform->select( 'post_types-' . $pt->name . '-maintax', $pt->labels->name, $values );
|
60 |
+
unset( $values, $tax );
|
61 |
+
}
|
62 |
+
unset( $taxonomies );
|
63 |
+
}
|
64 |
+
unset( $pt );
|
65 |
+
}
|
66 |
+
echo '<br/>';
|
67 |
+
|
68 |
+
$taxonomies = get_taxonomies(
|
69 |
+
array(
|
70 |
+
'public' => true,
|
71 |
+
'_builtin' => false,
|
72 |
+
),
|
73 |
+
'objects'
|
74 |
+
);
|
75 |
+
|
76 |
+
if ( is_array( $taxonomies ) && $taxonomies !== array() ) {
|
77 |
+
echo '<h2>' . esc_html__( 'Content type archive to show in breadcrumbs for taxonomies', 'wordpress-seo' ) . '</h2>';
|
78 |
+
foreach ( $taxonomies as $tax ) {
|
79 |
+
$values = array( 0 => __( 'None', 'wordpress-seo' ) );
|
80 |
+
if ( get_option( 'show_on_front' ) === 'page' && get_option( 'page_for_posts' ) > 0 ) {
|
81 |
+
$values['post'] = __( 'Blog', 'wordpress-seo' );
|
82 |
+
}
|
83 |
+
|
84 |
+
if ( is_array( $post_types ) && $post_types !== array() ) {
|
85 |
+
foreach ( $post_types as $pt ) {
|
86 |
+
if ( $pt->has_archive ) {
|
87 |
+
$values[ $pt->name ] = $pt->labels->name;
|
88 |
+
}
|
89 |
+
}
|
90 |
+
unset( $pt );
|
91 |
+
}
|
92 |
+
$yform->select( 'taxonomy-' . $tax->name . '-ptparent', $tax->labels->singular_name, $values );
|
93 |
+
unset( $values, $tax );
|
94 |
+
}
|
95 |
+
}
|
96 |
+
unset( $taxonomies, $post_types );
|
97 |
+
|
98 |
+
?>
|
99 |
+
<br class="clear"/>
|
100 |
+
</div>
|
101 |
+
<h2><?php esc_html_e( 'How to insert breadcrumbs in your theme', 'wordpress-seo' ); ?></h2>
|
102 |
+
<p>
|
103 |
+
<?php
|
104 |
+
printf(
|
105 |
+
/* translators: %1$s / %2$s: links to the breadcrumbs implementation page on the Yoast knowledgebase */
|
106 |
+
esc_html__( 'Usage of this breadcrumbs feature is explained in %1$sour knowledge-base article on breadcrumbs implementation%2$s.', 'wordpress-seo' ),
|
107 |
+
'<a href="' . esc_url( WPSEO_Shortlinker::get( 'http://yoa.st/breadcrumbs' ) ) . '" target="_blank">',
|
108 |
+
'</a>'
|
109 |
+
);
|
110 |
+
?>
|
111 |
+
</p>
|
admin/views/tabs/metas/general.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
// To improve readability, this tab has been divided into 5 separate blocks, included below.
|
17 |
+
require dirname( __FILE__ ) . '/general/force-rewrite-title.php';
|
18 |
+
require dirname( __FILE__ ) . '/general/title-separator.php';
|
19 |
+
require dirname( __FILE__ ) . '/general/homepage.php';
|
20 |
+
require dirname( __FILE__ ) . '/general/knowledge-graph.php';
|
admin/views/tabs/metas/general/force-rewrite-title.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views\General
|
4 |
+
*
|
5 |
+
* @var Yoast_Form $yform
|
6 |
+
*/
|
7 |
+
|
8 |
+
if ( ! current_theme_supports( 'title-tag' ) ) {
|
9 |
+
?>
|
10 |
+
<div class="tab-block">
|
11 |
+
<?php
|
12 |
+
$yform->light_switch( 'forcerewritetitle', __( 'Force rewrite titles', 'wordpress-seo' ) );
|
13 |
+
echo '<p class="description">';
|
14 |
+
printf(
|
15 |
+
/* translators: %1$s expands to Yoast SEO */
|
16 |
+
esc_html__( '%1$s has auto-detected whether it needs to force rewrite the titles for your pages, if you think it\'s wrong and you know what you\'re doing, you can change the setting here.', 'wordpress-seo' ),
|
17 |
+
'Yoast SEO'
|
18 |
+
);
|
19 |
+
echo '</p>';
|
20 |
+
?>
|
21 |
+
</div>
|
22 |
+
<?php
|
23 |
+
}
|
admin/views/tabs/metas/general/homepage.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views\General
|
4 |
+
*
|
5 |
+
* @var Yoast_Form $yform
|
6 |
+
*/
|
7 |
+
|
8 |
+
?>
|
9 |
+
<div class="tab-block">
|
10 |
+
<?php
|
11 |
+
if ( 'posts' === get_option( 'show_on_front' ) ) {
|
12 |
+
$homepage_help = new WPSEO_Admin_Help_Panel(
|
13 |
+
'search-appearance-homepage',
|
14 |
+
__( 'Learn more about the homepage setting', 'wordpress-seo' ),
|
15 |
+
__( 'This is what shows in the search results when people find your homepage. This means this is probably what they see when they search for your brand name.', 'wordpress-seo' ),
|
16 |
+
'has-wrapper'
|
17 |
+
);
|
18 |
+
|
19 |
+
echo '<h2 class="help-button-inline">', esc_html__( 'Homepage', 'wordpress-seo' ), $homepage_help->get_button_html(), '</h2>';
|
20 |
+
echo $homepage_help->get_panel_html();
|
21 |
+
$yform->textinput( 'title-home-wpseo', __( 'Title', 'wordpress-seo' ), 'template homepage-template' );
|
22 |
+
$yform->textarea( 'metadesc-home-wpseo', __( 'Meta description', 'wordpress-seo' ), array( 'class' => 'template homepage-template' ) );
|
23 |
+
}
|
24 |
+
else {
|
25 |
+
echo '<h2>', esc_html__( 'Homepage & Front page', 'wordpress-seo' ), '</h2>';
|
26 |
+
echo '<p>';
|
27 |
+
printf(
|
28 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
29 |
+
esc_html__( 'You can determine the title and description for the front page by %1$sediting the front page itself »%2$s', 'wordpress-seo' ),
|
30 |
+
'<a href="' . esc_url( get_edit_post_link( get_option( 'page_on_front' ) ) ) . '">',
|
31 |
+
'</a>'
|
32 |
+
);
|
33 |
+
echo '</p>';
|
34 |
+
if ( get_option( 'page_for_posts' ) > 0 ) {
|
35 |
+
echo '<p>';
|
36 |
+
printf(
|
37 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
38 |
+
esc_html__( 'You can determine the title and description for the blog page by %1$sediting the blog page itself »%2$s', 'wordpress-seo' ),
|
39 |
+
'<a href="' . esc_url( get_edit_post_link( get_option( 'page_for_posts' ) ) ) . '">',
|
40 |
+
'</a>'
|
41 |
+
);
|
42 |
+
echo '</p>';
|
43 |
+
}
|
44 |
+
}
|
45 |
+
?>
|
46 |
+
</div>
|
admin/views/tabs/metas/general/knowledge-graph.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views\General
|
4 |
+
*
|
5 |
+
* @var Yoast_Form $yform
|
6 |
+
*/
|
7 |
+
|
8 |
+
$knowledge_graph_help = new WPSEO_Admin_Help_Panel(
|
9 |
+
'search-appearance-knowledge-graph',
|
10 |
+
__( 'Learn more about the knowledge graph setting', 'wordpress-seo' ),
|
11 |
+
sprintf(
|
12 |
+
/* translators: %1$s opens the link to the Yoast.com article about Google's Knowledge Graph, %2$s closes the link, */
|
13 |
+
__( 'This data is shown as metadata in your site. It is intended to appear in %1$sGoogle\'s Knowledge Graph%2$s. You can be either a company, or a person.', 'wordpress-seo' ),
|
14 |
+
'<a href="' . esc_url( WPSEO_Shortlinker::get( 'https://yoa.st/1-p' ) ) . '" target="_blank" rel="noopener noreferer">',
|
15 |
+
'</a>'
|
16 |
+
),
|
17 |
+
'has-wrapper'
|
18 |
+
);
|
19 |
+
?>
|
20 |
+
<div class="tab-block">
|
21 |
+
<h2 class="help-button-inline"><?php echo esc_html__( 'Knowledge Graph', 'wordpress-seo' ) . $knowledge_graph_help->get_button_html(); ?></h2>
|
22 |
+
<?php
|
23 |
+
echo $knowledge_graph_help->get_panel_html();
|
24 |
+
$yform->select( 'company_or_person', __( 'Company or person', 'wordpress-seo' ), array(
|
25 |
+
'' => __( 'Choose whether you\'re a company or person', 'wordpress-seo' ),
|
26 |
+
'company' => __( 'Company', 'wordpress-seo' ),
|
27 |
+
'person' => __( 'Person', 'wordpress-seo' ),
|
28 |
+
) );
|
29 |
+
?>
|
30 |
+
<div id="knowledge-graph-company">
|
31 |
+
<h3><?php esc_html_e( 'Company', 'wordpress-seo' ); ?></h3>
|
32 |
+
<?php
|
33 |
+
$yform->textinput( 'company_name', __( 'Company name', 'wordpress-seo' ) );
|
34 |
+
$yform->media_input( 'company_logo', __( 'Company logo', 'wordpress-seo' ) );
|
35 |
+
?>
|
36 |
+
</div>
|
37 |
+
<div id="knowledge-graph-person">
|
38 |
+
<h3><?php esc_html_e( 'Person', 'wordpress-seo' ); ?></h3>
|
39 |
+
<?php $yform->textinput( 'person_name', __( 'Your name', 'wordpress-seo' ) ); ?>
|
40 |
+
</div>
|
41 |
+
</div>
|
admin/views/tabs/metas/general/title-separator.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views\General
|
4 |
+
*
|
5 |
+
* @var Yoast_Form $yform
|
6 |
+
*/
|
7 |
+
|
8 |
+
$title_separator_help = new WPSEO_Admin_Help_Panel(
|
9 |
+
'search-appearance-title-separator',
|
10 |
+
__( 'Learn more about the title separator setting', 'wordpress-seo' ),
|
11 |
+
__( 'Choose the symbol to use as your title separator. This will display, for instance, between your post title and site name. Symbols are shown in the size they\'ll appear in the search results.', 'wordpress-seo' ),
|
12 |
+
'has-wrapper'
|
13 |
+
);
|
14 |
+
?>
|
15 |
+
<div class="tab-block">
|
16 |
+
<h2 class="help-button-inline"><?php echo esc_html__( 'Title Separator', 'wordpress-seo' ) . $title_separator_help->get_button_html(); ?></h2>
|
17 |
+
<?php
|
18 |
+
echo $title_separator_help->get_panel_html();
|
19 |
+
$legend = __( 'Title separator symbol', 'wordpress-seo' );
|
20 |
+
$legend_attr = array( 'class' => 'radiogroup screen-reader-text' );
|
21 |
+
$yform->radio( 'separator', WPSEO_Option_Titles::get_instance()->get_separator_options(), $legend, $legend_attr );
|
22 |
+
?>
|
23 |
+
</div>
|
admin/views/tabs/metas/media.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
$media_help = new WPSEO_Admin_Help_Panel(
|
17 |
+
'search-appearance-media',
|
18 |
+
__( 'Learn more about the Media and attachment URLs setting', 'wordpress-seo' ),
|
19 |
+
__( 'When you upload media (an image or video for example) to WordPress, it doesn\'t just save the media, it creates an attachment URL for it. These attachment pages are quite empty: they contain the media item and maybe a title if you entered one. Because of that, if you never use these attachment URLs, it\'s better to disable them, and redirect them to the media item itself.', 'wordpress-seo' ),
|
20 |
+
'has-wrapper'
|
21 |
+
);
|
22 |
+
?>
|
23 |
+
<h2 class="help-button-inline"><?php echo esc_html__( 'Media & attachment URLs', 'wordpress-seo' ) . $media_help->get_button_html(); ?></h2>
|
24 |
+
<?php echo $media_help->get_panel_html(); ?>
|
25 |
+
<p><strong><?php esc_html_e( 'We recommend you set this to Yes.', 'wordpress-seo' ); ?></strong></p>
|
26 |
+
<?php
|
27 |
+
|
28 |
+
$yform->toggle_switch(
|
29 |
+
'disable-attachment',
|
30 |
+
array(
|
31 |
+
'on' => __( 'Yes', 'wordpress-seo' ),
|
32 |
+
'off' => __( 'No', 'wordpress-seo' ),
|
33 |
+
),
|
34 |
+
__( 'Redirect attachment URLs to the attachment itself?', 'wordpress-seo' )
|
35 |
+
);
|
36 |
+
|
37 |
+
?>
|
38 |
+
<div id="media_settings">
|
39 |
+
<br/>
|
40 |
+
<br/>
|
41 |
+
|
42 |
+
<?php
|
43 |
+
$view_utils = new Yoast_View_Utils();
|
44 |
+
$view_utils->show_post_type_settings( 'attachment' );
|
45 |
+
?>
|
46 |
+
</div>
|
admin/views/tabs/metas/post-types.php
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
/*
|
17 |
+
* WPSEO_Post_Type::get_accessible_post_types() should *not* be used here.
|
18 |
+
* Otherwise setting a post-type to `noindex` will remove it from the list,
|
19 |
+
* making it very hard to restore the setting again.
|
20 |
+
*/
|
21 |
+
$post_types = get_post_types( array( 'public' => true ), 'objects' );
|
22 |
+
|
23 |
+
// We'll show attachments on the Media tab.
|
24 |
+
$post_types = WPSEO_Post_Type::filter_attachment_post_type( $post_types );
|
25 |
+
|
26 |
+
$view_utils = new Yoast_View_Utils();
|
27 |
+
|
28 |
+
if ( is_array( $post_types ) && $post_types !== array() ) {
|
29 |
+
foreach ( $post_types as $post_type ) {
|
30 |
+
echo '<div class="tab-block" id="' . esc_attr( $post_type->name . '-titles-metas' ) . '">';
|
31 |
+
echo '<h2 id="' . esc_attr( $post_type->name ) . '">' . esc_html( $post_type->labels->name ) . ' (<code>' . esc_html( $post_type->name ) . '</code>)</h2>';
|
32 |
+
$view_utils->show_post_type_settings( $post_type );
|
33 |
+
echo '</div>';
|
34 |
+
/**
|
35 |
+
* Allow adding a custom checkboxes to the admin meta page - Post Types tab
|
36 |
+
*
|
37 |
+
* @api WPSEO_Admin_Pages $yform The WPSEO_Admin_Pages object
|
38 |
+
* @api String $name The post type name
|
39 |
+
*/
|
40 |
+
do_action( 'wpseo_admin_page_meta_post_types', $yform, $post_type->name );
|
41 |
+
}
|
42 |
+
unset( $post_type );
|
43 |
+
}
|
44 |
+
|
45 |
+
$post_types = get_post_types(
|
46 |
+
array(
|
47 |
+
'_builtin' => false,
|
48 |
+
'has_archive' => true,
|
49 |
+
),
|
50 |
+
'objects'
|
51 |
+
);
|
52 |
+
|
53 |
+
if ( is_array( $post_types ) && $post_types !== array() ) {
|
54 |
+
echo '<h2>' . esc_html__( 'Custom Post Type Archives', 'wordpress-seo' ) . '</h2>';
|
55 |
+
echo '<p>' . esc_html__( 'Note: instead of templates these are the actual titles and meta descriptions for these custom post type archive pages.', 'wordpress-seo' ) . '</p>';
|
56 |
+
foreach ( $post_types as $post_type ) {
|
57 |
+
$name = $post_type->name;
|
58 |
+
echo '<div class="tab-block">';
|
59 |
+
echo '<h3>' . esc_html( ucfirst( $post_type->labels->name ) ) . '</h3>';
|
60 |
+
|
61 |
+
$custom_post_type_archive_help = $view_utils->search_results_setting_help( $post_type, 'archive' );
|
62 |
+
|
63 |
+
$yform->index_switch(
|
64 |
+
'noindex-ptarchive-' . $name,
|
65 |
+
sprintf(
|
66 |
+
/* translators: %s expands to the post type's name. */
|
67 |
+
__( 'the archive for %s', 'wordpress-seo' ),
|
68 |
+
$post_type->labels->name
|
69 |
+
),
|
70 |
+
$custom_post_type_archive_help->get_button_html() . $custom_post_type_archive_help->get_panel_html()
|
71 |
+
);
|
72 |
+
|
73 |
+
$yform->textinput( 'title-ptarchive-' . $name, __( 'Title', 'wordpress-seo' ), 'template posttype-template' );
|
74 |
+
$yform->textarea( 'metadesc-ptarchive-' . $name, __( 'Meta description', 'wordpress-seo' ), array( 'class' => 'template posttype-template' ) );
|
75 |
+
if ( WPSEO_Options::get( 'breadcrumbs-enable' ) === true ) {
|
76 |
+
$yform->textinput( 'bctitle-ptarchive-' . $name, __( 'Breadcrumbs title', 'wordpress-seo' ) );
|
77 |
+
}
|
78 |
+
echo '</div>';
|
79 |
+
}
|
80 |
+
unset( $post_type );
|
81 |
+
}
|
82 |
+
unset( $post_types );
|
admin/views/tabs/metas/rss.php
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
$rss_help = new WPSEO_Admin_Help_Panel(
|
17 |
+
'search-appearance-rss',
|
18 |
+
__( 'Learn more about the RSS feed setting', 'wordpress-seo' ),
|
19 |
+
__( 'This feature is used to automatically add content to your RSS, more specifically, it\'s meant to add links back to your blog and your blog posts, so dumb scrapers will automatically add these links too, helping search engines identify you as the original source of the content.', 'wordpress-seo' ),
|
20 |
+
'has-wrapper'
|
21 |
+
);
|
22 |
+
|
23 |
+
echo '<h2 class="help-button-inline">' . esc_html__( 'RSS feed settings', 'wordpress-seo' ) . $rss_help->get_button_html() . '</h2>';
|
24 |
+
|
25 |
+
echo $rss_help->get_panel_html();
|
26 |
+
|
27 |
+
$textarea_atts = array(
|
28 |
+
'cols' => '50',
|
29 |
+
'rows' => '5',
|
30 |
+
);
|
31 |
+
$yform->textarea( 'rssbefore', __( 'Content to put before each post in the feed', 'wordpress-seo' ), '', $textarea_atts );
|
32 |
+
$yform->textarea( 'rssafter', __( 'Content to put after each post in the feed', 'wordpress-seo' ), '', $textarea_atts );
|
33 |
+
|
34 |
+
$rss_variables_help = new WPSEO_Admin_Help_Panel(
|
35 |
+
'search-appearance-rss-variables',
|
36 |
+
__( 'Learn more about the available variables', 'wordpress-seo' ),
|
37 |
+
__( 'You can use the following variables within the content, they will be replaced by the value on the right.', 'wordpress-seo' ),
|
38 |
+
'has-wrapper'
|
39 |
+
);
|
40 |
+
|
41 |
+
echo '<h2 class="help-button-inline">' . esc_html__( 'Available variables', 'wordpress-seo' ) . $rss_variables_help->get_button_html() . '</h2>';
|
42 |
+
echo $rss_variables_help->get_panel_html();
|
43 |
+
?>
|
44 |
+
<table class="wpseo yoast_help yoast-table-scrollable">
|
45 |
+
<thead>
|
46 |
+
<tr>
|
47 |
+
<th scope="col"><?php esc_html_e( 'Variable', 'wordpress-seo' ); ?></th>
|
48 |
+
<th scope="col"><?php esc_html_e( 'Description', 'wordpress-seo' ); ?></th>
|
49 |
+
</tr>
|
50 |
+
</thead>
|
51 |
+
<tbody>
|
52 |
+
<tr>
|
53 |
+
<td class="yoast-variable-name">%%AUTHORLINK%%</td>
|
54 |
+
<td class="yoast-variable-desc"><?php esc_html_e( 'A link to the archive for the post author, with the authors name as anchor text.', 'wordpress-seo' ); ?></td>
|
55 |
+
</tr>
|
56 |
+
<tr>
|
57 |
+
<td class="yoast-variable-name">%%POSTLINK%%</td>
|
58 |
+
<td class="yoast-variable-desc"><?php esc_html_e( 'A link to the post, with the title as anchor text.', 'wordpress-seo' ); ?></td>
|
59 |
+
</tr>
|
60 |
+
<tr>
|
61 |
+
<td class="yoast-variable-name">%%BLOGLINK%%</td>
|
62 |
+
<td class="yoast-variable-desc"><?php esc_html_e( "A link to your site, with your site's name as anchor text.", 'wordpress-seo' ); ?></td>
|
63 |
+
</tr>
|
64 |
+
<tr>
|
65 |
+
<td class="yoast-variable-name">%%BLOGDESCLINK%%</td>
|
66 |
+
<td class="yoast-variable-desc"><?php esc_html_e( "A link to your site, with your site's name and description as anchor text.", 'wordpress-seo' ); ?></td>
|
67 |
+
</tr>
|
68 |
+
</tbody>
|
69 |
+
</table>
|
admin/views/tabs/metas/taxonomies.php
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
$taxonomies = get_taxonomies( array( 'public' => true ), 'objects' );
|
17 |
+
if ( is_array( $taxonomies ) && $taxonomies !== array() ) {
|
18 |
+
foreach ( $taxonomies as $tax ) {
|
19 |
+
// Explicitly hide all the core taxonomies we never want to do stuff for.
|
20 |
+
if ( in_array( $tax->name, array( 'link_category', 'nav_menu' ), true ) ) {
|
21 |
+
continue;
|
22 |
+
}
|
23 |
+
|
24 |
+
echo '<div class="tab-block">';
|
25 |
+
echo '<h2>' . esc_html( ucfirst( $tax->labels->name ) ) . ' (<code>' . esc_html( $tax->name ) . '</code>)</h2>';
|
26 |
+
if ( $tax->name === 'post_format' ) {
|
27 |
+
$yform->light_switch(
|
28 |
+
'disable-post_format',
|
29 |
+
__( 'Format-based archives', 'wordpress-seo' ),
|
30 |
+
array( __( 'Enabled', 'wordpress-seo' ), __( 'Disabled', 'wordpress-seo' ) ),
|
31 |
+
false
|
32 |
+
);
|
33 |
+
}
|
34 |
+
echo "<div id='" . esc_attr( $tax->name ) . "-titles-metas'>";
|
35 |
+
|
36 |
+
$view_utils = new Yoast_View_Utils();
|
37 |
+
$taxonomies_help = $view_utils->search_results_setting_help( $tax );
|
38 |
+
|
39 |
+
$yform->index_switch(
|
40 |
+
'noindex-tax-' . $tax->name,
|
41 |
+
$tax->labels->name,
|
42 |
+
$taxonomies_help->get_button_html() . $taxonomies_help->get_panel_html()
|
43 |
+
);
|
44 |
+
|
45 |
+
$yform->textinput( 'title-tax-' . $tax->name, __( 'Title template', 'wordpress-seo' ), 'template taxonomy-template' );
|
46 |
+
$yform->textarea( 'metadesc-tax-' . $tax->name, __( 'Meta description template', 'wordpress-seo' ), array( 'class' => 'template taxonomy-template' ) );
|
47 |
+
if ( $tax->name !== 'post_format' ) {
|
48 |
+
/* translators: %1$s expands to Yoast SEO */
|
49 |
+
$yform->show_hide_switch( 'display-metabox-tax-' . $tax->name, sprintf( __( '%1$s Meta Box', 'wordpress-seo' ), 'Yoast SEO' ) );
|
50 |
+
}
|
51 |
+
/**
|
52 |
+
* Allow adding custom checkboxes to the admin meta page - Taxonomies tab
|
53 |
+
*
|
54 |
+
* @api WPSEO_Admin_Pages $yform The WPSEO_Admin_Pages object
|
55 |
+
* @api Object $tax The taxonomy
|
56 |
+
*/
|
57 |
+
do_action( 'wpseo_admin_page_meta_taxonomies', $yform, $tax );
|
58 |
+
echo '</div>';
|
59 |
+
echo '</div>';
|
60 |
+
}
|
61 |
+
unset( $tax );
|
62 |
+
}
|
63 |
+
unset( $taxonomies );
|
64 |
+
|
65 |
+
echo '<h2>', esc_html__( ' Category URLs', 'wordpress-seo' ), '</h2>';
|
66 |
+
|
67 |
+
$remove_buttons = array( __( 'Keep', 'wordpress-seo' ), __( 'Remove', 'wordpress-seo' ) );
|
68 |
+
|
69 |
+
$stripcategorybase_help = new WPSEO_Admin_Help_Panel(
|
70 |
+
'opengraph',
|
71 |
+
esc_html__( 'Help on the category prefix setting', 'wordpress-seo' ),
|
72 |
+
sprintf(
|
73 |
+
/* translators: %s expands to <code>/category/</code> */
|
74 |
+
esc_html__( 'Category URLs in WordPress contain a prefix, usually %s, this feature removes that prefix, for categories only.', 'wordpress-seo' ),
|
75 |
+
'<code>/category/</code>'
|
76 |
+
)
|
77 |
+
);
|
78 |
+
|
79 |
+
$yform->light_switch(
|
80 |
+
'stripcategorybase',
|
81 |
+
__( 'Remove the categories prefix', 'wordpress-seo' ),
|
82 |
+
$remove_buttons,
|
83 |
+
false,
|
84 |
+
$stripcategorybase_help->get_button_html() . $stripcategorybase_help->get_panel_html()
|
85 |
+
);
|
admin/views/tabs/social/accounts.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
$social_profiles_help = new WPSEO_Admin_Help_Panel(
|
17 |
+
'social-accounts',
|
18 |
+
__( 'Learn more about your social profiles settings', 'wordpress-seo' ),
|
19 |
+
__( 'To let search engines know which social profiles are associated to this site, enter your site social profiles data below.', 'wordpress-seo' ),
|
20 |
+
'has-wrapper'
|
21 |
+
);
|
22 |
+
|
23 |
+
echo '<h2 class="help-button-inline">' . esc_html__( 'Your social profiles', 'wordpress-seo' ) . $social_profiles_help->get_button_html() . '</h2>';
|
24 |
+
echo $social_profiles_help->get_panel_html();
|
25 |
+
|
26 |
+
$yform = Yoast_Form::get_instance();
|
27 |
+
$yform->textinput( 'facebook_site', __( 'Facebook Page URL', 'wordpress-seo' ) );
|
28 |
+
$yform->textinput( 'twitter_site', __( 'Twitter Username', 'wordpress-seo' ) );
|
29 |
+
$yform->textinput( 'instagram_url', __( 'Instagram URL', 'wordpress-seo' ) );
|
30 |
+
$yform->textinput( 'linkedin_url', __( 'LinkedIn URL', 'wordpress-seo' ) );
|
31 |
+
$yform->textinput( 'myspace_url', __( 'MySpace URL', 'wordpress-seo' ) );
|
32 |
+
$yform->textinput( 'pinterest_url', __( 'Pinterest URL', 'wordpress-seo' ) );
|
33 |
+
$yform->textinput( 'youtube_url', __( 'YouTube URL', 'wordpress-seo' ) );
|
34 |
+
$yform->textinput( 'google_plus_url', __( 'Google+ URL', 'wordpress-seo' ) );
|
35 |
+
|
36 |
+
do_action( 'wpseo_admin_other_section' );
|
admin/views/tabs/social/facebook.php
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
echo '<h2>' . esc_html__( 'Facebook settings', 'wordpress-seo' ) . '</h2>';
|
17 |
+
|
18 |
+
$yform->light_switch( 'opengraph', __( 'Add Open Graph meta data', 'wordpress-seo' ) );
|
19 |
+
|
20 |
+
?>
|
21 |
+
<p>
|
22 |
+
<?php
|
23 |
+
esc_html_e( 'Enable this feature if you want Facebook and other social media to display a preview with images and a text excerpt when a link to your site is shared.', 'wordpress-seo' );
|
24 |
+
?>
|
25 |
+
</p>
|
26 |
+
|
27 |
+
<?php
|
28 |
+
if ( 'posts' === get_option( 'show_on_front' ) ) {
|
29 |
+
$social_facebook_frontpage_help = new WPSEO_Admin_Help_Panel(
|
30 |
+
'social-facebook-frontpage',
|
31 |
+
esc_html__( 'Learn more about the title separator setting', 'wordpress-seo' ),
|
32 |
+
esc_html__( 'These are the title, description and image used in the Open Graph meta tags on the front page of your site.', 'wordpress-seo' ),
|
33 |
+
'has-wrapper'
|
34 |
+
);
|
35 |
+
echo '<h2 class="help-button-inline">' . esc_html__( 'Frontpage settings', 'wordpress-seo' ) . $social_facebook_frontpage_help->get_button_html() . '</h2>';
|
36 |
+
echo $social_facebook_frontpage_help->get_panel_html();
|
37 |
+
|
38 |
+
$yform->media_input( 'og_frontpage_image', __( 'Image URL', 'wordpress-seo' ) );
|
39 |
+
$yform->textinput( 'og_frontpage_title', __( 'Title', 'wordpress-seo' ) );
|
40 |
+
$yform->textinput( 'og_frontpage_desc', __( 'Description', 'wordpress-seo' ) );
|
41 |
+
|
42 |
+
$copy_home_description_button_label = esc_html__( 'Copy home meta description', 'wordpress-seo' );
|
43 |
+
|
44 |
+
// Offer copying of meta description.
|
45 |
+
$homepage_meta_description = WPSEO_Options::get( 'metadesc-home-wpseo' );
|
46 |
+
if ( ! empty( $homepage_meta_description ) ) {
|
47 |
+
$copy_home_meta_desc_help = new WPSEO_Admin_Help_Panel(
|
48 |
+
'copy-home-meda-desc',
|
49 |
+
esc_html__( 'Help on copying the home meta description', 'wordpress-seo' ),
|
50 |
+
sprintf(
|
51 |
+
/* translators: 1: link open tag; 2: link close tag., 3: the translated label of the button */
|
52 |
+
esc_html__( 'Click the "%3$s" button to use the meta description already set in the %1$sSearch Appearance Homepage%2$s setting.', 'wordpress-seo' ),
|
53 |
+
'<a href="' . esc_url( admin_url( 'admin.php?page=wpseo_titles' ) ) . '">',
|
54 |
+
'</a>',
|
55 |
+
$copy_home_description_button_label
|
56 |
+
)
|
57 |
+
);
|
58 |
+
|
59 |
+
echo '<input type="hidden" id="meta_description" value="', $homepage_meta_description, '" />';
|
60 |
+
echo '<div class="label desc copy-home-meta-description">' .
|
61 |
+
'<button type="button" id="copy-home-meta-description" class="button">', $copy_home_description_button_label, '</button>' .
|
62 |
+
$copy_home_meta_desc_help->get_button_html() .
|
63 |
+
$copy_home_meta_desc_help->get_panel_html() .
|
64 |
+
'</div>';
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
echo '<h2>' . esc_html__( 'Default settings', 'wordpress-seo' ) . '</h2>';
|
69 |
+
|
70 |
+
$yform->media_input( 'og_default_image', __( 'Image URL', 'wordpress-seo' ) );
|
71 |
+
|
72 |
+
?>
|
73 |
+
<p class="desc label">
|
74 |
+
<?php esc_html_e( 'This image is used if the post/page being shared does not contain any images.', 'wordpress-seo' ); ?>
|
75 |
+
</p>
|
76 |
+
|
77 |
+
<?php
|
78 |
+
|
79 |
+
$social_facebook = new Yoast_Social_Facebook();
|
80 |
+
$social_facebook->show_form();
|
81 |
+
|
82 |
+
do_action( 'wpseo_admin_opengraph_section' );
|
admin/views/tabs/social/google.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
echo '<h2>' . esc_html__( 'Google+ settings', 'wordpress-seo' ) . '</h2>';
|
17 |
+
|
18 |
+
printf(
|
19 |
+
'<p>%s</p>',
|
20 |
+
esc_html__( 'If you have a Google+ page for your business, add that URL here and link it on your Google+ page\'s about page.', 'wordpress-seo' )
|
21 |
+
);
|
22 |
+
|
23 |
+
$yform->textinput( 'plus-publisher', __( 'Google Publisher Page', 'wordpress-seo' ) );
|
24 |
+
|
25 |
+
do_action( 'wpseo_admin_googleplus_section' );
|
admin/views/tabs/social/pinterest.php
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
echo '<h2>' . esc_html__( 'Pinterest settings', 'wordpress-seo' ) . '</h2>';
|
17 |
+
|
18 |
+
printf(
|
19 |
+
'<p>%s</p>',
|
20 |
+
esc_html__( 'Pinterest uses Open Graph metadata just like Facebook, so be sure to keep the Open Graph checkbox on the Facebook tab checked if you want to optimize your site for Pinterest.', 'wordpress-seo' )
|
21 |
+
);
|
22 |
+
printf(
|
23 |
+
'<p>%s</p>',
|
24 |
+
esc_html__( 'If you have already confirmed your website with Pinterest, you can skip the step below.', 'wordpress-seo' )
|
25 |
+
);
|
26 |
+
|
27 |
+
echo '<p>';
|
28 |
+
printf(
|
29 |
+
/* translators: %1$s / %2$s expands to a link to pinterest.com's help page. */
|
30 |
+
esc_html__( 'To %1$sconfirm your site with Pinterest%2$s, add the meta tag here:', 'wordpress-seo' ),
|
31 |
+
'<a target="_blank" href="https://help.pinterest.com/en/articles/confirm-your-website#meta_tag">',
|
32 |
+
'</a>'
|
33 |
+
);
|
34 |
+
echo '</p>';
|
35 |
+
|
36 |
+
$yform->textinput( 'pinterestverify', __( 'Pinterest confirmation', 'wordpress-seo' ) );
|
37 |
+
|
38 |
+
do_action( 'wpseo_admin_pinterest_section' );
|
admin/views/tabs/social/twitterbox.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
echo '<h2>' . esc_html__( 'Twitter settings', 'wordpress-seo' ) . '</h2>';
|
17 |
+
|
18 |
+
$yform->light_switch( 'twitter', __( 'Add Twitter card meta data', 'wordpress-seo' ) );
|
19 |
+
|
20 |
+
echo '<p>';
|
21 |
+
esc_html_e( 'Enable this feature if you want Twitter to display a preview with images and a text excerpt when a link to your site is shared.', 'wordpress-seo' );
|
22 |
+
echo '</p>';
|
23 |
+
|
24 |
+
echo '<br />';
|
25 |
+
|
26 |
+
$yform->select( 'twitter_card_type', __( 'The default card type to use', 'wordpress-seo' ), WPSEO_Option_Social::$twitter_card_types );
|
27 |
+
|
28 |
+
do_action( 'wpseo_admin_twitter_section' );
|
admin/views/tabs/tool/import-seo.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
?>
|
17 |
+
<p><?php esc_html_e( 'No doubt you\'ve used an SEO plugin before if this site isn\'t new. Let\'s make it easy on you, you can import the data below. If you want, you can import first, check if it was imported correctly, and then import & delete. No duplicate data will be imported.', 'wordpress-seo' ); ?></p>
|
18 |
+
|
19 |
+
<p>
|
20 |
+
<?php
|
21 |
+
printf(
|
22 |
+
/* translators: 1: link open tag; 2: link close tag. */
|
23 |
+
esc_html__( 'If you\'ve used another SEO plugin, try the %1$sSEO Data Transporter%2$s plugin to move your data into this plugin, it rocks!', 'wordpress-seo' ),
|
24 |
+
'<a href="https://wordpress.org/plugins/seo-data-transporter/">',
|
25 |
+
'</a>'
|
26 |
+
);
|
27 |
+
?>
|
28 |
+
</p>
|
29 |
+
|
30 |
+
<form
|
31 |
+
action="<?php echo esc_url( admin_url( 'admin.php?page=wpseo_tools&tool=import-export#top#import-seo' ) ); ?>"
|
32 |
+
method="post" accept-charset="<?php echo esc_attr( get_bloginfo( 'charset' ) ); ?>">
|
33 |
+
<?php
|
34 |
+
wp_nonce_field( 'wpseo-import', '_wpnonce', true, true );
|
35 |
+
$yform->checkbox( 'importheadspace', __( 'Import from HeadSpace2', 'wordpress-seo' ) );
|
36 |
+
$yform->checkbox( 'importaioseo', __( 'Import from All-in-One SEO', 'wordpress-seo' ) );
|
37 |
+
$yform->checkbox( 'importjetpackseo', __( 'Import from Jetpack SEO', 'wordpress-seo' ) );
|
38 |
+
$yform->checkbox( 'importseoultimate', __( 'Import from Ultimate SEO', 'wordpress-seo' ) );
|
39 |
+
$yform->checkbox( 'importseopressor', __( 'Import from SEOpressor', 'wordpress-seo' ) );
|
40 |
+
$yform->checkbox( 'importwoo', __( 'Import from WooThemes SEO framework', 'wordpress-seo' ) );
|
41 |
+
$yform->checkbox( 'importwpseo', __( 'Import from wpSEO', 'wordpress-seo' ) );
|
42 |
+
|
43 |
+
do_action( 'wpseo_import_other_plugins' );
|
44 |
+
?>
|
45 |
+
<br/>
|
46 |
+
<?php
|
47 |
+
$yform->checkbox( 'deleteolddata', __( 'Delete the old data after import? (recommended)', 'wordpress-seo' ) );
|
48 |
+
?>
|
49 |
+
<br/>
|
50 |
+
<input type="submit" class="button button-primary" name="import"
|
51 |
+
value="<?php esc_attr_e( 'Import', 'wordpress-seo' ); ?>"/>
|
52 |
+
</form>
|
admin/views/tabs/tool/wpseo-export.php
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
/* translators: %1$s expands to Yoast SEO */
|
17 |
+
$submit_button_value = sprintf( __( 'Export your %1$s settings', 'wordpress-seo' ), 'Yoast SEO' );
|
18 |
+
|
19 |
+
$wpseo_export_phrase = sprintf(
|
20 |
+
/* translators: %1$s expands to Yoast SEO */
|
21 |
+
__( 'Export your %1$s settings here, to import them again later or to import them on another site.', 'wordpress-seo' ),
|
22 |
+
'Yoast SEO'
|
23 |
+
);
|
24 |
+
?>
|
25 |
+
|
26 |
+
<p><?php echo esc_html( $wpseo_export_phrase ); ?></p>
|
27 |
+
<form
|
28 |
+
action="<?php echo esc_url( admin_url( 'admin.php?page=wpseo_tools&tool=import-export#top#wpseo-export' ) ); ?>"
|
29 |
+
method="post"
|
30 |
+
accept-charset="<?php echo esc_attr( get_bloginfo( 'charset' ) ); ?>">
|
31 |
+
<?php $yform->checkbox( 'include_taxonomy_meta', __( 'Include Taxonomy Metadata', 'wordpress-seo' ) ); ?><br />
|
32 |
+
<?php wp_nonce_field( WPSEO_Export::NONCE_ACTION, WPSEO_Export::NONCE_NAME ); ?>
|
33 |
+
<button type="submit" class="button button-primary" id="export-button"><?php echo esc_html( $submit_button_value ); ?></button>
|
34 |
+
</form>
|
admin/views/tabs/tool/wpseo-import.php
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin\Views
|
4 |
+
*/
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var Yoast_Form $yform
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
11 |
+
header( 'Status: 403 Forbidden' );
|
12 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
13 |
+
exit();
|
14 |
+
}
|
15 |
+
|
16 |
+
?>
|
17 |
+
<p>
|
18 |
+
<?php
|
19 |
+
printf(
|
20 |
+
/* translators: 1: emphasis opener; 2: emphasis closer. */
|
21 |
+
esc_html__( 'Import settings by locating %1$ssettings.zip%2$s and clicking "Import settings"', 'wordpress-seo' ),
|
22 |
+
'<em>',
|
23 |
+
'</em>'
|
24 |
+
);
|
25 |
+
?>
|
26 |
+
</p>
|
27 |
+
|
28 |
+
<form
|
29 |
+
action="<?php echo esc_url( admin_url( 'admin.php?page=wpseo_tools&tool=import-export#top#wpseo-import' ) ); ?>"
|
30 |
+
method="post" enctype="multipart/form-data"
|
31 |
+
accept-charset="<?php echo esc_attr( get_bloginfo( 'charset' ) ); ?>">
|
32 |
+
<?php wp_nonce_field( 'wpseo-import-file', '_wpnonce', true, true ); ?>
|
33 |
+
<label class="screen-reader-text" for="settings-import-file"><?php esc_html_e( 'Choose your settings.zip file', 'wordpress-seo' ); ?></label>
|
34 |
+
<input type="file" name="settings_import_file" id="settings-import-file"
|
35 |
+
accept="application/x-zip,application/x-zip-compressed,application/zip"/>
|
36 |
+
<input type="hidden" name="action" value="wp_handle_upload"/><br/>
|
37 |
+
<br/>
|
38 |
+
<input type="submit" class="button button-primary" value="<?php esc_attr_e( 'Import settings', 'wordpress-seo' ); ?>"/>
|
39 |
+
</form>
|
admin/views/tool-bulk-editor.php
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
* @since 1.5.0
|
5 |
+
*/
|
6 |
+
|
7 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
8 |
+
header( 'Status: 403 Forbidden' );
|
9 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
10 |
+
exit();
|
11 |
+
}
|
12 |
+
|
13 |
+
$wpseo_bulk_titles_table = new WPSEO_Bulk_Title_Editor_List_Table();
|
14 |
+
$wpseo_bulk_description_table = new WPSEO_Bulk_Description_List_Table();
|
15 |
+
|
16 |
+
get_current_screen()->set_screen_reader_content( array(
|
17 |
+
'heading_views' => __( 'Filter posts list', 'wordpress-seo' ),
|
18 |
+
'heading_pagination' => __( 'Posts list navigation', 'wordpress-seo' ),
|
19 |
+
'heading_list' => __( 'Posts list', 'wordpress-seo' ),
|
20 |
+
) );
|
21 |
+
|
22 |
+
// If type is empty, fill it with value of first tab (title).
|
23 |
+
$_GET['type'] = ( ! empty( $_GET['type'] ) ) ? $_GET['type'] : 'title';
|
24 |
+
|
25 |
+
if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
|
26 |
+
wp_redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), stripslashes( $_SERVER['REQUEST_URI'] ) ) );
|
27 |
+
exit;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Outputs a help center.
|
32 |
+
*/
|
33 |
+
function wpseo_render_help_center() {
|
34 |
+
$tabs = new WPSEO_Option_Tabs( '', '' );
|
35 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'title', __( 'Bulk editor', 'wordpress-seo' ),
|
36 |
+
array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-tools-bulk-editor' ) ) ) );
|
37 |
+
|
38 |
+
$tabs->add_tab( new WPSEO_Option_Tab( 'description', __( 'Bulk editor', 'wordpress-seo' ),
|
39 |
+
array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-tools-bulk-editor' ) ) ) );
|
40 |
+
|
41 |
+
$helpcenter = new WPSEO_Help_Center( '', $tabs, WPSEO_Utils::is_yoast_seo_premium() );
|
42 |
+
$helpcenter->localize_data();
|
43 |
+
$helpcenter->mount();
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Renders a bulk editor tab.
|
48 |
+
*
|
49 |
+
* @param WPSEO_Bulk_List_Table $table The table to render.
|
50 |
+
* @param string $id The id for the tab.
|
51 |
+
*/
|
52 |
+
function wpseo_get_rendered_tab( $table, $id ) {
|
53 |
+
?>
|
54 |
+
<div id="<?php echo esc_attr( $id ); ?>" class="wpseotab">
|
55 |
+
<?php
|
56 |
+
$table->show_page();
|
57 |
+
?>
|
58 |
+
</div>
|
59 |
+
<?php
|
60 |
+
}
|
61 |
+
|
62 |
+
?>
|
63 |
+
<script>
|
64 |
+
var wpseoBulkEditorNonce = <?php echo wp_json_encode( wp_create_nonce( 'wpseo-bulk-editor' ) ); ?>;
|
65 |
+
|
66 |
+
// eslint-disable-next-line
|
67 |
+
var wpseo_bulk_editor_nonce = wpseoBulkEditorNonce;
|
68 |
+
</script>
|
69 |
+
|
70 |
+
<br/><br/>
|
71 |
+
|
72 |
+
<div class="wpseo_table_page">
|
73 |
+
|
74 |
+
<h2 class="nav-tab-wrapper" id="wpseo-tabs">
|
75 |
+
<a class="nav-tab" id="title-tab" href="#top#title"><?php esc_html_e( 'Title', 'wordpress-seo' ); ?></a>
|
76 |
+
<a class="nav-tab" id="description-tab"
|
77 |
+
href="#top#description"><?php esc_html_e( 'Description', 'wordpress-seo' ); ?></a>
|
78 |
+
</h2>
|
79 |
+
|
80 |
+
<?php wpseo_render_help_center(); ?>
|
81 |
+
|
82 |
+
<div class="tabwrapper">
|
83 |
+
<?php wpseo_get_rendered_tab( $wpseo_bulk_titles_table, 'title' ); ?>
|
84 |
+
<?php wpseo_get_rendered_tab( $wpseo_bulk_description_table, 'description' ); ?>
|
85 |
+
</div>
|
86 |
+
</div>
|
admin/views/tool-file-editor.php
ADDED
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
7 |
+
header( 'Status: 403 Forbidden' );
|
8 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
9 |
+
exit();
|
10 |
+
}
|
11 |
+
|
12 |
+
$robots_file = get_home_path() . 'robots.txt';
|
13 |
+
$ht_access_file = get_home_path() . '.htaccess';
|
14 |
+
|
15 |
+
if ( isset( $_POST['create_robots'] ) ) {
|
16 |
+
if ( ! current_user_can( 'edit_files' ) ) {
|
17 |
+
$die_msg = sprintf(
|
18 |
+
/* translators: %s expands to robots.txt. */
|
19 |
+
__( 'You cannot create a %s file.', 'wordpress-seo' ),
|
20 |
+
'robots.txt'
|
21 |
+
);
|
22 |
+
die( esc_html( $die_msg ) );
|
23 |
+
}
|
24 |
+
|
25 |
+
check_admin_referer( 'wpseo_create_robots' );
|
26 |
+
|
27 |
+
ob_start();
|
28 |
+
error_reporting( 0 );
|
29 |
+
do_robots();
|
30 |
+
$robots_content = ob_get_clean();
|
31 |
+
|
32 |
+
$f = fopen( $robots_file, 'x' );
|
33 |
+
fwrite( $f, $robots_content );
|
34 |
+
}
|
35 |
+
|
36 |
+
if ( isset( $_POST['submitrobots'] ) ) {
|
37 |
+
if ( ! current_user_can( 'edit_files' ) ) {
|
38 |
+
$die_msg = sprintf(
|
39 |
+
/* translators: %s expands to robots.txt. */
|
40 |
+
__( 'You cannot edit the %s file.', 'wordpress-seo' ),
|
41 |
+
'robots.txt'
|
42 |
+
);
|
43 |
+
die( esc_html( $die_msg ) );
|
44 |
+
}
|
45 |
+
|
46 |
+
check_admin_referer( 'wpseo-robotstxt' );
|
47 |
+
|
48 |
+
if ( file_exists( $robots_file ) ) {
|
49 |
+
$robotsnew = stripslashes( $_POST['robotsnew'] );
|
50 |
+
if ( is_writable( $robots_file ) ) {
|
51 |
+
$f = fopen( $robots_file, 'w+' );
|
52 |
+
fwrite( $f, $robotsnew );
|
53 |
+
fclose( $f );
|
54 |
+
$msg = sprintf(
|
55 |
+
/* translators: %s expands to robots.txt. */
|
56 |
+
__( 'Updated %s', 'wordpress-seo' ),
|
57 |
+
'robots.txt'
|
58 |
+
);
|
59 |
+
}
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
if ( isset( $_POST['submithtaccess'] ) ) {
|
64 |
+
if ( ! current_user_can( 'edit_files' ) ) {
|
65 |
+
$die_msg = sprintf(
|
66 |
+
/* translators: %s expands to ".htaccess". */
|
67 |
+
__( 'You cannot edit the %s file.', 'wordpress-seo' ),
|
68 |
+
'.htaccess'
|
69 |
+
);
|
70 |
+
die( esc_html( $die_msg ) );
|
71 |
+
}
|
72 |
+
|
73 |
+
check_admin_referer( 'wpseo-htaccess' );
|
74 |
+
|
75 |
+
if ( file_exists( $ht_access_file ) ) {
|
76 |
+
$ht_access_new = stripslashes( $_POST['htaccessnew'] );
|
77 |
+
if ( is_writeable( $ht_access_file ) ) {
|
78 |
+
$f = fopen( $ht_access_file, 'w+' );
|
79 |
+
fwrite( $f, $ht_access_new );
|
80 |
+
fclose( $f );
|
81 |
+
}
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
if ( isset( $msg ) && ! empty( $msg ) ) {
|
86 |
+
echo '<div id="message" class="updated fade"><p>', esc_html( $msg ), '</p></div>';
|
87 |
+
}
|
88 |
+
|
89 |
+
if ( is_multisite() ) {
|
90 |
+
$action_url = network_admin_url( 'admin.php?page=wpseo_files' );
|
91 |
+
}
|
92 |
+
else {
|
93 |
+
$action_url = admin_url( 'admin.php?page=wpseo_tools&tool=file-editor' );
|
94 |
+
}
|
95 |
+
|
96 |
+
echo '<br><br>';
|
97 |
+
$helpcenter_tab = new WPSEO_Option_Tab( 'bulk-editor', __( 'Bulk editor', 'wordpress-seo' ),
|
98 |
+
array( 'video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-tools-file-editor' ) ) );
|
99 |
+
|
100 |
+
$helpcenter = new WPSEO_Help_Center( 'bulk-editor', $helpcenter_tab, WPSEO_Utils::is_yoast_seo_premium() );
|
101 |
+
$helpcenter->localize_data();
|
102 |
+
$helpcenter->mount();
|
103 |
+
|
104 |
+
// N.B.: "robots.txt" is a fixed file name and should not be translatable.
|
105 |
+
echo '<h2>robots.txt</h2>';
|
106 |
+
|
107 |
+
|
108 |
+
if ( ! file_exists( $robots_file ) ) {
|
109 |
+
if ( is_writable( get_home_path() ) ) {
|
110 |
+
echo '<form action="', esc_url( $action_url ), '" method="post" id="robotstxtcreateform">';
|
111 |
+
wp_nonce_field( 'wpseo_create_robots', '_wpnonce', true, true );
|
112 |
+
echo '<p>';
|
113 |
+
printf(
|
114 |
+
/* translators: %s expands to robots.txt. */
|
115 |
+
esc_html__( 'You don\'t have a %s file, create one here:', 'wordpress-seo' ),
|
116 |
+
'robots.txt'
|
117 |
+
);
|
118 |
+
echo '</p>';
|
119 |
+
|
120 |
+
printf(
|
121 |
+
'<input type="submit" class="button" name="create_robots" value="%s">',
|
122 |
+
sprintf(
|
123 |
+
/* translators: %s expands to robots.txt. */
|
124 |
+
esc_attr__( 'Create %s file', 'wordpress-seo' ),
|
125 |
+
'robots.txt'
|
126 |
+
)
|
127 |
+
);
|
128 |
+
echo '</form>';
|
129 |
+
}
|
130 |
+
else {
|
131 |
+
echo '<p>';
|
132 |
+
printf(
|
133 |
+
/* translators: %s expands to robots.txt. */
|
134 |
+
esc_html__( 'If you had a %s file and it was editable, you could edit it from here.', 'wordpress-seo' ),
|
135 |
+
'robots.txt'
|
136 |
+
);
|
137 |
+
echo '</p>';
|
138 |
+
}
|
139 |
+
}
|
140 |
+
else {
|
141 |
+
$f = fopen( $robots_file, 'r' );
|
142 |
+
|
143 |
+
$content = '';
|
144 |
+
if ( filesize( $robots_file ) > 0 ) {
|
145 |
+
$content = fread( $f, filesize( $robots_file ) );
|
146 |
+
}
|
147 |
+
|
148 |
+
if ( ! is_writable( $robots_file ) ) {
|
149 |
+
echo '<p><em>';
|
150 |
+
printf(
|
151 |
+
/* translators: %s expands to robots.txt. */
|
152 |
+
esc_html__( 'If your %s were writable, you could edit it from here.', 'wordpress-seo' ),
|
153 |
+
'robots.txt'
|
154 |
+
);
|
155 |
+
echo '</em></p>';
|
156 |
+
echo '<textarea class="large-text code" disabled="disabled" rows="15" name="robotsnew">', esc_textarea( $content ), '</textarea><br/>';
|
157 |
+
}
|
158 |
+
else {
|
159 |
+
echo '<form action="', esc_url( $action_url ), '" method="post" id="robotstxtform">';
|
160 |
+
wp_nonce_field( 'wpseo-robotstxt', '_wpnonce', true, true );
|
161 |
+
echo '<p><label for="robotsnew" class="yoast-inline-label">';
|
162 |
+
printf(
|
163 |
+
/* translators: %s expands to robots.txt. */
|
164 |
+
esc_html__( 'Edit the content of your %s:', 'wordpress-seo' ),
|
165 |
+
'robots.txt'
|
166 |
+
);
|
167 |
+
echo '</label></p>';
|
168 |
+
echo '<textarea class="large-text code" rows="15" name="robotsnew" id="robotsnew">', esc_textarea( $content ), '</textarea><br/>';
|
169 |
+
printf(
|
170 |
+
'<div class="submit"><input class="button" type="submit" name="submitrobots" value="%s" /></div>',
|
171 |
+
sprintf(
|
172 |
+
/* translators: %s expands to robots.txt. */
|
173 |
+
esc_attr__( 'Save changes to %s', 'wordpress-seo' ),
|
174 |
+
'robots.txt'
|
175 |
+
)
|
176 |
+
);
|
177 |
+
echo '</form>';
|
178 |
+
}
|
179 |
+
}
|
180 |
+
if ( ( isset( $_SERVER['SERVER_SOFTWARE'] ) && stristr( $_SERVER['SERVER_SOFTWARE'], 'nginx' ) === false ) ) {
|
181 |
+
|
182 |
+
echo '<h2>';
|
183 |
+
printf(
|
184 |
+
/* translators: %s expands to ".htaccess". */
|
185 |
+
esc_html__( '%s file', 'wordpress-seo' ),
|
186 |
+
'.htaccess'
|
187 |
+
);
|
188 |
+
echo '</h2>';
|
189 |
+
|
190 |
+
if ( file_exists( $ht_access_file ) ) {
|
191 |
+
$f = fopen( $ht_access_file, 'r' );
|
192 |
+
|
193 |
+
$contentht = '';
|
194 |
+
if ( filesize( $ht_access_file ) > 0 ) {
|
195 |
+
$contentht = fread( $f, filesize( $ht_access_file ) );
|
196 |
+
}
|
197 |
+
|
198 |
+
if ( ! is_writable( $ht_access_file ) ) {
|
199 |
+
echo '<p><em>';
|
200 |
+
printf(
|
201 |
+
/* translators: %s expands to ".htaccess". */
|
202 |
+
esc_html__( 'If your %s were writable, you could edit it from here.', 'wordpress-seo' ),
|
203 |
+
'.htaccess'
|
204 |
+
);
|
205 |
+
echo '</em></p>';
|
206 |
+
echo '<textarea class="large-text code" disabled="disabled" rows="15" name="robotsnew">', esc_textarea( $contentht ), '</textarea><br/>';
|
207 |
+
}
|
208 |
+
else {
|
209 |
+
echo '<form action="', esc_url( $action_url ), '" method="post" id="htaccessform">';
|
210 |
+
wp_nonce_field( 'wpseo-htaccess', '_wpnonce', true, true );
|
211 |
+
echo '<p><label for="htaccessnew" class="yoast-inline-label">';
|
212 |
+
printf(
|
213 |
+
/* translators: %s expands to ".htaccess". */
|
214 |
+
esc_html__( 'Edit the content of your %s:', 'wordpress-seo' ),
|
215 |
+
'.htaccess'
|
216 |
+
);
|
217 |
+
echo '</label></p>';
|
218 |
+
echo '<textarea class="large-text code" rows="15" name="htaccessnew" id="htaccessnew">', esc_textarea( $contentht ), '</textarea><br/>';
|
219 |
+
printf(
|
220 |
+
'<div class="submit"><input class="button" type="submit" name="submithtaccess" value="%s" /></div>',
|
221 |
+
sprintf(
|
222 |
+
/* translators: %s expands to ".htaccess". */
|
223 |
+
esc_attr__( 'Save changes to %s', 'wordpress-seo' ),
|
224 |
+
'.htaccess'
|
225 |
+
)
|
226 |
+
);
|
227 |
+
echo '</form>';
|
228 |
+
}
|
229 |
+
}
|
230 |
+
else {
|
231 |
+
echo '<p>';
|
232 |
+
printf(
|
233 |
+
/* translators: %s expands to ".htaccess". */
|
234 |
+
esc_html__( 'If you had a %s file and it was editable, you could edit it from here.', 'wordpress-seo' ),
|
235 |
+
'.htaccess'
|
236 |
+
);
|
237 |
+
echo '</p>';
|
238 |
+
}
|
239 |
+
}
|
admin/views/tool-import-export.php
ADDED
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
7 |
+
header( 'Status: 403 Forbidden' );
|
8 |
+
header( 'HTTP/1.1 403 Forbidden' );
|
9 |
+
exit();
|
10 |
+
}
|
11 |
+
|
12 |
+
$yform = Yoast_Form::get_instance();
|
13 |
+
|
14 |
+
$replace = false;
|
15 |
+
$import = false;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* The import method is used to dermine if there should be something imported.
|
19 |
+
*
|
20 |
+
* In case of POST the user is on the Yoast SEO import page and in case of the GET the user sees a notice from
|
21 |
+
* Yoast SEO that we can import stuff for that plugin.
|
22 |
+
*/
|
23 |
+
if ( filter_input( INPUT_POST, 'import' ) || filter_input( INPUT_GET, 'import' ) ) {
|
24 |
+
|
25 |
+
check_admin_referer( 'wpseo-import' );
|
26 |
+
|
27 |
+
$post_wpseo = filter_input( INPUT_POST, 'wpseo', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
|
28 |
+
$replace = ( ! empty( $post_wpseo['deleteolddata'] ) && $post_wpseo['deleteolddata'] === 'on' );
|
29 |
+
|
30 |
+
if ( ! empty( $post_wpseo['importwoo'] ) ) {
|
31 |
+
$import = new WPSEO_Import_WooThemes_SEO( $replace );
|
32 |
+
}
|
33 |
+
|
34 |
+
if ( ! empty( $post_wpseo['importaioseo'] ) || filter_input( INPUT_GET, 'importaioseo' ) ) {
|
35 |
+
$import = new WPSEO_Import_AIOSEO( $replace );
|
36 |
+
}
|
37 |
+
|
38 |
+
if ( ! empty( $post_wpseo['importheadspace'] ) ) {
|
39 |
+
$import = new WPSEO_Import_External( $replace );
|
40 |
+
$import->import_headspace();
|
41 |
+
}
|
42 |
+
|
43 |
+
if ( ! empty( $post_wpseo['importjetpackseo'] ) || filter_input( INPUT_GET, 'importjetpackseo' ) ) {
|
44 |
+
$import = new WPSEO_Import_Jetpack_SEO( $replace );
|
45 |
+
}
|
46 |
+
|
47 |
+
if ( ! empty( $post_wpseo['importwpseo'] ) || filter_input( INPUT_GET, 'importwpseo' ) ) {
|
48 |
+
$import = new WPSEO_Import_WPSEO( $replace );
|
49 |
+
}
|
50 |
+
|
51 |
+
if ( ! empty( $post_wpseo['importseoultimate'] ) || filter_input( INPUT_GET, 'importseoultimate' ) ) {
|
52 |
+
$import = new WPSEO_Import_Ultimate_SEO( $replace );
|
53 |
+
}
|
54 |
+
|
55 |
+
if ( ! empty( $post_wpseo['importseopressor'] ) || filter_input( INPUT_GET, 'importseopressor' ) ) {
|
56 |
+
$import = new WPSEO_Import_SEOPressor( $replace );
|
57 |
+
}
|
58 |
+
}
|
59 |
+
|
60 |
+
if ( isset( $_FILES['settings_import_file'] ) ) {
|
61 |
+
check_admin_referer( 'wpseo-import-file' );
|
62 |
+
|
63 |
+
$import = new WPSEO_Import();
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Allow custom import actions.
|
68 |
+
*
|
69 |
+
* @api bool|object $import Contains info about the handled import
|
70 |
+
*/
|
71 |
+
$import = apply_filters( 'wpseo_handle_import', $import );
|
72 |
+
|
73 |
+
if ( $import ) {
|
74 |
+
/**
|
75 |
+
* Allow customization of import&export message
|
76 |
+
*
|
77 |
+
* @api string $msg The message.
|
78 |
+
*/
|
79 |
+
$msg = apply_filters( 'wpseo_import_message', isset( $import->msg ) ? $import->msg : '' );
|
80 |
+
|
81 |
+
if ( ! empty( $msg ) ) {
|
82 |
+
// Check if we've deleted old data and adjust message to match it.
|
83 |
+
if ( $replace ) {
|
84 |
+
$msg .= ' ' . __( 'The old data of the imported plugin was deleted successfully.', 'wordpress-seo' );
|
85 |
+
}
|
86 |
+
|
87 |
+
$status = ( ! empty( $import->success ) ) ? 'updated' : 'error';
|
88 |
+
|
89 |
+
echo '<div id="message" class="message ', $status, '"><p>', $msg, '</p></div>';
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
$tabs = array(
|
94 |
+
'wpseo-import' => array(
|
95 |
+
'label' => __( 'Import settings', 'wordpress-seo' ),
|
96 |
+
'screencast_video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-tools-import-export' ),
|
97 |
+
),
|
98 |
+
'wpseo-export' => array(
|
99 |
+
'label' => __( 'Export settings', 'wordpress-seo' ),
|
100 |
+
'screencast_video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-tools-import-export' ),
|
101 |
+
),
|
102 |
+
'import-seo' => array(
|
103 |
+
'label' => __( 'Import from other SEO plugins', 'wordpress-seo' ),
|
104 |
+
'screencast_video_url' => WPSEO_Shortlinker::get( 'https://yoa.st/screencast-tools-import-export' ),
|
105 |
+
),
|
106 |
+
);
|
107 |
+
|
108 |
+
?>
|
109 |
+
<br/><br/>
|
110 |
+
|
111 |
+
<h2 class="nav-tab-wrapper" id="wpseo-tabs">
|
112 |
+
<?php foreach ( $tabs as $identifier => $tab ) : ?>
|
113 |
+
<a class="nav-tab" id="<?php echo esc_attr( $identifier . '-tab' ); ?>" href="<?php echo esc_url( '#top#' . $identifier ); ?>"><?php echo esc_html( $tab['label'] ); ?></a>
|
114 |
+
<?php endforeach; ?>
|
115 |
+
|
116 |
+
<?php
|
117 |
+
/**
|
118 |
+
* Allow adding a custom import tab header
|
119 |
+
*/
|
120 |
+
do_action( 'wpseo_import_tab_header' );
|
121 |
+
?>
|
122 |
+
</h2>
|
123 |
+
|
124 |
+
<?php
|
125 |
+
|
126 |
+
$helpcenter_tabs = new WPSEO_Option_Tabs( '', '' );
|
127 |
+
|
128 |
+
foreach ( $tabs as $identifier => $tab ) {
|
129 |
+
if ( ! empty( $tab['screencast_video_url'] ) ) {
|
130 |
+
$tab_video_url = $tab['screencast_video_url'];
|
131 |
+
|
132 |
+
$helpcenter_tab = new WPSEO_Option_Tab( $identifier, $tab['label'],
|
133 |
+
array( 'video_url' => $tab['screencast_video_url'] ) );
|
134 |
+
}
|
135 |
+
|
136 |
+
$helpcenter_tabs->add_tab( $helpcenter_tab );
|
137 |
+
}
|
138 |
+
|
139 |
+
$helpcenter = new WPSEO_Help_Center( '', $helpcenter_tabs, WPSEO_Utils::is_yoast_seo_premium() );
|
140 |
+
$helpcenter->localize_data();
|
141 |
+
$helpcenter->mount();
|
142 |
+
|
143 |
+
foreach ( $tabs as $identifier => $tab ) {
|
144 |
+
printf( '<div id="%s" class="wpseotab">', esc_attr( $identifier ) );
|
145 |
+
require_once WPSEO_PATH . 'admin/views/tabs/tool/' . $identifier . '.php';
|
146 |
+
echo '</div>';
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Allow adding a custom import tab
|
151 |
+
*/
|
152 |
+
do_action( 'wpseo_import_tab_content' );
|
admin/views/user-profile.php
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package WPSEO\Admin
|
4 |
+
*/
|
5 |
+
|
6 |
+
/* translators: %1$s expands to Yoast SEO */
|
7 |
+
$wpseo_up_settings_header = sprintf( __( '%1$s settings', 'wordpress-seo' ), 'Yoast SEO' );
|
8 |
+
|
9 |
+
?>
|
10 |
+
|
11 |
+
<div class="yoast yoast-settings">
|
12 |
+
|
13 |
+
<h2 id="wordpress-seo"><?php echo esc_html( $wpseo_up_settings_header ); ?></h2>
|
14 |
+
|
15 |
+
<label for="wpseo_author_title"><?php esc_html_e( 'Title to use for Author page', 'wordpress-seo' ); ?></label>
|
16 |
+
<input class="yoast-settings__text regular-text" type="text" id="wpseo_author_title" name="wpseo_author_title"
|
17 |
+
value="<?php echo esc_attr( get_the_author_meta( 'wpseo_title', $user->ID ) ); ?>"/><br>
|
18 |
+
|
19 |
+
<label for="wpseo_author_metadesc"><?php esc_html_e( 'Meta description to use for Author page', 'wordpress-seo' ); ?></label>
|
20 |
+
<textarea rows="5" cols="30" id="wpseo_author_metadesc"
|
21 |
+
class="yoast-settings__textarea yoast-settings__textarea--medium"
|
22 |
+
name="wpseo_author_metadesc"><?php echo esc_textarea( get_the_author_meta( 'wpseo_metadesc', $user->ID ) ); ?></textarea><br>
|
23 |
+
|
24 |
+
<input class="yoast-settings__checkbox double" type="checkbox" id="wpseo_noindex_author"
|
25 |
+
name="wpseo_noindex_author"
|
26 |
+
value="on" <?php echo ( get_the_author_meta( 'wpseo_noindex_author', $user->ID ) === 'on' ) ? 'checked' : ''; ?> />
|
27 |
+
<label class="yoast-label-strong"
|
28 |
+
for="wpseo_noindex_author"><?php printf( esc_html__( 'Do not allow search engines to show %s in search results.', 'wordpress-seo' ), __( 'this author\'s archives', 'wordpress-seo' ) ); ?></label><br>
|
29 |
+
|
30 |
+
<?php if ( WPSEO_Options::get( 'keyword_analysis_active', false ) ) : ?>
|
31 |
+
<input class="yoast-settings__checkbox double" type="checkbox" id="wpseo_keyword_analysis_disable"
|
32 |
+
name="wpseo_keyword_analysis_disable" aria-describedby="wpseo_keyword_analysis_disable_desc"
|
33 |
+
value="on" <?php echo ( get_the_author_meta( 'wpseo_keyword_analysis_disable', $user->ID ) === 'on' ) ? 'checked' : ''; ?> />
|
34 |
+
<label class="yoast-label-strong"
|
35 |
+
for="wpseo_keyword_analysis_disable"><?php esc_html_e( 'Disable SEO analysis', 'wordpress-seo' ); ?></label>
|
36 |
+
<br>
|
37 |
+
<p class="description" id="wpseo_keyword_analysis_disable_desc">
|
38 |
+
<?php esc_html_e( 'Removes the keyword tab from the metabox and disables all SEO-related suggestions.', 'wordpress-seo' ); ?>
|
39 |
+
</p>
|
40 |
+
<?php endif; ?>
|
41 |
+
|
42 |
+
<?php if ( WPSEO_Options::get( 'content_analysis_active', false ) ) : ?>
|
43 |
+
<input class="yoast-settings__checkbox double" type="checkbox" id="wpseo_content_analysis_disable"
|
44 |
+
name="wpseo_content_analysis_disable" aria-describedby="wpseo_content_analysis_disable_desc"
|
45 |
+
value="on" <?php echo ( get_the_author_meta( 'wpseo_content_analysis_disable', $user->ID ) === 'on' ) ? 'checked' : ''; ?> />
|
46 |
+
<label class="yoast-label-strong"
|
47 |
+
for="wpseo_content_analysis_disable"><?php esc_html_e( 'Disable readability analysis', 'wordpress-seo' ); ?></label>
|
48 |
+
<br>
|
49 |
+
<p class="description" id="wpseo_content_analysis_disable_desc">
|
50 |
+
<?php esc_html_e( 'Removes the readability tab from the metabox and disables all readability-related suggestions.', 'wordpress-seo' ); ?>
|
51 |
+
</p>
|
52 |
+
<?php endif; ?>
|
53 |
+
</div>
|
admin/watchers/class-slug-change-watcher.php
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/** @package WPSEO\Admin\Watchers */
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Class WPSEO_Slug_Change_Watcher
|
6 |
+
*/
|
7 |
+
class WPSEO_Slug_Change_Watcher implements WPSEO_WordPress_Integration {
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Registers all hooks to WordPress.
|
11 |
+
*
|
12 |
+
* @return void
|
13 |
+
*/
|
14 |
+
public function register_hooks() {
|
15 |
+
|
16 |
+
// If the current plugin is Yoast SEO Premium, stop registering.
|
17 |
+
if ( WPSEO_Utils::is_yoast_seo_premium() ) {
|
18 |
+
return;
|
19 |
+
}
|
20 |
+
|
21 |
+
// Detect a post slug change.
|
22 |
+
add_action( 'post_updated', array( $this, 'detect_slug_change' ), 12, 3 );
|
23 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Enqueues the quick edit handler.
|
28 |
+
*/
|
29 |
+
public function enqueue_assets() {
|
30 |
+
global $pagenow;
|
31 |
+
|
32 |
+
if ( ! in_array( $pagenow, array( 'edit.php' ), true ) ) {
|
33 |
+
return;
|
34 |
+
}
|
35 |
+
|
36 |
+
$asset_manager = new WPSEO_Admin_Asset_Manager();
|
37 |
+
$asset_manager->enqueue_script( 'quick-edit-handler' );
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Detects if the slug changed, hooked into 'post_updated'.
|
42 |
+
*
|
43 |
+
* @param integer $post_id The ID of the post. Unused.
|
44 |
+
* @param WP_Post $post The post with the new values.
|
45 |
+
* @param WP_Post $post_before The post with the previous values.
|
46 |
+
*
|
47 |
+
* @return void
|
48 |
+
*/
|
49 |
+
public function detect_slug_change( $post_id, $post, $post_before ) {
|
50 |
+
// If post is a revision do not advise creating a redirect.
|
51 |
+
if ( wp_is_post_revision( $post_before ) && wp_is_post_revision( $post ) ) {
|
52 |
+
return;
|
53 |
+
}
|
54 |
+
|
55 |
+
// There is no slug change.
|
56 |
+
if ( $post->post_name === $post_before->post_name ) {
|
57 |
+
return;
|
58 |
+
}
|
59 |
+
|
60 |
+
// If the post URL wasn't visible before, or isn't visible now, don't advise creating a redirect.
|
61 |
+
if ( ! $this->check_visible_post_status( $post_before->post_status ) || ! $this->check_visible_post_status( $post->post_status ) ) {
|
62 |
+
return;
|
63 |
+
}
|
64 |
+
|
65 |
+
$post_type_object = get_post_type_object( $post->post_type );
|
66 |
+
|
67 |
+
// If the post type of this post wasn't registered default back to post.
|
68 |
+
if ( $post_type_object === null ) {
|
69 |
+
$post_type_object = get_post_type_object( 'post' );
|
70 |
+
}
|
71 |
+
|
72 |
+
$this->add_notification( $post_type_object->labels->singular_name );
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Checks whether the given post status is visible or not.
|
77 |
+
*
|
78 |
+
* @param string $post_status The post status to check.
|
79 |
+
*
|
80 |
+
* @return bool Whether or not the post is visible.
|
81 |
+
*/
|
82 |
+
protected function check_visible_post_status( $post_status ) {
|
83 |
+
$visible_post_statuses = array(
|
84 |
+
'publish',
|
85 |
+
'static',
|
86 |
+
'private',
|
87 |
+
);
|
88 |
+
|
89 |
+
return in_array( $post_status, $visible_post_statuses, true );
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Adds a notification to be shown on the next page request since posts are updated in an ajax request.
|
94 |
+
*
|
95 |
+
* @param string $post_type_label The singular_name label from a post_type_object.
|
96 |
+
*
|
97 |
+
* @return void
|
98 |
+
*/
|
99 |
+
protected function add_notification( $post_type_label ) {
|
100 |
+
$notification = new Yoast_Notification(
|
101 |
+
sprintf(
|
102 |
+
/* translators: %1$s expands to the translated name of the post type, %2$s expands to the anchor opening tag, %3$s to the anchor closing tag. */
|
103 |
+
__(
|
104 |
+
'You just changed the URL of this %1$s. To ensure your visitors do not see a 404 on the old URL, you should create a redirect. %2$sLearn how to create redirects here.%3$s',
|
105 |
+
'wordpress-seo'
|
106 |
+
),
|
107 |
+
$post_type_label,
|
108 |
+
'<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/1d0' ) . '" target="_blank">',
|
109 |
+
'</a>'
|
110 |
+
), array( 'type' => 'notice-info' )
|
111 |
+
);
|
112 |
+
|
113 |
+
$notification_center = Yoast_Notification_Center::get();
|
114 |
+
$notification_center->add_notification( $notification );
|
115 |
+
}
|
116 |
+
}
|
css/dist/admin-global-700-rtl.min.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
.wpseo-premium-indicator{display:inline-block;width:1px;height:1px}#adminmenu .wpseo-premium-indicator{margin:-2px 2px -3px 0;color:inherit}.wpseo-premium-indicator svg{display:none;width:auto;height:100%}.yoast-tooltip{position:relative}.yoast-tooltip::after,.yoast-tooltip::before{display:none;position:absolute;opacity:0;pointer-events:none}button.yoast-tooltip{overflow:visible}.yoast-tooltip::after{z-index:1000000;padding:6px 8px 5px;border-radius:3px;color:#fff;background:rgba(0,0,0,.8);text-shadow:none;font:normal normal 11px/1.45454545 Helvetica,arial,nimbussansl,liberationsans,freesans,clean,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";text-align:center;white-space:pre;text-decoration:none;letter-spacing:normal;text-transform:none;word-wrap:break-word;content:attr(aria-label);-webkit-font-smoothing:subpixel-antialiased}.yoast-tooltip-alt::after{content:attr(data-label)}.yoast-tooltip::before{z-index:1000001;width:0;height:0;border:5px solid transparent;color:rgba(0,0,0,.8);content:"\00a0"}@keyframes yoast-tooltip-appear{from{opacity:0}to{opacity:1}}.yoast-tooltip:active::after,.yoast-tooltip:active::before,.yoast-tooltip:focus::after,.yoast-tooltip:focus::before,.yoast-tooltip:hover::after,.yoast-tooltip:hover::before{display:inline-block;text-decoration:none;animation-name:yoast-tooltip-appear;animation-duration:.1s;animation-timing-function:ease-in;animation-delay:.4s;animation-fill-mode:forwards}.yoast-tooltip-no-delay:active::after,.yoast-tooltip-no-delay:active::before,.yoast-tooltip-no-delay:focus::after,.yoast-tooltip-no-delay:focus::before,.yoast-tooltip-no-delay:hover::after,.yoast-tooltip-no-delay:hover::before{opacity:1;animation:none}.yoast-tooltip-multiline:active::after,.yoast-tooltip-multiline:focus::after,.yoast-tooltip-multiline:hover::after{display:table-cell}.yoast-tooltip-s::after,.yoast-tooltip-se::after,.yoast-tooltip-sw::after{top:100%;left:50%;margin-top:5px}.yoast-tooltip-s::before,.yoast-tooltip-se::before,.yoast-tooltip-sw::before{top:auto;left:50%;bottom:-5px;margin-left:-5px;border-bottom-color:rgba(0,0,0,.8)}.yoast-tooltip-se::after{left:auto;right:50%;margin-right:-15px}.yoast-tooltip-sw::after{margin-left:-15px}.yoast-tooltip-n::after,.yoast-tooltip-ne::after,.yoast-tooltip-nw::after{left:50%;bottom:100%;margin-bottom:5px}.yoast-tooltip-n::before,.yoast-tooltip-ne::before,.yoast-tooltip-nw::before{top:-5px;left:50%;bottom:auto;margin-left:-5px;border-top-color:rgba(0,0,0,.8)}.yoast-tooltip-ne::after{left:auto;right:50%;margin-right:-15px}.yoast-tooltip-nw::after{margin-left:-15px}.yoast-tooltip-n::after,.yoast-tooltip-s::after{-ms-transform:translateX(-50%);transform:translateX(-50%)}.yoast-tooltip-w::after{left:100%;bottom:50%;margin-left:5px;-ms-transform:translateY(50%);transform:translateY(50%)}.yoast-tooltip-w::before{top:50%;bottom:50%;right:-5px;margin-top:-5px;border-right-color:rgba(0,0,0,.8)}.yoast-tooltip-e::after{bottom:50%;right:100%;margin-right:5px;-ms-transform:translateY(50%);transform:translateY(50%)}.yoast-tooltip-e::before{top:50%;left:-5px;bottom:50%;margin-top:-5px;border-left-color:rgba(0,0,0,.8)}.yoast-tooltip-multiline::after{width:250px;width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:250px;border-collapse:separate;white-space:pre-line;word-wrap:normal;word-break:break-word}.yoast-tooltip-multiline.yoast-tooltip-n::after,.yoast-tooltip-multiline.yoast-tooltip-s::after{left:auto;right:50%;-ms-transform:translateX(50%);transform:translateX(50%)}.yoast-tooltip-multiline.yoast-tooltip-e::after,.yoast-tooltip-multiline.yoast-tooltip-w::after{left:100%}@media screen and (min-width:0\0){.yoast-tooltip-multiline::after{width:250px}}.yoast-tooltip-sticky::after,.yoast-tooltip-sticky::before{display:inline-block}.yoast-tooltip-sticky.yoast-tooltip-multiline::after{display:table-cell}@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min--moz-device-pixel-ratio:2),only screen and (-moz-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2),only screen and (min-resolution:192dpi),only screen and (min-resolution:2dppx){.yoast-tooltip-w::after{margin-left:4.5px}}.yoast-tooltip.yoast-tooltip-hidden::after,.yoast-tooltip.yoast-tooltip-hidden::before{display:none}.yoast-measure{max-width:600px}#TB_window .wpseo_content_wrapper p{font-size:14px;font-style:normal}#TB_window .wpseo_content_wrapper label{margin:0 0 0 10px;font-size:14px;font-weight:600}.wpseo-premium-popup-title{margin:1em 0!important;padding:0!important;font-size:1.3em!important;font-weight:600!important}.wpseo-premium-popup-icon{margin:10px}.edit-tags-php .column-description img{max-width:100%;height:auto}.select2-search__field{margin:0}.select2-results__option,.select2-search--inline,.select2-selection__choice{margin-bottom:0}.select2-container .select2-search--inline .select2-search__field{margin-top:6px!important;line-height:inherit}.yoast-label-strong{font-weight:600}.yoast-video-container-max-width{max-width:560px}.yoast-video-container{overflow:hidden;position:relative;height:0;padding-bottom:56.25%}.yoast-video-container iframe{position:absolute;top:0;right:0;width:100%;height:100%}.yoast-settings{margin-bottom:2em;padding-right:220px}.yoast-settings h2{margin-bottom:0;margin-right:-220px}.yoast-settings label{display:inline-block;width:200px;margin-left:6px;margin-right:-220px;padding-top:4px;padding-left:10px;color:#23282d;font-size:14px;font-weight:600;line-height:1.3;vertical-align:top}.yoast .yoast-settings__checkbox,.yoast .yoast-settings__radio,.yoast-settings fieldset,.yoast-settings input[type=text],.yoast-settings label,.yoast-settings select,.yoast-settings textarea{margin-top:2em;margin-bottom:.5em}.yoast-settings__textarea--medium{width:100%;max-width:600px}.yoast .yoast-settings__checkbox,.yoast .yoast-settings__radio{position:relative;top:1px;vertical-align:top}.yoast-settings__group--checkbox,.yoast-settings__group--radio{padding-top:1em}.yoast-settings__group--checkbox .yoast-settings__checkbox,.yoast-settings__group--radio .yoast-settings__radio{margin:0 0 10px 4px}.yoast-settings__checkbox+label,.yoast-settings__radio+label{width:auto;max-width:calc(100% - 25px);margin-left:0;margin-right:0;padding:0}.yoast-settings__group--checkbox .yoast-settings__checkbox+label,.yoast-settings__group--radio .yoast-settings__radio+label{margin-top:0;margin-bottom:10px;font-weight:400}.yoast-settings legend{color:#23282d;font-size:14px;font-weight:600}.yoast-settings .description{margin-top:0;font-size:14px}td .wpseo-score-icon{display:inline-block;width:12px;height:12px;margin-right:5px;border-radius:50%;background:#888;line-height:16px;margin-top:3px}.fixed th.column-wpseo-linked,.fixed th.column-wpseo-links,.fixed th.column-wpseo-score,.fixed th.column-wpseo-score-readability{width:3em;padding:0}th.column-wpseo-linked a,th.column-wpseo-links a,th.column-wpseo-score .yoast-tooltip,th.column-wpseo-score-readability .yoast-tooltip{display:inline-block;overflow:visible;padding:8px 0;vertical-align:middle}th.column-wpseo-score .yoast-tooltip,th.column-wpseo-score-readability .yoast-tooltip{padding:8px 11px}.column-wpseo-links .yoast-tooltip-multiline::after{width:999px;max-width:160px}.column-wpseo-linked .yoast-tooltip-multiline::after{width:999px;max-width:170px}.yoast-column-header-has-tooltip{position:relative}.manage-column .yoast-column-header-has-tooltip:before{display:inline-block;width:20px;height:20px;padding:0;color:#444;vertical-align:top;text-decoration:none!important;content:""}.manage-column .yoast-linked-to:before{background:url(../../images/link-out-icon.svg) 100% 0 no-repeat;background-size:20px}.manage-column .yoast-linked-from:before{background:url(../../images/link-in-icon.svg) 100% 0 no-repeat;background-size:20px}.manage-column .yoast-column-seo-score:before{background:url(../../images/Yoast_SEO_negative_icon.svg) 100% 0 no-repeat;background-size:20px}.manage-column .yoast-column-readability:before{background:url(../../images/readability-icon.svg) 100% 0 no-repeat;background-size:20px}td.column-wpseo-linked,td.column-wpseo-links{word-wrap:normal}#screen-meta .yoast-column-header-has-tooltip .screen-reader-text{visibility:visible;position:static;width:auto;height:auto}@media screen and (max-width:782px){.yoast-settings{padding-right:0}.yoast-settings h2{margin-right:0}.yoast-settings label{width:auto;margin-left:0;margin-right:0;padding:0}.yoast .yoast-settings__radio,.yoast-settings__radio+label{margin-bottom:1em}.yoast-settings__checkbox+label,.yoast-settings__radio+label{max-width:calc(100% - 35px);padding-top:8px}.yoast-settings__group--checkbox .yoast-settings__checkbox+label,.yoast-settings__group--radio .yoast-settings__radio+label{padding-top:4px}.yoast-settings input[type=text],.yoast-settings select,.yoast-settings textarea{display:block;box-sizing:border-box;width:100%;max-width:none;margin-top:0;margin-bottom:0;padding:7px 10px;line-height:1.5}}#yoast-help-center-container{margin:16px 0 24px}.react-tabs__tab-panel{max-width:900px;margin:0 auto}.react-tabs__tab-panel li{max-width:none!important}.yoast-help-center-open #sidebar-container{display:none}.contact-premium-support{text-align:center}.contact-premium-support__content{margin:0 auto 1.5em;font-size:.9375rem;line-height:1.4}.contact-premium-support__content:nth-child(2){max-width:610px}.contact-premium-support__content:nth-child(3){max-width:560px}.contact-premium-support .contact-premium-support__button{margin-bottom:48px}.wpseo-premium-description{margin-top:.5em}.wpseo-premium-advantages-list{padding-right:1.5em;list-style:disc}.yoast_help.yoast-help-button{overflow:visible;position:relative;width:20px;height:20px;margin:0;padding:0;border:0;outline:0;color:#72777c;background:0 0;box-shadow:none;vertical-align:top;cursor:pointer}.help-button-inline .yoast_help.yoast-help-button{margin-top:-4px}.yoast-section .yoast_help.yoast-help-button{float:left;margin-top:-44px}.wpseo-admin-page .yoast_help.yoast-help-button{margin-left:6px}.yoast_help .yoast-help-icon::before{position:absolute;top:0;right:0;padding:4px;content:"\f223"}.yoast_help.yoast-help-button:focus,.yoast_help.yoast-help-button:hover{color:#0073aa}.assessment-results__mark:focus,.yoast_help.yoast-help-button:focus .yoast-help-icon::before{border-radius:100%;box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.yoast-help-panel{display:none;clear:both;max-width:30em!important;padding:0 0 1em;font-weight:400;white-space:normal}.wpseo-admin-page .yoast-help-panel{max-width:600px!important}.copy-home-meta-description .yoast-help-panel{max-width:400px!important}
|
css/dist/admin-global-700.min.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
.wpseo-premium-indicator{display:inline-block;width:1px;height:1px}#adminmenu .wpseo-premium-indicator{margin:-2px 0 -3px 2px;color:inherit}.wpseo-premium-indicator svg{display:none;width:auto;height:100%}.yoast-tooltip{position:relative}.yoast-tooltip::after,.yoast-tooltip::before{display:none;position:absolute;opacity:0;pointer-events:none}button.yoast-tooltip{overflow:visible}.yoast-tooltip::after{z-index:1000000;padding:6px 8px 5px;border-radius:3px;color:#fff;background:rgba(0,0,0,.8);text-shadow:none;font:normal normal 11px/1.45454545 Helvetica,arial,nimbussansl,liberationsans,freesans,clean,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";text-align:center;white-space:pre;text-decoration:none;letter-spacing:normal;text-transform:none;word-wrap:break-word;content:attr(aria-label);-webkit-font-smoothing:subpixel-antialiased}.yoast-tooltip-alt::after{content:attr(data-label)}.yoast-tooltip::before{z-index:1000001;width:0;height:0;border:5px solid transparent;color:rgba(0,0,0,.8);content:"\00a0"}@keyframes yoast-tooltip-appear{from{opacity:0}to{opacity:1}}.yoast-tooltip:active::after,.yoast-tooltip:active::before,.yoast-tooltip:focus::after,.yoast-tooltip:focus::before,.yoast-tooltip:hover::after,.yoast-tooltip:hover::before{display:inline-block;text-decoration:none;animation-name:yoast-tooltip-appear;animation-duration:.1s;animation-timing-function:ease-in;animation-delay:.4s;animation-fill-mode:forwards}.yoast-tooltip-no-delay:active::after,.yoast-tooltip-no-delay:active::before,.yoast-tooltip-no-delay:focus::after,.yoast-tooltip-no-delay:focus::before,.yoast-tooltip-no-delay:hover::after,.yoast-tooltip-no-delay:hover::before{opacity:1;animation:none}.yoast-tooltip-multiline:active::after,.yoast-tooltip-multiline:focus::after,.yoast-tooltip-multiline:hover::after{display:table-cell}.yoast-tooltip-s::after,.yoast-tooltip-se::after,.yoast-tooltip-sw::after{top:100%;right:50%;margin-top:5px}.yoast-tooltip-s::before,.yoast-tooltip-se::before,.yoast-tooltip-sw::before{top:auto;right:50%;bottom:-5px;margin-right:-5px;border-bottom-color:rgba(0,0,0,.8)}.yoast-tooltip-se::after{right:auto;left:50%;margin-left:-15px}.yoast-tooltip-sw::after{margin-right:-15px}.yoast-tooltip-n::after,.yoast-tooltip-ne::after,.yoast-tooltip-nw::after{right:50%;bottom:100%;margin-bottom:5px}.yoast-tooltip-n::before,.yoast-tooltip-ne::before,.yoast-tooltip-nw::before{top:-5px;right:50%;bottom:auto;margin-right:-5px;border-top-color:rgba(0,0,0,.8)}.yoast-tooltip-ne::after{right:auto;left:50%;margin-left:-15px}.yoast-tooltip-nw::after{margin-right:-15px}.yoast-tooltip-n::after,.yoast-tooltip-s::after{-ms-transform:translateX(50%);transform:translateX(50%)}.yoast-tooltip-w::after{right:100%;bottom:50%;margin-right:5px;-ms-transform:translateY(50%);transform:translateY(50%)}.yoast-tooltip-w::before{top:50%;bottom:50%;left:-5px;margin-top:-5px;border-left-color:rgba(0,0,0,.8)}.yoast-tooltip-e::after{bottom:50%;left:100%;margin-left:5px;-ms-transform:translateY(50%);transform:translateY(50%)}.yoast-tooltip-e::before{top:50%;right:-5px;bottom:50%;margin-top:-5px;border-right-color:rgba(0,0,0,.8)}.yoast-tooltip-multiline::after{width:250px;width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:250px;border-collapse:separate;white-space:pre-line;word-wrap:normal;word-break:break-word}.yoast-tooltip-multiline.yoast-tooltip-n::after,.yoast-tooltip-multiline.yoast-tooltip-s::after{right:auto;left:50%;-ms-transform:translateX(-50%);transform:translateX(-50%)}.yoast-tooltip-multiline.yoast-tooltip-e::after,.yoast-tooltip-multiline.yoast-tooltip-w::after{right:100%}@media screen and (min-width:0\0){.yoast-tooltip-multiline::after{width:250px}}.yoast-tooltip-sticky::after,.yoast-tooltip-sticky::before{display:inline-block}.yoast-tooltip-sticky.yoast-tooltip-multiline::after{display:table-cell}@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min--moz-device-pixel-ratio:2),only screen and (-moz-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2),only screen and (min-resolution:192dpi),only screen and (min-resolution:2dppx){.yoast-tooltip-w::after{margin-right:4.5px}}.yoast-tooltip.yoast-tooltip-hidden::after,.yoast-tooltip.yoast-tooltip-hidden::before{display:none}.yoast-measure{max-width:600px}#TB_window .wpseo_content_wrapper p{font-size:14px;font-style:normal}#TB_window .wpseo_content_wrapper label{margin:0 10px 0 0;font-size:14px;font-weight:600}.wpseo-premium-popup-title{margin:1em 0!important;padding:0!important;font-size:1.3em!important;font-weight:600!important}.wpseo-premium-popup-icon{margin:10px}.edit-tags-php .column-description img{max-width:100%;height:auto}.select2-search__field{margin:0}.select2-results__option,.select2-search--inline,.select2-selection__choice{margin-bottom:0}.select2-container .select2-search--inline .select2-search__field{margin-top:6px!important;line-height:inherit}.yoast-label-strong{font-weight:600}.yoast-video-container-max-width{max-width:560px}.yoast-video-container{overflow:hidden;position:relative;height:0;padding-bottom:56.25%}.yoast-video-container iframe{position:absolute;top:0;left:0;width:100%;height:100%}.yoast-settings{margin-bottom:2em;padding-left:220px}.yoast-settings h2{margin-bottom:0;margin-left:-220px}.yoast-settings label{display:inline-block;width:200px;margin-right:6px;margin-left:-220px;padding-top:4px;padding-right:10px;color:#23282d;font-size:14px;font-weight:600;line-height:1.3;vertical-align:top}.yoast .yoast-settings__checkbox,.yoast .yoast-settings__radio,.yoast-settings fieldset,.yoast-settings input[type=text],.yoast-settings label,.yoast-settings select,.yoast-settings textarea{margin-top:2em;margin-bottom:.5em}.yoast-settings__textarea--medium{width:100%;max-width:600px}.yoast .yoast-settings__checkbox,.yoast .yoast-settings__radio{position:relative;top:1px;vertical-align:top}.yoast-settings__group--checkbox,.yoast-settings__group--radio{padding-top:1em}.yoast-settings__group--checkbox .yoast-settings__checkbox,.yoast-settings__group--radio .yoast-settings__radio{margin:0 4px 10px 0}.yoast-settings__checkbox+label,.yoast-settings__radio+label{width:auto;max-width:calc(100% - 25px);margin-right:0;margin-left:0;padding:0}.yoast-settings__group--checkbox .yoast-settings__checkbox+label,.yoast-settings__group--radio .yoast-settings__radio+label{margin-top:0;margin-bottom:10px;font-weight:400}.yoast-settings legend{color:#23282d;font-size:14px;font-weight:600}.yoast-settings .description{margin-top:0;font-size:14px}td .wpseo-score-icon{display:inline-block;width:12px;height:12px;margin-left:5px;border-radius:50%;background:#888;line-height:16px;margin-top:3px}.fixed th.column-wpseo-linked,.fixed th.column-wpseo-links,.fixed th.column-wpseo-score,.fixed th.column-wpseo-score-readability{width:3em;padding:0}th.column-wpseo-linked a,th.column-wpseo-links a,th.column-wpseo-score .yoast-tooltip,th.column-wpseo-score-readability .yoast-tooltip{display:inline-block;overflow:visible;padding:8px 0;vertical-align:middle}th.column-wpseo-score .yoast-tooltip,th.column-wpseo-score-readability .yoast-tooltip{padding:8px 11px}.column-wpseo-links .yoast-tooltip-multiline::after{width:999px;max-width:160px}.column-wpseo-linked .yoast-tooltip-multiline::after{width:999px;max-width:170px}.yoast-column-header-has-tooltip{position:relative}.manage-column .yoast-column-header-has-tooltip:before{display:inline-block;width:20px;height:20px;padding:0;color:#444;vertical-align:top;text-decoration:none!important;content:""}.manage-column .yoast-linked-to:before{background:url(../../images/link-out-icon.svg) no-repeat;background-size:20px}.manage-column .yoast-linked-from:before{background:url(../../images/link-in-icon.svg) no-repeat;background-size:20px}.manage-column .yoast-column-seo-score:before{background:url(../../images/Yoast_SEO_negative_icon.svg) no-repeat;background-size:20px}.manage-column .yoast-column-readability:before{background:url(../../images/readability-icon.svg) no-repeat;background-size:20px}td.column-wpseo-linked,td.column-wpseo-links{word-wrap:normal}#screen-meta .yoast-column-header-has-tooltip .screen-reader-text{visibility:visible;position:static;width:auto;height:auto}@media screen and (max-width:782px){.yoast-settings{padding-left:0}.yoast-settings h2{margin-left:0}.yoast-settings label{width:auto;margin-right:0;margin-left:0;padding:0}.yoast .yoast-settings__radio,.yoast-settings__radio+label{margin-bottom:1em}.yoast-settings__checkbox+label,.yoast-settings__radio+label{max-width:calc(100% - 35px);padding-top:8px}.yoast-settings__group--checkbox .yoast-settings__checkbox+label,.yoast-settings__group--radio .yoast-settings__radio+label{padding-top:4px}.yoast-settings input[type=text],.yoast-settings select,.yoast-settings textarea{display:block;box-sizing:border-box;width:100%;max-width:none;margin-top:0;margin-bottom:0;padding:7px 10px;line-height:1.5}}#yoast-help-center-container{margin:16px 0 24px}.react-tabs__tab-panel{max-width:900px;margin:0 auto}.react-tabs__tab-panel li{max-width:none!important}.yoast-help-center-open #sidebar-container{display:none}.contact-premium-support{text-align:center}.contact-premium-support__content{margin:0 auto 1.5em;font-size:.9375rem;line-height:1.4}.contact-premium-support__content:nth-child(2){max-width:610px}.contact-premium-support__content:nth-child(3){max-width:560px}.contact-premium-support .contact-premium-support__button{margin-bottom:48px}.wpseo-premium-description{margin-top:.5em}.wpseo-premium-advantages-list{padding-left:1.5em;list-style:disc}.yoast_help.yoast-help-button{overflow:visible;position:relative;width:20px;height:20px;margin:0;padding:0;border:0;outline:0;color:#72777c;background:0 0;box-shadow:none;vertical-align:top;cursor:pointer}.help-button-inline .yoast_help.yoast-help-button{margin-top:-4px}.yoast-section .yoast_help.yoast-help-button{float:right;margin-top:-44px}.wpseo-admin-page .yoast_help.yoast-help-button{margin-right:6px}.yoast_help .yoast-help-icon::before{position:absolute;top:0;left:0;padding:4px;content:"\f223"}.yoast_help.yoast-help-button:focus,.yoast_help.yoast-help-button:hover{color:#0073aa}.assessment-results__mark:focus,.yoast_help.yoast-help-button:focus .yoast-help-icon::before{border-radius:100%;box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.yoast-help-panel{display:none;clear:both;max-width:30em!important;padding:0 0 1em;font-weight:400;white-space:normal}.wpseo-admin-page .yoast-help-panel{max-width:600px!important}.copy-home-meta-description .yoast-help-panel{max-width:400px!important}
|
css/dist/adminbar-700-rtl.min.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
.wpseo-score-icon{display:inline-block!important;float:right;width:12px!important;height:12px!important;border-radius:50%!important;background-color:#999}.wpseo-score-icon.good{background-color:#7ad03a}.wpseo-score-icon.ok{background-color:#ee7c1b}.wpseo-score-icon.bad{background-color:#dc3232}.wpseo-score-icon.na{background-color:#999}.wpseo-score-icon.noindex{background-color:#1e8cbe}.adminbar-seo-score{margin:10px 4px 0 10px!important}#wpadminbar .yoast-issue-added,#wpadminbar .yoast-issue-added:hover{position:absolute;top:32px;right:0;min-width:300px;padding:2px 8px;border-radius:10px 0 10px 10px;color:#fff;background-color:#a4286a;box-shadow:-1px 1px 1px 1px grey}#wpadminbar .yoast-issue-added{display:none}#wpadminbar .yoast-issue-counter{display:inline;padding:1px 6px 1px 7px!important;border-radius:50%;color:#fff}.yoast-issue-counter{background-color:#d54e21}#wpadminbar .yoast-logo.svg{float:right;width:26px;height:30px;background-image:url(data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgc3R5bGU9ImZpbGw6IzgyODc4YyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxnPjxnPjxnPjxnPjxwYXRoIGQ9Ik0yMDMuNiwzOTVjNi44LTE3LjQsNi44LTM2LjYsMC01NGwtNzkuNC0yMDRoNzAuOWw0Ny43LDE0OS40bDc0LjgtMjA3LjZIMTE2LjRjLTQxLjgsMC03NiwzNC4yLTc2LDc2VjM1N2MwLDQxLjgsMzQuMiw3Niw3Niw3NkgxNzNDMTg5LDQyNC4xLDE5Ny42LDQxMC4zLDIwMy42LDM5NXoiLz48L2c+PGc+PHBhdGggZD0iTTQ3MS42LDE1NC44YzAtNDEuOC0zNC4yLTc2LTc2LTc2aC0zTDI4NS43LDM2NWMtOS42LDI2LjctMTkuNCw0OS4zLTMwLjMsNjhoMjE2LjJWMTU0Ljh6Ii8+PC9nPjwvZz48cGF0aCBzdHJva2Utd2lkdGg9IjIuOTc0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIGQ9Ik0zMzgsMS4zbC05My4zLDI1OS4xbC00Mi4xLTEzMS45aC04OS4xbDgzLjgsMjE1LjJjNiwxNS41LDYsMzIuNSwwLDQ4Yy03LjQsMTktMTksMzcuMy01Myw0MS45bC03LjIsMXY3Nmg4LjNjODEuNywwLDExOC45LTU3LjIsMTQ5LjYtMTQyLjlMNDMxLjYsMS4zSDMzOHogTTI3OS40LDM2MmMtMzIuOSw5Mi02Ny42LDEyOC43LTEyNS43LDEzMS44di00NWMzNy41LTcuNSw1MS4zLTMxLDU5LjEtNTEuMWM3LjUtMTkuMyw3LjUtNDAuNywwLTYwbC03NS0xOTIuN2g1Mi44bDUzLjMsMTY2LjhsMTA1LjktMjk0aDU4LjFMMjc5LjQsMzYyeiIvPjwvZz48L2c+PC9zdmc+);background-repeat:no-repeat;background-position:100% 6px;background-size:20px}#wpadminbar #wp-admin-bar-wpseo-licenses .ab-item{color:#f18500}@media screen and (max-width:782px){#wpadminbar #wp-admin-bar-wpseo-menu{display:block;position:static}#wpadminbar .yoast-logo.svg{width:52px;height:46px;background-position:50% 8px;background-size:30px}#wpadminbar .yoast-logo+.yoast-issue-counter{margin-right:-10px}#wpadminbar .ab-sub-wrapper .yoast-issue-counter{position:relative;top:-5px;vertical-align:text-top}#wp-admin-bar-wpseo-menu.menupop .ab-sub-wrapper #wp-admin-bar-wpseo-kwresearch,#wp-admin-bar-wpseo-menu.menupop .ab-sub-wrapper #wp-admin-bar-wpseo-settings{display:none}#wpadminbar .yoast-issue-added{top:46px}}
|
css/dist/adminbar-700.min.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
.wpseo-score-icon{display:inline-block!important;float:left;width:12px!important;height:12px!important;border-radius:50%!important;background-color:#999}.wpseo-score-icon.good{background-color:#7ad03a}.wpseo-score-icon.ok{background-color:#ee7c1b}.wpseo-score-icon.bad{background-color:#dc3232}.wpseo-score-icon.na{background-color:#999}.wpseo-score-icon.noindex{background-color:#1e8cbe}.adminbar-seo-score{margin:10px 10px 0 4px!important}#wpadminbar .yoast-issue-added,#wpadminbar .yoast-issue-added:hover{position:absolute;top:32px;left:0;min-width:300px;padding:2px 8px;border-radius:0 10px 10px;color:#fff;background-color:#a4286a;box-shadow:1px 1px 1px 1px grey}#wpadminbar .yoast-issue-added{display:none}#wpadminbar .yoast-issue-counter{display:inline;padding:1px 7px 1px 6px!important;border-radius:50%;color:#fff}.yoast-issue-counter{background-color:#d54e21}#wpadminbar .yoast-logo.svg{float:left;width:26px;height:30px;background-image:url(data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgc3R5bGU9ImZpbGw6IzgyODc4YyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxnPjxnPjxnPjxnPjxwYXRoIGQ9Ik0yMDMuNiwzOTVjNi44LTE3LjQsNi44LTM2LjYsMC01NGwtNzkuNC0yMDRoNzAuOWw0Ny43LDE0OS40bDc0LjgtMjA3LjZIMTE2LjRjLTQxLjgsMC03NiwzNC4yLTc2LDc2VjM1N2MwLDQxLjgsMzQuMiw3Niw3Niw3NkgxNzNDMTg5LDQyNC4xLDE5Ny42LDQxMC4zLDIwMy42LDM5NXoiLz48L2c+PGc+PHBhdGggZD0iTTQ3MS42LDE1NC44YzAtNDEuOC0zNC4yLTc2LTc2LTc2aC0zTDI4NS43LDM2NWMtOS42LDI2LjctMTkuNCw0OS4zLTMwLjMsNjhoMjE2LjJWMTU0Ljh6Ii8+PC9nPjwvZz48cGF0aCBzdHJva2Utd2lkdGg9IjIuOTc0IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIGQ9Ik0zMzgsMS4zbC05My4zLDI1OS4xbC00Mi4xLTEzMS45aC04OS4xbDgzLjgsMjE1LjJjNiwxNS41LDYsMzIuNSwwLDQ4Yy03LjQsMTktMTksMzcuMy01Myw0MS45bC03LjIsMXY3Nmg4LjNjODEuNywwLDExOC45LTU3LjIsMTQ5LjYtMTQyLjlMNDMxLjYsMS4zSDMzOHogTTI3OS40LDM2MmMtMzIuOSw5Mi02Ny42LDEyOC43LTEyNS43LDEzMS44di00NWMzNy41LTcuNSw1MS4zLTMxLDU5LjEtNTEuMWM3LjUtMTkuMyw3LjUtNDAuNywwLTYwbC03NS0xOTIuN2g1Mi44bDUzLjMsMTY2LjhsMTA1LjktMjk0aDU4LjFMMjc5LjQsMzYyeiIvPjwvZz48L2c+PC9zdmc+);background-repeat:no-repeat;background-position:0 6px;background-size:20px}#wpadminbar #wp-admin-bar-wpseo-licenses .ab-item{color:#f18500}@media screen and (max-width:782px){#wpadminbar #wp-admin-bar-wpseo-menu{display:block;position:static}#wpadminbar .yoast-logo.svg{width:52px;height:46px;background-position:50% 8px;background-size:30px}#wpadminbar .yoast-logo+.yoast-issue-counter{margin-left:-10px}#wpadminbar .ab-sub-wrapper .yoast-issue-counter{position:relative;top:-5px;vertical-align:text-top}#wp-admin-bar-wpseo-menu.menupop .ab-sub-wrapper #wp-admin-bar-wpseo-kwresearch,#wp-admin-bar-wpseo-menu.menupop .ab-sub-wrapper #wp-admin-bar-wpseo-settings{display:none}#wpadminbar .yoast-issue-added{top:46px}}
|
css/dist/alerts-700-rtl.min.css
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
.screen-reader-text{overflow:hidden;clip:rect(1px,1px,1px,1px);position:absolute!important;width:1px;height:1px;padding:0;border:0;word-wrap:normal!important;-webkit-clip-path:inset(50%);clip-path:inset(50%)}.yoast-search-result-preview__heading{margin:0 -20px 15px;padding:8px 20px;border-bottom:1px solid #f7f7f7;color:#555;font-family:"Open Sans",sans-serif;font-size:.9rem;font-weight:300}.yoast-search-result-preview__field{overflow:hidden;position:relative;width:600px;cursor:pointer}.yoast-search-result-preview__field:after,.yoast-search-result-preview__field:before{display:table;content:" "}.yoast-search-result-preview__field--focus:before,.yoast-search-result-preview__field--hover:before{position:absolute;right:-3px;width:24px;height:24px;margin-top:-3px;background-size:25px;content:"";display:block}.yoast-search-result-preview__field:after{clear:both}.yoast-search-result-preview__field--hover:before{background-image:url("data:image/svg+xml;charset=utf8,_encode('<svg width=\"1792\" height=\"1792\" viewBox=\"0 0 1792 1792\" xmlns=\"http://www.w3.org/2000/svg\"><path fill=\"#646464\" d=\"M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z\" /></svg>')")}.yoast-search-result-preview__field--focus:before{background-image:url("data:image/svg+xml;charset=utf8,_encode('<svg width=\"1792\" height=\"1792\" viewBox=\"0 0 1792 1792\" xmlns=\"http://www.w3.org/2000/svg\"><path fill=\"#0066cd\" d=\"M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z\" /></svg>')")}.yoast-search-result-preview__title{overflow:hidden;margin:0;color:#1a0dab;font-size:18px;font-weight:400;line-height:1.2;white-space:nowrap;text-decoration:none;text-overflow:ellipsis}.yoast-search-result-preview__url{color:#006621;font-size:14px;font-style:normal;line-height:16px}.yoast-search-result-preview__date,.yoast-search-result-preview__description{font-size:13px;line-height:1.4}.yoast-search-result-preview__date{color:grey}.yoast-search-result-preview__description{color:#545454}.yoast-button,.yoast-button__edit{padding:8px 10px;border:1px solid #ccc;border-radius:4px;color:#555;background:#f7f7f7;font-size:.8rem;cursor:pointer}.yoast-button__edit{display:block;margin-top:1em;padding-right:32px;background:url("data:image/svg+xml;charset=utf8,_encode('<svg width=\"1792\" height=\"1792\" viewBox=\"0 0 1792 1792\" xmlns=\"http://www.w3.org/2000/svg\"><path fill=\"#555\" d=\"M491 1536l91-91-235-235-91 91v107h128v128h107zm523-928q0-22-22-22-10 0-17 7l-542 542q-7 7-7 17 0 22 22 22 10 0 17-7l542-542q7-7 7-17zm-54-192l416 416-832 832h-416v-416zm683 96q0 53-37 90l-166 166-416-416 166-165q36-38 90-38 53 0 91 38l235 234q37 39 37 91z\" /></svg>')") 8px center/16px 16px no-repeat #f7f7f7;background-size:16px}.yoast-search-result-form{overflow:hidden;background-color:#fff;font-family:Arial,sans-serif}.yoast-search-result-form__heading{margin:0 -20px 15px;padding:8px 20px;border-bottom:1px solid #f7f7f7;color:#555;font-family:"Open Sans",sans-serif;font-size:.9rem;font-weight:300}.yoast-search-result-form__container--focus:before,.yoast-search-result-form__container--hover:before{right:-3px;width:24px;height:24px;margin-top:15px;background-size:25px;display:block;position:absolute;content:""}.yoast-search-result-form__container--hover:before{background-image:url("data:image/svg+xml;charset=utf8,_encode('<svg width=\"1792\" height=\"1792\" viewBox=\"0 0 1792 1792\" xmlns=\"http://www.w3.org/2000/svg\"><path fill=\"#646464\" d=\"M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z\" /></svg>')")}.yoast-search-result-form__container--focus:before{background-image:url("data:image/svg+xml;charset=utf8,_encode('<svg width=\"1792\" height=\"1792\" viewBox=\"0 0 1792 1792\" xmlns=\"http://www.w3.org/2000/svg\"><path fill=\"#0066cd\" d=\"M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z\" /></svg>')")}.yoast-search-result-form__label{display:block;width:100%;margin-top:1em}.yoast-search-result-form__field{box-sizing:border-box;width:100%;border:1px solid #ddd}.yoast-search-result-form__description{display:block;box-sizing:border-box;width:100%;height:70px;border:1px solid #ddd}.yoast-search-result-form__progress{display:block;box-sizing:border-box;width:100%;height:8px;margin-top:5px;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}.yoast-search-result-form__progress::-webkit-progress-bar{border:1px solid #ddd;background-color:#f7f7f7}.yoast-search-result-form__close-button{margin-top:1em}.yoast-search-result-form__progress--bad{color:#dc3232}.yoast-search-result-form__progress--bad::-webkit-progress-value{background-color:#dc3232;transition:width 250ms}.yoast-search-result-form__progress--bad::-moz-progress-bar{background-color:#dc3232}.yoast-search-result-form__progress--bad--fallback{overflow:hidden;max-width:100%;background-color:#dc3232}.yoast-search-result-form__progress--ok{color:#ee7c1b}.yoast-search-result-form__progress--ok::-webkit-progress-value{background-color:#ee7c1b;transition:width 250ms}.yoast-search-result-form__progress--ok::-moz-progress-bar{background-color:#ee7c1b}.yoast-search-result-form__progress--ok--fallback{overflow:hidden;max-width:100%;background-color:#ee7c1b}.yoast-search-result-form__progress--good{color:#7ad03a}.yoast-search-result-form__progress--good::-webkit-progress-value{background-color:#7ad03a;transition:width 250ms}.yoast-search-result-form__progress--good::-moz-progress-bar{background-color:#7ad03a}.yoast-search-result-form__progress--good--fallback{overflow:hidden;max-width:100%;background-color:#7ad03a}body{margin:0;padding:0}#container{max-width:1660px;margin:0 auto}.yoast-wizard-body{box-sizing:border-box;width:80%;max-width:60em;margin:1rem auto 4rem}@media screen and (max-width:768px){.yoast-wizard-body{width:auto;margin:0}}.yoast-wizard__logo{display:block;margin:0 auto}.yoast-wizard{box-sizing:border-box;width:100%;min-height:20px;padding:2em;background:#fff;text-align:right}@media screen and (max-width:768px){.yoast-wizard{padding:1em 1em 2em}}.yoast-wizard--header{text-align:center}.yoast-wizard--header--page-title{margin:0 0 -16px;padding:0 8px;color:#a4286a;font-size:1.25em;font-weight:400;line-height:2.5;letter-spacing:.03em}.yoast-wizard--navigation{width:100%;text-align:left}.yoast-wizard--button{margin-top:1em}.yoast-wizard--button__next{margin-right:1em}.yoast-wizard--button__next button:focus,.yoast-wizard--button__next button:hover{background-color:#6c2548!important}.yoast-wizard--button__next button:focus div,.yoast-wizard--button__next button:hover div{background-color:transparent!important}.yoast-wizard--button__previous button:focus{background:#ddd!important}.yoast-wizard--button__previous button:focus:active{background:0 0!important}.yoast-wizard--button__previous button:focus>div>span,.yoast-wizard--button__previous button:hover>div>span{color:#000!important}.yoast-wizard--step__inactive div{pointer-events:none}@media screen and (max-width:768px){.yoast-wizard--header--page-title{font-size:1.5em;line-height:1.25}.yoast-wizard--step__active{overflow:hidden;width:38px}.yoast-wizard--step__active div{display:inline-block!important;vertical-align:middle}.yoast-wizard--step__active div>span>span{display:block!important;margin-right:-7px;padding-left:99px!important}.yoast-wizard--step__inactive{display:none!important}}.yoast-wizard--step--container:focus{outline:0}.yoast-wizard--step--container h1{margin:0;color:#a4286a;font-size:2.25em;font-weight:100;line-height:3.68rem;letter-spacing:.03em}@media screen and (max-width:768px){.yoast-wizard--step--container h1{font-size:2em;line-height:1.25}}.yoast-wizard--step--container h2{color:#a4286a;font-size:1.375em;font-weight:100}.yoast-wizard--stepper{width:100%;margin:auto}.yoast-wizard-overlay{position:absolute;z-index:10;top:0;right:0;width:100%;height:100%;opacity:.2;color:#fff;background-color:#000;text-align:center}.yoast-wizard-overlay-loader{position:relative}.yoast-wizard-container{position:relative;min-height:20px;border:1px solid #ccc;box-shadow:rgba(0,0,0,.15) 0 3px 10px,rgba(0,0,0,.2) 0 3px 10px;text-align:right}.yoast-wizard-container--no-navigation{margin-top:40px}.yoast-wizard-container--no-navigation .yoast-wizard{padding-top:3em}@media screen and (max-width:768px){.yoast-wizard-container{box-shadow:none}}.yoast-wizard-container fieldset{margin:1em 0;border:0}.yoast-wizard-text-input{padding-bottom:.5em;font-size:14px}.yoast-wizard-text-input-label{display:block;margin:.5em 0 0;font-size:14px;font-weight:700;cursor:pointer}.yoast-wizard-text-input [type=text]{box-sizing:border-box;width:100%;max-width:450px}.yoast-video-container-max-width,.yoast-wizard-content-container{max-width:560px}.yoast-wizard-field-description{font-weight:700}.yoast-wizard input{margin:.5em 0;padding:5px;font-size:14px;line-height:140%}.yoast-wizard label{cursor:pointer}.yoast-wizard input[type=radio]{margin:.3em 0 .3em .7em;vertical-align:middle}.yoast-wizard-input__explanation{margin-top:0;color:#555;font-style:italic}.yoast-wizard-input-radio{font-size:14px}.yoast-wizard-input-radio-option label{padding-top:2px}.yoast-wizard-input-radio-separator{padding:0}.yoast-wizard-input-radio-separator input{position:absolute;right:-9999em;width:1px;height:1px}.yoast-wizard-input-radio-separator input+label{float:right;width:30px!important;margin:0 0 5px 5px!important;padding:6px 3px;border:1px solid #ccc;font-family:Arial,Helvetica,sans-serif!important;font-size:18px!important;line-height:24px;text-align:center;cursor:pointer}.yoast-wizard-input-radio-separator input:checked+label{border:1px solid #a4286a;background-color:#fff;box-shadow:inset 0 0 0 2px #a4286a}.yoast-wizard-input-radio-separator input:checked+label,.yoast-wizard-input-radio-separator input:focus+label{border-radius:10px 10px 10px 0}.yoast-video-container{overflow:hidden;position:relative;height:0;padding-bottom:56.25%}.yoast-video-container iframe{position:absolute;top:0;right:0;width:100%;height:100%}.yoast-wizard-notice__error{margin-bottom:15px;padding:12px;border-right:4px solid #dc3232;background:#fff;box-shadow:0 1px 1px 0 rgba(0,0,0,.1)}.yoast-wizard-content-container.yoast-wizard-content-container__is-full-width{max-width:none}@keyframes heartbeat{0%{opacity:.4;transform:scale(.7)}80%{opacity:1}100%{opacity:1;transform:scale(.95)}}.yoast-loader{animation:heartbeat 1.15s infinite;animation-timing-function:cubic-bezier(.96,.02,.63,.86);animation-direction:alternate}.yoast-alert{padding:0 12px;border-right:4px solid #fff;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.2)}.yoast-alerts .yoast-alert-holder{margin-bottom:.8em}.yoast-alerts .yoast-alert{width:100%}.yoast-container__alert .yoast-alert{border-right-color:#dc3232}#yoast-alerts-dismissed .yoast-alert{border-right-color:#d93f69}.yoast-container__warning .yoast-alert{border-right-color:#5d237a}#yoast-warnings-dismissed .yoast-alert{border-right-color:#0075b3}.yoast-container{position:relative;max-width:1280px;margin:20px 0 1px;padding:20px 20px 0;border:1px solid #e5e5e5;background-color:#fdfdfd;box-shadow:0 1px 1px rgba(0,0,0,.04)}.yoast-alerts>h2:first-child{margin:0;padding:9px 0 4px;font-size:23px;font-weight:400;line-height:29px}.yoast-alerts .yoast-container h3{margin:-20px -20px 0;padding:1em;border-bottom:1px solid #ccc;background-color:#fdfdfd;font-size:1.4em}h3 .dashicons-warning{color:#dc3232}.yoast-container .container{max-width:980px}.yoast-container .yoast-alert-holder{display:-ms-flexbox;display:flex}.dismiss .dashicons,.restore .dashicons{width:24px;height:24px;font-size:24px}.yoast-bottom-spacing{margin-bottom:20px}.yoast-alerts .button.dismiss,.yoast-alerts .button.restore{width:45px;height:45px;margin-right:10px;padding:0;outline:0;line-height:inherit;cursor:pointer;-ms-flex:0 0 45px;flex:0 0 45px}.yoast-alerts .button.dismiss:focus,.yoast-alerts .button.dismiss:hover,.yoast-alerts .button.restore:focus,.yoast-alerts .button.restore:hover{background:0 0}.yoast-container .separator{margin-top:1em;margin-bottom:1em;border-top:1px solid #ddd}.yoast-container .dashicons-yes{color:#77b227}.yoast-container__warning .dashicons-flag{color:#5d237a}.yoast-container-disabled{displa
|