1fa60b0a by Jeff Balicki

broken-link-checker

Signed-off-by: Jeff <jeff@gotenzing.com>
1 parent 54b8c0ce
Showing 327 changed files with 5103 additions and 0 deletions
<?php
/**
* Plugin action links.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Action_Links\Plugin
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Action_Links\Plugin;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
use WPMUDEV_BLC\Core\Utils\Utilities;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Action_Links\Plugin
*/
class Controller extends Base {
public function init() {
Settings::instance()->init();
$plugin_file = plugin_basename( WPMUDEV_BLC_PLUGIN_FILE );
add_filter( "plugin_action_links_{$plugin_file}", array( $this, 'action_links' ), 10, 4 );
add_filter( "network_admin_plugin_action_links_{$plugin_file}", array( $this, 'action_links' ), 10, 4 );
}
/**
* Sets the plugin action links in plugins page.
*
* @param array $actions
* @param string $plugin_file
* @param array $plugin_data
* @param string $context
*
* @return array
*/
public function action_links( $actions = array(), $plugin_file = '', $plugin_data = null, $context = '' ) {
$new_actions = array();
if ( ! is_array( $actions ) ) {
$actions = array();
}
if ( boolval( Settings::instance()->get( 'use_legacy_blc_version' ) ) ) {
$new_actions = $this->legacy_action_links();
} else {
$new_actions = $this->get_action_links();
}
return apply_filters(
'wpmudev_blc_plugin_action_links',
wp_parse_args( $actions, $new_actions ),
$new_actions,
$actions,
$plugin_file,
$plugin_data,
$context
);
}
/**
* Returns the plugin's action links.
*
* @return array
*/
public function get_action_links () {
$actions = array();
$dashboard_url = menu_page_url( 'blc_dash', false );
$dashboard_label = esc_html__( 'Dashboard', 'broken-link-checker' );
$docs_url = 'https://wpmudev.com/docs/wpmu-dev-plugins/broken-link-checker';
$docs_label = esc_html__( 'Docs', 'broken-link-checker' );
if ( is_multisite() && Utilities::is_network_admin() ) {
$admin_url = get_admin_url( get_main_site_id(), 'admin.php' );
$dashboard_url = add_query_arg(
array(
'page' => 'blc_dash',
),
$admin_url
);
}
$actions['dashboard'] = "<a href=\"{$dashboard_url}\">{$dashboard_label}</a>";
$actions['docs'] = "<a href=\"{$docs_url}\" target=\"_blank\">{$docs_label}</a>";
return $actions;
}
/**
* Returns the plugin action links in plugins page when legacy mode is active.
*
* @return array
*/
public function legacy_action_links() {
$actions = array();
$dashboard_url = menu_page_url( 'blc_dash', false );
$settings_url = menu_page_url( 'link-checker-settings', false );
$link_url = menu_page_url( 'view-broken-links', false );
$dashboard_label = esc_html__( 'Dashboard', 'broken-link-checker' );
$settings_label = esc_html__( 'Settings', 'broken-link-checker' );
$links_label = esc_html__( 'Broken links', 'broken-link-checker' );
if ( is_multisite() && Utilities::is_network_admin() ) {
$admin_url = get_admin_url( get_main_site_id(), 'admin.php' );
$dashboard_url = add_query_arg(
array(
'page' => 'blc_dash',
),
$admin_url
);
$settings_url = add_query_arg(
array(
'page' => 'link-checker-settings',
),
$admin_url
);
$link_url = add_query_arg(
array(
'page' => 'view-broken-links',
),
$admin_url
);
}
$actions['dashboard'] = "<a href=\"{$dashboard_url}\">{$dashboard_label}</a>";
$actions['settings'] = "<a href=\"{$settings_url}\">{$settings_label}</a>";
$actions['links'] = "<a href=\"{$link_url}\">{$links_label}</a>";
if ( is_multisite() && ! ( is_main_site() || Utilities::is_network_admin() ) ) {
unset( $actions['dashboard'] );
}
return $actions;
}
}
<?php
/**
* BLC admin modal for legacy screens.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Notices\Legacy
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Admin_Modals\Legacy;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Controllers\Admin_Modals;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use WPMUDEV_BLC\Core\Utils\Utilities;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
use WPMUDEV_BLC\Core\Traits\Enqueue;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
*/
class Controller extends Base {
/**
* Use the Enqueue Trait.
*
* @since 2.0.0
*/
use Enqueue;
/**
* The unique id that can be used by react.
*
* @var int $unique_id
*
* @since 2.0.0
*/
public static $unique_id = null;
/**
* The admin pages the notice will be visible at.
*
* @var array $admin_pages
*
* @since 2.0.0
*/
protected $admin_pages = array();
/**
* Init function.
*
* @since 2.0.0
*/
public function init() {
add_action( 'current_screen', array( $this, 'boot' ), 11 );
}
/**
* Boots modal parts.
*/
public function boot() {
if ( $this->can_boot() ) {
add_filter( 'admin_body_class', array( $this, 'admin_body_classes' ), 999 );
add_action( 'admin_footer', array( $this, 'output' ) );
$this->unique_id = Utilities::get_unique_id();
$this->prepare_props();
$this->prepare_scripts();
}
}
/**
* Checks if admin page actions/scripts should load in current screen.
*
* @return boolean Checks if admin page actions/scripts should load. Useful for enqueuing scripts.
* @since 2.0.0
*/
public function can_boot() {
/*
We use the Utilities::$value_provider array variable.
This variable can hold values that can be used from different classes which should help avoid checking
same conditions multiple times.
In this case we are using `boot_admin_legacy_pages` key which is also used in
WPMUDEV_BLC\App\Admin_Notices\Legacy\Controller class.
*/
if ( ! isset( Utilities::$value_provider['boot_admin_legacy_pages'] ) ) {
Utilities::$value_provider['boot_admin_legacy_pages'] = Utilities::is_admin_screen( $this->admin_pages );
}
$legacy_option = Settings::instance()->get( 'use_legacy_blc_version' );
return Utilities::$value_provider['boot_admin_legacy_pages'] && empty( $legacy_option );
}
/**
* Prepares the properties of the Admin Page.
*
* @return void
* @since 2.0.0
*/
public function prepare_props() {
/*
* Set the admin pages the notice will be visible at.
*/
$this->admin_pages = array(
'view-broken-links',
'link-checker-settings',
);
}
/**
* Admin Menu Callback.
*
* @return void The callback function of the Admin Menu Page.
* @since 2.0.0
*/
public function output() {
View::instance()->render( array( 'unique_id' => $this->unique_id ) );
}
/**
* Register css files for admin page.
*
* @return array
*/
public function set_admin_styles() {
return array(
'blc_sui' => array(
'src' => $this->styles_dir . 'shared-ui-' . BLC_SHARED_UI_VERSION_NUMBER . '.min.css',
'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
),
'blc_legacy_modal' => array(
'src' => $this->styles_dir . 'legacy-modal.min.css',
'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
),
);
}
/**
* Adds the blc legacy class to body classes for styling purposes.
*
* @param string $classes The body classes.
* @return string Returns the body classes.
* @since 2.0.0
*/
public function admin_body_classes( string $classes = '' ) {
return $classes . ' blc-show-legacy-popup';
}
/**
* Register scripts for the admin page.
*
* @return array Register scripts for the admin page.
* @since 2.0.0
*/
public function set_admin_scripts() {
$script_data = include WPMUDEV_BLC_DIR . 'assets/js/activation-popup/main.asset.php';
$dependencies = $script_data['dependencies'] ?? array(
'react',
'wp-element',
'wp-i18n',
'wp-is-shallow-equal',
'wp-polyfill',
);
$version = $script_data['version'] ?? WPMUDEV_BLC_SCIPTS_VERSION;
return array(
'blc_legacy_popup' => array(
'src' => $this->scripts_dir . 'legacy-modal/main.js',
'deps' => $dependencies,
'ver' => $version,
'in_footer' => true,
'localize' => array(
'blc_legacy_popup' => array(
'data' => array(
'rest_url' => esc_url_raw( rest_url() ),
'settings_endpoint' => '/wpmudev_blc/v1/settings',
'unique_id' => $this->unique_id,
'nonce' => wp_create_nonce( 'wp_rest' ),
'site_connected' => false,
'show_legacy_link' => boolval( Settings::instance()->get( 'use_legacy_blc_version' ) ),
'legacy_blc_url' => admin_url( 'admin.php?page=view-broken-links' ),
'blc_url' => admin_url( 'admin.php?page=blc_dash' ),
),
'labels' => array(
'error_messages' => array(
'general' => __( 'Something went wrong here.', 'broken-link-checker' ),
),
),
),
),
'translate' => true,
),
// END OF blc_activation_popup.
);
}
}
<?php
/**
* BLC Dashboard admin page view.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Notice\Legacy
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Admin_Modals\Legacy;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
/**
* Class View
*
* @package WPMUDEV_BLC\App\Admin_Notice\Legacy
*/
class View extends Base {
/**
* The unique id that can be used by react. Sent over from Controller.
*
* @var int $unique_id
*
* @since 2.0.0
*/
public static $unique_id = null;
/**
* Renders the output.
*
* @param array $params Optional parameters ideal to hold the uniqueu id generated in controller.
* @return void Renders the output.
* @since 2.0.0
*/
public function render( $params = array() ) {
self::$unique_id = isset( $params['unique_id'] ) ? $params['unique_id'] : null;
?>
<div class="sui-wrap wrap-blc wrap-blc-legacy-modal <?php echo 'wrap-' . esc_attr( self::$unique_id ); ?>">
<?php
$this->render_body();
?>
</div>
<?php
}
/**
* Renders view body.
*/
public function render_body() {
?>
<div id="<?php esc_attr_e( self::$unique_id ); ?>"></div>
<?php
}
}
<?php
/**
* Controller for admin notices.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
use WPMUDEV_BLC\Core\Traits\Enqueue;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use WPMUDEV_BLC\Core\Models\Option;
use WPMUDEV_BLC\Core\Activation;
use WPMUDEV_BLC\Core\Utils\Utilities;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation
*/
class Controller extends Base {
/**
* Use the Enqueue Trait.
*
* @since 2.0.0
*/
use Enqueue;
/**
* @var null|array The plugin settings
*/
private $settings = null;
/**
* Init Admin_Modal
*
* @since 2.0.0
*
* @return void
*/
public function init() {
$this->settings = new Settings();
add_action( 'load-plugins.php', array( $this, 'boot' ) );
}
/**
* Show the modal on plugins.php page if allowed.
*
* @since 2.0.0
*
* @return void
*/
public function boot() {
if ( ! $this->can_boot() ) {
return;
}
add_action( 'admin_footer', array( $this, 'show_footer_output' ) );
add_filter( 'admin_body_class', array( $this, 'admin_body_classes' ), 999 );
$this->unique_id = Utilities::get_unique_id();
$this->prepare_scripts();
}
/**
* Check if we can show model output in footer.
* Called only on plugins.php page.
*/
public function show_footer_output() {
$this->settings->set( wp_parse_args( $this->settings->get(), $this->settings->default ) );
$this->settings->set( array( 'activation_modal_shown' => true ) );
$this->settings->save();
$this->output();
}
/**
* Checks if modal can load.
*
* @since 2.0.0
*
* @return boolean Checks if modal can load. Useful for enqueuing scripts.
*/
protected function can_boot() {
return empty( $this->settings->get( 'activation_modal_shown' ) ) && ! Utilities::is_subsite();
}
/**
* Modal output.
*
* @since 1.0.0
*
* @return void The callback function of the Admin Menu Page.
*/
public function output() {
View::instance()->render(
array(
'unique_id' => $this->unique_id,
)
);
}
/**
* Register scripts for the admin page.
*
* @since 1.0.0
*
* @return array Register scripts for the admin page.
*/
public function set_admin_scripts() {
$script_data = include WPMUDEV_BLC_DIR . 'assets/js/activation-popup/main.asset.php';
$dependencies = $script_data['dependencies'] ?? array(
'react',
'wp-element',
'wp-i18n',
'wp-is-shallow-equal',
'wp-polyfill',
);
$version = $script_data['version'] ?? WPMUDEV_BLC_SCIPTS_VERSION;
$legacy_option_key = 'wsblc_options';
$legacy_option = new Option( array( 'name' => $legacy_option_key ) );
// Defines if the legacy link at the very bottom of the modal
$show_legacy_link = ! empty( $legacy_option->get() );
$legacy_installation_dt = $legacy_option->get( 'first_installation_timestamp' );
$installation_dt = intval( $this->settings->get( 'installation_timestamp' ) );
$legacy_pre_installed = ( ! empty( $legacy_installation_dt ) && 1 < round( abs( $legacy_installation_dt - $installation_dt ) / 60 ) );
return array(
'blc_activation_popup' => array(
'src' => $this->scripts_dir . 'activation-popup/main.js',
'deps' => $dependencies,
'ver' => $version,
'in_footer' => true,
'localize' => array(
'blc_activation_popup' => array(
'data' => array(
'rest_url' => esc_url_raw( rest_url() ),
'settings_endpoint' => '/wpmudev_blc/v1/settings',
'unique_id' => $this->unique_id,
'nonce' => wp_create_nonce( 'wp_rest' ),
'site_connected' => false,
'show_legacy_link' => $show_legacy_link,
'legacy_pre_installed' => $legacy_pre_installed,
'legacy_blc_url' => admin_url( 'admin.php?page=view-broken-links' ),
'blc_url' => admin_url( 'admin.php?page=blc_dash' ),
'dash_api_active' => ( Utilities::get_dashboard_api() instanceof \WPMUDEV_Dashboard_Api || Utilities::get_dashboard_api() instanceof WPMUDEV_Dashboard_Api ),
),
'labels' => array(
'error_messages' => array(
'general' => __( 'Something went wrong here.', 'broken-link-checker' ),
),
),
),
),
'translate' => true,
),
// END OF blc_activation_popup
);
}
/**
* Register css files for admin page.
*
* @return array
*/
public function set_admin_styles() {
return array(
'blc_sui' => array(
'src' => $this->styles_dir . 'shared-ui-' . BLC_SHARED_UI_VERSION_NUMBER . '.min.css',
'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
),
'blc_dashboard' => array(
'src' => $this->styles_dir . 'plugin-activation.min.css',
'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
),
);
}
/**
* Adds SUI admin body class. It will be used in all admin pages.
*
* @param $classes
*
* @return string
*/
public function admin_body_classes( $classes ) {
$sui_classes = explode( ' ', $classes );
$sui_classes[] = BLC_SHARED_UI_VERSION;
if ( apply_filters( 'wpmudev_branding_hide_branding', false ) ) {
$sui_classes[] = 'wpmudev-hide-branding';
}
return join( ' ', $sui_classes );
}
}
<?php
/**
* Plugin activation modal view.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
/**
* Class View
*
* @package WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation
*/
class View extends Base {
/**
* The unique id that can be used by react. Sent over from Controller.
*
* @var int $unique_id
*
* @since 2.0.0
*/
public static $unique_id = null;
/**
* Render the output.
*
* @since 2.0.0
*
* @return void Render the output.
*/
public function render( $params = array() ) {
self::$unique_id = isset( $params['unique_id'] ) ? $params['unique_id'] : null;
?>
<div class="sui-wrap wrap-blc wrap-blc-activation-modal <?php echo 'wrap-' . esc_attr( self::$unique_id ); ?>">
<?php
$this->render_body();
?>
</div>
<?php
}
public function render_body() {
?>
<div id="<?php esc_attr_e( self::$unique_id ); ?>"></div>
<?php
}
}
<?php
/**
* BLC admin notice for legacy screens
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Notices\Legacy
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Admin_Notices\Legacy;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Controllers\Admin_Notice;
use WPMUDEV_BLC\Core\Utils\Utilities;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
*/
class Controller extends Admin_Notice {
/**
* Admin Menu Callback.
*
* @return void The callback function of the Admin Menu Page.
* @since 1.0.0
*
*/
public function output() {
View::instance()->render();
}
/**
* Register css files for admin page.
*
* @return array
*/
public function set_admin_styles() {
return array(
'blc_legacy_notice' => [
'src' => $this->styles_dir . 'legacy-notice.min.css',
'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
],
);
}
/**
* Adds Page specific hooks. Extends $this->actions().
*
* @return void
* @since 2.0.0
*
*/
public function notice_hooks() {
if ( $this->can_boot() ) {
add_filter( 'admin_body_class', array( $this, 'admin_dash_page_classes' ), 999 );
}
}
/**
* Returns true if module component should load, else returns false.
*
* @return void
* @since 2.0.0
*
*/
public function can_boot() {
/**
* Until multisites are officially supported, BLC v2 menus are disabled in subsites.
* Legacy menus are loaded instead. Lagecy admin notification does not need to appear on subsites at this point.
*/
if ( Utilities::is_subsite() ) {
return;
}
/* We use the Utilities::$value_provider array variable.
This variable can hold values that can be used from different classes which should help avoid checking
same conditions multiple times.
In this case we are using `boot_admin_legacy_pages` key which is also used in
WPMUDEV_BLC\App\Admin_Modals\Legacy\Controller class.
*/
if ( ! isset( Utilities::$value_provider[ 'boot_admin_legacy_pages' ] ) ) {
Utilities::$value_provider['boot_admin_legacy_pages'] = Utilities::is_admin_screen( $this->admin_pages );
}
return Utilities::$value_provider[ 'boot_admin_legacy_pages' ];
}
public function admin_dash_page_classes( $classes ) {
return $classes . ' blc-show-legacy-notice';
}
/**
* Prepares the properties of the Admin Page.
*
* @return void
* @since 1.0.0
*
*/
public function prepare_props() {
/*
* Se the admin pages the notice will be visible at.
*/
$this->admin_pages = array(
'view-broken-links',
'link-checker-settings',
);
}
}
<?php
/**
* BLC Dashboard admin page view.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Notice\Legacy
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Admin_Notices\Legacy;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
/**
* Class View
*
* @package WPMUDEV_BLC\App\Admin_Notice\Legacy
*/
class View extends Base {
/**
* Renders the output.
*
* @return void Renders the output.
* @since 2.0.0
*
*/
public function render( $params = array() ) {
$this->render_body();
}
public function render_body() {
$dashborad_url = admin_url( 'admin.php?page=blc_dash' );
$message = sprintf(
__( 'We have completely rebuilt BLC with a new cloud-based engine. It’s now 20x faster, more accurate, and works perfectly with any site. Plus, no page limits, no ads, and it’s still 100%% free! Check out Broken Link Checker\'s <a href="%1$s">new dashboard</a>.', 'broken-link-checker' ),
$dashborad_url
);
printf( '
<div class="wrap wrap-blc-legacy-notice notice notice-info">
<table>
<tr>
<td><span class="sui-notice-icon blc-icon sui-md" aria-hidden="true"></span></td>
<td>%1$s</td>
</tr>
</table>
</div>
',
$message
);
}
}
<?php
/**
* BLC admin notice for main site's onboarding page
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Notices\Multisite
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Admin_Notices\Multisite;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Controllers\Admin_Notice;
use WPMUDEV_BLC\Core\Utils\Utilities;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use WPMUDEV_BLC\Core\Traits\Dashboard_API;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Admin_Notices\Multisite
*/
class Controller extends Admin_Notice {
/**
* Use the Dashboard_API Trait.
*
* @since 2.0.0
*/
use Dashboard_API;
public function init() {
parent::init();
add_action( 'wp_ajax_wpmudev_blc_multisite_notification_dismiss', array(
$this,
'dismiss_multisite_notification'
) );
}
/**
* Admin Menu Callback.
*
* @since 1.0.0
*
* @return void The callback function of the Admin Menu Page.
*/
public function output() {
View::instance()->render( array(
'site_connected' => (bool) self::site_connected(),
'use_legacy' => (bool) Settings::instance()->get( 'use_legacy_blc_version' )
) );
}
/**
* Register css files for admin page.
*
* @return array
*/
public function set_admin_styles() {
return array(
'blc_multisite_notice' => [
'src' => $this->styles_dir . 'multisite-notice.min.css',
'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
],
);
}
/**
* Adds Page specific hooks. Extends $this->actions().
*
* @since 2.0.0
*
* @return void
*/
public function notice_hooks() {
if ( $this->can_boot() ) {
add_filter( 'admin_body_class', array( $this, 'admin_dash_page_classes' ), 999 );
add_action( 'admin_enqueue_scripts', array( $this, 'inline_script' ) );
}
}
/**
* Adds the inline script that handles the notification dismiss. Loads it from View.
* @return void
*/
public function inline_script() {
wp_add_inline_script( 'blc_dashboard', View::instance()->render_inline_script() );
}
public function dismiss_multisite_notification() {
check_ajax_referer( 'wpmudev-blc-multisite-notification-dismiss-nonce', 'security' );
Settings::instance()->init();
Settings::instance()->set( array( 'show_multisite_notice' => false ) );
Settings::instance()->save();
}
/**
* Returns true if module component should load, else returns false.
*
* @since 2.0.0
*
* @return boolean
*/
public function can_boot() {
if ( is_multisite() && Settings::instance()->get( 'show_multisite_notice' ) && Utilities::is_admin_screen( 'blc_dash' ) ) {
return true;
}
return false;
}
public function admin_dash_page_classes( $classes ) {
return $classes . ' blc-show-multisite-notice';
}
/**
* Prepares the properties of the Admin Page.
*
* @since 1.0.0
*
* @return void
*/
public function prepare_props() {
}
}
<?php
/**
* BLC Dashboard admin page view.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Notice\Multisite
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Admin_Notices\Multisite;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
/**
* Class View
*
* @package WPMUDEV_BLC\App\Admin_Notice\Multisite
*/
class View extends Base {
/**
* Renders the output.
*
* @since 2.0.0
*
* @return void Renders the output.
*/
public function render( $params = array() ) {
$use_legacy = $params['use_legacy'] ?? false;
$site_connected = $params['site_connected'] ?? false;
echo '<div class="sui-wrap blc-multisite-notice-legacy">';
if ( $use_legacy || ! $site_connected ) {
$this->render_onboarding_notification();
} else {
$this->render_dashboard_notification();
}
echo '</div>';
}
public function render_onboarding_notification() {
$message = __( 'New BLC supports Multisite’s main site only and doesn’t support subsites. Subsites will continue using Legacy BLC', 'broken-link-checker' );
printf( '
<div class="wrap multisite-onboarding-notice notice notice-info is-dismissible">
<span class="notice-content">%1$s</span>
</div>
',
$message
);
}
public function render_dashboard_notification() {
$message = __( 'New BLC supports Multisite’s main site only and doesn’t support subsites. Subsites will continue using Legacy BLC', 'broken-link-checker' );
$close_message = __( 'Dismiss', 'broken-link-checker' );
printf( '
<div role="alert" id="settingsSaved" class="sui-notice multisite-dashboard-notice sui-active sui-notice-yellow" aria-live="assertive" style="display: block; text-align: left;">
<div class="sui-notice-content">
<div class="sui-notice-message">
<span class="sui-notice-icon sui-icon-info sui-md" aria-hidden="true"></span>
<p>%1$s</p>
</div>
<div class="sui-notice-actions">
<div class="sui-tooltip sui-tooltip-bottom" data-tooltip="%2$s">
<button class="sui-button-icon notice-dismiss"><i class="sui-icon-check" aria-hidden="true"></i><span class="sui-screen-reader-text">%2$s</span></button>
</div>
</div>
</div>
</div>
',
$message,
$close_message
);
}
public function render_inline_script() {
$ajax_nonce = wp_create_nonce( 'wpmudev-blc-multisite-notification-dismiss-nonce' );
ob_start();
?>
(function($){
$(document).ready(function() {
$( '.blc-show-multisite-notice .blc-multisite-notice-legacy .notice-dismiss' ).on( 'click', function() {
var data = {
action: 'wpmudev_blc_multisite_notification_dismiss',
security: '<?php echo $ajax_nonce; ?>',
dismiss: true
};
$.post(ajaxurl, data, function(response) {
$( '.blc-show-multisite-notice .blc-multisite-notice-legacy' ).hide( 300 );
});
} )
});
})(jQuery);
<?php
return ob_get_clean();
}
}
<?php
/**
* The Dashboard model
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Admin_Pages\Dashboard;
// Abort if called directly.
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use WPMUDEV_BLC\Core\Utils\Utilities;
defined( 'WPINC' ) || die;
/**
* Class Settings
*
* @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
*/
class Model {
/**
* Holds an array with all schedule data.
*
* @since 2.0.0
* @var array $settings
*
*/
private static $settings = array();
/**
* Holds an array with all schedule data.
*
* @since 2.0.0
* @var array $schedule
*
*/
private static $schedule = array();
/**
* Returns an item from settings.
* @return array|string|null
*/
public static function get_settings_item( string $item = '', $default = null ) {
return isset( self::get_settings()[ $item ] ) ? self::get_settings()[ $item ] : $default;
}
/**
* Returns DB Settings from options table.
* @return array
*/
public static function get_settings() {
if ( empty( self::$settings ) ) {
$settings = new Settings();
self::$settings = wp_parse_args( $settings::instance()->get(), $settings::instance()->default );
}
return self::$settings;
}
public static function use_legacy() {
return boolval( self::get_settings_item( 'use_legacy_blc_version' ) );
}
/**
* Returns true if scan is currently in progress else it returns false.
*
* @return bool
*/
public static function scan_in_progress() {
return self::get_settings_item( 'scan_status' ) === 'in_progress';
}
/**
* Returns scan results stored in DB Settings option.
*
* @return array
*/
public static function get_scan_results() {
$scan_results = array();
$scan_defaults = isset( Settings::instance()->default['scan_results'] ) ?
Settings::instance()->default['scan_results'] :
array();
$settings_scan_results = wp_parse_args( self::get_settings_item( 'scan_results' ), $scan_defaults );
array_walk_recursive(
$settings_scan_results,
function ( $item, $key ) use ( &$scan_results ) {
if ( ! \is_numeric( $item ) && ! $item ) {
$item = '-';
}
if ( in_array( $key, array( 'succeeded_urls', 'unique_urls', 'total_urls' ) ) ) {
//$item .= __( ' URLs', 'broken-link-checker' );
}
if ( in_array( $key, array( 'start_time', 'end_time' ) ) ) {
if ( in_array( $item, array( '-', 0 ) ) ) {
$item = '-';
} else {
$item = Utilities::timestamp_to_formatted_date( \intval( $item ), true );
}
}
if ( 'duration' === $key ) {
$item = Utilities::normalize_seconds_format( ( \floatval( $item ) ) );
}
$scan_results[ $key ] = $item;
}
);
return $scan_results;
}
public static function get_hours_list() {
$hour_list = array();
$hour_list_unsorted = Utilities::get_hour_list();
array_walk_recursive(
$hour_list_unsorted,
function ( $item, $key ) use ( &$hour_list ) {
$hour_list[] = array(
'key' => $key,
'value' => $item,
);
}
);
return $hour_list;
}
public static function get_current_user_data_formated() {
return self::format_recipient_user_data( get_current_user_id() );
}
/**
* @param $input
* @param string $input_type
* @param array $args
*
* @return array
*/
public static function format_recipient_user_data( $input = null, string $input_type = 'id', array $args = array()
) {
$key = null;
$display_name = null;
$roles = array();
$avatar = '';
$user = null;
$default = array(
'id' => 0,
'avatar' => '',
'name' => '',
'roles' => '',
'validated' => '',
);
if ( ! in_array( $input_type, array( 'id', 'email' ) ) ) {
return $default;
}
if ( 'email' === $input_type ) {
if ( ! is_email( $input ) ) {
return $default;
}
$user = get_user_by( 'email', $input );
} else if ( 'id' === $input_type ) {
$key = intval( $input );
$user = get_user_by( 'id', $key );
if ( is_wp_error( $user ) ) {
return $default;
}
}
if ( $user instanceof \WP_User ) {
$display_name = $user->display_name;
$roles = implode( ', ', Utilities::user_role_names( $user->ID ) );
$avatar = get_avatar_url( (int) $user->ID, array( 'size' => 30 ) );
} else {
$key = isset( $args['key'] ) ? $args['key'] : sha1( $input );
$roles = '';
$avatar = get_avatar_url( $input, array( 'size' => 30 ) );
$display_name = isset( $args['display_name'] ) ? $args['display_name'] : $input;
}
return wp_parse_args(
array(
'id' => $key,
'avatar' => $avatar,
'name' => $display_name,
'roles' => $roles,
),
$default
);
}
/**
* Return registered recipients from db settings option.
*
* @return array
*/
public static function get_recipients() {
$recipients_user_ids = self::get_schedule_item( 'recipients' );
if ( empty( $recipients_user_ids ) ) {
return array();
}
$recipients = array_map(
function ( $recipient_id ) {
return self::format_recipient_user_data( $recipient_id );
},
$recipients_user_ids
);
return $recipients;
}
/**
* Return registered email recipients from db settings option.
*
* @return array
*/
public static function get_email_recipients() {
$email_recipients = self::get_schedule_item( 'emailrecipients' );
$recipients = array_map(
function ( $recipient ) {
$recipient['avatar'] = get_avatar_url( $recipient['email'], array( 'size' => 30 ) );
return $recipient;
},
$email_recipients
);
return $recipients;
}
/**
* Provides an item from the schedule data from DB settings option.The returned value is not escaped.
*
* @return array|string
* @var string|null $item
*
*/
public static function get_schedule_item( $item = null ) {
if ( is_null( $item ) ) {
return array();
}
// Important note. The returned value is not escaped here.
return isset( self::get_schedule()[ $item ] ) ? self::get_schedule()[ $item ] : array();
}
/**
* Provides the schedule data from DB settings option
*
* @return array
*/
public static function get_schedule() {
if ( ! empty( self::$schedule ) ) {
return self::$schedule;
}
$schedule_defaults = isset( Settings::instance()->default['scan_results'] ) ?
Settings::instance()->default['schedule'] :
array();
self::$schedule = wp_parse_args( self::get_settings_item( 'schedule' ), $schedule_defaults );
self::$schedule['recipients'] = self::get_recipients();
self::$schedule['emailRecipients'] = self::get_email_recipients();
if ( empty( self::$schedule['days'] ) ) {
self::$schedule['days'] = array( 0 );
}
if ( empty( self::$schedule['monthdays'] ) ) {
self::$schedule['monthdays'] = array( 1 );
}
// Match system time format as set from General Settings,
if ( Utilities::get_time_format() ) {
self::$schedule['time'] = date( Utilities::get_time_format(), strtotime(self::$schedule['time']) );
}
return self::$schedule;
}
/**
* Lists roles.
*/
public static function list_user_roles() {
$recipients = self::get_schedule_item( 'recipients' );
$allowed_roles = array_keys( Utilities::roles_names( array( 'manage_options', 'edit_posts' ) ) );
$user_count = count_users();
$roles_list = array();
$rec_roles_list = array();
array_walk_recursive( $recipients, function ( $value, $key ) use ( &$rec_roles_list ) {
if ( 'roles' === $key ) {
$roles = explode( ',', $value );
if ( ! empty( $roles ) ) {
foreach ( $roles as $role ) {
$role = trim( $role );
$rec_roles_list[ $role ] = array_key_exists( $role, $rec_roles_list ) ?
intval( $rec_roles_list[ $role ] ) + 1 :
1;
}
}
}
}, $rec_roles_list );
if ( ! empty( $user_count['avail_roles'] ) ) {
foreach ( $user_count['avail_roles'] as $role_name => $user_count ) {
if ( ! in_array( $role_name, $allowed_roles ) ) {
continue;
}
if ( isset( $rec_roles_list[ ucfirst( $role_name ) ] ) ) {
$user_count = $user_count - intval( $rec_roles_list[ ucfirst( $role_name ) ] );
}
$roles_list[] = array(
'role_slug' => $role_name,
'role_name' => ucfirst( $role_name ),
'user_count' => $user_count >= 0 ? $user_count : 0,
);
}
}
return $roles_list;
}
/**
* Calculates remaining cooldown period if required.
*/
public static function get_cooldown_data() {
$scan_results = self::get_settings_item( 'scan_results' );
$end_time = ! empty( $scan_results['end_time'] ) ? intval( $scan_results['end_time'] ) : 0;
$date_utc = new \DateTime( "now", new \DateTimeZone( "UTC" ) );
$diff = round( ( $date_utc->getTimestamp() - $end_time ) / 60 );
$remaining = intval( 15 - $diff );
return array(
'cooling_down' => ( 0 < $end_time && 0 < $remaining ),
'remaining' => $remaining,
);
}
}
<?php
/**
* BLC Dashboard admin page view.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Pages\Settings
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Admin_Pages\Dashboard;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Views\Admin_Page;
/**
* Class View
*
* @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
*/
class View extends Admin_Page {
/**
* Render the output.
*
* @since 2.0.0
*
* @return void Render the output.
*/
public function render( $params = array() ) {
self::$unique_id = isset( $params['unique_id'] ) ? $params['unique_id'] : null;
self::$slug = isset( $params['slug'] ) ? $params['slug'] : null;
$site_connected = isset( $params['site_connected'] ) ? boolval( $params['site_connected'] ) : false;
?>
<div class="sui-wrap wrap-blc wrap-blc-dashboard-page <?php echo 'wrap-' . esc_attr( self::$slug ); ?>">
<?php
$this->render_body();
if ( $site_connected ){
$this->render_footer();
}
?>
</div>
<?php
}
public function render_body() {
?>
<div id="<?php esc_attr_e( self::$unique_id ); ?>"></div>
<?php
}
}
<?php
/**
* BLC Links_Submenu admin page
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Pages\Links_Submenu
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Admin_Pages\Links_Submenu;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Controllers\Admin_Page;
use WPMUDEV_BLC\Core\Traits\Escape;
use WPMUDEV_BLC\Core\Utils\Utilities;
use WPMUDEV_BLC\App\Admin_Pages\Dashboard\Controller as DashboardPage;
use WPMUDEV_BLC\Core\Traits\Dashboard_API;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
*/
class Controller extends Admin_Page {
/**
* Use the Escape and Dashboard_API Traits.
*
* @since 2.0.0
*/
use Escape, Dashboard_API;
/**
* The Admin Page's Menu Type.
*
* @since 2.0.0
* @var bool $is_submenu Set to true if page uses submenu.
*
*/
protected $is_submenu = true;
/**
* The Admin SubPage's Parent Slug.
*
* @since 2.0.0
* @var string $parent_slug The slug of the parent admin menu.
*
*/
protected $parent_slug = 'blc_dash';
/**
* Prepares the properties of the Admin Page.
*
* @since 2.0.0
* @return void
*/
public function prepare_props() {
add_action( 'admin_enqueue_scripts', array( $this, 'menu_tag_style' ) );
$this->is_submenu = true;
$this->unique_id = Utilities::get_unique_id();
$this->page_title = __( 'Broken Link Checker', 'broken-link-checker' );
$this->menu_title = sprintf(
__( 'BLC 2.0 %sBeta%s', 'broken-link-checker' ),
'<span class="awaiting-mod blc-beta-tag">',
'</span>'
);
/*
$this->menu_title = sprintf(
__( 'BLC 2.0 %s', 'broken-link-checker' ),
'<img class="blc-new-tag" src="' . WPMUDEV_BLC_ASSETS_URL . '/images/admin-menu-tag.png"/>'
);
*/
$this->capability = 'manage_options';
$this->menu_slug = 'blc_dash';
$this->position = 0;
}
/**
* Admin Menu Callback.
*
* @since 1.0.0
* @return void The callback function of the Admin Menu Page.
*/
public function output() {
//DashboardPage::instance()->output();
// Output handled by Dashboard page since pages share common slug.
}
public function menu_tag_style() {
$style_handler = 'blc_main_menu_tag';
$style_data = "
@import url(https://fonts.bunny.net/css?family=roboto:100,500);
#adminmenu .awaiting-mod.blc-new-tag,
#adminmenu .menu-counter.blc-new-tag,
#adminmenu .update-plugins.blc-new-tag,
#adminmenu li a.wp-has-current-submenu .update-plugins.blc-new-tag,
#adminmenu li.current a .awaiting-mod.blc-new-tag,
#adminmenu .awaiting-mod.blc-beta-tag,
#adminmenu .menu-counter.blc-beta-tag,
#adminmenu .update-plugins.blc-beta-tag,
#adminmenu li a.wp-has-current-submenu .update-plugins.blc-beta-tag,
#adminmenu li.current a .awaiting-mod.blc-beta-tag{
/*padding: 1.8px 8px;*/
padding-top: 1.2px;
position: relative;
background: #18BB4B;
border-radius: 12px;
font-style: normal;
font-weight: 400;
font-size: 9px;
line-height: 12px;
text-align: center;
letter-spacing: -0.1px;
color: #FFFFFF;
flex: none;
text-transform: uppercase;
font-family: 'Roboto', sans-serif;
display: inline-block;
height: 16px;
width: 38px;
text-align: center;
}
#adminmenu .awaiting-mod.blc-beta-tag,
#adminmenu .menu-counter.blc-beta-tag,
#adminmenu .update-plugins.blc-beta-tag,
#adminmenu li a.wp-has-current-submenu .update-plugins.blc-beta-tag,
#adminmenu li.current a .awaiting-mod.blc-beta-tag{
background: none;
border: 1px solid #FFFFFF;
}
";
$style_data = str_replace( "\n", "", $style_data );
$style_data = str_replace( "\t", "", $style_data );
wp_register_style( $style_handler, false );
wp_enqueue_style( $style_handler );
wp_add_inline_style(
$style_handler,
$style_data
);
}
}
<?php
/**
* Controller for Recipient activation emails.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Emails\Recipient_Activation
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Emails\Recipient_Activation;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Controllers\Mailer;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Emails\Recipient_Activation
*/
class Controller extends Mailer {
/**
* Module name. It might be useful in hooks.
*
* @var string
*/
public $email_module_name = 'recipient_activation';
/**
* WP Cron interval.
*
* @var boolean
*/
protected $cron_generate_interval = true;
/**
* Sets required vars. In parent class it is an abstract method.
*
* @return void
*/
protected function prepare_vars() {
$site_name = get_bloginfo( 'name' );
$site_email = get_bloginfo( 'admin_email' );
$this->email_headers = array(
'Content-Type: text/html; charset=UTF-8',
"From: {$site_name} <{$site_email}> \r\n",
);
$this->use_cron = false;
$this->email_subject = esc_html__( 'Broken links reports activation', 'broken-link-checker' );
}
/**
* Sets up body variables to be mapped in email body.
*
* @param array $email_args
*
* @return void
*/
public function set_mail_variables( array $email_args = array() ) {
$activation_link = $email_args['activation_link'] ?? '';
$cancellation_link = $email_args['cancellation_link'] ?? '';
$name = $email_args['name'] ?? '';
$email = $email_args['email'] ?? '';
$site_name = get_bloginfo( 'name' );
$this->body_variables =
apply_filters(
'wpmudev_blc_scan_report_email_vars',
array(
//HEADER
'{{HEADER_LOGO_SOURCE}}' => esc_html( Model::header_logo() ),
'{{TITLE}}' => esc_html( Model::header_title() ),
'{{SITENAME}}' => $site_name,
//BODY
'{{GREETING}}' => esc_html__( 'Hi {{USERNAME}}', 'broken-link-checker' ),
'{{USERNAME}}' => $name,
'{{EMAIL_ADDRESS}}' => $email,
'{{SITE_URL}}' => site_url(),
'{{CONFIRM_BTN_LABEL}}' => esc_html__( 'Confirm Subscription', 'broken-link-checker' ),
'{{ACTIVATION_LINK}}' => $activation_link,
// FOOTER PART
'{{FOOTER_TITLE}}' => esc_html__( 'Broken Link Checker', 'broken-link-checker' ),
'{{FOOTER_COMPANY}}' => 'WPMU DEV', //$site_name,
'{{FOOTER_CONTENT}}' => '',//View::instance()->get_footer_content(),
'{{FOOTER_LOGO_SOURCE}}' => Model::footer_logo(),
'{{LINK_TO_WPMUDEV_HOME}}' => esc_html__( 'Link to WPMU DEV Home page', 'broken-link-checker' ),
'{{FOOTER_SLOGAN}}' => esc_html__( 'Build A Better WordPress Business', 'broken-link-checker' ),
'{{COMPANY_ADDRESS}}' => esc_html__( 'INCSUB PO BOX 163, ALBERT PARK, VICTORIA.3206 AUSTRALIA', 'broken-link-checker' ),
'{{COMPANY_TITLE}}' => $site_name,
'{{UNSUBSCRIBE}}' => esc_html__( 'Unsubscribe', 'broken-link-checker' ),
'{{UNSUBSCRIBE_LINK}}' => $cancellation_link,
'{{SOCIAL_LINKS}}' => Model::get_social_links(),
),
$this
);
}
}
<?php
/**
* The Emails model for Recipient activation.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Emails\Recipient_Activation;
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Emails\Recipient_Activation;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Utils\Utilities;
/**
* Class Settings
*
* @package WPMUDEV_BLC\App\Emails\Recipient_Activation
*/
class Model {
/**
* Returns the header logo of the email.
*
* @return string
*/
public static function header_logo() {
return apply_filters(
'wpmudev_blc_scan_report_email_header_logo',
esc_url( WPMUDEV_BLC_ASSETS_URL . 'images/blc-logo-white-28x28.png' )
);
}
/**
* Returns the BLC Title to be used in the email header.
*
* @return string
*/
public static function header_title() {
return apply_filters(
'wpmudev_blc_scan_report_email_header_title',
__( 'Broken Link Notification', 'brocken-link-checker' )
);
}
/**
* Returns home url.
*
* @retun string
*/
public static function get_hub_home_url() {
return Utilities::hub_home_url();
}
/**
* Returns the header logo of the email.
*
* @return string
*/
public static function footer_logo() {
return apply_filters(
'wpmudev_blc_scan_report_email_header_logo',
esc_url( WPMUDEV_BLC_ASSETS_URL . 'images/footer-slogan.png' )
);
}
/**
* Returns social links info.
*
* @return array
*/
public static function social_links() {
return apply_filters(
'wpmudev_blc_scan_report_email_social_data',
array(
'facebook' => array(
'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/facebook-dark-7x14.png',
'url' => 'https://www.facebook.com/wpmudev',
),
'instagram' => array(
'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/instagram-dark-14x14.png',
'url' => 'https://www.instagram.com/wpmu_dev/',
),
'twitter' => array(
'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/twitter-dark-13x11.png',
'url' => 'https://twitter.com/wpmudev/',
),
)
);
}
/**
* Returns the social links for the email footer.
*/
public static function get_social_links() {
$social_data = self::social_links();
$output = '';
if ( ! empty( $social_data ) ) {
$output .= '<tr>';
$output .= '<td><span style="font-weight: 700;font-size: 13px;">' . esc_html__( 'Follow us', 'broken-link-checker' ) . '</span></td>';
foreach ( $social_data as $key => $data ) {
$url = $data['url'];
$icon = $data['icon'];
$output .= "<td>
<a href=\"{$url}\" target=\"_blank\">
<img height=\"13\" src=\"{$icon}\" style=\"border-radius:3px;display:block;max-height:13px;margin-left: 10px;\" />
</a>
</td>
";
}
$output .= '<tr>';
}
return "<table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"float:none;display:inline-table;\">{$output}</table>";
}
}
<mjml>
<mj-head>
<mj-html-attributes>
<mj-html-attribute class="easy-email" multiple-attributes="false" attribute-name="text-color" text-color="#000000"></mj-html-attribute>
<mj-html-attribute class="easy-email" multiple-attributes="false" attribute-name="font-family" font-family="-apple-system, BlinkMacSystemFont, &#x27;Segoe UI&#x27;, &#x27;Roboto&#x27;, &#x27;Oxygen&#x27;, &#x27;Ubuntu&#x27;, &#x27;Cantarell&#x27;, &#x27;Fira Sans&#x27;, &#x27;Droid Sans&#x27;,&#x27;Helvetica Neue&#x27;, sans-serif"></mj-html-attribute>
<mj-html-attribute class="easy-email" multiple-attributes="false" attribute-name="font-size" font-size="14px"></mj-html-attribute>
<mj-html-attribute class="easy-email" multiple-attributes="false" attribute-name="line-height" line-height="1.7"></mj-html-attribute>
<mj-html-attribute class="easy-email" multiple-attributes="false" attribute-name="font-weight" font-weight="400"></mj-html-attribute>
<mj-html-attribute class="easy-email" multiple-attributes="false" attribute-name="responsive" responsive="true"></mj-html-attribute>
</mj-html-attributes>
<mj-style >.blc-inline-text {
width:100%;
padding-left: 0px !important;
}
.blc-inline-text,
.blc-inline-text div,
.blc-inline-text span{
display:inline-block !important;
vertical-align:top !important;
}</mj-style>
<mj-breakpoint width="480px" />
<mj-font name="Roboto" href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;600;700&display=swap" />
<mj-attributes>
<mj-class name="blc-inline-block" display="inline-block" />
<mj-text font-size="14px" />
<mj-text line-height="1.7" />
<mj-text font-weight="400" />
<mj-all font-family="-apple-system, BlinkMacSystemFont, &#x27;Segoe UI&#x27;, &#x27;Roboto&#x27;, &#x27;Oxygen&#x27;, &#x27;Ubuntu&#x27;, &#x27;Cantarell&#x27;, &#x27;Fira Sans&#x27;, &#x27;Droid Sans&#x27;,&#x27;Helvetica Neue&#x27;, sans-serif" />
<mj-text font-size="14px" />
<mj-text color="#000000" />
<mj-text line-height="1.7" />
<mj-text font-weight="400" />
</mj-attributes>
</mj-head>
<mj-body background-color="#F4F4F4" width="600px" ><mj-section padding="20px 0px 20px 0px" border="none" direction="ltr" text-align="center" background-repeat="repeat" background-size="auto" background-position="top center" ></mj-section><mj-wrapper border="none" direction="ltr" text-align="center" padding="0px 0px 0px 0px" ><mj-hero background-color="#00BFA5" background-position="center center" mode="fixed-height" vertical-align="top" height="100px" background-height="100px" border-radius="24px 24px 0px 0px;" padding="0px 0px 0px 0px" ><mj-text align="center" color="#ffffff" font-family="Roboto, sans-serif" font-size="20px" line-height="30px" font-weight="700" padding="35px 0px 35px 0px" ><img height="30" width="30" src="{{HEADER_LOGO_SOURCE}}" style="border:0;outline:none;text-decoration:none;height:30px;width:30px;font-size:13px;">&nbsp;Broken Links Notification</mj-text></mj-hero></mj-wrapper><mj-section background-repeat="repeat" background-size="auto" background-position="top center" border="none" direction="ltr" text-align="center" background-color="#ffffff" padding="20px 0px 0px 0px" ><mj-column border="none" vertical-align="top" padding="0px 0px 0px 0px" ><mj-text align="left" color="#000000" font-family="Roboto, sans-serif" font-weight="500" font-size="13px" padding="0px 25px 0px 25px" ><h1 style="text-align:left; margin-top: 10px; margin-bottom: 10px">
<span style="font-weight: 700;
font-size: 25px;line-height: 30px;font-family:Roboto, sans-serif; font-weight:500; color:#1A1A1A;text-align:left;">
Subscribe to Broken Links notification</span></h1></mj-text><mj-text align="left" color="#000000" font-family="Roboto, sans-serif" font-size="13px" padding="0px 25px 0px 25px" ><p class="text-build-content" data-testid="JiH7lfw73to" style="margin: 10px 0; margin-top: 10px;">
<span style="color:#1A1A1A;font-family:Roboto, sans-serif;font-size:18px;line-height:28px;">
Hi {{USERNAME}},<br></span><span style="word-spacing: normal; color: rgb(26, 26, 26); font-size: 18px;"><br>An administrator from <a href="{{SITE_URL}}" target="_blank" style="color: inherit; text-decoration: underline;">{{SITENAME}}</a> has subscribed {{EMAIL_ADDRESS}} to </span><b style="word-spacing: normal; color: rgb(26, 26, 26); font-size: 18px;">Broken Links notification</b><span style="word-spacing: normal; color: rgb(26, 26, 26); font-size: 18px;">. To confirm your subscription, click on Confirm Subscription below.</span></p></mj-text></mj-column></mj-section><mj-section background-repeat="repeat" background-size="auto" background-position="top center" border="none" direction="ltr" text-align="center" background-color="#ffffff" padding="0px 0px 0px 0px" ><mj-column border="none" vertical-align="top" padding="0px 0px 0px 0px" ><mj-button align="center" background-color="#286EFA" color="#ffffff" font-weight="normal" border-radius="6px" line-height="120%" target="_blank" vertical-align="middle" border="none" text-align="center" href="{{ACTIVATION_LINK}}" font-family="Roboto, sans-serif" font-size="14px" text-decoration="none" text-transform="none" padding="30px 25px 20px 25px" inner-padding="10px 25px 10px 25px" >{{CONFIRM_BTN_LABEL}}</mj-button></mj-column></mj-section><mj-section background-repeat="repeat" background-size="auto" background-position="top center" border="none" direction="ltr" text-align="center" background-color="#ffffff" padding="0px 0px 0px 0px" ><mj-column border="none" vertical-align="top" padding="0px 0px 0px 0px" ><mj-text align="left" color="#000000" font-family="Roboto, sans-serif" font-size="10px" padding="5px 25px 0px 25px" ><p class="text-build-content" style="text-align: left; margin-top: 10px; font-family: Roboto; font-style: normal; font-size: 16px; line-height: 28px; color: rgb(26, 26, 26);" data-testid="lsPh_hKQM"><b>
Broken Link Checker
</b><br>The WPMU DEV Team.</p></mj-text></mj-column></mj-section><mj-section background-repeat="repeat" background-size="auto" background-position="top center" border="none" direction="ltr" text-align="center" background-color="#E7F1FB" padding="20px 0px 20px 0px" ><mj-column border="none" vertical-align="top" padding="0px 0px 0px 0px" ><mj-text align="center" css-class="blc-inline-text" padding="10px 25px 10px 25px" ><span>
<img height="30" width="30" src="{{FOOTER_LOGO_SOURCE}}" style="border:0;outline:none;text-decoration:none;height:30px;width:30px;font-size:13px;"></span></mj-text></mj-column></mj-section><mj-section background-repeat="repeat" background-size="auto" background-position="top center" border="none" direction="ltr" text-align="center" padding="20px 0px 20px 0px" ><mj-column border="none" vertical-align="top" padding="0px 0px 0px 0px" ><mj-text align="center" padding="10px 25px 10px 25px" >{{SOCIAL_LINKS}}</mj-text></mj-column></mj-section><mj-section background-repeat="repeat" background-size="auto" background-position="top center" border="none" direction="ltr" text-align="center" padding="0px 0px 0px 0px" ><mj-column border="none" vertical-align="top" padding="0px 0px 0px 0px" ><mj-text align="center" color="#505050" font-family="Roboto, sans-serif" font-size="10px" padding="0px 0px 0px 0px" ><p style="margin-top: 0px;">{{COMPANY_ADDRESS}}</p></mj-text><mj-text align="center" color="#505050" font-family="Roboto, sans-serif" font-size="10px" padding="0px 0px 0px 0px" ><p><a href="{{UNSUBSCRIBE_LINK}}" target="_blank" style="color: inherit; text-decoration: underline;">{{UNSUBSCRIBE}}</a></p></mj-text></mj-column></mj-section></mj-body></mjml >
\ No newline at end of file
<?php
/**
* Email temaplte for scan results.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package broken-link-checker
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
// phpcs:ignore
/*
* Variables:
* {{HEADER_LOGO_SOURCE}}
* {{TITLE}}
* {{USERNAME}}
* {{SITEURL}}
* {{COMPANY_TITLE}} // WPMU DEV
* {{BROKEN_LINKS_LIST}}
* {{FOOTER_TITLE}}
* {{FOOTER_COMPANY}}
* {{FOOTER_LOGO_SRC}}
* {{FOOTER_SLOGAN}}
* {{SOCIAL_LINKS}}
* {{COMPANY_ADDRESS}}
* {{UNSUBSCRIBE}}
*/
require_once 'email-body-markup.php';
<?php
/**
* Controller for scan reports emails.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Emails\Scan_Report
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Emails\Scan_Report;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Controllers\Mailer;
use WPMUDEV_BLC\App\Emails\Scan_Report\Model;
use WPMUDEV_BLC\Core\Utils\Utilities;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Emails\Scan_Report
*/
class Controller extends Mailer {
/**
* Module name. It might be useful in hooks.
*
* @var string
*/
public $email_module_name = 'scan_report';
/**
* WP Cron interval.
*
* @var boolean
*/
protected $cron_generate_interval = false;
/**
* Define if scheduled events ( wp pseudo cron ) can be used for emails.
*
* @var bool
*/
protected $use_cron = false;
/**
* Sends the BLC scan report to recipients.
* Called by WPMUDEV_BLC\App\Hub_Endpoints\Set_Data\Controller
* @return void
*/
public function send_email() {
$site_name = get_bloginfo( 'name' );
$recipients = Model::get_recipients();
$broken_links_count = Model::get_scan_results( 'broken_links' );
$recipients_collection = array();
$this->body_variables =
apply_filters(
'wpmudev_blc_scan_report_email_vars',
array(
'{{HEADER_FULL_TITLE}}' => View::instance()->get_full_header_title(),
'{{HEADER_LOGO_SOURCE}}' => esc_html( Model::header_logo() ),
'{{TITLE}}' => esc_html( Model::header_title() ),
'{{SITENAME}}' => $site_name,
'{{HEADING_START}}' => esc_html__( 'Broken Link Report for', 'broken-link-checker' ),
'{{GREETING}}' => esc_html__( 'Hi {{USERNAME}}', 'broken-link-checker' ),
'{{USERNAME}}' => '',
'{{CONTENT_MENTION_SUMMARY}}' => esc_html__( 'Here\'s your latest broken link summary generated on {{SCANDATE}}.', 'broken-link-checker' ),
'{{SITEURL}}' => site_url(),
'{{SCANDATE}}' => esc_html( Model::scan_date() ),
// SUMMARY PART
'{{SUMMARY_TITLE}}' => esc_html__( 'Summary', 'broken-link-checker' ),
'{{SUMMARY_INTRO}}' => esc_html__( 'Here are your latest broken link test results.', 'broken-link-checker' ),
'{{SUMMARY_ROW_ONE}}' => View::instance()->get_summary_row( '{{SUMMARY_BROKEN_LINKS_LBL}}',
$this->get_broken_links_count_markup() ),
'{{SUMMARY_BROKEN_LINKS_LBL}}' => esc_html__( 'Broken Links', 'broken-link-checker' ),
'{{BROKEN_LINKS_COUNT}}' => $broken_links_count,
'{{BROKEN_LINKS_COUNT_COLOR}}' => $broken_links_count > 0 ? '#FF6D6D' : '#1ABC9C',
'{{SUMMARY_ROW_TWO}}' => View::instance()->get_summary_row( '{{SUMMARY_TOTAL_URLS_LBL}}', '{{TOTAL_URLS_COUNT}}' ),
//'{{SUMMARY_SUCCESSFUL_URLS_LBL}}' => esc_html__( 'Successful URLs', 'broken-link-checker' ),
//'{{SUCCESSFUL_URLS_COUNT}}' => Model::get_scan_results( 'succeeded_urls' ),
'{{SUMMARY_TOTAL_URLS_LBL}}' => esc_html__( 'Total Links', 'broken-link-checker' ),
'{{TOTAL_URLS_COUNT}}' => Model::get_scan_results( 'total_urls' ),
'{{SUMMARY_ROW_THREE}}' => View::instance()->get_summary_row( '{{SUMMARY_UNIQUE_URLS_LBL}}', '{{UNIQUE_URLS_COUNT}}' ),
'{{SUMMARY_UNIQUE_URLS_LBL}}' => esc_html__( 'Unique URLs', 'broken-link-checker' ),
'{{UNIQUE_URLS_COUNT}}' => Model::get_scan_results( 'unique_urls' ),
// REPORT PART
'{{REPORT_PADDING}}' => $broken_links_count > 0 ? '25px' : '0',
'{{REPORT_LIST_PADDING}}' => $broken_links_count > 0 ? '8px' : '0',
'{{REPORT_BTN_PADDING}}' => $broken_links_count > 0 ? '20px' : '0',
'{{REPORT_TITLE}}' => $broken_links_count > 0 ? esc_html__( 'Broken link report', 'broken-link-checker' ) : '',
'{{REPORT_DESCRIPTION}}' => $broken_links_count > 0 ? esc_html__( 'The list below shows a maximum of 20 broken links. Click the View Full Report button to see the full list.', 'broken-link-checker' ) : '',
'{{REPORT_BTN_URL}}' => esc_url( Model::get_hub_home_url() ),
'{{REPORT_BTN_TITLE}}' => $broken_links_count > 0 ? esc_html__( 'View Full Report', 'broken-link-checker' ) : esc_html__( 'View Report', 'broken-link-checker' ),
'{{BROKEN_LINKS_LIST}}' => $broken_links_count > 0 ? View::instance()->broken_links_list_markup() : '',
// FOOTER PART
'{{FOOTER_TITLE}}' => esc_html__( 'Broken Link Checker', 'broken-link-checker' ),
'{{FOOTER_COMPANY}}' => 'WPMU DEV', //$site_name,
'{{FOOTER_CONTENT}}' => View::instance()->get_footer_content(),
'{{FOOTER_LOGO_SOURCE}}' => Model::footer_logo(),
'{{LINK_TO_WPMUDEV_HOME}}' => esc_html__( 'Link to WPMU DEV Home page', 'broken-link-checker' ),
'{{FOOTER_SLOGAN}}' => esc_html__( 'Build A Better WordPress Business', 'broken-link-checker' ),
'{{COMPANY_ADDRESS}}' => esc_html__( 'INCSUB PO BOX 163, ALBERT PARK, VICTORIA.3206 AUSTRALIA', 'broken-link-checker' ),
'{{COMPANY_TITLE}}' => $site_name,
'{{UNSUBSCRIBE}}' => esc_html__( 'Unsubscribe', 'broken-link-checker' ),
'{{UNSUBSCRIBE_LINK}}' => '',
'{{SOCIAL_LINKS}}' => View::instance()->get_social_links(),
),
$this
);
if ( ! empty( $recipients ) ) {
foreach ( $recipients as $recipient ) {
$this->body_variables['{{USERNAME}}'] = $recipient['name'] ?? '';
$this->body_variables['{{UNSUBSCRIBE_LINK}}'] = $recipient['unsubscribe_link'] ?? '';
$recipient_collection = array(
'name' => $recipient['name'] ?? '',
'email' => $recipient['email'] ?? '',
);
$recipient_collection = array_merge( $recipient_collection, $this->body_variables );
$recipients_collection[] = $recipient_collection;
}
$this->send_multiple(
$recipients_collection,
true
);
}
}
/**
* Returns the markup for the Broken Links count value.
*
* @return string
*/
protected function get_broken_links_count_markup() {
return "
<div style=\"font-family:Roboto;font-size:22px;font-weight:700;line-height:22px;text-align:left;color:{{BROKEN_LINKS_COUNT_COLOR}};\">
<div style=\"text-align: right;\">{{BROKEN_LINKS_COUNT}}</div>
</div>
";
}
/**
* Sets required vars. In parent class it is an abstract method.
*
* @return void
*/
protected function prepare_vars() {
$site_name = get_bloginfo( 'name' );
$site_email = get_bloginfo( 'admin_email' );
$this->email_headers = array(
'Content-Type: text/html; charset=UTF-8',
"From: {$site_name} <{$site_email}> \r\n",
);
$this->email_subject = esc_html__( 'Broken links reports', 'broken-link-checker' );
}
}
<?php
/**
* The Emails model for Scan report.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Emails\Scan_Report;
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Emails\Scan_Report;
// Abort if called directly.
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use WPMUDEV_BLC\App\Webhooks\Recipient_Activation\Controller as Activation_Webhook;
use WPMUDEV_BLC\Core\Utils\Utilities;
defined( 'WPINC' ) || die;
/**
* Class Settings
*
* @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
*/
class Model {
/**
* The scan results from DB.
*
* @var array
*/
private static $scan_results = array();
/**
* Returns the header logo of the email.
*
* @return string
*/
public static function header_logo() {
return apply_filters(
'wpmudev_blc_scan_report_email_header_logo',
esc_url( WPMUDEV_BLC_ASSETS_URL . 'images/blc-logo-white-28x28.png' )
);
}
/**
* Returns the BLC Title to be used in the email header.
*
* @return string
*/
public static function header_title() {
return apply_filters(
'wpmudev_blc_scan_report_email_header_title',
__( 'BLC Report', 'brocken-link-checker' )
);
}
/**
* Returns home url.
*
* @retun string
*/
public static function get_hub_home_url() {
return Utilities::hub_home_url();
}
/**
* Returns the header logo of the email.
*
* @return string
*/
public static function footer_logo() {
return apply_filters(
'wpmudev_blc_scan_report_email_header_logo',
esc_url( WPMUDEV_BLC_ASSETS_URL . 'images/wpmudev-logo-dark-30x30.png' )
);
}
/**
* Returns social links info.
*
* @return array
*/
public static function social_links() {
return apply_filters(
'wpmudev_blc_scan_report_email_social_data',
array(
'facebook' => array(
'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/facebook-dark-7x14.png',
'url' => 'https://www.facebook.com/wpmudev',
),
'instagram' => array(
'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/instagram-dark-14x14.png',
'url' => 'https://www.instagram.com/wpmu_dev/',
),
'twitter' => array(
'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/twitter-dark-13x11.png',
'url' => 'https://twitter.com/wpmudev/',
),
)
);
}
/**
* Returns scan date.
*/
public static function scan_date() {
$start_time = self::get_scan_results( 'start_time' );
if ( ! empty( $start_time ) ) {
$start_time = Utilities::microtime_to_date( intval( $start_time ), 'full_date', true );
}
return apply_filters(
'wpmudev_blc_scan_report_email_scan_date',
$start_time
);
}
/**
* Returns scan results.
*
* @param string $key Optional scan results key.
*/
public static function get_scan_results( string $key = '' ) {
if ( empty( self::$scan_results ) ) {
$scan_results = array();
$scan_defaults = Settings::instance()->default['scan_results'] ?? array();
self::$scan_results = wp_parse_args( Settings::instance()->get( 'scan_results' ), $scan_defaults );
}
if ( ! empty( $key ) ) {
return self::$scan_results[ $key ] ?? null;
}
return self::$scan_results;
}
/**
* Returns array recipients email address and names.
*/
public static function get_recipients() {
$user_recipients = array();
$email_recipients = Settings::instance()->get_scan_active_email_recipients();
$schedule = Settings::instance()->get( 'schedule' );
$registered_recipients_data = ! empty( $schedule[ 'registered_recipients_data' ] ) ? $schedule['registered_recipients_data'] : array();
if ( ! empty( $email_recipients ) ) {
array_walk(
$email_recipients,
function ( &$recipient ) {
unset( $recipient['confirmed'] );
$recipient['unsubscribe_link'] = self::unsubscribe_link( base64_encode( md5( $recipient['email'] ) . '_' . $recipient['key'] ) );
}
);
}
if ( ! empty( $registered_recipients_data ) ) {
array_walk(
$registered_recipients_data,
function ( $user_data, $user_id ) use ( &$user_recipients ) {
$user_recipients[] = array(
'name' => $user_data['name'],
'email' => $user_data['email'],
'key' => $user_data['key'],
'unsubscribe_link' => self::unsubscribe_link( base64_encode( md5( $user_data['email'] ) . '_' . $user_data['key'] ) ),
);
}
);
}
return array_merge( $user_recipients, $email_recipients );
}
private static function unsubscribe_link( string $token = '' ) {
return add_query_arg(
array(
'action' => 'cancel',
'activation_code' => sanitize_text_field( $token ),
),
Activation_Webhook::instance()->webhook_url()
);
}
}
<?php
/**
* View for scan reports emails. Holds specific parts for email template to use.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Emails\Scan_Report
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Emails\Scan_Report;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\App\Emails\Scan_Report\Model;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
//use WPMUDEV_BLC\Core\Utils\Utilities;
class View extends Base {
/**
* Provides the header full title including the image.
*
* @return string
*/
public function get_full_header_title() {
ob_start();
?>
<img height="28" width="28" src="{{HEADER_LOGO_SOURCE}}"
style="border:0;outline:none;text-decoration:none;height:28px;width:28px;font-size:13px;vertical-align:middle"
/>
{{TITLE}}
<?php
return ob_get_clean();
}
/**
* Gives a table with a single row and 2 columns even in small screens. Widths are specific.
*
* @param string $title
* @param string $value
*
* @return string
*/
public function get_summary_row( string $title = '', string $value = '' ) {
return "
<table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\"
style=\"width:100%;\">
<tbody>
<tr>
<td align=\"left\" style=\"width: 70% !important;max-width: 70%;text-align:left;\">
<div style=\"font-family:Roboto;font-size:15px;font-weight:500;line-height:22px;text-align:left;color:#000000;\">
{$title}
</div>
</td>
<td align=\"right\" style=\"width: 30% !important;max-width: 30%;text-align: right;\">
<div>
{$value}
</div>
</td>
</tr>
</tbody>
</table>
";
}
/**
* Gives the markup of broken links email part.
*
* @return string
*/
public function broken_links_list_markup() {
$broken_links_list = Model::get_scan_results( 'broken_links_list' );
$markup = '';
if ( empty( $broken_links_list ) ) {
return $markup;
}
foreach ( $broken_links_list as $broken_link ) {
if ( is_object( $broken_link ) ) {
$broken_link = (array) $broken_link;
}
if ( ! empty( $broken_link['is_ignored'] ) ) {
continue;
}
$origin_source = $broken_link['origins'][0] ?? false;
$origin_source_id = null;
if ( $origin_source ) {
$origin_source_id = intval( url_to_postid( $origin_source ) );
}
$origin_post_title = $origin_source_id ? get_the_title( $origin_source_id ) : __( 'Unknown', 'broken-link-checker' );
$origin_post_edit_url = get_edit_post_link( $origin_source_id );
// If get_edit_post_link keeps returning empty.
if ( empty( $origin_post_edit_url ) ) {
$post = get_post( $origin_source_id );
if ( $post instanceof \WP_Post ) {
$post_type_object = get_post_type_object( $post->post_type );
if ( ! $post_type_object ) {
continue;
}
$origin_post_edit_url = admin_url( sprintf( $post_type_object->_edit_link . "&action=edit", $post->ID ) );
} else {
continue;
}
}
ob_start();
?>
<tr style="border: 1px solid #F2F2F2;">
<td style="font-family: Roboto, sans-serif;font-weight: 500;padding: 20px; font-size: 12px;
line-height: 14px;color: #286EFA; vertical-align:top; text-align: left;">
<a href="<?php echo $broken_link['url']; ?>" style="color: #286EFA;">
<?php echo $broken_link['url']; ?>
</a>
</td>
<td style="font-family: Roboto, sans-serif;font-weight: 500;padding: 20px; font-size: 12px;
line-height: 14px;color: #1A1A1A; width:33%; vertical-align:top; text-align: left;">
<table>
<tr>
<td style="min-width: 50px;">
<span style="padding: 4px 8px;width: 36px;height: 22px;background: #FF6D6D;border-radius: 12px;font-weight: 500;font-size: 12px;line-height: 14px;color: #FFFFFF; margin:0 4px 0 0;">
<?php echo $broken_link['status_code']; ?>
</span>
</td>
<td style="min-width: 50px;">
<?php echo $broken_link['status_ref']; ?>
</td>
</tr>
</table>
</td>
<td style="font-family: Roboto, sans-serif;font-weight: 500;padding: 20px; font-size: 12px;line-height: 14px;color: #286EFA; width:33%; vertical-align:top; text-align: left;">
<a href="<?php echo $origin_post_edit_url; ?>" target="_blank" style="color: #286EFA;">
<?php echo $origin_post_title; ?>
</a>
</td>
</tr>
<?php
$markup .= ob_get_clean();
}
return "<div style=\"overflow-x: auto;\"><table cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" border=\"0\" style=\"color:#000000;
#font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:22px;table-layout:auto;
#width:100%;border:none;min-width:400px;\">
<tr style=\"align-items: flex-start;padding: 0px;width: 550px;height: 32px;\">
<th style=\"box-shadow: -1px 0px 0px 0px #f2f2f2; -webkit-box-shadow: -1px 0px 0px 0px #f2f2f2; -moz-box-shadow: -1px 0px 0px 0px #f2f2f2; background: #F2F2F2;font-family: Roboto, sans-serif;font-weight: 600;padding: 10px 20px;font-size: 12px;line-height: 14px;color: #1A1A1A; text-align:left;border-radius: 4px 0px 0px 0px;min-width: 80px;\">
" . esc_html__( 'Broken Links', 'broken-link-checker' ) . "
</th>
<th style=\"background: #F2F2F2;font-family: Roboto, sans-serif;font-weight: 600;padding: 10px 20px;font-size: 12px;line-height: 14px;color: #1A1A1A;text-align:left;\">
" . esc_html__( 'Status', 'broken-link-checker' ) . "
</th>
<th style=\"background: #F2F2F2;font-family: Roboto, sans-serif;font-weight: 600;padding: 10px 20px;font-size: 12px;line-height: 14px;color: #1A1A1A;text-align:left;border-top-right-radius:4px;min-width: 80px;\">
" . esc_html__( 'Source URL', 'broken-link-checker' ) . "
</th>
</tr>
{$markup}
</table></div>";
}
/**
* Return the footer content.
*
* @return string
*/
public function get_footer_content() {
$footer_slogan_img_url = WPMUDEV_BLC_ASSETS_URL . 'images/footer-slogan.png';
ob_start();
?>
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:1;text-align:center;
color:#000000; width: 100%;">
<table style="width:100%;">
<tbody>
<tr>
<td align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation"
style="border-collapse:collapse;border-spacing:0px">
<tbody>
<tr>
<td style="width:168px">
<a href="https://wpmudev.com" title="{{LINK_TO_WPMUDEV_HOME}}" target="_blank"
data-saferedirecturl="https://wpmudev.com">
<img height="auto" src="<?php echo $footer_slogan_img_url; ?>" style="border:0;
display:block;outline:none; text-decoration:none;height:auto;width:100%;font-size:13px"
width="168">
</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<?php
return ob_get_clean();
}
/**
* Returns the social links for the email footer.
*/
public function get_social_links() {
$social_data = Model::social_links();
$output = '';
if ( ! empty( $social_data ) ) {
$output .= '<tr>';
$output .= '<td><span style="font-weight: 700;font-size: 13px;">' . esc_html__( 'Follow us', 'broken-link-checker' ) . '</span></td>';
foreach ( $social_data as $key => $data ) {
$url = $data['url'];
$icon = $data['icon'];
$output .= "<td>
<a href=\"{$url}\" target=\"_blank\">
<img height=\"13\" src=\"{$icon}\" style=\"border-radius:3px;display:block;max-height:13px;margin-left: 10px;\" />
</a>
</td>
";
}
$output .= '<tr>';
}
return "<table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"float:none;display:inline-table;\">{$output}</table>";
}
}
<?php
/**
* Email temaplte for scan results.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package broken-link-checker
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
// phpcs:ignore
/*
* Variables:
* {{HEADER_LOGO_SOURCE}}
* {{TITLE}}
* {{SCANDATE}}
* {{USERNAME}}
* {{SITEURL}}
* {{BROKEN_LINKS_COUNT}}
* {{SUCCESSFUL_URLS_COUNT}}
* {{UNIQUE_URLS_COUNT}}
* {{FULL_REPORT_TITLE}}
* {{FULL_REPORT_URL}}
* {{COMPANY_TITLE}} // WPMU DEV
* {{BROKEN_LINKS_LIST}}
* {{FOOTER_TITLE}}
* {{FOOTER_COMPANY}}
* {{FOOTER_LOGO_SRC}}
* {{FOOTER_SLOGAN}}
* {{SOCIAL_LINKS}}
* {{COMPANY_ADDRESS}}
* {{UNSUBSCRIBE}}
* -
* For Broken links list
* {{BROKEN_LINK_TITLE}}
* {{BROKEN_LINK_STATUS}}
* {{BROKEN_LINK_STATUS_TITLE}}
* {{BROKEN_LINK_URL}}
*/
require_once 'email-body-markup.php';
<?php
/**
* The Http Request model.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\Core\Models
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Http_Requests\Scan;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WP_Error;
use WP_HTTP_Response;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use WPMUDEV_BLC\App\Scan_Models\Scan_Data;
use WPMUDEV_BLC\Core\Models\Http_Request;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
use WPMUDEV_BLC\Core\Utils\Utilities;
use WPMUDEV_BLC\Core\Traits\Dashboard_API;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Http_Requests\Scan
*/
class Controller extends Base {
/**
* Use Dashboard_API Trait.
*
* @since 2.0.0
*/
use Dashboard_API;
/**
* Response
*
* @var null|array
*/
private $response = array(
'code' => 404,
'status' => null,
'scan_status' => '',
'message' => '',
'data' => array(
'broken_links' => null,
'succeses' => null,
'unique_urls' => null,
'total_urls' => null,
'start_time' => null,
'end_time' => null,
'duration' => null,
),
);
/**
* The BLC remote api full url.
*
* @var string
*/
private $url = null;
/**
* The BLC api key.
*
* @var string
*/
private $api_key = null;
/**
* Token
*
* @var string
*/
private $token = null;
/**
*
* Null or Object of eiter Http_Request or WP_Error.
*
* @var object
*/
private $request_api = null;
/**
* Starts the BLC scan. Request:
*
* @return WP_HTTP_Response|null|WP_Error
*/
public function start() {
$request_status = $this->can_do_request();
if ( is_wp_error( $request_status ) ) {
$this->set_response_code( 500 );
$this->set_response_message( esc_html( $request_status->get_error_message() ) );
return $this->get_response();
}
$this->prepare_request();
/*
* Request to start scan.
*/
$args = array(
'method' => 'POST',
'url' => $this->get_request_url(),
'headers' => array(
'Authorization' => $this->api_key,
),
);
$api_request = $this->request_api->request( $args );
$error_message = $this->error_messages( 'request_error' );
if ( ! $this->request_api->get_response() instanceof WP_HTTP_Response ) {
$this->set_response_code( 500 );
$this->set_response_message( $error_message );
return $this->get_response();
}
$response_data = json_decode( $this->request_api->get_data(), true );
if ( 200 !== $api_request->get_status() ) {
$error_message = isset( $response_data['message'] ) ? esc_html( $response_data['message'] ) : $error_message;
$this->set_response_code( 500 );
$this->set_response_message( $error_message );
return $this->get_response();
}
$json_to_import = json_encode( array( 'params' => $response_data ) );
if ( ! Scan_Data::instance()->set( $json_to_import ) ) {
// This doesn't mean that there was an error. There was probably no change in data that's why
// update_option returns false. So we're just logging a message.
Utilities::log( $this->error_messages( 'error_on_save' ) );
}
$this->set_response_scan_status( 'completed' );
Settings::instance()->set( array( 'scan_status' => 'completed' ) );
Settings::instance()->save();
$this->set_response_code( $api_request->get_status() );
$this->set_response_data( $api_request->get_data() );
$this->set_response_message(
esc_html__(
'Scan for Broken Links is in progress. Please wait a few minutes until scan completes.',
'broken-link-checker'
)
);
return $this->get_response();
}
/**
* Checks if the request can be carried out.
*
* @return bool|WP_Error
*/
private function can_do_request() {
if ( Utilities::is_localhost() ) {
return new WP_Error(
'blc-api-request-failled',
esc_html__(
'Scan could not be started because it seems you are on localhost. Broken Links Checker API can not reach sites on local hosts',
'broken-link-checker'
)
);
}
if ( ! (bool) self::site_connected() ||
Settings::instance()->get( 'use_legacy_blc_version' ) ||
! class_exists( '\WPMUDEV_Dashboard' ) ||
! \WPMUDEV_Dashboard::$api->has_key() ) {
return new WP_Error(
'blc-api-request-failled',
esc_html__(
esc_html__( 'Can not make request.', 'broken-link-checker' ),
'broken-link-checker'
)
);
}
return true;
}
public function set_response_code( int $code = 200 ) {
$this->response['status'] = absint( $code );
}
public function set_response_message( string $data = '' ) {
$this->response['message'] = $data;
}
/**
* Returns the response.
*
* @return WP_Http_Response|array
*/
public function get_response() {
return $this->response;
}
/**
* Prepares some params.
*
* @return void
*/
private function prepare_request() {
if ( is_null( $this->request_api ) ) {
$this->request_api = Http_Request::instance();
}
// WPMUDEV_Dashboard class has been checked already in `$this->can_do_request()`.
$this->api_key = \WPMUDEV_Dashboard::$api->get_key();
}
/**
* Returns full request url.
*
* @return string
*/
public function get_request_url() {
if ( is_null( $this->url ) ) {
$this->url = Utilities::hub_api_scan_url();
}
return $this->url;
}
protected function error_messages( string $message_code = '' ) {
$error_messages = array(
'request_error' => esc_html__( 'Something went wrong with request.', 'broken-link-checker' ),
'scan_in_progress' => esc_html__( 'Scan is currently in progress. Please try again in 15 minutes.', 'broken-link-checker' ),
);
if ( ! empty( $message_code ) ) {
return isset( $error_messages[ $message_code ] ) ? $error_messages[ $message_code ] : '';
}
return $error_messages;
}
/**
* Sets the response status in global array.
*
* @param string $status
*
* @return void
*/
public function set_response_scan_status( string $status = 'completed' ) {
if ( ! in_array( $status, array( 'completed', 'in_progress' ) ) ) {
$status = 'completed';
}
$this->response['scan_status'] = $status;
}
public function set_response_data( $data = '' ) {
$this->response['data'] = $data;
}
public function get_error_message() {
return $this->response['message'];
}
public function get_response_code() {
return $this->response['status'];
}
public function get_response_message() {
return $this->response['message'];
}
public function get_response_data() {
return $this->response['data'];
}
public function get_response_scan_status() {
return $this->response['scan_status'];
}
}
<?php
/**
* The Http Request model.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\Core\Models
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Http_Requests\Scan;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Traits\Sanitize;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
use WPMUDEV_BLC\Core\Models\Http_Request;
/**
* Class Installer
*
* @package WPMUDEV_BLC\Core\Models
*/
class Model extends Http_Request {
public function start_scan( string $url = '', string $api_key = '' ) {
$args = array(
);
$this->request( $args );
return $this->get_response();
}
}
<?php
/**
* An endpoint where Hub can send requests and retrieve scan schedule data.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Hub_Endpoints\Scan_Data
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Hub_Endpoints\Scan_Data;
// Abort if called directly.
defined( 'WPINC' ) || die;
use DateTime;
use DateTimeZone;
use WPMUDEV_BLC\Core\Controllers\Hub_Endpoint;
use WPMUDEV_BLC\Core\Utils\Utilities;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use function boolval;
use function gmdate;
use function intval;
use function wp_next_scheduled;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Hub_Endpoint\Scan_Data
*/
class Controller extends Hub_Endpoint {
/**
* Returns a json string with schedule data.
*/
public function get_schedule_data() {
$schedule_data = array(
'active' => false,
'recipients' => array(),
'emailRecipients' => array(),
'frequency' => 'daily',
'days' => array(),
'time' => '00:00',
'nextScanData' => array(),
);
$schedule = $this->get_schedule();
if ( ! empty( $schedule ) ) {
$schedule_data['active'] = isset( $schedule['active'] ) ? boolval( $schedule['active'] ) : false;
if ( ! $schedule_data['active'] ) {
$this->output_formatted_response( $schedule_data );
}
$schedule_data['recipients'] = $schedule['recipients'] ?? $schedule_data['recipients'];
$schedule_data['emailRecipients'] = Settings::instance()->get_scan_active_email_recipients() ?? $schedule_data['emailRecipients'];
$schedule_data['frequency'] = $schedule['frequency'] ?? $schedule_data['frequency'];
$schedule_data['days'] = ( 'monthly' === $schedule['frequency'] ) ?
$schedule['monthdays'] :
( 'weekly' === $schedule['monthdays'] ? $schedule['days'] : array() );
$schedule_data['time'] = $schedule['time'] ?? $schedule_data['time'];
$schedule_data['nextScanData'] = $this->get_next_scan_data();
}
$this->output_formatted_response( $schedule_data );
}
/**
* Returns the schedule from settings, or if a key is set, it returns that key's value
*
* @param string $key .
*
* @return array|mixed|null
*/
private function get_schedule( string $key = '' ) {
static $schedule = null;
if ( is_null( $schedule ) ) {
$schedule = Settings::instance()->get( 'schedule' );
}
if ( ! empty( $key ) && is_array( $schedule ) ) {
return $schedule[ $key ] ?? null;
}
return $schedule;
}
/**
* Returns an array which contains next blc schedule data.
*
* @return array.
*/
private function get_next_scan_data() {
$next_scan_data = array(
'siteZone' => '',
'timestampSiteZone' => '',
'timestampUTC' => '',
'formattedDateSiteZone' => '',
'formattedDateUTC' => '',
);
$schedule_cron = new \WPMUDEV_BLC\App\Scheduled_Events\Scan\Controller();
$schedule_cron->init();
//$next_scan_timestamp_utc = wp_next_scheduled( $schedule_cron->cron_hook );
$next_scan_timestamp_utc = $this->get_next_schedule_timestamp( $schedule_cron->get_hook_name() );
$date_format = Utilities::get_date_format();
$time_format = Utilities::get_time_format();
if ( $next_scan_timestamp_utc ) {
$next_scan_timestamp_utc = intval( $next_scan_timestamp_utc );
$next_scan_date = gmdate( 'Y-m-d H:i:s', $next_scan_timestamp_utc );
$next_scan_datetime = new DateTime( $next_scan_date, new DateTimeZone( 'UTC' ) );
$next_scan_datetime->setTimezone( new DateTimeZone( Utilities::get_timezone_string( true ) ) );
$next_scan_timestamp = strtotime( $next_scan_datetime->format( 'Y-m-d H:i:s' ) );
$next_scan_data['siteZone'] = Utilities::get_timezone_string( true );
$next_scan_data['timestampSiteZone'] = $next_scan_timestamp;
$next_scan_data['timestampUTC'] = $next_scan_timestamp_utc;
$next_scan_data['formattedDateSiteZone'] = gmdate( "{$date_format} {$time_format}", $next_scan_timestamp );
$next_scan_data['formattedDateUTC'] = gmdate( "{$date_format} {$time_format}", $next_scan_timestamp_utc );
}
return $next_scan_data;
}
/**
* Calculates next `blc_schedule_scan` cron timestamp.
*
* @return int|bool.
*/
private function get_next_schedule_timestamp( $cron_hook ) {
$next_timestamp = wp_next_scheduled( $cron_hook );
if ( ! $next_timestamp ) {
$schedule_cron = new \WPMUDEV_BLC\App\Scheduled_Events\Scan\Controller();
$schedule_cron->set_scan_schedule();
$next_timestamp = wp_next_scheduled( $cron_hook );
}
return $next_timestamp;
}
/**
* Sets the endpoint's action vars to be used by Dash plugin.
*/
protected function setup_action_vars() {
$this->endpoint_action_name = 'blc_get_data';
$this->endpoint_action_callback = 'get_schedule_data';
}
}
<?php
/**
* An enpoint where Hub can send requests and retrive scan schedule data.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Hub_Endpoints\Set_Data
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Hub_Endpoints\Set_Data;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Controllers\Hub_Endpoint;
use WPMUDEV_BLC\App\Scan_Models\Scan_Data;
use WPMUDEV_BLC\App\Emails\Scan_Report\Controller as ReportMailer;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Hub_Endpoint\Scan_Data
*/
class Controller extends Hub_Endpoint {
/**
* Sets the endpoint's action vars to be used by Dash plugin.
*/
protected function setup_action_vars() {
$this->endpoint_action_name = 'blc_set_data';
$this->endpoint_action_callback = 'set_scan_data';
}
/**
* Prints a json string with schedule data.
*/
public function set_scan_data() {
$input_json = file_get_contents( 'php://input' );
if ( ! Scan_Data::instance()->set( $input_json ) ) {
$this->output_formatted_response(
array(
'code' => 'ERROR_SET_SCAN_DATA',
'message' => 'Something went wrong when saving scan data.',
'data' => '',
),
false
);
} else {
if ( Settings::instance()->get( 'blc_schedule_scan_in_progress' ) ) {
ReportMailer::instance()->init();
ReportMailer::instance()->send_email();
Settings::instance()->set( array( 'blc_schedule_scan_in_progress' => false ) );
Settings::instance()->save();
}
$this->output_formatted_response(
array(
'data_received' => true,
'data_stored' => true,
),
true
);
}
}
}
<?php
/**
* Settings controller.
* A single scheduled event that gets triggered based on options set in "Schedule Scan"
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Options\Settings
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Options\Settings;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Models\Option;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings_Model;
use WPMUDEV_BLC\Core\Utils\Utilities;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Options\Settings
*/
class Controller extends Base {
/**
* Plugin settings
*/
private $settings = null;
/**
* Init Settings
*
* @since 2.0.0
*
* @return void
*/
public function init() {
$this->settings = new Settings_Model();
$this->settings->init();
add_action( 'wpmudev_blc_plugin_activated', array( $this, 'activation_actions' ), 9 );
//add_action( 'wpmudev_blc_plugin_deactivated', array( $this, 'deactivation_actions' ) );
add_action( 'load-toplevel_page_blc_dash', array( $this, 'blc_pages_init' ) );
add_action( 'load-link-checker_page_view-broken-links', array( $this, 'blc_pages_init' ) );
add_action( 'load-link-checker_page_link-checker-settings', array( $this, 'blc_pages_init' ) );
//add_action( 'delete_user', array( $this,'adapt_schedule_recipients' ), 10, 2 );
add_action( 'deleted_user', array( $this, 'adapt_schedule_recipients' ), 10, 2 );
add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog' ), 10, 3 );
}
/**
* Actions to be done when v2 or legacy blc pages are loaded.
* @return void
*/
public function blc_pages_init() {
// Activate V2 if there was a request to activate.
// This way we ensure that after site gets connected V2 gets activated when there was a settings request to activate V2 but at the time site was disconnected.
if ( $this->settings->get( 'v2_activation_request' ) && Utilities::site_connected() ) {
$this->settings->set( array( 'use_legacy_blc_version' => false ) );
$this->settings->set( array( 'v2_activation_request' => false ) );
$this->settings->save();
// After connecting site and setting V2 enabled we also need to reload the BLC page(s) so that as page content has already been set.
$parsed_query_params = wp_parse_url( $_SERVER['REQUEST_URI'] );
if ( ! empty( $parsed_query_params['query'] ) ) {
$query_params = $parsed_query_params['query'];
wp_safe_redirect( admin_url( "admin.php?{$query_params}" ) );
exit;
}
}
// Disable the welcome modal after plugin activation when BLC Dash or other BLC screen is visited.
$this->settings->set( array( 'activation_modal_shown' => true ) );
$this->settings->save();
}
/**
* Deleted the settings when plugin gets deactivated.
*
* @since 2.0.0
*
* @return void
*/
public function deactivation_actions() {
$this->settings->delete();
}
/**
* Set the settings when plugin gets activated.
*
* @since 2.0.0
*
* @return void
*/
public function activation_actions() {
// Check if legacy plugin was installed by checking legacy a`wsblc_options` option.
// If installed first time enable V2, else V1.
$legacy_option = new Option( array( 'name' => 'wsblc_options' ) );
if ( empty( $legacy_option->get() ) || ( empty( $this->settings->get( 'use_legacy_blc_version' ) ) && ! empty( $this->settings->get( 'schedule' ) ) && ! empty( $this->settings->get( 'schedule' )['active'] ) ) ) {
\WPMUDEV_BLC\App\Scheduled_Events\Scan\Controller::instance()->set_scan_schedule();
}
if ( ! empty( get_option( 'blc_settings' ) ) ) {
return;
}
if ( empty( $legacy_option->get() ) ) {
$this->settings->set( array( 'use_legacy_blc_version' => false ) );
} else {
$this->settings->set( array( 'use_legacy_blc_version' => true ) );
}
$this->settings->set( array( 'installation_timestamp' => time() ) );
$this->settings->save();
}
/**
* Gets triggerred once a user gets removed from current subsite on a multsite network.
*
* @param int|null $user_id
* @param int|null $user_id_reassign
*
* @return void
*/
public function remove_user_from_blog( int $user_id = null, int $user_id_reassign = null ) {
$this->adapt_schedule_recipients( $user_id );
}
/**
* Adapts schedule recipients when a user is deleted from admin users.
*
* @param int|null $user_id
* @param int|null $user_id_reassign
*
* @return void
*/
public function adapt_schedule_recipients( int $user_id = null, int $user_id_reassign = null ) {
if ( empty( $user_id ) ) {
return;
}
$schedule = $this->settings->get( 'schedule' );
if ( ! empty( $schedule['recipients'] ) ) {
if ( in_array( $user_id, $schedule['recipients'] ) ) {
$user_key = array_search(
$user_id,
$schedule['recipients']
);
if ( intval( $schedule['recipients'][ $user_key ] ) === $user_id ) {
unset( $schedule['recipients'][ $user_key ] );
$schedule['recipients'] = array_values( $schedule['recipients'] );
unset( $schedule['registered_recipients_data'][ $user_id ] );
$this->settings->set( array( 'schedule' => $schedule ) );
$this->settings->save();
}
}
}
}
}
<?php
/**
* The Option model
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Options\Settings
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Options\Settings;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Models\Option;
use function array_filter;
/**
* Class Settings
*
* @package WPMUDEV_BLC\App\Options\Settings
*/
class Model extends Option {
/**
* Default options. Optional
*
* @since 2.0.0
* @var string|array|null $option_keys
*/
public $default = array(
/*
* Scan status (scan_status) :
* none : Never started
* in_progress : Is currently running
* completed : Has been completed
*/
'scan_status' => 'none',
'site_connected' => false,
'activation_modal_shown' => false,
'use_legacy_blc_version' => true,
'blc_schedule_scan_in_progress' => false,
'show_multisite_notice' => true,
'installation_timestamp' => null,
'v2_activation_request' => false,
'schedule' => array(
'active' => false,
'recipients' => array(),
'registered_recipients_data' => array(),
'emailRecipients' => array(),
'frequency' => 'daily',
'days' => array(),
'monthdays' => array(),
'time' => '00:00',
),
'scan_results' => array(
/*
* List of broken links. Storing to be used in Scan Report Emails. Stores limited number links configured
* in `WPMUDEV_BLC\App\Scan_Models\limit_links_number()`
*/
'broken_links_list',
'broken_links' => null,
'succeeded_urls' => null,
'total_urls' => null,
'unique_urls' => null,
'start_time' => null,
'end_time' => null,
'duration' => null,
),
);
/**
* The option_name.
*
* @since 2.0.0
* @var string $name
*/
protected $name = 'blc_settings';
/**
* Returns the scheduled scan email recipients that have been confirmed.
*
* @return array.
*/
public function get_scan_active_email_recipients() {
$schedule = $this->get( 'schedule' );
if ( empty( $schedule ) ) {
return array();
}
$email_recipients = (array) $schedule['emailrecipients'] ?? array();
$active_recipients = array_filter(
$email_recipients,
function ( array $recipient = array() ) {
return isset( $recipient['confirmed'] ) && $recipient['confirmed'];
}
);
return $active_recipients;
}
}
<?php
/**
* Rest endpoint fetching Avatars.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Rest_Endpoints\Avatars
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Rest_Endpoints\Avatars;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WP_Error;
use WP_REST_Request;
use WP_REST_Server;
use WPMUDEV_BLC\Core\Controllers\Rest_Api;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Rest_Endpoints\Avatars
*/
class Controller extends Rest_Api {
/**
* Settings keys.
*
* @var array
*/
protected $settings_keys = array();
public function init() {
$this->settings_keys = array_map(
function ( $settings_key ) {
return sanitize_key( $settings_key );
},
array_keys( Settings::instance()->default )
);
$this->namespace = "wpmudev_blc/{$this->version}";
$this->rest_base = 'avatars';
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
}
/**
* Register the routes for the objects of the controller.
*
* @since 2.0.0
*
* @return void
*/
public function register_routes() {
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'get_avatar' ),
'permission_callback' => array( $this, 'get_avatar_permissions' ),
),
'schema' => array( $this, 'get_item_schema' ),
)
);
}
/**
* Returns avatar.
*
* @since 2.0.0
*
* @param object $request WP_REST_Request get data from request.
*
* @return mixed WP_REST_Response|WP_Error|WP_HTTP_Response|mixed $response
*/
public function get_avatar( $request ) {
$email = $request->get_param( 'email' );
$response_data = array(
'message' => __( 'Avatar url', 'broken-link-checker' ),
'status_code' => 200,
);
if ( ! is_email( $email ) ) {
$response_data['message'] = __( 'Invalid email address', 'broken-link-checker' );
$response_data['status_code'] = 500;
} else {
$avatar = get_avatar_url( $email, array( 'size' => 24 ) );
$response_data['avatar'] = $avatar;
}
$response = $this->prepare_item_for_response( $response_data, $request );
return rest_ensure_response( $response );
}
/**
* Check permissions for fetching avatar.
*
* @since 2.0.0
*
* @param object $request get data from request.
*
* @return bool|object Boolean or WP_Error.
*/
public function get_avatar_permissions( WP_REST_Request $request ) {
if ( ! current_user_can( 'manage_options' ) ) {
return new WP_Error(
'rest_forbidden',
esc_html__( 'You can not fetch avatars.', 'broken-link-checker' ),
array( 'status' => $this->authorization_status_code() )
);
}
return true;
}
/**
* Retrieves the item's schema, conforming to JSON Schema.
*
* @since 2.0.0
*
* @return array Item schema data.
*/
public function get_item_schema() {
if ( $this->schema ) {
return $this->add_additional_fields_schema( $this->schema );
}
$this->schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => isset( $args['rest_base'] ) ? $args['rest_base'] : '',
'type' => 'object',
'properties' => array(),
);
$this->schema['properties'] = array(
'avatar' => array(
'description' => esc_html__( 'Avatar by email.', 'broken-link-checker' ),
'type' => 'string',
),
'confirmed' => array(
'description' => esc_html__( 'Auto-confirmed when email belongs to user.', 'broken-link-checker' ),
'type' => 'boolean',
),
'message' => array(
'description' => esc_html__( 'Response message.', 'broken-link-checker' ),
'type' => 'string',
),
'status_code' => array(
'description' => esc_html__( 'Response status code.', 'broken-link-checker' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'enum' => array(
'200',
'400',
'401',
'403',
),
'readonly' => true,
),
);
return $this->add_additional_fields_schema( $this->schema );
}
}
<?php
/**
* Rest endpoint starting new Scan.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Rest_Endpoints\Scan
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Rest_Endpoints\Scan;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WP_Error;
use WP_REST_Request;
use WP_REST_Server;
use WPMUDEV_BLC\Core\Controllers\Rest_Api;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use WPMUDEV_BLC\App\Http_Requests\Sync_Scan_Results\Controller as Scan_API;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Rest_Endpoints\Scan
*/
class Controller extends Rest_Api {
/**
* Settings keys.
*
* @var array
*/
protected $settings_keys = array();
public function init() {
$this->settings_keys = array_map(
function ( $settings_key ) {
return sanitize_key( $settings_key );
},
array_keys( Settings::instance()->default )
);
$this->namespace = "wpmudev_blc/{$this->version}";
$this->rest_base = 'scan';
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
}
/**
* Register the routes for the objects of the controller.
*
* @since 2.0.0
*
* @return void
*/
public function register_routes() {
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'handle_scan_request' ),
'permission_callback' => array( $this, 'get_scan_permissions' ),
),
'schema' => array( $this, 'get_item_schema' ),
)
);
}
public function handle_scan_request( $request ) {
$action = $request->get_param( 'action' );
if ( 'start_scan' === $action ) {
return $this->start_scan( $request );
}
// Action is `fetch_scan_data`.
return $this->monitor_scan( $request );
}
/**
* Returns new scan info.
*
* @param object $request WP_REST_Request get data from request.
*
* @since 2.0.0
*
* @return mixed WP_REST_Response|WP_Error|WP_HTTP_Response|mixed $response
*/
public function start_scan( $request ) {
$response_data = array();
$scan = Scan_API::instance();
$scan->start();
$response_data['status_code'] = $scan->get_response_code();
$response_data['message'] = $scan->get_response_message();
$response_data['data'] = $scan->get_response_data();
$response_data['success'] = 200 === $scan->get_response_code();
$response_data['scan_status'] = $scan->get_response_scan_status();
$response = $this->prepare_item_for_response( $response_data, $request );
return rest_ensure_response( $response );
}
/**
* @param $request
*
* @return mixed
*/
public function monitor_scan( $request ) {
$response_data = array();
$scan = Scan_API::instance();
$scan->start();
if ( is_wp_error( $scan->get_response() ) ) {
$response_data['success'] = false;
$response_data['status_code'] = 500;
$response_data['message'] = $scan->get_error_message();
$response_data['data'] = null;
} else {
$response_data['status_code'] = $scan->get_response_code();
$response_data['message'] = $scan->get_response_message();
}
$response_data['success'] = $scan->get_response_code() === 200;
$response_data['scan_status'] = $scan->get_response_scan_status();
$response_data['data'] = $scan->get_response_data();
return rest_ensure_response( $this->prepare_item_for_response( $response_data, $request ) );
}
/**
* Check permissions for fetching avatar.
*
* @param object $request get data from request.
*
* @since 2.0.0
*
* @return bool|object Boolean or WP_Error.
*/
public function get_scan_permissions( WP_REST_Request $request ) {
if ( ! current_user_can( 'manage_options' ) ) {
return new WP_Error(
'rest_forbidden',
esc_html__( 'You are not allowed to start a new scan.', 'broken-link-checker' ),
array( 'status' => $this->authorization_status_code() )
);
}
return true;
}
/**
* Retrieves the item's schema, conforming to JSON Schema.
*
* @since 2.0.0
*
* @return array Item schema data.
*/
public function get_item_schema() {
if ( $this->schema ) {
return $this->add_additional_fields_schema( $this->schema );
}
$this->schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => isset( $args['rest_base'] ) ? $args['rest_base'] : '',
'type' => 'object',
'properties' => array(),
);
$this->schema['properties'] = array(
'success' => array(
'description' => esc_html__( 'If true scan has started successfully, else it has not.', 'broken-link-checker' ),
'type' => 'boolean',
),
'scan_status' => array(
'description' => esc_html__( 'The scan status.', 'broken-link-checker' ),
'type' => 'string',
'enum' => array(
'completed',
'in_progress',
'none',
),
),
'message' => array(
'description' => esc_html__( 'Response message.', 'broken-link-checker' ),
'type' => 'string',
),
'data' => array(
'description' => esc_html__( 'Scan response data/results.', 'broken-link-checker' ),
'type' => 'string',
),
'status_code' => array(
'description' => esc_html__( 'Response status code.', 'broken-link-checker' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'enum' => array(
'200',
'400',
'401',
'403',
'500',
),
'readonly' => true,
),
);
return $this->add_additional_fields_schema( $this->schema );
}
}
<?php
/**
* The Schema for Rest endpoint
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Rest_Endpoints\Settings
*/
namespace WPMUDEV_BLC\App\Rest_Endpoints\Settings\Includes;
// Abort if called directly.
defined( 'WPINC' ) || die;
class Schema {
/**
* Get Schema for Rest Endpoint.
*
* @since 1.0.0
*
* @param string $action A string that contains action that endpoint performs.
*
* @param array $args An array containing several options that we can use for returning specific schema properties.
*
* @return array An array containing Schema.
*/
public static function get_schema( string $action = null, array $args = array() ) {
if ( \is_null( $action ) ) {
return array();
}
$poperties_keys = array();
$schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => isset( $args['rest_base'] ) ? $args['rest_base'] : '',
'type' => 'object',
'properties' => array(),
);
switch ( $action ) {
case 'save':
$poperties_keys = array( 'message', 'status_code' );
break;
case 'get':
$poperties_keys = array( 'message', 'status_code', 'settings' );
break;
}
$schema['properties'] = self::get_schema_properties( $poperties_keys );
return $schema;
}
/**
* Get Schema properties for Rest Response.
*
* @since 1.0.0
*
* @param array $properties_keys An array containing field keys for properties needed.
*
* @return array An array of schema properties.
*/
protected static function get_schema_properties( array $properties_keys = array() ) {
$return_properties = array();
$schema_properties = array(
'settings' => array(
'description' => esc_html__( 'All BLC settings.', 'broken-link-checker' ),
'type' => 'object',
'properties' => array(
'activation_modal_shown' => array(
'description' => esc_html__( 'Activation modal shown or not.', 'broken-link-checker' ),
'type' => 'string',
),
'use_legacy_blc_version' => array(
'description' => esc_html__( 'Use legacy BLC', 'broken-link-checker' ),
'type' => 'string',
),
'userRolesAllowed' => array(
'type' => 'object',
'properties' => array(
'name' => array(
'description' => esc_html__( 'User role name', 'broken-link-checker' ),
'type' => 'string',
),
'label' => array(
'description' => esc_html__( 'User role label', 'broken-link-checker' ),
'type' => 'string',
),
),
),
),
),
'message' => array(
'description' => esc_html__( 'Response message.', 'broken-link-checker' ),
'type' => 'string',
),
'status_code' => array(
'description' => esc_html__( 'Response status code.', 'broken-link-checker' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'enum' => array(
'200',
'400',
'401',
'403',
),
'readonly' => true,
),
'instructions' => array(
'description' => esc_html__( 'Response instructions.', 'broken-link-checker' ),
'type' => 'object',
),
);
if ( empty( $properties_keys ) ) {
$return_properties = $schema_properties;
} else {
$return_properties = \array_filter(
$schema_properties,
function( string $property_key = '' ) use ( $properties_keys ) {
return in_array( $property_key, $properties_keys );
},
ARRAY_FILTER_USE_KEY
);
}
return apply_filters( 'wpmudev_blc_rest_enpoints_settings_schema_properties', $return_properties );
}
}
\ No newline at end of file
<?php
/**
* Sets or Inserts fresh scan records in DB.
* Used by
* Cron that syncs report data (WPMUDEV_BLC\App\Http_Requests\Sync_Scan_Results\Controller)
* Hub endpoint the sets report data (WPMUDEV_BLC\App\Hub_Endpoints\Set_Data\Controller)
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Scan_Models
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Scan_Models;
// Abort if called directly.
defined( 'WPINC' ) || die;
use stdClass;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
use WPMUDEV_BLC\Core\Utils\Utilities;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use function is_multisite;
use function property_exists;
use function restore_current_blog;
use function switch_to_blog;
/**
* Class Scan_Data
*
* @package WPMUDEV_BLC\App\Scan_Models
*/
class Scan_Data extends Base {
/**
* Inserts new scan data to DB.
*
* @param string $json_data The data to insert needs to be in json format.
*
* @return bool
*/
public function set( string $json_data = '' ) {
if ( empty( $json_data ) ) {
Utilities::log( 'BLC_SET_SCAN_DATA_ERROR - Exiting because input does not contain any data.' );
return false;
}
$unformated_input = json_decode( $json_data );
// Make sure input is valid json.
if ( json_last_error() !== JSON_ERROR_NONE ) {
Utilities::log( 'BLC_SET_SCAN_DATA_ERROR - Exiting because input was not valid json string.' );
return false;
}
$params = $unformated_input->params;
$url = property_exists( $params, 'url' ) ? $params->url : null;
$site_id = property_exists( $params, 'site_id' ) ? intval( $params->site_id ) : null;
if ( $site_id !== Utilities::site_id() ) {
Utilities::log( 'BLC_SET_SCAN_DATA_ERROR - Exiting because BLC HUB API ping does not contain site id.' );
return false;
}
$input = $this->get_formatted_input( $params );
$use_subsite_id = false;
if ( empty( $input ) ) {
return false;
}
if ( is_multisite() ) {
$subside_id = Utilities::subsite_id_from_url( $url );
if ( ! empty( $subside_id ) ) {
$use_subsite_id = true;
switch_to_blog( $subside_id );
}
}
Settings::instance()->init();
Settings::instance()->set( array( 'scan_results' => $input ) );
Settings::instance()->set( array( 'scan_status' => 'completed' ) );
Settings::instance()->save();
if ( $use_subsite_id ) {
restore_current_blog();
}
return true;
}
/**
* Returns an array of expected scan data values.
*
* @param stdClass $params The input object.
*
* @return array|null
*/
public function get_formatted_input( stdClass $params ) {
//if ( ! empty( $params->scanning->is_running ) ) {
// Utilities::log( 'BLC_SET_SCAN_DATA_ERROR - Exiting because BLC HUB API scan is running currently.' );
// return null;
//}
$results = $params->last_result;
return array(
'broken_links_list' => $this->limit_links_number( $results->broken_links ),
'broken_links' => $results->num_broken_links ?? null,
'succeeded_urls' => $results->num_successful_links ?? null,
'total_urls' => $results->num_found_links ?? null,
'unique_urls' => $results->num_site_unique_links ?? null,
'start_time' => $results->start_unix_time_utc ?? null,
'end_time' => $results->ended_unix_time_utc ?? null,
'duration' => $results->scan_duration ?? null,
);
}
/**
* Limits the number of links to be stored in db. Limit is set to 20.
*
* @param array $links_list An array with all broken links sent from Hub API.
*
* @return array|null
*/
public function limit_links_number( array $links_list = array() ) {
if ( empty( $links_list ) ) {
return null;
}
$links_limit = apply_filters( 'wpmudev_blc_settings_links_limit', 20, $links_list, $this );
// Make sure we don't store ignored ones.
// We only store the broken links to be sent to be sent from the
if ( count( $links_list ) > $links_limit ) {
$new_links_list = array();
foreach( $links_list as $key => $broken_link ) {
if ( property_exists( $broken_link, 'is_ignored' ) && ! empty( $broken_link->is_ignored ) ) {
continue;
}
$new_links_list[] = $broken_link;
}
$links_list = $new_links_list;
}
return array_slice(
$links_list,
0,
$links_limit
);
}
}
<?php
/**
* Legacy Scheduled event for BLC v1.
* Handles legacy cron jobs.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Schedule_Events\Legacy
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Scheduled_Events\Legacy;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Schedule_Events\Scan
*/
class Controller extends Base {
/**
* @var array $legacy_crons List of all legacy scheduled events.
*/
private $legacy_crons = array(
'blc_cron_check_links',
'blc_cron_email_notifications',
'blc_cron_database_maintenance',
'blc_corn_clear_log_file',
'blc_cron_check_news',
);
/**
* @var array $v2_crons List of all v2 scheduled events.
*/
private $v2_crons = array(
'blc_recipients_activation_email_schedule',
'blc_schedule_sync_scan_results',
'blc_schedule_scan',
);
/**
* Init Schedule
*
* @since 2.0.0
*
* @return void
*/
public function init() {
add_action( 'wpmudev_blc_plugin_deactivated', array( $this, 'deactivate_legacy_crons' ) );
add_action( 'wpmudev_blc_rest_enpoints_switch_version_mode', array( $this, 'switch_version_mode' ) );
}
/**
* Switched cron jobs depending on plugin version mode (legacy or v2).
*
* @since 2.0.0
*
* @param bool $legacy_active A boolean indicating if legacy mode is set.
*
* @retun void
*/
public function switch_version_mode( bool $legacy_active = true ) {
if ( $legacy_active ) {
// Enable legacy crons.
$this->activate_legacy_crons();
// Disable v2 cron events.
$this->deactivate_v2_crons();
} else {
// Disable legacy crons.
$this->deactivate_legacy_crons();
// Enable v2 crons.
do_action( 'wpmudev_blc_plugin_activated' );
\WPMUDEV_BLC\App\Scheduled_Events\Scan\Controller::instance()->set_scan_schedule();
\WPMUDEV_BLC\App\Scheduled_Events\Sync_Scan_Results\Controller::instance()->activate_cron();
}
}
/**
* Deactivates all legacy plugin's scheduled events.
*
* @since 2.0.0
*
* @return void
*/
public function deactivate_legacy_crons() {
$this->deactivate_crons( $this->legacy_crons );
// We still need to force clear `blc_schedule_scan` cron.
wp_clear_scheduled_hook( 'blc_schedule_scan' );
add_filter( 'blc_allow_send_email_notification', '__return_false' );
}
/**
* Deactivates all v2 plugin's scheduled events.
*
* @since 2.0.0
*
* @return void
*/
public function deactivate_v2_crons() {
$this->deactivate_crons( $this->v2_crons );
}
/**
* Deactivates given list of scheduled events.
*
* @since 2.0.0
*
* @param array $crons List of scheduled events to be deactivated.
*
* @return void
*/
public function deactivate_crons( array $cron_hooks = array() ) {
if ( empty( $cron_hooks ) ) {
return;
}
foreach ( $cron_hooks as $cron_hook ) {
wp_clear_scheduled_hook( $cron_hook );
}
}
public function activate_legacy_crons() {
global $blc_config_manager;
$ws_link_checker = null;
if ( ! class_exists( 'wsBrokenLinkChecker' ) ) {
require_once BLC_DIRECTORY_LEGACY . '/core/core.php';
}
if ( $blc_config_manager instanceof \blcConfigurationManager ) {
$ws_link_checker = new \wsBrokenLinkChecker( BLC_PLUGIN_FILE_LEGACY, $blc_config_manager );
}
if ( ! is_null( $ws_link_checker ) ) {
// Enable legacy cron events.
$ws_link_checker->setup_cron_events();
}
}
}
<?php
/**
* Scheduled event for BLC Scan.
* A single scheduled event that gets triggered based on options set in "Schedule Scan"
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Schedule_Events\Scan
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Scheduled_Events\Scan;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\App\Http_Requests\Scan\Controller as Scan_API;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use WPMUDEV_BLC\Core\Traits\Cron;
use WPMUDEV_BLC\Core\Utils\Utilities;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Schedule_Events\Scan
*/
class Controller extends Base {
use Cron;
/**
* WP Cron hook to execute when event is run.
*
* @var string
*/
public $cron_hook_name = 'blc_schedule_scan';
/**
* BLC settings from options table.
*
* @var array
*/
private $settings = null;
/**
* Init Schedule
*
* @since 2.0.0
*
* @return void
*/
public function init() {
//if ( wp_doing_ajax() || ! $this->get_schedule( 'active' ) ) {
if ( wp_doing_ajax() ) {
return;
}
Settings::instance()->init();
//add_action( 'init', array( $this, 'load' ) );
//add_action( 'wpmudev_blc_rest_enpoints_after_save_schedule_settings', array( $this, 'deactivate_cron' ), 10 );
add_action( 'wpmudev_blc_rest_enpoints_after_save_schedule_settings', array( $this, 'set_scan_schedule' ) );
add_action( 'wpmudev_blc_plugin_deactivated', array( $this, 'deactivate_cron' ) );
}
/**
* Returns the scheduled event's hook name.
* Overriding Trait's method.
*/
public function get_hook_name() {
return $this->cron_hook_name;
}
/**
* Starts the scheduled scan.
*/
public function process_scheduled_event() {
// At his point we're setting the scan status flag to `in_progress`. So if it doesn't get `completed` there
// will ba a sync request fired on page load.
Settings::instance()->set( array( 'scan_status' => 'in_progress' ) );
Settings::instance()->save();
$scan = Scan_API::instance();
$scan->start();
$this->set_scan_schedule_flag();
//$this->deactivate_cron();
$this->set_scan_schedule();
}
/**
* Sets the scan flag to true. Useful when API sends the SET request, an email about the current schedule should
* be sent to schedule receivers.
*/
public function set_scan_schedule_flag( bool $flag = true ) {
Settings::instance()->set( array( 'blc_schedule_scan_in_progress' => $flag ) );
Settings::instance()->set( array( 'scan_status' => 'in_progress' ) );
Settings::instance()->save();
}
/**
* Sets new scan schedule.
*
* @param array $settings The settings param from `wpmudev_blc_rest_enpoints_after_save_schedule_settings` action.
*
* @return bool
*/
public function set_scan_schedule( array $settings = array() ) {
if ( ! $this->get_schedule( 'active' ) ) {
return false;
}
// Deactivate cron if is already created, so we will replace it later on.
$this->deactivate_cron();
// As a single event it will be possible to set custom timestamps to run.
$this->is_single_event = true;
// Set the timestamp based on Schedule options.
$this->timestamp = intval( $this->get_timestamp( $settings['schedule'] ?? array() ) );
/*
* setup_cron() is handled by Cron trait.
*/
//$this->setup_cron();
return $this->activate_cron();
}
/**
* Returns the schedule from settings, or if a key is set, it returns that key's value
*
* @param string $key .
*
* @return array|mixed|null
*/
private function get_schedule( string $key = '' ) {
if ( is_null( $this->settings ) ) {
$this->settings = Settings::instance()->get( 'schedule' );
}
if ( ! empty( $key ) && is_array( $this->settings ) ) {
return $this->settings[ $key ] ?? null;
}
return $this->settings;
}
/**
* Returns the timestamp of next scheduled scan.
*
* @param array $schedule
*
* @return false|int|null
*/
public function get_timestamp( array $schedule = array() ) {
$schedule = ! empty( $schedule['frequency'] ) ? $schedule : $this->get_schedule();
$timestamp = null;
if ( empty( $schedule['frequency'] ) || empty( $schedule['time'] ) ) {
return $timestamp;
}
$schedule_time = $schedule['time'];
// phpcs:ignore
$current_day_num = 'monthly' === $schedule['frequency'] ? date( 'd' ) : date( 'w', time() );
$schedule_days = 'monthly' === $schedule['frequency'] ? $schedule['monthdays'] : $schedule['days'];
if ( 'daily' !== $schedule['frequency'] && empty( $schedule_days ) ) {
return $timestamp;
}
sort( $schedule_days );
switch ( $schedule['frequency'] ) {
case 'daily':
if ( date_format( date_create( date_i18n( 'H:i' ) ), 'Hi' ) > date_format( date_create( $schedule_time ), 'Hi' ) ) {
$timestamp = strtotime( "tomorrow {$schedule_time} " . wp_timezone_string() );
} else {
$timestamp = strtotime( "today {$schedule_time} " . wp_timezone_string() );
}
break;
case 'weekly':
case 'monthly':
$next_day_num = null;
$move_to_next_period = false;
if ( in_array( $current_day_num, $schedule_days, true ) ) {
$day_key = array_keys( $schedule_days, $current_day_num, true )[0];
if ( date_format( date_create( date_i18n( 'H:i' ) ), 'Hi' ) >= date_format( date_create( $schedule_time ), 'Hi' ) ) {
$next_day_num = array_key_exists( ( $day_key + 1 ), $schedule_days ) ? $schedule_days[ ( $day_key + 1 ) ] : null;
} else {
$timestamp = strtotime( "today {$schedule_time}" . ' ' . wp_timezone_string() );
}
}
if ( is_null( $next_day_num ) ) {
foreach ( $schedule_days as $day_num ) {
if ( intval( $day_num ) > intval( $current_day_num ) ) {
$next_day_num = intval( $day_num );
break;
}
}
}
if ( is_null( $next_day_num ) ) {
$next_day_num = intval( $schedule_days[0] );
$move_to_next_period = true;
}
if ( is_null( $timestamp ) ) {
if ( 'weekly' === $schedule['frequency'] ) {
// phpcs:ignore
$day_name = date( 'l', strtotime( "Sunday +{$next_day_num} days" ) );
$timestamp = strtotime( "next {$day_name} {$schedule_time}" . ' ' . wp_timezone_string() );
} else {
if ( $move_to_next_period ) {
// As we're adding $next_day_num as additional days in next month, we need to deduct it by one.
-- $next_day_num;
$timestamp = strtotime( "+{$next_day_num} days {$schedule_time}" . ' ' . wp_timezone_string(), strtotime( 'first day of next month' ) );
} else {
$days_diff = $next_day_num - $current_day_num;
$timestamp = strtotime( "+{$days_diff} days {$schedule_time}" . ' ' . wp_timezone_string() );
}
}
}
break;
default:
$timestamp = null;
}
return $timestamp;
}
}
<?php
/**
* Schedule to sync plugin with latest scan results from BLC API.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Schedule_Events\Sync_Scan_Results
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Scheduled_Events\Sync_Scan_Results;
// Abort if called directly.
defined( 'WPINC' ) || die;
// use WPMUDEV_BLC\App\Http_Requests\Sync_Scan_Results\Controller as Scan_API;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use WPMUDEV_BLC\Core\Traits\Cron;
use WPMUDEV_BLC\Core\Utils\Utilities;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Schedule_Events\Sync_Scan_Results
*/
class Controller extends Base {
use Cron;
/**
* WP Cron hook to execute when event is run.
*
* @var string
*/
public $cron_hook_name = 'blc_schedule_sync_scan_results';
/**
* BLC settings from options table.
*
* @var array
*/
private $settings = null;
/**
* Init Mailer
*
* @since 2.0.0
*
* @return void
*/
public function init() {
add_action( 'wpmudev_blc_plugin_deactivated', array( $this, 'deactivate_cron' ) );
if ( wp_doing_ajax() || Settings::instance()->get( 'use_legacy_blc_version' ) ) {
return;
}
$this->setup_cron();
}
/**
* Prepares vars
*
* @return void
*/
public function prepare_vars() {
$this->cron_interval_title = 'hourly';
$this->timestamp = time() + MINUTE_IN_SECONDS;
$this->cron_interval_title = 'twicedaily';
}
/**
* Gives the cron interval.
*
* @return string
*/
public function ___get_cron_interval_title() {
return 'twicedaily';
}
/**
* Returns the scheduled event's hook name.
* Overriding Trait's method.
*/
public function get_hook_name() {
return $this->cron_hook_name;
}
/**
* Starts the scheduled scan.
*/
public function process_scheduled_event() {
Utilities::log( 'Scheduled event for sync started' );
$scan = new \WPMUDEV_BLC\App\Http_Requests\Sync_Scan_Results\Controller();
$scan->start();
}
}
<?php
/**
* Controller for Recipient activation virtual post.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Emails\Recipient_Activation
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Virtual_Posts\Recipient_Activation;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Controllers\Virtual_Post;
use WPMUDEV_BLC\App\Webhooks\Recipient_Activation\Controller as Recipient_Activation;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Virtual_Posts\Recipient_Activation
*/
class Controller extends Virtual_Post {
public function prepare_vars() {
$this->post_title = __( 'Broken links reports', 'broken-link-checker' );
}
/**
* @param string $the_content
*
* @return string
*/
protected function post_content( string $the_content = '' ) {
$activated_recipient = Recipient_Activation::instance()->activated_recipient;
$action = isset( $_GET['action'] ) && in_array(
$_GET['action'],
array(
'activate',
'cancel',
)
) ?
$_GET['action'] : null;
if ( empty( $activated_recipient ) || is_null( $action ) ) {
global $wp_query;
$wp_query->set_404();
status_header( 404 );
nocache_headers();
include get_query_template( '404' );
die();
}
ob_start();
if ( 'activate' === $action ) {
View::instance()->render_activation_message( $activated_recipient );
} elseif ( 'cancel' === $action ) {
View::instance()->render_cancellation_message( $activated_recipient );
}
return ob_get_clean();
}
public function can_load_virtual_post() {
global $wp;
return ! empty( $wp->query_vars ) && array_key_exists( Recipient_Activation::instance()->webhook_tag, $wp->query_vars );
}
}
<?php
/**
* Virtual post for Recipient Activation view.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Admin_Pages\Settings
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Virtual_Posts\Recipient_Activation;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
/**
* Class View
*
* @package WPMUDEV_BLC\App\Virtual_Posts\Recipient_Activation
*/
class View extends Base {
/**
* Render the output.
*
* @since 2.0.0
*
* @return void Render the output.
*/
public function render_activation_message( $params = array() ) {
$cancellation_link = $params['cancellation_link'] ?? '';
$cancelation_message = $this->cancellation_instructions_message( $cancellation_link );
?>
<div style="width: 100%; margin: auto;" class="blc-vpost blc-activation-message">
<h4>
<?php esc_html_e( 'You have been added in broken links reports recipients.', 'broken-link-checker' ); ?>
</h4>
<p>
<?php
printf(
/* translators: 1: Recipient name 2: Opening link tag 3; Closing link tag */
esc_html__(
'Hi %1$s. You have become a recipient of site\'s broken links reports. You can continue to site from %2$shere%3$s.',
'broken-link-checker'
),
$params['name'],
'<a href="' . site_url() . '">',
'</a>'
);
?>
</p>
<p>
<?php echo $cancelation_message; ?>
</p>
</div>
<?php
}
protected function cancellation_instructions_message( string $cancellation_link = '' ) {
$message = '';
if ( ! empty( $cancellation_link ) ) {
$message = sprintf(
/* translators: 1: Opening link tag 2; Closing link tag */
esc_html__(
'If you do not wish to receive these reports in your email, you can contact site admins or click %1$shere to cancel email reports instantly%2$s.',
'broken-link-checker'
),
'<a href="' . esc_html( $cancellation_link ) . '">',
'</a>'
);
} else {
$message = esc_html__(
'If you do not wish to receive these reports in your email, you can contact site admins.',
'broken-link-checker'
);
}
return $message;
}
public function render_cancellation_message( $params = array() ) {
?>
<div style="width: 100%; margin: auto;" class="blc-vpost blc-activation-message">
<h4>
<?php
esc_html_e( 'Your email has been successfully removed from broken links recipients list.', 'broken-link-checker' );
?>
</h4>
<p>
<?php
printf(
/* translators: 1: Recipient name 2: Opening link tag 3; Closing link tag */
esc_html__(
'Hi %1$s. You should stop receiving broken links reports for this site. You can visit site from %2$shere%3$s.',
'broken-link-checker'
),
$params['name'],
'<a href="' . site_url() . '">',
'</a>'
);
?>
</p>
</div>
<?php
}
}
<?php
/**
* Controller for Recipient activation webhook.
*
* @link https://wordpress.org/plugins/broken-link-checker/
* @since 2.0.0
*
* @author WPMUDEV (https://wpmudev.com)
* @package WPMUDEV_BLC\App\Webhooks\Recipient_Activation
*
* @copyright (c) 2022, Incsub (http://incsub.com)
*/
namespace WPMUDEV_BLC\App\Webhooks\Recipient_Activation;
// Abort if called directly.
defined( 'WPINC' ) || die;
use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
use WPMUDEV_BLC\Core\Controllers\Webhook;
/**
* Class Controller
*
* @package WPMUDEV_BLC\App\Emails\Recipient_Activation
*/
class Controller extends Webhook {
/**
* The webhook tag.
*
* @var string $webhook The webhook tag
*/
public $webhook_tag = 'blc-activate-recipient';
/**
* The activated recipient data in an array. Retrieved by activation code.
*
* @var array $activated_recipient
*/
public $activated_recipient = array();
/**
* The plugin settings.
*
* @var array $settings
*/
public $settings = array();
/**
* Prepares the class properties.
*
* @return void
*/
public function prepare_vars() {
$this->webhook_title = __( 'Activate broken links recipient', 'broken-link-checker' );
}
/**
* Executes the webhook action(s).
*
* @param $wp
*
* @return void
*/
public function webhook_action( &$wp ) {
$this->settings = Settings::instance()->get();
$activation_key = $_GET['activation_code'] ?? null;
$action = isset( $_GET['action'] ) && in_array( $_GET['action'], array(
'activate',
'cancel'
) ) ?
$_GET['action'] : null;
if ( is_null( $action ) ) {
return;
}
$this->activated_recipient = $this->get_recipient_by_key( $activation_key );
if ( empty( $this->activated_recipient ) ) {
return;
}
switch ( $action ) {
case 'activate' :
if ( $this->set_recipient_status( $this->activated_recipient ) ) {
$this->activated_recipient['cancellation_link'] = add_query_arg( array(
'action' => 'cancel',
'activation_code' => esc_html( $activation_key ),
), $this->webhook_url() );
}
break;
case 'cancel' :
if ( isset( $this->activated_recipient['user_id'] ) ) {
$this->unset_registered_recipient( intval( $this->activated_recipient['user_id'] ) );
} else {
$this->set_recipient_status( $this->activated_recipient, false );
}
break;
}
}
protected function get_recipient_by_key( string $key = '' ) {
if ( empty( $key ) ) {
return false;
}
$key_parts = explode( '_', base64_decode( $key ) );
if ( count( $key_parts ) < 2 ) {
return array();
}
$hashed_email = $key_parts[0];
$recipient_key = sanitize_text_field( $key_parts[1] );
$schedule = $this->settings['schedule'] ?? array();
$recipient = array();
//if ( empty( $schedule ) || ( empty( $schedule['emailrecipients'] ) && empty( $schedule['registered_recipients_data'] ) ) ) {
//return array();
//}
if ( ! empty( $schedule['emailrecipients'] ) ) {
$recipient = array_filter(
$schedule['emailrecipients'],
function ( $recipient_data ) use ( $recipient_key, $hashed_email ) {
return isset( $recipient_data['key'] ) &&
$recipient_data['key'] === $recipient_key &&
$hashed_email === md5( $recipient_data['email'] );
}
);
}
if ( empty( $recipient ) ) {
if ( ! empty( $schedule['registered_recipients_data'] ) ) {
foreach ( $schedule['registered_recipients_data'] as $user_id => $user_data ) {
$user = null;
if ( md5( $user_data['email'] ) === $hashed_email ) {
$user = get_user_by( 'email', sanitize_email( $user_data['email'] ) );
}
if ( $user instanceof \WP_User ) {
$recipient = array(
'key' => $user_data['key'],
'name' => $user->display_name,
'email' => $user->user_email,
'user_id' => $user->ID,
);
break;
}
}
}
if ( empty( $recipient ) ) {
$user_key_parts = explode( '|', $recipient_key );
$user = ! empty( $user_key_parts[1] ) ? get_userdata( intval( $user_key_parts[1] ) ) : null;
if ( $user instanceof \WP_User && md5( $user->user_email ) === $hashed_email ) {
$recipient = array(
'key' => $recipient_key,
'name' => $user->display_name,
'email' => $user->user_email,
'user_id' => $user->ID,
);
}
}
} else {
$recipient = array_values( $recipient )[0];
}
return $recipient;
}
/**
* Changes the recipient status for recipients added by email.
*
* @param array $recipient_data
* @param bool $new_status
*
* @return bool
*/
protected function set_recipient_status( array $recipient_data = array(), bool $new_status = true ) {
if (
empty( $recipient_data ) ||
! isset( $recipient_data['email'] ) ||
! isset( $this->settings['schedule'] ) ||
( empty( $this->settings['schedule']['emailrecipients'] ) && empty( $this->settings['schedule']['registered_recipients_data'] ) )
) {
return false;
}
Settings::instance()->init();
$this->settings['schedule']['emailrecipients'] = array_map(
function ( $recipient ) use ( $recipient_data, $new_status ) {
if ( isset( $recipient['email'] ) && $recipient['email'] === $recipient_data['email'] ) {
$recipient['confirmed'] = boolval( $new_status );
}
return $recipient;
},
$this->settings['schedule']['emailrecipients']
);
Settings::instance()->set( array( 'schedule' => $this->settings['schedule'] ) );
Settings::instance()->save();
return true;
}
/**
* Removes a recipient by user id.
*
* @param int|null $user_id
*
* @return false|void
*/
protected function unset_registered_recipient( int $user_id = null ) {
if ( empty( $user_id ) ) {
return false;
}
$user_key = array_search(
$user_id,
$this->settings['schedule']['recipients']
);
if (
! is_numeric( $user_key ) ||
empty( $this->settings['schedule']['recipients'][ $user_key ] ) ||
intval( $this->settings['schedule']['recipients'][ $user_key ] ) !== $user_id
) {
return false;
}
unset( $this->settings['schedule']['recipients'][ $user_key ] );
$this->settings['schedule']['recipients'] = array_values( $this->settings['schedule']['recipients'] );
unset( $this->settings['schedule']['registered_recipients_data'][ $user_id ] );
Settings::instance()->set( array( 'schedule' => $this->settings['schedule'] ) );
Settings::instance()->save();
}
}
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.