| 1 |
<?php |
| 2 |
/** |
| 3 |
* WPSEO plugin file. |
| 4 |
* |
| 5 |
* @package WPSEO\Admin\Menu |
| 6 |
*/ |
| 7 |
|
| 8 |
use Yoast\WP\SEO\Promotions\Application\Promotion_Manager; |
| 9 |
|
| 10 |
/** |
| 11 |
* Admin menu base class. |
| 12 |
*/ |
| 13 |
abstract class WPSEO_Base_Menu implements WPSEO_WordPress_Integration { |
| 14 |
|
| 15 |
/** |
| 16 |
* A menu. |
| 17 |
* |
| 18 |
* @var WPSEO_Menu |
| 19 |
*/ |
| 20 |
protected $menu; |
| 21 |
|
| 22 |
/** |
| 23 |
* Constructs the Admin Menu. |
| 24 |
* |
| 25 |
* @param WPSEO_Menu $menu Menu to use. |
| 26 |
*/ |
| 27 |
public function __construct( WPSEO_Menu $menu ) { |
| 28 |
$this->menu = $menu; |
| 29 |
} |
| 30 |
|
| 31 |
/** |
| 32 |
* Returns the list of registered submenu pages. |
| 33 |
* |
| 34 |
* @return array List of registered submenu pages. |
| 35 |
*/ |
| 36 |
abstract public function get_submenu_pages(); |
| 37 |
|
| 38 |
/** |
| 39 |
* Creates a submenu formatted array. |
| 40 |
* |
| 41 |
* @param string $page_title Page title to use. |
| 42 |
* @param string $page_slug Page slug to use. |
| 43 |
* @param callable|null $callback Optional. Callback which handles the page request. |
| 44 |
* @param callable[]|null $hook Optional. Hook to trigger when the page is registered. |
| 45 |
* |
| 46 |
* @return array Formatted submenu. |
| 47 |
*/ |
| 48 |
protected function get_submenu_page( $page_title, $page_slug, $callback = null, $hook = null ) { |
| 49 |
$callback ??= $this->get_admin_page_callback(); |
| 50 |
|
| 51 |
return [ |
| 52 |
$this->get_page_identifier(), |
| 53 |
'', |
| 54 |
$page_title, |
| 55 |
$this->get_manage_capability(), |
| 56 |
$page_slug, |
| 57 |
$callback, |
| 58 |
$hook, |
| 59 |
]; |
| 60 |
} |
| 61 |
|
| 62 |
/** |
| 63 |
* Registers submenu pages as menu pages. |
| 64 |
* |
| 65 |
* This method should only be used if the user does not have the required capabilities |
| 66 |
* to access the parent menu page. |
| 67 |
* |
| 68 |
* @param array $submenu_pages List of submenu pages to register. |
| 69 |
* |
| 70 |
* @return void |
| 71 |
*/ |
| 72 |
protected function register_menu_pages( $submenu_pages ) { |
| 73 |
if ( ! is_array( $submenu_pages ) || empty( $submenu_pages ) ) { |
| 74 |
return; |
| 75 |
} |
| 76 |
|
| 77 |
// Loop through submenu pages and add them. |
| 78 |
array_walk( $submenu_pages, [ $this, 'register_menu_page' ] ); |
| 79 |
} |
| 80 |
|
| 81 |
/** |
| 82 |
* Registers submenu pages. |
| 83 |
* |
| 84 |
* @param array $submenu_pages List of submenu pages to register. |
| 85 |
* |
| 86 |
* @return void |
| 87 |
*/ |
| 88 |
protected function register_submenu_pages( $submenu_pages ) { |
| 89 |
if ( ! is_array( $submenu_pages ) || empty( $submenu_pages ) ) { |
| 90 |
return; |
| 91 |
} |
| 92 |
|
| 93 |
// Loop through submenu pages and add them. |
| 94 |
array_walk( $submenu_pages, [ $this, 'register_submenu_page' ] ); |
| 95 |
} |
| 96 |
|
| 97 |
/** |
| 98 |
* Registers a submenu page as a menu page. |
| 99 |
* |
| 100 |
* This method should only be used if the user does not have the required capabilities |
| 101 |
* to access the parent menu page. |
| 102 |
* |
| 103 |
* @param array $submenu_page { |
| 104 |
* Submenu page definition. |
| 105 |
* |
| 106 |
* @type string $0 Parent menu page slug. |
| 107 |
* @type string $1 Page title, currently unused. |
| 108 |
* @type string $2 Title to display in the menu. |
| 109 |
* @type string $3 Required capability to access the page. |
| 110 |
* @type string $4 Page slug. |
| 111 |
* @type callable $5 Callback to run when the page is rendered. |
| 112 |
* @type array $6 Optional. List of callbacks to run when the page is loaded. |
| 113 |
* } |
| 114 |
* |
| 115 |
* @return void |
| 116 |
*/ |
| 117 |
protected function register_menu_page( $submenu_page ) { |
| 118 |
|
| 119 |
// If the submenu page requires the general manage capability, it must be added as an actual submenu page. |
| 120 |
if ( $submenu_page[3] === $this->get_manage_capability() ) { |
| 121 |
return; |
| 122 |
} |
| 123 |
|
| 124 |
$page_title = 'Yoast SEO: ' . $submenu_page[2]; |
| 125 |
|
| 126 |
// Register submenu page as menu page. |
| 127 |
$hook_suffix = add_menu_page( |
| 128 |
$page_title, |
| 129 |
$submenu_page[2], |
| 130 |
$submenu_page[3], |
| 131 |
$submenu_page[4], |
| 132 |
$submenu_page[5], |
| 133 |
$this->get_icon_svg(), |
| 134 |
99, |
| 135 |
); |
| 136 |
|
| 137 |
// If necessary, add hooks for the submenu page. |
| 138 |
if ( isset( $submenu_page[6] ) && ( is_array( $submenu_page[6] ) ) ) { |
| 139 |
$this->add_page_hooks( $hook_suffix, $submenu_page[6] ); |
| 140 |
} |
| 141 |
} |
| 142 |
|
| 143 |
/** |
| 144 |
* Registers a submenu page. |
| 145 |
* |
| 146 |
* This method will override the capability of the page to automatically use the |
| 147 |
* general manage capability. Use the `register_menu_page()` method if the submenu |
| 148 |
* page should actually use a different capability. |
| 149 |
* |
| 150 |
* @param array $submenu_page { |
| 151 |
* Submenu page definition. |
| 152 |
* |
| 153 |
* @type string $0 Parent menu page slug. |
| 154 |
* @type string $1 Page title, currently unused. |
| 155 |
* @type string $2 Title to display in the menu. |
| 156 |
* @type string $3 Required capability to access the page. |
| 157 |
* @type string $4 Page slug. |
| 158 |
* @type callable $5 Callback to run when the page is rendered. |
| 159 |
* @type array $6 Optional. List of callbacks to run when the page is loaded. |
| 160 |
* } |
| 161 |
* |
| 162 |
* @return void |
| 163 |
*/ |
| 164 |
protected function register_submenu_page( $submenu_page ) { |
| 165 |
$page_title = $submenu_page[2]; |
| 166 |
|
| 167 |
/* |
| 168 |
* Handle the Google Search Console special case by passing a fake parent |
| 169 |
* page slug. This way, the sub-page is stil registered and can be accessed |
| 170 |
* directly. Its menu item won't be displayed. |
| 171 |
*/ |
| 172 |
if ( $submenu_page[4] === 'wpseo_search_console' ) { |
| 173 |
// Set the parent page slug to a non-existing one. |
| 174 |
$submenu_page[0] = 'wpseo_fake_menu_parent_page_slug'; |
| 175 |
} |
| 176 |
|
| 177 |
$page_title .= ' - Yoast SEO'; |
| 178 |
|
| 179 |
// Register submenu page. |
| 180 |
$hook_suffix = add_submenu_page( |
| 181 |
$submenu_page[0], |
| 182 |
$page_title, |
| 183 |
$submenu_page[2], |
| 184 |
$submenu_page[3], |
| 185 |
$submenu_page[4], |
| 186 |
$submenu_page[5], |
| 187 |
); |
| 188 |
|
| 189 |
// If necessary, add hooks for the submenu page. |
| 190 |
if ( isset( $submenu_page[6] ) && ( is_array( $submenu_page[6] ) ) ) { |
| 191 |
$this->add_page_hooks( $hook_suffix, $submenu_page[6] ); |
| 192 |
} |
| 193 |
} |
| 194 |
|
| 195 |
/** |
| 196 |
* Adds hook callbacks for a given admin page hook suffix. |
| 197 |
* |
| 198 |
* @param string $hook_suffix Admin page hook suffix, as returned by `add_menu_page()` |
| 199 |
* or `add_submenu_page()`. |
| 200 |
* @param array $callbacks Callbacks to add. |
| 201 |
* |
| 202 |
* @return void |
| 203 |
*/ |
| 204 |
protected function add_page_hooks( $hook_suffix, array $callbacks ) { |
| 205 |
foreach ( $callbacks as $callback ) { |
| 206 |
add_action( 'load-' . $hook_suffix, $callback ); |
| 207 |
} |
| 208 |
} |
| 209 |
|
| 210 |
/** |
| 211 |
* Gets the main admin page identifier. |
| 212 |
* |
| 213 |
* @return string Admin page identifier. |
| 214 |
*/ |
| 215 |
protected function get_page_identifier() { |
| 216 |
return $this->menu->get_page_identifier(); |
| 217 |
} |
| 218 |
|
| 219 |
/** |
| 220 |
* Checks whether the current user has capabilities to manage all options. |
| 221 |
* |
| 222 |
* @return bool True if capabilities are sufficient, false otherwise. |
| 223 |
*/ |
| 224 |
protected function check_manage_capability() { |
| 225 |
return WPSEO_Capability_Utils::current_user_can( $this->get_manage_capability() ); |
| 226 |
} |
| 227 |
|
| 228 |
/** |
| 229 |
* Returns the capability that is required to manage all options. |
| 230 |
* |
| 231 |
* @return string Capability to check against. |
| 232 |
*/ |
| 233 |
abstract protected function get_manage_capability(); |
| 234 |
|
| 235 |
/** |
| 236 |
* Returns the page handler callback. |
| 237 |
* |
| 238 |
* @return array Callback page handler. |
| 239 |
*/ |
| 240 |
protected function get_admin_page_callback() { |
| 241 |
return [ $this->menu, 'load_page' ]; |
| 242 |
} |
| 243 |
|
| 244 |
/** |
| 245 |
* Returns the page title to use for the licenses page. |
| 246 |
* |
| 247 |
* @deprecated 25.5 |
| 248 |
* @codeCoverageIgnore |
| 249 |
* |
| 250 |
* @return string The title for the license page. |
| 251 |
*/ |
| 252 |
protected function get_license_page_title() { |
| 253 |
static $title = null; |
| 254 |
|
| 255 |
_deprecated_function( __METHOD__, 'Yoast SEO 25.5' ); |
| 256 |
|
| 257 |
$title ??= __( 'Upgrades', 'wordpress-seo' ); |
| 258 |
|
| 259 |
if ( YoastSEO()->classes->get( Promotion_Manager::class )->is( 'black-friday-promotion' ) && ! YoastSEO()->helpers->product->is_premium() ) { |
| 260 |
$title = __( 'Upgrades', 'wordpress-seo' ) . '<span class="yoast-menu-bf-sale-badge">' . __( '30% OFF', 'wordpress-seo' ) . '</span>'; |
| 261 |
} |
| 262 |
|
| 263 |
return $title; |
| 264 |
} |
| 265 |
|
| 266 |
/** |
| 267 |
* Returns a base64 URL for the svg for use in the menu. |
| 268 |
* |
| 269 |
* @param bool $base64 Whether or not to return base64'd output. |
| 270 |
* |
| 271 |
* @return string SVG icon. |
| 272 |
*/ |
| 273 |
public function get_icon_svg( $base64 = true ) { |
| 274 |
$svg = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" style="fill:#82878c" viewBox="0 0 512 512" role="img" aria-hidden="true" focusable="false"><g><g><g><g><path d="M203.6,395c6.8-17.4,6.8-36.6,0-54l-79.4-204h70.9l47.7,149.4l74.8-207.6H116.4c-41.8,0-76,34.2-76,76V357c0,41.8,34.2,76,76,76H173C189,424.1,197.6,410.3,203.6,395z"/></g><g><path d="M471.6,154.8c0-41.8-34.2-76-76-76h-3L285.7,365c-9.6,26.7-19.4,49.3-30.3,68h216.2V154.8z"/></g></g><path stroke-width="2.974" stroke-miterlimit="10" d="M338,1.3l-93.3,259.1l-42.1-131.9h-89.1l83.8,215.2c6,15.5,6,32.5,0,48c-7.4,19-19,37.3-53,41.9l-7.2,1v76h8.3c81.7,0,118.9-57.2,149.6-142.9L431.6,1.3H338z M279.4,362c-32.9,92-67.6,128.7-125.7,131.8v-45c37.5-7.5,51.3-31,59.1-51.1c7.5-19.3,7.5-40.7,0-60l-75-192.7h52.8l53.3,166.8l105.9-294h58.1L279.4,362z"/></g></g></svg>'; |
| 275 |
|
| 276 |
if ( $base64 ) { |
| 277 |
//phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- This encoding is intended. |
| 278 |
return 'data:image/svg+xml;base64,' . base64_encode( $svg ); |
| 279 |
} |
| 280 |
|
| 281 |
return $svg; |
| 282 |
} |
| 283 |
} |
| 284 |
|