Yoast SEO - Version 14.0.1

Version Description

Release Date: April 28th, 2020

Bugfixes:

  • Fixes a bug where a fatal error would be thrown when a breadcrumb title was too long.
  • Fixes a bug where a fatal error would be thrown when DB_CHARSET was not defined.
  • Fixes a bug where a fatal error would be thrown when breadcrumbs were rendered in the admin.
  • Fixes a bug where a fatal error would be thrown when the Yoast migrations table did not have a primary key.
  • Fixes a bug where a fatal exception would be thrown when building an indexable failed.
  • Fixes a bug where the order of the breadcrumbs was incorrect when more than 3 nested taxonomies were used.
  • Fixes a bug where HTML tags would no longer be allowed in the breadcrumbs.
  • Fixes a bug where no title would be shown in the Yoast indexation status modal.
  • Fixes a bug where changes made through the wpseo_robots filter would not be shown in the googlebot and bingbot meta tag output.

Other:

  • Yoast SEO needs to have the right to create a database index. If you have restricted the creation of database indexes on your setup, please make sure to temporarily allow Yoast SEO to create indexes before updating.
Download this release

Release Info

Developer Yoast
Plugin Icon 128x128 Yoast SEO
Version 14.0.1
Comparing to
See all releases

Code changes from version 14.0 to 14.0.1

Files changed (113) hide show
  1. admin/class-primary-term-admin.php +0 -3
  2. css/dist/{admin-global-1400-rtl.css → admin-global-1401-rtl.css} +0 -0
  3. css/dist/{admin-global-1400.css → admin-global-1401.css} +0 -0
  4. css/dist/{adminbar-1400-rtl.css → adminbar-1401-rtl.css} +0 -0
  5. css/dist/{adminbar-1400.css → adminbar-1401.css} +0 -0
  6. css/dist/{alerts-1400-rtl.css → alerts-1401-rtl.css} +0 -0
  7. css/dist/{alerts-1400.css → alerts-1401.css} +0 -0
  8. css/dist/{dashboard-1400-rtl.css → dashboard-1401-rtl.css} +0 -0
  9. css/dist/{dashboard-1400.css → dashboard-1401.css} +0 -0
  10. css/dist/{edit-page-1400-rtl.css → edit-page-1401-rtl.css} +0 -0
  11. css/dist/{edit-page-1400.css → edit-page-1401.css} +0 -0
  12. css/dist/{featured-image-1400-rtl.css → featured-image-1401-rtl.css} +0 -0
  13. css/dist/{featured-image-1400.css → featured-image-1401.css} +0 -0
  14. css/dist/{filter-explanation-1400-rtl.css → filter-explanation-1401-rtl.css} +0 -0
  15. css/dist/{filter-explanation-1400.css → filter-explanation-1401.css} +0 -0
  16. css/dist/{inside-editor-1400-rtl.css → inside-editor-1401-rtl.css} +0 -0
  17. css/dist/{inside-editor-1400.css → inside-editor-1401.css} +0 -0
  18. css/dist/{metabox-1400-rtl.css → metabox-1401-rtl.css} +0 -0
  19. css/dist/{metabox-1400.css → metabox-1401.css} +0 -0
  20. css/dist/{metabox-primary-category-1400-rtl.css → metabox-primary-category-1401-rtl.css} +0 -0
  21. css/dist/{metabox-primary-category-1400.css → metabox-primary-category-1401.css} +0 -0
  22. css/dist/{monorepo-1400-rtl.css → monorepo-1401-rtl.css} +0 -0
  23. css/dist/{monorepo-1400.css → monorepo-1401.css} +0 -0
  24. css/dist/{search-appearance-1400-rtl.css → search-appearance-1401-rtl.css} +0 -0
  25. css/dist/{search-appearance-1400.css → search-appearance-1401.css} +0 -0
  26. css/dist/{structured-data-blocks-1400-rtl.css → structured-data-blocks-1401-rtl.css} +0 -0
  27. css/dist/{structured-data-blocks-1400.css → structured-data-blocks-1401.css} +0 -0
  28. css/dist/{toggle-switch-1400-rtl.css → toggle-switch-1401-rtl.css} +0 -0
  29. css/dist/{toggle-switch-1400.css → toggle-switch-1401.css} +0 -0
  30. css/dist/{wpseo-dismissible-1400-rtl.css → wpseo-dismissible-1401-rtl.css} +0 -0
  31. css/dist/{wpseo-dismissible-1400.css → wpseo-dismissible-1401.css} +0 -0
  32. css/dist/{yoast-components-1400-rtl.css → yoast-components-1401-rtl.css} +0 -0
  33. css/dist/{yoast-components-1400.css → yoast-components-1401.css} +0 -0
  34. css/dist/{yoast-extensions-1400-rtl.css → yoast-extensions-1401-rtl.css} +0 -0
  35. css/dist/{yoast-extensions-1400.css → yoast-extensions-1401.css} +0 -0
  36. css/dist/{yst_plugin_tools-1400-rtl.css → yst_plugin_tools-1401-rtl.css} +0 -0
  37. css/dist/{yst_plugin_tools-1400.css → yst_plugin_tools-1401.css} +0 -0
  38. css/dist/{yst_seo_score-1400-rtl.css → yst_seo_score-1401-rtl.css} +0 -0
  39. css/dist/{yst_seo_score-1400.css → yst_seo_score-1401.css} +0 -0
  40. js/dist/{analysis-1400.js → analysis-1401.js} +0 -0
  41. js/dist/{babel-polyfill-1400.js → babel-polyfill-1401.js} +0 -0
  42. js/dist/{commons-1400.js → commons-1401.js} +0 -0
  43. js/dist/{components-1400.js → components-1401.js} +0 -0
  44. js/dist/{configuration-wizard-1400.js → configuration-wizard-1401.js} +0 -0
  45. js/dist/{help-scout-beacon-1400.js → help-scout-beacon-1401.js} +0 -0
  46. js/dist/{jed-1400.js → jed-1401.js} +0 -0
  47. js/dist/{redux-1400.js → redux-1401.js} +0 -0
  48. js/dist/{search-appearance-1400.js → search-appearance-1401.js} +0 -0
  49. js/dist/{styled-components-1400.js → styled-components-1401.js} +0 -0
  50. js/dist/{wp-seo-admin-1400.js → wp-seo-admin-1401.js} +0 -0
  51. js/dist/{wp-seo-admin-global-1400.js → wp-seo-admin-global-1401.js} +0 -0
  52. js/dist/{wp-seo-admin-gsc-1400.js → wp-seo-admin-gsc-1401.js} +0 -0
  53. js/dist/{wp-seo-admin-media-1400.js → wp-seo-admin-media-1401.js} +0 -0
  54. js/dist/{wp-seo-analysis-worker-1400.js → wp-seo-analysis-worker-1401.js} +0 -0
  55. js/dist/{wp-seo-api-1400.js → wp-seo-api-1401.js} +0 -0
  56. js/dist/{wp-seo-bulk-editor-1400.js → wp-seo-bulk-editor-1401.js} +0 -0
  57. js/dist/{wp-seo-dashboard-widget-1400.js → wp-seo-dashboard-widget-1401.js} +0 -0
  58. js/dist/{wp-seo-edit-page-1400.js → wp-seo-edit-page-1401.js} +0 -0
  59. js/dist/{wp-seo-featured-image-1400.js → wp-seo-featured-image-1401.js} +0 -0
  60. js/dist/{wp-seo-filter-explanation-1400.js → wp-seo-filter-explanation-1401.js} +0 -0
  61. js/dist/{wp-seo-indexation-1400.js → wp-seo-indexation-1401.js} +0 -0
  62. js/dist/{wp-seo-metabox-1400.js → wp-seo-metabox-1401.js} +0 -0
  63. js/dist/{wp-seo-metabox-category-1400.js → wp-seo-metabox-category-1401.js} +0 -0
  64. js/dist/{wp-seo-modal-1400.js → wp-seo-modal-1401.js} +0 -0
  65. js/dist/{wp-seo-network-admin-1400.js → wp-seo-network-admin-1401.js} +0 -0
  66. js/dist/{wp-seo-post-scraper-1400.js → wp-seo-post-scraper-1401.js} +0 -0
  67. js/dist/{wp-seo-quick-edit-handler-1400.js → wp-seo-quick-edit-handler-1401.js} +0 -0
  68. js/dist/{wp-seo-recalculate-1400.js → wp-seo-recalculate-1401.js} +0 -0
  69. js/dist/{wp-seo-reindex-links-1400.js → wp-seo-reindex-links-1401.js} +0 -0
  70. js/dist/{wp-seo-replacevar-plugin-1400.js → wp-seo-replacevar-plugin-1401.js} +0 -0
  71. js/dist/{wp-seo-shortcode-plugin-1400.js → wp-seo-shortcode-plugin-1401.js} +0 -0
  72. js/dist/{wp-seo-structured-data-blocks-1400.js → wp-seo-structured-data-blocks-1401.js} +0 -0
  73. js/dist/{wp-seo-term-scraper-1400.js → wp-seo-term-scraper-1401.js} +0 -0
  74. js/dist/{wp-seo-used-keywords-assessment-1400.js → wp-seo-used-keywords-assessment-1401.js} +0 -0
  75. languages/yoast-components-es_ES.json +1 -1
  76. migrations/20200428123747_BreadcrumbTitleAndHierarchyReset.php +52 -0
  77. readme.txt +20 -1
  78. src/actions/indexation/indexable-post-indexation-action.php +4 -1
  79. src/actions/indexation/indexable-term-indexation-action.php +4 -1
  80. src/backwards-compatibility/breadcrumbs.php +3 -8
  81. src/builders/indexable-builder.php +6 -3
  82. src/builders/indexable-hierarchy-builder.php +12 -1
  83. src/builders/indexable-post-builder.php +6 -9
  84. src/builders/indexable-term-builder.php +4 -6
  85. src/config/ruckusing-framework.php +7 -2
  86. src/generators/breadcrumbs-generator.php +8 -2
  87. src/generators/schema/breadcrumb.php +1 -1
  88. src/initializers/database-setup.php +1 -1
  89. src/initializers/migration-runner.php +1 -1
  90. src/integrations/watchers/indexable-post-watcher.php +6 -3
  91. src/integrations/watchers/indexable-term-watcher.php +1 -7
  92. src/presentations/indexable-author-archive-presentation.php +6 -10
  93. src/presentations/indexable-date-archive-presentation.php +2 -2
  94. src/presentations/indexable-error-page-presentation.php +2 -2
  95. src/presentations/indexable-post-type-archive-presentation.php +2 -2
  96. src/presentations/indexable-post-type-presentation.php +2 -2
  97. src/presentations/indexable-presentation.php +53 -1
  98. src/presentations/indexable-search-result-page-presentation.php +2 -2
  99. src/presentations/indexable-term-archive-presentation.php +3 -3
  100. src/presenters/admin/indexation-modal-presenter.php +1 -1
  101. src/presenters/admin/indexation-warning-presenter.php +2 -1
  102. src/presenters/breadcrumbs-presenter.php +1 -8
  103. src/presenters/robots-presenter.php +0 -18
  104. src/repositories/indexable-repository.php +31 -11
  105. vendor/autoload.php +1 -1
  106. vendor/autoload_52.php +7 -0
  107. vendor/composer/ClassLoader.php +4 -4
  108. vendor/composer/ClassLoader52.php +271 -0
  109. vendor/composer/autoload_real.php +7 -4
  110. vendor/composer/autoload_real_52.php +58 -0
  111. vendor/composer/autoload_static.php +4 -4
  112. wp-seo-main.php +1 -1
  113. wp-seo.php +1 -1
admin/class-primary-term-admin.php CHANGED
@@ -21,9 +21,6 @@ class WPSEO_Primary_Term_Admin implements WPSEO_WordPress_Integration {
21
  add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
22
 
23
  add_action( 'save_post', [ $this, 'save_primary_terms' ] );
24
-
25
- $primary_term = new WPSEO_Frontend_Primary_Category();
26
- $primary_term->register_hooks();
27
  }
