| 1 |
<?php |
| 2 |
/** |
| 3 |
* Admin options page |
| 4 |
*/ |
| 5 |
|
| 6 |
// Exit if accessed directly. |
| 7 |
if ( !defined( 'ABSPATH' ) ) { |
| 8 |
exit; |
| 9 |
} |
| 10 |
|
| 11 |
|
| 12 |
/** |
| 13 |
* Initiate the class |
| 14 |
*/ |
| 15 |
if ( !is_network_admin() ) { |
| 16 |
add_action( 'init', function() { |
| 17 |
(new BLNOTIFIER_MENU)->init(); |
| 18 |
} ); |
| 19 |
} |
| 20 |
|
| 21 |
|
| 22 |
/** |
| 23 |
* Main plugin class. |
| 24 |
*/ |
| 25 |
class BLNOTIFIER_MENU { |
| 26 |
|
| 27 |
/** |
| 28 |
* The menu slug |
| 29 |
* |
| 30 |
* @var string |
| 31 |
*/ |
| 32 |
public $page_slug = 'blnotifier-settings'; |
| 33 |
|
| 34 |
|
| 35 |
/** |
| 36 |
* The menu items |
| 37 |
* |
| 38 |
* @var array |
| 39 |
*/ |
| 40 |
public $menu_items; |
| 41 |
|
| 42 |
|
| 43 |
/** |
| 44 |
* The capability required to access the menu |
| 45 |
* |
| 46 |
* @var string |
| 47 |
*/ |
| 48 |
public $capability = 'manage_broken_links'; |
| 49 |
|
| 50 |
|
| 51 |
/** |
| 52 |
* Constructor |
| 53 |
*/ |
| 54 |
public function __construct() { |
| 55 |
|
| 56 |
// The menu items |
| 57 |
$this->menu_items = [ |
| 58 |
'results' => [ __( 'Results', 'broken-link-notifier' ) ], |
| 59 |
'omit-links' => [ __( 'Omitted Links', 'broken-link-notifier' ), 'edit-tags.php?taxonomy=omit-links' ], |
| 60 |
'omit-pages' => [ __( 'Omitted Pages', 'broken-link-notifier' ), 'edit-tags.php?taxonomy=omit-pages' ], |
| 61 |
'scan-single' => [ __( 'Page Scan', 'broken-link-notifier' ) ], |
| 62 |
'scan-multi' => [ __( 'Multi-Scan', 'broken-link-notifier' ) ], |
| 63 |
'link-search' => [ __( 'Link Search', 'broken-link-notifier' ) ], |
| 64 |
'export' => [ __( 'Export', 'broken-link-notifier' ) ], |
| 65 |
'settings' => [ __( 'Settings', 'broken-link-notifier' ) ], |
| 66 |
'help' => [ __( 'Help', 'broken-link-notifier' ) ], |
| 67 |
]; |
| 68 |
|
| 69 |
} // End __construct() |
| 70 |
|
| 71 |
|
| 72 |
/** |
| 73 |
* Load on init |
| 74 |
*/ |
| 75 |
public function init() { |
| 76 |
|
| 77 |
// Add the menu |
| 78 |
add_action( 'admin_menu', [ $this, 'admin_menu' ] ); |
| 79 |
|
| 80 |
// Update role after sett |
| 81 |
add_action( 'admin_init', [ $this, 'update_roles' ] ); |
| 82 |
|
| 83 |
// Fix the Manage link to show active |
| 84 |
add_filter( 'parent_file', [ $this, 'submenus' ] ); |
| 85 |
|
| 86 |
// Settings page fields |
| 87 |
add_action( 'admin_init', [ $this, 'settings_fields' ] ); |
| 88 |
|
| 89 |
// Enqueue script |
| 90 |
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); |
| 91 |
|
| 92 |
// AJAX test notification |
| 93 |
add_action( 'wp_ajax_blnotifier_test_notification', [ $this, 'ajax_test_notification' ] ); |
| 94 |
|
| 95 |
} // End init() |
| 96 |
|
| 97 |
|
| 98 |
/** |
| 99 |
* Add page to Tools menu |
| 100 |
* |
| 101 |
* @return void |
| 102 |
*/ |
| 103 |
public function admin_menu() { |
| 104 |
// Get the current user's roles |
| 105 |
$current_user = wp_get_current_user(); |
| 106 |
$user_roles = (array) $current_user->roles; |
| 107 |
|
| 108 |
$capability = 'do_not_allow'; |
| 109 |
$show_menu = false; |
| 110 |
|
| 111 |
if ( in_array( 'administrator', $user_roles ) ) { |
| 112 |
$capability = 'manage_options'; |
| 113 |
$show_menu = true; |
| 114 |
|
| 115 |
} else { |
| 116 |
$allowed_roles = get_option( 'blnotifier_editable_roles', [] ); |
| 117 |
if ( is_array( $allowed_roles ) && !empty( $allowed_roles ) ) { |
| 118 |
foreach ( $allowed_roles as $role_slug => $value ) { |
| 119 |
$role_slug = sanitize_key( $role_slug ); |
| 120 |
|
| 121 |
if ( in_array( $role_slug, $user_roles ) ) { |
| 122 |
$capability = $this->capability; |
| 123 |
$show_menu = true; |
| 124 |
break; |
| 125 |
} |
| 126 |
} |
| 127 |
} |
| 128 |
} |
| 129 |
|
| 130 |
if ( !$show_menu ) { |
| 131 |
return; |
| 132 |
} |
| 133 |
|
| 134 |
// Count broken links |
| 135 |
$count = (new BLNOTIFIER_HELPERS)->count_broken_links(); |
| 136 |
$notif = $count > 0 ? ' <span class="awaiting-mod">'.(new BLNOTIFIER_HELPERS)->count_broken_links().'</span>' : ''; |
| 137 |
|
| 138 |
// Add the menu |
| 139 |
add_menu_page( |
| 140 |
BLNOTIFIER_NAME, |
| 141 |
BLNOTIFIER_NAME. $notif, |
| 142 |
$capability, |
| 143 |
BLNOTIFIER_TEXTDOMAIN, |
| 144 |
[ $this, 'settings_page' ], |
| 145 |
'dashicons-editor-unlink' |
| 146 |
); |
| 147 |
|
| 148 |
// Add the submenus |
| 149 |
global $submenu; |
| 150 |
foreach ( $this->menu_items as $key => $menu_item ) { |
| 151 |
$link = isset( $menu_item[1] ) ? $menu_item[1] : 'admin.php?page='.BLNOTIFIER_TEXTDOMAIN.'&tab='.$key; |
| 152 |
$submenu[ BLNOTIFIER_TEXTDOMAIN ][] = [ $menu_item[0], $capability, $link ]; |
| 153 |
} |
| 154 |
} // End admin_menu() |
| 155 |
|
| 156 |
|
| 157 |
/** |
| 158 |
* Update user roles |
| 159 |
* |
| 160 |
* @return void |
| 161 |
*/ |
| 162 |
public function update_roles() { |
| 163 |
if ( isset( $_GET[ 'settings-updated' ] ) && $_GET[ 'settings-updated' ] == 'true' ) { |
| 164 |
|
| 165 |
$allowed_roles = get_option( 'blnotifier_editable_roles', [] ); |
| 166 |
if ( is_array( $allowed_roles ) && !empty( $allowed_roles ) ) { |
| 167 |
$saniotized_allowed_roles = []; |
| 168 |
foreach ( $allowed_roles as $role_slug => $value ) { |
| 169 |
$saniotized_allowed_roles[] = sanitize_key( $role_slug ); |
| 170 |
} |
| 171 |
$allowed_roles = $saniotized_allowed_roles; |
| 172 |
} |
| 173 |
|
| 174 |
$editable_roles = $this->get_editable_roles_choices(); |
| 175 |
if ( !empty( $editable_roles ) && is_array( $editable_roles ) ) { |
| 176 |
|
| 177 |
foreach ( $editable_roles as $editable_role_slug => $label ) { |
| 178 |
$role = get_role( $editable_role_slug ); |
| 179 |
if ( !$role ) { |
| 180 |
continue; |
| 181 |
} |
| 182 |
|
| 183 |
if ( in_array( $editable_role_slug, $allowed_roles ) && ! $role->has_cap( $this->capability ) ) { |
| 184 |
$role->add_cap( $this->capability ); |
| 185 |
} elseif ( $role->has_cap( $this->capability ) ) { |
| 186 |
$role->remove_cap( $this->capability ); |
| 187 |
} |
| 188 |
} |
| 189 |
} |
| 190 |
} |
| 191 |
} // End update_roles() |
| 192 |
|
| 193 |
|
| 194 |
/** |
| 195 |
* Fix the Manage link to show active |
| 196 |
* |
| 197 |
* @param string $parent_file |
| 198 |
* @return string |
| 199 |
*/ |
| 200 |
public function submenus( $parent_file ) { |
| 201 |
global $submenu_file, $current_screen; |
| 202 |
$options_page = 'toplevel_page_'.BLNOTIFIER_TEXTDOMAIN; |
| 203 |
|
| 204 |
// Top level page |
| 205 |
if ( $current_screen->id == $options_page ) { |
| 206 |
$tab = (new BLNOTIFIER_HELPERS)->get_tab() ?? ''; |
| 207 |
$submenu_file = 'admin.php?page='.BLNOTIFIER_TEXTDOMAIN.'&tab='.$tab; |
| 208 |
|
| 209 |
// Taxonomies first |
| 210 |
} elseif ( $current_screen->id == 'edit-omit-links' ) { |
| 211 |
$submenu_file = 'edit-tags.php?taxonomy=omit-links'; |
| 212 |
$parent_file = $this->get_plugin_page_short_path( null ); |
| 213 |
} elseif ( $current_screen->id == 'edit-omit-pages' ) { |
| 214 |
$submenu_file = 'edit-tags.php?taxonomy=omit-pages'; |
| 215 |
$parent_file = $this->get_plugin_page_short_path( null ); |
| 216 |
} |
| 217 |
|
| 218 |
// Return |
| 219 |
return $parent_file; |
| 220 |
} // End submenus() |
| 221 |
|
| 222 |
|
| 223 |
/** |
| 224 |
* Settings page |
| 225 |
* |
| 226 |
* @return void |
| 227 |
*/ |
| 228 |
public function settings_page() { |
| 229 |
include BLNOTIFIER_PLUGIN_INCLUDES_PATH.'page.php'; |
| 230 |
} // End settings_page() |
| 231 |
|
| 232 |
|
| 233 |
/** |
| 234 |
* Settings fields |
| 235 |
* |
| 236 |
* @return void |
| 237 |
*/ |
| 238 |
public function settings_fields() { |
| 239 |
// Add section |
| 240 |
add_settings_section( |
| 241 |
'general', |
| 242 |
'Settings', |
| 243 |
'', |
| 244 |
$this->page_slug |
| 245 |
); |
| 246 |
|
| 247 |
// Has updated settings |
| 248 |
$has_updated_settings = 'blnotifier_has_updated_settings'; |
| 249 |
register_setting( $this->page_slug, $has_updated_settings, [ $this, 'sanitize_boolean' ] ); |
| 250 |
|
| 251 |
// Pause front-end scanning |
| 252 |
$pause_frontend_scanning_option_name = 'blnotifier_pause_frontend_scanning'; |
| 253 |
register_setting( $this->page_slug, $pause_frontend_scanning_option_name, [ $this, 'sanitize_checkbox' ] ); |
| 254 |
add_settings_field( |
| 255 |
$pause_frontend_scanning_option_name, |
| 256 |
'Pause Front-End Scanning', |
| 257 |
[ $this, 'field_checkbox' ], |
| 258 |
$this->page_slug, |
| 259 |
'general', |
| 260 |
[ |
| 261 |
'class' => $pause_frontend_scanning_option_name, |
| 262 |
'name' => $pause_frontend_scanning_option_name, |
| 263 |
'default' => false, |
| 264 |
'comments' => 'You can pause front-end scanning if you just want to scan manually; disabling this means you will NOT get notified when someone visits a page with broken links' |
| 265 |
] |
| 266 |
); |
| 267 |
|
| 268 |
// Pause results verification |
| 269 |
$pause_results_verification_option_name = 'blnotifier_pause_results_verification'; |
| 270 |
register_setting( $this->page_slug, $pause_results_verification_option_name, [ $this, 'sanitize_checkbox' ] ); |
| 271 |
add_settings_field( |
| 272 |
$pause_results_verification_option_name, |
| 273 |
'Pause Results Auto-Verification', |
| 274 |
[ $this, 'field_checkbox' ], |
| 275 |
$this->page_slug, |
| 276 |
'general', |
| 277 |
[ |
| 278 |
'class' => $pause_results_verification_option_name, |
| 279 |
'name' => $pause_results_verification_option_name, |
| 280 |
'default' => false, |
| 281 |
'comments' => 'You can pause the automatic verification and removal on the results page' |
| 282 |
] |
| 283 |
); |
| 284 |
|
| 285 |
// Enable emailing |
| 286 |
$enable_emailing_option_name = 'blnotifier_enable_emailing'; |
| 287 |
register_setting( $this->page_slug, $enable_emailing_option_name, [ $this, 'sanitize_checkbox' ] ); |
| 288 |
add_settings_field( |
| 289 |
$enable_emailing_option_name, |
| 290 |
'Enable Emailing', |
| 291 |
[ $this, 'field_checkbox' ], |
| 292 |
$this->page_slug, |
| 293 |
'general', |
| 294 |
[ |
| 295 |
'class' => $enable_emailing_option_name, |
| 296 |
'name' => $enable_emailing_option_name, |
| 297 |
'default' => true, |
| 298 |
'comments' => 'You can turn off email notifications and still get website notifications' |
| 299 |
] |
| 300 |
); |
| 301 |
|
| 302 |
// Emails |
| 303 |
$emails_option_name = 'blnotifier_emails'; |
| 304 |
register_setting( $this->page_slug, $emails_option_name, 'sanitize_text_field' ); |
| 305 |
add_settings_field( |
| 306 |
$emails_option_name, |
| 307 |
'Emails to Send Notifications', |
| 308 |
[ $this, 'field_emails_with_test' ], |
| 309 |
$this->page_slug, |
| 310 |
'general', |
| 311 |
[ |
| 312 |
'class' => $emails_option_name, |
| 313 |
'name' => $emails_option_name, |
| 314 |
'default' => get_bloginfo( 'admin_email' ), |
| 315 |
'comments' => 'Separated by commas' |
| 316 |
] |
| 317 |
); |
| 318 |
|
| 319 |
// Webhook fields |
| 320 |
$webhook_fields = [ |
| 321 |
[ |
| 322 |
'name' => 'discord', |
| 323 |
'label' => 'Discord', |
| 324 |
'comments' => 'URL should look like this: https://discord.com/api/webhooks/xxx/xxx...' |
| 325 |
], |
| 326 |
[ |
| 327 |
'name' => 'slack', |
| 328 |
'label' => 'Slack', |
| 329 |
'comments' => 'URL should look like this: https://hooks.slack.com/services/xxx/xxx/xxx' |
| 330 |
], |
| 331 |
[ |
| 332 |
'name' => 'msteams', |
| 333 |
'label' => 'Microsoft Teams', |
| 334 |
'comments' => 'URL should look like this: https://yourdomain.webhook.office.com/xxx/xxx...' |
| 335 |
] |
| 336 |
]; |
| 337 |
foreach ( $webhook_fields as $webhook_field ) { |
| 338 |
|
| 339 |
// Enable checkbox |
| 340 |
$enable_option_name = 'blnotifier_enable_'.$webhook_field[ 'name' ]; |
| 341 |
register_setting( $this->page_slug, $enable_option_name, [ $this, 'sanitize_checkbox' ] ); |
| 342 |
add_settings_field( |
| 343 |
$enable_option_name, |
| 344 |
'Enable '.$webhook_field[ 'label' ].' Notifications', |
| 345 |
[ $this, 'field_checkbox' ], |
| 346 |
$this->page_slug, |
| 347 |
'general', |
| 348 |
[ |
| 349 |
'class' => $enable_option_name, |
| 350 |
'name' => $enable_option_name, |
| 351 |
'default' => false, |
| 352 |
'comments' => 'You can also send notifications to a '.$webhook_field[ 'label' ].' channel' |
| 353 |
] |
| 354 |
); |
| 355 |
|
| 356 |
// The url |
| 357 |
$url_field_option_name = 'blnotifier_'.$webhook_field[ 'name' ]; |
| 358 |
register_setting( $this->page_slug, $url_field_option_name, [ $this, 'sanitize_url' ] ); |
| 359 |
add_settings_field( |
| 360 |
$url_field_option_name, |
| 361 |
$webhook_field[ 'label' ].' Webhook URL', |
| 362 |
[ $this, 'field_url_with_test' ], |
| 363 |
$this->page_slug, |
| 364 |
'general', |
| 365 |
[ |
| 366 |
'class' => $url_field_option_name, |
| 367 |
'name' => $url_field_option_name, |
| 368 |
'default' => '', |
| 369 |
'comments' => $webhook_field[ 'comments' ], |
| 370 |
'test_type' => $webhook_field[ 'name' ], |
| 371 |
] |
| 372 |
); |
| 373 |
} |
| 374 |
|
| 375 |
// Text |
| 376 |
$user_agent_option_name = 'blnotifier_user_agent'; |
| 377 |
register_setting( $this->page_slug, $user_agent_option_name, 'sanitize_text_field' ); |
| 378 |
add_settings_field( |
| 379 |
$user_agent_option_name, |
| 380 |
'User Agent', |
| 381 |
[ $this, 'field_text' ], |
| 382 |
$this->page_slug, |
| 383 |
'general', |
| 384 |
[ |
| 385 |
'class' => $user_agent_option_name, |
| 386 |
'name' => $user_agent_option_name, |
| 387 |
'default' => 'WordPress/{blog_version}; {blog_url}', |
| 388 |
'comments' => 'Only change this if you know what you are doing. Default is "WordPress/{blog_version}; {blog_url}" (WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ) . ')' |
| 389 |
] |
| 390 |
); |
| 391 |
|
| 392 |
// Define an array of number fields |
| 393 |
$number_fields = [ |
| 394 |
[ |
| 395 |
'name' => 'timeout', |
| 396 |
'label' => 'Timeout (seconds)', |
| 397 |
'default' => 5, |
| 398 |
'min' => 5, |
| 399 |
'comments' => 'How long to try to connect to a link\'s server before quitting' |
| 400 |
], |
| 401 |
[ |
| 402 |
'name' => 'max_redirects', |
| 403 |
'label' => 'Max Redirects', |
| 404 |
'default' => 5, |
| 405 |
'min' => 0, |
| 406 |
'comments' => 'Maximum number of redirects before giving up on a link (will only be used if you allow redirects below)' |
| 407 |
], |
| 408 |
[ |
| 409 |
'name' => 'max_links_per_page', |
| 410 |
'label' => 'Max Links Per Page', |
| 411 |
'default' => 200, |
| 412 |
'min' => 0, |
| 413 |
'comments' => 'Maximum number of links to check per page (0 for unlimited) - this is to prevent attacks and timeouts on pages with a large number of links' |
| 414 |
], |
| 415 |
]; |
| 416 |
|
| 417 |
// Loop through the array to add number fields |
| 418 |
foreach ( $number_fields as $field ) { |
| 419 |
$field_option_name = 'blnotifier_' . $field[ 'name' ]; |
| 420 |
|
| 421 |
// Register the field with a sanitization callback |
| 422 |
register_setting( $this->page_slug, $field_option_name, 'absint' ); |
| 423 |
|
| 424 |
// Add the number field to the settings page |
| 425 |
add_settings_field( |
| 426 |
$field_option_name, |
| 427 |
$field[ 'label' ], |
| 428 |
[ $this, 'field_number' ], |
| 429 |
$this->page_slug, |
| 430 |
'general', |
| 431 |
[ |
| 432 |
'class' => $field_option_name, |
| 433 |
'name' => $field_option_name, |
| 434 |
'default' => $field[ 'default' ], |
| 435 |
'min' => $field[ 'min' ], |
| 436 |
'comments' => $field[ 'comments' ] |
| 437 |
] |
| 438 |
); |
| 439 |
} |
| 440 |
|
| 441 |
// Other checkboxes |
| 442 |
$checkboxes = [ |
| 443 |
[ |
| 444 |
'name' => 'allow_redirects', |
| 445 |
'label' => 'Allow Redirects', |
| 446 |
'default' => true, |
| 447 |
'comments' => 'Changes the method of checking for broken links from <code>HEAD</code> to <code>GET</code>. May cause issues linking to larger documents.' |
| 448 |
], |
| 449 |
[ |
| 450 |
'name' => 'documents_use_head', |
| 451 |
'label' => 'Force Documents to Use <code>HEAD</code> Requests', |
| 452 |
'default' => false, |
| 453 |
'comments' => 'If you have enabled allowing redirects (above), by default images, videos, and audio files force the use of <code>HEAD</code> requests rather than <code>GET</code>. Some servers automatically block <code>HEAD</code> requests for documents, so we don\'t force them by default. If you are having issues with large documents not completing a scan, then you can try enabling this option to see if it helps. If they are blocked, at least you will know why.' |
| 454 |
], |
| 455 |
[ |
| 456 |
'name' => 'enable_warnings', |
| 457 |
'label' => 'Enable Warnings', |
| 458 |
'default' => true, |
| 459 |
'comments' => 'Includes warnings in all scans' |
| 460 |
], |
| 461 |
[ |
| 462 |
'name' => 'enable_good_links', |
| 463 |
'label' => 'Show Good Links in Results', |
| 464 |
'default' => false, |
| 465 |
'comments' => 'Includes good links on results page for verification purposes only (more performance heavy and will be omitted immediately unless you pause results auto-verification)' |
| 466 |
], |
| 467 |
[ |
| 468 |
'name' => 'enable_delete_source', |
| 469 |
'label' => 'Enable Delete Source Action Link', |
| 470 |
'default' => false, |
| 471 |
'comments' => 'An action link will appear on the Results tab under the source where you can trash the page entirely' |
| 472 |
], |
| 473 |
[ |
| 474 |
'name' => 'include_images', |
| 475 |
'label' => 'Check for Broken Images', |
| 476 |
'default' => true, |
| 477 |
'comments' => 'Includes image src links in all scans' |
| 478 |
], |
| 479 |
[ |
| 480 |
'name' => 'ssl_verify', |
| 481 |
'label' => 'Warn if SSL is Not Verified', |
| 482 |
'default' => true, |
| 483 |
'comments' => 'If you are not concerned about insecure links, you can disable this' |
| 484 |
], |
| 485 |
[ |
| 486 |
'name' => 'scan_header', |
| 487 |
'label' => 'Scan <code><header></code> Elements', |
| 488 |
'default' => false, |
| 489 |
'comments' => 'Only applies to page load scans - the header elements usually include the navigation menu(s) at the top of the page' |
| 490 |
], |
| 491 |
[ |
| 492 |
'name' => 'scan_footer', |
| 493 |
'label' => 'Scan <code><footer></code> Elements', |
| 494 |
'default' => false, |
| 495 |
'comments' => 'Only applies to page load scans - the footer elements include any links at the bottom of every page' |
| 496 |
], |
| 497 |
[ |
| 498 |
'name' => 'show_in_console', |
| 499 |
'label' => 'Show Results in Dev Console', |
| 500 |
'default' => false, |
| 501 |
'comments' => 'Only applies to page load scans' |
| 502 |
] |
| 503 |
]; |
| 504 |
foreach ( $checkboxes as $checkbox ) { |
| 505 |
$checkbox_option_name = 'blnotifier_'.$checkbox[ 'name' ]; |
| 506 |
register_setting( $this->page_slug, $checkbox_option_name, [ $this, 'sanitize_checkbox' ] ); |
| 507 |
add_settings_field( |
| 508 |
$checkbox_option_name, |
| 509 |
$checkbox[ 'label' ], |
| 510 |
[ $this, 'field_checkbox' ], |
| 511 |
$this->page_slug, |
| 512 |
'general', |
| 513 |
[ |
| 514 |
'class' => $checkbox_option_name, |
| 515 |
'name' => $checkbox_option_name, |
| 516 |
'default' => $checkbox[ 'default' ], |
| 517 |
'comments' => $checkbox[ 'comments' ] |
| 518 |
] |
| 519 |
); |
| 520 |
} |
| 521 |
|
| 522 |
// REST API |
| 523 |
$enable_rest_api_option_name = 'blnotifier_enable_rest_api'; |
| 524 |
register_setting( $this->page_slug, $enable_rest_api_option_name, [ $this, 'sanitize_checkbox' ] ); |
| 525 |
add_settings_field( |
| 526 |
$enable_rest_api_option_name, |
| 527 |
'Enable REST API', |
| 528 |
[ $this, 'field_checkbox' ], |
| 529 |
$this->page_slug, |
| 530 |
'general', |
| 531 |
[ |
| 532 |
'class' => $enable_rest_api_option_name, |
| 533 |
'name' => $enable_rest_api_option_name, |
| 534 |
'default' => false, |
| 535 |
'comments' => 'Exposes a read/delete REST API endpoint for use with AI agents and external tools.' |
| 536 |
] |
| 537 |
); |
| 538 |
|
| 539 |
$api_key_option_name = 'blnotifier_api_key'; |
| 540 |
register_setting( $this->page_slug, $api_key_option_name, 'sanitize_text_field' ); |
| 541 |
add_settings_field( |
| 542 |
$api_key_option_name, |
| 543 |
'API Key', |
| 544 |
[ $this, 'field_api_key' ], |
| 545 |
$this->page_slug, |
| 546 |
'general', |
| 547 |
[ |
| 548 |
'class' => $api_key_option_name, |
| 549 |
'name' => $api_key_option_name, |
| 550 |
] |
| 551 |
); |
| 552 |
// Caching |
| 553 |
$cache_option_name = 'blnotifier_cache'; |
| 554 |
register_setting( $this->page_slug, $cache_option_name, 'sanitize_text_field' ); |
| 555 |
add_settings_field( |
| 556 |
$cache_option_name, |
| 557 |
'Length of Time to Cache Good Links (in Seconds)', |
| 558 |
[ $this, 'field_number' ], |
| 559 |
$this->page_slug, |
| 560 |
'general', |
| 561 |
[ |
| 562 |
'class' => $cache_option_name, |
| 563 |
'name' => $cache_option_name, |
| 564 |
'default' => 0, |
| 565 |
'min' => 0, |
| 566 |
'comments' => 'Use 0 to disable caching. If you are experienced performance issues, you can set the value to 28800 (8 hours), 43200 (12 hours), 86400 (24 hours) or whatever you feel is best. Broken and warning links will never be cached. Deactivating or uninstalling the plugin will clear the cache completely.' |
| 567 |
] |
| 568 |
); |
| 569 |
|
| 570 |
// User Roles for Editing Links |
| 571 |
$roles_option_name = 'blnotifier_editable_roles'; |
| 572 |
register_setting( $this->page_slug, $roles_option_name, [ $this, 'sanitize_checkboxes' ] ); |
| 573 |
add_settings_field( |
| 574 |
$roles_option_name, |
| 575 |
'Allow These Additional Roles to Manage Broken Links', |
| 576 |
[ $this, 'field_checkboxes' ], |
| 577 |
$this->page_slug, |
| 578 |
'general', |
| 579 |
[ |
| 580 |
'class' => $roles_option_name, |
| 581 |
'name' => $roles_option_name, |
| 582 |
'options' => $this->get_editable_roles_choices(), |
| 583 |
] |
| 584 |
); |
| 585 |
|
| 586 |
// Post types |
| 587 |
$post_types_option_name = 'blnotifier_post_types'; |
| 588 |
register_setting( $this->page_slug, $post_types_option_name, [ $this, 'sanitize_checkboxes' ] ); |
| 589 |
add_settings_field( |
| 590 |
$post_types_option_name, |
| 591 |
'Enable Multi-Scan for These Post Types', |
| 592 |
[ $this, 'field_checkboxes' ], |
| 593 |
$this->page_slug, |
| 594 |
'general', |
| 595 |
[ |
| 596 |
'class' => $post_types_option_name, |
| 597 |
'name' => $post_types_option_name, |
| 598 |
'options' => $this->get_post_type_choices(), |
| 599 |
'default' => [ 'post', 'page' ] |
| 600 |
] |
| 601 |
); |
| 602 |
|
| 603 |
// Status codes |
| 604 |
$status_codes_option_name = 'blnotifier_status_codes'; |
| 605 |
register_setting( $this->page_slug, $status_codes_option_name, [] ); |
| 606 |
add_settings_field( |
| 607 |
$status_codes_option_name, |
| 608 |
'Status Codes', |
| 609 |
[ $this, 'field_status_codes' ], |
| 610 |
$this->page_slug, |
| 611 |
'general', |
| 612 |
[ |
| 613 |
'class' => $status_codes_option_name, |
| 614 |
'name' => $status_codes_option_name, |
| 615 |
'options' => (new BLNOTIFIER_HELPERS)->get_status_codes(), |
| 616 |
] |
| 617 |
); |
| 618 |
|
| 619 |
// Uninstall database cleanup |
| 620 |
$uninstall_cleanup_option_name = 'blnotifier_uninstall_cleanup'; |
| 621 |
register_setting( $this->page_slug, $uninstall_cleanup_option_name, [ $this, 'sanitize_checkbox' ] ); |
| 622 |
add_settings_field( |
| 623 |
$uninstall_cleanup_option_name, |
| 624 |
'Remove Data on Uninstall', |
| 625 |
[ $this, 'field_checkbox' ], |
| 626 |
$this->page_slug, |
| 627 |
'general', |
| 628 |
[ |
| 629 |
'class' => $uninstall_cleanup_option_name, |
| 630 |
'name' => $uninstall_cleanup_option_name, |
| 631 |
'default' => false, |
| 632 |
'comments' => 'Enable this option to automatically remove the database table that the links are stored in and all options when the plugin is uninstalled.' |
| 633 |
] |
| 634 |
); |
| 635 |
} // End settings_fields() |
| 636 |
|
| 637 |
|
| 638 |
/** |
| 639 |
* Custom callback function to print text field |
| 640 |
* |
| 641 |
* @param array $args |
| 642 |
* @return void |
| 643 |
*/ |
| 644 |
public function field_text( $args ) { |
| 645 |
printf( |
| 646 |
'<input type="text" id="%s" name="%s" value="%s"/><br><p class="description">%s</p>', |
| 647 |
esc_html( $args[ 'name' ] ), |
| 648 |
esc_html( $args[ 'name' ] ), |
| 649 |
esc_html( get_option( $args[ 'name' ], isset( $args[ 'default' ] ) ? $args[ 'default' ] : '' ) ), |
| 650 |
esc_html( $args[ 'comments' ] ) |
| 651 |
); |
| 652 |
} // End field_text() |
| 653 |
|
| 654 |
|
| 655 |
/** |
| 656 |
* Custom callback function to print url field |
| 657 |
* |
| 658 |
* @param array $args |
| 659 |
* @return void |
| 660 |
*/ |
| 661 |
public function field_url( $args ) { |
| 662 |
printf( |
| 663 |
'<input type="url" id="%s" name="%s" value="%s"/><br><p class="description">%s</p>', |
| 664 |
esc_html( $args[ 'name' ] ), |
| 665 |
esc_html( $args[ 'name' ] ), |
| 666 |
esc_url( get_option( $args[ 'name' ], isset( $args[ 'default' ] ) ? $args[ 'default' ] : '' ) ), |
| 667 |
esc_html( $args[ 'comments' ] ) |
| 668 |
); |
| 669 |
} // End field_url() |
| 670 |
|
| 671 |
|
| 672 |
/** |
| 673 |
* Render a URL field with an optional test notification button |
| 674 |
* |
| 675 |
* @param array $args |
| 676 |
* @return void |
| 677 |
*/ |
| 678 |
public function field_url_with_test( $args ) { |
| 679 |
$value = esc_url( get_option( $args[ 'name' ], '' ) ); |
| 680 |
$has_value = !empty( $value ); |
| 681 |
?> |
| 682 |
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;"> |
| 683 |
<input type="url" id="<?php echo esc_attr( $args[ 'name' ] ); ?>" name="<?php echo esc_attr( $args[ 'name' ] ); ?>" value="<?php echo esc_attr( $value ); ?>"/> |
| 684 |
<button type="button" class="button button-secondary blnotifier-test-btn" data-type="<?php echo esc_attr( $args[ 'test_type' ] ); ?>" data-field="<?php echo esc_attr( $args[ 'name' ] ); ?>" <?php echo !$has_value ? 'disabled' : ''; ?>> |
| 685 |
<?php esc_html_e( 'Send Test', 'broken-link-notifier' ); ?> |
| 686 |
</button> |
| 687 |
</div> |
| 688 |
<p class="description"><?php echo esc_html( $args[ 'comments' ] ); ?></p> |
| 689 |
<?php |
| 690 |
} // End field_url_with_test() |
| 691 |
|
| 692 |
|
| 693 |
/** |
| 694 |
* Render the emails field with a test notification button |
| 695 |
* |
| 696 |
* @param array $args |
| 697 |
* @return void |
| 698 |
*/ |
| 699 |
public function field_emails_with_test( $args ) { |
| 700 |
$value = esc_attr( get_option( $args[ 'name' ], isset( $args[ 'default' ] ) ? $args[ 'default' ] : '' ) ); |
| 701 |
$has_value = !empty( $value ); |
| 702 |
?> |
| 703 |
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;"> |
| 704 |
<input type="text" id="<?php echo esc_attr( $args[ 'name' ] ); ?>" name="<?php echo esc_attr( $args[ 'name' ] ); ?>" value="<?php echo esc_attr( $value ); ?>" pattern="([a-zA-Z0-9+_.\-]+@[a-zA-Z0-9.\-]+.[a-zA-Z0-9]+)(\s*,\s*([a-zA-Z0-9+_.\-]+@[a-zA-Z0-9.\-]+.[a-zA-Z0-9]+))*"/> |
| 705 |
<button type="button" class="button button-secondary blnotifier-test-btn" data-type="email" data-field="<?php echo esc_attr( $args[ 'name' ] ); ?>" <?php echo !$has_value ? 'disabled' : ''; ?>> |
| 706 |
<?php esc_html_e( 'Send Test', 'broken-link-notifier' ); ?> |
| 707 |
</button> |
| 708 |
</div> |
| 709 |
<p class="description"><?php echo esc_html( $args[ 'comments' ] ); ?></p> |
| 710 |
<?php |
| 711 |
} // End field_emails_with_test() |
| 712 |
|
| 713 |
|
| 714 |
/** |
| 715 |
* Sanitize url |
| 716 |
* |
| 717 |
* @param string $value |
| 718 |
* @return string |
| 719 |
*/ |
| 720 |
public function sanitize_url( $value ) { |
| 721 |
return filter_var( $value, FILTER_SANITIZE_URL ); |
| 722 |
} // End sanitize_url() |
| 723 |
|
| 724 |
|
| 725 |
/** |
| 726 |
* Custom callback function to print checkbox field |
| 727 |
* |
| 728 |
* @param array $args |
| 729 |
* @return void |
| 730 |
*/ |
| 731 |
public function field_checkbox( $args ) { |
| 732 |
$has_updated_settings = get_option( 'blnotifier_has_updated_settings' ); |
| 733 |
if ( !$has_updated_settings ) { |
| 734 |
$value = isset( $args[ 'default' ] ) ? $args[ 'default' ] : false; |
| 735 |
} else { |
| 736 |
$value = $this->sanitize_checkbox( get_option( $args[ 'name' ] ) ); |
| 737 |
} |
| 738 |
printf( |
| 739 |
'<input type="checkbox" id="%s" name="%s" value="yes" %s/> <p class="description">%s</p>', |
| 740 |
esc_html( $args[ 'name' ] ), |
| 741 |
esc_html( $args[ 'name' ] ), |
| 742 |
esc_html( checked( 1, $value, false ) ), |
| 743 |
wp_kses( $args[ 'comments' ], [ 'code' => [] ] ) |
| 744 |
); |
| 745 |
} // End field_checkbox() |
| 746 |
|
| 747 |
|
| 748 |
/** |
| 749 |
* Sanitize checkbox |
| 750 |
* |
| 751 |
* @param int $value |
| 752 |
* @return boolean |
| 753 |
*/ |
| 754 |
public function sanitize_checkbox( $value ) { |
| 755 |
return filter_var( $value, FILTER_VALIDATE_BOOLEAN ); |
| 756 |
} // End sanitize_checkbox() |
| 757 |
|
| 758 |
|
| 759 |
/** |
| 760 |
* Custom callback function to print checkboxes field |
| 761 |
* |
| 762 |
* @param array $args |
| 763 |
* @return void |
| 764 |
*/ |
| 765 |
public function field_checkboxes( $args ) { |
| 766 |
$value = get_option( $args[ 'name' ] ); |
| 767 |
|
| 768 |
if ( get_option( 'blnotifier_has_updated_settings' ) ) { |
| 769 |
$value = ! empty( $value ) && is_array( $value ) ? array_keys( $value ) : []; |
| 770 |
} else { |
| 771 |
$value = isset( $args[ 'default' ] ) && is_array( $args[ 'default' ] ) ? $args[ 'default' ] : []; |
| 772 |
} |
| 773 |
|
| 774 |
if ( isset( $args[ 'options' ] ) && is_array( $args[ 'options' ] ) ) { |
| 775 |
foreach ( $args[ 'options' ] as $key => $label ) { |
| 776 |
$checked = in_array( $key, $value ) ? 'checked' : ''; |
| 777 |
printf( |
| 778 |
'<input type="checkbox" id="%s" name="%s[%s]" value="1" %s/> <label for="%s">%s</label><br>', |
| 779 |
esc_html( $args[ 'name' ] . '_' . $key ), |
| 780 |
esc_html( $args[ 'name' ] ), |
| 781 |
esc_attr( $key ), |
| 782 |
esc_html( $checked ), |
| 783 |
esc_html( $args[ 'name' ] . '_' . $key ), |
| 784 |
esc_html( $label ) |
| 785 |
); |
| 786 |
} |
| 787 |
} |
| 788 |
} // field_checkboxes() |
| 789 |
|
| 790 |
|
| 791 |
/** |
| 792 |
* Custom callback function to print status codes field |
| 793 |
* |
| 794 |
* @param array $args |
| 795 |
* @return void |
| 796 |
*/ |
| 797 |
public function field_status_codes( $args ) { |
| 798 |
$value = filter_var_array( get_option( $args[ 'name' ], [] ), FILTER_SANITIZE_SPECIAL_CHARS ); |
| 799 |
|
| 800 |
$HELPERS = new BLNOTIFIER_HELPERS; |
| 801 |
$broken = $HELPERS->get_bad_status_codes(); |
| 802 |
$warning = $HELPERS->get_warning_status_codes( true ); |
| 803 |
|
| 804 |
if ( empty( $value ) ) { |
| 805 |
$mark_zero_as_broken = filter_var( get_option( 'blnotifier_mark_code_zero_broken' ), FILTER_VALIDATE_BOOLEAN ); |
| 806 |
if ( $mark_zero_as_broken ) { |
| 807 |
$value[ 0 ] = 'broken'; |
| 808 |
} else { |
| 809 |
$value[ 0 ] = 'warning'; |
| 810 |
} |
| 811 |
|
| 812 |
foreach ( $broken as $code ) { |
| 813 |
$value[ $code ] = 'broken'; |
| 814 |
} |
| 815 |
|
| 816 |
foreach ( $warning as $code ) { |
| 817 |
if ( !in_array( $code, array_keys( $value ) ) ) { |
| 818 |
$value[ $code ] = 'warning'; |
| 819 |
} |
| 820 |
} |
| 821 |
} |
| 822 |
|
| 823 |
if ( isset( $args[ 'options' ] ) ) { |
| 824 |
echo '<style> |
| 825 |
.status-row { |
| 826 |
display: flex; |
| 827 |
justify-content: space-between; |
| 828 |
align-items: center; |
| 829 |
gap: 2rem; |
| 830 |
border-radius: 5px; |
| 831 |
border: 1px solid #ccc; |
| 832 |
padding: 10px; |
| 833 |
font-weight: bold; |
| 834 |
} |
| 835 |
.status-row.warning .type { |
| 836 |
background: yellow; |
| 837 |
color: black; |
| 838 |
} |
| 839 |
.status-row.broken .type { |
| 840 |
background: red; |
| 841 |
color: white; |
| 842 |
} |
| 843 |
.status-row.good .type { |
| 844 |
background: #008000; |
| 845 |
color: white; |
| 846 |
} |
| 847 |
.status-row .type { |
| 848 |
float: right; |
| 849 |
padding: 5px 10px; |
| 850 |
font-weight: bold; |
| 851 |
margin: 10px; |
| 852 |
width: 100px; |
| 853 |
text-align: center; |
| 854 |
text-transform: uppercase; |
| 855 |
box-shadow: 0 2px 4px 0 rgba(7, 36, 86, 0.075); |
| 856 |
border: 1px solid rgba(7, 36, 86, 0.075); |
| 857 |
border-radius: 10px; |
| 858 |
} |
| 859 |
.status-row .code-msg, |
| 860 |
.status-row .description { |
| 861 |
margin-bottom: 1rem; |
| 862 |
margin-left: 0 !important; |
| 863 |
} |
| 864 |
.status-row label { |
| 865 |
margin-right: 10px; |
| 866 |
} |
| 867 |
.status-container { |
| 868 |
margin-top: 1rem; |
| 869 |
display: none; /* Initially hidden */ |
| 870 |
} |
| 871 |
.toggle-link { |
| 872 |
cursor: pointer; |
| 873 |
color: #0073aa; |
| 874 |
text-decoration: underline; |
| 875 |
margin-bottom: 10px; |
| 876 |
display: inline-block; |
| 877 |
} |
| 878 |
</style>'; |
| 879 |
|
| 880 |
echo '<strong>Broken Status Codes:</strong> ' . esc_html( !empty( $broken ) ? implode( ', ', $broken ) : 'None' ).'<br>'; |
| 881 |
echo '<strong>Warning Status Codes:</strong> ' . esc_html( !empty( $warning ) ? implode( ', ', $warning ) : 'None' ).'<br><br><br>'; |
| 882 |
|
| 883 |
echo '<a class="toggle-link" data-target="status-container">View/Change Status Types</a>'; |
| 884 |
echo '<div class="status-container">'; |
| 885 |
|
| 886 |
foreach ( $args[ 'options' ] as $code => $c ) { |
| 887 |
$type = isset( $value[ $code ] ) ? $value[ $code ] : 'good'; |
| 888 |
$checked_good = $type === 'good' ? 'checked' : ''; |
| 889 |
$checked_warning = $type === 'warning' ? 'checked' : ''; |
| 890 |
$checked_broken = $type === 'broken' ? 'checked' : ''; |
| 891 |
$display_code = isset( $c[ 'official' ] ) && !$c[ 'official' ] ? $code : '<a href="https://http.dev/' . $code . '" target="_blank">' . $code . '</a>'; |
| 892 |
|
| 893 |
printf( |
| 894 |
'<div class="status-row ' . esc_attr( $type ) . '"> |
| 895 |
<div class="info-input"> |
| 896 |
<div class="code-msg"> |
| 897 |
<span class="code">%s</span> <span class="message">(%s)</span> |
| 898 |
</div> |
| 899 |
<div class="description">%s</div> |
| 900 |
<div class="selections"> |
| 901 |
<input type="radio" id="%s_good" name="%s[%s]" value="good" %s/> |
| 902 |
<label for="%s_good">Good</label> |
| 903 |
<input type="radio" id="%s_warning" name="%s[%s]" value="warning" %s/> |
| 904 |
<label for="%s_warning">Warning</label> |
| 905 |
<input type="radio" id="%s_broken" name="%s[%s]" value="broken" %s/> |
| 906 |
<label for="%s_broken">Broken</label> |
| 907 |
</div> |
| 908 |
</div> |
| 909 |
<div class="indicator"> |
| 910 |
<div class="type">%s</div> |
| 911 |
</div> |
| 912 |
</div><br><br>', |
| 913 |
wp_kses( $display_code, [ 'a' => [ 'href' => [], 'target' => [] ] ] ), |
| 914 |
esc_html( $c[ 'msg' ] ), |
| 915 |
wp_kses( $c[ 'desc' ], [ 'code' ] ), |
| 916 |
|
| 917 |
esc_html( $args[ 'name' ].'_'.$code ), |
| 918 |
esc_html( $args[ 'name' ] ), |
| 919 |
esc_attr( $code ), |
| 920 |
esc_html( $checked_good ), |
| 921 |
esc_html( $args[ 'name' ].'_'.$code ), |
| 922 |
|
| 923 |
esc_html( $args[ 'name' ].'_'.$code ), |
| 924 |
esc_html( $args[ 'name' ] ), |
| 925 |
esc_attr( $code ), |
| 926 |
esc_html( $checked_warning ), |
| 927 |
esc_html( $args[ 'name' ].'_'.$code ), |
| 928 |
|
| 929 |
esc_html( $args[ 'name' ].'_'.$code ), |
| 930 |
esc_html( $args[ 'name' ] ), |
| 931 |
esc_attr( $code ), |
| 932 |
esc_html( $checked_broken ), |
| 933 |
esc_html( $args[ 'name' ].'_'.$code ), |
| 934 |
|
| 935 |
esc_attr( strtoupper( $type ) ) |
| 936 |
); |
| 937 |
} |
| 938 |
|
| 939 |
echo '</div>'; |
| 940 |
} |
| 941 |
} // End field_status_codes() |
| 942 |
|
| 943 |
|
| 944 |
/** |
| 945 |
* Sanitize checkboxes |
| 946 |
* |
| 947 |
* @param array $value |
| 948 |
* @return boolean |
| 949 |
*/ |
| 950 |
public function sanitize_checkboxes( $value ) { |
| 951 |
if ( !is_null( $value ) ) { |
| 952 |
return filter_var_array( $value, FILTER_VALIDATE_BOOLEAN ); |
| 953 |
} else { |
| 954 |
return []; |
| 955 |
} |
| 956 |
} // End sanitize_checkboxes() |
| 957 |
|
| 958 |
|
| 959 |
/** |
| 960 |
* Sanitize boolean |
| 961 |
* |
| 962 |
* @param mixed $input |
| 963 |
* @return boolean |
| 964 |
*/ |
| 965 |
public function sanitize_boolean( $input ) { |
| 966 |
return (bool) $input; |
| 967 |
} // End sanitize_boolean() |
| 968 |
|
| 969 |
|
| 970 |
/** |
| 971 |
* Get post type choices |
| 972 |
* |
| 973 |
* @return array |
| 974 |
*/ |
| 975 |
public function get_post_type_choices() { |
| 976 |
$HELPERS = new BLNOTIFIER_HELPERS; |
| 977 |
$results = []; |
| 978 |
|
| 979 |
$post_types = $HELPERS->get_post_types(); |
| 980 |
|
| 981 |
if ( ! is_array( $post_types ) || empty( $post_types ) ) { |
| 982 |
return $results; // return empty array if no post types found |
| 983 |
} |
| 984 |
|
| 985 |
foreach ( $post_types as $post_type ) { |
| 986 |
$post_type_name = $HELPERS->get_post_type_name( $post_type ); |
| 987 |
if ( $post_type_name === null ) { |
| 988 |
$post_type_name = $post_type; // fallback to post type slug |
| 989 |
} |
| 990 |
$results[ $post_type ] = $post_type_name; |
| 991 |
} |
| 992 |
|
| 993 |
return $results; |
| 994 |
} // End get_post_type_choices() |
| 995 |
|
| 996 |
|
| 997 |
/** |
| 998 |
* Get editable roles choices |
| 999 |
* |
| 1000 |
* @return array |
| 1001 |
*/ |
| 1002 |
public function get_editable_roles_choices() { |
| 1003 |
global $wp_roles; |
| 1004 |
$results = []; |
| 1005 |
$roles = $wp_roles->get_names(); |
| 1006 |
foreach ( $roles as $role_slug => $role_name ) { |
| 1007 |
if ( $role_slug == 'administrator' ) { |
| 1008 |
continue; |
| 1009 |
} |
| 1010 |
$results[ $role_slug ] = $role_name; |
| 1011 |
} |
| 1012 |
return $results; |
| 1013 |
} // End get_editable_roles_choices() |
| 1014 |
|
| 1015 |
|
| 1016 |
/** |
| 1017 |
* Custom callback function to print multiple emails field |
| 1018 |
* |
| 1019 |
* @param array $args |
| 1020 |
* @return void |
| 1021 |
*/ |
| 1022 |
public function field_emails( $args ) { |
| 1023 |
printf( |
| 1024 |
'<input type="text" id="%s" name="%s" value="%s" pattern="%s"/><br><p class="description">%s</p>', |
| 1025 |
esc_html( $args[ 'name' ] ), |
| 1026 |
esc_html( $args[ 'name' ] ), |
| 1027 |
esc_html( get_option( $args[ 'name' ], isset( $args[ 'default' ] ) ? $args[ 'default' ] : '' ) ), |
| 1028 |
'([a-zA-Z0-9+_.\-]+@[a-zA-Z0-9.\-]+.[a-zA-Z0-9]+)(\s*,\s*([a-zA-Z0-9+_.\-]+@[a-zA-Z0-9.\-]+.[a-zA-Z0-9]+))*', |
| 1029 |
esc_html( $args[ 'comments' ] ) |
| 1030 |
); |
| 1031 |
} // field_text() |
| 1032 |
|
| 1033 |
|
| 1034 |
/** |
| 1035 |
* Custom callback function to print number field |
| 1036 |
* |
| 1037 |
* @param array $args |
| 1038 |
* @return void |
| 1039 |
*/ |
| 1040 |
public function field_number( $args ) { |
| 1041 |
printf( |
| 1042 |
'<input type="number" id="%s" name="%s" value="%d" min="%d" required/><br><p class="description">%s</p>', |
| 1043 |
esc_html( $args[ 'name' ] ), |
| 1044 |
esc_html( $args[ 'name' ] ), |
| 1045 |
esc_attr( get_option( $args[ 'name' ], isset( $args[ 'default' ] ) ? $args[ 'default' ] : '' ) ), |
| 1046 |
esc_attr( $args[ 'min' ] ), |
| 1047 |
esc_html( $args[ 'comments' ] ) |
| 1048 |
); |
| 1049 |
} // End field_number() |
| 1050 |
|
| 1051 |
|
| 1052 |
/** |
| 1053 |
* Custom callback function to print textarea field |
| 1054 |
* |
| 1055 |
* @param array $args |
| 1056 |
* @return void |
| 1057 |
*/ |
| 1058 |
public function field_textarea( $args ) { |
| 1059 |
printf( |
| 1060 |
'<textarea class="textarea" id="%s" name="%s"/>%s</textarea><br><p class="description">%s</p>', |
| 1061 |
esc_html( $args[ 'name' ] ), |
| 1062 |
esc_html( $args[ 'name' ] ), |
| 1063 |
esc_html( get_option( $args[ 'name' ], '' ) ), |
| 1064 |
esc_html( $args[ 'comments' ] ) |
| 1065 |
); |
| 1066 |
} // field_text() |
| 1067 |
|
| 1068 |
|
| 1069 |
/** |
| 1070 |
* API key field |
| 1071 |
* |
| 1072 |
* @param array $args |
| 1073 |
* @return void |
| 1074 |
*/ |
| 1075 |
public function field_api_key( $args ) { |
| 1076 |
$key = get_option( $args[ 'name' ], '' ); |
| 1077 |
$display = $key ? 'inline-block' : 'none'; |
| 1078 |
?> |
| 1079 |
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;"> |
| 1080 |
<code id="blnotifier-api-key-display" style="display:<?php echo esc_attr( $display ); ?>;padding:8px 12px;background:#f0f0f1;border:1px solid #c3c4c7;border-radius:3px;font-size:13px;letter-spacing:0.5px;word-break:break-all;"><?php echo esc_html( $key ); ?></code> |
| 1081 |
<input type="hidden" id="<?php echo esc_attr( $args[ 'name' ] ); ?>" name="<?php echo esc_attr( $args[ 'name' ] ); ?>" value="<?php echo esc_attr( $key ); ?>"> |
| 1082 |
<button type="button" id="blnotifier-generate-key" class="button button-secondary">Generate New API Key</button> |
| 1083 |
<button type="button" id="blnotifier-copy-key" class="button button-secondary" style="display:<?php echo esc_attr( $display ); ?>;">Copy</button> |
| 1084 |
</div> |
| 1085 |
<p class="description">Send as a header <code>X-API-Key: your_key</code> or as a query param <code>?api_key=your_key</code>. Save Settings to persist a newly generated key.</p> |
| 1086 |
<?php |
| 1087 |
} // End field_api_key() |
| 1088 |
|
| 1089 |
|
| 1090 |
/** |
| 1091 |
* Get the full plugin page path |
| 1092 |
* |
| 1093 |
* @param string $tab |
| 1094 |
* @return string |
| 1095 |
*/ |
| 1096 |
public function get_plugin_page( $tab = 'settings' ) { |
| 1097 |
if ( $tab == 'omit-links' || $tab == 'omit-pages' ) { |
| 1098 |
return admin_url( 'edit-tags.php?taxonomy='.$tab ); |
| 1099 |
} else { |
| 1100 |
return admin_url( 'admin.php?page='.BLNOTIFIER_TEXTDOMAIN ).'&tab='.sanitize_key( $tab ); |
| 1101 |
} |
| 1102 |
} // End get_plugin_page() |
| 1103 |
|
| 1104 |
|
| 1105 |
/** |
| 1106 |
* Get the full plugin short path |
| 1107 |
* |
| 1108 |
* @param string $tab |
| 1109 |
* @return string |
| 1110 |
*/ |
| 1111 |
public function get_plugin_page_short_path( $tab = 'settings' ) { |
| 1112 |
if ( !is_null( $tab ) ) { |
| 1113 |
$add_tab = '&tab='.sanitize_key( $tab ); |
| 1114 |
} else { |
| 1115 |
$add_tab = ''; |
| 1116 |
} |
| 1117 |
return BLNOTIFIER_TEXTDOMAIN.$add_tab; |
| 1118 |
} // End get_plugin_page_short_path() |
| 1119 |
|
| 1120 |
|
| 1121 |
/** |
| 1122 |
* Enqueue script |
| 1123 |
* |
| 1124 |
* @param string $screen |
| 1125 |
* @return void |
| 1126 |
*/ |
| 1127 |
public function enqueue_scripts( $screen ) { |
| 1128 |
$options_page = 'toplevel_page_'.BLNOTIFIER_TEXTDOMAIN; |
| 1129 |
$tab = (new BLNOTIFIER_HELPERS)->get_tab(); |
| 1130 |
if ( ( $screen == $options_page && $tab == 'settings' ) ) { |
| 1131 |
$handle = 'blnotifier_settings_script'; |
| 1132 |
wp_register_script( $handle, BLNOTIFIER_PLUGIN_JS_PATH.'settings.js', [ 'jquery' ], BLNOTIFIER_SCRIPT_VERSION, true ); |
| 1133 |
wp_localize_script( $handle, 'blnotifier_settings', [ |
| 1134 |
'api_key' => get_option( 'blnotifier_api_key', '' ), |
| 1135 |
'nonce' => wp_create_nonce( 'blnotifier_test_notification' ), |
| 1136 |
'ajaxurl' => admin_url( 'admin-ajax.php' ), |
| 1137 |
] ); |
| 1138 |
wp_enqueue_script( $handle ); |
| 1139 |
wp_enqueue_script( 'jquery' ); |
| 1140 |
} |
| 1141 |
} // End enqueue_scripts() |
| 1142 |
|
| 1143 |
|
| 1144 |
/** |
| 1145 |
* AJAX handler for test notifications |
| 1146 |
* |
| 1147 |
* @return void |
| 1148 |
*/ |
| 1149 |
public function ajax_test_notification() { |
| 1150 |
if ( !isset( $_REQUEST[ 'nonce' ] ) || !wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST[ 'nonce' ] ) ), 'blnotifier_test_notification' ) ) { |
| 1151 |
wp_send_json_error( [ 'msg' => __( 'Invalid nonce.', 'broken-link-notifier' ) ] ); |
| 1152 |
} |
| 1153 |
|
| 1154 |
if ( !current_user_can( 'manage_options' ) ) { |
| 1155 |
wp_send_json_error( [ 'msg' => __( 'Unauthorized.', 'broken-link-notifier' ) ] ); |
| 1156 |
} |
| 1157 |
|
| 1158 |
$type = isset( $_REQUEST[ 'type' ] ) ? sanitize_key( $_REQUEST[ 'type' ] ) : ''; |
| 1159 |
$value = isset( $_REQUEST[ 'value' ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ 'value' ] ) ) : ''; |
| 1160 |
|
| 1161 |
if ( !$type || !$value ) { |
| 1162 |
wp_send_json_error( [ 'msg' => __( 'Missing data.', 'broken-link-notifier' ) ] ); |
| 1163 |
} |
| 1164 |
|
| 1165 |
$fake_link = 'https://example.com/broken-link'; |
| 1166 |
$fake_code = 404; |
| 1167 |
$fake_text = 'Not Found'; |
| 1168 |
$fake_source = home_url( '/sample-page/' ); |
| 1169 |
|
| 1170 |
if ( $type === 'email' ) { |
| 1171 |
$emails = array_map( 'trim', explode( ',', $value ) ); |
| 1172 |
$headers = [ |
| 1173 |
'From: ' . BLNOTIFIER_NAME . ' <' . get_bloginfo( 'admin_email' ) . '>', |
| 1174 |
'Content-Type: text/html; charset=UTF-8', |
| 1175 |
]; |
| 1176 |
$subject = 'Test: Broken Links Found'; |
| 1177 |
$message = 'This is a test notification from ' . BLNOTIFIER_NAME . '.<br><br>'; |
| 1178 |
$message .= 'CONTENT:<br><br>'; |
| 1179 |
$message .= 'URL: ' . $fake_link . '<br>Status Code: ' . $fake_code . ' - ' . $fake_text; |
| 1180 |
$message .= '<br><br><hr><br>' . get_bloginfo( 'name' ) . '<br><em>' . BLNOTIFIER_NAME . ' Plugin</em>'; |
| 1181 |
|
| 1182 |
if ( wp_mail( $emails, $subject, $message, $headers ) ) { |
| 1183 |
wp_send_json_success(); |
| 1184 |
} else { |
| 1185 |
wp_send_json_error( [ 'msg' => __( 'Email could not be sent.', 'broken-link-notifier' ) ] ); |
| 1186 |
} |
| 1187 |
|
| 1188 |
} elseif ( $type === 'discord' ) { |
| 1189 |
$DISCORD = new BLNOTIFIER_DISCORD; |
| 1190 |
$args = [ |
| 1191 |
'msg' => '', |
| 1192 |
'embed' => true, |
| 1193 |
'author_name' => 'Source: ' . $fake_source, |
| 1194 |
'author_url' => $fake_source, |
| 1195 |
'title' => get_bloginfo( 'name' ), |
| 1196 |
'title_url' => home_url(), |
| 1197 |
'desc' => '-------------------', |
| 1198 |
'img_url' => '', |
| 1199 |
'thumbnail_url' => '', |
| 1200 |
'disable_footer' => false, |
| 1201 |
'bot_avatar_url' => BLNOTIFIER_PLUGIN_IMG_PATH . 'logo-teal.png', |
| 1202 |
'bot_name' => BLNOTIFIER_NAME, |
| 1203 |
'fields' => [ |
| 1204 |
[ |
| 1205 |
'name' => 'Broken Link:', |
| 1206 |
'value' => $fake_link . "\nStatus Code: " . $fake_code . ' - ' . $fake_text, |
| 1207 |
'inline' => false, |
| 1208 |
], |
| 1209 |
], |
| 1210 |
]; |
| 1211 |
$result = $DISCORD->send( $value, $args ); |
| 1212 |
$result ? wp_send_json_success() : wp_send_json_error( [ 'msg' => __( 'Could not send to Discord.', 'broken-link-notifier' ) ] ); |
| 1213 |
|
| 1214 |
} elseif ( $type === 'msteams' ) { |
| 1215 |
$MSTEAMS = new BLNOTIFIER_MSTEAMS; |
| 1216 |
$args = [ |
| 1217 |
'site_name' => get_bloginfo( 'name' ), |
| 1218 |
'title' => 'Broken Links Found', |
| 1219 |
'msg' => 'The following broken links were found:', |
| 1220 |
'img_url' => '', |
| 1221 |
'source_url' => $fake_source, |
| 1222 |
'facts' => [ |
| 1223 |
[ |
| 1224 |
'name' => 'Broken Link:', |
| 1225 |
'value' => '[' . $fake_link . '](' . $fake_link . ') _Status Code: **' . $fake_code . '** - ' . $fake_text . '_', |
| 1226 |
], |
| 1227 |
], |
| 1228 |
]; |
| 1229 |
$result = $MSTEAMS->send( $value, $args ); |
| 1230 |
$result ? wp_send_json_success() : wp_send_json_error( [ 'msg' => __( 'Could not send to Microsoft Teams.', 'broken-link-notifier' ) ] ); |
| 1231 |
|
| 1232 |
} elseif ( $type === 'slack' ) { |
| 1233 |
$SLACK = new BLNOTIFIER_SLACK; |
| 1234 |
$args = [ |
| 1235 |
'title' => 'Broken Links Found', |
| 1236 |
'source' => $fake_source, |
| 1237 |
'fields' => [ |
| 1238 |
[ |
| 1239 |
'link' => $fake_link, |
| 1240 |
'code' => $fake_code, |
| 1241 |
'text' => $fake_text, |
| 1242 |
], |
| 1243 |
], |
| 1244 |
]; |
| 1245 |
$result = $SLACK->send( $value, $args ); |
| 1246 |
$result ? wp_send_json_success() : wp_send_json_error( [ 'msg' => __( 'Could not send to Slack.', 'broken-link-notifier' ) ] ); |
| 1247 |
|
| 1248 |
} else { |
| 1249 |
wp_send_json_error( [ 'msg' => __( 'Unknown notification type.', 'broken-link-notifier' ) ] ); |
| 1250 |
} |
| 1251 |
} // End ajax_test_notification() |
| 1252 |
|
| 1253 |
} |