From: Steve Sutton Date: Fri, 19 Sep 2014 15:36:53 +0000 (-0400) Subject: Update for theme and plugins X-Git-Url: http://cvs2.gaslightmedia.com/gitweb/?a=commitdiff_plain;h=a75e282d3aaf452b68fae94a2f18b57bd22cc8b7;p=web%2FPetoskeyRobotics.git Update for theme and plugins Setup Gaslightify plugin --- diff --git a/wp-content/plugins/Gaslightify.php b/wp-content/plugins/Gaslightify.php deleted file mode 100644 index 350ded4..0000000 --- a/wp-content/plugins/Gaslightify.php +++ /dev/null @@ -1,32 +0,0 @@ - \ No newline at end of file diff --git a/wp-content/plugins/Gaslightify/Gaslightify2.php b/wp-content/plugins/Gaslightify/Gaslightify2.php new file mode 100644 index 0000000..9581497 --- /dev/null +++ b/wp-content/plugins/Gaslightify/Gaslightify2.php @@ -0,0 +1,42 @@ + +#wp-admin-bar-wp-logo > .ab-item .ab-icon:before, #header-logo { + content: url('.plugins_url('img/icon-twitter3.gif', __FILE__).') !important; +} +'; +} +add_action('admin_head', 'glm_custom_logo'); +?> diff --git a/wp-content/plugins/Gaslightify/css/Gaslightify2.css b/wp-content/plugins/Gaslightify/css/Gaslightify2.css new file mode 100644 index 0000000..fb9d1e4 --- /dev/null +++ b/wp-content/plugins/Gaslightify/css/Gaslightify2.css @@ -0,0 +1,17 @@ +#screen-meta-links, +select[name="seo_filter"], +#menu-posts, +#menu-comments, +#menu-tools, +#menu-users, +#wp-admin-bar-edit-profile, +#wp-admin-bar-user-info, +#wp-admin-bar-wp-logo-default, +#wp-admin-bar-wp-logo-external, +#wp-admin-bar-comments, +#wp-admin-bar-new-post, +#wp-admin-bar-wpseo-menu, +#footer-thankyou, +#footer-upgrade { + display: none !important; +} diff --git a/wp-content/plugins/Gaslightify/img/flame_16x16.png b/wp-content/plugins/Gaslightify/img/flame_16x16.png new file mode 100644 index 0000000..53b4d03 Binary files /dev/null and b/wp-content/plugins/Gaslightify/img/flame_16x16.png differ diff --git a/wp-content/plugins/Gaslightify/img/icon-twitter3.gif b/wp-content/plugins/Gaslightify/img/icon-twitter3.gif new file mode 100644 index 0000000..499dd28 Binary files /dev/null and b/wp-content/plugins/Gaslightify/img/icon-twitter3.gif differ diff --git a/wp-content/plugins/Gaslightify/js/Gaslightify2.js b/wp-content/plugins/Gaslightify/js/Gaslightify2.js new file mode 100644 index 0000000..6bf62c4 --- /dev/null +++ b/wp-content/plugins/Gaslightify/js/Gaslightify2.js @@ -0,0 +1,5 @@ +jQuery(document).ready(function(){ + jQuery("#wp-admin-bar-my-account a").click(function(){ + return false; + }); +}); diff --git a/wp-content/plugins/admin-menu-editor/includes/menu-editor-core.php b/wp-content/plugins/admin-menu-editor/includes/menu-editor-core.php index 04abbfa..eedd3b5 100644 --- a/wp-content/plugins/admin-menu-editor/includes/menu-editor-core.php +++ b/wp-content/plugins/admin-menu-editor/includes/menu-editor-core.php @@ -75,6 +75,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { //Our personal copy of the request vars, without any "magic quotes". private $post = array(); private $get = array(); + private $originalPost = array(); function init(){ $this->sitewide_options = true; @@ -267,6 +268,8 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { //Compatibility fix for bbPress. $this->apply_bbpress_compat_fix(); + //Compatibility fix for WooCommerce (woo). + $this->apply_woocommerce_compat_fix(); //Compatibility fix for WordPress Mu Domain Mapping. $this->apply_wpmu_domain_mapping_fix(); @@ -390,6 +393,23 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { } } + //Remove consecutive submenu separators. This can happen if there are separators around a menu item + //that is not accessible to the current user. + foreach ($submenu as $parent => $items) { + $found_separator = false; + foreach ($items as $index => $item) { + //Separator have a dummy #anchor as a URL. See wsMenuEditorExtras::create_submenu_separator(). + if (strpos($item[2], '#submenu-separator-') === 0) { + if ( $found_separator ) { + unset($submenu[$parent][$index]); + } + $found_separator = true; + } else { + $found_separator = false; + } + } + } + //Remove menus that have no accessible sub-menus and require privileges that the user does not have. //Ensure the rest are visible. Run re-parent loop again. foreach ( $menu as $id => $data ) { @@ -1030,6 +1050,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { $priority--; } + //TODO: Include more details like menu title and template ID for debugging purposes (log output). $this->page_access_lookup[$item['url']][$priority] = $item['access_level']; } @@ -1088,7 +1109,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { $items = $topmenu['items']; //Sort by position uasort($items, 'ameMenuItem::compare_position'); - + foreach ($items as $item) { //Skip missing and hidden items if ( !empty($item['missing']) || !empty($item['hidden']) ) { @@ -1441,9 +1462,24 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { try { $menu = ameMenu::load_json($post['data'], true); } catch (InvalidMenuException $ex) { - //Or redirect & display the error message - wp_redirect( add_query_arg('message', 2, $url) ); - die(); + $debugData = ''; + $debugData .= "Exception:\n" . $ex->getMessage() . "\n\n"; + $debugData .= "Used POST data:\n" . print_r($this->post, true) . "\n\n"; + $debugData .= "Original POST:\n" . print_r($this->originalPost, true) . "\n\n"; + $debugData .= "\$_POST global:\n" . print_r($_POST, true); + + $debugData = sprintf( + "", + htmlentities($debugData) + ); + + wp_die( + "Error: Failed to decode menu data!

\n" + . "Please send this debugging information to the developer:
" + . $debugData + ); + + return; } //Save the custom menu @@ -1915,6 +1951,14 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { $current_url = $this->parse_url($current_url); + //Special case: if post_type is not specified for edit.php and post-new.php, + //WordPress assumes it is "post". Here we make this explicit. + if ( $this->endsWith($current_url['path'], '/wp-admin/edit.php') || $this->endsWith($current_url['path'], '/wp-admin/post-new.php') ) { + if ( !isset($current_url['params']['post_type']) ) { + $current_url['params']['post_type'] = 'post'; + } + } + //Hook-based submenu pages can be accessed via both "parent-page.php?page=foo" and "admin.php?page=foo". //WP has a private API function for determining the canonical parent page for the current request. if ( $this->endsWith($current_url['path'], '/admin.php') && is_callable('get_admin_page_parent') ) { @@ -1955,6 +1999,13 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { } } + //Same as above - default post type is "post". + if ( $this->endsWith($item_url['path'], '/wp-admin/edit.php') || $this->endsWith($item_url['path'], '/wp-admin/post-new.php') ) { + if ( !isset($item_url['params']['post_type']) ) { + $item_url['params']['post_type'] = 'post'; + } + } + //The current URL must match all query parameters of the item URL. $different_params = array_diff_assoc($item_url['params'], $current_url['params']); @@ -1967,6 +2018,19 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { } } + //Special case for CPTs: When the "Add New" menu is disabled by CPT settings (show_ui, etc), and someone goes + //to add a new item, WordPress highlights the "$CPT-Name" item as the current one. Lets do the same for + //consistency. See also: /wp-admin/post-new.php, lines #20 to #40. + if ( + ($best_item === null) + && isset($current_url['params']['post_type']) + && (!empty($current_url['params']['post_type'])) + && $this->endsWith($current_url['path'], '/wp-admin/post-new.php') + && isset($this->reverse_item_lookup['edit.php?post_type=' . $current_url['params']['post_type']]) + ) { + $best_item = $this->reverse_item_lookup['edit.php?post_type=' . $current_url['params']['post_type']]; + } + $cached_item = $best_item; return $best_item; } @@ -2107,7 +2171,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { * @return void */ function capture_request_vars(){ - $this->post = $_POST; + $this->post = $this->originalPost = $_POST; $this->get = $_GET; if ( function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc() ) { @@ -2324,6 +2388,36 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework { } } + /** + * Compatibility fix for WooCommerce 2.2.1+. + * Summary: When AME is active, an unusable WooCommerce -> WooCommerce menu item shows up. Here we remove it. + * + * WooCommerce creates a top level "WooCommerce" menu with no callback. By default, WordPress automatically adds + * a submenu item with the same name. However, since the item doesn't have a callback, it is unusable and clicking + * it just triggers a "Cannot load woocommerce" error. So WooCommerce removes this item in an admin_head hook to + * hide it. With AME active, the item shows up anyway, and users get confused by the error. + * + * Fix it by removing the problematic menu item. + * + * Caution: If the user hides all WooCommerce submenus but not the top level menu, the WooCommerce menu will still + * show up but be inaccessible. This may be slightly counter-intuitive, but seems reasonable. + */ + private function apply_woocommerce_compat_fix() { + if ( !isset($this->default_wp_submenu, $this->default_wp_submenu['woocommerce']) ) { + return; + } + + $badSubmenuExists = isset($this->default_wp_submenu['woocommerce'][0]) + && isset($this->default_wp_submenu['woocommerce'][0][2]) + && ($this->default_wp_submenu['woocommerce'][0][2] === 'woocommerce'); + $anotherSubmenuExists = isset($this->default_wp_submenu['woocommerce'][1]); + + if ( $badSubmenuExists && $anotherSubmenuExists ) { + $this->default_wp_submenu['woocommerce'][0] = $this->default_wp_submenu['woocommerce'][1]; + unset($this->default_wp_submenu['woocommerce'][1]); + } + } + /** * Compatibility fix for WordPress Mu Domain Mapping 0.5.4.3. * diff --git a/wp-content/plugins/admin-menu-editor/includes/menu-item.php b/wp-content/plugins/admin-menu-editor/includes/menu-item.php index 87f5eaf..30f277a 100644 --- a/wp-content/plugins/admin-menu-editor/includes/menu-item.php +++ b/wp-content/plugins/admin-menu-editor/includes/menu-item.php @@ -223,6 +223,14 @@ abstract class ameMenuItem { $parent_file = 'users.php'; } + //Special case: In WP 4.0+ the URL of the "Appearance -> Customize" item is different on every admin page. + //This is because the URL includes a "return" parameter that contains the current page's URL. It also makes + //the template ID different on every page, so it's impossible to identify the menu. To fix that, lets remove + //the "return" parameter from the ID. + if ( ($parent_file === 'themes.php') && (strpos($item_file, 'customize.php?') === 0) ) { + $item_file = remove_query_arg('return', $item_file); + } + return $parent_file . '>' . $item_file; } @@ -379,6 +387,11 @@ abstract class ameMenuItem { $menu_url = is_array($item_slug) ? self::get($item_slug, 'file') : $item_slug; $parent_url = !empty($parent_slug) ? $parent_slug : 'admin.php'; + //Workaround for WooCommerce 2.1.12: For some reason, it uses "&" instead of a plain "&" to separate + //query parameters. We need a plain URL, not a HTML-entity-encoded one. + //It is theoretically possible that another plugin might want to use a literal "&", but its very unlikely. + $menu_url = str_replace('&', '&', $menu_url); + if ( strpos($menu_url, '://') !== false ) { return $menu_url; } @@ -398,13 +411,35 @@ abstract class ameMenuItem { } $pageFile = self::remove_query_from($page_url); + /* + * Special case: Absolute paths. + * + * - add_submenu_page() applies plugin_basename() to the menu slug, so we don't need to worry about plugin + * paths. However, absolute paths that *don't* point point to the plugins directory can be a problem. + * + * - If we blindly append $pageFile to another path, we'll get something like "C:\a\b/wp-admin/C:\c\d.php". + * PHP 5.2.5 has a known bug where calling file_exists() on that kind of an invalid filename will cause + * a timeout and a crash in some configurations. See: https://bugs.php.net/bug.php?id=44412 + * + * - WP 3.9.2 and 4.0+ unintentionally break menu URLs like "foo.php?page=c:\a\b.php" because esc_url() + * interprets the part before the colon as an invalid protocol. As a result, such links have an empty URL + * on Windows (but they might still work on other OS). + * + * - Recent versions of WP won't let you load a PHP file from outside the plugins and mu-plugins directories + * with "admin.php?page=filename". See the validate_file() call in /wp-admin/admin.php. However, such filenames + * can still be used as unique slugs for menus with hook callbacks, so we shouldn't reject them outright. + * Related: https://core.trac.wordpress.org/ticket/10011 + */ + $allowPathConcatenation = (substr($pageFile, 1, 1) !== ':'); //Reject "C:\whatever" and similar. + //Check our hard-coded list of admin pages first. It's measurably faster than //hitting the disk with is_file(). if ( isset(self::$known_wp_admin_files[$pageFile]) ) { return false; } + //Now actually check the filesystem. - $adminFileExists = is_file(ABSPATH . '/wp-admin/' . $pageFile); + $adminFileExists = $allowPathConcatenation && is_file(ABSPATH . 'wp-admin/' . $pageFile); if ( $adminFileExists ) { return false; } @@ -414,7 +449,10 @@ abstract class ameMenuItem { return true; } - $pluginFileExists = ($page_url != 'index.php') && is_file(WP_PLUGIN_DIR . '/' . $pageFile); + //Note: We don't need to call plugin_basename() on $pageFile because add_submenu_page() already did that. + $pluginFileExists = $allowPathConcatenation + && ($page_url != 'index.php') + && is_file(WP_PLUGIN_DIR . '/' . $pageFile); if ( $pluginFileExists ) { return true; } diff --git a/wp-content/plugins/admin-menu-editor/menu-editor.php b/wp-content/plugins/admin-menu-editor/menu-editor.php index c3fa367..c86a5bd 100644 --- a/wp-content/plugins/admin-menu-editor/menu-editor.php +++ b/wp-content/plugins/admin-menu-editor/menu-editor.php @@ -3,7 +3,7 @@ Plugin Name: Admin Menu Editor Plugin URI: http://w-shadow.com/blog/2008/12/20/admin-menu-editor-for-wordpress/ Description: Lets you directly edit the WordPress admin menu. You can re-order, hide or rename existing menus, add custom menus and more. -Version: 1.4 +Version: 1.4.1 Author: Janis Elsts Author URI: http://w-shadow.com/blog/ */ diff --git a/wp-content/plugins/admin-menu-editor/readme.txt b/wp-content/plugins/admin-menu-editor/readme.txt index 147fc97..c773385 100644 --- a/wp-content/plugins/admin-menu-editor/readme.txt +++ b/wp-content/plugins/admin-menu-editor/readme.txt @@ -3,8 +3,8 @@ Contributors: whiteshadow Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A6P9S6CE3SRSW Tags: admin, dashboard, menu, security, wpmu Requires at least: 3.8 -Tested up to: 4.0-beta2 -Stable tag: 1.4 +Tested up to: 4.0 +Stable tag: 1.4.1 Lets you edit the WordPress admin menu. You can re-order, hide or rename menus, add custom menus and more. @@ -63,6 +63,14 @@ Plugins installed in the `mu-plugins` directory are treated as "always on", so y == Changelog == += 1.4.1 = +* Fixed "Appearance -> Customize" always showing up as "new" and ignoring custom settings. +* Fixed a WooCommerce 2.2.1+ compatibility issue that caused a superfluous "WooCommerce -> WooCommerce" submenu item to show up. Normally this item is invisible. +* Fixed a bug where the plugin would fail to determine the current menu if the user tries to add a new item of a custom post type that doesn't have an "Add New" menu. Now it highlights the CPT parent menu instead. +* Fixed a very obscure bug where certain old versions of PHP would crash if another plugin created a menu item using an absolute file name as the slug while AME was active. The crash was due to a known bug in PHP and only affected Windows systems with open_basedir enabled. +* Added more debugging information for situations where the plugin can't save menu settings due to server configuration problems. +* Other minor fixes. + = 1.4 = * Added a special target page option: "< None >". It makes the selected menu item unclickable. This could be useful for creating menu headers and so on. * Added a new menu editor colour scheme that's similar to the default WordPress admin colour scheme. Click the "Settings" button next to the menu editor page title to switch colour schemes. diff --git a/wp-content/plugins/nextgen-gallery/changelog.txt b/wp-content/plugins/nextgen-gallery/changelog.txt index 6afdf2a..244a836 100644 --- a/wp-content/plugins/nextgen-gallery/changelog.txt +++ b/wp-content/plugins/nextgen-gallery/changelog.txt @@ -1,6 +1,14 @@ NextGEN Gallery by Photocrati Media += V2.0.66.29 - 09.17.2014 = +* NEW: Added skip_excluding_globally_excluded_images property to displayed gallery objects +* Fixed: SQL generation for random image selection +* Fixed: Adjust regex for replacing displayed gallery placeholder images +* Fixed: Removed filters to home_url needed previously for WMPL compatibility +* Fixed: Use canonical redirects when appropriate +* Fixed: Ability to override image files using XML-RPC + = V2.0.66.27 - 08.18.2014 = * Fixed: Missing class.frame_communication_option_handerl.php error @@ -35,7 +43,7 @@ by Photocrati Media * NEW: Spanish (es_ES) language thanks to Andrew Kurtis at WebHostingHub * Changed: Updated Czech language thanks to Separatista; additional thanks to Martin Krizek for the original translation who was mistakenly unaccredited * Changed: "Upgrade to Pro" page has new design, advertises for NextGEN Plus -* Changed: Basic Albums templates now given the image counter

element the class 'ngg-album-gallery-image-counter' (by user request) +* Changed: Basic Albums templates now given the image counter P element the class 'ngg-album-gallery-image-counter' (by user request) * Changed: Gallery widgets now apply height:auto to their element; fixes compatibility with some themes * Changed: Random galleries should be substantially faster now (1000% or more for large image tables) * Fixed: Complete WPML compatibility @@ -49,7 +57,7 @@ by Photocrati Media * Fixed: Multisite gallery path tooltip gave a wrong default setting * Fixed: Flush 'all' caches when pope_module_list setting changes * Fixed: Don't enqueue related images css in the admin -* Fixed: Basic Slideshows fixes WP creating extraneous

element above the slideshow display +* Fixed: Basic Slideshows fixes WP creating extraneous P element above the slideshow display * Fixed: Basic Singlepic will now display images marked 'excluded' in the admin * Fixed: Admin pages can now update when the "Save" button text has been translated @@ -831,7 +839,7 @@ Fixed: Match legacy behaviour when changing gallery path, i.e. don't move fi * Added : New filter function to add custom columns * Bugfix : Fixed width for Thickbox in Manage gallery * Bugfix : fixed width for media upload select box -* Bugfix : Remove

tag in singlepic regex +* Bugfix : Remove P tag in singlepic regex * Bugfix : Correct format of shutter speed * Bugfix : Album name in Short code not useable diff --git a/wp-content/plugins/nextgen-gallery/nggallery.php b/wp-content/plugins/nextgen-gallery/nggallery.php index 6361842..66b33a1 100644 --- a/wp-content/plugins/nextgen-gallery/nggallery.php +++ b/wp-content/plugins/nextgen-gallery/nggallery.php @@ -4,7 +4,7 @@ if(preg_match('#' . basename(__FILE__) . '#', $_SERVER['PHP_SELF'])) { die('You /** * Plugin Name: NextGEN Gallery by Photocrati * Description: The most popular gallery plugin for WordPress and one of the most popular plugins of all time with over 9 million downloads. - * Version: 2.0.66.27 + * Version: 2.0.66.29 * Author: Photocrati Media * Plugin URI: http://www.nextgen-gallery.com * Author URI: http://www.photocrati.com @@ -431,7 +431,7 @@ class C_NextGEN_Bootstrap define('NGG_PRODUCT_URL', path_join(str_replace("\\", '/', NGG_PLUGIN_URL), 'products')); define('NGG_MODULE_URL', path_join(str_replace("\\", '/', NGG_PRODUCT_URL), 'photocrati_nextgen/modules')); define('NGG_PLUGIN_STARTED_AT', microtime()); - define('NGG_PLUGIN_VERSION', '2.0.66.27'); + define('NGG_PLUGIN_VERSION', '2.0.66.29'); if (!defined('NGG_HIDE_STRICT_ERRORS')) { define('NGG_HIDE_STRICT_ERRORS', TRUE); diff --git a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/attach_to_post/module.attach_to_post.php b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/attach_to_post/module.attach_to_post.php index fa6df74..48d6dce 100644 --- a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/attach_to_post/module.attach_to_post.php +++ b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/attach_to_post/module.attach_to_post.php @@ -167,27 +167,10 @@ class M_Attach_To_Post extends C_Base_Module */ function substitute_placeholder_imgs($content) { - // Get some utilities - $mapper = $this->get_registry()->get_utility('I_Displayed_Gallery_Mapper'); - $router = $this->get_registry()->get_utility('I_Router'); - - // To match ATP entries we compare the stored url against a generic path - // We must check HTTP and HTTPS as well as permalink and non-permalink forms - $preview_url = parse_url($router->join_paths( - $router->remove_url_segment('index.php', $router->get_base_url('root')), - '/nextgen-attach_to_post/preview' - )); - $router->debug = TRUE; - $preview_url = preg_quote($preview_url['host'] . $preview_url['path'], '#'); - - $alt_preview_url = parse_url($router->join_paths( - $router->remove_url_segment('index.php', $router->get_base_url('root')), - 'index.php/nextgen-attach_to_post/preview' - )); - $alt_preview_url = preg_quote($alt_preview_url['host'] . $alt_preview_url['path'], '#'); - // The placeholder MUST have a gallery instance id - if (preg_match_all("##mi", $content, $matches, PREG_SET_ORDER)) { + if (preg_match_all("##mi", $content, $matches, PREG_SET_ORDER)) + { + $mapper = C_Displayed_Gallery_Mapper::get_instance(); foreach ($matches as $match) { // Find the displayed gallery $displayed_gallery_id = $match[3]; diff --git a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_basic_singlepic/adapter.nextgen_basic_singlepic_controller.php b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_basic_singlepic/adapter.nextgen_basic_singlepic_controller.php index 0224642..1a0df15 100644 --- a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_basic_singlepic/adapter.nextgen_basic_singlepic_controller.php +++ b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_basic_singlepic/adapter.nextgen_basic_singlepic_controller.php @@ -14,7 +14,8 @@ class A_NextGen_Basic_Singlepic_Controller extends Mixin $display_settings = $displayed_gallery->display_settings; // use this over get_included_entities() so we can display images marked 'excluded' - $image = array_shift($displayed_gallery->get_entities(1, FALSE, FALSE, 'both')); + $displayed_gallery->skip_excluding_globally_excluded_images = TRUE; + $image = array_shift($displayed_gallery->get_entities(1, FALSE, FALSE, 'included')); if (!$image) return $this->object->render_partial("photocrati-nextgen_gallery_display#no_images_found", array(), $return); diff --git a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_data/class.gallerystorage_driver_base.php b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_data/class.gallerystorage_driver_base.php index 0e49fe4..b4d0d11 100644 --- a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_data/class.gallerystorage_driver_base.php +++ b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_data/class.gallerystorage_driver_base.php @@ -626,7 +626,7 @@ class Mixin_GalleryStorage_Driver_Base extends Mixin * @param type $filename specifies the name of the file * @return C_Image */ - function upload_base64_image($gallery, $data, $filename=FALSE, $image_id=FALSE) + function upload_base64_image($gallery, $data, $filename=FALSE, $image_id=FALSE, $override=FALSE) { $settings = C_NextGen_Settings::get_instance(); $memory_limit = intval(ini_get('memory_limit')); @@ -663,7 +663,7 @@ class Mixin_GalleryStorage_Driver_Base extends Mixin // Prevent duplicate filenames: check if the filename exists and // begin appending '-i' until we find an open slot - if (!ini_get('safe_mode') && @file_exists($abs_filename)) + if (!ini_get('safe_mode') && @file_exists($abs_filename) && !$override) { $file_exists = TRUE; $i = 0; diff --git a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_gallery_display/class.displayed_gallery.php b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_gallery_display/class.displayed_gallery.php index be16344..225013e 100644 --- a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_gallery_display/class.displayed_gallery.php +++ b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_gallery_display/class.displayed_gallery.php @@ -255,9 +255,9 @@ class Mixin_Displayed_Gallery_Queries extends Mixin $mapper->where(array("{$image_key} NOT IN %s", $this->object->exclusions)); } - // Ensure that no images marked as excluded at the gallery level are - // returned - $mapper->where(array("exclude = %d", 0)); + // Ensure that no images marked as excluded at the gallery level are returned + if (empty($this->object->skip_excluding_globally_excluded_images)) + $mapper->where(array("exclude = %d", 0)); } // When returns is "excluded", it's a little more complicated as the @@ -349,8 +349,15 @@ class Mixin_Displayed_Gallery_Queries extends Mixin // gallery created by randomly selecting X image ids that are then set as the gallery entity_ids elseif ($this->object->source == 'random_images' && empty($this->object->entity_ids)) { $table_name = $mapper->get_table_name(); - $mapper->_where_clauses[] = " /*NGG_NO_EXTRAS_TABLE*/ `{$image_key}` IN (SELECT `{$image_key}` FROM (SELECT `{$image_key}` FROM `{$table_name}` i ORDER BY RAND() LIMIT {$this->object->maximum_entity_count}) o) /*NGG_NO_EXTRAS_TABLE*/"; - } + $where_clauses = array(); + $sub_where_sql = ''; + foreach ($mapper->_where_clauses as $where) { + $where_clauses[] = '(' . $where . ')'; + } + if ($where_clauses) + $sub_where_sql = 'WHERE ' . implode(' AND ', $where_clauses); + $mapper->_where_clauses = array(" /*NGG_NO_EXTRAS_TABLE*/ `{$image_key}` IN (SELECT `{$image_key}` FROM (SELECT `{$image_key}` FROM `{$table_name}` i {$sub_where_sql} ORDER BY RAND() LIMIT {$this->object->maximum_entity_count}) o) /*NGG_NO_EXTRAS_TABLE*/"); + } // Apply a sorting order if ($sort_by) $mapper->order_by($sort_by, $sort_direction); diff --git a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_xmlrpc/module.nextgen_xmlrpc.php b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_xmlrpc/module.nextgen_xmlrpc.php index 7bc8060..5b6cee7 100644 --- a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_xmlrpc/module.nextgen_xmlrpc.php +++ b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/nextgen_xmlrpc/module.nextgen_xmlrpc.php @@ -238,6 +238,9 @@ class M_NextGen_XmlRpc extends C_Base_Module $password = strval($args[2]); $data = $args[3]; $gallery_id = isset($data['gallery_id']) ? $data['gallery_id'] : $data['gallery']; + if (!isset($data['override'])) $data['override'] = FALSE; + if (!isset($data['overwrite']))$data['overwrite']= FALSE; + $data['override'] = $data['overwrite']; // Authenticate the user if ($this->_login($username, $password, $blog_id)) { @@ -251,7 +254,7 @@ class M_NextGen_XmlRpc extends C_Base_Module // Upload the image $storage = C_Gallery_Storage::get_instance(); - $image = $storage->upload_base64_image($gallery, $data['bits'], $data['name'], $data['image_id']); + $image = $storage->upload_base64_image($gallery, $data['bits'], $data['name'], $data['image_id'], $data['override']); if ($image) { $storage = C_Gallery_Storage::get_instance(); $image->imageURL = $storage->get_image_url($image); diff --git a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/third_party_compat/module.third_party_compat.php b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/third_party_compat/module.third_party_compat.php index bd8315e..bf3ca54 100644 --- a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/third_party_compat/module.third_party_compat.php +++ b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/third_party_compat/module.third_party_compat.php @@ -83,7 +83,6 @@ class M_Third_Party_Compat extends C_Base_Module add_action('plugins_loaded', array(&$this, 'wpml'), PHP_INT_MAX); add_action('plugins_loaded', array(&$this, 'wpml_translation_management'), PHP_INT_MAX); - add_filter('home_url', array(&$this, 'wpml_home_url'), -1, 4); add_filter('headway_gzip', array(&$this, 'headway_gzip'), (PHP_INT_MAX - 1)); add_filter('ckeditor_external_plugins', array(&$this, 'ckeditor_plugins'), 11); add_filter('bp_do_redirect_canonical', array(&$this, 'fix_buddypress_routing')); @@ -114,38 +113,6 @@ class M_Third_Party_Compat extends C_Base_Module M_WordPress_Routing::$_use_old_slugs = FALSE; } - /** - * WPML's home_url filter causes a conflict with NextGEN's url generation, but doesn't appear to be necessary for - * WPML to function. This is necessary until we properly support WP_CONTENT_URL & WP_PLUGINS_URL. - * - * @param $url - * @param $path - * @param $orig_scheme - * @param $blog_id - * @return mixed - */ - function wpml_home_url($url, $path, $orig_scheme, $blog_id) - { - if (!class_exists('SitePress')) - return $url; - - global $wp_filter; - - if (empty($wp_filter['home_url'][1])) - return $url; - - foreach ($wp_filter['home_url'][1] as $id => $filter) { - if (!strpos($id, 'home_url')) - continue; - $object = $filter['function'][0]; - if (is_object($object) && get_class($object) != 'SitePress') - continue; - remove_filter('home_url', array($object, 'home_url'), 1); - } - - return $url; - } - /** * CKEditor features a custom NextGEN shortcode generator that unfortunately relies on parts of the NextGEN * 1.9x API that has been deprecated in NextGEN 2.0 diff --git a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/wordpress_routing/module.wordpress_routing.php b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/wordpress_routing/module.wordpress_routing.php index eef10b7..7f906ae 100644 --- a/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/wordpress_routing/module.wordpress_routing.php +++ b/wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/wordpress_routing/module.wordpress_routing.php @@ -8,8 +8,8 @@ ***/ class M_WordPress_Routing extends C_Base_Module { - static $_use_canonical_redirect = NULL; - static $_use_old_slugs = NULL; + static $_use_canonical_redirect = TRUE; + static $_use_old_slugs = TRUE; function define() { @@ -39,11 +39,9 @@ class M_WordPress_Routing extends C_Base_Module // in the restore_request_uri() method if (has_action('template_redirect', 'wp_old_slug_redirect')) { remove_action( 'template_redirect', 'wp_old_slug_redirect'); - if (!is_null(self::$_use_canonical_redirect)) self::$_use_old_slugs = TRUE; } if (has_action('template_redirect', 'redirect_canonical')) { remove_action( 'template_redirect', 'redirect_canonical'); - if (!is_null(self::$_use_canonical_redirect)) self::$_use_canonical_redirect = TRUE; } } diff --git a/wp-content/plugins/nextgen-gallery/readme.txt b/wp-content/plugins/nextgen-gallery/readme.txt index 8653b23..5ce3619 100644 --- a/wp-content/plugins/nextgen-gallery/readme.txt +++ b/wp-content/plugins/nextgen-gallery/readme.txt @@ -199,6 +199,14 @@ For more information, feel free to visit the official website for the NextGEN Ga == Changelog == += V2.0.66.29 - 09.17.2014 = +* NEW: Added skip_excluding_globally_excluded_images property to displayed gallery objects +* Fixed: SQL generation for random image selection +* Fixed: Adjust regex for replacing displayed gallery placeholder images +* Fixed: Removed filters to home_url needed previously for WMPL compatibility +* Fixed: Use canonical redirects when appropriate +* Fixed: Ability to override image files using XML-RPC + = V2.0.66.27 - 08.18.2014 = * Fixed: Missing class.frame_communication_option_handerl.php error @@ -233,7 +241,7 @@ For more information, feel free to visit the official website for the NextGEN Ga * NEW: Spanish (es_ES) language thanks to Andrew Kurtis at WebHostingHub * Changed: Updated Czech language thanks to Separatista; additional thanks to Martin Krizek for the original translation who was mistakenly unaccredited * Changed: "Upgrade to Pro" page has new design, advertises for NextGEN Plus -* Changed: Basic Albums templates now given the image counter

element the class 'ngg-album-gallery-image-counter' (by user request) +* Changed: Basic Albums templates now given the image counter P element the class 'ngg-album-gallery-image-counter' (by user request) * Changed: Gallery widgets now apply height:auto to their element; fixes compatibility with some themes * Changed: Random galleries should be substantially faster now (1000% or more for large image tables) * Fixed: Complete WPML compatibility @@ -247,7 +255,7 @@ For more information, feel free to visit the official website for the NextGEN Ga * Fixed: Multisite gallery path tooltip gave a wrong default setting * Fixed: Flush 'all' caches when pope_module_list setting changes * Fixed: Don't enqueue related images css in the admin -* Fixed: Basic Slideshows fixes WP creating extraneous

element above the slideshow display +* Fixed: Basic Slideshows fixes WP creating extraneous P element above the slideshow display * Fixed: Basic Singlepic will now display images marked 'excluded' in the admin * Fixed: Admin pages can now update when the "Save" button text has been translated diff --git a/wp-content/plugins/nextgen-gallery/screenshot-1.png b/wp-content/plugins/nextgen-gallery/screenshot-1.png deleted file mode 100644 index 3657a9d..0000000 Binary files a/wp-content/plugins/nextgen-gallery/screenshot-1.png and /dev/null differ diff --git a/wp-content/plugins/nextgen-gallery/screenshot-2.png b/wp-content/plugins/nextgen-gallery/screenshot-2.png deleted file mode 100644 index 0557499..0000000 Binary files a/wp-content/plugins/nextgen-gallery/screenshot-2.png and /dev/null differ diff --git a/wp-content/plugins/nextgen-gallery/screenshot-3.png b/wp-content/plugins/nextgen-gallery/screenshot-3.png deleted file mode 100644 index ef64950..0000000 Binary files a/wp-content/plugins/nextgen-gallery/screenshot-3.png and /dev/null differ diff --git a/wp-content/plugins/nextgen-gallery/screenshot-4.png b/wp-content/plugins/nextgen-gallery/screenshot-4.png deleted file mode 100644 index 130699d..0000000 Binary files a/wp-content/plugins/nextgen-gallery/screenshot-4.png and /dev/null differ diff --git a/wp-content/plugins/nextgen-gallery/screenshot-5.png b/wp-content/plugins/nextgen-gallery/screenshot-5.png deleted file mode 100644 index 6f72d37..0000000 Binary files a/wp-content/plugins/nextgen-gallery/screenshot-5.png and /dev/null differ diff --git a/wp-content/plugins/nextgen-gallery/screenshot-6.png b/wp-content/plugins/nextgen-gallery/screenshot-6.png deleted file mode 100644 index b309b79..0000000 Binary files a/wp-content/plugins/nextgen-gallery/screenshot-6.png and /dev/null differ diff --git a/wp-content/plugins/nextgen-gallery/screenshot-7.png b/wp-content/plugins/nextgen-gallery/screenshot-7.png deleted file mode 100644 index b974547..0000000 Binary files a/wp-content/plugins/nextgen-gallery/screenshot-7.png and /dev/null differ diff --git a/wp-content/plugins/nextgen-gallery/screenshot-8.png b/wp-content/plugins/nextgen-gallery/screenshot-8.png deleted file mode 100644 index e32f645..0000000 Binary files a/wp-content/plugins/nextgen-gallery/screenshot-8.png and /dev/null differ diff --git a/wp-content/plugins/nextgen-gallery/screenshot-9.png b/wp-content/plugins/nextgen-gallery/screenshot-9.png deleted file mode 100644 index debf5f4..0000000 Binary files a/wp-content/plugins/nextgen-gallery/screenshot-9.png and /dev/null differ diff --git a/wp-content/plugins/wordfence/lib/IPTraf.php b/wp-content/plugins/wordfence/lib/IPTraf.php index df8734a..7ddf1b0 100644 --- a/wp-content/plugins/wordfence/lib/IPTraf.php +++ b/wp-content/plugins/wordfence/lib/IPTraf.php @@ -9,7 +9,11 @@ $v){ ?> Time: ago -- -- in Unixtime Secs since last hit:' . $v['timeSinceLastHit'] . ''; } ?> + +URL:Possible XSS code filtered out for your security + URL: + Type:Page not found'; } ?> Referrer: Full Browser ID: diff --git a/wp-content/plugins/wordfence/lib/wfCache.php b/wp-content/plugins/wordfence/lib/wfCache.php index 0efd2d6..cdbf2bb 100644 --- a/wp-content/plugins/wordfence/lib/wfCache.php +++ b/wp-content/plugins/wordfence/lib/wfCache.php @@ -154,8 +154,8 @@ class wfCache { $append .= "Time created on server: " . date('Y-m-d H:i:s T') . ". "; $append .= "Is HTTPS page: " . (self::isHTTPSPage() ? 'HTTPS' : 'no') . ". "; $append .= "Page size: " . strlen($buffer) . " bytes. "; - $append .= "Host: " . ($_SERVER['HTTP_HOST'] ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME']) . ". "; - $append .= "Request URI: " . $_SERVER['REQUEST_URI'] . " "; + $append .= "Host: " . ($_SERVER['HTTP_HOST'] ? htmlentities($_SERVER['HTTP_HOST']) : htmlentities($_SERVER['SERVER_NAME'])) . ". "; + $append .= "Request URI: " . htmlentities($_SERVER['REQUEST_URI']) . " "; $appendGzip = $append . " Encoding: GZEncode -->\n"; $append .= " Encoding: Uncompressed -->\n"; } diff --git a/wp-content/plugins/wordfence/lib/wfLog.php b/wp-content/plugins/wordfence/lib/wfLog.php index 4461ee8..1ee0910 100644 --- a/wp-content/plugins/wordfence/lib/wfLog.php +++ b/wp-content/plugins/wordfence/lib/wfLog.php @@ -505,7 +505,7 @@ class wfLog { $res['IP'] = wfUtils::inet_ntoa($res['IP']); $res['extReferer'] = false; if(isset( $res['referer'] ) && $res['referer']){ - if(! preg_match('/^https?:\/\/[a-z0-9\.\-]+\/[^\':<>\"\\\]*$/i', $res['referer'] )){ //filtering out XSS + if(wfUtils::hasXSS($res['referer'] )){ //filtering out XSS $res['referer'] = ''; } } diff --git a/wp-content/plugins/wordfence/lib/wfUtils.php b/wp-content/plugins/wordfence/lib/wfUtils.php index 7a26e4a..af49305 100644 --- a/wp-content/plugins/wordfence/lib/wfUtils.php +++ b/wp-content/plugins/wordfence/lib/wfUtils.php @@ -136,7 +136,7 @@ class wfUtils { public static function getIP(){ //You can use the following examples to force Wordfence to think a visitor has a certain IP if you're testing. Remember to re-comment this out or you will break Wordfence badly. //return '1.2.33.57'; - //return '4.2.3.14'; + //return '4.22.23.114'; //return self::makeRandomIP(); $howGet = wfConfig::get('howGetIPs', false); @@ -642,6 +642,13 @@ class wfUtils { } return true; } + public static function hasXSS($URL){ + if(! preg_match('/^https?:\/\/[a-z0-9\.\-]+\/[^\':<>\"\\\]*$/i', $URL)){ + return true; + } else { + return false; + } + } } diff --git a/wp-content/plugins/wordfence/lib/wordfenceClass.php b/wp-content/plugins/wordfence/lib/wordfenceClass.php index ed81a96..d676eba 100644 --- a/wp-content/plugins/wordfence/lib/wordfenceClass.php +++ b/wp-content/plugins/wordfence/lib/wordfenceClass.php @@ -332,7 +332,10 @@ class wordfence { } public static function initProtection(){ if(preg_match('/\/wp\-admin\/admin\-ajax\.php/', $_SERVER['REQUEST_URI'])){ - if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'revslider_show_image' && isset($_REQUEST['img']) && preg_match('/\.php$/i', $_REQUEST['img']) ){ + if( + (isset($_GET['action']) && $_GET['action'] == 'revslider_show_image' && isset($_GET['img']) && preg_match('/\.php$/i', $_GET['img'])) || + (isset($_POST['action']) && $_POST['action'] == 'revslider_show_image' && isset($_POST['img']) && preg_match('/\.php$/i', $_POST['img'])) + ){ self::getLog()->do503(86400, "URL not allowed. Slider Revolution Hack attempt detected. #2"); exit(); //function above exits anyway } @@ -1881,7 +1884,7 @@ class wordfence { return array('err' => 1, 'errorMsg' => "The IP address " . htmlentities($IP) . " is whitelisted and can't be blocked or it is in a range of internal IP addresses that Wordfence does not block. You can remove this IP from the whitelist on the Wordfence options page."); } if(wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers'){ //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google - if(wfCrawl::verifyCrawlerPTR('/googlebot\.com$/i', $IP)){ + if(wfCrawl::verifyCrawlerPTR('/\.googlebot\.com$/i', $IP)){ return array('err' => 1, 'errorMsg' => "The IP address you're trying to block belongs to Google. Your options are currently set to not block these crawlers. Change this in Wordfence options if you want to manually block Google."); } } diff --git a/wp-content/plugins/wordfence/readme.txt b/wp-content/plugins/wordfence/readme.txt index 99e253b..34b8e58 100644 --- a/wp-content/plugins/wordfence/readme.txt +++ b/wp-content/plugins/wordfence/readme.txt @@ -3,7 +3,7 @@ Contributors: mmaunder Tags: wordpress, security, performance, speed, caching, cache, caching plugin, wordpress cache, wordpress caching, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure, two factor, cellphone sign-in, cellphone signin, cellphone, twofactor, security, secure, htaccess, login, log, users, login alerts, lock, chmod, maintenance, plugin, private, privacy, protection, permissions, 503, base64, injection, code, encode, script, attack, hack, hackers, block, blocked, prevent, prevention, RFI, XSS, CRLF, CSRF, SQL Injection, vulnerability, website security, WordPress security, security log, logging, HTTP log, error log, login security, personal security, infrastructure security, firewall security, front-end security, web server security, proxy security, reverse proxy security, secure website, secure login, two factor security, maximum login security, heartbleed, heart bleed, heartbleed vulnerability, openssl vulnerability, nginx, litespeed, php5-fpm, woocommerce support, woocommerce caching Requires at least: 3.3.1 Tested up to: 4.0 -Stable tag: 5.2.3 +Stable tag: 5.2.4 Wordfence Security is a free enterprise class security and performance plugin that makes your site up to 50 times faster and more secure. @@ -163,6 +163,13 @@ cause a security hole on your site. == Changelog == += 5.2.4 = +* Security release. Upgrade immediately. +* This release fixes an XSS vunlerability on Wordfence "view all traffic from IP" page. +* Also fixes a hard to exploit XSS which exists if you have your site as the default site on your web server, falcon enabled and debugging comments enabled. +* Improves Revolution Slider proteciton. +* Fixed bypass for fake googlebot blocking. + = 5.2.3 = * Updated Geo IP country database to newest version (September 2014 edition) * Security fix. Improved referrer sanitization in live traffic. diff --git a/wp-content/plugins/wordfence/tmp/configCache.php b/wp-content/plugins/wordfence/tmp/configCache.php deleted file mode 100644 index 5318c9a..0000000 --- a/wp-content/plugins/wordfence/tmp/configCache.php +++ /dev/null @@ -1,4 +0,0 @@ -a:18:{s:9:"cacheType";s:0:"";s:15:"firewallEnabled";s:1:"1";s:6:"apiKey";s:192:"9a57151a642820cab102741d5a92a91e788dc8d15823e8de2acf65c38ee9122d12d031e407fd6e07063ef5a2450379e96e3fc6d3152957ba67a0b99301da91a13a54962479db212e15db85e28ed9225d7b221f9ee3de6af39d16e984ebdd4fb4";s:9:"howGetIPs";s:0:"";s:9:"IPGetFail";s:0:"";s:11:"whitelisted";s:0:"";s:13:"cbl_countries";s:0:"";s:18:"cbl_bypassRedirURL";s:0:"";s:17:"cbl_bypassViewURL";s:0:"";s:11:"blockedTime";s:3:"300";s:18:"liveTrafficEnabled";s:1:"1";s:14:"disableCookies";s:1:"0";s:10:"tourClosed";s:1:"1";s:10:"autoUpdate";s:1:"0";s:16:"autoUpdateChoice";s:1:"1";s:11:"alertEmails";s:21:"dev@gaslightmedia.com";s:17:"actUpdateInterval";s:0:"";s:7:"debugOn";s:1:"0";} \ No newline at end of file diff --git a/wp-content/plugins/wordfence/wordfence.php b/wp-content/plugins/wordfence/wordfence.php index 22a2232..5ae43a2 100644 --- a/wp-content/plugins/wordfence/wordfence.php +++ b/wp-content/plugins/wordfence/wordfence.php @@ -4,13 +4,13 @@ Plugin Name: Wordfence Security Plugin URI: http://www.wordfence.com/ Description: Wordfence Security - Anti-virus, Firewall and High Speed Cache Author: Wordfence -Version: 5.2.3 +Version: 5.2.4 Author URI: http://www.wordfence.com/ */ if(defined('WP_INSTALLING') && WP_INSTALLING){ return; } -define('WORDFENCE_VERSION', '5.2.3'); +define('WORDFENCE_VERSION', '5.2.4'); if(get_option('wordfenceActivated') != 1){ add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); } } diff --git a/wp-content/plugins/wordpress-seo/.csscomb.json b/wp-content/plugins/wordpress-seo/.csscomb.json new file mode 100644 index 0000000..6cc7e28 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/.csscomb.json @@ -0,0 +1,331 @@ +{ + "exclude": [ + "node_modules/**", + "vendor/**", + "*.min.css" + ], + "verbose": true, + + "always-semicolon" : true, + "block-indent" : "\t", + "colon-space" : [ + 0, + 1 + ], + "color-case" : "lower", + "color-shorthand" : true, + "combinator-space" : [ + 1, + 1 + ], + "element-case" : "lower", + "eof-newline" : true, + "leading-zero" : true, + "quotes" : "double", + "remove-empty-rulesets" : true, + "rule-indent" : "\t", + "space-after-selector-delimiter": "\n", + "space-after-closing-brace" : "\n", + "space-after-opening-brace" : "\n", + "space-before-closing-brace" : "\n", + "space-before-opening-brace" : 1, + "space-before-colon" : 0, + "space-before-combinator" : 1, + "stick-brace" : 1, + "strip-spaces" : true, + "unitless-zero" : true, + "vendor-prefix-align" : false, + "sort-order" : [ + "$variable", + "$include", + + "display", + "visibility", + "float", + "clear", + "overflow", + "overflow-x", + "overflow-y", + "-ms-overflow-x", + "-ms-overflow-y", + "clip", + "zoom", + "flex-direction", + "flex-order", + "flex-pack", + "flex-align", + + "position", + "z-index", + "top", + "right", + "bottom", + "left", + + "-webkit-box-sizing", + "-moz-box-sizing", + "box-sizing", + "width", + "min-width", + "max-width", + "height", + "min-height", + "max-height", + "margin", + "margin-top", + "margin-right", + "margin-bottom", + "margin-left", + "padding", + "padding-top", + "padding-right", + "padding-bottom", + "padding-left", + "border", + "border-width", + "border-style", + "border-color", + "border-top", + "border-top-width", + "border-top-style", + "border-top-color", + "border-right", + "border-right-width", + "border-right-style", + "border-right-color", + "border-bottom", + "border-bottom-width", + "border-bottom-style", + "border-bottom-color", + "border-left", + "border-left-width", + "border-left-style", + "border-left-color", + "-webkit-border-radius", + "-moz-border-radius", + "border-radius", + "-webkit-border-top-left-radius", + "-moz-border-radius-topleft", + "border-top-left-radius", + "-webkit-border-top-right-radius", + "-moz-border-radius-topright", + "border-top-right-radius", + "-webkit-border-bottom-right-radius", + "-moz-border-radius-bottomright", + "border-bottom-right-radius", + "-webkit-border-bottom-left-radius", + "-moz-border-radius-bottomleft", + "border-bottom-left-radius", + "-webkit-border-image", + "-moz-border-image", + "-o-border-image", + "border-image", + "-webkit-border-image-source", + "-moz-border-image-source", + "-o-border-image-source", + "border-image-source", + "-webkit-border-image-slice", + "-moz-border-image-slice", + "-o-border-image-slice", + "border-image-slice", + "-webkit-border-image-width", + "-moz-border-image-width", + "-o-border-image-width", + "border-image-width", + "-webkit-border-image-outset", + "-moz-border-image-outset", + "-o-border-image-outset", + "border-image-outset", + "-webkit-border-image-repeat", + "-moz-border-image-repeat", + "-o-border-image-repeat", + "border-image-repeat", + "table-layout", + "empty-cells", + "caption-side", + "border-spacing", + "border-collapse", + + "outline", + "outline-width", + "outline-style", + "outline-color", + "outline-offset", + "opacity", + "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity", + "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha", + "-ms-interpolation-mode", + "color", + "background", + "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader", + "background-color", + "background-image", + "background-repeat", + "background-attachment", + "background-position", + "background-position-x", + "-ms-background-position-x", + "background-position-y", + "-ms-background-position-y", + "-webkit-background-clip", + "-moz-background-clip", + "background-clip", + "background-origin", + "-webkit-background-size", + "-moz-background-size", + "-o-background-size", + "background-size", + "box-decoration-break", + "-webkit-box-shadow", + "-moz-box-shadow", + "box-shadow", + "filter:progid:DXImageTransform.Microsoft.gradient", + "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", + "text-shadow", + "font", + "font-family", + "font-size", + "font-weight", + "font-style", + "font-variant", + "font-size-adjust", + "font-stretch", + "font-effect", + "font-emphasize", + "font-emphasize-position", + "font-emphasize-style", + "font-smooth", + "line-height", + "text-align", + "-webkit-text-align-last", + "-moz-text-align-last", + "-ms-text-align-last", + "text-align-last", + "vertical-align", + "white-space", + "text-decoration", + "text-emphasis", + "text-emphasis-color", + "text-emphasis-style", + "text-emphasis-position", + "text-indent", + "-ms-text-justify", + "text-justify", + "letter-spacing", + "word-spacing", + "-ms-writing-mode", + "text-outline", + "text-transform", + "text-wrap", + "text-overflow", + "-ms-text-overflow", + "text-overflow-ellipsis", + "text-overflow-mode", + "-ms-word-wrap", + "word-wrap", + "word-break", + "-ms-word-break", + "-moz-tab-size", + "-o-tab-size", + "tab-size", + "-webkit-hyphens", + "-moz-hyphens", + "hyphens", + + "list-style", + "list-style-position", + "list-style-type", + "list-style-image", + "content", + "quotes", + "counter-reset", + "counter-increment", + "resize", + "cursor", + "-webkit-user-select", + "-moz-user-select", + "-ms-user-select", + "user-select", + "nav-index", + "nav-up", + "nav-right", + "nav-down", + "nav-left", + "-webkit-transition", + "-moz-transition", + "-ms-transition", + "-o-transition", + "transition", + "-webkit-transition-delay", + "-moz-transition-delay", + "-ms-transition-delay", + "-o-transition-delay", + "transition-delay", + "-webkit-transition-timing-function", + "-moz-transition-timing-function", + "-ms-transition-timing-function", + "-o-transition-timing-function", + "transition-timing-function", + "-webkit-transition-duration", + "-moz-transition-duration", + "-ms-transition-duration", + "-o-transition-duration", + "transition-duration", + "-webkit-transition-property", + "-moz-transition-property", + "-ms-transition-property", + "-o-transition-property", + "transition-property", + "-webkit-transform", + "-moz-transform", + "-ms-transform", + "-o-transform", + "transform", + "-webkit-transform-origin", + "-moz-transform-origin", + "-ms-transform-origin", + "-o-transform-origin", + "transform-origin", + "-webkit-animation", + "-moz-animation", + "-ms-animation", + "-o-animation", + "animation", + "-webkit-animation-name", + "-moz-animation-name", + "-ms-animation-name", + "-o-animation-name", + "animation-name", + "-webkit-animation-duration", + "-moz-animation-duration", + "-ms-animation-duration", + "-o-animation-duration", + "animation-duration", + "-webkit-animation-play-state", + "-moz-animation-play-state", + "-ms-animation-play-state", + "-o-animation-play-state", + "animation-play-state", + "-webkit-animation-timing-function", + "-moz-animation-timing-function", + "-ms-animation-timing-function", + "-o-animation-timing-function", + "animation-timing-function", + "-webkit-animation-delay", + "-moz-animation-delay", + "-ms-animation-delay", + "-o-animation-delay", + "animation-delay", + "-webkit-animation-iteration-count", + "-moz-animation-iteration-count", + "-ms-animation-iteration-count", + "-o-animation-iteration-count", + "animation-iteration-count", + "-webkit-animation-direction", + "-moz-animation-direction", + "-ms-animation-direction", + "-o-animation-direction", + "animation-direction", + "pointer-events" + ] +} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/CONTRIBUTING.md b/wp-content/plugins/wordpress-seo/CONTRIBUTING.md new file mode 100644 index 0000000..f687b53 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/CONTRIBUTING.md @@ -0,0 +1,56 @@ +Thanks for reading our conribution guidelines! What do you want to do: + +* [File a bug / an issue](#filing-issue) +* [Contribute to WordPress SEO](#contribute) + + +#Filing issues + +__Please Note:__ GitHub is for bug reports and code contributions only - if you have a support question or a request for a customisation don't post here, go to our [Support Forum](http://wordpress.org/support/plugin/wordpress-seo) instead. + +For localization, please refer to [translate.yoast.com](http://translate.yoast.com/projects/wordpress-seo), though bugs with strings that can't be translated are welcome here. + +## How to write a useful bug report +If you think you have found a bug (we acknowledge that that's a possibility), please make sure you're using the latest version of the plugin. If possible check out the latest version from GitHub and see if the bug still exists there. + +A useful bug report explains: + +1. What you were trying to achieve. +2. What you were expecting to happen. +3. What actually happened, illustrated with screenshots if possible. + +Your bug report should also contain your WordPress version and if there are any errors, the _exact_ error text, including line numbers. + +### Blank / white screen +If you're getting a blank screen and you report just that, we can do _absolutely_ nothing. By default, your WordPress install suppresses all errors, to prevent information leaks, but we need those errors to be able to help you. If you apply the small piece of code in [this post on WP_DEBUG](https://yoast.com/wordpress-debug/) to your site, you should be able to open the URL that gave you a white screen, append `?debug=debug` to the URL and get the actual error. + +That error will help us, without that error, we're completely in the dark about your white page problem... + +### Interface errors +If you're reporting a bug about specific interface elements not working as expected, there's probably an error showing in your browsers JavaScript console. Please open your browsers console and copy the exact error showing there, or make a screenshot. If you don't know how to open your browsers console, here is info for [Chrome](https://developer.chrome.com/devtools/docs/console) and [Firefox](https://developer.mozilla.org/en/docs/Tools/Web_Console). For IE, some Googling will help but it changes with every version. + + +#Contribute To WordPress SEO + +Community made patches, localisations, bug reports and contributions are very welcome and help make WordPress SEO remains the #1 SEO plugin for WordPress. + +When contributing please ensure you follow the guidelines below so that we can keep on top of things. + +## Getting Started + +If there is no ticket for your issue, submit it first, following the above guidelines. + +## Making Changes + +* Fork the repository on GitHub. +* Make the changes to your forked repository. + * Ensure you stick to the [WordPress Coding Standards](http://make.wordpress.org/core/handbook/coding-standards/) and have properly documented any new functions, actions and filters following the [documentation standards](http://make.wordpress.org/core/handbook/inline-documentation-standards/php-documentation-standards/). +* When committing, reference your issue (if present) and include a note about the fix. +* Push the changes to your fork and submit a pull request to the 'master' branch of the WordPress SEO repository. + +At this point you're waiting on us to merge your pull request. We'll review all pull requests, and make suggestions and changes if necessary. + +# Additional Resources +* [WordPress SEO API](https://yoast.com/wordpress/plugins/seo/api/) +* [General GitHub Documentation](http://help.github.com/) +* [GitHub Pull Request documentation](http://help.github.com/send-pull-requests/) diff --git a/wp-content/plugins/wordpress-seo/README.md b/wp-content/plugins/wordpress-seo/README.md new file mode 100644 index 0000000..b43c340 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/README.md @@ -0,0 +1,47 @@ +WordPress SEO by Yoast +====================== + +[![Build Status](https://api.travis-ci.org/Yoast/wordpress-seo.png?branch=master)](https://travis-ci.org/Yoast/wordpress-seo) +[![Stable Version](https://poser.pugx.org/yoast/wordpress-seo/v/stable.svg)](https://packagist.org/packages/yoast/wordpress-seo) +[![License](https://poser.pugx.org/yoast/wordpress-seo/license.svg)](https://packagist.org/packages/yoast/wordpress-seo) + +Welcome to the WordPress SEO Github repository +---------------------------------------------- + +While the documenation for the [WordPress SEO plugin](https://yoast.com/wordpress/seo/) can be found on yoast.com, here +you can browse the source of the project, find and discuss open issues and even +[contribute yourself](https://github.com/yoast/wordpress-seo/blob/master/CONTRIBUTING.md). + +Installation +------------ + +Here's a [guide on how to install WordPress SEO in your WordPress site](https://yoast.com/wordpress/seo/installation/). +If you want to run the Git version though, you have two options: + +* You can clone the GitHub repository: https://github.com/yoast/wordpress-seo.git +* Downloading it directly unfortunately won't work as the .zip file doesn't contain the submodules. + +This will download the latest development version of WordPress SEO by Yoast. While this version is usually stable, +it is not recommended for use in a production environment. + +Bugs +---- +If you find an issue, [let us know here](https://github.com/yoast/wordpress-seo/issues/new)! + +Support +------- +This is a developer's portal for WordPress SEO by Yoast and should not be used for support. Please visit the +[support forums](https://wordpress.org/support/plugin/wordpress-seo). + +Contributions +------------- +Anyone is welcome to contribute to WordPress SEO. Please +[read the guidelines](https://github.com/yoast/wordpress-seo/blob/master/CONTRIBUTING.md) for contributing to this +repository. + +There are various ways you can contribute: + +* [Raise an issue](https://github.com/yoast/wordpress-seo/issues) on GitHub. +* Send us a Pull Request with your bug fixes and/or new features. +* [Translate WordPress SEO by Yoast into different languages](http://translate.yoast.com/projects/wordpress-seo/). +* Provide feedback and [suggestions on enhancements](https://github.com/yoast/wordpress-seo/issues?direction=desc&labels=Enhancement&page=1&sort=created&state=open). diff --git a/wp-content/plugins/wordpress-seo/admin/TextStatistics.php b/wp-content/plugins/wordpress-seo/admin/TextStatistics.php new file mode 100644 index 0000000..559c3e5 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/TextStatistics.php @@ -0,0 +1,416 @@ + whomever] Research if a class/library can be found which will offer + * this functionality to a broader scope of languages/charsets. + * Now basically limited to English. + */ + class Yoast_TextStatistics { + + /** + * @var string $strEncoding Used to hold character encoding to be used by object, if set + */ + protected $strEncoding = ''; + + /** + * @var string $blnMbstring Efficiency: Is the MB String extension loaded ? + */ + protected $blnMbstring = true; + + /** + * @var bool $normalize Should the result be normalized ? + */ + public $normalize = true; + + + /** + * Constructor. + * + * @param string $strEncoding Optional character encoding. + */ + public function __construct( $strEncoding = '' ) { + if ( $strEncoding <> '' ) { + // Encoding is given. Use it! + $this->strEncoding = $strEncoding; + } + $this->blnMbstring = extension_loaded( 'mbstring' ); + } + + /** + * Gives the Flesch-Kincaid Reading Ease of text entered rounded to one digit + * + * @param string $strText Text to be checked + * @return int|float + */ + public function flesch_kincaid_reading_ease( $strText ) { + $strText = $this->clean_text( $strText ); + $score = wpseo_calc( wpseo_calc( 206.835, '-', wpseo_calc( 1.015, '*', $this->average_words_per_sentence( $strText ) ) ), '-', wpseo_calc( 84.6, '*', $this->average_syllables_per_word( $strText ) ) ); + + return $this->normalize_score( $score, 0, 100 ); + } + + /** + * Gives string length. + * + * @param string $strText Text to be measured + * + * @return int + */ + public function text_length( $strText ) { + if ( ! $this->blnMbstring ) { + return strlen( $strText ); + } + + try { + if ( $this->strEncoding == '' ) { + $intTextLength = mb_strlen( $strText ); + } else { + $intTextLength = mb_strlen( $strText, $this->strEncoding ); + } + } catch ( Exception $e ) { + $intTextLength = strlen( $strText ); + } + + return $intTextLength; + } + + /** + * Gives letter count (ignores all non-letters). Tries mb_strlen and if that fails uses regular strlen. + * + * @param string $strText Text to be measured + * + * @return int + */ + public function letter_count( $strText ) { + $strText = $this->clean_text( $strText ); // To clear out newlines etc + $strText = preg_replace( '`[^A-Za-z]+`', '', $strText ); + + if ( ! $this->blnMbstring ) { + return strlen( $strText ); + } + + try { + if ( $this->strEncoding == '' ) { + $intTextLength = mb_strlen( $strText ); + } else { + $intTextLength = mb_strlen( $strText, $this->strEncoding ); + } + } catch ( Exception $e ) { + $intTextLength = strlen( $strText ); + } + + return $intTextLength; + } + + /** + * Trims, removes line breaks, multiple spaces and generally cleans text before processing. + * + * @param string $strText Text to be transformed + * @return string + */ + protected function clean_text( $strText ) { + static $clean = array(); + + $key = sha1( $strText ); + + if ( isset( $clean[ $key ] ) ) { + return $clean[ $key ]; + } + + // all these tags should be preceeded by a full stop. + $fullStopTags = array( 'li', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'dd' ); + foreach ( $fullStopTags as $tag ) { + $strText = str_ireplace( '', '.', $strText ); + } + $strText = strip_tags( $strText ); + $strText = preg_replace( '`[",:;\(\)-]`', ' ', $strText ); // Replace commas, hyphens etc (count them as spaces) + $strText = preg_replace( '`[\.!?]`', '.', $strText ); // Unify terminators + $strText = trim( $strText ) . '.'; // Add final terminator, just in case it's missing. + $strText = preg_replace( '`[ ]*(\n|\r\n|\r)[ ]*`', ' ', $strText ); // Replace new lines with spaces + $strText = preg_replace( '`([\.])[\. ]+`', '$1', $strText ); // Check for duplicated terminators + $strText = trim( preg_replace( '`[ ]*([\.])`', '$1 ', $strText ) ); // Pad sentence terminators + $strText = preg_replace( '` [0-9]+ `', ' ', ' ' . $strText . ' ' ); // Remove "words" comprised only of numbers + $strText = preg_replace( '`[ ]+`', ' ', $strText ); // Remove multiple spaces + $strText = preg_replace_callback( '`\. [^ ]+?`', create_function( '$matches', 'return strtolower( $matches[0] );' ), $strText ); // Lower case all words following terminators (for gunning fog score) + + $strText = trim( $strText ); + + // Cache it and return + $clean[ $key ] = $strText; + return $strText; + } + + /** + * Converts string to lower case. Tries mb_strtolower and if that fails uses regular strtolower. + * + * @param string $strText Text to be transformed + * @return string + */ + protected function lower_case( $strText ) { + + if ( ! $this->blnMbstring ) { + return strtolower( $strText ); + } + + try { + if ( $this->strEncoding == '' ) { + $strLowerCaseText = mb_strtolower( $strText ); + } else { + $strLowerCaseText = mb_strtolower( $strText, $this->strEncoding ); + } + } catch ( Exception $e ) { + $strLowerCaseText = strtolower( $strText ); + } + + return $strLowerCaseText; + } + + /** + * Converts string to upper case. Tries mb_strtoupper and if that fails uses regular strtoupper. + * + * @param string $strText Text to be transformed + * @return string + */ + protected function upper_case( $strText ) { + if ( ! $this->blnMbstring ) { + return strtoupper( $strText ); + } + + try { + if ( $this->strEncoding == '' ) { + $strUpperCaseText = mb_strtoupper( $strText ); + } else { + $strUpperCaseText = mb_strtoupper( $strText, $this->strEncoding ); + } + } catch ( Exception $e ) { + $strUpperCaseText = strtoupper( $strText ); + } + + return $strUpperCaseText; + } + + /** + * Returns sentence count for text. + * + * @param string $strText Text to be measured + * @return int + */ + public function sentence_count( $strText ) { + if ( strlen( trim( $strText ) ) == 0 ) { + return 0; + } + + $strText = $this->clean_text( $strText ); + // Will be tripped up by "Mr." or "U.K.". Not a major concern at this point. + // [JRF] Will also be tripped up by ... or ?! + // @todo [JRF => whomever] May be replace with something along the lines of this - will at least provide better count in ... and ?! situations: + // $intSentences = max( 1, preg_match_all( '`[^\.!?]+[\.!?]+([\s]+|$)`u', $strText, $matches ) ); [/JRF] + $intSentences = max( 1, $this->text_length( preg_replace( '`[^\.!?]`', '', $strText ) ) ); + return $intSentences; + } + + /** + * Returns word count for text. + * + * @param string $strText Text to be measured + * @return int + */ + public function word_count( $strText ) { + if ( strlen( trim( $strText ) ) == 0 ) { + return 0; + } + + $strText = $this->clean_text( $strText ); + // Will be tripped by em dashes with spaces either side, among other similar characters + $intWords = 1 + $this->text_length( preg_replace( '`[^ ]`', '', $strText ) ); // Space count + 1 is word count + return $intWords; + } + + /** + * Returns average words per sentence for text. + * + * @param string $strText Text to be measured + * @return int|float + */ + public function average_words_per_sentence( $strText ) { + $strText = $this->clean_text( $strText ); + $intSentenceCount = $this->sentence_count( $strText ); + $intWordCount = $this->word_count( $strText ); + return ( wpseo_calc( $intWordCount, '/', $intSentenceCount ) ); + } + + /** + * Returns average syllables per word for text. + * + * @param string $strText Text to be measured + * @return int|float + */ + public function average_syllables_per_word( $strText ) { + $strText = $this->clean_text( $strText ); + $intSyllableCount = 0; + $intWordCount = $this->word_count( $strText ); + $arrWords = explode( ' ', $strText ); + for ( $i = 0; $i < $intWordCount; $i++ ) { + $intSyllableCount += $this->syllable_count( $arrWords[ $i ] ); + } + return ( wpseo_calc( $intSyllableCount, '/', $intWordCount ) ); + } + + /** + * Returns the number of syllables in the word. + * Based in part on Greg Fast's Perl module Lingua::EN::Syllables + * + * @param string $strWord Word to be measured + * @return int + */ + public function syllable_count( $strWord ) { + if ( strlen( trim( $strWord ) ) == 0 ) { + return 0; + } + + // Should be no non-alpha characters + $strWord = preg_replace( '`[^A-Za-z]`', '', $strWord ); + + $intSyllableCount = 0; + $strWord = $this->lower_case( $strWord ); + + // Specific common exceptions that don't follow the rule set below are handled individually + // Array of problem words (with word as key, syllable count as value) + $arrProblemWords = array( + 'simile' => 3, + 'forever' => 3, + 'shoreline' => 2, + ); + if ( isset( $arrProblemWords[ $strWord ] ) ) { + $intSyllableCount = $arrProblemWords[ $strWord ]; + } + if ( $intSyllableCount > 0 ) { + return $intSyllableCount; + } + + // These syllables would be counted as two but should be one + $arrSubSyllables = array( + 'cial', + 'tia', + 'cius', + 'cious', + 'giu', + 'ion', + 'iou', + 'sia$', + '[^aeiuoyt]{2,}ed$', + '.ely$', + '[cg]h?e[rsd]?$', + 'rved?$', + '[aeiouy][dt]es?$', + '[aeiouy][^aeiouydt]e[rsd]?$', + // Sorts out deal, deign etc + '^[dr]e[aeiou][^aeiou]+$', + // Purse, hearse + '[aeiouy]rse$', + ); + + // These syllables would be counted as one but should be two + $arrAddSyllables = array( + 'ia', + 'riet', + 'dien', + 'iu', + 'io', + 'ii', + '[aeiouym]bl$', + '[aeiou]{3}', + '^mc', + 'ism$', + '([^aeiouy])\1l$', + '[^l]lien', + '^coa[dglx].', + '[^gq]ua[^auieo]', + 'dnt$', + 'uity$', + 'ie(r|st)$', + ); + + // Single syllable prefixes and suffixes + $arrPrefixSuffix = array( + '`^un`', + '`^fore`', + '`ly$`', + '`less$`', + '`ful$`', + '`ers?$`', + '`ings?$`', + ); + + // Remove prefixes and suffixes and count how many were taken + $strWord = preg_replace( $arrPrefixSuffix, '', $strWord, -1, $intPrefixSuffixCount ); + + // Removed non-word characters from word + $strWord = preg_replace( '`[^a-z]`is', '', $strWord ); + $arrWordParts = preg_split( '`[^aeiouy]+`', $strWord ); + $intWordPartCount = 0; + foreach ( $arrWordParts as $strWordPart ) { + if ( $strWordPart <> '' ) { + $intWordPartCount++; + } + } + + // Some syllables do not follow normal rules - check for them + // Thanks to Joe Kovar for correcting a bug in the following lines + $intSyllableCount = $intWordPartCount + $intPrefixSuffixCount; + foreach ( $arrSubSyllables as $strSyllable ) { + $intSyllableCount -= preg_match( '`' . $strSyllable . '`', $strWord ); + } + foreach ( $arrAddSyllables as $strSyllable ) { + $intSyllableCount += preg_match( '`' . $strSyllable . '`', $strWord ); + } + $intSyllableCount = ( $intSyllableCount == 0 ) ? 1 : $intSyllableCount; + return $intSyllableCount; + } + + /** + * Normalizes score according to min & max allowed. If score larger + * than max, max is returned. If score less than min, min is returned. + * Also rounds result to specified precision. + * Thanks to github.com/lvil. + * + * @param int|float $score Initial score + * @param int $min Minimum score allowed + * @param int $max Maximum score allowed + * @return int|float + */ + public function normalize_score( $score, $min, $max, $dps = 1 ) { + $score = wpseo_calc( $score, '+', 0, true, $dps ); // Round + if ( ! $this->normalize ) { + return $score; + } + + if ( $score > $max ) { + $score = $max; + } elseif ( $score < $min ) { + $score = $min; + } + + return $score; + } + + } /* End of class */ +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/admin/ajax.php b/wp-content/plugins/wordpress-seo/admin/ajax.php new file mode 100644 index 0000000..c2edd95 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/ajax.php @@ -0,0 +1,295 @@ + $file ) { + if ( ! @unlink( $file ) ) { + $message = __( 'Some files could not be removed. Please remove them via FTP.', 'wordpress-seo' ); + } + else { + unset( $options['blocking_files'][ $k ] ); + $files_removed++; + } + } + if ( $files_removed > 0 ) { + update_option( 'wpseo', $options ); + } + } + + die( $message ); +} + +add_action( 'wp_ajax_wpseo_kill_blocking_files', 'wpseo_kill_blocking_files' ); + +/** + * Retrieve the suggestions from the Google Suggest API and return them to be + * used in the suggest box within the plugin. Dies on exit. + */ +function wpseo_get_suggest() { + check_ajax_referer( 'wpseo-get-suggest' ); + + $term = urlencode( $_GET['term'] ); + $result = wp_remote_get( 'https://www.google.com/complete/search?output=toolbar&q=' . $term ); + + $return_arr = array(); + + if ( ! is_wp_error( $result ) ) { + preg_match_all( '`suggestion data="([^"]+)"/>`u', $result['body'], $matches ); + + if ( isset( $matches[1] ) && ( is_array( $matches[1] ) && $matches[1] !== array() ) ) { + foreach ( $matches[1] as $match ) { + $return_arr[] = html_entity_decode( $match, ENT_COMPAT, 'UTF-8' ); + } + } + } + echo json_encode( $return_arr ); + die(); +} + +add_action( 'wp_ajax_wpseo_get_suggest', 'wpseo_get_suggest' ); + +/** + * Used in the editor to replace vars for the snippet preview + */ +function wpseo_ajax_replace_vars() { + check_ajax_referer( 'wpseo-replace-vars' ); + + $post = get_post( $_POST['post_id'] ); + $omit = array( 'excerpt', 'excerpt_only', 'title' ); + echo wpseo_replace_vars( stripslashes( $_POST['string'] ), $post, $omit ); + die; +} + +add_action( 'wp_ajax_wpseo_replace_vars', 'wpseo_ajax_replace_vars' ); + +/** + * Save an individual SEO title from the Bulk Editor. + */ +function wpseo_save_title() { + check_ajax_referer( 'wpseo-bulk-editor' ); + + $new_title = $_POST['new_title']; + $id = intval( $_POST['wpseo_post_id'] ); + $original_title = $_POST['existing_title']; + + $results = wpseo_upsert_new_title( $id, $new_title, $original_title ); + + echo json_encode( $results ); + die(); +} + +add_action( 'wp_ajax_wpseo_save_title', 'wpseo_save_title' ); + +/** + * Helper function for updating an existing seo title or create a new one + * if it doesn't already exist. + */ +function wpseo_upsert_new_title( $post_id, $new_title, $original_title ) { + + $meta_key = WPSEO_Meta::$meta_prefix . 'title'; + $return_key = 'title'; + return wpseo_upsert_meta( $post_id, $new_title, $original_title, $meta_key, $return_key ); +} + +/** + * Helper function to update a post's meta data, returning relevant information + * about the information updated and the results or the meta update. + */ +function wpseo_upsert_meta( $post_id, $new_meta_value, $orig_meta_value, $meta_key, $return_key ) { + + $post_id = intval( $post_id ); + $sanitized_new_meta_value = wp_strip_all_tags( $new_meta_value ); + $orig_meta_value = wp_strip_all_tags( $orig_meta_value ); + + $upsert_results = array( + 'status' => 'success', + 'post_id' => $post_id, + "new_{$return_key}" => $new_meta_value, + "original_{$return_key}" => $orig_meta_value, + ); + + $the_post = get_post( $post_id ); + if ( empty( $the_post ) ) { + + $upsert_results['status'] = 'failure'; + $upsert_results['results'] = __( 'Post doesn\'t exist.', 'wordpress-seo' ); + + return $upsert_results; + } + + $post_type_object = get_post_type_object( $the_post->post_type ); + if ( ! $post_type_object ) { + + $upsert_results['status'] = 'failure'; + $upsert_results['results'] = sprintf( __( 'Post has an invalid Post Type: %s.', 'wordpress-seo' ), $the_post->post_type ); + + return $upsert_results; + } + + if ( ! current_user_can( $post_type_object->cap->edit_posts ) ) { + + $upsert_results['status'] = 'failure'; + $upsert_results['results'] = sprintf( __( 'You can\'t edit %s.', 'wordpress-seo' ), $post_type_object->label ); + + return $upsert_results; + } + + if ( ! current_user_can( $post_type_object->cap->edit_others_posts ) && $the_post->post_author != get_current_user_id() ) { + + $upsert_results['status'] = 'failure'; + $upsert_results['results'] = sprintf( __( 'You can\'t edit %s that aren\'t yours.', 'wordpress-seo' ), $post_type_object->label ); + + return $upsert_results; + + } + + if ( $sanitized_new_meta_value === $orig_meta_value && $sanitized_new_meta_value !== $new_meta_value ) { + $upsert_results['status'] = 'failure'; + $upsert_results['results'] = __( 'You have used HTML in your value which is not allowed.', 'wordpress-seo' ); + + return $upsert_results; + } + + $res = update_post_meta( $post_id, $meta_key, $sanitized_new_meta_value ); + + $upsert_results['status'] = ( $res !== false ) ? 'success' : 'failure'; + $upsert_results['results'] = $res; + + return $upsert_results; +} + +/** + * Save all titles sent from the Bulk Editor. + */ +function wpseo_save_all_titles() { + check_ajax_referer( 'wpseo-bulk-editor' ); + + $new_titles = $_POST['titles']; + $original_titles = $_POST['existing_titles']; + + $results = array(); + + if ( is_array( $new_titles ) && $new_titles !== array() ) { + foreach ( $new_titles as $id => $new_title ) { + $original_title = $original_titles[ $id ]; + $results[] = wpseo_upsert_new_title( $id, $new_title, $original_title ); + } + } + echo json_encode( $results ); + die(); +} + +add_action( 'wp_ajax_wpseo_save_all_titles', 'wpseo_save_all_titles' ); + +/** + * Save an individual meta description from the Bulk Editor. + */ +function wpseo_save_description() { + check_ajax_referer( 'wpseo-bulk-editor' ); + + $new_metadesc = $_POST['new_metadesc']; + $id = intval( $_POST['wpseo_post_id'] ); + $original_metadesc = $_POST['existing_metadesc']; + + $results = wpseo_upsert_new_description( $id, $new_metadesc, $original_metadesc ); + + echo json_encode( $results ); + die(); +} + +add_action( 'wp_ajax_wpseo_save_desc', 'wpseo_save_description' ); + +/** + * Helper function to create or update a post's meta description. + */ +function wpseo_upsert_new_description( $post_id, $new_metadesc, $original_metadesc ) { + + $meta_key = WPSEO_Meta::$meta_prefix . 'metadesc'; + $return_key = 'metadesc'; + return wpseo_upsert_meta( $post_id, $new_metadesc, $original_metadesc, $meta_key, $return_key ); +} + +/** + * Save all description sent from the Bulk Editor. + */ +function wpseo_save_all_descriptions() { + check_ajax_referer( 'wpseo-bulk-editor' ); + + $new_metadescs = $_POST['metadescs']; + $original_metadescs = $_POST['existing_metadescs']; + + $results = array(); + + if ( is_array( $new_metadescs ) && $new_metadescs !== array() ) { + foreach ( $new_metadescs as $id => $new_metadesc ) { + $original_metadesc = $original_metadescs[ $id ]; + $results[] = wpseo_upsert_new_description( $id, $new_metadesc, $original_metadesc ); + } + } + echo json_encode( $results ); + die(); +} + +add_action( 'wp_ajax_wpseo_save_all_descs', 'wpseo_save_all_descriptions' ); diff --git a/wp-content/plugins/wordpress-seo/admin/class-admin.php b/wp-content/plugins/wordpress-seo/admin/class-admin.php new file mode 100644 index 0000000..3bb8800 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-admin.php @@ -0,0 +1,872 @@ +' . __( 'Extensions', 'wordpress-seo' ) . '', + $manage_options_cap, + 'wpseo_licenses', + array( $this, 'load_page' ), + null, + ); + + // Allow submenu pages manipulation + $submenu_pages = apply_filters( 'wpseo_submenu_pages', $submenu_pages ); + + // Loop through submenu pages and add them + if ( count( $submenu_pages ) ) { + foreach ( $submenu_pages as $submenu_page ) { + + // Add submenu page + $admin_page = add_submenu_page( $submenu_page[0], $submenu_page[2] . ' - ' . __( 'Yoast WordPress SEO:', 'wordpress-seo' ), $submenu_page[2], $submenu_page[3], $submenu_page[4], $submenu_page[5] ); + + // Check if we need to hook + if ( isset( $submenu_page[6] ) && null != $submenu_page[6] && is_array( $submenu_page[6] ) && count( $submenu_page[6] ) > 0 ) { + foreach ( $submenu_page[6] as $submenu_page_action ) { + add_action( 'load-' . $admin_page, $submenu_page_action ); + } + } + } + } + + global $submenu; + if ( isset( $submenu['wpseo_dashboard'] ) && current_user_can( $manage_options_cap ) ) { + $submenu['wpseo_dashboard'][0][0] = __( 'Dashboard', 'wordpress-seo' ); + } + } + + /** + * Adds contextual help to the titles & metas page. + */ + function title_metas_help_tab() { + $screen = get_current_screen(); + + $screen->set_help_sidebar( + '

' . __( 'For more information:', 'wordpress-seo' ) . '

' . + '

' . __( 'Title optimization', 'wordpress-seo' ) . '

' . + '

' . __( 'Why Google won\'t display the right page title', 'wordpress-seo' ) . '

' + ); + + $screen->add_help_tab( + array( + 'id' => 'basic-help', + 'title' => __( 'Template explanation', 'wordpress-seo' ), + 'content' => '

' . __( 'The title & metas settings for WordPress SEO 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' ) . '

', + ) + ); + + + $screen->add_help_tab( + array( + 'id' => 'title-vars', + 'title' => __( 'Basic Variables', 'wordpress-seo' ), + 'content' => "\n\t\t

" . __( 'Basic Variables', 'wordpress-seo' ) . "

\n\t\t" . WPSEO_Replace_Vars::get_basic_help_texts(), + ) + ); + + $screen->add_help_tab( + array( + 'id' => 'title-vars-advanced', + 'title' => __( 'Advanced Variables', 'wordpress-seo' ), + 'content' => "\n\t\t

" . __( 'Advanced Variables', 'wordpress-seo' ) . "

\n\t\t" . WPSEO_Replace_Vars::get_advanced_help_texts(), + ) + ); + } + + /** + * Register the settings page for the Network settings. + */ + function register_network_settings_page() { + if ( WPSEO_Options::grant_access() ) { + // Base 64 encoded SVG image + $icon_svg = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCIgWw0KCTwhRU5USVRZIG5zX2Zsb3dzICJodHRwOi8vbnMuYWRvYmUuY29tL0Zsb3dzLzEuMC8iPg0KCTwhRU5USVRZIG5zX2V4dGVuZCAiaHR0cDovL25zLmFkb2JlLmNvbS9FeHRlbnNpYmlsaXR5LzEuMC8iPg0KCTwhRU5USVRZIG5zX2FpICJodHRwOi8vbnMuYWRvYmUuY29tL0Fkb2JlSWxsdXN0cmF0b3IvMTAuMC8iPg0KCTwhRU5USVRZIG5zX2dyYXBocyAiaHR0cDovL25zLmFkb2JlLmNvbS9HcmFwaHMvMS4wLyI+DQpdPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYWFnXzEiIHhtbG5zOng9IiZuc19leHRlbmQ7IiB4bWxuczppPSImbnNfYWk7IiB4bWxuczpncmFwaD0iJm5zX2dyYXBoczsiDQoJIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbG5zOmE9Imh0dHA6Ly9ucy5hZG9iZS5jb20vQWRvYmVTVkdWaWV3ZXJFeHRlbnNpb25zLzMuMC8iDQoJIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNDAgMzEuODkiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDQwIDMxLjg5IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KPHBhdGggZmlsbD0iI0ZGRkZGRiIgZD0iTTQwLDEyLjUyNEM0MCw1LjYwOCwzMS40NjksMCwyMCwwQzguNTMsMCwwLDUuNjA4LDAsMTIuNTI0YzAsNS41Niw1LjI0MywxMC4yNzIsMTMuNTU3LDExLjkwN3YtNC4wNjUNCgljMCwwLDAuMDQtMS0wLjI4LTEuOTJjLTAuMzItMC45MjEtMS43Ni0zLjAwMS0xLjc2LTUuMTIxYzAtMi4xMjEsMi41NjEtOS41NjMsNS4xMjItMTAuNDQ0Yy0wLjQsMS4yMDEtMC4zMiw3LjY4My0wLjMyLDcuNjgzDQoJczEuNCwyLjcyLDQuNjQxLDIuNzJjMy4yNDIsMCw0LjUxMS0xLjc2LDQuNzE1LTIuMmMwLjIwNi0wLjQ0LDAuODQ2LTguNzIzLDAuODQ2LTguNzIzczQuMDgyLDQuNDAyLDMuNjgyLDkuMzYzDQoJYy0wLjQwMSw0Ljk2Mi00LjQ4Miw3LjIwMy02LjEyMiw5LjEyM2MtMS4yODYsMS41MDUtMi4yMjQsMy4xMy0yLjYyOSw0LjE2OGMwLjgwMS0wLjAzNCwxLjU4Ny0wLjA5OCwyLjM2MS0wLjE4NGw5LjE1MSw3LjA1OQ0KCWwtNC44ODQtNy44M0MzNS41MzUsMjIuMTYxLDQwLDE3LjcxMyw0MCwxMi41MjR6Ii8+DQo8L2c+DQo8L3N2Zz4='; + add_menu_page( __( 'Yoast WordPress SEO:', 'wordpress-seo' ) . ' ' . __( 'MultiSite Settings', 'wordpress-seo' ), __( 'SEO', 'wordpress-seo' ), 'delete_users', 'wpseo_dashboard', array( + $this, + 'network_config_page', + ), $icon_svg ); + + if ( wpseo_allow_system_file_edit() === true ) { + add_submenu_page( 'wpseo_dashboard', __( 'Yoast WordPress SEO:', 'wordpress-seo' ) . ' ' . __( 'Edit Files', 'wordpress-seo' ), __( 'Edit Files', 'wordpress-seo' ), 'delete_users', 'wpseo_files', array( + $this, + 'load_page', + ) ); + } + + // Add Extension submenu page + add_submenu_page( 'wpseo_dashboard', __( 'Yoast WordPress SEO:', 'wordpress-seo' ) . ' ' . __( 'Extensions', 'wordpress-seo' ), __( 'Extensions', 'wordpress-seo' ), 'delete_users', 'wpseo_licenses', array( + $this, + 'load_page', + ) ); + } + } + + + /** + * Load the form for a WPSEO admin page + */ + function load_page() { + if ( isset( $_GET['page'] ) ) { + switch ( $_GET['page'] ) { + case 'wpseo_titles': + require_once( WPSEO_PATH . 'admin/pages/metas.php' ); + break; + + case 'wpseo_social': + require_once( WPSEO_PATH . 'admin/pages/social.php' ); + break; + + case 'wpseo_xml': + require_once( WPSEO_PATH . 'admin/pages/xml-sitemaps.php' ); + break; + + case 'wpseo_permalinks': + require_once( WPSEO_PATH . 'admin/pages/permalinks.php' ); + break; + + case 'wpseo_internal-links': + require_once( WPSEO_PATH . 'admin/pages/internal-links.php' ); + break; + + case 'wpseo_rss': + require_once( WPSEO_PATH . 'admin/pages/rss.php' ); + break; + + case 'wpseo_import': + require_once( WPSEO_PATH . 'admin/pages/import.php' ); + break; + + case 'wpseo_files': + require_once( WPSEO_PATH . 'admin/pages/files.php' ); + break; + + case 'wpseo_bulk-editor': + require_once( WPSEO_PATH . 'admin/pages/bulk-editor.php' ); + break; + + case 'wpseo_licenses': + require_once( WPSEO_PATH . 'admin/pages/licenses.php' ); + break; + + case 'wpseo_dashboard': + default: + require_once( WPSEO_PATH . 'admin/pages/dashboard.php' ); + break; + } + } + } + + + /** + * Loads the form for the network configuration page. + */ + function network_config_page() { + require_once( WPSEO_PATH . 'admin/pages/network.php' ); + } + + + /** + * Adds the ability to choose how many posts are displayed per page + * on the bulk edit pages. + */ + function bulk_edit_options() { + $option = 'per_page'; + $args = array( + 'label' => __( 'Posts', 'wordpress-seo' ), + 'default' => 10, + 'option' => 'wpseo_posts_per_page', + ); + add_screen_option( $option, $args ); + } + + /** + * Saves the posts per page limit for bulk edit pages. + */ + function save_bulk_edit_options( $status, $option, $value ) { + if ( 'wpseo_posts_per_page' === $option && ( $value > 0 && $value < 1000 ) ) { + return $value; + } + + return $status; + } + + /** + * Display an error message when the blog is set to private. + */ + function blog_public_warning() { + if ( ( function_exists( 'is_network_admin' ) && is_network_admin() ) || WPSEO_Options::grant_access() !== true ) { + return; + } + + $options = get_option( 'wpseo' ); + if ( $options['ignore_blog_public_warning'] === true ) { + return; + } + echo '
'; + echo '

' . __( 'Huge SEO Issue: You\'re blocking access to robots.', 'wordpress-seo' ) . ' ' . sprintf( __( 'You must %sgo to your Reading Settings%s and uncheck the box for Search Engine Visibility.', 'wordpress-seo' ), '', '' ) . ' ' . __( 'I know, don\'t bug me.', 'wordpress-seo' ) . '

'; + } + + /** + * Display an error message when the theme contains a meta description tag. + * + * @since 1.4.14 + */ + function meta_description_warning() { + if ( ( function_exists( 'is_network_admin' ) && is_network_admin() ) || WPSEO_Options::grant_access() !== true ) { + return; + } + + // No need to double display it on the dashboard + if ( isset( $_GET['page'] ) && 'wpseo_dashboard' === $_GET['page'] ) { + return; + } + + $options = get_option( 'wpseo' ); + if ( true === $options['ignore_meta_description_warning'] ) { + return; + } + + echo '
'; + echo '

' . __( 'SEO Issue:', 'wordpress-seo' ) . ' ' . sprintf( __( 'Your theme contains a meta description, which blocks WordPress SEO from working properly. Please visit the %sSEO Dashboard%s to fix this.', 'wordpress-seo' ), '', '' ) . ' ' . __( 'I know, don\'t bug me.', 'wordpress-seo' ) . '

'; + } + + /** + * Add a link to the settings page to the plugins list + * + * @staticvar string $this_plugin holds the directory & filename for the plugin + * + * @param array $links array of links for the plugins, adapted when the current plugin is found. + * @param string $file the filename for the current plugin, which the filter loops through. + * + * @return array $links + */ + function add_action_link( $links, $file ) { + if ( WPSEO_BASENAME === $file && WPSEO_Options::grant_access() ) { + $settings_link = '' . __( 'Settings', 'wordpress-seo' ) . ''; + array_unshift( $links, $settings_link ); + } + + if ( class_exists( 'Yoast_Product_WPSEO_Premium' ) ) { + $license_manager = new Yoast_Plugin_License_Manager( new Yoast_Product_WPSEO_Premium() ); + if ( $license_manager->license_is_valid() ) { + return $links; + } + } + + // add link to premium support landing page + $premium_link = '' . __( 'Premium Support', 'wordpress-seo' ) . ''; + array_unshift( $links, $premium_link ); + + // add link to docs + $faq_link = '' . __( 'FAQ', 'wordpress-seo' ) . ''; + array_unshift( $links, $faq_link ); + + return $links; + } + + /** + * Enqueues the (tiny) global JS needed for the plugin. + */ + function config_page_scripts() { + if ( WPSEO_Options::grant_access() ) { + wp_enqueue_script( 'wpseo-admin-global-script', plugins_url( 'js/wp-seo-admin-global' . WPSEO_CSSJS_SUFFIX . '.js', WPSEO_FILE ), array( 'jquery' ), WPSEO_VERSION, true ); + } + } + + + /** + * Updates the user metas that (might) have been set on the user profile page. + * + * @param int $user_id of the updated user + */ + function process_user_option_update( $user_id ) { + if ( isset( $_POST['wpseo_author_title'] ) ) { + check_admin_referer( 'wpseo_user_profile_update', 'wpseo_nonce' ); + update_user_meta( $user_id, 'wpseo_title', ( isset( $_POST['wpseo_author_title'] ) ? WPSEO_Option::sanitize_text_field( $_POST['wpseo_author_title'] ) : '' ) ); + update_user_meta( $user_id, 'wpseo_metadesc', ( isset( $_POST['wpseo_author_metadesc'] ) ? WPSEO_Option::sanitize_text_field( $_POST['wpseo_author_metadesc'] ) : '' ) ); + update_user_meta( $user_id, 'wpseo_metakey', ( isset( $_POST['wpseo_author_metakey'] ) ? WPSEO_Option::sanitize_text_field( $_POST['wpseo_author_metakey'] ) : '' ) ); + update_user_meta( $user_id, 'wpseo_excludeauthorsitemap', ( isset( $_POST['wpseo_author_exclude'] ) ? WPSEO_Option::sanitize_text_field( $_POST['wpseo_author_exclude'] ) : '' ) ); + } + } + + /** + * Filter the $contactmethods array and add Facebook, Google+ and Twitter. + * + * These are used with the Facebook author, rel="author" and Twitter cards implementation. + * + * @param array $contactmethods currently set contactmethods. + * + * @return array $contactmethods with added contactmethods. + */ + function update_contactmethods( $contactmethods ) { + // Add Google+ + $contactmethods['googleplus'] = __( 'Google+', 'wordpress-seo' ); + // Add Twitter + $contactmethods['twitter'] = __( 'Twitter username (without @)', 'wordpress-seo' ); + // Add Facebook + $contactmethods['facebook'] = __( 'Facebook profile URL', 'wordpress-seo' ); + + return $contactmethods; + } + + /** + * Add the inputs needed for SEO values to the User Profile page + * + * @param object $user + */ + function user_profile( $user ) { + + if ( ! current_user_can( 'edit_users' ) ) { + return; + } + + $options = WPSEO_Options::get_all(); + + wp_nonce_field( 'wpseo_user_profile_update', 'wpseo_nonce' ); + ?> +

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

+ stopwords() ); + + // Turn the sanitized array into a string + $clean_slug = join( '-', $clean_slug_array ); + + return $clean_slug; + } + + /** + * Returns the stopwords for the current language + * + * @since 1.1.7 + * + * @return array $stopwords array of stop words to check and / or remove from slug + */ + function stopwords() { + /* translators: this should be an array of stopwords for your language, separated by comma's. */ + $stopwords = explode( ',', __( "a,about,above,after,again,against,all,am,an,and,any,are,aren't,as,at,be,because,been,before,being,below,between,both,but,by,can't,cannot,could,couldn't,did,didn't,do,does,doesn't,doing,don't,down,during,each,few,for,from,further,had,hadn't,has,hasn't,have,haven't,having,he,he'd,he'll,he's,her,here,here's,hers,herself,him,himself,his,how,how's,i,i'd,i'll,i'm,i've,if,in,into,is,isn't,it,it's,its,itself,let's,me,more,most,mustn't,my,myself,no,nor,not,of,off,on,once,only,or,other,ought,our,ours,ourselves,out,over,own,same,shan't,she,she'd,she'll,she's,should,shouldn't,so,some,such,than,that,that's,the,their,theirs,them,themselves,then,there,there's,these,they,they'd,they'll,they're,they've,this,those,through,to,too,under,until,up,very,was,wasn't,we,we'd,we'll,we're,we've,were,weren't,what,what's,when,when's,where,where's,which,while,who,who's,whom,why,why's,with,won't,would,wouldn't,you,you'd,you'll,you're,you've,your,yours,yourself,yourselves", 'wordpress-seo' ) ); + + /** + * Allows filtering of the stop words list + * Especially useful for users on a language in which WPSEO is not available yet + * and/or users who want to turn off stop word filtering + * @api array $stopwords Array of all lowercase stopwords to check and/or remove from slug + */ + $stopwords = apply_filters( 'wpseo_stopwords', $stopwords ); + + return $stopwords; + } + + + /** + * Check whether the stopword appears in the string + * + * @param string $haystack The string to be checked for the stopword + * @param bool $checkingUrl Whether or not we're checking a URL + * + * @return bool|mixed + */ + function stopwords_check( $haystack, $checkingUrl = false ) { + $stopWords = $this->stopwords(); + + if ( is_array( $stopWords ) && $stopWords !== array() ) { + foreach ( $stopWords as $stopWord ) { + // If checking a URL remove the single quotes + if ( $checkingUrl ) { + $stopWord = str_replace( "'", '', $stopWord ); + } + + // Check whether the stopword appears as a whole word + // @todo [JRF => whomever] check whether the use of \b (=word boundary) would be more efficient ;-) + $res = preg_match( "`(^|[ \n\r\t\.,'\(\)\"\+;!?:])" . preg_quote( $stopWord, '`' ) . "($|[ \n\r\t\.,'\(\)\"\+;!?:])`iu", $haystack, $match ); + if ( $res > 0 ) { + return $stopWord; + } + } + } + + return false; + } + + /** + * Log the timestamp when a user profile has been updated + */ + function update_user_profile( $user_id ) { + if ( current_user_can( 'edit_user', $user_id ) ) { + update_user_meta( $user_id, '_yoast_wpseo_profile_updated', time() ); + } + } + + /** + * Log the updated timestamp for user profiles when theme is changed + */ + function switch_theme() { + $users = get_users( array( 'who' => 'authors' ) ); + if ( is_array( $users ) && $users !== array() ) { + foreach ( $users as $user ) { + update_user_meta( $user->ID, '_yoast_wpseo_profile_updated', time() ); + } + } + } + + /** + * This method will remove the sitemap transients on upgrade + * + * @param boolean $response + * + * @return boolean $response + */ + function remove_transients_on_update( $response ) { + + global $wpdb; + + $results = $wpdb->get_results( + " + SELECT option_name + FROM {$wpdb->options} + WHERE option_name LIKE '%_transient_wpseo_sitemap_cache%' + " + ); + + foreach ( $results as $result ) { + $transient_name = substr( $result->option_name, 11 ); + delete_transient( $transient_name ); + } + + return $response; + } + + + + + /********************** DEPRECATED METHODS **********************/ + + /** + * Check whether the current user is allowed to access the configuration. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Options::grant_access() + * @see WPSEO_Options::grant_access() + * + * @return boolean + */ + function grant_access() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Options::grant_access()' ); + + return WPSEO_Options::grant_access(); + } + + /** + * Check whether the current user is allowed to access the configuration. + * + * @deprecated 1.5.0 + * @deprecated use wpseo_do_upgrade() + * @see wpseo_do_upgrade() + * + * @return boolean + */ + function maybe_upgrade() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'wpseo_do_upgrade' ); + wpseo_do_upgrade(); + } + + /** + * Clears the cache + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Options::clear_cache() + * @see WPSEO_Options::clear_cache() + */ + function clear_cache() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Options::clear_cache()' ); + WPSEO_Options::clear_cache(); + } + + /** + * Clear rewrites + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Options::clear_rewrites() + * @see WPSEO_Options::clear_rewrites() + */ + function clear_rewrites() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Options::clear_rewrites()' ); + WPSEO_Options::clear_rewrites(); + } + + /** + * Register all the options needed for the configuration pages. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Option::register_setting() on each individual option + * @see WPSEO_Option::register_setting() + */ + function options_init() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Option::register_setting()' ); + } + + /** + * Initialize default values for a new multisite blog. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Options::set_multisite_defaults() + * @see WPSEO_Options::set_multisite_defaults() + */ + function multisite_defaults() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Options::set_multisite_defaults()' ); + WPSEO_Options::set_multisite_defaults(); + } + + /** + * Loads the form for the import/export page. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Admin::load_page() + */ + function import_page() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Admin::load_page()' ); + $this->load_page(); + } + + /** + * Loads the form for the titles & metas page. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Admin::load_page() + */ + function titles_page() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Admin::load_page()' ); + $this->load_page(); + } + + /** + * Loads the form for the permalinks page. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Admin::load_page() + */ + function permalinks_page() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Admin::load_page()' ); + $this->load_page(); + } + + /** + * Loads the form for the internal links / breadcrumbs page. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Admin::load_page() + */ + function internallinks_page() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Admin::load_page()' ); + $this->load_page(); + } + + /** + * Loads the form for the file edit page. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Admin::load_page() + */ + function files_page() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Admin::load_page()' ); + $this->load_page(); + } + + /** + * Loads the form for the RSS page. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Admin::load_page() + */ + function rss_page() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Admin::load_page()' ); + $this->load_page(); + } + + /** + * Loads the form for the XML Sitemaps page. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Admin::load_page() + */ + function xml_sitemaps_page() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Admin::load_page()' ); + $this->load_page(); + } + + /** + * Loads the form for the Dashboard page. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Admin::load_page() + */ + function config_page() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Admin::load_page()' ); + $this->load_page(); + } + + /** + * Loads the form for the Social Settings page. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Admin::load_page() + */ + function social_page() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Admin::load_page()' ); + $this->load_page(); + } + + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/admin/class-bulk-description-editor-list-table.php b/wp-content/plugins/wordpress-seo/admin/class-bulk-description-editor-list-table.php new file mode 100644 index 0000000..e4b870b --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-bulk-description-editor-list-table.php @@ -0,0 +1,92 @@ + 'wpseo_bulk_description', + 'plural' => 'wpseo_bulk_descriptions', + 'ajax' => true, + ); + + /** + * The columns shown on the table + * + * @return array + */ + function get_columns() { + return $columns = array( + 'col_page_title' => __( 'WP Page Title', 'wordpress-seo' ), + 'col_post_type' => __( 'Post Type', 'wordpress-seo' ), + 'col_post_status' => __( 'Post Status', 'wordpress-seo' ), + 'col_post_date' => __( 'Publication date', 'wordpress-seo' ), + 'col_page_slug' => __( 'Page URL/Slug', 'wordpress-seo' ), + 'col_existing_yoast_seo_metadesc' => __( 'Existing Yoast Meta Description', 'wordpress-seo' ), + 'col_new_yoast_seo_metadesc' => __( 'New Yoast Meta Description', 'wordpress-seo' ), + 'col_row_action' => __( 'Action', 'wordpress-seo' ), + ); + } + + /** + * Method for setting the meta data, which belongs to the records that will be shown on the current page + * + * This method will loop through the current items ($this->items) for getting the post_id. With this data + * ($needed_ids) the method will query the meta-data table for getting the metadescription. + * + */ + function get_meta_data() { + + global $wpdb; + + $needed_ids = array(); + foreach ( $this->items AS $item ) { + $needed_ids[] = $item->ID; + } + + $post_ids = "'" . implode( "', '", $needed_ids ) . "'"; + $meta_data = $wpdb->get_results( + " + SELECT * + FROM {$wpdb->postmeta} + WHERE post_id IN({$post_ids}) && meta_key = '" . WPSEO_Meta::$meta_prefix . "metadesc' + " + ); + + foreach ( $meta_data AS $row ) { + $this->meta_data[$row->post_id][$row->meta_key] = $row->meta_value; + } + + // Little housekeeping + unset( $needed_ids, $post_ids, $meta_data ); + + } + + + } /* End of class */ +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/admin/class-bulk-editor-list-table.php b/wp-content/plugins/wordpress-seo/admin/class-bulk-editor-list-table.php new file mode 100644 index 0000000..bce4d64 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-bulk-editor-list-table.php @@ -0,0 +1,591 @@ +page_type) there will be constructed an url part, for subpages and + * navigation + * + * @var string + */ + protected $page_url; + + /** + * The settings which will be used in the __construct. + * @var + */ + protected $settings; + + /** + * Class constructor + */ + function __construct() { + parent::__construct( $this->settings ); + + $this->request_url = $_SERVER['REQUEST_URI']; + $this->current_page = ( ! empty( $_GET['paged'] ) ) ? $_GET['paged'] : 1; + $this->current_filter = ( ! empty( $_GET['post_type_filter'] ) ) ? $_GET['post_type_filter'] : 1; + $this->current_status = ( ! empty( $_GET['post_status'] ) ) ? $_GET['post_status'] : 1; + $this->current_order = array( + 'order' => ( ! empty( $_GET['order'] ) ) ? $_GET['order'] : 'asc', + 'orderby' => ( ! empty( $_GET['orderby'] ) ) ? $_GET['orderby'] : 'post_title', + ); + $this->page_url = "&type={$this->page_type}#top#{$this->page_type}"; + + $this->populate_editable_post_types(); + + } + + /** + * Used in the constructor to build a reference list of post types the current user can edit. + */ + protected function populate_editable_post_types() { + $post_types = get_post_types( array( 'public' => true, 'exclude_from_search' => false ), 'object' ); + + $this->all_posts = array(); + $this->own_posts = array(); + + if ( is_array( $post_types ) && $post_types !== array() ) { + foreach ( $post_types as $post_type ) { + if ( ! current_user_can( $post_type->cap->edit_posts ) ) { + continue; + } + + if ( current_user_can( $post_type->cap->edit_others_posts ) ) { + $this->all_posts[] = esc_sql( $post_type->name ); + } else { + $this->own_posts[] = esc_sql( $post_type->name ); + } + } + } + } + + + /** + * Will shown the navigation for the table like pagenavigation and pagefilter; + * + * + * @param $which + */ + function display_tablenav( $which ) { + $post_status = ''; + if ( ! empty( $_GET['post_status'] ) ) { + $post_status = sanitize_text_field( $_GET['post_status'] ); + } + ?> +
+ + +
+ + + + + + + + + + + extra_tablenav( $which ); + $this->pagination( $which ); + ?> + +
+ +
+ +
+ + prepare(), passing the current user_id in as the first parameter. + */ + function get_base_subquery() { + global $wpdb; + + $all_posts_string = "'" . implode( "', '", $this->all_posts ) . "'"; + $own_posts_string = "'" . implode( "', '", $this->own_posts ) . "'"; + + $post_author = esc_sql( (int) get_current_user_id() ); + + $subquery = "( + SELECT * + FROM {$wpdb->posts} + WHERE post_type IN ({$all_posts_string}) + UNION ALL + SELECT * + FROM {$wpdb->posts} + WHERE post_type IN ({$own_posts_string}) AND post_author = {$post_author} + ) sub_base"; + + return $subquery; + } + + + /** + * @return array + */ + function get_views() { + global $wpdb; + + $status_links = array(); + + $states = get_post_stati( array( 'show_in_admin_all_list' => true ) ); + $states['trash'] = 'trash'; + $states = esc_sql( $states ); + $all_states = "'" . implode( "', '", $states ) . "'"; + + $subquery = $this->get_base_subquery(); + + $total_posts = $wpdb->get_var( + " + SELECT COUNT(ID) FROM {$subquery} + WHERE post_status IN ({$all_states}) + " + ); + + + $class = empty( $_GET['post_status'] ) ? ' class="current"' : ''; + $status_links['all'] = '' . sprintf( _nx( 'All (%s)', 'All (%s)', $total_posts, 'posts', 'wordpress-seo' ), number_format_i18n( $total_posts ) ) . ''; + + $post_stati = get_post_stati( array( 'show_in_admin_all_list' => true ), 'objects' ); + if ( is_array( $post_stati ) && $post_stati !== array() ) { + foreach ( $post_stati as $status ) { + + $status_name = esc_sql( $status->name ); + + $total = $wpdb->get_var( + $wpdb->prepare( + " + SELECT COUNT(ID) FROM {$subquery} + WHERE post_status = %s + ", + $status_name + ) + ); + + if ( $total == 0 ) { + continue; + } + + $class = ''; + if ( isset( $_GET['post_status'] ) && $status_name == $_GET['post_status'] ) { + $class = ' class="current"'; + } + + $status_links[$status_name] = '' . sprintf( translate_nooped_plural( $status->label_count, $total ), number_format_i18n( $total ) ) . ''; + } + } + unset( $post_stati, $status, $status_name, $total, $class ); + + $trashed_posts = $wpdb->get_var( + " + SELECT COUNT(ID) FROM {$subquery} + WHERE post_status IN ('trash') + " + ); + + $class = ( isset( $_GET['post_status'] ) && 'trash' == $_GET['post_status'] ) ? 'class="current"' : ''; + $status_links['trash'] = '' . sprintf( _nx( 'Trash (%s)', 'Trash (%s)', $trashed_posts, 'posts', 'wordpress-seo' ), number_format_i18n( $trashed_posts ) ) . ''; + + return $status_links; + } + + + /** + * @param $which + */ + function extra_tablenav( $which ) { + + if ( 'top' === $which ) { + $post_types = get_post_types( array( 'public' => true, 'exclude_from_search' => false ) ); + + if ( is_array( $post_types ) && $post_types !== array() ) { + global $wpdb; + + echo '
'; + + $post_types = esc_sql( $post_types ); + $post_types = "'" . implode( "', '", $post_types ) . "'"; + + $states = get_post_stati( array( 'show_in_admin_all_list' => true ) ); + $states['trash'] = 'trash'; + $states = esc_sql( $states ); + $all_states = "'" . implode( "', '", $states ) . "'"; + + $subquery = $this->get_base_subquery(); + + $post_types = $wpdb->get_results( + " + SELECT DISTINCT post_type FROM {$subquery} + WHERE post_status IN ({$all_states}) + ORDER BY 'post_type' ASC + " + ); + + $selected = ! empty( $_GET['post_type_filter'] ) ? sanitize_text_field( $_GET['post_type_filter'] ) : - 1; + + $options = ''; + + if ( is_array( $post_types ) && $post_types !== array() ) { + foreach ( $post_types as $post_type ) { + $obj = get_post_type_object( $post_type->post_type ); + $options .= sprintf( '', $obj->labels->name, $post_type->post_type, selected( $selected, $post_type->post_type, false ) ); + } + } + + echo sprintf( '', $options ); + submit_button( __( 'Filter', 'wordpress-seo' ), 'button', false, false, array( 'id' => 'post-query-submit' ) ); + echo '
'; + } + } elseif ( 'bottom' === $which ) { + + } + + } + + /** + * + * @return array + */ + function get_sortable_columns() { + return $sortable = array( + 'col_page_title' => array( 'post_title', true ), + 'col_post_type' => array( 'post_type', false ), + 'col_post_date' => array( 'post_date', false ), + ); + } + + /** + * Sets the correct pagenumber and pageurl for the navigation + * + * @param string $page_type + */ + function prepare_page_navigation() { + + $request_url = $this->request_url . $this->page_url; + + $current_page = $this->current_page; + $current_filter = $this->current_filter; + $current_status = $this->current_status; + $current_order = $this->current_order; + + // If current type doesn't compare with objects page_type, than we have to unset some vars in the requested url (which will be use for internal table urls) + if ( $_GET['type'] != $this->page_type ) { + $request_url = remove_query_arg( 'paged', $request_url ); // page will be set with value 1 below. + $request_url = remove_query_arg( 'post_type_filter', $request_url ); + $request_url = remove_query_arg( 'post_status', $request_url ); + $request_url = remove_query_arg( 'orderby', $request_url ); + $request_url = remove_query_arg( 'order', $request_url ); + $request_url = add_query_arg( 'pages', 1, $request_url ); + + $current_page = 1; + $current_filter = '-1'; + $current_status = ''; + $current_order = array( 'orderby' => 'post_title', 'order' => 'asc' ); + + } + + $_SERVER['REQUEST_URI'] = $request_url; + + $_GET['paged'] = $current_page; + $_REQUEST['paged'] = $current_page; + $_REQUEST['post_type_filter'] = $current_filter; + $_GET['post_type_filter'] = $current_filter; + $_GET['post_status'] = $current_status; + $_GET['orderby'] = $current_order['orderby']; + $_GET['order'] = $current_order['order']; + + } + + /** + * Preparing the requested pagerows and setting the needed variables + */ + function prepare_items() { + global $wpdb; + + // Filter Block + + $post_types = null; + $post_type_clause = ''; + + if ( ! empty( $_GET['post_type_filter'] ) && get_post_type_object( sanitize_text_field( $_GET['post_type_filter'] ) ) ) { + $post_types = esc_sql( sanitize_text_field( $_GET['post_type_filter'] ) ); + $post_type_clause = "AND post_type IN ('{$post_types}')"; + } + + // Order By block + $orderby = ! empty( $_GET['orderby'] ) ? esc_sql( sanitize_text_field( $_GET['orderby'] ) ) : 'post_title'; + $order = 'ASC'; + + if ( ! empty( $_GET['order'] ) ) { + $order = esc_sql( strtoupper( sanitize_text_field( $_GET['order'] ) ) ); + } + + $states = get_post_stati( array( 'show_in_admin_all_list' => true ) ); + $states['trash'] = 'trash'; + + if ( ! empty( $_GET['post_status'] ) ) { + $requested_state = sanitize_text_field( $_GET['post_status'] ); + if ( in_array( $requested_state, $states ) ) { + $states = array( $requested_state ); + } + } + + $states = esc_sql( $states ); + $all_states = "'" . implode( "', '", $states ) . "'"; + + $subquery = $this->get_base_subquery(); + + // Count the total number of needed items + $total_items = $wpdb->get_var( + " + SELECT COUNT(ID) + FROM {$subquery} + WHERE post_status IN ({$all_states}) $post_type_clause + " + ); + + // Get all needed results + $query = " + SELECT ID, post_title, post_type, post_status, post_modified, post_date + FROM {$subquery} + WHERE post_status IN ({$all_states}) $post_type_clause + ORDER BY {$orderby} {$order} + LIMIT %d,%d + "; + + $per_page = $this->get_items_per_page( 'wpseo_posts_per_page', 10 ); + + $paged = ! empty( $_GET['paged'] ) ? esc_sql( sanitize_text_field( $_GET['paged'] ) ) : ''; + + if ( empty( $paged ) || ! is_numeric( $paged ) || $paged <= 0 ) { + $paged = 1; + } + + $total_pages = ceil( $total_items / $per_page ); + + $offset = ( $paged - 1 ) * $per_page; + + $this->set_pagination_args( + array( + 'total_items' => $total_items, + 'total_pages' => $total_pages, + 'per_page' => $per_page, + ) + ); + + $columns = $this->get_columns(); + $hidden = array(); + $sortable = $this->get_sortable_columns(); + $this->_column_headers = array( $columns, $hidden, $sortable ); + + $this->items = $wpdb->get_results( + $wpdb->prepare( + $query, + $offset, + $per_page + ) + ); + + // Get the metadata for the current items ($this->items) + $this->get_meta_data(); + + } + + /** + * Based on $this->items and the defined columns, the table rows will be displayed. + * + */ + function display_rows() { + + $records = $this->items; + + list( $columns, $hidden ) = $this->get_column_info(); + + + $date_format = get_option( 'date_format' ); + + if ( ( is_array( $records ) && $records !== array() ) && ( is_array( $columns ) && $columns !== array() ) ) { + foreach ( $records as $rec ) { + + // Fill meta data if exists in $this->meta_data + $meta_data = ( ! empty( $this->meta_data[$rec->ID] ) ) ? $this->meta_data[$rec->ID] : array(); + + echo ''; + + foreach ( $columns as $column_name => $column_display_name ) { + + $class = sprintf( 'class="%1$s column-%1$s"', $column_name ); + $style = ''; + + if ( in_array( $column_name, $hidden ) ) { + $style = ' style="display:none;"'; + } + + $attributes = $class . $style; + + switch ( $column_name ) { + case 'col_page_title': + echo sprintf( '%1$s', stripslashes( $rec->post_title ), $attributes ); + + $post_type_object = get_post_type_object( $rec->post_type ); + $can_edit_post = current_user_can( $post_type_object->cap->edit_post, $rec->ID ); + + $actions = array(); + + if ( $can_edit_post && 'trash' != $rec->post_status ) { + $actions['edit'] = '' . __( 'Edit', 'wordpress-seo' ) . ''; + } + + if ( $post_type_object->public ) { + if ( in_array( $rec->post_status, array( 'pending', 'draft', 'future' ) ) ) { + if ( $can_edit_post ) { + $actions['view'] = '' . __( 'Preview', 'wordpress-seo' ) . ''; + } + } elseif ( 'trash' != $rec->post_status ) { + $actions['view'] = '' . __( 'View', 'wordpress-seo' ) . ''; + } + } + + echo $this->row_actions( $actions ); + echo ''; + break; + + case 'col_page_slug': + $permalink = get_permalink( $rec->ID ); + $display_slug = str_replace( get_bloginfo( 'url' ), '', $permalink ); + echo sprintf( '%1$s', stripslashes( $display_slug ), $attributes, esc_url( $permalink ) ); + break; + + case 'col_post_type': + $post_type = get_post_type_object( $rec->post_type ); + echo sprintf( '%1$s', $post_type->labels->singular_name, $attributes ); + break; + + case 'col_post_status': + $post_status = get_post_status_object( $rec->post_status ); + echo sprintf( '%1$s', $post_status->label, $attributes ); + break; + + case 'col_post_date': + $cell_value = date_i18n( $date_format, strtotime( $rec->post_date ) ); + echo sprintf( '%1$s', $cell_value, $attributes ); + break; + + case 'col_existing_yoast_seo_title': + $cell_value = ( ( ! empty( $meta_data[WPSEO_Meta::$meta_prefix . 'title'] ) ) ? $meta_data[WPSEO_Meta::$meta_prefix . 'title'] : '' ); + echo sprintf( '%1$s', $cell_value, $attributes, $rec->ID ); + break; + + case 'col_new_yoast_seo_title': + $input = sprintf( '', 'wpseo-new-title-' . $rec->ID, $rec->ID ); + echo sprintf( '%1$s', $input, $attributes ); + break; + + case 'col_new_yoast_seo_metadesc' : + $input = sprintf( '', 'wpseo-new-metadesc-' . $rec->ID, $rec->ID ); + echo sprintf( '%1$s', $input, $attributes ); + break; + + + case 'col_existing_yoast_seo_metadesc': + $cell_value = ( ( ! empty( $meta_data[WPSEO_Meta::$meta_prefix . 'metadesc'] ) ) ? $meta_data[WPSEO_Meta::$meta_prefix . 'metadesc'] : '' ); + echo sprintf( '%1$s', $cell_value, $attributes, $rec->ID ); + break; + + case 'col_row_action': + $actions = sprintf( 'Save | Save All', $rec->ID ); + echo sprintf( '%1$s', $actions, $attributes ); + break; + } + } + + echo ''; + } + } + } + } /* End of class */ +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/admin/class-bulk-title-editor-list-table.php b/wp-content/plugins/wordpress-seo/admin/class-bulk-title-editor-list-table.php new file mode 100644 index 0000000..142b988 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-bulk-title-editor-list-table.php @@ -0,0 +1,92 @@ + 'wpseo_bulk_title', + 'plural' => 'wpseo_bulk_titles', + 'ajax' => true, + ); + + /** + * The columns shown on the table + * + * @return array + */ + function get_columns() { + return $columns = array( + 'col_page_title' => __( 'WP Page Title', 'wordpress-seo' ), + 'col_post_type' => __( 'Post Type', 'wordpress-seo' ), + 'col_post_status' => __( 'Post Status', 'wordpress-seo' ), + 'col_post_date' => __( 'Publication date', 'wordpress-seo' ), + 'col_page_slug' => __( 'Page URL/Slug', 'wordpress-seo' ), + 'col_existing_yoast_seo_title' => __( 'Existing Yoast SEO Title', 'wordpress-seo' ), + 'col_new_yoast_seo_title' => __( 'New Yoast SEO Title', 'wordpress-seo' ), + 'col_row_action' => __( 'Action', 'wordpress-seo' ), + ); + } + + /** + * Method for setting the meta data, which belongs to the records that will be shown on the current page + * + * This method will loop through the current items ($this->items) for getting the post_id. With this data + * ($needed_ids) the method will query the meta-data table for getting the title. + * + */ + function get_meta_data() { + + global $wpdb; + + $needed_ids = array(); + foreach ( $this->items AS $item ) { + $needed_ids[] = $item->ID; + } + + $post_ids = "'" . implode( "', '", $needed_ids ) . "'"; + $meta_data = $wpdb->get_results( + " + SELECT * + FROM {$wpdb->postmeta} + WHERE post_id IN({$post_ids}) && meta_key = '" . WPSEO_Meta::$meta_prefix . "title' + " + ); + + foreach ( $meta_data AS $row ) { + $this->meta_data[$row->post_id][$row->meta_key] = $row->meta_value; + } + + + // Little housekeeping + unset( $needed_ids, $post_ids, $meta_data ); + + } + + } /* End of class */ +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/admin/class-config.php b/wp-content/plugins/wordpress-seo/admin/class-config.php new file mode 100644 index 0000000..5f6eb21 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-config.php @@ -0,0 +1,716 @@ +adminpages = apply_filters( 'wpseo_admin_pages', $this->adminpages ); + + if ( WPSEO_Options::grant_access() ) { + add_action( 'admin_enqueue_scripts', array( $this, 'config_page_scripts' ) ); + add_action( 'admin_enqueue_scripts', array( $this, 'config_page_styles' ) ); + } + } + + /** + * Generates the sidebar for admin pages. + */ + function admin_sidebar() { + + // No banners in Premium + if ( class_exists( 'WPSEO_Product_Premium' ) ) { + $license_manager = new Yoast_Plugin_License_Manager( new WPSEO_Product_Premium() ); + if ( $license_manager->license_is_valid() ) { + return; + } + } + + $service_banners = array( + array( + 'url' => 'https://yoast.com/hire-us/website-review/#utm_source=wordpress-seo-config&utm_medium=banner&utm_campaign=website-review-banner', + 'img' => 'banner-website-review.png', + 'alt' => 'Website Review banner', + ), + ); + + $plugin_banners = array( + array( + 'url' => 'https://yoast.com/wordpress/plugins/seo-premium/#utm_source=wordpress-seo-config&utm_medium=banner&utm_campaign=premium-seo-banner', + 'img' => 'banner-premium-seo.png', + 'alt' => 'Banner WordPress SEO Premium', + ), + ); + + if ( ! class_exists( 'wpseo_Video_Sitemap' ) ) { + $plugin_banners[] = array( + 'url' => 'https://yoast.com/wordpress/plugins/video-seo/#utm_source=wordpress-seo-config&utm_medium=banner&utm_campaign=video-seo-banner', + 'img' => 'banner-video-seo.png', + 'alt' => 'Banner WordPress SEO Video SEO extension', + ); + } + + if ( class_exists( 'Woocommerce' ) && ! class_exists( 'Yoast_WooCommerce_SEO' ) ) { + $plugin_banners[] = array( + 'url' => 'https://yoast.com/wordpress/plugins/yoast-woocommerce-seo/#utm_source=wordpress-seo-config&utm_medium=banner&utm_campaign=woocommerce-seo-banner', + 'img' => 'banner-woocommerce-seo.png', + 'alt' => 'Banner WooCommerce SEO plugin', + ); + } + + if ( ! defined( 'WPSEO_LOCAL_VERSION' ) ) { + $plugin_banners[] = array( + 'url' => 'https://yoast.com/wordpress/plugins/local-seo/#utm_source=wordpress-seo-config&utm_medium=banner&utm_campaign=local-seo-banner', + 'img' => 'banner-local-seo.png', + 'alt' => 'Banner Local SEO plugin', + ); + } + + if ( ! class_exists( 'WPSEO_News' ) ) { + $plugin_banners[] = array( + 'url' => 'https://yoast.com/wordpress/plugins/news-seo/#utm_source=wordpress-seo-config&utm_medium=banner&utm_campaign=news-seo-banner', + 'img' => 'banner-news-seo.png', + 'alt' => 'Banner News SEO', + ); + } + + shuffle( $service_banners ); + shuffle( $plugin_banners ); + ?> + + +
+ +

+
+
+
+
+ '; + settings_fields( $option ); + } + $this->currentoption = $optionshort; + } + + /** + * Generates the footer for admin pages + * + * @param bool $submit Whether or not a submit button and form end tag should be shown. + * @param bool $show_sidebar Whether or not to show the banner sidebar - used by premium plugins to disable it + */ + function admin_footer( $submit = true, $show_sidebar = true ) { + if ( $submit ) { + submit_button(); + + echo ' + '; + } + + echo ' +
+
+
'; + + if ( $show_sidebar ) { + $this->admin_sidebar(); + } + + echo '
'; + + + /* Add the current settings array to the page for debugging purposes, + but not for a limited set of pages were it wouldn't make sense */ + $excluded = array( + 'wpseo_import', + 'wpseo_files', + 'bulk_title_editor_page', + 'bulk_description_editor_page', + ); + + if ( ( WP_DEBUG === true || ( defined( 'WPSEO_DEBUG' ) && WPSEO_DEBUG === true ) ) && isset( $_GET['page'] ) && ! in_array( $_GET['page'], $excluded, true ) ) { + $xdebug = ( extension_loaded( 'xdebug' ) ? true : false ); + echo ' +
+
+ +

' . __( 'Debug Information', 'wordpress-seo' ) . '

+
+

' . esc_html( __( 'Current option:', 'wordpress-seo' ) ) . ' ' . esc_html( $this->currentoption ) . '

+ ' . ( $xdebug ? '' : '
' );
+				var_dump( $this->get_option( $this->currentoption ) );
+				echo '
+					' . ( $xdebug ? '' : '
' ) . ' +
+
+
'; + } + + echo ' +
'; + } + + /** + * Deletes all post meta values with a given meta key from the database + * + * @todo [JRF => whomever] This method does not seem to be used anywhere. Double-check before removal. + * + * @param string $meta_key Key to delete all meta values for. + */ + /*function delete_meta( $meta_key ) { + global $wpdb; + $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE meta_key = %s", $meta_key ) ); + }*/ + + /** + * Exports the current site's WP SEO settings. + * + * @param bool $include_taxonomy Whether to include the taxonomy metadata the plugin creates. + * + * @return bool|string $return False when failed, the URL to the export file when succeeded. + */ + function export_settings( $include_taxonomy ) { + $content = '; ' . __( 'This is a settings export file for the WordPress SEO plugin by Yoast.com', 'wordpress-seo' ) . " - https://yoast.com/wordpress/plugins/seo/ \r\n"; + + $optarr = WPSEO_Options::get_option_names(); + + foreach ( $optarr as $optgroup ) { + $content .= "\n" . '[' . $optgroup . ']' . "\n"; + $options = get_option( $optgroup ); + if ( ! is_array( $options ) ) { + continue; + } + foreach ( $options as $key => $elem ) { + if ( is_array( $elem ) ) { + $elm_count = count( $elem ); + for ( $i = 0; $i < $elm_count; $i ++ ) { + $content .= $key . '[] = "' . $elem[ $i ] . "\"\n"; + } + } elseif ( is_string( $elem ) && $elem == '' ) { + $content .= $key . " = \n"; + } elseif ( is_bool( $elem ) ) { + $content .= $key . ' = "' . ( ( $elem === true ) ? 'on' : 'off' ) . "\"\n"; + } else { + $content .= $key . ' = "' . $elem . "\"\n"; + } + } + } + + if ( $include_taxonomy ) { + $content .= "\r\n\r\n[wpseo_taxonomy_meta]\r\n"; + $content .= 'wpseo_taxonomy_meta = "' . urlencode( json_encode( get_option( 'wpseo_taxonomy_meta' ) ) ) . '"'; + } + + $dir = wp_upload_dir(); + + if ( ! $handle = fopen( $dir['path'] . '/settings.ini', 'w' ) ) { + die(); + } + + if ( ! fwrite( $handle, $content ) ) { + die(); + } + + fclose( $handle ); + + chdir( $dir['path'] ); + $zip = new PclZip( './settings.zip' ); + if ( $zip->create( './settings.ini' ) == 0 ) { + return false; + } + + return $dir['url'] . '/settings.zip'; + } + + /** + * Loads the required styles for the config page. + */ + function config_page_styles() { + global $pagenow; + if ( $pagenow === 'admin.php' && isset( $_GET['page'] ) && in_array( $_GET['page'], $this->adminpages ) ) { + wp_enqueue_style( 'dashboard' ); + wp_enqueue_style( 'thickbox' ); + wp_enqueue_style( 'global' ); + wp_enqueue_style( 'wp-admin' ); + wp_enqueue_style( 'yoast-admin-css', plugins_url( 'css/yst_plugin_tools' . WPSEO_CSSJS_SUFFIX . '.css', WPSEO_FILE ), array(), WPSEO_VERSION ); + + if ( is_rtl() ) { + wp_enqueue_style( 'wpseo-rtl', plugins_url( 'css/wpseo-rtl' . WPSEO_CSSJS_SUFFIX . '.css', WPSEO_FILE ), array(), WPSEO_VERSION ); + } + } + } + + /** + * Loads the required scripts for the config page. + */ + function config_page_scripts() { + global $pagenow; + + if ( $pagenow == 'admin.php' && isset( $_GET['page'] ) && in_array( $_GET['page'], $this->adminpages ) ) { + wp_enqueue_script( 'wpseo-admin-script', plugins_url( 'js/wp-seo-admin' . WPSEO_CSSJS_SUFFIX . '.js', WPSEO_FILE ), array( + 'jquery', + 'jquery-ui-core', + ), WPSEO_VERSION, true ); + wp_enqueue_script( 'dashboard' ); + wp_enqueue_script( 'thickbox' ); + } + + if ( $pagenow == 'admin.php' && isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'wpseo_social' ) ) ) { + wp_enqueue_media(); + wp_enqueue_script( 'wpseo-admin-media', plugins_url( 'js/wp-seo-admin-media' . WPSEO_CSSJS_SUFFIX . '.js', WPSEO_FILE ), array( + 'jquery', + 'jquery-ui-core', + ), WPSEO_VERSION, true ); + wp_localize_script( 'wpseo-admin-media', 'wpseoMediaL10n', $this->localize_media_script() ); + } + + if ( $pagenow == 'admin.php' && isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'wpseo_bulk-editor' ) ) ) { + wp_enqueue_script( 'wpseo-bulk-editor', plugins_url( 'js/wp-seo-bulk-editor' . WPSEO_CSSJS_SUFFIX . '.js', WPSEO_FILE ), array( 'jquery' ), WPSEO_VERSION, true ); + } + } + + /** + * Pass some variables to js for upload module. + * + * @return array + */ + public function localize_media_script() { + return array( + 'choose_image' => __( 'Use Image', 'wordpress-seo' ), + ); + } + + /** + * Retrieve options based on whether we're on multisite or not. + * + * @since 1.2.4 + * + * @param string $option The option to retrieve. + * + * @return array + */ + function get_option( $option ) { + if ( is_network_admin() ) { + return get_site_option( $option ); + } else { + return get_option( $option ); + } + } + + /** + * Create a Checkbox input field. + * + * @param string $var The variable within the option to create the checkbox for. + * @param string $label The label to show for the variable. + * @param bool $label_left Whether the label should be left (true) or right (false). + * @param string $option The option the variable belongs to. + * + * @return string + */ + function checkbox( $var, $label, $label_left = false, $option = '' ) { + if ( empty( $option ) ) { + $option = $this->currentoption; + } + + $options = $this->get_option( $option ); + + if ( ! isset( $options[ $var ] ) ) { + $options[ $var ] = false; + } + + if ( $options[ $var ] === true ) { + $options[ $var ] = 'on'; + } + + if ( $label_left !== false ) { + if ( ! empty( $label_left ) ) { + $label_left .= ':'; + } + $output_label = ''; + $class = 'checkbox'; + } else { + $output_label = ''; + $class = 'checkbox double'; + } + + $output_input = ''; + + if ( $label_left !== false ) { + $output = $output_label . $output_input . ''; + } else { + $output = $output_input . $output_label; + } + + return $output . '
'; + } + + /** + * Create a Text input field. + * + * @param string $var The variable within the option to create the text input field for. + * @param string $label The label to show for the variable. + * @param string $option The option the variable belongs to. + * + * @return string + */ + function textinput( $var, $label, $option = '' ) { + if ( empty( $option ) ) { + $option = $this->currentoption; + } + + $options = $this->get_option( $option ); + $val = ( isset( $options[ $var ] ) ) ? $options[ $var ] : ''; + + return '' . '
'; + } + + /** + * Create a textarea. + * + * @param string $var The variable within the option to create the textarea for. + * @param string $label The label to show for the variable. + * @param string $option The option the variable belongs to. + * @param string $class The CSS class to assign to the textarea. + * + * @return string + */ + function textarea( $var, $label, $option = '', $class = '' ) { + if ( empty( $option ) ) { + $option = $this->currentoption; + } + + $options = $this->get_option( $option ); + $val = ( isset( $options[ $var ] ) ) ? $options[ $var ] : ''; + + return '' . '
'; + } + + /** + * Create a hidden input field. + * + * @param string $var The variable within the option to create the hidden input for. + * @param string $option The option the variable belongs to. + * + * @return string + */ + function hidden( $var, $option = '' ) { + if ( empty( $option ) ) { + $option = $this->currentoption; + } + + $options = $this->get_option( $option ); + + $val = ( isset( $options[ $var ] ) ) ? $options[ $var ] : ''; + if ( is_bool( $val ) ) { + $val = ( $val === true ) ? 'true' : 'false'; + } + + return ''; + } + + /** + * Create a Select Box. + * + * @param string $var The variable within the option to create the select for. + * @param string $label The label to show for the variable. + * @param array $values The select options to choose from. + * @param string $option The option the variable belongs to. + * + * @return string + */ + function select( $var, $label, $values, $option = '' ) { + if ( ! is_array( $values ) || $values === array() ) { + return ''; + } + if ( empty( $option ) ) { + $option = $this->currentoption; + } + + $options = $this->get_option( $option ); + $val = ( isset( $options[ $var ] ) ) ? $options[ $var ] : ''; + + $output = ''; + $output .= ''; + + return $output . '
'; + } + + /** + * Create a File upload field. + * + * @param string $var The variable within the option to create the file upload field for. + * @param string $label The label to show for the variable. + * @param string $option The option the variable belongs to. + * + * @return string + */ + function file_upload( $var, $label, $option = '' ) { + if ( empty( $option ) ) { + $option = $this->currentoption; + } + + $options = $this->get_option( $option ); + + $val = ''; + if ( isset( $options[ $var ] ) && is_array( $options[ $var ] ) ) { + $val = $options[ $var ]['url']; + } + + $var_esc = esc_attr( $var ); + $output = ''; + $output .= ''; + + // Need to save separate array items in hidden inputs, because empty file inputs type will be deleted by settings API. + if ( ! empty( $options[ $var ] ) ) { + $output .= ''; + $output .= ''; + $output .= ''; + } + $output .= '
'; + + return $output; + } + + /** + * Media input + * + * @param string $var + * @param string $label + * @param string $option + * + * @return string + */ + function media_input( $var, $label, $option = '' ) { + if ( empty( $option ) ) { + $option = $this->currentoption; + } + + $options = $this->get_option( $option ); + + $val = ''; + if ( isset( $options[ $var ] ) ) { + $val = $options[ $var ]; + } + + $var_esc = esc_attr( $var ); + + $output = ''; + $output .= ''; + $output .= ''; + $output .= '
'; + + return $output; + } + + /** + * Create a Radio input field. + * + * @param string $var The variable within the option to create the file upload field for. + * @param array $values The radio options to choose from. + * @param string $label The label to show for the variable. + * @param string $option The option the variable belongs to. + * + * @return string + */ + function radio( $var, $values, $label, $option = '' ) { + if ( ! is_array( $values ) || $values === array() ) { + return ''; + } + if ( empty( $option ) ) { + $option = $this->currentoption; + } + + $options = $this->get_option( $option ); + + if ( ! isset( $options[ $var ] ) ) { + $options[ $var ] = false; + } + + $var_esc = esc_attr( $var ); + + $output = '
'; + if ( is_string( $label ) && $label !== '' ) { + $output .= ''; + } + + foreach ( $values as $key => $value ) { + $key_esc = esc_attr( $key ); + $output .= ' '; + } + $output .= '
'; + $output .= '

'; + + return $output; + } + + /** + * Create a postbox widget. + * + * @param string $id ID of the postbox. + * @param string $title Title of the postbox. + * @param string $content Content of the postbox. + */ + function postbox( $id, $title, $content ) { + ?> +
+

+ +
+ '; + foreach ( $rows as $row ) { + $content .= ''; + if ( isset( $row['id'] ) && $row['id'] != '' ) { + $content .= ''; + } else { + $content .= esc_html( $row['label'] ); + } + if ( isset( $row['desc'] ) && $row['desc'] != '' ) { + $content .= '
' . esc_html( $row['desc'] ) . ''; + } + $content .= ''; + $content .= $row['content']; + $content .= ''; + } + $content .= ''; + + return $content; + } + + + + /********************** DEPRECATED METHODS **********************/ + + /** + * Resets the site to the default WordPress SEO settings and runs a title test to check + * whether force rewrite needs to be on. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Options::reset() + * @see WPSEO_Options::reset() + */ + function reset_defaults() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Options::reset()' ); + WPSEO_Options::reset(); + } + + + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/admin/class-metabox.php b/wp-content/plugins/wordpress-seo/admin/class-metabox.php new file mode 100644 index 0000000..be27f4f --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-metabox.php @@ -0,0 +1,2024 @@ +
Read %sthis post%s for more info.', 'wordpress-seo' ), '', '' ); + + self::$meta_fields['general']['focuskw']['title'] = __( 'Focus Keyword', 'wordpress-seo' ); + self::$meta_fields['general']['focuskw']['help'] = sprintf( __( 'Pick the main keyword or keyphrase that this post/page is about.

Read %sthis post%s for more info.', 'wordpress-seo' ), '', '' ); + + self::$meta_fields['general']['title']['title'] = __( 'SEO Title', 'wordpress-seo' ); + self::$meta_fields['general']['title']['description'] = '

' . '' . __( 'Warning:', 'wordpress-seo' ) . ' ' . __( 'Title display in Google is limited to a fixed width, yours is too long.', 'wordpress-seo' ) . '

'; + self::$meta_fields['general']['title']['help'] = __( 'The SEO Title defaults to what is generated based on this sites title template for this posttype.', 'wordpress-seo' ); + + self::$meta_fields['general']['metadesc']['title'] = __( 'Meta Description', 'wordpress-seo' ); + self::$meta_fields['general']['metadesc']['description'] = sprintf( __( 'The meta description will be limited to %s chars%s, %s chars left.', 'wordpress-seo' ), self::$meta_length, self::$meta_length_reason, '' ) . '
'; + self::$meta_fields['general']['metadesc']['help'] = sprintf( __( 'The meta description is often shown as the black text under the title in a search result. For this to work it has to contain the keyword that was searched for.

Read %sthis post%s for more info.', 'wordpress-seo' ), '', '' ); + + self::$meta_fields['general']['metakeywords']['title'] = __( 'Meta Keywords', 'wordpress-seo' ); + self::$meta_fields['general']['metakeywords']['description'] = __( 'If you type something above it will override your %smeta keywords template%s.', 'wordpress-seo' ); + + + self::$meta_fields['advanced']['meta-robots-noindex']['title'] = __( 'Meta Robots Index', 'wordpress-seo' ); + if ( '0' == get_option( 'blog_public' ) ) { + self::$meta_fields['advanced']['meta-robots-noindex']['description'] = '

' . __( 'Warning: even though you can set the meta robots setting here, the entire site is set to noindex in the sitewide privacy settings, so these settings won\'t have an effect.', 'wordpress-seo' ) . '

'; + } + self::$meta_fields['advanced']['meta-robots-noindex']['options']['0'] = __( 'Default for post type, currently: %s', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-noindex']['options']['2'] = __( 'index', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-noindex']['options']['1'] = __( 'noindex', 'wordpress-seo' ); + + self::$meta_fields['advanced']['meta-robots-nofollow']['title'] = __( 'Meta Robots Follow', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-nofollow']['options']['0'] = __( 'Follow', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-nofollow']['options']['1'] = __( 'Nofollow', 'wordpress-seo' ); + + self::$meta_fields['advanced']['meta-robots-adv']['title'] = __( 'Meta Robots Advanced', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-adv']['description'] = __( 'Advanced meta robots settings for this page.', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-adv']['options']['-'] = __( 'Site-wide default: %s', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-adv']['options']['none'] = __( 'None', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-adv']['options']['noodp'] = __( 'NO ODP', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-adv']['options']['noydir'] = __( 'NO YDIR', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-adv']['options']['noimageindex'] = __( 'No Image Index', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-adv']['options']['noarchive'] = __( 'No Archive', 'wordpress-seo' ); + self::$meta_fields['advanced']['meta-robots-adv']['options']['nosnippet'] = __( 'No Snippet', 'wordpress-seo' ); + + self::$meta_fields['advanced']['bctitle']['title'] = __( 'Breadcrumbs title', 'wordpress-seo' ); + self::$meta_fields['advanced']['bctitle']['description'] = __( 'Title to use for this page in breadcrumb paths', 'wordpress-seo' ); + + self::$meta_fields['advanced']['sitemap-include']['title'] = __( 'Include in Sitemap', 'wordpress-seo' ); + self::$meta_fields['advanced']['sitemap-include']['description'] = __( 'Should this page be in the XML Sitemap at all times, regardless of Robots Meta settings?', 'wordpress-seo' ); + self::$meta_fields['advanced']['sitemap-include']['options']['-'] = __( 'Auto detect', 'wordpress-seo' ); + self::$meta_fields['advanced']['sitemap-include']['options']['always'] = __( 'Always include', 'wordpress-seo' ); + self::$meta_fields['advanced']['sitemap-include']['options']['never'] = __( 'Never include', 'wordpress-seo' ); + + self::$meta_fields['advanced']['sitemap-prio']['title'] = __( 'Sitemap Priority', 'wordpress-seo' ); + self::$meta_fields['advanced']['sitemap-prio']['description'] = __( 'The priority given to this page in the XML sitemap.', 'wordpress-seo' ); + self::$meta_fields['advanced']['sitemap-prio']['options']['-'] = __( 'Automatic prioritization', 'wordpress-seo' ); + self::$meta_fields['advanced']['sitemap-prio']['options']['1'] = __( '1 - Highest priority', 'wordpress-seo' ); + self::$meta_fields['advanced']['sitemap-prio']['options']['0.8'] .= __( 'Default for first tier pages', 'wordpress-seo' ); + self::$meta_fields['advanced']['sitemap-prio']['options']['0.6'] .= __( 'Default for second tier pages and posts', 'wordpress-seo' ); + self::$meta_fields['advanced']['sitemap-prio']['options']['0.5'] .= __( 'Medium priority', 'wordpress-seo' ); + self::$meta_fields['advanced']['sitemap-prio']['options']['0.1'] .= __( 'Lowest priority', 'wordpress-seo' ); + + self::$meta_fields['advanced']['canonical']['title'] = __( 'Canonical URL', 'wordpress-seo' ); + self::$meta_fields['advanced']['canonical']['description'] = sprintf( __( 'The canonical URL that this page should point to, leave empty to default to permalink. %sCross domain canonical%s supported too.', 'wordpress-seo' ), '', '' ); + + self::$meta_fields['advanced']['redirect']['title'] = __( '301 Redirect', 'wordpress-seo' ); + self::$meta_fields['advanced']['redirect']['description'] = __( 'The URL that this page should redirect to.', 'wordpress-seo' ); + + do_action( 'wpseo_tab_translate' ); + } + + /** + * Test whether the metabox should be hidden either by choice of the admin or because + * the post type is not a public post type + * + * @since 1.5.0 + * + * @param string $post_type (optional) The post type to test, defaults to the current post post_type + * + * @return bool Whether or not the meta box (and associated columns etc) should be hidden + */ + function is_metabox_hidden( $post_type = null ) { + if ( ! isset( $post_type ) ) { + if ( isset( $GLOBALS['post'] ) && ( is_object( $GLOBALS['post'] ) && isset( $GLOBALS['post']->post_type ) ) ) { + $post_type = $GLOBALS['post']->post_type; + } elseif ( isset( $_GET['post_type'] ) && $_GET['post_type'] !== '' ) { + $post_type = sanitize_text_field( $_GET['post_type'] ); + } + } + + if ( isset( $post_type ) ) { + // Don't make static as post_types may still be added during the run + $cpts = get_post_types( array( 'public' => true ), 'names' ); + $options = get_option( 'wpseo_titles' ); + + return ( ( isset( $options[ 'hideeditbox-' . $post_type ] ) && $options[ 'hideeditbox-' . $post_type ] === true ) || in_array( $post_type, $cpts ) === false ); + } else { + return false; + } + } + + /** + * Sets up all the functionality related to the prominence of the page analysis functionality. + */ + public function setup_page_analysis() { + + if ( apply_filters( 'wpseo_use_page_analysis', true ) === true ) { + + $post_types = get_post_types( array( 'public' => true ), 'names' ); + + if ( is_array( $post_types ) && $post_types !== array() ) { + foreach ( $post_types as $pt ) { + if ( $this->is_metabox_hidden( $pt ) === false ) { + add_filter( 'manage_' . $pt . '_posts_columns', array( $this, 'column_heading' ), 10, 1 ); + add_action( 'manage_' . $pt . '_posts_custom_column', array( + $this, + 'column_content', + ), 10, 2 ); + add_action( 'manage_edit-' . $pt . '_sortable_columns', array( + $this, + 'column_sort', + ), 10, 2 ); + } + } + } + add_action( 'restrict_manage_posts', array( $this, 'posts_filter_dropdown' ) ); + add_filter( 'request', array( $this, 'column_sort_orderby' ) ); + + add_action( 'post_submitbox_misc_actions', array( $this, 'publish_box' ) ); + } + } + + /** + * Get an instance of the text statistics class + * + * @return Yoast_TextStatistics + */ + private function statistics() { + if ( ! isset( $this->statistics ) ) { + $this->statistics = new Yoast_TextStatistics( get_bloginfo( 'charset' ) ); + } + + return $this->statistics; + } + + /** + * Returns post in metabox context + * + * @returns WP_Post + */ + private function get_metabox_post() { + if ( isset( $_GET['post'] ) ) { + $post_id = (int) WPSEO_Option::validate_int( $_GET['post'] ); + $post = get_post( $post_id ); + } else { + global $post; + } + + return $post; + } + + /** + * Lowercase a sentence while preserving "weird" characters. + * + * This should work with Greek, Russian, Polish & French amongst other languages... + * + * @param string $string String to lowercase + * + * @return string + */ + public function strtolower_utf8( $string ) { + + // Prevent comparison between utf8 characters and html entities (é vs é) + $string = html_entity_decode( $string ); + + $convert_to = array( + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', + 'v', 'w', 'x', 'y', 'z', 'à', 'á', 'â', 'ã', 'ä', 'Ã¥', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', + 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'а', 'б', 'в', 'г', 'д', 'е', 'ё', 'ж', + 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п', 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', 'ъ', 'ы', + 'ь', 'э', 'ю', 'я', 'ą', 'ć', 'ę', 'ł', 'ń', 'ó', 'ś', 'ź', 'ż', + ); + $convert_from = array( + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', + 'V', 'W', 'X', 'Y', 'Z', 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', + 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', + 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Ð¥', 'Ц', 'Ч', 'Ш', 'Щ', 'Ъ', 'Ъ', + 'Ь', 'Э', 'Ю', 'Я', 'Ą', 'Ć', 'Ę', 'Ł', 'Ń', 'Ó', 'Ś', 'Ź', 'Å»', + ); + + return str_replace( $convert_from, $convert_to, $string ); + } + + /** + * Outputs the page analysis score in the Publish Box. + * + */ + public function publish_box() { + if ( $this->is_metabox_hidden() === true ) { + return; + } + + echo '
'; + + $post = $this->get_metabox_post(); + + if ( self::get_value( 'meta-robots-noindex', $post->ID ) === '1' ) { + $score_label = 'noindex'; + $title = __( 'Post is set to noindex.', 'wordpress-seo' ); + $score_title = $title; + } else { + + $score = ''; + $results = $this->calculate_results( $post ); + if ( ! is_wp_error( $results ) && isset( $results['total'] ) ) { + $score = $results['total']; + unset( $results ); + } + + if ( $score === '' ) { + $score_label = 'na'; + $title = __( 'No focus keyword set.', 'wordpress-seo' ); + } else { + $score_label = wpseo_translate_score( $score ); + } + + $score_title = wpseo_translate_score( $score, false ); + if ( ! isset( $title ) ) { + $title = $score_title; + } + } + + echo '
'; + + echo __( 'SEO: ', 'wordpress-seo' ) . '' . $score_title . ''; + + echo ' ' . __( 'Check', 'wordpress-seo' ) . ''; + + echo '
'; + } + + /** + * Adds the WordPress SEO meta box to the edit boxes in the edit post / page / cpt pages. + */ + public function add_meta_box() { + $post_types = get_post_types( array( 'public' => true ) ); + + if ( is_array( $post_types ) && $post_types !== array() ) { + foreach ( $post_types as $post_type ) { + if ( $this->is_metabox_hidden( $post_type ) === false ) { + add_meta_box( 'wpseo_meta', __( 'WordPress SEO by Yoast', 'wordpress-seo' ), array( + $this, + 'meta_box', + ), $post_type, 'normal', apply_filters( 'wpseo_metabox_prio', 'high' ) ); + } + } + } + } + + /** + * Pass some variables to js for the edit / post page overview, snippet preview, etc. + * + * @return array + */ + public function localize_script() { + $post = $this->get_metabox_post(); + + if ( ( ! is_object( $post ) || ! isset( $post->post_type ) ) || $this->is_metabox_hidden( $post->post_type ) === true ) { + return array(); + } + + $options = get_option( 'wpseo_titles' ); + + $date = ''; + if ( isset( $options[ 'showdate-' . $post->post_type ] ) && $options[ 'showdate-' . $post->post_type ] === true ) { + $date = $this->get_post_date( $post ); + + self::$meta_length = self::$meta_length - ( strlen( $date ) + 5 ); + self::$meta_length_reason = __( ' (because of date display)', 'wordpress-seo' ); + } + + self::$meta_length_reason = apply_filters( 'wpseo_metadesc_length_reason', self::$meta_length_reason, $post ); + self::$meta_length = apply_filters( 'wpseo_metadesc_length', self::$meta_length, $post ); + + unset( $date ); + + $title_template = ''; + if ( isset( $options[ 'title-' . $post->post_type ] ) && $options[ 'title-' . $post->post_type ] !== '' ) { + $title_template = $options[ 'title-' . $post->post_type ]; + } + + // If there's no title template set, use the default, otherwise title preview won't work. + if ( $title_template == '' ) { + $title_template = '%%title%% - %%sitename%%'; + } + + $metadesc_template = ''; + if ( isset( $options[ 'metadesc-' . $post->post_type ] ) && $options[ 'metadesc-' . $post->post_type ] !== '' ) { + $metadesc_template = $options[ 'metadesc-' . $post->post_type ]; + } + + $sample_permalink = get_sample_permalink( $post->ID ); + $sample_permalink = str_replace( '%page', '%post', $sample_permalink[0] ); + + $cached_replacement_vars = array(); + foreach ( + array( + 'date', + 'id', + 'sitename', + 'sitedesc', + 'sep', + 'page', + 'currenttime', + 'currentdate', + 'currentday', + 'currentmonth', + 'currentyear', + ) as $var + ) { + $cached_replacement_vars[ $var ] = wpseo_replace_vars( '%%' . $var . '%%', $post ); + } + + return array_merge( $cached_replacement_vars, array( + 'field_prefix' => self::$form_prefix, + 'keyword_header' => __( 'Your focus keyword was found in:', 'wordpress-seo' ), + 'article_header_text' => __( 'Article Heading: ', 'wordpress-seo' ), + 'page_title_text' => __( 'Page title: ', 'wordpress-seo' ), + 'page_url_text' => __( 'Page URL: ', 'wordpress-seo' ), + 'content_text' => __( 'Content: ', 'wordpress-seo' ), + 'meta_description_text' => __( 'Meta description: ', 'wordpress-seo' ), + 'choose_image' => __( 'Use Image', 'wordpress-seo' ), + 'wpseo_meta_desc_length' => self::$meta_length, + 'wpseo_title_template' => $title_template, + 'wpseo_metadesc_template' => $metadesc_template, + 'wpseo_permalink_template' => $sample_permalink, + 'wpseo_keyword_suggest_nonce' => wp_create_nonce( 'wpseo-get-suggest' ), + 'wpseo_replace_vars_nonce' => wp_create_nonce( 'wpseo-replace-vars' ), + 'no_parent_text' => __( '(no parent)', 'wordpress-seo' ), + ) ); + } + + /** + * Output a tab in the WP SEO Metabox + * + * @param string $id CSS ID of the tab. + * @param string $heading Heading for the tab. + * @param string $content Content of the tab. This content should be escaped. + */ + public function do_tab( $id, $heading, $content ) { + ?> +
+

+ + +
+
+ get_metabox_post(); + $options = WPSEO_Options::get_all(); + + ?> +
+ + post_type ) ) { + foreach ( $this->get_meta_field_defs( 'general', $post->post_type ) as $key => $meta_field ) { + $content .= $this->do_meta_box( $meta_field, $key ); + } + } + $this->do_tab( 'general', __( 'General', 'wordpress-seo' ), $content ); + + $this->do_tab( 'linkdex', __( 'Page Analysis', 'wordpress-seo' ), $this->linkdex_output( $post ) ); + + if ( current_user_can( 'manage_options' ) || $options['disableadvanced_meta'] === false ) { + $content = ''; + foreach ( $this->get_meta_field_defs( 'advanced' ) as $key => $meta_field ) { + $content .= $this->do_meta_box( $meta_field, $key ); + } + $this->do_tab( 'advanced', __( 'Advanced', 'wordpress-seo' ), $content ); + } + + do_action( 'wpseo_tab_content' ); + + echo '
'; + } + + /** + * Adds a line in the meta box + * + * @todo [JRF] check if $class is added appropriately everywhere + * + * @param array $meta_field_def Contains the vars based on which output is generated. + * @param string $key Internal key (without prefix) + * + * @return string + */ + function do_meta_box( $meta_field_def, $key = '' ) { + $content = ''; + $esc_form_key = esc_attr( self::$form_prefix . $key ); + $post = $this->get_metabox_post(); + $meta_value = self::get_value( $key, $post->ID ); + + $class = ''; + if ( isset( $meta_field_def['class'] ) && $meta_field_def['class'] !== '' ) { + $class = ' ' . $meta_field_def['class']; + } + + $placeholder = ''; + if ( isset( $meta_field_def['placeholder'] ) && $meta_field_def['placeholder'] !== '' ) { + $placeholder = $meta_field_def['placeholder']; + } + + switch ( $meta_field_def['type'] ) { + case 'snippetpreview': + $content .= $this->snippet(); + break; + + case 'text': + $ac = ''; + if ( isset( $meta_field_def['autocomplete'] ) && $meta_field_def['autocomplete'] === false ) { + $ac = 'autocomplete="off" '; + } + if ( $placeholder !== '' ) { + $placeholder = ' placeholder="' . esc_attr( $placeholder ) . '"'; + } + $content .= '
'; + break; + + case 'textarea': + $rows = 3; + if ( isset( $meta_field_def['rows'] ) && $meta_field_def['rows'] > 0 ) { + $rows = $meta_field_def['rows']; + } + $content .= ''; + break; + + case 'select': + if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== array() ) { + $content .= ''; + } + break; + + case 'multiselect': + if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== array() ) { + + // Set $meta_value as $selectedarr + $selected_arr = $meta_value; + + // If the multiselect field is 'meta-robots-adv' we should explode on , + if ( 'meta-robots-adv' === $key ) { + $selected_arr = explode( ',', $meta_value ); + } + + if ( ! is_array( $selected_arr ) ) { + $selected_arr = (array) $selected_arr; + } + + $options_count = count( $meta_field_def['options'] ); + + // @todo [JRF => whomever] verify height calculation for older WP versions, was 16x, for WP3.8 20x is more appropriate + $content .= ''; + } + break; + + case 'checkbox': + $checked = checked( $meta_value, 'on', false ); + $expl = ( isset( $meta_field_def['expl'] ) ) ? esc_html( $meta_field_def['expl'] ) : ''; + $content .= '
'; + break; + + case 'radio': + if ( isset( $meta_field_def['options'] ) && is_array( $meta_field_def['options'] ) && $meta_field_def['options'] !== array() ) { + foreach ( $meta_field_def['options'] as $val => $option ) { + $checked = checked( $meta_value, $val, false ); + $content .= ' '; + } + } + break; + + case 'upload': + $content .= ''; + $content .= ''; + break; + } + + + $html = ''; + if ( $content === '' ) { + $content = apply_filters( 'wpseo_do_meta_box_field_' . $key, $content, $meta_value, $esc_form_key, $meta_field_def, $key ); + } + + if ( $content !== '' ) { + + $label = esc_html( $meta_field_def['title'] ); + if ( in_array( $meta_field_def['type'], array( + 'snippetpreview', + 'radio', + 'checkbox', + ), true ) === false + ) { + $label = ''; + } + + $help = ''; + if ( isset( $meta_field_def['help'] ) && $meta_field_def['help'] !== '' ) { + $help = '' . esc_attr( $meta_field_def['help'] ) . ''; + } + + $html = ' + + ' . $label . $help . ' + '; + + $html .= $content; + + if ( isset( $meta_field_def['description'] ) ) { + $html .= '
' . $meta_field_def['description'] . '
'; + } + + $html .= ' + + '; + } + + return $html; + } + + /** + * Retrieve a post date when post is published, or return current date when it's not. + * + * @param object $post Post to retrieve the date for. + * + * @return string + */ + function get_post_date( $post ) { + if ( isset( $post->post_date ) && $post->post_status == 'publish' ) { + $date = date_i18n( 'j M Y', strtotime( $post->post_date ) ); + } else { + $date = date_i18n( 'j M Y' ); + } + + return (string) $date; + } + + /** + * Generate a snippet preview. + * + * @return string + */ + function snippet() { + $post = $this->get_metabox_post(); + + $options = WPSEO_Options::get_all(); + + $date = ''; + if ( is_object( $post ) && isset( $options[ 'showdate-' . $post->post_type ] ) && $options[ 'showdate-' . $post->post_type ] === true ) { + $date = $this->get_post_date( $post ); + } + + $title = self::get_value( 'title', $post->ID ); + $desc = self::get_value( 'metadesc', $post->ID ); + + $slug = ( is_object( $post ) && isset( $post->post_name ) ) ? $post->post_name : ''; + if ( $slug !== '' ) { + $slug = sanitize_title( $title ); + } + + if ( is_string( $date ) && $date !== '' ) { + $datestr = '' . $date . ' - '; + } else { + $datestr = ''; + } + $content = '
+ ' . esc_html( $title ) . ''; + + $content .= '' . str_replace( 'http://', '', get_bloginfo( 'url' ) ) . '/' . esc_html( $slug ) . '/'; + + $content .= '

' . $datestr . '' . esc_html( $desc ) . '

'; + + $content .= '
'; + + $content = apply_filters( 'wpseo_snippet', $content, $post, compact( 'title', 'desc', 'date', 'slug' ) ); + + return $content; + } + + /** + * Save the WP SEO metadata for posts. + * + * @internal $_POST parameters are validated via sanitize_post_meta() + * + * @param int $post_id + * + * @return bool|void Boolean false if invalid save post request + */ + function save_postdata( $post_id ) { + if ( $post_id === null ) { + return false; + } + + if ( wp_is_post_revision( $post_id ) ) { + $post_id = wp_is_post_revision( $post_id ); + } + + clean_post_cache( $post_id ); + $post = get_post( $post_id ); + + if ( ! is_object( $post ) ) { + // non-existent post + return false; + } + + $meta_boxes = apply_filters( 'wpseo_save_metaboxes', array() ); + $meta_boxes = array_merge( $meta_boxes, $this->get_meta_field_defs( 'general', $post->post_type ), $this->get_meta_field_defs( 'advanced' ) ); + + foreach ( $meta_boxes as $key => $meta_box ) { + $data = null; + if ( 'checkbox' === $meta_box['type'] ) { + $data = isset( $_POST[ self::$form_prefix . $key ] ) ? 'on' : 'off'; + } else { + if ( isset( $_POST[ self::$form_prefix . $key ] ) ) { + $data = $_POST[ self::$form_prefix . $key ]; + } + } + if ( isset( $data ) ) { + self::set_value( $key, $data, $post_id ); + } + } + + do_action( 'wpseo_saved_postdata' ); + } + + /** + * Enqueues all the needed JS and CSS. + * @todo [JRF => whomever] create css/metabox-mp6.css file and add it to the below allowed colors array when done + */ + public function enqueue() { + global $pagenow; + if ( ! in_array( $pagenow, array( + 'post-new.php', + 'post.php', + 'edit.php', + ), true ) || $this->is_metabox_hidden() === true + ) { + return; + } + + + $color = get_user_meta( get_current_user_id(), 'admin_color', true ); + if ( '' == $color || in_array( $color, array( 'classic', 'fresh' ), true ) === false ) { + $color = 'fresh'; + } + + + if ( $pagenow == 'edit.php' ) { + wp_enqueue_style( 'edit-page', plugins_url( 'css/edit-page' . WPSEO_CSSJS_SUFFIX . '.css', WPSEO_FILE ), array(), WPSEO_VERSION ); + } else { + if ( 0 != get_queried_object_id() ) { + wp_enqueue_media( array( 'post' => get_queried_object_id() ) ); // enqueue files needed for upload functionality + } + wp_enqueue_style( 'metabox-tabs', plugins_url( 'css/metabox-tabs' . WPSEO_CSSJS_SUFFIX . '.css', WPSEO_FILE ), array(), WPSEO_VERSION ); + wp_enqueue_style( "metabox-$color", plugins_url( 'css/metabox-' . esc_attr( $color ) . WPSEO_CSSJS_SUFFIX . '.css', WPSEO_FILE ), array(), WPSEO_VERSION ); + + wp_enqueue_script( 'jquery-ui-autocomplete' ); + + wp_enqueue_script( 'jquery-qtip', plugins_url( 'js/jquery.qtip.min.js', WPSEO_FILE ), array( 'jquery' ), '1.0.0-RC3', true ); + wp_enqueue_script( 'wp-seo-metabox', plugins_url( 'js/wp-seo-metabox' . WPSEO_CSSJS_SUFFIX . '.js', WPSEO_FILE ), array( + 'jquery', + 'jquery-ui-core', + 'jquery-ui-autocomplete', + ), WPSEO_VERSION, true ); + + wp_enqueue_script( 'wpseo-admin-media', plugins_url( 'js/wp-seo-admin-media' . WPSEO_CSSJS_SUFFIX . '.js', WPSEO_FILE ), array( 'jquery', 'jquery-ui-core' ), WPSEO_VERSION, true ); + wp_localize_script( 'wpseo-admin-media', 'wpseoMediaL10n', $this->localize_media_script() ); + + // Text strings to pass to metabox for keyword analysis + wp_localize_script( 'wp-seo-metabox', 'wpseoMetaboxL10n', $this->localize_script() ); + } + } + + /** + * Pass some variables to js for upload module. + * + * @return array + */ + public function localize_media_script() { + return array( + 'choose_image' => __( 'Use Image', 'wordpress-seo' ), + ); + } + + /** + * Adds a dropdown that allows filtering on the posts SEO Quality. + * + * @return bool + */ + function posts_filter_dropdown() { + global $pagenow; + if ( $pagenow === 'upload.php' || $this->is_metabox_hidden() === true ) { + return; + } + + $scores_array = array( + 'na' => __( 'SEO: No Focus Keyword', 'wordpress-seo' ), + 'bad' => __( 'SEO: Bad', 'wordpress-seo' ), + 'poor' => __( 'SEO: Poor', 'wordpress-seo' ), + 'ok' => __( 'SEO: OK', 'wordpress-seo' ), + 'good' => __( 'SEO: Good', 'wordpress-seo' ), + 'noindex' => __( 'SEO: Post Noindexed', 'wordpress-seo' ) + ); + + echo ''; + } + + /** + * Adds the column headings for the SEO plugin for edit posts / pages overview + * + * @param array $columns Already existing columns. + * + * @return array + */ + function column_heading( $columns ) { + if ( $this->is_metabox_hidden() === true ) { + return $columns; + } + + return array_merge( $columns, array( + 'wpseo-score' => __( 'SEO', 'wordpress-seo' ), + 'wpseo-title' => __( 'SEO Title', 'wordpress-seo' ), + 'wpseo-metadesc' => __( 'Meta Desc.', 'wordpress-seo' ), + 'wpseo-focuskw' => __( 'Focus KW', 'wordpress-seo' ) + ) ); + } + + /** + * Display the column content for the given column + * + * @param string $column_name Column to display the content for. + * @param int $post_id Post to display the column content for. + */ + function column_content( $column_name, $post_id ) { + if ( $this->is_metabox_hidden() === true ) { + return; + } + + if ( $column_name === 'wpseo-score' ) { + $score = self::get_value( 'linkdex', $post_id ); + if ( self::get_value( 'meta-robots-noindex', $post_id ) === '1' ) { + $score_label = 'noindex'; + $title = __( 'Post is set to noindex.', 'wordpress-seo' ); + self::set_value( 'linkdex', 0, $post_id ); + } elseif ( $score !== '' ) { + $nr = wpseo_calc( $score, '/', 10, true ); + $score_label = wpseo_translate_score( $nr ); + $title = wpseo_translate_score( $nr, false ); + unset( $nr ); + } else { + $this->calculate_results( get_post( $post_id ) ); + $score = self::get_value( 'linkdex', $post_id ); + if ( $score === '' ) { + $score_label = 'na'; + $title = __( 'Focus keyword not set.', 'wordpress-seo' ); + } else { + $score_label = wpseo_translate_score( $score ); + $title = wpseo_translate_score( $score, false ); + } + } + + echo '
'; + } + if ( $column_name === 'wpseo-title' ) { + echo esc_html( apply_filters( 'wpseo_title', wpseo_replace_vars( $this->page_title( $post_id ), get_post( $post_id, ARRAY_A ) ) ) ); + } + if ( $column_name === 'wpseo-metadesc' ) { + echo esc_html( apply_filters( 'wpseo_metadesc', wpseo_replace_vars( self::get_value( 'metadesc', $post_id ), get_post( $post_id, ARRAY_A ) ) ) ); + } + if ( $column_name === 'wpseo-focuskw' ) { + $focuskw = self::get_value( 'focuskw', $post_id ); + echo esc_html( $focuskw ); + } + } + + /** + * Indicate which of the SEO columns are sortable. + * + * @param array $columns appended with their orderby variable. + * + * @return array + */ + function column_sort( $columns ) { + if ( $this->is_metabox_hidden() === true ) { + return $columns; + } + + $columns['wpseo-score'] = 'wpseo-score'; + $columns['wpseo-metadesc'] = 'wpseo-metadesc'; + $columns['wpseo-focuskw'] = 'wpseo-focuskw'; + + return $columns; + } + + /** + * Modify the query based on the seo_filter variable in $_GET + * + * @param array $vars Query variables. + * + * @return array + */ + function column_sort_orderby( $vars ) { + if ( isset( $_GET['seo_filter'] ) ) { + $noindex = false; + $high = false; + switch ( $_GET['seo_filter'] ) { + case 'noindex': + $low = false; + $noindex = true; + break; + case 'na': + $low = 0; + $high = 0; + break; + case 'bad': + $low = 1; + $high = 34; + break; + case 'poor': + $low = 35; + $high = 54; + break; + case 'ok': + $low = 55; + $high = 74; + break; + case 'good': + $low = 75; + $high = 100; + break; + default: + $low = false; + $high = false; + $noindex = false; + break; + } + if ( $low !== false ) { + /* @internal DON'T touch the order of these without double-checking/adjusting + * the seo_score_posts_where() method below! */ + $vars = array_merge( + $vars, + array( + 'meta_query' => array( + 'relation' => 'AND', + array( + 'key' => self::$meta_prefix . 'linkdex', + 'value' => array( $low, $high ), + 'type' => 'numeric', + 'compare' => 'BETWEEN', + ), + array( + 'key' => self::$meta_prefix . 'meta-robots-noindex', + 'value' => 'needs-a-value-anyway', + 'compare' => 'NOT EXISTS', + ), + array( + 'key' => self::$meta_prefix . 'meta-robots-noindex', + 'value' => '1', + 'compare' => '!=', + ), + ) + ) + ); + + add_filter( 'posts_where', array( $this, 'seo_score_posts_where' ) ); + + } elseif ( $noindex ) { + $vars = array_merge( + $vars, + array( + 'meta_query' => array( + array( + 'key' => self::$meta_prefix . 'meta-robots-noindex', + 'value' => '1', + 'compare' => '=', + ), + ) + ) + ); + } + } + if ( isset( $_GET['seo_kw_filter'] ) && $_GET['seo_kw_filter'] !== '' ) { + $vars = array_merge( + $vars, array( + 'post_type' => 'any', + 'meta_key' => self::$meta_prefix . 'focuskw', + 'meta_value' => sanitize_text_field( $_GET['seo_kw_filter'] ), + ) + ); + } + if ( isset( $vars['orderby'] ) && 'wpseo-score' === $vars['orderby'] ) { + $vars = array_merge( + $vars, array( + 'meta_key' => self::$meta_prefix . 'linkdex', + 'orderby' => 'meta_value_num', + ) + ); + } + if ( isset( $vars['orderby'] ) && 'wpseo-metadesc' === $vars['orderby'] ) { + $vars = array_merge( + $vars, array( + 'meta_key' => self::$meta_prefix . 'metadesc', + 'orderby' => 'meta_value', + ) + ); + } + if ( isset( $vars['orderby'] ) && 'wpseo-focuskw' === $vars['orderby'] ) { + $vars = array_merge( + $vars, array( + 'meta_key' => self::$meta_prefix . 'focuskw', + 'orderby' => 'meta_value', + ) + ); + } + + return $vars; + } + + /** + * Hacky way to get round the limitation that you can only have AND *or* OR relationship between + * meta key clauses and not a combination - which is what we need. + * + * @param string $where + * + * @return string + */ + function seo_score_posts_where( $where ) { + global $wpdb; + + /* Find the two mutually exclusive noindex clauses which should be changed from AND to OR relation */ + $find = '`([\s]+AND[\s]+)((?:' . $wpdb->prefix . 'postmeta|mt[0-9]|mt1)\.post_id IS NULL[\s]+)AND([\s]+\([\s]*(?:' . $wpdb->prefix . 'postmeta|mt[0-9])\.meta_key = \'' . self::$meta_prefix . 'meta-robots-noindex\' AND CAST\([^\)]+\)[^\)]+\))`'; + + $replace = '$1( $2OR$3 )'; + + $new_where = preg_replace( $find, $replace, $where ); + + if ( $new_where ) { + return $new_where; + } else { + return $where; + } + } + + /** + * Retrieve the page title. + * + * @param int $post_id Post to retrieve the title for. + * + * @return string + */ + function page_title( $post_id ) { + $fixed_title = self::get_value( 'title', $post_id ); + if ( $fixed_title !== '' ) { + return $fixed_title; + } else { + $post = get_post( $post_id ); + $options = WPSEO_Options::get_all(); + if ( is_object( $post ) && ( isset( $options[ 'title-' . $post->post_type ] ) && $options[ 'title-' . $post->post_type ] !== '' ) ) { + $title_template = $options[ 'title-' . $post->post_type ]; + $title_template = str_replace( ' %%page%% ', ' ', $title_template ); + + return wpseo_replace_vars( $title_template, $post ); + } else { + return wpseo_replace_vars( '%%title%%', $post ); + } + } + } + + /** + * Sort an array by a given key. + * + * @param array $array Array to sort, array is returned sorted. + * @param string $key Key to sort array by. + */ + function aasort( &$array, $key ) { + $sorter = array(); + $ret = array(); + reset( $array ); + foreach ( $array as $ii => $va ) { + $sorter[ $ii ] = $va[ $key ]; + } + asort( $sorter ); + foreach ( $sorter as $ii => $va ) { + $ret[ $ii ] = $array[ $ii ]; + } + $array = $ret; + } + + /** + * Output the page analysis results. + * + * @param object $post Post to output the page analysis results for. + * + * @return string + */ + function linkdex_output( $post ) { + $results = $this->calculate_results( $post ); + + if ( is_wp_error( $results ) ) { + $error = $results->get_error_messages(); + + return '

' . esc_html( $error[0] ) . '

'; + } + $output = ''; + + if ( is_array( $results ) && $results !== array() ) { + + $output = ''; + $perc_score = absint( $results['total'] ); + unset( $results['total'] ); // unset to prevent echoing it. + + foreach ( $results as $result ) { + if ( is_array( $result ) ) { + $score = wpseo_translate_score( $result['val'] ); + $output .= ''; + } + } + $output .= '
' . $result['msg'] . '
'; + + if ( WP_DEBUG === true || ( defined( 'WPSEO_DEBUG' ) && WPSEO_DEBUG === true ) ) { + $output .= '

(' . $perc_score . '%)

'; + } + } + + $output = '

' . __( 'To update this page analysis, save as draft or update and check this tab again', 'wordpress-seo' ) . '.

' . $output; + + unset( $results ); + + return $output; + } + + /** + * Calculate the page analysis results for post. + * + * @todo [JRF => whomever] check whether the results of this method are always checked with is_wp_error() + * @todo [JRF => whomever] check the usage of this method as it's quite intense/heavy, see if it's only + * used when really necessary + * @todo [JRF => whomever] see if we can get rid of the passing by reference of $results as it makes + * the code obfuscated + * + * @param object $post Post to calculate the results for. + * + * @return array|WP_Error + */ + function calculate_results( $post ) { + $options = WPSEO_Options::get_all(); + + if ( ! class_exists( 'DOMDocument' ) ) { + $result = new WP_Error( 'no-domdocument', sprintf( __( "Your hosting environment does not support PHP's %sDocument Object Model%s.", 'wordpress-seo' ), '', '' ) . ' ' . __( "To enjoy all the benefits of the page analysis feature, you'll need to (get your host to) install it.", 'wordpress-seo' ) ); + + return $result; + } + + if ( ! is_array( $post ) && ! is_object( $post ) ) { + $result = new WP_Error( 'no-post', __( 'No post content to analyse.', 'wordpress-seo' ) ); + + return $result; + } elseif ( self::get_value( 'focuskw', $post->ID ) === '' ) { + $result = new WP_Error( 'no-focuskw', sprintf( __( 'No focus keyword was set for this %s. If you do not set a focus keyword, no score can be calculated.', 'wordpress-seo' ), $post->post_type ) ); + + self::set_value( 'linkdex', 0, $post->ID ); + + return $result; + } elseif ( apply_filters( 'wpseo_use_page_analysis', true ) !== true ) { + $result = new WP_Error( 'page-analysis-disabled', sprintf( __( 'Page Analysis has been disabled.', 'wordpress-seo' ), $post->post_type ) ); + + return $result; + } + + $results = array(); + $job = array(); + + $sampleurl = $this->get_sample_permalink( $post ); + $job['pageUrl'] = preg_replace( '`%(?:post|page)name%`', $sampleurl[1], $sampleurl[0] ); + $job['pageSlug'] = urldecode( $post->post_name ); + $job['keyword'] = self::get_value( 'focuskw', $post->ID ); + $job['keyword_folded'] = $this->strip_separators_and_fold( $job['keyword'] ); + $job['post_id'] = $post->ID; + $job['post_type'] = $post->post_type; + + $dom = new domDocument; + $dom->strictErrorChecking = false; + $dom->preserveWhiteSpace = false; + + /** + * Filter: 'wpseo_pre_analysis_post_content' - Make the post content filterable before calculating the page analysis + * + * @api string $post_content The post content + * + * @param object $post The post + */ + $post_content = apply_filters( 'wpseo_pre_analysis_post_content', $post->post_content, $post ); + + // Check if the post content is not empty + if ( ! empty( $post_content ) ) { + @$dom->loadHTML( $post_content ); + } + + unset( $post_content ); + + $xpath = new DOMXPath( $dom ); + + // Check if this focus keyword has been used already. + $this->check_double_focus_keyword( $job, $results ); + + // Keyword + $this->score_keyword( $job['keyword'], $results ); + + // Title + $title = self::get_value( 'title', $post->ID ); + if ( $title !== '' ) { + $job['title'] = $title; + } else { + if ( isset( $options[ 'title-' . $post->post_type ] ) && $options[ 'title-' . $post->post_type ] !== '' ) { + $title_template = $options[ 'title-' . $post->post_type ]; + } else { + $title_template = '%%title%% - %%sitename%%'; + } + $job['title'] = wpseo_replace_vars( $title_template, $post ); + } + unset( $title ); + $this->score_title( $job, $results ); + + // Meta description + $description = ''; + $desc_meta = self::get_value( 'metadesc', $post->ID ); + if ( $desc_meta !== '' ) { + $description = $desc_meta; + } elseif ( isset( $options[ 'metadesc-' . $post->post_type ] ) && $options[ 'metadesc-' . $post->post_type ] !== '' ) { + $description = wpseo_replace_vars( $options[ 'metadesc-' . $post->post_type ], $post ); + } + unset( $desc_meta ); + + self::$meta_length = apply_filters( 'wpseo_metadesc_length', self::$meta_length, $post ); + + $this->score_description( $job, $results, $description, self::$meta_length ); + unset( $description ); + + // Body + $body = $this->get_body( $post ); + $firstp = $this->get_first_paragraph( $body ); + $this->score_body( $job, $results, $body, $firstp ); + unset( $firstp ); + + // URL + $this->score_url( $job, $results ); + + // Headings + $headings = $this->get_headings( $body ); + $this->score_headings( $job, $results, $headings ); + unset( $headings ); + + // Images + $imgs = array(); + $imgs['count'] = substr_count( $body, 'get_images_alt_text( $post->ID, $body, $imgs ); + + // Check featured image + if ( function_exists( 'has_post_thumbnail' ) && has_post_thumbnail() ) { + $imgs['count'] += 1; + + if ( empty( $imgs['alts'] ) ) { + $imgs['alts'] = array(); + } + + $imgs['alts'][] = $this->strtolower_utf8( get_post_meta( get_post_thumbnail_id( $post->ID ), '_wp_attachment_image_alt', true ) ); + } + + $this->score_images_alt_text( $job, $results, $imgs ); + unset( $imgs ); + unset( $body ); + + // Anchors + $anchors = $this->get_anchor_texts( $xpath ); + $count = $this->get_anchor_count( $xpath ); + + $this->score_anchor_texts( $job, $results, $anchors, $count ); + unset( $anchors, $count, $dom ); + + $results = apply_filters( 'wpseo_linkdex_results', $results, $job, $post ); + + $this->aasort( $results, 'val' ); + + $overall = 0; + $overall_max = 0; + + foreach ( $results as $result ) { + $overall += $result['val']; + $overall_max += 9; + } + + if ( $overall < 1 ) { + $overall = 1; + } + $score = wpseo_calc( wpseo_calc( $overall, '/', $overall_max ), '*', 100, true ); + + if ( ! is_wp_error( $score ) ) { + self::set_value( 'linkdex', absint( $score ), $post->ID ); + + $results['total'] = $score; + } + + return $results; + } + + /** + * Get sample permalink + * + * @param object $post + * + * @return array + */ + function get_sample_permalink( $post ) { + if ( ! function_exists( 'get_sample_permalink' ) ) { + // Front-end post update + include_once( ABSPATH . 'wp-admin/includes/post.php' ); + } + + return get_sample_permalink( $post ); + } + + /** + * Save the score result to the results array. + * + * @param array $results The results array used to store results. + * @param int $scoreValue The score value. + * @param string $scoreMessage The score message. + * @param string $scoreLabel The label of the score to use in the results array. + * @param string $rawScore The raw score, to be used by other filters. + */ + function save_score_result( &$results, $scoreValue, $scoreMessage, $scoreLabel, $rawScore = null ) { + $score = array( + 'val' => $scoreValue, + 'msg' => $scoreMessage, + 'raw' => $rawScore, + ); + $results[ $scoreLabel ] = $score; + } + + /** + * Clean up the input string. + * + * @param string $inputString String to clean up. + * @param bool $removeOptionalCharacters Whether or not to do a cleanup of optional chars too. + * + * @return string + */ + function strip_separators_and_fold( $inputString, $removeOptionalCharacters = false ) { + $keywordCharactersAlwaysReplacedBySpace = array( ',', "'", '"', '?', '’', '“', '”', '|', '/' ); + $keywordCharactersRemovedOrReplaced = array( '_', '-' ); + $keywordWordsRemoved = array( ' a ', ' in ', ' an ', ' on ', ' for ', ' the ', ' and ' ); + + // lower + $inputString = $this->strtolower_utf8( $inputString ); + + // default characters replaced by space + $inputString = str_replace( $keywordCharactersAlwaysReplacedBySpace, ' ', $inputString ); + + // standardise whitespace + $inputString = wpseo_standardize_whitespace( $inputString ); + + // deal with the separators that can be either removed or replaced by space + if ( $removeOptionalCharacters ) { + // remove word separators with a space + $inputString = str_replace( $keywordWordsRemoved, ' ', $inputString ); + + $inputString = str_replace( $keywordCharactersRemovedOrReplaced, '', $inputString ); + } else { + $inputString = str_replace( $keywordCharactersRemovedOrReplaced, ' ', $inputString ); + } + + // standardise whitespace again + $inputString = wpseo_standardize_whitespace( $inputString ); + + return trim( $inputString ); + } + + /** + * Check whether this focus keyword has been used for other posts before. + * + * @param array $job + * @param array $results + */ + function check_double_focus_keyword( $job, &$results ) { + $posts = get_posts( + array( + 'meta_key' => self::$meta_prefix . 'focuskw', + 'meta_value' => $job['keyword'], + 'exclude' => $job['post_id'], + 'fields' => 'ids', + 'post_type' => 'any', + 'numberposts' => - 1, + ) + ); + + if ( count( $posts ) == 0 ) { + $this->save_score_result( $results, 9, __( 'You\'ve never used this focus keyword before, very good.', 'wordpress-seo' ), 'keyword_overused' ); + } elseif ( count( $posts ) == 1 ) { + $this->save_score_result( $results, 6, sprintf( __( 'You\'ve used this focus keyword %1$sonce before%2$s, be sure to make very clear which URL on your site is the most important for this keyword.', 'wordpress-seo' ), '', '' ), 'keyword_overused' ); + } else { + $this->save_score_result( $results, 1, sprintf( __( 'You\'ve used this focus keyword %3$s%4$d times before%2$s, it\'s probably a good idea to read %1$sthis post on cornerstone content%2$s and improve your keyword strategy.', 'wordpress-seo' ), '', '', '', count( $posts ) ), 'keyword_overused' ); + } + } + + /** + * Check whether the keyword contains stopwords. + * + * @param string $keyword The keyword to check for stopwords. + * @param array $results The results array. + */ + function score_keyword( $keyword, &$results ) { + global $wpseo_admin; + + $keywordStopWord = __( 'The keyword for this page contains one or more %sstop words%s, consider removing them. Found \'%s\'.', 'wordpress-seo' ); + + if ( $wpseo_admin->stopwords_check( $keyword ) !== false ) { + $this->save_score_result( $results, 5, sprintf( $keywordStopWord, '', '', $wpseo_admin->stopwords_check( $keyword ) ), 'keyword_stopwords' ); + } + } + + /** + * Check whether the keyword is contained in the URL. + * + * @param array $job The job array holding both the keyword and the URLs. + * @param array $results The results array. + */ + function score_url( $job, &$results ) { + global $wpseo_admin; + + $urlGood = __( 'The keyword / phrase appears in the URL for this page.', 'wordpress-seo' ); + $urlMedium = __( 'The keyword / phrase does not appear in the URL for this page. If you decide to rename the URL be sure to check the old URL 301 redirects to the new one!', 'wordpress-seo' ); + $urlStopWords = __( 'The slug for this page contains one or more stop words, consider removing them.', 'wordpress-seo' ); + $longSlug = __( 'The slug for this page is a bit long, consider shortening it.', 'wordpress-seo' ); + + $needle = $this->strip_separators_and_fold( remove_accents( $job['keyword'] ) ); + $haystack1 = $this->strip_separators_and_fold( $job['pageUrl'], true ); + $haystack2 = $this->strip_separators_and_fold( $job['pageUrl'], false ); + + if ( stripos( $haystack1, $needle ) || stripos( $haystack2, $needle ) ) { + $this->save_score_result( $results, 9, $urlGood, 'url_keyword' ); + } else { + $this->save_score_result( $results, 6, $urlMedium, 'url_keyword' ); + } + + // Check for Stop Words in the slug + if ( $wpseo_admin->stopwords_check( $job['pageSlug'], true ) !== false ) { + $this->save_score_result( $results, 5, $urlStopWords, 'url_stopword' ); + } + + // Check if the slug isn't too long relative to the length of the keyword + if ( ( $this->statistics()->text_length( $job['keyword'] ) + 20 ) < $this->statistics()->text_length( $job['pageSlug'] ) && 40 < $this->statistics()->text_length( $job['pageSlug'] ) ) { + $this->save_score_result( $results, 5, $longSlug, 'url_length' ); + } + } + + /** + * Check whether the keyword is contained in the title. + * + * @param array $job The job array holding both the keyword versions. + * @param array $results The results array. + */ + function score_title( $job, &$results ) { + $scoreTitleMinLength = 40; + $scoreTitleMaxLength = 70; + $scoreTitleKeywordLimit = 0; + + $scoreTitleMissing = __( 'Please create a page title.', 'wordpress-seo' ); + $scoreTitleCorrectLength = __( 'The page title is more than 40 characters and less than the recommended 70 character limit.', 'wordpress-seo' ); + $scoreTitleTooShort = __( 'The page title contains %d characters, which is less than the recommended minimum of 40 characters. Use the space to add keyword variations or create compelling call-to-action copy.', 'wordpress-seo' ); + $scoreTitleTooLong = __( 'The page title contains %d characters, which is more than the viewable limit of 70 characters; some words will not be visible to users in your listing.', 'wordpress-seo' ); + $scoreTitleKeywordMissing = __( 'The keyword / phrase %s does not appear in the page title.', 'wordpress-seo' ); + $scoreTitleKeywordBeginning = __( 'The page title contains keyword / phrase, at the beginning which is considered to improve rankings.', 'wordpress-seo' ); + $scoreTitleKeywordEnd = __( 'The page title contains keyword / phrase, but it does not appear at the beginning; try and move it to the beginning.', 'wordpress-seo' ); + + if ( $job['title'] == '' ) { + $this->save_score_result( $results, 1, $scoreTitleMissing, 'title' ); + } else { + $job['title'] = wp_strip_all_tags( $job['title'] ); + + $length = $this->statistics()->text_length( $job['title'] ); + if ( $length < $scoreTitleMinLength ) { + $this->save_score_result( $results, 6, sprintf( $scoreTitleTooShort, $length ), 'title_length' ); + } elseif ( $length > $scoreTitleMaxLength ) { + $this->save_score_result( $results, 6, sprintf( $scoreTitleTooLong, $length ), 'title_length' ); + } else { + $this->save_score_result( $results, 9, $scoreTitleCorrectLength, 'title_length' ); + } + + // @todo MA Keyword/Title matching is exact match with separators removed, but should extend to distributed match + $needle_position = stripos( $job['title'], $job['keyword_folded'] ); + + if ( $needle_position === false ) { + $needle_position = stripos( $job['title'], $job['keyword'] ); + } + + if ( $needle_position === false ) { + $this->save_score_result( $results, 2, sprintf( $scoreTitleKeywordMissing, $job['keyword_folded'] ), 'title_keyword' ); + } elseif ( $needle_position <= $scoreTitleKeywordLimit ) { + $this->save_score_result( $results, 9, $scoreTitleKeywordBeginning, 'title_keyword' ); + } else { + $this->save_score_result( $results, 6, $scoreTitleKeywordEnd, 'title_keyword' ); + } + } + } + + /** + * Check whether the document contains outbound links and whether it's anchor text matches the keyword. + * + * @param array $job The job array holding both the keyword versions. + * @param array $results The results array. + * @param array $anchor_texts The array holding all anchors in the document. + * @param array $count The number of anchors in the document, grouped by type. + */ + function score_anchor_texts( $job, &$results, $anchor_texts, $count ) { + $scoreNoLinks = __( 'No outbound links appear in this page, consider adding some as appropriate.', 'wordpress-seo' ); + $scoreKeywordInOutboundLink = __( 'You\'re linking to another page with the keyword you want this page to rank for, consider changing that if you truly want this page to rank.', 'wordpress-seo' ); + $scoreLinksDofollow = __( 'This page has %s outbound link(s).', 'wordpress-seo' ); + $scoreLinksNofollow = __( 'This page has %s outbound link(s), all nofollowed.', 'wordpress-seo' ); + $scoreLinks = __( 'This page has %s nofollowed link(s) and %s normal outbound link(s).', 'wordpress-seo' ); + + if ( $count['external']['nofollow'] == 0 && $count['external']['dofollow'] == 0 ) { + $this->save_score_result( $results, 6, $scoreNoLinks, 'links' ); + } else { + $found = false; + if ( is_array( $anchor_texts ) && $anchor_texts !== array() ) { + foreach ( $anchor_texts as $anchor_text ) { + if ( $this->strtolower_utf8( $anchor_text ) == $job['keyword_folded'] ) { + $found = true; + } + } + } + if ( $found ) { + $this->save_score_result( $results, 2, $scoreKeywordInOutboundLink, 'links_focus_keyword' ); + } + + if ( $count['external']['nofollow'] == 0 && $count['external']['dofollow'] > 0 ) { + $this->save_score_result( $results, 9, sprintf( $scoreLinksDofollow, $count['external']['dofollow'] ), 'links_number' ); + } elseif ( $count['external']['nofollow'] > 0 && $count['external']['dofollow'] == 0 ) { + $this->save_score_result( $results, 7, sprintf( $scoreLinksNofollow, $count['external']['nofollow'] ), 'links_number' ); + } else { + $this->save_score_result( $results, 8, sprintf( $scoreLinks, $count['external']['nofollow'], $count['external']['dofollow'] ), 'links_number' ); + } + } + } + + /** + * Retrieve the anchor texts used in the current document. + * + * @param object $xpath An XPATH object of the current document. + * + * @return array + */ + function get_anchor_texts( &$xpath ) { + $query = '//a|//A'; + $dom_objects = $xpath->query( $query ); + $anchor_texts = array(); + if ( is_object( $dom_objects ) && is_a( $dom_objects, 'DOMNodeList' ) && $dom_objects->length > 0 ) { + foreach ( $dom_objects as $dom_object ) { + if ( $dom_object->attributes->getNamedItem( 'href' ) ) { + $href = $dom_object->attributes->getNamedItem( 'href' )->textContent; + if ( substr( $href, 0, 4 ) == 'http' ) { + $anchor_texts['external'] = $dom_object->textContent; + } + } + } + } + + return $anchor_texts; + } + + /** + * Count the number of anchors and group them by type. + * + * @param object $xpath An XPATH object of the current document. + * + * @return array + */ + function get_anchor_count( &$xpath ) { + $query = '//a|//A'; + $dom_objects = $xpath->query( $query ); + + $count = array( + 'total' => 0, + 'internal' => array( 'nofollow' => 0, 'dofollow' => 0 ), + 'external' => array( 'nofollow' => 0, 'dofollow' => 0 ), + 'other' => array( 'nofollow' => 0, 'dofollow' => 0 ), + ); + + if ( is_object( $dom_objects ) && is_a( $dom_objects, 'DOMNodeList' ) && $dom_objects->length > 0 ) { + foreach ( $dom_objects as $dom_object ) { + $count['total'] ++; + if ( $dom_object->attributes->getNamedItem( 'href' ) ) { + $href = $dom_object->attributes->getNamedItem( 'href' )->textContent; + $wpurl = get_bloginfo( 'url' ); + if ( wpseo_is_url_relative( $href ) === true || substr( $href, 0, strlen( $wpurl ) ) === $wpurl ) { + $type = 'internal'; + } elseif ( substr( $href, 0, 4 ) == 'http' ) { + $type = 'external'; + } else { + $type = 'other'; + } + + if ( $dom_object->attributes->getNamedItem( 'rel' ) ) { + $link_rel = $dom_object->attributes->getNamedItem( 'rel' )->textContent; + if ( stripos( $link_rel, 'nofollow' ) !== false ) { + $count[ $type ]['nofollow'] ++; + } else { + $count[ $type ]['dofollow'] ++; + } + } else { + $count[ $type ]['dofollow'] ++; + } + } + } + } + + return $count; + } + + /** + * Check whether the images alt texts contain the keyword. + * + * @param array $job The job array holding both the keyword versions. + * @param array $results The results array. + * @param array $imgs The array with images alt texts. + */ + function score_images_alt_text( $job, &$results, $imgs ) { + $scoreImagesNoImages = __( 'No images appear in this page, consider adding some as appropriate.', 'wordpress-seo' ); + $scoreImagesNoAlt = __( 'The images on this page are missing alt tags.', 'wordpress-seo' ); + $scoreImagesAltKeywordIn = __( 'The images on this page contain alt tags with the target keyword / phrase.', 'wordpress-seo' ); + $scoreImagesAltKeywordMissing = __( 'The images on this page do not have alt tags containing your keyword / phrase.', 'wordpress-seo' ); + + if ( $imgs['count'] == 0 ) { + $this->save_score_result( $results, 3, $scoreImagesNoImages, 'images_alt' ); + } elseif ( count( $imgs['alts'] ) == 0 && $imgs['count'] != 0 ) { + $this->save_score_result( $results, 5, $scoreImagesNoAlt, 'images_alt' ); + } else { + $found = false; + foreach ( $imgs['alts'] as $alt ) { + $haystack1 = $this->strip_separators_and_fold( $alt, true ); + $haystack2 = $this->strip_separators_and_fold( $alt, false ); + if ( strrpos( $haystack1, $job['keyword_folded'] ) !== false ) { + $found = true; + } elseif ( strrpos( $haystack2, $job['keyword_folded'] ) !== false ) { + $found = true; + } + } + if ( $found ) { + $this->save_score_result( $results, 9, $scoreImagesAltKeywordIn, 'images_alt' ); + } else { + $this->save_score_result( $results, 5, $scoreImagesAltKeywordMissing, 'images_alt' ); + } + } + } + + /** + * Retrieve the alt texts from the images. + * + * @param int $post_id The post to find images in. + * @param string $body The post content to find images in. + * @param array $imgs The array holding the image information. + * + * @return array The updated images array. + */ + function get_images_alt_text( $post_id, $body, $imgs ) { + preg_match_all( '`]+>`im', $body, $matches ); + $imgs['alts'] = array(); + if ( isset( $matches[0] ) && is_array( $matches[0] ) && $matches[0] !== array() ) { + foreach ( $matches[0] as $img ) { + if ( preg_match( '`alt=(["\'])(.*?)\1`', $img, $alt ) && isset( $alt[2] ) ) { + $imgs['alts'][] = $this->strtolower_utf8( $alt[2] ); + } + } + } + if ( strpos( $body, '[gallery' ) !== false ) { + $attachments = get_children( array( + 'post_parent' => $post_id, + 'post_status' => 'inherit', + 'post_type' => 'attachment', + 'post_mime_type' => 'image', + 'fields' => 'ids', + ) ); + if ( is_array( $attachments ) && $attachments !== array() ) { + foreach ( $attachments as $att_id ) { + $alt = get_post_meta( $att_id, '_wp_attachment_image_alt', true ); + if ( $alt && ! empty( $alt ) ) { + $imgs['alts'][] = $alt; + } + $imgs['count'] ++; + } + } + } + + return $imgs; + } + + /** + * Score the headings for keyword appearance. + * + * @param array $job The array holding the keywords. + * @param array $results The results array. + * @param array $headings The headings found in the document. + */ + function score_headings( $job, &$results, $headings ) { + $scoreHeadingsNone = __( 'No subheading tags (like an H2) appear in the copy.', 'wordpress-seo' ); + $scoreHeadingsKeywordIn = __( 'Keyword / keyphrase appears in %s (out of %s) subheadings in the copy. While not a major ranking factor, this is beneficial.', 'wordpress-seo' ); + $scoreHeadingsKeywordMissing = __( 'You have not used your keyword / keyphrase in any subheading (such as an H2) in your copy.', 'wordpress-seo' ); + + $headingCount = count( $headings ); + if ( $headingCount == 0 ) { + $this->save_score_result( $results, 7, $scoreHeadingsNone, 'headings' ); + } else { + $found = 0; + foreach ( $headings as $heading ) { + $haystack1 = $this->strip_separators_and_fold( $heading, true ); + $haystack2 = $this->strip_separators_and_fold( $heading, false ); + + if ( strrpos( $haystack1, $job['keyword_folded'] ) !== false ) { + $found ++; + } elseif ( strrpos( $haystack2, $job['keyword_folded'] ) !== false ) { + $found ++; + } + } + if ( $found ) { + $this->save_score_result( $results, 9, sprintf( $scoreHeadingsKeywordIn, $found, $headingCount ), 'headings' ); + } else { + $this->save_score_result( $results, 3, $scoreHeadingsKeywordMissing, 'headings' ); + } + } + } + + /** + * Fetch all headings and return their content. + * + * @param string $postcontent Post content to find headings in. + * + * @return array Array of heading texts. + */ + function get_headings( $postcontent ) { + $headings = array(); + + preg_match_all( '`]+)?>(.*?)`si', $postcontent, $matches ); + + if ( isset( $matches[2] ) && is_array( $matches[2] ) && $matches[2] !== array() ) { + foreach ( $matches[2] as $heading ) { + $headings[] = $this->strtolower_utf8( $heading ); + } + } + + return $headings; + } + + /** + * Score the meta description for length and keyword appearance. + * + * @param array $job The array holding the keywords. + * @param array $results The results array. + * @param string $description The meta description. + * @param int $maxlength The maximum length of the meta description. + */ + function score_description( $job, &$results, $description, $maxlength = 155 ) { + $scoreDescriptionMinLength = 120; + $scoreDescriptionCorrectLength = __( 'In the specified meta description, consider: How does it compare to the competition? Could it be made more appealing?', 'wordpress-seo' ); + $scoreDescriptionTooShort = __( 'The meta description is under 120 characters, however up to %s characters are available. %s', 'wordpress-seo' ); + $scoreDescriptionTooLong = __( 'The specified meta description is over %s characters, reducing it will ensure the entire description is visible. %s', 'wordpress-seo' ); + $scoreDescriptionMissing = __( 'No meta description has been specified, search engines will display copy from the page instead.', 'wordpress-seo' ); + $scoreDescriptionKeywordIn = __( 'The meta description contains the primary keyword / phrase.', 'wordpress-seo' ); + $scoreDescriptionKeywordMissing = __( 'A meta description has been specified, but it does not contain the target keyword / phrase.', 'wordpress-seo' ); + + $metaShorter = ''; + if ( $maxlength != 155 ) { + $metaShorter = __( 'The available space is shorter than the usual 155 characters because Google will also include the publication date in the snippet.', 'wordpress-seo' ); + } + + if ( $description == '' ) { + $this->save_score_result( $results, 1, $scoreDescriptionMissing, 'description_length' ); + } else { + $length = $this->statistics()->text_length( $description ); + + if ( $length < $scoreDescriptionMinLength ) { + $this->save_score_result( $results, 6, sprintf( $scoreDescriptionTooShort, $maxlength, $metaShorter ), 'description_length' ); + } elseif ( $length <= $maxlength ) { + $this->save_score_result( $results, 9, $scoreDescriptionCorrectLength, 'description_length' ); + } else { + $this->save_score_result( $results, 6, sprintf( $scoreDescriptionTooLong, $maxlength, $metaShorter ), 'description_length' ); + } + + // @todo MA Keyword/Title matching is exact match with separators removed, but should extend to distributed match + $haystack1 = $this->strip_separators_and_fold( $description, true ); + $haystack2 = $this->strip_separators_and_fold( $description, false ); + if ( strrpos( $haystack1, $job['keyword_folded'] ) === false && strrpos( $haystack2, $job['keyword_folded'] ) === false ) { + $this->save_score_result( $results, 3, $scoreDescriptionKeywordMissing, 'description_keyword' ); + } else { + $this->save_score_result( $results, 9, $scoreDescriptionKeywordIn, 'description_keyword' ); + } + } + } + + /** + * Score the body for length and keyword appearance. + * + * @param array $job The array holding the keywords. + * @param array $results The results array. + * @param string $body The body. + * @param string $firstp The first paragraph. + */ + function score_body( $job, &$results, $body, $firstp ) { + $lengthScore = array( + 'good' => 300, + 'ok' => 250, + 'poor' => 200, + 'bad' => 100, + ); + $lengthScore = apply_filters( 'wpseo_body_length_score', $lengthScore, $job ); + + $scoreBodyGoodLength = __( 'There are %d words contained in the body copy, this is more than the %d word recommended minimum.', 'wordpress-seo' ); + $scoreBodyPoorLength = __( 'There are %d words contained in the body copy, this is below the %d word recommended minimum. Add more useful content on this topic for readers.', 'wordpress-seo' ); + $scoreBodyOKLength = __( 'There are %d words contained in the body copy, this is slightly below the %d word recommended minimum, add a bit more copy.', 'wordpress-seo' ); + $scoreBodyBadLength = __( 'There are %d words contained in the body copy. This is far too low and should be increased.', 'wordpress-seo' ); + + $scoreKeywordDensityLow = __( 'The keyword density is %s%%, which is a bit low, the keyword was found %s times.', 'wordpress-seo' ); + $scoreKeywordDensityHigh = __( 'The keyword density is %s%%, which is over the advised 4.5%% maximum, the keyword was found %s times.', 'wordpress-seo' ); + $scoreKeywordDensityGood = __( 'The keyword density is %s%%, which is great, the keyword was found %s times.', 'wordpress-seo' ); + + $scoreFirstParagraphLow = __( 'The keyword doesn\'t appear in the first paragraph of the copy, make sure the topic is clear immediately.', 'wordpress-seo' ); + $scoreFirstParagraphHigh = __( 'The keyword appears in the first paragraph of the copy.', 'wordpress-seo' ); + + $fleschurl = '' . __( 'Flesch Reading Ease', 'wordpress-seo' ) . ''; + $scoreFlesch = __( 'The copy scores %s in the %s test, which is considered %s to read. %s', 'wordpress-seo' ); + + // Replace images with their alt tags, then strip all tags + $body = preg_replace( '`]+)?alt="([^"]+)"(?:[^>]+)>`', '$1', $body ); + $body = strip_tags( $body ); + + // Copy length check + $wordCount = $this->statistics()->word_count( $body ); + + if ( $wordCount < $lengthScore['bad'] ) { + $this->save_score_result( $results, - 20, sprintf( $scoreBodyBadLength, $wordCount, $lengthScore['good'] ), 'body_length', $wordCount ); + } elseif ( $wordCount < $lengthScore['poor'] ) { + $this->save_score_result( $results, - 10, sprintf( $scoreBodyPoorLength, $wordCount, $lengthScore['good'] ), 'body_length', $wordCount ); + } elseif ( $wordCount < $lengthScore['ok'] ) { + $this->save_score_result( $results, 5, sprintf( $scoreBodyPoorLength, $wordCount, $lengthScore['good'] ), 'body_length', $wordCount ); + } elseif ( $wordCount < $lengthScore['good'] ) { + $this->save_score_result( $results, 7, sprintf( $scoreBodyOKLength, $wordCount, $lengthScore['good'] ), 'body_length', $wordCount ); + } else { + $this->save_score_result( $results, 9, sprintf( $scoreBodyGoodLength, $wordCount, $lengthScore['good'] ), 'body_length', $wordCount ); + } + + $body = $this->strtolower_utf8( $body ); + $job['keyword'] = $this->strtolower_utf8( $job['keyword'] ); + + $keywordWordCount = $this->statistics()->word_count( $job['keyword'] ); + + if ( $keywordWordCount > 10 ) { + $this->save_score_result( $results, 0, __( 'Your keyphrase is over 10 words, a keyphrase should be shorter and there can be only one keyphrase.', 'wordpress-seo' ), 'focus_keyword_length' ); + } else { + // Keyword Density check + $keywordDensity = 0; + if ( $wordCount > 100 ) { + $keywordCount = preg_match_all( '`\b' . preg_quote( $job['keyword'], '`' ) . '\b`miu', utf8_encode( $body ), $res ); + if ( ( $keywordCount > 0 && $keywordWordCount > 0 ) && $wordCount > $keywordCount ) { + $keywordDensity = wpseo_calc( wpseo_calc( $keywordCount, '/', wpseo_calc( $wordCount, '-', ( wpseo_calc( wpseo_calc( $keywordWordCount, '-', 1 ), '*', $keywordCount ) ) ) ), '*', 100, true, 2 ); + } + if ( $keywordDensity < 1 ) { + $this->save_score_result( $results, 4, sprintf( $scoreKeywordDensityLow, $keywordDensity, $keywordCount ), 'keyword_density' ); + } elseif ( $keywordDensity > 4.5 ) { + $this->save_score_result( $results, - 50, sprintf( $scoreKeywordDensityHigh, $keywordDensity, $keywordCount ), 'keyword_density' ); + } else { + $this->save_score_result( $results, 9, sprintf( $scoreKeywordDensityGood, $keywordDensity, $keywordCount ), 'keyword_density' ); + } + } + } + + $firstp = $this->strtolower_utf8( $firstp ); + + // First Paragraph Test + // check without /u modifier as well as /u might break with non UTF-8 chars. + if ( preg_match( '`\b' . preg_quote( $job['keyword'], '`' ) . '\b`miu', $firstp ) || preg_match( '`\b' . preg_quote( $job['keyword'], '`' ) . '\b`mi', $firstp ) || preg_match( '`\b' . preg_quote( $job['keyword_folded'], '`' ) . '\b`miu', $firstp ) + ) { + $this->save_score_result( $results, 9, $scoreFirstParagraphHigh, 'keyword_first_paragraph' ); + } else { + $this->save_score_result( $results, 3, $scoreFirstParagraphLow, 'keyword_first_paragraph' ); + } + + $lang = get_bloginfo( 'language' ); + if ( substr( $lang, 0, 2 ) == 'en' && $wordCount > 100 ) { + // Flesch Reading Ease check + $flesch = $this->statistics()->flesch_kincaid_reading_ease( $body ); + + $note = ''; + $level = ''; + $score = 1; + if ( $flesch >= 90 ) { + $level = __( 'very easy', 'wordpress-seo' ); + $score = 9; + } elseif ( $flesch >= 80 ) { + $level = __( 'easy', 'wordpress-seo' ); + $score = 9; + } elseif ( $flesch >= 70 ) { + $level = __( 'fairly easy', 'wordpress-seo' ); + $score = 8; + } elseif ( $flesch >= 60 ) { + $level = __( 'OK', 'wordpress-seo' ); + $score = 7; + } elseif ( $flesch >= 50 ) { + $level = __( 'fairly difficult', 'wordpress-seo' ); + $note = __( 'Try to make shorter sentences to improve readability.', 'wordpress-seo' ); + $score = 6; + } elseif ( $flesch >= 30 ) { + $level = __( 'difficult', 'wordpress-seo' ); + $note = __( 'Try to make shorter sentences, using less difficult words to improve readability.', 'wordpress-seo' ); + $score = 5; + } elseif ( $flesch >= 0 ) { + $level = __( 'very difficult', 'wordpress-seo' ); + $note = __( 'Try to make shorter sentences, using less difficult words to improve readability.', 'wordpress-seo' ); + $score = 4; + } + $this->save_score_result( $results, $score, sprintf( $scoreFlesch, $flesch, $fleschurl, $level, $note ), 'flesch_kincaid' ); + } + } + + /** + * Retrieve the body from the post. + * + * @param object $post The post object. + * + * @return string The post content. + */ + function get_body( $post ) { + // This filter allows plugins to add their content to the content to be analyzed. + $post_content = apply_filters( 'wpseo_pre_analysis_post_content', $post->post_content, $post ); + + // Strip shortcodes, for obvious reasons, if plugins think their content should be in the analysis, they should + // hook into the above filter. + $post_content = wpseo_strip_shortcode( $post_content ); + + if ( trim( $post_content ) == '' ) { + return ''; + } + + $htmdata3 = preg_replace( '`<(?:\x20*script|script).*?(?:/>|/script>)`', '', $post_content ); + if ( $htmdata3 == null ) { + $htmdata3 = $post_content; + } else { + unset( $post_content ); + } + + $htmdata4 = preg_replace( '``', '', $htmdata3 ); + if ( $htmdata4 == null ) { + $htmdata4 = $htmdata3; + } else { + unset( $htmdata3 ); + } + + $htmdata5 = preg_replace( '`<(?:\x20*style|style).*?(?:/>|/style>)`', '', $htmdata4 ); + if ( $htmdata5 == null ) { + $htmdata5 = $htmdata4; + } else { + unset( $htmdata4 ); + } + + return $htmdata5; + } + + /** + * Retrieve the first paragraph from the post. + * + * @param string $body The post content to retrieve the first paragraph from. + * + * @return string + */ + function get_first_paragraph( $body ) { + // To determine the first paragraph we first need to autop the content, then match the first paragraph and return. + $res = preg_match( '`(.*)

`s', wpautop( $body ), $matches ); + if ( $res ) { + return $matches[1]; + } + + return false; + } + + /********************** DEPRECATED METHODS **********************/ + + /** + * Adds the WordPress SEO box + * + * @deprecated 1.4.24 + * @deprecated use WPSEO_Metabox::add_meta_box() + * @see WPSEO_Meta::add_meta_box() + */ + public function add_custom_box() { + _deprecated_function( __METHOD__, 'WPSEO 1.4.24', 'WPSEO_Metabox::add_meta_box()' ); + $this->add_meta_box(); + } + + /** + * Retrieve the meta boxes for the given post type. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Meta::get_meta_field_defs() + * @see WPSEO_Meta::get_meta_field_defs() + * + * @param string $post_type + * + * @return array + */ + public function get_meta_boxes( $post_type = 'post' ) { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Meta::get_meta_field_defs()' ); + + return $this->get_meta_field_defs( 'general', $post_type ); + } + + /** + * Pass some variables to js + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Meta::localize_script() + * @see WPSEO_Meta::localize_script() + */ + public function script() { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Meta::localize_script()' ); + + return $this->localize_script(); + } + + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/admin/class-opengraph-admin.php b/wp-content/plugins/wordpress-seo/admin/class-opengraph-admin.php new file mode 100644 index 0000000..50089c6 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-opengraph-admin.php @@ -0,0 +1,103 @@ +' . __( 'Social', 'wordpress-seo' ) . ''; + } + + /** + * Output the tab content + */ + public function tab_content() { + $content = ''; + foreach ( $this->get_meta_field_defs( 'social' ) as $meta_key => $meta_field ) { + $content .= $this->do_meta_box( $meta_field, $meta_key ); + } + $this->do_tab( 'social', __( 'Social', 'wordpress-seo' ), $content ); + } + + + /** + * Filter over the meta boxes to save, this function adds the Social meta boxes. + * + * @param array $field_defs Array of metaboxes to save. + * @return array + */ + public function save_meta_boxes( $field_defs ) { + return array_merge( $field_defs, $this->get_meta_field_defs( 'social' ) ); + } + + + /********************** DEPRECATED METHODS **********************/ + + /** + * Define the meta boxes for the Social tab + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Meta::get_meta_field_defs() + * @see WPSEO_Meta::get_meta_field_defs() + * + * @param string $post_type + * @return array Array containing the meta boxes + */ + public function get_meta_boxes( $post_type = 'post' ) { + _deprecated_function( __METHOD__, 'WPSEO 1.5.0', 'WPSEO_Meta::get_meta_field_defs()' ); + return $this->get_meta_field_defs( 'social' ); + } + + } /* End of class */ + +} /* End of class-exists wrapper */ \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/class-pointers.php b/wp-content/plugins/wordpress-seo/admin/class-pointers.php new file mode 100644 index 0000000..e8155c0 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-pointers.php @@ -0,0 +1,362 @@ +' . __( 'Help improve WordPress SEO', 'wordpress-seo' ) . ''; + $content .= '

' . __( 'You\'ve just installed WordPress SEO by Yoast. Please helps us improve it by allowing us to gather anonymous usage stats so we know which configurations, plugins and themes to test with.', 'wordpress-seo' ) . '

'; + $opt_arr = array( + 'content' => $content, + 'position' => array( 'edge' => 'top', 'align' => 'center' ) + ); + $button_array = array( + 'button1' => array( + 'text' => __( 'Do not allow tracking', 'wordpress-seo' ), + 'function' => 'wpseo_store_answer("no","' . $nonce . '")', + ), + 'button2' => array( + 'text' => __( 'Allow tracking', 'wordpress-seo' ), + 'function' => 'wpseo_store_answer("yes","' . $nonce . '")', + ), + ); + + $this->print_scripts( $id, $opt_arr, $button_array ); + } + + /** + * Load the introduction tour + */ + function intro_tour() { + global $pagenow, $current_user; + + // @FIXME: Links to tabs only work with target="_blank" and thus open in a new window + $adminpages = array( + 'wpseo_dashboard' => array( + 'content' => '

' . __( 'Dashboard', 'wordpress-seo' ) . '

' . __( 'This is the WordPress SEO Dashboard, here you can restart this tour or revert the WP SEO settings to default.', 'wordpress-seo' ) . '

' + . '

' . __( 'More WordPress SEO', 'wordpress-seo' ) . '
' . sprintf( __( 'There\'s more to learn about WordPress & SEO than just using this plugin. A great start is our article %1$sthe definitive guide to WordPress SEO%2$s.', 'wordpress-seo' ), '', '' ) . '

' + . '

' . __( 'Tracking', 'wordpress-seo' ) . '
' . __( 'To provide you with the best experience possible, we need your help. Please enable tracking to help us gather anonymous usage data.', 'wordpress-seo' ) . '

' + . '

' . __( 'Webmaster Tools', 'wordpress-seo' ) . '
' . __( 'You can also add the verification codes for the different Webmaster Tools programs here, we highly encourage you to check out both Google and Bing\'s Webmaster Tools.', 'wordpress-seo' ) . '

' + . '

' . __( 'WordPress SEO Tour', 'wordpress-seo' ) . '
' . __( 'This tour will show you around in the plugin, to give you a general overview of the plugin.', 'wordpress-seo' ) . '

' + . '

' . __( 'Newsletter', 'wordpress-seo' ) . '
' . + __( 'If you would like to us to keep you up-to-date regarding WordPress SEO and other plugins by Yoast, subscribe to our newsletter:', 'wordpress-seo' ) . '

' . + '
' . + '

' . + '
' . + '' . + '' . + '

', + 'next' => __( 'Next', 'wordpress-seo' ), + 'next_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_titles' ) . '";', + 'position' => array( 'edge' => 'top', 'align' => 'center' ), + ), + 'wpseo_titles' => array( + 'content' => '

' . __( 'Title & Metas settings', 'wordpress-seo' ) . '

' . '

' . __( 'This is where you set the titles and meta-information for all your post types, taxonomies, archives, special pages and for your homepage. The page is divided into different tabs., make sure you check \'m all out!', 'wordpress-seo' ) . '

' + . '

' . __( 'Sitewide settings', 'wordpress-seo' ) . '
' . __( 'The first tab will show you site-wide settings. You can also set some settings for the entire site here to add specific meta tags or to remove some unneeded cruft.', 'wordpress-seo' ) . '

' + . '

' . __( 'Templates and settings', 'wordpress-seo' ) . '
' . sprintf( __( 'Now click on the \'%1$sPost Types%2$s\'-tab, as this will be our example.', 'wordpress-seo' ), '', '' ) . '
' . __( 'The templates are built using variables. You can find all these variables in the help tab (in the top-right corner of the page). The settings allow you to set specific behavior for the post types.', 'wordpress-seo' ) . '

', + 'next' => __( 'Next', 'wordpress-seo' ), + 'next_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_social' ) . '";', + 'prev' => __( 'Previous', 'wordpress-seo' ), + 'prev_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_dashboard' ) . '";', + ), + 'wpseo_social' => array( + 'content' => '

' . __( 'Social settings', 'wordpress-seo' ) . '

' + . '

' . __( 'Facebook', 'wordpress-seo' ) . '
' . sprintf( __( 'On this tab you can enable the %1$sFacebook Open Graph%2$s functionality from this plugin, as well as assign a Facebook user or Application to be the admin of your site, so you can view the Facebook insights.', 'wordpress-seo' ), '', '' ) . '

' . __( 'The frontpage settings allow you to set meta-data for your homepage, whereas the default settings allow you to set a fallback for all posts/pages without images. ', 'wordpress-seo' ) . '

' + . '

' . __( 'Twitter', 'wordpress-seo' ) . '
' . sprintf( __( 'With %1$sTwitter Cards%2$s, you can attach rich photos, videos and media experience to tweets that drive traffic to your website. Simply check the box, sign up for the service, and users who Tweet links to your content will have a "Card" added to the tweet that\'s visible to all of their followers.', 'wordpress-seo' ), '', '' ) . '

' + . '

' . __( 'Google+', 'wordpress-seo' ) . '
' . sprintf( __( 'This tab allows you to add specific post meta data for Google+. And if you have a Google+ page for your business, add that URL here and link it on your %1$sGoogle+%2$s page\'s about page.', 'wordpress-seo' ), '', '' ) . '

', + 'next' => __( 'Next', 'wordpress-seo' ), + 'next_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_xml' ) . '";', + 'prev' => __( 'Previous', 'wordpress-seo' ), + 'prev_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_titles' ) . '";', + ), + 'wpseo_xml' => array( + 'content' => '

' . __( 'XML Sitemaps', 'wordpress-seo' ) . '

' + . '

' . __( 'What are XML sitemaps?', 'wordpress-seo' ) . '
' . __( 'A Sitemap is an XML file that lists the URLs for a site. It allows webmasters to include additional information about each URL: when it was last updated, how often it changes, and how important it is in relation to other URLs in the site. This allows search engines to crawl the site more intelligently.', 'wordpress-seo' ) . '

' + . '

' . __( 'What does the plugin do with XML Sitemaps?', 'wordpress-seo' ) . '
' . __( 'This plugin adds XML sitemaps to your site. The sitemaps are automatically updated when you publish a new post, page or custom post and Google and Bing will be automatically notified. You can also have the plugin automatically notify Yahoo! and Ask.com.', 'wordpress-seo' ) . '

' . __( 'If you want to exclude certain post types and/or taxonomies, you can also set that on this page.', 'wordpress-seo' ) . '

' . __( 'Is your webserver low on memory? Decrease the entries per sitemap (default: 1000) to reduce load.', 'wordpress-seo' ) . '

', + 'next' => __( 'Next', 'wordpress-seo' ), + 'next_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_permalinks' ) . '";', + 'prev' => __( 'Previous', 'wordpress-seo' ), + 'prev_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_social' ) . '";', + ), + 'wpseo_permalinks' => array( + 'content' => '

' . __( 'Permalink Settings', 'wordpress-seo' ) . '

' . __( 'All of the options here are for advanced users only, if you don\'t know whether you should check any, don\'t touch them.', 'wordpress-seo' ) . '

', + 'next' => __( 'Next', 'wordpress-seo' ), + 'next_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_internal-links' ) . '";', + 'prev' => __( 'Previous', 'wordpress-seo' ), + 'prev_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_xml' ) . '";', + ), + 'wpseo_internal-links' => array( + 'content' => '

' . __( 'Breadcrumbs Settings', 'wordpress-seo' ) . '

' . sprintf( __( 'If your theme supports my breadcrumbs, as all Genesis and WooThemes themes as well as a couple of other ones do, you can change the settings for those here. If you want to modify your theme to support them, %sfollow these instructions%s.', 'wordpress-seo' ), '', '' ) . '

', + 'next' => __( 'Next', 'wordpress-seo' ), + 'next_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_rss' ) . '";', + 'prev' => __( 'Previous', 'wordpress-seo' ), + 'prev_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_permalinks' ) . '";', + ), + 'wpseo_rss' => array( + 'content' => '

' . __( 'RSS Settings', 'wordpress-seo' ) . '

' . __( 'This incredibly powerful function allows you to add content to the beginning and end of your posts in your RSS feed. This helps you gain links from people who steal your content!', 'wordpress-seo' ) . '

', + 'next' => __( 'Next', 'wordpress-seo' ), + 'next_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_import' ) . '";', + 'prev' => __( 'Previous', 'wordpress-seo' ), + 'prev_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_internal-links' ) . '";', + ), + 'wpseo_import' => array( + 'content' => '

' . __( 'Import & Export', 'wordpress-seo' ) . '

' + . '

' . __( 'Import from other (SEO) plugins', 'wordpress-seo' ) . '
' . __( 'We can imagine that you switch from another SEO plugin to WordPress SEO. If you just did, you can use these options to transfer your SEO-data. If you were using one of my older plugins like Robots Meta & RSS Footer, you can import the settings here too.', 'wordpress-seo' ) . '

' + . '

' . __( 'Other imports', 'wordpress-seo' ) . '
' . sprintf( __( 'If you\'re using one of our premium plugins, such as %1$sLocal SEO%2$s, you can also find specific import-options for that plugin here.', 'wordpress-seo' ), '', '' ) . '

' + . '

' . __( 'Export', 'wordpress-seo' ) . '
' . __( 'If you have multiple blogs and you\'re happy with how you\'ve configured this blog, you can export the settings and import them on another blog so you don\'t have to go through this process twice!', 'wordpress-seo' ) . '

', + 'next' => __( 'Next', 'wordpress-seo' ), + 'next_function' => 'window.location="' . network_admin_url( 'admin.php?page=wpseo_bulk-editor' ) . '";', // will auto-use admin_url if not on multi-site + 'prev' => __( 'Previous', 'wordpress-seo' ), + 'prev_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_rss' ) . '";', + ), + 'wpseo_bulk-editor' => array( + 'content' => '

' . __( 'Bulk Editor', 'wordpress-seo' ) . '

' . __( 'This page lets you view and edit the titles and meta descriptions of all posts and pages on your site. This allows you to edit the title or meta description of all your pages in one place, rather than having to edit each individual page.', 'wordpress-seo' ) . '

', + 'next' => __( 'Next', 'wordpress-seo' ), + 'next_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_files' ) . '";', + 'prev' => __( 'Previous', 'wordpress-seo' ), + 'prev_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_import' ) . '";', + ), + 'wpseo_files' => array( + 'content' => '

' . __( 'File Editor', 'wordpress-seo' ) . '

' . __( 'Here you can edit the .htaccess and robots.txt files, two of the most powerful files in your WordPress install, if your WordPress installation has write-access to the files. But please, only touch these files if you know what you\'re doing!', 'wordpress-seo' ) . '

', + 'next' => __( 'Next', 'wordpress-seo' ), + 'next_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_licenses' ) . '";', + 'prev' => __( 'Previous', 'wordpress-seo' ), + 'prev_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_bulk-editor' ) . '";', + ), + 'wpseo_licenses' => array( + 'content' => '

' . __( 'Extensions and Licenses', 'wordpress-seo' ) . '

' + . '

' . __( 'Extensions', 'wordpress-seo' ) . '
' . sprintf( __( 'The powerful functions of WordPress SEO can be extended with %1$sYoast premium plugins%2$s. These premium plugins require the installation of WordPress SEO or WordPress SEO Premium and add specific functionality. You can read all about the Yoast Premium Plugins on %1$shttp://yoast.com/wordpress/plugins/%2$s.', 'wordpress-seo' ), '', '' ) . '

' + . '

' . __( 'Licenses', 'wordpress-seo' ) . '
' . __( 'Once you\'ve purchased WordPress SEO Premium or any other premium Yoast plugin, you\'ll have to enter a license key. You can do so on the Licenses-tab. Once you\'ve activated your premium plugin, you can use all its powerful features.', 'wordpress-seo' ) . '

' + . '

' . __( 'Like this plugin?', 'wordpress-seo' ) . '
' . sprintf( __( 'So, we\'ve come to the end of the tour. If you like the plugin, please %srate it 5 stars on WordPress.org%s!', 'wordpress-seo' ), '', '' ) . '

' + . '

' . sprintf( __( 'Thank you for using my plugin and good luck with your SEO!

Best,
Team Yoast - %1$sYoast.com%2$s', 'wordpress-seo' ), '', '' ) . '

', + 'prev' => __( 'Previous', 'wordpress-seo' ), + 'prev_function' => 'window.location="' . admin_url( 'admin.php?page=wpseo_files' ) . '";', + ), + ); + + // Skip tour about wpseo_files page if file editing is disallowed or if the site is a multisite and the current user isn't a superadmin + if ( false === wpseo_allow_system_file_edit() ) { + unset( $adminpages['wpseo_files'] ); + $adminpages['wpseo_bulk-editor']['function'] = 'window.location="' . admin_url( 'admin.php?page=wpseo_licenses' ) . '";'; + } + + $page = ''; + if ( isset( $_GET['page'] ) ) { + $page = $_GET['page']; + } + + $button_array = array( + 'button1' => array( + 'text' => __( 'Close', 'wordpress-seo' ), + 'function' => '', + ) + ); + $opt_arr = array(); + $id = '#wpseo-title'; + if ( 'admin.php' != $pagenow || ! array_key_exists( $page, $adminpages ) ) { + $id = 'li.toplevel_page_wpseo_dashboard'; + $content = '

' . __( 'Congratulations!', 'wordpress-seo' ) . '

'; + $content .= '

' . __( 'You\'ve just installed WordPress SEO by Yoast! Click "Start Tour" to view a quick introduction of this plugins core functionality.', 'wordpress-seo' ) . '

'; + $opt_arr = array( + 'content' => $content, + 'position' => array( 'edge' => 'top', 'align' => 'center' ) + ); + $button_array['button2']['text'] = __( 'Start Tour', 'wordpress-seo' ); + $button_array['button2']['function'] = 'document.location="' . admin_url( 'admin.php?page=wpseo_dashboard' ) . '";'; + } else { + if ( '' != $page && in_array( $page, array_keys( $adminpages ) ) ) { + $align = ( is_rtl() ) ? 'left' : 'right'; + $opt_arr = array( + 'content' => $adminpages[$page]['content'], + 'position' => ( isset ( $adminpages[$page]['position'] ) ) ? ( $adminpages[$page]['position'] ) : array( 'edge' => 'top', 'align' => $align ), + 'pointerWidth' => 450, + ); + if ( isset( $adminpages[$page]['next'] ) && isset( $adminpages[$page]['next_function'] ) ) { + $button_array['button2'] = array( + 'text' => $adminpages[$page]['next'], + 'function' => $adminpages[$page]['next_function'], + ); + } + if ( isset( $adminpages[$page]['prev'] ) && isset( $adminpages[$page]['prev_function'] ) ) { + $button_array['button3'] = array( + 'text' => $adminpages[$page]['prev'], + 'function' => $adminpages[$page]['prev_function'], + ); + } + } + } + + $this->print_scripts( $id, $opt_arr, $button_array ); + } + + + /** + * Prints the pointer script + * + * @param string $selector The CSS selector the pointer is attached to. + * @param array $options The options for the pointer. + * @param array $button_array The options for the buttons. + */ + function print_scripts( $selector, $options, $button_array ) { + $button_array_defaults = array( + 'button1' => array( + 'text' => false, + 'function' => '', + ), + 'button2' => array( + 'text' => false, + 'function' => '', + ), + 'button3' => array( + 'text' => false, + 'function' => '', + ), + ); + $button_array = wp_parse_args( $button_array, $button_array_defaults ); + ?> + + post_type, 'timeinfo' ); // #17455 + + $options = WPSEO_Options::get_all(); + if ( isset( $options[ 'post_types-' . $post->post_type . '-not_in_sitemap' ] ) && $options[ 'post_types-' . $post->post_type . '-not_in_sitemap' ] === true ) { + return; + } + + if ( WP_CACHE ) { + wp_schedule_single_event( time() + 300, 'wpseo_hit_sitemap_index' ); + } + + // Allow the pinging to happen slightly after the hit sitemap index so the sitemap is fully regenerated when the ping happens. + if ( WPSEO_Meta::get_value( 'sitemap-include', $post->ID ) !== 'never' ) { + if ( defined( 'YOAST_SEO_PING_IMMEDIATELY' ) && YOAST_SEO_PING_IMMEDIATELY ) { + wpseo_ping_search_engines(); + } + else { + wp_schedule_single_event( ( time() + 300 ), 'wpseo_ping_search_engines' ); + } + } + } + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/admin/class-taxonomy.php b/wp-content/plugins/wordpress-seo/admin/class-taxonomy.php new file mode 100644 index 0000000..252d5bc --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-taxonomy.php @@ -0,0 +1,285 @@ +no_index_options = WPSEO_Taxonomy_Meta::$no_index_options; + $this->sitemap_include_options = WPSEO_Taxonomy_Meta::$sitemap_include_options; + + $this->no_index_options['default'] = __( 'Use %s default (Currently: %s)', 'wordpress-seo' ); + $this->no_index_options['index'] = __( 'Always index', 'wordpress-seo' ); + $this->no_index_options['noindex'] = __( 'Always noindex', 'wordpress-seo' ); + + $this->sitemap_include_options['-'] = __( 'Auto detect', 'wordpress-seo' ); + $this->sitemap_include_options['always'] = __( 'Always include', 'wordpress-seo' ); + $this->sitemap_include_options['never'] = __( 'Never include', 'wordpress-seo' ); + } + + + + + + /** + * Test whether we are on a public taxonomy - no metabox actions needed if we are not + * Unfortunately we have to hook most everything in before the point where all taxonomies are registered and + * we know which taxonomy is being requested, so we need to use this check in nearly every hooked in function. + * + * @since 1.5.0 + */ + function tax_is_public() { + // Don't make static as taxonomies may still be added during the run + $taxonomies = get_taxonomies( array( 'public' => true ), 'names' ); + + return ( isset( $_GET['taxonomy'] ) && in_array( $_GET['taxonomy'], $taxonomies ) ); + } + + + /** + * Add our admin css file + */ + function admin_enqueue_scripts() { + global $pagenow; + + if ( $pagenow === 'edit-tags.php' && ( isset( $_GET['action'] ) && $_GET['action'] === 'edit' ) ) { + wp_enqueue_style( 'yoast-taxonomy-css', plugins_url( 'css/taxonomy-meta' . WPSEO_CSSJS_SUFFIX . '.css', WPSEO_FILE ), array(), WPSEO_VERSION ); + } + } + + + /** + * Create a row in the form table. + * + * @param string $var Variable the row controls. + * @param string $label Label for the variable. + * @param string $desc Description of the use of the variable. + * @param array $tax_meta Taxonomy meta value. + * @param string $type Type of form row to create. + * @param array $options Options to use when form row is a select box. + */ + function form_row( $var, $label, $desc, $tax_meta, $type = 'text', $options = array() ) { + $val = ''; + if ( isset( $tax_meta[ $var ] ) && $tax_meta[ $var ] !== '' ) { + $val = $tax_meta[ $var ]; + } + + $esc_var = esc_attr( $var ); + $field = ''; + + if ( $type == 'text' ) { + $field .= ' + '; + } + elseif ( $type == 'checkbox' ) { + $field .= ' + '; + } + elseif ( $type == 'select' ) { + if ( is_array( $options ) && $options !== array() ) { + $field .= ' + '; + } + } + elseif ( $type == 'hidden' ) { + $field .= ' + '; + } + + if ( $field !== '' && ( is_string( $desc ) && $desc !== '' ) ) { + $field .= ' +

' . $desc . '

'; + } + + echo ' + + ' . ( '' !== $label ? '' : '' ) . ' + ' . $field . ' + '; + } + + /** + * Show the SEO inputs for term. + * + * @param object $term Term to show the edit boxes for. + */ + function term_seo_form( $term ) { + if ( $this->tax_is_public() === false ) { + return; + } + + $tax_meta = WPSEO_Taxonomy_Meta::get_term_meta( (int) $term->term_id, $term->taxonomy ); + $options = WPSEO_Options::get_all(); + + + echo '

' . __( 'Yoast WordPress SEO Settings', 'wordpress-seo' ) . '

'; + echo ''; + + $this->form_row( 'wpseo_title', __( 'SEO Title', 'wordpress-seo' ), esc_html__( 'The SEO title is used on the archive page for this term.', 'wordpress-seo' ), $tax_meta ); + $this->form_row( 'wpseo_desc', __( 'SEO Description', 'wordpress-seo' ), esc_html__( 'The SEO description is used for the meta description on the archive page for this term.', 'wordpress-seo' ), $tax_meta ); + + if ( $options['usemetakeywords'] === true ) { + $this->form_row( 'wpseo_metakey', __( 'Meta Keywords', 'wordpress-seo' ), esc_html__( 'Meta keywords used on the archive page for this term.', 'wordpress-seo' ), $tax_meta ); + } + + $this->form_row( 'wpseo_canonical', __( 'Canonical', 'wordpress-seo' ), esc_html__( 'The canonical link is shown on the archive page for this term.', 'wordpress-seo' ), $tax_meta ); + + if ( $options['breadcrumbs-enable'] === true ) { + $this->form_row( 'wpseo_bctitle', __( 'Breadcrumbs Title', 'wordpress-seo' ), sprintf( esc_html__( 'The Breadcrumbs title is used in the breadcrumbs where this %s appears.', 'wordpress-seo' ), $term->taxonomy ), $tax_meta ); + } + + $current = 'index'; + if ( isset( $options[ 'noindex-tax-' . $term->taxonomy ] ) && $options[ 'noindex-tax-' . $term->taxonomy ] === true ) { + $current = 'noindex'; + } + + $noindex_options = $this->no_index_options; + $noindex_options['default'] = sprintf( $noindex_options['default'], $term->taxonomy, $current ); + + $desc = sprintf( esc_html__( 'This %s follows the indexation rules set under Metas and Titles, you can override it here.', 'wordpress-seo' ), $term->taxonomy ); + if ( '0' == get_option( 'blog_public' ) ) { + $desc .= '
' . esc_html__( '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' ) . ''; + } + + $this->form_row( 'wpseo_noindex', sprintf( __( 'Noindex this %s', 'wordpress-seo' ), $term->taxonomy ), $desc, $tax_meta, 'select', $noindex_options ); + unset( $current, $no_index_options, $desc ); + + + $this->form_row( 'wpseo_sitemap_include', __( 'Include in sitemap?', 'wordpress-seo' ), '', $tax_meta, 'select', $this->sitemap_include_options ); + + echo '
'; + } + + /** + * Update the taxonomy meta data on save. + * + * @param int $term_id ID of the term to save data for + * @param int $tt_id The taxonomy_term_id for the term. + * @param string $taxonomy The taxonomy the term belongs to. + */ + function update_term( $term_id, $tt_id, $taxonomy ) { + $tax_meta = get_option( 'wpseo_taxonomy_meta' ); + + /* Create post array with only our values */ + $new_meta_data = array(); + foreach ( WPSEO_Taxonomy_Meta::$defaults_per_term as $key => $default ) { + if ( isset( $_POST[ $key ] ) ) { + $new_meta_data[ $key ] = $_POST[ $key ]; + } + } + + /* Validate the post values */ + $old = WPSEO_Taxonomy_Meta::get_term_meta( $term_id, $taxonomy ); + $clean = WPSEO_Taxonomy_Meta::validate_term_meta_data( $new_meta_data, $old ); + + /* Add/remove the result to/from the original option value */ + if ( $clean !== array() ) { + $tax_meta[ $taxonomy ][ $term_id ] = $clean; + } + else { + unset( $tax_meta[ $taxonomy ][ $term_id ] ); + if ( isset( $tax_meta[ $taxonomy ] ) && $tax_meta[ $taxonomy ] === array() ) { + unset( $tax_meta[ $taxonomy ] ); + } + } + + // Prevent complete array validation + $tax_meta['wpseo_already_validated'] = true; + + update_option( 'wpseo_taxonomy_meta', $tax_meta ); + } + + + /** + * Allows HTML in descriptions + */ + function custom_category_descriptions_allow_html() { + $filters = array( + 'pre_term_description', + 'pre_link_description', + 'pre_link_notes', + 'pre_user_description', + ); + + foreach ( $filters as $filter ) { + remove_filter( $filter, 'wp_filter_kses' ); + } + remove_filter( 'term_description', 'wp_kses_data' ); + } + + /** + * Adds shortcode support to category descriptions. + * + * @param string $desc String to add shortcodes in. + * @return string + */ + function custom_category_descriptions_add_shortcode_support( $desc ) { + // Wrap in output buffering to prevent shortcodes that echo stuff instead of return from breaking things. + ob_start(); + $desc = do_shortcode( $desc ); + ob_end_clean(); + + return $desc; + } + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/admin/class-tracking.php b/wp-content/plugins/wordpress-seo/admin/class-tracking.php new file mode 100644 index 0000000..219bd30 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-tracking.php @@ -0,0 +1,239 @@ + testers] check if tracking still works if an old version of the Yoast Tracking class was loaded + * (i.e. another plugin loaded their version first) + */ + class Yoast_Tracking { + + /** + * @var object Instance of this class + */ + public static $instance; + + + /** + * Class constructor + */ + function __construct() { + // Constructor is called from WP SEO + if ( current_filter( 'yoast_tracking' ) ) { + $this->tracking(); + } // Backward compatibility - constructor is called from other Yoast plugin + elseif ( ! has_action( 'yoast_tracking', array( $this, 'tracking' ) ) ) { + add_action( 'yoast_tracking', array( $this, 'tracking' ) ); + } + } + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Main tracking function. + */ + function tracking() { + + $transient_key = 'yoast_tracking_cache'; + $data = get_transient( $transient_key ); + + // bail if transient is set and valid + if ( $data !== false ) { + return; + } + + // Make sure to only send tracking data once a week + set_transient( $transient_key, 1, WEEK_IN_SECONDS ); + + // Start of Metrics + global $blog_id, $wpdb; + + $hash = get_option( 'Yoast_Tracking_Hash', false ); + + if ( ! $hash || empty( $hash ) ) { + // create and store hash + $hash = md5( site_url() ); + update_option( 'Yoast_Tracking_Hash', $hash ); + } + + $pts = array(); + $post_types = get_post_types( array( 'public' => true ) ); + if ( is_array( $post_types ) && $post_types !== array() ) { + foreach ( $post_types as $post_type ) { + $count = wp_count_posts( $post_type ); + $pts[ $post_type ] = $count->publish; + } + } + unset( $post_types ); + + $comments_count = wp_count_comments(); + + $theme_data = wp_get_theme(); + $theme = array( + 'name' => $theme_data->display( 'Name', false, false ), + 'theme_uri' => $theme_data->display( 'ThemeURI', false, false ), + 'version' => $theme_data->display( 'Version', false, false ), + 'author' => $theme_data->display( 'Author', false, false ), + 'author_uri' => $theme_data->display( 'AuthorURI', false, false ), + ); + $theme_template = $theme_data->get_template(); + if ( $theme_template !== '' && $theme_data->parent() ) { + $theme['template'] = array( + 'version' => $theme_data->parent()->display( 'Version', false, false ), + 'name' => $theme_data->parent()->display( 'Name', false, false ), + 'theme_uri' => $theme_data->parent()->display( 'ThemeURI', false, false ), + 'author' => $theme_data->parent()->display( 'Author', false, false ), + 'author_uri' => $theme_data->parent()->display( 'AuthorURI', false, false ), + ); + } else { + $theme['template'] = ''; + } + unset( $theme_template ); + + + $plugins = array(); + $active_plugin = get_option( 'active_plugins' ); + foreach ( $active_plugin as $plugin_path ) { + if ( ! function_exists( 'get_plugin_data' ) ) { + require_once( ABSPATH . 'wp-admin/includes/plugin.php' ); + } + + $plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_path ); + + $slug = str_replace( '/' . basename( $plugin_path ), '', $plugin_path ); + $plugins[ $slug ] = array( + 'version' => $plugin_info['Version'], + 'name' => $plugin_info['Name'], + 'plugin_uri' => $plugin_info['PluginURI'], + 'author' => $plugin_info['AuthorName'], + 'author_uri' => $plugin_info['AuthorURI'], + ); + } + unset( $active_plugins, $plugin_path ); + + + $data = array( + 'site' => array( + 'hash' => $hash, + 'version' => get_bloginfo( 'version' ), + 'multisite' => is_multisite(), + 'users' => $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->users INNER JOIN $wpdb->usermeta ON ({$wpdb->users}.ID = {$wpdb->usermeta}.user_id) WHERE 1 = 1 AND ( {$wpdb->usermeta}.meta_key = %s )", 'wp_' . $blog_id . '_capabilities' ) ), + 'lang' => get_locale(), + ), + 'pts' => $pts, + 'comments' => array( + 'total' => $comments_count->total_comments, + 'approved' => $comments_count->approved, + 'spam' => $comments_count->spam, + 'pings' => $wpdb->get_var( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_type = 'pingback'" ), + ), + 'options' => apply_filters( 'yoast_tracking_filters', array() ), + 'theme' => $theme, + 'plugins' => $plugins, + ); + + $args = array( + 'body' => $data, + 'blocking' => false, + 'sslverify' => false, + ); + + wp_remote_post( 'https://tracking.yoast.com/', $args ); + + } + } /* End of class */ +} /* End of class-exists wrapper */ + +/** + * Adds tracking parameters for WP SEO settings. Outside of the main class as the class could also be in use in other plugins. + * + * @param array $options + * + * @return array + */ +function wpseo_tracking_additions( $options ) { + if ( function_exists( 'curl_version' ) ) { + $curl = curl_version(); + } else { + $curl = null; + } + + + $opt = WPSEO_Options::get_all(); + + $options['wpseo'] = array( + 'xml_sitemaps' => ( $opt['enablexmlsitemap'] === true ) ? 1 : 0, + 'force_rewrite' => ( $opt['forcerewritetitle'] === true ) ? 1 : 0, + 'opengraph' => ( $opt['opengraph'] === true ) ? 1 : 0, + 'twitter' => ( $opt['twitter'] === true ) ? 1 : 0, + 'strip_category_base' => ( $opt['stripcategorybase'] === true ) ? 1 : 0, + 'on_front' => get_option( 'show_on_front' ), + 'wmt_alexa' => ( ! empty( $opt['alexaverify'] ) ) ? 1 : 0, + 'wmt_bing' => ( ! empty( $opt['msverify'] ) ) ? 1 : 0, + 'wmt_google' => ( ! empty( $opt['googleverify'] ) ) ? 1 : 0, + 'wmt_pinterest' => ( ! empty( $opt['pinterestverify'] ) ) ? 1 : 0, + 'wmt_yandex' => ( ! empty( $opt['yandexverify'] ) ) ? 1 : 0, + 'permalinks_clean' => ( $opt['cleanpermalinks'] == 1 ) ? 1 : 0, + + 'site_db_charset' => DB_CHARSET, + + 'webserver_apache' => wpseo_is_apache() ? 1 : 0, + 'webserver_apache_version' => function_exists( 'apache_get_version' ) ? apache_get_version() : 0, + 'webserver_nginx' => wpseo_is_nginx() ? 1 : 0, + + 'webserver_server_software' => $_SERVER['SERVER_SOFTWARE'], + 'webserver_gateway_interface' => $_SERVER['GATEWAY_INTERFACE'], + 'webserver_server_protocol' => $_SERVER['SERVER_PROTOCOL'], + + 'php_version' => phpversion(), + + 'php_max_execution_time' => ini_get( 'max_execution_time' ), + 'php_memory_limit' => ini_get( 'memory_limit' ), + 'php_open_basedir' => ini_get( 'open_basedir' ), + + 'php_bcmath_enabled' => extension_loaded( 'bcmath' ) ? 1 : 0, + 'php_ctype_enabled' => extension_loaded( 'ctype' ) ? 1 : 0, + 'php_curl_enabled' => extension_loaded( 'curl' ) ? 1 : 0, + 'php_curl_version_a' => phpversion( 'curl' ), + 'php_curl' => ( ! is_null( $curl ) ) ? $curl['version'] : 0, + 'php_dom_enabled' => extension_loaded( 'dom' ) ? 1 : 0, + 'php_dom_version' => phpversion( 'dom' ), + 'php_filter_enabled' => extension_loaded( 'filter' ) ? 1 : 0, + 'php_mbstring_enabled' => extension_loaded( 'mbstring' ) ? 1 : 0, + 'php_mbstring_version' => phpversion( 'mbstring' ), + 'php_pcre_enabled' => extension_loaded( 'pcre' ) ? 1 : 0, + 'php_pcre_version' => phpversion( 'pcre' ), + 'php_pcre_with_utf8_a' => @preg_match( '/^.{1}$/u', 'ñ', $UTF8_ar ), + 'php_pcre_with_utf8_b' => defined( 'PREG_BAD_UTF8_ERROR' ), + 'php_spl_enabled' => extension_loaded( 'spl' ) ? 1 : 0, + ); + + return $options; +} + +add_filter( 'yoast_tracking_filters', 'wpseo_tracking_additions' ); diff --git a/wp-content/plugins/wordpress-seo/admin/class-yoast-notification-center.php b/wp-content/plugins/wordpress-seo/admin/class-yoast-notification-center.php new file mode 100644 index 0000000..23ea068 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-yoast-notification-center.php @@ -0,0 +1,152 @@ +notifications = $this->get_notifications_from_transient(); + + // Clear the cookie + if ( count( $this->notifications ) > 0 ) { + $this->remove_transient(); + } + + // Display the notifications in all_admin_notices + add_action( 'all_admin_notices', array( $this, 'display_notifications' ) ); + + // Write the cookie on shutdown + add_action( 'shutdown', array( $this, 'set_transient' ) ); + + // AJAX + add_action( 'wp_ajax_yoast_get_notifications', array( $this, 'ajax_get_notifications' ) ); + } + + /** + * Singleton getter + * + * @return Yoast_Notification_Center + */ + public static function get() { + + if ( null == self::$instance ) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Get the notifications from cookie + * + * @return array + */ + private function get_notifications_from_transient() { + + // The notifications array + $notifications = array(); + + $transient_notifications = get_transient( self::TRANSIENT_KEY ); + + // Check if cookie is set + if ( false !== $transient_notifications ) { + + // Get json notifications from cookie + $json_notifications = json_decode( $transient_notifications, true ); + + // Create Yoast_Notification objects + if ( count( $json_notifications ) > 0 ) { + foreach ( $json_notifications as $json_notification ) { + $notifications[] = new Yoast_Notification( $json_notification['message'], $json_notification['type'] ); + } + } + } + + return $notifications; + } + + /** + * Clear the cookie + */ + private function remove_transient() { + delete_transient( self::TRANSIENT_KEY ); + } + + /** + * Clear local stored notifications + */ + private function clear_notifications() { + $this->notifications = array(); + } + + /** + * Write the notifications to cookie + */ + public function set_transient() { + + // Count local stored notifications + if ( count( $this->notifications ) > 0 ) { + + // Create array with all notifications + $arr_notifications = array(); + + // Add each notification as array to $arr_notifications + foreach ( $this->notifications as $notification ) { + $arr_notifications[] = $notification->to_array(); + } + + // Set the cookie with notifications + set_transient( self::TRANSIENT_KEY, json_encode( $arr_notifications ), MINUTE_IN_SECONDS * 10 ); + + } + + } + + /** + * Add notification to the cookie + * + * @param Yoast_Notification $notification + */ + public function add_notification( Yoast_Notification $notification ) { + $this->notifications[] = $notification; + } + + /** + * Display the notifications + */ + public function display_notifications() { + + // Display notifications + if ( count( $this->notifications ) > 0 ) { + foreach ( $this->notifications as $notification ) { + $notification->output(); + } + } + + // Clear the local stored notifications + $this->clear_notifications(); + + } + + /** + * AJAX display notifications + */ + public function ajax_get_notifications() { + + // Display the notices + $this->display_notifications(); + + // AJAX die + exit; + } + +} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/class-yoast-notification.php b/wp-content/plugins/wordpress-seo/admin/class-yoast-notification.php new file mode 100644 index 0000000..6f93e93 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/class-yoast-notification.php @@ -0,0 +1,66 @@ +message = $message; + $this->type = $type; + } + + /** + * @return String + */ + public function get_message() { + return $this->message; + } + + /** + * @param String $message + */ + public function set_message( $message ) { + $this->message = $message; + } + + /** + * @return String + */ + public function get_type() { + return $this->type; + } + + /** + * @param String $type + */ + public function set_type( $type ) { + $this->type = $type; + } + + /** + * Return the object properties as an array + * + * @return array + */ + public function to_array() { + return array( + 'message' => $this->get_message(), + 'type' => $this->get_type() + ); + } + + /** + * Output the message + */ + public function output() { + echo '
' . wpautop( $this->get_message() ) . '
' . PHP_EOL; + } + +} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/index.php b/wp-content/plugins/wordpress-seo/admin/index.php new file mode 100644 index 0000000..12c197f --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/index.php @@ -0,0 +1,2 @@ + 'GET', + 'timeout' => 10, + 'sslverify' => false, + 'headers' => array( + 'Accept-Encoding' => '*', + 'X-Yoast-EDD' => '1' + ) + ); + + /** + * @var boolean + */ + private $success = false; + + /** + * @var mixed + */ + private $response; + + /** + * @var string + */ + private $error_message = ''; + + /** + * Constructor + * + * @param string url + * @param array $args + */ + public function __construct( $url, array $args = array() ) { + + // set api url + $this->url = $url; + + // set request args (merge with defaults) + $this->args = wp_parse_args( $args, $this->args ); + + // fire the request + $this->success = $this->fire(); + } + + /** + * Fires the request, automatically called from constructor + * + * @return boolean + */ + private function fire() { + + // fire request to shop + $response = wp_remote_request( $this->url, $this->args ); + + // validate raw response + if( $this->validate_raw_response( $response ) === false ) { + return false; + } + + // decode the response + $this->response = json_decode( wp_remote_retrieve_body( $response ) ); + + // response should be an object + if( ! is_object( $this->response ) ) { + $this->error_message = 'No JSON object was returned.'; + return false; + } + + return true; + } + + /** + * @param object $response + * @return boolean + */ + private function validate_raw_response( $response ) { + + // make sure response came back okay + if( is_wp_error( $response ) ) { + $this->error_message = $response->get_error_message(); + return false; + } + + // check response code, should be 200 + $response_code = wp_remote_retrieve_response_code( $response ); + + if( false === strstr( $response_code, '200' ) ) { + + $response_message = wp_remote_retrieve_response_message( $response ); + $this->error_message = "{$response_code} {$response_message}"; + + return false; + } + + return true; + } + + /** + * Was a valid response returned? + * + * @return boolean + */ + public function is_valid() { + return ( $this->success === true ); + } + + /** + * @return string + */ + public function get_error_message() { + return $this->error_message; + } + + /** + * @return object + */ + public function get_response() { + return $this->response; + } + + } + +} + diff --git a/wp-content/plugins/wordpress-seo/admin/license-manager/class-license-manager.php b/wp-content/plugins/wordpress-seo/admin/license-manager/class-license-manager.php new file mode 100644 index 0000000..31cc407 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/license-manager/class-license-manager.php @@ -0,0 +1,568 @@ +product = $product; + + // set prefix + $this->prefix = sanitize_title_with_dashes( $this->product->get_item_name() . '_', null, 'save' ); + + // maybe set license key from constant + $this->maybe_set_license_key_from_constant(); + } + + /** + * Setup hooks + * + */ + public function setup_hooks() { + + // show admin notice if license is not active + add_action( 'admin_notices', array( $this, 'display_admin_notices' ) ); + + // catch POST requests from license form + add_action( 'admin_init', array( $this, 'catch_post_request') ); + + // setup item type (plugin|theme) specific hooks + $this->specific_hooks(); + + // setup the auto updater + $this->setup_auto_updater(); + + } + + /** + * Display license specific admin notices, namely: + * + * - License for the product isn't activated + * - External requests are blocked through WP_HTTP_BLOCK_EXTERNAL + */ + public function display_admin_notices() { + + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + // show notice if license is invalid + if( ! $this->license_is_valid() ) { + if( $this->get_license_key() == '' ) { + $message = 'Warning! You didn\'t set your %s license key yet, which means you\'re missing out on updates and support! Enter your license key or get a license here.'; + } else { + $message = 'Warning! Your %s license is inactive which means you\'re missing out on updates and support! Activate your license or get a license here.'; + } + ?> +
+

product->get_text_domain() ), $this->product->get_item_name(), $this->product->get_license_page_url(), $this->product->get_tracking_url( 'activate-license-notice' ) ); ?>

+
+ product->get_api_url(), PHP_URL_HOST ); + + if( ! defined( "WP_ACCESSIBLE_HOSTS" ) || stristr( WP_ACCESSIBLE_HOSTS, $host ) === false ) { + ?> +
+

Warning! You\'re blocking external requests which means you won\'t be able to get %s updates. Please add %s to %s.', $this->product->get_text_domain() ), $this->product->get_item_name(), '' . $host . '', 'WP_ACCESSIBLE_HOSTS'); ?>

+
+ prefix . 'license', 'license-notice', $message, $css_class ); + } + + /** + * Remotely activate License + * @return boolean True if the license is now activated, false if not + */ + public function activate_license() { + + $result = $this->call_license_api( 'activate' ); + + + if( $result ) { + + // story expiry date + if( isset( $result->expires ) ) { + $this->set_license_expiry_date( $result->expires ); + $expiry_date = strtotime( $result->expires ); + } else { + $expiry_date = false; + } + + // show success notice if license is valid + if($result->license === 'valid') { + + // show a custom notice if users have an unlimited license + if( $result->license_limit == 0 ) { + $message = sprintf( __( "Your %s license has been activated. You have an unlimited license. ", $this->product->get_text_domain() ), $this->product->get_item_name() ); + } else { + $message = sprintf( __( "Your %s license has been activated. You have used %d/%d activations. ", $this->product->get_text_domain() ), $this->product->get_item_name(), $result->site_count, $result->license_limit ); + } + + // add upgrade notice if user has less than 3 activations left + if( $result->license_limit > 0 && ( $result->license_limit - $result->site_count ) <= 3 ) { + $message .= sprintf( __( 'Did you know you can upgrade your license?', $this->product->get_text_domain() ), $this->product->get_tracking_url( 'license-nearing-limit-notice' ) ); + // add extend notice if license is expiring in less than 1 month + } elseif( $expiry_date !== false && $expiry_date < strtotime( "+1 month" ) ) { + $days_left = round( ( $expiry_date - strtotime( "now" ) ) / 86400 ); + $message .= sprintf( __( 'Your license is expiring in %d days, would you like to extend it?', $this->product->get_text_domain() ), $this->product->get_tracking_url( 'license-expiring-notice' ), $days_left ); + } + + $this->set_notice( $message, true ); + + } else { + + if( isset($result->error) && $result->error === 'no_activations_left' ) { + // show notice if user is at their activation limit + $this->set_notice( sprintf( __('You\'ve reached your activation limit. You must upgrade your license to use it on this site.', $this->product->get_text_domain() ), $this->product->get_tracking_url( 'license-at-limit-notice' ) ), false ); + } elseif( isset($result->error) && $result->error == "expired" ) { + // show notice if the license is expired + $this->set_notice( sprintf( __('Your license has expired. You must extend your license in order to use it again.', $this->product->get_text_domain() ), $this->product->get_tracking_url( 'license-expired-notice' ) ), false ); + } else { + // show a general notice if it's any other error + $this->set_notice( __( "Failed to activate your license, your license key seems to be invalid.", $this->product->get_text_domain() ), false ); + } + + $this->remote_license_activation_failed = true; + } + + $this->set_license_status( $result->license ); + } + + return ( $this->license_is_valid() ); + } + + /** + * Remotely deactivate License + * @return boolean True if the license is now deactivated, false if not + */ + public function deactivate_license () { + + $result = $this->call_license_api( 'deactivate' ); + + if( $result ) { + + // show notice if license is deactivated + if( $result->license === 'deactivated' ) { + $this->set_notice( sprintf( __( "Your %s license has been deactivated.", $this->product->get_text_domain() ), $this->product->get_item_name() ) ); + } else { + $this->set_notice( sprintf( __( "Failed to deactivate your %s license.", $this->product->get_text_domain() ), $this->product->get_item_name() ), false ); + } + + $this->set_license_status( $result->license ); + } + + return ( $this->get_license_status() === 'deactivated' ); + } + + /** + * @param string $action activate|deactivate + * @return mixed + */ + protected function call_license_api( $action ) { + + // don't make a request if license key is empty + if( $this->get_license_key() === '' ) { + return false; + } + + // data to send in our API request + $api_params = array( + 'edd_action' => $action . '_license', + 'license' => $this->get_license_key(), + 'item_name' => urlencode( trim( $this->product->get_item_name() ) ), + 'url' => get_option( 'home' ) // grab the URL straight from the option to prevent filters from breaking it. + ); + + // create api request url + $url = add_query_arg( $api_params, $this->product->get_api_url() ); + + require_once dirname( __FILE__ ) . '/class-api-request.php'; + $request = new Yoast_API_Request( $url ); + + if( $request->is_valid() !== true ) { + $this->set_notice( sprintf( __( "Request error: \"%s\" (%scommon license notices%s)", $this->product->get_text_domain() ), $request->get_error_message(), '', '' ), false ); + } + + // get response + $response = $request->get_response(); + + // update license status + $license_data = $response; + + return $license_data; + } + + + + /** + * Set the license status + * + * @param string $license_status + */ + public function set_license_status( $license_status ) { + $this->set_option( 'status', $license_status ); + } + + /** + * Get the license status + * + * @return string $license_status; + */ + public function get_license_status() { + $license_status = $this->get_option( 'status' ); + return trim( $license_status ); + } + + /** + * Set the license key + * + * @param string $license_key + */ + public function set_license_key( $license_key ) { + $this->set_option( 'key', $license_key ); + } + + /** + * Gets the license key from constant or option + * + * @return string $license_key + */ + public function get_license_key() { + $license_key = $this->get_option( 'key' ); + return trim( $license_key ); + } + + /** + * Gets the license expiry date + * + * @return string + */ + public function get_license_expiry_date() { + return $this->get_option( 'expiry_date'); + } + + /** + * Stores the license expiry date + */ + public function set_license_expiry_date( $expiry_date ) { + $this->set_option( 'expiry_date', $expiry_date ); + } + + /** + * Checks whether the license status is active + * + * @return boolean True if license is active + */ + public function license_is_valid() { + return ( $this->get_license_status() === 'valid' ); + } + + /** + * Get all license related options + * + * @return array Array of license options + */ + protected function get_options() { + + // create option name + $option_name = $this->prefix . 'license'; + + // get array of options from db + if( $this->is_network_activated ) { + $options = get_site_option( $option_name, array( ) ); + } else { + $options = get_option( $option_name, array( ) ); + } + + // setup array of defaults + $defaults = array( + 'key' => '', + 'status' => '', + 'expiry_date' => '' + ); + + // merge options with defaults + $this->options = wp_parse_args( $options, $defaults ); + + return $this->options; + } + + /** + * Set license related options + * + * @param array $options Array of new license options + */ + protected function set_options( array $options ) { + // create option name + $option_name = $this->prefix . 'license'; + + // update db + if( $this->is_network_activated ) { + update_site_option( $option_name, $options ); + } else { + update_option( $option_name, $options ); + } + + } + + /** + * Gets a license related option + * + * @param string $name The option name + * @return mixed The option value + */ + protected function get_option( $name ) { + $options = $this->get_options(); + return $options[ $name ]; + } + + /** + * Set a license related option + * + * @param string $name The option name + * @param mixed $value The option value + */ + protected function set_option( $name, $value ) { + // get options + $options = $this->get_options(); + + // update option + $options[ $name ] = $value; + + // save options + $this->set_options( $options ); + } + + public function show_license_form_heading() { + ?> +

+ product->get_text_domain() ), $this->product->get_item_name() ); ?>    +

+ prefix . 'license_key'; + $nonce_name = $this->prefix . 'license_nonce'; + $action_name = $this->prefix . 'license_action'; + + + $visible_license_key = $this->get_license_key(); + + // obfuscate license key + $obfuscate = ( strlen( $this->get_license_key() ) > 5 && ( $this->license_is_valid() || ! $this->remote_license_activation_failed ) ); + + if($obfuscate) { + $visible_license_key = str_repeat('*', strlen( $this->get_license_key() ) - 4) . substr( $this->get_license_key(), -4 ); + } + + // make license key readonly when license key is valid or license is defined with a constant + $readonly = ( $this->license_is_valid() || $this->license_constant_is_defined ); + + require dirname( __FILE__ ) . '/views/form.php'; + + // enqueue script in the footer + add_action( 'admin_footer', array( $this, 'output_script'), 99 ); + } + + /** + * Check if the license form has been submitted + */ + public function catch_post_request() { + + $name = $this->prefix . 'license_key'; + + // check if license key was posted and not empty + if( ! isset( $_POST[$name] ) ) { + return; + } + + // run a quick security check + $nonce_name = $this->prefix . 'license_nonce'; + + if ( ! check_admin_referer( $nonce_name, $nonce_name ) ) { + return; + } + + // @TODO: check for user cap? + + // get key from posted value + $license_key = $_POST[$name]; + + // check if license key doesn't accidentally contain asterisks + if( strstr($license_key, '*') === false ) { + + // sanitize key + $license_key = trim( sanitize_key( $_POST[$name] ) ); + + // save license key + $this->set_license_key( $license_key ); + } + + // does user have an activated valid license + if( ! $this->license_is_valid() ) { + + // try to auto-activate license + return $this->activate_license(); + + } + + $action_name = $this->prefix . 'license_action'; + + // was one of the action buttons clicked? + if( isset( $_POST[ $action_name ] ) ) { + + $action = trim( $_POST[ $action_name ] ); + + switch($action) { + + case 'activate': + return $this->activate_license(); + break; + + case 'deactivate': + return $this->deactivate_license(); + break; + } + + } + + } + + /** + * Output the script containing the YoastLicenseManager JS Object + * + * This takes care of disabling the 'activate' and 'deactivate' buttons + */ + public function output_script() { + require_once dirname( __FILE__ ) . '/views/script.php'; + } + + /** + * Set the constant used to define the license + * + * @param string $license_constant_name The license constant name + */ + public function set_license_constant_name( $license_constant_name ) { + $this->license_constant_name = trim( $license_constant_name ); + $this->maybe_set_license_key_from_constant(); + } + + /** + * Maybe set license key from a defined constant + */ + private function maybe_set_license_key_from_constant( ) { + + if( empty( $this->license_constant_name ) ) { + // generate license constant name + $this->set_license_constant_name( strtoupper( str_replace( array(' ', '-' ), '', sanitize_key( $this->product->get_item_name() ) ) ) . '_LICENSE'); + } + + // set license key from constant + if( defined( $this->license_constant_name ) ) { + + $license_constant_value = constant( $this->license_constant_name ); + + // update license key value with value of constant + if( $this->get_license_key() !== $license_constant_value ) { + $this->set_license_key( $license_constant_value ); + } + + $this->license_constant_is_defined = true; + } + } + + + } + +} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/license-manager/class-plugin-license-manager.php b/wp-content/plugins/wordpress-seo/admin/license-manager/class-plugin-license-manager.php new file mode 100644 index 0000000..059d851 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/license-manager/class-plugin-license-manager.php @@ -0,0 +1,90 @@ +is_network_activated = is_plugin_active_for_network( $product->get_slug() ); + } + } + + /** + * Setup auto updater for plugins + */ + public function setup_auto_updater() { + if ( $this->license_is_valid() ) { + // setup auto updater + require_once( dirname( __FILE__ ) . '/class-update-manager.php' ); + require_once( dirname( __FILE__ ) . '/class-plugin-update-manager.php' ); + new Yoast_Plugin_Update_Manager( $this->product, $this ); + } + } + + /** + * Setup hooks + */ + public function specific_hooks() { + + // deactivate the license remotely on plugin deactivation + register_deactivation_hook( $this->product->get_slug(), array( $this, 'deactivate_license' ) ); + } + + /** + * Show a form where users can enter their license key + * Takes Multisites into account + * + * @param bool $embedded + * @return null + */ + public function show_license_form( $embedded = true ) { + + // For non-multisites, always show the license form + if( ! is_multisite() ) { + parent::show_license_form( $embedded ); + return; + } + + // Plugin is network activated + if( $this->is_network_activated ) { + + // We're on the network admin + if( is_network_admin() ) { + parent::show_license_form( $embedded ); + } else { + // We're not in the network admin area, show a notice + parent::show_license_form_heading(); + if ( is_super_admin() ) { + echo "

" . sprintf( __( '%s is network activated, you can manage your license in the network admin license page.', $this->product->get_text_domain() ), $this->product->get_item_name(), $this->product->get_license_page_url() ) . "

"; + } else { + echo "

" . sprintf( __( '%s is network activated, please contact your site administrator to manage the license.', $this->product->get_text_domain() ), $this->product->get_item_name() ) . "

"; + } + + } + + } else { + + if( false == is_network_admin() ) { + parent::show_license_form( $embedded ); + } + + } + } + } +} + diff --git a/wp-content/plugins/wordpress-seo/admin/license-manager/class-plugin-update-manager.php b/wp-content/plugins/wordpress-seo/admin/license-manager/class-plugin-update-manager.php new file mode 100644 index 0000000..91ea40b --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/license-manager/class-plugin-update-manager.php @@ -0,0 +1,102 @@ +setup_hooks(); + + } + + /** + * Setup hooks + */ + private function setup_hooks() { + + // check for updates + add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'set_updates_available_data' ) ); + + // get correct plugin information (when viewing details) + add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 ); + } + + /** + * Check for updates and if so, add to "updates available" data + * + * @param object $data + * @return object $data + */ + public function set_updates_available_data( $data ) { + + if ( empty( $data ) ) { + return $data; + } + + // send of API request to check for updates + $remote_data = $this->get_remote_data(); + + // did we get a response? + if( $remote_data === false ) { + return $data; + } + + // compare local version with remote version + if ( version_compare( $this->product->get_version(), $remote_data->new_version, '<' ) ) { + + // remote version is newer, add to data + $data->response[ $this->product->get_slug() ] = $remote_data; + + } + + return $data; + } + + /** + * Gets new plugin version details (view version x.x.x details) + * + * @uses api_request() + * + * @param object $data + * @param string $action + * @param object $args (optional) + * + * @return object $data + */ + public function plugins_api_filter( $data, $action = '', $args = null ) { + + // only do something if we're checking for our plugin + if ( $action !== 'plugin_information' || ! isset( $args->slug ) || $args->slug !== $this->product->get_slug() ) { + return $data; + } + + $api_response = $this->get_remote_data(); + + // did we get a response? + if ( $api_response === false ) { + return $data; + } + + // return api response + return $api_response; + } + + } + +} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/license-manager/class-product.php b/wp-content/plugins/wordpress-seo/admin/license-manager/class-product.php new file mode 100644 index 0000000..c16b47d --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/license-manager/class-product.php @@ -0,0 +1,228 @@ +api_url = $api_url; + $this->item_name = $item_name; + $this->slug = $slug; + $this->version = $version; + $this->item_url = $item_url; + $this->license_page_url = admin_url( $license_page_url ); + $this->text_domain = $text_domain; + $this->author = $author; + + // Fix possible empty item url + if ( $this->item_url === '' ) { + $this->item_url = $this->api_url; + } + + if( is_admin() && is_multisite() ) { + + if ( ! function_exists( 'is_plugin_active_for_network' ) ) { + require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); + } + + if( is_plugin_active_for_network( $slug ) ) { + $this->license_page_url = network_admin_url( $license_page_url ); + } + } + } + + + /** + * @param string $api_url + */ + public function set_api_url( $api_url ) { + $this->api_url = $api_url; + } + + /** + * @return string + */ + public function get_api_url() { + return $this->api_url; + } + + /** + * @param string $author + */ + public function set_author( $author ) { + $this->author = $author; + } + + /** + * @return string + */ + public function get_author() { + return $this->author; + } + + /** + * @param string $item_name + */ + public function set_item_name( $item_name ) { + $this->item_name = $item_name; + } + + /** + * @return string + */ + public function get_item_name() { + return $this->item_name; + } + + /** + * @param string $item_url + */ + public function set_item_url( $item_url ) { + $this->item_url = $item_url; + } + + /** + * @return string + */ + public function get_item_url() { + return $this->item_url; + } + + /** + * @param string $license_page_url + */ + public function set_license_page_url( $license_page_url ) { + $this->license_page_url = admin_page( $license_page_url ); + } + + /** + * @return string + */ + public function get_license_page_url() { + return $this->license_page_url; + } + + /** + * @param string $slug + */ + public function set_slug( $slug ) { + $this->slug = $slug; + } + + /** + * @return string + */ + public function get_slug() { + return $this->slug; + } + + /** + * Returns the dirname of the slug and limits it to 15 chars + * + * @return string + */ + public function get_transient_prefix() { + return substr( dirname( $this->slug ), 0, 15 ); + } + + /** + * @param string $text_domain + */ + public function set_text_domain( $text_domain ) { + $this->text_domain = $text_domain; + } + + /** + * @return string + */ + public function get_text_domain() { + return $this->text_domain; + } + + /** + * @param string $version + */ + public function set_version( $version ) { + $this->version = $version; + } + + /** + * @return string + */ + public function get_version() { + return $this->version; + } + + /** + * Gets a Google Analytics Campaign url for this product + * + * @param string $link_identifier + * @return string The full URL + */ + public function get_tracking_url( $link_identifier = '' ) { + + $tracking_vars = array( + 'utm_campaign' => $this->get_item_name() . ' licensing', + 'utm_medium' => 'link', + 'utm_source' => $this->get_item_name(), + 'utm_content' => $link_identifier + ); + + // url encode tracking vars + $tracking_vars = urlencode_deep( $tracking_vars ); + + $query_string = build_query( $tracking_vars ); + + + return $this->get_item_url() . '#' . $query_string; + } + + } + +} + diff --git a/wp-content/plugins/wordpress-seo/admin/license-manager/class-theme-license-manager.php b/wp-content/plugins/wordpress-seo/admin/license-manager/class-theme-license-manager.php new file mode 100644 index 0000000..edb166c --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/license-manager/class-theme-license-manager.php @@ -0,0 +1,53 @@ +license_is_valid() ) { + // setup auto updater + require_once dirname( __FILE__ ) . '/class-update-manager.php'; + require_once dirname( __FILE__ ) . '/class-theme-update-manager.php'; // @TODO: Autoload? + new Yoast_Theme_Update_Manager( $this->product, $this ); + } + } + + /** + * Setup hooks + */ + public function specific_hooks() { + // remotely deactivate license upon switching away from this theme + add_action( 'switch_theme', array( $this, 'deactivate_license' ) ); + + // Add the license menu + add_action( 'admin_menu', array( $this, 'add_license_menu' ) ); + } + + /** + * Add license page and add it to Themes menu + */ + public function add_license_menu() { + $theme_page = add_theme_page( sprintf( __( '%s License', $this->product->get_text_domain() ), $this->product->get_item_name() ), __( 'Theme License', $this->product->get_text_domain() ), 'manage_options', 'theme-license', array( $this, 'show_license_page' ) ); + } + + /** + * Shows license page + */ + public function show_license_page() { + ?> +
+ + + show_license_form( false ); ?> +
+ setup_hooks(); + } + + /** + * Get the current theme version + * + * @return string The version number + */ + private function get_theme_version() { + + // if version was not set, get it from the Theme stylesheet + if( $this->product->get_version() === '' ) { + $theme = wp_get_theme( $this->product->get_slug() ); + return $theme->get( 'Version' ); + } + + return $this->product->get_version(); + } + + /** + * Setup hooks + */ + private function setup_hooks() { + add_filter( 'site_transient_update_themes', array( $this, 'set_theme_update_transient' ) ); + add_action( 'load-themes.php', array( $this, 'load_themes_screen' ) ); + } + + /** + * Set "updates available" transient + */ + public function set_theme_update_transient( $value ) { + + $update_data = $this->get_update_data(); + + if( $update_data === false ) { + return $value; + } + + // add update data to "updates available" array. convert object to array. + $value->response[ $this->product->get_slug() ] = (array) $update_data; + + return $value; + } + + /** + * Add hooks and scripts to the Appearance > Themes screen + */ + public function load_themes_screen() { + + $update_data = $this->get_update_data(); + + // only do if an update is available + if( $update_data === false ) { + return; + } + + add_thickbox(); + add_action( 'admin_notices', array( $this, 'show_update_details' ) ); + } + + /** + * Show update link. + * Opens Thickbox with Changelog. + */ + public function show_update_details() { + + $update_data = $this->get_update_data(); + + // only show if an update is available + if( $update_data === false ) { + return; + } + + $update_url = wp_nonce_url( 'update.php?action=upgrade-theme&theme=' . urlencode( $this->product->get_slug() ), 'upgrade-theme_' . $this->product->get_slug() ); + $update_onclick = ' onclick="if ( confirm(\'' . esc_js( __( "Updating this theme will lose any customizations you have made. 'Cancel' to stop, 'OK' to update." ) ) . '\') ) {return true;}return false;"'; + ?> +
+ %s version %s is available. Check out what\'s new or update now.' ), + $this->product->get_item_name(), + $update_data->new_version, + '#TB_inline?width=640&inlineId=' . $this->product->get_slug() . '_changelog', + $this->get_item_name(), + $update_url, + $update_onclick + ); + ?> +
+ + get_remote_data(); + + if( false === $api_response ) { + return false; + } + + $update_data = $api_response; + + // check if a new version is available. + if ( version_compare( $this->get_theme_version(), $update_data->new_version, '>=' ) ) { + return false; + } + + + // an update is available + return $update_data; + } + + + } + +} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/license-manager/class-update-manager.php b/wp-content/plugins/wordpress-seo/admin/license-manager/class-update-manager.php new file mode 100644 index 0000000..14e113d --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/license-manager/class-update-manager.php @@ -0,0 +1,211 @@ +product = $product; + $this->license_manager = $license_manager; + + // generate transient names + $this->response_transient_key = $this->product->get_transient_prefix() . '-update-response'; + $this->request_failed_transient_key = $this->product->get_transient_prefix() . '-update-request-failed'; + + // maybe delete transient + $this->maybe_delete_transients(); + } + + /** + * Deletes the various transients + * If we're on the update-core.php?force-check=1 page + */ + private function maybe_delete_transients() { + global $pagenow; + + if( $pagenow === 'update-core.php' && isset( $_GET['force-check'] ) ) { + delete_transient( $this->response_transient_key ); + delete_transient( $this->request_failed_transient_key ); + } + } + + /** + * If the update check returned a WP_Error, show it to the user + */ + public function show_update_error() { + + if ( $this->error_message === '' ) { + return; + } + + ?> +
+

%s', $this->product->get_text_domain() ), $this->product->get_item_name(), $this->error_message ); ?>

+
+ request_failed_transient_key ) !== false ) { + return false; + } + + // start request process + global $wp_version; + + // set a transient to prevent failed update checks on every page load + // this transient will be removed if a request succeeds + set_transient( $this->request_failed_transient_key, 'failed', 10800 ); + + // setup api parameters + $api_params = array( + 'edd_action' => 'get_version', + 'license' => $this->license_manager->get_license_key(), + 'item_name' => $this->product->get_item_name(), + 'wp_version' => $wp_version, + 'item_version' => $this->product->get_version(), + 'url' => home_url(), + 'slug' => $this->product->get_slug() + ); + + // setup request parameters + $request_params = array( + 'method' => 'POST', + 'body' => $api_params + ); + + require_once dirname( __FILE__ ) . '/class-api-request.php'; + $request = new Yoast_API_Request( $this->product->get_api_url(), $request_params ); + + if( $request->is_valid() !== true ) { + + // show error message + $this->error_message = $request->get_error_message(); + add_action( 'admin_notices', array( $this, 'show_update_error' ) ); + + return false; + } + + // request succeeded, delete transient indicating a request failed + delete_transient( $this->request_failed_transient_key ); + + // decode response + $response = $request->get_response(); + + // check if response returned that a given site was inactive + if( isset( $response->license_check ) && ! empty( $response->license_check ) && $response->license_check != 'valid' ) { + + // deactivate local license + $this->license_manager->set_license_status( 'invalid' ); + + // show notice to let the user know we deactivated his/her license + $this->error_message = __( "This site has not been activated properly on yoast.com and thus cannot check for future updates. Please activate your site with a valid license key.", $this->product->get_text_domain() ); + add_action( 'admin_notices', array( $this, 'show_update_error' ) ); + } + + $response->sections = maybe_unserialize( $response->sections ); + + // store response + set_transient( $this->response_transient_key, $response, 10800 ); + + return $response; + } + + /** + * Gets the remote product data (from the EDD API) + * + * - If it was previously fetched in the current requests, this gets it from the instance property + * - Next, it tries the 3-hour transient + * - Next, it calls the remote API and stores the result + * + * @return object + */ + protected function get_remote_data() { + + // always use property if it's set + if( null !== $this->update_response ) { + return $this->update_response; + } + + // get cached remote data + $data = $this->get_cached_remote_data(); + + // if cache is empty or expired, call remote api + if( $data === false ) { + $data = $this->call_remote_api(); + } + + $this->update_response = $data; + return $data; + } + + /** + * Gets the remote product data from a 3-hour transient + * + * @return bool|mixed + */ + private function get_cached_remote_data() { + + $data = get_transient( $this->response_transient_key ); + + if( $data ) { + return $data; + } + + return false; + } + + } + +} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/license-manager/index.php b/wp-content/plugins/wordpress-seo/admin/license-manager/index.php new file mode 100644 index 0000000..12c197f --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/license-manager/index.php @@ -0,0 +1,2 @@ +load_license_manager(); + } + + + } + + /** + * Loads the License_Plugin_Manager class + * + * The class will take care of the rest: notices, license (de)activations, updates, etc.. + */ + public function load_license_manager() { + + // Instantiate license class + $license_manager = new Yoast_Plugin_License_Manager( new Sample_Product() ); + + // Setup the required hooks + $license_manager->setup_hooks(); + + } + + /** + * Add license page and add it to Themes menu + */ + public function add_license_menu() { + $theme_page = add_options_page( sprintf( __( '%s License', $this->text_domain ), $this->item_name ), sprintf( __( '%s License', $this->text_domain ), $this->item_name ), 'manage_options', $this->text_domain . '-license', array( $this, 'show_license_page' ) ); + } + + /** + * Shows license page + */ + public function show_license_page() { + + // Instantiate license class + $license_manager = new Yoast_Plugin_License_Manager( new Sample_Product() ); + + ?> +
+ + + show_license_form( false ); ?> +
+ setup_hooks(); + +} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/license-manager/views/form.php b/wp-content/plugins/wordpress-seo/admin/license-manager/views/form.php new file mode 100644 index 0000000..8110862 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/license-manager/views/form.php @@ -0,0 +1,94 @@ +product; + +$this->show_license_form_heading(); + +// Output form tags if we're not embedded in another form +if( ! $embedded ) { + echo '
'; +} + +wp_nonce_field( $nonce_name, $nonce_name ); ?> + + + + + + + + + + + + + + + + +
get_text_domain() ); ?> + license_is_valid() ) { ?> + ACTIVE   -   you are receiving updates. + + INACTIVE   -   you are not receiving updates. + +
get_text_domain() ); ?> + + license_is_valid() ) { ?> +   + get_text_domain() ); ?> + get_license_key() !== '') { ?> +   + get_text_domain() ); + } + + } ?> + +
get_text_domain() ); ?> + /> + license_constant_is_defined ) { ?> +

get_text_domain() ), '' . $this->license_constant_name . '' ); ?>

+ +
+ +license_is_valid() ) { + + $expiry_date = strtotime( $this->get_license_expiry_date() ); + + if( $expiry_date !== false ) { + echo '

'; + + printf( __( 'Your %s license will expire on %s.', $product->get_text_domain() ), $product->get_item_name(), date('F jS Y', $expiry_date ) ); + + if( strtotime( '+3 months' ) > $expiry_date ) { + printf( ' ' . __('%sRenew your license now%s.', $product->get_text_domain() ), '', '' ); + } + + echo '

'; + } +} + +// Only show a "Save Changes" button and end form if we're not embedded in another form. +if( ! $embedded ) { + + // only show "Save Changes" button if license is not activated and not defined with a constant + if( $readonly === false ) { + submit_button(); + } + + echo '
'; +} + +$product = null; diff --git a/wp-content/plugins/wordpress-seo/admin/license-manager/views/index.php b/wp-content/plugins/wordpress-seo/admin/license-manager/views/index.php new file mode 100644 index 0000000..12c197f --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/license-manager/views/index.php @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/pages/bulk-editor.php b/wp-content/plugins/wordpress-seo/admin/pages/bulk-editor.php new file mode 100644 index 0000000..ebff98c --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/bulk-editor.php @@ -0,0 +1,62 @@ + + + +
+ +

+ + + +
+
+ prepare_page_navigation(); + $wpseo_bulk_titles_table->prepare_items(); + ?> + + views(); ?> + display(); ?> + +
+
+ prepare_page_navigation(); + $wpseo_bulk_description_table->prepare_items(); + ?> + + views(); ?> + display(); ?> +
+ +
+
\ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/pages/dashboard.php b/wp-content/plugins/wordpress-seo/admin/pages/dashboard.php new file mode 100644 index 0000000..ac5ffd6 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/dashboard.php @@ -0,0 +1,172 @@ +' . esc_html( $backup_file ) . ', ', 'wordpress-seo' ); + + $count = 0; + $fcontent = str_replace( $options['theme_description_found'], '', $fcontent, $count ); + if ( $count > 0 ) { + $header_file = fopen( $path . '/header.php', 'w+' ); + if ( $header_file ) { + if ( fwrite( $header_file, $fcontent ) !== false ) { + $msg .= __( 'Removed hardcoded meta description.', 'wordpress-seo' ); + $options['theme_has_description'] = false; + $options['theme_description_found'] = ''; + update_option( 'wpseo', $options ); + } + else { + $msg .= '' . __( 'Failed to remove hardcoded meta description.', 'wordpress-seo' ) . ''; + } + fclose( $header_file ); + } + } + else { + wpseo_description_test(); + $msg .= '' . __( 'Earlier found meta description was not found in file. Renewed the description test data.', 'wordpress-seo' ) . ''; + } + add_settings_error( 'yoast_wpseo_dashboard_options', 'error', $msg, 'updated' ); + } + } + } + + // Clean up the referrer url for later use + if ( isset( $_SERVER['REQUEST_URI'] ) ) { + $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'nonce', 'fixmetadesc' ), $_SERVER['REQUEST_URI'] ); + } +} + +if ( ( ! isset( $options['theme_has_description'] ) || ( ( isset( $options['theme_has_description'] ) && $options['theme_has_description'] === true ) || $options['theme_description_found'] !== '' ) ) || ( isset( $_GET['checkmetadesc'] ) && check_admin_referer( 'wpseo-check-metadesc', 'nonce' ) ) ) { + wpseo_description_test(); + // Renew the options after the test + $options = get_option( 'wpseo' ); +} +if ( isset( $_GET['checkmetadesc'] ) ) { + // Clean up the referrer url for later use + if ( isset( $_SERVER['REQUEST_URI'] ) ) { + $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'nonce', 'checkmetadesc' ), $_SERVER['REQUEST_URI'] ); + } +} + + + +$wpseo_admin_pages->admin_header( true, WPSEO_Options::get_group_name( 'wpseo' ), 'wpseo' ); + +do_action( 'wpseo_all_admin_notices' ); + +if ( is_array( $options['blocking_files'] ) && count( $options['blocking_files'] ) > 0 ) { + echo '

' + . '' . __( 'Fix it.', 'wordpress-seo' ) . '' + . __( 'The following file(s) is/are blocking your XML sitemaps from working properly:', 'wordpress-seo' ) . '
'; + foreach ( $options['blocking_files'] as $file ) { + echo esc_html( $file ) . '
'; + } + echo __( 'Either delete them (this can be done with the "Fix it" button) or disable WP SEO XML sitemaps.', 'wordpress-seo' ); + echo '

'; +} + + +if ( $options['theme_description_found'] !== '' ) { + echo '

' + . '' . __( 'Fix it.', 'wordpress-seo' ) . '' + . ' ' . __( 'Re-check theme.', 'wordpress-seo' ) . '' + . __( 'Your theme contains a meta description, which blocks WordPress SEO from working properly, please delete the following line, or press fix it:', 'wordpress-seo' ) . '
'; + echo '' . esc_html( $options['theme_description_found'] ) . ''; + echo '

'; +} + + +if ( strpos( get_option( 'permalink_structure' ), '%postname%' ) === false && $options['ignore_permalink'] === false ) { + echo ''; +} + +if ( get_option( 'page_comments' ) && $options['ignore_page_comments'] === false ) { + echo '

' + . '' . __( 'Fix it.', 'wordpress-seo' ) . '' + . '' . __( 'Ignore.', 'wordpress-seo' ) . '' + . __( 'Paging comments is enabled, this is not needed in 999 out of 1000 cases, so the suggestion is to disable it, to do that, simply uncheck the box before "Break comments into pages..."', 'wordpress-seo' ) . '

'; +} + +echo '

' . __( 'General', 'wordpress-seo' ) . '

'; + +if ( $options['ignore_tour'] === true ) { + echo '' . __( 'Start Tour', 'wordpress-seo' ) . ''; + echo '

' . __( 'Take this tour to quickly learn about the use of this plugin.', 'wordpress-seo' ) . '

'; +} + +echo '' . __( 'Reset Default Settings', 'wordpress-seo' ) . ''; +echo '

' . __( 'If you want to restore a site to the default WordPress SEO settings, press this button.', 'wordpress-seo' ) . '

'; + +echo '

' . __( 'Tracking', 'wordpress-seo' ) . '

'; +echo $wpseo_admin_pages->checkbox( 'yoast_tracking', __( 'Allow tracking of this WordPress install\'s anonymous data.', 'wordpress-seo' ) ); +echo '

' . __( "To maintain a plugin as big as WordPress SEO, we need to know what we're dealing with: what kinds of other plugins our users are using, what themes, etc. Please allow us to track that data from your install. It will not track any user details, so your security and privacy are safe with us.", 'wordpress-seo' ) . '

'; + +echo '

' . __( 'Security', 'wordpress-seo' ) . '

'; +echo $wpseo_admin_pages->checkbox( 'disableadvanced_meta', __( 'Disable the Advanced part of the WordPress SEO meta box', 'wordpress-seo' ) ); +echo '

' . __( 'Unchecking this box allows authors and editors to redirect posts, noindex them and do other things you might not want if you don\'t trust your authors.', 'wordpress-seo' ) . '

'; + +echo '

' . __( 'Webmaster Tools', 'wordpress-seo' ) . '

'; +echo '

' . __( 'You can use the boxes below to verify with the different Webmaster Tools, if your site is already verified, you can just forget about these. Enter the verify meta values for:', 'wordpress-seo' ) . '

'; +echo $wpseo_admin_pages->textinput( 'alexaverify', '' . __( 'Alexa Verification ID', 'wordpress-seo' ) . '' ); +echo $wpseo_admin_pages->textinput( 'msverify', '' . __( 'Bing Webmaster Tools', 'wordpress-seo' ) . '' ); +echo $wpseo_admin_pages->textinput( 'googleverify', '' . __( 'Google Webmaster Tools', 'wordpress-seo' ) . '' ); +echo $wpseo_admin_pages->textinput( 'pinterestverify', '' . __( 'Pinterest', 'wordpress-seo' ) . '' ); +echo $wpseo_admin_pages->textinput( 'yandexverify', '' . __( 'Yandex Webmaster Tools', 'wordpress-seo' ) . '' ); + +do_action( 'wpseo_dashboard' ); + +$wpseo_admin_pages->admin_footer(); \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/pages/files.php b/wp-content/plugins/wordpress-seo/admin/pages/files.php new file mode 100644 index 0000000..e1fa129 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/files.php @@ -0,0 +1,137 @@ +admin_header( false ); +if ( isset( $msg ) && ! empty( $msg ) ) { + echo '

' . esc_html( $msg ) . '

'; +} + +$action_url = network_admin_url( 'admin.php?page=wpseo_files' ); // auto-falls back on admin_url for non-multisite + +if ( ! file_exists( $robots_file ) ) { + if ( is_writable( get_home_path() ) ) { + $content = '
'; + $content .= wp_nonce_field( 'wpseo_create_robots', '_wpnonce', true, false ); + $content .= '

' . __( 'You don\'t have a robots.txt file, create one here:', 'wordpress-seo' ) . '

'; + $content .= ''; + $content .= '
'; + } + else { + $content = '

' . __( 'If you had a robots.txt file and it was editable, you could edit it from here.', 'wordpress-seo' ); + } +} +else { + $f = fopen( $robots_file, 'r' ); + + $content = ''; + if ( filesize( $robots_file ) > 0 ) { + $content = fread( $f, filesize( $robots_file ) ); + } + $robots_txt_content = esc_textarea( $content ); + + if ( ! is_writable( $robots_file ) ) { + $content = '

' . __( 'If your robots.txt were writable, you could edit it from here.', 'wordpress-seo' ) . '

'; + $content .= '
'; + } else { + $content = '
'; + $content .= wp_nonce_field( 'wpseo-robotstxt', '_wpnonce', true, false ); + $content .= '

' . __( 'Edit the content of your robots.txt:', 'wordpress-seo' ) . '

'; + $content .= '
'; + $content .= '
'; + $content .= '
'; + } +} + +$wpseo_admin_pages->postbox( 'robotstxt', __( 'Robots.txt', 'wordpress-seo' ), $content ); + +if ( ( isset( $_SERVER['SERVER_SOFTWARE'] ) && stristr( $_SERVER['SERVER_SOFTWARE'], 'nginx' ) === false ) && file_exists( $ht_access_file ) ) { + $f = fopen( $ht_access_file, 'r' ); + + $contentht = ''; + if ( filesize( $ht_access_file ) > 0 ) { + $contentht = fread( $f, filesize( $ht_access_file ) ); + } + $contentht = esc_textarea( $contentht ); + + if ( ! is_writable( $ht_access_file ) ) { + $content = '

' . __( 'If your .htaccess were writable, you could edit it from here.', 'wordpress-seo' ) . '

'; + $content .= '
'; + } else { + $content = '
'; + $content .= wp_nonce_field( 'wpseo-htaccess', '_wpnonce', true, false ); + $content .= '

' . __( 'Edit the content of your .htaccess:', 'wordpress-seo' ) . '

'; + $content .= '
'; + $content .= '
'; + $content .= '
'; + } + $wpseo_admin_pages->postbox( 'htaccess', __( '.htaccess file', 'wordpress-seo' ), $content ); +} elseif ( ( isset( $_SERVER['SERVER_SOFTWARE'] ) && stristr( $_SERVER['SERVER_SOFTWARE'], 'nginx' ) === false ) && ! file_exists( $ht_access_file ) ) { + $content = '

' . __( 'If you had a .htaccess file and it was editable, you could edit it from here.', 'wordpress-seo' ); + $wpseo_admin_pages->postbox( 'htaccess', __( '.htaccess file', 'wordpress-seo' ), $content ); +} + +$wpseo_admin_pages->admin_footer( false ); \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/pages/import.php b/wp-content/plugins/wordpress-seo/admin/pages/import.php new file mode 100644 index 0000000..ec725bd --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/import.php @@ -0,0 +1,474 @@ + testers] Extensively test the export & import of the (new) settings! + * If that all works fine, getting testers to export before and after upgrade will make testing easier. + * + * @todo [Yoast] The import for the RSS Footer plugin checks for data already entered via WP SEO, + * the other import routines should do that too. +*/ + +global $wpseo_admin_pages; + +$msg = ''; +if ( isset( $_POST['import'] ) || isset( $_GET['import'] ) ) { + + check_admin_referer( 'wpseo-import' ); + + global $wpdb; + $replace = false; + $deletekw = false; + + if ( isset( $_POST['wpseo']['deleteolddata'] ) && $_POST['wpseo']['deleteolddata'] == 'on' ) { + $replace = true; + } + + if ( isset( $_POST['wpseo']['importwoo'] ) ) { + WPSEO_Options::initialize(); + + $sep = get_option( 'seo_woo_seperator' ); + $options = get_option( 'wpseo_titles' ); + + switch ( get_option( 'seo_woo_home_layout' ) ) { + case 'a': + $options['title-home-wpseo'] = '%%sitename%% ' . $sep . ' %%sitedesc%%'; + break; + case 'b': + $options['title-home-wpseo'] = '%%sitename%% ' . get_option( 'seo_woo_paged_var' ) . ' %%pagenum%%'; + break; + case 'c': + $options['title-home-wpseo'] = '%%sitedesc%%'; + break; + } + if ( $replace ) { + delete_option( 'seo_woo_home_layout' ); + } + + switch ( get_option( 'seo_woo_single_layout' ) ) { + case 'a': + $options['title-post'] = '%%title%% ' . $sep . ' %%sitename%%'; + break; + case 'b': + $options['title-post'] = '%%title%%'; + break; + case 'c': + $options['title-post'] = '%%sitename%% ' . $sep . ' %%title%%'; + break; + case 'd': + $options['title-post'] = '%%title%% ' . $sep . ' %%sitedesc%%'; + break; + case 'e': + $options['title-post'] = '%%sitename%% ' . $sep . ' %%title%% ' . $sep . ' %%sitedesc%%'; + break; + } + if ( $replace ) { + delete_option( 'seo_woo_single_layout' ); + } + + switch ( get_option( 'seo_woo_page_layout' ) ) { + case 'a': + $options['title-page'] = '%%title%% ' . $sep . ' %%sitename%%'; + break; + case 'b': + $options['title-page'] = '%%title%%'; + break; + case 'c': + $options['title-page'] = '%%sitename%% ' . $sep . ' %%title%%'; + break; + case 'd': + $options['title-page'] = '%%title%% ' . $sep . ' %%sitedesc%%'; + break; + case 'e': + $options['title-page'] = '%%sitename%% ' . $sep . ' %%title%% ' . $sep . ' %%sitedesc%%'; + break; + } + if ( $replace ) { + delete_option( 'seo_woo_page_layout' ); + } + + $template = WPSEO_Options::get_default( 'wpseo_titles', 'title-tax-post' ); // the default is the same for all taxonomies, so post will do + switch ( get_option( 'seo_woo_archive_layout' ) ) { + case 'a': + $template = '%%term_title%% ' . $sep . ' %%page%% ' . $sep . ' %%sitename%%'; + break; + case 'b': + $template = '%%term_title%%'; + break; + case 'c': + $template = '%%sitename%% ' . $sep . ' %%term_title%% ' . $sep . ' %%page%%'; + break; + case 'd': + $template = '%%term_title%% ' . $sep . ' %%page%%' . $sep . ' %%sitedesc%%'; + break; + case 'e': + $template = '%%sitename%% ' . $sep . ' %%term_title%% ' . $sep . ' %%page%% ' . $sep . ' %%sitedesc%%'; + break; + } + $taxonomies = get_taxonomies( array( 'public' => true ), 'names' ); + if ( is_array( $taxonomies ) && $taxonomies !== array() ) { + foreach ( $taxonomies as $tax ) { + $options[ 'title-tax-'.$tax ] = $template; + } + } + unset( $taxonomies, $tax, $template ); + if ( $replace ) { + delete_option( 'seo_woo_archive_layout' ); + } + + // Import the custom homepage description + if ( 'c' == get_option( 'seo_woo_meta_home_desc' ) ) { + $options['metadesc-home-wpseo'] = get_option( 'seo_woo_meta_home_desc_custom' ); + } + if ( $replace ) { + delete_option( 'seo_woo_meta_home_desc' ); + } + + // Import the custom homepage keywords + if ( 'c' == get_option( 'seo_woo_meta_home_key' ) ) { + $options['metakey-home-wpseo'] = get_option( 'seo_woo_meta_home_key_custom' ); + } + if ( $replace ) { + delete_option( 'seo_woo_meta_home_key' ); + } + + // If WooSEO is set to use the Woo titles, import those + if ( 'true' == get_option( 'seo_woo_wp_title' ) ) { + WPSEO_Meta::replace_meta( 'seo_title', WPSEO_Meta::$meta_prefix . 'title', $replace ); + } + + // If WooSEO is set to use the Woo meta descriptions, import those + if ( 'b' == get_option( 'seo_woo_meta_single_desc' ) ) { + WPSEO_Meta::replace_meta( 'seo_description', WPSEO_Meta::$meta_prefix . 'metadesc', $replace ); + } + + // If WooSEO is set to use the Woo meta keywords, import those + if ( 'b' == get_option( 'seo_woo_meta_single_key' ) ) { + WPSEO_Meta::replace_meta( 'seo_keywords', WPSEO_Meta::$meta_prefix . 'metakeywords', $replace ); + } + + /* @todo [JRF => whomever] verify how WooSEO sets these metas ( 'noindex', 'follow' ) + and if the values saved are concurrent with the ones we use (i.e. 0/1/2) */ + WPSEO_Meta::replace_meta( 'seo_follow', WPSEO_Meta::$meta_prefix . 'meta-robots-nofollow', $replace ); + WPSEO_Meta::replace_meta( 'seo_noindex', WPSEO_Meta::$meta_prefix . 'meta-robots-noindex', $replace ); + + update_option( 'wpseo_titles', $options ); + $msg .= __( 'WooThemes SEO framework settings & data successfully imported.', 'wordpress-seo' ); + unset( $options, $sep ); + } + + if ( isset( $_POST['wpseo']['importheadspace'] ) ) { + WPSEO_Meta::replace_meta( '_headspace_description', WPSEO_Meta::$meta_prefix . 'metadesc', $replace ); + WPSEO_Meta::replace_meta( '_headspace_keywords', WPSEO_Meta::$meta_prefix . 'metakeywords', $replace ); + WPSEO_Meta::replace_meta( '_headspace_page_title', WPSEO_Meta::$meta_prefix . 'title', $replace ); + /* @todo [JRF => whomever] verify how headspace sets these metas ( 'noindex', 'nofollow', 'noarchive', 'noodp', 'noydir' ) + and if the values saved are concurrent with the ones we use (i.e. 0/1/2) */ + WPSEO_Meta::replace_meta( '_headspace_noindex', WPSEO_Meta::$meta_prefix . 'meta-robots-noindex', $replace ); + WPSEO_Meta::replace_meta( '_headspace_nofollow', WPSEO_Meta::$meta_prefix . 'meta-robots-nofollow', $replace ); + + /* @todo - [JRF => whomever] check if this can be done more efficiently by querying only the meta table + possibly directly changing it using concat on the existing values + */ + $posts = $wpdb->get_results( "SELECT ID FROM $wpdb->posts" ); + if ( is_array( $posts ) && $posts !== array() ) { + foreach ( $posts as $post ) { + $custom = get_post_custom( $post->ID ); + $robotsmeta_adv = ''; + if ( isset( $custom['_headspace_noarchive'] ) ) { + $robotsmeta_adv .= 'noarchive,'; + } + if ( isset( $custom['_headspace_noodp'] ) ) { + $robotsmeta_adv .= 'noodp,'; + } + if ( isset( $custom['_headspace_noydir'] ) ) { + $robotsmeta_adv .= 'noydir'; + } + $robotsmeta_adv = preg_replace( '`,$`', '', $robotsmeta_adv ); + WPSEO_Meta::set_value( 'meta-robots-adv', $robotsmeta_adv, $post->ID ); + } + } + unset( $posts, $post, $custom, $robotsmeta_adv ); + + if ( $replace ) { + foreach ( array( 'noarchive', 'noodp', 'noydir' ) as $meta ) { + delete_post_meta_by_key( '_headspace_' . $meta ); + } + unset( $meta ); + } + $msg .= __( 'HeadSpace2 data successfully imported', 'wordpress-seo' ); + } + + // @todo [JRF => whomever] how does this correlate with the routine on the dashboard page ? isn't one superfluous ? + if ( isset( $_POST['wpseo']['importaioseo'] ) || isset( $_GET['importaioseo'] ) ) { + WPSEO_Meta::replace_meta( '_aioseop_description', WPSEO_Meta::$meta_prefix . 'metadesc', $replace ); + WPSEO_Meta::replace_meta( '_aioseop_keywords', WPSEO_Meta::$meta_prefix . 'metakeywords', $replace ); + WPSEO_Meta::replace_meta( '_aioseop_title', WPSEO_Meta::$meta_prefix . 'title', $replace ); + $msg .= __( sprintf( 'All in One SEO data successfully imported. Would you like to %sdisable the All in One SEO plugin%s.', '', '' ), 'wordpress-seo' ); + } + + if ( isset( $_POST['wpseo']['importaioseoold'] ) ) { + WPSEO_Meta::replace_meta( 'description', WPSEO_Meta::$meta_prefix . 'metadesc', $replace ); + WPSEO_Meta::replace_meta( 'keywords', WPSEO_Meta::$meta_prefix . 'metakeywords', $replace ); + WPSEO_Meta::replace_meta( 'title', WPSEO_Meta::$meta_prefix . 'title', $replace ); + $msg .= __( 'All in One SEO (Old version) data successfully imported.', 'wordpress-seo' ); + } + + if ( isset( $_POST['wpseo']['importrobotsmeta'] ) || isset( $_GET['importrobotsmeta'] ) ) { + $posts = $wpdb->get_results( "SELECT ID, robotsmeta FROM $wpdb->posts" ); + if ( is_array( $posts ) && $posts !== array() ) { + foreach ( $posts as $post ) { + // sync all possible settings + if ( $post->robotsmeta ) { + $pieces = explode( ',', $post->robotsmeta ); + foreach ( $pieces as $meta ) { + switch ( $meta ) { + case 'noindex': + WPSEO_Meta::set_value( 'meta-robots-noindex', '1', $post->ID ); + break; + + case 'index': + WPSEO_Meta::set_value( 'meta-robots-noindex', '2', $post->ID ); + break; + + case 'nofollow': + WPSEO_Meta::set_value( 'meta-robots-nofollow', '1', $post->ID ); + break; + } + } + } + } + } + unset( $posts, $post, $pieces, $meta ); + $msg .= __( sprintf( 'Robots Meta values imported. We recommend %sdisabling the Robots-Meta plugin%s to avoid any conflicts.', '', '' ), 'wordpress-seo' ); + } + + if ( isset( $_POST['wpseo']['importrssfooter'] ) ) { + $optold = get_option( 'RSSFooterOptions' ); + $optnew = get_option( 'wpseo_rss' ); + if ( $optold['position'] == 'after' ) { + if ( $optnew['rssafter'] === '' || $optnew['rssafter'] === WPSEO_Options::get_default( 'wpseo_rss', 'rssafter' ) ) { + $optnew['rssafter'] = $optold['footerstring']; + } + } + else { + /* @internal Uncomment the second part if a default would be given to the rssbefore value */ + if ( $optnew['rssbefore'] === '' /*|| $optnew['rssbefore'] === WPSEO_Options::get_default( 'wpseo_rss', 'rssbefore' )*/ ) { + $optnew['rssbefore'] = $optold['footerstring']; + } + } + update_option( 'wpseo_rss', $optnew ); + unset( $optold, $optnew ); + $msg .= __( 'RSS Footer options imported successfully.', 'wordpress-seo' ); + } + + if ( isset( $_POST['wpseo']['importbreadcrumbs'] ) ) { + $optold = get_option( 'yoast_breadcrumbs' ); + $optnew = get_option( 'wpseo_internallinks' ); + + if ( is_array( $optold ) && $optold !== array() ) { + foreach ( $optold as $opt => $val ) { + if ( is_bool( $val ) && $val == true ) { + $optnew[ 'breadcrumbs-' . $opt ] = true; + } + else { + $optnew[ 'breadcrumbs-' . $opt ] = $val; + } + } + unset( $opt, $val ); + update_option( 'wpseo_internallinks', $optnew ); + $msg .= __( 'Yoast Breadcrumbs options imported successfully.', 'wordpress-seo' ); + } + else { + $msg .= __( 'Yoast Breadcrumbs options could not be found', 'wordpress-seo' ); + } + unset( $optold, $optnew ); + } + + // Allow custom import actions + do_action( 'wpseo_handle_import' ); + + /** + * Allow customization of import&export message + * @api string $msg The message. + */ + $msg = apply_filters( 'wpseo_import_message', $msg ); + + // Check if we've deleted old data and adjust message to match it + if ( $replace ) { + $msg .= __( ', and old data deleted.', 'wordpress-seo' ); + } + if ( $deletekw ) { + $msg .= __( ', and meta keywords data deleted.', 'wordpress-seo' ); + } +} + + +$wpseo_admin_pages->admin_header( false ); +if ( $msg != '' ) { + echo '

' . $msg . '

'; +} + +$content = '

' . __( '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' ) . '

'; +$content .= '

' . sprintf( __( 'If you\'ve used another SEO plugin, try the %sSEO Data Transporter%s plugin to move your data into this plugin, it rocks!', 'wordpress-seo' ), '', '' ) . '

'; +// @todo [JRF => whomever] add action for form tag +$content .= '
'; +$content .= wp_nonce_field( 'wpseo-import', '_wpnonce', true, false ); +$content .= $wpseo_admin_pages->checkbox( 'importheadspace', __( 'Import from HeadSpace2?', 'wordpress-seo' ) ); +$content .= $wpseo_admin_pages->checkbox( 'importaioseo', __( 'Import from All-in-One SEO?', 'wordpress-seo' ) ); +$content .= $wpseo_admin_pages->checkbox( 'importaioseoold', __( 'Import from OLD All-in-One SEO?', 'wordpress-seo' ) ); +$content .= $wpseo_admin_pages->checkbox( 'importwoo', __( 'Import from WooThemes SEO framework?', 'wordpress-seo' ) ); +$content .= '
'; +$content .= $wpseo_admin_pages->checkbox( 'deleteolddata', __( 'Delete the old data after import? (recommended)', 'wordpress-seo' ) ); +$content .= '
'; +$content .= ''; +$content .= '

'; + +$content .= '

' . __( 'Import settings from other plugins', 'wordpress-seo' ) . '

'; +$content .= $wpseo_admin_pages->checkbox( 'importrobotsmeta', __( 'Import from Robots Meta (by Yoast)?', 'wordpress-seo' ) ); +$content .= $wpseo_admin_pages->checkbox( 'importrssfooter', __( 'Import from RSS Footer (by Yoast)?', 'wordpress-seo' ) ); +$content .= $wpseo_admin_pages->checkbox( 'importbreadcrumbs', __( 'Import from Yoast Breadcrumbs?', 'wordpress-seo' ) ); + +/** + * Allow option of importing from other 'other' plugins + * @api string $content The content containing all import and export methods + */ +$content = apply_filters( 'wpseo_import_other_plugins', $content ); + +$content .= '
'; +$content .= ''; +$content .= '

'; + +$wpseo_admin_pages->postbox( 'import', __( 'Import', 'wordpress-seo' ), $content ); + +/** + * Allow adding a custom import block + * @api WPSEO_Admin $this The WPSEO_Admin object + */ +do_action( 'wpseo_import', $this ); + +// @todo [JRF => whomever] add action for form tag +$content = '

' . __( 'Export', 'wordpress-seo' ) . '

'; +$content .= '
'; +$content .= wp_nonce_field( 'wpseo-export', '_wpnonce', true, false ); +$content .= '

' . __( 'Export your WordPress SEO settings here, to import them again later or to import them on another site.', 'wordpress-seo' ) . '

'; +$content .= $wpseo_admin_pages->checkbox( 'include_taxonomy_meta', __( 'Include Taxonomy Metadata', 'wordpress-seo' ) ); +$content .= '
'; +$content .= '
'; +if ( isset( $_POST['wpseo_export'] ) ) { + check_admin_referer( 'wpseo-export' ); + $include_taxonomy = false; + if ( isset( $_POST['wpseo']['include_taxonomy_meta'] ) ) { + $include_taxonomy = true; + } + $url = $wpseo_admin_pages->export_settings( $include_taxonomy ); + if ( $url ) { + $GLOBALS['export_js'] = ' + '; + add_action( 'admin_footer-' . $GLOBALS['hook_suffix'], 'wpseo_deliver_export_zip' ); + } + else { + $content .= 'Error: ' . $url; + } +} + +$content .= '

' . __( 'Import', 'wordpress-seo' ) . '

'; +if ( ! isset( $_FILES['settings_import_file'] ) || empty( $_FILES['settings_import_file'] ) ) { + $content .= '

' . __( 'Import settings by locating settings.zip and clicking', 'wordpress-seo' ) . ' "' . __( 'Import settings', 'wordpress-seo' ) . '":

'; + // @todo [JRF => whomever] add action for form tag + $content .= '
'; + $content .= wp_nonce_field( 'wpseo-import-file', '_wpnonce', true, false ); + $content .= ''; + $content .= ''; + $content .= ''; + $content .= '

'; +} +elseif ( isset( $_FILES['settings_import_file'] ) ) { + check_admin_referer( 'wpseo-import-file' ); + $file = wp_handle_upload( $_FILES['settings_import_file'] ); + + if ( isset( $file['file'] ) && ! is_wp_error( $file ) ) { + $upload_dir = wp_upload_dir(); + + if ( ! defined( 'DIRECTORY_SEPARATOR' ) ) { + define( 'DIRECTORY_SEPARATOR', '/' ); + } + $p_path = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'wpseo-import' . DIRECTORY_SEPARATOR; + + if ( ! isset( $GLOBALS['wp_filesystem'] ) || ! is_object( $GLOBALS['wp_filesystem'] ) ) { + WP_Filesystem(); + } + + $unzipped = unzip_file( $file['file'], $p_path ); + if ( ! is_wp_error( $unzipped ) ) { + $filename = $p_path . 'settings.ini'; + if ( @is_file( $filename ) && is_readable( $filename ) ) { + $options = parse_ini_file( $filename, true ); + + if ( is_array( $options ) && $options !== array() ) { + $old_wpseo_version = null; + if ( isset( $options['wpseo']['version'] ) && $options['wpseo']['version'] !== '' ) { + $old_wpseo_version = $options['wpseo']['version']; + } + foreach ( $options as $name => $optgroup ) { + if ( $name === 'wpseo_taxonomy_meta' ) { + $optgroup = json_decode( urldecode( $optgroup['wpseo_taxonomy_meta'] ), true ); + } + + // Make sure that the imported options are cleaned/converted on import + $option_instance = WPSEO_Options::get_option_instance( $name ); + if ( is_object( $option_instance ) && method_exists( $option_instance, 'import' ) ) { + $optgroup = $option_instance->import( $optgroup, $old_wpseo_version, $options ); + } + elseif ( WP_DEBUG === true || ( defined( 'WPSEO_DEBUG' ) && WPSEO_DEBUG === true ) ) { + $content .= '

' . sprintf( __( 'Setting "%s" is no longer used and has been discarded.', 'wordpress-seo' ), $name ) . '

'; + + } + } + $content .= '

' . __( 'Settings successfully imported.', 'wordpress-seo' ) . '

'; + } + else { + $content .= '

' . __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . __( 'No settings found in file.', 'wordpress-seo' ) . '

'; + } + unset( $options, $name, $optgroup ); + } + else { + $content .= '

' . __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . __( 'Unzipping failed - file settings.ini not found.', 'wordpress-seo' ) . '

'; + } + @unlink( $filename ); + @unlink( $p_path ); + } + else { + $content .= '

' . __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . sprintf( __( 'Unzipping failed with error "%s".', 'wordpress-seo' ), $unzipped->get_error_message() ) . '

'; + } + unset( $zip, $unzipped ); + @unlink( $file['file'] ); + } + else { + if ( is_wp_error( $file ) ) { + $content .= '

' . __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . $file->get_error_message() . '

'; + } + else { + $content .= '

' . __( 'Settings could not be imported:', 'wordpress-seo' ) . ' ' . __( 'Upload failed.', 'wordpress-seo' ) . '

'; + } + } +} +$wpseo_admin_pages->postbox( 'wpseo_export', __( 'Export & Import SEO Settings', 'wordpress-seo' ), $content ); + +$wpseo_admin_pages->admin_footer( false ); + + +function wpseo_deliver_export_zip() { + if ( isset( $GLOBALS['export_js'] ) && $GLOBALS['export_js'] !== '' ) { + echo $GLOBALS['export_js']; + } +} diff --git a/wp-content/plugins/wordpress-seo/admin/pages/internal-links.php b/wp-content/plugins/wordpress-seo/admin/pages/internal-links.php new file mode 100644 index 0000000..f074385 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/internal-links.php @@ -0,0 +1,83 @@ +admin_header( true, WPSEO_Options::get_group_name( 'wpseo_internallinks' ), 'wpseo_internallinks' ); + +$content = $wpseo_admin_pages->checkbox( 'breadcrumbs-enable', __( 'Enable Breadcrumbs', 'wordpress-seo' ) ); +$content .= '
'; +$content .= $wpseo_admin_pages->textinput( 'breadcrumbs-sep', __( 'Separator between breadcrumbs', 'wordpress-seo' ) ); +$content .= $wpseo_admin_pages->textinput( 'breadcrumbs-home', __( 'Anchor text for the Homepage', 'wordpress-seo' ) ); +$content .= $wpseo_admin_pages->textinput( 'breadcrumbs-prefix', __( 'Prefix for the breadcrumb path', 'wordpress-seo' ) ); +$content .= $wpseo_admin_pages->textinput( 'breadcrumbs-archiveprefix', __( 'Prefix for Archive breadcrumbs', 'wordpress-seo' ) ); +$content .= $wpseo_admin_pages->textinput( 'breadcrumbs-searchprefix', __( 'Prefix for Search Page breadcrumbs', 'wordpress-seo' ) ); +$content .= $wpseo_admin_pages->textinput( 'breadcrumbs-404crumb', __( 'Breadcrumb for 404 Page', 'wordpress-seo' ) ); +if ( get_option( 'show_on_front' ) == 'page' && get_option( 'page_for_posts' ) > 0 ) { + $content .= $wpseo_admin_pages->checkbox( 'breadcrumbs-blog-remove', __( 'Remove Blog page from Breadcrumbs', 'wordpress-seo' ) ); +} +$content .= $wpseo_admin_pages->checkbox( 'breadcrumbs-boldlast', __( 'Bold the last page in the breadcrumb', 'wordpress-seo' ) ); +$content .= '

'; + + +$post_types = get_post_types( array( 'public' => true ), 'objects' ); +if ( is_array( $post_types ) && $post_types !== array() ) { + $content .= '' . __( 'Taxonomy to show in breadcrumbs for:', 'wordpress-seo' ) . '
'; + foreach ( $post_types as $pt ) { + $taxonomies = get_object_taxonomies( $pt->name, 'objects' ); + if ( is_array( $taxonomies ) && $taxonomies !== array() ) { + $values = array( 0 => __( 'None', 'wordpress-seo' ) ); + foreach ( $taxonomies as $tax ) { + $values[ $tax->name ] = $tax->labels->singular_name; + } + $content .= $wpseo_admin_pages->select( 'post_types-' . $pt->name . '-maintax', $pt->labels->name, $values ); + unset( $values, $tax ); + } + unset( $taxonomies ); + } + unset( $pt ); +} +$content .= '
'; + + +$taxonomies = get_taxonomies( array( 'public' => true, '_builtin' => false ), 'objects' ); +if ( is_array( $taxonomies ) && $taxonomies !== array() ) { + $content .= '' . __( 'Post type archive to show in breadcrumbs for:', 'wordpress-seo' ) . '
'; + foreach ( $taxonomies as $tax ) { + $values = array( 0 => __( 'None', 'wordpress-seo' ) ); + if ( get_option( 'show_on_front' ) == 'page' && get_option( 'page_for_posts' ) > 0 ) { + $values['post'] = __( 'Blog', 'wordpress-seo' ); + } + + if ( is_array( $post_types ) && $post_types !== array() ) { + foreach ( $post_types as $pt ) { + if ( $pt->has_archive ) { + $values[ $pt->name ] = $pt->labels->name; + } + } + unset( $pt ); + } + $content .= $wpseo_admin_pages->select( 'taxonomy-' . $tax->name . '-ptparent', $tax->labels->singular_name, $values ); + unset( $values, $tax ); + } +} +unset( $taxonomies, $post_types ); + + +$content .= '
'; +$content .= '

' . __( 'How to insert breadcrumbs in your theme', 'wordpress-seo' ) . '

'; +$content .= '

' . __( 'Usage of this breadcrumbs feature is explained here. For the more code savvy, insert this in your theme:', 'wordpress-seo' ) . '

'; +$content .= '
<?php if ( function_exists('yoast_breadcrumb') ) {
+yoast_breadcrumb('<p id="breadcrumbs">','</p>');
+} ?>
'; +$wpseo_admin_pages->postbox( 'internallinks', __( 'Breadcrumbs Settings', 'wordpress-seo' ), $content ); + +$wpseo_admin_pages->admin_footer(); \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/pages/licenses.php b/wp-content/plugins/wordpress-seo/admin/pages/licenses.php new file mode 100644 index 0000000..ae6dc35 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/licenses.php @@ -0,0 +1,94 @@ + + +
+ +

+ + + +
+
+ 'https://yoast.com/wordpress/plugins/seo-premium/', + 'title' => __( 'WordPress SEO Premium', 'wordpress-seo' ), + 'desc' => __( 'The premium version of WordPress SEO with more features & support.', 'wordpress-seo' ), + ); + } + if ( ! class_exists( 'wpseo_Video_Sitemap' ) ) { + $extensions['video-seo'] = (object) array( + 'url' => 'https://yoast.com/wordpress/plugins/video-seo/', + 'title' => __( 'Video SEO', 'wordpress-seo' ), + 'desc' => __( 'Optimize your videos to show them off in search results and get more clicks!', 'wordpress-seo' ), + ); + } + if ( ! class_exists( 'WPSEO_News' ) ) { + $extensions['news-seo'] = (object) array( + 'url' => 'https://yoast.com/wordpress/plugins/news-seo/', + 'title' => __( 'News SEO', 'wordpress-seo' ), + 'desc' => __( 'Are you in Google News? Increase your traffic from Google News by optimizing for it!', 'wordpress-seo' ), + ); + } + if ( ! defined( 'WPSEO_LOCAL_VERSION' ) ) { + $extensions['local-seo'] = (object) array( + 'url' => 'https://yoast.com/wordpress/plugins/local-seo/', + 'title' => __( 'Local SEO', 'wordpress-seo' ), + 'desc' => __( 'Rank better locally and in Google Maps, without breaking a sweat!', 'wordpress-seo' ), + ); + } + if ( class_exists( 'Woocommerce' ) && ! class_exists( 'Yoast_WooCommerce_SEO' ) ) { + $extensions['woocommerce-seo'] = (object) array( + 'url' => 'https://yoast.com/wordpress/plugins/yoast-woocommerce-seo/', + 'title' => __( 'Yoast WooCommerce SEO', 'wordpress-seo' ), + 'desc' => __( 'Seamlessly integrate WooCommerce with WordPress SEO and get extra features!', 'wordpress-seo' ) + ); + } + + foreach ( $extensions as $id => $extension ) { + $utm = '#utm_source=wordpress-seo-config&utm_medium=banner&utm_campaign=extension-page-banners'; + ?> +
+ +

title ); ?>

+
+

desc ); ?>

+

+ +

+
+ +
+
+ +
+
+ +
\ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/pages/metas.php b/wp-content/plugins/wordpress-seo/admin/pages/metas.php new file mode 100644 index 0000000..3eb66ca --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/metas.php @@ -0,0 +1,209 @@ +admin_header( true, WPSEO_Options::get_group_name( 'wpseo_titles' ), 'wpseo_titles' ); +?> + + + +
+
+ ' . __( 'Title settings', 'wordpress-seo' ) . ''; + echo $wpseo_admin_pages->checkbox( 'forcerewritetitle', __( 'Force rewrite titles', 'wordpress-seo' ) ); + echo '

' . __( 'WordPress SEO 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' ) . '

'; + + echo '

' . __( 'Title Separator', 'wordpress-seo' ) . '

'; + echo $wpseo_admin_pages->radio( 'separator', WPSEO_Option_Titles::get_instance()->get_separator_options(), '' ); + echo '

' . __( 'Choose the symbol to use as your title separator. This will display, for instance, between your post title and site name.', 'wordpress-seo' ) . ' ' . __( 'Symbols are shown in the size they\'ll appear in in search results.', 'wordpress-seo' ) . '

'; + + echo '

' . __( 'Sitewide meta settings', 'wordpress-seo' ) . '

'; + echo $wpseo_admin_pages->checkbox( 'noindex-subpages-wpseo', __( 'Noindex subpages of archives', 'wordpress-seo' ) ); + echo '

' . __( 'If you want to prevent /page/2/ and further of any archive to show up in the search results, enable this.', 'wordpress-seo' ) . '

'; + + echo $wpseo_admin_pages->checkbox( 'usemetakeywords', __( 'Use meta keywords tag?', 'wordpress-seo' ) ); + echo '

' . __( 'I don\'t know why you\'d want to use meta keywords, but if you want to, check this box.', 'wordpress-seo' ) . '

'; + + echo $wpseo_admin_pages->checkbox( 'noodp', __( 'Add noodp meta robots tag sitewide', 'wordpress-seo' ) ); + echo '

' . __( 'Prevents search engines from using the DMOZ description for pages from this site in the search results.', 'wordpress-seo' ) . '

'; + + echo $wpseo_admin_pages->checkbox( 'noydir', __( 'Add noydir meta robots tag sitewide', 'wordpress-seo' ) ); + echo '

' . __( 'Prevents search engines from using the Yahoo! directory description for pages from this site in the search results.', 'wordpress-seo' ) . '

'; + + echo '

' . __( 'Clean up the <head>', 'wordpress-seo' ) . '

'; + echo $wpseo_admin_pages->checkbox( 'hide-rsdlink', __( 'Hide RSD Links', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->checkbox( 'hide-wlwmanifest', __( 'Hide WLW Manifest Links', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->checkbox( 'hide-shortlink', __( 'Hide Shortlink for posts', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->checkbox( 'hide-feedlinks', __( 'Hide RSS Links', 'wordpress-seo' ) ); + ?> +
+
+ ' . __( 'Homepage', 'wordpress-seo' ) . ''; + echo $wpseo_admin_pages->textinput( 'title-home-wpseo', __( 'Title template', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->textarea( 'metadesc-home-wpseo', __( 'Meta description template', 'wordpress-seo' ), '', 'metadesc' ); + if ( $options['usemetakeywords'] === true ) { + echo $wpseo_admin_pages->textinput( 'metakey-home-wpseo', __( 'Meta keywords template', 'wordpress-seo' ) ); + } + } + else { + echo '

' . __( 'Homepage & Front page', 'wordpress-seo' ) . '

'; + echo '

' . sprintf( __( 'You can determine the title and description for the front page by %sediting the front page itself »%s', 'wordpress-seo' ), '', '' ) . '

'; + if ( get_option( 'page_for_posts' ) > 0 ) { + echo '

' . sprintf( __( 'You can determine the title and description for the blog page by %sediting the blog page itself »%s', 'wordpress-seo' ), '', '' ) . '

'; + } + } + ?> +
+
+ true ), 'objects' ); + if ( is_array( $post_types ) && $post_types !== array() ) { + foreach ( $post_types as $pt ) { + $warn = false; + if ( $options['redirectattachment'] === true && $pt->name == 'attachment' ) { + echo '
'; + $warn = true; + } + + $name = $pt->name; + echo '

' . esc_html( ucfirst( $pt->labels->name ) ) . '

'; + if ( $warn === true ) { + echo '

' . __( 'Take note:', 'wordpress-seo' ) . '

'; + + echo '

' . __( 'As you are redirecting attachment URLs to parent post URLs, these settings will currently only have an effect on unattached media items!', 'wordpress-seo' ) . '

'; + echo '

' . sprintf( __( 'So remember: If you change the %sattachment redirection setting%s in the future, the below settings will take effect for *all* media items.', 'wordpress-seo' ), '', '' ) . '

'; + } + + echo $wpseo_admin_pages->textinput( 'title-' . $name, __( 'Title template', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->textarea( 'metadesc-' . $name, __( 'Meta description template', 'wordpress-seo' ), '', 'metadesc' ); + if ( $options['usemetakeywords'] === true ) { + echo $wpseo_admin_pages->textinput( 'metakey-' . $name, __( 'Meta keywords template', 'wordpress-seo' ) ); + } + echo $wpseo_admin_pages->checkbox( 'noindex-' . $name, 'noindex, follow', __( 'Meta Robots', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->checkbox( 'showdate-' . $name, __( 'Show date in snippet preview?', 'wordpress-seo' ), __( 'Date in Snippet Preview', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->checkbox( 'hideeditbox-' . $name, __( 'Hide', 'wordpress-seo' ), __( 'WordPress SEO Meta Box', 'wordpress-seo' ) ); + + /** + * Allow adding a custom checkboxes to the admin meta page - Post Types tab + * @api WPSEO_Admin_Pages $wpseo_admin_pages The WPSEO_Admin_Pages object + * @api String $name The post type name + */ + do_action( 'wpseo_admin_page_meta_post_types', $wpseo_admin_pages, $name ); + + echo '
'; + if ( $warn === true ) { + echo '
'; + } + unset( $warn ); + } + unset( $pt ); + } + unset( $post_types ); + + + $post_types = get_post_types( array( 'public' => true, '_builtin' => false ), 'objects' ); + if ( is_array( $post_types ) && $post_types !== array() ) { + echo '

' . __( 'Custom Post Type Archives', 'wordpress-seo' ) . '

'; + echo '

' . __( 'Note: instead of templates these are the actual titles and meta descriptions for these custom post type archive pages.', 'wordpress-seo' ) . '

'; + + foreach ( $post_types as $pt ) { + if ( ! $pt->has_archive ) { + continue; + } + + $name = $pt->name; + + echo '

' . esc_html( ucfirst( $pt->labels->name ) ) . '

'; + echo $wpseo_admin_pages->textinput( 'title-ptarchive-' . $name, __( 'Title', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->textarea( 'metadesc-ptarchive-' . $name, __( 'Meta description', 'wordpress-seo' ), '', 'metadesc' ); + if ( $options['usemetakeywords'] === true ) { + echo $wpseo_admin_pages->textinput( 'metakey-ptarchive-' . $name, __( 'Meta keywords', 'wordpress-seo' ) ); + } + if ( $options['breadcrumbs-enable'] === true ) { + echo $wpseo_admin_pages->textinput( 'bctitle-ptarchive-' . $name, __( 'Breadcrumbs Title', 'wordpress-seo' ) ); + } + echo $wpseo_admin_pages->checkbox( 'noindex-ptarchive-' . $name, 'noindex, follow', __( 'Meta Robots', 'wordpress-seo' ) ); + } + unset( $pt ); + } + unset( $post_types ); + + ?> +
+
+ true ), 'objects' ); + if ( is_array( $taxonomies ) && $taxonomies !== array() ) { + foreach ( $taxonomies as $tax ) { + echo '

' . esc_html( ucfirst( $tax->labels->name ) ). '

'; + echo $wpseo_admin_pages->textinput( 'title-tax-' . $tax->name, __( 'Title template', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->textarea( 'metadesc-tax-' . $tax->name, __( 'Meta description template', 'wordpress-seo' ), '', 'metadesc' ); + if ( $options['usemetakeywords'] === true ) { + echo $wpseo_admin_pages->textinput( 'metakey-tax-' . $tax->name, __( 'Meta keywords template', 'wordpress-seo' ) ); + } + echo $wpseo_admin_pages->checkbox( 'noindex-tax-' . $tax->name, 'noindex, follow', __( 'Meta Robots', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->checkbox( 'hideeditbox-tax-' . $tax->name, __( 'Hide', 'wordpress-seo' ), __( 'WordPress SEO Meta Box', 'wordpress-seo' ) ); + echo '
'; + } + unset( $tax ); + } + unset( $taxonomies ); + + ?> +
+
+ ' . __( 'Author Archives', 'wordpress-seo' ) . ''; + echo $wpseo_admin_pages->textinput( 'title-author-wpseo', __( 'Title template', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->textarea( 'metadesc-author-wpseo', __( 'Meta description template', 'wordpress-seo' ), '', 'metadesc' ); + if ( $options['usemetakeywords'] === true ) { + echo $wpseo_admin_pages->textinput( 'metakey-author-wpseo', __( 'Meta keywords template', 'wordpress-seo' ) ); + } + echo $wpseo_admin_pages->checkbox( 'noindex-author-wpseo', 'noindex, follow', __( 'Meta Robots', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->checkbox( 'disable-author', __( 'Disable the author archives', 'wordpress-seo' ), '' ); + echo '

' . __( 'If you\'re running a one author blog, the author archive will always look exactly the same as your homepage. And even though you may not link to it, others might, to do you harm. Disabling them here will make sure any link to those archives will be 301 redirected to the homepage.', 'wordpress-seo' ) . '

'; + echo '
'; + echo '

' . __( 'Date Archives', 'wordpress-seo' ) . '

'; + echo $wpseo_admin_pages->textinput( 'title-archive-wpseo', __( 'Title template', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->textarea( 'metadesc-archive-wpseo', __( 'Meta description template', 'wordpress-seo' ), '', 'metadesc' ); + echo '
'; + echo $wpseo_admin_pages->checkbox( 'noindex-archive-wpseo', 'noindex, follow', __( 'Meta Robots', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->checkbox( 'disable-date', __( 'Disable the date-based archives', 'wordpress-seo' ), '' ); + echo '

' . __( 'For the date based archives, the same applies: they probably look a lot like your homepage, and could thus be seen as duplicate content.', 'wordpress-seo' ) . '

'; + + echo '

' . __( 'Special Pages', 'wordpress-seo' ) . '

'; + echo '

' . __( 'These pages will be noindex, followed by default, so they will never show up in search results.', 'wordpress-seo' ) . '

'; + echo '

' . __( 'Search pages', 'wordpress-seo' ) . '

'; + echo $wpseo_admin_pages->textinput( 'title-search-wpseo', __( 'Title template', 'wordpress-seo' ) ); + echo '

' . __( '404 pages', 'wordpress-seo' ) . '

'; + echo $wpseo_admin_pages->textinput( 'title-404-wpseo', __( 'Title template', 'wordpress-seo' ) ); + echo '
'; + ?> +
+
+ ' . __( 'Variables', 'wordpress-seo' ) . ''; + echo '
'; + echo '
'; + $wpseo_admin_pages->admin_footer(); \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/pages/network.php b/wp-content/plugins/wordpress-seo/admin/pages/network.php new file mode 100644 index 0000000..6b6f9a4 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/network.php @@ -0,0 +1,145 @@ +blogname ) ), 'updated' ); + } + else { + add_settings_error( 'wpseo_ms', 'settings_updated', sprintf( __( 'Blog %s not found.', 'wordpress-seo' ), esc_html( $restoreblog ) ), 'error' ); + } + unset( $restoreblog ); + } +} + +/* Set up selectbox dropdowns for smaller networks (usability) */ +$use_dropdown = true; +if ( get_blog_count() > 100 ) { + $use_dropdown = false; +} +else { + $sites = wp_get_sites( array( 'deleted' => 0 ) ); + if ( is_array( $sites ) && $sites !== array() ) { + $dropdown_input = array( + '-' => __( 'None', 'wordpress-seo' ), + ); + + foreach ( $sites as $site ) { + $dropdown_input[ $site['blog_id'] ] = $site['blog_id'] . ': ' . $site['domain']; + + $blog_states = array(); + if ( $site['public'] === '1' ) { + $blog_states[] = __( 'public', 'wordpress-seo' ); + } + if ( $site['archived'] === '1' ) { + $blog_states[] = __( 'archived', 'wordpress-seo' ); + } + if ( $site['mature'] === '1' ) { + $blog_states[] = __( 'mature', 'wordpress-seo' ); + } + if ( $site['spam'] === '1' ) { + $blog_states[] = __( 'spam', 'wordpress-seo' ); + } + if ( $blog_states !== array() ) { + $dropdown_input[ $site['blog_id'] ] .= ' [' . implode( ', ', $blog_states ) . ']'; + } + } + unset( $site, $blog_states ); + } + else { + $use_dropdown = false; + } + unset( $sites ); +} + + + +$wpseo_admin_pages->admin_header( false, 'wpseo-network-settings', 'wpseo_ms' ); + +$content = '
'; +$content .= wp_nonce_field( 'wpseo-network-settings', '_wpnonce', true, false ); + +/* @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 */ +$content .= $wpseo_admin_pages->select( + 'access', + __( 'Who should have access to the WordPress SEO settings', 'wordpress-seo' ), + array( + 'admin' => __( 'Site Admins (default)', 'wordpress-seo' ), + 'superadmin' => __( 'Super Admins only', 'wordpress-seo' ) + ), + 'wpseo_ms' +); + +if ( $use_dropdown === true ) { + $content .= $wpseo_admin_pages->select( + 'defaultblog', + __( 'New sites in the network inherit their SEO settings from this site', 'wordpress-seo' ), + $dropdown_input, + 'wpseo_ms' + ); + $content .= '

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

'; +} +else { + $content .= $wpseo_admin_pages->textinput( 'defaultblog', __( 'New sites in the network get the SEO settings from this site', 'wordpress-seo' ), 'wpseo_ms' ); + $content .= '

' . sprintf( __( 'Enter the %sSite ID%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' ), '', '' ) . '

'; +} + $content .= '

' . __( 'Take note :', 'wordpress-seo' ) . ' ' . __( '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' ) . '

'; + + +$content .= ''; +$content .= '
'; + +$wpseo_admin_pages->postbox( 'wpseo_network_settings', __( 'MultiSite Settings', 'wordpress-seo' ), $content ); + + +$content = '
'; +$content .= wp_nonce_field( 'wpseo-network-restore', '_wpnonce', true, false ); +$content .= '

' . __( 'Using this form you can reset a site to the default SEO settings.', 'wordpress-seo' ) . '

'; + +if ( $use_dropdown === true ) { + unset( $dropdown_input['-'] ); + $content .= $wpseo_admin_pages->select( + 'restoreblog', + __( 'Site ID', 'wordpress-seo' ), + $dropdown_input, + 'wpseo_ms' + ); +} +else { + $content .= $wpseo_admin_pages->textinput( 'restoreblog', __( 'Blog ID', 'wordpress-seo' ), 'wpseo_ms' ); +} + +$content .= ''; +$content .= '
'; + +$wpseo_admin_pages->postbox( 'wpseo-network-restore', __( 'Restore site to default settings', 'wordpress-seo' ), $content ); + +$wpseo_admin_pages->admin_footer( false ); diff --git a/wp-content/plugins/wordpress-seo/admin/pages/permalinks.php b/wp-content/plugins/wordpress-seo/admin/pages/permalinks.php new file mode 100644 index 0000000..f9a9f2b --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/permalinks.php @@ -0,0 +1,59 @@ +admin_header( true, WPSEO_Options::get_group_name( 'wpseo_permalinks' ), 'wpseo_permalinks' ); + +$options = get_option( 'wpseo_permalinks' ); + +$content = ''; + +$content .= $wpseo_admin_pages->checkbox( 'stripcategorybase', __( 'Strip the category base (usually /category/) from the category URL.', 'wordpress-seo' ) ); +$content .= '

' . sprintf( __( 'We suggest using %1$sFV Top Level Categories%2$s, if you insist on keeping this but do know that the feature is very error prone and not that important for your SEO.', 'wordpress-seo' ), '', '' ) . '

'; + +$content .= $wpseo_admin_pages->checkbox( 'trailingslash', __( 'Enforce a trailing slash on all category and tag URL\'s', 'wordpress-seo' ) ); +$content .= '

' . __( 'If you choose a permalink for your posts with .html, or anything else but a / on the end, this will force WordPress to add a trailing slash to non-post pages nonetheless.', 'wordpress-seo' ) . '

'; + +$content .= $wpseo_admin_pages->checkbox( 'cleanslugs', __( 'Remove stop words from slugs.', 'wordpress-seo' ) ); +$content .= '

' . __( 'This helps you to create cleaner URLs by automatically removing the stopwords from them.', 'wordpress-seo' ) . '

'; + +$content .= $wpseo_admin_pages->checkbox( 'redirectattachment', __( 'Redirect attachment URL\'s to parent post URL.', 'wordpress-seo' ) ); +$content .= '

' . __( 'Attachments to posts are stored in the database as posts, this means they\'re accessible under their own URL\'s if you do not redirect them, enabling this will redirect them to the post they were attached to.', 'wordpress-seo' ) . '

'; + +$content .= $wpseo_admin_pages->checkbox( 'cleanreplytocom', __( 'Remove the ?replytocom variables.', 'wordpress-seo' ) ); +$content .= '

' . __( 'This prevents threaded replies from working when the user has JavaScript disabled, but on a large site can mean a huge improvement in crawl efficiency for search engines when you have a lot of comments.', 'wordpress-seo' ) . '

'; + +$content .= $wpseo_admin_pages->checkbox( 'cleanpermalinks', __( 'Redirect ugly URL\'s to clean permalinks. (Not recommended in many cases!)', 'wordpress-seo' ) ); +$content .= '

' . __( 'People make mistakes in their links towards you sometimes, or unwanted parameters are added to the end of your URLs, this allows you to redirect them all away. Please note that while this is a feature that is actively maintained, it is known to break several plugins, and should for that reason be the first feature you disable when you encounter issues after installing this plugin.', 'wordpress-seo' ) . '

'; + +$wpseo_admin_pages->postbox( 'permalinks', __( 'Permalink Settings', 'wordpress-seo' ), $content ); + +/* @internal Important: Make sure the options added to the array here are in line with the options set in the WPSEO_Option_Permalinks::$force_transport_options property */ +$content = $wpseo_admin_pages->select( 'force_transport', __( 'Force Transport', 'wordpress-seo' ), array( 'default' => __( 'Leave default', 'wordpress-seo' ), 'http' => __( 'Force http', 'wordpress-seo' ), 'https' => __( 'Force https', 'wordpress-seo' ) ) ); +$content .= '

' . __( 'Force the canonical to either http or https, when your blog runs under both.', 'wordpress-seo' ) . '

'; + +$wpseo_admin_pages->postbox( 'canonical', __( 'Canonical Settings', 'wordpress-seo' ), $content ); + + +$content = $wpseo_admin_pages->checkbox( 'cleanpermalink-googlesitesearch', __( 'Prevent cleaning out Google Site Search URL\'s.', 'wordpress-seo' ) ); +$content .= '

' . __( 'Google Site Search URL\'s look weird, and ugly, but if you\'re using Google Site Search, you probably do not want them cleaned out.', 'wordpress-seo' ) . '

'; + +$content .= $wpseo_admin_pages->checkbox( 'cleanpermalink-googlecampaign', __( 'Prevent cleaning out Google Analytics Campaign & Google AdWords Parameters.', 'wordpress-seo' ) ); +$content .= '

' . __( 'If you use Google Analytics campaign parameters starting with ?utm_, check this box. You shouldn\'t use these btw, you should instead use the hash tagged version instead.', 'wordpress-seo' ) . '

'; + +$content .= $wpseo_admin_pages->textinput( 'cleanpermalink-extravars', __( 'Other variables not to clean', 'wordpress-seo' ) ); +$content .= '

' . __( 'You might have extra variables you want to prevent from cleaning out, add them here, comma separated.', 'wordpress-seo' ) . '

'; + +$wpseo_admin_pages->postbox( 'cleanpermalinksdiv', __( 'Clean Permalink Settings', 'wordpress-seo' ), $content ); + + +$wpseo_admin_pages->admin_footer(); \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/pages/rss.php b/wp-content/plugins/wordpress-seo/admin/pages/rss.php new file mode 100644 index 0000000..70081ef --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/rss.php @@ -0,0 +1,44 @@ +admin_header( true, WPSEO_Options::get_group_name( 'wpseo_rss' ), 'wpseo_rss' ); + +$content = '

' . __( "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' ) . '

'; +$rows = array(); + +$rows[] = array( + 'id' => 'rssbefore', + 'label' => __( 'Content to put before each post in the feed', 'wordpress-seo' ), + 'desc' => __( '(HTML allowed)', 'wordpress-seo' ), + 'content' => '', +); +$rows[] = array( + 'id' => 'rssafter', + 'label' => __( 'Content to put after each post', 'wordpress-seo' ), + 'desc' => __( '(HTML allowed)', 'wordpress-seo' ), + 'content' => '', +); +$rows[] = array( + 'label' => __( 'Explanation', 'wordpress-seo' ), + 'content' => '

' . __( 'You can use the following variables within the content, they will be replaced by the value on the right.', 'wordpress-seo' ) . '

' . + '' . + '' . + '' . + '' . + '' . + '
%%AUTHORLINK%%' . __( 'A link to the archive for the post author, with the authors name as anchor text.', 'wordpress-seo' ) . '
%%POSTLINK%%' . __( 'A link to the post, with the title as anchor text.', 'wordpress-seo' ) . '
%%BLOGLINK%%' . __( "A link to your site, with your site's name as anchor text.", 'wordpress-seo' ) . '
%%BLOGDESCLINK%%' . __( "A link to your site, with your site's name and description as anchor text.", 'wordpress-seo' ) . '
' +); +$wpseo_admin_pages->postbox( 'rssfootercontent', __( 'Content of your RSS Feed', 'wordpress-seo' ), $content . $wpseo_admin_pages->form_table( $rows ) ); + +$wpseo_admin_pages->admin_footer(); \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/pages/social.php b/wp-content/plugins/wordpress-seo/admin/pages/social.php new file mode 100644 index 0000000..2242612 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/social.php @@ -0,0 +1,233 @@ +' . esc_html__( 'Facebook Insights and Admins', 'wordpress-seo' ) . '
+ ' . sprintf( esc_html__( 'To be able to access your %sFacebook Insights%s for your site, you need to specify a Facebook Admin. This can be a user, but if you have an app for your site, you could use that. For most people a user will be "good enough" though.', 'wordpress-seo' ), '', '' ) . '

'; +$fbbuttons = array(); + +$clearall = false; + +$options = get_option( 'wpseo_social' ); + +if ( isset( $_GET['delfbadmin'] ) ) { + if ( wp_verify_nonce( $_GET['nonce'], 'delfbadmin' ) != 1 ) { + die( 'I don\'t think that\'s really nice of you!.' ); + } + + $id = sanitize_text_field( $_GET['delfbadmin'] ); + if ( isset( $options['fb_admins'][ $id ] ) ) { + $fbadmin = $options['fb_admins'][ $id ]['name']; + unset( $options['fb_admins'][ $id ] ); + update_option( 'wpseo_social', $options ); + add_settings_error( 'yoast_wpseo_social_options', 'success', sprintf( __( 'Successfully removed admin %s', 'wordpress-seo' ), $fbadmin ), 'updated' ); + unset( $fbadmin ); + } + unset( $id ); + + // Clean up the referrer url for later use + if ( isset( $_SERVER['REQUEST_URI'] ) ) { + $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'nonce', 'delfbadmin' ), sanitize_text_field( $_SERVER['REQUEST_URI'] ) ); + } +} + +elseif ( isset( $_GET['fbclearall'] ) ) { + if ( wp_verify_nonce( $_GET['nonce'], 'fbclearall' ) != 1 ) { + die( "I don't think that's really nice of you!." ); + } + // Reset to defaults, don't unset as otherwise the old values will be retained + $options['fb_admins'] = WPSEO_Options::get_default( 'wpseo_social', 'fb_admins' ); + $options['fbapps'] = WPSEO_Options::get_default( 'wpseo_social', 'fbapps' ); + $options['fbadminapp'] = WPSEO_Options::get_default( 'wpseo_social', 'fbadminapp' ); + update_option( 'wpseo_social', $options ); + add_settings_error( 'yoast_wpseo_social_options', 'success', __( 'Successfully cleared all Facebook Data', 'wordpress-seo' ), 'updated' ); + + // Clean up the referrer url for later use + if ( isset( $_SERVER['REQUEST_URI'] ) ) { + $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'nonce', 'fbclearall' ), sanitize_text_field( $_SERVER['REQUEST_URI'] ) ); + } +} + +elseif ( isset( $_GET['key'] ) ) { + if ( $_GET['key'] === $options['fbconnectkey'] ) { + if ( isset( $_GET['userid'] ) ) { + $user_id = sanitize_text_field( $_GET['userid'] ); + if ( ! isset( $options['fb_admins'][ $user_id ] ) ) { + $options['fb_admins'][ $user_id ]['name'] = sanitize_text_field( urldecode( $_GET['userrealname'] ) ); + $options['fb_admins'][ $user_id ]['link'] = sanitize_text_field( urldecode( $_GET['link'] ) ); + update_option( 'wpseo_social', $options ); + add_settings_error( 'yoast_wpseo_social_options', 'success', sprintf( __( 'Successfully added %s as a Facebook Admin!', 'wordpress-seo' ), '' . esc_html( $options['fb_admins'][ $user_id ]['name'] ) . '' ), 'updated' ); + } + else { + add_settings_error( 'yoast_wpseo_social_options', 'error', sprintf( __( '%s already exists as a Facebook Admin.', 'wordpress-seo' ), '' . esc_html( $options['fb_admins'][ $user_id ]['name'] ) . '' ), 'error' ); + } + unset( $user_id ); + } + elseif ( isset( $_GET['apps'] ) ) { + $apps = json_decode( stripslashes( $_GET['apps'] ), true ); + if ( is_array( $apps ) && $apps !== array() ) { + $options['fbapps'] = array( '0' => __( 'Do not use a Facebook App as Admin', 'wordpress-seo' ) ); + foreach ( $apps as $app ) { + $options['fbapps'][ $app['app_id'] ] = $app['display_name']; + } + update_option( 'wpseo_social', $options ); + add_settings_error( 'yoast_wpseo_social_options', 'success', __( 'Successfully retrieved your apps from Facebook, now select an app to use as admin.', 'wordpress-seo' ), 'updated' ); + } + else { + add_settings_error( 'yoast_wpseo_social_options', 'error', __( 'Failed to retrieve your apps from Facebook.', 'wordpress-seo' ), 'error' ); + } + unset( $apps, $app ); + } + } + + // Clean up the referrer url for later use + if ( isset( $_SERVER['REQUEST_URI'] ) ) { + $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'key', 'userid', 'userrealname', 'link', 'apps' ), sanitize_text_field( $_SERVER['REQUEST_URI'] ) ); + } +} + +// Refresh option after updates +$options = get_option( 'wpseo_social' ); + +if ( is_array( $options['fb_admins'] ) && $options['fb_admins'] !== array() ) { + $clearall = true; +} + +if ( is_array( $options['fbapps'] ) && $options['fbapps'] !== array() ) { + $clearall = true; +} + +$app_button_text = __( 'Use a Facebook App as Admin', 'wordpress-seo' ); +if ( is_array( $options['fbapps'] ) && $options['fbapps'] !== array() ) { + // @todo [JRF => whomever] use WPSEO_Admin_Pages->select() method ? + $fbconnect .= ' +

' . __( 'Select an app to use as Facebook admin:', 'wordpress-seo' ) . '

+ +

'; + + $app_button_text = __( 'Update Facebook Apps', 'wordpress-seo' ); +} + +if ( $options['fbadminapp'] == 0 ) { + $button_text = __( 'Add Facebook Admin', 'wordpress-seo' ); + $primary = true; + if ( is_array( $options['fb_admins'] ) && $options['fb_admins'] !== array() ) { + $fbconnect .= ' +

' . __( 'Currently connected Facebook admins:', 'wordpress-seo' ) . '

+
    '; + $nonce = wp_create_nonce( 'delfbadmin' ); + + foreach ( $options['fb_admins'] as $admin_id => $admin ) { + $admin_id = esc_attr( $admin_id ); + $fbconnect .= ' +
  • ' . esc_html( $admin['name'] ) . ' - X
  • '; + } + $fbconnect .= ' +
'; + $button_text = __( 'Add Another Facebook Admin', 'wordpress-seo' ); + $primary = false; + } + $but_primary = ''; + if ( $primary ) { + $but_primary = '-primary'; + } + $fbbuttons[] = ' + ' . $button_text . ''; +} + +$fbbuttons[] = ' + ' . esc_html( $app_button_text ) . ''; + +if ( $clearall ) { + $fbbuttons[] = ' + ' . __( 'Clear all Facebook Data', 'wordpress-seo' ) . ' '; +} + +if ( is_array( $fbbuttons ) && $fbbuttons !== array() ) { + $fbconnect .= ' +

' . implode( '', $fbbuttons ) . '

'; +} + +$wpseo_admin_pages->admin_header( true, WPSEO_Options::get_group_name( 'wpseo_social' ), 'wpseo_social' ); +?> + + + +
+ '; + echo $wpseo_admin_pages->checkbox( 'opengraph', __( 'Add Open Graph meta data', 'wordpress-seo' ) ); + echo '

'; + echo'

' . __( 'Add Open Graph meta data to your site\'s <head> section. You can specify some of the ID\'s that are sometimes needed below:', 'wordpress-seo' ) . '

'; + echo $fbconnect; + echo $wpseo_admin_pages->textinput( 'facebook_site', __( 'Facebook Page URL', 'wordpress-seo' ) ); + if ( 'posts' == get_option( 'show_on_front' ) ) { + echo '

' . esc_html__( 'Frontpage settings', 'wordpress-seo' ) . '

'; + echo $wpseo_admin_pages->media_input( 'og_frontpage_image', __( 'Image URL', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->textinput( 'og_frontpage_title', __( 'Title', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->textinput( 'og_frontpage_desc', __( 'Description', 'wordpress-seo' ) ); + + // Offer copying of meta description + $meta_options = get_option( 'wpseo_titles' ); + echo ''; + echo '

' . __( 'Copy home meta description', 'wordpress-seo' ) . '

'; + + echo '

' . 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' ) . '

'; + } + echo '

' . esc_html__( 'Default settings', 'wordpress-seo' ) . '

'; + echo $wpseo_admin_pages->media_input( 'og_default_image', __( 'Image URL', 'wordpress-seo' ) ); + echo '

' . esc_html__( 'This image is used if the post/page being shared does not contain any images.', 'wordpress-seo' ) . '

'; + do_action( 'wpseo_admin_opengraph_section' ); + ?> +
+ +
+ '; + printf( esc_html__( 'Note that for the Twitter Cards to work, you have to check the box below and then validate your Twitter Cards through the %1$sTwitter Card Validator%2$s.', 'wordpress-seo' ), '', '' ); + echo '

'; + echo '

'; + echo $wpseo_admin_pages->checkbox( 'twitter', __( 'Add Twitter card meta data', 'wordpress-seo' ) ); + echo '

'; + echo'

' . __( 'Add Twitter card meta data to your site\'s <head> section.', 'wordpress-seo' ) . '

'; + echo $wpseo_admin_pages->textinput( 'twitter_site', __( 'Site Twitter Username', 'wordpress-seo' ) ); + echo $wpseo_admin_pages->select( 'twitter_card_type', __( 'The default card type to use', 'wordpress-seo' ), WPSEO_Option_Social::$twitter_card_types ); + do_action( 'wpseo_admin_twitter_section' ); + ?> +
+ +
+ '; + echo $wpseo_admin_pages->checkbox( 'googleplus', __( 'Add Google+ specific post meta data', 'wordpress-seo' ) ); + echo '

'; + + echo $wpseo_admin_pages->textinput( 'plus-publisher', __( 'Google Publisher Page', 'wordpress-seo' ) ); + echo '

' . 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' ) . '

'; + do_action( 'wpseo_admin_googleplus_section' ); + ?> +
+ +admin_footer(); \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/admin/pages/xml-sitemaps.php b/wp-content/plugins/wordpress-seo/admin/pages/xml-sitemaps.php new file mode 100644 index 0000000..919652f --- /dev/null +++ b/wp-content/plugins/wordpress-seo/admin/pages/xml-sitemaps.php @@ -0,0 +1,102 @@ + whomever] check for other sitemap plugins which may conflict ? + * @todo - [JRF => whomever] check for existance of .xls rewrite rule in .htaccess from + * google-sitemaps-plugin/generator and remove as it will cause errors for our sitemaps + * (or inform the user and disallow enabling of sitemaps ) + * @todo - [JRF => whomever] check if anything along these lines is already being done + */ + + +if ( ! defined( 'WPSEO_VERSION' ) ) { + header( 'Status: 403 Forbidden' ); + header( 'HTTP/1.1 403 Forbidden' ); + exit(); +} + +global $wpseo_admin_pages; + +$wpseo_admin_pages->admin_header( true, WPSEO_Options::get_group_name( 'wpseo_xml' ), 'wpseo_xml' ); + +$options = get_option( 'wpseo_xml' ); + +$content = $wpseo_admin_pages->checkbox( 'enablexmlsitemap', __( 'Check this box to enable XML sitemap functionality.', 'wordpress-seo' ), false ); +$content .= '
'; +if ( wpseo_is_nginx() ) { + $content .= '
'; + $content .= '

' . __( 'As you\'re on NGINX, you\'ll need the following rewrites:', 'wordpress-seo' ) . '

'; + $content .= '
rewrite ^/sitemap_index\.xml$ /index.php?sitemap=1 last;
+rewrite ^/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;
'; + $content .= '
'; +} + +if ( $options['enablexmlsitemap'] === true ) { + $content .= '

' . sprintf( esc_html__( 'You can find your XML Sitemap here: %sXML Sitemap%s', 'wordpress-seo' ), '', '' ) . '

' . __( 'You do not need to generate the XML sitemap, nor will it take up time to generate after publishing a post.', 'wordpress-seo' ) . '

'; +} else { + $content .= '

' . __( 'Save your settings to activate XML Sitemaps.', 'wordpress-seo' ) . '

'; +} + +// When we write the help tab for this we should definitely reference this plugin :https://wordpress.org/plugins/edit-author-slug/ +$content .= '

' . __( 'User sitemap', 'wordpress-seo' ) . '

'; +$content .= $wpseo_admin_pages->checkbox( 'disable_author_sitemap', __( 'Disable author/user sitemap', 'wordpress-seo' ), false ); + +$content .= '
'; +$content .= '

' . __( 'Exclude users without posts', 'wordpress-seo' ) . '
'; +$content .= $wpseo_admin_pages->checkbox( 'disable_author_noposts', __( 'Disable all users with zero posts', 'wordpress-seo' ), false ); + +$roles = wpseo_get_roles(); +if ( is_array( $roles ) && $roles !== array() ) { + $content .= '

' . __( 'Exclude userroles', 'wordpress-seo' ) . '
'; + $content .= __( 'Please check the appropriate box below if there\'s a user role that you do NOT want to include in your sitemap:', 'wordpress-seo' ) . '

'; + foreach ( $roles as $role_key => $role_name ) { + $content .= $wpseo_admin_pages->checkbox( 'user_role-' . $role_key . '-not_in_sitemap', $role_name ); + } +} +$content .= '
'; + +$content .= '
'; +$content .= '

' . __( 'General settings', 'wordpress-seo' ) . '

'; +$content .= '

' . __( 'After content publication, the plugin automatically pings Google and Bing, do you need it to ping other search engines too? If so, check the box:', 'wordpress-seo' ) . '

'; +$content .= $wpseo_admin_pages->checkbox( 'xml_ping_yahoo', __( 'Ping Yahoo!', 'wordpress-seo' ), false ); +$content .= $wpseo_admin_pages->checkbox( 'xml_ping_ask', __( 'Ping Ask.com', 'wordpress-seo' ), false ); + + +$post_types = apply_filters( 'wpseo_sitemaps_supported_post_types', get_post_types( array( 'public' => true ), 'objects' ) ); +if ( is_array( $post_types ) && $post_types !== array() ) { + $content .= '

' . __( 'Exclude post types', 'wordpress-seo' ) . '

'; + $content .= '

' . __( 'Please check the appropriate box below if there\'s a post type that you do NOT want to include in your sitemap:', 'wordpress-seo' ) . '

'; + foreach ( $post_types as $pt ) { + $content .= $wpseo_admin_pages->checkbox( 'post_types-' . $pt->name . '-not_in_sitemap', $pt->labels->name . ' (' . $pt->name . ')' ); + } +} + + +$taxonomies = apply_filters( 'wpseo_sitemaps_supported_taxonomies', get_taxonomies( array( 'public' => true ), 'objects' ) ); +if ( is_array( $taxonomies ) && $taxonomies !== array() ) { + $content .= '

' . __( 'Exclude taxonomies', 'wordpress-seo' ) . '

'; + $content .= '

' . __( 'Please check the appropriate box below if there\'s a taxonomy that you do NOT want to include in your sitemap:', 'wordpress-seo' ) . '

'; + foreach ( $taxonomies as $tax ) { + if ( isset( $tax->labels->name ) && trim( $tax->labels->name ) != '' ) { + $content .= $wpseo_admin_pages->checkbox( 'taxonomies-' . $tax->name . '-not_in_sitemap', $tax->labels->name . ' (' . $tax->name . ')' ); + } + } +} + + +$content .= '
'; +$content .= '

' . __( 'Entries per page', 'wordpress-seo' ) . '

'; +$content .= '

' . sprintf( __( 'Please enter the maximum number of entries per sitemap page (defaults to %s, you might want to lower this to prevent memory issues on some installs):', 'wordpress-seo' ), WPSEO_Options::get_default( 'wpseo_xml', 'entries-per-page' ) ) . '

'; +$content .= $wpseo_admin_pages->textinput( 'entries-per-page', __( 'Max entries per sitemap page', 'wordpress-seo' ) ); + +$content .= '
'; +$content .= '
'; + +$wpseo_admin_pages->postbox( 'xmlsitemaps', __( 'XML Sitemap', 'wordpress-seo' ), $content ); + +do_action( 'wpseo_xmlsitemaps_config' ); + +$wpseo_admin_pages->admin_footer(); \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/changelog.txt b/wp-content/plugins/wordpress-seo/changelog.txt new file mode 100644 index 0000000..549fd45 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/changelog.txt @@ -0,0 +1,1269 @@ += 1.4.17 = + +* Missed a line in the commit of the option to stop stop words cleaning. + += 1.4.16 = + +* Fix for compatibility with NextGen Gallery. + +* Enhancements + * Add option to enable slug stop word cleaning, find it under SEO -> Permalinks. It's on by default. + * Remove tracking variables from the Yoast Tracking that weren't used. + +* i18n + * Updated de_DE, fa_IR, fi, hu_HU, it_IT, pl_PL, sv_SE and tr_TK + += 1.4.15 = + +* Bugfixes + * Fix the white XML sitemap errors caused by non-working XSL. + * Fixed the errors in content analysis reporting an H2 was not found when it was really there. + * Fix slug stopwords removal, props [amm350](https://github.com/amm350). + * Fix PHP Notice logged when site has capabilities created without 3rd value in args array, props [mbijon](https://github.com/mbijon). + * Fix the fact that meta description template for archive pages didn't work, props [MarcQueralt](https://github.com/MarcQueralt). + * Prevent wrong shortcodes (that echo instead of return) from causing erroneous output. + * Fix edge cases issue for keyword in first paragraph test not working. + * Revert change in 1.4.14 that did a `do_shortcode` while in the `head` to retrieve images from posts, as too many plugins crash then, instead added `wpseo_pre_analysis_post_content` filter there as well. + += 1.4.14 = + +This release contains tons and tons of bugfixes, thanks in *large* part to [Jrf](http://profiles.wordpress.org/jrf), who now has commit rights to the code on Github directly. Please join me in thanking her for her efforts! + +* Notes: + * Our GitHub repository moved to [https://github.com/Yoast/wordpress-seo](https://github.com/Yoast/wordpress-seo), old links should redirect but please check. + +* Bugfixes + * Switch to stock autocomplete file and fix clash with color picker, props [Heinrich Luehrsen](http://www.luehrsen-heinrich.de/). + * Prevent strip category base code from breaking Custom Post Type rewrites, props [Steve Hulet](http://about.me/stevehulet). + * Fixed [issue with canonical links](http://wordpress.org/support/topic/serious-canonical-issue-with-paginated-posts) on last page of paginated posts - props [maxbugfiy](http://wordpress.org/support/profile/maxbuxfiy) + * Fixed bug in shortcode removal from meta description as reported by [professor44](http://profiles.wordpress.org/professor44/) - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed bug preventing saving of taxonomy meta data on first try - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed small (potential) issue in wpseo_title_test() - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed bug where RSS excerpt would be double wrapped in `<p>` tags as reported by [mikeprince](http://profiles.wordpress.org/mikeprince) - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed HTML validation error: Duplicate id Twitter on Social tab - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed undefined index notice as reported by [szepeviktor](http://profiles.wordpress.org/szepeviktor). + * Fixed error in a database query as reported by [Watch Teller](http://wordpress.org/support/profile/watchteller) - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed small issue with how styles where enqueued/registered - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed bug in alt text of score dots as [reported by Rocket Pixels](http://wordpress.org/support/topic/dots-on-hover-over-show-na-tooltip) - props [Jrf](http://profiles.wordpress.org/jrf). + * Applied best practices to all uses of preg_ functions fixing some bugs in the process - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed bug in processing of `%%ct_%%` as [reported by Joy](http://wordpress.org/support/topic/plugin-dies-when-processing-ct_desc_) - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed: no more empty og: or twitter: tags. Also added additional escaping where needed - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed: Meta description tag discovery looked in parent theme header file even when a child theme is the current theme - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed: Using the 'Fix it' button would remove the meta description tag from the parent theme header file, even when a child theme is the current theme - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed: Using the 'Fix it' button would fail if it had already been used once (i.e. if a wpseo backup file already existed) - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed repeated unnecessary meta description tag checks on each visit to dashboard page - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed: Meta description 'Fix it' feedback message was not shown - props [Jrf](http://profiles.wordpress.org/jrf). + * Mini-fix for plugin_dir_url - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed Author Highlighting to only show authors as possible choice for Google+ Plus author as reported by [Sanoma](https://github.com/jdevalk/wordpress-seo/issues/131) - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed `adjacent_rel_links()` for Genesis users - props [benjamin74](https://github.com/benjamin74) for reporting. + * Replace jQuery .live function with .on(), as .live() has been deprecated and deleted. Props [Viktor Kostadinov](http://www.2buy1click.com/) & [Taco Verdonschot](https://yoast.com/about-us/taco-verdonschot/). + * Fix how breadcrumbs deal with taxonomy orders. Props [Gaya Kessler](http://www.gayadesign.com/). + * Fixed some PHP warnings + +* Enhancements + * Added `wpseo_pre_analysis_post_content` filter. This allows plugins to add content to the content that is analyzed by the page analysis functionality. + * Added `wpseo_genesis_force_adjacent_rel_home` filter to allow forcing of rel=next / rel=prev links on the homepage pagination for Genesis users, they're off by default. + * Make `$wpseo_metabox` a global, props [Peter Chester](http://tri.be/). + * No need to show Twitter image when OpenGraph is showing, props [Gary Jones](http://garyjones.co.uk/). + * Make sure WPML works again, props [dominykasgel](https://github.com/dominykasgel). + * Added checks for the meta description tag on theme switch, on theme update and on (re-)activation of the WP SEO plugin including a visual warning if the check would warrant it - props [Jrf](http://profiles.wordpress.org/jrf). + * Added the ability to request re-checking a theme for the meta description tag. Useful when you've manually removed it (to get rid of the warning), inspired by [tzeldin88](http://wordpress.org/support/topic/plugin-wordpress-seo-by-yoast-your-theme-contains-a-meta-description-which-blocks-wordpress-seo) - props [Jrf](http://profiles.wordpress.org/jrf). + * OpenGraph image tags will now also be added for images added to the post via shortcodes, as suggested by [msebald](http://wordpress.org/support/topic/ogimage-set-to-default-image-but-articlepage-has-own-images?replies=3#post-4436317) - props [Jrf](http://profiles.wordpress.org/jrf). + * Added 'wpseo_breadcrumb_single_link_with_sep' filter which allows users to filter a complete breadcrumb element including the separator - props [Jrf](http://profiles.wordpress.org/jrf). + * Added 'wpseo_stopwords' filter which allows users to filter the stopwords list - props [Jrf](http://profiles.wordpress.org/jrf). + * Added 'wpseo_terms' filter which allows users to filter the terms string - props [Jrf](http://profiles.wordpress.org/jrf). + * Hide advanced tab for users for which it has been disabled, as [suggested by jrgmartin](https://github.com/jdevalk/wordpress-seo/issues/93) - props [Jrf](http://profiles.wordpress.org/jrf). + * Updated Facebook supported locales list for og:locale + +* i18n + * Updated languages tr_TK, fi, ru_RU & da_DK + * Added language hi_IN + * Updated wordpress-seo.pot file + += 1.4.13 = + +* Bugfixes + * Fixed ampersand (&) in site title in Title Templates loading as & + * Fixed error when focus keyword contains a / - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed issue with utf8 characters in meta description - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed undefined property error - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed undefined index error for the last page of the tour - props [Jrf](http://profiles.wordpress.org/jrf). + * Fixed undefined index error for images without alt - props [Jrf](http://profiles.wordpress.org/jrf). + * Fix output of author for Google+ when using a static front page - props [petervanderdoes](https://github.com/petervanderdoes). + * Keyword density calculation not working when special character in focus keyword - props [siriuzwhite](https://github.com/siriuzwhite). + * Reverse output buffer cleaning for XML sitemaps, as that collides with WP Super Cache, thanks to [Rarst](https://github.com/Rarst) for finding this. + * Fix canonical and rel=prev / rel=next links for paginated home pages using index.php links. + * Fixed og:title not following title settings. +* Enhancements + * Improved breadcrumbs and titles for 404 pages - props [Jrf](http://profiles.wordpress.org/jrf). + * Moved XSL stylesheet from a static file in wp-content folder to a dynamic one, allowing it to work for sites that prevented the wp-content dir from being opened directly, f.i. through Sucuri's hardening. + * Added a link in the XSL pointing back to the sitemap index on individual sitemaps. + * When remove replytocom is checked in the permalink settings, these are now also redirected out. + * Added filters to OpenGraph output functions that didn't have them yet. + += 1.4.12 = + +* Bugfixes + * Submit button displays again on Titles & Metas page. + * SEO Title now calculates length correctly. + * Force rewrite titles should no longer reset wrongly on update. + += 1.4.11 = + +* i18n + * Updated de_DE, ru_RU, zh_CN. +* Bugfixes + * Make rel="publisher" markup appear on every page. + * Prevent empty property='article:publisher' markup from being output . + * Fixed twitter:description tag should only appears if OpenGraph is inactive. + * og:description will default to get_the_excerpt when meta description is blank (similar to how twitter:description works). + * Fixes only 25 tags (and other taxonomy) are being indexed in taxonomy sitemaps. + * Fix lastmod dates for taxonomies in XML sitemap index file. +* Enhancements + * Changed Social Admin section to have a tab-layout. + * Moved Google+ section from Homepage tab of Titles & Metas to Social tab. + * Make twitter:domain use WordPress site name instead of domain name. + * Added more output filters in the Twitter class. + += 1.4.10 = + +* Fixes + * Caching was disabled in certain cases, this update fixes that. +* Enhancements + * Added option to disable author sitemap. + * If author pages are disabled, author sitemaps are now automatically disabled. + += 1.4.9 = + +* i18n + * Updated .pot file + * Updated ar, da_DK, de_DE, el_GR, es_ES, fa_IR, fr_FR, he_IL, id_ID, nl_NL, ro_RO, sv_SE & tr_TK + * Added hr & sl_SI + * Many localization fixes +* Bugfixes + * Fixed sitemap "loc" element to have encoded entities. + * Honor the language setting if other plugins set the language. + * sitemap.xml will now redirect to sitemap_index.xml if it doesn't exist statically. + * Added filters 'wpseo_sitemap_exclude_post_type' and 'wpseo_sitemap_exclude_taxonomy' to allow themes/plugins to exclude entries in the XML sitemap. + * Added RTL support, some CSS fixes. + * Focus word gets counted in meta description when defined by a template. + * Fixed some bugs with the focus keyword in the first paragraph test. + * Fixed display bug in SEO Title column when defined by a template ('Page # of #'). + * Fixed a few strict notices that would pop up in WP 3.6. + * Prevent other plugins from overriding the WP SEO menu position. + * Enabled the advanced tab for site-admins on a multi-site install. + * Fixed post save error when page analysis is disabled. + * OpenGraph frontpage og:description and og:image tags now properly added to the frontpage. +* Enhancements + * Added an HTML sitemap shortcode [wpseo_sitemap]. + * Added an XML sitemap listing the author profile URLs. + * Added detection of Yoast's robots meta plugin and All In One SEO plugins, plugin now gives a notice to import settings and disable those plugins. + * Prevent empty image tags in Twitter Cards - props [Mike Bijon](https://github.com/mbijon). + * Add new `twitter:domain` tag - props [Mike Bijon](https://github.com/mbijon). + * Add support for Facebooks new OG tags for media publishers. + * Allow authorship to be removed per post type. + += 1.4.7 = + +* Properly fix security bug that should've been fixed in 1.4.5. +* Move from using several $options arrays in the frontend to 1 class wide option. +* Instead of firing all plugin options as function within head function, attach them to `wpseo_head` action, allowing easier filtering and changing. +* Where possible, use larger images for Facebook Opengraph. +* Add several filters and actions around social settings. + += 1.4.6 = + +* Fix a possible fatal error in tracking. + += 1.4.5 = + +* Bug fixes: + * Fix security issue which allowed any user to reset settings. + * Allow saving of SEO metadata for attachments. + * Set the max-width of the snippet preview to 520px to look more like Google search results, while still allowing it to work on lower resolutions. +* Enhancements: + * Remove the shortlink http header when the hide shortlink checkbox is checked. + * Added a check on focus keyword in the page analysis functionality, checking whether a focus keyword has already been used before. + * Update how the tracking class calculates users to improve speed. + += 1.4.4 = + +* Fix changelog for 1.4.3 +* Bugfixes + * Fix activation bug. +* i18n + * Updated es_ES, id_ID, he_IL. + += 1.4.3 = + +* Bugfixes + * Register core SEO menu at a lower than default prio so other plugins can tie in more easily. + * Remove alt= from page analysis score divs. + * Make site tracking use the site hash consistently between plugins. + * Improve popup pointer removal. + += 1.4.2 = + +* Bugfixes + * Made the sitemaps class load in backend too so it always generates rewrites correctly. + * Changed > to /> in class-twitter.php for validation as XHTML. + * Small fix in metabox CSS for small screens (thx [Ryan Hellyer](http://ryanhellyer.net)). + * Load classes on plugins_loaded instead of immediately on load to allow WPML to filter options. +* i18n + * Updated bs_BA, cs_CZ, da_DK, de_DE, fa_IR, fr_FR, he_IL, hu_HU, id_ID, it_IT, nl_NL, pl_PL, pt_BR, ru_RU and tr_TR + += 1.4.1 = + +* i18n: + * Updated .pot file + * Updated bg_BG, bs_BA, cs_CZ, fa_IR, hu_HU, pl_PL & ru_RU +* Bugfixes: + * Focus keyword check now works again in all cases. + * Fix typo in Video SEO banner. +* Enhancements: + * Don't show banners for plugins you already have. + += 1.4 = + +* i18n & documentation: + * Updated Hebrew (he_IL) + * Updated Italian (it_IT) + * Updated Dutch (nl_NL) + * Updated Swedish (sv_SE) + * Updated some strings to fix typos. + * Removed affiliate links from readme.txt. +* Bugfixes: + * Fixed a bug in saving post meta details for revisions. + * Prevent an error when there are no posts for post type. + * Fix the privacy warning to point to the right place. +* Enhancements: + * Slight performance improvement in functionality by not resetting query when its not needed (kudos to @Rarst). + * Slight performance improvement in options call by adding some caching (kudos to @Rarst as well). + * Changed inner workings of search engine ping, adding YOAST_SEO_PING_IMMEDIATELY constant to allow immediate ping on publish. + * Changed design of meta box, moving much of the help text out in favor of clicking on a help icon. + * Removed Linkdex branding from page analysis functionality. + += 1.3.4.4 = + +* Bug with revisions in XML sitemap for some weird combinations. +* Improved logic for rel=publisher on frontpage. +* Allow variables in meta description for post type archive. +* Improved counting of images for page analysis. +* updated Turkish (tr_TR) +* updated Russian (ru_RU) +* updated Indonesian (id_ID) +* updated French (fr_FR) +* updated Czech (cs_CZ) +* added Japanese (ja) + += 1.3.4.3 = + +* Regex annoyances anyone? Sigh. Bug fixed. + += 1.3.4.2 = + +* Added missing filter for meta box priority. +* Fixed bug in JS encoding. + += 1.3.4.1 = + +* Bug in page analysis regex. + += 1.3.4 = + +* Fix bug in custom field value retrieval for new drafts. +* Fix bug in meta box value for checkboxes (only used currently in News extension). +* Remove redirect added in 1.3.3 as it seems to cause loops on some servers, will investigate later. +* Add option to filter `wpseo_admin_pages` so more pages can use WP SEO admin stylesheets. +* Prevent notice for images without alt tags. +* Use mb_string when possible. + += 1.3.3 = + +* Properly `$wpdb->prepare` all queries that need preparing. +* Fix wrong escaping in admin pointers. +* Make %%currentdate%% and %%currenttime%% variables respect WP date format settings. +* Add %%currentday%% format. +* Force remove Jetpack OpenGraph. +* Fix the weird addition of `noindex, nofollow` on URLs with ?replytocom that was added in 3.5. +* Force XML sitemap to be displayed on the proper domain URL, so XSLT works. + += 1.3.2 = + +* Updated wordpress-seo.pot +* Updated Turkish (tr_TR) filename. +* Updated Spanish (es_ES) translation. +* Fixed bug where non-admin users couldn't save their profile updates. +* Fixed bug with the same OpenGraph image appearing multiple times. +* Fixed bug that would prevent import and export of plugin settings. +* Try to do a redirect back after saving settings. +* Properly allow for attachment pages in XML sitemaps, default them to off. +* Fixed annoying bug where checkboxes wouldn't display as "checked" even when the value was set to true. +* Show post type name and taxonomy name (as opposed to label) next to labels in XML sitemap settings to more easily identify post types and taxonomies. +* Switch tracking to a daily cronjob instead of an admin process to prevent tracking from slowing down admin interface. +* Focus keyword detection now properly works for diacritical focus keywords as well. +* Properly apply filters to meta desc and titles in admin grid. +* Properly detect new versions of Facebook plugin too. +* Allow changing of the number of posts per XML sitemap, to prevent memory issues on certain installs. + += 1.3.1.1 = + +* Some of that escaping was too aggressive. + += 1.3.1 = + +* Fix somewhat too aggressive escaping of content. +* Added notice text for non-existing .htaccess file / robots.txt file. + += 1.3 = + +* Long list of small fixes and improvements to code best practices after Sucuri review. Fixes 3 small security issues. +* Updated .pot file +* Updated Danish (da_DK), Indonesian (id_ID), Chinese (zh_CN), Russian (ru_RU), Norwegian (nb_NO), Turkish (tr_TK), Hebrew (he_IL) and Persian (fa_IR). +* Added Arabic (ar), Catalan (ca) and Romanian (ro_RO). + += 1.2.8.7 = + +* Fixed %%category%% and %%tag%% and some other variables that weren't working since 1.2.8.6. + += 1.2.8.6 = + +* Revert gplus changes in 1.2.8.5 that were causing issues. +* Fix a tracking timeout. +* Fix a bunch of notices throughout variables functions. + += 1.2.8.5 = + +* Fixed a bug for MultiSite due to a missing attribute in calling `get_admin_url`. +* Updated Hebrew (he_IL), Dutch (nl_NL) French (fr_FR), Czech (cs_CZ), Italian (it_IT), Brazilian Portuguese (pt_BR). +* Added Norwegian (nb_NO) and Portuguese (pt_PT). +* Added a `wpseo_robots` filter for the robots meta tag. +* Fixed integration with the [Facebook plugin](https://wordpress.org/plugins/facebook). + += 1.2.8.4 = + +* Fix for double title issues with themes that filter `wp_title`, by having WP SEO filter a bit later in the process + (but no too late because the genesis wrap filter is on 30). +* Improved Twitter Card functionality: twitter meta tags now appear above OpenGraph meta tags. +* Updated a bunch of languages: da_DK, de_DE, es_ES, fa_IR, fr_FR, he_IL, hu, hu_HU, it_IT, nl_NL, pt_BR, ru_RU, tr_TK, zh_CN. +* Added Czech (cs_CZ) and Spanish - Venezuela (es_VE). + += 1.2.8.3 = + +* Updated all the languages in hopes to fix the empty text strings. +* Added basic translations for Danish (da_DK) and Finnish (fi). + += 1.2.8.2 = + +* "Fix" for tracking popup with empty buttons. + +* Updated languages + * French fr_FR + * Dutch nl_NL + * Turkish tr_TK + += 1.2.8.1 = + +* Fixed one s, that caused a fatal error. Sigh. + += 1.2.8 = + +* Bug fixes: + * Fix for images not showing up in XML sitemap. + * Fix to allow breadcrumb titles to once again be set for CPT archive pages. + * Prevent empty rel=publisher link from being put out. + * Several fixes to the strip category base settings. + * Several fixes to the hardcoded meta description recognition code. + * Prevent title testing from priming the cache. + * Prevent upgrading from a recent version to force a title test, no longer overrides manual force rewrite settings. + * Fix paginated singular post / page issue when single isn't paginated. +* Enhancements: + * No longer show .htaccess editor when on NGINX. + * Move tracking to its own file, switching to my own tracking instead of PressTrends so more specific options can be tracked. + * Tracking can now be enabled and disabled from the SEO Dashboard screen. +* Documentation: + * Added rewrite rules for NGINX to FAQ. + * Now showing rewrite rules for NGINX on XML sitemaps settings page. + += 1.2.7 = + +* Fixed compatibility with 3.3 and lower. + += 1.2.6 = + +* Enhancements: + * Added (optional) PressTrends tracking to get some more info on common configurations of WP to test for. + * Made config page loading even faster. + * Added a link to my [Amazon wishlist](http://amzn.com/w/CBV7CEOJJH98) to the plugin sidebar in the admin ;) + * Added a check to see whether your theme contains a hardcoded meta description, and options to remove it. + * Added a Google+ Publisher input option for the homepage. + * Added the option to allow showing the date in the snippet preview per post type. + * Removed aggressive cache deleting in the XML sitemaps. Could cause issues with some plugins but should increase speed. + * Force the XML Sitemap to return a 200 OK Header. +* Deprecated functionality: + * The breadcrumbs functionality no longer automatically hooks as this was giving too many issues. +* Bug fixes: + * Prevent adding trailing slash on paged posts when force trailing slash on categories and tags is on. + * Breadcrumbs now properly use `home_url` instead of `site_url`. + * Simplify shortcode stripping, to make it actually work. + * Prevent several notices in XML Sitemaps class. +* Textual / documentation changes: + * Add a "without @" notice to the Twitter username input field. + += 1.2.5 = + +* Bug fixes: + * Make sure html entities are decoded and if needed re-encoded to XML entities for the XML sitemap. + * Fix infinite loop in sub-category or other sub-taxonomy archive page breadcrumbs. + * Fix breaking rewrite for categories when strip category is enabled. + * Fix non-global classes that should be global. +* i18n: + * Updated French translation. + * Updated Bosnian language code and deleted unused Bosnian version. + * Updated Bulgarian and deleted unused Bulgarian `bul` version. + * Updated and completed it_IT and pt_BR translations. + += 1.2.4 = + +* Bug fixes: + * Properly restore $wp_query after running header functionality, so we're not breaking badly built themes. + * Make the title test helper function only return the test title to the right WordPress user-agent. + * Fix for slug saving that should help interoperability with other slug-enhancing / changing functions. + * Fix wrong homepage titles with 12345 in them because of bug in 1.2.2. + * Added text domains on some strings that were missing it. + * Replace `split` with `explode` as `split` is deprecated in newer PHP versions. + * Properly deal with shortcodes with text inbetween. + * Remove several functions that are no longer used. + * `%%page%%` only outputs '`%%sep%%` Page 2 of X' when not on page 1. If you want it to show up on Page 1 you can use `%%pagetotal%%` and `%%pagenumber%%`. This both works for paginated posts & pages too. + * Allow for rel=author on sites with static frontpage too. +* Enhancements: + * Massive updates to how parts of the plugin are loaded, leading to a reduction of memory usage in WordPress admin of 1~2 MB. + * Respect `DISALLOW_FILE_MODS` and `DISALLOW_FILE_EDIT` constants. When set to true, edit files menu option won't show. + * Added support for image galleries in the page analysis functionality, alt tags from images in galleries are now parsed too. + * Add an option to remove the `replytocom` variables from comment links (they're bloody stupid). + * Added variables `%%pt_single%%` and `%%pt_plural%%` which output the single and plural label of the current post type (useful for post type archives). + * Made the default settings smarter, they'll now use better titles _and_ will set titles for each public post type, post type archive and taxonomy. + * Updates to introductory tour. + * Added PHPdoc to the entire plugins codebase. + * Refactored all of the code not using WordPress code style. + * Breadcrumbs now use [RDFA](http://support.google.com/webmasters/bin/answer.py?hl=en&answer=185417) and have been completely rewritten for speed and more filter options. +* i18n: + * Updated Russian translation. + * Better language codes for Hungarian and Bulgarian added. + * Updated .pot file. + += 1.2.3 = + +* Bug fixes: + * Fixed possible bug on multi site. + * Fixed a bug in alt & title elements for XML sitemaps. + * Fix to force title rewrites in two places: call `wp_reset_query` for stupid themes and plugins. + * Fixed bug in saving some options. + * Fixed OpenGraph bug where default image wouldn't be used for post without images. + * Prevent error on division by zero when swapping around text. + * Prevent notice in title_test and also fix possible bugs. + * Properly escape the delimiter too in all `preg_quote` calls. + * Don't show SEO filter on upload.php. + * Only count alt tags in keyword density and word count calculations, leave out title attributes. + * Rewrote the force rewrite test to be simpler and better. +* Enhancements: + * New icons for the analysis functionality. + * Twitter card functionality. + * Removed 200 lines of code from OpenGraph class because I could just inherit it from the parent class. d0h! + * Added a *bunch* of translations; bg, bos, bs, bul, es_ES, hu, hun, id_ID, pl_PL, pt_BR. + * Updated another bunch: de_DE, fr_FR, he_IL, it_IT, nl_NL, ru_RU, sv_SE. + += 1.2.2 = + +* Some small bug fixes. +* Made loading the TextStatistics class conditional on the existence of said class. +* Added a posts filter option for SEO quality in the edit posts / pages overview. +* Added a filter `wpseo_use_page_analysis` that disables the page analysis functionality when false is returned. +* Added a filter `wpseo_show_date_in_snippet` that disables the date in the preview snippet when false is returned. + += 1.2.1 = + +* Bugs fixed: + * Trim the focus keyword before running page analysis tests. + * Title's should be (and now is) Titles. + * Fixes to Theme integrations for Woo, Genesis and Thematic. + * Enhancement to force rewrite title test. + += 1.2 = + +* Bugs fixed: + * ereg_replace != preg_replace ; in other words: alt and title tags for images in xml sitemap fixed. + * Image size for OpenGraph now defaults to medium for thumbnail image. + * Selecting a Facebook App as the admin of your site now actually works. + * Saving the SEO -> Dashboard settings no longer makes you loose the SEO -> Social settings. + * Tweaks to clean slug functionality. + * Fix for UTF-8 terms in titles and descriptions. + * Fixed bug where empty but saved title template settings could lead to empty homepage title on blogs with a static front page. + * Fixed several bugs around page numbers in titles and descriptions. + * Prevented an error in the opendir functionality for WP SEO modules. + * Allow ';' in focus keyword. + * Don't double encode characters in suggest functionality. + * Don't remove non-alphanumeric values for keyword checks. + * Fixed a bug in snippet preview occurring when content was shorter than max snippet length. + * Fixed keyword count in content for cases where keyword was surrounded by parentheses and some other characters. + * Loads of Regex Fu to improve keyword bolding. + * Activation and deactivation handlers properly specified. + * WP Super Cache now properly emptied on update of settings. + * Fixes to OpenGraph images for homepage. + * Fixed a notice in OpenGraph class on 404 pages. + * Fixed notices in OpenGraph admin when selecting Facebook app as admin. + * Fixed a bug where half the Page analyses wouldn't work when the visual editor is disabled. + * Changed the mime type of the XSL file for XML sitemaps to text/xml, so Firefox will display them properly. + * Made sure the default OpenGraph image will always show up when there's no other image. + * Updated tablesorting JS used in XML Sitemaps. +* Enhancements: + * Added a page level "score" for the site analysis functionality. + * Allow sitewide noindexing of post types and taxonomies with post- and term-level overrides. + * Automatically check whether force rewrite needs to be enabled. + * Upon activation, XML sitemaps are automatically enabled. + * Upon activation, title templates are pre-filled with sensible defaults. + * Plugin now auto-detects whether titles need to be force-rewritten (using output buffering) or not. + * Redesign of the admin, removal of the indexations page and renaming the Titles page into Metas & Titles page. + * Allow noindex-following per custom taxonomy and custom post type. + * No longer show non-public post types on the Titles & Metas page. + * On activation, W3 Total Cache or WP Super Cache cache gets cleared automatically. + * Added an uninstall handler, deleting the plugin through the WP backend will now delete options from the DB too. + * Added the option to display custom taxonomy in titles and descriptions, use `%%ct_<custom-taxname>%%` for a comma separated list or `%%ct_<custom-taxname>%%single%%` for only one taxonomy term. + * Added the option to display custom taxonomy descriptions in post description fields, use `%%ct_desc_<custom-taxname>%%`. + * Allow for 'Page x of x' to be localized too. + * Force the query for the current page to be used instead of the query that a bad plugin or even theme was using by calling `wp_reset_query` before the header functionality. + * If you're a Woothemes user and you activate WordPress SEO, the "use 3rd party plugins data" checkbox will be checked on upon activation. + * Non front page blog pages now have a title template: `%%title%% %%page%% - %%sitename%%` if they don't have a specific SEO title and there is no page template. + * Pinging search engines on post of new content now moved to cron to prevent lag. + * Only embed images in the XML sitemap that match the main domain, subdomains should not matter but images from other domains are now ignored. + * Fixed a bug where homepage wouldn't be in the XML sitemap when there are no posts, yet the homepage is set to display recent posts. +* API Improvements: + * Added a filter to allow adding URLs to specific XML sitemaps, see [this thread](http://wordpress.org/support/topic/plugin-wordpress-seo-by-yoast-how-to-add-a-non-wordpress-page-to-the-sitemap). The filter is `wpseo_sitemap_<$post_type>_content`. + * Added a filter for the meta keywords, `wpseo_metakey`. + * Added a filter to allow disabling `rel="next"` and `rel="prev"` links, use `wpseo_<prev|next>_rel_link`. + * Added a filter `wpseo_xml_sitemap_img_src` to allow changing the hostname of images, most common use case is to force them to the CDN. + * See the new [WordPress SEO API docs](http://yoast.com/wordpress/seo/api-docs/). + += 1.1.9 = + +* Sigh... Sorry about that. + += 1.1.8 = + +* Bugs fixed / Changes: + * Fix the clean slug function to not freak people out and remove chars. + * Fixed a couple of notices. + * Allow '+' in focus keyword. + * A *load* of i18n fixes (including a new POT file). + += 1.1.7 = + +* Bugs fixed / Changes: + * Fixes issue with un-paginated canonicals for paged single posts / pages. + * Fixes %%page%% variable to work in title & description template on paginated singular post(type)s. + * Allow - in focus keyword. + * Removed the option to use a Facebook page as an admin in the Facebook OpenGraph, as Facebook deprecated that. + * Force OpenGraph locale to lowercase. + * Catch some weird locales and convert them to proper Facebook supported iso_country versions. + * Now adding _all_ the images in a post with an og:image tag, so people can more easily share the right image. + * Allow regex specific characters in the focus keyword for the Page Analysis checks. + * Add proper (and i18n compatible) [stop word](http://en.wikipedia.org/wiki/Stop_words) removal. + * Removed code to add noindex to login page as that's now in core for long enough. + * Fixed several notices. + * When a static homepage has no SEO title, default to the site's name + description. + * Only show images once in OpenGraph tags. + * Prevent a timeout on retrieving term meta. + * Don't do next / prev links on the homepage for Genesis based themes as that leads to trouble. + * XML Sitemaps & feeds: + * Properly fix featured image showing up in XML Sitemap. + * Optimized the main query for XML sitemaps per [this thread](http://wordpress.org/support/topic/plugin-wordpress-seo-by-yoast-performance-suggestion). + * Switch feed noindexing from `xhtml:meta` tags to X-Robots-Tag HTTP headers to prevent feed display issues. + * Force XML Sitemap descriptions for images to be clean to prevent XML parse errors. + * Tiny change in CSS for explanatory text in XSL. +* New filters: + * Add filter `wpseo_locale` for the locale in the opengraph settings. + * Add filter `wpseo_metabox_prio` to allow WP SEO metabox priority to be changed. +* Documentation: + * Removed the "Other Notes" tab from the plugin page, enough tabs there already. + * Added %%cf_ options to config page for titles. + * Fixed the Yoast Facebook URL. + * Changed plugin support link to [the new support URL format](http://wordpress.org/support/plugin/wordpress-seo). + += 1.1.6 = + +* Tiny fix to showing meta description on posts page. +* Fix for showing proper link to bug tracker. +* Fix for redirecting attachment pages when they don't have a parent. +* Fix for titles of custom post type pages. +* Dozens more tiny bugfixes. + += 1.1.5 = + +* Removing the Dashboard widget. + += 1.1.4 = + +* Removed the canonical redirect as that was screwing with people's setups (and minds). + += 1.1.3 = + +* Fix for the blog homepage title, description etc. +* Added several filters for use in the soon to be released [Video SEO module](http://yoast.com/wordpress/seo/video-seo/). +* Change to XSL for XML Sitemaps. +* Non-canonical hostnames (like example.com when site setting is www.example.com) now 301 redirect to canonical hostname. +* Static frontpages now added to XML sitemap *with* images if it has them, same for interior blog pages. + += 1.1.2 = + +* No longer add redirected URLs to the XML Sitemap. +* Plugin now properly adds images in galleries to the XML Sitemap too. +* Fixed a bug in home page title logic, affecting blogs with a static front page and a separate posts page, who were unable to set the posts page's title. +* Fixed a bug in the OpenGraph admin implementation. +* Google Suggest works again, and properly this time. +* Using entities in the snippet preview title & description field now still renders a correct "chars left" count. +* Replaced the last single quotes with double quotes for meta fields, apparently Bing wouldn't verify because of the single quotes in the verification line. +* Added option to verify your site with Alexa, as a lot of people requested this. + += 1.1.1 = + +* Fixed `sprintf` bug in page analysis. +* Fixed bug that caused inability to edit / save the search page title. +* Fixed bug that caused inability to edit / save the "parent" blog in multisite settings. +* Removed ability to edit WP Super Cache .htaccess file as it doesn't seem to be there anymore. +* Removed Yahoo! Site Explorer. + += 1.1 = + +* Biggest change: entire plugin now supports localization, testing can be done by dropping your properly named .mo file (wordpress-seo-nl_NL.mo for instance) into the languages dir. +* Added Dutch, German, French, Hebrew, Italian, Russian and Swedish translations. +* Show proper post type names in admin menu for disabling editor box and hide non-public post types (props Nacin). +* Disabling the Advanced Editor now only disables it for non admins. +* Replaced single quotes with double quotes in meta description and canonical, to please [Danny Sullivan](http://searchengineland.com). +* Fixed issue with images in XML sitemaps. +* Added Social menu item. +* Lots of changes to the Facebook integration: + * Added the locale. + * Added default image. + * Added homepage image & description. + * New method of adding Facebook Admins. +* Removed all nofollow settings, login register links are now nofollow by default, all other functionality removed as it doesn't make sense anymore. +* Cosmetic changes: added some Yoast links to backend widget in the right sidebar. +* Switched text domain from a constant to a proper string 'wordpress-seo'. +* Removed the now unneeded extra styling for admin pointers. +* Fixed a notice in 3.3beta with caused by `wp_reset_query` being called too early. +* Added several filters to allow WPML (and other plugins) to create XML Sitemaps for other languages. +* Minor updates to the plugin tour, removing the "Stop tour" button, "Close" now closes until you manually restart. + += 1.0.3 = + +* This release is dedicated to removing options and setting defaults instead. Specifically, the following settings are now defaults without options to change them: + * Images are now always included in the XML sitemap as Bing no longer breaks on them. + * Pinging Google and Bing is now a default action as that's the whole point of having XML sitemaps. + * RSS feeds are now always noindex, followed. No search engine should ever list an RSS feed as a result in the resultpages. + * Admin, login and registration pages are always noindexed now for the same reason. + * Search result pages are now always noindex, follow. + * Subpages of the homepage are now also noindex, follow. It just doesn't make sense to index,follow those especially with the new rel="prev" and rel="next" changes. +* The option to add `nosnippet` and `noarchive` meta tags sitewide has been removed. No one in his right mind would want to do that, and if you do, then adding the robots meta to your template yourself should be doable. +* Pinging search engines with your RSS feed is no longer part of this plugin (as its unneeded because the XML sitemaps do that). +* UTF-8 characters in RSS footer no longer break. +* Added the option to use the author name and link in the RSS header and footer using %%AUTHORLINK%%. +* No longer show the admin bar SEO menu for subscribers. +* Some style changes to make the backend look nicer in WP 3.2 and 3.3. +* Bug with importing settings fixed. +* No longer redirect attachments that have no parent. +* Correctly grab thumbnail for OpenGraph settings. +* Pages now get the correct priority (0.8). +* Added the option to noindex or completely disable post format archives. + += 1.0.2.3 = + +* Fixed some edge cases with rel=next and rel=prev, particularly relating to a paginated page as a frontpage. +* Updated the snippet preview so the date part of the snippet has the right color. +* Fixed a bug in the update routine that could cause errors in the backend. +* Enabled OpenGraph meta box on edit screens, first "stab". + += 1.0.2.2 = + +* Removed all rel=index, rel=prev etc references that WP core currently uses as they're wrongly implemented. +* Added rel=prev and rel=next for paginated posts and pages. +* Removed the interface options for all rel= links, they're off by default now. +* Removed the option to hide the version number. It's very easy to detect the version number anyway so let's not clutter the interface. + += 1.0.2.1 = + +* I missed a case where the next link would point to the current page, fixed it though :) + += 1.0.2 = + +* Fixed keyword in slug detection on non-post post types. +* Optimizations in canonical functionality, adding canonicals to author archives and more. +* Added prev and next links as [suggested by Google](http://googlewebmastercentral.blogspot.com/2011/09/pagination-with-relnext-and-relprev.html). +* Fixed issue with unescaped characters in title and description when updating edit screen. +* Posts with a canonical set to another URL are no longer included in the XML sitemap. + += 1.0.1 = + +* Fixed a bug where canonical would be set to /page/1/ on archives. +* Fixed an error for blocking files. +* Fixed a JS error that would cause the link editor in the rich editor not to work. +* Fixed possible error when reading settings have gone haywire (ie not 'posts' or 'page' on front but something else). + += 1.0 = + +* I now dare call this plugin STABLE. This doesn't mean there are no more bugs, it does mean that I think it won't break sites anymore. +* Fixed the import/export settings functionality. +* Updated the snippet preview to Google's new snippet design. + += 0.4.3 = + +* Heavily, very heavily, reduced memory usage in XML Sitemap generation. + += 0.4.2 = + +* Fixes: + * XML Sitemaps: + * Bug in redirection of www to non-www + * Removed no longer needed robots_txt code. + * Proper flushing of rewrites. + * Fix for sites using index.php in permalinks. + * Moved XML settings to its own options array, cleaning up the settings. This also allows you to save XML Sitemap settings again. + * Sitemaps now contains all the last updated posts (under 1,000) of one post type in one new XML sitemap, so SE only has to crawl one sitemap per post type. + * XML Sitemap Index file is now being pushed into cache if a caching plugin is active (by loading it through a cron job). + * No longer breaks when `get_post_type_archive` doesn't exist. + * Metaboxes / Edit Post: + * Proper escaping of attribute values / already filled boxes. + * Bug in bolding / keyword recognition JS code. + * Background of tab content is now white again. + * Taxonomies: + * Fixed bug that could prevent noindex from showing up. + * Other: + * Admin menu works properly again and has XML Sitemaps menu added. + += 0.4.1 = + +* Fixes bug in saving XML Sitemap and Dashboard settings. +* Forces flush of rewrite rules so XML sitemaps start working immediately after enabling. +* Adds a line of copy to the XML Sitemaps page to point the user at the index sitemap file. + += 0.4 = + +* Fixes: + * XML Sitemaps: + * Complete rewrite of the XML Sitemap system, now using a sitemap index file and sitemap files per taxonomy and post type. Way more scaleable and awesome. + * Updated the XSL to work with Sitemap Index file too. + * Added functionality to remove old style & potential blocking XML sitemaps. + * Removed all code that wrote files, as it's no longer needed. + * Removed all functionality for updating sitemaps after publish, as it's no longer needed (search engines will still be pinged though). + * Breadcrumbs: + * Support for bbPress (the plugin), breadcrumbs. + * Fixed bug with blog URL appearing for non-post post_types. + * Fixed bug with post ancestors being in wrong order. + * Removed erroneous var_dump. + * Bug with title for homepage when using page as homepage. + * OpenGraph: + * Moved all OpenGraph code to specific OpenGraph class. + * Added option to specify and add FB Page and App ID and FB admin ID or ID's. + * Page Analaysis: + * Fixed bug in detection of headings with an ID or other attribute. + * Several performance optimizations to class includes. + * Some fixes in JavaScript keyword detection and keyword bolding in snippet when using colon and semicolons etc in title or meta description. + * Tiny CSS fixes so it all looks nice in WordPress 3.2. + + +* Also: + * XML Sitemaps now have their own settings page. + * Plugin version is now stored in the options for the plugin to allow easy upgrade. + * Added the option to use custom fields in title and description templates. Use `%%cf_<custom-field-name>%%` and it'll be replaced with your custom field. So for instance %%cf_city%% when your custom field is named "city". + * Removed some of the tabs and empty lines the plugin was outputting. + * Added some filters: + * `wpseo_sitemap_urlimages` so you can add images to the sitemap, found in inc/class-sitemaps.php + * `wpseo_title` and `wpseo_metadesc` in frontend/class-frontend.php + + += 0.3.5 = + +* Fixes: + * Issue in post / page editor with link dialog and other plugins using jQuery UI. + * No longer uses Google JS API for jQuery UI, but just include jQuery UI autocomplete library and uses the rest from WP core. + * No longer uses Google JS API for jQuery UI CSS, but included the needed classes in the plugins CSS files. + * Properly update sitemap for custom post types on publication. + * Fixed a notice in heading detection when no headings were found. + * Fixed a typo in Page Analysis messages. + += 0.3.4 = + +* Fixes: + * CSS issue caused by 0.3.3. + * Some security issues, thanks Jon Cave and Andrew Nacin for pointing them out and helping to fix! + += 0.3.3 = + +* Fixes: + * CSS collisions due to too generic class names. + * Issue with outbound link recognition caused in 0.3.2. + * Improved first paragraph detection. + * Word count is now correct for non western european languages too. + * Keyword detection in content and first paragraph in Cyrillic, Russian and other languages. + * Handling of UTF-8 slugs. + * Proper detection of keywords underneath the focus keyword input field by using word boundaries. + * Proper keyword bolding and counting in snippet preview for Cyrillic, Russian and other languages. + * Proper counting of length of description and title for strings with accents or non-ASCII characters. + * No longer calculating Flesch score for non-English, as it's not correct. + * Related keywords button works again. + * Several performance improvements to edit post page JavaScript. + +* Also: + * Notice now properly tells you to save as draft or update a post to see new Linkdex analysis. No, it's not going to be AJAX, ever, for performance reasons. + += 0.3.2 = + +* Fixes: + * Instant update of snippet preview now also works when editing post excerpt. + * Bugs with non-ASCII characters in the focus keyword and keyword recognition. + * Issues with themes using `cat` in search, most specifically the ClassiPress themes. + * The snippet preview no longer crashes when you use "on", "strong" or any other term contained in "strong" in your focus keyword. + * Made the Linkdex check for keyword in URL work in all occasions. + * Notices on XML import. + * Issue with title of blog page on paginated pages. + +* Also: + * Removed 3 no longer needed JavaScript files. + * Added the new bug tracker to the plugins sidebar notice under support. + * Canonical now has a filter (`wpseo_canonical`) so you can remove it or change it. + += 0.3.1 = + +* Fixes: + * Bug in meta box JS code causing annoying issues in Firefox and Internet Explorer. + * Issue with outbound anchor text detection in Linkdex Page Analysis. + * Small bug in detection of keyword in Title when keyword contained dashes or other non alphanumeric chars. + += 0.3 = + +* Major new feature: Linkdex Page Analysis has been integrated into WordPress SEO. + +* Other new features: + * You can now noindex, follow all sub pages of archives, taxonomies and categories. + +* Fixes / Updates: + * Snippet will now use ellipsis when post title or meta description is too long. + * Various JS optimizations in snippet preview generation. + * Snippet length will once again correctly correct for date inclusion. + * Date in snippet will be the current date when post is an unpublished draft. + * Preventing some errors when there's no focus keyword. + * Keyword detection in the URL now works correctly. + * URLs are no longer (incorrectly) "shortened". + * Fixed possible infinite loop in editor when adding focus keyword, which was causing FF crashes. + * Fixed count of keyword in content by removing tags properly. + * Fixed issues with HTML tags appearing in snippet inappropriately. + * Switched Google Suggest autocompletion for focus keyword to use [Google Suggest jQuery](http://code.google.com/p/googlesuggest-jquery/). + * No longer relies on `is_post_type_archive` to work, so compatible with WP 3.0 again. + * On themes that badly include $post in the head a redirect no longer occurs if the last post was redirected. + * SEO Settings menu in Admin Bar will no longer show for people that don't have the rights needed to use it. + * No longer wrongly showing image for last post in OpenGraph when on front page. + * No longer redirecting taxonomy feeds wrongly under some conditions with clean permalinks enabled. + * Fixed wrong titles for feeds. + += 0.2.5.4 = + +* Custom post type archives update: + * You can now set a title, meta description and breadcrumbs title for custom post type archives on the Titles page + * Custom post type archives now have a correct canonical +* Other fixes: + * Fixed a notice for an uninitialized setting + * Slightly changed the CSS for the admin area + * Changed input fields for meta descriptions on titles page from text input to textarea + * More preparations for allowing the plugin to be fully translated + += 0.2.5.3 = + +* Bugs fixed: + * `edit_posts` is not the same as `edit_post`, what a difference an s makes, thanks to nacin. + * preview URLs now work again with clean permalinks on, but only for those with the actual rights to view them. + * Fix for catchable fatal error in canonical function. + * First stab at fix for errors with focus keyword check and keywords filled with HTML tags. + * canonical links for paginated posts are now set correctly. + * Issue where parent taxonomy items would display in the reverse order in breadcrumbs. + * Improved error handling for non PHP 5.2+ installs and also made automatic deactivation work properly. + +* Documentation fixes: + * Added screenshot and a FAQ with several items to this plugin's page. + += 0.2.5.2 = + +* This WordPress SEO plugin now officially requires PHP5. WordPress version 3.2 will also require it, so you'd better upgrade now. If you're on any version lower than PHP 5.2, this plugin will deactivate itself. You can then use SEO Data Transporter to migrate your data to another plugin, or you could do the more sensible thing and get your hosting upgraded to PHP 5.2 ASAP. + +* Bugs fixed: + * Attempted compatibility fix with other plugins that hook into robots.txt functionality. + * Issue with not loading meta box for some custom post types. + * Issue where SEO data for custom post types would not save. + * Issue where post title in snippet preview would show %%sitename% instead of your site's name when no title template for post type was set. + * Issue with removing tags when string was empty in JavaScript. + * Hiding the post_format taxonomy on the right places. + * Should now work better with crappy themes that do weird things in header.php. + +* Design fixes: + * Updated tabs in meta boxes to reflect Core UI, for more info see [this post](http://developersmind.com/tabbed-meta-box-in-wordpress/) by Pete Mall. + +* Enhancements: + * Now using plugin version number to enqueue files so browser cache isn't in the way when upgrading. + * Loading scripts in footer now when possible. + += 0.2.5.1 = + +* Security fixes: + * Added nonces for security to htaccess and robots.txt file editing to prevent possible CSRF. + +* Bugs fixed: + * Prevent JS error when WordPress SEO Meta box was hidden on edit pages. + * Fix for title in snippet preview not showing when no title template was set for the post type. + * Fix for focus keyword count, would give wrong return on slugs that were too long. + * Removed post_format from list of taxonomies you can edit title and meta desc template for as it's of no use. + * Removed post_format from list of taxonomies to exclude for XML Sitemap, it was already excluded by default. + +* New features: + * Option to disable WordPress SEO meta data box on (custom) taxonomies. + += 0.2.5 = + +* Bugs fixed: + * Snippet preview quicker than ever and it no longer blows up some browsers: all AJAX calls have been removed and the entire process is done with JavaScript within the browser. + +* Feature enhancements: + * The meta box on the edit posts page now features tabs, thanks to Pete Mall (even works nicely in the blue theme). + * Advanced features moved to their own tab instead of button. + * Focus keyword now shown straight below snippet preview. + +* Other news: The Google News module almost ready for mass-deployment. Stay tuned. + += 0.2.3.4 = + +* Added WPML config file, so you can SEO in multiple languages. + += 0.2.3.3 = + +* Bugs fixed: + * Comma in priority in sitemap which should be dot. I hate European servers. + * Fixed a notice in generating a path to the wpseo directory in the uploads dir. + * Fixed a rather annoying XML Sitemap date issue, props to [Staze in wp.org forums](http://wordpress.org/support/topic/plugin-wordpress-seo-by-yoast-sitemapxml-wrong-date-for-blog?replies=8). + +* Feature enhancement: + * Breadcrumbs now output links to post type archives too. + += 0.2.3.2 = + +* Bugs fixed: + * Fixed race condition where sitemap wouldn't load sometimes. + * ... in snippet preview are now bold as they should be. + * Desc and Title in snippet preview should now update less frequently to prevent crashing low mem browsers. + += 0.2.3.1 = + +* Bugs fixed: + * Error in saving certain data when it was a checkbox. + * Fixed notice for non-existing title and for empty metakey. + * Fix for an error that could occur when the post thumbnail functionality is not active. +* Changes: + * Added page numbers to default titles for taxonomies and archives. + += 0.2.3 = + +* New features: + * First stab at (Facebook) OpenGraph implementation. + * Meta Description can now be returned, using `$wpseo_front->metadesc( false );` for use elsewhere. + * Plugins can now register their own variables to not be cleaned out when permalink redirect is enabled. + +* Bugs fixed: + * Deleting the dashboard widget will now really delete it. + * Some fixes for notices. + * Strip tags out of titles. + * Use blog charset for XML Sitemap instead of UTF-8. + * Import of Meta Keywords fixed. + * Small fix for possible error in AJAX routines. + * Breadcrumb now actually returns when you ask it to. + * Fixed some errors in JavaScript of title generation within snippet preview. + * Removed SEO title from post edit overview as you couldn't edit it there anyway. + +* Documentation fixes: + * Added an extra notice to clean permalink to let people know they're playing with fire. + * Small improvement to error handling for upload path. + += 0.2.2 = + +* Bugs fixed: + * Disabling sitemaps now properly does what it says on the tin: disable sitemaps. + * Properly return title for homepage in rare instances where `is_home` returns true for front page even when front page is set to static page (yes, that's a WordPress bug I had to work around). + * An empty title separator will now be changed to ' - ' so titles don't get all borked. + * Several fixes in rewrites for MultiSite instances. + * Option to force http or https on canonical URLs. + * Several other bugfixes. + += 0.2.1 = + +* Bugs fixed: + * Plugin frontend URL should now be properly defined for sites with https admin. + * Manually entered category title now actually works. + * Import now works properly again for HeadSpace and AIOSEO, even for meta keywords. + * Fixed typo in *wpseo-functions.php*, apparently `udpate_option` is not the same as `update_option`. + * Fixed a notice about date snippet. + * Fixed a notice about empty canonical. + * Prevent cleaning out the WP Subscription managers interface for everyone. + * Meta keywords are now properly comma separated. + * Year archives now give proper breadcrumb. + * Nofollowed meta widget actually works now. + * %%date%% replacement in templates improved significantly. + * Shortcodes stripped out in generation of title & description templates. + +* Changes: + * Moved all rewrites to their own class, *inc/class-rewrite.php*. + * Further improved error handling when *uploads/wpseo* dir creation fails. + +* New features: + * Remove category base, removes `/category/` from category URL's. Find it under Permalinks. Props to [WP No Category Base](http://wordpresssupplies.com/wordpress-plugins/no-category-base/) for having the cleanest code I could find in this area, which I reused and modified. + * Admin bar goodness: an SEO menu! Try it if you're on 3.1 already, it allows you to perform several SEO actions! + += 0.2 = + +* Bugs fixed: + * Chars left counter works again as you type in title and SEO title. + * No longer error out when unable to delete sitemap files in site root. + * Fixed error when `memory_get_peak_usage` doesn't exist (below PHP 5.2). + * Fixed error when Yoast News feed couldn't be loaded. + * Fix for people who agressively empty their dashboards. + * Permalink redirect fix for paginated searches. + +* Changes: + * Plugin now properly reports which sitemap files are blocking it from working properly and asks you to delete them if it can't delete them itself. + * Some cosmetic fixes to dashboard widget. + * Removed some old links to Yoast CDN and replaced with images shipped with plugin, for SSL backends. + * New general settings panel on WPSEO Dashboard which allows you to disable WordPress SEO box on certain post types. + * Option to use focus keyword in title, meta description and keyword templates. + * Changed the hook for the permalink cleaning from `get_header` to `template_redirect`, which means it redirects faster and is less error prone. + +* New Features: + * Added option to export taxonomy metadata (PHP 5.2+ only for now). + * Meta keywords are now an option... I don't like them but there's sufficient demand apparently. Works for homepage, post types, author pages and taxonomies. + * Added an option to disable the advanced part of the edit post / page metabox. + * Added option to disable date display in snippet preview for posts. + * Multisite Network Admin page added, with three features: + * The option to make WordPress SEO only accessible to Super admins instead of site admins. + * The option to set a "default" site, from which new sites will henceforth acquire their settings on creation. + * The option to revert a site to the "default" site's settings. + += 0.1.8 = + +* Notice: The functionality in the post / page editor has changed quite a bit. Meta descriptions are now generated using the meta descriptions template if no meta description is entered, so it will for instance use the post excerpt, the SEO title is no longer filled automatically BUT it is properly shown in the snippet preview based on your title template. It should work faster, more intuitive and just better in general, but I do need your feedback, let me know if it's an improvement. + +* Bugs fixed: + * Fixed a notice for non existing metadesc. + * Fixed several notices in title generation. + * Directory paths in backend now properly recognized even when erroneously set to 1. + * Fixed bug where frontpage title wouldn't be generated properly. + * Made sure unzip of settings.zip (for settings import) works properly everywhere (by getting rid of `WP_Filesystem` and `unzip_file()`, as they do not work reliably). + * Made sure meta descriptions are not shown on paged archives or homepages. + +* Changes: + * Admin: + * Moved image used in news widget into images directory instead of loading from CDN to prevent https issues. + * Breadcrumbs: + * Creating proper breadcrumbs for daily archives now (linking back to month archives). + * Post / Page edit box: + * Meta description now properly generated using template for that particular post_type. + * SEO Title is no longer auto filled, if you leave it empty "normal" title template generation is used. + * Several improvements to javascripts. + * Titles, Meta descriptions & Canonicals: + * Speed up of variable replacement for titles and meta descriptions. + * In fallback titles (when there's no template), plugin now sticks to `$sep` defined in `wp_title`. + * Now properly generating canonical links for date archives. + * The %%date%% variable now works properly on date archives too. + * Added new filter to make title work properly on HeadWay 2.0.5 and up. + * Fixed canonical and permalink redirection for paginated pages and posts (props to @rrolfe for finding the bug and coming up with first patch). + * XML Sitemaps: + * During sitemap generation, plugin now checks whether old sitemap.xml or sitemap.xml.gz files exist in root and deletes those if so. + * Made including images optional. + * Made it possible to pick which search engines to ping. + * Fix in XSL path generation on HTTPS admin backends when frontend is normal HTTP. + * XML Sitemap update on post publish now actually works properly. + * No longer are XML Sitemaps enabled automatically when publishing a post (sorry about that). + += 0.1.7.1 = + +* Apparently `is_network_admin()` didn't exist before WP 3.1. D0h!!! + += 0.1.7 = + +* Bugs fixed: + * Empty Home link when blog page is used and no settings have been set. + * Fixed couple more notices (well, like, 10). + * Bug in directory creation that would create the directory correctly but still throw an error and save the path wrongly to options. + * Dismissing Blog public warning was only possible on SEO pages, now it's possible everywhere. + * Excerpts, when used in description, are now properly sanitized from tags and shortcodes. + * Properly fallback to `$wp_query->get_queried_object()` instead of `get_queried_object()` for < 3.1 installs. + * Fixed several bugs in title generation, making it more stable and faster in the process. + * Properly escape entities in page titles, both in front end and in posts overview. + +* Changes: + * Latest news from Yoast now appears on Network Admin too, and you can disable it there and on normal admin pages individually. First step towards getting a Multi Site Network Admin SEO page. + * Added a "Re-test focus keyword" button for people using the Rich Text editor, which wasn't sending update events properly. + += 0.1.6 = + +* New features: + * Export & Import your WordPress Settings easily. + * You can now supply extra variables to prevent from being cleaned out when clean permalinks is on. + +* Bugs fixed: + * No longer throw errors when wpseo dir cannot be created. + * Your blog is not public warning can now be properly dismissed. + * Fixed rewrite issues: apparently if you only load rewrite rules on the front-end, they don't get added when changing rewrites in the backend. D0h. + * Rewrite rule for sitemap is now forced even harder when regenerating sitemap by hand. + * Search permalinks now work properly, though in "old" ?s=query style, because of a bug in core. + * Breadcrumbs no longer errors when term that is supposed to show is empty. + * Enabling breadcrumbs without setting any of the text fields no longer gives notices but proper defaults. + * Proper fallback for get_term_title for pre WP 3.1 sites with custom taxonomies. + +* Changes: + * You can now dismiss settings advice. + * You can now fix some of the settings advice just by clicking the button. + * You can now make posts, pages and taxonomy terms of any kind always appear in sitemap even if they're noindex, or never, set on a piece by piece basis. + * Permalink changes now invoke immediate XML sitemap update. + * Added canonical url to the blog page if using a static page for front page (props [@rrolfe](http://twitter.com/rrolfe)). + * Removing RSS feeds now actually works (props @rrolfe). + * Added breadcrumb for 404 pages (props @rrolfe). + * Drastically reduced memory usage during XML sitemap generation. + += 0.1.5 = + +* Bugs fixed: + * Duplicate noodp,noydir showing up in some occasions. Reworked most of robots meta output function. + * Fixed couple more notices. + * Trailing slash (when option set) now applied correctly in XML sitemap too. + * Made sure regenerating sitemap worked again on post publish. + * Force flush rewrite rules on activation / upgrade of plugin to make rewrite work. + * Fixed empty RSS content bug caused in 0.1.4. + +* Changes: + * Removed part done quick edit functionality, will need to revisit once API improves. + * Implemented a hook that would make the title work with Thematic based themes properly. + * Added option to remove "start" rel link from head section. + * Several style sheet changes to make backend styling easier and more robust. + * Added option to force rewrite titles for people that can't adapt their theme, it's ugly but not as ugly as how All In One SEO handles it. + * If title templates aren't set, the plugin now generates proper default titles. + * The News module has moved to a separate directory, where all other modules will reside too, so they can be updated individually later. Download link for the news module will appear on yoast.com shortly. + +* Documentation: + * Added Admin Only notice in HTML code when no meta description could be generated. + * Added a donation box, I'll gladly take your money ;) + += 0.1.4 = + +* Bugs fixed: + * Fixed canonical for paginated archives of any kind when permalink structure doesn't end with / + * Fixed permalink redirect for paginated archives of any kind when permalink structure doesn't end with / + * Made sure blog shows up in breadcrumbs when you want it too. + * Fixed small javascript notice for js/wp-seo-metabox.js + * Rewrote parts of XML Sitemap generation so it's now fully compliant with MultiSite. You no longer have to choose paths for sitemaps, they'll all have fixed locations and using WP Rewrite will be placed in the correct positions, f.i. example.com/sitemap.xml. + * Heavily reduced memory usage on admin pages. + * Rewrote module structure and added some API's to be used in the modules. + * Plugin now creates uploads/wpseo dir to store all files it creates and takes in. + * Fixed several notices throughout the code. + * Made sure SEO title in edit posts screen shows correct SEO Title. + * Changed table sorting javascript for XSL's to Yoast CDN. + += 0.1.3 = + +* Bugs fixed: + * SEO Title no longer being overwritten when it's already set. + * Titles for date archives work too now. + * On initial page load or SEO title regeneration number of chars remaining updates properly. + * Entities in titles and meta descriptions should now work correctly. + * When editing SEO title snippet preview now correctly updates with focus keyword bolded. + * Entities in XML sitemap should now show correctly. + * When using %%excerpt%% in descriptions it now correctly is shortened to 155 chars. + * Regenerating XML News sitemaps should no longer give a Fatal error but just work. + * Focus keyword should now properly be recognized in slug even when slug is too long to display. + * Breadcrumbs now show proper home link when showing blog link is disabled. + * Non post singular pages (pages and custom post types) no longer show blog link in breadcrumb path. + +* New features: + * Added option to regenerate SEO title (just click the button). + * Advanced button now looks cooler (hey even little changes deserve a changelog line!). + * Now pinging Ask.com too for updated sitemaps, for those of you caring about SEO for Ask. + * Added plugin version number to "branding" comment to help in bug fixing. + += 0.1.2.1 = + +* Added a missing ) to prevent death on install / going into wp-admin. + += 0.1.2 = + +* Bugs fixed: + * Non ASCII characaters should now display properly. + * Google News Module: added input field for Google News publication name, as this has to match how Google has you on file. + * Stripped tags out of title and meta description output when using, f.i., excerpts in template. + * Meta description now updates in snippet preview as well when post content changes and no meta description has been set yet. + * Meta description generated from post content now searches ahead to focus keyword and bolds it. + * Meta description should now show properly on blog pages when blog page is not site homepage. + * Alt or title for previous image could show up in image sitemap when one image didn't have that attribute. + * Prevented fatal error on remote_get of XML sitemap in admin/ajax.php. + * When there's a blog in / and in /example/ file editor should now properly get robots.txt and .htaccess from /example/ and not /. + * Reference to wrongly named yoast_breadcrumb_output fixed, should fix auto insertion of breadcrumbs in supported theme frameworks. + * Prevented error when yoast.com/feed/ doesn't work. + * Fixed several notices for unset variables. + * Added get text calls in several places to allow localization. + +* (Inline) Documentation fixes: + * Exclusion list in XML sitemap box for post types now shows proper label instead of internal name. + * Exclusion list in XML sitemap box for custom taxonomies now shows plural instead of singular form. + * Added explanation on how to add breadcrumbs to your theme, as well as link to more explanatory page. + +* Changes: + * Links to Webmaster Tools etc. now open in new window. + * Heavily simplified the javascript used for snippet preview, removing HTML5 placeholder code and instead inserting the title into the input straight away. Lot faster this way. + * Removed Anchor text for the blog page option from breadcrumbs section as you can simply set a breadcrumbs title on the blog page itself. + * Added option to always remove the Blog page from the breadcrumb. + += 0.1.1 = + +* Bugs fixed: + * Double comma in robots meta values, as well as index,follow in robots meta. + * Oddities with categories in breadcrumbs fixed. + * If complete meta value for SE is entered it's now properly stripped, preventing /> from showing up in your page. + * Category meta description now shows properly when using category description template. + * Removed Hybrid breadcrumb in favor of Yoast breadcrumb when automatically adding breadcrumb to Hybrid based themes. + * First stab at fixing trailing slashed URL's in XML sitemaps. + * Made %%page%% also work on page 1 of a result set. + * Fixed design of broken feed error. + * Made sure %%tag%% works too in title templates. + +* (Inline) Documentation fixes: + * Added this readme.txt file describing all the SEO functionality, and why this is _the_ All in one SEO plugin. + * MS Webmaster Central renamed to Bing Webmaster Tools. + * Added links to Bing Webmaster Tools and Yahoo! Site explorer to meta values box, as well as an explanation that you do not need to use those values if your site is already verified. + * Changed wording on description of clean permalinks. + * Added line explaining that SEO title overwrites the SEO title template. + * Added line telling to save taxonomy and post_type excludes before rebuilding XML sitemap. + +* Changes: + * Changed robots meta noindex and nofollow storage for pages to boolean on noindex and nofollow, please check when upgrading. + * Now purging W3TC Object Cache when saving taxonomy meta data to make sure new settings are immediately reflected in output. + * Namespaced all menu items to prevent collissions with other plugins. + * Several code optimizations in admin panels. + * Huge code optimizations in breadcrumbs generation and permalink clean up. + * Permalink cleaning now works for taxonomies too. + * Faked All in One SEO class to make plugin work with themes that check for that. + +* New features: + * Noindex and nofollow options for taxonomies (noindexing a term automatically removes it from XML sitemap too). + * Editable canonicals for taxonomies. + * Completed module functionality, using the XML News sitemap as first module. + * Added experimental "Find related keywords" feature that'll return keywords that are related to your focus keyword. + +* Issues currently in progress: + * WPML compatibility for the multilingual SEO's. + * XML Sitemap errors in Bing Webmaster Tools (due to use of "caption" for images). + + += 0.1 = + +* Initial beta release. diff --git a/wp-content/plugins/wordpress-seo/css/adminbar.css b/wp-content/plugins/wordpress-seo/css/adminbar.css new file mode 100644 index 0000000..fa268c4 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/adminbar.css @@ -0,0 +1,37 @@ +.wpseo-score-icon { + display: inline-block !important; + float: left; + width: 12px !important; + height: 12px !important; + margin: 10px 10px 0 4px !important; + border-radius: 50% !important; + background-color: #999; +} + +.wpseo-score-icon.good { + background-color: #7ad03a; +} + +.wpseo-score-icon.ok { + background-color: #ffba00; +} + +.wpseo-score-icon.poor { + background-color: #ee7c1b; +} + +.wpseo-score-icon.bad { + background-color: #dd3d36; +} + +.wpseo-score-icon.na { + background-color: #999; +} + +.wpseo-score-icon.noindex { + background-color: #1e8cbe; +} + +#wp-admin-bar-wpseo-menu:hover .wpseo-score-icon { + background-color: #2ea2cc; +} diff --git a/wp-content/plugins/wordpress-seo/css/adminbar.min.css b/wp-content/plugins/wordpress-seo/css/adminbar.min.css new file mode 100644 index 0000000..7a17970 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/adminbar.min.css @@ -0,0 +1 @@ +.wpseo-score-icon{display:inline-block!important;float:left;width:12px!important;height:12px!important;margin:10px 10px 0 4px!important;border-radius:50%!important;background-color:#999}.wpseo-score-icon.good{background-color:#7ad03a}.wpseo-score-icon.ok{background-color:#ffba00}.wpseo-score-icon.poor{background-color:#ee7c1b}.wpseo-score-icon.bad{background-color:#dd3d36}.wpseo-score-icon.na{background-color:#999}.wpseo-score-icon.noindex{background-color:#1e8cbe}#wp-admin-bar-wpseo-menu:hover .wpseo-score-icon{background-color:#2ea2cc} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/css/edit-page.css b/wp-content/plugins/wordpress-seo/css/edit-page.css new file mode 100644 index 0000000..123c4a4 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/edit-page.css @@ -0,0 +1,43 @@ +.wpseo-score-icon { + display: inline-block; + width: 12px; + height: 12px; + margin-left: 6px; + border-radius: 50%; + background: #888; + line-height: 16px; +} + +.wpseo-score-icon.good { + background-color: #7ad03a; +} + +.wpseo-score-icon.ok { + background-color: #ffba00; +} + +.wpseo-score-icon.poor { + background-color: #ee7c1b; +} + +.wpseo-score-icon.bad { + background-color: #dd3d36; +} + +.wpseo-score-icon.na { + background-color: #888; +} + +.wpseo-score-icon.noindex { + background-color: #1e8cbe; +} + +th#wpseo-score { + width: 60px; +} + +@media screen and ( max-width: 782px ) { + .column-wpseo-title, .column-wpseo-score, .column-wpseo-metadesc, .column-wpseo-focuskw { + display: none; + } +} diff --git a/wp-content/plugins/wordpress-seo/css/edit-page.min.css b/wp-content/plugins/wordpress-seo/css/edit-page.min.css new file mode 100644 index 0000000..cd66cf4 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/edit-page.min.css @@ -0,0 +1 @@ +.wpseo-score-icon{display:inline-block;width:12px;height:12px;margin-left:6px;border-radius:50%;background:#888;line-height:16px}.wpseo-score-icon.good{background-color:#7ad03a}.wpseo-score-icon.ok{background-color:#ffba00}.wpseo-score-icon.poor{background-color:#ee7c1b}.wpseo-score-icon.bad{background-color:#dd3d36}.wpseo-score-icon.na{background-color:#888}.wpseo-score-icon.noindex{background-color:#1e8cbe}th#wpseo-score{width:60px}@media screen and (max-width:782px){.column-wpseo-focuskw,.column-wpseo-metadesc,.column-wpseo-score,.column-wpseo-title{display:none}} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/css/index.php b/wp-content/plugins/wordpress-seo/css/index.php new file mode 100644 index 0000000..12c197f --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/index.php @@ -0,0 +1,2 @@ +<?php +//Nothing to see here \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/css/metabox-classic.css b/wp-content/plugins/wordpress-seo/css/metabox-classic.css new file mode 100644 index 0000000..1dfbc73 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/metabox-classic.css @@ -0,0 +1,21 @@ +/** + * Metabox Tabs + */ + +ul.wpseo-metabox-tabs li.active { + background-color: #eff8ff; +} + +ul.wpseo-metabox-tabs li.active, div.wpseo-tab-content { + border-color: #d1e5ee; +} + +.wpseo-metabox-tabs .active a { + color: #333; +} + +#wpseotab .ui-widget-content .ui-state-hover { + border: 1px solid #d1e5ee; + color: #333; + background: #eff8ff; +} diff --git a/wp-content/plugins/wordpress-seo/css/metabox-classic.min.css b/wp-content/plugins/wordpress-seo/css/metabox-classic.min.css new file mode 100644 index 0000000..bfc15df --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/metabox-classic.min.css @@ -0,0 +1 @@ +ul.wpseo-metabox-tabs li.active{background-color:#eff8ff}div.wpseo-tab-content,ul.wpseo-metabox-tabs li.active{border-color:#d1e5ee}.wpseo-metabox-tabs .active a{color:#333}#wpseotab .ui-widget-content .ui-state-hover{border:1px solid #d1e5ee;color:#333;background:#eff8ff} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/css/metabox-fresh.css b/wp-content/plugins/wordpress-seo/css/metabox-fresh.css new file mode 100644 index 0000000..6f16608 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/metabox-fresh.css @@ -0,0 +1,22 @@ +/** + * Metabox Tabs + */ + +ul.wpseo-metabox-tabs li.active { + background-color: #f1f1f1; +} + +ul.wpseo-metabox-tabs li.active, div.wpseo-tab-content { + border-color: #dfdfdf; + background-color: #fff; +} + +.wpseo-metabox-tabs .active a { + color: #333; +} + +#wpseotab .ui-widget-content .ui-state-hover { + border: 1px solid #dfdfdf; + color: #333; + background: #f1f1f1; +} diff --git a/wp-content/plugins/wordpress-seo/css/metabox-fresh.min.css b/wp-content/plugins/wordpress-seo/css/metabox-fresh.min.css new file mode 100644 index 0000000..31ebcff --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/metabox-fresh.min.css @@ -0,0 +1 @@ +div.wpseo-tab-content,ul.wpseo-metabox-tabs li.active{border-color:#dfdfdf;background-color:#fff}.wpseo-metabox-tabs .active a{color:#333}#wpseotab .ui-widget-content .ui-state-hover{border:1px solid #dfdfdf;color:#333;background:#f1f1f1} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/css/metabox-tabs.css b/wp-content/plugins/wordpress-seo/css/metabox-tabs.css new file mode 100644 index 0000000..85960da --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/metabox-tabs.css @@ -0,0 +1,328 @@ +/** + * Metabox Tabs + */ + +ul.wpseo-metabox-tabs { + display: none; + margin-top: 12px; + margin-bottom: 3px; +} + +.wpseo-metabox-tabs-div ul { + list-style: none; +} + +.wpseo-metabox-tabs li { + display: inline; +} + +ul.wpseo-metabox-tabs li.active { + border-width: 1px 1px 0; + border-style: solid solid none; + background-color: #fdfdfd; +} + +ul.wpseo-metabox-tabs li { + padding: 5px; +} + +.wpseo-metabox-tabs a { + text-decoration: none; +} + +.wpseo-metabox-tabs-div div.wpseo-tabs-panel { + overflow: auto; + padding: 0.5em 0.9em; + border: 1px solid; +} + +.wpseo-heading { + padding-left: 10px; +} + +.wpseotab { + display: none; +} + +.wpseotab.active { + display: block; + overflow: auto; + padding: 0.5em 0.9em; + border: 1px solid #ddd; + background-color: #fdfdfd; +} + +#wpseo_meta .postbox .inside .wpseotab { + font-size: 13px !important; +} + +.inside .wpseotab .form-table th { + width: 140px !important; + font-size: 13px; +} + +.good, .warn, .wrong { + font-weight: bold; +} + +.good { + color: green; +} + +.warn { + color: maroon; +} + +.wrong { + color: red; +} + +#current_seo_title span { + padding: 2px 5px; + background-color: lightyellow; +} + +#wpseosnippet { + width: auto; + max-width: 520px; + margin: 0 0 10px; + padding: 0 5px; + font-family: Arial, Helvetica, sans-serif; + font-style: normal; +} + +#wpseosnippet td { + margin: 0; + padding: 0; +} + +#wpseosnippet cite.url { + font-weight: normal; + font-style: normal; +} + +#wpseosnippet a { + text-decoration: none; +} + +#wpseosnippet .title { + display: block; + overflow: hidden; + width: 512px; + color: #1e0fbe; + font-size: 18px !important; + line-height: 1.2; + white-space: nowrap; + text-overflow: ellipsis; +} + +#wpseosnippet .desc { + font-size: small; + line-height: 1.4; + word-wrap: break-word; +} + +#wpseosnippet .desc .autogen { + color: #777; +} + +#wpseosnippet .date { + color: #808080; +} + +#wpseosnippet .desc p { + color: #545454; + font-size: small; + line-height: 1.4; + word-wrap: break-word; +} + +#wpseosnippet .url { + color: #006621; + font-size: 13px; + line-height: 16px; +} + +#wpseosnippet .meta { + color: #767676; +} + +#wpseosnippet .util { + color: #4272db; +} + +#wpseosnippet p { + margin: 0 !important; +} + +#wpseosnippet a:hover { + text-decoration: underline; +} + +#focuskwresults ul { + margin: 0; +} + +#focuskwresults p, #focuskwresults li { + font-size: 13px; +} + +#focuskwresults li { + margin: 0 0 0 20px; + list-style-type: disc; +} + +.wpseo_hidden { + display: none; +} + +/* Linkdex analysis block */ + +table.wpseoanalysis th { + padding: 15px 0 5px 0; + font-size: 14px; + text-align: left; +} + +table.wpseoanalysis th.first { + padding-top: 0; +} + +table.wpseoanalysis td { + margin: 5px 0; + font-size: 13px; + line-height: 16px; +} + +table.wpseoanalysis td.score { + width: 20px; + height: 18px; + padding-left: 10px; +} + +.wpseo_msg { + margin: 5px 0 10px 0; + padding: 0 5px; + border: 1px solid #e6db55; + background-color: lightYellow; +} + +/* + * jQuery UI CSS Framework 1.8.12 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Component containers +----------------------------------*/ +#wpseotab .ui-widget-content { + border: 1px solid #ddd; + color: #333; + background: #f1f1f1; +} + +#wpseotab .ui-widget-content a { + color: #333; +} + +/* + * jQuery UI Autocomplete 1.8.12 + * http://docs.jquery.com/UI/Autocomplete#theming + */ +#wpseotab .ui-autocomplete { + position: absolute; + cursor: default; +} + +/* workarounds */ +* html #wpseotab .ui-autocomplete { + width: 1px; +} + +/* without this, the menu expands to 100% in IE6 */ + +/* + * jQuery UI Menu 1.8.12 + * + * http://docs.jquery.com/UI/Menu#theming + */ +.ui-menu { + display: block; + float: left; + margin: 0; + padding: 2px; + list-style: none; +} + +.ui-menu .ui-menu { + margin-top: -3px; +} + +.ui-menu .ui-menu-item { + float: left; + clear: left; + zoom: 1; + width: 100%; + margin: 0; + padding: 0; +} + +.ui-menu .ui-menu-item a { + display: block; + zoom: 1; + padding: 0.2em 0.4em; + line-height: 1.5; + text-decoration: none; +} + +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-active { + margin: -1px; + color: #000; +} + +.wpseo-score-icon { + display: inline-block; + width: 12px; + height: 12px; + margin: 3px 10px 0 3px; + border-radius: 50%; + background: #888; + vertical-align: top; +} + +.wpseo-score-icon.good { + background-color: #7ad03a; +} + +.wpseo-score-icon.ok { + background-color: #ffba00; +} + +.wpseo-score-icon.poor { + background-color: #ee7c1b; +} + +.wpseo-score-icon.bad { + background-color: #dd3d36; +} + +.wpseo-score-icon.na { + background-color: #888; +} + +.wpseo-score-icon.noindex { + background-color: #1e8cbe; +} + +.wpseo-score-title { + font-weight: 600; +} + +img.yoast_help { + cursor: pointer; +} diff --git a/wp-content/plugins/wordpress-seo/css/metabox-tabs.min.css b/wp-content/plugins/wordpress-seo/css/metabox-tabs.min.css new file mode 100644 index 0000000..bb1e1b5 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/metabox-tabs.min.css @@ -0,0 +1 @@ +ul.wpseo-metabox-tabs{display:none;margin-top:12px;margin-bottom:3px}.wpseo-metabox-tabs-div ul{list-style:none}.wpseo-metabox-tabs li{display:inline}ul.wpseo-metabox-tabs li.active{border-width:1px 1px 0;border-style:solid solid none;background-color:#fdfdfd}ul.wpseo-metabox-tabs li{padding:5px}.wpseo-metabox-tabs a{text-decoration:none}.wpseo-metabox-tabs-div div.wpseo-tabs-panel{overflow:auto;padding:.5em .9em;border:1px solid}.wpseo-heading{padding-left:10px}.wpseotab{display:none}.wpseotab.active{display:block;overflow:auto;padding:.5em .9em;border:1px solid #ddd;background-color:#fdfdfd}#wpseo_meta .postbox .inside .wpseotab{font-size:13px!important}.inside .wpseotab .form-table th{width:140px!important;font-size:13px}.good,.warn,.wrong{font-weight:700}.good{color:green}.warn{color:maroon}.wrong{color:red}#current_seo_title span{padding:2px 5px;background-color:#ffffe0}#wpseosnippet{width:auto;max-width:520px;margin:0 0 10px;padding:0 5px;font-family:Arial,Helvetica,sans-serif;font-style:normal}#wpseosnippet td{margin:0;padding:0}#wpseosnippet cite.url{font-weight:400;font-style:normal}#wpseosnippet a{text-decoration:none}#wpseosnippet .title{display:block;overflow:hidden;width:512px;color:#1e0fbe;font-size:18px!important;line-height:1.2;white-space:nowrap;text-overflow:ellipsis}#wpseosnippet .desc{font-size:small;line-height:1.4;word-wrap:break-word}#wpseosnippet .desc .autogen{color:#777}#wpseosnippet .date{color:gray}#wpseosnippet .desc p{color:#545454;font-size:small;line-height:1.4;word-wrap:break-word}#wpseosnippet .url{color:#006621;font-size:13px;line-height:16px}#wpseosnippet .meta{color:#767676}#wpseosnippet .util{color:#4272db}#wpseosnippet p{margin:0!important}#wpseosnippet a:hover{text-decoration:underline}#focuskwresults ul{margin:0}#focuskwresults li,#focuskwresults p{font-size:13px}#focuskwresults li{margin:0 0 0 20px;list-style-type:disc}.wpseo_hidden{display:none}table.wpseoanalysis th{padding:15px 0 5px;font-size:14px;text-align:left}table.wpseoanalysis th.first{padding-top:0}table.wpseoanalysis td{margin:5px 0;font-size:13px;line-height:16px}table.wpseoanalysis td.score{width:20px;height:18px;padding-left:10px}.wpseo_msg{margin:5px 0 10px;padding:0 5px;border:1px solid #e6db55;background-color:#ffffe0}#wpseotab .ui-widget-content{border:1px solid #ddd;color:#333;background:#f1f1f1}#wpseotab .ui-widget-content a{color:#333}#wpseotab .ui-autocomplete{position:absolute;cursor:default}* html #wpseotab .ui-autocomplete{width:1px}.ui-menu{display:block;float:left;margin:0;padding:2px;list-style:none}.ui-menu .ui-menu{margin-top:-3px}.ui-menu .ui-menu-item{float:left;clear:left;zoom:1;width:100%;margin:0;padding:0}.ui-menu .ui-menu-item a{display:block;zoom:1;padding:.2em .4em;line-height:1.5;text-decoration:none}.ui-menu .ui-menu-item a.ui-state-active,.ui-menu .ui-menu-item a.ui-state-hover{margin:-1px;color:#000}.wpseo-score-icon{display:inline-block;width:12px;height:12px;margin:3px 10px 0 3px;border-radius:50%;background:#888;vertical-align:top}.wpseo-score-icon.good{background-color:#7ad03a}.wpseo-score-icon.ok{background-color:#ffba00}.wpseo-score-icon.poor{background-color:#ee7c1b}.wpseo-score-icon.bad{background-color:#dd3d36}.wpseo-score-icon.na{background-color:#888}.wpseo-score-icon.noindex{background-color:#1e8cbe}.wpseo-score-title{font-weight:600}img.yoast_help{cursor:pointer} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/css/taxonomy-meta.css b/wp-content/plugins/wordpress-seo/css/taxonomy-meta.css new file mode 100644 index 0000000..51289f7 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/taxonomy-meta.css @@ -0,0 +1,5 @@ +.wpseo-taxonomy-form tr, +.wpseo-taxonomy-form th, +.wpseo-taxonomy-form td { + vertical-align: top; +} diff --git a/wp-content/plugins/wordpress-seo/css/taxonomy-meta.min.css b/wp-content/plugins/wordpress-seo/css/taxonomy-meta.min.css new file mode 100644 index 0000000..6fd0c5c --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/taxonomy-meta.min.css @@ -0,0 +1 @@ +.wpseo-taxonomy-form td,.wpseo-taxonomy-form th,.wpseo-taxonomy-form tr{vertical-align:top} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/css/wpseo-rtl.css b/wp-content/plugins/wordpress-seo/css/wpseo-rtl.css new file mode 100644 index 0000000..06dd1b6 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/wpseo-rtl.css @@ -0,0 +1,69 @@ +/** + * RTL support + */ + +p.desc { + padding: 0 22px 8px 0; +} + +p.desc.label { + padding: 2px 180px 10px 0; +} + +div.yoastbox ul { + margin-right: 20px; + margin-left: auto; +} + +.postbox { + margin: 10px 0 0 10px; +} + +label { + float: right; + margin-right: 6px; + margin-left: auto; +} + +input.textinput, textarea.textinput, select.select, input.checkbox { + float: right; + margin: 12px 0 0 3px; +} + +label.radio { + margin-left: auto; +} + +table.yoast_help th, table.yoast_help td { + text-align: right; +} + +.button.fixit { + float: left; +} + +pre { + direction: ltr; +} + +#pointer-primary { + margin: 0 0 0 5px; +} + +.wpseo-heading { + padding-right: 10px; + padding-left: 0; +} + +.wpseo_yahoo_kw { + margin: 3px 0 3px 8px; +} + +table.wpseoanalysis th { + text-align: right; +} + +table.wpseoanalysis td.score { + padding-right: 10px; + padding-left: 0; +} diff --git a/wp-content/plugins/wordpress-seo/css/wpseo-rtl.min.css b/wp-content/plugins/wordpress-seo/css/wpseo-rtl.min.css new file mode 100644 index 0000000..9b6405c --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/wpseo-rtl.min.css @@ -0,0 +1 @@ +p.desc{padding:0 22px 8px 0}p.desc.label{padding:2px 180px 10px 0}div.yoastbox ul{margin-right:20px;margin-left:auto}.postbox{margin:10px 0 0 10px}label{float:right;margin-right:6px;margin-left:auto}input.checkbox,input.textinput,select.select,textarea.textinput{float:right;margin:12px 0 0 3px}label.radio{margin-left:auto}table.yoast_help td,table.yoast_help th{text-align:right}.button.fixit{float:left}pre{direction:ltr}#pointer-primary{margin:0 0 0 5px}.wpseo-heading{padding-right:10px;padding-left:0}.wpseo_yahoo_kw{margin:3px 0 3px 8px}table.wpseoanalysis th{text-align:right}table.wpseoanalysis td.score{padding-right:10px;padding-left:0} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/css/xml-sitemap-xsl.php b/wp-content/plugins/wordpress-seo/css/xml-sitemap-xsl.php new file mode 100644 index 0000000..4274e20 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/xml-sitemap-xsl.php @@ -0,0 +1,180 @@ +<?php +/** + * @package XML_Sitemaps + */ + +if ( ! defined( 'WPSEO_VERSION' ) ) { + header( 'Status: 403 Forbidden' ); + header( 'HTTP/1.1 403 Forbidden' ); + exit(); +} + +// This is to prevent issues with New Relics stupid auto injection of code. It's ugly but I don't want +// to deal with support requests for other people's wrong code... +if ( extension_loaded( 'newrelic' ) && function_exists( 'newrelic_disable_autorum' ) ) { + newrelic_disable_autorum(); +} + +$xsl = '<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet version="2.0" + xmlns:html="http://www.w3.org/TR/REC-html40" + xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" + xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/> + <xsl:template match="/"> + <html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>XML Sitemap + + + + +
+

XML Sitemap

+

+ Generated by Yoast\'s WordPress SEO plugin, this is an XML Sitemap, meant for consumption by search engines.
+ You can find more information about XML sitemaps on sitemaps.org. +

+ +

+ This XML Sitemap Index file contains sitemaps. +

+ + + + + + + + + + + + + + + + + + +
SitemapLast Modified
+ + + +
+
+ +

+ This XML Sitemap contains URLs. +

+

↑ Sitemap Index

+ + + + + + + + + + + + + + + + + + + + + + + +
URLPrioImagesCh. Freq.Last Mod.
+ + + + + + + + + + + + + + +
+
+
+ + + + + + +'; +echo $xsl; diff --git a/wp-content/plugins/wordpress-seo/css/yst_plugin_tools.css b/wp-content/plugins/wordpress-seo/css/yst_plugin_tools.css new file mode 100644 index 0000000..99bb15d --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/yst_plugin_tools.css @@ -0,0 +1,377 @@ +h4 { + clear: both; + margin: 2em 0 0 0; +} + +p.desc { + margin: 6px 0 10px 0; + padding: 0 0 8px 25px; + border-bottom: 1px solid #ddd; +} + +p.desc.label { + margin-bottom: 20px; + padding: 2px 0 10px 180px; +} + +tr.yst_row { + margin: 5px 0 0 0; + padding: 5px 0 0 0; +} + +tr.yst_row.even { + background-color: #f6f6f6; +} + +div.yoastbox ul { + margin-left: 20px; +} + +.postbox { + margin: 10px 10px 0 0; +} + +label { + float: left; + margin-left: 6px; +} + +label.radio { + float: none; +} + +input.textinput, textarea, select { + width: 400px; +} + +input.textinput, textarea.textinput, select.select, input.checkbox { + float: left; + margin: 12px 3px 0 0; + padding: 5px; +} + +select.select { + padding: 3px; +} + +input.checkbox.double { + margin-top: 2px; +} + +.textinput.metadesc { + height: 50px; +} + +textarea.import { + width: 500px; + height: 100px; +} + +label.textinput, label.select, label.checkbox { + width: 180px; + margin: 10px 0 5px 0; + background-color: transparent; +} + +label.radio { + margin-right: 20px; +} + +.wpseo_content_wrapper { + display: table; + width: 100%; +} + +.wpseo_content_cell { + display: table-cell; + height: 500px; + margin: 0; + padding: 0; + vertical-align: top; +} + +#wpseo_content_top { + min-width: 800px; +} + +.wpseo_content_wrapper div.wpseo-warning { + margin: 2em 0 0 0; + padding-right: 10px; + padding-left: 10px; + border: 2px solid red; + background-color: #dedede; +} + +.wpseo_content_wrapper div.wpseo-warning h4 { + margin: 1em 0 0 0; +} + +.wpseo_content_wrapper div.wpseo-warning p.error-message { + font-weight: normal; +} + +#sidebar-container { + width: 261px; + padding: 0 0 0 20px; +} + +@media (max-width: 1020px) { + #wpseo_content_top { + margin-left: 0; + padding-left: 0; + } +} + +.wpseo-admin-page .form-table tr, +.wpseo-admin-page .form-table th, +.wpseo-admin-page .form-table td { + vertical-align: top; +} + +.postbox form { + line-height: 150%; +} + +div.yoastbox ul li { + font-size: 11px; + line-height: 16px; + list-style: square; +} + +div.yoastbox a { + font-family: sans-serif; + font-size: 12px; +} + +div.yoastbox a:hover { + text-decoration: underline; +} + +.text { + width: 250px; +} + +div.yoastbox .button:hover, div.yoastbox .button-primary:hover { + text-decoration: none; +} + +/*.button, .button-primary {*/ +/*margin-top: 10px;*/ +/*}*/ + +table.yoast_help, table.yoast_help th, table.yoast_help td { + border: 1px solid #ddd; + border-collapse: collapse; + font-size: 12px; +} + +table.yoast_help th, table.yoast_help td { + padding: 5px 10px; + text-align: left; + vertical-align: text-top; +} + +table.yoast_help tr:nth-child(2n) { + background-color: #fbfbfe; +} + +table.yoast_help tr:hover { + background-color: #ddd; +} + +.correct { + padding: 5px; + color: white; + background-color: green; +} + +.wrong { + padding: 5px; + color: white; + background-color: red; +} + +.wrong code { + padding: 3px 8px; + color: #000; +} + +.button.fixit { + float: right; + margin: 0 5px; +} + +.button.checkit { + float: right; + margin: 0 5px; + padding: 5px 8px; +} + +.fb-buttons .button-primary, +.fb-buttons .button { + margin-right: 8px; +} + +.button { + color: #000 !important; +} + +.button#submit { + color: #fff !important; +} + +.postbox#donate { + border: 2px green; +} + +.wpseotab { + display: none; + /*max-width: 600px !important;*/ + ; +} + +.wpseotab.active { + display: block; +} + +#sidebar .yoastbox { + margin: 0 0 10px 0; + padding: 10px 15px; + border: 1px solid #ccc; + border-radius: 5px 25px; +} + +#sidebar .yoastbox h2 { + font-size: 16px; +} + +#sidebar .promo { + color: #000; +} + +#donate.yoastbox, #sitereview.yoastbox { + border-color: green; + background-color: #cfc; +} + +h2 { + margin-bottom: 20px; +} + +h2 code { + font-size: 23px; +} + +#wpseo-conf { + /*max-width: 600px;*/ + ; +} + +#pointer-primary { + margin: 0 5px 0 0; +} + +#wpseo-debug-info { + clear: both; +} + +#wpseo-debug-info .hndle { + cursor: auto; +} + +#wpseo-debug-info h3 span:last-child { + padding-left: 16px; +} + +#wpseo-debug-info .wpseo-debug { + display: inline-block; + padding-left: 20px; + color: #c00; +} + +input.wpseo-new-title, textarea.wpseo-new-metadesc { + width: 100%; + max-width: 100%; +} + +.wpseotab .extension { + float: left; + box-sizing: border-box; + width: 300px; + height: 230px; + margin: 10px 20px 10px 0; + border: 1px solid #ccc; +} + +.wpseotab .extension p { + margin: 0; + padding: 10px; +} + +.wpseotab .extension h3 { + box-sizing: border-box; + height: 110px; + margin: 0; + padding: 20px 10px 0 120px; + border-bottom: 1px solid #ccc; + background: #fff no-repeat left 10px; + background-size: 130px 100px; +} + +.wpseotab .extension a { + text-decoration: none; +} + +.wpseotab .seo-premium h3 { + background-image: url(../images/Premium_130x100.png); +} + +.wpseotab .video-seo h3 { + background-image: url(../images/Video_130x100.png); +} + +.wpseotab .local-seo h3 { + background-image: url(../images/Local_130x100.png); +} + +.wpseotab .woocommerce-seo h3 { + background-image: url(../images/Woo_130x100.png); +} + +.wpseotab .news-seo h3 { + background-image: url(../images/News_SEO.png); + background-size: 115px 100px; +} + +div#separator { + clear: right; + margin: 0 0 0 25px; +} + +div#separator input.radio { + display: none; + float: left; + width: 0 !important; + min-width: 0 !important; + opacity: 0; +} + +div#separator input.radio + label { + float: left; + width: 30px !important; + margin: 3px 3px 0 0; + padding: 5px 8px; + border: 1px solid #ccc; + /* Don't change: these mimic Google's font and font size for titles */ + font-family: Arial,Helvetica,sans-serif !important; + font-size: 18px !important; + line-height: 25px; + text-align: center; + cursor: pointer; +} + +div#separator input.radio:checked + label { + border: 1px solid green; + background-color: #fff; + box-shadow: 1px 1px 1px green; +} diff --git a/wp-content/plugins/wordpress-seo/css/yst_plugin_tools.min.css b/wp-content/plugins/wordpress-seo/css/yst_plugin_tools.min.css new file mode 100644 index 0000000..f12ac6b --- /dev/null +++ b/wp-content/plugins/wordpress-seo/css/yst_plugin_tools.min.css @@ -0,0 +1 @@ +h4{clear:both;margin:2em 0 0}p.desc{margin:6px 0 10px;padding:0 0 8px 25px;border-bottom:1px solid #ddd}p.desc.label{margin-bottom:20px;padding:2px 0 10px 180px}tr.yst_row{margin:5px 0 0;padding:5px 0 0}tr.yst_row.even{background-color:#f6f6f6}div.yoastbox ul{margin-left:20px}.postbox{margin:10px 10px 0 0}label{float:left;margin-left:6px}label.radio{float:none}input.textinput,select,textarea{width:400px}input.checkbox,input.textinput,select.select,textarea.textinput{float:left;margin:12px 3px 0 0;padding:5px}select.select{padding:3px}input.checkbox.double{margin-top:2px}.textinput.metadesc{height:50px}textarea.import{width:500px;height:100px}label.checkbox,label.select,label.textinput{width:180px;margin:10px 0 5px;background-color:transparent}label.radio{margin-right:20px}.wpseo_content_wrapper{display:table;width:100%}.wpseo_content_cell{display:table-cell;height:500px;margin:0;padding:0;vertical-align:top}#wpseo_content_top{min-width:800px}.wpseo_content_wrapper div.wpseo-warning{margin:2em 0 0;padding-right:10px;padding-left:10px;border:2px solid red;background-color:#dedede}.wpseo_content_wrapper div.wpseo-warning h4{margin:1em 0 0}.wpseo_content_wrapper div.wpseo-warning p.error-message{font-weight:400}#sidebar-container{width:261px;padding:0 0 0 20px}@media (max-width:1020px){#wpseo_content_top{margin-left:0;padding-left:0}}.wpseo-admin-page .form-table td,.wpseo-admin-page .form-table th,.wpseo-admin-page .form-table tr{vertical-align:top}.postbox form{line-height:150%}div.yoastbox ul li{font-size:11px;line-height:16px;list-style:square}div.yoastbox a{font-family:sans-serif;font-size:12px}div.yoastbox a:hover{text-decoration:underline}.text{width:250px}div.yoastbox .button-primary:hover,div.yoastbox .button:hover{text-decoration:none}table.yoast_help,table.yoast_help td,table.yoast_help th{border:1px solid #ddd;border-collapse:collapse;font-size:12px}table.yoast_help td,table.yoast_help th{padding:5px 10px;text-align:left;vertical-align:text-top}table.yoast_help tr:nth-child(2n){background-color:#fbfbfe}table.yoast_help tr:hover{background-color:#ddd}.correct{padding:5px;color:#fff;background-color:green}.wrong{padding:5px;color:#fff;background-color:red}.wrong code{padding:3px 8px;color:#000}.button.fixit{float:right;margin:0 5px}.button.checkit{float:right;margin:0 5px;padding:5px 8px}.fb-buttons .button,.fb-buttons .button-primary{margin-right:8px}.button{color:#000!important}.button#submit{color:#fff!important}.postbox#donate{border:2px green}.wpseotab{display:none}.wpseotab.active{display:block}#sidebar .yoastbox{margin:0 0 10px;padding:10px 15px;border:1px solid #ccc;border-radius:5px 25px}#sidebar .yoastbox h2{font-size:16px}#sidebar .promo{color:#000}#donate.yoastbox,#sitereview.yoastbox{border-color:green;background-color:#cfc}h2{margin-bottom:20px}h2 code{font-size:23px}#pointer-primary{margin:0 5px 0 0}#wpseo-debug-info{clear:both}#wpseo-debug-info .hndle{cursor:auto}#wpseo-debug-info h3 span:last-child{padding-left:16px}#wpseo-debug-info .wpseo-debug{display:inline-block;padding-left:20px;color:#c00}input.wpseo-new-title,textarea.wpseo-new-metadesc{width:100%;max-width:100%}.wpseotab .extension{float:left;box-sizing:border-box;width:300px;height:230px;margin:10px 20px 10px 0;border:1px solid #ccc}.wpseotab .extension p{margin:0;padding:10px}.wpseotab .extension h3{box-sizing:border-box;height:110px;margin:0;padding:20px 10px 0 120px;border-bottom:1px solid #ccc;background:#fff no-repeat left 10px;background-size:130px 100px}.wpseotab .extension a{text-decoration:none}.wpseotab .seo-premium h3{background-image:url(../images/Premium_130x100.png)}.wpseotab .video-seo h3{background-image:url(../images/Video_130x100.png)}.wpseotab .local-seo h3{background-image:url(../images/Local_130x100.png)}.wpseotab .woocommerce-seo h3{background-image:url(../images/Woo_130x100.png)}.wpseotab .news-seo h3{background-image:url(../images/News_SEO.png);background-size:115px 100px}div#separator{clear:right;margin:0 0 0 25px}div#separator input.radio{display:none;float:left;width:0!important;min-width:0!important;opacity:0}div#separator input.radio+label{float:left;width:30px!important;margin:3px 3px 0 0;padding:5px 8px;border:1px solid #ccc;font-family:Arial,Helvetica,sans-serif!important;font-size:18px!important;line-height:25px;text-align:center;cursor:pointer}div#separator input.radio:checked+label{border:1px solid green;background-color:#fff;box-shadow:1px 1px 1px green} \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/frontend/class-breadcrumbs.php b/wp-content/plugins/wordpress-seo/frontend/class-breadcrumbs.php new file mode 100644 index 0000000..9f56ab4 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/frontend/class-breadcrumbs.php @@ -0,0 +1,873 @@ +options = WPSEO_Options::get_all(); + $this->post = ( isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null ); + $this->show_on_front = get_option( 'show_on_front' ); + $this->page_for_posts = get_option( 'page_for_posts' ); + + $this->filter_element(); + $this->filter_separator(); + $this->filter_wrapper(); + + $this->set_crumbs(); + $this->prepare_links(); + $this->links_to_string(); + $this->wrap_breadcrumb( ); + } + + /** + * Get breadcrumb string using the singleton instance of this class + * + * @return object + */ + public static function breadcrumb( $before = '', $after = '', $display = true ) { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + // Remember the last used before/after for use in case the object goes __toString() + self::$before = $before; + self::$after = $after; + + $output = $before . self::$instance->output . $after; + + if ( $display === true ) { + echo $output; + return true; + } + else { + return $output; + } + } + + /** + * Magic method to use in case the class would be send to string + * + * @return string + */ + public function __toString() { + return self::$before . $this->output . self::$after; + } + + + /** + * Filter: 'wpseo_breadcrumb_single_link_wrapper' - Allows developer to change or wrap each breadcrumb element + * + * @api string $element + */ + private function filter_element() { + $this->element = esc_attr( apply_filters( 'wpseo_breadcrumb_single_link_wrapper', $this->element ) ); + } + + /** + * Filter: 'wpseo_breadcrumb_separator' - Allow (theme) developer to change the WP SEO breadcrumb separator. + * + * @api string $breadcrumbs_sep Breadcrumbs separator + */ + private function filter_separator() { + $separator = apply_filters( 'wpseo_breadcrumb_separator', $this->options['breadcrumbs-sep'] ); + $this->separator = ' ' . $separator . ' '; + } + + /** + * Filter: 'wpseo_breadcrumb_output_wrapper' - Allow changing the HTML wrapper element for the WP SEO breadcrumbs output + * + * @api string $wrapper The wrapper element + */ + private function filter_wrapper() { + $wrapper = apply_filters( 'wpseo_breadcrumb_output_wrapper', $this->wrapper ); + $wrapper = tag_escape( $wrapper ); + if ( is_string( $wrapper ) && '' !== $wrapper ) { + $this->wrapper = $wrapper; + } + } + + + /** + * Get a term's parents. + * + * @param object $term Term to get the parents for + * @return array + */ + private function get_term_parents( $term ) { + $tax = $term->taxonomy; + $parents = array(); + while ( $term->parent != 0 ) { + $term = get_term( $term->parent, $tax ); + $parents[] = $term; + } + return array_reverse( $parents ); + } + + /** + * Find the deepest term in an array of term objects + * + * @param array $terms + * + * @return object + */ + private function find_deepest_term( $terms ) { + /* Let's find the deepest term in this array, by looping through and then + unsetting every term that is used as a parent by another one in the array. */ + $terms_by_id = array(); + foreach ( $terms as $term ) { + $terms_by_id[ $term->term_id ] = $term; + } + foreach ( $terms as $term ) { + unset( $terms_by_id[ $term->parent ] ); + } + + /* As we could still have two subcategories, from different parent categories, + let's pick the one with the lowest ordered ancestor. */ + $parents_count = 0; + $term_order = 9999; //because ASC + reset( $terms_by_id ); + $deepest_term = current( $terms_by_id ); + foreach ( $terms_by_id as $term ) { + $parents = $this->get_term_parents( $term ); + + if ( count( $parents ) >= $parents_count ) { + $parents_count = count( $parents ); + + //if higher count + if ( count( $parents ) > $parents_count ) { + //reset order + $term_order = 9999; + } + + $parent_order = 9999; //set default order + foreach ( $parents as $parent ) { + if ( $parent->parent == 0 && isset( $parent->term_order ) ) { + $parent_order = $parent->term_order; + } + } + + //check if parent has lowest order + if ( $parent_order < $term_order ) { + $term_order = $parent_order; + + $deepest_term = $term; + } + } + } + return $deepest_term; + } + + /** + * Retrieve the hierachical ancestors for the current 'post' + * + * @return array + */ + private function get_post_ancestors() { + $ancestors = array(); + + if ( isset( $this->post->ancestors ) ) { + if ( is_array( $this->post->ancestors ) ) { + $ancestors = array_values( $this->post->ancestors ); + } + else { + $ancestors = array( $this->post->ancestors ); + } + } + elseif ( isset( $this->post->post_parent ) ) { + $ancestors = array( $this->post->post_parent ); + } + + /** + * Filter: Allow changing the ancestors for the WP SEO breadcrumbs output + * + * @api array $ancestors Ancestors + */ + $ancestors = apply_filters( 'wp_seo_get_bc_ancestors', $ancestors ); + + if ( ! is_array( $ancestors ) ) { + trigger_error( 'The return value for the "wp_seo_get_bc_ancestors" filter should be an array.', E_USER_WARNING ); + $ancestors = (array) $ancestors; + } + + // Reverse the order so it's oldest to newest + $ancestors = array_reverse( $ancestors ); + + return $ancestors; + } + + /** + * Determine the crumbs which should form the breadcrumb. + */ + private function set_crumbs() { + global $wp_query; + + $this->add_home_crumb(); + $this->maybe_add_blog_crumb(); + + if ( ( $this->show_on_front === 'page' && is_front_page() ) || ( $this->show_on_front === 'posts' && is_home() ) ) { + // do nothing + } + elseif ( $this->show_on_front == 'page' && is_home() ) { + $this->add_blog_crumb(); + } + elseif ( is_singular() ) { + $this->maybe_add_pt_archive_crumb_for_post(); + + if ( isset( $this->post->post_parent ) && 0 == $this->post->post_parent ) { + $this->maybe_add_taxonomy_crumbs_for_post(); + } + else { + $this->add_post_ancestor_crumbs(); + } + + if ( isset( $this->post->ID ) ) { + $this->add_single_post_crumb( $this->post->ID ); + } + } + else { + if ( is_post_type_archive() ) { + $this->add_ptarchive_crumb( $wp_query->query['post_type'] ); + } + elseif ( is_tax() || is_tag() || is_category() ) { + $this->add_crumbs_for_taxonomy(); + } + elseif ( is_date() ) { + if ( is_day() ) { + $this->add_linked_month_year_crumb(); + $this->add_date_crumb(); + } + elseif ( is_month() ) { + $this->add_month_crumb(); + } + elseif ( is_year() ) { + $this->add_year_crumb(); + } + } + elseif ( is_author() ) { + $user = $wp_query->get_queried_object(); + $this->add_predefined_crumb( + $this->options['breadcrumbs-archiveprefix'] . ' ' . $user->display_name, + null, + true + ); + } + elseif ( is_search() ) { + $this->add_predefined_crumb( + $this->options['breadcrumbs-searchprefix'] . ' "' . esc_html( get_search_query() ) . '"', + null, + true + ); + } + elseif ( is_404() ) { + + if ( 0 !== get_query_var( 'year' ) || ( 0 !== get_query_var( 'monthnum' ) || 0 !== get_query_var( 'day' ) ) ) { + if ( 'page' == $this->show_on_front && ! is_home() ) { + if ( $this->page_for_posts && $this->options['breadcrumbs-blog-remove'] === false ) { + $this->add_blog_crumb(); + } + } + + if ( 0 !== get_query_var( 'day' ) ) { + $this->add_linked_month_year_crumb(); + + $date = sprintf( '%04d-%02d-%02d 00:00:00', get_query_var( 'year' ), get_query_var( 'monthnum' ), get_query_var( 'day' ) ); + $this->add_date_crumb( $date ); + } + elseif ( 0 !== get_query_var( 'monthnum' ) ) { + $this->add_month_crumb(); + } + elseif ( 0 !== get_query_var( 'year' ) ) { + $this->add_year_crumb(); + } + } + else { + $this->add_predefined_crumb( + $this->options['breadcrumbs-404crumb'], + null, + true + ); + } + } + } + + /** + * Filter: 'wpseo_breadcrumb_links' - Allow the developer to filter the WP SEO breadcrumb links, add to them, change order, etc. + * + * @api array $crumbs The crumbs array + */ + $this->crumbs = apply_filters( 'wpseo_breadcrumb_links', $this->crumbs ); + + $this->crumb_count = count( $this->crumbs ); + } + + + /** + * Add a single id based crumb to the crumbs property + */ + private function add_single_post_crumb( $id ) { + $this->crumbs[] = array( + 'id' => $id, + ); + } + + /** + * Add a term based crumb to the crumbs property + */ + private function add_term_crumb( $term ) { + $this->crumbs[] = array( + 'term' => $term, + ); + } + + /** + * Add a ptarchive based crumb to the crumbs property + */ + private function add_ptarchive_crumb( $pt ) { + $this->crumbs[] = array( + 'ptarchive' => $pt, + ); + } + + /** + * Add a predefined crumb to the crumbs property + */ + private function add_predefined_crumb( $text, $url = '', $allow_html = false ) { + $this->crumbs[] = array( + 'text' => $text, + 'url' => $url, + 'allow_html' => $allow_html, + ); + } + + /** + * Add Homepage crumb to the crumbs property + */ + private function add_home_crumb() { + $this->add_predefined_crumb( + $this->options['breadcrumbs-home'], + get_home_url(), + true + ); + } + + /** + * Add Blog crumb to the crumbs property + */ + private function add_blog_crumb() { + $this->add_single_post_crumb( $this->page_for_posts ); + } + + /** + * Add Blog crumb to the crumbs property for single posts where Home != blogpage + */ + private function maybe_add_blog_crumb() { + if ( ( 'page' === $this->show_on_front && 'post' === get_post_type() ) && ( ! is_home() && ! is_search() ) ) { + if ( $this->page_for_posts && $this->options['breadcrumbs-blog-remove'] === false ) { + $this->add_blog_crumb(); + } + } + } + + /** + * Add ptarchive crumb to the crumbs property if it can be linked to, for a single post + */ + private function maybe_add_pt_archive_crumb_for_post() { + if ( isset( $this->post->post_type ) && get_post_type_archive_link( $this->post->post_type ) ) { + $this->add_ptarchive_crumb( $this->post->post_type ); + } + } + + /** + * Add taxonomy crumbs to the crumbs property for a single post + */ + private function maybe_add_taxonomy_crumbs_for_post() { + if ( isset( $this->options[ 'post_types-' . $this->post->post_type . '-maintax' ] ) && $this->options[ 'post_types-' . $this->post->post_type . '-maintax' ] != '0' ) { + $main_tax = $this->options[ 'post_types-' . $this->post->post_type . '-maintax' ]; + if ( isset( $this->post->ID ) ) { + $terms = wp_get_object_terms( $this->post->ID, $main_tax ); + + if ( is_array( $terms ) && $terms !== array() ) { + + $deepest_term = $this->find_deepest_term( $terms ); + + if ( is_taxonomy_hierarchical( $main_tax ) && $deepest_term->parent != 0 ) { + $parent_terms = $this->get_term_parents( $deepest_term ); + foreach ( $parent_terms as $parent_term ) { + $this->add_term_crumb( $parent_term ); + } + } + + $this->add_term_crumb( $deepest_term ); + } + } + } + } + + /** + * Add hierarchical ancestor crumbs to the crumbs property for a single post + */ + private function add_post_ancestor_crumbs() { + $ancestors = $this->get_post_ancestors(); + if ( is_array( $ancestors ) && $ancestors !== array() ) { + foreach ( $ancestors as $ancestor ) { + $this->add_single_post_crumb( $ancestor ); + } + } + } + + /** + * Add taxonomy parent crumbs to the crumbs property for a taxonomy + */ + private function add_crumbs_for_taxonomy() { + global $wp_query; + + $term = $wp_query->get_queried_object(); + + // @todo adjust function name!! + $this->maybe_add_preferred_term_parent_crumb( $term ); + + $this->maybe_add_term_parent_crumbs( $term ); + + $this->add_term_crumb( $term ); + } + + /** + * Add parent taxonomy crumb based on user defined preference + */ + private function maybe_add_preferred_term_parent_crumb( $term ) { + if ( isset( $this->options[ 'taxonomy-' . $term->taxonomy . '-ptparent' ] ) && $this->options[ 'taxonomy-' . $term->taxonomy . '-ptparent' ] != '0' ) { + if ( 'post' == $this->options[ 'taxonomy-' . $term->taxonomy . '-ptparent' ] && $this->show_on_front == 'page' ) { + if ( $this->page_for_posts ) { + $this->add_blog_crumb(); + } + } + else { + $this->add_ptarchive_crumb( $this->options[ 'taxonomy-' . $term->taxonomy . '-ptparent' ] ); + } + } + } + + /** + * Add parent taxonomy crumbs to the crumb property for hierachical taxonomy + */ + private function maybe_add_term_parent_crumbs( $term ) { + if ( is_taxonomy_hierarchical( $term->taxonomy ) && $term->parent != 0 ) { + foreach ( $this->get_term_parents( $term ) as $parent_term ) { + $this->add_term_crumb( $parent_term ); + } + } + } + + /** + * Add month-year crumb to crumbs property + */ + private function add_linked_month_year_crumb() { + global $wp_locale; + + $this->add_predefined_crumb( + $wp_locale->get_month( get_query_var( 'monthnum' ) ) . ' ' . get_query_var( 'year' ), + get_month_link( get_query_var( 'year' ), get_query_var( 'monthnum' ) ) + ); + } + + /** + * Add (non-link) month crumb to crumbs property + */ + private function add_month_crumb() { + $this->add_predefined_crumb( + $this->options['breadcrumbs-archiveprefix'] . ' ' . esc_html( single_month_title( ' ', false ) ), + null, + true + ); + } + + /** + * Add (non-link) year crumb to crumbs property + */ + private function add_year_crumb() { + $this->add_predefined_crumb( + $this->options['breadcrumbs-archiveprefix'] . ' ' . esc_html( get_query_var( 'year' ) ), + null, + true + ); + } + + /** + * Add (non-link) date crumb to crumbs property + */ + private function add_date_crumb( $date = null ) { + if ( is_null( $date ) ) { + $date = get_the_date(); + } + else { + $date = mysql2date( get_option( 'date_format' ), $date, true ); + $date = apply_filters( 'get_the_date', $date, '' ); + } + + $this->add_predefined_crumb( + $this->options['breadcrumbs-archiveprefix'] . ' ' . esc_html( $date ), + null, + true + ); + } + + + /** + * Take the crumbs array and convert each crumb to a single breadcrumb string. + * + * @link http://support.google.com/webmasters/bin/answer.py?hl=en&answer=185417 Google documentation on RDFA + */ + private function prepare_links() { + if ( ! is_array( $this->crumbs ) || $this->crumbs === array() ) { + return; + } + + foreach ( $this->crumbs as $i => $crumb ) { + $link_info = $crumb; // Keep pre-set url/text combis + + if ( isset( $crumb['id'] ) ) { + $link_info = $this->get_link_info_for_id( $crumb['id'] ); + } + if ( isset( $crumb['term'] ) ) { + $link_info = $this->get_link_info_for_term( $crumb['term'] ); + } + if ( isset( $crumb['ptarchive'] ) ) { + $link_info = $this->get_link_info_for_ptarchive( $crumb['ptarchive'] ); + } + + $this->links[] = $this->crumb_to_link( $link_info, $i ); + } + } + + /** + * Retrieve link url and text based on post id + * + * @param int $id Post id + * + * @return array $link Array of link text and url + */ + private function get_link_info_for_id( $id ) { + $link = array(); + + $link['url'] = get_permalink( $id ); + $link['text'] = WPSEO_Meta::get_value( 'bctitle', $id ); + if ( $link['text'] === '' ) { + $link['text'] = strip_tags( get_the_title( $id ) ); + } + + /** + * Filter: 'wp_seo_get_bc_title' - Allow developer to filter the WP SEO Breadcrumb title. + * + * @api string $link_text The Breadcrumb title text + * + * @param int $link_id The post ID + */ + $link['text'] = apply_filters( 'wp_seo_get_bc_title', $link['text'], $id ); + + return $link; + } + + /** + * Retrieve link url and text based on term object + * + * @param object $term Term object + * + * @return array $link Array of link text and url + */ + private function get_link_info_for_term( $term ) { + $link = array(); + + $bctitle = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'bctitle' ); + if ( ! is_string( $bctitle ) || $bctitle === '' ) { + $bctitle = $term->name; + } + + $link['url'] = get_term_link( $term ); + $link['text'] = $bctitle; + + return $link; + } + + /** + * Retrieve link url and text based on post type + * + * @param string $pt Post type + * + * @return array $link Array of link text and url + */ + private function get_link_info_for_ptarchive( $pt ) { + $link = array(); + $archive_title = ''; + + if ( isset( $this->options[ 'bctitle-ptarchive-' . $pt ] ) && $this->options[ 'bctitle-ptarchive-' . $pt ] !== '' ) { + + $archive_title = $this->options[ 'bctitle-ptarchive-' . $pt ]; + } + else { + $post_type_obj = get_post_type_object( $pt ); + if ( is_object( $post_type_obj ) ) { + if ( isset( $post_type_obj->label ) && $post_type_obj->label !== '' ) { + $archive_title = $post_type_obj->label; + } + elseif ( isset( $post_type_obj->labels->menu_name ) && $post_type_obj->labels->menu_name !== '' ) { + $archive_title = $post_type_obj->labels->menu_name; + } + else { + $archive_title = $post_type_obj->name; + } + } + } + + $link['url'] = get_post_type_archive_link( $pt ); + $link['text'] = $archive_title; + + return $link; + } + + + /** + * Create a breadcrumb element string + * + * @param array $link Link info array containing the keys: + * 'text' => (string) link text + * 'url' => (string) link url + * (optional) 'allow_html' => (bool) whether to (not) escape html in the link text + * This prevents html stripping from the text strings set in the + * WPSEO -> Internal Links options page + * + * @return string + */ + private function crumb_to_link( $link, $i ) { + global $paged; // @todo ? -> only works for archives, not for paged articles + + $link_output = ''; + + if ( isset( $link['text'] ) && ( is_string( $link['text'] ) && $link['text'] !== '' ) ) { + + $link['text'] = trim( $link['text'] ); + if ( ! isset( $link['allow_html'] ) || $link['allow_html'] !== true ) { + $link['text'] = esc_html( $link['text'] ); + } + + $inner_elm = 'span'; + if ( $this->options['breadcrumbs-boldlast'] === true && $i === ( $this->crumb_count - 1 ) ) { + $inner_elm = 'strong'; + } + + $class = ''; + if ( $i === ( $this->crumb_count - 1 ) ) { + $class = ' class="breadcrumb_last"'; + } + + + $link_output = '<' . $this->element . ' typeof="v:Breadcrumb">'; + + if ( ( isset( $link['url'] ) && ( is_string( $link['url'] ) && $link['url'] !== '' ) ) && + ( $i < ( $this->crumb_count - 1 ) || $paged ) ) { + $link_output .= '' . $link['text'] . ''; + } + else { + $link_output .= '<' . $inner_elm . $class . ' property="v:title">' . $link['text'] . ''; + } + + $link_output .= 'element . '>'; + + } + /** + * Filter: 'wpseo_breadcrumb_single_link' - Allow changing of each link being put out by the WP SEO breadcrumbs class + * + * @api string $link_output The output string + * + * @param array $link The link array + */ + return apply_filters( 'wpseo_breadcrumb_single_link', $link_output, $link ); + } + + + /** + * Create a complete breadcrumb string from an array of breadcrumb element strings + */ + private function links_to_string() { + if ( is_array( $this->links ) && $this->links !== array() ) { + // Remove any effectively empty links + $links = array_map( 'trim', $this->links ); + $links = array_filter( $links ); + + $this->output = implode( $this->separator, $links ); + } + } + + /** + * Wrap a complete breadcrumb string in a Breadcrumb RDFA wrapper + */ + private function wrap_breadcrumb() { + if ( is_string( $this->output ) && $this->output !== '' ) { + $output = ' + <' . $this->wrapper . $this->get_output_id() . $this->get_output_class() . ' prefix="v: http://rdf.data-vocabulary.org/#"> + ' . $this->output . ' + wrapper . '>'; + + /** + * Filter: 'wpseo_breadcrumb_output' - Allow changing the HTML output of the WP SEO breadcrumbs class + * + * @api string $unsigned HTML output + */ + $output = apply_filters( 'wpseo_breadcrumb_output', $output ); + + if ( $this->options['breadcrumbs-prefix'] !== '' ) { + $output = "\t" . $this->options['breadcrumbs-prefix'] . "\n" . $output; + } + + $this->output = $output; + } + } + + + /** + * Filter: 'wpseo_breadcrumb_output_id' - Allow changing the HTML ID on the WP SEO breadcrumbs wrapper element + * + * @api string $unsigned ID to add to the wrapper element + */ + private function get_output_id() { + $id = apply_filters( 'wpseo_breadcrumb_output_id', '' ); + if ( is_string( $id ) && '' !== $id ) { + $id = ' id="' . esc_attr( $id ) . '"'; + } + return $id; + } + + /** + * Filter: 'wpseo_breadcrumb_output_class' - Allow changing the HTML class on the WP SEO breadcrumbs wrapper element + * + * @api string $unsigned class to add to the wrapper element + */ + private function get_output_class() { + $class = apply_filters( 'wpseo_breadcrumb_output_class', '' ); + if ( is_string( $class ) && '' !== $class ) { + $class = ' class="' . esc_attr( $class ) . '"'; + } + return $class; + } + + + /********************** DEPRECATED METHODS **********************/ + + /** + * Wrapper function for the breadcrumb so it can be output for the supported themes. + * + * @deprecated 1.5.0 + */ + public function breadcrumb_output() { + _deprecated_function( __METHOD__, '1.5.0', 'yoast_breadcrumb' ); + self::breadcrumb( '
', '
' ); + } + + /** + * Take the links array and return a full breadcrumb string. + * + * @deprecated 1.5.2.3 + * + * @return string + */ + public function create_breadcrumbs_string( $links, $wrapper = 'span', $element = 'span' ) { + _deprecated_function( __METHOD__, 'WPSEO 1.5.2.3', 'yoast_breadcrumbs' ); + } + + + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/frontend/class-frontend.php b/wp-content/plugins/wordpress-seo/frontend/class-frontend.php new file mode 100644 index 0000000..22de2e4 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/frontend/class-frontend.php @@ -0,0 +1,1651 @@ +options = WPSEO_Options::get_all(); + + add_action( 'wp_head', array( $this, 'head' ), 1 ); + + // The head function here calls action wpseo_head, to which we hook all our functionality + add_action( 'wpseo_head', array( $this, 'debug_marker' ), 2 ); + add_action( 'wpseo_head', array( $this, 'robots' ), 6 ); + add_action( 'wpseo_head', array( $this, 'metadesc' ), 10 ); + add_action( 'wpseo_head', array( $this, 'metakeywords' ), 11 ); + add_action( 'wpseo_head', array( $this, 'canonical' ), 20 ); + add_action( 'wpseo_head', array( $this, 'adjacent_rel_links' ), 21 ); + add_action( 'wpseo_head', array( $this, 'publisher' ), 22 ); + add_action( 'wpseo_head', array( $this, 'webmaster_tools_authentication' ), 90 ); + add_action( 'wpseo_head', array( $this, 'internal_search_json_ld' ), 90 ); + + // Remove actions that we will handle through our wpseo_head call, and probably change the output of + remove_action( 'wp_head', 'rel_canonical' ); + remove_action( 'wp_head', 'index_rel_link' ); + remove_action( 'wp_head', 'start_post_rel_link' ); + remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' ); + remove_action( 'wp_head', 'noindex', 1 ); + + add_filter( 'wp_title', array( $this, 'title' ), 15, 3 ); + add_filter( 'thematic_doctitle', array( $this, 'title' ), 15 ); + + add_action( 'wp', array( $this, 'page_redirect' ), 99 ); + + add_action( 'template_redirect', array( $this, 'noindex_feed' ) ); + + add_filter( 'loginout', array( $this, 'nofollow_link' ) ); + add_filter( 'register', array( $this, 'nofollow_link' ) ); + + // Fix the WooThemes woo_title() output + add_filter( 'woo_title', array( $this, 'fix_woo_title' ), 99 ); + + if ( $this->options['hide-rsdlink'] === true ) { + remove_action( 'wp_head', 'rsd_link' ); + } + if ( $this->options['hide-wlwmanifest'] === true ) { + remove_action( 'wp_head', 'wlwmanifest_link' ); + } + if ( $this->options['hide-shortlink'] === true ) { + remove_action( 'wp_head', 'wp_shortlink_wp_head' ); + remove_action( 'template_redirect', 'wp_shortlink_header', 11 ); + } + if ( $this->options['hide-feedlinks'] === true ) { + // @todo: add option to display just normal feed and hide comment feed. + remove_action( 'wp_head', 'feed_links', 2 ); + remove_action( 'wp_head', 'feed_links_extra', 3 ); + } + + if ( ( $this->options['disable-date'] === true || + $this->options['disable-author'] === true ) || + ( isset( $this->options['disable-post_formats'] ) && $this->options['disable-post_formats'] ) + ) { + add_action( 'wp', array( $this, 'archive_redirect' ) ); + } + if ( $this->options['redirectattachment'] === true ) { + add_action( 'template_redirect', array( $this, 'attachment_redirect' ), 1 ); + } + if ( $this->options['trailingslash'] === true ) { + add_filter( 'user_trailingslashit', array( $this, 'add_trailingslash' ), 10, 2 ); + } + if ( $this->options['cleanpermalinks'] === true ) { + add_action( 'template_redirect', array( $this, 'clean_permalink' ), 1 ); + } + if ( $this->options['cleanreplytocom'] === true ) { + add_filter( 'comment_reply_link', array( $this, 'remove_reply_to_com' ) ); + } + add_filter( 'the_content_feed', array( $this, 'embed_rssfooter' ) ); + add_filter( 'the_excerpt_rss', array( $this, 'embed_rssfooter_excerpt' ) ); + + if ( $this->options['forcerewritetitle'] === true ) { + add_action( 'template_redirect', array( $this, 'force_rewrite_output_buffer' ), 99999 ); + add_action( 'wp_footer', array( $this, 'flush_cache' ), -1 ); + } + + if ( $this->options['title_test'] > 0 ) { + add_filter( 'wpseo_title', array( $this, 'title_test_helper' ) ); + } + if ( isset( $_GET['replytocom'] ) ) { + remove_action( 'wp_head', 'wp_no_robots' ); + add_action( 'template_redirect', array( $this, 'replytocom_redirect' ), 1 ); + } + } + + /** + * Override Woo's title with our own. + * + * @param string $title + * + * @return string + */ + public function fix_woo_title( $title ) { + return $this->title( $title ); + } + + /** + * Determine whether the current page is the homepage and shows posts. + * + * @return bool + */ + function is_home_posts_page() { + return ( is_home() && 'posts' == get_option( 'show_on_front' ) ); + } + + /** + * Determine whether the current page is a static homepage. + * + * @return bool + */ + function is_home_static_page() { + return ( is_front_page() && 'page' == get_option( 'show_on_front' ) && is_page( get_option( 'page_on_front' ) ) ); + } + + /** + * Determine whether this is the posts page, regardless of whether it's the frontpage or not. + * + * @return bool + */ + function is_posts_page() { + return ( is_home() && 'page' == get_option( 'show_on_front' ) ); + } + + /** + * Used for static home and posts pages as well as singular titles. + * + * @param object|null $object if filled, object to get the title for + * + * @return string + */ + function get_content_title( $object = null ) { + if ( is_null( $object ) ) { + global $wp_query; + $object = $wp_query->get_queried_object(); + } + + $title = WPSEO_Meta::get_value( 'title', $object->ID ); + + if ( $title !== '' ) { + return wpseo_replace_vars( $title, $object ); + } + + $post_type = ( isset( $object->post_type ) ? $object->post_type : $object->query_var ); + + return $this->get_title_from_options( 'title-' . $post_type, $object ); + } + + /** + * Used for category, tag, and tax titles. + * + * @return string + */ + function get_taxonomy_title() { + global $wp_query; + $object = $wp_query->get_queried_object(); + + $title = WPSEO_Taxonomy_Meta::get_term_meta( $object, $object->taxonomy, 'title' ); + + if ( is_string( $title ) && $title !== '' ) { + return wpseo_replace_vars( $title, $object ); + } else { + return $this->get_title_from_options( 'title-tax-' . $object->taxonomy, $object ); + } + } + + /** + * Used for author titles. + * + * @return string + */ + function get_author_title() { + $author_id = get_query_var( 'author' ); + $title = trim( get_the_author_meta( 'wpseo_title', $author_id ) ); + + if ( $title !== '' ) { + return wpseo_replace_vars( $title, array() ); + } + + return $this->get_title_from_options( 'title-author-wpseo' ); + } + + /** + * Simple function to use to pull data from $options. + * + * All titles pulled from options will be run through the wpseo_replace_vars function. + * + * @param string $index name of the page to get the title from the settings for. + * @param object|array $var_source possible object to pull variables from. + * + * @return string + */ + function get_title_from_options( $index, $var_source = array() ) { + if ( ! isset( $this->options[ $index ] ) || $this->options[ $index ] === '' ) { + if ( is_singular() ) { + return wpseo_replace_vars( '%%title%% %%sep%% %%sitename%%', $var_source ); + } else { + return ''; + } + } else { + return wpseo_replace_vars( $this->options[ $index ], $var_source ); + } + } + + /** + * Get the default title for the current page. + * + * This is the fallback title generator used when a title hasn't been set for the specific content, taxonomy, author + * details, or in the options. It scrubs off any present prefix before or after the title (based on $seplocation) in + * order to prevent duplicate seperations from appearing in the title (this happens when a prefix is supplied to the + * wp_title call on singular pages). + * + * @param string $sep the separator used between variables + * @param string $seplocation Whether the separator should be left or right. + * @param string $title possible title that's already set + * + * @return string + */ + function get_default_title( $sep, $seplocation, $title = '' ) { + if ( 'right' == $seplocation ) { + $regex = '`\s*' . preg_quote( trim( $sep ), '`' ) . '\s*`u'; + } else { + $regex = '`^\s*' . preg_quote( trim( $sep ), '`' ) . '\s*`u'; + } + $title = preg_replace( $regex, '', $title ); + + if ( ! is_string( $title ) || ( is_string( $title ) && $title === '' ) ) { + $title = get_bloginfo( 'name' ); + $title = $this->add_paging_to_title( $sep, $seplocation, $title ); + $title = $this->add_to_title( $sep, $seplocation, $title, strip_tags( get_bloginfo( 'description' ) ) ); + + return $title; + } + + $title = $this->add_paging_to_title( $sep, $seplocation, $title ); + $title = $this->add_to_title( $sep, $seplocation, $title, strip_tags( get_bloginfo( 'name' ) ) ); + + return $title; + } + + /** + * This function adds paging details to the title. + * + * @param string $sep separator used in the title + * @param string $seplocation Whether the separator should be left or right. + * @param string $title the title to append the paging info to + * + * @return string + */ + function add_paging_to_title( $sep, $seplocation, $title ) { + global $wp_query; + + if ( ! empty( $wp_query->query_vars['paged'] ) && $wp_query->query_vars['paged'] > 1 ) { + return $this->add_to_title( $sep, $seplocation, $title, $wp_query->query_vars['paged'] . '/' . $wp_query->max_num_pages ); + } + + return $title; + } + + /** + * Add part to title, while ensuring that the $seplocation variable is respected. + * + * @param string $sep separator used in the title + * @param string $seplocation Whether the separator should be left or right. + * @param string $title the title to append the title_part to + * @param string $title_part the part to append to the title + * + * @return string + */ + function add_to_title( $sep, $seplocation, $title, $title_part ) { + if ( 'right' === $seplocation ) { + return $title . $sep . $title_part; + } + + return $title_part . $sep . $title; + } + + /** + * Main title function. + * + * @param string $title Title that might have already been set. + * @param string $separator Separator determined in theme (unused) + * @param string $separator_location Whether the separator should be left or right. + * + * @return string + */ + function title( $title, $separator = '', $separator_location = '' ) { + + if ( is_feed() ) { + return $title; + } + + $separator = wpseo_replace_vars( '%%sep%%', array() ); + $separator = ' ' . trim( $separator ) . ' '; + + if ( '' === trim( $separator_location ) ) { + $separator_location = ( is_rtl() ) ? 'left' : 'right'; + } + + // This needs to be kept track of in order to generate + // default titles for singular pages. + $original_title = $title; + + // This flag is used to determine if any additional + // processing should be done to the title after the + // main section of title generation completes. + $modified_title = true; + + // This variable holds the page-specific title part + // that is used to generate default titles. + $title_part = ''; + + if ( $this->is_home_static_page() ) { + $title = $this->get_content_title(); + } elseif ( $this->is_home_posts_page() ) { + $title = $this->get_title_from_options( 'title-home-wpseo' ); + } elseif ( $this->is_posts_page() ) { + $title = $this->get_content_title( get_post( get_option( 'page_for_posts' ) ) ); + } elseif ( is_singular() ) { + $title = $this->get_content_title(); + + if ( ! is_string( $title ) || '' === $title ) { + $title_part = $original_title; + } + } elseif ( is_search() ) { + $title = $this->get_title_from_options( 'title-search-wpseo' ); + + if ( ! is_string( $title ) || '' === $title ) { + $title_part = sprintf( __( 'Search for "%s"', 'wordpress-seo' ), esc_html( get_search_query() ) ); + } + } elseif ( is_category() || is_tag() || is_tax() ) { + $title = $this->get_taxonomy_title(); + + if ( ! is_string( $title ) || '' === $title ) { + if ( is_category() ) { + $title_part = single_cat_title( '', false ); + } elseif ( is_tag() ) { + $title_part = single_tag_title( '', false ); + } else { + $title_part = single_term_title( '', false ); + if ( $title_part === '' ) { + global $wp_query; + $term = $wp_query->get_queried_object(); + $title_part = $term->name; + } + } + } + } elseif ( is_author() ) { + $title = $this->get_author_title(); + + if ( ! is_string( $title ) || '' === $title ) { + $title_part = get_the_author_meta( 'display_name', get_query_var( 'author' ) ); + } + } elseif ( is_post_type_archive() ) { + $post_type = get_query_var( 'post_type' ); + + if ( is_array( $post_type ) ) { + $post_type = reset( $post_type ); + } + + $title = $this->get_title_from_options( 'title-ptarchive-' . $post_type ); + + if ( ! is_string( $title ) || '' === $title ) { + $post_type_obj = get_post_type_object( $post_type ); + if ( isset( $post_type_obj->labels->menu_name ) ) { + $title_part = $post_type_obj->labels->menu_name; + } elseif ( isset( $post_type_obj->name ) ) { + $title_part = $post_type_obj->name; + } else { + $title_part = ''; //To be determined what this should be + } + } + } elseif ( is_archive() ) { + $title = $this->get_title_from_options( 'title-archive-wpseo' ); + + // @todo [JRF => Yoast] Should these not use the archive default if no title found ? + // WPSEO_Options::get_default( 'wpseo_titles', 'title-archive-wpseo' ) + // Replacement would be needed! + if ( empty( $title ) ) { + if ( is_month() ) { + $title_part = sprintf( __( '%s Archives', 'wordpress-seo' ), single_month_title( ' ', false ) ); + } elseif ( is_year() ) { + $title_part = sprintf( __( '%s Archives', 'wordpress-seo' ), get_query_var( 'year' ) ); + } elseif ( is_day() ) { + $title_part = sprintf( __( '%s Archives', 'wordpress-seo' ), get_the_date() ); + } else { + $title_part = __( 'Archives', 'wordpress-seo' ); + } + } + } elseif ( is_404() ) { + + if ( 0 !== get_query_var( 'year' ) || ( 0 !== get_query_var( 'monthnum' ) || 0 !== get_query_var( 'day' ) ) ) { + // @todo [JRF => Yoast] Should these not use the archive default if no title found ? + if ( 0 !== get_query_var( 'day' ) ) { + $date = sprintf( '%04d-%02d-%02d 00:00:00', get_query_var( 'year' ), get_query_var( 'monthnum' ), get_query_var( 'day' ) ); + $date = mysql2date( get_option( 'date_format' ), $date, true ); + $date = apply_filters( 'get_the_date', $date, '' ); + $title_part = sprintf( __( '%s Archives', 'wordpress-seo' ), $date ); + } elseif ( 0 !== get_query_var( 'monthnum' ) ) { + $title_part = sprintf( __( '%s Archives', 'wordpress-seo' ), single_month_title( ' ', false ) ); + } elseif ( 0 !== get_query_var( 'year' ) ) { + $title_part = sprintf( __( '%s Archives', 'wordpress-seo' ), get_query_var( 'year' ) ); + } else { + $title_part = __( 'Archives', 'wordpress-seo' ); + } + } else { + $title = $this->get_title_from_options( 'title-404-wpseo' ); + + // @todo [JRF => Yoast] Should these not use the 404 default if no title found ? + // WPSEO_Options::get_default( 'wpseo_titles', 'title-404-wpseo' ) + // Replacement would be needed! + if ( empty( $title ) ) { + $title_part = __( 'Page not found', 'wordpress-seo' ); + } + } + } else { + // In case the page type is unknown, leave the title alone. + $modified_title = false; + + // If you would like to generate a default title instead, + // the following code could be used instead of the line above: + // $title_part = $title; + } + + if ( ( $modified_title && empty( $title ) ) || ! empty( $title_part ) ) { + $title = $this->get_default_title( $separator, $separator_location, $title_part ); + } + + if ( defined( 'ICL_LANGUAGE_CODE' ) && false !== strpos( $title, ICL_LANGUAGE_CODE ) ) { + $title = str_replace( ' @' . ICL_LANGUAGE_CODE, '', $title ); + } + + /** + * Filter: 'wpseo_title' - Allow changing the WP SEO output + * + * @api string $title The page title being put out. + */ + return esc_html( strip_tags( stripslashes( apply_filters( 'wpseo_title', $title ) ) ) ); + } + + /** + * Function used when title needs to be force overridden. + * + * @return string + */ + function force_wp_title() { + global $wp_query; + $old_wp_query = null; + + if ( ! $wp_query->is_main_query() ) { + $old_wp_query = $wp_query; + wp_reset_query(); + } + + $title = $this->title( '' ); + + if ( ! empty( $old_wp_query ) ) { + $GLOBALS['wp_query'] = $old_wp_query; + unset( $old_wp_query ); + } + + return $title; + } + + /** + * Outputs or returns the debug marker, which is also used for title replacement when force rewrite is active. + * + * @param bool $echo Whether or not to echo the debug marker. + * + * @return string + */ + public function debug_marker( $echo = true ) { + $marker = '<!-- This site is optimized with the Yoast WordPress SEO plugin v' . WPSEO_VERSION . ' - https://yoast.com/wordpress/plugins/seo/ -->'; + if ( $echo === false ) { + return $marker; + } else { + echo "\n${marker}\n"; + } + } + + /** + * Output Webmaster Tools authentication strings + */ + public function webmaster_tools_authentication() { + if ( is_front_page() ) { + // Alexa + if ( $this->options['alexaverify'] !== '' ) { + echo '<meta name="alexaVerifyID" content="' . esc_attr( $this->options['alexaverify'] ) . "\" />\n"; + } + + // Bing + if ( $this->options['msverify'] !== '' ) { + echo '<meta name="msvalidate.01" content="' . esc_attr( $this->options['msverify'] ) . "\" />\n"; + } + + // Google + if ( $this->options['googleverify'] !== '' ) { + echo '<meta name="google-site-verification" content="' . esc_attr( $this->options['googleverify'] ) . "\" />\n"; + } + + // Pinterest + if ( $this->options['pinterestverify'] !== '' ) { + echo '<meta name="p:domain_verify" content="' . esc_attr( $this->options['pinterestverify'] ) . "\" />\n"; + } + + // Yandex + if ( $this->options['yandexverify'] !== '' ) { + echo '<meta name="yandex-verification" content="' . esc_attr( $this->options['yandexverify'] ) . "\" />\n"; + } + } + } + + /** + * Outputs JSON+LD code to allow recognition of the internal search engine + * + * @since 1.5.7 + * + * @link https://developers.google.com/webmasters/richsnippets/sitelinkssearch + */ + public function internal_search_json_ld() { + if ( ! is_front_page() ) { + return; + } + + /** + * Filter: 'disable_wpseo_json_ld_search' - Allow disabling of the json+ld output + * + * @api bool $display_search Whether or not to display json+ld search on the frontend + */ + if ( apply_filters( 'disable_wpseo_json_ld_search', false ) === true ) { + return; + } + + $home_url = trailingslashit( home_url() ); + + /** + * Filter: 'wpseo_json_ld_search_url' - Allows filtering of the search URL for WP SEO + * + * @api string $search_url The search URL for this site with a `{search_term}` variable. + */ + $search_url = apply_filters( 'wpseo_json_ld_search_url', $home_url . '?s={search_term}' ); + + /** + * Filter: 'wpseo_json_ld_search_output' - Allows filtering of the entire output of the function + * + * @api string $output The output of the function. + */ + echo apply_filters( 'wpseo_json_ld_search_output', '<script type="application/ld+json">{ "@context": "http://schema.org", "@type": "WebSite", "url": "' . $home_url . '", "potentialAction": { "@type": "SearchAction", "target": "' . $search_url .'", "query-input": "required name=search_term" } }</script>' . "\n" ); + } + + /** + * Main wrapper function attached to wp_head. This combines all the output on the frontend of the WP SEO plugin. + */ + public function head() { + global $wp_query; + + $old_wp_query = null; + + if ( ! $wp_query->is_main_query() ) { + $old_wp_query = $wp_query; + wp_reset_query(); + } + + /** + * Action: 'wpseo_head' - Allow other plugins to output inside the WP SEO section of the head section. + */ + do_action( 'wpseo_head' ); + + echo "<!-- / Yoast WordPress SEO plugin. -->\n\n"; + + if ( ! empty( $old_wp_query ) ) { + $GLOBALS['wp_query'] = $old_wp_query; + unset( $old_wp_query ); + } + + return; + } + + + /** + * Output the meta robots value. + * + * @return string + */ + public function robots() { + global $wp_query; + + $robots = array(); + $robots['index'] = 'index'; + $robots['follow'] = 'follow'; + $robots['other'] = array(); + + if ( is_singular() ) { + global $post; + + if ( is_object( $post ) && ( isset( $this->options[ 'noindex-' . $post->post_type ] ) && $this->options[ 'noindex-' . $post->post_type ] === true ) ) { + $robots['index'] = 'noindex'; + } + + if ( 'private' == $post->post_status ) { + $robots['index'] = 'noindex'; + } + + $robots = $this->robots_for_single_post( $robots ); + + } else { + if ( is_search() ) { + $robots['index'] = 'noindex'; + } elseif ( is_tax() || is_tag() || is_category() ) { + $term = $wp_query->get_queried_object(); + if ( is_object( $term ) && ( isset( $this->options[ 'noindex-tax-' . $term->taxonomy ] ) && $this->options[ 'noindex-tax-' . $term->taxonomy ] === true ) ) { + $robots['index'] = 'noindex'; + } + + // Three possible values, index, noindex and default, do nothing for default + $term_meta = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'noindex' ); + if ( is_string( $term_meta ) && 'default' !== $term_meta ) { + $robots['index'] = $term_meta; + } + } elseif ( + ( is_author() && $this->options['noindex-author-wpseo'] === true ) || + ( is_date() && $this->options['noindex-archive-wpseo'] === true ) + ) { + $robots['index'] = 'noindex'; + } elseif ( is_home() ) { + if ( get_query_var( 'paged' ) > 1 && $this->options['noindex-subpages-wpseo'] === true ) { + $robots['index'] = 'noindex'; + } + + $page_for_posts = get_option( 'page_for_posts' ); + if ( $page_for_posts ) { + $robots = $this->robots_for_single_post( $robots, $page_for_posts ); + } + unset( $page_for_posts ); + + } elseif ( is_post_type_archive() ) { + $post_type = get_query_var( 'post_type' ); + + if ( is_array( $post_type ) ) { + $post_type = reset( $post_type ); + } + + if ( isset( $this->options[ 'noindex-ptarchive-' . $post_type ] ) && $this->options[ 'noindex-ptarchive-' . $post_type ] === true ) { + $robots['index'] = 'noindex'; + } + } + + if ( isset( $wp_query->query_vars['paged'] ) && ( $wp_query->query_vars['paged'] && $wp_query->query_vars['paged'] > 1 ) && ( $this->options['noindex-subpages-wpseo'] === true ) ) { + $robots['index'] = 'noindex'; + $robots['follow'] = 'follow'; + } + + foreach ( array( 'noodp', 'noydir' ) as $robot ) { + if ( $this->options[ $robot ] === true ) { + $robots['other'][] = $robot; + } + } + unset( $robot ); + } + + // Force override to respect the WP settings + if ( '0' == get_option( 'blog_public' ) || isset( $_GET['replytocom'] ) ) { + $robots['index'] = 'noindex'; + } + + + $robotsstr = $robots['index'] . ',' . $robots['follow']; + + if ( $robots['other'] !== array() ) { + $robots['other'] = array_unique( $robots['other'] ); // most likely no longer needed, needs testing + $robotsstr .= ',' . implode( ',', $robots['other'] ); + } + + $robotsstr = preg_replace( '`^index,follow,?`', '', $robotsstr ); + + /** + * Filter: 'wpseo_robots' - Allows filtering of the meta robots output of WP SEO + * + * @api string $robotsstr The meta robots directives to be echoed. + */ + $robotsstr = apply_filters( 'wpseo_robots', $robotsstr ); + + if ( is_string( $robotsstr ) && $robotsstr !== '' ) { + echo '<meta name="robots" content="' . esc_attr( $robotsstr ) . '"/>' . "\n"; + } + + return $robotsstr; + } + + /** + * Determine $robots values for a single post + * + * @param array $robots + * @param int|string $postid The postid for which to determine the $robots values, defaults to + * the current post + * + * @return array + */ + function robots_for_single_post( $robots, $postid = 0 ) { + if ( WPSEO_Meta::get_value( 'meta-robots-noindex', $postid ) === '1' ) { + $robots['index'] = 'noindex'; + } elseif ( WPSEO_Meta::get_value( 'meta-robots-noindex', $postid ) === '2' ) { + $robots['index'] = 'index'; + } + + if ( WPSEO_Meta::get_value( 'meta-robots-nofollow', $postid ) === '1' ) { + $robots['follow'] = 'nofollow'; + } + + $meta_robots_adv = WPSEO_Meta::get_value( 'meta-robots-adv', $postid ); + + if ( $meta_robots_adv !== '' && ( $meta_robots_adv !== '-' && $meta_robots_adv !== 'none' ) ) { + $meta_robots_adv = explode( ',', $meta_robots_adv ); + foreach ( $meta_robots_adv as $robot ) { + $robots['other'][] = $robot; + } + unset( $robot ); + } elseif ( $meta_robots_adv === '' || $meta_robots_adv === '-' ) { + foreach ( array( 'noodp', 'noydir' ) as $robot ) { + if ( $this->options[ $robot ] === true ) { + $robots['other'][] = $robot; + } + } + unset( $robot ); + } + unset( $meta_robots_adv ); + + return $robots; + } + + + /** + * This function normally outputs the canonical but is also used in other places to retrieve + * the canonical URL for the current page. + * + * @param bool $echo Whether or not to output the canonical element. + * @param bool $un_paged Whether or not to return the canonical with or without pagination added to the URL. + * @param bool $no_override Whether or not to return a manually overridden canonical + * + * @return string $canonical + */ + public function canonical( $echo = true, $un_paged = false, $no_override = false ) { + $canonical = false; + $skip_pagination = false; + + // Set decent canonicals for homepage, singulars and taxonomy pages + if ( is_singular() ) { + $meta_canon = WPSEO_Meta::get_value( 'canonical' ); + if ( $no_override === false && $meta_canon !== '' ) { + $canonical = $meta_canon; + $skip_pagination = true; + } else { + $obj = get_queried_object(); + $canonical = get_permalink( $obj->ID ); + + // Fix paginated pages canonical, but only if the page is truly paginated. + if ( get_query_var( 'page' ) > 1 ) { + global $wp_rewrite; + $numpages = substr_count( $obj->post_content, '<!--nextpage-->' ) + 1; + if ( $numpages && get_query_var( 'page' ) <= $numpages ) { + if ( ! $wp_rewrite->using_permalinks() ) { + $canonical = add_query_arg( 'page', get_query_var( 'page' ), $canonical ); + } else { + $canonical = user_trailingslashit( trailingslashit( $canonical ) . get_query_var( 'page' ) ); + } + } + } + } + unset( $meta_canon ); + } else { + if ( is_search() ) { + $canonical = get_search_link(); + } elseif ( is_front_page() ) { + $canonical = home_url(); + } elseif ( $this->is_posts_page() ) { + $canonical = get_permalink( get_option( 'page_for_posts' ) ); + } elseif ( is_tax() || is_tag() || is_category() ) { + $term = get_queried_object(); + + if ( $no_override === false ) { + $canonical = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'canonical' ); + if ( is_string( $canonical ) && $canonical !== '' ) { + $skip_pagination = true; + } + } + + if ( ! is_string( $canonical ) || $canonical === '' ) { + $canonical = get_term_link( $term, $term->taxonomy ); + } + } elseif ( is_post_type_archive() ) { + $post_type = get_query_var( 'post_type' ); + if ( is_array( $post_type ) ) { + $post_type = reset( $post_type ); + } + $canonical = get_post_type_archive_link( $post_type ); + } elseif ( is_author() ) { + $canonical = get_author_posts_url( get_query_var( 'author' ), get_query_var( 'author_name' ) ); + } elseif ( is_archive() ) { + if ( is_date() ) { + if ( is_day() ) { + $canonical = get_day_link( get_query_var( 'year' ), get_query_var( 'monthnum' ), get_query_var( 'day' ) ); + } elseif ( is_month() ) { + $canonical = get_month_link( get_query_var( 'year' ), get_query_var( 'monthnum' ) ); + } elseif ( is_year() ) { + $canonical = get_year_link( get_query_var( 'year' ) ); + } + } + } + } + + if ( $canonical && $un_paged ) { + return $canonical; + } + + if ( $canonical && ! $skip_pagination && get_query_var( 'paged' ) > 1 ) { + global $wp_rewrite; + if ( ! $wp_rewrite->using_permalinks() ) { + $canonical = add_query_arg( 'paged', get_query_var( 'paged' ), $canonical ); + } else { + if ( is_front_page() ) { + $canonical = wpseo_xml_sitemaps_base_url( '' ); + } + $canonical = user_trailingslashit( trailingslashit( $canonical ) . trailingslashit( $wp_rewrite->pagination_base ) . get_query_var( 'paged' ) ); + } + } + + if ( $canonical && 'default' !== $this->options['force_transport'] ) { + $canonical = preg_replace( '`^http[s]?`', $this->options['force_transport'], $canonical ); + } + + /** + * Filter: 'wpseo_canonical' - Allow filtering of the canonical URL put out by WP SEO + * + * @api string $canonical The canonical URL + */ + $canonical = apply_filters( 'wpseo_canonical', $canonical ); + + if ( is_string( $canonical ) && $canonical !== '' ) { + // Force canonical links to be absolute, relative is NOT an option. + if ( wpseo_is_url_relative( $canonical ) === true ) { + $canonical = home_url( $canonical ); + } + + if ( $echo !== false ) { + echo '<link rel="canonical" href="' . esc_url( $canonical, null, 'other' ) . '" />' . "\n"; + } else { + return $canonical; + } + } else { + return false; + } + } + + /** + * Adds 'prev' and 'next' links to archives. + * + * @link http://googlewebmastercentral.blogspot.com/2011/09/pagination-with-relnext-and-relprev.html + * @since 1.0.3 + */ + public function adjacent_rel_links() { + // Don't do this for Genesis, as the way Genesis handles homepage functionality is different and causes issues sometimes. + /** + * Filter 'wpseo_genesis_force_adjacent_rel_home' - Allows devs to allow echoing rel="next" / rel="prev" by WP SEO on Genesis installs + * + * @api bool $unsigned Whether or not to rel=next / rel=prev + */ + if ( is_home() && function_exists( 'genesis' ) && apply_filters( 'wpseo_genesis_force_adjacent_rel_home', false ) === false ) { + return; + } + + global $wp_query; + + if ( ! is_singular() ) { + $url = $this->canonical( false, true, true ); + + if ( is_string( $url ) && $url !== '' ) { + $paged = get_query_var( 'paged' ); + + if ( 0 == $paged ) { + $paged = 1; + } + + if ( $paged == 2 ) { + $this->adjacent_rel_link( 'prev', $url, $paged - 1, true ); + } + + // Make sure to use index.php when needed, done after paged == 2 check so the prev links to homepage will not have index.php erroneously. + if ( is_front_page() ) { + $url = wpseo_xml_sitemaps_base_url( '' ); + } + + if ( $paged > 2 ) { + $this->adjacent_rel_link( 'prev', $url, $paged - 1, true ); + } + + if ( $paged < $wp_query->max_num_pages ) { + $this->adjacent_rel_link( 'next', $url, $paged + 1, true ); + } + } + } else { + $numpages = 0; + if ( isset( $wp_query->post->post_content ) ) { + $numpages = substr_count( $wp_query->post->post_content, '<!--nextpage-->' ) + 1; + } + if ( $numpages > 1 ) { + $page = get_query_var( 'page' ); + if ( ! $page ) { + $page = 1; + } + + $url = get_permalink( $wp_query->post->ID ); + + // If the current page is the frontpage, pagination should use /base/ + if ( $this->is_home_static_page() ) { + $usebase = true; + } + else { + $usebase = false; + } + + if ( $page > 1 ) { + $this->adjacent_rel_link( 'prev', $url, $page - 1, $usebase, 'single_paged' ); + } + if ( $page < $numpages ) { + $this->adjacent_rel_link( 'next', $url, $page + 1, $usebase, 'single_paged' ); + } + } + } + } + + /** + * Get adjacent pages link for archives + * + * @param string $rel Link relationship, prev or next. + * @param string $url the unpaginated URL of the current archive. + * @param string $page the page number to add on to $url for the $link tag. + * @param boolean $incl_pagination_base whether or not to include /page/ or not. + * + * @return string $link link element + * + * @since 1.0.2 + */ + private function adjacent_rel_link( $rel, $url, $page, $incl_pagination_base ) { + global $wp_rewrite; + if ( ! $wp_rewrite->using_permalinks() ) { + if ( $page > 1 ) { + $url = add_query_arg( 'paged', $page, $url ); + } + } else { + if ( $page > 1 ) { + $base = ''; + if ( $incl_pagination_base ) { + $base = trailingslashit( $wp_rewrite->pagination_base ); + } + $url = user_trailingslashit( trailingslashit( $url ) . $base . $page ); + } + } + /** + * Filter: 'wpseo_' . $rel . '_rel_link' - Allow changing link rel output by WP SEO + * + * @api string $unsigned The full `<link` element. + */ + $link = apply_filters( 'wpseo_' . $rel . '_rel_link', '<link rel="' . $rel . '" href="' . esc_url( $url ) . "\" />\n" ); + + if ( is_string( $link ) && $link !== '' ) { + echo $link; + } + } + + /** + * Output the rel=publisher code on every page of the site. + * @return boolean Boolean indicating whether the publisher link was printed + */ + public function publisher() { + + if ( $this->options['plus-publisher'] !== '' ) { + echo '<link rel="publisher" href="' . esc_url( $this->options['plus-publisher'] ) . '"/>' . "\n"; + + return true; + } + + return false; + } + + /** + * Outputs the meta keywords element. + * + * @return string + */ + public function metakeywords() { + global $wp_query, $post; + + if ( $this->options['usemetakeywords'] === false ) { + return; + } + + $keywords = ''; + + if ( is_singular() ) { + $keywords = WPSEO_Meta::get_value( 'metakeywords' ); + if ( $keywords === '' && ( is_object( $post ) && ( ( isset( $this->options[ 'metakey-' . $post->post_type ] ) && $this->options[ 'metakey-' . $post->post_type ] !== '' ) ) ) ) { + $keywords = wpseo_replace_vars( $this->options[ 'metakey-' . $post->post_type ], $post ); + } + } else { + if ( $this->is_home_posts_page() && $this->options['metakey-home-wpseo'] !== '' ) { + $keywords = wpseo_replace_vars( $this->options['metakey-home-wpseo'], array() ); + } elseif ( $this->is_home_static_page() ) { + $keywords = WPSEO_Meta::get_value( 'metakeywords' ); + if ( $keywords === '' && ( is_object( $post ) && ( isset( $this->options[ 'metakey-' . $post->post_type ] ) && $this->options[ 'metakey-' . $post->post_type ] !== '' ) ) ) { + $keywords = wpseo_replace_vars( $this->options[ 'metakey-' . $post->post_type ], $post ); + } + } elseif ( is_category() || is_tag() || is_tax() ) { + $term = $wp_query->get_queried_object(); + + if ( is_object( $term ) ) { + $keywords = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'metakey' ); + if ( ( ! is_string( $keywords ) || $keywords === '' ) && ( isset( $this->options[ 'metakey-tax-' . $term->taxonomy ] ) && $this->options[ 'metakey-tax-' . $term->taxonomy ] !== '' ) ) { + $keywords = wpseo_replace_vars( $this->options[ 'metakey-tax-' . $term->taxonomy ], $term ); + } + } + } elseif ( is_author() ) { + $author_id = get_query_var( 'author' ); + $keywords = get_the_author_meta( 'metakey', $author_id ); + if ( ! $keywords && $this->options['metakey-author-wpseo'] !== '' ) { + $keywords = wpseo_replace_vars( $this->options['metakey-author-wpseo'], $wp_query->get_queried_object() ); + } + } elseif ( is_post_type_archive() ) { + $post_type = get_query_var( 'post_type' ); + if ( is_array( $post_type ) ) { + $post_type = reset( $post_type ); + } + if ( isset( $this->options[ 'metakey-ptarchive-' . $post_type ] ) && $this->options[ 'metakey-ptarchive-' . $post_type ] !== '' ) { + $keywords = wpseo_replace_vars( $this->options[ 'metakey-ptarchive-' . $post_type ], $wp_query->get_queried_object() ); + } + } + } + + $keywords = apply_filters( 'wpseo_metakey', trim( $keywords ) ); // make deprecated + + /** + * Filter: 'wpseo_metakeywords' - Allow changing the WP SEO meta keywords + * + * @api string $keywords The meta keywords to be echoed. + */ + $keywords = apply_filters( 'wpseo_metakeywords', trim( $keywords ) ); // more appropriately named + + if ( is_string( $keywords ) && $keywords !== '' ) { + echo '<meta name="keywords" content="' . esc_attr( strip_tags( stripslashes( $keywords ) ) ) . '"/>' . "\n"; + } + } + + /** + * Outputs the meta description element or returns the description text. + * + * @param bool $echo Whether or not to echo the description. + * + * @return string + */ + public function metadesc( $echo = true ) { + global $post, $wp_query; + + $metadesc = ''; + $post_type = ''; + $template = ''; + + if ( is_object( $post ) && ( isset( $post->post_type ) && $post->post_type !== '' ) ) { + $post_type = $post->post_type; + } + + if ( is_singular() ) { + $metadesc = WPSEO_Meta::get_value( 'metadesc' ); + if ( ( $metadesc === '' && $post_type !== '' ) && isset( $this->options[ 'metadesc-' . $post_type ] ) ) { + $template = $this->options[ 'metadesc-' . $post_type ]; + $term = $post; + } + } else { + if ( is_search() ) { + return ''; + } elseif ( $this->is_home_posts_page() ) { + $template = $this->options['metadesc-home-wpseo']; + $term = array(); + } elseif ( $this->is_posts_page() ) { + $metadesc = WPSEO_Meta::get_value( 'metadesc', get_option( 'page_for_posts' ) ); + if ( ( $metadesc === '' && $post_type !== '' ) && isset( $this->options[ 'metadesc-' . $post_type ] ) ) { + $page = get_post( get_option( 'page_for_posts' ) ); + $template = $this->options[ 'metadesc-' . $post_type ]; + $term = $page; + } + } elseif ( $this->is_home_static_page() ) { + $metadesc = WPSEO_Meta::get_value( 'metadesc' ); + if ( ( $metadesc === '' && $post_type !== '' ) && isset( $this->options[ 'metadesc-' . $post_type ] ) ) { + $template = $this->options[ 'metadesc-' . $post_type ]; + } + } elseif ( is_category() || is_tag() || is_tax() ) { + $term = $wp_query->get_queried_object(); + $metadesc = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'desc' ); + if ( ( ! is_string( $metadesc ) || $metadesc === '' ) && ( ( is_object( $term ) && isset( $term->taxonomy ) ) && isset( $this->options[ 'metadesc-tax-' . $term->taxonomy ] ) ) ) { + $template = $this->options[ 'metadesc-tax-' . $term->taxonomy ]; + } + } elseif ( is_author() ) { + $author_id = get_query_var( 'author' ); + $metadesc = get_the_author_meta( 'wpseo_metadesc', $author_id ); + if ( ( ! is_string( $metadesc ) || $metadesc === '' ) && '' !== $this->options[ 'metadesc-author-wpseo' ] ) { + $template = $this->options[ 'metadesc-author-wpseo' ]; + } + } elseif ( is_post_type_archive() ) { + $post_type = get_query_var( 'post_type' ); + if ( is_array( $post_type ) ) { + $post_type = reset( $post_type ); + } + if ( isset( $this->options[ 'metadesc-ptarchive-' . $post_type ] ) ) { + $template = $this->options[ 'metadesc-ptarchive-' . $post_type ]; + } + } elseif ( is_archive() ) { + $template = $this->options['metadesc-archive-wpseo']; + } + + // If we're on a paginated page, and the template doesn't change for paginated pages, bail. + if ( ( ! is_string( $metadesc ) || $metadesc === '' ) && get_query_var( 'paged' ) && get_query_var( 'paged' ) > 1 && $template !== '' ) { + if ( strpos( $template, '%%page' ) === false ) { + return ''; + } + } + } + + if ( ( ! is_string( $metadesc ) || '' === $metadesc ) && '' !== $template ) { + if ( ! isset( $term ) ) { + $term = $wp_query->get_queried_object(); + } + $metadesc = wpseo_replace_vars( $template, $term ); + } + + /** + * Filter: 'wpseo_metadesc' - Allow changing the WP SEO meta description sentence. + * + * @api string $metadesc The description sentence. + */ + $metadesc = apply_filters( 'wpseo_metadesc', trim( $metadesc ) ); + + if ( $echo !== false ) { + if ( is_string( $metadesc ) && $metadesc !== '' ) { + echo '<meta name="description" content="' . esc_attr( strip_tags( stripslashes( $metadesc ) ) ) . '"/>' . "\n"; + } elseif ( current_user_can( 'manage_options' ) && is_singular() ) { + echo '<!-- ' . __( 'Admin only notice: this page doesn\'t show a meta description because it doesn\'t have one, either write it for this page specifically or go into the SEO -> Titles menu and set up a template.', 'wordpress-seo' ) . ' -->' . "\n"; + } + } else { + return $metadesc; + } + } + + /** + * Based on the redirect meta value, this function determines whether it should redirect the current post / page. + * + * @return boolean + */ + function page_redirect() { + if ( is_singular() ) { + global $post; + if ( ! isset( $post ) || ! is_object( $post ) ) { + return false; + } + + $redir = WPSEO_Meta::get_value( 'redirect', $post->ID ); + if ( $redir !== '' ) { + wp_redirect( $redir, 301 ); + exit; + } + } + + return false; + } + + /** + * Outputs noindex values for the current page. + */ + public function noindex_page() { + echo '<meta name="robots" content="noindex" />' . "\n"; + } + + /** + * Send a Robots HTTP header preventing URL from being indexed in the search results while allowing search engines + * to follow the links in the object at the URL. + * + * @since 1.1.7 + * @return boolean Boolean indicating whether the noindex header was sent + */ + public function noindex_feed() { + + if ( ( is_feed() || is_robots() ) && headers_sent() === false ) { + header( 'X-Robots-Tag: noindex,follow', true ); + return true; + } + + return false; + } + + /** + * Adds rel="nofollow" to a link, only used for login / registration links. + * + * @param string $input The link element as a string. + * + * @return string + */ + public function nofollow_link( $input ) { + return str_replace( '<a ', '<a rel="nofollow" ', $input ); + } + + /** + * When certain archives are disabled, this redirects those to the homepage. + * @return boolean False when no redirect was triggered + */ + function archive_redirect() { + global $wp_query; + + if ( + ( $this->options['disable-date'] === true && $wp_query->is_date ) || + ( $this->options['disable-author'] === true && $wp_query->is_author ) || + ( isset( $this->options['disable-post_formats'] ) && $this->options['disable-post_formats'] && $wp_query->is_tax( 'post_format' ) ) + ) { + wp_safe_redirect( get_bloginfo( 'url' ), 301 ); + exit; + } + + return false; + } + + /** + * If the option to redirect attachments to their parent is checked, this performs the redirect. + * + * An extra check is done for when the attachment has no parent. + * @return boolean False when no redirect was triggered + */ + function attachment_redirect() { + global $post; + if ( is_attachment() && ( ( is_object( $post ) && isset( $post->post_parent ) ) && ( is_numeric( $post->post_parent ) && $post->post_parent != 0 ) ) ) { + wp_safe_redirect( get_permalink( $post->post_parent ), 301 ); + exit; + } + + return false; + } + + /** + * Trailing slashes for everything except is_single(). + * + * Thanks to Mark Jaquith for this code. + * + * @param string $url + * @param string $type + * + * @return string + */ + function add_trailingslash( $url, $type ) { + if ( 'single' === $type || 'single_paged' === $type ) { + return $url; + } else { + return trailingslashit( $url ); + } + } + + /** + * Removes the ?replytocom variable from the link, replacing it with a #comment-<number> anchor. + * + * @todo Should this function also allow for relative urls ? + * + * @param string $link The comment link as a string. + * + * @return string + */ + public function remove_reply_to_com( $link ) { + return preg_replace( '`href=(["\'])(?:.*(?:\?|&|&)replytocom=(\d+)#respond)`', 'href=$1#comment-$2', $link ); + } + + /** + * Redirect out the ?replytocom variables when cleanreplytocom is enabled + * + * @since 1.4.13 + * @return boolean + */ + function replytocom_redirect() { + + if ( $this->options['cleanreplytocom'] !== true ) { + return false; + } + + if ( isset( $_GET['replytocom'] ) && is_singular() ) { + global $post; + $url = get_permalink( $post->ID ); + $hash = sanitize_text_field( $_GET['replytocom'] ); + $query_string = remove_query_arg( 'replytocom', sanitize_text_field( $_SERVER['QUERY_STRING'] ) ); + if ( ! empty( $query_string ) ) { + $url .= '?' . $query_string; + } + $url .= '#comment-' . $hash; + wp_safe_redirect( $url, 301 ); + exit; + } + + return false; + } + + /** + * Removes unneeded query variables from the URL. + * @return boolean + */ + public function clean_permalink() { + if ( is_robots() || get_query_var( 'sitemap' ) ) { + return false; + } + + global $wp_query; + + // Recreate current URL + $cururl = 'http'; + if ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' ) { + $cururl .= 's'; + } + $cururl .= '://'; + + if ( $_SERVER['SERVER_PORT'] != '80' && $_SERVER['SERVER_PORT'] != '443' ) { + $cururl .= $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI']; + } else { + $cururl .= $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; + } + $properurl = ''; + + if ( is_singular() ) { + global $post; + if ( empty( $post ) ) { + $post = $wp_query->get_queried_object(); + } + + $properurl = get_permalink( $post->ID ); + + $page = get_query_var( 'page' ); + if ( $page && $page != 1 ) { + $post = get_post( $post->ID ); + $page_count = substr_count( $post->post_content, '<!--nextpage-->' ); + if ( $page > ( $page_count + 1 ) ) { + $properurl = user_trailingslashit( trailingslashit( $properurl ) . ( $page_count + 1 ) ); + } else { + $properurl = user_trailingslashit( trailingslashit( $properurl ) . $page ); + } + } + + // Fix reply to comment links, whoever decided this should be a GET variable? + $result = preg_match( '`(\?replytocom=[^&]+)`', sanitize_text_field( $_SERVER['REQUEST_URI'] ), $matches ); + if ( $result ) { + $properurl .= str_replace( '?replytocom=', '#comment-', $matches[0] ); + } + + // Prevent cleaning out posts & page previews for people capable of viewing them + if ( isset( $_GET['preview'] ) && isset( $_GET['preview_nonce'] ) && current_user_can( 'edit_post' ) ) { + $properurl = ''; + } + } elseif ( is_front_page() ) { + if ( $this->is_home_posts_page() ) { + $properurl = get_bloginfo( 'url' ) . '/'; + } elseif ( $this->is_home_static_page() ) { + global $post; + $properurl = get_permalink( $post->ID ); + } + } elseif ( is_category() || is_tag() || is_tax() ) { + $term = $wp_query->get_queried_object(); + if ( is_feed() ) { + $properurl = get_term_feed_link( $term->term_id, $term->taxonomy ); + } else { + $properurl = get_term_link( $term, $term->taxonomy ); + } + } elseif ( is_search() ) { + $s = urlencode( preg_replace( '`(%20|\+)`', ' ', get_search_query() ) ); + $properurl = get_bloginfo( 'url' ) . '/?s=' . $s; + } elseif ( is_404() ) { + if ( is_multisite() && ! is_subdomain_install() && is_main_site() ) { + if ( $cururl == get_bloginfo( 'url' ) . '/blog/' || $cururl == get_bloginfo( 'url' ) . '/blog' ) { + if ( $this->is_home_static_page() ) { + $properurl = get_permalink( get_option( 'page_for_posts' ) ); + } else { + $properurl = get_bloginfo( 'url' ) . '/'; + } + } + } + } + + if ( ! empty( $properurl ) && $wp_query->query_vars['paged'] != 0 && $wp_query->post_count != 0 ) { + if ( is_search() && ! empty( $s ) ) { + $properurl = get_bloginfo( 'url' ) . '/page/' . $wp_query->query_vars['paged'] . '/?s=' . $s; + } else { + $properurl = user_trailingslashit( trailingslashit( $properurl ) . 'page/' . $wp_query->query_vars['paged'] ); + } + } + + // Prevent cleaning out the WP Subscription managers interface for everyone + if ( isset( $_GET['wp-subscription-manager'] ) ) { + $properurl = ''; + } + + /** + * Filter: 'wpseo_whitelist_permalink_vars' - Allow plugins to register their own variables not to clean + * + * @api array $unsigned Array of permalink variables _not_ to clean. Empty by default. + */ + $whitelisted_extravars = apply_filters( 'wpseo_whitelist_permalink_vars', array() ); + + if ( $this->options['cleanpermalink-googlesitesearch'] === true ) { + // Prevent cleaning out Google Site searches + $whitelisted_extravars = array_merge( $whitelisted_extravars, array( 'q', 'cx', 'debug', 'cof', 'ie', 'sa' ) ); + } + + if ( $this->options['cleanpermalink-googlecampaign'] === true ) { + // Prevent cleaning out Google Analytics campaign variables + $whitelisted_extravars = array_merge( $whitelisted_extravars, array( 'utm_campaign', 'utm_medium', 'utm_source', 'utm_content', 'utm_term', 'utm_id', 'gclid' ) ); + } + + if ( $this->options['cleanpermalink-extravars'] !== '' ) { + $extravars = explode( ',', $this->options['cleanpermalink-extravars'] ); + $extravars = array_map( 'trim', $extravars ); + $whitelisted_extravars = array_merge( $whitelisted_extravars, $extravars ); + unset( $extravars ); + } + + foreach ( $whitelisted_extravars as $get ) { + if ( isset( $_GET[ trim( $get ) ] ) ) { + $properurl = ''; + } + } + + if ( ! empty( $properurl ) && $cururl != $properurl ) { + wp_safe_redirect( $properurl, 301 ); + exit; + } + } + + /** + * Replaces the possible RSS variables with their actual values. + * + * @param string $content The RSS content that should have the variables replaced. + * + * @return string + */ + function rss_replace_vars( $content ) { + global $post; + + /** + * Allow the developer to determine whether or not to follow the links in the bits WP SEO adds to the RSS feed, defaults to true. + * + * @api bool $unsigned Whether or not to follow the links in RSS feed, defaults to true. + * + * @since 1.4.20 + */ + $no_follow = apply_filters( 'nofollow_rss_links', true ); + $no_follow_attr = ''; + if ( $no_follow === true ) { + $no_follow_attr = 'rel="nofollow" '; + } + + $author_link = ''; + if ( is_object( $post ) ) { + $author_link = '<a ' . $no_follow_attr . 'href="' . esc_url( get_author_posts_url( $post->post_author ) ) . '">' . get_the_author() . '</a>'; + } + + $post_link = '<a ' . $no_follow_attr . 'href="' . esc_url( get_permalink() ) . '">' . get_the_title() . '</a>'; + $blog_link = '<a ' . $no_follow_attr . 'href="' . esc_url( get_bloginfo( 'url' ) ) . '">' . get_bloginfo( 'name' ) . '</a>'; + $blog_desc_link = '<a ' . $no_follow_attr . 'href="' . esc_url( get_bloginfo( 'url' ) ) . '">' . get_bloginfo( 'name' ) . ' - ' . strip_tags( get_bloginfo( 'description' ) ) . '</a>'; + + $content = stripslashes( trim( $content ) ); + $content = str_replace( '%%AUTHORLINK%%', $author_link, $content ); + $content = str_replace( '%%POSTLINK%%', $post_link, $content ); + $content = str_replace( '%%BLOGLINK%%', $blog_link, $content ); + $content = str_replace( '%%BLOGDESCLINK%%', $blog_desc_link, $content ); + + return $content; + } + + /** + * Adds the RSS footer (or header) to the full RSS feed item. + * + * @param string $content Feed item content. + * + * @return string + */ + function embed_rssfooter( $content ) { + return $this->embed_rss( $content, 'full' ); + } + + /** + * Adds the RSS footer (or header) to the excerpt RSS feed item. + * + * @param string $content Feed item excerpt. + * + * @return string + */ + function embed_rssfooter_excerpt( $content ) { + return $this->embed_rss( $content, 'excerpt' ); + } + + /** + * Adds the RSS footer and/or header to an RSS feed item. + * + * @since 1.4.14 + * + * @param string $content Feed item content. + * @param string $context Feed item context, either 'excerpt' or 'full'. + * + * @return string + */ + function embed_rss( $content, $context = 'full' ) { + if ( is_feed() ) { + + $before = ''; + $after = ''; + + if ( $this->options['rssbefore'] !== '' ) { + $before = wpautop( $this->rss_replace_vars( $this->options['rssbefore'] ) ); + } + if ( $this->options['rssafter'] !== '' ) { + $after = wpautop( $this->rss_replace_vars( $this->options['rssafter'] ) ); + } + if ( $before !== '' || $after !== '' ) { + if ( ( isset( $context ) && $context === 'excerpt' ) && trim( $content ) !== '' ) { + $content = wpautop( $content ); + } + $content = $before . $content . $after; + } + } + + return $content; + } + + + /** + * Used in the force rewrite functionality this retrieves the output, replaces the title with the proper SEO + * title and then flushes the output. + */ + function flush_cache() { + + global $wp_query; + + if ( $this->ob_started !== true ) { + return false; + } + + $content = ob_get_contents(); + ob_end_clean(); + + $old_wp_query = $wp_query; + + wp_reset_query(); + + $title = $this->title( '' ); + + // Find all titles, strip them out and add the new one in within the debug marker, so it's easily identified whether a site uses force rewrite. + $content = preg_replace( '/<title.*?\/title>/i', '', $content ); + $content = str_replace( $this->debug_marker( false ), $this->debug_marker( false ) . "\n" . '<title>' . $title . '', $content ); + + $GLOBALS['wp_query'] = $old_wp_query; + + echo $content; + return true; + } + + /** + * Starts the output buffer so it can later be fixed by flush_cache() + */ + function force_rewrite_output_buffer() { + $this->ob_started = true; + ob_start(); + } + + /** + * Function used in testing whether the title should be force rewritten or not. + * + * @param string $title + * + * @return string + */ + function title_test_helper( $title ) { + $wpseo_titles = get_option( 'wpseo_titles' ); + + $wpseo_titles['title_test']++; + update_option( 'wpseo_titles', $wpseo_titles ); + + // Prevent this setting from being on forever when something breaks, as it breaks caching. + if ( $wpseo_titles['title_test'] > 5 ) { + $wpseo_titles['title_test'] = 0; + update_option( 'wpseo_titles', $wpseo_titles ); + + remove_filter( 'wpseo_title', array( $this, 'title_test_helper' ) ); + return $title; + } + + if ( ! defined( 'DONOTCACHEPAGE' ) ) { + define( 'DONOTCACHEPAGE', true ); + } + if ( ! defined( 'DONOTCACHCEOBJECT' ) ) { + define( 'DONOTCACHCEOBJECT', true ); + } + if ( ! defined( 'DONOTMINIFY' ) ) { + define( 'DONOTMINIFY', true ); + } + + global $wp_version; + if ( $_SERVER['HTTP_USER_AGENT'] == "WordPress/${wp_version}; " . get_bloginfo( 'url' ) . ' - Yoast' ) { + return 'This is a Yoast Test Title'; + } + + return $title; + } + + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/frontend/class-googleplus.php b/wp-content/plugins/wordpress-seo/frontend/class-googleplus.php new file mode 100644 index 0000000..9c9a2d3 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/frontend/class-googleplus.php @@ -0,0 +1,118 @@ +' . "\n"; + } + } + } + + /** + * Output the Google+ specific title + */ + public function google_plus_title() { + if ( is_singular() ) { + $title = WPSEO_Meta::get_value( 'google-plus-title' ); + + /** + * Filter: 'wpseo_googleplus_title' - Allow developers to change the Google+ specific title + * + * @api string $title The title string + */ + $title = trim( apply_filters( 'wpseo_googleplus_title', $title ) ); + + if ( is_string( $title ) && $title !== '' ) { + $title = wpseo_replace_vars( $title, get_post() ); + + echo '' . "\n"; + } + } + } + + /** + * Output the Google+ specific image + */ + public function google_plus_image() { + if ( is_singular() ) { + $image = WPSEO_Meta::get_value( 'google-plus-image' ); + + /** + * Filter: 'wpseo_googleplus_image' - Allow changing the Google+ image + * + * @api string $img Image URL string + */ + $image = trim( apply_filters( 'wpseo_googleplus_image', $image ) ); + + if ( is_string( $image ) && $image !== '' ) { + echo '' . "\n"; + } + } + } + } +} // end if class exists \ No newline at end of file diff --git a/wp-content/plugins/wordpress-seo/frontend/class-opengraph.php b/wp-content/plugins/wordpress-seo/frontend/class-opengraph.php new file mode 100644 index 0000000..8e837e3 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/frontend/class-opengraph.php @@ -0,0 +1,647 @@ +options = WPSEO_Options::get_all(); + + global $fb_ver; + if ( isset( $fb_ver ) || class_exists( 'Facebook_Loader' ) ) { + add_filter( 'fb_meta_tags', array( $this, 'facebook_filter' ), 10, 1 ); + } else { + add_filter( 'language_attributes', array( $this, 'add_opengraph_namespace' ) ); + + add_action( 'wpseo_opengraph', array( $this, 'locale' ), 1 ); + add_action( 'wpseo_opengraph', array( $this, 'type' ), 5 ); + add_action( 'wpseo_opengraph', array( $this, 'og_title' ), 10 ); + add_action( 'wpseo_opengraph', array( $this, 'site_owner' ), 20 ); + add_action( 'wpseo_opengraph', array( $this, 'description' ), 11 ); + add_action( 'wpseo_opengraph', array( $this, 'url' ), 12 ); + add_action( 'wpseo_opengraph', array( $this, 'site_name' ), 13 ); + add_action( 'wpseo_opengraph', array( $this, 'website_facebook' ), 14 ); + if ( is_singular() && ! is_front_page() ) { + add_action( 'wpseo_opengraph', array( $this, 'article_author_facebook' ), 15 ); + add_action( 'wpseo_opengraph', array( $this, 'tags' ), 16 ); + add_action( 'wpseo_opengraph', array( $this, 'category' ), 17 ); + add_action( 'wpseo_opengraph', array( $this, 'publish_date' ), 19 ); + } + + add_action( 'wpseo_opengraph', array( $this, 'image' ), 30 ); + } + add_filter( 'jetpack_enable_open_graph', '__return_false' ); + add_action( 'wpseo_head', array( $this, 'opengraph' ), 30 ); + } + + /** + * Main OpenGraph output. + */ + public function opengraph() { + wp_reset_query(); + /** + * Action: 'wpseo_opengraph' - Hook to add all Facebook OpenGraph output to so they're close together. + */ + do_action( 'wpseo_opengraph' ); + } + + /** + * Internal function to output FB tags. This also adds an output filter to each bit of output based on the property. + * + * @param string $property + * @param string $content + * @return boolean + */ + public function og_tag( $property, $content ) { + $og_property = str_replace( ':', '_', $property ); + /** + * Filter: 'wpseo_og_' . $og_property - Allow developers to change the content of specific OG meta tags. + * + * @api string $content The content of the property + */ + $content = apply_filters( 'wpseo_og_' . $og_property, $content ); + if ( empty( $content ) ) { + return false; + } + + echo '' . "\n"; + return true; + } + + /** + * Filter the Facebook plugins metadata + * + * @param array $meta_tags the array to fix. + * + * @return array $meta_tags + */ + public function facebook_filter( $meta_tags ) { + $meta_tags['http://ogp.me/ns#type'] = $this->type( false ); + $meta_tags['http://ogp.me/ns#title'] = $this->og_title( false ); + + // Filter the locale too because the Facebook plugin locale code is not as good as ours. + $meta_tags['http://ogp.me/ns#locale'] = $this->locale( false ); + + $ogdesc = $this->description( false ); + if ( ! empty( $ogdesc ) ) { + $meta_tags['http://ogp.me/ns#description'] = $ogdesc; + } + + return $meta_tags; + } + + /** + * Filter for the namespace, adding the OpenGraph namespace. + * + * @link https://developers.facebook.com/docs/web/tutorials/scrumptious/open-graph-object/ + * + * @param string $input The input namespace string. + * + * @return string + */ + public function add_opengraph_namespace( $input ) { + return $input . ' prefix="og: http://ogp.me/ns#' . ( ( $this->options['fbadminapp'] != 0 || ( is_array( $this->options['fb_admins'] ) && $this->options['fb_admins'] !== array() ) ) ? ' fb: http://ogp.me/ns/fb#' : '' ) . '"'; + } + + /** + * Outputs the authors FB page. + * + * @link https://developers.facebook.com/blog/post/2013/06/19/platform-updates--new-open-graph-tags-for-media-publishers-and-more/ + * @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/ + * + * @return boolean + */ + public function article_author_facebook() { + if ( ! is_singular() ) { + return false; + } + + global $post; + /** + * Filter: 'wpseo_opengraph_author_facebook' - Allow developers to filter the WP SEO post authors facebook profile URL + * + * @api bool|string $unsigned The Facebook author URL, return false to disable + */ + $facebook = apply_filters( 'wpseo_opengraph_author_facebook', get_the_author_meta( 'facebook', $post->post_author ) ); + + if ( $facebook && ( is_string( $facebook ) && $facebook !== '' ) ) { + $this->og_tag( 'article:author', $facebook ); + return true; + } + + return false; + } + + /** + * Outputs the websites FB page. + * + * @link https://developers.facebook.com/blog/post/2013/06/19/platform-updates--new-open-graph-tags-for-media-publishers-and-more/ + * @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/ + * @return boolean + */ + public function website_facebook() { + + if ( $this->options['facebook_site'] !== '' ) { + $this->og_tag( 'article:publisher', $this->options['facebook_site'] ); + return true; + } + + return false; + } + + /** + * Outputs the site owner + * + * @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/ + * @return boolean + */ + public function site_owner() { + if ( 0 != $this->options['fbadminapp'] ) { + $this->og_tag( 'fb:app_id', $this->options['fbadminapp'] ); + return true; + } elseif ( is_array( $this->options['fb_admins'] ) && $this->options['fb_admins'] !== array() ) { + $adminstr = implode( ',', array_keys( $this->options['fb_admins'] ) ); + /** + * Filter: 'wpseo_opengraph_admin' - Allow developer to filter the fb:admins string put out by WP SEO + * + * @api string $adminstr The admin string + */ + $adminstr = apply_filters( 'wpseo_opengraph_admin', $adminstr ); + if ( is_string( $adminstr ) && $adminstr !== '' ) { + $this->og_tag( 'fb:admins', $adminstr ); + return true; + } + } + + return false; + } + + /** + * Outputs the SEO title as OpenGraph title. + * + * @param bool $echo Whether or not to echo the output. + * + * @return string $title + * + * @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/ + * @return boolean + */ + public function og_title( $echo = true ) { + if ( is_singular() ) { + $title = WPSEO_Meta::get_value( 'opengraph-title' ); + if ( $title === '' ) { + $title = $this->title( '' ); + } else { + // Replace WP SEO Variables + $title = wpseo_replace_vars( $title, get_post() ); + } + } else if ( is_front_page() ) { + $title = ( $this->options['og_frontpage_title'] !== '' ) ? $this->options['og_frontpage_title'] : $this->title( '' ); + } else { + $title = $this->title( '' ); + } + + /** + * Filter: 'wpseo_opengraph_title' - Allow changing the title specifically for OpenGraph + * + * @api string $unsigned The title string + */ + $title = trim( apply_filters( 'wpseo_opengraph_title', $title ) ); + + if ( is_string( $title ) && $title !== '' ) { + if ( $echo !== false ) { + $this->og_tag( 'og:title', $title ); + return true; + } + } + + if ( $echo === false ) { + return $title; + } + + return false; + } + + /** + * Outputs the canonical URL as OpenGraph URL, which consolidates likes and shares. + * + * @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/ + * @return boolean + */ + public function url() { + /** + * Filter: 'wpseo_opengraph_url' - Allow changing the OpenGraph URL + * + * @api string $unsigned Canonical URL + */ + $url = apply_filters( 'wpseo_opengraph_url', $this->canonical( false ) ); + + if ( is_string( $url ) && $url !== '' ) { + $this->og_tag( 'og:url', esc_url( $url ) ); + return true; + } + + return false; + } + + /** + * Output the locale, doing some conversions to make sure the proper Facebook locale is outputted. + * + * Last update/compare with FB list done on July 14, 2013 by JRF + * Results: 1 new locale added, found 32 in the below list which are not in the FB list (not removed), 76 OK. + * @see http://www.facebook.com/translations/FacebookLocales.xml for the list of supported locales + * + * @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/ + * + * @param bool $echo Whether to echo or return the locale + * + * @return string $locale + */ + public function locale( $echo = true ) { + /** + * Filter: 'wpseo_locale' - Allow changing the locale output + * + * @api string $unsigned Locale string + */ + $locale = apply_filters( 'wpseo_locale', get_locale() ); + + // catch some weird locales served out by WP that are not easily doubled up. + $fix_locales = array( + 'ca' => 'ca_ES', + 'en' => 'en_US', + 'el' => 'el_GR', + 'et' => 'et_EE', + 'ja' => 'ja_JP', + 'sq' => 'sq_AL', + 'uk' => 'uk_UA', + 'vi' => 'vi_VN', + 'zh' => 'zh_CN', + ); + + if ( isset( $fix_locales[ $locale ] ) ) { + $locale = $fix_locales[ $locale ]; + } + + // convert locales like "es" to "es_ES", in case that works for the given locale (sometimes it does) + if ( strlen( $locale ) == 2 ) { + $locale = strtolower( $locale ) . '_' . strtoupper( $locale ); + } + + // These are the locales FB supports + $fb_valid_fb_locales = array( + 'ca_ES', 'cs_CZ', 'cy_GB', 'da_DK', 'de_DE', 'eu_ES', 'en_PI', 'en_UD', 'ck_US', 'en_US', 'es_LA', 'es_CL', 'es_CO', 'es_ES', 'es_MX', + 'es_VE', 'fb_FI', 'fi_FI', 'fr_FR', 'gl_ES', 'hu_HU', 'it_IT', 'ja_JP', 'ko_KR', 'nb_NO', 'nn_NO', 'nl_NL', 'pl_PL', 'pt_BR', 'pt_PT', + 'ro_RO', 'ru_RU', 'sk_SK', 'sl_SI', 'sv_SE', 'th_TH', 'tr_TR', 'ku_TR', 'zh_CN', 'zh_HK', 'zh_TW', 'fb_LT', 'af_ZA', 'sq_AL', 'hy_AM', + 'az_AZ', 'be_BY', 'bn_IN', 'bs_BA', 'bg_BG', 'hr_HR', 'nl_BE', 'en_GB', 'eo_EO', 'et_EE', 'fo_FO', 'fr_CA', 'ka_GE', 'el_GR', 'gu_IN', + 'hi_IN', 'is_IS', 'id_ID', 'ga_IE', 'jv_ID', 'kn_IN', 'kk_KZ', 'la_VA', 'lv_LV', 'li_NL', 'lt_LT', 'mk_MK', 'mg_MG', 'ms_MY', 'mt_MT', + 'mr_IN', 'mn_MN', 'ne_NP', 'pa_IN', 'rm_CH', 'sa_IN', 'sr_RS', 'so_SO', 'sw_KE', 'tl_PH', 'ta_IN', 'tt_RU', 'te_IN', 'ml_IN', 'uk_UA', + 'uz_UZ', 'vi_VN', 'xh_ZA', 'zu_ZA', 'km_KH', 'tg_TJ', 'ar_AR', 'he_IL', 'ur_PK', 'fa_IR', 'sy_SY', 'yi_DE', 'gn_PY', 'qu_PE', 'ay_BO', + 'se_NO', 'ps_AF', 'tl_ST', 'fy_NL', + ); + + // check to see if the locale is a valid FB one, if not, use en_US as a fallback + // check to see if the locale is a valid FB one, if not, use en_US as a fallback + if ( ! in_array( $locale, $fb_valid_fb_locales ) ) { + $locale = strtolower( substr( $locale, 0, 2 ) ) . '_' . strtoupper( substr( $locale, 0, 2 ) ); + if ( ! in_array( $locale, $fb_valid_fb_locales ) ) { + $locale = 'en_US'; + } + } + + if ( $echo !== false ) { + $this->og_tag( 'og:locale', $locale ); + } + + return $locale; + } + + /** + * Output the OpenGraph type. + * + * @param boolean $echo Whether to echo or return the type + * + * @link https://developers.facebook.com/docs/reference/opengraph/object-type/object/ + * + * @return string $type + */ + public function type( $echo = true ) { + + if ( is_front_page() || is_home() ) { + $type = 'website'; + } elseif ( is_singular() ) { + + // This'll usually only be changed by plugins right now. + $type = WPSEO_Meta::get_value( 'og_type' ); + + if ( $type === '' ) { + $type = 'article'; + } + } else { + // We use "object" for archives etc. as article doesn't apply there + $type = 'object'; + } + + /** + * Filter: 'wpseo_opengraph_type' - Allow changing the OpenGraph type of the page + * + * @api string $type The OpenGraph type string. + */ + $type = apply_filters( 'wpseo_opengraph_type', $type ); + + if ( is_string( $type ) && $type !== '' ) { + if ( $echo !== false ) { + $this->og_tag( 'og:type', $type ); + } else { + return $type; + } + } + + return ''; + } + + /** + * Display an OpenGraph image tag + * + * @param string $img Source URL to the image + * + * @return bool + */ + function image_output( $img ) { + /** + * Filter: 'wpseo_opengraph_image' - Allow changing the OpenGraph image + * + * @api string $img Image URL string + */ + $img = trim( apply_filters( 'wpseo_opengraph_image', $img ) ); + + if ( empty( $img ) ) { + return false; + } + + if ( wpseo_is_url_relative( $img ) === true ) { + if ( $img[0] != '/' ) { + return false; + } + + // If it's a relative URL, it's relative to the domain, not necessarily to the WordPress install, we + // want to preserve domain name and URL scheme (http / https) though. + $parsed_url = parse_url( home_url() ); + $img = $parsed_url['scheme'] . '://' . $parsed_url['host'] . $img; + } + + if ( in_array( $img, $this->shown_images ) ) { + return false; + } + + array_push( $this->shown_images, $img ); + + $this->og_tag( 'og:image', esc_url( $img ) ); + + return true; + } + + /** + * Output the OpenGraph image elements for all the images within the current post/page. + * + * @return bool + */ + public function image() { + + global $post; + + if ( is_front_page() ) { + if ( $this->options['og_frontpage_image'] !== '' ) { + $this->image_output( $this->options['og_frontpage_image'] ); + } + } + + if ( is_singular() ) { + $ogimg = WPSEO_Meta::get_value( 'opengraph-image' ); + if ( $ogimg !== '' ) { + $this->image_output( $ogimg ); + + return; + } + + if ( function_exists( 'has_post_thumbnail' ) && has_post_thumbnail( $post->ID ) ) { + /** + * Filter: 'wpseo_opengraph_image_size' - Allow changing the image size used for OpenGraph sharing + * + * @api string $unsigned Size string + */ + $thumb = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), apply_filters( 'wpseo_opengraph_image_size', 'original' ) ); + $this->image_output( $thumb[0] ); + } + + /** + * Filter: 'wpseo_pre_analysis_post_content' - Allow filtering the content before analysis + * + * @api string $post_content The Post content string + * + * @param object $post The post object. + */ + $content = apply_filters( 'wpseo_pre_analysis_post_content', $post->post_content, $post ); + + if ( preg_match_all( '`]+>`', $content, $matches ) ) { + foreach ( $matches[0] as $img ) { + if ( preg_match( '`src=(["\'])(.*?)\1`', $img, $match ) ) { + $this->image_output( $match[2] ); + } + } + } + } + + if ( count( $this->shown_images ) == 0 && $this->options['og_default_image'] !== '' ) { + $this->image_output( $this->options['og_default_image'] ); + } + + // @TODO add G+ image stuff + } + + /** + * Output the OpenGraph description, specific OG description first, if not, grab the meta description. + * + * @param bool $echo Whether to echo or return the description + * + * @return string $ogdesc + */ + public function description( $echo = true ) { + $ogdesc = ''; + + if ( is_front_page() ) { + $ogdesc = ( $this->options['og_frontpage_desc'] !== '' ) ? $this->options['og_frontpage_desc'] : $this->metadesc( false ); + } + + if ( is_singular() ) { + $ogdesc = WPSEO_Meta::get_value( 'opengraph-description' ); + + // Replace WP SEO Variables + $ogdesc = wpseo_replace_vars( $ogdesc, get_post() ); + + // Use metadesc if $ogdesc is empty + if ( $ogdesc === '' ) { + $ogdesc = $this->metadesc( false ); + } + + // og:description is still blank so grab it from get_the_excerpt() + if ( ! is_string( $ogdesc ) || ( is_string( $ogdesc ) && $ogdesc === '' ) ) { + $ogdesc = str_replace( '[…]', '…', strip_tags( get_the_excerpt() ) ); + } + } + + if ( is_category() || is_tag() || is_tax() ) { + $ogdesc = trim( strip_tags( term_description() ) ); + if ( '' == $ogdesc ) { + global $wp_query; + $term = $wp_query->get_queried_object(); + $ogdesc = WPSEO_Taxonomy_Meta::get_term_meta( $term, $term->taxonomy, 'desc' ); + } + } + + // Strip shortcodes if any + $ogdesc = strip_shortcodes( $ogdesc ); + + /** + * Filter: 'wpseo_opengraph_desc' - Allow changing the OpenGraph description + * + * @api string $ogdesc The description string. + */ + $ogdesc = trim( apply_filters( 'wpseo_opengraph_desc', $ogdesc ) ); + + if ( is_string( $ogdesc ) && $ogdesc !== '' ) { + if ( $echo !== false ) { + $this->og_tag( 'og:description', $ogdesc ); + } + } + + return $ogdesc; + } + + /** + * Output the site name straight from the blog info. + */ + public function site_name() { + /** + * Filter: 'wpseo_opengraph_site_name' - Allow changing the OpenGraph site name + * + * @api string $unsigned Blog name string + */ + $name = apply_filters( 'wpseo_opengraph_site_name', get_bloginfo( 'name' ) ); + if ( is_string( $name ) && $name !== '' ) { + $this->og_tag( 'og:site_name', $name ); + } + } + + /** + * Output the article tags as article:tag tags. + * + * @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/ + * @return boolean + */ + public function tags() { + if ( ! is_singular() ) { + return false; + } + + $tags = get_the_tags(); + if ( ! is_wp_error( $tags ) && ( is_array( $tags ) && $tags !== array() ) ) { + + foreach ( $tags as $tag ) { + $this->og_tag( 'article:tag', $tag->name ); + } + + return true; + } + + return false; + } + + /** + * Output the article category as an article:section tag. + * + * @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/ + * @return boolean; + */ + public function category() { + + if ( ! is_singular() ) { + return false; + } + + $terms = get_the_category(); + + if ( ! is_wp_error( $terms ) && ( is_array( $terms ) && $terms !== array() ) ) { + + foreach ( $terms as $term ) { + $this->og_tag( 'article:section', $term->name ); + } + + return true; + } + + return false; + } + + /** + * Output the article publish and last modification date + * + * @link https://developers.facebook.com/docs/reference/opengraph/object-type/article/ + * @return boolean; + */ + public function publish_date() { + + if ( ! is_singular( 'post' ) ) { + /** + * Filter: 'wpseo_opengraph_show_publish_date' - Allow showing publication date for other post types + * + * @api bool $unsigned Whether or not to show publish date + * + * @param string $post_type The current URL's post type. + */ + if ( false === apply_filters( 'wpseo_opengraph_show_publish_date', false, get_post_type() ) ) { + return false; + } + } + + $pub = get_the_date( 'c' ); + $this->og_tag( 'article:published_time', $pub ); + + $mod = get_the_modified_date( 'c' ); + if ( $mod != $pub ) { + $this->og_tag( 'article:modified_time', $mod ); + $this->og_tag( 'og:updated_time', $mod ); + } + + return true; + } + + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/frontend/class-twitter.php b/wp-content/plugins/wordpress-seo/frontend/class-twitter.php new file mode 100644 index 0000000..77e5f44 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/frontend/class-twitter.php @@ -0,0 +1,349 @@ +options = WPSEO_Options::get_all(); + $this->shown_images = array(); // Instantiate as empty array + $this->twitter(); + } + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + return self::$instance; + } + + /** + * Output the metatag + * + * @param $name + * @param $value + * @param $escaped + */ + private function output_metatag( $name, $value, $escaped = false ) { + + // Escape the value if not escaped + if ( false === $escaped ) { + $value = esc_attr( $value ); + } + + /** + * Filter: 'wpseo_twitter_metatag_key' - Make the Twitter metatag key filterable + * + * @api string $key The Twitter metatag key + */ + $metatag_key = apply_filters( 'wpseo_twitter_metatag_key', 'name' ); + + // Output meta + echo '' . "\n"; + } + + /** + * Outputs the Twitter Card code on singular pages. + * + * @return void Only shows on singular pages, false on non-singular pages. + */ + public function twitter() { + wp_reset_query(); + + $this->type(); + $this->site_twitter(); + $this->site_domain(); + $this->author_twitter(); + if ( 'summary_large_image' === $this->options['twitter_card_type'] ) { + $this->image(); + } + + // No need to show these when OpenGraph is also showing, as it'd be the same contents and Twitter + // would fallback to OpenGraph anyway. + if ( $this->options['opengraph'] === false ) { + if ( 'summary' === $this->options['twitter_card_type'] ) { + $this->image(); + } + $this->twitter_description(); + $this->twitter_title(); + $this->twitter_url(); + } + + /** + * Action: 'wpseo_twitter' - Hook to add all WP SEO Twitter output to so they're close together. + */ + do_action( 'wpseo_twitter' ); + } + + /** + * Display the Twitter card type. + * + * This defaults to summary but can be filtered using the wpseo_twitter_card_type filter. + * + * @link https://dev.twitter.com/docs/cards + */ + public function type() { + /** + * Filter: 'wpseo_twitter_card_type' - Allow changing the Twitter Card type as output in the Twitter card by WP SEO + * + * @api string $unsigned The type string + */ + $type = apply_filters( 'wpseo_twitter_card_type', $this->options['twitter_card_type'] ); + if ( ! in_array( $type, array( 'summary', 'summary_large_image', 'photo', 'gallery', 'app', 'player', 'product' ) ) ) { + $type = 'summary'; + } + + $this->output_metatag( 'card', $type ); + } + + /** + * Displays the Twitter account for the site. + */ + public function site_twitter() { + /** + * Filter: 'wpseo_twitter_site' - Allow changing the Twitter site account as output in the Twitter card by WP SEO + * + * @api string $unsigned Twitter site account string + */ + $site = apply_filters( 'wpseo_twitter_site', $this->options['twitter_site'] ); + $site = $this->get_twitter_id( $site ); + + if ( is_string( $site ) && $site !== '' ) { + $this->output_metatag( 'site', '@' . $site ); + } + } + + /** + * Displays the domain tag for the site. + */ + public function site_domain() { + /** + * Filter: 'wpseo_twitter_domain' - Allow changing the Twitter domain as output in the Twitter card by WP SEO + * + * @api string $unsigned Name string + */ + $domain = apply_filters( 'wpseo_twitter_domain', get_bloginfo( 'name' ) ); + if ( is_string( $domain ) && $domain !== '' ) { + $this->output_metatag( 'domain', $domain ); + } + } + + /** + * Displays the authors Twitter account. + */ + public function author_twitter() { + $twitter = ltrim( trim( get_the_author_meta( 'twitter' ) ), '@' ); + /** + * Filter: 'wpseo_twitter_creator_account' - Allow changing the Twitter account as output in the Twitter card by WP SEO + * + * @api string $twitter The twitter account name string + */ + $twitter = apply_filters( 'wpseo_twitter_creator_account', $twitter ); + $twitter = $this->get_twitter_id( $twitter ); + + if ( is_string( $twitter ) && $twitter !== '' ) { + $this->output_metatag( 'creator', '@' . $twitter ); + } + elseif ( $this->options['twitter_site'] !== '' ) { + if ( is_string( $this->options['twitter_site'] ) && $this->options['twitter_site'] !== '' ) { + $this->output_metatag( 'creator', '@' . $this->options['twitter_site'] ); + } + } + } + + /** + * Displays the title for Twitter. + * + * Only used when OpenGraph is inactive. + */ + public function twitter_title() { + /** + * Filter: 'wpseo_twitter_title' - Allow changing the Twitter title as output in the Twitter card by WP SEO + * + * @api string $twitter The title string + */ + $title = apply_filters( 'wpseo_twitter_title', $this->title( '' ) ); + if ( is_string( $title ) && $title !== '' ) { + $this->output_metatag( 'title', $title ); + } + } + + /** + * Displays the description for Twitter. + * + * Only used when OpenGraph is inactive. + */ + public function twitter_description() { + $meta_desc = trim( $this->metadesc( false ) ); + if ( ! is_string( $meta_desc ) || '' === $meta_desc ) { + $meta_desc = false; + } + + if ( ! $meta_desc ) { + $meta_desc = strip_tags( get_the_excerpt() ); + } + + /** + * Filter: 'wpseo_twitter_description' - Allow changing the Twitter description as output in the Twitter card by WP SEO + * + * @api string $twitter The description string + */ + $meta_desc = apply_filters( 'wpseo_twitter_description', $meta_desc ); + if ( is_string( $meta_desc ) && $meta_desc !== '' ) { + $this->output_metatag( 'description', $meta_desc ); + } + } + + /** + * Displays the URL for Twitter. + * + * Only used when OpenGraph is inactive. + */ + public function twitter_url() { + /** + * Filter: 'wpseo_twitter_url' - Allow changing the URL as output in the Twitter card by WP SEO + * + * @api string $unsigned Canonical URL + */ + $url = apply_filters( 'wpseo_twitter_url', $this->canonical( false ) ); + if ( is_string( $url ) && $url !== '' ) { + $this->output_metatag( 'url', esc_url( $url ), true ); + } + } + + /** + * Outputs a Twitter image tag for a given image + * + * @param string $img + * @return bool + */ + public function image_output( $img ) { + + /** + * Filter: 'wpseo_twitter_image' - Allow changing the Twitter Card image + * + * @api string $img Image URL string + */ + $img = apply_filters( 'wpseo_twitter_image', $img ); + + $escaped_img = esc_url( $img ); + + if ( in_array( $escaped_img, $this->shown_images ) ) { + return false; + } + + if ( is_string( $escaped_img ) && $escaped_img !== '' ) { + $this->output_metatag( 'image:src', $escaped_img, true ); + + array_push( $this->shown_images, $escaped_img ); + return true; + } + + return false; + } + + /** + * Displays the image for Twitter + * + * Only used when OpenGraph is inactive or Summary Large Image card is chosen. + */ + public function image() { + global $post; + + if ( is_singular() ) { + if ( is_front_page() ) { + if ( $this->options['og_frontpage_image'] !== '' ) { + $this->image_output( $this->options['og_frontpage_image'] ); + } + } + + $twitter_img = WPSEO_Meta::get_value( 'twitter-image' ); + if ( $twitter_img !== '' ) { + $this->image_output( $twitter_img ); + return; + } + elseif ( function_exists( 'has_post_thumbnail' ) && has_post_thumbnail( $post->ID ) ) { + /** + * Filter: 'wpseo_twitter_image_size' - Allow changing the Twitter Card image size + * + * @api string $featured_img Image size string + */ + $featured_img = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), apply_filters( 'wpseo_twitter_image_size', 'full' ) ); + + if ( $featured_img ) { + $this->image_output( $featured_img[0] ); + } + } elseif ( preg_match_all( '`]+>`', $post->post_content, $matches ) ) { + foreach ( $matches[0] as $img ) { + if ( preg_match( '`src=(["\'])(.*?)\1`', $img, $match ) ) { + $this->image_output( $match[2] ); + } + } + } + } + + if ( count( $this->shown_images ) == 0 && $this->options['og_default_image'] !== '' ) { + $this->image_output( $this->options['og_default_image'] ); + } + } + + + /** + * Checks if the given id is actually an id or a url and if url, distills the id from it. + * + * Solves issues with filters returning urls and theme's/other plugins also adding a user meta + * twitter field which expects url rather than an id (which is what we expect). + * + * @param string $id Twitter id or url + * + * @return string|bool Twitter id or false if it failed to get a valid twitter id + */ + private function get_twitter_id( $id ) { + if ( preg_match( '`([A-Za-z0-9_]{1,25})$`', $id, $match ) ) { + return $match[1]; + } + else { + return false; + } + } + + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/frontend/index.php b/wp-content/plugins/wordpress-seo/frontend/index.php new file mode 100644 index 0000000..12c197f --- /dev/null +++ b/wp-content/plugins/wordpress-seo/frontend/index.php @@ -0,0 +1,2 @@ + false ) ); + if ( is_array( $categories ) && $categories !== array() ) { + foreach ( $categories as $category ) { + $category_nicename = $category->slug; + if ( $category->parent == $category->cat_ID ) { + // recursive recursion + $category->parent = 0; + } elseif ( $taxonomy->rewrite['hierarchical'] != 0 && $category->parent != 0 ) { + $parents = get_category_parents( $category->parent, false, '/', true ); + if ( ! is_wp_error( $parents ) ) { + $category_nicename = $parents . $category_nicename; + } + unset( $parents ); + } + + $category_rewrite[ $blog_prefix . '(' . $category_nicename . ')/(?:feed/)?(feed|rdf|rss|rss2|atom)/?$' ] = 'index.php?category_name=$matches[1]&feed=$matches[2]'; + $category_rewrite[ $blog_prefix . '(' . $category_nicename . ')/' . $wp_rewrite->pagination_base . '/?([0-9]{1,})/?$' ] = 'index.php?category_name=$matches[1]&paged=$matches[2]'; + $category_rewrite[ $blog_prefix . '(' . $category_nicename . ')/?$' ] = 'index.php?category_name=$matches[1]'; + } + } + + // Redirect support from Old Category Base + $old_base = $wp_rewrite->get_category_permastruct(); + $old_base = str_replace( '%category%', '(.+)', $old_base ); + $old_base = trim( $old_base, '/' ); + $category_rewrite[ $old_base . '$' ] = 'index.php?wpseo_category_redirect=$matches[1]'; + + return $category_rewrite; + } + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/inc/class-sitemaps.php b/wp-content/plugins/wordpress-seo/inc/class-sitemaps.php new file mode 100644 index 0000000..ae1895c --- /dev/null +++ b/wp-content/plugins/wordpress-seo/inc/class-sitemaps.php @@ -0,0 +1,1432 @@ + whomever] If at all possible, move the adding of rewrite rules, actions and filters + * elsewhere and only load this file when an actual sitemap is being requested. + */ + class WPSEO_Sitemaps { + /** + * Content of the sitemap to output. + */ + private $sitemap = ''; + + /** + * XSL stylesheet for styling a sitemap for web browsers + */ + private $stylesheet = ''; + + /** + * Flag to indicate if this is an invalid or empty sitemap. + */ + public $bad_sitemap = false; + + /** + * Whether or not the XML sitemap was served from a transient or not. + */ + private $transient = false; + + /** + * The maximum number of entries per sitemap page + */ + private $max_entries; + + /** + * Holds the post type's newest publish dates + */ + private $post_type_dates; + + /** + * Holds the WP SEO options + */ + private $options = array(); + + /** + * Holds the n variable + */ + private $n = 1; + + /** + * Holds the home_url() value to speed up loops + * @var string $home_url + */ + private $home_url = ''; + + /** + * Holds the get_bloginfo( 'charset' ) value to reuse for performance + * + * @var string $charset + */ + private $charset = ''; + + /** + * Holds the timezone string value to reuse for performance + * + * @var string $timezone_string + */ + private $timezone_string = ''; + + /** + * Class constructor + */ + function __construct() { + if ( ! defined( 'ENT_XML1' ) ) { + define( 'ENT_XML1', 16 ); + } + + add_action( 'after_setup_theme', array( $this, 'reduce_query_load' ), 99 ); + add_filter( 'posts_where', array( $this, 'invalidate_main_query' ) ); + + add_action( 'pre_get_posts', array( $this, 'redirect' ), 1 ); + add_filter( 'redirect_canonical', array( $this, 'canonical' ) ); + add_action( 'wpseo_hit_sitemap_index', array( $this, 'hit_sitemap_index' ) ); + + // default stylesheet + $this->stylesheet = ''; + + $this->options = WPSEO_Options::get_all(); + $this->max_entries = $this->options['entries-per-page']; + $this->home_url = home_url(); + $this->charset = get_bloginfo( 'charset' ); + + } + + /** + * Check the current request URI, if we can determine it's probably an XML sitemap, kill loading the widgets + */ + public function reduce_query_load() { + if ( isset( $_SERVER['REQUEST_URI'] ) && ( in_array( substr( $_SERVER['REQUEST_URI'], - 4 ), array( + '.xml', + '.xsl', + ) ) ) + ) { + remove_all_actions( 'widgets_init' ); + } + } + + /** + * This query invalidates the main query on purpose so it returns nice and quickly + * + * @param string $where + * + * @return string + */ + function invalidate_main_query( $where ) { + + global $wp_query; + + // check if $wp_query is properly set which isn't always the case in older WP development versions + if ( ! is_object( $wp_query ) ) { + return $where; + } + + if ( is_main_query() && ( get_query_var( 'sitemap' ) != '' || get_query_var( 'xsl' ) != '' ) ) { + $where = ' AND 0=1 ' . $where; + } + + return $where; + } + + + /** + * Returns the server HTTP protocol to use for output, if it's set. + * + * @return string + */ + private function http_protocol() { + return ( isset( $_SERVER['SERVER_PROTOCOL'] ) && $_SERVER['SERVER_PROTOCOL'] !== '' ) ? sanitize_text_field( $_SERVER['SERVER_PROTOCOL'] ) : 'HTTP/1.1'; + } + + /** + * Returns the timezone string for a site, even if it's set to a UTC offset + * + * Adapted from http://www.php.net/manual/en/function.timezone-name-from-abbr.php#89155 + * + * @return string valid PHP timezone string + */ + private function determine_timezone_string() { + + // if site timezone string exists, return it + if ( $timezone = get_option( 'timezone_string' ) ) { + return $timezone; + } + + // get UTC offset, if it isn't set then return UTC + if ( 0 === ( $utc_offset = get_option( 'gmt_offset', 0 ) ) ) { + return 'UTC'; + } + + // adjust UTC offset from hours to seconds + $utc_offset *= HOUR_IN_SECONDS; + + // attempt to guess the timezone string from the UTC offset + $timezone = timezone_name_from_abbr( '', $utc_offset ); + + // last try, guess timezone string manually + if ( false === $timezone ) { + + $is_dst = date( 'I' ); + + foreach ( timezone_abbreviations_list() as $abbr ) { + foreach ( $abbr as $city ) { + if ( $city['dst'] == $is_dst && $city['offset'] == $utc_offset ) { + return $city['timezone_id']; + } + } + } + } + + // fallback to UTC + return 'UTC'; + } + + /** + * Returns the correct timezone string + * + * @return string + */ + private function get_timezone_string() { + if ( '' == $this->timezone_string ) { + $this->timezone_string = $this->determine_timezone_string(); + } + + return $this->timezone_string; + } + + /** + * Register your own sitemap. Call this during 'init'. + * + * @param string $name The name of the sitemap + * @param callback $function Function to build your sitemap + * @param string $rewrite Optional. Regular expression to match your sitemap with + */ + function register_sitemap( $name, $function, $rewrite = '' ) { + add_action( 'wpseo_do_sitemap_' . $name, $function ); + if ( ! empty( $rewrite ) ) { + add_rewrite_rule( $rewrite, 'index.php?sitemap=' . $name, 'top' ); + } + } + + /** + * Register your own XSL file. Call this during 'init'. + * + * @param string $name The name of the XSL file + * @param callback $function Function to build your XSL file + * @param string $rewrite Optional. Regular expression to match your sitemap with + */ + function register_xsl( $name, $function, $rewrite = '' ) { + add_action( 'wpseo_xsl_' . $name, $function ); + if ( ! empty( $rewrite ) ) { + add_rewrite_rule( $rewrite, 'index.php?xsl=' . $name, 'top' ); + } + } + + /** + * Set the sitemap content to display after you have generated it. + * + * @param string $sitemap The generated sitemap to output + */ + function set_sitemap( $sitemap ) { + $this->sitemap = $sitemap; + } + + /** + * Set a custom stylesheet for this sitemap. Set to empty to just remove + * the default stylesheet. + * + * @param string $stylesheet Full xml-stylesheet declaration + */ + public function set_stylesheet( $stylesheet ) { + $this->stylesheet = $stylesheet; + } + + /** + * Set as true to make the request 404. Used stop the display of empty sitemaps or + * invalid requests. + * + * @param bool $bool Is this a bad request. True or false. + */ + function set_bad_sitemap( $bool ) { + $this->bad_sitemap = (bool) $bool; + } + + /** + * Prevent stupid plugins from running shutdown scripts when we're obviously not outputting HTML. + * + * @since 1.4.16 + */ + function sitemap_close() { + remove_all_actions( 'wp_footer' ); + die(); + } + + /** + * Hijack requests for potential sitemaps and XSL files. + */ + function redirect( $query ) { + + if ( ! $query->is_main_query() ) { + return; + } + + $xsl = get_query_var( 'xsl' ); + if ( ! empty( $xsl ) ) { + $this->xsl_output( $xsl ); + $this->sitemap_close(); + } + + $type = get_query_var( 'sitemap' ); + if ( empty( $type ) ) { + return; + } + + $n = get_query_var( 'sitemap_n' ); + if ( is_scalar( $n ) && intval( $n ) > 0 ) { + $this->n = intval( $n ); + } + + /** + * Filter: 'wpseo_enable_xml_sitemap_transient_caching' - Allow disabling the transient cache + * + * @api bool $unsigned Enable cache or not, defaults to true + */ + $caching = apply_filters( 'wpseo_enable_xml_sitemap_transient_caching', true ); + + if ( $caching ) { + $this->sitemap = get_transient( 'wpseo_sitemap_cache_' . $type . '_' . $this->n ); + } + + if ( ! $this->sitemap || '' == $this->sitemap ) { + $this->build_sitemap( $type ); + + // 404 for invalid or emtpy sitemaps + if ( $this->bad_sitemap ) { + global $wp_query; + $wp_query->set_404(); + status_header( 404 ); + + return; + } + + if ( $caching ) { + set_transient( 'wpseo_sitemap_cache_' . $type . '_' . $n, $this->sitemap, DAY_IN_SECONDS ); + } + } else { + $this->transient = true; + } + + $this->output(); + $this->sitemap_close(); + } + + /** + * Attempt to build the requested sitemap. Sets $bad_sitemap if this isn't + * for the root sitemap, a post type or taxonomy. + * + * @param string $type The requested sitemap's identifier. + */ + function build_sitemap( $type ) { + + $type = apply_filters( 'wpseo_build_sitemap_post_type', $type ); + + if ( $type == 1 ) { + $this->build_root_map(); + } elseif ( post_type_exists( $type ) ) { + $this->build_post_type_map( $type ); + } elseif ( $tax = get_taxonomy( $type ) ) { + $this->build_tax_map( $tax ); + } elseif ( $type == 'author' ) { + $this->build_user_map(); + } elseif ( has_action( 'wpseo_do_sitemap_' . $type ) ) { + do_action( 'wpseo_do_sitemap_' . $type ); + } else { + $this->bad_sitemap = true; + } + } + + /** + * Build the root sitemap -- example.com/sitemap_index.xml -- which lists sub-sitemaps + * for other content types. + */ + function build_root_map() { + + global $wpdb; + + $this->sitemap = '' . "\n"; + // reference post type specific sitemaps + $post_types = get_post_types( array( 'public' => true ) ); + if ( is_array( $post_types ) && $post_types !== array() ) { + + foreach ( $post_types as $post_type ) { + if ( isset( $this->options[ 'post_types-' . $post_type . '-not_in_sitemap' ] ) && $this->options[ 'post_types-' . $post_type . '-not_in_sitemap' ] === true ) { + unset( $post_types[ $post_type ] ); + } else { + if ( apply_filters( 'wpseo_sitemap_exclude_post_type', false, $post_type ) ) { + unset( $post_types[ $post_type ] ); + } + } + } + + // No prepare here because $wpdb->prepare can't properly prepare IN statements. + $query = "SELECT post_type, COUNT(ID) AS count FROM $wpdb->posts WHERE post_status IN ('publish','inherit') AND post_type IN ( '" . implode( "','", $post_types ) . "' ) GROUP BY post_type "; + $result = $wpdb->get_results( $query ); + + $post_type_counts = array(); + foreach ( $result as $obj ) { + $post_type_counts[ $obj->post_type ] = $obj->count; + } + unset( $result ); + + foreach ( $post_types as $post_type ) { + + $count = false; + if ( isset( $post_type_counts[ $post_type ] ) ) { + $count = $post_type_counts[ $post_type ]; + } else { + continue; + } + + $n = ( $count > $this->max_entries ) ? (int) ceil( $count / $this->max_entries ) : 1; + for ( $i = 0; $i < $n; $i ++ ) { + $count = ( $n > 1 ) ? $i + 1 : ''; + + if ( empty( $count ) || $count == $n ) { + $date = $this->get_last_modified( $post_type ); + } else { + if ( ! isset( $all_dates ) ) { + $all_dates = $wpdb->get_col( $wpdb->prepare( "SELECT post_modified_gmt FROM (SELECT @rownum:=@rownum+1 rownum, $wpdb->posts.post_modified_gmt FROM (SELECT @rownum:=0) r, $wpdb->posts WHERE post_status IN ('publish','inherit') AND post_type = %s ORDER BY post_modified_gmt ASC) x WHERE rownum %%%d=0", $post_type, $this->max_entries ) ); + } + $datetime = new DateTime( $all_dates[ $i ], new DateTimeZone( $this->get_timezone_string() ) ); + $date = $datetime->format( 'c' ); + } + + $this->sitemap .= '' . "\n"; + $this->sitemap .= '' . wpseo_xml_sitemaps_base_url( $post_type . '-sitemap' . $count . '.xml' ) . '' . "\n"; + $this->sitemap .= '' . htmlspecialchars( $date ) . '' . "\n"; + $this->sitemap .= '' . "\n"; + } + unset( $all_dates ); + } + } + unset( $post_types, $query, $count, $n, $i, $date ); + + // reference taxonomy specific sitemaps + $taxonomies = get_taxonomies( array( 'public' => true ), 'objects' ); + $taxonomy_names = array_keys( $taxonomies ); + + if ( is_array( $taxonomies ) && $taxonomies !== array() ) { + foreach ( $taxonomy_names as $tax ) { + if ( in_array( $tax, array( 'link_category', 'nav_menu', 'post_format' ) ) ) { + unset( $taxonomy_names[ $tax ], $taxonomies[ $tax ] ); + continue; + } + + if ( apply_filters( 'wpseo_sitemap_exclude_taxonomy', false, $tax ) ) { + unset( $taxonomy_names[ $tax ], $taxonomies[ $tax ] ); + continue; + } + + if ( isset( $this->options[ 'taxonomies-' . $tax . '-not_in_sitemap' ] ) && $this->options[ 'taxonomies-' . $tax . '-not_in_sitemap' ] === true ) { + unset( $taxonomy_names[ $tax ], $taxonomies[ $tax ] ); + continue; + } + } + + // Retrieve all the taxonomies and their terms so we can do a proper count on them. + $hide_empty = ( apply_filters( 'wpseo_sitemap_exclude_empty_terms', true, $tax ) ) ? 'count != 0 AND' : ''; + $query = "SELECT taxonomy, term_id FROM $wpdb->term_taxonomy WHERE $hide_empty taxonomy IN ('" . implode( "','", $taxonomy_names ) . "');"; + $all_taxonomy_terms = $wpdb->get_results( $query ); + $all_taxonomies = array(); + foreach ( $all_taxonomy_terms as $obj ) { + $all_taxonomies[ $obj->taxonomy ][] = $obj->term_id; + } + unset( $all_taxonomy_terms ); + + foreach ( $taxonomies as $tax_name => $tax ) { + + $steps = $this->max_entries; + $count = ( isset ( $all_taxonomies[ $tax_name ] ) ) ? count( $all_taxonomies[ $tax_name ] ) : 1; + $n = ( $count > $this->max_entries ) ? (int) ceil( $count / $this->max_entries ) : 1; + + for ( $i = 0; $i < $n; $i ++ ) { + $count = ( $n > 1 ) ? $i + 1 : ''; + + if ( ! is_array( $tax->object_type ) || count( $tax->object_type ) == 0 ) { + continue; + } + + if ( ( empty( $count ) || $count == $n ) ) { + $date = $this->get_last_modified( $tax->object_type ); + } else { + $terms = array_splice( $all_taxonomies[ $tax_name ], 0, $steps ); + if ( ! $terms ) { + continue; + } + + $args = array( + 'post_type' => $tax->object_type, + 'tax_query' => array( + array( + 'taxonomy' => $tax_name, + 'field' => 'slug', + 'terms' => $terms, + ), + ), + 'orderby' => 'modified', + 'order' => 'DESC', + ); + $query = new WP_Query( $args ); + + $date = ''; + if ( $query->have_posts() ) { + $datetime = new DateTime( $query->posts[0]->post_modified_gmt, new DateTimeZone( $this->get_timezone_string() ) ); + $date = $datetime->format( 'c' ); + } else { + $date = $this->get_last_modified( $tax->object_type ); + } + } + + $this->sitemap .= '' . "\n"; + $this->sitemap .= '' . wpseo_xml_sitemaps_base_url( $tax_name . '-sitemap' . $count . '.xml' ) . '' . "\n"; + $this->sitemap .= '' . htmlspecialchars( $date ) . '' . "\n"; + $this->sitemap .= '' . "\n"; + } + } + } + unset( $taxonomies, $tax, $all_terms, $steps, $count, $n, $i, $tax_object, $date, $terms, $args, $query ); + + if ( $this->options['disable-author'] === false && $this->options['disable_author_sitemap'] === false ) { + + // reference user profile specific sitemaps + $users = get_users( array( 'who' => 'authors', 'fields' => 'id' ) ); + + $count = count( $users ); + $n = ( $count > $this->max_entries ) ? (int) ceil( $count / $this->max_entries ) : 1; + + for ( $i = 0; $i < $n; $i ++ ) { + $count = ( $n > 1 ) ? $i + 1 : ''; + + // must use custom raw query because WP User Query does not support ordering by usermeta + // Retrieve the newest updated profile timestamp overall + if ( empty( $count ) || $count == $n ) { + $date = $wpdb->get_var( + $wpdb->prepare( + " + SELECT mt1.meta_value FROM $wpdb->users + INNER JOIN $wpdb->usermeta ON ($wpdb->users.ID = $wpdb->usermeta.user_id) + INNER JOIN $wpdb->usermeta AS mt1 ON ($wpdb->users.ID = mt1.user_id) WHERE 1=1 + AND ( ($wpdb->usermeta.meta_key = %s AND CAST($wpdb->usermeta.meta_value AS CHAR) != '0') + AND mt1.meta_key = '_yoast_wpseo_profile_updated' ) ORDER BY mt1.meta_value DESC LIMIT 1 + ", + $wpdb->get_blog_prefix() . 'user_level' + ) + ); + $date = new DateTime( date( 'y-m-d H:i:s', $date ), new DateTimeZone( $this->get_timezone_string() ) ); + + // Retrieve the newest updated profile timestamp by an offset + } else { + $date = $wpdb->get_var( + $wpdb->prepare( + " + SELECT mt1.meta_value FROM $wpdb->users + INNER JOIN $wpdb->usermeta ON ($wpdb->users.ID = $wpdb->usermeta.user_id) + INNER JOIN $wpdb->usermeta AS mt1 ON ($wpdb->users.ID = mt1.user_id) WHERE 1=1 + AND ( ($wpdb->usermeta.meta_key = %s AND CAST($wpdb->usermeta.meta_value AS CHAR) != '0') + AND mt1.meta_key = '_yoast_wpseo_profile_updated' ) ORDER BY mt1.meta_value ASC LIMIT 1 OFFSET %d + ", + $wpdb->get_blog_prefix() . 'user_level', + $this->max_entries * ( $i + 1 ) - 1 + ) + ); + $date = new DateTime( date( 'y-m-d H:i:s', $date ), new DateTimeZone( $this->get_timezone_string() ) ); + } + + $this->sitemap .= '' . "\n"; + $this->sitemap .= '' . wpseo_xml_sitemaps_base_url( 'author-sitemap' . $count . '.xml' ) . '' . "\n"; + $this->sitemap .= '' . htmlspecialchars( $date->format( 'c' ) ) . '' . "\n"; + $this->sitemap .= '' . "\n"; + } + unset( $users, $count, $n, $i, $date ); + } + + // allow other plugins to add their sitemaps to the index + $this->sitemap .= apply_filters( 'wpseo_sitemap_index', '' ); + $this->sitemap .= ''; + } + + /** + * Function to dynamically filter the change frequency + * + * @param string $filter Expands to wpseo_sitemap_$filter_change_freq, allowing for a change of the frequency for numerous specific URLs + * @param string $default The default value for the frequency + * @param string $url The URL of the currenty entry + * + * @return mixed|void + */ + private function filter_frequency( $filter, $default, $url ) { + /** + * Filter: 'wpseo_sitemap_' . $filter . '_change_freq' - Allow filtering of the specific change frequency + * + * @api string $default The default change frequency + */ + $change_freq = apply_filters( 'wpseo_sitemap_' . $filter . '_change_freq', $default, $url ); + + if ( ! in_array( $change_freq, array( + 'always', + 'hourly', + 'daily', + 'weekly', + 'monthly', + 'yearly', + 'never', + ) ) + ) { + $change_freq = $default; + } + + return $change_freq; + } + + /** + * Build a sub-sitemap for a specific post type -- example.com/post_type-sitemap.xml + * + * @param string $post_type Registered post type's slug + */ + function build_post_type_map( $post_type ) { + global $wpdb; + + if ( + ( isset( $this->options[ 'post_types-' . $post_type . '-not_in_sitemap' ] ) && $this->options[ 'post_types-' . $post_type . '-not_in_sitemap' ] === true ) + || in_array( $post_type, array( 'revision', 'nav_menu_item' ) ) + || apply_filters( 'wpseo_sitemap_exclude_post_type', false, $post_type ) + ) { + $this->bad_sitemap = true; + + return; + } + + $output = ''; + + $steps = ( 100 > $this->max_entries ) ? $this->max_entries : 100; + $n = (int) $this->n; + $offset = ( $n > 1 ) ? ( $n - 1 ) * $this->max_entries : 0; + $total = $offset + $this->max_entries; + + $join_filter = ''; + $join_filter = apply_filters( 'wpseo_typecount_join', $join_filter, $post_type ); + $where_filter = ''; + $where_filter = apply_filters( 'wpseo_typecount_where', $where_filter, $post_type ); + + $query = $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts {$join_filter} WHERE post_status IN ('publish','inherit') AND post_password = '' AND post_author != 0 AND post_date != '0000-00-00 00:00:00' AND post_type = %s " . $where_filter, $post_type ); + + $typecount = $wpdb->get_var( $query ); + + if ( $total > $typecount ) { + $total = $typecount; + } + + if ( $n === 1 ) { + $front_id = get_option( 'page_on_front' ); + if ( ! $front_id && ( $post_type == 'post' || $post_type == 'page' ) ) { + $output .= $this->sitemap_url( + array( + 'loc' => $this->home_url, + 'pri' => 1, + 'chf' => $this->filter_frequency( 'homepage', 'daily', $this->home_url ), + ) + ); + } elseif ( $front_id && $post_type == 'post' ) { + $page_for_posts = get_option( 'page_for_posts' ); + if ( $page_for_posts ) { + $page_for_posts_url = get_permalink( $page_for_posts ); + $output .= $this->sitemap_url( + array( + 'loc' => $page_for_posts_url, + 'pri' => 1, + 'chf' => $change_freq = $this->filter_frequency( 'blogpage', 'daily', $page_for_posts_url ), + ) + ); + } + } + + $archive = get_post_type_archive_link( $post_type ); + if ( $archive ) { + /** + * Filter: 'wpseo_xml_post_type_archive_priority' - Allow changing the priority of the URL WordPress SEO uses in the XML sitemap. + * + * @api float $priority The priority for this URL, ranging from 0 to 1 + * + * @param string $post_type The post type this archive is for + */ + $output .= $this->sitemap_url( + array( + 'loc' => $archive, + 'pri' => apply_filters( 'wpseo_xml_post_type_archive_priority', 0.8, $post_type ), + 'chf' => $this->filter_frequency( $post_type . '_archive', 'weekly', $archive ), + 'mod' => $this->get_last_modified( $post_type ), + // get_lastpostmodified( 'gmt', $post_type ) #17455 + ) + ); + } + } + + if ( $typecount == 0 && empty( $archive ) ) { + $this->bad_sitemap = true; + + return; + } + + $stackedurls = array(); + + // Make sure you're wpdb->preparing everything you throw into this!! + $join_filter = apply_filters( 'wpseo_posts_join', false, $post_type ); + $where_filter = apply_filters( 'wpseo_posts_where', false, $post_type ); + + $status = ( $post_type == 'attachment' ) ? 'inherit' : 'publish'; + + $parsed_home = parse_url( $this->home_url ); + $host = ''; + $scheme = 'http'; + if ( isset( $parsed_home['host'] ) && ! empty( $parsed_home['host'] ) ) { + $host = str_replace( 'www.', '', $parsed_home['host'] ); + } + if ( isset( $parsed_home['scheme'] ) && ! empty( $parsed_home['scheme'] ) ) { + $scheme = $parsed_home['scheme']; + } + + + /** + * We grab post_date, post_name, post_author and post_status too so we can throw these objects + * into get_permalink, which saves a get_post call for each permalink. + */ + while ( $total > $offset ) { + + // Optimized query per this thread: http://wordpress.org/support/topic/plugin-wordpress-seo-by-yoast-performance-suggestion + // Also see http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ + $query = $wpdb->prepare( "SELECT l.ID, post_title, post_content, post_name, post_author, post_parent, post_modified_gmt, post_date, post_date_gmt FROM ( SELECT ID FROM $wpdb->posts {$join_filter} WHERE post_status = '%s' AND post_password = '' AND post_type = '%s' AND post_author != 0 AND post_date != '0000-00-00 00:00:00' {$where_filter} ORDER BY post_modified ASC LIMIT %d OFFSET %d ) o JOIN $wpdb->posts l ON l.ID = o.ID ORDER BY l.ID", + $status, $post_type, $steps, $offset + ); + + $posts = $wpdb->get_results( $query ); + + $post_ids = array(); + foreach ( $posts as $p ) { + $post_ids[] = $p->ID; + } + + if ( count( $post_ids ) > 0 ) { + update_meta_cache( 'post', $post_ids ); + + $child_query = "SELECT ID, post_title, post_parent FROM $wpdb->posts WHERE post_status = 'inherit' AND post_type = 'attachment' AND post_parent IN (" . implode( $post_ids, ',' ) . ')'; + $wpdb->query( $child_query ); + $attachments = $wpdb->get_results( $child_query ); + $attachment_ids = wp_list_pluck( $attachments, 'ID' ); + + $thumbnail_query = "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = '_thumbnail_id' AND post_id IN (" . implode( $post_ids, ',' ) . ')'; + $wpdb->query( $thumbnail_query ); + $thumbnails = $wpdb->get_results( $thumbnail_query ); + $thumbnail_ids = wp_list_pluck( $thumbnails, 'meta_value' ); + + $attachment_ids = array_merge( $thumbnail_ids, $attachment_ids ); + + _prime_post_caches( $attachment_ids ); + update_meta_cache( 'post', $attachment_ids ); + } + + $offset = $offset + $steps; + + if ( is_array( $posts ) && $posts !== array() ) { + foreach ( $posts as $p ) { + $p->post_type = $post_type; + $p->post_status = 'publish'; + $p->filter = 'sample'; + + if ( WPSEO_Meta::get_value( 'meta-robots-noindex', $p->ID ) === '1' && WPSEO_Meta::get_value( 'sitemap-include', $p->ID ) !== 'always' ) { + continue; + } + if ( WPSEO_Meta::get_value( 'sitemap-include', $p->ID ) === 'never' ) { + continue; + } + if ( WPSEO_Meta::get_value( 'redirect', $p->ID ) !== '' ) { + continue; + } + + $url = array(); + + $url['mod'] = ( isset( $p->post_modified_gmt ) && $p->post_modified_gmt != '0000-00-00 00:00:00' && $p->post_modified_gmt > $p->post_date_gmt ) ? $p->post_modified_gmt : ( ( '0000-00-00 00:00:00' != $p->post_date_gmt ) ? $p->post_date_gmt : $p->post_date ); + $url['loc'] = get_permalink( $p ); + + /** + * Filter: 'wpseo_xml_sitemap_post_url' - Allow changing the URL WordPress SEO uses in the XML sitemap. + * + * Note that only absolute local URLs are allowed as the check after this removes external URLs. + * + * @api string $url URL to use in the XML sitemap + * + * @param object $p Post object for the URL + */ + $url['loc'] = apply_filters( 'wpseo_xml_sitemap_post_url', $url['loc'], $p ); + + $url['chf'] = $this->filter_frequency( $post_type . '_single', 'weekly', $url['loc'] ); + + /** + * Do not include external URLs. + * @see https://wordpress.org/plugins/page-links-to/ can rewrite permalinks to external URLs. + */ + if ( false === strpos( $url['loc'], $this->home_url ) ) { + continue; + } + + $canonical = WPSEO_Meta::get_value( 'canonical', $p->ID ); + if ( $canonical !== '' && $canonical !== $url['loc'] ) { + /* Let's assume that if a canonical is set for this page and it's different from + the URL of this post, that page is either already in the XML sitemap OR is on + an external site, either way, we shouldn't include it here. */ + continue; + } else { + if ( $this->options['trailingslash'] === true && $p->post_type != 'post' ) { + $url['loc'] = trailingslashit( $url['loc'] ); + } + } + + $pri = WPSEO_Meta::get_value( 'sitemap-prio', $p->ID ); + if ( is_numeric( $pri ) ) { + $url['pri'] = (float) $pri; + } else { + if ( $p->post_parent == 0 && $p->post_type == 'page' ) { + $url['pri'] = 0.8; + } else { + $url['pri'] = 0.6; + } + } + + if ( isset( $front_id ) && $p->ID == $front_id ) { + $url['pri'] = 1.0; + } + + /** + * Filter: 'wpseo_xml_post_type_archive_priority' - Allow changing the priority of the URL WordPress SEO uses in the XML sitemap. + * + * @api float $priority The priority for this URL, ranging from 0 to 1 + * + * @param string $post_type The post type this archive is for + * @param object $p The post object + */ + $url['pri'] = apply_filters( 'wpseo_xml_sitemap_post_priority', $url['pri'], $p->post_type, $p ); + + $url['images'] = array(); + + $content = $p->post_content; + $content = '

' . $p->post_title . '

' . $content; + + if ( preg_match_all( '`]+>`', $content, $matches ) ) { + foreach ( $matches[0] as $img ) { + if ( preg_match( '`src=["\']([^"\']+)["\']`', $img, $match ) ) { + $src = $match[1]; + if ( wpseo_is_url_relative( $src ) === true ) { + if ( $src[0] !== '/' ) { + continue; + } else { + // The URL is relative, we'll have to make it absolute + $src = $this->home_url . $src; + } + } + elseif ( strpos( $src, 'http' ) !== 0 ) { + // Protocol relative url, we add the scheme as the standard requires a protocol + $src = $scheme . ':' . $src; + + } + + if ( strpos( $src, $host ) === false ) { + continue; + } + + if ( $src != esc_url( $src ) ) { + continue; + } + + if ( isset( $url['images'][ $src ] ) ) { + continue; + } + + $image = array( + 'src' => apply_filters( 'wpseo_xml_sitemap_img_src', $src, $p ) + ); + + if ( preg_match( '`title=["\']([^"\']+)["\']`', $img, $match ) ) { + $image['title'] = str_replace( array( '-', '_' ), ' ', $match[1] ); + } + + if ( preg_match( '`alt=["\']([^"\']+)["\']`', $img, $match ) ) { + $image['alt'] = str_replace( array( '-', '_' ), ' ', $match[1] ); + } + + $image = apply_filters( 'wpseo_xml_sitemap_img', $image, $p ); + + $url['images'][] = $image; + } + } + } + + if ( strpos( $p->post_content, '[gallery' ) !== false ) { + if ( is_array( $attachments ) && $attachments !== array() ) { + + foreach ( $attachments as $attachment ) { + if ( $attachment->post_parent !== $p->ID ) { + continue; + } + + $src = $this->image_url( $attachment->ID ); + $image = array( + 'src' => apply_filters( 'wpseo_xml_sitemap_img_src', $src, $p ) + ); + + $alt = get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true ); + if ( $alt !== '' ) { + $image['alt'] = $alt; + } + unset( $alt ); + + $image['title'] = $attachment->post_title; + + $image = apply_filters( 'wpseo_xml_sitemap_img', $image, $p ); + + $url['images'][] = $image; + } + } + unset( $attachment, $src, $image, $alt ); + } + + $url['images'] = apply_filters( 'wpseo_sitemap_urlimages', $url['images'], $p->ID ); + + if ( ! in_array( $url['loc'], $stackedurls ) ) { + // Use this filter to adjust the entry before it gets added to the sitemap + $url = apply_filters( 'wpseo_sitemap_entry', $url, 'post', $p ); + if ( is_array( $url ) && $url !== array() ) { + $output .= $this->sitemap_url( $url ); + $stackedurls[] = $url['loc']; + } + } + + // Clear the post_meta and the term cache for the post, as we no longer need it now. + // wp_cache_delete( $p->ID, 'post_meta' ); + // clean_object_term_cache( $p->ID, $post_type ); + } + } + } + + if ( empty( $output ) ) { + $this->bad_sitemap = true; + + return; + } + + $this->sitemap = 'sitemap .= 'xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" '; + $this->sitemap .= 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n"; + $this->sitemap .= $output; + + // Filter to allow adding extra URLs, only do this on the first XML sitemap, not on all. + if ( $n === 1 ) { + $this->sitemap .= apply_filters( 'wpseo_sitemap_' . $post_type . '_content', '' ); + } + + $this->sitemap .= ''; + } + + /** + * Build a sub-sitemap for a specific taxonomy -- example.com/tax-sitemap.xml + * + * @param string $taxonomy Registered taxonomy's slug + */ + function build_tax_map( $taxonomy ) { + if ( + ( isset( $this->options[ 'taxonomies-' . $taxonomy->name . '-not_in_sitemap' ] ) && $this->options[ 'taxonomies-' . $taxonomy->name . '-not_in_sitemap' ] === true ) + || in_array( $taxonomy, array( 'link_category', 'nav_menu', 'post_format' ) ) + || apply_filters( 'wpseo_sitemap_exclude_taxonomy', false, $taxonomy->name ) + ) { + $this->bad_sitemap = true; + + return; + } + + global $wpdb; + $output = ''; + + $steps = $this->max_entries; + $n = (int) $this->n; + $offset = ( $n > 1 ) ? ( $n - 1 ) * $this->max_entries : 0; + + /** + * Filter: 'wpseo_sitemap_exclude_empty_terms' - Allow people to include empty terms in sitemap + * + * @api bool $hide_empty Whether or not to hide empty terms, defaults to true. + * + * @param object $taxonomy The taxonomy we're getting terms for. + */ + $hide_empty = apply_filters( 'wpseo_sitemap_exclude_empty_terms', true, $taxonomy ); + $terms = get_terms( $taxonomy->name, array( 'hide_empty' => $hide_empty ) ); + $terms = array_splice( $terms, $offset, $steps ); + + if ( is_array( $terms ) && $terms !== array() ) { + foreach ( $terms as $c ) { + $url = array(); + + $tax_noindex = WPSEO_Taxonomy_Meta::get_term_meta( $c, $c->taxonomy, 'noindex' ); + $tax_sitemap_inc = WPSEO_Taxonomy_Meta::get_term_meta( $c, $c->taxonomy, 'sitemap_include' ); + + if ( ( is_string( $tax_noindex ) && $tax_noindex === 'noindex' ) && ( ! is_string( $tax_sitemap_inc ) || $tax_sitemap_inc !== 'always' ) ) { + continue; + } + + if ( $tax_sitemap_inc === 'never' ) { + continue; + } + + $url['loc'] = WPSEO_Taxonomy_Meta::get_term_meta( $c, $c->taxonomy, 'canonical' ); + if ( ! is_string( $url['loc'] ) || $url['loc'] === '' ) { + $url['loc'] = get_term_link( $c, $c->taxonomy ); + if ( $this->options['trailingslash'] === true ) { + $url['loc'] = trailingslashit( $url['loc'] ); + } + } + if ( $c->count > 10 ) { + $url['pri'] = 0.6; + } else { + if ( $c->count > 3 ) { + $url['pri'] = 0.4; + } else { + $url['pri'] = 0.2; + } + } + + // Grab last modified date + $sql = $wpdb->prepare( + " + SELECT MAX(p.post_modified_gmt) AS lastmod + FROM $wpdb->posts AS p + INNER JOIN $wpdb->term_relationships AS term_rel + ON term_rel.object_id = p.ID + INNER JOIN $wpdb->term_taxonomy AS term_tax + ON term_tax.term_taxonomy_id = term_rel.term_taxonomy_id + AND term_tax.taxonomy = %s + AND term_tax.term_id = %d + WHERE p.post_status IN ('publish','inherit') + AND p.post_password = ''", + $c->taxonomy, + $c->term_id + ); + $url['mod'] = $wpdb->get_var( $sql ); + $url['chf'] = $this->filter_frequency( $c->taxonomy . '_term', 'weekly', $url['loc'] ); + + // Use this filter to adjust the entry before it gets added to the sitemap + $url = apply_filters( 'wpseo_sitemap_entry', $url, 'term', $c ); + + if ( is_array( $url ) && $url !== array() ) { + $output .= $this->sitemap_url( $url ); + } + } + } + + if ( empty( $output ) ) { + $this->bad_sitemap = true; + + return; + } + + $this->sitemap = 'sitemap .= 'xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" '; + $this->sitemap .= 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n"; + if ( is_string( $output ) && trim( $output ) !== '' ) { + $this->sitemap .= $output; + } else { + // If the sitemap is empty, add the homepage URL to make sure it doesn't throw errors in GWT. + $this->sitemap .= $this->sitemap_url( home_url() ); + } + $this->sitemap .= ''; + } + + + /** + * Build the sub-sitemap for authors + * + * @since 1.4.8 + */ + function build_user_map() { + if ( $this->options['disable-author'] === true || $this->options['disable_author_sitemap'] === true ) { + $this->bad_sitemap = true; + + return; + } + + $output = ''; + + $steps = $this->max_entries; + $n = (int) $this->n; + $offset = ( $n > 1 ) ? ( $n - 1 ) * $this->max_entries : 0; + + // initial query to fill in missing usermeta with the current timestamp + $users = get_users( + array( + 'who' => 'authors', + 'meta_query' => array( + array( + 'key' => '_yoast_wpseo_profile_updated', + 'value' => 'needs-a-value-anyway', // This is ignored, but is necessary... + 'compare' => 'NOT EXISTS', + ), + ) + ) + ); + + if ( is_array( $users ) && $users !== array() ) { + foreach ( $users as $user ) { + update_user_meta( $user->ID, '_yoast_wpseo_profile_updated', time() ); + } + } + unset( $users, $user ); + + // query for users with this meta + $users = get_users( + array( + 'who' => 'authors', + 'offset' => $offset, + 'number' => $steps, + 'meta_key' => '_yoast_wpseo_profile_updated', + 'orderby' => 'meta_value_num', + 'order' => 'ASC', + ) + ); + + add_filter( 'wpseo_sitemap_exclude_author', array( $this, 'user_sitemap_remove_excluded_authors' ), 8 ); + + $users = apply_filters( 'wpseo_sitemap_exclude_author', $users ); + + // ascending sort + usort( $users, array( $this, 'user_map_sorter' ) ); + + if ( is_array( $users ) && $users !== array() ) { + foreach ( $users as $user ) { + $author_link = get_author_posts_url( $user->ID ); + if ( $author_link !== '' ) { + $url = array( + 'loc' => $author_link, + 'pri' => 0.8, + 'chf' => $change_freq = $this->filter_frequency( 'author_archive', 'daily', $author_link ), + 'mod' => date( 'c', isset( $user->_yoast_wpseo_profile_updated ) ? $user->_yoast_wpseo_profile_updated : time() ), + ); + // Use this filter to adjust the entry before it gets added to the sitemap + $url = apply_filters( 'wpseo_sitemap_entry', $url, 'user', $user ); + + if ( is_array( $url ) && $url !== array() ) { + $output .= $this->sitemap_url( $url ); + } + } + } + unset( $user, $author_link ); + } + + if ( empty( $output ) ) { + $this->bad_sitemap = true; + + return; + } + + $this->sitemap = 'sitemap .= 'xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" '; + $this->sitemap .= 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n"; + $this->sitemap .= $output; + + // Filter to allow adding extra URLs, only do this on the first XML sitemap, not on all. + if ( $n === 1 ) { + $this->sitemap .= apply_filters( 'wpseo_sitemap_author_content', '' ); + } + + $this->sitemap .= ''; + } + + /** + * Spits out the XSL for the XML sitemap. + * + * @param string $type + * + * @since 1.4.13 + */ + function xsl_output( $type ) { + if ( $type == 'main' ) { + header( $this->http_protocol() . ' 200 OK', true, 200 ); + // Prevent the search engines from indexing the XML Sitemap. + header( 'X-Robots-Tag: noindex, follow', true ); + header( 'Content-Type: text/xml' ); + + // Make the browser cache this file properly. + $expires = YEAR_IN_SECONDS; + header( 'Pragma: public' ); + header( 'Cache-Control: maxage=' . $expires ); + header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + $expires ) . ' GMT' ); + + require_once( WPSEO_PATH . 'css/xml-sitemap-xsl.php' ); + } else { + do_action( 'wpseo_xsl_' . $type ); + } + } + + /** + * Spit out the generated sitemap and relevant headers and encoding information. + */ + function output() { + header( $this->http_protocol() . ' 200 OK', true, 200 ); + // Prevent the search engines from indexing the XML Sitemap. + header( 'X-Robots-Tag: noindex,follow', true ); + header( 'Content-Type: text/xml' ); + echo 'charset ) . '"?>'; + if ( $this->stylesheet ) { + echo apply_filters( 'wpseo_stylesheet_url', $this->stylesheet ) . "\n"; + } + echo $this->sitemap; + echo "\n" . ''; + + $debug_display = defined( 'WP_DEBUG_DISPLAY' ) && true === WP_DEBUG_DISPLAY; + $debug = defined( 'WP_DEBUG' ) && true === WP_DEBUG; + $wpseo_debug = defined( 'WPSEO_DEBUG' ) && true === WPSEO_DEBUG; + + if ( $debug_display && ( $debug || $wpseo_debug ) ) { + if ( $this->transient ) { + echo "\n" . ''; + } else { + global $wpdb; + echo "\n" . ''; + if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) { + echo "\n" . ''; + } + } + } + } + + /** + * Build the tag for a given URL. + * + * @param array $url Array of parts that make up this entry + * + * @return string + */ + function sitemap_url( $url ) { + + // Create a DateTime object date in the correct timezone + if ( isset( $url['mod'] ) ) { + $date = new DateTime( $url['mod'], new DateTimeZone( $this->get_timezone_string() ) ); + } else { + $date = new DateTime( date( 'y-m-d H:i:s' ), new DateTimeZone( $this->get_timezone_string() ) ); + } + + $url['loc'] = htmlspecialchars( $url['loc'] ); + + $output = "\t\n"; + $output .= "\t\t" . $url['loc'] . "\n"; + $output .= "\t\t" . $date->format( 'c' ) . "\n"; + $output .= "\t\t" . $url['chf'] . "\n"; + $output .= "\t\t" . str_replace( ',', '.', $url['pri'] ) . "\n"; + + if ( isset( $url['images'] ) && ( is_array( $url['images'] ) && $url['images'] !== array() ) ) { + foreach ( $url['images'] as $img ) { + if ( ! isset( $img['src'] ) || empty( $img['src'] ) ) { + continue; + } + $output .= "\t\t\n"; + $output .= "\t\t\t" . esc_html( $img['src'] ) . "\n"; + if ( isset( $img['title'] ) && ! empty( $img['title'] ) ) { + $output .= "\t\t\tcharset ) ) . "]]>\n"; + } + if ( isset( $img['alt'] ) && ! empty( $img['alt'] ) ) { + $output .= "\t\t\tcharset ) ) . "]]>\n"; + } + $output .= "\t\t\n"; + } + } + $output .= "\t\n"; + + return $output; + } + + /** + * Make a request for the sitemap index so as to cache it before the arrival of the search engines. + */ + function hit_sitemap_index() { + $url = wpseo_xml_sitemaps_base_url( 'sitemap_index.xml' ); + wp_remote_get( $url ); + } + + /** + * Hook into redirect_canonical to stop trailing slashes on sitemap.xml URLs + * + * @param string $redirect The redirect URL currently determined. + * + * @return bool|string $redirect + */ + function canonical( $redirect ) { + $sitemap = get_query_var( 'sitemap' ); + if ( ! empty( $sitemap ) ) { + return false; + } + + $xsl = get_query_var( 'xsl' ); + if ( ! empty( $xsl ) ) { + return false; + } + + return $redirect; + } + + /** + * Get the modification date for the last modified post in the post type: + * + * @param array $post_types Post types to get the last modification date for + * + * @return string + */ + function get_last_modified( $post_types ) { + global $wpdb; + if ( ! is_array( $post_types ) ) { + $post_types = array( $post_types ); + } + + // We need to do this only once, as otherwise we'd be doing a query for each post type + if ( ! is_array( $this->post_type_dates ) ) { + $this->post_type_dates = array(); + $query = "SELECT post_type, MAX(post_modified_gmt) AS date FROM $wpdb->posts WHERE post_status IN ('publish','inherit') AND post_type IN ('" . implode( "','", get_post_types( array( 'public' => true ) ) ) . "') GROUP BY post_type ORDER BY post_modified_gmt DESC"; + $results = $wpdb->get_results( $query ); + foreach ( $results as $obj ) { + $this->post_type_dates[ $obj->post_type ] = $obj->date; + } + unset( $results ); + } + + if ( count( $post_types ) === 1 && isset( $this->post_type_dates[ $post_types[0] ] ) ) { + $result = $this->post_type_dates[ $post_types[0] ]; + } else { + $result = null; + foreach ( $post_types as $post_type ) { + if ( isset( $this->post_type_dates[ $post_type ] ) && strtotime( $this->post_type_dates[ $post_type ] ) > $result ) { + $result = $this->post_type_dates[ $post_type ]; + } + } + } + + $date = new DateTime( $result, new DateTimeZone( $this->get_timezone_string() ) ); + + return $date->format( 'c' ); + } + + /** + * Sorts an array of WP_User by the _yoast_wpseo_profile_updated meta field + * + * since 1.6 + * + * @param Wp_User $a The first WP user + * @param Wp_User $b The second WP user + * + * @return int 0 if equal, 1 if $a is larger else or -1; + */ + private function user_map_sorter( $a, $b ) { + if ( ! isset( $a->_yoast_wpseo_profile_updated ) ) { + $a->_yoast_wpseo_profile_updated = time(); + } + if ( ! isset( $b->_yoast_wpseo_profile_updated ) ) { + $b->_yoast_wpseo_profile_updated = time(); + } + + if ( $a->_yoast_wpseo_profile_updated == $b->_yoast_wpseo_profile_updated ) { + return 0; + } + + return ( $a->_yoast_wpseo_profile_updated > $b->_yoast_wpseo_profile_updated ) ? 1 : - 1; + } + + /** + * Filter users that should be excluded from the sitemap (by author metatag: wpseo_excludeauthorsitemap). + * + * Also filtering users that should be exclude by excluded role. + * + * @param array $users + * + * @return array all the user that aren't excluded from the sitemap + */ + public function user_sitemap_remove_excluded_authors( $users ) { + + if ( is_array( $users ) && $users !== array() ) { + $options = get_option( 'wpseo_xml' ); + + foreach ( $users as $user_key => $user ) { + $exclude_user = false; + + $is_exclude_on = get_the_author_meta( 'wpseo_excludeauthorsitemap', $user->ID ); + if ( $is_exclude_on === 'on' ) { + $exclude_user = true; + } elseif ( $options['disable_author_noposts'] === true ) { + $count_posts = count_user_posts( $user->ID ); + $exclude_user = $count_posts == 0; + } else { + $user_role = $user->roles[0]; + $target_key = "user_role-{$user_role}-not_in_sitemap"; + $exclude_user = $options[$target_key]; + } + + if ( $exclude_user === true ) { + unset( $users[$user_key] ); + } + } + } + + return $users; + } + + /** + * Get attached image URL - Adapted from core for speed + * + * @param int $post_id + * + * @return string + */ + private function image_url( $post_id ) { + + static $uploads; + + if ( empty( $uploads ) ) { + $uploads = wp_upload_dir(); + } + + if ( false !== $uploads['error'] ) { + return ''; + } + + $url = ''; + + if ( $file = get_post_meta( $post_id, '_wp_attached_file', true ) ) { //Get attached file + if ( 0 === strpos( $file, $uploads['basedir'] ) ) { //Check that the upload base exists in the file location + $url = str_replace( $uploads['basedir'], $uploads['baseurl'], $file ); + } //replace file location with url location + elseif ( false !== strpos( $file, 'wp-content/uploads' ) ) { + $url = $uploads['baseurl'] . substr( $file, strpos( $file, 'wp-content/uploads' ) + 18 ); + } else { + $url = $uploads['baseurl'] . "/$file"; + } //Its a newly uploaded file, therefore $file is relative to the basedir. + } + + return $url; + } + + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/inc/class-wpseo-meta.php b/wp-content/plugins/wordpress-seo/inc/class-wpseo-meta.php new file mode 100644 index 0000000..ce89a7b --- /dev/null +++ b/wp-content/plugins/wordpress-seo/inc/class-wpseo-meta.php @@ -0,0 +1,1015 @@ + (string) field type. i.e. text / textarea / checkbox / + * radio / select / multiselect / upload / snippetpreview etc + * (required) 'title' => (string) table row title + * (recommended) 'default_value' => (string|array) default value for the field + * IMPORTANT: + * - if the field has options, the default has to be the + * key of one of the options + * - if the field is a text field, the default **has** to be + * an empty string as otherwise the user can't save + * an empty value/delete the meta value + * - if the field is a checkbox, the only valid values + * are 'on' or 'off' + * (semi-required) 'options' => (array) options for used with (multi-)select and radio + * fields, required if that's the field type + * key = (string) value which will be saved to db + * value = (string) text label for the option + * (optional) 'autocomplete' => (bool) whether autocomplete is on for text fields, + * defaults to true + * (optional) 'class' => (string) classname(s) to add to the actual tag + * (optional) 'description' => (string) description to show underneath the field + * (optional) 'expl' => (string) label for a checkbox + * (optional) 'help' => (string) help text to show on mouse over ? image + * (optional) 'rows' => (int) number of rows for a textarea, defaults to 3 + * + * (optional) 'placeholder' => (string) Currently only used by add-on plugins + * (optional) 'serialized' => (bool) whether the value is expected to be serialized, + * i.e. an array or object, defaults to false + * Currently only used by add-on plugins + * + * @internal + * - Titles, help texts, description text and option labels are added via a translate_meta_boxes() method + * in the relevant child classes (WPSEO_Metabox and WPSEO_Social_admin) as they are only needed there. + * - Beware: even though the meta keys are divided into subsets, they still have to be uniquely named! + */ + public static $meta_fields = array( + 'general' => array( + 'snippetpreview' => array( + 'type' => 'snippetpreview', + 'title' => '', // translation added later + 'help' => '', // translation added later + ), + 'focuskw' => array( + 'type' => 'text', + 'title' => '', // translation added later + 'default_value' => '', + 'autocomplete' => false, + 'help' => '', // translation added later + 'description' => '
', + ), + 'title' => array( + 'type' => 'text', + 'title' => '', // translation added later + 'default_value' => '', + 'description' => '', // translation added later + 'help' => '', // translation added later + ), + 'metadesc' => array( + 'type' => 'textarea', + 'title' => '', // translation added later + 'default_value' => '', + 'class' => 'metadesc', + 'rows' => 2, + 'description' => '', // translation added later + 'help' => '', // translation added later + ), + 'metakeywords' => array( + 'type' => 'text', + 'title' => '', // translation added later + 'default_value' => '', + 'class' => 'metakeywords', + 'description' => '', // translation added later + ), + ), + 'advanced' => array( + 'meta-robots-noindex' => array( + 'type' => 'select', + 'title' => '', // translation added later + 'default_value' => '0', // = post-type default + 'options' => array( + '0' => '', // post type default - translation added later + '2' => '', // index - translation added later + '1' => '', // no-index - translation added later + ), + ), + 'meta-robots-nofollow' => array( + 'type' => 'radio', + 'title' => '', // translation added later + 'default_value' => '0', // = follow + 'options' => array( + '0' => '', // follow - translation added later + '1' => '', // no-follow - translation added later + ), + ), + 'meta-robots-adv' => array( + 'type' => 'multiselect', + 'title' => '', // translation added later + 'default_value' => '-', // = site-wide default + 'description' => '', // translation added later + 'options' => array( + '-' => '', // site-wide default - translation added later + 'none' => '', // translation added later + 'noodp' => '', // translation added later + 'noydir' => '', // translation added later + 'noimageindex' => '', // translation added later + 'noarchive' => '', // translation added later + 'nosnippet' => '', // translation added later + ), + ), + 'bctitle' => array( + 'type' => 'text', + 'title' => '', // translation added later + 'default_value' => '', + 'description' => '', // translation added later + ), + 'sitemap-include' => array( + 'type' => 'select', + 'title' => '', // translation added later + 'default_value' => '-', + 'description' => '', // translation added later + 'options' => array( + '-' => '', // translation added later + 'always' => '', // translation added later + 'never' => '', // translation added later + ), + ), + 'sitemap-prio' => array( + 'type' => 'select', + 'title' => '', // translation added later + 'default_value' => '-', + 'description' => '', // translation added later + 'options' => array( + '-' => '', // translation added later + '1' => '', // translation added later + '0.9' => '0.9', + '0.8' => '0.8 - ', // translation added later + '0.7' => '0.7', + '0.6' => '0.6 - ', // translation added later + '0.5' => '0.5 - ', // translation added later + '0.4' => '0.4', + '0.3' => '0.3', + '0.2' => '0.2', + '0.1' => '0.1 - ', // translation added later + ), + ), + 'canonical' => array( + 'type' => 'text', + 'title' => '', // translation added later + 'default_value' => '', + 'description' => '', // translation added later + ), + 'redirect' => array( + 'type' => 'text', + 'title' => '', // translation added later + 'default_value' => '', + 'description' => '', // translation added later + ), + ), + 'social' => array( + 'opengraph-title' => array( + 'type' => 'text', + 'title' => '', // translation added later + 'default_value' => '', + 'description' => '', // translation added later + ), + 'opengraph-description' => array( + 'type' => 'textarea', + 'title' => '', // translation added later + 'default_value' => '', + 'description' => '', // translation added later + ), + 'opengraph-image' => array( + 'type' => 'upload', + 'title' => '', // translation added later + 'default_value' => '', + 'description' => '', // translation added later + ), + 'google-plus-title' => array( + 'type' => 'text', + 'title' => '', // translation added later + 'default_value' => '', + 'description' => '', // translation added later + ), + 'google-plus-description' => array( + 'type' => 'textarea', + 'title' => '', // translation added later + 'default_value' => '', + 'description' => '', // translation added later + ), + 'google-plus-image' => array( + 'type' => 'upload', + 'title' => '', // translation added later + 'default_value' => '', + 'description' => '', // translation added later + ), + + ), + + /* Fields we should validate & save, but not show on any form */ + 'non_form' => array( + 'linkdex' => array( + 'type' => null, + 'default_value' => '0', + ), + ), + ); + + + /** + * @static + * @var array Helper property - reverse index of the definition array + * Format: [full meta key including prefix] => array + * ['subset'] => (string) primary index + * ['key'] => (string) internal key + */ + public static $fields_index = array(); + + + /** + * @static + * @var array Helper property - array containing only the defaults in the format: + * [full meta key including prefix] => (string) default value + */ + public static $defaults = array(); + + + /** + * Register our actions and filters + * + * @static + * @return void + */ + public static function init() { + /** + * Allow add-on plugins to register their meta fields for management by this class + * add_filter() calls must be made before plugins_loaded prio 14 + */ + $extra_fields = apply_filters( 'add_extra_wpseo_meta_fields', array() ); + if ( is_array( $extra_fields ) ) { + self::$meta_fields = self::array_merge_recursive_distinct( $extra_fields, self::$meta_fields ); + } + + $register = function_exists( 'register_meta' ); + + foreach ( self::$meta_fields as $subset => $field_group ) { + foreach ( $field_group as $key => $field_def ) { + if ( $field_def['type'] !== 'snippetpreview' ) { + /* register_meta() is undocumented and not used by WP internally, wrapped in + function_exists as a precaution in case they remove it. */ + if ( $register === true ) { + register_meta( 'post', self::$meta_prefix . $key, array( __CLASS__, 'sanitize_post_meta' ) ); + } + else { + add_filter( 'sanitize_post_meta_' . self::$meta_prefix . $key, array( __CLASS__, 'sanitize_post_meta' ), 10, 2 ); + } + + // Set the $fields_index property for efficiency + self::$fields_index[ self::$meta_prefix . $key ] = array( + 'subset' => $subset, + 'key' => $key, + ); + + // Set the $defaults property for efficiency + if ( isset( $field_def['default_value'] ) ) { + self::$defaults[ self::$meta_prefix . $key ] = $field_def['default_value']; + } + else { + // meta will always be a string, so let's make the meta meta default also a string + self::$defaults[ self::$meta_prefix . $key ] = ''; + } + } + } + } + + add_filter( 'update_post_metadata', array( __CLASS__, 'remove_meta_if_default' ), 10, 5 ); + add_filter( 'add_post_metadata', array( __CLASS__, 'dont_save_meta_if_default' ), 10, 4 ); + } + + + /** + * Retrieve the meta box form field definitions for the given tab and post type. + * + * @static + * + * @param string $tab Tab for which to retrieve the field definitions + * @param string $post_type Post type of the current post + * @return array Array containing the meta box field definitions + */ + public static function get_meta_field_defs( $tab, $post_type = 'post' ) { + if ( ! isset( self::$meta_fields[ $tab ] ) ) { + return array(); + } + + $field_defs = self::$meta_fields[ $tab ]; + + switch ( $tab ) { + case 'non-form': + // prevent non-form fields from being passed to forms + $field_defs = array(); + break; + + + case 'general': + $options = get_option( 'wpseo_titles' ); + if ( $options['usemetakeywords'] === true ) { + /* Adjust the link in the keywords description text string based on the post type */ + $field_defs['metakeywords']['description'] = sprintf( $field_defs['metakeywords']['description'], '', '' ); + } + else { + /* Don't show the keywords field if keywords aren't enabled */ + unset( $field_defs['metakeywords'] ); + } + /** + * Filter the WPSEO metabox form field definitions for the general tab, backward compatibility + * + * @deprecated 1.5.0 + * @deprecated use the 'wpseo_metabox_entries_general' filter instead + * @see WPSEO_Meta::get_meta_field_defs() + * + * @param array $field_defs metabox form definitions + * @return array + */ + $field_defs = apply_filters( 'wpseo_metabox_entries', $field_defs ); + break; + + + case 'advanced': + global $post; + + $options = WPSEO_Options::get_all(); + + $post_type = ''; + if ( isset( $post->post_type ) ) { + $post_type = $post->post_type; + } + elseif ( ! isset( $post->post_type ) && isset( $_GET['post_type'] ) ) { + $post_type = sanitize_text_field( $_GET['post_type'] ); + } + + /* Adjust the no-index 'default for post type' text string based on the post type */ + $field_defs['meta-robots-noindex']['options']['0'] = sprintf( $field_defs['meta-robots-noindex']['options']['0'], ( ( isset( $options[ 'noindex-' . $post_type ] ) && $options[ 'noindex-' . $post_type ] === true ) ? 'noindex' : 'index' ) ); + + /* Adjust the robots advanced 'site-wide default' text string based on those settings */ + if ( $options['noodp'] !== false || $options['noydir'] !== false ) { + $robots_adv = array(); + foreach ( array( 'noodp', 'noydir' ) as $robot ) { + if ( $options[ $robot ] === true ) { + // use translation from field def options - mind that $options and $field_def['options'] keys should be the same! + $robots_adv[] = $field_defs['meta-robots-adv']['options'][ $robot ]; + } + } + $robots_adv = implode( ', ', $robots_adv ); + } + else { + $robots_adv = __( 'None', 'wordpress-seo' ); + } + $field_defs['meta-robots-adv']['options']['-'] = sprintf( $field_defs['meta-robots-adv']['options']['-'], $robots_adv ); + unset( $robots_adv ); + + + /* Don't show the breadcrumb title field if breadcrumbs aren't enabled */ + if ( $options['breadcrumbs-enable'] !== true ) { + unset( $field_defs['bctitle'] ); + } + + /* Don't show the xml sitemap fields, if xml sitemaps aren't enabled */ + if ( $options['enablexmlsitemap'] !== true ) { + unset( + $field_defs['sitemap-include'], + $field_defs['sitemap-prio'] + ); + } + + break; + } + + /** + * Filter the WPSEO metabox form field definitions for a tab + * {tab} can be 'general', 'advanced' or 'social' + * + * @param array $field_defs metabox form definitions + * @param string $post_type post type of the post the metabox is for, defaults to 'post' + * @return array + */ + return apply_filters( 'wpseo_metabox_entries_' . $tab, $field_defs, $post_type ); + } + + + /** + * Validate the post meta values + * + * @static + * + * @param mixed $meta_value The new value + * @param string $meta_key The full meta key (including prefix) + * @return string Validated meta value + */ + public static function sanitize_post_meta( $meta_value, $meta_key ) { + $field_def = self::$meta_fields[ self::$fields_index[ $meta_key ]['subset'] ][ self::$fields_index[ $meta_key ]['key'] ]; + $clean = self::$defaults[ $meta_key ]; + + switch ( true ) { + case ( $meta_key === self::$meta_prefix . 'linkdex' ): + $int = WPSEO_Option::validate_int( $meta_value ); + if ( $int !== false && $int >= 0 ) { + $clean = strval( $int ); // Convert to string to make sure default check works + } + break; + + + case ( $field_def['type'] === 'checkbox' ): + // Only allow value if it's one of the predefined options + if ( in_array( $meta_value, array( 'on', 'off' ), true ) ) { + $clean = $meta_value; + } + break; + + + case ( $field_def['type'] === 'select' || $field_def['type'] === 'radio' ): + // Only allow value if it's one of the predefined options + if ( isset( $field_def['options'][ $meta_value ] ) ) { + $clean = $meta_value; + } + break; + + + case ( $field_def['type'] === 'multiselect' && $meta_key === self::$meta_prefix . 'meta-robots-adv' ): + $clean = self::validate_meta_robots_adv( $meta_value ); + break; + + + case ( $field_def['type'] === 'text' && $meta_key === self::$meta_prefix . 'canonical' ): + case ( $field_def['type'] === 'text' && $meta_key === self::$meta_prefix . 'redirect' ): + // Validate as url(-part) + $url = WPSEO_Option::sanitize_url( $meta_value ); + if ( $url !== '' ) { + $clean = $url; + } + break; + + + case ( $field_def['type'] === 'upload' && $meta_key === self::$meta_prefix . 'opengraph-image' ): + // Validate as url + $url = WPSEO_Option::sanitize_url( $meta_value, array( 'http', 'https', 'ftp', 'ftps' ) ); + if ( $url !== '' ) { + $clean = $url; + } + break; + + + case ( $field_def['type'] === 'textarea' ): + if ( is_string( $meta_value ) ) { + // Remove line breaks and tabs + // @todo [JRF => Yoast] verify that line breaks and the likes aren't allowed/recommended in meta header fields + $meta_value = str_replace( array( "\n", "\r", "\t", ' ' ), ' ', $meta_value ); + $clean = WPSEO_Option::sanitize_text_field( trim( $meta_value ) ); + } + break; + + case ( 'multiselect' === $field_def['type'] ) : + $clean = $meta_value; + break; + + + case ( $field_def['type'] === 'text' ): + default: + if ( is_string( $meta_value ) ) { + $clean = WPSEO_Option::sanitize_text_field( trim( $meta_value ) ); + } + break; + } + + $clean = apply_filters( 'wpseo_sanitize_post_meta_' . $meta_key, $clean, $meta_value, $field_def, $meta_key ); + + return $clean; + } + + + /** + * Validate a meta-robots-adv meta value + * + * @todo [JRF => Yoast] Verify that this logic for the prioritisation is correct + * + * @static + * + * @param array|string $meta_value The value to validate + * @return string Clean value + */ + public static function validate_meta_robots_adv( $meta_value ) { + $clean = self::$meta_fields['advanced']['meta-robots-adv']['default_value']; + $options = self::$meta_fields['advanced']['meta-robots-adv']['options']; + + if ( is_string( $meta_value ) ) { + $meta_value = explode( ',', $meta_value ); + } + + if ( is_array( $meta_value ) && $meta_value !== array() ) { + $meta_value = array_map( 'trim', $meta_value ); + + if ( in_array( 'none', $meta_value, true ) ) { + // None is one of the selected values, takes priority over everything else + $clean = 'none'; + } + elseif ( in_array( '-', $meta_value, true ) ) { + // Site-wide defaults is one of the selected values, takes priority over + // individual selected entries + $clean = '-'; + } + else { + // Individual selected entries + $cleaning = array(); + foreach ( $meta_value as $value ) { + if ( isset( $options[ $value ] ) ) { + $cleaning[] = $value; + } + } + + if ( $cleaning !== array() ) { + $clean = implode( ',', $cleaning ); + } + unset( $cleaning, $value ); + } + } + + return $clean; + } + + + /** + * Prevent saving of default values and remove potential old value from the database if replaced by a default + * + * @static + * + * @param null $null old, disregard + * @param int $object_id ID of the current object for which the meta is being updated + * @param string $meta_key The full meta key (including prefix) + * @param string $meta_value New meta value + * @param string $prev_value The old meta value + * @return null|bool true = stop saving, null = continue saving + */ + public static function remove_meta_if_default( $null, $object_id, $meta_key, $meta_value, $prev_value = '' ) { + /* If it's one of our meta fields, check against default */ + if ( isset( self::$fields_index[ $meta_key ] ) && self::meta_value_is_default( $meta_key, $meta_value ) === true ) { + if ( $prev_value !== '' ) { + delete_post_meta( $object_id, $meta_key, $prev_value ); + } + else { + delete_post_meta( $object_id, $meta_key ); + } + return true; // stop saving the value + } + + return null; // go on with the normal execution (update) in meta.php + } + + + /** + * Prevent adding of default values to the database + * + * @static + * + * @param null $null old, disregard + * @param int $object_id ID of the current object for which the meta is being added + * @param string $meta_key The full meta key (including prefix) + * @param string $meta_value New meta value + * @return null|bool true = stop saving, null = continue saving + */ + public static function dont_save_meta_if_default( $null, $object_id, $meta_key, $meta_value ) { + /* If it's one of our meta fields, check against default */ + if ( isset( self::$fields_index[ $meta_key ] ) && self::meta_value_is_default( $meta_key, $meta_value ) === true ) { + return true; // stop saving the value + } + + return null; // go on with the normal execution (add) in meta.php + } + + + /** + * Is the given meta value the same as the default value ? + * + * @static + * + * @param string $meta_key The full meta key (including prefix) + * @param mixed $meta_value The value to check + * @return bool + */ + public static function meta_value_is_default( $meta_key, $meta_value ) { + return ( isset( self::$defaults[ $meta_key ] ) && $meta_value === self::$defaults[ $meta_key ] ); + } + + + /** + * Get a custom post meta value + * Returns the default value if the meta value has not been set + * + * @internal Unfortunately there isn't a filter available to hook into before returning the results + * for get_post_meta(), get_post_custom() and the likes. That would have been the preferred solution. + * + * @static + * + * @param string $key internal key of the value to get (without prefix) + * @param int $postid post ID of the post to get the value for + * @return string All 'normal' values returned from get_post_meta() are strings. + * Objects and arrays are possible, but not used by this plugin + * and therefore discarted (except when the special 'serialized' field def + * value is set to true - only used by add-on plugins for now) + * Will return the default value if no value was found. + * Will return empty string if no default was found (not one of our keys) or + * if the post does not exist + */ + public static function get_value( $key, $postid = 0 ) { + global $post; + + $postid = absint( $postid ); + if ( $postid === 0 ) { + if ( ( isset( $post ) && is_object( $post ) ) && ( isset( $post->post_status ) && $post->post_status !== 'auto-draft' ) ){ + $postid = $post->ID; + } + else { + return ''; + } + } + + $custom = get_post_custom( $postid ); // array of strings or empty array + + if ( isset( $custom[ self::$meta_prefix . $key ][0] ) ) { + $unserialized = maybe_unserialize( $custom[ self::$meta_prefix . $key ][0] ); + if ( $custom[ self::$meta_prefix . $key ][0] === $unserialized ) { + return $custom[ self::$meta_prefix . $key ][0]; + } + else { + $field_def = self::$meta_fields[ self::$fields_index[ self::$meta_prefix . $key ]['subset'] ][ self::$fields_index[ self::$meta_prefix . $key ]['key'] ]; + if ( isset( $field_def['serialized'] ) && $field_def['serialized'] === true ) { + // Ok, serialize value expected/allowed + return $unserialized; + } + } + } + + // Meta was either not found or found, but object/array while not allowed to be + if ( isset( self::$defaults[ self::$meta_prefix . $key ] ) ) { + return self::$defaults[ self::$meta_prefix . $key ]; + } + else { + /* Shouldn't ever happen, means not one of our keys as there will always be a default available + for all our keys */ + return ''; + } + } + + + /** + * Update a meta value for a post + * + * @static + * + * @param string $key the internal key of the meta value to change (without prefix) + * @param mixed $meta_value the value to set the meta to + * @param int $post_id the ID of the post to change the meta for. + * @return bool whether the value was changed + */ + public static function set_value( $key, $meta_value, $post_id ) { + return update_post_meta( $post_id, self::$meta_prefix . $key, $meta_value ); + } + + + /** + * Used for imports, this functions imports the value of $old_metakey into $new_metakey for those post + * where no WPSEO meta data has been set. + * Optionally deletes the $old_metakey values. + * + * @static + * + * @param string $old_metakey The old key of the meta value. + * @param string $new_metakey The new key, usually the WPSEO meta key (including prefix). + * @param bool $delete_old Whether to delete the old meta key/value-sets. + * @return void + */ + public static function replace_meta( $old_metakey, $new_metakey, $delete_old = false ) { + global $wpdb; + + /* Get only those rows where no wpseo meta values exist for the same post + (with the exception of linkdex as that will be set independently of whether the post has been edited) + @internal Query is pretty well optimized this way */ + $query = $wpdb->prepare( + " + SELECT `a`.* + FROM {$wpdb->postmeta} AS a + WHERE `a`.`meta_key` = %s + AND NOT EXISTS ( + SELECT DISTINCT `post_id` , count( `meta_id` ) AS count + FROM {$wpdb->postmeta} AS b + WHERE `a`.`post_id` = `b`.`post_id` + AND `meta_key` LIKE %s + AND `meta_key` <> %s + GROUP BY `post_id` + ) + ;", + $old_metakey, + like_escape( self::$meta_prefix . '%' ), + self::$meta_prefix . 'linkdex' + ); + $oldies = $wpdb->get_results( $query ); + + if ( is_array( $oldies ) && $oldies !== array() ) { + foreach ( $oldies as $old ) { + update_post_meta( $old->post_id, $new_metakey, $old->meta_value ); + } + } + + // Delete old keys + if ( $delete_old === true ) { + delete_post_meta_by_key( $old_metakey ); + } + } + + + /** + * General clean-up of the saved meta values + * - Remove potentially lingering old meta keys + * - Remove all default and invalid values + * + * @static + * @return void + */ + public static function clean_up() { + global $wpdb; + + /** + * Clean up '_yoast_wpseo_meta-robots' + * + * Retrieve all '_yoast_wpseo_meta-robots' meta values and convert if no new values found + * @internal Query is pretty well optimized this way + * + * @todo [JRF => Yoast] find out all possible values which the old '_yoast_wpseo_meta-robots' could contain + * to convert the data correctly + */ + $query = $wpdb->prepare( + " + SELECT `a`.* + FROM {$wpdb->postmeta} AS a + WHERE `a`.`meta_key` = %s + AND NOT EXISTS ( + SELECT DISTINCT `post_id` , count( `meta_id` ) AS count + FROM {$wpdb->postmeta} AS b + WHERE `a`.`post_id` = `b`.`post_id` + AND ( `meta_key` = %s + OR `meta_key` = %s ) + GROUP BY `post_id` + ) + ;", + self::$meta_prefix . 'meta-robots', + self::$meta_prefix . 'meta-robots-noindex', + self::$meta_prefix . 'meta-robots-nofollow' + ); + $oldies = $wpdb->get_results( $query ); + + if ( is_array( $oldies ) && $oldies !== array() ) { + foreach ( $oldies as $old ) { + $old_values = explode( ',', $old->meta_value ); + foreach ( $old_values as $value ) { + if ( $value === 'noindex' ) { + update_post_meta( $old->post_id, self::$meta_prefix . 'meta-robots-noindex', 1 ); + } + elseif ( $value === 'nofollow' ) { + update_post_meta( $old->post_id, self::$meta_prefix . 'meta-robots-nofollow', 1 ); + } + } + } + } + unset( $query, $oldies, $old, $old_values, $value ); + + // Delete old keys + delete_post_meta_by_key( self::$meta_prefix . 'meta-robots' ); + + + /** + * Remove all default values and (most) invalid option values + * Invalid option values for the multiselect (meta-robots-adv) field will be dealt with seperately + * + * @internal some of the defaults have changed in v1.5, but as the defaults will be removed and + * new defaults will now automatically be passed when no data found, this update is automatic + * (as long as we remove the old values which we do in the below routine) + * + * @internal unfortunately we can't use the normal delete_meta() with key/value combination as '' + * (empty string) values will be ignored and would result in all metas with that key being deleted, + * not just the empty fields. + * Still, the below implementation is largely based on the delete_meta() function + */ + $query = array(); + + foreach ( self::$meta_fields as $subset => $field_group ) { + foreach ( $field_group as $key => $field_def ) { + if ( $field_def['type'] === 'snippetpreview' || ! isset( $field_def['default_value'] ) ) { + continue; + } + + if ( $key === 'meta-robots-adv' ) { + $query[] = $wpdb->prepare( + "( meta_key = %s AND ( meta_value = 'none' OR meta_value = '-' ) )", + self::$meta_prefix . $key + ); + } + elseif ( isset( $field_def['options'] ) && is_array( $field_def['options'] ) && $field_def['options'] !== array() ) { + $valid = $field_def['options']; + // remove the default value from the valid options + unset( $valid[ $field_def['default_value'] ] ); + $valid = array_keys( $valid ); + + $query[] = $wpdb->prepare( + "( meta_key = %s AND meta_value NOT IN ( '" . implode( "','", esc_sql( $valid ) ) . "' ) )", + self::$meta_prefix . $key + ); + unset( $valid ); + } + elseif ( is_string( $field_def['default_value'] ) && $field_def['default_value'] !== '' ) { + $query[] = $wpdb->prepare( + '( meta_key = %s AND meta_value = %s )', + self::$meta_prefix . $key, + $field_def['default_value'] + ); + } + else { + $query[] = $wpdb->prepare( + "( meta_key = %s AND meta_value = '' )", + self::$meta_prefix . $key + ); + } + } + } + unset( $subset, $field_group, $key, $field_def, $where_or_or ); + + $query = "SELECT meta_id FROM {$wpdb->postmeta} WHERE " . implode( ' OR ', $query ) . ';'; + $meta_ids = $wpdb->get_col( $query ); + + if ( is_array( $meta_ids ) && $meta_ids !== array() ) { + // wp native action + do_action( 'delete_post_meta', $meta_ids, null, null, null ); + + $query = "DELETE FROM {$wpdb->postmeta} WHERE meta_id IN( " . implode( ',', $meta_ids ) . ' )'; + $count = $wpdb->query( $query ); + + if ( $count ) { + foreach ( $meta_ids as $object_id ) { + wp_cache_delete( $object_id, 'post_meta' ); + } + + // wp native action + do_action( 'deleted_post_meta', $meta_ids, null, null, null ); + } + } + unset( $query, $meta_ids, $count, $object_id ); + + + /** + * Deal with the multiselect (meta-robots-adv) field + * + * Removes invalid option combinations, such as 'none,noarchive' + * + * Default values have already been removed, so we should have a small result set and + * (hopefully) even smaller set of invalid results. + */ + $query = $wpdb->prepare( + "SELECT meta_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s", + self::$meta_prefix . 'meta-robots-adv' + ); + $oldies = $wpdb->get_results( $query ); + + if ( is_array( $oldies ) && $oldies !== array() ) { + foreach ( $oldies as $old ) { + $clean = self::validate_meta_robots_adv( $old->meta_value ); + + if ( $clean !== $old->meta_value ) { + if ( $clean !== self::$meta_fields['advanced']['meta-robots-adv']['default_value'] ) { + update_metadata_by_mid( 'post', $old->meta_id, $clean ); + } + else { + delete_metadata_by_mid( 'post', $old->meta_id ); + } + } + } + } + unset( $query, $oldies, $old, $clean ); + + do_action( 'wpseo_meta_clean_up' ); + } + + + /** + * Recursively merge a variable number of arrays, using the left array as base, + * giving priority to the right array. + * + * Difference with native array_merge_recursive(): + * array_merge_recursive converts values with duplicate keys to arrays rather than + * overwriting the value in the first array with the duplicate value in the second array. + * + * array_merge_recursive_distinct does not change the data types of the values in the arrays. + * Matching keys' values in the second array overwrite those in the first array, as is the + * case with array_merge. + * + * Freely based on information found on http://www.php.net/manual/en/function.array-merge-recursive.php + * + * @internal Should be moved to a general utility class + * + * @param array 2 or more arrays to merge + * @return array + */ + public static function array_merge_recursive_distinct() { + + $arrays = func_get_args(); + if ( count( $arrays ) < 2 ) { + if ( $arrays === array() ) { + return array(); + } + else { + return $arrays[0]; + } + } + + $merged = array_shift( $arrays ); + + foreach ( $arrays as $array ) { + foreach ( $array as $key => $value ) { + if ( is_array( $value ) && ( isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) ) { + $merged[ $key ] = self::array_merge_recursive_distinct( $merged[ $key ], $value ); + } + else { + $merged[ $key ] = $value; + } + } + unset( $key, $value ); + } + return $merged; + } + + + } /* End of class */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/inc/class-wpseo-options.php b/wp-content/plugins/wordpress-seo/inc/class-wpseo-options.php new file mode 100644 index 0000000..6785931 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/inc/class-wpseo-options.php @@ -0,0 +1,4046 @@ +'theme_has_description' + * + * [Updating/Adding options] + * - For multisite site_options, please use the WPSEO_Options::update_site_option() method. + * - For normal options, use the normal add/update_option() functions. As long a the classes here + * are instantiated, validation for all options and their subkeys will be automatic. + * - On (succesfull) update of a couple of options, certain related actions will be run automatically. + * Some examples: + * - on change of wpseo[yoast_tracking], the cron schedule will be adjusted accordingly + * - on change of wpseo_permalinks and wpseo_xml, the rewrite rules will be flushed + * - on change of wpseo and wpseo_title, some caches will be cleared + * + * + * [Important information about add/updating/changing these classes] + * - Make sure that option array key names are unique across options. The WPSEO_Options::get_all() + * method merges most options together. If any of them have non-unique names, even if they + * are in a different option, they *will* overwrite each other. + * - When you add a new array key in an option: make sure you add proper defaults and add the key + * to the validation routine in the proper place or add a new validation case. + * You don't need to do any upgrading as any option returned will always be merged with the + * defaults, so new options will automatically be available. + * If the default value is a string which need translating, add this to the concrete class + * translate_defaults() method. + * - When you remove an array key from an option: if it's important that the option is really removed, + * add the WPSEO_Option::clean_up( $option_name ) method to the upgrade run. + * This will re-save the option and automatically remove the array key no longer in existance. + * - When you rename a sub-option: add it to the clean_option() routine and run that in the upgrade run. + * - When you change the default for an option sub-key, make sure you verify that the validation routine will + * still work the way it should. + * Example: changing a default from '' (empty string) to 'text' with a validation routine with tests + * for an empty string will prevent a user from saving an empty string as the real value. So the + * test for '' with the validation routine would have to be removed in that case. + * - If an option needs specific actions different from defined in this abstract class, you can just overrule + * a method by defining it in the concrete class. + * + * + * @todo - [JRF => testers] double check that validation will not cause errors when called + * from upgrade routine (some of the WP functions may not yet be available) + */ + abstract class WPSEO_Option { + + /** + * @var string Option name - MUST be set in concrete class and set to public. + */ + protected $option_name; + + /** + * @var string Option group name for use in settings forms + * - will be set automagically if not set in concrete class + * (i.e. if it confirm to the normal pattern 'yoast' . $option_name . 'options', + * only set in conrete class if it doesn't) + */ + public $group_name; + + /** + * @var bool Whether to include the option in the return for WPSEO_Options::get_all(). + * Also determines which options are copied over for ms_(re)set_blog(). + */ + public $include_in_all = true; + + /** + * @var bool Whether this option is only for when the install is multisite. + */ + public $multisite_only = false; + + /** + * @var array Array of defaults for the option - MUST be set in concrete class. + * Shouldn't be requested directly, use $this->get_defaults(); + */ + protected $defaults; + + /** + * @var array Array of variable option name patterns for the option - if any - + * Set this when the option contains array keys which vary based on post_type + * or taxonomy + */ + protected $variable_array_key_patterns; + + /** + * @var array Array of sub-options which should not be overloaded with multi-site defaults + */ + public $ms_exclude = array(); + + /** + * @var object Instance of this class + */ + protected static $instance; + + /** + * + * @var bool Whether the filter extension is loaded + */ + public static $has_filters = true; + + + /* *********** INSTANTIATION METHODS *********** */ + + /** + * Add all the actions and filters for the option + * + * @return \WPSEO_Option + */ + protected function __construct() { + + self::$has_filters = extension_loaded( 'filter' ); + + /* Add filters which get applied to the get_options() results */ + $this->add_default_filters(); // return defaults if option not set + $this->add_option_filters(); // merge with defaults if option *is* set + + + if ( $this->multisite_only !== true ) { + /* The option validation routines remove the default filters to prevent failing + to insert an option if it's new. Let's add them back afterwards. */ + add_action( 'add_option', array( $this, 'add_default_filters' ) ); // adding back after INSERT + + if ( version_compare( $GLOBALS['wp_version'], '3.7', '!=' ) ) { // adding back after non-WP 3.7 UPDATE + add_action( 'update_option', array( $this, 'add_default_filters' ) ); + } else { // adding back after WP 3.7 UPDATE + add_filter( 'pre_update_option_' . $this->option_name, array( $this, 'wp37_add_default_filters' ) ); + } + } + else if ( is_multisite() ) { + /* The option validation routines remove the default filters to prevent failing + to insert an option if it's new. Let's add them back afterwards. + + For site_options, this method is not foolproof as these actions are not fired + on an insert/update failure. Please use the WPSEO_Options::update_site_option() method + for updating site options to make sure the filters are in place. */ + add_action( 'add_site_option_' . $this->option_name, array( $this, 'add_default_filters' ) ); + add_action( 'update_site_option_' . $this->option_name, array( $this, 'add_default_filters' ) ); + } + + + + /* Make sure the option will always get validated, independently of register_setting() + (only available on back-end) */ + add_filter( 'sanitize_option_' . $this->option_name, array( $this, 'validate' ) ); + + /* Register our option for the admin pages */ + add_action( 'admin_init', array( $this, 'register_setting' ) ); + + + /* Set option group name if not given */ + if ( ! isset( $this->group_name ) || $this->group_name === '' ) { + $this->group_name = 'yoast_' . $this->option_name . '_options'; + } + + /* Translate some defaults as early as possible - textdomain is loaded in init on priority 1 */ + if ( method_exists( $this, 'translate_defaults' ) ) { + add_action( 'init', array( $this, 'translate_defaults' ), 2 ); + } + + /** + * Enrich defaults once custom post types and taxonomies have been registered + * which is normally done on the init action. + * + * @todo - [JRF/testers] verify that none of the options which are only available after + * enrichment are used before the enriching + */ + if ( method_exists( $this, 'enrich_defaults' ) ) { + add_action( 'init', array( $this, 'enrich_defaults' ), 99 ); + } + } + + + /** + * All concrete classes *must* contain the get_instance method + * @internal Unfortunately I can't define it as an abstract as it also *has* to be static.... + */ + //abstract protected static function get_instance(); + + + /** + * Concrete classes *may* contain a translate_defaults method + */ + //abstract public function translate_defaults(); + + + /** + * Concrete classes *may* contain a enrich_defaults method to add additional defaults once + * all post_types and taxonomies have been registered + */ + //abstract public function enrich_defaults(); + + + /* *********** METHODS INFLUENCING get_option() *********** */ + + /** + * Add filters to make sure that the option default is returned if the option is not set + * + * @return void + */ + public function add_default_filters() { + // Don't change, needs to check for false as could return prio 0 which would evaluate to false + if ( has_filter( 'default_option_' . $this->option_name, array( $this, 'get_defaults' ) ) === false ) { + add_filter( 'default_option_' . $this->option_name, array( $this, 'get_defaults' ) ); + } + } + + + /** + * Abusing a filter to re-add our default filters + * WP 3.7 specific as update_option action hook was in the wrong place temporarily + * @see http://core.trac.wordpress.org/ticket/25705 + * + * @param mixed $new_value + * + * @return mixed unchanged value + */ + public function wp37_add_default_filters( $new_value ) { + $this->add_default_filters(); + + return $new_value; + } + + + /** + * Remove the default filters. + * Called from the validate() method to prevent failure to add new options + * + * @return void + */ + public function remove_default_filters() { + remove_filter( 'default_option_' . $this->option_name, array( $this, 'get_defaults' ) ); + } + + + /** + * Get the enriched default value for an option + * + * Checks if the concrete class contains an enrich_defaults() method and if so, runs it. + * + * @internal the enrich_defaults method is used to set defaults for variable array keys in an option, + * such as array keys depending on post_types and/or taxonomies + * + * @return array + */ + public function get_defaults() { + if ( method_exists( $this, 'translate_defaults' ) ) { + $this->translate_defaults(); + } + + if ( method_exists( $this, 'enrich_defaults' ) ) { + $this->enrich_defaults(); + } + + return apply_filters( 'wpseo_defaults', $this->defaults, $this->option_name ); + } + + + /** + * Add filters to make sure that the option is merged with its defaults before being returned + * + * @return void + */ + public function add_option_filters() { + // Don't change, needs to check for false as could return prio 0 which would evaluate to false + if ( has_filter( 'option_' . $this->option_name, array( $this, 'get_option' ) ) === false ) { + add_filter( 'option_' . $this->option_name, array( $this, 'get_option' ) ); + } + } + + + /** + * Remove the option filters. + * Called from the clean_up methods to make sure we retrieve the original old option + * + * @return void + */ + public function remove_option_filters() { + remove_filter( 'option_' . $this->option_name, array( $this, 'get_option' ) ); + } + + + /** + * Merge an option with its default values + * + * This method should *not* be called directly!!! It is only meant to filter the get_option() results + * + * @param mixed $options Option value + * + * @return mixed Option merged with the defaults for that option + */ + public function get_option( $options = null ) { + $filtered = $this->array_filter_merge( $options ); + + /* If the option contains variable option keys, make sure we don't remove those settings + - even if the defaults are not complete yet. + Unfortunately this means we also won't be removing the settings for post types or taxonomies + which are no longer in the WP install, but rather that than the other way around */ + if ( isset( $this->variable_array_key_patterns ) ) { + $filtered = $this->retain_variable_keys( $options, $filtered ); + } + + return $filtered; + } + + + /* *********** METHODS influencing add_uption(), update_option() and saving from admin pages *********** */ + + /** + * Register (whitelist) the option for the configuration pages. + * The validation callback is already registered separately on the sanitize_option hook, + * so no need to double register. + * + * @return void + */ + public function register_setting() { + if ( WPSEO_Options::grant_access() ) { + register_setting( $this->group_name, $this->option_name ); + } + } + + + /** + * Validate the option + * + * @param mixed $option_value The unvalidated new value for the option + * + * @return array Validated new value for the option + */ + public function validate( $option_value ) { + $clean = $this->get_defaults(); + + /* Return the defaults if the new value is empty */ + if ( ! is_array( $option_value ) || $option_value === array() ) { + return $clean; + } + + + $option_value = array_map( array( __CLASS__, 'trim_recursive' ), $option_value ); + if ( $this->multisite_only !== true ) { + $old = get_option( $this->option_name ); + } + else { + $old = get_site_option( $this->option_name ); + } + $clean = $this->validate_option( $option_value, $clean, $old ); + + /* Retain the values for variable array keys even when the post type/taxonomy is not yet registered */ + if ( isset( $this->variable_array_key_patterns ) ) { + $clean = $this->retain_variable_keys( $option_value, $clean ); + } + + $this->remove_default_filters(); + + return $clean; + } + + + /** + * All concrete classes must contain a validate_option() method which validates all + * values within the option + */ + abstract protected function validate_option( $dirty, $clean, $old ); + + + /* *********** METHODS for ADDING/UPDATING/UPGRADING the option *********** */ + + /** + * Retrieve the real old value (unmerged with defaults) + * + * @return array|bool the original option value (which can be false if the option doesn't exist) + */ + protected function get_original_option() { + $this->remove_default_filters(); + $this->remove_option_filters(); + + // Get (unvalidated) array, NOT merged with defaults + if ( $this->multisite_only !== true ) { + $option_value = get_option( $this->option_name ); + } + else { + $option_value = get_site_option( $this->option_name ); + } + + $this->add_option_filters(); + $this->add_default_filters(); + + return $option_value; + } + + /** + * Add the option if it doesn't exist for some strange reason + * + * @uses WPSEO_Option::get_original_option() + * + * @return void + */ + public function maybe_add_option() { + if ( $this->get_original_option() === false ) { + if ( $this->multisite_only !== true ) { + update_option( $this->option_name, $this->get_defaults() ); + } + else { + $this->update_site_option( $this->get_defaults() ); + } + } + } + + + /** + * Update a site_option + * + * @internal This special method is only needed for multisite options, but very needed indeed there. + * The order in which certain functions and hooks are run is different between get_option() and + * get_site_option() which means in practice that the removing of the default filters would be + * done too late and the re-adding of the default filters might not be done at all. + * Aka: use the WPSEO_Options::update_site_option() method (which calls this method) for + * safely adding/updating multisite options. + * + * @return bool whether the update was succesfull + */ + public function update_site_option( $value ) { + if ( $this->multisite_only === true && is_multisite() ) { + $this->remove_default_filters(); + $result = update_site_option( $this->option_name, $value ); + $this->add_default_filters(); + return $result; + } + else { + return false; + } + } + + + /** + * Retrieve the real old value (unmerged with defaults), clean and re-save the option + * + * @uses WPSEO_Option::get_original_option() + * @uses WPSEO_Option::import() + * + * @param string $current_version (optional) Version from which to upgrade, if not set, + * version specific upgrades will be disregarded + * + * @return void + */ + public function clean( $current_version = null ) { + $option_value = $this->get_original_option(); + $this->import( $option_value, $current_version ); + } + + + /** + * Clean and re-save the option + * @uses clean_option() method from concrete class if it exists + * + * @todo [JRF/whomever] Figure out a way to show settings error during/after the upgrade - maybe + * something along the lines of: + * -> add them to a property in this class + * -> if that property isset at the end of the routine and add_settings_error function does not exist, + * save as transient (or update the transient if one already exists) + * -> next time an admin is in the WP back-end, show the errors and delete the transient or only delete it + * once the admin has dismissed the message (add ajax function) + * Important: all validation routines which add_settings_errors would need to be changed for this to work + * + * @param array $option_value Option value to be imported + * @param string $current_version (optional) Version from which to upgrade, if not set, + * version specific upgrades will be disregarded + * @param array $all_old_option_values (optional) Only used when importing old options to have + * access to the real old values, in contrast to the saved ones + * + * @return void + */ + public function import( $option_value, $current_version = null, $all_old_option_values = null ) { + if ( $option_value === false ) { + $option_value = $this->get_defaults(); + } elseif ( is_array( $option_value ) && method_exists( $this, 'clean_option' ) ) { + $option_value = $this->clean_option( $option_value, $current_version, $all_old_option_values ); + } + + /* Save the cleaned value - validation will take care of cleaning out array keys which + should no longer be there */ + if ( $this->multisite_only !== true ) { + update_option( $this->option_name, $option_value ); + } + else { + $this->update_site_option( $this->option_name, $option_value ); + } + } + + + /** + * Concrete classes *may* contain a clean_option method which will clean out old/renamed + * values within the option + */ + //abstract public function clean_option( $option_value, $current_version = null, $all_old_option_values = null ); + + + /* *********** HELPER METHODS for internal use *********** */ + + /** + * Helper method - Combines a fixed array of default values with an options array + * while filtering out any keys which are not in the defaults array. + * + * @todo [JRF] - shouldn't this be a straight array merge ? at the end of the day, the validation + * removes any invalid keys on save + * + * @param array $options (Optional) Current options + * - if not set, the option defaults for the $option_key will be returned. + * + * @return array Combined and filtered options array. + */ + protected function array_filter_merge( $options = null ) { + + $defaults = $this->get_defaults(); + + if ( ! isset( $options ) || $options === false || $options === array() ) { + return $defaults; + } + + $options = (array) $options; + /*$filtered = array(); + + if ( $defaults !== array() ) { + foreach ( $defaults as $key => $default_value ) { + // @todo should this walk through array subkeys ? + $filtered[ $key ] = ( isset( $options[ $key ] ) ? $options[ $key ] : $default_value ); + } + }*/ + $filtered = array_merge( $defaults, $options ); + + return $filtered; + } + + + /** + * Make sure that any set option values relating to post_types and/or taxonomies are retained, + * even when that post_type or taxonomy may not yet have been registered. + * + * @internal The wpseo_titles concrete class overrules this method. Make sure that any changes + * applied here, also get ported to that version. + * + * @param array $dirty Original option as retrieved from the database + * @param array $clean Filtered option where any options which shouldn't be in our option + * have already been removed and any options which weren't set + * have been set to their defaults + * + * @return array + */ + protected function retain_variable_keys( $dirty, $clean ) { + if ( ( is_array( $this->variable_array_key_patterns ) && $this->variable_array_key_patterns !== array() ) && ( is_array( $dirty ) && $dirty !== array() ) ) { + foreach ( $dirty as $key => $value ) { + foreach ( $this->variable_array_key_patterns as $pattern ) { + if ( strpos( $key, $pattern ) === 0 && ! isset( $clean[ $key ] ) ) { + $clean[ $key ] = $value; + break; + } + } + } + } + + return $clean; + } + + + /** + * Check whether a given array key conforms to one of the variable array key patterns for this option + * + * @usedby validate_option() methods for options with variable array keys + * + * @param string $key Array key to check + * + * @return string Pattern if it conforms, original array key if it doesn't or if the option + * does not have variable array keys + */ + protected function get_switch_key( $key ) { + if ( ! isset( $this->variable_array_key_patterns ) || ( ! is_array( $this->variable_array_key_patterns ) || $this->variable_array_key_patterns === array() ) ) { + return $key; + } + + foreach ( $this->variable_array_key_patterns as $pattern ) { + if ( strpos( $key, $pattern ) === 0 ) { + return $pattern; + } + } + + return $key; + } + + + /* *********** GENERIC HELPER METHODS *********** */ + + /** + * Emulate the WP native sanitize_text_field function in a %%variable%% safe way + * @see https://core.trac.wordpress.org/browser/trunk/src/wp-includes/formatting.php for the original + * + * Sanitize a string from user input or from the db + * + * check for invalid UTF-8, + * Convert single < characters to entity, + * strip all tags, + * remove line breaks, tabs and extra white space, + * strip octets - BUT DO NOT REMOVE (part of) VARIABLES WHICH WILL BE REPLACED. + * + * @param string $value + * + * @return string + */ + public static function sanitize_text_field( $value ) { + $filtered = wp_check_invalid_utf8( $value ); + + if ( strpos( $filtered, '<' ) !== false ) { + $filtered = wp_pre_kses_less_than( $filtered ); + // This will strip extra whitespace for us. + $filtered = wp_strip_all_tags( $filtered, true ); + } else { + $filtered = trim( preg_replace( '`[\r\n\t ]+`', ' ', $filtered ) ); + } + + $found = false; + while ( preg_match( '`[^%](%[a-f0-9]{2})`i', $filtered, $match ) ) { + $filtered = str_replace( $match[1], '', $filtered ); + $found = true; + } + + if ( $found ) { + // Strip out the whitespace that may now exist after removing the octets. + $filtered = trim( preg_replace( '` +`', ' ', $filtered ) ); + } + + /** + * Filter a sanitized text field string. + * + * @since WP 2.9.0 + * + * @param string $filtered The sanitized string. + * @param string $str The string prior to being sanitized. + */ + return apply_filters( 'sanitize_text_field', $filtered, $value ); + } + + + /** + * Sanitize a url for saving to the database + * Not to be confused with the old native WP function + * + * @todo [JRF => whomever] check/improve url verification + * + * @todo [JRF => whomever] when someone would reorganize the classes, this should maybe + * be moved to a general WPSEO_Utils class. Obviously all calls to this method should be + * adjusted in that case. + * + * @param string $value + * @param array $allowed_protocols + * + * @return string + */ + public static function sanitize_url( $value, $allowed_protocols = array( 'http', 'https' ) ) { + return esc_url_raw( sanitize_text_field( rawurldecode( $value ) ), $allowed_protocols ); + } + + /** + * Validate a value as boolean + * + * @todo [JRF => whomever] when someone would reorganize the classes, this (and the emulate method + * below) should maybe be moved to a general WPSEO_Utils class. Obviously all calls to this method + * should be adjusted in that case. + * + * @static + * + * @param mixed $value + * + * @return bool + */ + public static function validate_bool( $value ) { + if ( self::$has_filters ) { + return filter_var( $value, FILTER_VALIDATE_BOOLEAN ); + } else { + return self::emulate_filter_bool( $value ); + } + } + + /** + * Cast a value to bool + * + * @static + * + * @param mixed $value Value to cast + * + * @return bool + */ + public static function emulate_filter_bool( $value ) { + $true = array( + '1', + 'true', + 'True', + 'TRUE', + 'y', + 'Y', + 'yes', + 'Yes', + 'YES', + 'on', + 'On', + 'On', + + ); + $false = array( + '0', + 'false', + 'False', + 'FALSE', + 'n', + 'N', + 'no', + 'No', + 'NO', + 'off', + 'Off', + 'OFF', + ); + + if ( is_bool( $value ) ) { + return $value; + } else if ( is_int( $value ) && ( $value === 0 || $value === 1 ) ) { + return (bool) $value; + } else if ( ( is_float( $value ) && ! is_nan( $value ) ) && ( $value === (float) 0 || $value === (float) 1 ) ) { + return (bool) $value; + } else if ( is_string( $value ) ) { + $value = trim( $value ); + if ( in_array( $value, $true, true ) ) { + return true; + } else if ( in_array( $value, $false, true ) ) { + return false; + } else { + return false; + } + } + + return false; + } + + + /** + * Validate a value as integer + * + * @todo [JRF => whomever] when someone would reorganize the classes, this (and the emulate method + * below) should maybe be moved to a general WPSEO_Utils class. Obviously all calls to this method + * should be adjusted in that case. + * + * @static + * + * @param mixed $value + * + * @return mixed int or false in case of failure to convert to int + */ + public static function validate_int( $value ) { + if ( self::$has_filters ) { + return filter_var( $value, FILTER_VALIDATE_INT ); + } else { + return self::emulate_filter_int( $value ); + } + } + + /** + * Cast a value to integer + * + * @static + * + * @param mixed $value Value to cast + * + * @return int|bool + */ + public static function emulate_filter_int( $value ) { + if ( is_int( $value ) ) { + return $value; + } else if ( is_float( $value ) ) { + if ( (int) $value == $value && ! is_nan( $value ) ) { + return (int) $value; + } else { + return false; + } + } else if ( is_string( $value ) ) { + $value = trim( $value ); + if ( $value === '' ) { + return false; + } else if ( ctype_digit( $value ) ) { + return (int) $value; + } else if ( strpos( $value, '-' ) === 0 && ctype_digit( substr( $value, 1 ) ) ) { + return (int) $value; + } else { + return false; + } + } + + return false; + } + + + /** + * Recursively trim whitespace round a string value or of string values within an array + * Only trims strings to avoid typecasting a variable (to string) + * + * @todo [JRF => whomever] when someone would reorganize the classes, this should maybe + * be moved to a general WPSEO_Utils class. Obviously all calls to this method should be + * adjusted in that case. + * + * @static + * + * @param mixed $value Value to trim or array of values to trim + * + * @return mixed Trimmed value or array of trimmed values + */ + public static function trim_recursive( $value ) { + if ( is_string( $value ) ) { + $value = trim( $value ); + } elseif ( is_array( $value ) ) { + $value = array_map( array( __CLASS__, 'trim_recursive' ), $value ); + } + + return $value; + } + + } /* End of class WPSEO_Option */ + +} /* End of class-exists wrapper */ + + +/******************************************************************* + * Option: wpseo + *******************************************************************/ +if ( ! class_exists( 'WPSEO_Option_Wpseo' ) ) { + + class WPSEO_Option_Wpseo extends WPSEO_Option { + + /** + * @var string option name + */ + public $option_name = 'wpseo'; + + /** + * @var array Array of defaults for the option + * Shouldn't be requested directly, use $this->get_defaults(); + */ + protected $defaults = array( + // Non-form fields, set via (ajax) function + 'blocking_files' => array(), + 'ignore_blog_public_warning' => false, + 'ignore_meta_description_warning' => null, // overwrite in __construct() + 'ignore_page_comments' => false, + 'ignore_permalink' => false, + 'ignore_tour' => false, + 'ms_defaults_set' => false, + 'theme_description_found' => null, // overwrite in __construct() + 'theme_has_description' => null, // overwrite in __construct() + 'tracking_popup_done' => false, + // Non-form field, should only be set via validation routine + 'version' => '', // leave default as empty to ensure activation/upgrade works + + // Form fields: + 'alexaverify' => '', // text field + 'disableadvanced_meta' => true, + 'googleverify' => '', // text field + 'msverify' => '', // text field + 'pinterestverify' => '', + 'yandexverify' => '', + 'yoast_tracking' => false, + ); + + public static $desc_defaults = array( + 'ignore_meta_description_warning' => false, + 'theme_description_found' => '', // text string description + 'theme_has_description' => null, + ); + + /** + * @var array Array of sub-options which should not be overloaded with multi-site defaults + */ + public $ms_exclude = array( + 'ignore_blog_public_warning', + 'ignore_meta_description_warning', + 'ignore_page_comments', + 'ignore_permalink', + 'ignore_tour', + + /* theme dependent */ + 'theme_description_found', + 'theme_has_description', + + /* privacy */ + 'alexaverify', + 'googleverify', + 'msverify', + 'pinterestverify', + 'yandexverify', + ); + + + /** + * Add the actions and filters for the option + * + * @todo [JRF => testers] Check if the extra actions below would run into problems if an option + * is updated early on and if so, change the call to schedule these for a later action on add/update + * instead of running them straight away + * + * @return \WPSEO_Option_Wpseo + */ + protected function __construct() { + /* Dirty fix for making certain defaults available during activation while still only + defining them once */ + foreach ( self::$desc_defaults as $key => $value ) { + $this->defaults[ $key ] = $value; + } + + parent::__construct(); + + /* Clear the cache on update/add */ + add_action( 'add_option_' . $this->option_name, array( 'WPSEO_Options', 'clear_cache' ) ); + add_action( 'update_option_' . $this->option_name, array( 'WPSEO_Options', 'clear_cache' ) ); + + + /* Check if the yoast tracking cron job needs adding/removing on successfull option add/update */ + add_action( 'add_option_' . $this->option_name, array( + 'WPSEO_Options', + 'schedule_yoast_tracking', + ), 15, 2 ); + add_action( 'update_option_' . $this->option_name, array( + 'WPSEO_Options', + 'schedule_yoast_tracking', + ), 15, 2 ); + } + + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + + return self::$instance; + } + + + /** + * Validate the option + * + * @param array $dirty New value for the option + * @param array $clean Clean value for the option, normally the defaults + * @param array $old Old value of the option + * + * @return array Validated clean value for the option to be saved to the database + */ + protected function validate_option( $dirty, $clean, $old ) { + + foreach ( $clean as $key => $value ) { + switch ( $key ) { + case 'version': + $clean[ $key ] = WPSEO_VERSION; + break; + + + case 'blocking_files': + /* @internal [JRF] to really validate this we should also do a file_exists() + * on each array entry and remove files which no longer exist, but that might be overkill */ + if ( isset( $dirty[ $key ] ) && is_array( $dirty[ $key ] ) ) { + $clean[ $key ] = array_unique( $dirty[ $key ] ); + } elseif ( isset( $old[ $key ] ) && is_array( $old[ $key ] ) ) { + $clean[ $key ] = array_unique( $old[ $key ] ); + } + break; + + + case 'theme_description_found': + if ( isset( $dirty[ $key ] ) && is_string( $dirty[ $key ] ) ) { + $clean[ $key ] = $dirty[ $key ]; // @todo [JRF/whomever] maybe do wp_kses ? + } elseif ( isset( $old[ $key ] ) && is_string( $old[ $key ] ) ) { + $clean[ $key ] = $old[ $key ]; + } + break; + + + /* text fields */ + case 'alexaverify': + case 'googleverify': + case 'msverify': + case 'pinterestverify': + case 'yandexverify': + if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) { + $meta = $dirty[ $key ]; + if ( strpos( $meta, 'content=' ) ) { + // Make sure we only have the real key, not a complete meta tag + preg_match( '`content=([\'"])?([^\'"> ]+)(?:\1|[ />])`', $meta, $match ); + if ( isset( $match[2] ) ) { + $meta = $match[2]; + } + unset( $match ); + } + + $meta = sanitize_text_field( $meta ); + if ( $meta !== '' ) { + $regex = '`^[A-Fa-f0-9_-]+$`'; + $service = ''; + + switch ( $key ) { + case 'googleverify': + $regex = '`^[A-Za-z0-9_-]+$`'; + $service = 'Google Webmaster tools'; + break; + + case 'msverify': + $service = 'Bing Webmaster tools'; + break; + + case 'pinterestverify': + $service = 'Pinterest'; + break; + + case 'yandexverify': + $service = 'Yandex Webmaster tools'; + break; + + case 'alexaverify': + $regex = '`^[A-Za-z0-9_-]{20,}$`'; + $service = 'Alexa ID'; + } + + if ( preg_match( $regex, $meta ) ) { + $clean[ $key ] = $meta; + } else { + if ( isset( $old[ $key ] ) && preg_match( $regex, $old[ $key ] ) ) { + $clean[ $key ] = $old[ $key ]; + } + if ( function_exists( 'add_settings_error' ) ) { + add_settings_error( + $this->group_name, // slug title of the setting + '_' . $key, // suffix-id for the error message box + sprintf( __( '%s does not seem to be a valid %s verification string. Please correct.', 'wordpress-seo' ), '' . esc_html( $meta ) . '', $service ), // the error message + 'error' // error type, either 'error' or 'updated' + ); + } + } + } + unset( $meta, $regex, $service ); + } + break; + + + /* boolean|null fields - if set a check was done, if null, it hasn't */ + case 'theme_has_description': + if ( isset( $dirty[ $key ] ) ) { + $clean[ $key ] = self::validate_bool( $dirty[ $key ] ); + } elseif ( isset( $old[ $key ] ) ) { + $clean[ $key ] = self::validate_bool( $old[ $key ] ); + } + break; + + + /* boolean dismiss warnings - not fields - may not be in form + (and don't need to be either as long as the default is false) */ + case 'ignore_blog_public_warning': + case 'ignore_meta_description_warning': + case 'ignore_page_comments': + case 'ignore_permalink': + case 'ignore_tour': + case 'ms_defaults_set': + case 'tracking_popup_done': + if ( isset( $dirty[ $key ] ) ) { + $clean[ $key ] = self::validate_bool( $dirty[ $key ] ); + } elseif ( isset( $old[ $key ] ) ) { + $clean[ $key ] = self::validate_bool( $old[ $key ] ); + } + break; + + + /* boolean (checkbox) fields */ + case 'disableadvanced_meta': + case 'yoast_tracking': + default: + $clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false ); + break; + } + } + + return $clean; + } + + + /** + * Clean a given option value + * + * @param array $option_value Old (not merged with defaults or filtered) option value to + * clean according to the rules for this option + * @param string $current_version (optional) Version from which to upgrade, if not set, + * version specific upgrades will be disregarded + * @param array $all_old_option_values (optional) Only used when importing old options to have + * access to the real old values, in contrast to the saved ones + * + * @return array Cleaned option + */ + protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) { + + // Rename some options *and* change their value + $rename = array( + 'presstrends' => array( + 'new_name' => 'yoast_tracking', + 'new_value' => true, + ), + 'presstrends_popup' => array( + 'new_name' => 'tracking_popup_done', + 'new_value' => true, + ), + ); + foreach ( $rename as $old => $new ) { + if ( isset( $option_value[ $old ] ) && ! isset( $option_value[ $new['new_name'] ] ) ) { + $option_value[ $new['new_name'] ] = $new['new_value']; + unset( $option_value[ $old ] ); + } + } + unset( $rename, $old, $new ); + + + // Deal with renaming of some options without losing the settings + $rename = array( + 'tracking_popup' => 'tracking_popup_done', + 'meta_description_warning' => 'ignore_meta_description_warning', + ); + foreach ( $rename as $old => $new ) { + if ( isset( $option_value[ $old ] ) && ! isset( $option_value[ $new ] ) ) { + $option_value[ $new ] = $option_value[ $old ]; + unset( $option_value[ $old ] ); + } + } + unset( $rename, $old, $new ); + + + // Change a array sub-option to two straight sub-options + if ( isset( $option_value['theme_check']['description'] ) && ! isset( $option_value['theme_has_description'] ) ) { + // @internal the negate is by design! + $option_value['theme_has_description'] = ! $option_value['theme_check']['description']; + } + if ( isset( $option_values['theme_check']['description_found'] ) && ! isset( $option_value['theme_description_found'] ) ) { + $option_value['theme_description_found'] = $option_value['theme_check']['description_found']; + } + + + // Deal with value change from text string to boolean + $value_change = array( + 'ignore_blog_public_warning', + 'ignore_meta_description_warning', + 'ignore_page_comments', + 'ignore_permalink', + 'ignore_tour', + //'disableadvanced_meta', => not needed as is 'on' which will auto-convert to true + 'tracking_popup_done', + ); + foreach ( $value_change as $key ) { + if ( isset( $option_value[ $key ] ) && in_array( $option_value[ $key ], array( + 'ignore', + 'done', + ), true ) + ) { + $option_value[ $key ] = true; + } + } + + return $option_value; + } + + } /* End of class WPSEO_Option_Wpseo */ + +} /* End of class-exists wrapper */ + + +/******************************************************************* + * Option: wpseo_permalinks + *******************************************************************/ +if ( ! class_exists( 'WPSEO_Option_Permalinks' ) ) { + + /** + * @internal Clean routine for 1.5 not needed as values used to be saved as string 'on' and those will convert + * automatically + */ + class WPSEO_Option_Permalinks extends WPSEO_Option { + + /** + * @var string option name + */ + public $option_name = 'wpseo_permalinks'; + + /** + * @var array Array of defaults for the option + * Shouldn't be requested directly, use $this->get_defaults(); + */ + protected $defaults = array( + 'cleanpermalinks' => false, + 'cleanpermalink-extravars' => '', // text field + 'cleanpermalink-googlecampaign' => false, + 'cleanpermalink-googlesitesearch' => false, + 'cleanreplytocom' => false, + 'cleanslugs' => true, + 'force_transport' => 'default', + 'redirectattachment' => false, + 'stripcategorybase' => false, + 'trailingslash' => false, + ); + + + /** + * @static + * @var array $force_transport_options Available options for the force_transport setting + * Used for input validation + * + * @internal Important: Make sure the options added to the array here are in line with the keys + * for the options set for the select box in the admin/pages/permalinks.php file + */ + public static $force_transport_options = array( + 'default', // = leave as-is + 'http', + 'https', + ); + + + /** + * Add the actions and filters for the option + * + * @todo [JRF => testers] Check if the extra actions below would run into problems if an option + * is updated early on and if so, change the call to schedule these for a later action on add/update + * instead of running them straight away + * + * @return \WPSEO_Option_Permalinks + */ + protected function __construct() { + parent::__construct(); + add_action( 'update_option_' . $this->option_name, array( 'WPSEO_Options', 'clear_rewrites' ) ); + } + + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + + return self::$instance; + } + + + /** + * Validate the option + * + * @param array $dirty New value for the option + * @param array $clean Clean value for the option, normally the defaults + * @param array $old Old value of the option (not used here as all fields will always be in the form) + * + * @return array Validated clean value for the option to be saved to the database + */ + protected function validate_option( $dirty, $clean, $old ) { + + foreach ( $clean as $key => $value ) { + switch ( $key ) { + case 'force_transport': + if ( isset( $dirty[ $key ] ) && in_array( $dirty[ $key ], self::$force_transport_options, true ) ) { + $clean[ $key ] = $dirty[ $key ]; + } else { + if ( isset( $old[ $key ] ) && in_array( $old[ $key ], self::$force_transport_options, true ) ) { + $clean[ $key ] = $old[ $key ]; + } + if ( function_exists( 'add_settings_error' ) ) { + add_settings_error( + $this->group_name, // slug title of the setting + '_' . $key, // suffix-id for the error message box + __( 'Invalid transport mode set for the canonical settings. Value reset to default.', 'wordpress-seo' ), // the error message + 'error' // error type, either 'error' or 'updated' + ); + } + } + break; + + /* text fields */ + case 'cleanpermalink-extravars': + if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) { + $clean[ $key ] = sanitize_text_field( $dirty[ $key ] ); + } + break; + + /* boolean (checkbox) fields */ + case 'cleanpermalinks': + case 'cleanpermalink-googlesitesearch': + case 'cleanpermalink-googlecampaign': + case 'cleanreplytocom': + case 'cleanslugs': + case 'redirectattachment': + case 'stripcategorybase': + case 'trailingslash': + default: + $clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false ); + break; + } + } + + return $clean; + } + + + /** + * Clean a given option value + * + * @param array $option_value Old (not merged with defaults or filtered) option value to + * clean according to the rules for this option + * @param string $current_version (optional) Version from which to upgrade, if not set, + * version specific upgrades will be disregarded + * @param array $all_old_option_values (optional) Only used when importing old options to have + * access to the real old values, in contrast to the saved ones + * + * @return array Cleaned option + */ + /*protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) { + + return $option_value; + }*/ + + + } /* End of class WPSEO_Option_Permalinks */ + +} /* End of class-exists wrapper */ + + +/******************************************************************* + * Option: wpseo_titles + *******************************************************************/ +if ( ! class_exists( 'WPSEO_Option_Titles' ) ) { + + class WPSEO_Option_Titles extends WPSEO_Option { + + /** + * @var string option name + */ + public $option_name = 'wpseo_titles'; + + /** + * @var array Array of defaults for the option + * Shouldn't be requested directly, use $this->get_defaults(); + * @internal Note: Some of the default values are added via the translate_defaults() method + */ + protected $defaults = array( + // Non-form fields, set via (ajax) function + 'title_test' => 0, + // Form fields + 'forcerewritetitle' => false, + 'separator' => 'sc-dash', + 'hide-feedlinks' => false, + 'hide-rsdlink' => false, + 'hide-shortlink' => false, + 'hide-wlwmanifest' => false, + 'noodp' => false, + 'noydir' => false, + 'usemetakeywords' => false, + 'title-home-wpseo' => '%%sitename%% %%page%% %%sep%% %%sitedesc%%', // text field + 'title-author-wpseo' => '', // text field + 'title-archive-wpseo' => '%%date%% %%page%% %%sep%% %%sitename%%', // text field + 'title-search-wpseo' => '', // text field + 'title-404-wpseo' => '', // text field + + 'metadesc-home-wpseo' => '', // text area + 'metadesc-author-wpseo' => '', // text area + 'metadesc-archive-wpseo' => '', // text area + + 'metakey-home-wpseo' => '', // text field + 'metakey-author-wpseo' => '', // text field + + 'noindex-subpages-wpseo' => false, + 'noindex-author-wpseo' => false, + 'noindex-archive-wpseo' => true, + 'disable-author' => false, + 'disable-date' => false, + + + /** + * Uses enrich_defaults to add more along the lines of: + * - 'title-' . $pt->name => ''; // text field + * - 'metadesc-' . $pt->name => ''; // text field + * - 'metakey-' . $pt->name => ''; // text field + * - 'noindex-' . $pt->name => false; + * - 'showdate-' . $pt->name => false; + * - 'hideeditbox-' . $pt->name => false; + * + * - 'title-ptarchive-' . $pt->name => ''; // text field + * - 'metadesc-ptarchive-' . $pt->name => ''; // text field + * - 'metakey-ptarchive-' . $pt->name => ''; // text field + * - 'bctitle-ptarchive-' . $pt->name => ''; // text field + * - 'noindex-ptarchive-' . $pt->name => false; + * + * - 'title-tax-' . $tax->name => '''; // text field + * - 'metadesc-tax-' . $tax->name => ''; // text field + * - 'metakey-tax-' . $tax->name => ''; // text field + * - 'noindex-tax-' . $tax->name => false; + * - 'hideeditbox-tax-' . $tax->name => false; + */ + ); + + /** + * @var array Array of variable option name patterns for the option + */ + protected $variable_array_key_patterns = array( + 'title-', + 'metadesc-', + 'metakey-', + 'noindex-', + 'showdate-', + 'hideeditbox-', + 'bctitle-ptarchive-', + ); + + /** + * @var array Array of sub-options which should not be overloaded with multi-site defaults + */ + public $ms_exclude = array( + /* theme dependent */ + 'title_test', + 'forcerewritetitle', + ); + + /** + * @var array Array of the separator options. To get these options use WPSEO_Option_Titles::get_instance()->get_separator_options() + */ + private $separator_options = array( + 'sc-dash' => '-', + 'sc-ndash' => '–', + 'sc-mdash' => '—', + 'sc-middot' => '·', + 'sc-bull' => '•', + 'sc-star' => '*', + 'sc-smstar' => '⋆', + 'sc-pipe' => '|', + 'sc-tilde' => '~', + 'sc-laquo' => '«', + 'sc-raquo' => '»', + 'sc-lt' => '<', + 'sc-gt' => '>', + ); + + /** + * Add the actions and filters for the option + * + * @todo [JRF => testers] Check if the extra actions below would run into problems if an option + * is updated early on and if so, change the call to schedule these for a later action on add/update + * instead of running them straight away + * + * @return \WPSEO_Option_Titles + */ + protected function __construct() { + parent::__construct(); + add_action( 'update_option_' . $this->option_name, array( 'WPSEO_Options', 'clear_cache' ) ); + add_action( 'init', array( $this, 'end_of_init' ), 999 ); + } + + + /** + * Make sure we can recognize the right action for the double cleaning + */ + public function end_of_init() { + do_action( 'wpseo_double_clean_titles' ); + } + + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Get the available separator options + * + * @return array + */ + public function get_separator_options() { + $separators = $this->separator_options; + + /** + * Allow altering the array with separator options + * @api array $separator_options Array with the separator options + */ + $filtered_separators = apply_filters( 'wpseo_separator_options', $separators ); + + if ( is_array( $filtered_separators ) && $filtered_separators !== array() ) { + $separators = array_merge( $separators, $filtered_separators ); + } + + return $separators; + } + + /** + * Translate strings used in the option defaults + * + * @return void + */ + public function translate_defaults() { + $this->defaults['title-author-wpseo'] = sprintf( __( '%s, Author at %s', 'wordpress-seo' ), '%%name%%', '%%sitename%%' ) . ' %%page%% '; + $this->defaults['title-search-wpseo'] = sprintf( __( 'You searched for %s', 'wordpress-seo' ), '%%searchphrase%%' ) . ' %%page%% %%sep%% %%sitename%%'; + $this->defaults['title-404-wpseo'] = __( 'Page Not Found', 'wordpress-seo' ) . ' %%sep%% %%sitename%%'; + } + + + /** + * Add dynamically created default options based on available post types and taxonomies + * + * @return void + */ + public function enrich_defaults() { + + // Retrieve all the relevant post type and taxonomy arrays + $post_type_names = get_post_types( array( 'public' => true ), 'names' ); + + $post_type_objects_custom = get_post_types( array( 'public' => true, '_builtin' => false ), 'objects' ); + + $taxonomy_names = get_taxonomies( array( 'public' => true ), 'names' ); + + + if ( $post_type_names !== array() ) { + foreach ( $post_type_names as $pt ) { + $this->defaults[ 'title-' . $pt ] = '%%title%% %%page%% %%sep%% %%sitename%%'; // text field + $this->defaults[ 'metadesc-' . $pt ] = ''; // text area + $this->defaults[ 'metakey-' . $pt ] = ''; // text field + $this->defaults[ 'noindex-' . $pt ] = false; + $this->defaults[ 'showdate-' . $pt ] = false; + $this->defaults[ 'hideeditbox-' . $pt ] = false; + } + unset( $pt ); + } + + if ( $post_type_objects_custom !== array() ) { + foreach ( $post_type_objects_custom as $pt ) { + if ( ! $pt->has_archive ) { + continue; + } + + $this->defaults[ 'title-ptarchive-' . $pt->name ] = sprintf( __( '%s Archive', 'wordpress-seo' ), '%%pt_plural%%' ) . ' %%page%% %%sep%% %%sitename%%'; // text field + $this->defaults[ 'metadesc-ptarchive-' . $pt->name ] = ''; // text area + $this->defaults[ 'metakey-ptarchive-' . $pt->name ] = ''; // text field + $this->defaults[ 'bctitle-ptarchive-' . $pt->name ] = ''; // text field + $this->defaults[ 'noindex-ptarchive-' . $pt->name ] = false; + } + unset( $pt ); + } + + if ( $taxonomy_names !== array() ) { + foreach ( $taxonomy_names as $tax ) { + $this->defaults[ 'title-tax-' . $tax ] = sprintf( __( '%s Archives', 'wordpress-seo' ), '%%term_title%%' ) . ' %%page%% %%sep%% %%sitename%%'; // text field + $this->defaults[ 'metadesc-tax-' . $tax ] = ''; // text area + $this->defaults[ 'metakey-tax-' . $tax ] = ''; // text field + $this->defaults[ 'hideeditbox-tax-' . $tax ] = false; + + if ( $tax !== 'post_format' ) { + $this->defaults[ 'noindex-tax-' . $tax ] = false; + } else { + $this->defaults[ 'noindex-tax-' . $tax ] = true; + } + } + unset( $tax ); + } + } + + + /** + * Validate the option + * + * @param array $dirty New value for the option + * @param array $clean Clean value for the option, normally the defaults + * @param array $old Old value of the option + * + * @return array Validated clean value for the option to be saved to the database + */ + protected function validate_option( $dirty, $clean, $old ) { + + foreach ( $clean as $key => $value ) { + $switch_key = $this->get_switch_key( $key ); + + switch ( $switch_key ) { + /* text fields */ + /* Covers: + 'title-home-wpseo', 'title-author-wpseo', 'title-archive-wpseo', + 'title-search-wpseo', 'title-404-wpseo' + 'title-' . $pt->name + 'title-ptarchive-' . $pt->name + 'title-tax-' . $tax->name */ + case 'title-': + if ( isset( $dirty[ $key ] ) ) { + $clean[ $key ] = self::sanitize_text_field( $dirty[ $key ] ); + } + break; + + /* Covers: + 'metadesc-home-wpseo', 'metadesc-author-wpseo', 'metadesc-archive-wpseo' + 'metadesc-' . $pt->name + 'metadesc-ptarchive-' . $pt->name + 'metadesc-tax-' . $tax->name */ + case 'metadesc-': + /* Covers: + 'metakey-home-wpseo', 'metakey-author-wpseo' + 'metakey-' . $pt->name + 'metakey-ptarchive-' . $pt->name + 'metakey-tax-' . $tax->name */ + case 'metakey-': + /* Covers: + ''bctitle-ptarchive-' . $pt->name */ + case 'bctitle-ptarchive-': + if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) { + $clean[ $key ] = self::sanitize_text_field( $dirty[ $key ] ); + } + break; + + + /* integer field - not in form*/ + case 'title_test': + if ( isset( $dirty[ $key ] ) ) { + $int = self::validate_int( $dirty[ $key ] ); + if ( $int !== false && $int >= 0 ) { + $clean[ $key ] = $int; + } + } elseif ( isset( $old[ $key ] ) ) { + $int = self::validate_int( $old[ $key ] ); + if ( $int !== false && $int >= 0 ) { + $clean[ $key ] = $int; + } + } + break; + + /* Separator field - Radio */ + case 'separator': + if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) { + + // Get separator fields + $separator_fields = $this->get_separator_options(); + + // Check if the given separator is exists + if ( isset( $separator_fields[ $dirty[ $key ] ] ) ) { + $clean[ $key ] = $dirty[ $key ]; + } + } + break; + + /* boolean fields */ + case 'forcerewritetitle': + case 'usemetakeywords': + case 'noodp': + case 'noydir': + case 'hide-rsdlink': + case 'hide-wlwmanifest': + case 'hide-shortlink': + case 'hide-feedlinks': + case 'disable-author': + case 'disable-date': + /* Covers: + 'noindex-subpages-wpseo', 'noindex-author-wpseo', 'noindex-archive-wpseo' + 'noindex-' . $pt->name + 'noindex-ptarchive-' . $pt->name + 'noindex-tax-' . $tax->name */ + case 'noindex-': + case 'showdate-': /* 'showdate-'. $pt->name */ + /* Covers: + 'hideeditbox-'. $pt->name + 'hideeditbox-tax-' . $tax->name */ + case 'hideeditbox-': + default: + $clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false ); + break; + } + } + + return $clean; + } + + + /** + * Clean a given option value + * + * @param array $option_value Old (not merged with defaults or filtered) option value to + * clean according to the rules for this option + * @param string $current_version (optional) Version from which to upgrade, if not set, + * version specific upgrades will be disregarded + * @param array $all_old_option_values (optional) Only used when importing old options to have + * access to the real old values, in contrast to the saved ones + * + * @return array Cleaned option + */ + protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) { + static $original = null; + + // Double-run this function to ensure renaming of the taxonomy options will work + if ( ! isset( $original ) && has_action( 'wpseo_double_clean_titles', array( + $this, + 'clean', + ) ) === false + ) { + add_action( 'wpseo_double_clean_titles', array( $this, 'clean' ) ); + $original = $option_value; + } + + /* Move options from very old option to this one + @internal Don't rename to the 'current' names straight away as that would prevent + the rename/unset combi below from working + @todo [JRF] maybe figure out a smarter way to deal with this */ + $old_option = null; + if ( isset( $all_old_option_values ) ) { + // Ok, we have an import + if ( isset( $all_old_option_values['wpseo_indexation'] ) && is_array( $all_old_option_values['wpseo_indexation'] ) && $all_old_option_values['wpseo_indexation'] !== array() ) { + $old_option = $all_old_option_values['wpseo_indexation']; + } + } else { + $old_option = get_option( 'wpseo_indexation' ); + } + if ( is_array( $old_option ) && $old_option !== array() ) { + $move = array( + 'noindexauthor' => 'noindex-author', + 'disableauthor' => 'disable-author', + 'noindexdate' => 'noindex-archive', + 'noindexcat' => 'noindex-category', + 'noindextag' => 'noindex-post_tag', + 'noindexpostformat' => 'noindex-post_format', + 'noindexsubpages' => 'noindex-subpages', + 'hidersdlink' => 'hide-rsdlink', + 'hidefeedlinks' => 'hide-feedlinks', + 'hidewlwmanifest' => 'hide-wlwmanifest', + 'hideshortlink' => 'hide-shortlink', + ); + foreach ( $move as $old => $new ) { + if ( isset( $old_option[ $old ] ) && ! isset( $option_value[ $new ] ) ) { + $option_value[ $new ] = $old_option[ $old ]; + } + } + unset( $move, $old, $new ); + } + unset( $old_option ); + + + // Fix wrongness created by buggy version 1.2.2 + if ( isset( $option_value['title-home'] ) && $option_value['title-home'] === '%%sitename%% - %%sitedesc%% - 12345' ) { + $option_value['title-home-wpseo'] = '%%sitename%% - %%sitedesc%%'; + } + + + /* Renaming these options to avoid ever overwritting these if a (bloody stupid) user / + programmer would use any of the following as a custom post type or custom taxonomy: + 'home', 'author', 'archive', 'search', '404', 'subpages' + + Similarly, renaming the tax options to avoid a custom post type and a taxonomy + with the same name occupying the same option */ + $rename = array( + 'title-home' => 'title-home-wpseo', + 'title-author' => 'title-author-wpseo', + 'title-archive' => 'title-archive-wpseo', + 'title-search' => 'title-search-wpseo', + 'title-404' => 'title-404-wpseo', + 'metadesc-home' => 'metadesc-home-wpseo', + 'metadesc-author' => 'metadesc-author-wpseo', + 'metadesc-archive' => 'metadesc-archive-wpseo', + 'metakey-home' => 'metakey-home-wpseo', + 'metakey-author' => 'metakey-author-wpseo', + 'noindex-subpages' => 'noindex-subpages-wpseo', + 'noindex-author' => 'noindex-author-wpseo', + 'noindex-archive' => 'noindex-archive-wpseo', + ); + foreach ( $rename as $old => $new ) { + if ( isset( $option_value[ $old ] ) && ! isset( $option_value[ $new ] ) ) { + $option_value[ $new ] = $option_value[ $old ]; + unset( $option_value[ $old ] ); + } + } + unset( $rename, $old, $new ); + + + /* @internal This clean-up action can only be done effectively once the taxonomies and post_types + * have been registered, i.e. at the end of the init action. */ + if ( isset( $original ) && current_filter() === 'wpseo_double_clean_titles' || did_action( 'wpseo_double_clean_titles' ) > 0 ) { + $rename = array( + 'title-' => 'title-tax-', + 'metadesc-' => 'metadesc-tax-', + 'metakey-' => 'metakey-tax-', + 'noindex-' => 'noindex-tax-', + 'tax-hideeditbox-' => 'hideeditbox-tax-', + + ); + $taxonomy_names = get_taxonomies( array( 'public' => true ), 'names' ); + $post_type_names = get_post_types( array( 'public' => true ), 'names' ); + $defaults = $this->get_defaults(); + if ( $taxonomy_names !== array() ) { + foreach ( $taxonomy_names as $tax ) { + foreach ( $rename as $old_prefix => $new_prefix ) { + if ( + ( isset( $original[ $old_prefix . $tax ] ) && ! isset( $original[ $new_prefix . $tax ] ) ) + && ( ! isset( $option_value[ $new_prefix . $tax ] ) + || ( isset( $option_value[ $new_prefix . $tax ] ) + && $option_value[ $new_prefix . $tax ] === $defaults[ $new_prefix . $tax ] ) ) + ) { + $option_value[ $new_prefix . $tax ] = $original[ $old_prefix . $tax ]; + + /* Check if there is a cpt with the same name as the tax, + if so, we should make sure that the old setting hasn't been removed */ + if ( ! isset( $post_type_names[ $tax ] ) && isset( $option_value[ $old_prefix . $tax ] ) ) { + unset( $option_value[ $old_prefix . $tax ] ); + } else { + if ( isset( $post_type_names[ $tax ] ) && ! isset( $option_value[ $old_prefix . $tax ] ) ) { + $option_value[ $old_prefix . $tax ] = $original[ $old_prefix . $tax ]; + } + } + + if ( $old_prefix === 'tax-hideeditbox-' ) { + unset( $option_value[ $old_prefix . $tax ] ); + } + } + } + } + } + unset( $rename, $taxonomy_names, $post_type_names, $tax, $old_prefix, $new_prefix ); + } + + + /* Make sure the values of the variable option key options are cleaned as they + may be retained and would not be cleaned/validated then */ + if ( is_array( $option_value ) && $option_value !== array() ) { + foreach ( $option_value as $key => $value ) { + $switch_key = $this->get_switch_key( $key ); + + // Similar to validation routine - any changes made there should be made here too + switch ( $switch_key ) { + /* text fields */ + case 'title-': + case 'metadesc-': + case 'metakey-': + case 'bctitle-ptarchive-': + $option_value[ $key ] = self::sanitize_text_field( $value ); + break; + + + /* boolean fields */ + case 'noindex-': + case 'showdate-': + case 'hideeditbox-': + default: + $option_value[ $key ] = self::validate_bool( $value ); + break; + } + } + } + + return $option_value; + } + + + /** + * Make sure that any set option values relating to post_types and/or taxonomies are retained, + * even when that post_type or taxonomy may not yet have been registered. + * + * @internal Overrule the abstract class version of this to make sure one extra renamed variable key + * does not get removed. IMPORTANT: keep this method in line with the parent on which it is based! + * + * @param array $dirty Original option as retrieved from the database + * @param array $clean Filtered option where any options which shouldn't be in our option + * have already been removed and any options which weren't set + * have been set to their defaults + * + * @return array + */ + protected function retain_variable_keys( $dirty, $clean ) { + if ( ( is_array( $this->variable_array_key_patterns ) && $this->variable_array_key_patterns !== array() ) && ( is_array( $dirty ) && $dirty !== array() ) ) { + + // Add the extra pattern + $patterns = $this->variable_array_key_patterns; + $patterns[] = 'tax-hideeditbox-'; + + /** + * Allow altering the array with variable array key patterns + * @api array $patterns Array with the variable array key patterns + */ + $patterns = apply_filters( 'wpseo_option_titles_variable_array_key_patterns', $patterns ); + + foreach ( $dirty as $key => $value ) { + foreach ( $patterns as $pattern ) { + if ( strpos( $key, $pattern ) === 0 && ! isset( $clean[ $key ] ) ) { + $clean[ $key ] = $value; + break; + } + } + } + } + + return $clean; + } + + + } /* End of class WPSEO_Option_Titles */ + +} /* End of class-exists wrapper */ + + +/******************************************************************* + * Option: wpseo_rss + *******************************************************************/ +if ( ! class_exists( 'WPSEO_Option_RSS' ) ) { + + class WPSEO_Option_RSS extends WPSEO_Option { + + /** + * @var string option name + */ + public $option_name = 'wpseo_rss'; + + /** + * @var array Array of defaults for the option + * Shouldn't be requested directly, use $this->get_defaults(); + * @internal Note: Some of the default values are added via the translate_defaults() method + */ + protected $defaults = array( + 'rssbefore' => '', // text area + 'rssafter' => '', // text area + ); + + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + + return self::$instance; + } + + + /** + * Translate strings used in the option defaults + * + * @return void + */ + public function translate_defaults() { + $this->defaults['rssafter'] = sprintf( __( 'The post %s appeared first on %s.', 'wordpress-seo' ), '%%POSTLINK%%', '%%BLOGLINK%%' ); + } + + + /** + * Validate the option + * + * @param array $dirty New value for the option + * @param array $clean Clean value for the option, normally the defaults + * @param array $old Old value of the option + * + * @return array Validated clean value for the option to be saved to the database + */ + protected function validate_option( $dirty, $clean, $old ) { + foreach ( $clean as $key => $value ) { + if ( isset( $dirty[ $key ] ) ) { + $clean[ $key ] = wp_kses_post( $dirty[ $key ] ); + } + } + + return $clean; + } + + } /* End of class WPSEO_Option_RSS */ + +} /* End of class-exists wrapper */ + + +/******************************************************************* + * Option: wpseo_internallinks + *******************************************************************/ +if ( ! class_exists( 'WPSEO_Option_InternalLinks' ) ) { + + class WPSEO_Option_InternalLinks extends WPSEO_Option { + + /** + * @var string option name + */ + public $option_name = 'wpseo_internallinks'; + + /** + * @var array Array of defaults for the option + * Shouldn't be requested directly, use $this->get_defaults(); + * @internal Note: Some of the default values are added via the translate_defaults() method + */ + protected $defaults = array( + 'breadcrumbs-404crumb' => '', // text field + 'breadcrumbs-blog-remove' => false, + 'breadcrumbs-boldlast' => false, + 'breadcrumbs-archiveprefix' => '', // text field + 'breadcrumbs-enable' => false, + 'breadcrumbs-home' => '', // text field + 'breadcrumbs-prefix' => '', // text field + 'breadcrumbs-searchprefix' => '', // text field + 'breadcrumbs-sep' => '»', // text field + + /** + * Uses enrich_defaults() to add more along the lines of: + * - 'post_types-' . $pt->name . '-maintax' => 0 / string + * - 'taxonomy-' . $tax->name . '-ptparent' => 0 / string + */ + ); + + /** + * @var array Array of variable option name patterns for the option + */ + protected $variable_array_key_patterns = array( + 'post_types-', + 'taxonomy-', + ); + + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + + return self::$instance; + } + + + /** + * Translate strings used in the option defaults + * + * @return void + */ + public function translate_defaults() { + $this->defaults['breadcrumbs-404crumb'] = __( 'Error 404: Page not found', 'wordpress-seo' ); + $this->defaults['breadcrumbs-archiveprefix'] = __( 'Archives for', 'wordpress-seo' ); + $this->defaults['breadcrumbs-home'] = __( 'Home', 'wordpress-seo' ); + $this->defaults['breadcrumbs-searchprefix'] = __( 'You searched for', 'wordpress-seo' ); + } + + + /** + * Add dynamically created default options based on available post types and taxonomies + * + * @return void + */ + public function enrich_defaults() { + + // Retrieve all the relevant post type and taxonomy arrays + $post_type_names = get_post_types( array( 'public' => true ), 'names' ); + $taxonomy_names_custom = get_taxonomies( array( 'public' => true, '_builtin' => false ), 'names' ); + + if ( $post_type_names !== array() ) { + foreach ( $post_type_names as $pt ) { + $pto_taxonomies = get_object_taxonomies( $pt, 'names' ); + if ( $pto_taxonomies !== array() ) { + $this->defaults[ 'post_types-' . $pt . '-maintax' ] = 0; // select box + } + unset( $pto_taxonomies ); + } + unset( $pt ); + } + + if ( $taxonomy_names_custom !== array() ) { + foreach ( $taxonomy_names_custom as $tax ) { + $this->defaults[ 'taxonomy-' . $tax . '-ptparent' ] = 0; // select box; + } + unset( $tax ); + } + } + + + /** + * Validate the option + * + * @param array $dirty New value for the option + * @param array $clean Clean value for the option, normally the defaults + * @param array $old Old value of the option + * + * @return array Validated clean value for the option to be saved to the database + */ + protected function validate_option( $dirty, $clean, $old ) { + + $allowed_post_types = $this->get_allowed_post_types(); + + foreach ( $clean as $key => $value ) { + + $switch_key = $this->get_switch_key( $key ); + + switch ( $switch_key ) { + /* text fields */ + case 'breadcrumbs-404crumb': + case 'breadcrumbs-archiveprefix': + case 'breadcrumbs-home': + case 'breadcrumbs-prefix': + case 'breadcrumbs-searchprefix': + case 'breadcrumbs-sep': + if ( isset( $dirty[ $key ] ) ) { + $clean[ $key ] = wp_kses_post( $dirty[ $key ] ); + } + break; + + + /* 'post_types-' . $pt->name . '-maintax' fields */ + case 'post_types-': + $post_type = str_replace( array( 'post_types-', '-maintax' ), '', $key ); + $taxonomies = get_object_taxonomies( $post_type, 'names' ); + + if ( isset( $dirty[ $key ] ) ) { + if ( $taxonomies !== array() && in_array( $dirty[ $key ], $taxonomies, true ) ) { + $clean[ $key ] = $dirty[ $key ]; + } elseif ( (string) $dirty[ $key ] === '0' || (string) $dirty[ $key ] === '' ) { + $clean[ $key ] = 0; + } elseif ( sanitize_title_with_dashes( $dirty[ $key ] ) === $dirty[ $key ] ) { + // Allow taxonomies which may not be registered yet + $clean[ $key ] = $dirty[ $key ]; + } else { + if ( isset( $old[ $key ] ) ) { + $clean[ $key ] = sanitize_title_with_dashes( $old[ $key ] ); + } + if ( function_exists( 'add_settings_error' ) ) { + /* @todo [JRF => whomever] maybe change the untranslated $pt name in the + * error message to the nicely translated label ? */ + add_settings_error( + $this->group_name, // slug title of the setting + '_' . $key, // suffix-id for the error message box + sprintf( __( 'Please select a valid taxonomy for post type "%s"', 'wordpress-seo' ), $post_type ), // the error message + 'error' // error type, either 'error' or 'updated' + ); + } + } + } elseif ( isset( $old[ $key ] ) ) { + $clean[ $key ] = sanitize_title_with_dashes( $old[ $key ] ); + } + unset( $taxonomies, $post_type ); + break; + + + /* 'taxonomy-' . $tax->name . '-ptparent' fields */ + case 'taxonomy-': + if ( isset( $dirty[ $key ] ) ) { + if ( $allowed_post_types !== array() && in_array( $dirty[ $key ], $allowed_post_types, true ) ) { + $clean[ $key ] = $dirty[ $key ]; + } elseif ( (string) $dirty[ $key ] === '0' || (string) $dirty[ $key ] === '' ) { + $clean[ $key ] = 0; + } elseif ( sanitize_key( $dirty[ $key ] ) === $dirty[ $key ] ) { + // Allow taxonomies which may not be registered yet + $clean[ $key ] = $dirty[ $key ]; + } else { + if ( isset( $old[ $key ] ) ) { + $clean[ $key ] = sanitize_key( $old[ $key ] ); + } + if ( function_exists( 'add_settings_error' ) ) { + /* @todo [JRF =? whomever] maybe change the untranslated $tax name in the + * error message to the nicely translated label ? */ + $tax = str_replace( array( 'taxonomy-', '-ptparent' ), '', $key ); + add_settings_error( + $this->group_name, // slug title of the setting + '_' . $tax, // suffix-id for the error message box + sprintf( __( 'Please select a valid post type for taxonomy "%s"', 'wordpress-seo' ), $tax ), // the error message + 'error' // error type, either 'error' or 'updated' + ); + unset( $tax ); + } + } + } elseif ( isset( $old[ $key ] ) ) { + $clean[ $key ] = sanitize_key( $old[ $key ] ); + } + break; + + + /* boolean fields */ + case 'breadcrumbs-blog-remove': + case 'breadcrumbs-boldlast': + case 'breadcrumbs-enable': + default: + $clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false ); + break; + } + } + + return $clean; + } + + + /** + * Retrieve a list of the allowed post types as breadcrumb parent for a taxonomy + * Helper method for validation + * @internal don't make static as new types may still be registered + */ + protected function get_allowed_post_types() { + $allowed_post_types = array(); + + $post_types = get_post_types( array( 'public' => true ), 'objects' ); + + if ( get_option( 'show_on_front' ) == 'page' && get_option( 'page_for_posts' ) > 0 ) { + $allowed_post_types[] = 'post'; + } + + if ( is_array( $post_types ) && $post_types !== array() ) { + foreach ( $post_types as $type ) { + if ( $type->has_archive ) { + $allowed_post_types[] = $type->name; + } + } + } + + return $allowed_post_types; + } + + + /** + * Clean a given option value + * + * @param array $option_value Old (not merged with defaults or filtered) option value to + * clean according to the rules for this option + * @param string $current_version (optional) Version from which to upgrade, if not set, + * version specific upgrades will be disregarded + * @param array $all_old_option_values (optional) Only used when importing old options to have + * access to the real old values, in contrast to the saved ones + * + * @return array Cleaned option + */ + protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) { + + /* Make sure the old fall-back defaults for empty option keys are now added to the option */ + if ( isset( $current_version ) && version_compare( $current_version, '1.5.2.3', '<' ) ) { + if ( has_action( 'init', array( 'WPSEO_Options', 'bring_back_breadcrumb_defaults' ) ) === false ) { + add_action( 'init', array( 'WPSEO_Options', 'bring_back_breadcrumb_defaults' ), 3 ); + } + } + + /* Make sure the values of the variable option key options are cleaned as they + may be retained and would not be cleaned/validated then */ + if ( is_array( $option_value ) && $option_value !== array() ) { + + $allowed_post_types = $this->get_allowed_post_types(); + + foreach ( $option_value as $key => $value ) { + $switch_key = $this->get_switch_key( $key ); + + // Similar to validation routine - any changes made there should be made here too + switch ( $switch_key ) { + /* 'post_types-' . $pt->name . '-maintax' fields */ + case 'post_types-': + $post_type = str_replace( array( 'post_types-', '-maintax' ), '', $key ); + $taxonomies = get_object_taxonomies( $post_type, 'names' ); + + if ( $taxonomies !== array() && in_array( $value, $taxonomies, true ) ) { + $option_value[ $key ] = $value; + } elseif ( (string) $value === '0' || (string) $value === '' ) { + $option_value[ $key ] = 0; + } elseif ( sanitize_title_with_dashes( $value ) === $value ) { + // Allow taxonomies which may not be registered yet + $option_value[ $key ] = $value; + } + unset( $taxonomies, $post_type ); + break; + + + /* 'taxonomy-' . $tax->name . '-ptparent' fields */ + case 'taxonomy-': + if ( $allowed_post_types !== array() && in_array( $value, $allowed_post_types, true ) ) { + $option_value[ $key ] = $value; + } elseif ( (string) $value === '0' || (string) $value === '' ) { + $option_value[ $key ] = 0; + } elseif ( sanitize_key( $option_value[ $key ] ) === $option_value[ $key ] ) { + // Allow post types which may not be registered yet + $option_value[ $key ] = $value; + } + break; + } + } + } + + return $option_value; + } + + /** + * With the changes to v1.5, the defaults for some of the textual breadcrumb settings are added + * dynamically, but empty strings are allowed. + * This caused issues for people who left the fields empty on purpose relying on the defaults. + * This little routine fixes that. + * Needs to be run on 'init' hook at prio 3 to make sure the defaults are translated. + */ + public function bring_back_defaults() { + $option = get_option( $this->option_name ); + + $values_to_bring_back = array( + 'breadcrumbs-404crumb', + 'breadcrumbs-archiveprefix', + 'breadcrumbs-home', + 'breadcrumbs-searchprefix', + 'breadcrumbs-sep', + ); + foreach ( $values_to_bring_back as $key ) { + if ( $option[ $key ] === '' && $this->defaults[ $key ] !== '' ) { + $option[ $key ] = $this->defaults[ $key ]; + } + } + update_option( $this->option_name, $option ); + } + + } /* End of class WPSEO_Option_InternalLinks */ + +} /* End of class-exists wrapper */ + + +/******************************************************************* + * Option: wpseo_xml + *******************************************************************/ +if ( ! class_exists( 'WPSEO_Option_XML' ) ) { + + class WPSEO_Option_XML extends WPSEO_Option { + + /** + * @var string option name + */ + public $option_name = 'wpseo_xml'; + + /** + * @var string option group name for use in settings forms + */ + public $group_name = 'yoast_wpseo_xml_sitemap_options'; + + /** + * @var array Array of defaults for the option + * Shouldn't be requested directly, use $this->get_defaults(); + */ + protected $defaults = array( + 'disable_author_sitemap' => true, + 'disable_author_noposts' => true, + 'enablexmlsitemap' => true, + 'entries-per-page' => 1000, + 'xml_ping_yahoo' => false, + 'xml_ping_ask' => false, + + /** + * Uses enrich_defaults to add more along the lines of: + * - 'user_role-' . $role_name . '-not_in_sitemap' => bool + * - 'post_types-' . $pt->name . '-not_in_sitemap' => bool + * - 'taxonomies-' . $tax->name . '-not_in_sitemap' => bool + */ + ); + + /** + * @var array Array of variable option name patterns for the option + */ + protected $variable_array_key_patterns = array( + 'user_role-', + 'post_types-', + 'taxonomies-', + ); + + + /** + * Add the actions and filters for the option + * + * @todo [JRF => testers] Check if the extra actions below would run into problems if an option + * is updated early on and if so, change the call to schedule these for a later action on add/update + * instead of running them straight away + * + * @return \WPSEO_Option_XML + */ + protected function __construct() { + parent::__construct(); + add_action( 'update_option_' . $this->option_name, array( 'WPSEO_Options', 'clear_rewrites' ) ); + } + + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + + return self::$instance; + } + + + /** + * Add dynamically created default options based on available post types and taxonomies + * + * @return void + */ + public function enrich_defaults() { + + $user_roles = wpseo_get_roles(); + $filtered_user_roles = apply_filters( 'wpseo_sitemaps_supported_user_roles', $user_roles ); + if ( is_array( $filtered_user_roles ) && $filtered_user_roles !== array() ) { + foreach ( $filtered_user_roles as $role_name => $role_value ) { + $this->defaults['user_role-' . $role_name . '-not_in_sitemap'] = false; + + unset( $user_role ); + } + } + unset( $filtered_user_roles ); + + $post_type_names = get_post_types( array( 'public' => true ), 'names' ); + $filtered_post_types = apply_filters( 'wpseo_sitemaps_supported_post_types', $post_type_names ); + + if ( is_array( $filtered_post_types ) && $filtered_post_types !== array() ) { + foreach ( $filtered_post_types as $pt ) { + if ( $pt !== 'attachment' ) { + $this->defaults[ 'post_types-' . $pt . '-not_in_sitemap' ] = false; + } else { + $this->defaults[ 'post_types-' . $pt . '-not_in_sitemap' ] = true; + } + } + unset( $pt ); + } + unset( $filtered_post_types ); + + $taxonomy_objects = get_taxonomies( array( 'public' => true ), 'objects' ); + $filtered_taxonomies = apply_filters( 'wpseo_sitemaps_supported_taxonomies', $taxonomy_objects ); + if ( is_array( $filtered_taxonomies ) && $filtered_taxonomies !== array() ) { + foreach ( $filtered_taxonomies as $tax ) { + if ( isset( $tax->labels->name ) && trim( $tax->labels->name ) != '' ) { + $this->defaults[ 'taxonomies-' . $tax->name . '-not_in_sitemap' ] = false; + } + } + unset( $tax ); + } + unset( $filtered_taxonomies ); + + } + + + /** + * Validate the option + * + * @param array $dirty New value for the option + * @param array $clean Clean value for the option, normally the defaults + * @param array $old Old value of the option + * + * @return array Validated clean value for the option to be saved to the database + */ + protected function validate_option( $dirty, $clean, $old ) { + + foreach ( $clean as $key => $value ) { + $switch_key = $this->get_switch_key( $key ); + + switch ( $switch_key ) { + /* integer fields */ + case 'entries-per-page': + /* @todo [JRF/JRF => Yoast] add some more rules (minimum 50 or something + * - what should be the guideline?) and adjust error message */ + if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) { + $int = self::validate_int( $dirty[ $key ] ); + if ( $int !== false && $int > 0 ) { + $clean[ $key ] = $int; + } else { + if ( isset( $old[ $key ] ) && $old[ $key ] !== '' ) { + $int = self::validate_int( $old[ $key ] ); + if ( $int !== false && $int > 0 ) { + $clean[ $key ] = $int; + } + } + if ( function_exists( 'add_settings_error' ) ) { + add_settings_error( + $this->group_name, // slug title of the setting + '_' . $key, // suffix-id for the error message box + sprintf( __( '"Max entries per sitemap page" should be a positive number, which %s is not. Please correct.', 'wordpress-seo' ), '' . esc_html( sanitize_text_field( $dirty[ $key ] ) ) . '' ), // the error message + 'error' // error type, either 'error' or 'updated' + ); + } + } + unset( $int ); + } + break; + + + /* boolean fields */ + case 'disable_author_sitemap': + case 'disable_author_noposts': + case 'enablexmlsitemap': + case 'user_role-': /* 'user_role' . $role_name . '-not_in_sitemap' fields */ + case 'post_types-': /* 'post_types-' . $pt->name . '-not_in_sitemap' fields */ + case 'taxonomies-': /* 'taxonomies-' . $tax->name . '-not_in_sitemap' fields */ + case 'xml_ping_yahoo': + case 'xml_ping_ask': + default: + $clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false ); + break; + } + } + + return $clean; + } + + + /** + * Clean a given option value + * + * @param array $option_value Old (not merged with defaults or filtered) option value to + * clean according to the rules for this option + * @param string $current_version (optional) Version from which to upgrade, if not set, + * version specific upgrades will be disregarded + * @param array $all_old_option_values (optional) Only used when importing old options to have + * access to the real old values, in contrast to the saved ones + * + * @return array Cleaned option + */ + protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) { + + /* Make sure the values of the variable option key options are cleaned as they + may be retained and would not be cleaned/validated then */ + if ( is_array( $option_value ) && $option_value !== array() ) { + + foreach ( $option_value as $key => $value ) { + $switch_key = $this->get_switch_key( $key ); + + // Similar to validation routine - any changes made there should be made here too + switch ( $switch_key ) { + case 'user_role-': /* 'user_role-' . $role_name. '-not_in_sitemap' fields */ + case 'post_types-': /* 'post_types-' . $pt->name . '-not_in_sitemap' fields */ + case 'taxonomies-': /* 'taxonomies-' . $tax->name . '-not_in_sitemap' fields */ + $option_value[ $key ] = self::validate_bool( $value ); + break; + } + } + } + + return $option_value; + } + + + } /* End of class WPSEO_Option_XML */ + +} /* End of class-exists wrapper */ + + +/******************************************************************* + * Option: wpseo_social + *******************************************************************/ +if ( ! class_exists( 'WPSEO_Option_Social' ) ) { + + class WPSEO_Option_Social extends WPSEO_Option { + + /** + * @var string option name + */ + public $option_name = 'wpseo_social'; + + /** + * @var array Array of defaults for the option + * Shouldn't be requested directly, use $this->get_defaults(); + */ + protected $defaults = array( + // Non-form fields, set via procedural code in admin/pages/social.php + 'fb_admins' => array(), // array of user id's => array( name => '', link => '' ) + 'fbapps' => array(), // array of linked fb apps id's => fb app display names + + // Non-form field, set via translate_defaults() and validate_option() methods + 'fbconnectkey' => '', + // Form fields: + 'facebook_site' => '', // text field + 'og_default_image' => '', // text field + 'og_frontpage_title' => '', // text field + 'og_frontpage_desc' => '', // text field + 'og_frontpage_image' => '', // text field + 'opengraph' => true, + 'googleplus' => false, + 'plus-publisher' => '', // text field + 'twitter' => false, + 'twitter_site' => '', // text field + 'twitter_card_type' => 'summary', + // Form field, but not always available: + 'fbadminapp' => 0, // app id from fbapps list + ); + + /** + * @var array Array of sub-options which should not be overloaded with multi-site defaults + */ + public $ms_exclude = array( + /* privacy */ + 'fb_admins', + 'fbapps', + 'fbconnectkey', + 'fbadminapp', + ); + + + /** + * @var array Array of allowed twitter card types + * While we only have the options summary and summary_large_image in the + * interface now, we might change that at some point. + * + * @internal Uncomment any of these to allow them in validation *and* automatically add them as a choice + * in the options page + */ + public static $twitter_card_types = array( + 'summary' => '', + 'summary_large_image' => '', + //'photo' => '', + //'gallery' => '', + //'app' => '', + //'player' => '', + //'product' => '', + ); + + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + + return self::$instance; + } + + + /** + * Translate/set strings used in the option defaults + * + * @return void + */ + public function translate_defaults() { + /* Auto-magically set the fb connect key */ + $this->defaults['fbconnectkey'] = self::get_fbconnectkey(); + + self::$twitter_card_types['summary'] = __( 'Summary', 'wordpress-seo' ); + self::$twitter_card_types['summary_large_image'] = __( 'Summary with large image', 'wordpress-seo' ); + } + + + /** + * Get a Facebook connect key for the blog + * + * @static + * @return string + */ + public static function get_fbconnectkey() { + return md5( get_bloginfo( 'url' ) . rand() ); + } + + + /** + * Validate the option + * + * @param array $dirty New value for the option + * @param array $clean Clean value for the option, normally the defaults + * @param array $old Old value of the option + * + * @return array Validated clean value for the option to be saved to the database + */ + protected function validate_option( $dirty, $clean, $old ) { + + foreach ( $clean as $key => $value ) { + switch ( $key ) { + /* Automagic Facebook connect key */ + case 'fbconnectkey': + if ( ( isset( $old[ $key ] ) && $old[ $key ] !== '' ) && preg_match( '`^[a-f0-9]{32}$`', $old[ $key ] ) > 0 ) { + $clean[ $key ] = $old[ $key ]; + } else { + $clean[ $key ] = self::get_fbconnectkey(); + } + break; + + + /* Will not always exist in form */ + case 'fb_admins': + if ( isset( $dirty[ $key ] ) && is_array( $dirty[ $key ] ) ) { + if ( $dirty[ $key ] === array() ) { + $clean[ $key ] = array(); + } else { + foreach ( $dirty[ $key ] as $user_id => $fb_array ) { + /* @todo [JRF/JRF => Yoast/whomever] add user_id validation - + * are these WP user-ids or FB user-ids ? Probably FB user-ids, + * if so, find out the rules for FB user-ids + */ + if ( is_array( $fb_array ) && $fb_array !== array() ) { + foreach ( $fb_array as $fb_key => $fb_value ) { + switch ( $fb_key ) { + case 'name': + /* @todo [JRF => whomever] add validation for name based + * on rules if there are any + * Input comes from: $_GET['userrealname'] */ + $clean[ $key ][ $user_id ][ $fb_key ] = sanitize_text_field( $fb_value ); + break; + + case 'link': + $clean[ $key ][ $user_id ][ $fb_key ] = self::sanitize_url( $fb_value ); + break; + } + } + } + } + unset( $user_id, $fb_array, $fb_key, $fb_value ); + } + } elseif ( isset( $old[ $key ] ) && is_array( $old[ $key ] ) ) { + $clean[ $key ] = $old[ $key ]; + } + break; + + + /* Will not always exist in form */ + case 'fbapps': + if ( isset( $dirty[ $key ] ) && is_array( $dirty[ $key ] ) ) { + if ( $dirty[ $key ] === array() ) { + $clean[ $key ] = array(); + } else { + $clean[ $key ] = array(); + foreach ( $dirty[ $key ] as $app_id => $display_name ) { + if ( ctype_digit( (string) $app_id ) !== false ) { + $clean[ $key ][ $app_id ] = sanitize_text_field( $display_name ); + } + } + unset( $app_id, $display_name ); + } + } elseif ( isset( $old[ $key ] ) && is_array( $old[ $key ] ) ) { + $clean[ $key ] = $old[ $key ]; + } + break; + + + /* text fields */ + case 'og_frontpage_desc': + case 'og_frontpage_title': + if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) { + $clean[ $key ] = self::sanitize_text_field( $dirty[ $key ] ); + } + break; + + + /* url text fields - no ftp allowed */ + case 'facebook_site': + case 'plus-publisher': + case 'og_default_image': + case 'og_frontpage_image': + if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) { + $url = self::sanitize_url( $dirty[ $key ] ); + if ( $url !== '' ) { + $clean[ $key ] = $url; + } else { + if ( isset( $old[ $key ] ) && $old[ $key ] !== '' ) { + $url = self::sanitize_url( $old[ $key ] ); + if ( $url !== '' ) { + $clean[ $key ] = $url; + } + } + if ( function_exists( 'add_settings_error' ) ) { + $url = self::sanitize_url( $dirty[ $key ] ); + add_settings_error( + $this->group_name, // slug title of the setting + '_' . $key, // suffix-id for the error message box + sprintf( __( '%s does not seem to be a valid url. Please correct.', 'wordpress-seo' ), '' . esc_html( $url ) . '' ), // the error message + 'error' // error type, either 'error' or 'updated' + ); + } + } + unset( $url ); + } + break; + + + /* twitter user name */ + case 'twitter_site': + if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) { + $twitter_id = sanitize_text_field( ltrim( $dirty[ $key ], '@' ) ); + /** + * From the Twitter documentation about twitter screen names: + * Typically a maximum of 15 characters long, but some historical accounts + * may exist with longer names. + * A username can only contain alphanumeric characters (letters A-Z, numbers 0-9) + * with the exception of underscores + * @link https://support.twitter.com/articles/101299-why-can-t-i-register-certain-usernames + * @link https://dev.twitter.com/docs/platform-objects/users + */ + if ( preg_match( '`^[A-Za-z0-9_]{1,25}$`', $twitter_id ) ) { + $clean[ $key ] = $twitter_id; + } else { + if ( isset( $old[ $key ] ) && $old[ $key ] !== '' ) { + $twitter_id = sanitize_text_field( ltrim( $old[ $key ], '@' ) ); + if ( preg_match( '`^[A-Za-z0-9_]{1,25}$`', $twitter_id ) ) { + $clean[ $key ] = $twitter_id; + } + } + if ( function_exists( 'add_settings_error' ) ) { + add_settings_error( + $this->group_name, // slug title of the setting + '_' . $key, // suffix-id for the error message box + sprintf( __( '%s does not seem to be a valid Twitter user-id. Please correct.', 'wordpress-seo' ), '' . esc_html( sanitize_text_field( $dirty[ $key ] ) ) . '' ), // the error message + 'error' // error type, either 'error' or 'updated' + ); + } + } + unset( $twitter_id ); + } + break; + + case 'twitter_card_type': + if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' && isset( self::$twitter_card_types[ $dirty[ $key ] ] ) ) { + $clean[ $key ] = $dirty[ $key ]; + } + break; + + /* boolean fields */ + case 'googleplus': + case 'opengraph': + case 'twitter': + $clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false ); + break; + } + } + + /** + * Only validate 'fbadminapp' once we are sure that 'fbapps' has been validated already. + * Will not always exist in form - if not available it means that fbapps is empty, + * so leave the clean default. + */ + if ( ( isset( $dirty['fbadminapp'] ) && $dirty['fbadminapp'] != 0 ) && isset( $clean['fbapps'][ $dirty['fbadminapp'] ] ) ) { + $clean['fbadminapp'] = $dirty['fbadminapp']; + } + + + return $clean; + } + + + /** + * Clean a given option value + * + * @param array $option_value Old (not merged with defaults or filtered) option value to + * clean according to the rules for this option + * @param string $current_version (optional) Version from which to upgrade, if not set, + * version specific upgrades will be disregarded + * @param array $all_old_option_values (optional) Only used when importing old options to have + * access to the real old values, in contrast to the saved ones + * + * @return array Cleaned option + */ + protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) { + + /* Move options from very old option to this one */ + $old_option = null; + if ( isset( $all_old_option_values ) ) { + // Ok, we have an import + if ( isset( $all_old_option_values['wpseo_indexation'] ) && is_array( $all_old_option_values['wpseo_indexation'] ) && $all_old_option_values['wpseo_indexation'] !== array() ) { + $old_option = $all_old_option_values['wpseo_indexation']; + } + } else { + $old_option = get_option( 'wpseo_indexation' ); + } + + if ( is_array( $old_option ) && $old_option !== array() ) { + $move = array( + 'opengraph', + 'fb_adminid', + 'fb_appid', + ); + foreach ( $move as $key ) { + if ( isset( $old_option[ $key ] ) && ! isset( $option_value[ $key ] ) ) { + $option_value[ $key ] = $old_option[ $key ]; + } + } + unset( $move, $key ); + } + unset( $old_option ); + + + /* Clean some values which may not always be in form and may otherwise not be cleaned/validated */ + if ( isset( $option_value['fbapps'] ) && ( is_array( $option_value['fbapps'] ) && $option_value['fbapps'] !== array() ) ) { + $fbapps = array(); + foreach ( $option_value['fbapps'] as $app_id => $display_name ) { + if ( ctype_digit( (string) $app_id ) !== false ) { + $fbapps[ $app_id ] = sanitize_text_field( $display_name ); + } + } + unset( $app_id, $display_name ); + + $option_value['fbapps'] = $fbapps; + } + + return $option_value; + } + + + } /* End of class WPSEO_Option_Social */ + +} /* End of class-exists wrapper */ + + +/******************************************************************* + * Option: wpseo_ms + *******************************************************************/ +if ( is_multisite() && ! class_exists( 'WPSEO_Option_MS' ) ) { + + /** + * Site option for Multisite installs only + * + * This class will not even be available/loaded if not on multisite, so none of the actions will + * register if not on multisite. + * + * Overloads a number of methods of the abstract class to ensure the use of the correct site_option + * WP functions. + */ + class WPSEO_Option_MS extends WPSEO_Option { + + /** + * @var string option name + */ + public $option_name = 'wpseo_ms'; + + /** + * @var string option group name for use in settings forms + */ + public $group_name = 'yoast_wpseo_multisite_options'; + + /** + * @var bool whether to include the option in the return for WPSEO_Options::get_all() + */ + public $include_in_all = false; + + /** + * @var bool whether this option is only for when the install is multisite + */ + public $multisite_only = true; + + /** + * @var array Array of defaults for the option + * Shouldn't be requested directly, use $this->get_defaults(); + */ + protected $defaults = array( + 'access' => 'admin', + 'defaultblog' => '', //numeric blog id or empty + ); + + /** + * @static + * @var array $allowed_access_options Available options for the 'access' setting + * Used for input validation + * + * @internal Important: Make sure the options added to the array here are in line with the keys + * for the options set for the select box in the admin/pages/network.php file + */ + public static $allowed_access_options = array( + 'admin', + 'superadmin', + ); + + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + + return self::$instance; + } + + + /** + * Add filters to make sure that the option default is returned if the option is not set + * + * @return void + */ + public function add_default_filters() { + // Don't change, needs to check for false as could return prio 0 which would evaluate to false + if ( has_filter( 'default_site_option_' . $this->option_name, array( $this, 'get_defaults' ) ) === false ) { + add_filter( 'default_site_option_' . $this->option_name, array( $this, 'get_defaults' ) ); + } + } + + + /** + * Remove the default filters. + * Called from the validate() method to prevent failure to add new options + * + * @return void + */ + public function remove_default_filters() { + remove_filter( 'default_site_option_' . $this->option_name, array( $this, 'get_defaults' ) ); + } + + + /** + * Add filters to make sure that the option is merged with its defaults before being returned + * + * @return void + */ + public function add_option_filters() { + // Don't change, needs to check for false as could return prio 0 which would evaluate to false + if ( has_filter( 'site_option_' . $this->option_name, array( $this, 'get_option' ) ) === false ) { + add_filter( 'site_option_' . $this->option_name, array( $this, 'get_option' ) ); + } + } + + + /** + * Remove the option filters. + * Called from the clean_up methods to make sure we retrieve the original old option + * + * @return void + */ + public function remove_option_filters() { + remove_filter( 'site_option_' . $this->option_name, array( $this, 'get_option' ) ); + } + + + /* *********** METHODS influencing add_uption(), update_option() and saving from admin pages *********** */ + + + /** + * Validate the option + * + * @param array $dirty New value for the option + * @param array $clean Clean value for the option, normally the defaults + * @param array $old Old value of the option + * + * @return array Validated clean value for the option to be saved to the database + */ + protected function validate_option( $dirty, $clean, $old ) { + + foreach ( $clean as $key => $value ) { + switch ( $key ) { + case 'access': + if ( isset( $dirty[ $key ] ) && in_array( $dirty[ $key ], self::$allowed_access_options, true ) ) { + $clean[ $key ] = $dirty[ $key ]; + } elseif ( function_exists( 'add_settings_error' ) ) { + add_settings_error( + $this->group_name, // slug title of the setting + '_' . $key, // suffix-id for the error message box + sprintf( __( '%s is not a valid choice for who should be allowed access to the WP SEO settings. Value reset to the default.', 'wordpress-seo' ), esc_html( sanitize_text_field( $dirty[ $key ] ) ) ), // the error message + 'error' // error type, either 'error' or 'updated' + ); + } + break; + + + case 'defaultblog': + if ( isset( $dirty[ $key ] ) && ( $dirty[ $key ] !== '' && $dirty[ $key ] !== '-' ) ) { + $int = self::validate_int( $dirty[ $key ] ); + if ( $int !== false && $int > 0 ) { + // Check if a valid blog number has been received + $exists = get_blog_details( $int, false ); + if ( $exists && $exists->deleted == 0 ) { + $clean[ $key ] = $int; + } elseif ( function_exists( 'add_settings_error' ) ) { + add_settings_error( + $this->group_name, // slug title of the setting + '_' . $key, // suffix-id for the error message box + esc_html__( 'The default blog setting must be the numeric blog id of the blog you want to use as default.', 'wordpress-seo' ) . '
' . sprintf( esc_html__( 'This must be an existing blog. Blog %s does not exist or has been marked as deleted.', 'wordpress-seo' ), '' . esc_html( sanitize_text_field( $dirty[ $key ] ) ) . '' ), // the error message + 'error' // error type, either 'error' or 'updated' + ); + } + unset( $exists ); + } elseif ( function_exists( 'add_settings_error' ) ) { + add_settings_error( + $this->group_name, // slug title of the setting + '_' . $key, // suffix-id for the error message box + esc_html__( 'The default blog setting must be the numeric blog id of the blog you want to use as default.', 'wordpress-seo' ) . '
' . esc_html__( 'No numeric value was received.', 'wordpress-seo' ), // the error message + 'error' // error type, either 'error' or 'updated' + ); + } + unset( $int ); + } + break; + + default: + $clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false ); + break; + } + } + + return $clean; + } + + + /** + * Clean a given option value + * + * @param array $option_value Old (not merged with defaults or filtered) option value to + * clean according to the rules for this option + * @param string $current_version (optional) Version from which to upgrade, if not set, + * version specific upgrades will be disregarded + * @param array $all_old_option_values (optional) Only used when importing old options to have + * access to the real old values, in contrast to the saved ones + * + * @return array Cleaned option + */ + /*protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) { + + return $option_value; + }*/ + + + } /* End of class WPSEO_Option_MS */ + +} /* End of class-exists wrapper */ + + +/******************************************************************* + * Option: wpseo_taxonomy_meta + *******************************************************************/ +if ( ! class_exists( 'WPSEO_Taxonomy_Meta' ) ) { + + class WPSEO_Taxonomy_Meta extends WPSEO_Option { + + /** + * @var string option name + */ + public $option_name = 'wpseo_taxonomy_meta'; + + /** + * @var bool whether to include the option in the return for WPSEO_Options::get_all() + */ + public $include_in_all = false; + + /** + * @var array Array of defaults for the option + * Shouldn't be requested directly, use $this->get_defaults(); + * @internal Important: in contrast to most defaults, the below array format is + * very bare. The real option is in the format [taxonomy_name][term_id][...] + * where [...] is any of the $defaults_per_term options shown below. + * This is of course taken into account in the below methods. + */ + protected $defaults = array(); + + + /** + * @static + * @var string Option name - same as $option_name property, but now also available to static + * methods + */ + public static $name; + + /** + * @static + * @var array Array of defaults for individual taxonomy meta entries + */ + public static $defaults_per_term = array( + 'wpseo_title' => '', + 'wpseo_desc' => '', + 'wpseo_metakey' => '', + 'wpseo_canonical' => '', + 'wpseo_bctitle' => '', + 'wpseo_noindex' => 'default', + 'wpseo_sitemap_include' => '-', + ); + + /** + * @static + * @var array Available index options + * Used for form generation and input validation + * @internal Labels (translation) added on admin_init via WPSEO_Taxonomy::translate_meta_options() + */ + public static $no_index_options = array( + 'default' => '', + 'index' => '', + 'noindex' => '', + ); + + /** + * @static + * @var array Available sitemap include options + * Used for form generation and input validation + * @internal Labels (translation) added on admin_init via WPSEO_Taxonomy::translate_meta_options() + */ + public static $sitemap_include_options = array( + '-' => '', + 'always' => '', + 'never' => '', + ); + + + /** + * Add the actions and filters for the option + * + * @todo [JRF => testers] Check if the extra actions below would run into problems if an option + * is updated early on and if so, change the call to schedule these for a later action on add/update + * instead of running them straight away + * + * @return \WPSEO_Taxonomy_Meta + */ + protected function __construct() { + parent::__construct(); + + /* On succesfull update/add of the option, flush the W3TC cache */ + add_action( 'add_option_' . $this->option_name, array( 'WPSEO_Options', 'flush_w3tc_cache' ) ); + add_action( 'update_option_' . $this->option_name, array( 'WPSEO_Options', 'flush_w3tc_cache' ) ); + } + + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + self::$name = self::$instance->option_name; + } + + return self::$instance; + } + + + public function enrich_defaults() { + $extra_defaults_per_term = apply_filters( 'wpseo_add_extra_taxmeta_term_defaults', array() ); + if ( is_array( $extra_defaults_per_term ) ) { + self::$defaults_per_term = array_merge( $extra_defaults_per_term, self::$defaults_per_term ); + } + } + + + /** + * Helper method - Combines a fixed array of default values with an options array + * while filtering out any keys which are not in the defaults array. + * + * @static + * + * @param string $option_key Option name of the option we're doing the merge for + * @param array $options (Optional) Current options + * - if not set, the option defaults for the $option_key will be returned. + * + * @return array Combined and filtered options array. + */ + /*public function array_filter_merge( $option_key, $options = null ) { + + $defaults = $this->get_defaults( $option_key ); + + if ( ! isset( $options ) || $options === false ) { + return $defaults; + } + + / * + @internal Adding the defaults to all taxonomy terms each time the option is retrieved + will be quite inefficient if there are a lot of taxonomy terms + As long as taxonomy_meta is only retrieved via methods in this class, we shouldn't need this + + $options = (array) $options; + $filtered = array(); + + if ( $options !== array() ) { + foreach ( $options as $taxonomy => $terms ) { + if ( is_array( $terms ) && $terms !== array() ) { + foreach ( $terms as $id => $term_meta ) { + foreach ( self::$defaults_per_term as $name => $default ) { + if ( isset( $options[ $taxonomy ][ $id ][ $name ] ) ) { + $filtered[ $taxonomy ][ $id ][ $name ] = $options[ $taxonomy ][ $id ][ $name ]; + } + else { + $filtered[ $name ] = $default; + } + } + } + } + } + unset( $taxonomy, $terms, $id, $term_meta, $name, $default ); + } + // end of may be remove + + return $filtered; + * / + + return (array) $options; + }*/ + + + /** + * Validate the option + * + * @param array $dirty New value for the option + * @param array $clean Clean value for the option, normally the defaults + * @param array $old Old value of the option + * + * @return array Validated clean value for the option to be saved to the database + */ + protected function validate_option( $dirty, $clean, $old ) { + + /* Prevent complete validation (which can be expensive when there are lots of terms) + if only one item has changed and has already been validated */ + if ( isset( $dirty['wpseo_already_validated'] ) && $dirty['wpseo_already_validated'] === true ) { + unset( $dirty['wpseo_already_validated'] ); + + return $dirty; + } + + + foreach ( $dirty as $taxonomy => $terms ) { + /* Don't validate taxonomy - may not be registered yet and we don't want to remove valid ones */ + if ( is_array( $terms ) && $terms !== array() ) { + foreach ( $terms as $term_id => $meta_data ) { + /* Only validate term if the taxonomy exists */ + if ( taxonomy_exists( $taxonomy ) && get_term_by( 'id', $term_id, $taxonomy ) === false ) { + /* Is this term id a special case ? */ + if ( has_filter( 'wpseo_tax_meta_special_term_id_validation_' . $term_id ) !== false ) { + $clean[ $taxonomy ][ $term_id ] = apply_filters( 'wpseo_tax_meta_special_term_id_validation_' . $term_id, $meta_data, $taxonomy, $term_id ); + } + continue; + } + + if ( is_array( $meta_data ) && $meta_data !== array() ) { + /* Validate meta data */ + $old_meta = self::get_term_meta( $term_id, $taxonomy ); + $meta_data = self::validate_term_meta_data( $meta_data, $old_meta ); + if ( $meta_data !== array() ) { + $clean[ $taxonomy ][ $term_id ] = $meta_data; + } + } + + // Deal with special cases (for when taxonomy doesn't exist yet) + if ( ! isset( $clean[ $taxonomy ][ $term_id ] ) && has_filter( 'wpseo_tax_meta_special_term_id_validation_' . $term_id ) !== false ) { + $clean[ $taxonomy ][ $term_id ] = apply_filters( 'wpseo_tax_meta_special_term_id_validation_' . $term_id, $meta_data, $taxonomy, $term_id ); + } + } + } + } + + return $clean; + } + + + /** + * Validate the meta data for one individual term and removes default values (no need to save those) + * + * @static + * + * @param array $meta_data New values + * @param array $old_meta The original values + * + * @return array Validated and filtered value + */ + public static function validate_term_meta_data( $meta_data, $old_meta ) { + + $clean = self::$defaults_per_term; + $meta_data = array_map( array( __CLASS__, 'trim_recursive' ), $meta_data ); + + if ( ! is_array( $meta_data ) || $meta_data === array() ) { + return $clean; + } + + foreach ( $clean as $key => $value ) { + switch ( $key ) { + + case 'wpseo_noindex': + if ( isset( $meta_data[ $key ] ) ) { + if ( isset( self::$no_index_options[ $meta_data[ $key ] ] ) ) { + $clean[ $key ] = $meta_data[ $key ]; + } + } elseif ( isset( $old_meta[ $key ] ) ) { + // Retain old value if field currently not in use + $clean[ $key ] = $old_meta[ $key ]; + } + break; + + case 'wpseo_sitemap_include': + if ( isset( $meta_data[ $key ] ) && isset( self::$sitemap_include_options[ $meta_data[ $key ] ] ) ) { + $clean[ $key ] = $meta_data[ $key ]; + } + break; + + case 'wpseo_canonical': + if ( isset( $meta_data[ $key ] ) && $meta_data[ $key ] !== '' ) { + $url = self::sanitize_url( $meta_data[ $key ] ); + if ( $url !== '' ) { + $clean[ $key ] = $url; + } + } + break; + + case 'wpseo_metakey': + case 'wpseo_bctitle': + if ( isset( $meta_data[ $key ] ) ) { + $clean[ $key ] = self::sanitize_text_field( stripslashes( $meta_data[ $key ] ) ); + } elseif ( isset( $old_meta[ $key ] ) ) { + // Retain old value if field currently not in use + $clean[ $key ] = $old_meta[ $key ]; + } + break; + + case 'wpseo_title': + case 'wpseo_desc': + default: + if ( isset( $meta_data[ $key ] ) && is_string( $meta_data[ $key ] ) ) { + $clean[ $key ] = self::sanitize_text_field( stripslashes( $meta_data[ $key ] ) ); + } + break; + } + + $clean[ $key ] = apply_filters( 'wpseo_sanitize_tax_meta_' . $key, $clean[ $key ], ( isset( $meta_data[ $key ] ) ? $meta_data[ $key ] : null ), ( isset( $old_meta[ $key ] ) ? $old_meta[ $key ] : null ) ); + } + + // Only save the non-default values + return array_diff_assoc( $clean, self::$defaults_per_term ); + } + + + /** + * Clean a given option value + * - Convert old option values to new + * - Fixes strings which were escaped (should have been sanitized - escaping is for output) + * + * @param array $option_value Old (not merged with defaults or filtered) option value to + * clean according to the rules for this option + * @param string $current_version (optional) Version from which to upgrade, if not set, + * version specific upgrades will be disregarded + * @param array $all_old_option_values (optional) Only used when importing old options to have + * access to the real old values, in contrast to the saved ones + * + * @return array Cleaned option + */ + protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) { + + /* Clean up old values and remove empty arrays */ + if ( is_array( $option_value ) && $option_value !== array() ) { + + foreach ( $option_value as $taxonomy => $terms ) { + + if ( is_array( $terms ) && $terms !== array() ) { + + foreach ( $terms as $term_id => $meta_data ) { + if ( ! is_array( $meta_data ) || $meta_data === array() ) { + // Remove empty term arrays + unset( $option_value[ $taxonomy ][ $term_id ] ); + } else { + foreach ( $meta_data as $key => $value ) { + + switch ( $key ) { + case 'noindex': + if ( $value === 'on' ) { + // Convert 'on' to 'noindex' + $option_value[ $taxonomy ][ $term_id ][ $key ] = 'noindex'; + } + break; + + case 'canonical': + case 'wpseo_metakey': + case 'wpseo_bctitle': + case 'wpseo_title': + case 'wpseo_desc': + // @todo [JRF => whomever] needs checking, I don't have example data [JRF] + if ( $value !== '' ) { + // Fix incorrectly saved (encoded) canonical urls and texts + $option_value[ $taxonomy ][ $term_id ][ $key ] = wp_specialchars_decode( stripslashes( $value ), ENT_QUOTES ); + } + break; + + default: + // @todo [JRF => whomever] needs checking, I don't have example data [JRF] + if ( $value !== '' ) { + // Fix incorrectly saved (escaped) text strings + $option_value[ $taxonomy ][ $term_id ][ $key ] = wp_specialchars_decode( $value, ENT_QUOTES ); + } + break; + } + } + } + } + } else { + // Remove empty taxonomy arrays + unset( $option_value[ $taxonomy ] ); + } + } + } + + return $option_value; + } + + + /** + * Retrieve a taxonomy term's meta value(s). + * + * @static + * + * @param mixed $term Term to get the meta value for + * either (string) term name, (int) term id or (object) term + * @param string $taxonomy Name of the taxonomy to which the term is attached + * @param string $meta (optional) Meta value to get (without prefix) + * + * @return mixed|bool Value for the $meta if one is given, might be the default + * If no meta is given, an array of all the meta data for the term + * False if the term does not exist or the $meta provided is invalid + */ + public static function get_term_meta( $term, $taxonomy, $meta = null ) { + /* Figure out the term id */ + if ( is_int( $term ) ) { + $term = get_term_by( 'id', $term, $taxonomy ); + } elseif ( is_string( $term ) ) { + $term = get_term_by( 'slug', $term, $taxonomy ); + } + + if ( is_object( $term ) && isset( $term->term_id ) ) { + $term_id = $term->term_id; + } else { + return false; + } + + + $tax_meta = get_option( self::$name ); + + /* If we have data for the term, merge with defaults for complete array, otherwise set defaults */ + if ( isset( $tax_meta[ $taxonomy ][ $term_id ] ) ) { + $tax_meta = array_merge( self::$defaults_per_term, $tax_meta[ $taxonomy ][ $term_id ] ); + } else { + $tax_meta = self::$defaults_per_term; + } + + /* Either return the complete array or a single value from it or false if the value does not exist + (shouldn't happen after merge with defaults, indicates typo in request) */ + if ( ! isset( $meta ) ) { + return $tax_meta; + } else { + if ( isset( $tax_meta[ 'wpseo_' . $meta ] ) ) { + return $tax_meta[ 'wpseo_' . $meta ]; + } else { + return false; + } + } + } + + } /* End of class WPSEO_Taxonomy_Meta */ + +} /* End of class-exists wrapper */ + + +/******************************************************************* + * Overall Option Management + *******************************************************************/ +if ( ! class_exists( 'WPSEO_Options' ) ) { + /** + * Overal Option Management class + * + * Instantiates all the options and offers a number of utility methods to work with the options + */ + class WPSEO_Options { + + + /** + * @static + * @var array Options this class uses + * Array format: (string) option_name => (string) name of concrete class for the option + */ + public static $options = array( + 'wpseo' => 'WPSEO_Option_Wpseo', + 'wpseo_permalinks' => 'WPSEO_Option_Permalinks', + 'wpseo_titles' => 'WPSEO_Option_Titles', + 'wpseo_social' => 'WPSEO_Option_Social', + 'wpseo_rss' => 'WPSEO_Option_RSS', + 'wpseo_internallinks' => 'WPSEO_Option_InternalLinks', + 'wpseo_xml' => 'WPSEO_Option_XML', + 'wpseo_ms' => 'WPSEO_Option_MS', + 'wpseo_taxonomy_meta' => 'WPSEO_Taxonomy_Meta', + ); + + protected static $option_instances; + + /** + * @var object Instance of this class + */ + protected static $instance; + + + /** + * Instantiate all the WPSEO option management classes + */ + protected function __construct() { + foreach ( self::$options as $option_name => $option_class ) { + if ( class_exists( $option_class ) ) { + self::$option_instances[ $option_name ] = call_user_func( array( $option_class, 'get_instance' ) ); + } + else { + unset( self::$options[ $option_name ] ); + } + } + } + + /** + * Get the singleton instance of this class + * + * @return object + */ + public static function get_instance() { + if ( ! ( self::$instance instanceof self ) ) { + self::$instance = new self(); + } + + return self::$instance; + } + + + /** + * Check whether the current user is allowed to access the configuration. + * + * @todo [JRF => whomever] when someone would reorganize the classes, this should maybe + * be moved to a general WPSEO_Utils class. Obviously all calls to this method should be + * adjusted in that case. + * + * @return boolean + */ + public static function grant_access() { + if ( ! is_multisite() ) { + return true; + } + + $options = get_site_option( 'wpseo_ms' ); + if ( $options['access'] === 'admin' && current_user_can( 'manage_options' ) ) { + return true; + } + + if ( $options['access'] === 'superadmin' && ! is_super_admin() ) { + return false; + } + + return true; + } + + + /** + * Get the group name of an option for use in the settings form + * + * @param string $option_name the option for which you want to retrieve the option group name + * + * @return string|bool + */ + public static function get_group_name( $option_name ) { + if ( isset( self::$option_instances[ $option_name ] ) ) { + return self::$option_instances[ $option_name ]->group_name; + } + + return false; + } + + + /** + * Get a specific default value for an option + * + * @param string $option_name The option for which you want to retrieve a default + * @param string $key The key within the option who's default you want + * + * @return mixed + */ + public static function get_default( $option_name, $key ) { + if ( isset( self::$option_instances[ $option_name ] ) ) { + $defaults = self::$option_instances[ $option_name ]->get_defaults(); + if ( isset( $defaults[ $key ] ) ) { + return $defaults[ $key ]; + } + } + + return null; + } + + + /** + * Update a site_option + * + * @param string $option_name The option name of the option to save + * @param mized $value The new value for the option + * + * @return bool + */ + public static function update_site_option( $option_name, $value ) { + if ( is_network_admin() && isset( self::$option_instances[ $option_name ] ) ) { + return self::$option_instances[ $option_name ]->update_site_option( $value ); + } + else { + return false; + } + } + + + /** + * Get the instantiated option instance + * + * @param string $option_name The option for which you want to retrieve the instance + * + * @return object|bool + */ + public static function get_option_instance( $option_name ) { + if ( isset( self::$option_instances[ $option_name ] ) ) { + return self::$option_instances[ $option_name ]; + } + + return false; + } + + + /** + * Retrieve an array of the options which should be included in get_all() and reset(). + * + * @static + * @return array Array of option names + */ + public static function get_option_names() { + static $option_names = array(); + + if ( $option_names === array() ) { + foreach ( self::$option_instances as $option_name => $option_object ) { + if ( $option_object->include_in_all === true ) { + $option_names[] = $option_name; + } + } + $option_names = apply_filters( 'wpseo_options', $option_names ); + } + + return $option_names; + } + + + /** + * Retrieve all the options for the SEO plugin in one go. + * + * @todo [JRF] see if we can get some extra efficiency for this one, though probably not as options may + * well change between calls (enriched defaults and such) + * + * @static + * @return array Array combining the values of (nearly) all the options + */ + public static function get_all() { + $all_options = array(); + $option_names = self::get_option_names(); + + if ( is_array( $option_names ) && $option_names !== array() ) { + foreach ( $option_names as $option_name ) { + if ( self::$option_instances[ $option_name ]->multisite_only !== true ) { + $option = get_option( $option_name ); + } + else { + $option = get_site_option( $option_name ); + } + + if ( is_array( $option ) && $option !== array() ) { + $all_options = array_merge( $all_options, $option ); + } + } + } + + return $all_options; + } + + + /** + * Run the clean up routine for one or all options + * + * @param array|string $option_name (optional) the option you want to clean or an array of + * option names for the options you want to clean. + * If not set, all options will be cleaned + * @param string $current_version (optional) Version from which to upgrade, if not set, + * version specific upgrades will be disregarded + * + * @return void + */ + public static function clean_up( $option_name = null, $current_version = null ) { + if ( isset( $option_name ) && is_string( $option_name ) && $option_name !== '' ) { + if ( isset( self::$option_instances[ $option_name ] ) ) { + self::$option_instances[ $option_name ]->clean( $current_version ); + } + } elseif ( isset( $option_name ) && is_array( $option_name ) && $option_name !== array() ) { + foreach ( $option_name as $option ) { + if ( isset( self::$option_instances[ $option ] ) ) { + self::$option_instances[ $option ]->clean( $current_version ); + } + } + } else { + foreach ( self::$option_instances as $instance ) { + $instance->clean( $current_version ); + } + + // If we've done a full clean-up, we can safely remove this really old option + delete_option( 'wpseo_indexation' ); + } + } + + + /** + * Check that all options exist in the database and add any which don't + * + * @return void + */ + public static function ensure_options_exist() { + foreach ( self::$option_instances as $instance ) { + $instance->maybe_add_option(); + } + } + + + /** + * Correct the inadvertent removal of the fallback to default values from the breadcrumbs + * + * @since 1.5.2.3 + */ + public static function bring_back_breadcrumb_defaults() { + if ( isset( self::$option_instances['wpseo_internallinks'] ) ) { + self::$option_instances['wpseo_internallinks']->bring_back_defaults(); + } + } + + + /** + * Initialize some options on first install/activate/reset + * + * @static + * @return void + */ + public static function initialize() { + /* Make sure title_test and description_test function are available even when called + from the isolated activation */ + require_once( WPSEO_PATH . 'inc/wpseo-non-ajax-functions.php' ); + +// wpseo_title_test(); + wpseo_description_test(); + + /* Force WooThemes to use WordPress SEO data. */ + if ( function_exists( 'woo_version_init' ) ) { + update_option( 'seo_woo_use_third_party_data', 'true' ); + } + } + + + /** + * Reset all options to their default values and rerun some tests + * + * @static + * @return void + */ + public static function reset() { + if ( ! is_multisite() ) { + $option_names = self::get_option_names(); + if ( is_array( $option_names ) && $option_names !== array() ) { + foreach ( $option_names as $option_name ) { + delete_option( $option_name ); + update_option( $option_name, get_option( $option_name ) ); + } + } + } + else { + // Reset MS blog based on network default blog setting + self::reset_ms_blog( get_current_blog_id() ); + } + + self::initialize(); + } + + + /** + * Initialize default values for a new multisite blog + * + * @static + * + * @param bool $force_init Whether to always do the initialization routine (title/desc test) + * @return void + */ + public static function maybe_set_multisite_defaults( $force_init = false ) { + $option = get_option( 'wpseo' ); + + if ( is_multisite() ) { + if ( $option['ms_defaults_set'] === false ) { + self::reset_ms_blog( get_current_blog_id() ); + self::initialize(); + } + else if ( $force_init === true ) { + self::initialize(); + } + } + } + + + /** + * Reset all options for a specific multisite blog to their default values based upon a + * specified default blog if one was chosen on the network page or the plugin defaults if it was not + * + * @static + * + * @param int|string $blog_id Blog id of the blog for which to reset the options + * + * @return void + */ + public static function reset_ms_blog( $blog_id ) { + if ( is_multisite() ) { + $options = get_site_option( 'wpseo_ms' ); + $option_names = self::get_option_names(); + + if ( is_array( $option_names ) && $option_names !== array() ) { + $base_blog_id = $blog_id; + if ( $options['defaultblog'] !== '' && $options['defaultblog'] != 0 ) { + $base_blog_id = $options['defaultblog']; + } + + foreach ( $option_names as $option_name ) { + delete_blog_option( $blog_id, $option_name ); + + $new_option = get_blog_option( $base_blog_id, $option_name ); + + /* Remove sensitive, theme dependent and site dependent info */ + if ( isset( self::$option_instances[ $option_name ] ) && self::$option_instances[ $option_name ]->ms_exclude !== array() ) { + foreach ( self::$option_instances[ $option_name ]->ms_exclude as $key ) { + unset( $new_option[ $key ] ); + } + } + + if ( $option_name === 'wpseo' ) { + $new_option['ms_defaults_set'] = true; + } + + update_blog_option( $blog_id, $option_name, $new_option ); + } + } + } + } + + + /* ************** METHODS FOR ACTIONS TO TAKE ON CERTAIN OPTION UPDATES ****************/ + + /** + * (Un-)schedule the yoast tracking cronjob if the tracking option has changed + * + * @internal Better to be done here, rather than in the Yoast_Tracking class as + * class-tracking.php may not be loaded and might not need to be (lean loading). + * + * @todo [JRF => whomever] when someone would reorganize the classes, this should maybe + * be moved to a general WPSEO_Utils class. Obviously all calls to this method should be + * adjusted in that case. + * + * @todo - [JRF => Yoast] check if this has any impact on other Yoast plugins which may + * use the same tracking schedule hook. If so, maybe get any other yoast plugin options, + * check for the tracking status and unschedule based on the combined status. + * + * @static + * + * @param mixed $disregard Not needed - passed by add/update_option action call + * Option name if option was added, old value if option was updated + * @param array $value The (new/current) value of the wpseo option + * @param bool $force_unschedule Whether to force an unschedule (i.e. on deactivate) + * + * @return void + */ + public static function schedule_yoast_tracking( $disregard, $value, $force_unschedule = false ) { + $current_schedule = wp_next_scheduled( 'yoast_tracking' ); + + if ( $force_unschedule !== true && ( $value['yoast_tracking'] === true && $current_schedule === false ) ) { + // The tracking checks daily, but only sends new data every 7 days. + wp_schedule_event( time(), 'daily', 'yoast_tracking' ); + } elseif ( $force_unschedule === true || ( $value['yoast_tracking'] === false && $current_schedule !== false ) ) { + wp_clear_scheduled_hook( 'yoast_tracking' ); + } + } + + + /** + * Clears the WP or W3TC cache depending on which is used + * + * @todo [JRF => whomever] when someone would reorganize the classes, this should maybe + * be moved to a general WPSEO_Utils class. Obviously all calls to this method should be + * adjusted in that case. + * + * @static + * @return void + */ + public static function clear_cache() { + if ( function_exists( 'w3tc_pgcache_flush' ) ) { + w3tc_pgcache_flush(); + } elseif ( function_exists( 'wp_cache_clear_cache' ) ) { + wp_cache_clear_cache(); + } + } + + + /** + * Flush W3TC cache after succesfull update/add of taxonomy meta option + * + * @todo [JRF => whomever] when someone would reorganize the classes, this should maybe + * be moved to a general WPSEO_Utils class. Obviously all calls to this method should be + * adjusted in that case. + * + * @todo [JRF => whomever] check the above and this function to see if they should be combined or really + * do something significantly different + * + * @static + * @return void + */ + public static function flush_w3tc_cache() { + if ( defined( 'W3TC_DIR' ) && function_exists( 'w3tc_objectcache_flush' ) ) { + w3tc_objectcache_flush(); + } + } + + + /** + * Clear rewrite rules + * + * @todo [JRF => whomever] when someone would reorganize the classes, this should maybe + * be moved to a general WPSEO_Utils class. Obviously all calls to this method should be + * adjusted in that case. + * + * @static + * @return void + */ + public static function clear_rewrites() { + delete_option( 'rewrite_rules' ); + } + + + } /* End of class WPSEO_Options */ + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/inc/class-wpseo-replace-vars.php b/wp-content/plugins/wordpress-seo/inc/class-wpseo-replace-vars.php new file mode 100644 index 0000000..9a89e85 --- /dev/null +++ b/wp-content/plugins/wordpress-seo/inc/class-wpseo-replace-vars.php @@ -0,0 +1,1192 @@ + '', + 'name' => '', + 'post_author' => '', + 'post_content' => '', + 'post_date' => '', + 'post_excerpt' => '', + 'post_modified' => '', + 'post_title' => '', + 'taxonomy' => '', + 'term_id' => '', + 'term404' => '', + ); + + /** + * @var object Current post/page/cpt information + */ + protected $args; + + /** + * @var array Help texts for use in WPSEO -> Titles and Meta's help tabs + */ + protected static $help_texts = array(); + + /** + * @var array Register of additional variable replacements registered by other plugins/themes + */ + protected static $external_replacements = array(); + + + /** + * Constructor + * + * @return \WPSEO_Replace_Vars + */ + public function __construct() { + } + + + /** + * Setup the help texts and external replacements as statics so they will be available to all instances + */ + public static function setup_statics_once() { + // + if ( self::$help_texts === array() ) { + self::set_basic_help_texts(); + self::set_advanced_help_texts(); + } + + if ( self::$external_replacements === array() ) { + /** + * Action: 'wpseo_register_extra_replacements' - Allows for registration of additional + * variables to replace + */ + do_action( 'wpseo_register_extra_replacements' ); + } + } + + + /** + * Register new replacement %%variables%% + * For use by other plugins/themes to register extra variables + * + * @see wpseo_register_var_replacement() for a usage example + * + * @param string $var The name of the variable to replace, i.e. '%%var%%' + * - the surrounding %% are optional + * @param mixed $replace_function Function or method to call to retrieve the replacement value for the variable + * Uses the same format as add_filter/add_action function parameter and + * should *return* the replacement value. DON'T echo it! + * @param string $type Type of variable: 'basic' or 'advanced', defaults to 'advanced' + * @param string $help_text Help text to be added to the help tab for this variable + * + * @return bool Whether the replacement function was succesfully registered + */ + public static function register_replacement( $var, $replace_function, $type = 'advanced', $help_text = '' ) { + $success = false; + + if ( is_string( $var ) && $var !== '' ) { + $var = self::remove_var_delimiter( $var ); + + if ( preg_match( '`^[A-Z0-9_-]+$`i', $var ) === false ) { + trigger_error( __( 'A replacement variable can only contain alphanumeric characters, an underscore or a dash. Try renaming your variable.', 'wordpress-seo' ), E_USER_WARNING ); + } elseif ( strpos( $var, 'cf_' ) === 0 || strpos( $var, 'ct_' ) === 0 ) { + trigger_error( __( 'A replacement variable can not start with "%%cf_" or "%%ct_" as these are reserved for the WPSEO standard variable variables for custom fields and custom taxonomies. Try making your variable name unique.', 'wordpress-seo' ), E_USER_WARNING ); + } elseif ( ! method_exists( __CLASS__, 'retrieve_' . $var ) ) { + if ( ! isset( self::$external_replacements[ $var ] ) ) { + self::$external_replacements[ $var ] = $replace_function; + self::register_help_text( $type, $var, $help_text ); + $success = true; + } else { + trigger_error( __( 'A replacement variable with the same name has already been registered. Try making your variable name more unique.', 'wordpress-seo' ), E_USER_WARNING ); + } + } else { + trigger_error( __( 'You cannot overrule a WPSEO standard variable replacement by registering a variable with the same name. Use the "wpseo_replacements" filter instead to adjust the replacement value.', 'wordpress-seo' ), E_USER_WARNING ); + } + } + + return $success; + } + + + /** + * Replace `%%variable_placeholders%%` with their real value based on the current requested page/post/cpt/etc + * + * @param string $string the string to replace the variables in. + * @param array $args the object some of the replacement values might come from, + * could be a post, taxonomy or term. + * @param array $omit variables that should not be replaced by this function. + * + * @return string + */ + public function replace( $string, $args, $omit = array() ) { + + $string = strip_tags( $string ); + + // Let's see if we can bail super early. + if ( strpos( $string, '%%' ) === false ) { + return wpseo_standardize_whitespace( $string ); + } + + $args = (array) $args; + if ( isset( $args['post_content'] ) && ! empty( $args['post_content'] ) ) { + $args['post_content'] = wpseo_strip_shortcode( $args['post_content'] ); + } + if ( isset( $args['post_excerpt'] ) && ! empty( $args['post_excerpt'] ) ) { + $args['post_excerpt'] = wpseo_strip_shortcode( $args['post_excerpt'] ); + } + $this->args = (object) wp_parse_args( $args, $this->defaults ); + + // Clean $omit array + if ( is_array( $omit ) && $omit !== array() ) { + $omit = array_map( array( __CLASS__, 'remove_var_delimiter' ), $omit ); + } + + $replacements = array(); + if ( preg_match_all( '`%%([^%]+(%%single)?)%%?`iu', $string, $matches ) ) { + $replacements = $this->set_up_replacements( $matches, $omit ); + } + + /** + * Filter: 'wpseo_replacements' - Allow customization of the replacements before they are applied + * + * @api array $replacements The replacements + */ + $replacements = apply_filters( 'wpseo_replacements', $replacements ); + + // Do the actual replacements + if ( is_array( $replacements ) && $replacements !== array() ) { + $string = str_replace( array_keys( $replacements ), array_values( $replacements ), $string ); + } + + /** + * Filter: 'wpseo_replacements_final' - Allow overruling of whether or not to remove placeholders + * which didn't yield a replacement + * + * @example add_filter( 'wpseo_replacements_final', '__return_false' ); + * + * @api bool $final + */ + if ( apply_filters( 'wpseo_replacements_final', true ) === true && ( isset( $matches[1] ) && is_array( $matches[1] ) ) ) { + // Remove non-replaced variables + $remove = array_diff( $matches[1], $omit ); // Make sure the $omit variables do not get removed + $remove = array_map( array( __CLASS__, 'add_var_delimiter' ), $remove ); + $string = str_replace( $remove, '', $string ); + } + + // Undouble separators which have nothing between them, i.e. where a non-replaced variable was removed + if ( isset( $replacements['%%sep%%'] ) && ( is_string( $replacements['%%sep%%'] ) && $replacements['%%sep%%'] !== '' ) ) { + $q_sep = preg_quote( $replacements['%%sep%%'], '`' ); + $string = preg_replace( '`' . $q_sep . '(?:\s*' . $q_sep . ')*`u', $replacements['%%sep%%'], $string ); + } + + // Remove superfluous whitespace + $string = wpseo_standardize_whitespace( $string ); + + return trim( $string ); + } + + + /** + * Retrieve the replacements for the variables found. + * + * @param array $matches variables found in the original string - regex result. + * @param array $omit variables that should not be replaced by this function. + * + * @return array retrieved replacements - this might be a smaller array as some variables + * may not yield a replacement in certain contexts. + */ + private function set_up_replacements( $matches, $omit ) { + + $replacements = array(); + + // @todo -> figure out a way to deal with external functions starting with cf_/ct_ + foreach ( $matches[1] as $k => $var ) { + + // Don't set up replacements which should be omitted + if ( in_array( $var, $omit, true ) ) { + continue; + } + + // Deal with variable variable names first + if ( strpos( $var, 'cf_' ) === 0 ) { + $replacement = $this->retrieve_cf_custom_field_name( $var ); + } elseif ( strpos( $var, 'ct_desc_' ) === 0 ) { + $replacement = $this->retrieve_ct_desc_custom_tax_name( $var ); + } elseif ( strpos( $var, 'ct_' ) === 0 ) { + $single = ( isset( $matches[2][ $k ] ) && $matches[2][ $k ] !== '' ) ? true : false; + $replacement = $this->retrieve_ct_custom_tax_name( $var, $single ); + } // Deal with non-variable variable names + elseif ( method_exists( $this, 'retrieve_' . $var ) ) { + $method_name = 'retrieve_' . $var; + $replacement = $this->$method_name(); + } // Deal with externally defined variable names + elseif ( isset( self::$external_replacements[ $var ] ) && ! is_null( self::$external_replacements[ $var ] ) ) { + $replacement = call_user_func( self::$external_replacements[ $var ], $var ); + } + + // Replacement retrievals can return null if no replacement can be determined, root those outs + if ( isset( $replacement ) ) { + $var = self::add_var_delimiter( $var ); + $replacements[ $var ] = $replacement; + } + } + + return $replacements; + } + + + + /* *********************** BASIC VARIABLES ************************** */ + + /** + * Retrieve the post/cpt categories (comma separated) for use as replacement string. + * + * @return string|null + */ + private function retrieve_category() { + $replacement = null; + + if ( ! empty( $this->args->ID ) ) { + $cat = $this->get_terms( $this->args->ID, 'category' ); + if ( $cat !== '' ) { + $replacement = $cat; + } + } + + if ( ( ! isset( $replacement ) || $replacement === '' ) && ( isset( $this->args->cat_name ) && ! empty( $this->args->cat_name ) ) ) { + $replacement = $this->args->cat_name; + } + + return $replacement; + } + + /** + * Retrieve the category description for use as replacement string. + * + * @return string|null + */ + private function retrieve_category_description() { + return $this->retrieve_term_description(); + } + + /** + * Retrieve the date of the post/page/cpt for use as replacement string. + * + * @return string|null + */ + private function retrieve_date() { + $replacement = null; + + if ( $this->args->post_date != '' ) { + $replacement = mysql2date( get_option( 'date_format' ), $this->args->post_date, true ); + } else { + if ( get_query_var( 'day' ) && get_query_var( 'day' ) != '' ) { + $replacement = get_the_date(); + } else { + if ( single_month_title( ' ', false ) && single_month_title( ' ', false ) != '' ) { + $replacement = single_month_title( ' ', false ); + } elseif ( get_query_var( 'year' ) != '' ) { + $replacement = get_query_var( 'year' ); + } + } + } + + return $replacement; + } + + /** + * Retrieve the post/page/cpt excerpt for use as replacement string. + * The excerpt will be auto-generated if it does not exist. + * + * @return string|null + */ + private function retrieve_excerpt() { + $replacement = null; + + if ( ! empty( $this->args->ID ) ) { + if ( $this->args->post_excerpt !== '' ) { + $replacement = strip_tags( $this->args->post_excerpt ); + } elseif ( $this->args->post_content !== '' ) { + $replacement = wp_html_excerpt( strip_shortcodes( $this->args->post_content ), 155 ); + } + } + + return $replacement; + } + + /** + * Retrieve the post/page/cpt excerpt for use as replacement string (without auto-generation). + * + * @return string|null + */ + private function retrieve_excerpt_only() { + $replacement = null; + + if ( ! empty( $this->args->ID ) && $this->args->post_excerpt !== '' ) { + $replacement = strip_tags( $this->args->post_excerpt ); + } + + return $replacement; + } + + /** + * Retrieve the title of the parent page of the current page/cpt for use as replacement string. + * Only applicable for hierarchical post types. + * + * @todo - check: shouldn't this use $this->args as well ? + * + * @return string|null + */ + private function retrieve_parent_title() { + $replacement = null; + + if ( ! isset( $replacement ) && ( ( is_singular() || is_admin() ) && isset( $GLOBALS['post'] ) ) ) { + if ( isset( $GLOBALS['post']->post_parent ) && 0 != $GLOBALS['post']->post_parent ) { + $replacement = get_the_title( $GLOBALS['post']->post_parent ); + } + } + + return $replacement; + } + + /** + * Retrieve the current search phrase for use as replacement string. + * + * @return string|null + */ + private function retrieve_searchphrase() { + $replacement = null; + + if ( ! isset( $replacement ) ) { + $search = get_query_var( 's' ); + if ( $search !== '' ) { + $replacement = esc_html( $search ); + } + } + + return $replacement; + } + + /** + * Retrieve the separator for use as replacement string. + * + * @return string + */ + private function retrieve_sep() { + $replacement = WPSEO_Options::get_default( 'wpseo_titles', 'separator' ); + + // Get the titles option and the separator options + $titles_options = get_option( 'wpseo_titles' ); + $seperator_options = WPSEO_Option_Titles::get_instance()->get_separator_options(); + + // This should always be set, but just to be sure + if ( isset( $seperator_options[ $titles_options['separator'] ] ) ) { + // Set the new replacement + $replacement = $seperator_options[ $titles_options['separator'] ]; + } + + /** + * Filter: 'wpseo_replacements_filter_sep' - Allow customization of the separator character(s) + * + * @api string $replacement The current separator + */ + + return apply_filters( 'wpseo_replacements_filter_sep', $replacement ); + } + + /** + * Retrieve the site's tag line / description for use as replacement string. + * + * @return string|null + */ + private function retrieve_sitedesc() { + static $replacement; + + if ( ! isset( $replacement ) ) { + $description = trim( strip_tags( get_bloginfo( 'description' ) ) ); + if ( $description !== '' ) { + $replacement = $description; + } + } + + return $replacement; + } + + + /** + * Retrieve the site's name for use as replacement string. + * + * @return string|null + */ + private function retrieve_sitename() { + static $replacement; + + if ( ! isset( $replacement ) ) { + $sitename = trim( strip_tags( get_bloginfo( 'name' ) ) ); + if ( $sitename !== '' ) { + $replacement = $sitename; + } + } + + return $replacement; + } + + /** + * Retrieve the current tag/tags for use as replacement string. + * + * @return string|null + */ + private function retrieve_tag() { + $replacement = null; + + if ( isset( $this->args->ID ) ) { + $tags = $this->get_terms( $this->args->ID, 'post_tag' ); + if ( $tags !== '' ) { + $replacement = $tags; + } + } + + return $replacement; + } + + /** + * Retrieve the tag description for use as replacement string. + * + * @return string|null + */ + private function retrieve_tag_description() { + return $this->retrieve_term_description(); + } + + /** + * Retrieve the term description for use as replacement string. + * + * @return string|null + */ + private function retrieve_term_description() { + $replacement = null; + + if ( isset( $this->args->term_id ) && ! empty( $this->args->taxonomy ) ) { + $term_desc = get_term_field( 'description', $this->args->term_id, $this->args->taxonomy ); + if ( $term_desc !== '' ) { + $replacement = trim( strip_tags( $term_desc ) ); + } + } + + return $replacement; + } + + /** + * Retrieve the term name for use as replacement string. + * + * @return string|null + */ + private function retrieve_term_title() { + $replacement = null; + + if ( ! empty( $this->args->taxonomy ) && ! empty( $this->args->name ) ) { + $replacement = $this->args->name; + } + + return $replacement; + } + + /** + * Retrieve the title of the post/page/cpt for use as replacement string. + * + * @return string|null + */ + private function retrieve_title() { + $replacement = null; + + if ( is_string( $this->args->post_title ) && $this->args->post_title !== '' ) { + $replacement = stripslashes( $this->args->post_title ); + } + + return $replacement; + } + + + + /* *********************** ADVANCED VARIABLES ************************** */ + + /** + * Determine the page numbering of the current post/page/cpt + * + * @param string $request 'nr'|'max' - whether to return the page number or the max number of pages + * + * @return int|null + */ + private function determine_pagenumbering( $request = 'nr' ) { + global $wp_query, $post; + $max_num_pages = null; + $page_number = null; + + $max_num_pages = 1; + + if ( ! is_singular() ) { + $page_number = get_query_var( 'paged' ); + if ( $page_number === 0 || $page_number === '' ) { + $page_number = 1; + } + + if ( isset( $wp_query->max_num_pages ) && ( $wp_query->max_num_pages != '' && $wp_query->max_num_pages != 0 ) ) { + $max_num_pages = $wp_query->max_num_pages; + } + } else { + $page_number = get_query_var( 'page' ); + if ( $page_number === 0 || $page_number === '' ) { + $page_number = 1; + } + + if ( isset( $post->post_content ) ) { + $max_num_pages = substr_count( $post->post_content, '' ) + 1; + } + } + + $return = null; + + switch ( $request ) { + case 'nr': + $return = $page_number; + break; + case 'max': + $return = $max_num_pages; + break; + } + + return $return; + } + + + /** + * Determine the post type names for the current post/page/cpt + * + * @param string $request 'single'|'plural' - whether to return the single or plural form + * + * @return string|null + */ + private function determine_pt_names( $request = 'single' ) { + global $wp_query; + $pt_single = null; + $pt_plural = null; + + if ( isset( $wp_query->query_vars['post_type'] ) && ( ( is_string( $wp_query->query_vars['post_type'] ) && $wp_query->query_vars['post_type'] !== '' ) || ( is_array( $wp_query->query_vars['post_type'] ) && $wp_query->query_vars['post_type'] !== array() ) ) ) { + $post_type = $wp_query->query_vars['post_type']; + } else { + // Make it work in preview mode + $post_type = $wp_query->get_queried_object()->post_type; + } + + if ( is_array( $post_type ) ) { + $post_type = reset( $post_type ); + } + + if ( $post_type !== '' ) { + $pt = get_post_type_object( $post_type ); + $pt_plural = $pt_single = $pt->name; + if ( isset( $pt->labels->singular_name ) ) { + $pt_single = $pt->labels->singular_name; + } + if ( isset( $pt->labels->name ) ) { + $pt_plural = $pt->labels->name; + } + } + + $return = null; + + switch ( $request ) { + case 'single': + $return = $pt_single; + break; + case 'plural': + $return = $pt_plural; + break; + } + + return $return; + } + + /** + * Retrieve the attachment caption for use as replacement string. + * + * @return string|null + */ + private function retrieve_caption() { + return $this->retrieve_excerpt_only(); + } + + + /** + * Retrieve a post/page/cpt's custom field value for use as replacement string + * + * @param string $var The complete variable to replace which includes the name of + * the custom field which value is to be retrieved. + * + * @return string|null + */ + private function retrieve_cf_custom_field_name( $var ) { + global $post; + $replacement = null; + + if ( is_string( $var ) && $var !== '' ) { + $field = substr( $var, 3 ); + if ( ( is_singular() || is_admin() ) && ( is_object( $post ) && isset( $post->ID ) ) ) { + $name = get_post_meta( $post->ID, $field, true ); + if ( $name !== '' ) { + $replacement = $name; + } + } + } + + return $replacement; + } + + + /** + * Retrieve a post/page/cpt's custom taxonomies for use as replacement string + * + * @param string $var The complete variable to replace which includes the name of + * the custom taxonomy which value(s) is to be retrieved. + * @param bool $single Whether to retrieve only the first or all values for the taxonomy + * + * @return string|null + */ + private function retrieve_ct_custom_tax_name( $var, $single = false ) { + $replacement = null; + + if ( ( is_string( $var ) && $var !== '' ) && ! empty( $this->args->ID ) ) { + $tax = substr( $var, 3 ); + $name = $this->get_terms( $this->args->ID, $tax, $single ); + if ( $name !== '' ) { + $replacement = $name; + } + } + + return $replacement; + } + + + /** + * Retrieve a post/page/cpt's custom taxonomies description for use as replacement string + * + * @param string $var The complete variable to replace which includes the name of + * the custom taxonomy which description is to be retrieved. + * + * @return string|null + */ + private function retrieve_ct_desc_custom_tax_name( $var ) { + global $post; + $replacement = null; + + if ( is_string( $var ) && $var !== '' ) { + $tax = substr( $var, 8 ); + if ( is_object( $post ) && isset( $post->ID ) ) { + $terms = get_the_terms( $post->ID, $tax ); + if ( is_array( $terms ) && $terms !== array() ) { + $term = current( $terms ); + $term_desc = get_term_field( 'description', $term->term_id, $tax ); + if ( $term_desc !== '' ) { + $replacement = $term_desc; + } + } + } + } + + return $replacement; + } + + /** + * Retrieve the current date for use as replacement string. + * + * @return string + */ + private function retrieve_currentdate() { + static $replacement; + + if ( ! isset( $replacement ) ) { + $replacement = date_i18n( get_option( 'date_format' ) ); + } + + return $replacement; + } + + /** + * Retrieve the current day for use as replacement string. + * + * @return string + */ + private function retrieve_currentday() { + static $replacement; + + if ( ! isset( $replacement ) ) { + $replacement = date_i18n( 'j' ); + } + + return $replacement; + } + + /** + * Retrieve the current month for use as replacement string. + * + * @return string + */ + private function retrieve_currentmonth() { + static $replacement; + + if ( ! isset( $replacement ) ) { + $replacement = date_i18n( 'F' ); + } + + return $replacement; + } + + /** + * Retrieve the current time for use as replacement string. + * + * @return string + */ + private function retrieve_currenttime() { + static $replacement; + + if ( ! isset( $replacement ) ) { + $replacement = date_i18n( get_option( 'time_format' ) ); + } + + return $replacement; + } + + /** + * Retrieve the current year for use as replacement string. + * + * @return string + */ + private function retrieve_currentyear() { + static $replacement; + + if ( ! isset( $replacement ) ) { + $replacement = date_i18n( 'Y' ); + } + + return $replacement; + } + + /** + * Retrieve the post/page/cpt's focus keyword for use as replacement string. + * + * @return string|null + */ + private function retrieve_focuskw() { + $replacement = null; + + if ( ! empty( $this->args->ID ) ) { + $focus_kw = WPSEO_Meta::get_value( 'focuskw', $this->args->ID ); + if ( $focus_kw !== '' ) { + $replacement = $focus_kw; + } + } + + return $replacement; + } + + /** + * Retrieve the post/page/cpt ID for use as replacement string. + * + * @return string|null + */ + private function retrieve_id() { + $replacement = null; + + if ( ! empty( $this->args->ID ) ) { + $replacement = $this->args->ID; + } + + return $replacement; + } + + /** + * Retrieve the post/page/cpt modified time for use as replacement string. + * + * @return string|null + */ + private function retrieve_modified() { + $replacement = null; + + if ( ! empty( $this->args->post_modified ) ) { + $replacement = mysql2date( get_option( 'date_format' ), $this->args->post_modified, true ); + } + + return $replacement; + } + + /** + * Retrieve the post/page/cpt author's "nice name" for use as replacement string. + * + * @return string|null + */ + private function retrieve_name() { + $replacement = null; + + $user_id = $this->retrieve_userid(); + $name = get_the_author_meta( 'display_name', $user_id ); + if ( $name !== '' ) { + $replacement = $name; + } + + return $replacement; + } + + /** + * Retrieve the post/page/cpt author's users description for use as a replacement string. + * + * @return null|string + */ + private function retrieve_user_description() { + $replacement = null; + + $user_id = $this->retrieve_userid(); + $description = get_the_author_meta( 'description', $user_id ); + if ( $description != '' ) { + $replacement = $description; + } + + return $replacement; + } + + /** + * Retrieve the current page number with context (i.e. 'page 2 of 4') for use as replacement string. + * + * @return string + */ + private function retrieve_page() { + $replacement = null; + + $max = $this->determine_pagenumbering( 'max' ); + $nr = $this->determine_pagenumbering( 'nr' ); + $sep = $this->retrieve_sep(); + + if ( $max > 1 && $nr > 1 ) { + $replacement = sprintf( $sep . ' ' . __( 'Page %d of %d', 'wordpress-seo' ), $nr, $max ); + } + + return $replacement; + } + + /** + * Retrieve the current page number for use as replacement string. + * + * @return string|null + */ + private function retrieve_pagenumber() { + $replacement = null; + + $nr = $this->determine_pagenumbering( 'nr' ); + if ( isset( $nr ) && $nr > 0 ) { + $replacement = (string) $nr; + } + + return $replacement; + } + + /** + * Retrieve the current page total for use as replacement string. + * + * @return string|null + */ + private function retrieve_pagetotal() { + $replacement = null; + + $max = $this->determine_pagenumbering( 'max' ); + if ( isset( $max ) && $max > 0 ) { + $replacement = (string) $max; + } + + return $replacement; + } + + /** + * Retrieve the post type plural label for use as replacement string. + * + * @return string|null + */ + private function retrieve_pt_plural() { + $replacement = null; + + $name = $this->determine_pt_names( 'plural' ); + if ( isset( $name ) && $name !== '' ) { + $replacement = $name; + } + + return $replacement; + } + + /** + * Retrieve the post type single label for use as replacement string. + * + * @return string|null + */ + private function retrieve_pt_single() { + $replacement = null; + + $name = $this->determine_pt_names( 'single' ); + if ( isset( $name ) && $name !== '' ) { + $replacement = $name; + } + + return $replacement; + } + + /** + * Retrieve the slug which caused the 404 for use as replacement string. + * + * @return string|null + */ + private function retrieve_term404() { + $replacement = null; + + if ( $this->args->term404 !== '' ) { + $replacement = sanitize_text_field( str_replace( '-', ' ', $this->args->term404 ) ); + } else { + $error_request = get_query_var( 'pagename' ); + if ( $error_request !== '' ) { + $replacement = sanitize_text_field( str_replace( '-', ' ', $error_request ) ); + } else { + $error_request = get_query_var( 'name' ); + if ( $error_request !== '' ) { + $replacement = sanitize_text_field( str_replace( '-', ' ', $error_request ) ); + } + } + } + + return $replacement; + } + + /** + * Retrieve the post/page/cpt author's user id for use as replacement string. + * + * @return string + */ + private function retrieve_userid() { + $replacement = ! empty( $this->args->post_author ) ? $this->args->post_author : get_query_var( 'author' ); + + return $replacement; + } + + + + /* *********************** HELP TEXT RELATED ************************** */ + + /** + * Create a variable help text table + * + * @param string $type Either 'basic' or 'advanced' + * + * @return string Help text table + */ + private static function create_variable_help_table( $type ) { + if ( ! in_array( $type, array( 'basic', 'advanced' ), true ) ) { + return ''; + } + + $table = ' + '; + + foreach ( self::$help_texts[ $type ] as $replace => $help_text ) { + $table .= ' + + + + '; + } + + $table .= ' +
%%' . esc_html( $replace ) . '%%' . $help_text . '
'; + + return $table; + } + + /** + * Create the help text table for the basic variables for use in a help tab + * + * @return string + */ + public static function get_basic_help_texts() { + return self::create_variable_help_table( 'basic' ); + } + + + /** + * Create the help text table for the advanced variables for use in a help tab + * + * @return string + */ + public static function get_advanced_help_texts() { + return self::create_variable_help_table( 'advanced' ); + } + + + /** + * Set the help text for a user/plugin/theme defined extra variable. + * + * @param string $type Type of variable: 'basic' or 'advanced' + * @param string $replace Variable to replace, i.e. '%%var%%' + * @param string $help_text The actual help text string + */ + private static function register_help_text( $type, $replace, $help_text = '' ) { + if ( is_string( $replace ) && $replace !== '' ) { + $replace = self::remove_var_delimiter( $replace ); + + if ( ( is_string( $type ) && in_array( $type, array( 'basic', 'advanced' ), true ) ) && ( $replace !== '' && ! isset( self::$help_texts[ $type ][ $replace ] ) ) + ) { + self::$help_texts[ $type ][ $replace ] = $help_text; + } + } + } + + + /** + * Set/translate the help texts for the WPSEO standard basic variables. + */ + private static function set_basic_help_texts() { + self::$help_texts['basic'] = array( + 'date' => __( 'Replaced with the date of the post/page', 'wordpress-seo' ), + 'title' => __( 'Replaced with the title of the post/page', 'wordpress-seo' ), + 'parent_title' => __( 'Replaced with the title of the parent page of the current page', 'wordpress-seo' ), + 'sitename' => __( 'The site\'s name', 'wordpress-seo' ), + 'sitedesc' => __( 'The site\'s tag line / description', 'wordpress-seo' ), + 'excerpt' => __( 'Replaced with the post/page excerpt (or auto-generated if it does not exist)', 'wordpress-seo' ), + 'excerpt_only' => __( 'Replaced with the post/page excerpt (without auto-generation)', 'wordpress-seo' ), + 'tag' => __( 'Replaced with the current tag/tags', 'wordpress-seo' ), + 'category' => __( 'Replaced with the post categories (comma separated)', 'wordpress-seo' ), + 'category_description' => __( 'Replaced with the category description', 'wordpress-seo' ), + 'tag_description' => __( 'Replaced with the tag description', 'wordpress-seo' ), + 'term_description' => __( 'Replaced with the term description', 'wordpress-seo' ), + 'term_title' => __( 'Replaced with the term name', 'wordpress-seo' ), + 'searchphrase' => __( 'Replaced with the current search phrase', 'wordpress-seo' ), + 'sep' => __( 'The separator defined in your theme\'s wp_title() tag.', 'wordpress-seo' ), + ); + } + + /** + * Set/translate the help texts for the WPSEO standard advanced variables. + */ + private static function set_advanced_help_texts() { + self::$help_texts['advanced'] = array( + 'pt_single' => __( 'Replaced with the post type single label', 'wordpress-seo' ), + 'pt_plural' => __( 'Replaced with the post type plural label', 'wordpress-seo' ), + 'modified' => __( 'Replaced with the post/page modified time', 'wordpress-seo' ), + 'id' => __( 'Replaced with the post/page ID', 'wordpress-seo' ), + 'name' => __( 'Replaced with the post/page author\'s \'nicename\'', 'wordpress-seo' ), + 'user_description' => __( 'Replaced with the post/page author\'s \'Biographical Info\'', 'wordpress-seo' ), + 'userid' => __( 'Replaced with the post/page author\'s userid', 'wordpress-seo' ), + 'currenttime' => __( 'Replaced with the current time', 'wordpress-seo' ), + 'currentdate' => __( 'Replaced with the current date', 'wordpress-seo' ), + 'currentday' => __( 'Replaced with the current day', 'wordpress-seo' ), + 'currentmonth' => __( 'Replaced with the current month', 'wordpress-seo' ), + 'currentyear' => __( 'Replaced with the current year', 'wordpress-seo' ), + 'page' => __( 'Replaced with the current page number with context (i.e. page 2 of 4)', 'wordpress-seo' ), + 'pagetotal' => __( 'Replaced with the current page total', 'wordpress-seo' ), + 'pagenumber' => __( 'Replaced with the current page number', 'wordpress-seo' ), + 'caption' => __( 'Attachment caption', 'wordpress-seo' ), + 'focuskw' => __( 'Replaced with the posts focus keyword', 'wordpress-seo' ), + 'term404' => __( 'Replaced with the slug which caused the 404', 'wordpress-seo' ), + 'cf_' => __( 'Replaced with a posts custom field value', 'wordpress-seo' ), + 'ct_' => __( 'Replaced with a posts custom taxonomies, comma separated.', 'wordpress-seo' ), + 'ct_desc_' => __( 'Replaced with a custom taxonomies description', 'wordpress-seo' ), + ); + } + + + + + /* *********************** GENERAL HELPER METHODS ************************** */ + + /** + * Remove the '%%' delimiters from a variable string + * + * @param string $string Variable string to be cleaned + * + * @return string + */ + private static function remove_var_delimiter( $string ) { + return trim( $string, '%' ); + } + + /** + * Add the '%%' delimiters to a variable string + * + * @param string $string Variable string to be delimited + * + * @return string + */ + private static function add_var_delimiter( $string ) { + return '%%' . $string . '%%'; + } + + /** + * Retrieve a post's terms, comma delimited. + * + * @param int $id ID of the post to get the terms for. + * @param string $taxonomy The taxonomy to get the terms for this post from. + * @param bool $return_single If true, return the first term. + * + * @return string either a single term or a comma delimited string of terms. + */ + public function get_terms( $id, $taxonomy, $return_single = false ) { + + $output = ''; + + // If we're on a specific tag, category or taxonomy page, use that. + if ( is_category() || is_tag() || is_tax() ) { + global $wp_query; + $term = $wp_query->get_queried_object(); + $output = $term->name; + } elseif ( ! empty( $id ) && ! empty( $taxonomy ) ) { + $terms = get_the_terms( $id, $taxonomy ); + if ( is_array( $terms ) && $terms !== array() ) { + foreach ( $terms as $term ) { + if ( $return_single ) { + $output = $term->name; + break; + } else { + $output .= $term->name . ', '; + } + } + $output = rtrim( trim( $output ), ',' ); + } + } + + /** + * Allows filtering of the terms list used to replace %%category%%, %%tag%% and %%ct_%% variables + * @api string $output Comma-delimited string containing the terms + */ + + return apply_filters( 'wpseo_terms', $output ); + } + + } /* End of class WPSEO_Replace_Vars */ + + + /** + * Setup the class statics when the file is first loaded + */ + WPSEO_Replace_Vars::setup_statics_once(); + +} /* End of class-exists wrapper */ diff --git a/wp-content/plugins/wordpress-seo/inc/index.php b/wp-content/plugins/wordpress-seo/inc/index.php new file mode 100644 index 0000000..12c197f --- /dev/null +++ b/wp-content/plugins/wordpress-seo/inc/index.php @@ -0,0 +1,2 @@ + Yoast] check: if upgrade is run on multi-site installation, upgrade for all sites ? + * Maybe not necessary as it is now run on plugins_loaded, so upgrade will run as soon as any page + * on a site is requested. + */ +function wpseo_do_upgrade() { + /* Make sure title_test and description_test functions are available */ + require_once( WPSEO_PATH . 'inc/wpseo-non-ajax-functions.php' ); + + $option_wpseo = get_option( 'wpseo' ); + + WPSEO_Options::maybe_set_multisite_defaults( false ); + +// if ( $option_wpseo['version'] === '' || version_compare( $option_wpseo['version'], '1.2', '<' ) ) { +// add_action( 'init', 'wpseo_title_test' ); +// } + + if ( $option_wpseo['version'] === '' || version_compare( $option_wpseo['version'], '1.4.13', '<' ) ) { + // Run description test once theme has loaded + add_action( 'init', 'wpseo_description_test' ); + } + + if ( $option_wpseo['version'] === '' || version_compare( $option_wpseo['version'], '1.4.15', '<' ) ) { + add_action( 'shutdown', 'flush_rewrite_rules' ); + } + + if ( version_compare( $option_wpseo['version'], '1.5.0', '<' ) ) { + + // Clean up options and meta + WPSEO_Options::clean_up( null, $option_wpseo['version'] ); + WPSEO_Meta::clean_up(); + + // Add new capabilities on upgrade + wpseo_add_capabilities(); + } + + /* Only correct the breadcrumb defaults for upgrades from v1.5+ to v1.5.2.3, upgrades from earlier version + will already get this functionality in the clean_up routine. */ + if ( version_compare( $option_wpseo['version'], '1.4.25', '>' ) && version_compare( $option_wpseo['version'], '1.5.2.3', '<' ) ) { + add_action( 'init', array( 'WPSEO_Options', 'bring_back_breadcrumb_defaults' ), 3 ); + } + + if ( version_compare( $option_wpseo['version'], '1.4.25', '>' ) && version_compare( $option_wpseo['version'], '1.5.2.4', '<' ) ) { + /* Make sure empty maintax/mainpt strings will convert to 0 */ + WPSEO_Options::clean_up( 'wpseo_internallinks', $option_wpseo['version'] ); + + /* Remove slashes from taxonomy meta texts */ + WPSEO_Options::clean_up( 'wpseo_taxonomy_meta', $option_wpseo['version'] ); + } + + /* Clean up stray wpseo_ms options from the options table, option should only exist in the sitemeta table */ + delete_option( 'wpseo_ms' ); + + + // Make sure version nr gets updated for any version without specific upgrades + $option_wpseo = get_option( 'wpseo' ); // re-get to make sure we have the latest version + if ( version_compare( $option_wpseo['version'], WPSEO_VERSION, '<' ) ) { + update_option( 'wpseo', $option_wpseo ); + } + + // Make sure all our options always exist - issue #1245 + WPSEO_Options::ensure_options_exist(); +} + + +if ( ! function_exists( 'initialize_wpseo_front' ) ) { + function initialize_wpseo_front() { + $GLOBALS['wpseo_front'] = new WPSEO_Frontend; + } +} + + +if ( ! function_exists( 'yoast_breadcrumb' ) ) { + /** + * Template tag for breadcrumbs. + * + * @todo [JRF => Yoast/whomever] We could probably get rid of the 'breadcrumbs-enable' option key + * as the file is now only loaded when the template tag is encountered anyway. + * Only issue with that would be the removal of the bbPress crumb from within wpseo_frontend_init() + * in wpseo.php which is also based on this setting. + * Whether or not to show the bctitle field within meta boxes is also based on this setting, but + * showing these when someone hasn't implemented the template tag shouldn't really give cause for concern. + * Other than that, leaving the setting is an easy way to enable/disable the bc without having to + * edit the template files again, but having to manually enable when you've added the template tag + * in your theme is kind of double, so I'm undecided about what to do. + * I guess I'm leaning towards removing the option key. + * + * @param string $before What to show before the breadcrumb. + * @param string $after What to show after the breadcrumb. + * @param bool $display Whether to display the breadcrumb (true) or return it (false). + * + * @return string + */ + function yoast_breadcrumb( $before = '', $after = '', $display = true ) { + $options = get_option( 'wpseo_internallinks' ); + + if ( $options['breadcrumbs-enable'] === true ) { + return WPSEO_Breadcrumbs::breadcrumb( $before, $after, $display ); + } + } +} + +/** + * Add the bulk edit capability to the proper default roles. + */ +function wpseo_add_capabilities() { + $roles = array( + 'administrator', + 'editor', + 'author', + ); + + $roles = apply_filters( 'wpseo_bulk_edit_roles', $roles ); + + foreach ( $roles as $role ) { + $r = get_role( $role ); + if ( $r ) { + $r->add_cap( 'wpseo_bulk_edit' ); + } + } +} + + +/** + * Remove the bulk edit capability from the proper default roles. + * + * Contributor is still removed for legacy reasons. + */ +function wpseo_remove_capabilities() { + $roles = array( + 'administrator', + 'editor', + 'author', + 'contributor', + ); + + $roles = apply_filters( 'wpseo_bulk_edit_roles', $roles ); + + foreach ( $roles as $role ) { + $r = get_role( $role ); + if ( $r ) { + $r->remove_cap( 'wpseo_bulk_edit' ); + } + } +} + + +/** + * Replace `%%variable_placeholders%%` with their real value based on the current requested page/post/cpt + * + * @param string $string the string to replace the variables in. + * @param object $args the object some of the replacement values might come from, could be a post, taxonomy or term. + * @param array $omit variables that should not be replaced by this function. + * @return string + */ +function wpseo_replace_vars( $string, $args, $omit = array() ) { + $replacer = new WPSEO_Replace_Vars; + return $replacer->replace( $string, $args, $omit ); +} + +/** + * Register a new variable replacement + * + * This function is for use by other plugins/themes to easily add their own additional variables to replace. + * This function should be called from a function on the 'wpseo_register_extra_replacements' action hook. + * The use of this function is preferred over the older 'wpseo_replacements' filter as a way to add new replacements. + * The 'wpseo_replacements' filter should still be used to adjust standard WPSEO replacement values. + * The function can not be used to replace standard WPSEO replacement value functions and will thrown a warning + * if you accidently try. + * To avoid conflicts with variables registered by WPSEO and other themes/plugins, try and make the + * name of your variable unique. Variable names also can not start with "%%cf_" or "%%ct_" as these are reserved + * for the standard WPSEO variable variables 'cf_', 'ct_' and + * 'ct_desc_'. + * The replacement function will be passed the undelimited name (i.e. stripped of the %%) of the variable + * to replace in case you need it. + * + * Example code: + * + * + * + * + * @since 1.5.4 + * + * @param string $var The name of the variable to replace, i.e. '%%var%%' + * - the surrounding %% are optional, name can only contain [A-Za-z0-9_-] + * @param mixed $replace_function Function or method to call to retrieve the replacement value for the variable + * Uses the same format as add_filter/add_action function parameter and + * should *return* the replacement value. DON'T echo it! + * @param string $type Type of variable: 'basic' or 'advanced', defaults to 'advanced' + * @param string $help_text Help text to be added to the help tab for this variable + * @return bool Whether the replacement function was succesfully registered + */ +function wpseo_register_var_replacement( $var, $replace_function, $type = 'advanced', $help_text = '' ) { + return WPSEO_Replace_Vars::register_replacement( $var, $replace_function, $type, $help_text ); +} + +/** + * Strip out the shortcodes with a filthy regex, because people don't properly register their shortcodes. + * + * @param string $text input string that might contain shortcodes + * @return string $text string without shortcodes + */ +function wpseo_strip_shortcode( $text ) { + return preg_replace( '`\[[^\]]+\]`s', '', $text ); +} + +/** + * Redirect /sitemap.xml to /sitemap_index.xml + */ +function wpseo_xml_redirect_sitemap() { + global $wp_query; + + $current_url = ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] == 'on' ) ? 'https://' : 'http://'; + $current_url .= sanitize_text_field( $_SERVER['SERVER_NAME'] ) . sanitize_text_field( $_SERVER['REQUEST_URI'] ); + + // must be 'sitemap.xml' and must be 404 + if ( home_url( '/sitemap.xml' ) == $current_url && $wp_query->is_404 ) { + wp_redirect( home_url( '/sitemap_index.xml' ) ); + exit; + } +} + +/** + * Create base URL for the sitemaps and applies filters + * + * @since 1.5.7 + * + * @param string $page page to append to the base URL + * + * @return string base URL (incl page) for the sitemaps + */ +function wpseo_xml_sitemaps_base_url( $page ) { + $base = $GLOBALS['wp_rewrite']->using_index_permalinks() ? 'index.php/' : '/'; + $base = apply_filters( 'wpseo_sitemaps_base_url', $base ); + + return home_url( $base . $page ); +} + +/** + * Initialize sitemaps. Add sitemap & XSL rewrite rules and query vars + */ +function wpseo_xml_sitemaps_init() { + $options = get_option( 'wpseo_xml' ); + if ( $options['enablexmlsitemap'] !== true ) { + return; + } + + // redirects sitemap.xml to sitemap_index.xml + add_action( 'template_redirect', 'wpseo_xml_redirect_sitemap', 0 ); + + if ( ! is_object( $GLOBALS['wp'] ) ) { + return; + } + + $GLOBALS['wp']->add_query_var( 'sitemap' ); + $GLOBALS['wp']->add_query_var( 'sitemap_n' ); + $GLOBALS['wp']->add_query_var( 'xsl' ); + add_rewrite_rule( 'sitemap_index\.xml$', 'index.php?sitemap=1', 'top' ); + add_rewrite_rule( '([^/]+?)-sitemap([0-9]+)?\.xml$', 'index.php?sitemap=$matches[1]&sitemap_n=$matches[2]', 'top' ); + add_rewrite_rule( '([a-z]+)?-?sitemap\.xsl$', 'index.php?xsl=$matches[1]', 'top' ); +} + +add_action( 'init', 'wpseo_xml_sitemaps_init', 1 ); + +/** + * Notify search engines of the updated sitemap. + */ +function wpseo_ping_search_engines( $sitemapurl = null ) { + // Don't ping if blog is not public + if ( '0' == get_option( 'blog_public' ) ) { + return; + } + + $options = get_option( 'wpseo_xml' ); + if ( $sitemapurl == null ) { + $sitemapurl = urlencode( wpseo_xml_sitemaps_base_url( 'sitemap_index.xml' ) ); + } + + // Always ping Google and Bing, optionally ping Ask and Yahoo! + wp_remote_get( 'http://www.google.com/webmasters/tools/ping?sitemap=' . $sitemapurl ); + wp_remote_get( 'http://www.bing.com/ping?sitemap=' . $sitemapurl ); + + if ( $options['xml_ping_yahoo'] === true ) { + wp_remote_get( 'http://search.yahooapis.com/SiteExplorerService/V1/updateNotification?appid=3usdTDLV34HbjQpIBuzMM1UkECFl5KDN7fogidABihmHBfqaebDuZk1vpLDR64I-&url=' . $sitemapurl ); + } + + if ( $options['xml_ping_ask'] === true ) { + wp_remote_get( 'http://submissions.ask.com/ping?sitemap=' . $sitemapurl ); + } +} +add_action( 'wpseo_ping_search_engines', 'wpseo_ping_search_engines' ); + + +function wpseo_store_tracking_response() { + if ( ! wp_verify_nonce( $_POST['nonce'], 'wpseo_activate_tracking' ) ) { + die(); + } + + $options = get_option( 'wpseo' ); + $options['tracking_popup_done'] = true; + + if ( $_POST['allow_tracking'] == 'yes' ) { + $options['yoast_tracking'] = true; + } + else { + $options['yoast_tracking'] = false; + } + + update_option( 'wpseo', $options ); +} +add_action( 'wp_ajax_wpseo_allow_tracking', 'wpseo_store_tracking_response' ); + +/** + * WPML plugin support: Set titles for custom types / taxonomies as translatable. + * It adds new keys to a wpml-config.xml file for a custom post type title, metadesc, title-ptarchive and metadesc-ptarchive fields translation. + * Documentation: http://wpml.org/documentation/support/language-configuration-files/ + * + * @global $sitepress + * @param array $config + * @return array + */ +function wpseo_wpml_config( $config ) { + global $sitepress; + + if ( ( is_array( $config ) && isset( $config['wpml-config']['admin-texts']['key'] ) ) && ( is_array( $config['wpml-config']['admin-texts']['key'] ) && $config['wpml-config']['admin-texts']['key'] !== array() ) ) { + $admin_texts = $config['wpml-config']['admin-texts']['key']; + foreach ( $admin_texts as $k => $val ) { + if ( $val['attr']['name'] === 'wpseo_titles' ) { + $translate_cp = array_keys( $sitepress->get_translatable_documents() ); + if ( is_array( $translate_cp ) && $translate_cp !== array() ) { + foreach ( $translate_cp as $post_type ) { + $admin_texts[ $k ]['key'][]['attr']['name'] = 'title-'. $post_type; + $admin_texts[ $k ]['key'][]['attr']['name'] = 'metadesc-'. $post_type; + $admin_texts[ $k ]['key'][]['attr']['name'] = 'metakey-'. $post_type; + $admin_texts[ $k ]['key'][]['attr']['name'] = 'title-ptarchive-'. $post_type; + $admin_texts[ $k ]['key'][]['attr']['name'] = 'metadesc-ptarchive-'. $post_type; + $admin_texts[ $k ]['key'][]['attr']['name'] = 'metakey-ptarchive-'. $post_type; + + $translate_tax = $sitepress->get_translatable_taxonomies( false, $post_type ); + if ( is_array( $translate_tax ) && $translate_tax !== array() ) { + foreach ( $translate_tax as $taxonomy ) { + $admin_texts[ $k ]['key'][]['attr']['name'] = 'title-tax-'. $taxonomy; + $admin_texts[ $k ]['key'][]['attr']['name'] = 'metadesc-tax-'. $taxonomy; + $admin_texts[ $k ]['key'][]['attr']['name'] = 'metakey-tax-'. $taxonomy; + } + } + } + } + break; + } + } + $config['wpml-config']['admin-texts']['key'] = $admin_texts; + } + + return $config; +} +add_filter( 'icl_wpml_config_array', 'wpseo_wpml_config' ); + +if ( ! function_exists( 'wpseo_calc' ) ) { + /** + * Do simple reliable math calculations without the risk of wrong results + * @see http://floating-point-gui.de/ + * @see the big red warning on http://php.net/language.types.float.php + * + * In the rare case that the bcmath extension would not be loaded, it will return the normal calculation results + * + * @since 1.5.0 + * + * @param mixed $number1 Scalar (string/int/float/bool) + * @param string $action Calculation action to execute. Valid input: + * '+' or 'add' or 'addition', + * '-' or 'sub' or 'subtract', + * '*' or 'mul' or 'multiply', + * '/' or 'div' or 'divide', + * '%' or 'mod' or 'modulus' + * '=' or 'comp' or 'compare' + * @param mixed $number2 Scalar (string/int/float/bool) + * @param bool $round Whether or not to round the result. Defaults to false. + * Will be disregarded for a compare operation + * @param int $decimals Decimals for rounding operation. Defaults to 0. + * @param int $precision Calculation precision. Defaults to 10. + * @return mixed Calculation Result or false if either or the numbers isn't scalar or + * an invalid operation was passed + * - for compare the result will always be an integer + * - for all other operations, the result will either be an integer (preferred) + * or a float + */ + function wpseo_calc( $number1, $action, $number2, $round = false, $decimals = 0, $precision = 10 ) { + static $bc; + + if ( ! is_scalar( $number1 ) || ! is_scalar( $number2 ) ) { + return false; + } + + if ( ! isset( $bc ) ) { + $bc = extension_loaded( 'bcmath' ); + } + + if ( $bc ) { + $number1 = strval( $number1 ); + $number2 = strval( $number2 ); + } + + $result = null; + $compare = false; + + switch ( $action ) { + case '+': + case 'add': + case 'addition': + $result = ( $bc ) ? bcadd( $number1, $number2, $precision ) /* string */ : ( $number1 + $number2 ); + break; + + case '-': + case 'sub': + case 'subtract': + $result = ( $bc ) ? bcsub( $number1, $number2, $precision ) /* string */ : ( $number1 - $number2 ); + break; + + case '*': + case 'mul': + case 'multiply': + $result = ( $bc ) ? bcmul( $number1, $number2, $precision ) /* string */ : ( $number1 * $number2 ); + break; + + case '/': + case 'div': + case 'divide': + if ( $bc ) { + $result = bcdiv( $number1, $number2, $precision ); // string, or NULL if right_operand is 0 + } + elseif ( $number2 != 0 ) { + $result = $number1 / $number2; + } + + if ( ! isset( $result ) ) { + $result = 0; + } + break; + + case '%': + case 'mod': + case 'modulus': + if ( $bc ) { + $result = bcmod( $number1, $number2, $precision ); // string, or NULL if modulus is 0. + } + elseif ( $number2 != 0 ) { + $result = $number1 % $number2; + } + + if ( ! isset( $result ) ) { + $result = 0; + } + break; + + case '=': + case 'comp': + case 'compare': + $compare = true; + if ( $bc ) { + $result = bccomp( $number1, $number2, $precision ); // returns int 0, 1 or -1 + } + else { + $result = ( $number1 == $number2 ) ? 0 : ( ( $number1 > $number2 ) ? 1 : -1 ); + } + break; + } + + if ( isset( $result ) ) { + if ( $compare === false ) { + if ( $round === true ) { + $result = round( floatval( $result ), $decimals ); + if ( $decimals === 0 ) { + $result = (int) $result; + } + } + else { + $result = ( intval( $result ) == $result ) ? intval( $result ) : floatval( $result ); + } + } + return $result; + } + return false; + } +} + +/** + * Check if the web server is running on Apache + * @return bool + */ +function wpseo_is_apache() { + if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && stristr( $_SERVER['SERVER_SOFTWARE'], 'apache' ) !== false ) { + return true; + } + return false; +} + +/** + * Check if the web service is running on Nginx + * + * @return bool + */ +function wpseo_is_nginx() { + if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && stristr( $_SERVER['SERVER_SOFTWARE'], 'nginx' ) !== false ) { + return true; + } + return false; +} + +/** + * WordPress SEO breadcrumb shortcode + * [wpseo_breadcrumb] + * + * @return string + */ +function wpseo_shortcode_yoast_breadcrumb() { + return yoast_breadcrumb( '', '', false ); +} +add_shortcode( 'wpseo_breadcrumb', 'wpseo_shortcode_yoast_breadcrumb' ); + + +/** + * This invalidates our XML Sitemaps cache. + * + * @param $type + */ +function wpseo_invalidate_sitemap_cache( $type ) { + // Always delete the main index sitemaps cache, as that's always invalidated by any other change + delete_transient( 'wpseo_sitemap_cache_1' ); + delete_transient( 'wpseo_sitemap_cache_' . $type ); +} + +add_action( 'deleted_term_relationships', 'wpseo_invalidate_sitemap_cache' ); + +/** + * Invalidate XML sitemap cache for taxonomy / term actions + * + * @param unsigned $unused + * @param string $type + */ +function wpseo_invalidate_sitemap_cache_terms( $unused, $type ) { + wpseo_invalidate_sitemap_cache( $type ); +} + +add_action( 'edited_terms', 'wpseo_invalidate_sitemap_cache_terms', 10, 2 ); +add_action( 'clean_term_cache', 'wpseo_invalidate_sitemap_cache_terms', 10, 2 ); +add_action( 'clean_object_term_cache', 'wpseo_invalidate_sitemap_cache_terms', 10, 2 ); + +/** + * Invalidate the XML sitemap cache for a post type when publishing or updating a post + * + * @param int $post_id + */ +function wpseo_invalidate_sitemap_cache_on_save_post( $post_id ) { + + // If this is just a revision, don't invalidate the sitemap cache yet. + if ( wp_is_post_revision( $post_id ) ) { + return; + } + + wpseo_invalidate_sitemap_cache( get_post_type( $post_id ) ); +} + +add_action( 'save_post', 'wpseo_invalidate_sitemap_cache_on_save_post' ); + +/** + * List all the available user roles + * + * @return array $roles + */ +function wpseo_get_roles() { + global $wp_roles; + + if ( ! isset( $wp_roles ) ) { + $wp_roles = new WP_Roles(); + } + + $roles = $wp_roles->get_names(); + + return $roles; +} + +/** + * Check whether a url is relative + * + * @param string $url + * + * @return bool + */ +function wpseo_is_url_relative( $url ) { + return ( strpos( $url, 'http' ) !== 0 && strpos( $url, '//' ) !== 0 ); +} + +/** + * Standardize whitespace in a string + * + * Replace line breaks, carriage returns, tabs with a space, then remove double spaces. + * + * @param string $string + * + * @return string + */ +function wpseo_standardize_whitespace( $string ) { + return trim( str_replace( ' ', ' ', str_replace( array( "\t", "\n", "\r", "\f" ), ' ', $string ) ) ); +} + +/** + * Emulate PHP native ctype_digit() function for when the ctype extension would be disabled *sigh* + * Only emulates the behaviour for when the input is a string, does not handle integer input as ascii value + * + * @param string $string + * + * @return bool + */ +if ( ! extension_loaded( 'ctype' ) || ! function_exists( 'ctype_digit' ) ) { + function ctype_digit( $string ) { + $return = false; + if ( ( is_string( $string ) && $string !== '' ) && preg_match( '`^\d+$`', $string ) === 1 ){ + $return = true; + } + return $return; + } +} + + +/********************** DEPRECATED FUNCTIONS **********************/ + + +/** + * Get the value from the post custom values + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Meta::get_value() + * @see WPSEO_Meta::get_value() + * + * @param string $val internal name of the value to get + * @param int $postid post ID of the post to get the value for + * @return string + */ +function wpseo_get_value( $val, $postid = 0 ) { + _deprecated_function( __FUNCTION__, 'WPSEO 1.5.0', 'WPSEO_Meta::get_value()' ); + return WPSEO_Meta::get_value( $val, $postid ); +} + + +/** + * Save a custom meta value + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Meta::set_value() or just use update_post_meta() + * @see WPSEO_Meta::set_value() + * + * @param string $meta_key the meta to change + * @param mixed $meta_value the value to set the meta to + * @param int $post_id the ID of the post to change the meta for. + * @return bool whether the value was changed + */ +function wpseo_set_value( $meta_key, $meta_value, $post_id ) { + _deprecated_function( __FUNCTION__, 'WPSEO 1.5.0', 'WPSEO_Meta::set_value()' ); + return WPSEO_Meta::set_value( $meta_key, $meta_value, $post_id ); +} + + +/** + * Retrieve an array of all the options the plugin uses. It can't use only one due to limitations of the options API. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Options::get_option_names() + * @see WPSEO_Options::get_option_names() + * + * @return array of options. + */ +function get_wpseo_options_arr() { + _deprecated_function( __FUNCTION__, 'WPSEO 1.5.0', 'WPSEO_Options::get_option_names()' ); + return WPSEO_Options::get_option_names(); +} + + +/** + * Retrieve all the options for the SEO plugin in one go. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Options::get_all() + * @see WPSEO_Options::get_all() + * + * @return array of options + */ +function get_wpseo_options() { + _deprecated_function( __FUNCTION__, 'WPSEO 1.5.0', 'WPSEO_Options::get_all()' ); + return WPSEO_Options::get_all(); +} + +/** + * Used for imports, both in dashboard and import settings pages, this functions either copies + * $old_metakey into $new_metakey or just plain replaces $old_metakey with $new_metakey + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Meta::replace_meta() + * @see WPSEO_Meta::replace_meta() + * + * @param string $old_metakey The old name of the meta value. + * @param string $new_metakey The new name of the meta value, usually the WP SEO name. + * @param bool $replace Whether to replace or to copy the values. + */ +function replace_meta( $old_metakey, $new_metakey, $replace = false ) { + _deprecated_function( __FUNCTION__, 'WPSEO 1.5.0', 'WPSEO_Meta::replace_meta()' ); + WPSEO_Meta::replace_meta( $old_metakey, $new_metakey, $replace ); +} + + +/** + * Retrieve a taxonomy term's meta value. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Taxonomy_Meta::get_term_meta() + * @see WPSEO_Taxonomy_Meta::get_term_meta() + * + * @param string|object $term term to get the meta value for + * @param string $taxonomy name of the taxonomy to which the term is attached + * @param string $meta meta value to get + * @return bool|mixed value when the meta exists, false when it does not + */ +function wpseo_get_term_meta( $term, $taxonomy, $meta ) { + _deprecated_function( __FUNCTION__, 'WPSEO 1.5.0', 'WPSEO_Taxonomy_Meta::get_term_meta' ); + WPSEO_Taxonomy_Meta::get_term_meta( $term, $taxonomy, $meta ); +} + +/** + * Throw a notice about an invalid custom taxonomy used + * + * @since 1.4.14 + * @deprecated 1.5.4 (removed) + */ +function wpseo_invalid_custom_taxonomy() { + _deprecated_function( __FUNCTION__, 'WPSEO 1.5.4' ); +} + +/** + * Retrieve a post's terms, comma delimited. + * + * @deprecated 1.5.4 + * @deprecated use WPSEO_Replace_Vars::get_terms() + * @see WPSEO_Replace_Vars::get_terms() + * + * @param int $id ID of the post to get the terms for. + * @param string $taxonomy The taxonomy to get the terms for this post from. + * @param bool $return_single If true, return the first term. + * @return string either a single term or a comma delimited string of terms. + */ +function wpseo_get_terms( $id, $taxonomy, $return_single = false ) { + _deprecated_function( __FUNCTION__, 'WPSEO 1.5.4', 'WPSEO_Replace_Vars::get_terms' ); + $replacer = new WPSEO_Replace_Vars; + return $replacer->get_terms( $id, $taxonomy, $return_single ); +} + +/** + * Generate an HTML sitemap + * + * @deprecated 1.5.5.4 + * @deprecated use plugin WordPress SEO Premium + * @see WordPress SEO Premium + * + * @param array $atts The attributes passed to the shortcode. + * + * @return string + */ +function wpseo_sitemap_handler( $atts ) { + _deprecated_function( __FUNCTION__, 'WPSEO 1.5.5.4', 'Functionality has been discontinued after being in beta, it\'ll be available in the WordPress SEO Premium plugin soon.' ); + return ''; +} + +add_shortcode( 'wpseo_sitemap', 'wpseo_sitemap_handler' ); diff --git a/wp-content/plugins/wordpress-seo/inc/wpseo-non-ajax-functions.php b/wp-content/plugins/wordpress-seo/inc/wpseo-non-ajax-functions.php new file mode 100644 index 0000000..5dd218d --- /dev/null +++ b/wp-content/plugins/wordpress-seo/inc/wpseo-non-ajax-functions.php @@ -0,0 +1,658 @@ + 0 forces the plugin to output the title below through a filter in class-frontend.php + $expected_title = 'This is a Yoast Test Title'; + + WPSEO_Options::clear_cache(); + + + global $wp_version; + $args = array( + 'user-agent' => "WordPress/${wp_version}; " . get_site_url() . ' - Yoast', + ); + $resp = wp_remote_get( get_bloginfo( 'url' ), $args ); + + // echo '
'.$resp['body'].'
'; + + if ( ( $resp && ! is_wp_error( $resp ) ) && ( 200 == $resp['response']['code'] && isset( $resp['body'] ) ) ) { + $res = preg_match( '`([^<]+)`im', $resp['body'], $matches ); + + if ( $res && strcmp( $matches[1], $expected_title ) !== 0 ) { + $options['forcerewritetitle'] = true; + + $resp = wp_remote_get( get_bloginfo( 'url' ), $args ); + $res = false; + if ( ( $resp && ! is_wp_error( $resp ) ) && ( 200 == $resp['response']['code'] && isset( $resp['body'] ) ) ) { + $res = preg_match( '`/([^>]+)`im', $resp['body'], $matches ); + } + } + + if ( ! $res || $matches[1] != $expected_title ) { + $options['forcerewritetitle'] = false; + } + } else { + // If that dies, let's make sure the titles are correct and force the output. + $options['forcerewritetitle'] = true; + } + + $options['title_test'] = 0; + update_option( 'wpseo_titles', $options ); +} + +//add_filter( 'switch_theme', 'wpseo_title_test', 0 ); + + +/** + * Test whether the active theme contains a description tag. + * + * @since 1.4.14 Moved from dashboard.php and adjusted - see changelog + * + * @return void + */ +function wpseo_description_test() { + $options = get_option( 'wpseo' ); + + // Reset any related options - dirty way of getting the default to make sure it works on activation + $options['theme_has_description'] = WPSEO_Option_Wpseo::$desc_defaults['theme_has_description']; + $options['theme_description_found'] = WPSEO_Option_Wpseo::$desc_defaults['theme_description_found']; + /* @internal Should this be reset too ? Best to do so as test is done on re-activate and switch_theme + * as well and new warning would be warranted then. Only might give irritation on theme upgrade. */ + $options['ignore_meta_description_warning'] = WPSEO_Option_Wpseo::$desc_defaults['ignore_meta_description_warning']; + + $file = false; + if ( file_exists( get_stylesheet_directory() . '/header.php' ) ) { + // theme or child theme + $file = get_stylesheet_directory() . '/header.php'; + } elseif ( file_exists( get_template_directory() . '/header.php' ) ) { + // parent theme in case of a child theme + $file = get_template_directory() . '/header.php'; + } + + if ( is_string( $file ) && $file !== '' ) { + $header_file = file_get_contents( $file ); + $issue = preg_match_all( '#<\s*meta\s*(name|content)\s*=\s*("|\')(.*)("|\')\s*(name|content)\s*=\s*("|\')(.*)("|\')(\s+)?/?>#i', $header_file, $matches, PREG_SET_ORDER ); + if ( $issue === false ) { + $options['theme_has_description'] = false; + } else { + foreach ( $matches as $meta ) { + if ( ( strtolower( $meta[1] ) == 'name' && strtolower( $meta[3] ) == 'description' ) || ( strtolower( $meta[5] ) == 'name' && strtolower( $meta[7] ) == 'description' ) ) { + $options['theme_description_found'] = $meta[0]; + $options['ignore_meta_description_warning'] = false; + break; // no need to run through the rest of the meta's + } + } + if ( $options['theme_description_found'] !== '' ) { + $options['theme_has_description'] = true; + } else { + $options['theme_has_description'] = false; + } + } + } + update_option( 'wpseo', $options ); +} + +add_filter( 'after_switch_theme', 'wpseo_description_test', 0 ); + +if ( version_compare( $GLOBALS['wp_version'], '3.6.99', '>' ) ) { + // Use the new and *sigh* adjusted action hook WP 3.7+ + add_action( 'upgrader_process_complete', 'wpseo_upgrader_process_complete', 10, 2 ); +} elseif ( version_compare( $GLOBALS['wp_version'], '3.5.99', '>' ) ) { + // Use the new action hook WP 3.6+ + add_action( 'upgrader_process_complete', 'wpseo_upgrader_process_complete', 10, 3 ); +} else { + // Abuse filters to do our action + add_filter( 'update_theme_complete_actions', 'wpseo_update_theme_complete_actions', 10, 2 ); + add_filter( 'update_bulk_theme_complete_actions', 'wpseo_update_theme_complete_actions', 10, 2 ); +} + + +/** + * Check if the current theme was updated and if so, test the updated theme + * for the title and meta description tag + * + * @since 1.4.14 + * + * @param object $upgrader_object + * @param array $context_array + * @param mixed $themes + * + * @return void + */ +function wpseo_upgrader_process_complete( $upgrader_object, $context_array, $themes = null ) { + $options = get_option( 'wpseo' ); + + // Break if admin_notice already in place + if ( ( ( isset( $options['theme_has_description'] ) && $options['theme_has_description'] === true ) || $options['theme_description_found'] !== '' ) && $options['ignore_meta_description_warning'] !== true ) { + return; + } + // Break if this is not a theme update, not interested in installs as after_switch_theme would still be called + if ( ! isset( $context_array['type'] ) || $context_array['type'] !== 'theme' || ! isset( $context_array['action'] ) || $context_array['action'] !== 'update' ) { + return; + } + + $theme = get_stylesheet(); + if ( ! isset( $themes ) ) { + // WP 3.7+ + $themes = array(); + if ( isset( $context_array['themes'] ) && $context_array['themes'] !== array() ) { + $themes = $context_array['themes']; + } elseif ( isset( $context_array['theme'] ) && $context_array['theme'] !== '' ) { + $themes = $context_array['theme']; + } + } + + if ( ( isset( $context_array['bulk'] ) && $context_array['bulk'] === true ) && ( is_array( $themes ) && count( $themes ) > 0 ) ) { + + if ( in_array( $theme, $themes ) ) { +// wpseo_title_test(); + wpseo_description_test(); + } + } elseif ( is_string( $themes ) && $themes === $theme ) { +// wpseo_title_test(); + wpseo_description_test(); + } + + return; +} + +/** + * Abuse a filter to check if the current theme was updated and if so, test the updated theme + * for the title and meta description tag + * + * @since 1.4.14 + * + * @param array $update_actions + * @param mixed $updated_theme + * + * @return array $update_actions Unchanged array + */ +function wpseo_update_theme_complete_actions( $update_actions, $updated_theme ) { + $options = get_option( 'wpseo' ); + + // Break if admin_notice already in place + if ( ( ( isset( $options['theme_has_description'] ) && $options['theme_has_description'] === true ) || $options['theme_description_found'] !== '' ) && $options['ignore_meta_description_warning'] !== true ) { + return $update_actions; + } + + $theme = get_stylesheet(); + if ( is_object( $updated_theme ) ) { + /* Bulk update and $updated_theme only contains info on which theme was last in the list + of updated themes, so go & test */ +// wpseo_title_test(); + wpseo_description_test(); + } elseif ( $updated_theme === $theme ) { + /* Single theme update for the active theme */ +// wpseo_title_test(); + wpseo_description_test(); + } + + return $update_actions; +} + +/** + * Translates a decimal analysis score into a textual one. + * + * @param int $val The decimal score to translate. + * @param bool $css_value Whether to return the i18n translated score or the CSS class value. + * + * @return string + */ +function wpseo_translate_score( $val, $css_value = true ) { + if ( $val > 10 ) { + $val = round( $val / 10 ); + } + switch ( $val ) { + case 0: + $score = __( 'N/A', 'wordpress-seo' ); + $css = 'na'; + break; + case 4: + case 5: + $score = __( 'Poor', 'wordpress-seo' ); + $css = 'poor'; + break; + case 6: + case 7: + $score = __( 'OK', 'wordpress-seo' ); + $css = 'ok'; + break; + case 8: + case 9: + case 10: + $score = __( 'Good', 'wordpress-seo' ); + $css = 'good'; + break; + default: + $score = __( 'Bad', 'wordpress-seo' ); + $css = 'bad'; + } + + if ( $css_value ) { + return $css; + } else { + return $score; + } +} + + +/** + * Check whether file editing is allowed for the .htaccess and robots.txt files + * + * @internal current_user_can() checks internally whether a user is on wp-ms and adjusts accordingly. + * + * @return bool + */ +function wpseo_allow_system_file_edit() { + $allowed = true; + + if ( current_user_can( 'edit_files' ) === false ) { + $allowed = false; + } + + /** + * Filter: 'wpseo_allow_system_file_edit' - Allow developers to change whether the editing of + * .htaccess and robots.txt is allowed + * + * @api bool $allowed Whether file editing is allowed + */ + + return apply_filters( 'wpseo_allow_system_file_edit', $allowed ); +} + + +/** + * Adds an SEO admin bar menu with several options. If the current user is an admin he can also go straight to several settings menu's from here. + */ +function wpseo_admin_bar_menu() { + // If the current user can't write posts, this is all of no use, so let's not output an admin menu + if ( ! current_user_can( 'edit_posts' ) ) { + return; + } + + global $wp_admin_bar, $wpseo_front, $post; + + $url = ''; + if ( is_object( $wpseo_front ) ) { + $url = $wpseo_front->canonical( false ); + } + + $focuskw = ''; + $score = ''; + $seo_url = get_admin_url( null, 'admin.php?page=wpseo_dashboard' ); + + if ( ( is_singular() || ( is_admin() && in_array( $GLOBALS['pagenow'], array( + 'post.php', + 'post-new.php', + ), true ) ) ) && isset( $post ) && is_object( $post ) && apply_filters( 'wpseo_use_page_analysis', true ) === true + ) { + $focuskw = WPSEO_Meta::get_value( 'focuskw', $post->ID ); + $perc_score = WPSEO_Meta::get_value( 'linkdex', $post->ID ); + $calc_score = wpseo_calc( $perc_score, '/', 10, true ); + $txtscore = wpseo_translate_score( $calc_score ); + $title = wpseo_translate_score( $calc_score, false ); + $score = '
'; + + $seo_url = get_edit_post_link( $post->ID ); + if ( $txtscore !== 'na' ) { + $seo_url .= '#wpseo_linkdex'; + } + } + + $wp_admin_bar->add_menu( array( + 'id' => 'wpseo-menu', + 'title' => __( 'SEO', 'wordpress-seo' ) . $score, + 'href' => $seo_url, + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-menu', + 'id' => 'wpseo-kwresearch', + 'title' => __( 'Keyword Research', 'wordpress-seo' ), + '#', + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-kwresearch', + 'id' => 'wpseo-adwordsexternal', + 'title' => __( 'AdWords External', 'wordpress-seo' ), + 'href' => 'http://adwords.google.com/keywordplanner', + 'meta' => array( 'target' => '_blank' ) + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-kwresearch', + 'id' => 'wpseo-googleinsights', + 'title' => __( 'Google Insights', 'wordpress-seo' ), + 'href' => 'http://www.google.com/insights/search/#q=' . urlencode( $focuskw ) . '&cmpt=q', + 'meta' => array( 'target' => '_blank' ) + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-kwresearch', + 'id' => 'wpseo-wordtracker', + 'title' => __( 'SEO Book', 'wordpress-seo' ), + 'href' => 'http://tools.seobook.com/keyword-tools/seobook/?keyword=' . urlencode( $focuskw ), + 'meta' => array( 'target' => '_blank' ) + ) ); + + if ( ! is_admin() ) { + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-menu', + 'id' => 'wpseo-analysis', + 'title' => __( 'Analyze this page', 'wordpress-seo' ), + '#', + ) ); + if ( is_string( $url ) ) { + // @todo [JRF => whomever] check if this url shouldn't be encoded either with urlencode or with esc_url or something + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-analysis', + 'id' => 'wpseo-inlinks-ose', + 'title' => __( 'Check Inlinks (OSE)', 'wordpress-seo' ), + 'href' => 'http://www.opensiteexplorer.org/' . str_replace( '/', '%252F', preg_replace( '`^http[s]?://`', '', $url ) ) . '/a!links', + 'meta' => array( 'target' => '_blank' ) + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-analysis', + 'id' => 'wpseo-kwdensity', + 'title' => __( 'Check Keyword Density', 'wordpress-seo' ), + 'href' => 'http://www.zippy.co.uk/keyworddensity/index.php?url=' . urlencode( $url ) . '&keyword=' . urlencode( $focuskw ), + 'meta' => array( 'target' => '_blank' ) + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-analysis', + 'id' => 'wpseo-cache', + 'title' => __( 'Check Google Cache', 'wordpress-seo' ), + 'href' => 'http://webcache.googleusercontent.com/search?strip=1&q=cache:' . urlencode( $url ), + 'meta' => array( 'target' => '_blank' ) + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-analysis', + 'id' => 'wpseo-header', + 'title' => __( 'Check Headers', 'wordpress-seo' ), + 'href' => 'http://quixapp.com/headers/?r=' . urlencode( $url ), + 'meta' => array( 'target' => '_blank' ) + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-analysis', + 'id' => 'wpseo-richsnippets', + 'title' => __( 'Check Rich Snippets', 'wordpress-seo' ), + 'href' => 'http://www.google.com/webmasters/tools/richsnippets?q=' . urlencode( $url ), + 'meta' => array( 'target' => '_blank' ) + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-analysis', + 'id' => 'wpseo-facebookdebug', + 'title' => __( 'Facebook Debugger', 'wordpress-seo' ), + 'href' => 'https://developers.facebook.com/tools/debug/og/object?q=' . urlencode( $url ), + 'meta' => array( 'target' => '_blank' ) + ) ); + } + } + + $admin_menu = false; + if ( is_multisite() ) { + $options = get_site_option( 'wpseo_ms' ); + if ( $options['access'] === 'superadmin' && is_super_admin() ) { + $admin_menu = true; + } elseif ( current_user_can( 'manage_options' ) ) { + $admin_menu = true; + } + } elseif ( current_user_can( 'manage_options' ) ) { + $admin_menu = true; + } + + // @todo: add links to bulk title and bulk description edit pages + if ( $admin_menu ) { + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-menu', + 'id' => 'wpseo-settings', + 'title' => __( 'SEO Settings', 'wordpress-seo' ), + 'href' => admin_url( 'admin.php?page=wpseo_titles' ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-settings', + 'id' => 'wpseo-titles', + 'title' => __( 'Titles & Metas', 'wordpress-seo' ), + 'href' => admin_url( 'admin.php?page=wpseo_titles' ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-settings', + 'id' => 'wpseo-social', + 'title' => __( 'Social', 'wordpress-seo' ), + 'href' => admin_url( 'admin.php?page=wpseo_social' ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-settings', + 'id' => 'wpseo-xml', + 'title' => __( 'XML Sitemaps', 'wordpress-seo' ), + 'href' => admin_url( 'admin.php?page=wpseo_xml' ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-settings', + 'id' => 'wpseo-permalinks', + 'title' => __( 'Permalinks', 'wordpress-seo' ), + 'href' => admin_url( 'admin.php?page=wpseo_permalinks' ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-settings', + 'id' => 'wpseo-internal-links', + 'title' => __( 'Internal Links', 'wordpress-seo' ), + 'href' => admin_url( 'admin.php?page=wpseo_internal-links' ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-settings', + 'id' => 'wpseo-rss', + 'title' => __( 'RSS', 'wordpress-seo' ), + 'href' => admin_url( 'admin.php?page=wpseo_rss' ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-settings', + 'id' => 'wpseo-import', + 'title' => __( 'Import & Export', 'wordpress-seo' ), + 'href' => admin_url( 'admin.php?page=wpseo_import' ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-settings', + 'id' => 'wpseo_bulk-editor', + 'title' => __( 'Bulk Editor', 'wordpress-seo' ), + 'href' => admin_url( 'admin.php?page=wpseo_bulk-editor' ), + ) ); + + // Check where to add the edit files page + if ( wpseo_allow_system_file_edit() === true ) { + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-settings', + 'id' => 'wpseo-files', + 'title' => __( 'Edit Files', 'wordpress-seo' ), + 'href' => network_admin_url( 'admin.php?page=wpseo_files' ), + ) ); // will auto-use admin_url if not in multi-site + } + + $wp_admin_bar->add_menu( array( + 'parent' => 'wpseo-settings', + 'id' => 'wpseo-licenses', + 'title' => __( 'Extensions', 'wordpress-seo' ), + 'href' => admin_url( 'admin.php?page=wpseo_licenses' ), + ) ); + } +} + +add_action( 'admin_bar_menu', 'wpseo_admin_bar_menu', 95 ); + +/** + * Enqueue a tiny bit of CSS to show so the adminbar shows right. + */ +function wpseo_admin_bar_css() { + if ( is_admin_bar_showing() && is_singular() ) { + wp_enqueue_style( 'boxes', plugins_url( 'css/adminbar' . WPSEO_CSSJS_SUFFIX . '.css', WPSEO_FILE ), array(), WPSEO_VERSION ); + } +} + +add_action( 'wp_enqueue_scripts', 'wpseo_admin_bar_css' ); + +/** + * Allows editing of the meta fields through weblog editors like Marsedit. + * + * @param array $allcaps Capabilities that must all be true to allow action. + * @param array $cap Array of capabilities to be checked, unused here. + * @param array $args List of arguments for the specific cap to be checked. + * + * @return array $allcaps + */ +function allow_custom_field_edits( $allcaps, $cap, $args ) { + // $args[0] holds the capability + // $args[2] holds the post ID + // $args[3] holds the custom field + + // Make sure the request is to edit or add a post meta (this is usually also the second value in $cap, + // but this is safer to check). + if ( in_array( $args[0], array( 'edit_post_meta', 'add_post_meta' ) ) ) { + // Only allow editing rights for users who have the rights to edit this post and make sure + // the meta value starts with _yoast_wpseo (WPSEO_Meta::$meta_prefix). + if ( ( isset( $args[2] ) && current_user_can( 'edit_post', $args[2] ) ) && ( ( isset( $args[3] ) && $args[3] !== '' ) && strpos( $args[3], WPSEO_Meta::$meta_prefix ) === 0 ) ) { + $allcaps[ $args[0] ] = true; + } + } + + return $allcaps; +} + +add_filter( 'user_has_cap', 'allow_custom_field_edits', 0, 3 ); + +/** + * Display an import message when robots-meta is active + * + * @since 1.5.0 + */ +function wpseo_robots_meta_message() { + // check if robots meta is running + if ( ( ! isset( $_GET['page'] ) || 'wpseo_import' !== $_GET['page'] ) && is_plugin_active( 'robots-meta/robots-meta.php' ) ) { + add_action( 'admin_notices', 'wpseo_import_robots_meta_notice' ); + } +} + +add_action( 'admin_init', 'wpseo_robots_meta_message' ); + +/** + * Handle deactivation Robots Meta + * + * @since 1.5.0 + */ +function wpseo_disable_robots_meta() { + if ( isset( $_GET['deactivate_robots_meta'] ) && $_GET['deactivate_robots_meta'] === '1' && is_plugin_active( 'robots-meta/robots-meta.php' ) ) { + // Deactivate the plugin + deactivate_plugins( 'robots-meta/robots-meta.php' ); + + // show notice that robots meta has been deactivated + add_action( 'admin_notices', 'wpseo_deactivate_robots_meta_notice' ); + + // Clean up the referrer url for later use + if ( isset( $_SERVER['REQUEST_URI'] ) ) { + $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'deactivate_robots_meta' ), sanitize_text_field( $_SERVER['REQUEST_URI'] ) ); + } + } +} + +add_action( 'admin_init', 'wpseo_disable_robots_meta' ); + +/** + * Handle deactivation & import of AIOSEO data + * + * @since 1.5.0 + */ +function wpseo_aioseo_message() { + // check if aioseo is running + if ( ( ! isset( $_GET['page'] ) || 'wpseo_import' != $_GET['page'] ) && is_plugin_active( 'all-in-one-seo-pack/all_in_one_seo_pack.php' ) ) { + add_action( 'admin_notices', 'wpseo_import_aioseo_setting_notice' ); + } +} + +add_action( 'admin_init', 'wpseo_aioseo_message' ); + +/** + * Handle deactivation AIOSEO + * + * @since 1.5.0 + */ +function wpseo_disable_aioseo() { + if ( isset( $_GET['deactivate_aioseo'] ) && $_GET['deactivate_aioseo'] === '1' && is_plugin_active( 'all-in-one-seo-pack/all_in_one_seo_pack.php' ) ) { + // Deactivate AIO + deactivate_plugins( 'all-in-one-seo-pack/all_in_one_seo_pack.php' ); + + // show notice that aioseo has been deactivated + add_action( 'admin_notices', 'wpseo_deactivate_aioseo_notice' ); + + // Clean up the referrer url for later use + if ( isset( $_SERVER['REQUEST_URI'] ) ) { + $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'deactivate_aioseo' ), sanitize_text_field( $_SERVER['REQUEST_URI'] ) ); + } + } +} + +add_action( 'admin_init', 'wpseo_disable_aioseo' ); + +/** + * Throw a notice to import AIOSEO. + * + * @since 1.4.8 + */ +function wpseo_import_aioseo_setting_notice() { + echo '

' . sprintf( esc_html__( 'The plugin All-In-One-SEO has been detected. Do you want to %simport its settings%s.', 'wordpress-seo' ), '', '' ) . '

'; +} + +/** + * Throw a notice to inform the user AIOSEO has been deactivated + * + * @since 1.4.8 + */ +function wpseo_deactivate_aioseo_notice() { + echo '

' . esc_html__( 'All-In-One-SEO has been deactivated', 'wordpress-seo' ) . '

'; +} + +/** + * Throw a notice to import Robots Meta. + * + * @since 1.4.8 + */ +function wpseo_import_robots_meta_notice() { + echo '

' . sprintf( esc_html__( 'The plugin Robots-Meta has been detected. Do you want to %simport its settings%s.', 'wordpress-seo' ), '', '' ) . '

'; +} + +/** + * Throw a notice to inform the user Robots Meta has been deactivated + * + * @since 1.4.8 + */ +function wpseo_deactivate_robots_meta_notice() { + echo '

' . esc_html__( 'Robots-Meta has been deactivated', 'wordpress-seo' ) . '

'; +} + +/********************** DEPRECATED FUNCTIONS **********************/ + +/** + * Set the default settings. + * + * @deprecated 1.5.0 + * @deprecated use WPSEO_Options::initialize() + * @see WPSEO_Options::initialize() + */ +function wpseo_defaults() { + _deprecated_function( __FUNCTION__, 'WPSEO 1.5.0', 'WPSEO_Options::initialize()' ); + WPSEO_Options::initialize(); +} diff --git a/wp-content/plugins/wordpress-seo/index.php b/wp-content/plugins/wordpress-seo/index.php new file mode 100644 index 0000000..12c197f --- /dev/null +++ b/wp-content/plugins/wordpress-seo/index.php @@ -0,0 +1,2 @@ +0)){B.content={text:B.content}}if(typeof B.content.title!=="object"){B.content.title={text:B.content.title}}if(typeof B.position!=="object"){B.position={corner:B.position}}if(typeof B.position.corner!=="object"){B.position.corner={target:B.position.corner,tooltip:B.position.corner}}if(typeof B.show!=="object"){B.show={when:B.show}}if(typeof B.show.when!=="object"){B.show.when={event:B.show.when}}if(typeof B.show.effect!=="object"){B.show.effect={type:B.show.effect}}if(typeof B.hide!=="object"){B.hide={when:B.hide}}if(typeof B.hide.when!=="object"){B.hide.when={event:B.hide.when}}if(typeof B.hide.effect!=="object"){B.hide.effect={type:B.hide.effect}}if(typeof B.style!=="object"){B.style={name:B.style}}B.style=c(B.style);s=f.extend(true,{},f.fn.qtip.defaults,B);s.style=a.call({options:s},s.style);s.user=f.extend(true,{},B)}return f(this).each(function(){if(typeof B=="string"){w=B.toLowerCase();A=f(this).qtip("interfaces");if(typeof A=="object"){if(u===true&&w=="destroy"){while(A.length>0){A[A.length-1].destroy()}}else{if(u!==true){A=[f(this).qtip("api")]}for(y=0;y0))}if(typeof s.options.show.solo=="object"){z=f(s.options.show.solo)}else{if(s.options.show.solo===true){z=f("div.qtip").not(s.elements.tooltip)}}if(z){z.each(function(){if(f(this).qtip("api").status.rendered===true){f(this).qtip("api").hide()}})}if(typeof s.options.show.effect.type=="function"){s.options.show.effect.type.call(s.elements.tooltip,s.options.show.effect.length);s.elements.tooltip.queue(function(){w();f(this).dequeue()})}else{switch(s.options.show.effect.type.toLowerCase()){case"fade":s.elements.tooltip.fadeIn(s.options.show.effect.length,w);break;case"slide":s.elements.tooltip.slideDown(s.options.show.effect.length,function(){w();if(s.options.position.type!=="static"){s.updatePosition(y,true)}});break;case"grow":s.elements.tooltip.show(s.options.show.effect.length,w);break;default:s.elements.tooltip.show(null,w);break}s.elements.tooltip.addClass(s.options.style.classes.active)}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_SHOWN,"show")},hide:function(y){var x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"hide")}else{if(s.elements.tooltip.css("display")==="none"){return s}}clearTimeout(s.timers.show);s.elements.tooltip.stop(true,false);x=s.beforeHide.call(s,y);if(x===false){return s}function w(){s.onHide.call(s,y)}s.cache.toggle=0;if(typeof s.options.hide.effect.type=="function"){s.options.hide.effect.type.call(s.elements.tooltip,s.options.hide.effect.length);s.elements.tooltip.queue(function(){w();f(this).dequeue()})}else{switch(s.options.hide.effect.type.toLowerCase()){case"fade":s.elements.tooltip.fadeOut(s.options.hide.effect.length,w);break;case"slide":s.elements.tooltip.slideUp(s.options.hide.effect.length,w);break;case"grow":s.elements.tooltip.hide(s.options.hide.effect.length,w);break;default:s.elements.tooltip.hide(null,w);break}s.elements.tooltip.removeClass(s.options.style.classes.active)}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_HIDDEN,"hide")},updatePosition:function(w,x){var C,G,L,J,H,E,y,I,B,D,K,A,F,z;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updatePosition")}else{if(s.options.position.type=="static"){return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.CANNOT_POSITION_STATIC,"updatePosition")}}G={position:{left:0,top:0},dimensions:{height:0,width:0},corner:s.options.position.corner.target};L={position:s.getPosition(),dimensions:s.getDimensions(),corner:s.options.position.corner.tooltip};if(s.options.position.target!=="mouse"){if(s.options.position.target.get(0).nodeName.toLowerCase()=="area"){J=s.options.position.target.attr("coords").split(",");for(C=0;CG.dimensions.width){G.dimensions.width=J[C]}if(J[C]G.dimensions.height){G.dimensions.height=J[C]}if(J[C]0){if(L.corner.search(/Left/)!==-1){y.left-=s.options.style.border.radius}else{if(L.corner.search(/Right/)!==-1){y.left+=s.options.style.border.radius}}if(L.corner.search(/Top/)!==-1){y.top-=s.options.style.border.radius}else{if(L.corner.search(/Bottom/)!==-1){y.top+=s.options.style.border.radius}}}if(I){if(L.corner.search(/top/)!==-1){y.top-=I}else{if(L.corner.search(/bottom/)!==-1){y.top+=I}}if(L.corner.search(/left/)!==-1){y.left-=I}else{if(L.corner.search(/right/)!==-1){y.left+=I}}if(L.corner.search(/leftMiddle|rightMiddle/)!==-1){y.top-=1}}if(s.options.position.adjust.screen===true){y=o.call(s,y,G,L)}if(s.options.position.target==="mouse"&&s.options.position.adjust.mouse===true){if(s.options.position.adjust.screen===true&&s.elements.tip){K=s.elements.tip.attr("rel")}else{K=s.options.position.corner.tooltip}y.left+=(K.search(/right/i)!==-1)?-6:6;y.top+=(K.search(/bottom/i)!==-1)?-6:6}if(!s.elements.bgiframe&&f.browser.msie&&parseInt(f.browser.version.charAt(0))==6){f("select, object").each(function(){A=f(this).offset();A.bottom=A.top+f(this).height();A.right=A.left+f(this).width();if(y.top+L.dimensions.height>=A.top&&y.left+L.dimensions.width>=A.left){k.call(s)}})}y.left+=s.options.position.adjust.x;y.top+=s.options.position.adjust.y;F=s.getPosition();if(y.left!=F.left||y.top!=F.top){z=s.beforePositionUpdate.call(s,w);if(z===false){return s}s.cache.position=y;if(x===true){s.status.animated=true;s.elements.tooltip.animate(y,200,"swing",function(){s.status.animated=false})}else{s.elements.tooltip.css(y)}s.onPositionUpdate.call(s,w);if(typeof w!=="undefined"&&w.type&&w.type!=="mousemove"){f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_POSITION_UPDATED,"updatePosition")}}return s},updateWidth:function(w){var x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateWidth")}else{if(w&&typeof w!=="number"){return f.fn.qtip.log.error.call(s,2,"newWidth must be of type number","updateWidth")}}x=s.elements.contentWrapper.siblings().add(s.elements.tip).add(s.elements.button);if(!w){if(typeof s.options.style.width.value=="number"){w=s.options.style.width.value}else{s.elements.tooltip.css({width:"auto"});x.hide();if(f.browser.msie){s.elements.wrapper.add(s.elements.contentWrapper.children()).css({zoom:"normal"})}w=s.getDimensions().width+1;if(!s.options.style.width.value){if(w>s.options.style.width.max){w=s.options.style.width.max}if(w").get(0).getContext){z=s.elements.tooltip.find(".qtip-tip canvas:first");x=z.get(0).getContext("2d");x.clearRect(0,0,300,300);y=z.parent("div[rel]:first").attr("rel");B=b(y,s.options.style.tip.size.width,s.options.style.tip.size.height);h.call(s,z,B,s.options.style.tip.color||s.options.style.border.color)}else{if(f.browser.msie){z=s.elements.tooltip.find('.qtip-tip [nodeName="shape"]');z.attr("fillcolor",s.options.style.tip.color||s.options.style.border.color)}}}if(s.options.style.border.radius>0){s.elements.tooltip.find(".qtip-betweenCorners").css({backgroundColor:s.options.style.border.color});if(f("").get(0).getContext){A=g(s.options.style.border.radius);s.elements.tooltip.find(".qtip-wrapper canvas").each(function(){x=f(this).get(0).getContext("2d");x.clearRect(0,0,300,300);y=f(this).parent("div[rel]:first").attr("rel");r.call(s,f(this),A[y],s.options.style.border.radius,s.options.style.border.color)})}else{if(f.browser.msie){s.elements.tooltip.find('.qtip-wrapper [nodeName="arc"]').each(function(){f(this).attr("fillcolor",s.options.style.border.color)})}}}return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_STYLE_UPDATED,"updateStyle")},updateContent:function(A,y){var z,x,w;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateContent")}else{if(!A){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.NO_CONTENT_PROVIDED,"updateContent")}}z=s.beforeContentUpdate.call(s,A);if(typeof z=="string"){A=z}else{if(z===false){return}}if(f.browser.msie){s.elements.contentWrapper.children().css({zoom:"normal"})}if(A.jquery&&A.length>0){A.clone(true).appendTo(s.elements.content).show()}else{s.elements.content.html(A)}x=s.elements.content.find("img[complete=false]");if(x.length>0){w=0;x.each(function(C){f('').load(function(){if(++w==x.length){B()}})})}else{B()}function B(){s.updateWidth();if(y!==false){if(s.options.position.type!=="static"){s.updatePosition(s.elements.tooltip.is(":visible"),true)}if(s.options.style.tip.corner!==false){n.call(s)}}}s.onContentUpdate.call(s);return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_CONTENT_UPDATED,"loadContent")},loadContent:function(w,z,A){var y;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"loadContent")}y=s.beforeContentLoad.call(s);if(y===false){return s}if(A=="post"){f.post(w,z,x)}else{f.get(w,z,x)}function x(B){s.onContentLoad.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_CONTENT_LOADED,"loadContent");s.updateContent(B)}return s},updateTitle:function(w){if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"updateTitle")}else{if(!w){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.NO_CONTENT_PROVIDED,"updateTitle")}}returned=s.beforeTitleUpdate.call(s);if(returned===false){return s}if(s.elements.button){s.elements.button=s.elements.button.clone(true)}s.elements.title.html(w);if(s.elements.button){s.elements.title.prepend(s.elements.button)}s.onTitleUpdate.call(s);return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_TITLE_UPDATED,"updateTitle")},focus:function(A){var y,x,w,z;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"focus")}else{if(s.options.position.type=="static"){return f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.CANNOT_FOCUS_STATIC,"focus")}}y=parseInt(s.elements.tooltip.css("z-index"));x=6000+f("div.qtip[qtip]").length-1;if(!s.status.focused&&y!==x){z=s.beforeFocus.call(s,A);if(z===false){return s}f("div.qtip[qtip]").not(s.elements.tooltip).each(function(){if(f(this).qtip("api").status.rendered===true){w=parseInt(f(this).css("z-index"));if(typeof w=="number"&&w>-1){f(this).css({zIndex:parseInt(f(this).css("z-index"))-1})}f(this).qtip("api").status.focused=false}});s.elements.tooltip.css({zIndex:x});s.status.focused=true;s.onFocus.call(s,A);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_FOCUSED,"focus")}return s},disable:function(w){if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"disable")}if(w){if(!s.status.disabled){s.status.disabled=true;f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_DISABLED,"disable")}else{f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.TOOLTIP_ALREADY_DISABLED,"disable")}}else{if(s.status.disabled){s.status.disabled=false;f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_ENABLED,"disable")}else{f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.TOOLTIP_ALREADY_ENABLED,"disable")}}return s},destroy:function(){var w,x,y;x=s.beforeDestroy.call(s);if(x===false){return s}if(s.status.rendered){s.options.show.when.target.unbind("mousemove.qtip",s.updatePosition);s.options.show.when.target.unbind("mouseout.qtip",s.hide);s.options.show.when.target.unbind(s.options.show.when.event+".qtip");s.options.hide.when.target.unbind(s.options.hide.when.event+".qtip");s.elements.tooltip.unbind(s.options.hide.when.event+".qtip");s.elements.tooltip.unbind("mouseover.qtip",s.focus);s.elements.tooltip.remove()}else{s.options.show.when.target.unbind(s.options.show.when.event+".qtip-create")}if(typeof s.elements.target.data("qtip")=="object"){y=s.elements.target.data("qtip").interfaces;if(typeof y=="object"&&y.length>0){for(w=0;w0){s.elements.target.data("qtip").current=y.length-1}else{s.elements.target.removeData("qtip")}s.onDestroy.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_DESTROYED,"destroy");return s.elements.target},getPosition:function(){var w,x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"getPosition")}w=(s.elements.tooltip.css("display")!=="none")?false:true;if(w){s.elements.tooltip.css({visiblity:"hidden"}).show()}x=s.elements.tooltip.offset();if(w){s.elements.tooltip.css({visiblity:"visible"}).hide()}return x},getDimensions:function(){var w,x;if(!s.status.rendered){return f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.TOOLTIP_NOT_RENDERED,"getDimensions")}w=(!s.elements.tooltip.is(":visible"))?true:false;if(w){s.elements.tooltip.css({visiblity:"hidden"}).show()}x={height:s.elements.tooltip.outerHeight(),width:s.elements.tooltip.outerWidth()};if(w){s.elements.tooltip.css({visiblity:"visible"}).hide()}return x}})}function p(){var s,w,u,t,v,y,x;s=this;s.beforeRender.call(s);s.status.rendered=true;s.elements.tooltip='';s.elements.tooltip=f(s.elements.tooltip);s.elements.tooltip.appendTo(s.options.position.container);s.elements.tooltip.data("qtip",{current:0,interfaces:[s]});s.elements.wrapper=s.elements.tooltip.children("div:first");s.elements.contentWrapper=s.elements.wrapper.children("div:first").css({background:s.options.style.background});s.elements.content=s.elements.contentWrapper.children("div:first").css(q(s.options.style));if(f.browser.msie){s.elements.wrapper.add(s.elements.content).css({zoom:1})}if(s.options.hide.when.event=="unfocus"){s.elements.tooltip.attr("unfocus",true)}if(typeof s.options.style.width.value=="number"){s.updateWidth()}if(f("").get(0).getContext||f.browser.msie){if(s.options.style.border.radius>0){m.call(s)}else{s.elements.contentWrapper.css({border:s.options.style.border.width+"px solid "+s.options.style.border.color})}if(s.options.style.tip.corner!==false){e.call(s)}}else{s.elements.contentWrapper.css({border:s.options.style.border.width+"px solid "+s.options.style.border.color});s.options.style.border.radius=0;s.options.style.tip.corner=false;f.fn.qtip.log.error.call(s,2,f.fn.qtip.constants.CANVAS_VML_NOT_SUPPORTED,"render")}if((typeof s.options.content.text=="string"&&s.options.content.text.length>0)||(s.options.content.text.jquery&&s.options.content.text.length>0)){u=s.options.content.text}else{if(typeof s.elements.target.attr("title")=="string"&&s.elements.target.attr("title").length>0){u=s.elements.target.attr("title").replace("\\n","
");s.elements.target.attr("title","")}else{if(typeof s.elements.target.attr("alt")=="string"&&s.elements.target.attr("alt").length>0){u=s.elements.target.attr("alt").replace("\\n","
");s.elements.target.attr("alt","")}else{u=" ";f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.NO_VALID_CONTENT,"render")}}}if(s.options.content.title.text!==false){j.call(s)}s.updateContent(u);l.call(s);if(s.options.show.ready===true){s.show()}if(s.options.content.url!==false){t=s.options.content.url;v=s.options.content.data;y=s.options.content.method||"get";s.loadContent(t,v,y)}s.onRender.call(s);f.fn.qtip.log.error.call(s,1,f.fn.qtip.constants.EVENT_RENDERED,"render")}function m(){var F,z,t,B,x,E,u,G,D,y,w,C,A,s,v;F=this;F.elements.wrapper.find(".qtip-borderBottom, .qtip-borderTop").remove();t=F.options.style.border.width;B=F.options.style.border.radius;x=F.options.style.border.color||F.options.style.tip.color;E=g(B);u={};for(z in E){u[z]='
';if(f("").get(0).getContext){u[z]+=''}else{if(f.browser.msie){G=B*2+3;u[z]+=''}}u[z]+="
"}D=F.getDimensions().width-(Math.max(t,B)*2);y='
';w='
'+u.topLeft+u.topRight+y;F.elements.wrapper.prepend(w);C='
'+u.bottomLeft+u.bottomRight+y;F.elements.wrapper.append(C);if(f("").get(0).getContext){F.elements.wrapper.find("canvas").each(function(){A=E[f(this).parent("[rel]:first").attr("rel")];r.call(F,f(this),A,B,x)})}else{if(f.browser.msie){F.elements.tooltip.append('')}}s=Math.max(B,(B+(t-B)));v=Math.max(t-B,0);F.elements.contentWrapper.css({border:"0px solid "+x,borderWidth:v+"px "+s+"px"})}function r(u,w,s,t){var v=u.get(0).getContext("2d");v.fillStyle=t;v.beginPath();v.arc(w[0],w[1],s,0,Math.PI*2,false);v.fill()}function e(v){var t,s,x,u,w;t=this;if(t.elements.tip!==null){t.elements.tip.remove()}s=t.options.style.tip.color||t.options.style.border.color;if(t.options.style.tip.corner===false){return}else{if(!v){v=t.options.style.tip.corner}}x=b(v,t.options.style.tip.size.width,t.options.style.tip.size.height);t.elements.tip='
';if(f("").get(0).getContext){t.elements.tip+=''}else{if(f.browser.msie){u=t.options.style.tip.size.width+","+t.options.style.tip.size.height;w="m"+x[0][0]+","+x[0][1];w+=" l"+x[1][0]+","+x[1][1];w+=" "+x[2][0]+","+x[2][1];w+=" xe";t.elements.tip+='';t.elements.tip+='';t.elements.contentWrapper.css("position","relative")}}t.elements.tooltip.prepend(t.elements.tip+"
");t.elements.tip=t.elements.tooltip.find("."+t.options.style.classes.tip).eq(0);if(f("").get(0).getContext){h.call(t,t.elements.tip.find("canvas:first"),x,s)}if(v.search(/top/)!==-1&&f.browser.msie&&parseInt(f.browser.version.charAt(0))===6){t.elements.tip.css({marginTop:-4})}n.call(t,v)}function h(t,v,s){var u=t.get(0).getContext("2d");u.fillStyle=s;u.beginPath();u.moveTo(v[0][0],v[0][1]);u.lineTo(v[1][0],v[1][1]);u.lineTo(v[2][0],v[2][1]);u.fill()}function n(u){var t,w,s,x,v;t=this;if(t.options.style.tip.corner===false||!t.elements.tip){return}if(!u){u=t.elements.tip.attr("rel")}w=positionAdjust=(f.browser.msie)?1:0;t.elements.tip.css(u.match(/left|right|top|bottom/)[0],0);if(u.search(/top|bottom/)!==-1){if(f.browser.msie){if(parseInt(f.browser.version.charAt(0))===6){positionAdjust=(u.search(/top/)!==-1)?-3:1}else{positionAdjust=(u.search(/top/)!==-1)?1:2}}if(u.search(/Middle/)!==-1){t.elements.tip.css({left:"50%",marginLeft:-(t.options.style.tip.size.width/2)})}else{if(u.search(/Left/)!==-1){t.elements.tip.css({left:t.options.style.border.radius-w})}else{if(u.search(/Right/)!==-1){t.elements.tip.css({right:t.options.style.border.radius+w})}}}if(u.search(/top/)!==-1){t.elements.tip.css({top:-positionAdjust})}else{t.elements.tip.css({bottom:positionAdjust})}}else{if(u.search(/left|right/)!==-1){if(f.browser.msie){positionAdjust=(parseInt(f.browser.version.charAt(0))===6)?1:((u.search(/left/)!==-1)?1:2)}if(u.search(/Middle/)!==-1){t.elements.tip.css({top:"50%",marginTop:-(t.options.style.tip.size.height/2)})}else{if(u.search(/Top/)!==-1){t.elements.tip.css({top:t.options.style.border.radius-w})}else{if(u.search(/Bottom/)!==-1){t.elements.tip.css({bottom:t.options.style.border.radius+w})}}}if(u.search(/left/)!==-1){t.elements.tip.css({left:-positionAdjust})}else{t.elements.tip.css({right:positionAdjust})}}}s="padding-"+u.match(/left|right|top|bottom/)[0];x=t.options.style.tip.size[(s.search(/left|right/)!==-1)?"width":"height"];t.elements.tooltip.css("padding",0);t.elements.tooltip.css(s,x);if(f.browser.msie&&parseInt(f.browser.version.charAt(0))==6){v=parseInt(t.elements.tip.css("margin-top"))||0;v+=parseInt(t.elements.content.css("margin-top"))||0;t.elements.tip.css({marginTop:v})}}function j(){var s=this;if(s.elements.title!==null){s.elements.title.remove()}s.elements.title=f('
').css(q(s.options.style.title,true)).css({zoom:(f.browser.msie)?1:0}).prependTo(s.elements.contentWrapper);if(s.options.content.title.text){s.updateTitle.call(s,s.options.content.title.text)}if(s.options.content.title.button!==false&&typeof s.options.content.title.button=="string"){s.elements.button=f('').css(q(s.options.style.button,true)).html(s.options.content.title.button).prependTo(s.elements.title).click(function(t){if(!s.status.disabled){s.hide(t)}})}}function l(){var t,v,u,s;t=this;v=t.options.show.when.target;u=t.options.hide.when.target;if(t.options.hide.fixed){u=u.add(t.elements.tooltip)}if(t.options.hide.when.event=="inactive"){s=["click","dblclick","mousedown","mouseup","mousemove","mouseout","mouseenter","mouseleave","mouseover"];function y(z){if(t.status.disabled===true){return}clearTimeout(t.timers.inactive);t.timers.inactive=setTimeout(function(){f(s).each(function(){u.unbind(this+".qtip-inactive");t.elements.content.unbind(this+".qtip-inactive")});t.hide(z)},t.options.hide.delay)}}else{if(t.options.hide.fixed===true){t.elements.tooltip.bind("mouseover.qtip",function(){if(t.status.disabled===true){return}clearTimeout(t.timers.hide)})}}function x(z){if(t.status.disabled===true){return}if(t.options.hide.when.event=="inactive"){f(s).each(function(){u.bind(this+".qtip-inactive",y);t.elements.content.bind(this+".qtip-inactive",y)});y()}clearTimeout(t.timers.show);clearTimeout(t.timers.hide);t.timers.show=setTimeout(function(){t.show(z)},t.options.show.delay)}function w(z){if(t.status.disabled===true){return}if(t.options.hide.fixed===true&&t.options.hide.when.event.search(/mouse(out|leave)/i)!==-1&&f(z.relatedTarget).parents("div.qtip[qtip]").length>0){z.stopPropagation();z.preventDefault();clearTimeout(t.timers.hide);return false}clearTimeout(t.timers.show);clearTimeout(t.timers.hide);t.elements.tooltip.stop(true,true);t.timers.hide=setTimeout(function(){t.hide(z)},t.options.hide.delay)}if((t.options.show.when.target.add(t.options.hide.when.target).length===1&&t.options.show.when.event==t.options.hide.when.event&&t.options.hide.when.event!=="inactive")||t.options.hide.when.event=="unfocus"){t.cache.toggle=0;v.bind(t.options.show.when.event+".qtip",function(z){if(t.cache.toggle==0){x(z)}else{w(z)}})}else{v.bind(t.options.show.when.event+".qtip",x);if(t.options.hide.when.event!=="inactive"){u.bind(t.options.hide.when.event+".qtip",w)}}if(t.options.position.type.search(/(fixed|absolute)/)!==-1){t.elements.tooltip.bind("mouseover.qtip",t.focus)}if(t.options.position.target==="mouse"&&t.options.position.type!=="static"){v.bind("mousemove.qtip",function(z){t.cache.mouse={x:z.pageX,y:z.pageY};if(t.status.disabled===false&&t.options.position.adjust.mouse===true&&t.options.position.type!=="static"&&t.elements.tooltip.css("display")!=="none"){t.updatePosition(z)}})}}function o(u,v,A){var z,s,x,y,t,w;z=this;if(A.corner=="center"){return v.position}s=f.extend({},u);y={x:false,y:false};t={left:(s.left=f.fn.qtip.cache.screen.width+f.fn.qtip.cache.screen.scroll.left),top:(s.top=f.fn.qtip.cache.screen.height+f.fn.qtip.cache.screen.scroll.top)};x={left:(t.left&&(A.corner.search(/right/i)!=-1||(A.corner.search(/right/i)==-1&&!t.right))),right:(t.right&&(A.corner.search(/left/i)!=-1||(A.corner.search(/left/i)==-1&&!t.left))),top:(t.top&&A.corner.search(/top/i)==-1),bottom:(t.bottom&&A.corner.search(/bottom/i)==-1)};if(x.left){if(z.options.position.target!=="mouse"){s.left=v.position.left+v.dimensions.width}else{s.left=z.cache.mouse.x}y.x="Left"}else{if(x.right){if(z.options.position.target!=="mouse"){s.left=v.position.left-A.dimensions.width}else{s.left=z.cache.mouse.x-A.dimensions.width}y.x="Right"}}if(x.top){if(z.options.position.target!=="mouse"){s.top=v.position.top+v.dimensions.height}else{s.top=z.cache.mouse.y}y.y="top"}else{if(x.bottom){if(z.options.position.target!=="mouse"){s.top=v.position.top-A.dimensions.height}else{s.top=z.cache.mouse.y-A.dimensions.height}y.y="bottom"}}if(s.left<0){s.left=u.left;y.x=false}if(s.top<0){s.top=u.top;y.y=false}if(z.options.style.tip.corner!==false){s.corner=new String(A.corner);if(y.x!==false){s.corner=s.corner.replace(/Left|Right|Middle/,y.x)}if(y.y!==false){s.corner=s.corner.replace(/top|bottom/,y.y)}if(s.corner!==z.elements.tip.attr("rel")){e.call(z,s.corner)}}return s}function q(u,t){var v,s;v=f.extend(true,{},u);for(s in v){if(t===true&&s.search(/(tip|classes)/i)!==-1){delete v[s]}else{if(!t&&s.search(/(width|border|tip|title|classes|user)/i)!==-1){delete v[s]}}}return v}function c(s){if(typeof s.tip!=="object"){s.tip={corner:s.tip}}if(typeof s.tip.size!=="object"){s.tip.size={width:s.tip.size,height:s.tip.size}}if(typeof s.border!=="object"){s.border={width:s.border}}if(typeof s.width!=="object"){s.width={value:s.width}}if(typeof s.width.max=="string"){s.width.max=parseInt(s.width.max.replace(/([0-9]+)/i,"$1"))}if(typeof s.width.min=="string"){s.width.min=parseInt(s.width.min.replace(/([0-9]+)/i,"$1"))}if(typeof s.tip.size.x=="number"){s.tip.size.width=s.tip.size.x;delete s.tip.size.x}if(typeof s.tip.size.y=="number"){s.tip.size.height=s.tip.size.y;delete s.tip.size.y}return s}function a(){var s,t,u,x,v,w;s=this;u=[true,{}];for(t=0;t0){v.tip.size.width+=1}if(v.tip.size.height%2>0){v.tip.size.height+=1}if(v.tip.corner===true){v.tip.corner=(s.options.position.corner.tooltip==="center")?false:s.options.position.corner.tooltip}return v}function b(v,u,t){var s={bottomRight:[[0,0],[u,t],[u,0]],bottomLeft:[[0,0],[u,0],[0,t]],topRight:[[0,t],[u,0],[u,t]],topLeft:[[0,0],[0,t],[u,t]],topMiddle:[[0,t],[u/2,0],[u,t]],bottomMiddle:[[0,0],[u,0],[u/2,t]],rightMiddle:[[0,0],[u,t/2],[0,t]],leftMiddle:[[u,0],[u,t],[0,t/2]]};s.leftTop=s.bottomRight;s.rightTop=s.bottomLeft;s.leftBottom=s.topRight;s.rightBottom=s.topLeft;return s[v]}function g(s){var t;if(f("").get(0).getContext){t={topLeft:[s,s],topRight:[0,s],bottomLeft:[s,0],bottomRight:[0,0]}}else{if(f.browser.msie){t={topLeft:[-90,90,0],topRight:[-90,90,-s],bottomLeft:[90,270,0],bottomRight:[90,270,-s]}}}return t}function k(){var s,t,u;s=this;u=s.getDimensions();t='