28
 
29
  /**
21
  add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
22
 
23
  add_action( 'save_post', [ $this, 'save_primary_terms' ] );
 
 
 
24
  }
25
 
26
  /**
css/dist/{admin-global-1400-rtl.css → admin-global-1401-rtl.css} RENAMED
File without changes
css/dist/{admin-global-1400.css → admin-global-1401.css} RENAMED
File without changes
css/dist/{adminbar-1400-rtl.css → adminbar-1401-rtl.css} RENAMED
File without changes
css/dist/{adminbar-1400.css → adminbar-1401.css} RENAMED
File without changes
css/dist/{alerts-1400-rtl.css → alerts-1401-rtl.css} RENAMED
File without changes
css/dist/{alerts-1400.css → alerts-1401.css} RENAMED
File without changes
css/dist/{dashboard-1400-rtl.css → dashboard-1401-rtl.css} RENAMED
File without changes
css/dist/{dashboard-1400.css → dashboard-1401.css} RENAMED
File without changes
css/dist/{edit-page-1400-rtl.css → edit-page-1401-rtl.css} RENAMED
File without changes
css/dist/{edit-page-1400.css → edit-page-1401.css} RENAMED
File without changes
css/dist/{featured-image-1400-rtl.css → featured-image-1401-rtl.css} RENAMED
File without changes
css/dist/{featured-image-1400.css → featured-image-1401.css} RENAMED
File without changes
css/dist/{filter-explanation-1400-rtl.css → filter-explanation-1401-rtl.css} RENAMED
File without changes
css/dist/{filter-explanation-1400.css → filter-explanation-1401.css} RENAMED
File without changes
css/dist/{inside-editor-1400-rtl.css → inside-editor-1401-rtl.css} RENAMED
File without changes
css/dist/{inside-editor-1400.css → inside-editor-1401.css} RENAMED
File without changes
css/dist/{metabox-1400-rtl.css → metabox-1401-rtl.css} RENAMED
File without changes
css/dist/{metabox-1400.css → metabox-1401.css} RENAMED
File without changes
css/dist/{metabox-primary-category-1400-rtl.css → metabox-primary-category-1401-rtl.css} RENAMED
File without changes
css/dist/{metabox-primary-category-1400.css → metabox-primary-category-1401.css} RENAMED
File without changes
css/dist/{monorepo-1400-rtl.css → monorepo-1401-rtl.css} RENAMED
File without changes
css/dist/{monorepo-1400.css → monorepo-1401.css} RENAMED
File without changes
css/dist/{search-appearance-1400-rtl.css → search-appearance-1401-rtl.css} RENAMED
File without changes
css/dist/{search-appearance-1400.css → search-appearance-1401.css} RENAMED
File without changes
css/dist/{structured-data-blocks-1400-rtl.css → structured-data-blocks-1401-rtl.css} RENAMED
File without changes
css/dist/{structured-data-blocks-1400.css → structured-data-blocks-1401.css} RENAMED
File without changes
css/dist/{toggle-switch-1400-rtl.css → toggle-switch-1401-rtl.css} RENAMED
File without changes
css/dist/{toggle-switch-1400.css → toggle-switch-1401.css} RENAMED
File without changes
css/dist/{wpseo-dismissible-1400-rtl.css → wpseo-dismissible-1401-rtl.css} RENAMED
File without changes
css/dist/{wpseo-dismissible-1400.css → wpseo-dismissible-1401.css} RENAMED
File without changes
css/dist/{yoast-components-1400-rtl.css → yoast-components-1401-rtl.css} RENAMED
File without changes
css/dist/{yoast-components-1400.css → yoast-components-1401.css} RENAMED
File without changes
css/dist/{yoast-extensions-1400-rtl.css → yoast-extensions-1401-rtl.css} RENAMED
File without changes
css/dist/{yoast-extensions-1400.css → yoast-extensions-1401.css} RENAMED
File without changes
css/dist/{yst_plugin_tools-1400-rtl.css → yst_plugin_tools-1401-rtl.css} RENAMED
File without changes
css/dist/{yst_plugin_tools-1400.css → yst_plugin_tools-1401.css} RENAMED
File without changes
css/dist/{yst_seo_score-1400-rtl.css → yst_seo_score-1401-rtl.css} RENAMED
File without changes
css/dist/{yst_seo_score-1400.css → yst_seo_score-1401.css} RENAMED
File without changes
js/dist/{analysis-1400.js → analysis-1401.js} RENAMED
File without changes
js/dist/{babel-polyfill-1400.js → babel-polyfill-1401.js} RENAMED
File without changes
js/dist/{commons-1400.js → commons-1401.js} RENAMED
File without changes
js/dist/{components-1400.js → components-1401.js} RENAMED
File without changes
js/dist/{configuration-wizard-1400.js → configuration-wizard-1401.js} RENAMED
File without changes
js/dist/{help-scout-beacon-1400.js → help-scout-beacon-1401.js} RENAMED
File without changes
js/dist/{jed-1400.js → jed-1401.js} RENAMED
File without changes
js/dist/{redux-1400.js → redux-1401.js} RENAMED
File without changes
js/dist/{search-appearance-1400.js → search-appearance-1401.js} RENAMED
File without changes
js/dist/{styled-components-1400.js → styled-components-1401.js} RENAMED
File without changes
js/dist/{wp-seo-admin-1400.js → wp-seo-admin-1401.js} RENAMED
File without changes
js/dist/{wp-seo-admin-global-1400.js → wp-seo-admin-global-1401.js} RENAMED
File without changes
js/dist/{wp-seo-admin-gsc-1400.js → wp-seo-admin-gsc-1401.js} RENAMED
File without changes
js/dist/{wp-seo-admin-media-1400.js → wp-seo-admin-media-1401.js} RENAMED
File without changes
js/dist/{wp-seo-analysis-worker-1400.js → wp-seo-analysis-worker-1401.js} RENAMED
File without changes
js/dist/{wp-seo-api-1400.js → wp-seo-api-1401.js} RENAMED
File without changes
js/dist/{wp-seo-bulk-editor-1400.js → wp-seo-bulk-editor-1401.js} RENAMED
File without changes
js/dist/{wp-seo-dashboard-widget-1400.js → wp-seo-dashboard-widget-1401.js} RENAMED
File without changes
js/dist/{wp-seo-edit-page-1400.js → wp-seo-edit-page-1401.js} RENAMED
File without changes
js/dist/{wp-seo-featured-image-1400.js → wp-seo-featured-image-1401.js} RENAMED
File without changes
js/dist/{wp-seo-filter-explanation-1400.js → wp-seo-filter-explanation-1401.js} RENAMED
File without changes
js/dist/{wp-seo-indexation-1400.js → wp-seo-indexation-1401.js} RENAMED
File without changes
js/dist/{wp-seo-metabox-1400.js → wp-seo-metabox-1401.js} RENAMED
File without changes
js/dist/{wp-seo-metabox-category-1400.js → wp-seo-metabox-category-1401.js} RENAMED
File without changes
js/dist/{wp-seo-modal-1400.js → wp-seo-modal-1401.js} RENAMED
File without changes
js/dist/{wp-seo-network-admin-1400.js → wp-seo-network-admin-1401.js} RENAMED
File without changes
js/dist/{wp-seo-post-scraper-1400.js → wp-seo-post-scraper-1401.js} RENAMED
File without changes
js/dist/{wp-seo-quick-edit-handler-1400.js → wp-seo-quick-edit-handler-1401.js} RENAMED
File without changes
js/dist/{wp-seo-recalculate-1400.js → wp-seo-recalculate-1401.js} RENAMED
File without changes
js/dist/{wp-seo-reindex-links-1400.js → wp-seo-reindex-links-1401.js} RENAMED
File without changes
js/dist/{wp-seo-replacevar-plugin-1400.js → wp-seo-replacevar-plugin-1401.js} RENAMED
File without changes
js/dist/{wp-seo-shortcode-plugin-1400.js → wp-seo-shortcode-plugin-1401.js} RENAMED
File without changes
js/dist/{wp-seo-structured-data-blocks-1400.js → wp-seo-structured-data-blocks-1401.js} RENAMED
File without changes
js/dist/{wp-seo-term-scraper-1400.js → wp-seo-term-scraper-1401.js} RENAMED
File without changes
js/dist/{wp-seo-used-keywords-assessment-1400.js → wp-seo-used-keywords-assessment-1401.js} RENAMED
File without changes
languages/yoast-components-es_ES.json CHANGED
@@ -1 +1 @@
1
- {"domain":"yoast-components","locale_data":{"yoast-components":{"":{"domain":"yoast-components","plural-forms":"nplurals=2; plural=n != 1;","lang":"es"},"Preview as:":["Vista preview como:"],"Mobile result":["Resultado móvil"],"Desktop result":["Resultado en escritorio"],"Dismiss this alert":["Descartar esta alerta"],"The following words and word combinations occur the most in the content. These give an indication of what your content focuses on. If the words differ a lot from your topic, you might want to rewrite your content accordingly. ":["Las siguientes palabras y combinaciones de palabras son las que más aparecen en el contenido. Estas dan una indicación de en qué se centra tu contenido. Si las palabras difieren mucho de tu temática, puede que quieras reescribir tu contenido debidamente."],"Once you add a bit more copy, we'll give you a list of words that occur the most in the content. These give an indication of what your content focuses on.":["Una vez que hayas escrito un poco más, te daremos la lista de las palabras que más aparecen en el contenido. Estas dan una indicación de en qué se centra tu contenido."],"%d occurrences":["%d apariciones"],"We could not find any relevant articles on your website that you could link to from your post.":["No hemos podido encontrar ningún artículo relevante en tu web al que puedas enlazar desde tu entrada."],"The image you selected is too small for Facebook":["La imagen que has elegido es demasiado pequeña para Facebook"],"The given image url cannot be loaded":["La URL de la imagen dada no se puede cargar"],"This is a list of related content to which you could link in your post. {{a}}Read our article about site structure{{/a}} to learn more about how internal linking can help improve your SEO.":["Esta es una lista de contenido relacionado al que podrías enlazar en tu entrada. {{a}}Lee nuestro artículo sobre la estructura del sitio{{/a}} para aprende rmás sobre como el enlazado interno puede ayudar a mejorar tu SEO."],"Are you trying to use multiple keyphrases? You should add them separately below.":["¿Estás tratando de utilizar varias frases clave? Debes añadirlas por separado a continuación."],"Mark as cornerstone content":["Marcar como contenido esencial"],"image preview":["vista previa de imagen"],"Copied!":["¡Copiado!"],"Not supported!":["¡No compatible!"],"Read {{a}}our article about site structure{{/a}} to learn more about how internal linking can help improve your SEO.":["Lee {{a}}nuestro artículo sobre de la estructura del sitio{{{/a}} para saber más sobre cómo los enlaces internos pueden ayudar a mejorar tu SEO."],"Once you add a bit more copy, we'll give you a list of related content here to which you could link in your post.":["Cuando añadas algo más de texto te mostraremos aquí una lista de contenidos relacionados a los que podrías enlazar en tu entrada."],"Consider linking to these {{a}}cornerstone articles:{{/a}}":["Plantéate enlazar a estos {{a}}artículos esenciales{{/a}}"],"Consider linking to these articles:":["Plantéate enlazar a estos artículos:"],"Copy link":["Copiar enlace"],"Copy link to suggested article: %s":["Copiar enlace al artículo sugerido: %s"],"Read our %1$sultimate guide to keyword research%2$s to learn more about keyword research and keyword strategy.":["Lee nuestra %1$sguía definitiva para la búsqueda de palabras clave%2$s para aprender más sobre la búsqueda y estrategia de palabras clave."],"Once you add a bit more copy, we'll give you a list of words and word combinations that occur the most in the content. These give an indication of what your content focuses on.":["Una vez que hayas escrito un poco más, te daremos la lista de las palabras y combinaciones de palabras que más aparecen en el contenido. Estas dan una indicación de en qué se centra tu contenido."],"The following words occur the most in the content. These give an indication of what your content focuses on. If the words differ a lot from your topic, you might want to rewrite your content accordingly. ":["Las siguientes palabras son las que más aparecen en el contenido. Estas dan una indicación de en qué se centra tu contenido. Si las palabras difieren mucho de tu temática, puede que quieras reescribir tu contenido debidamente."],"Prominent words":["Palabras importantes"],"Something went wrong. Please reload the page.":["Algo salió mal. Por favor, recarga la página."],"Modify your meta description by editing it right here":["Modifica tu meta description editándola aquí mismo"],"Url preview":["Vista previa de la URL"],"Please provide a meta description by editing the snippet below. If you don’t, Google will try to find a relevant part of your post to show in the search results.":["Por favor, introduce una meta description editando el snippet de abajo. Si no lo haces Google intentará encontrar una parte relevante de tu contenido para mostrarla en los resultados de búsqueda."],"Insert snippet variable":["Insertar variable del snippet"],"Dismiss this notice":["Descartar este aviso"],"No results":["Sin resultados"],"%d result found, use up and down arrow keys to navigate":["%d resultado encontrado, usa las teclas arriba y abajo para navegar","%d resultados encontrados, usa las teclas arriba y abajo para navegar"],"Your site language is set to %s. If this is not correct, contact your site administrator.":["El idioma de tu sitio está configurado a %s. Si no es así contacta con el administrador de tu sitio."],"On":["Activo"],"Off":["Inactivo"],"Good results":["Buenos resultados"],"Remove highlight from the text":["Quitar el resaltado del texto"],"Your site language is set to %s. ":["El idioma de tu sitio está configurado a %s."],"Highlight this result in the text":["Resalta este resultado en el texto"],"Considerations":["Consideraciones"],"Errors":["Errores"],"Change language":["Cambiar idioma"],"(Opens in a new browser tab)":["(Se abre en una nueva pestaña del navegador)"],"Scroll to see the preview content.":["Navega para ver la vista previa del contenido."],"Step %1$d: %2$s":["Paso %1$d: %2$s"],"Close snippet editor":["Cerrar el editor de snippet"],"Slug":["Slug"],"Marks are disabled in current view":["Las marcas están desactivadas en la vista actual"],"Choose an image":["Elige una imagen"],"Remove the image":["Quita la imagen"],"MailChimp signup failed:":["El registro en Mailchimp ha fallado:"],"Sign Up!":["¡Regístrate!"],"Edit snippet":["Editar snippet"],"SEO title preview":["Vista previa del título SEO"],"Meta description preview":["Vista previa de la meta description"],"A problem occurred when saving the current step, {{link}}please file a bug report{{/link}} describing what step you are on and which changes you want to make (if any).":["Ocurrió un problema al guardar este paso, {{link}}por favor, envía un informe de fallos{{/link}} describiendo en qué paso estás y qué cambios querías hacer (si los hubiera)."],"Close the Wizard":["Cerrar el asistente"],"%s installation wizard":["Asistente de instalación de %s"],"SEO title":["Título SEO"],"Improvements":["A mejorar"],"Problems":["Problemas"],"Email":["Correo electrónico"],"Previous":["Anterior"],"Next":["Siguiente"],"Close":["Cerrar"],"Meta description":["Descripción meta"]}}}
1
+ {"domain":"yoast-components","locale_data":{"yoast-components":{"":{"domain":"yoast-components","plural-forms":"nplurals=2; plural=n != 1;","lang":"es"},"Preview as:":["Previsualizar como:"],"Mobile result":["Resultado móvil"],"Desktop result":["Resultado en escritorio"],"Dismiss this alert":["Descartar esta alerta"],"The following words and word combinations occur the most in the content. These give an indication of what your content focuses on. If the words differ a lot from your topic, you might want to rewrite your content accordingly. ":["Las siguientes palabras y combinaciones de palabras son las que más aparecen en el contenido. Estas dan una indicación de en qué se centra tu contenido. Si las palabras difieren mucho de tu temática, puede que quieras reescribir tu contenido debidamente."],"Once you add a bit more copy, we'll give you a list of words that occur the most in the content. These give an indication of what your content focuses on.":["Una vez que hayas escrito un poco más, te daremos la lista de las palabras que más aparecen en el contenido. Estas dan una indicación de en qué se centra tu contenido."],"%d occurrences":["%d apariciones"],"We could not find any relevant articles on your website that you could link to from your post.":["No hemos podido encontrar ningún artículo relevante en tu web al que puedas enlazar desde tu entrada."],"The image you selected is too small for Facebook":["La imagen que has elegido es demasiado pequeña para Facebook"],"The given image url cannot be loaded":["La URL de la imagen dada no se puede cargar"],"This is a list of related content to which you could link in your post. {{a}}Read our article about site structure{{/a}} to learn more about how internal linking can help improve your SEO.":["Esta es una lista de contenido relacionado al que podrías enlazar en tu entrada. {{a}}Lee nuestro artículo sobre la estructura del sitio{{/a}} para aprende rmás sobre como el enlazado interno puede ayudar a mejorar tu SEO."],"Are you trying to use multiple keyphrases? You should add them separately below.":["¿Estás tratando de utilizar varias frases clave? Debes añadirlas por separado a continuación."],"Mark as cornerstone content":["Marcar como contenido esencial"],"image preview":["vista previa de imagen"],"Copied!":["¡Copiado!"],"Not supported!":["¡No compatible!"],"Read {{a}}our article about site structure{{/a}} to learn more about how internal linking can help improve your SEO.":["Lee {{a}}nuestro artículo sobre de la estructura del sitio{{{/a}} para saber más sobre cómo los enlaces internos pueden ayudar a mejorar tu SEO."],"Once you add a bit more copy, we'll give you a list of related content here to which you could link in your post.":["Cuando añadas algo más de texto te mostraremos aquí una lista de contenidos relacionados a los que podrías enlazar en tu entrada."],"Consider linking to these {{a}}cornerstone articles:{{/a}}":["Plantéate enlazar a estos {{a}}artículos esenciales{{/a}}"],"Consider linking to these articles:":["Plantéate enlazar a estos artículos:"],"Copy link":["Copiar enlace"],"Copy link to suggested article: %s":["Copiar enlace al artículo sugerido: %s"],"Read our %1$sultimate guide to keyword research%2$s to learn more about keyword research and keyword strategy.":["Lee nuestra %1$sguía definitiva para la búsqueda de palabras clave%2$s para aprender más sobre la búsqueda y estrategia de palabras clave."],"Once you add a bit more copy, we'll give you a list of words and word combinations that occur the most in the content. These give an indication of what your content focuses on.":["Una vez que hayas escrito un poco más, te daremos la lista de las palabras y combinaciones de palabras que más aparecen en el contenido. Estas dan una indicación de en qué se centra tu contenido."],"The following words occur the most in the content. These give an indication of what your content focuses on. If the words differ a lot from your topic, you might want to rewrite your content accordingly. ":["Las siguientes palabras son las que más aparecen en el contenido. Estas dan una indicación de en qué se centra tu contenido. Si las palabras difieren mucho de tu temática, puede que quieras reescribir tu contenido debidamente."],"Prominent words":["Palabras importantes"],"Something went wrong. Please reload the page.":["Algo salió mal. Por favor, recarga la página."],"Modify your meta description by editing it right here":["Modifica tu meta description editándola aquí mismo"],"Url preview":["Vista previa de la URL"],"Please provide a meta description by editing the snippet below. If you don’t, Google will try to find a relevant part of your post to show in the search results.":["Por favor, introduce una meta description editando el snippet de abajo. Si no lo haces Google intentará encontrar una parte relevante de tu contenido para mostrarla en los resultados de búsqueda."],"Insert snippet variable":["Insertar variable del snippet"],"Dismiss this notice":["Descartar este aviso"],"No results":["Sin resultados"],"%d result found, use up and down arrow keys to navigate":["%d resultado encontrado, usa las teclas arriba y abajo para navegar","%d resultados encontrados, usa las teclas arriba y abajo para navegar"],"Your site language is set to %s. If this is not correct, contact your site administrator.":["El idioma de tu sitio está configurado a %s. Si no es así contacta con el administrador de tu sitio."],"On":["Activo"],"Off":["Inactivo"],"Good results":["Buenos resultados"],"Remove highlight from the text":["Quitar el resaltado del texto"],"Your site language is set to %s. ":["El idioma de tu sitio está configurado a %s."],"Highlight this result in the text":["Resalta este resultado en el texto"],"Considerations":["Consideraciones"],"Errors":["Errores"],"Change language":["Cambiar idioma"],"(Opens in a new browser tab)":["(Se abre en una nueva pestaña del navegador)"],"Scroll to see the preview content.":["Navega para ver la vista previa del contenido."],"Step %1$d: %2$s":["Paso %1$d: %2$s"],"Close snippet editor":["Cerrar el editor de snippet"],"Slug":["Slug"],"Marks are disabled in current view":["Las marcas están desactivadas en la vista actual"],"Choose an image":["Elige una imagen"],"Remove the image":["Quita la imagen"],"MailChimp signup failed:":["El registro en Mailchimp ha fallado:"],"Sign Up!":["¡Regístrate!"],"Edit snippet":["Editar snippet"],"SEO title preview":["Vista previa del título SEO"],"Meta description preview":["Vista previa de la meta description"],"A problem occurred when saving the current step, {{link}}please file a bug report{{/link}} describing what step you are on and which changes you want to make (if any).":["Ocurrió un problema al guardar este paso, {{link}}por favor, envía un informe de fallos{{/link}} describiendo en qué paso estás y qué cambios querías hacer (si los hubiera)."],"Close the Wizard":["Cerrar el asistente"],"%s installation wizard":["Asistente de instalación de %s"],"SEO title":["Título SEO"],"Improvements":["A mejorar"],"Problems":["Problemas"],"Email":["Correo electrónico"],"Previous":["Anterior"],"Next":["Siguiente"],"Close":["Cerrar"],"Meta description":["Descripción meta"]}}}
migrations/20200428123747_BreadcrumbTitleAndHierarchyReset.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Yoast SEO Plugin File.
4
+ *
5
+ * @package WPSEO\Migrations
6
+ */
7
+
8
+ use Yoast\WP\SEO\ORM\Yoast_Model;
9
+ use YoastSEO_Vendor\Ruckusing_Migration_Base;
10
+
11
+ /**
12
+ * BreadcrumbTitleAndHierarchyReset
13
+ */
14
+ class BreadcrumbTitleAndHierarchyReset extends Ruckusing_Migration_Base {
15
+ /**
16
+ * Migration up.
17
+ */
18
+ public function up() {
19
+ $this->change_column( $this->get_indexable_table_name(), 'breadcrumb_title', 'text', [ 'null' => true ] );
20
+ $this->query( 'DELETE FROM ' . $this->get_indexable_hierarchy_table_name() );
21
+ }
22
+
23
+ /**
24
+ * Migration down.
25
+ */
26
+ public function down() {
27
+ $this->change_column(
28
+ $this->get_indexable_table_name(),
29
+ 'breadcrumb_title',
30
+ 'string',
31
+ [ 'null' => true, 'limit' => 191 ]
32
+ );
33
+ }
34
+
35
+ /**
36
+ * Retrieves the table name to use for storing indexables.
37
+ *
38
+ * @return string The table name to use.
39
+ */
40
+ protected function get_indexable_table_name() {
41
+ return Yoast_Model::get_table_name( 'Indexable' );
42
+ }
43
+
44
+ /**
45
+ * Retrieves the table name to use.
46
+ *
47
+ * @return string The table name to use.
48
+ */
49
+ protected function get_indexable_hierarchy_table_name() {
50
+ return Yoast_Model::get_table_name( 'Indexable_Hierarchy' );
51
+ }
52
+ }
readme.txt CHANGED
@@ -6,7 +6,7 @@ License URI: http://www.gnu.org/licenses/gpl.html
6
  Tags: SEO, XML sitemap, Content analysis, Readability
7
  Requires at least: 5.3
8
  Tested up to: 5.4
9
- Stable tag: 14.0
10
  Requires PHP: 5.6.20
11
 
12
  Improve your WordPress SEO: Write better content and have a fully optimized WordPress site using the Yoast SEO plugin.
@@ -209,6 +209,25 @@ Your question has most likely been answered on our knowledge base: [kb.yoast.com
209
 
210
  == Changelog ==
211
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  = 14.0 =
213
  Release Date: April 28th, 2020
214
 
6
  Tags: SEO, XML sitemap, Content analysis, Readability
7
  Requires at least: 5.3
8
  Tested up to: 5.4
9
+ Stable tag: 14.0.1
10
  Requires PHP: 5.6.20
11
 
12
  Improve your WordPress SEO: Write better content and have a fully optimized WordPress site using the Yoast SEO plugin.
209
 
210
  == Changelog ==
211
 
212
+ = 14.0.1 =
213
+ Release Date: April 28th, 2020
214
+
215
+ Bugfixes:
216
+
217
+ * Fixes a bug where a fatal error would be thrown when a breadcrumb title was too long.
218
+ * Fixes a bug where a fatal error would be thrown when `DB_CHARSET` was not defined.
219
+ * Fixes a bug where a fatal error would be thrown when breadcrumbs were rendered in the admin.
220
+ * Fixes a bug where a fatal error would be thrown when the Yoast migrations table did not have a primary key.
221
+ * Fixes a bug where a fatal exception would be thrown when building an indexable failed.
222
+ * Fixes a bug where the order of the breadcrumbs was incorrect when more than 3 nested taxonomies were used.
223
+ * Fixes a bug where HTML tags would no longer be allowed in the breadcrumbs.
224
+ * Fixes a bug where no title would be shown in the Yoast indexation status modal.
225
+ * Fixes a bug where changes made through the `wpseo_robots` filter would not be shown in the googlebot and bingbot meta tag output.
226
+
227
+ Other:
228
+
229
+ * Yoast SEO needs to have the right to create a database index. If you have restricted the creation of database indexes on your setup, please make sure to temporarily allow Yoast SEO to create indexes before updating.
230
+
231
  = 14.0 =
232
  Release Date: April 28th, 2020
233
 
src/actions/indexation/indexable-post-indexation-action.php CHANGED
@@ -79,7 +79,10 @@ class Indexable_Post_Indexation_Action implements Indexation_Action_Interface {
79
 
80
  $indexables = [];
81
  foreach ( $post_ids as $post_id ) {
82
- $indexables[] = $this->builder->build_for_id_and_type( (int) $post_id, 'post' );
 
 
 
83
  }
84
 
85
  return $indexables;
79
 
80
  $indexables = [];
81
  foreach ( $post_ids as $post_id ) {
82
+ $indexable = $this->builder->build_for_id_and_type( (int) $post_id, 'post' );
83
+ if ( $indexable !== false ) {
84
+ $indexables[] = $indexable;
85
+ }
86
  }
87
 
88
  return $indexables;
src/actions/indexation/indexable-term-indexation-action.php CHANGED
@@ -80,7 +80,10 @@ class Indexable_Term_Indexation_Action implements Indexation_Action_Interface {
80
 
81
  $indexables = [];
82
  foreach ( $term_ids as $term_id ) {
83
- $indexables[] = $this->builder->build_for_id_and_type( (int) $term_id, 'term' );
 
 
 
84
  }
85
 
86
  return $indexables;
80
 
81
  $indexables = [];
82
  foreach ( $term_ids as $term_id ) {
83
+ $indexable = $this->builder->build_for_id_and_type( (int) $term_id, 'term' );
84
+ if ( $indexable !== false ) {
85
+ $indexables[] = $indexable;
86
+ }
87
  }
88
 
89
  return $indexables;
src/backwards-compatibility/breadcrumbs.php CHANGED
@@ -5,7 +5,7 @@
5
  * @package Yoast\YoastSEO\Backwards_Compatibility
6
  */
7
 
8
- use Yoast\WP\SEO\Conditionals\Front_End_Conditional;
9
  use Yoast\WP\SEO\Initializers\Initializer_Interface;
10
  use Yoast\WP\SEO\Memoizers\Meta_Tags_Context_Memoizer;
11
  use Yoast\WP\SEO\Presenters\Breadcrumbs_Presenter;
@@ -18,6 +18,8 @@ use Yoast\WP\SEO\Surfaces\Helpers_Surface;
18
  */
19
  class WPSEO_Breadcrumbs implements Initializer_Interface {
20
 
 
 
21
  /**
22
  * Instance of this class.
23
  *
@@ -60,13 +62,6 @@ class WPSEO_Breadcrumbs implements Initializer_Interface {
60
  */
61
  protected $replace_vars;
62
 
63
- /**
64
- * @inheritDoc
65
- */
66
- public static function get_conditionals() {
67
- return [ Front_End_Conditional::class ];
68
- }
69
-
70
  /**
71
  * WPSEO_Breadcrumbs constructor.
72
  *
5
  * @package Yoast\YoastSEO\Backwards_Compatibility
6
  */
7
 
8
+ use Yoast\WP\SEO\Conditionals\No_Conditionals;
9
  use Yoast\WP\SEO\Initializers\Initializer_Interface;
10
  use Yoast\WP\SEO\Memoizers\Meta_Tags_Context_Memoizer;
11
  use Yoast\WP\SEO\Presenters\Breadcrumbs_Presenter;
18
  */
19
  class WPSEO_Breadcrumbs implements Initializer_Interface {
20
 
21
+ use No_Conditionals;
22
+
23
  /**
24
  * Instance of this class.
25
  *
62
  */
63
  protected $replace_vars;
64
 
 
 
 
 
 
 
 
65
  /**
66
  * WPSEO_Breadcrumbs constructor.
67
  *
src/builders/indexable-builder.php CHANGED
@@ -138,9 +138,7 @@ class Indexable_Builder {
138
  * @param string $object_type The indexable object type.
139
  * @param Indexable|bool $indexable Optional. An existing indexable to overwrite.
140
  *
141
- * @return bool|Indexable Instance of indexable.
142
- *
143
- * @throws \Exception If the object_id could not be found.
144
  */
145
  public function build_for_id_and_type( $object_id, $object_type, $indexable = false ) {
146
  $indexable = $this->ensure_indexable( $indexable );
@@ -167,6 +165,11 @@ class Indexable_Builder {
167
  return $indexable;
168
  }
169
 
 
 
 
 
 
170
  $this->save_indexable( $indexable, $indexable_before );
171
 
172
  if ( in_array( $object_type, [ 'post', 'term' ], true ) ) {
138
  * @param string $object_type The indexable object type.
139
  * @param Indexable|bool $indexable Optional. An existing indexable to overwrite.
140
  *
141
+ * @return bool|Indexable Instance of indexable. False when unable to build.
 
 
142
  */
143
  public function build_for_id_and_type( $object_id, $object_type, $indexable = false ) {
144
  $indexable = $this->ensure_indexable( $indexable );
165
  return $indexable;
166
  }
167
 
168
+ // Something went wrong building, nothing to do.
169
+ if ( $indexable === false ) {
170
+ return false;
171
+ }
172
+
173
  $this->save_indexable( $indexable, $indexable_before );
174
 
175
  if ( in_array( $object_type, [ 'post', 'term' ], true ) ) {
src/builders/indexable-hierarchy-builder.php CHANGED
@@ -127,6 +127,10 @@ class Indexable_Hierarchy_Builder {
127
 
128
  if ( $post->post_parent !== 0 && $this->post->get_post( $post->post_parent ) !== null ) {
129
  $ancestor = $this->indexable_repository->find_by_id_and_type( $post->post_parent, 'post' );
 
 
 
 
130
  $this->indexable_hierarchy_repository->add_ancestor( $indexable_id, $ancestor->id, $depth );
131
  $this->add_ancestors_for_post( $indexable_id, $ancestor->object_id, ( $depth + 1 ) );
132
  return;
@@ -139,6 +143,10 @@ class Indexable_Hierarchy_Builder {
139
  }
140
 
141
  $ancestor = $this->indexable_repository->find_by_id_and_type( $primary_term_id, 'term' );
 
 
 
 
142
  $this->indexable_hierarchy_repository->add_ancestor( $indexable_id, $ancestor->id, $depth );
143
  $this->add_ancestors_for_term( $indexable_id, $ancestor->object_id, ( $depth + 1 ) );
144
  }
@@ -158,6 +166,9 @@ class Indexable_Hierarchy_Builder {
158
 
159
  foreach ( $parents as $parent ) {
160
  $ancestor = $this->indexable_repository->find_by_id_and_type( $parent->term_id, 'term' );
 
 
 
161
  $this->indexable_hierarchy_repository->add_ancestor( $indexable_id, $ancestor->id, $depth );
162
  $depth = ( $depth + 1 );
163
  }
@@ -265,6 +276,6 @@ class Indexable_Hierarchy_Builder {
265
  $parents[] = $term;
266
  }
267
 
268
- return array_reverse( $parents );
269
  }
270
  }
127
 
128
  if ( $post->post_parent !== 0 && $this->post->get_post( $post->post_parent ) !== null ) {
129
  $ancestor = $this->indexable_repository->find_by_id_and_type( $post->post_parent, 'post' );
130
+ if ( $ancestor === false ) {
131
+ return;
132
+ }
133
+
134
  $this->indexable_hierarchy_repository->add_ancestor( $indexable_id, $ancestor->id, $depth );
135
  $this->add_ancestors_for_post( $indexable_id, $ancestor->object_id, ( $depth + 1 ) );
136
  return;
143
  }
144
 
145
  $ancestor = $this->indexable_repository->find_by_id_and_type( $primary_term_id, 'term' );
146
+ if ( $ancestor === false ) {
147
+ return;
148
+ }
149
+
150
  $this->indexable_hierarchy_repository->add_ancestor( $indexable_id, $ancestor->id, $depth );
151
  $this->add_ancestors_for_term( $indexable_id, $ancestor->object_id, ( $depth + 1 ) );
152
  }
166
 
167
  foreach ( $parents as $parent ) {
168
  $ancestor = $this->indexable_repository->find_by_id_and_type( $parent->term_id, 'term' );
169
+ if ( $ancestor === false ) {
170
+ continue;
171
+ }
172
  $this->indexable_hierarchy_repository->add_ancestor( $indexable_id, $ancestor->id, $depth );
173
  $depth = ( $depth + 1 );
174
  }
276
  $parents[] = $term;
277
  }
278
 
279
+ return $parents;
280
  }
281
  }
src/builders/indexable-post-builder.php CHANGED
@@ -70,15 +70,13 @@ class Indexable_Post_Builder {
70
  * @param int $post_id The post ID to use.
71
  * @param Indexable $indexable The indexable to format.
72
  *
73
- * @throws Exception If the post could not be found.
74
- *
75
- * @return Indexable The extended indexable.
76
  */
77
  public function build( $post_id, $indexable ) {
78
  $post = $this->post->get_post( $post_id );
79
 
80
  if ( $post === null ) {
81
- throw new Exception( 'Post could not be found.' );
82
  }
83
 
84
  $indexable->object_id = $post_id;
@@ -206,13 +204,12 @@ class Indexable_Post_Builder {
206
  }
207
 
208
  // The post parent should be public.
209
- try {
210
- $post_parent_indexable = $this->indexable_repository->find_by_id_and_type( $indexable->post_parent, 'post' );
211
- } catch ( Exception $exception ) {
212
- return false;
213
  }
214
 
215
- return $post_parent_indexable->is_public;
216
  }
217
 
218
  /**
70
  * @param int $post_id The post ID to use.
71
  * @param Indexable $indexable The indexable to format.
72
  *
73
+ * @return bool|Indexable The extended indexable. False when unable to build.
 
 
74
  */
75
  public function build( $post_id, $indexable ) {
76
  $post = $this->post->get_post( $post_id );
77
 
78
  if ( $post === null ) {
79
+ return false;
80
  }
81
 
82
  $indexable->object_id = $post_id;
204
  }
205
 
206
  // The post parent should be public.
207
+ $post_parent_indexable = $this->indexable_repository->find_by_id_and_type( $indexable->post_parent, 'post' );
208
+ if ( $post_parent_indexable !== false ) {
209
+ return $post_parent_indexable->is_public;
 
210
  }
211
 
212
+ return false;
213
  }
214
 
215
  /**
src/builders/indexable-term-builder.php CHANGED
@@ -38,25 +38,23 @@ class Indexable_Term_Builder {
38
  * @param int $term_id ID of the term to save data for.
39
  * @param \Yoast\WP\SEO\Models\Indexable $indexable The indexable to format.
40
  *
41
- * @throws \Exception If the term could not be found.
42
- *
43
- * @return Indexable The extended indexable.
44
  */
45
  public function build( $term_id, $indexable ) {
46
  $term = \get_term( $term_id );
47
 
48
  if ( $term === null ) {
49
- throw new \Exception( 'Term could not be found.' );
50
  }
51
 
52
  if ( is_wp_error( $term ) ) {
53
- throw new \Exception( \current( \array_keys( $term->errors ) ) );
54
  }
55
 
56
  $term_link = \get_term_link( $term, $term->taxonomy );
57
 
58
  if ( is_wp_error( $term_link ) ) {
59
- throw new \Exception( \current( \array_keys( $term_link->errors ) ) );
60
  }
61
 
62
  $term_meta = $this->taxonomy->get_term_meta( $term );
38
  * @param int $term_id ID of the term to save data for.
39
  * @param \Yoast\WP\SEO\Models\Indexable $indexable The indexable to format.
40
  *
41
+ * @return bool|Indexable The extended indexable. False when unable to build.
 
 
42
  */
43
  public function build( $term_id, $indexable ) {
44
  $term = \get_term( $term_id );
45
 
46
  if ( $term === null ) {
47
+ return false;
48
  }
49
 
50
  if ( is_wp_error( $term ) ) {
51
+ return false;
52
  }
53
 
54
  $term_link = \get_term_link( $term, $term->taxonomy );
55
 
56
  if ( is_wp_error( $term_link ) ) {
57
+ return false;
58
  }
59
 
60
  $term_meta = $this->taxonomy->get_term_meta( $term );
src/config/ruckusing-framework.php CHANGED
@@ -121,7 +121,7 @@ class Ruckusing_Framework {
121
  public function get_configuration( $migrations_table_name, $migrations_directory ) {
122
  $config = $this->database_setup->get_database_config();
123
 
124
- return [
125
  'db' => [
126
  'production' => [
127
  'type' => 'mysql',
@@ -130,7 +130,6 @@ class Ruckusing_Framework {
130
  'database' => \DB_NAME,
131
  'user' => \DB_USER,
132
  'password' => \DB_PASSWORD,
133
- 'charset' => $this->wpdb->charset,
134
  'directory' => '', // This needs to be set, to use the migrations folder as base folder.
135
  'schema_version_table_name' => $migrations_table_name,
136
  ],
@@ -142,6 +141,12 @@ class Ruckusing_Framework {
142
  'log_dir' => true,
143
  // This needs to be set but is not used.
144
  ];
 
 
 
 
 
 
145
  }
146
 
147
  /**
121
  public function get_configuration( $migrations_table_name, $migrations_directory ) {
122
  $config = $this->database_setup->get_database_config();
123
 
124
+ $ruckusing_config = [
125
  'db' => [
126
  'production' => [
127
  'type' => 'mysql',
130
  'database' => \DB_NAME,
131
  'user' => \DB_USER,
132
  'password' => \DB_PASSWORD,
 
133
  'directory' => '', // This needs to be set, to use the migrations folder as base folder.
134
  'schema_version_table_name' => $migrations_table_name,
135
  ],
141
  'log_dir' => true,
142
  // This needs to be set but is not used.
143
  ];
144
+
145
+ if ( ! empty( $this->wpdb->charset ) ) {
146
+ $ruckusing_config['db']['production']['charset'] = $this->wpdb->charset;
147
+ }
148
+
149
+ return $ruckusing_config;
150
  }
151
 
152
  /**
src/generators/breadcrumbs-generator.php CHANGED
@@ -73,12 +73,18 @@ class Breadcrumbs_Generator implements Generator_Interface {
73
  $static_ancestors[] = $this->repository->find_for_home_page();
74
  }
75
  else {
76
- $static_ancestors[] = $this->repository->find_by_id_and_type( $front_page_id, 'post' );
 
 
 
77
  }
78
  }
79
  $page_for_posts = \get_option( 'page_for_posts' );
80
  if ( $this->should_have_blog_crumb( $page_for_posts ) ) {
81
- $static_ancestors[] = $this->repository->find_by_id_and_type( $page_for_posts, 'post' );
 
 
 
82
  }
83
  if (
84
  $context->indexable->object_type === 'post' &&
73
  $static_ancestors[] = $this->repository->find_for_home_page();
74
  }
75
  else {
76
+ $static_ancestor = $this->repository->find_by_id_and_type( $front_page_id, 'post' );
77
+ if ( $static_ancestor !== false ) {
78
+ $static_ancestors[] = $static_ancestor;
79
+ }
80
  }
81
  }
82
  $page_for_posts = \get_option( 'page_for_posts' );
83
  if ( $this->should_have_blog_crumb( $page_for_posts ) ) {
84
+ $static_ancestor = $this->repository->find_by_id_and_type( $page_for_posts, 'post' );
85
+ if ( $static_ancestor !== false ) {
86
+ $static_ancestors[] = $static_ancestor;
87
+ }
88
  }
89
  if (
90
  $context->indexable->object_type === 'post' &&
src/generators/schema/breadcrumb.php CHANGED
@@ -102,7 +102,7 @@ class Breadcrumb extends Abstract_Schema_Piece {
102
  '@type' => 'WebPage',
103
  '@id' => $breadcrumb['url'],
104
  'url' => $breadcrumb['url'], // For future proofing, we're trying to change the standard for this.
105
- 'name' => $breadcrumb['text'],
106
  ],
107
  ];
108
  }
102
  '@type' => 'WebPage',
103
  '@id' => $breadcrumb['url'],
104
  'url' => $breadcrumb['url'], // For future proofing, we're trying to change the standard for this.
105
+ 'name' => $this->helpers->schema->html->smart_strip_tags( $breadcrumb['text'] ),
106
  ],
107
  ];
108
  }
src/initializers/database-setup.php CHANGED
@@ -107,7 +107,7 @@ class Database_Setup implements Initializer_Interface {
107
  if ( ! empty( $config['socket'] ) ) {
108
  $connection_string .= 'unix_socket=' . $config['socket'] . ';';
109
  }
110
- if ( ! empty( \DB_CHARSET ) ) {
111
  $connection_string .= 'charset=' . \DB_CHARSET . ';';
112
  }
113
  return $connection_string;
107
  if ( ! empty( $config['socket'] ) ) {
108
  $connection_string .= 'unix_socket=' . $config['socket'] . ';';
109
  }
110
+ if ( \defined( 'DB_CHARSET' ) && ! empty( \DB_CHARSET ) ) {
111
  $connection_string .= 'charset=' . \DB_CHARSET . ';';
112
  }
113
  return $connection_string;
src/initializers/migration-runner.php CHANGED
@@ -137,7 +137,7 @@ class Migration_Runner implements Initializer_Interface {
137
  // Create our own migrations table with a 191 string limit to support older versions of MySQL.
138
  // Run this before calling the framework runner so it doesn't create it's own.
139
  if ( ! $adapter->has_table( $migrations_table_name ) ) {
140
- $table = $adapter->create_table( $migrations_table_name, [ 'id' => false ] );
141
  $table->column( 'version', 'string', [ 'limit' => 191 ] );
142
  $table->finish();
143
  $adapter->add_index( $migrations_table_name, 'version', [ 'unique' => true ] );
137
  // Create our own migrations table with a 191 string limit to support older versions of MySQL.
138
  // Run this before calling the framework runner so it doesn't create it's own.
139
  if ( ! $adapter->has_table( $migrations_table_name ) ) {
140
+ $table = $adapter->create_table( $migrations_table_name );
141
  $table->column( 'version', 'string', [ 'limit' => 191 ] );
142
  $table->finish();
143
  $adapter->add_index( $migrations_table_name, 'version', [ 'unique' => true ] );
src/integrations/watchers/indexable-post-watcher.php CHANGED
@@ -183,8 +183,7 @@ class Indexable_Post_Watcher implements Integration_Interface {
183
 
184
  try {
185
  $indexable = $this->repository->find_by_id_and_type( $post_id, 'post', false );
186
- $indexable = $this->builder->build_for_id_and_type( $post_id, 'post', $indexable );
187
- $indexable->save();
188
  } catch ( Exception $exception ) { // @codingStandardsIgnoreLine Generic.CodeAnalysis.EmptyStatement.DetectedCATCH -- There is nothing to do.
189
  // Do nothing.
190
  }
@@ -198,7 +197,11 @@ class Indexable_Post_Watcher implements Integration_Interface {
198
  protected function update_has_public_posts( $indexable ) {
199
  // Update the author indexable's has public posts value.
200
  try {
201
- $author_indexable = $this->repository->find_by_id_and_type( $indexable->author_id, 'user' );
 
 
 
 
202
  $author_indexable->has_public_posts = $this->author_archive->author_has_public_posts( $author_indexable->object_id );
203
  $author_indexable->save();
204
  } catch ( Exception $exception ) { // @codingStandardsIgnoreLine Generic.CodeAnalysis.EmptyStatement.DetectedCATCH -- There is nothing to do.
183
 
184
  try {
185
  $indexable = $this->repository->find_by_id_and_type( $post_id, 'post', false );
186
+ $this->builder->build_for_id_and_type( $post_id, 'post', $indexable );
 
187
  } catch ( Exception $exception ) { // @codingStandardsIgnoreLine Generic.CodeAnalysis.EmptyStatement.DetectedCATCH -- There is nothing to do.
188
  // Do nothing.
189
  }
197
  protected function update_has_public_posts( $indexable ) {
198
  // Update the author indexable's has public posts value.
199
  try {
200
+ $author_indexable = $this->repository->find_by_id_and_type( $indexable->author_id, 'user' );
201
+ if ( $author_indexable === false ) {
202
+ return;
203
+ }
204
+
205
  $author_indexable->has_public_posts = $this->author_archive->author_has_public_posts( $author_indexable->object_id );
206
  $author_indexable->save();
207
  } catch ( Exception $exception ) { // @codingStandardsIgnoreLine Generic.CodeAnalysis.EmptyStatement.DetectedCATCH -- There is nothing to do.
src/integrations/watchers/indexable-term-watcher.php CHANGED
@@ -105,12 +105,6 @@ class Indexable_Term_Watcher implements Integration_Interface {
105
  $indexable = $this->repository->find_by_id_and_type( $term_id, 'term', false );
106
 
107
  // If we haven't found an existing indexable, create it. Otherwise update it.
108
- try {
109
- $indexable = $this->builder->build_for_id_and_type( $term_id, 'term', $indexable );
110
- } catch ( \Exception $exception ) {
111
- return;
112
- }
113
-
114
- $indexable->save();
115
  }
116
  }
105
  $indexable = $this->repository->find_by_id_and_type( $term_id, 'term', false );
106
 
107
  // If we haven't found an existing indexable, create it. Otherwise update it.
108
+ $this->builder->build_for_id_and_type( $term_id, 'term', $indexable );
 
 
 
 
 
 
109
  }
110
  }
src/presentations/indexable-author-archive-presentation.php CHANGED
@@ -91,13 +91,12 @@ class Indexable_Author_Archive_Presentation extends Indexable_Presentation {
91
  * @inheritDoc
92
  */
93
  public function generate_robots() {
94
- $robots = parent::generate_robots();
95
 
96
  // Global option: "Show author archives in search results".
97
  if ( $this->options->get( 'noindex-author-wpseo', false ) ) {
98
  $robots['index'] = 'noindex';
99
-
100
- return $robots;
101
  }
102
 
103
  $current_author = \get_userdata( $this->model->object_id );
@@ -105,8 +104,7 @@ class Indexable_Author_Archive_Presentation extends Indexable_Presentation {
105
  // Safety check. The call to `get_user_data` could return false (called in `get_queried_object`).
106
  if ( $current_author === false ) {
107
  $robots['index'] = 'noindex';
108
-
109
- return $robots;
110
  }
111
 
112
  $public_post_types = $this->post_type->get_public_post_types();
@@ -114,18 +112,16 @@ class Indexable_Author_Archive_Presentation extends Indexable_Presentation {
114
  // Global option: "Show archives for authors without posts in search results".
115
  if ( $this->options->get( 'noindex-author-noposts-wpseo', false ) && $this->user->count_posts( $current_author->ID, $public_post_types ) === 0 ) {
116
  $robots['index'] = 'noindex';
117
-
118
- return $robots;
119
  }
120
 
121
  // User option: "Do not allow search engines to show this author's archives in search results".
122
  if ( $this->user->get_meta( $current_author->ID, 'wpseo_noindex_author', true ) === 'on' ) {
123
  $robots['index'] = 'noindex';
124
-
125
- return $robots;
126
  }
127
 
128
- return \array_filter( $robots );
129
  }
130
 
131
  /**
91
  * @inheritDoc
92
  */
93
  public function generate_robots() {
94
+ $robots = $this->get_base_robots();
95
 
96
  // Global option: "Show author archives in search results".
97
  if ( $this->options->get( 'noindex-author-wpseo', false ) ) {
98
  $robots['index'] = 'noindex';
99
+ return $this->filter_robots( $robots );
 
100
  }
101
 
102
  $current_author = \get_userdata( $this->model->object_id );
104
  // Safety check. The call to `get_user_data` could return false (called in `get_queried_object`).
105
  if ( $current_author === false ) {
106
  $robots['index'] = 'noindex';
107
+ return $this->filter_robots( $robots );
 
108
  }
109
 
110
  $public_post_types = $this->post_type->get_public_post_types();
112
  // Global option: "Show archives for authors without posts in search results".
113
  if ( $this->options->get( 'noindex-author-noposts-wpseo', false ) && $this->user->count_posts( $current_author->ID, $public_post_types ) === 0 ) {
114
  $robots['index'] = 'noindex';
115
+ return $this->filter_robots( $robots );
 
116
  }
117
 
118
  // User option: "Do not allow search engines to show this author's archives in search results".
119
  if ( $this->user->get_meta( $current_author->ID, 'wpseo_noindex_author', true ) === 'on' ) {
120
  $robots['index'] = 'noindex';
121
+ return $this->filter_robots( $robots );
 
122
  }
123
 
124
+ return $this->filter_robots( $robots );
125
  }
126
 
127
  /**
src/presentations/indexable-date-archive-presentation.php CHANGED
@@ -48,13 +48,13 @@ class Indexable_Date_Archive_Presentation extends Indexable_Presentation {
48
  * @inheritDoc
49
  */
50
  public function generate_robots() {
51
- $robots = parent::generate_robots();
52
 
53
  if ( $this->options->get( 'noindex-archive-wpseo', false ) ) {
54
  $robots['index'] = 'noindex';
55
  }
56
 
57
- return $robots;
58
  }
59
 
60
  /**
48
  * @inheritDoc
49
  */
50
  public function generate_robots() {
51
+ $robots = $this->get_base_robots();
52
 
53
  if ( $this->options->get( 'noindex-archive-wpseo', false ) ) {
54
  $robots['index'] = 'noindex';
55
  }
56
 
57
+ return $this->filter_robots( $robots );
58
  }
59
 
60
  /**
src/presentations/indexable-error-page-presentation.php CHANGED
@@ -16,11 +16,11 @@ class Indexable_Error_Page_Presentation extends Indexable_Presentation {
16
  * @inheritDoc
17
  */
18
  public function generate_robots() {
19
- $robots = parent::generate_robots();
20
 
21
  $robots['index'] = 'noindex';
22
 
23
- return $robots;
24
  }
25
 
26
  /**
16
  * @inheritDoc
17
  */
18
  public function generate_robots() {
19
+ $robots = $this->get_base_robots();
20
 
21
  $robots['index'] = 'noindex';
22
 
23
+ return $this->filter_robots( $robots );
24
  }
25
 
26
  /**
src/presentations/indexable-post-type-archive-presentation.php CHANGED
@@ -33,13 +33,13 @@ class Indexable_Post_Type_Archive_Presentation extends Indexable_Presentation {
33
  * @inheritDoc
34
  */
35
  public function generate_robots() {
36
- $robots = parent::generate_robots();
37
 
38
  if ( $this->options->get( 'noindex-ptarchive-' . $this->model->object_sub_type, false ) ) {
39
  $robots['index'] = 'noindex';
40
  }
41
 
42
- return $robots;
43
  }
44
 
45
  /**
33
  * @inheritDoc
34
  */
35
  public function generate_robots() {
36
+ $robots = $this->get_base_robots();
37
 
38
  if ( $this->options->get( 'noindex-ptarchive-' . $this->model->object_sub_type, false ) ) {
39
  $robots['index'] = 'noindex';
40
  }
41
 
42
+ return $this->filter_robots( $robots );
43
  }
44
 
45
  /**
src/presentations/indexable-post-type-presentation.php CHANGED
@@ -283,7 +283,7 @@ class Indexable_Post_Type_Presentation extends Indexable_Presentation {
283
  * @inheritDoc
284
  */
285
  public function generate_robots() {
286
- $robots = parent::generate_robots();
287
  $robots = array_merge(
288
  $robots,
289
  [
@@ -303,7 +303,7 @@ class Indexable_Post_Type_Presentation extends Indexable_Presentation {
303
  }
304
  }
305
 
306
- return \array_filter( $robots );
307
  }
308
 
309
  /**
283
  * @inheritDoc
284
  */
285
  public function generate_robots() {
286
+ $robots = $this->get_base_robots();
287
  $robots = array_merge(
288
  $robots,
289
  [
303
  }
304
  }
305
 
306
+ return $this->filter_robots( $robots );
307
  }
308
 
309
  /**
src/presentations/indexable-presentation.php CHANGED
@@ -221,12 +221,64 @@ class Indexable_Presentation extends Abstract_Presentation {
221
  * @return array The robots value.
222
  */
223
  public function generate_robots() {
 
 
 
 
 
 
 
 
 
 
 
224
  return [
225
  'index' => ( $this->model->is_robots_noindex === true ) ? 'noindex' : 'index',
226
  'follow' => ( $this->model->is_robots_nofollow === true ) ? 'nofollow' : 'follow',
227
  ];
228
  }
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  /**
231
  * Generates the robots value for the googlebot tag.
232
  *
@@ -251,7 +303,7 @@ class Indexable_Presentation extends Abstract_Presentation {
251
  * @return array The googlebot value.
252
  */
253
  private function generate_snippet_opt_in() {
254
- if ( isset( $this->robots['index'] ) && $this->robots['index'] === 'noindex' ) {
255
  return [];
256
  }
257
 
221
  * @return array The robots value.
222
  */
223
  public function generate_robots() {
224
+ $robots = $this->get_base_robots();
225
+
226
+ return $this->filter_robots( $robots );
227
+ }
228
+
229
+ /**
230
+ * Gets the base robots value.
231
+ *
232
+ * @return array The base robots value.
233
+ */
234
+ protected function get_base_robots() {
235
  return [
236
  'index' => ( $this->model->is_robots_noindex === true ) ? 'noindex' : 'index',
237
  'follow' => ( $this->model->is_robots_nofollow === true ) ? 'nofollow' : 'follow',
238
  ];
239
  }
240
 
241
+ /**
242
+ * Run the robots output content through the `wpseo_robots` filter.
243
+ *
244
+ * @param array $robots The meta robots values to filter.
245
+ *
246
+ * @return array The filtered meta robots values.
247
+ */
248
+ protected function filter_robots( $robots ) {
249
+ $robots_string = \implode( ', ', $robots );
250
+
251
+ /**
252
+ * Filter: 'wpseo_robots' - Allows filtering of the meta robots output of Yoast SEO.
253
+ *
254
+ * @api string $robots The meta robots directives to be echoed.
255
+ *
256
+ * @param Indexable_Presentation $presentation The presentation of an indexable.
257
+ */
258
+ $robots_filtered = \apply_filters( 'wpseo_robots', $robots_string, $this );
259
+
260
+ if ( is_string( $robots_filtered ) ) {
261
+ $robots_values = \explode( ', ', $robots_filtered );
262
+ $robots_new = [];
263
+
264
+ foreach ( $robots_values as $value ) {
265
+ $key = $value;
266
+ if ( \strpos( $key, 'no' ) === 0 ) {
267
+ $key = \substr( $value, 2 );
268
+ }
269
+ $robots_new[ $key ] = $value;
270
+ }
271
+
272
+ return \array_filter( $robots_new );
273
+ }
274
+
275
+ if ( ! $robots_filtered ) {
276
+ return [];
277
+ }
278
+
279
+ return \array_filter( $robots );
280
+ }
281
+
282
  /**
283
  * Generates the robots value for the googlebot tag.
284
  *
303
  * @return array The googlebot value.
304
  */
305
  private function generate_snippet_opt_in() {
306
+ if ( in_array( 'noindex', $this->robots, true ) ) {
307
  return [];
308
  }
309
 
src/presentations/indexable-search-result-page-presentation.php CHANGED
@@ -16,11 +16,11 @@ class Indexable_Search_Result_Page_Presentation extends Indexable_Presentation {
16
  * @inheritDoc
17
  */
18
  public function generate_robots() {
19
- $robots = parent::generate_robots();
20
 
21
  $robots['index'] = 'noindex';
22
 
23
- return $robots;
24
  }
25
 
26
  /**
16
  * @inheritDoc
17
  */
18
  public function generate_robots() {
19
+ $robots = $this->get_base_robots();
20
 
21
  $robots['index'] = 'noindex';
22
 
23
+ return $this->filter_robots( $robots );
24
  }
25
 
26
  /**
src/presentations/indexable-term-archive-presentation.php CHANGED
@@ -122,7 +122,7 @@ class Indexable_Term_Archive_Presentation extends Indexable_Presentation {
122
  * @inheritDoc
123
  */
124
  public function generate_robots() {
125
- $robots = parent::generate_robots();
126
 
127
  /**
128
  * If its a multiple terms archive page return a noindex.
@@ -130,7 +130,7 @@ class Indexable_Term_Archive_Presentation extends Indexable_Presentation {
130
  if ( $this->current_page->is_multiple_terms_page() ) {
131
  $robots['index'] = 'noindex';
132
 
133
- return $robots;
134
  }
135
 
136
  /**
@@ -148,7 +148,7 @@ class Indexable_Term_Archive_Presentation extends Indexable_Presentation {
148
  $robots['index'] = ( $this->model->is_robots_noindex ) ? 'noindex' : 'index';
149
  }
150
 
151
- return $robots;
152
  }
153
 
154
  /**
122
  * @inheritDoc
123
  */
124
  public function generate_robots() {
125
+ $robots = $this->get_base_robots();
126
 
127
  /**
128
  * If its a multiple terms archive page return a noindex.
130
  if ( $this->current_page->is_multiple_terms_page() ) {
131
  $robots['index'] = 'noindex';
132
 
133
+ return $this->filter_robots( $robots );
134
  }
135
 
136
  /**
148
  $robots['index'] = ( $this->model->is_robots_noindex ) ? 'noindex' : 'index';
149
  }
150
 
151
+ return $this->filter_robots( $robots );
152
  }
153
 
154
  /**
src/presenters/admin/indexation-modal-presenter.php CHANGED
@@ -41,7 +41,7 @@ class Indexation_Modal_Presenter extends Abstract_Presenter {
41
  if ( $this->total_unindexed === 0 ) {
42
  $inner_text = \sprintf(
43
  '<p>%s</p>',
44
- \esc_html__( 'All your content is already indexed, there is no need to index them again.', 'wordpress-seo' )
45
  );
46
  }
47
  else {
41
  if ( $this->total_unindexed === 0 ) {
42
  $inner_text = \sprintf(
43
  '<p>%s</p>',
44
+ \esc_html__( 'All your content is already indexed, there is no need to index it again.', 'wordpress-seo' )
45
  );
46
  }
47
  else {
src/presenters/admin/indexation-warning-presenter.php CHANGED
@@ -32,7 +32,8 @@ class Indexation_Warning_Presenter extends Abstract_Presenter {
32
  \sprintf(
33
  /* translators: 1: Button start tag to open the indexation modal, 2: Button closing tag. */
34
  \esc_html__( '%1$sClick here to speed up your site now%2$s', 'wordpress-seo' ),
35
- '<button type="button" class="button yoast-open-indexation">',
 
36
  '</button>'
37
  ),
38
  \sprintf(
32
  \sprintf(
33
  /* translators: 1: Button start tag to open the indexation modal, 2: Button closing tag. */
34
  \esc_html__( '%1$sClick here to speed up your site now%2$s', 'wordpress-seo' ),
35
+ \sprintf( '<button type="button" class="button yoast-open-indexation" data-title="%s">',
36
+ \esc_html__( 'Yoast indexation status', 'wordpress-seo' ) ),
37
  '</button>'
38
  ),
39
  \sprintf(
src/presenters/breadcrumbs-presenter.php CHANGED
@@ -76,7 +76,7 @@ class Breadcrumbs_Presenter extends Abstract_Indexable_Presenter {
76
  }
77
 
78
  $output = '<' . $this->get_wrapper() . $this->get_id() . $this->get_class() . '>' . $output . '</' . $this->get_wrapper() . '>';
79
- $output = $this->filter( $output, $this->presentation );
80
 
81
  $prefix = $this->helpers->options->get( 'breadcrumbs-prefix' );
82
  if ( $prefix !== '' ) {
@@ -120,10 +120,6 @@ class Breadcrumbs_Presenter extends Abstract_Indexable_Presenter {
120
  * 'text' => (string) link text.
121
  * 'url' => (string) link url.
122
  * (optional) 'title' => (string) link title attribute text.
123
- * (optional) 'allow_html' => (bool) whether to (not) escape html in the link text.
124
- * 'allow_html' prevents html stripping from the text strings set in the WPSEO -> Internal Links options page.
125
- * We never set 'title' or 'allow_html' ourselves but they could be set by users through the
126
- * 'wpseo_breadcrumb_links' and 'wpseo_breadcrumb_single_link_info' filters.
127
  * @param int $index Index for the current breadcrumb.
128
  * @param int $total The total number of breadcrumbs.
129
  *
@@ -137,9 +133,6 @@ class Breadcrumbs_Presenter extends Abstract_Indexable_Presenter {
137
  }
138
 
139
  $text = \trim( $breadcrumb['text'] );
140
- if ( ! isset( $breadcrumb['allow_html'] ) || $breadcrumb['allow_html'] !== true ) {
141
- $text = \esc_html( $text );
142
- }
143
 
144
  if (
145
  $index < ( $total - 1 ) &&
76
  }
77
 
78
  $output = '<' . $this->get_wrapper() . $this->get_id() . $this->get_class() . '>' . $output . '</' . $this->get_wrapper() . '>';
79
+ $output = $this->filter( $output );
80
 
81
  $prefix = $this->helpers->options->get( 'breadcrumbs-prefix' );
82
  if ( $prefix !== '' ) {
120
  * 'text' => (string) link text.
121
  * 'url' => (string) link url.
122
  * (optional) 'title' => (string) link title attribute text.
 
 
 
 
123
  * @param int $index Index for the current breadcrumb.
124
  * @param int $total The total number of breadcrumbs.
125
  *
133
  }
134
 
135
  $text = \trim( $breadcrumb['text'] );
 
 
 
136
 
137
  if (
138
  $index < ( $total - 1 ) &&
src/presenters/robots-presenter.php CHANGED
@@ -21,7 +21,6 @@ class Robots_Presenter extends Abstract_Indexable_Presenter {
21
  */
22
  public function present() {
23
  $robots = \implode( ', ', $this->get() );
24
- $robots = $this->filter( $robots );
25
 
26
  if ( \is_string( $robots ) && $robots !== '' ) {
27
  return \sprintf( '<meta name="robots" content="%s" />', \esc_attr( $robots ) );
@@ -39,21 +38,4 @@ class Robots_Presenter extends Abstract_Indexable_Presenter {
39
  return $this->presentation->robots;
40
  }
41
 
42
- /**
43
- * Run the robots output content through the `wpseo_robots` filter.
44
- *
45
- * @param string $robots The meta robots output to filter.
46
- *
47
- * @return string The filtered meta robots output.
48
- */
49
- private function filter( $robots ) {
50
- /**
51
- * Filter: 'wpseo_robots' - Allows filtering of the meta robots output of Yoast SEO.
52
- *
53
- * @api string $robots The meta robots directives to be echoed.
54
- *
55
- * @param Indexable_Presentation $presentation The presentation of an indexable.
56
- */
57
- return (string) \apply_filters( 'wpseo_robots', $robots, $this->presentation );
58
- }
59
  }
21
  */
22
  public function present() {
23
  $robots = \implode( ', ', $this->get() );
 
24
 
25
  if ( \is_string( $robots ) && $robots !== '' ) {
26
  return \sprintf( '<meta name="robots" content="%s" />', \esc_attr( $robots ) );
38
  return $this->presentation->robots;
39
  }
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
src/repositories/indexable-repository.php CHANGED
@@ -88,30 +88,44 @@ class Indexable_Repository {
88
  * @return bool|Indexable The indexable, false if none could be found.
89
  */
90
  public function for_current_page() {
 
 
91
  switch ( true ) {
92
  case $this->current_page->is_simple_page():
93
- return $this->find_by_id_and_type( $this->current_page->get_simple_page_id(), 'post' );
 
94
  case $this->current_page->is_home_static_page():
95
- return $this->find_by_id_and_type( $this->current_page->get_front_page_id(), 'post' );
 
96
  case $this->current_page->is_home_posts_page():
97
- return $this->find_for_home_page();
 
98
  case $this->current_page->is_term_archive():
99
- return $this->find_by_id_and_type( $this->current_page->get_term_id(), 'term' );
 
100
  case $this->current_page->is_date_archive():
101
- return $this->find_for_date_archive();
 
102
  case $this->current_page->is_search_result():
103
- return $this->find_for_system_page( 'search-result' );
 
104
  case $this->current_page->is_post_type_archive():
105
- return $this->find_for_post_type_archive( $this->current_page->get_queried_post_type() );
 
106
  case $this->current_page->is_author_archive():
107
- return $this->find_by_id_and_type( $this->current_page->get_author_id(), 'user' );
 
108
  case $this->current_page->is_404():
109
- return $this->find_for_system_page( '404' );
 
110
  }
111
 
112
- return $this->query()->create( [ 'object_type' => 'unknown' ] );
113
- }
 
114
 
 
 
115
  /**
116
  * Retrieves an indexable by its permalink.
117
  *
@@ -322,6 +336,10 @@ class Indexable_Repository {
322
 
323
  foreach ( $indexables_to_create as $indexable_to_create ) {
324
  $indexable = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type );
 
 
 
 
325
  $indexable->save();
326
 
327
  $indexables[] = $indexable;
@@ -366,6 +384,8 @@ class Indexable_Repository {
366
  * Ensures that the given indexable has a permalink.
367
  *
368
  * @param Indexable $indexable The indexable.
 
 
369
  */
370
  protected function ensure_permalink( $indexable ) {
371
  if ( $indexable && $indexable->permalink === null ) {
88
  * @return bool|Indexable The indexable, false if none could be found.
89
  */
90
  public function for_current_page() {
91
+ $indexable = false;
92
+
93
  switch ( true ) {
94
  case $this->current_page->is_simple_page():
95
+ $indexable = $this->find_by_id_and_type( $this->current_page->get_simple_page_id(), 'post' );
96
+ break;
97
  case $this->current_page->is_home_static_page():
98
+ $indexable = $this->find_by_id_and_type( $this->current_page->get_front_page_id(), 'post' );
99
+ break;
100
  case $this->current_page->is_home_posts_page():
101
+ $indexable = $this->find_for_home_page();
102
+ break;
103
  case $this->current_page->is_term_archive():
104
+ $indexable = $this->find_by_id_and_type( $this->current_page->get_term_id(), 'term' );
105
+ break;
106
  case $this->current_page->is_date_archive():
107
+ $indexable = $this->find_for_date_archive();
108
+ break;
109
  case $this->current_page->is_search_result():
110
+ $indexable = $this->find_for_system_page( 'search-result' );
111
+ break;
112
  case $this->current_page->is_post_type_archive():
113
+ $indexable = $this->find_for_post_type_archive( $this->current_page->get_queried_post_type() );
114
+ break;
115
  case $this->current_page->is_author_archive():
116
+ $indexable = $this->find_by_id_and_type( $this->current_page->get_author_id(), 'user' );
117
+ break;
118
  case $this->current_page->is_404():
119
+ $indexable = $this->find_for_system_page( '404' );
120
+ break;
121
  }
122
 
123
+ if ( $indexable === false ) {
124
+ return $this->query()->create( [ 'object_type' => 'unknown' ] );
125
+ }
126
 
127
+ return $indexable;
128
+ }
129
  /**
130
  * Retrieves an indexable by its permalink.
131
  *
336
 
337
  foreach ( $indexables_to_create as $indexable_to_create ) {
338
  $indexable = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type );
339
+ if ( $indexable === false ) {
340
+ continue;
341
+ }
342
+
343
  $indexable->save();
344
 
345
  $indexables[] = $indexable;
384
  * Ensures that the given indexable has a permalink.
385
  *
386
  * @param Indexable $indexable The indexable.
387
+ *
388
+ * @return bool|Indexable The indexable.
389
  */
390
  protected function ensure_permalink( $indexable ) {
391
  if ( $indexable && $indexable->permalink === null ) {
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit6cd015794c26ae72e6a76652322b517f::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInit424d5d6160c52f59ba476750c190098f::getLoader();
vendor/autoload_52.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_52.php generated by xrstf/composer-php52
4
+
5
+ require_once dirname(__FILE__) . '/composer'.'/autoload_real_52.php';
6
+
7
+ return ComposerAutoloaderInitfd13764fbdc9191745a39fc948165309::getLoader();
vendor/composer/ClassLoader.php CHANGED
@@ -279,7 +279,7 @@ class ClassLoader
279
  */
280
  public function setApcuPrefix($apcuPrefix)
281
  {
282
- $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
283
  }
284
 
285
  /**
@@ -377,11 +377,11 @@ class ClassLoader
377
  $subPath = $class;
378
  while (false !== $lastPos = strrpos($subPath, '\\')) {
379
  $subPath = substr($subPath, 0, $lastPos);
380
- $search = $subPath.'\\';
381
  if (isset($this->prefixDirsPsr4[$search])) {
 
382
  foreach ($this->prefixDirsPsr4[$search] as $dir) {
383
- $length = $this->prefixLengthsPsr4[$first][$search];
384
- if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
385
  return $file;
386
  }
387
  }
279
  */
280
  public function setApcuPrefix($apcuPrefix)
281
  {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
283
  }
284
 
285
  /**
377
  $subPath = $class;
378
  while (false !== $lastPos = strrpos($subPath, '\\')) {
379
  $subPath = substr($subPath, 0, $lastPos);
380
+ $search = $subPath . '\\';
381
  if (isset($this->prefixDirsPsr4[$search])) {
382
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
383
  foreach ($this->prefixDirsPsr4[$search] as $dir) {
384
+ if (file_exists($file = $dir . $pathEnd)) {
 
385
  return $file;
386
  }
387
  }
vendor/composer/ClassLoader52.php ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright (c) 2013, Christoph Mewes, http://www.xrstf.de
4
+ *
5
+ * This file is released under the terms of the MIT license. You can find the
6
+ * complete text in the attached LICENSE file or online at:
7
+ *
8
+ * http://www.opensource.org/licenses/mit-license.php
9
+ *
10
+ * --------------------------------------------------------------------------
11
+ *
12
+ * 99% of this is copied as-is from the original Composer source code and is
13
+ * released under MIT license as well. Copyright goes to:
14
+ *
15
+ * - Fabien Potencier <fabien@symfony.com>
16
+ * - Jordi Boggiano <j.boggiano@seld.be>
17
+ */
18
+
19
+ class xrstf_Composer52_ClassLoader {
20
+ private $prefixes = array();
21
+ private $fallbackDirs = array();
22
+ private $useIncludePath = false;
23
+ private $classMap = array();
24
+ private $classMapAuthoratative = false;
25
+ private $allowUnderscore = false;
26
+
27
+ /**
28
+ * @param boolean $flag true to allow class names with a leading underscore, false to disable
29
+ */
30
+ public function setAllowUnderscore($flag) {
31
+ $this->allowUnderscore = (boolean) $flag;
32
+ }
33
+
34
+ /**
35
+ * @return array
36
+ */
37
+ public function getPrefixes() {
38
+ return $this->prefixes;
39
+ }
40
+
41
+ /**
42
+ * Turns off searching the prefix and fallback directories for classes
43
+ * that have not been registered with the class map.
44
+ *
45
+ * @param bool $classMapAuthoratative
46
+ */
47
+ public function setClassMapAuthoritative($classMapAuthoratative) {
48
+ $this->classMapAuthoratative = $classMapAuthoratative;
49
+ }
50
+
51
+ /**
52
+ * Should class lookup fail if not found in the current class map?
53
+ *
54
+ * @return bool
55
+ */
56
+ public function getClassMapAuthoratative() {
57
+ return $this->classMapAuthoratative;
58
+ }
59
+
60
+ /**
61
+ * @return array
62
+ */
63
+ public function getFallbackDirs() {
64
+ return $this->fallbackDirs;
65
+ }
66
+
67
+ /**
68
+ * @return array
69
+ */
70
+ public function getClassMap() {
71
+ return $this->classMap;
72
+ }
73
+
74
+ /**
75
+ * @param array $classMap class to filename map
76
+ */
77
+ public function addClassMap(array $classMap) {
78
+ if ($this->classMap) {
79
+ $this->classMap = array_merge($this->classMap, $classMap);
80
+ }
81
+ else {
82
+ $this->classMap = $classMap;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Registers a set of classes, merging with any others previously set.
88
+ *
89
+ * @param string $prefix the classes prefix
90
+ * @param array|string $paths the location(s) of the classes
91
+ * @param bool $prepend prepend the location(s)
92
+ */
93
+ public function add($prefix, $paths, $prepend = false) {
94
+ if (!$prefix) {
95
+ if ($prepend) {
96
+ $this->fallbackDirs = array_merge(
97
+ (array) $paths,
98
+ $this->fallbackDirs
99
+ );
100
+ }
101
+ else {
102
+ $this->fallbackDirs = array_merge(
103
+ $this->fallbackDirs,
104
+ (array) $paths
105
+ );
106
+ }
107
+
108
+ return;
109
+ }
110
+
111
+ if (!isset($this->prefixes[$prefix])) {
112
+ $this->prefixes[$prefix] = (array) $paths;
113
+ return;
114
+ }
115
+
116
+ if ($prepend) {
117
+ $this->prefixes[$prefix] = array_merge(
118
+ (array) $paths,
119
+ $this->prefixes[$prefix]
120
+ );
121
+ }
122
+ else {
123
+ $this->prefixes[$prefix] = array_merge(
124
+ $this->prefixes[$prefix],
125
+ (array) $paths
126
+ );
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Registers a set of classes, replacing any others previously set.
132
+ *
133
+ * @param string $prefix the classes prefix
134
+ * @param array|string $paths the location(s) of the classes
135
+ */
136
+ public function set($prefix, $paths) {
137
+ if (!$prefix) {
138
+ $this->fallbackDirs = (array) $paths;
139
+ return;
140
+ }
141
+
142
+ $this->prefixes[$prefix] = (array) $paths;
143
+ }
144
+
145
+ /**
146
+ * Turns on searching the include path for class files.
147
+ *
148
+ * @param bool $useIncludePath
149
+ */
150
+ public function setUseIncludePath($useIncludePath) {
151
+ $this->useIncludePath = $useIncludePath;
152
+ }
153
+
154
+ /**
155
+ * Can be used to check if the autoloader uses the include path to check
156
+ * for classes.
157
+ *
158
+ * @return bool
159
+ */
160
+ public function getUseIncludePath() {
161
+ return $this->useIncludePath;
162
+ }
163
+
164
+ /**
165
+ * Registers this instance as an autoloader.
166
+ */
167
+ public function register() {
168
+ spl_autoload_register(array($this, 'loadClass'), true);
169
+ }
170
+
171
+ /**
172
+ * Unregisters this instance as an autoloader.
173
+ */
174
+ public function unregister() {
175
+ spl_autoload_unregister(array($this, 'loadClass'));
176
+ }
177
+
178
+ /**
179
+ * Loads the given class or interface.
180
+ *
181
+ * @param string $class the name of the class
182
+ * @return bool|null true, if loaded
183
+ */
184
+ public function loadClass($class) {
185
+ if ($file = $this->findFile($class)) {
186
+ include $file;
187
+ return true;
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Finds the path to the file where the class is defined.
193
+ *
194
+ * @param string $class the name of the class
195
+ * @return string|null the path, if found
196
+ */
197
+ public function findFile($class) {
198
+ if ('\\' === $class[0]) {
199
+ $class = substr($class, 1);
200
+ }
201
+
202
+ if (isset($this->classMap[$class])) {
203
+ return $this->classMap[$class];
204
+ }
205
+ elseif ($this->classMapAuthoratative) {
206
+ return false;
207
+ }
208
+
209
+ $classPath = $this->getClassPath($class);
210
+
211
+ foreach ($this->prefixes as $prefix => $dirs) {
212
+ if (0 === strpos($class, $prefix)) {
213
+ foreach ($dirs as $dir) {
214
+ if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) {
215
+ return $dir.DIRECTORY_SEPARATOR.$classPath;
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ foreach ($this->fallbackDirs as $dir) {
222
+ if (file_exists($dir.DIRECTORY_SEPARATOR.$classPath)) {
223
+ return $dir.DIRECTORY_SEPARATOR.$classPath;
224
+ }
225
+ }
226
+
227
+ if ($this->useIncludePath && $file = self::resolveIncludePath($classPath)) {
228
+ return $file;
229
+ }
230
+
231
+ return $this->classMap[$class] = false;
232
+ }
233
+
234
+ private function getClassPath($class) {
235
+ if (false !== $pos = strrpos($class, '\\')) {
236
+ // namespaced class name
237
+ $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)).DIRECTORY_SEPARATOR;
238
+ $className = substr($class, $pos + 1);
239
+ }
240
+ else {
241
+ // PEAR-like class name
242
+ $classPath = null;
243
+ $className = $class;
244
+ }
245
+
246
+ $className = str_replace('_', DIRECTORY_SEPARATOR, $className);
247
+
248
+ // restore the prefix
249
+ if ($this->allowUnderscore && DIRECTORY_SEPARATOR === $className[0]) {
250
+ $className[0] = '_';
251
+ }
252
+
253
+ $classPath .= $className.'.php';
254
+
255
+ return $classPath;
256
+ }
257
+
258
+ public static function resolveIncludePath($classPath) {
259
+ $paths = explode(PATH_SEPARATOR, get_include_path());
260
+
261
+ foreach ($paths as $path) {
262
+ $path = rtrim($path, '/\\');
263
+
264
+ if ($file = file_exists($path.DIRECTORY_SEPARATOR.$file)) {
265
+ return $file;
266
+ }
267
+ }
268
+
269
+ return false;
270
+ }
271
+ }
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit6cd015794c26ae72e6a76652322b517f
6
  {
7
  private static $loader;
8
 
@@ -13,21 +13,24 @@ class ComposerAutoloaderInit6cd015794c26ae72e6a76652322b517f
13
  }
14
  }
15
 
 
 
 
16
  public static function getLoader()
17
  {
18
  if (null !== self::$loader) {
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit6cd015794c26ae72e6a76652322b517f', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit6cd015794c26ae72e6a76652322b517f', 'loadClassLoader'));
25
 
26
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
  if ($useStaticLoader) {
28
  require_once __DIR__ . '/autoload_static.php';
29
 
30
- call_user_func(\Composer\Autoload\ComposerStaticInit6cd015794c26ae72e6a76652322b517f::getInitializer($loader));
31
  } else {
32
  $map = require __DIR__ . '/autoload_namespaces.php';
33
  foreach ($map as $namespace => $path) {
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInit424d5d6160c52f59ba476750c190098f
6
  {
7
  private static $loader;
8
 
13
  }
14
  }
15
 
16
+ /**
17
+ * @return \Composer\Autoload\ClassLoader
18
+ */
19
  public static function getLoader()
20
  {
21
  if (null !== self::$loader) {
22
  return self::$loader;
23
  }
24
 
25
+ spl_autoload_register(array('ComposerAutoloaderInit424d5d6160c52f59ba476750c190098f', 'loadClassLoader'), true, true);
26
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
27
+ spl_autoload_unregister(array('ComposerAutoloaderInit424d5d6160c52f59ba476750c190098f', 'loadClassLoader'));
28
 
29
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
  if ($useStaticLoader) {
31
  require_once __DIR__ . '/autoload_static.php';
32
 
33
+ call_user_func(\Composer\Autoload\ComposerStaticInit424d5d6160c52f59ba476750c190098f::getInitializer($loader));
34
  } else {
35
  $map = require __DIR__ . '/autoload_namespaces.php';
36
  foreach ($map as $namespace => $path) {
vendor/composer/autoload_real_52.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real_52.php generated by xrstf/composer-php52
4
+
5
+ class ComposerAutoloaderInitfd13764fbdc9191745a39fc948165309 {
6
+ private static $loader;
7
+
8
+ public static function loadClassLoader($class) {
9
+ if ('xrstf_Composer52_ClassLoader' === $class) {
10
+ require dirname(__FILE__).'/ClassLoader52.php';
11
+ }
12
+ }
13
+
14
+ /**
15
+ * @return xrstf_Composer52_ClassLoader
16
+ */
17
+ public static function getLoader() {
18
+ if (null !== self::$loader) {
19
+ return self::$loader;
20
+ }
21
+
22
+ spl_autoload_register(array('ComposerAutoloaderInitfd13764fbdc9191745a39fc948165309', 'loadClassLoader'), true /*, true */);
23
+ self::$loader = $loader = new xrstf_Composer52_ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInitfd13764fbdc9191745a39fc948165309', 'loadClassLoader'));
25
+
26
+