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
1 <?php
2 /**
3 * Plugin action links.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Action_Links\Plugin
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Action_Links\Plugin;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
20 use WPMUDEV_BLC\Core\Utils\Utilities;
21 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
22
23 /**
24 * Class Controller
25 *
26 * @package WPMUDEV_BLC\App\Action_Links\Plugin
27 */
28 class Controller extends Base {
29
30 public function init() {
31 Settings::instance()->init();
32
33 $plugin_file = plugin_basename( WPMUDEV_BLC_PLUGIN_FILE );
34
35 add_filter( "plugin_action_links_{$plugin_file}", array( $this, 'action_links' ), 10, 4 );
36 add_filter( "network_admin_plugin_action_links_{$plugin_file}", array( $this, 'action_links' ), 10, 4 );
37 }
38
39 /**
40 * Sets the plugin action links in plugins page.
41 *
42 * @param array $actions
43 * @param string $plugin_file
44 * @param array $plugin_data
45 * @param string $context
46 *
47 * @return array
48 */
49 public function action_links( $actions = array(), $plugin_file = '', $plugin_data = null, $context = '' ) {
50 $new_actions = array();
51
52 if ( ! is_array( $actions ) ) {
53 $actions = array();
54 }
55
56 if ( boolval( Settings::instance()->get( 'use_legacy_blc_version' ) ) ) {
57 $new_actions = $this->legacy_action_links();
58 } else {
59 $new_actions = $this->get_action_links();
60 }
61
62 return apply_filters(
63 'wpmudev_blc_plugin_action_links',
64 wp_parse_args( $actions, $new_actions ),
65 $new_actions,
66 $actions,
67 $plugin_file,
68 $plugin_data,
69 $context
70 );
71 }
72
73 /**
74 * Returns the plugin's action links.
75 *
76 * @return array
77 */
78 public function get_action_links () {
79 $actions = array();
80 $dashboard_url = menu_page_url( 'blc_dash', false );
81 $dashboard_label = esc_html__( 'Dashboard', 'broken-link-checker' );
82 $docs_url = 'https://wpmudev.com/docs/wpmu-dev-plugins/broken-link-checker';
83 $docs_label = esc_html__( 'Docs', 'broken-link-checker' );
84
85 if ( is_multisite() && Utilities::is_network_admin() ) {
86 $admin_url = get_admin_url( get_main_site_id(), 'admin.php' );
87 $dashboard_url = add_query_arg(
88 array(
89 'page' => 'blc_dash',
90 ),
91 $admin_url
92 );
93 }
94
95 $actions['dashboard'] = "<a href=\"{$dashboard_url}\">{$dashboard_label}</a>";
96 $actions['docs'] = "<a href=\"{$docs_url}\" target=\"_blank\">{$docs_label}</a>";
97
98 return $actions;
99 }
100
101 /**
102 * Returns the plugin action links in plugins page when legacy mode is active.
103 *
104 * @return array
105 */
106 public function legacy_action_links() {
107 $actions = array();
108 $dashboard_url = menu_page_url( 'blc_dash', false );
109 $settings_url = menu_page_url( 'link-checker-settings', false );
110 $link_url = menu_page_url( 'view-broken-links', false );
111 $dashboard_label = esc_html__( 'Dashboard', 'broken-link-checker' );
112 $settings_label = esc_html__( 'Settings', 'broken-link-checker' );
113 $links_label = esc_html__( 'Broken links', 'broken-link-checker' );
114
115
116 if ( is_multisite() && Utilities::is_network_admin() ) {
117 $admin_url = get_admin_url( get_main_site_id(), 'admin.php' );
118 $dashboard_url = add_query_arg(
119 array(
120 'page' => 'blc_dash',
121 ),
122 $admin_url
123 );
124 $settings_url = add_query_arg(
125 array(
126 'page' => 'link-checker-settings',
127 ),
128 $admin_url
129 );
130 $link_url = add_query_arg(
131 array(
132 'page' => 'view-broken-links',
133 ),
134 $admin_url
135 );
136 }
137
138 $actions['dashboard'] = "<a href=\"{$dashboard_url}\">{$dashboard_label}</a>";
139 $actions['settings'] = "<a href=\"{$settings_url}\">{$settings_label}</a>";
140 $actions['links'] = "<a href=\"{$link_url}\">{$links_label}</a>";
141
142 if ( is_multisite() && ! ( is_main_site() || Utilities::is_network_admin() ) ) {
143 unset( $actions['dashboard'] );
144 }
145
146 return $actions;
147 }
148
149 }
1 <?php
2 /**
3 * BLC admin modal for legacy screens.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Notices\Legacy
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Admin_Modals\Legacy;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Controllers\Admin_Modals;
20 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
21
22 use WPMUDEV_BLC\Core\Utils\Utilities;
23 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
24 use WPMUDEV_BLC\Core\Traits\Enqueue;
25
26 /**
27 * Class Controller
28 *
29 * @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
30 */
31 class Controller extends Base {
32 /**
33 * Use the Enqueue Trait.
34 *
35 * @since 2.0.0
36 */
37 use Enqueue;
38
39 /**
40 * The unique id that can be used by react.
41 *
42 * @var int $unique_id
43 *
44 * @since 2.0.0
45 */
46 public static $unique_id = null;
47
48 /**
49 * The admin pages the notice will be visible at.
50 *
51 * @var array $admin_pages
52 *
53 * @since 2.0.0
54 */
55 protected $admin_pages = array();
56
57 /**
58 * Init function.
59 *
60 * @since 2.0.0
61 */
62 public function init() {
63 add_action( 'current_screen', array( $this, 'boot' ), 11 );
64 }
65
66 /**
67 * Boots modal parts.
68 */
69 public function boot() {
70 if ( $this->can_boot() ) {
71 add_filter( 'admin_body_class', array( $this, 'admin_body_classes' ), 999 );
72 add_action( 'admin_footer', array( $this, 'output' ) );
73
74 $this->unique_id = Utilities::get_unique_id();
75
76 $this->prepare_props();
77 $this->prepare_scripts();
78 }
79 }
80
81 /**
82 * Checks if admin page actions/scripts should load in current screen.
83 *
84 * @return boolean Checks if admin page actions/scripts should load. Useful for enqueuing scripts.
85 * @since 2.0.0
86 */
87 public function can_boot() {
88 /*
89 We use the Utilities::$value_provider array variable.
90 This variable can hold values that can be used from different classes which should help avoid checking
91 same conditions multiple times.
92 In this case we are using `boot_admin_legacy_pages` key which is also used in
93 WPMUDEV_BLC\App\Admin_Notices\Legacy\Controller class.
94 */
95 if ( ! isset( Utilities::$value_provider['boot_admin_legacy_pages'] ) ) {
96 Utilities::$value_provider['boot_admin_legacy_pages'] = Utilities::is_admin_screen( $this->admin_pages );
97 }
98
99 $legacy_option = Settings::instance()->get( 'use_legacy_blc_version' );
100
101 return Utilities::$value_provider['boot_admin_legacy_pages'] && empty( $legacy_option );
102 }
103
104 /**
105 * Prepares the properties of the Admin Page.
106 *
107 * @return void
108 * @since 2.0.0
109 */
110 public function prepare_props() {
111 /*
112 * Set the admin pages the notice will be visible at.
113 */
114 $this->admin_pages = array(
115 'view-broken-links',
116 'link-checker-settings',
117 );
118 }
119
120 /**
121 * Admin Menu Callback.
122 *
123 * @return void The callback function of the Admin Menu Page.
124 * @since 2.0.0
125 */
126 public function output() {
127 View::instance()->render( array( 'unique_id' => $this->unique_id ) );
128 }
129
130 /**
131 * Register css files for admin page.
132 *
133 * @return array
134 */
135 public function set_admin_styles() {
136 return array(
137 'blc_sui' => array(
138 'src' => $this->styles_dir . 'shared-ui-' . BLC_SHARED_UI_VERSION_NUMBER . '.min.css',
139 'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
140 ),
141 'blc_legacy_modal' => array(
142 'src' => $this->styles_dir . 'legacy-modal.min.css',
143 'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
144 ),
145 );
146 }
147
148 /**
149 * Adds the blc legacy class to body classes for styling purposes.
150 *
151 * @param string $classes The body classes.
152 * @return string Returns the body classes.
153 * @since 2.0.0
154 */
155 public function admin_body_classes( string $classes = '' ) {
156 return $classes . ' blc-show-legacy-popup';
157 }
158
159 /**
160 * Register scripts for the admin page.
161 *
162 * @return array Register scripts for the admin page.
163 * @since 2.0.0
164 */
165 public function set_admin_scripts() {
166 $script_data = include WPMUDEV_BLC_DIR . 'assets/js/activation-popup/main.asset.php';
167 $dependencies = $script_data['dependencies'] ?? array(
168 'react',
169 'wp-element',
170 'wp-i18n',
171 'wp-is-shallow-equal',
172 'wp-polyfill',
173 );
174 $version = $script_data['version'] ?? WPMUDEV_BLC_SCIPTS_VERSION;
175
176 return array(
177 'blc_legacy_popup' => array(
178 'src' => $this->scripts_dir . 'legacy-modal/main.js',
179 'deps' => $dependencies,
180 'ver' => $version,
181 'in_footer' => true,
182 'localize' => array(
183 'blc_legacy_popup' => array(
184 'data' => array(
185 'rest_url' => esc_url_raw( rest_url() ),
186 'settings_endpoint' => '/wpmudev_blc/v1/settings',
187 'unique_id' => $this->unique_id,
188 'nonce' => wp_create_nonce( 'wp_rest' ),
189 'site_connected' => false,
190 'show_legacy_link' => boolval( Settings::instance()->get( 'use_legacy_blc_version' ) ),
191 'legacy_blc_url' => admin_url( 'admin.php?page=view-broken-links' ),
192 'blc_url' => admin_url( 'admin.php?page=blc_dash' ),
193 ),
194 'labels' => array(
195 'error_messages' => array(
196 'general' => __( 'Something went wrong here.', 'broken-link-checker' ),
197 ),
198 ),
199 ),
200 ),
201 'translate' => true,
202 ),
203 // END OF blc_activation_popup.
204 );
205 }
206
207 }
1 <?php
2 /**
3 * BLC Dashboard admin page view.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Notice\Legacy
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Admin_Modals\Legacy;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
20
21 /**
22 * Class View
23 *
24 * @package WPMUDEV_BLC\App\Admin_Notice\Legacy
25 */
26 class View extends Base {
27 /**
28 * The unique id that can be used by react. Sent over from Controller.
29 *
30 * @var int $unique_id
31 *
32 * @since 2.0.0
33 */
34 public static $unique_id = null;
35
36 /**
37 * Renders the output.
38 *
39 * @param array $params Optional parameters ideal to hold the uniqueu id generated in controller.
40 * @return void Renders the output.
41 * @since 2.0.0
42 */
43 public function render( $params = array() ) {
44 self::$unique_id = isset( $params['unique_id'] ) ? $params['unique_id'] : null;
45
46 ?>
47 <div class="sui-wrap wrap-blc wrap-blc-legacy-modal <?php echo 'wrap-' . esc_attr( self::$unique_id ); ?>">
48 <?php
49 $this->render_body();
50 ?>
51 </div>
52 <?php
53 }
54
55 /**
56 * Renders view body.
57 */
58 public function render_body() {
59 ?>
60 <div id="<?php esc_attr_e( self::$unique_id ); ?>"></div>
61 <?php
62 }
63 }
1 <?php
2 /**
3 * Controller for admin notices.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
20 use WPMUDEV_BLC\Core\Traits\Enqueue;
21 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
22 use WPMUDEV_BLC\Core\Models\Option;
23 use WPMUDEV_BLC\Core\Activation;
24 use WPMUDEV_BLC\Core\Utils\Utilities;
25
26 /**
27 * Class Controller
28 *
29 * @package WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation
30 */
31 class Controller extends Base {
32 /**
33 * Use the Enqueue Trait.
34 *
35 * @since 2.0.0
36 */
37 use Enqueue;
38
39 /**
40 * @var null|array The plugin settings
41 */
42 private $settings = null;
43
44 /**
45 * Init Admin_Modal
46 *
47 * @since 2.0.0
48 *
49 * @return void
50 */
51 public function init() {
52 $this->settings = new Settings();
53
54 add_action( 'load-plugins.php', array( $this, 'boot' ) );
55 }
56
57 /**
58 * Show the modal on plugins.php page if allowed.
59 *
60 * @since 2.0.0
61 *
62 * @return void
63 */
64 public function boot() {
65 if ( ! $this->can_boot() ) {
66 return;
67 }
68
69 add_action( 'admin_footer', array( $this, 'show_footer_output' ) );
70 add_filter( 'admin_body_class', array( $this, 'admin_body_classes' ), 999 );
71
72 $this->unique_id = Utilities::get_unique_id();
73
74 $this->prepare_scripts();
75 }
76
77 /**
78 * Check if we can show model output in footer.
79 * Called only on plugins.php page.
80 */
81 public function show_footer_output() {
82 $this->settings->set( wp_parse_args( $this->settings->get(), $this->settings->default ) );
83 $this->settings->set( array( 'activation_modal_shown' => true ) );
84 $this->settings->save();
85 $this->output();
86 }
87
88 /**
89 * Checks if modal can load.
90 *
91 * @since 2.0.0
92 *
93 * @return boolean Checks if modal can load. Useful for enqueuing scripts.
94 */
95 protected function can_boot() {
96 return empty( $this->settings->get( 'activation_modal_shown' ) ) && ! Utilities::is_subsite();
97 }
98
99 /**
100 * Modal output.
101 *
102 * @since 1.0.0
103 *
104 * @return void The callback function of the Admin Menu Page.
105 */
106 public function output() {
107 View::instance()->render(
108 array(
109 'unique_id' => $this->unique_id,
110 )
111 );
112 }
113
114 /**
115 * Register scripts for the admin page.
116 *
117 * @since 1.0.0
118 *
119 * @return array Register scripts for the admin page.
120 */
121 public function set_admin_scripts() {
122 $script_data = include WPMUDEV_BLC_DIR . 'assets/js/activation-popup/main.asset.php';
123 $dependencies = $script_data['dependencies'] ?? array(
124 'react',
125 'wp-element',
126 'wp-i18n',
127 'wp-is-shallow-equal',
128 'wp-polyfill',
129 );
130 $version = $script_data['version'] ?? WPMUDEV_BLC_SCIPTS_VERSION;
131 $legacy_option_key = 'wsblc_options';
132 $legacy_option = new Option( array( 'name' => $legacy_option_key ) );
133 // Defines if the legacy link at the very bottom of the modal
134 $show_legacy_link = ! empty( $legacy_option->get() );
135 $legacy_installation_dt = $legacy_option->get( 'first_installation_timestamp' );
136 $installation_dt = intval( $this->settings->get( 'installation_timestamp' ) );
137 $legacy_pre_installed = ( ! empty( $legacy_installation_dt ) && 1 < round( abs( $legacy_installation_dt - $installation_dt ) / 60 ) );
138
139 return array(
140 'blc_activation_popup' => array(
141 'src' => $this->scripts_dir . 'activation-popup/main.js',
142 'deps' => $dependencies,
143 'ver' => $version,
144 'in_footer' => true,
145 'localize' => array(
146 'blc_activation_popup' => array(
147 'data' => array(
148 'rest_url' => esc_url_raw( rest_url() ),
149 'settings_endpoint' => '/wpmudev_blc/v1/settings',
150 'unique_id' => $this->unique_id,
151 'nonce' => wp_create_nonce( 'wp_rest' ),
152 'site_connected' => false,
153 'show_legacy_link' => $show_legacy_link,
154 'legacy_pre_installed' => $legacy_pre_installed,
155 'legacy_blc_url' => admin_url( 'admin.php?page=view-broken-links' ),
156 'blc_url' => admin_url( 'admin.php?page=blc_dash' ),
157 'dash_api_active' => ( Utilities::get_dashboard_api() instanceof \WPMUDEV_Dashboard_Api || Utilities::get_dashboard_api() instanceof WPMUDEV_Dashboard_Api ),
158 ),
159 'labels' => array(
160 'error_messages' => array(
161 'general' => __( 'Something went wrong here.', 'broken-link-checker' ),
162 ),
163 ),
164 ),
165 ),
166 'translate' => true,
167 ),
168 // END OF blc_activation_popup
169 );
170 }
171
172 /**
173 * Register css files for admin page.
174 *
175 * @return array
176 */
177 public function set_admin_styles() {
178 return array(
179 'blc_sui' => array(
180 'src' => $this->styles_dir . 'shared-ui-' . BLC_SHARED_UI_VERSION_NUMBER . '.min.css',
181 'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
182 ),
183 'blc_dashboard' => array(
184 'src' => $this->styles_dir . 'plugin-activation.min.css',
185 'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
186 ),
187 );
188 }
189
190 /**
191 * Adds SUI admin body class. It will be used in all admin pages.
192 *
193 * @param $classes
194 *
195 * @return string
196 */
197 public function admin_body_classes( $classes ) {
198 $sui_classes = explode( ' ', $classes );
199 $sui_classes[] = BLC_SHARED_UI_VERSION;
200
201 if ( apply_filters( 'wpmudev_branding_hide_branding', false ) ) {
202 $sui_classes[] = 'wpmudev-hide-branding';
203 }
204
205 return join( ' ', $sui_classes );
206 }
207 }
1 <?php
2 /**
3 * Plugin activation modal view.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
20
21
22 /**
23 * Class View
24 *
25 * @package WPMUDEV_BLC\App\Admin_Modals\Plugin_Activation
26 */
27 class View extends Base {
28 /**
29 * The unique id that can be used by react. Sent over from Controller.
30 *
31 * @var int $unique_id
32 *
33 * @since 2.0.0
34 */
35 public static $unique_id = null;
36
37 /**
38 * Render the output.
39 *
40 * @since 2.0.0
41 *
42 * @return void Render the output.
43 */
44 public function render( $params = array() ) {
45 self::$unique_id = isset( $params['unique_id'] ) ? $params['unique_id'] : null;
46
47 ?>
48 <div class="sui-wrap wrap-blc wrap-blc-activation-modal <?php echo 'wrap-' . esc_attr( self::$unique_id ); ?>">
49 <?php
50 $this->render_body();
51 ?>
52 </div>
53 <?php
54 }
55
56 public function render_body() {
57 ?>
58 <div id="<?php esc_attr_e( self::$unique_id ); ?>"></div>
59 <?php
60 }
61 }
1 <?php
2 /**
3 * BLC admin notice for legacy screens
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Notices\Legacy
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Admin_Notices\Legacy;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Controllers\Admin_Notice;
20 use WPMUDEV_BLC\Core\Utils\Utilities;
21
22 /**
23 * Class Controller
24 *
25 * @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
26 */
27 class Controller extends Admin_Notice {
28 /**
29 * Admin Menu Callback.
30 *
31 * @return void The callback function of the Admin Menu Page.
32 * @since 1.0.0
33 *
34 */
35 public function output() {
36 View::instance()->render();
37 }
38
39 /**
40 * Register css files for admin page.
41 *
42 * @return array
43 */
44 public function set_admin_styles() {
45 return array(
46 'blc_legacy_notice' => [
47 'src' => $this->styles_dir . 'legacy-notice.min.css',
48 'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
49 ],
50 );
51 }
52
53 /**
54 * Adds Page specific hooks. Extends $this->actions().
55 *
56 * @return void
57 * @since 2.0.0
58 *
59 */
60 public function notice_hooks() {
61 if ( $this->can_boot() ) {
62 add_filter( 'admin_body_class', array( $this, 'admin_dash_page_classes' ), 999 );
63 }
64 }
65
66 /**
67 * Returns true if module component should load, else returns false.
68 *
69 * @return void
70 * @since 2.0.0
71 *
72 */
73 public function can_boot() {
74 /**
75 * Until multisites are officially supported, BLC v2 menus are disabled in subsites.
76 * Legacy menus are loaded instead. Lagecy admin notification does not need to appear on subsites at this point.
77 */
78 if ( Utilities::is_subsite() ) {
79 return;
80 }
81
82 /* We use the Utilities::$value_provider array variable.
83 This variable can hold values that can be used from different classes which should help avoid checking
84 same conditions multiple times.
85 In this case we are using `boot_admin_legacy_pages` key which is also used in
86 WPMUDEV_BLC\App\Admin_Modals\Legacy\Controller class.
87 */
88 if ( ! isset( Utilities::$value_provider[ 'boot_admin_legacy_pages' ] ) ) {
89 Utilities::$value_provider['boot_admin_legacy_pages'] = Utilities::is_admin_screen( $this->admin_pages );
90 }
91
92 return Utilities::$value_provider[ 'boot_admin_legacy_pages' ];
93 }
94
95 public function admin_dash_page_classes( $classes ) {
96 return $classes . ' blc-show-legacy-notice';
97 }
98
99 /**
100 * Prepares the properties of the Admin Page.
101 *
102 * @return void
103 * @since 1.0.0
104 *
105 */
106 public function prepare_props() {
107 /*
108 * Se the admin pages the notice will be visible at.
109 */
110 $this->admin_pages = array(
111 'view-broken-links',
112 'link-checker-settings',
113 );
114 }
115 }
1 <?php
2 /**
3 * BLC Dashboard admin page view.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Notice\Legacy
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Admin_Notices\Legacy;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
20
21 /**
22 * Class View
23 *
24 * @package WPMUDEV_BLC\App\Admin_Notice\Legacy
25 */
26 class View extends Base {
27 /**
28 * Renders the output.
29 *
30 * @return void Renders the output.
31 * @since 2.0.0
32 *
33 */
34 public function render( $params = array() ) {
35 $this->render_body();
36 }
37
38 public function render_body() {
39 $dashborad_url = admin_url( 'admin.php?page=blc_dash' );
40 $message = sprintf(
41 __( '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' ),
42 $dashborad_url
43 );
44
45 printf( '
46 <div class="wrap wrap-blc-legacy-notice notice notice-info">
47 <table>
48 <tr>
49 <td><span class="sui-notice-icon blc-icon sui-md" aria-hidden="true"></span></td>
50 <td>%1$s</td>
51 </tr>
52 </table>
53 </div>
54 ',
55 $message
56 );
57 }
58 }
1 <?php
2 /**
3 * BLC admin notice for main site's onboarding page
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Notices\Multisite
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Admin_Notices\Multisite;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Controllers\Admin_Notice;
20 use WPMUDEV_BLC\Core\Utils\Utilities;
21 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
22
23 use WPMUDEV_BLC\Core\Traits\Dashboard_API;
24
25 /**
26 * Class Controller
27 *
28 * @package WPMUDEV_BLC\App\Admin_Notices\Multisite
29 */
30 class Controller extends Admin_Notice {
31 /**
32 * Use the Dashboard_API Trait.
33 *
34 * @since 2.0.0
35 */
36 use Dashboard_API;
37
38 public function init() {
39 parent::init();
40 add_action( 'wp_ajax_wpmudev_blc_multisite_notification_dismiss', array(
41 $this,
42 'dismiss_multisite_notification'
43 ) );
44 }
45
46 /**
47 * Admin Menu Callback.
48 *
49 * @since 1.0.0
50 *
51 * @return void The callback function of the Admin Menu Page.
52 */
53 public function output() {
54 View::instance()->render( array(
55 'site_connected' => (bool) self::site_connected(),
56 'use_legacy' => (bool) Settings::instance()->get( 'use_legacy_blc_version' )
57 ) );
58 }
59
60 /**
61 * Register css files for admin page.
62 *
63 * @return array
64 */
65 public function set_admin_styles() {
66 return array(
67 'blc_multisite_notice' => [
68 'src' => $this->styles_dir . 'multisite-notice.min.css',
69 'ver' => WPMUDEV_BLC_SCIPTS_VERSION,
70 ],
71 );
72 }
73
74 /**
75 * Adds Page specific hooks. Extends $this->actions().
76 *
77 * @since 2.0.0
78 *
79 * @return void
80 */
81 public function notice_hooks() {
82 if ( $this->can_boot() ) {
83 add_filter( 'admin_body_class', array( $this, 'admin_dash_page_classes' ), 999 );
84 add_action( 'admin_enqueue_scripts', array( $this, 'inline_script' ) );
85
86 }
87 }
88
89 /**
90 * Adds the inline script that handles the notification dismiss. Loads it from View.
91 * @return void
92 */
93 public function inline_script() {
94 wp_add_inline_script( 'blc_dashboard', View::instance()->render_inline_script() );
95 }
96
97 public function dismiss_multisite_notification() {
98 check_ajax_referer( 'wpmudev-blc-multisite-notification-dismiss-nonce', 'security' );
99
100 Settings::instance()->init();
101 Settings::instance()->set( array( 'show_multisite_notice' => false ) );
102 Settings::instance()->save();
103 }
104
105 /**
106 * Returns true if module component should load, else returns false.
107 *
108 * @since 2.0.0
109 *
110 * @return boolean
111 */
112 public function can_boot() {
113 if ( is_multisite() && Settings::instance()->get( 'show_multisite_notice' ) && Utilities::is_admin_screen( 'blc_dash' ) ) {
114 return true;
115 }
116
117 return false;
118 }
119
120 public function admin_dash_page_classes( $classes ) {
121 return $classes . ' blc-show-multisite-notice';
122 }
123
124 /**
125 * Prepares the properties of the Admin Page.
126 *
127 * @since 1.0.0
128 *
129 * @return void
130 */
131 public function prepare_props() {
132 }
133 }
1 <?php
2 /**
3 * BLC Dashboard admin page view.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Notice\Multisite
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Admin_Notices\Multisite;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
20
21 /**
22 * Class View
23 *
24 * @package WPMUDEV_BLC\App\Admin_Notice\Multisite
25 */
26 class View extends Base {
27 /**
28 * Renders the output.
29 *
30 * @since 2.0.0
31 *
32 * @return void Renders the output.
33 */
34 public function render( $params = array() ) {
35 $use_legacy = $params['use_legacy'] ?? false;
36 $site_connected = $params['site_connected'] ?? false;
37
38
39 echo '<div class="sui-wrap blc-multisite-notice-legacy">';
40
41 if ( $use_legacy || ! $site_connected ) {
42 $this->render_onboarding_notification();
43 } else {
44 $this->render_dashboard_notification();
45 }
46
47 echo '</div>';
48 }
49
50 public function render_onboarding_notification() {
51 $message = __( 'New BLC supports Multisite’s main site only and doesn’t support subsites. Subsites will continue using Legacy BLC', 'broken-link-checker' );
52
53 printf( '
54 <div class="wrap multisite-onboarding-notice notice notice-info is-dismissible">
55 <span class="notice-content">%1$s</span>
56 </div>
57 ',
58 $message
59 );
60 }
61
62 public function render_dashboard_notification() {
63 $message = __( 'New BLC supports Multisite’s main site only and doesn’t support subsites. Subsites will continue using Legacy BLC', 'broken-link-checker' );
64 $close_message = __( 'Dismiss', 'broken-link-checker' );
65
66 printf( '
67 <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;">
68 <div class="sui-notice-content">
69 <div class="sui-notice-message">
70 <span class="sui-notice-icon sui-icon-info sui-md" aria-hidden="true"></span>
71 <p>%1$s</p>
72 </div>
73 <div class="sui-notice-actions">
74 <div class="sui-tooltip sui-tooltip-bottom" data-tooltip="%2$s">
75 <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>
76 </div>
77 </div>
78 </div>
79 </div>
80 ',
81 $message,
82 $close_message
83 );
84 }
85
86 public function render_inline_script() {
87 $ajax_nonce = wp_create_nonce( 'wpmudev-blc-multisite-notification-dismiss-nonce' );
88
89 ob_start();
90 ?>
91 (function($){
92 $(document).ready(function() {
93 $( '.blc-show-multisite-notice .blc-multisite-notice-legacy .notice-dismiss' ).on( 'click', function() {
94 var data = {
95 action: 'wpmudev_blc_multisite_notification_dismiss',
96 security: '<?php echo $ajax_nonce; ?>',
97 dismiss: true
98 };
99
100 $.post(ajaxurl, data, function(response) {
101 $( '.blc-show-multisite-notice .blc-multisite-notice-legacy' ).hide( 300 );
102 });
103 } )
104
105 });
106 })(jQuery);
107 <?php
108
109 return ob_get_clean();
110 }
111 }
1 <?php
2 /**
3 * The Dashboard model
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Admin_Pages\Dashboard;
15
16 // Abort if called directly.
17 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
18 use WPMUDEV_BLC\Core\Utils\Utilities;
19
20 defined( 'WPINC' ) || die;
21
22 /**
23 * Class Settings
24 *
25 * @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
26 */
27 class Model {
28 /**
29 * Holds an array with all schedule data.
30 *
31 * @since 2.0.0
32 * @var array $settings
33 *
34 */
35 private static $settings = array();
36
37 /**
38 * Holds an array with all schedule data.
39 *
40 * @since 2.0.0
41 * @var array $schedule
42 *
43 */
44 private static $schedule = array();
45
46 /**
47 * Returns an item from settings.
48 * @return array|string|null
49 */
50 public static function get_settings_item( string $item = '', $default = null ) {
51 return isset( self::get_settings()[ $item ] ) ? self::get_settings()[ $item ] : $default;
52 }
53
54 /**
55 * Returns DB Settings from options table.
56 * @return array
57 */
58 public static function get_settings() {
59 if ( empty( self::$settings ) ) {
60 $settings = new Settings();
61 self::$settings = wp_parse_args( $settings::instance()->get(), $settings::instance()->default );
62 }
63
64 return self::$settings;
65 }
66
67 public static function use_legacy() {
68 return boolval( self::get_settings_item( 'use_legacy_blc_version' ) );
69 }
70
71 /**
72 * Returns true if scan is currently in progress else it returns false.
73 *
74 * @return bool
75 */
76 public static function scan_in_progress() {
77 return self::get_settings_item( 'scan_status' ) === 'in_progress';
78 }
79
80 /**
81 * Returns scan results stored in DB Settings option.
82 *
83 * @return array
84 */
85 public static function get_scan_results() {
86 $scan_results = array();
87 $scan_defaults = isset( Settings::instance()->default['scan_results'] ) ?
88 Settings::instance()->default['scan_results'] :
89 array();
90 $settings_scan_results = wp_parse_args( self::get_settings_item( 'scan_results' ), $scan_defaults );
91
92 array_walk_recursive(
93 $settings_scan_results,
94 function ( $item, $key ) use ( &$scan_results ) {
95 if ( ! \is_numeric( $item ) && ! $item ) {
96 $item = '-';
97 }
98
99 if ( in_array( $key, array( 'succeeded_urls', 'unique_urls', 'total_urls' ) ) ) {
100 //$item .= __( ' URLs', 'broken-link-checker' );
101 }
102
103 if ( in_array( $key, array( 'start_time', 'end_time' ) ) ) {
104 if ( in_array( $item, array( '-', 0 ) ) ) {
105 $item = '-';
106 } else {
107 $item = Utilities::timestamp_to_formatted_date( \intval( $item ), true );
108 }
109 }
110
111 if ( 'duration' === $key ) {
112 $item = Utilities::normalize_seconds_format( ( \floatval( $item ) ) );
113 }
114
115 $scan_results[ $key ] = $item;
116 }
117 );
118
119 return $scan_results;
120 }
121
122 public static function get_hours_list() {
123 $hour_list = array();
124 $hour_list_unsorted = Utilities::get_hour_list();
125
126 array_walk_recursive(
127 $hour_list_unsorted,
128 function ( $item, $key ) use ( &$hour_list ) {
129 $hour_list[] = array(
130 'key' => $key,
131 'value' => $item,
132 );
133 }
134 );
135
136 return $hour_list;
137 }
138
139 public static function get_current_user_data_formated() {
140 return self::format_recipient_user_data( get_current_user_id() );
141 }
142
143 /**
144 * @param $input
145 * @param string $input_type
146 * @param array $args
147 *
148 * @return array
149 */
150 public static function format_recipient_user_data( $input = null, string $input_type = 'id', array $args = array()
151 ) {
152 $key = null;
153 $display_name = null;
154 $roles = array();
155 $avatar = '';
156 $user = null;
157
158 $default = array(
159 'id' => 0,
160 'avatar' => '',
161 'name' => '',
162 'roles' => '',
163 'validated' => '',
164 );
165
166 if ( ! in_array( $input_type, array( 'id', 'email' ) ) ) {
167 return $default;
168 }
169
170 if ( 'email' === $input_type ) {
171 if ( ! is_email( $input ) ) {
172 return $default;
173 }
174
175 $user = get_user_by( 'email', $input );
176 } else if ( 'id' === $input_type ) {
177 $key = intval( $input );
178 $user = get_user_by( 'id', $key );
179
180 if ( is_wp_error( $user ) ) {
181 return $default;
182 }
183 }
184
185 if ( $user instanceof \WP_User ) {
186 $display_name = $user->display_name;
187 $roles = implode( ', ', Utilities::user_role_names( $user->ID ) );
188 $avatar = get_avatar_url( (int) $user->ID, array( 'size' => 30 ) );
189 } else {
190 $key = isset( $args['key'] ) ? $args['key'] : sha1( $input );
191 $roles = '';
192 $avatar = get_avatar_url( $input, array( 'size' => 30 ) );
193 $display_name = isset( $args['display_name'] ) ? $args['display_name'] : $input;
194 }
195
196 return wp_parse_args(
197 array(
198 'id' => $key,
199 'avatar' => $avatar,
200 'name' => $display_name,
201 'roles' => $roles,
202 ),
203 $default
204 );
205 }
206
207 /**
208 * Return registered recipients from db settings option.
209 *
210 * @return array
211 */
212 public static function get_recipients() {
213 $recipients_user_ids = self::get_schedule_item( 'recipients' );
214
215 if ( empty( $recipients_user_ids ) ) {
216 return array();
217 }
218
219 $recipients = array_map(
220 function ( $recipient_id ) {
221 return self::format_recipient_user_data( $recipient_id );
222 },
223 $recipients_user_ids
224 );
225
226 return $recipients;
227 }
228
229 /**
230 * Return registered email recipients from db settings option.
231 *
232 * @return array
233 */
234 public static function get_email_recipients() {
235 $email_recipients = self::get_schedule_item( 'emailrecipients' );
236
237 $recipients = array_map(
238 function ( $recipient ) {
239 $recipient['avatar'] = get_avatar_url( $recipient['email'], array( 'size' => 30 ) );
240
241 return $recipient;
242 },
243 $email_recipients
244 );
245
246 return $recipients;
247 }
248
249 /**
250 * Provides an item from the schedule data from DB settings option.The returned value is not escaped.
251 *
252 * @return array|string
253 * @var string|null $item
254 *
255 */
256 public static function get_schedule_item( $item = null ) {
257 if ( is_null( $item ) ) {
258 return array();
259 }
260
261 // Important note. The returned value is not escaped here.
262 return isset( self::get_schedule()[ $item ] ) ? self::get_schedule()[ $item ] : array();
263 }
264
265 /**
266 * Provides the schedule data from DB settings option
267 *
268 * @return array
269 */
270 public static function get_schedule() {
271 if ( ! empty( self::$schedule ) ) {
272 return self::$schedule;
273 }
274
275 $schedule_defaults = isset( Settings::instance()->default['scan_results'] ) ?
276 Settings::instance()->default['schedule'] :
277 array();
278
279 self::$schedule = wp_parse_args( self::get_settings_item( 'schedule' ), $schedule_defaults );
280 self::$schedule['recipients'] = self::get_recipients();
281 self::$schedule['emailRecipients'] = self::get_email_recipients();
282
283 if ( empty( self::$schedule['days'] ) ) {
284 self::$schedule['days'] = array( 0 );
285 }
286
287 if ( empty( self::$schedule['monthdays'] ) ) {
288 self::$schedule['monthdays'] = array( 1 );
289 }
290
291 // Match system time format as set from General Settings,
292 if ( Utilities::get_time_format() ) {
293 self::$schedule['time'] = date( Utilities::get_time_format(), strtotime(self::$schedule['time']) );
294 }
295
296 return self::$schedule;
297 }
298
299 /**
300 * Lists roles.
301 */
302 public static function list_user_roles() {
303 $recipients = self::get_schedule_item( 'recipients' );
304 $allowed_roles = array_keys( Utilities::roles_names( array( 'manage_options', 'edit_posts' ) ) );
305 $user_count = count_users();
306 $roles_list = array();
307 $rec_roles_list = array();
308
309 array_walk_recursive( $recipients, function ( $value, $key ) use ( &$rec_roles_list ) {
310 if ( 'roles' === $key ) {
311 $roles = explode( ',', $value );
312
313 if ( ! empty( $roles ) ) {
314 foreach ( $roles as $role ) {
315 $role = trim( $role );
316
317 $rec_roles_list[ $role ] = array_key_exists( $role, $rec_roles_list ) ?
318 intval( $rec_roles_list[ $role ] ) + 1 :
319 1;
320 }
321 }
322
323 }
324 }, $rec_roles_list );
325
326 if ( ! empty( $user_count['avail_roles'] ) ) {
327 foreach ( $user_count['avail_roles'] as $role_name => $user_count ) {
328 if ( ! in_array( $role_name, $allowed_roles ) ) {
329 continue;
330 }
331
332 if ( isset( $rec_roles_list[ ucfirst( $role_name ) ] ) ) {
333 $user_count = $user_count - intval( $rec_roles_list[ ucfirst( $role_name ) ] );
334 }
335
336 $roles_list[] = array(
337 'role_slug' => $role_name,
338 'role_name' => ucfirst( $role_name ),
339 'user_count' => $user_count >= 0 ? $user_count : 0,
340 );
341 }
342 }
343
344 return $roles_list;
345 }
346
347 /**
348 * Calculates remaining cooldown period if required.
349 */
350 public static function get_cooldown_data() {
351 $scan_results = self::get_settings_item( 'scan_results' );
352 $end_time = ! empty( $scan_results['end_time'] ) ? intval( $scan_results['end_time'] ) : 0;
353 $date_utc = new \DateTime( "now", new \DateTimeZone( "UTC" ) );
354 $diff = round( ( $date_utc->getTimestamp() - $end_time ) / 60 );
355 $remaining = intval( 15 - $diff );
356
357 return array(
358 'cooling_down' => ( 0 < $end_time && 0 < $remaining ),
359 'remaining' => $remaining,
360 );
361 }
362
363 }
1 <?php
2 /**
3 * BLC Dashboard admin page view.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Pages\Settings
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Admin_Pages\Dashboard;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Views\Admin_Page;
20
21
22 /**
23 * Class View
24 *
25 * @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
26 */
27 class View extends Admin_Page {
28
29 /**
30 * Render the output.
31 *
32 * @since 2.0.0
33 *
34 * @return void Render the output.
35 */
36 public function render( $params = array() ) {
37 self::$unique_id = isset( $params['unique_id'] ) ? $params['unique_id'] : null;
38 self::$slug = isset( $params['slug'] ) ? $params['slug'] : null;
39 $site_connected = isset( $params['site_connected'] ) ? boolval( $params['site_connected'] ) : false;
40 ?>
41 <div class="sui-wrap wrap-blc wrap-blc-dashboard-page <?php echo 'wrap-' . esc_attr( self::$slug ); ?>">
42 <?php
43 $this->render_body();
44
45 if ( $site_connected ){
46 $this->render_footer();
47 }
48 ?>
49 </div>
50 <?php
51 }
52
53 public function render_body() {
54 ?>
55 <div id="<?php esc_attr_e( self::$unique_id ); ?>"></div>
56 <?php
57 }
58
59 }
1 <?php
2 /**
3 * BLC Links_Submenu admin page
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Pages\Links_Submenu
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Admin_Pages\Links_Submenu;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Controllers\Admin_Page;
20 use WPMUDEV_BLC\Core\Traits\Escape;
21 use WPMUDEV_BLC\Core\Utils\Utilities;
22 use WPMUDEV_BLC\App\Admin_Pages\Dashboard\Controller as DashboardPage;
23
24
25 use WPMUDEV_BLC\Core\Traits\Dashboard_API;
26
27 /**
28 * Class Controller
29 *
30 * @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
31 */
32 class Controller extends Admin_Page {
33 /**
34 * Use the Escape and Dashboard_API Traits.
35 *
36 * @since 2.0.0
37 */
38 use Escape, Dashboard_API;
39
40 /**
41 * The Admin Page's Menu Type.
42 *
43 * @since 2.0.0
44 * @var bool $is_submenu Set to true if page uses submenu.
45 *
46 */
47 protected $is_submenu = true;
48
49 /**
50 * The Admin SubPage's Parent Slug.
51 *
52 * @since 2.0.0
53 * @var string $parent_slug The slug of the parent admin menu.
54 *
55 */
56 protected $parent_slug = 'blc_dash';
57
58 /**
59 * Prepares the properties of the Admin Page.
60 *
61 * @since 2.0.0
62 * @return void
63 */
64 public function prepare_props() {
65 add_action( 'admin_enqueue_scripts', array( $this, 'menu_tag_style' ) );
66
67 $this->is_submenu = true;
68 $this->unique_id = Utilities::get_unique_id();
69 $this->page_title = __( 'Broken Link Checker', 'broken-link-checker' );
70 $this->menu_title = sprintf(
71 __( 'BLC 2.0 %sBeta%s', 'broken-link-checker' ),
72 '<span class="awaiting-mod blc-beta-tag">',
73 '</span>'
74 );
75 /*
76 $this->menu_title = sprintf(
77 __( 'BLC 2.0 %s', 'broken-link-checker' ),
78 '<img class="blc-new-tag" src="' . WPMUDEV_BLC_ASSETS_URL . '/images/admin-menu-tag.png"/>'
79 );
80 */
81 $this->capability = 'manage_options';
82 $this->menu_slug = 'blc_dash';
83 $this->position = 0;
84 }
85
86 /**
87 * Admin Menu Callback.
88 *
89 * @since 1.0.0
90 * @return void The callback function of the Admin Menu Page.
91 */
92 public function output() {
93 //DashboardPage::instance()->output();
94 // Output handled by Dashboard page since pages share common slug.
95 }
96
97 public function menu_tag_style() {
98 $style_handler = 'blc_main_menu_tag';
99 $style_data = "
100 @import url(https://fonts.bunny.net/css?family=roboto:100,500);
101
102 #adminmenu .awaiting-mod.blc-new-tag,
103 #adminmenu .menu-counter.blc-new-tag,
104 #adminmenu .update-plugins.blc-new-tag,
105 #adminmenu li a.wp-has-current-submenu .update-plugins.blc-new-tag,
106 #adminmenu li.current a .awaiting-mod.blc-new-tag,
107 #adminmenu .awaiting-mod.blc-beta-tag,
108 #adminmenu .menu-counter.blc-beta-tag,
109 #adminmenu .update-plugins.blc-beta-tag,
110 #adminmenu li a.wp-has-current-submenu .update-plugins.blc-beta-tag,
111 #adminmenu li.current a .awaiting-mod.blc-beta-tag{
112 /*padding: 1.8px 8px;*/
113 padding-top: 1.2px;
114 position: relative;
115 background: #18BB4B;
116 border-radius: 12px;
117 font-style: normal;
118 font-weight: 400;
119 font-size: 9px;
120 line-height: 12px;
121 text-align: center;
122 letter-spacing: -0.1px;
123 color: #FFFFFF;
124 flex: none;
125 text-transform: uppercase;
126 font-family: 'Roboto', sans-serif;
127 display: inline-block;
128 height: 16px;
129 width: 38px;
130 text-align: center;
131 }
132
133 #adminmenu .awaiting-mod.blc-beta-tag,
134 #adminmenu .menu-counter.blc-beta-tag,
135 #adminmenu .update-plugins.blc-beta-tag,
136 #adminmenu li a.wp-has-current-submenu .update-plugins.blc-beta-tag,
137 #adminmenu li.current a .awaiting-mod.blc-beta-tag{
138 background: none;
139 border: 1px solid #FFFFFF;
140 }
141 ";
142
143 $style_data = str_replace( "\n", "", $style_data );
144 $style_data = str_replace( "\t", "", $style_data );
145
146 wp_register_style( $style_handler, false );
147 wp_enqueue_style( $style_handler );
148 wp_add_inline_style(
149 $style_handler,
150 $style_data
151 );
152 }
153
154 }
1 <?php
2 /**
3 * Controller for Recipient activation emails.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Emails\Recipient_Activation
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Emails\Recipient_Activation;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Controllers\Mailer;
20
21 /**
22 * Class Controller
23 *
24 * @package WPMUDEV_BLC\App\Emails\Recipient_Activation
25 */
26 class Controller extends Mailer {
27 /**
28 * Module name. It might be useful in hooks.
29 *
30 * @var string
31 */
32 public $email_module_name = 'recipient_activation';
33
34 /**
35 * WP Cron interval.
36 *
37 * @var boolean
38 */
39 protected $cron_generate_interval = true;
40
41 /**
42 * Sets required vars. In parent class it is an abstract method.
43 *
44 * @return void
45 */
46 protected function prepare_vars() {
47 $site_name = get_bloginfo( 'name' );
48 $site_email = get_bloginfo( 'admin_email' );
49 $this->email_headers = array(
50 'Content-Type: text/html; charset=UTF-8',
51 "From: {$site_name} <{$site_email}> \r\n",
52 );
53
54 $this->use_cron = false;
55 $this->email_subject = esc_html__( 'Broken links reports activation', 'broken-link-checker' );
56 }
57
58 /**
59 * Sets up body variables to be mapped in email body.
60 *
61 * @param array $email_args
62 *
63 * @return void
64 */
65 public function set_mail_variables( array $email_args = array() ) {
66 $activation_link = $email_args['activation_link'] ?? '';
67 $cancellation_link = $email_args['cancellation_link'] ?? '';
68 $name = $email_args['name'] ?? '';
69 $email = $email_args['email'] ?? '';
70 $site_name = get_bloginfo( 'name' );
71
72 $this->body_variables =
73 apply_filters(
74 'wpmudev_blc_scan_report_email_vars',
75 array(
76 //HEADER
77 '{{HEADER_LOGO_SOURCE}}' => esc_html( Model::header_logo() ),
78 '{{TITLE}}' => esc_html( Model::header_title() ),
79 '{{SITENAME}}' => $site_name,
80 //BODY
81 '{{GREETING}}' => esc_html__( 'Hi {{USERNAME}}', 'broken-link-checker' ),
82 '{{USERNAME}}' => $name,
83 '{{EMAIL_ADDRESS}}' => $email,
84 '{{SITE_URL}}' => site_url(),
85 '{{CONFIRM_BTN_LABEL}}' => esc_html__( 'Confirm Subscription', 'broken-link-checker' ),
86 '{{ACTIVATION_LINK}}' => $activation_link,
87 // FOOTER PART
88 '{{FOOTER_TITLE}}' => esc_html__( 'Broken Link Checker', 'broken-link-checker' ),
89 '{{FOOTER_COMPANY}}' => 'WPMU DEV', //$site_name,
90 '{{FOOTER_CONTENT}}' => '',//View::instance()->get_footer_content(),
91 '{{FOOTER_LOGO_SOURCE}}' => Model::footer_logo(),
92 '{{LINK_TO_WPMUDEV_HOME}}' => esc_html__( 'Link to WPMU DEV Home page', 'broken-link-checker' ),
93 '{{FOOTER_SLOGAN}}' => esc_html__( 'Build A Better WordPress Business', 'broken-link-checker' ),
94 '{{COMPANY_ADDRESS}}' => esc_html__( 'INCSUB PO BOX 163, ALBERT PARK, VICTORIA.3206 AUSTRALIA', 'broken-link-checker' ),
95 '{{COMPANY_TITLE}}' => $site_name,
96 '{{UNSUBSCRIBE}}' => esc_html__( 'Unsubscribe', 'broken-link-checker' ),
97 '{{UNSUBSCRIBE_LINK}}' => $cancellation_link,
98 '{{SOCIAL_LINKS}}' => Model::get_social_links(),
99 ),
100 $this
101 );
102 }
103
104 }
1 <?php
2 /**
3 * The Emails model for Recipient activation.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Emails\Recipient_Activation;
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Emails\Recipient_Activation;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Utils\Utilities;
20
21 /**
22 * Class Settings
23 *
24 * @package WPMUDEV_BLC\App\Emails\Recipient_Activation
25 */
26 class Model {
27 /**
28 * Returns the header logo of the email.
29 *
30 * @return string
31 */
32 public static function header_logo() {
33 return apply_filters(
34 'wpmudev_blc_scan_report_email_header_logo',
35 esc_url( WPMUDEV_BLC_ASSETS_URL . 'images/blc-logo-white-28x28.png' )
36 );
37 }
38
39 /**
40 * Returns the BLC Title to be used in the email header.
41 *
42 * @return string
43 */
44 public static function header_title() {
45 return apply_filters(
46 'wpmudev_blc_scan_report_email_header_title',
47 __( 'Broken Link Notification', 'brocken-link-checker' )
48 );
49 }
50
51 /**
52 * Returns home url.
53 *
54 * @retun string
55 */
56 public static function get_hub_home_url() {
57 return Utilities::hub_home_url();
58 }
59
60 /**
61 * Returns the header logo of the email.
62 *
63 * @return string
64 */
65 public static function footer_logo() {
66 return apply_filters(
67 'wpmudev_blc_scan_report_email_header_logo',
68 esc_url( WPMUDEV_BLC_ASSETS_URL . 'images/footer-slogan.png' )
69 );
70 }
71
72 /**
73 * Returns social links info.
74 *
75 * @return array
76 */
77 public static function social_links() {
78 return apply_filters(
79 'wpmudev_blc_scan_report_email_social_data',
80 array(
81 'facebook' => array(
82 'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/facebook-dark-7x14.png',
83 'url' => 'https://www.facebook.com/wpmudev',
84 ),
85 'instagram' => array(
86 'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/instagram-dark-14x14.png',
87 'url' => 'https://www.instagram.com/wpmu_dev/',
88 ),
89 'twitter' => array(
90 'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/twitter-dark-13x11.png',
91 'url' => 'https://twitter.com/wpmudev/',
92 ),
93 )
94 );
95 }
96
97 /**
98 * Returns the social links for the email footer.
99 */
100 public static function get_social_links() {
101 $social_data = self::social_links();
102 $output = '';
103
104 if ( ! empty( $social_data ) ) {
105 $output .= '<tr>';
106 $output .= '<td><span style="font-weight: 700;font-size: 13px;">' . esc_html__( 'Follow us', 'broken-link-checker' ) . '</span></td>';
107
108 foreach ( $social_data as $key => $data ) {
109 $url = $data['url'];
110 $icon = $data['icon'];
111 $output .= "<td>
112 <a href=\"{$url}\" target=\"_blank\">
113 <img height=\"13\" src=\"{$icon}\" style=\"border-radius:3px;display:block;max-height:13px;margin-left: 10px;\" />
114 </a>
115 </td>
116 ";
117 }
118
119 $output .= '<tr>';
120 }
121
122 return "<table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"float:none;display:inline-table;\">{$output}</table>";
123 }
124
125 }
1
2 <mjml>
3 <mj-head>
4
5 <mj-html-attributes>
6 <mj-html-attribute class="easy-email" multiple-attributes="false" attribute-name="text-color" text-color="#000000"></mj-html-attribute>
7 <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>
8 <mj-html-attribute class="easy-email" multiple-attributes="false" attribute-name="font-size" font-size="14px"></mj-html-attribute>
9 <mj-html-attribute class="easy-email" multiple-attributes="false" attribute-name="line-height" line-height="1.7"></mj-html-attribute>
10 <mj-html-attribute class="easy-email" multiple-attributes="false" attribute-name="font-weight" font-weight="400"></mj-html-attribute>
11 <mj-html-attribute class="easy-email" multiple-attributes="false" attribute-name="responsive" responsive="true"></mj-html-attribute>
12
13 </mj-html-attributes>
14
15
16 <mj-style >.blc-inline-text {
17 width:100%;
18 padding-left: 0px !important;
19 }
20
21 .blc-inline-text,
22 .blc-inline-text div,
23 .blc-inline-text span{
24 display:inline-block !important;
25 vertical-align:top !important;
26 }</mj-style>
27
28 <mj-breakpoint width="480px" />
29
30 <mj-font name="Roboto" href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;600;700&display=swap" />
31 <mj-attributes>
32 <mj-class name="blc-inline-block" display="inline-block" />
33 <mj-text font-size="14px" />
34 <mj-text line-height="1.7" />
35 <mj-text font-weight="400" />
36 <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" />
37 <mj-text font-size="14px" />
38 <mj-text color="#000000" />
39 <mj-text line-height="1.7" />
40 <mj-text font-weight="400" />
41
42
43 </mj-attributes>
44 </mj-head>
45 <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">
46 <span style="font-weight: 700;
47 font-size: 25px;line-height: 30px;font-family:Roboto, sans-serif; font-weight:500; color:#1A1A1A;text-align:left;">
48 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;">
49 <span style="color:#1A1A1A;font-family:Roboto, sans-serif;font-size:18px;line-height:28px;">
50 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>
51 Broken Link Checker
52 </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>
53 <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 ...\ No newline at end of file
1 <?php
2 /**
3 * Email temaplte for scan results.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package broken-link-checker
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 // phpcs:ignore
15 /*
16 * Variables:
17 * {{HEADER_LOGO_SOURCE}}
18 * {{TITLE}}
19 * {{USERNAME}}
20 * {{SITEURL}}
21 * {{COMPANY_TITLE}} // WPMU DEV
22 * {{BROKEN_LINKS_LIST}}
23 * {{FOOTER_TITLE}}
24 * {{FOOTER_COMPANY}}
25 * {{FOOTER_LOGO_SRC}}
26 * {{FOOTER_SLOGAN}}
27 * {{SOCIAL_LINKS}}
28 * {{COMPANY_ADDRESS}}
29 * {{UNSUBSCRIBE}}
30 */
31
32 require_once 'email-body-markup.php';
1 <?php
2 /**
3 * Controller for scan reports emails.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Emails\Scan_Report
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Emails\Scan_Report;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Controllers\Mailer;
20 use WPMUDEV_BLC\App\Emails\Scan_Report\Model;
21 use WPMUDEV_BLC\Core\Utils\Utilities;
22
23 /**
24 * Class Controller
25 *
26 * @package WPMUDEV_BLC\App\Emails\Scan_Report
27 */
28 class Controller extends Mailer {
29 /**
30 * Module name. It might be useful in hooks.
31 *
32 * @var string
33 */
34 public $email_module_name = 'scan_report';
35
36 /**
37 * WP Cron interval.
38 *
39 * @var boolean
40 */
41 protected $cron_generate_interval = false;
42
43 /**
44 * Define if scheduled events ( wp pseudo cron ) can be used for emails.
45 *
46 * @var bool
47 */
48 protected $use_cron = false;
49
50 /**
51 * Sends the BLC scan report to recipients.
52 * Called by WPMUDEV_BLC\App\Hub_Endpoints\Set_Data\Controller
53 * @return void
54 */
55 public function send_email() {
56 $site_name = get_bloginfo( 'name' );
57 $recipients = Model::get_recipients();
58 $broken_links_count = Model::get_scan_results( 'broken_links' );
59 $recipients_collection = array();
60
61 $this->body_variables =
62 apply_filters(
63 'wpmudev_blc_scan_report_email_vars',
64 array(
65 '{{HEADER_FULL_TITLE}}' => View::instance()->get_full_header_title(),
66 '{{HEADER_LOGO_SOURCE}}' => esc_html( Model::header_logo() ),
67 '{{TITLE}}' => esc_html( Model::header_title() ),
68 '{{SITENAME}}' => $site_name,
69 '{{HEADING_START}}' => esc_html__( 'Broken Link Report for', 'broken-link-checker' ),
70 '{{GREETING}}' => esc_html__( 'Hi {{USERNAME}}', 'broken-link-checker' ),
71 '{{USERNAME}}' => '',
72 '{{CONTENT_MENTION_SUMMARY}}' => esc_html__( 'Here\'s your latest broken link summary generated on {{SCANDATE}}.', 'broken-link-checker' ),
73 '{{SITEURL}}' => site_url(),
74 '{{SCANDATE}}' => esc_html( Model::scan_date() ),
75 // SUMMARY PART
76 '{{SUMMARY_TITLE}}' => esc_html__( 'Summary', 'broken-link-checker' ),
77 '{{SUMMARY_INTRO}}' => esc_html__( 'Here are your latest broken link test results.', 'broken-link-checker' ),
78 '{{SUMMARY_ROW_ONE}}' => View::instance()->get_summary_row( '{{SUMMARY_BROKEN_LINKS_LBL}}',
79 $this->get_broken_links_count_markup() ),
80 '{{SUMMARY_BROKEN_LINKS_LBL}}' => esc_html__( 'Broken Links', 'broken-link-checker' ),
81 '{{BROKEN_LINKS_COUNT}}' => $broken_links_count,
82 '{{BROKEN_LINKS_COUNT_COLOR}}' => $broken_links_count > 0 ? '#FF6D6D' : '#1ABC9C',
83 '{{SUMMARY_ROW_TWO}}' => View::instance()->get_summary_row( '{{SUMMARY_TOTAL_URLS_LBL}}', '{{TOTAL_URLS_COUNT}}' ),
84 //'{{SUMMARY_SUCCESSFUL_URLS_LBL}}' => esc_html__( 'Successful URLs', 'broken-link-checker' ),
85 //'{{SUCCESSFUL_URLS_COUNT}}' => Model::get_scan_results( 'succeeded_urls' ),
86 '{{SUMMARY_TOTAL_URLS_LBL}}' => esc_html__( 'Total Links', 'broken-link-checker' ),
87 '{{TOTAL_URLS_COUNT}}' => Model::get_scan_results( 'total_urls' ),
88 '{{SUMMARY_ROW_THREE}}' => View::instance()->get_summary_row( '{{SUMMARY_UNIQUE_URLS_LBL}}', '{{UNIQUE_URLS_COUNT}}' ),
89 '{{SUMMARY_UNIQUE_URLS_LBL}}' => esc_html__( 'Unique URLs', 'broken-link-checker' ),
90 '{{UNIQUE_URLS_COUNT}}' => Model::get_scan_results( 'unique_urls' ),
91 // REPORT PART
92 '{{REPORT_PADDING}}' => $broken_links_count > 0 ? '25px' : '0',
93 '{{REPORT_LIST_PADDING}}' => $broken_links_count > 0 ? '8px' : '0',
94 '{{REPORT_BTN_PADDING}}' => $broken_links_count > 0 ? '20px' : '0',
95 '{{REPORT_TITLE}}' => $broken_links_count > 0 ? esc_html__( 'Broken link report', 'broken-link-checker' ) : '',
96 '{{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' ) : '',
97 '{{REPORT_BTN_URL}}' => esc_url( Model::get_hub_home_url() ),
98 '{{REPORT_BTN_TITLE}}' => $broken_links_count > 0 ? esc_html__( 'View Full Report', 'broken-link-checker' ) : esc_html__( 'View Report', 'broken-link-checker' ),
99 '{{BROKEN_LINKS_LIST}}' => $broken_links_count > 0 ? View::instance()->broken_links_list_markup() : '',
100 // FOOTER PART
101 '{{FOOTER_TITLE}}' => esc_html__( 'Broken Link Checker', 'broken-link-checker' ),
102 '{{FOOTER_COMPANY}}' => 'WPMU DEV', //$site_name,
103 '{{FOOTER_CONTENT}}' => View::instance()->get_footer_content(),
104 '{{FOOTER_LOGO_SOURCE}}' => Model::footer_logo(),
105 '{{LINK_TO_WPMUDEV_HOME}}' => esc_html__( 'Link to WPMU DEV Home page', 'broken-link-checker' ),
106 '{{FOOTER_SLOGAN}}' => esc_html__( 'Build A Better WordPress Business', 'broken-link-checker' ),
107 '{{COMPANY_ADDRESS}}' => esc_html__( 'INCSUB PO BOX 163, ALBERT PARK, VICTORIA.3206 AUSTRALIA', 'broken-link-checker' ),
108 '{{COMPANY_TITLE}}' => $site_name,
109 '{{UNSUBSCRIBE}}' => esc_html__( 'Unsubscribe', 'broken-link-checker' ),
110 '{{UNSUBSCRIBE_LINK}}' => '',
111 '{{SOCIAL_LINKS}}' => View::instance()->get_social_links(),
112 ),
113 $this
114 );
115
116 if ( ! empty( $recipients ) ) {
117 foreach ( $recipients as $recipient ) {
118 $this->body_variables['{{USERNAME}}'] = $recipient['name'] ?? '';
119 $this->body_variables['{{UNSUBSCRIBE_LINK}}'] = $recipient['unsubscribe_link'] ?? '';
120 $recipient_collection = array(
121 'name' => $recipient['name'] ?? '',
122 'email' => $recipient['email'] ?? '',
123 );
124
125 $recipient_collection = array_merge( $recipient_collection, $this->body_variables );
126 $recipients_collection[] = $recipient_collection;
127 }
128
129 $this->send_multiple(
130 $recipients_collection,
131 true
132 );
133 }
134
135 }
136
137 /**
138 * Returns the markup for the Broken Links count value.
139 *
140 * @return string
141 */
142 protected function get_broken_links_count_markup() {
143 return "
144 <div style=\"font-family:Roboto;font-size:22px;font-weight:700;line-height:22px;text-align:left;color:{{BROKEN_LINKS_COUNT_COLOR}};\">
145 <div style=\"text-align: right;\">{{BROKEN_LINKS_COUNT}}</div>
146 </div>
147 ";
148 }
149
150 /**
151 * Sets required vars. In parent class it is an abstract method.
152 *
153 * @return void
154 */
155 protected function prepare_vars() {
156 $site_name = get_bloginfo( 'name' );
157 $site_email = get_bloginfo( 'admin_email' );
158 $this->email_headers = array(
159 'Content-Type: text/html; charset=UTF-8',
160 "From: {$site_name} <{$site_email}> \r\n",
161 );
162 $this->email_subject = esc_html__( 'Broken links reports', 'broken-link-checker' );
163 }
164
165 }
1 <?php
2 /**
3 * The Emails model for Scan report.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Emails\Scan_Report;
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Emails\Scan_Report;
15
16 // Abort if called directly.
17 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
18 use WPMUDEV_BLC\App\Webhooks\Recipient_Activation\Controller as Activation_Webhook;
19 use WPMUDEV_BLC\Core\Utils\Utilities;
20
21 defined( 'WPINC' ) || die;
22
23 /**
24 * Class Settings
25 *
26 * @package WPMUDEV_BLC\App\Admin_Pages\Dashboard
27 */
28 class Model {
29 /**
30 * The scan results from DB.
31 *
32 * @var array
33 */
34 private static $scan_results = array();
35
36 /**
37 * Returns the header logo of the email.
38 *
39 * @return string
40 */
41 public static function header_logo() {
42 return apply_filters(
43 'wpmudev_blc_scan_report_email_header_logo',
44 esc_url( WPMUDEV_BLC_ASSETS_URL . 'images/blc-logo-white-28x28.png' )
45 );
46 }
47
48 /**
49 * Returns the BLC Title to be used in the email header.
50 *
51 * @return string
52 */
53 public static function header_title() {
54 return apply_filters(
55 'wpmudev_blc_scan_report_email_header_title',
56 __( 'BLC Report', 'brocken-link-checker' )
57 );
58 }
59
60 /**
61 * Returns home url.
62 *
63 * @retun string
64 */
65 public static function get_hub_home_url() {
66 return Utilities::hub_home_url();
67 }
68
69 /**
70 * Returns the header logo of the email.
71 *
72 * @return string
73 */
74 public static function footer_logo() {
75 return apply_filters(
76 'wpmudev_blc_scan_report_email_header_logo',
77 esc_url( WPMUDEV_BLC_ASSETS_URL . 'images/wpmudev-logo-dark-30x30.png' )
78 );
79 }
80
81 /**
82 * Returns social links info.
83 *
84 * @return array
85 */
86 public static function social_links() {
87 return apply_filters(
88 'wpmudev_blc_scan_report_email_social_data',
89 array(
90 'facebook' => array(
91 'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/facebook-dark-7x14.png',
92 'url' => 'https://www.facebook.com/wpmudev',
93 ),
94 'instagram' => array(
95 'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/instagram-dark-14x14.png',
96 'url' => 'https://www.instagram.com/wpmu_dev/',
97 ),
98 'twitter' => array(
99 'icon' => WPMUDEV_BLC_ASSETS_URL . 'images/social/twitter-dark-13x11.png',
100 'url' => 'https://twitter.com/wpmudev/',
101 ),
102 )
103 );
104 }
105
106 /**
107 * Returns scan date.
108 */
109 public static function scan_date() {
110 $start_time = self::get_scan_results( 'start_time' );
111
112 if ( ! empty( $start_time ) ) {
113 $start_time = Utilities::microtime_to_date( intval( $start_time ), 'full_date', true );
114 }
115
116 return apply_filters(
117 'wpmudev_blc_scan_report_email_scan_date',
118 $start_time
119 );
120 }
121
122 /**
123 * Returns scan results.
124 *
125 * @param string $key Optional scan results key.
126 */
127 public static function get_scan_results( string $key = '' ) {
128 if ( empty( self::$scan_results ) ) {
129 $scan_results = array();
130 $scan_defaults = Settings::instance()->default['scan_results'] ?? array();
131 self::$scan_results = wp_parse_args( Settings::instance()->get( 'scan_results' ), $scan_defaults );
132 }
133
134 if ( ! empty( $key ) ) {
135 return self::$scan_results[ $key ] ?? null;
136 }
137
138 return self::$scan_results;
139 }
140
141 /**
142 * Returns array recipients email address and names.
143 */
144 public static function get_recipients() {
145 $user_recipients = array();
146 $email_recipients = Settings::instance()->get_scan_active_email_recipients();
147 $schedule = Settings::instance()->get( 'schedule' );
148 $registered_recipients_data = ! empty( $schedule[ 'registered_recipients_data' ] ) ? $schedule['registered_recipients_data'] : array();
149
150 if ( ! empty( $email_recipients ) ) {
151 array_walk(
152 $email_recipients,
153 function ( &$recipient ) {
154 unset( $recipient['confirmed'] );
155 $recipient['unsubscribe_link'] = self::unsubscribe_link( base64_encode( md5( $recipient['email'] ) . '_' . $recipient['key'] ) );
156 }
157 );
158 }
159
160 if ( ! empty( $registered_recipients_data ) ) {
161 array_walk(
162 $registered_recipients_data,
163 function ( $user_data, $user_id ) use ( &$user_recipients ) {
164 $user_recipients[] = array(
165 'name' => $user_data['name'],
166 'email' => $user_data['email'],
167 'key' => $user_data['key'],
168 'unsubscribe_link' => self::unsubscribe_link( base64_encode( md5( $user_data['email'] ) . '_' . $user_data['key'] ) ),
169 );
170 }
171 );
172 }
173
174 return array_merge( $user_recipients, $email_recipients );
175 }
176
177 private static function unsubscribe_link( string $token = '' ) {
178 return add_query_arg(
179 array(
180 'action' => 'cancel',
181 'activation_code' => sanitize_text_field( $token ),
182 ),
183 Activation_Webhook::instance()->webhook_url()
184 );
185 }
186 }
1 <?php
2 /**
3 * View for scan reports emails. Holds specific parts for email template to use.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Emails\Scan_Report
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Emails\Scan_Report;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\App\Emails\Scan_Report\Model;
20 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
21
22 //use WPMUDEV_BLC\Core\Utils\Utilities;
23
24 class View extends Base {
25 /**
26 * Provides the header full title including the image.
27 *
28 * @return string
29 */
30 public function get_full_header_title() {
31 ob_start();
32 ?>
33 <img height="28" width="28" src="{{HEADER_LOGO_SOURCE}}"
34 style="border:0;outline:none;text-decoration:none;height:28px;width:28px;font-size:13px;vertical-align:middle"
35 />
36 {{TITLE}}
37 <?php
38
39 return ob_get_clean();
40 }
41
42 /**
43 * Gives a table with a single row and 2 columns even in small screens. Widths are specific.
44 *
45 * @param string $title
46 * @param string $value
47 *
48 * @return string
49 */
50 public function get_summary_row( string $title = '', string $value = '' ) {
51 return "
52 <table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\"
53 style=\"width:100%;\">
54 <tbody>
55 <tr>
56 <td align=\"left\" style=\"width: 70% !important;max-width: 70%;text-align:left;\">
57 <div style=\"font-family:Roboto;font-size:15px;font-weight:500;line-height:22px;text-align:left;color:#000000;\">
58 {$title}
59 </div>
60 </td>
61 <td align=\"right\" style=\"width: 30% !important;max-width: 30%;text-align: right;\">
62 <div>
63 {$value}
64 </div>
65
66 </td>
67 </tr>
68 </tbody>
69 </table>
70 ";
71 }
72
73 /**
74 * Gives the markup of broken links email part.
75 *
76 * @return string
77 */
78 public function broken_links_list_markup() {
79 $broken_links_list = Model::get_scan_results( 'broken_links_list' );
80 $markup = '';
81
82 if ( empty( $broken_links_list ) ) {
83 return $markup;
84 }
85
86 foreach ( $broken_links_list as $broken_link ) {
87
88 if ( is_object( $broken_link ) ) {
89 $broken_link = (array) $broken_link;
90 }
91
92 if ( ! empty( $broken_link['is_ignored'] ) ) {
93 continue;
94 }
95
96 $origin_source = $broken_link['origins'][0] ?? false;
97 $origin_source_id = null;
98
99 if ( $origin_source ) {
100 $origin_source_id = intval( url_to_postid( $origin_source ) );
101 }
102
103 $origin_post_title = $origin_source_id ? get_the_title( $origin_source_id ) : __( 'Unknown', 'broken-link-checker' );
104 $origin_post_edit_url = get_edit_post_link( $origin_source_id );
105
106 // If get_edit_post_link keeps returning empty.
107 if ( empty( $origin_post_edit_url ) ) {
108 $post = get_post( $origin_source_id );
109
110 if ( $post instanceof \WP_Post ) {
111 $post_type_object = get_post_type_object( $post->post_type );
112
113 if ( ! $post_type_object ) {
114 continue;
115 }
116
117 $origin_post_edit_url = admin_url( sprintf( $post_type_object->_edit_link . "&action=edit", $post->ID ) );
118 } else {
119 continue;
120 }
121 }
122
123 ob_start();
124 ?>
125 <tr style="border: 1px solid #F2F2F2;">
126 <td style="font-family: Roboto, sans-serif;font-weight: 500;padding: 20px; font-size: 12px;
127 line-height: 14px;color: #286EFA; vertical-align:top; text-align: left;">
128 <a href="<?php echo $broken_link['url']; ?>" style="color: #286EFA;">
129 <?php echo $broken_link['url']; ?>
130 </a>
131 </td>
132 <td style="font-family: Roboto, sans-serif;font-weight: 500;padding: 20px; font-size: 12px;
133 line-height: 14px;color: #1A1A1A; width:33%; vertical-align:top; text-align: left;">
134
135 <table>
136 <tr>
137 <td style="min-width: 50px;">
138 <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;">
139 <?php echo $broken_link['status_code']; ?>
140 </span>
141 </td>
142 <td style="min-width: 50px;">
143 <?php echo $broken_link['status_ref']; ?>
144 </td>
145 </tr>
146 </table>
147
148 </td>
149 <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;">
150 <a href="<?php echo $origin_post_edit_url; ?>" target="_blank" style="color: #286EFA;">
151 <?php echo $origin_post_title; ?>
152 </a>
153 </td>
154 </tr>
155 <?php
156 $markup .= ob_get_clean();
157 }
158
159 return "<div style=\"overflow-x: auto;\"><table cellpadding=\"0\" cellspacing=\"0\" width=\"100%\" border=\"0\" style=\"color:#000000;
160 #font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:22px;table-layout:auto;
161 #width:100%;border:none;min-width:400px;\">
162 <tr style=\"align-items: flex-start;padding: 0px;width: 550px;height: 32px;\">
163 <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;\">
164 " . esc_html__( 'Broken Links', 'broken-link-checker' ) . "
165 </th>
166 <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;\">
167 " . esc_html__( 'Status', 'broken-link-checker' ) . "
168 </th>
169 <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;\">
170 " . esc_html__( 'Source URL', 'broken-link-checker' ) . "
171 </th>
172 </tr>
173 {$markup}
174 </table></div>";
175 }
176
177 /**
178 * Return the footer content.
179 *
180 * @return string
181 */
182 public function get_footer_content() {
183 $footer_slogan_img_url = WPMUDEV_BLC_ASSETS_URL . 'images/footer-slogan.png';
184
185 ob_start();
186 ?>
187 <div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:1;text-align:center;
188 color:#000000; width: 100%;">
189 <table style="width:100%;">
190 <tbody>
191 <tr>
192 <td align="center">
193 <table border="0" cellpadding="0" cellspacing="0" role="presentation"
194 style="border-collapse:collapse;border-spacing:0px">
195 <tbody>
196 <tr>
197 <td style="width:168px">
198 <a href="https://wpmudev.com" title="{{LINK_TO_WPMUDEV_HOME}}" target="_blank"
199 data-saferedirecturl="https://wpmudev.com">
200 <img height="auto" src="<?php echo $footer_slogan_img_url; ?>" style="border:0;
201 display:block;outline:none; text-decoration:none;height:auto;width:100%;font-size:13px"
202 width="168">
203 </a>
204 </td>
205 </tr>
206 </tbody>
207 </table>
208 </td>
209 </tr>
210 </tbody>
211 </table>
212
213
214 </div>
215 <?php
216
217 return ob_get_clean();
218 }
219
220 /**
221 * Returns the social links for the email footer.
222 */
223 public function get_social_links() {
224 $social_data = Model::social_links();
225 $output = '';
226
227 if ( ! empty( $social_data ) ) {
228 $output .= '<tr>';
229 $output .= '<td><span style="font-weight: 700;font-size: 13px;">' . esc_html__( 'Follow us', 'broken-link-checker' ) . '</span></td>';
230
231 foreach ( $social_data as $key => $data ) {
232 $url = $data['url'];
233 $icon = $data['icon'];
234 $output .= "<td>
235 <a href=\"{$url}\" target=\"_blank\">
236 <img height=\"13\" src=\"{$icon}\" style=\"border-radius:3px;display:block;max-height:13px;margin-left: 10px;\" />
237 </a>
238 </td>
239 ";
240 }
241
242 $output .= '<tr>';
243 }
244
245 return "<table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"float:none;display:inline-table;\">{$output}</table>";
246 }
247 }
1 <?php
2 /**
3 * Email temaplte for scan results.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package broken-link-checker
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 // phpcs:ignore
15 /*
16 * Variables:
17 * {{HEADER_LOGO_SOURCE}}
18 * {{TITLE}}
19 * {{SCANDATE}}
20 * {{USERNAME}}
21 * {{SITEURL}}
22 * {{BROKEN_LINKS_COUNT}}
23 * {{SUCCESSFUL_URLS_COUNT}}
24 * {{UNIQUE_URLS_COUNT}}
25 * {{FULL_REPORT_TITLE}}
26 * {{FULL_REPORT_URL}}
27 * {{COMPANY_TITLE}} // WPMU DEV
28 * {{BROKEN_LINKS_LIST}}
29 * {{FOOTER_TITLE}}
30 * {{FOOTER_COMPANY}}
31 * {{FOOTER_LOGO_SRC}}
32 * {{FOOTER_SLOGAN}}
33 * {{SOCIAL_LINKS}}
34 * {{COMPANY_ADDRESS}}
35 * {{UNSUBSCRIBE}}
36 * -
37 * For Broken links list
38 * {{BROKEN_LINK_TITLE}}
39 * {{BROKEN_LINK_STATUS}}
40 * {{BROKEN_LINK_STATUS_TITLE}}
41 * {{BROKEN_LINK_URL}}
42 */
43
44 require_once 'email-body-markup.php';
1 <?php
2 /**
3 * The Http Request model.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\Core\Models
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Http_Requests\Scan;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WP_Error;
20 use WP_HTTP_Response;
21 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
22 use WPMUDEV_BLC\App\Scan_Models\Scan_Data;
23 use WPMUDEV_BLC\Core\Models\Http_Request;
24 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
25 use WPMUDEV_BLC\Core\Utils\Utilities;
26
27 use WPMUDEV_BLC\Core\Traits\Dashboard_API;
28
29 /**
30 * Class Controller
31 *
32 * @package WPMUDEV_BLC\App\Http_Requests\Scan
33 */
34 class Controller extends Base {
35 /**
36 * Use Dashboard_API Trait.
37 *
38 * @since 2.0.0
39 */
40 use Dashboard_API;
41
42 /**
43 * Response
44 *
45 * @var null|array
46 */
47 private $response = array(
48 'code' => 404,
49 'status' => null,
50 'scan_status' => '',
51 'message' => '',
52 'data' => array(
53 'broken_links' => null,
54 'succeses' => null,
55 'unique_urls' => null,
56 'total_urls' => null,
57 'start_time' => null,
58 'end_time' => null,
59 'duration' => null,
60 ),
61 );
62
63 /**
64 * The BLC remote api full url.
65 *
66 * @var string
67 */
68 private $url = null;
69
70 /**
71 * The BLC api key.
72 *
73 * @var string
74 */
75 private $api_key = null;
76
77 /**
78 * Token
79 *
80 * @var string
81 */
82 private $token = null;
83
84 /**
85 *
86 * Null or Object of eiter Http_Request or WP_Error.
87 *
88 * @var object
89 */
90 private $request_api = null;
91
92 /**
93 * Starts the BLC scan. Request:
94 *
95 * @return WP_HTTP_Response|null|WP_Error
96 */
97 public function start() {
98 $request_status = $this->can_do_request();
99
100 if ( is_wp_error( $request_status ) ) {
101 $this->set_response_code( 500 );
102 $this->set_response_message( esc_html( $request_status->get_error_message() ) );
103
104 return $this->get_response();
105 }
106
107 $this->prepare_request();
108
109 /*
110 * Request to start scan.
111 */
112 $args = array(
113 'method' => 'POST',
114 'url' => $this->get_request_url(),
115 'headers' => array(
116 'Authorization' => $this->api_key,
117 ),
118 );
119
120 $api_request = $this->request_api->request( $args );
121 $error_message = $this->error_messages( 'request_error' );
122
123 if ( ! $this->request_api->get_response() instanceof WP_HTTP_Response ) {
124 $this->set_response_code( 500 );
125 $this->set_response_message( $error_message );
126
127 return $this->get_response();
128 }
129
130 $response_data = json_decode( $this->request_api->get_data(), true );
131
132 if ( 200 !== $api_request->get_status() ) {
133 $error_message = isset( $response_data['message'] ) ? esc_html( $response_data['message'] ) : $error_message;
134
135 $this->set_response_code( 500 );
136 $this->set_response_message( $error_message );
137
138 return $this->get_response();
139 }
140
141 $json_to_import = json_encode( array( 'params' => $response_data ) );
142
143 if ( ! Scan_Data::instance()->set( $json_to_import ) ) {
144 // This doesn't mean that there was an error. There was probably no change in data that's why
145 // update_option returns false. So we're just logging a message.
146 Utilities::log( $this->error_messages( 'error_on_save' ) );
147 }
148
149 $this->set_response_scan_status( 'completed' );
150 Settings::instance()->set( array( 'scan_status' => 'completed' ) );
151 Settings::instance()->save();
152
153 $this->set_response_code( $api_request->get_status() );
154 $this->set_response_data( $api_request->get_data() );
155 $this->set_response_message(
156 esc_html__(
157 'Scan for Broken Links is in progress. Please wait a few minutes until scan completes.',
158 'broken-link-checker'
159 )
160 );
161
162 return $this->get_response();
163 }
164
165 /**
166 * Checks if the request can be carried out.
167 *
168 * @return bool|WP_Error
169 */
170 private function can_do_request() {
171 if ( Utilities::is_localhost() ) {
172 return new WP_Error(
173 'blc-api-request-failled',
174 esc_html__(
175 'Scan could not be started because it seems you are on localhost. Broken Links Checker API can not reach sites on local hosts',
176 'broken-link-checker'
177 )
178 );
179 }
180
181 if ( ! (bool) self::site_connected() ||
182 Settings::instance()->get( 'use_legacy_blc_version' ) ||
183 ! class_exists( '\WPMUDEV_Dashboard' ) ||
184 ! \WPMUDEV_Dashboard::$api->has_key() ) {
185 return new WP_Error(
186 'blc-api-request-failled',
187 esc_html__(
188 esc_html__( 'Can not make request.', 'broken-link-checker' ),
189 'broken-link-checker'
190 )
191 );
192 }
193
194 return true;
195 }
196
197 public function set_response_code( int $code = 200 ) {
198 $this->response['status'] = absint( $code );
199 }
200
201 public function set_response_message( string $data = '' ) {
202 $this->response['message'] = $data;
203 }
204
205 /**
206 * Returns the response.
207 *
208 * @return WP_Http_Response|array
209 */
210 public function get_response() {
211 return $this->response;
212 }
213
214 /**
215 * Prepares some params.
216 *
217 * @return void
218 */
219 private function prepare_request() {
220 if ( is_null( $this->request_api ) ) {
221 $this->request_api = Http_Request::instance();
222 }
223
224 // WPMUDEV_Dashboard class has been checked already in `$this->can_do_request()`.
225 $this->api_key = \WPMUDEV_Dashboard::$api->get_key();
226 }
227
228 /**
229 * Returns full request url.
230 *
231 * @return string
232 */
233 public function get_request_url() {
234 if ( is_null( $this->url ) ) {
235 $this->url = Utilities::hub_api_scan_url();
236 }
237
238 return $this->url;
239 }
240
241 protected function error_messages( string $message_code = '' ) {
242 $error_messages = array(
243 'request_error' => esc_html__( 'Something went wrong with request.', 'broken-link-checker' ),
244 'scan_in_progress' => esc_html__( 'Scan is currently in progress. Please try again in 15 minutes.', 'broken-link-checker' ),
245 );
246
247 if ( ! empty( $message_code ) ) {
248 return isset( $error_messages[ $message_code ] ) ? $error_messages[ $message_code ] : '';
249 }
250
251 return $error_messages;
252 }
253
254 /**
255 * Sets the response status in global array.
256 *
257 * @param string $status
258 *
259 * @return void
260 */
261 public function set_response_scan_status( string $status = 'completed' ) {
262 if ( ! in_array( $status, array( 'completed', 'in_progress' ) ) ) {
263 $status = 'completed';
264 }
265 $this->response['scan_status'] = $status;
266 }
267
268 public function set_response_data( $data = '' ) {
269 $this->response['data'] = $data;
270 }
271
272 public function get_error_message() {
273 return $this->response['message'];
274 }
275
276 public function get_response_code() {
277 return $this->response['status'];
278 }
279
280 public function get_response_message() {
281 return $this->response['message'];
282 }
283
284 public function get_response_data() {
285 return $this->response['data'];
286 }
287
288 public function get_response_scan_status() {
289 return $this->response['scan_status'];
290 }
291
292 }
1 <?php
2 /**
3 * The Http Request model.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\Core\Models
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Http_Requests\Scan;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Traits\Sanitize;
20 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
21 use WPMUDEV_BLC\Core\Models\Http_Request;
22
23
24 /**
25 * Class Installer
26 *
27 * @package WPMUDEV_BLC\Core\Models
28 */
29 class Model extends Http_Request {
30
31 public function start_scan( string $url = '', string $api_key = '' ) {
32 $args = array(
33
34 );
35
36 $this->request( $args );
37 return $this->get_response();
38 }
39 }
1 <?php
2 /**
3 * An endpoint where Hub can send requests and retrieve scan schedule data.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Hub_Endpoints\Scan_Data
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Hub_Endpoints\Scan_Data;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use DateTime;
20 use DateTimeZone;
21 use WPMUDEV_BLC\Core\Controllers\Hub_Endpoint;
22 use WPMUDEV_BLC\Core\Utils\Utilities;
23 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
24 use function boolval;
25 use function gmdate;
26 use function intval;
27 use function wp_next_scheduled;
28
29 /**
30 * Class Controller
31 *
32 * @package WPMUDEV_BLC\App\Hub_Endpoint\Scan_Data
33 */
34 class Controller extends Hub_Endpoint {
35 /**
36 * Returns a json string with schedule data.
37 */
38 public function get_schedule_data() {
39 $schedule_data = array(
40 'active' => false,
41 'recipients' => array(),
42 'emailRecipients' => array(),
43 'frequency' => 'daily',
44 'days' => array(),
45 'time' => '00:00',
46 'nextScanData' => array(),
47 );
48 $schedule = $this->get_schedule();
49
50 if ( ! empty( $schedule ) ) {
51 $schedule_data['active'] = isset( $schedule['active'] ) ? boolval( $schedule['active'] ) : false;
52
53 if ( ! $schedule_data['active'] ) {
54 $this->output_formatted_response( $schedule_data );
55 }
56
57 $schedule_data['recipients'] = $schedule['recipients'] ?? $schedule_data['recipients'];
58 $schedule_data['emailRecipients'] = Settings::instance()->get_scan_active_email_recipients() ?? $schedule_data['emailRecipients'];
59 $schedule_data['frequency'] = $schedule['frequency'] ?? $schedule_data['frequency'];
60 $schedule_data['days'] = ( 'monthly' === $schedule['frequency'] ) ?
61 $schedule['monthdays'] :
62 ( 'weekly' === $schedule['monthdays'] ? $schedule['days'] : array() );
63 $schedule_data['time'] = $schedule['time'] ?? $schedule_data['time'];
64 $schedule_data['nextScanData'] = $this->get_next_scan_data();
65 }
66
67 $this->output_formatted_response( $schedule_data );
68 }
69
70 /**
71 * Returns the schedule from settings, or if a key is set, it returns that key's value
72 *
73 * @param string $key .
74 *
75 * @return array|mixed|null
76 */
77 private function get_schedule( string $key = '' ) {
78 static $schedule = null;
79
80 if ( is_null( $schedule ) ) {
81 $schedule = Settings::instance()->get( 'schedule' );
82 }
83
84 if ( ! empty( $key ) && is_array( $schedule ) ) {
85 return $schedule[ $key ] ?? null;
86 }
87
88 return $schedule;
89 }
90
91 /**
92 * Returns an array which contains next blc schedule data.
93 *
94 * @return array.
95 */
96 private function get_next_scan_data() {
97 $next_scan_data = array(
98 'siteZone' => '',
99 'timestampSiteZone' => '',
100 'timestampUTC' => '',
101 'formattedDateSiteZone' => '',
102 'formattedDateUTC' => '',
103 );
104 $schedule_cron = new \WPMUDEV_BLC\App\Scheduled_Events\Scan\Controller();
105
106 $schedule_cron->init();
107
108 //$next_scan_timestamp_utc = wp_next_scheduled( $schedule_cron->cron_hook );
109 $next_scan_timestamp_utc = $this->get_next_schedule_timestamp( $schedule_cron->get_hook_name() );
110 $date_format = Utilities::get_date_format();
111 $time_format = Utilities::get_time_format();
112
113 if ( $next_scan_timestamp_utc ) {
114 $next_scan_timestamp_utc = intval( $next_scan_timestamp_utc );
115 $next_scan_date = gmdate( 'Y-m-d H:i:s', $next_scan_timestamp_utc );
116 $next_scan_datetime = new DateTime( $next_scan_date, new DateTimeZone( 'UTC' ) );
117
118 $next_scan_datetime->setTimezone( new DateTimeZone( Utilities::get_timezone_string( true ) ) );
119
120 $next_scan_timestamp = strtotime( $next_scan_datetime->format( 'Y-m-d H:i:s' ) );
121 $next_scan_data['siteZone'] = Utilities::get_timezone_string( true );
122 $next_scan_data['timestampSiteZone'] = $next_scan_timestamp;
123 $next_scan_data['timestampUTC'] = $next_scan_timestamp_utc;
124 $next_scan_data['formattedDateSiteZone'] = gmdate( "{$date_format} {$time_format}", $next_scan_timestamp );
125 $next_scan_data['formattedDateUTC'] = gmdate( "{$date_format} {$time_format}", $next_scan_timestamp_utc );
126 }
127
128 return $next_scan_data;
129 }
130
131 /**
132 * Calculates next `blc_schedule_scan` cron timestamp.
133 *
134 * @return int|bool.
135 */
136 private function get_next_schedule_timestamp( $cron_hook ) {
137 $next_timestamp = wp_next_scheduled( $cron_hook );
138
139 if ( ! $next_timestamp ) {
140 $schedule_cron = new \WPMUDEV_BLC\App\Scheduled_Events\Scan\Controller();
141 $schedule_cron->set_scan_schedule();
142
143 $next_timestamp = wp_next_scheduled( $cron_hook );
144 }
145
146 return $next_timestamp;
147 }
148
149 /**
150 * Sets the endpoint's action vars to be used by Dash plugin.
151 */
152 protected function setup_action_vars() {
153 $this->endpoint_action_name = 'blc_get_data';
154 $this->endpoint_action_callback = 'get_schedule_data';
155 }
156 }
1 <?php
2 /**
3 * An enpoint where Hub can send requests and retrive scan schedule data.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Hub_Endpoints\Set_Data
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Hub_Endpoints\Set_Data;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Controllers\Hub_Endpoint;
20 use WPMUDEV_BLC\App\Scan_Models\Scan_Data;
21 use WPMUDEV_BLC\App\Emails\Scan_Report\Controller as ReportMailer;
22 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
23
24 /**
25 * Class Controller
26 *
27 * @package WPMUDEV_BLC\App\Hub_Endpoint\Scan_Data
28 */
29 class Controller extends Hub_Endpoint {
30 /**
31 * Sets the endpoint's action vars to be used by Dash plugin.
32 */
33 protected function setup_action_vars() {
34 $this->endpoint_action_name = 'blc_set_data';
35 $this->endpoint_action_callback = 'set_scan_data';
36 }
37
38 /**
39 * Prints a json string with schedule data.
40 */
41 public function set_scan_data() {
42 $input_json = file_get_contents( 'php://input' );
43
44 if ( ! Scan_Data::instance()->set( $input_json ) ) {
45 $this->output_formatted_response(
46 array(
47 'code' => 'ERROR_SET_SCAN_DATA',
48 'message' => 'Something went wrong when saving scan data.',
49 'data' => '',
50 ),
51 false
52 );
53 } else {
54 if ( Settings::instance()->get( 'blc_schedule_scan_in_progress' ) ) {
55 ReportMailer::instance()->init();
56 ReportMailer::instance()->send_email();
57 Settings::instance()->set( array( 'blc_schedule_scan_in_progress' => false ) );
58 Settings::instance()->save();
59 }
60
61 $this->output_formatted_response(
62 array(
63 'data_received' => true,
64 'data_stored' => true,
65 ),
66 true
67 );
68 }
69 }
70
71 }
1 <?php
2 /**
3 * Settings controller.
4 * A single scheduled event that gets triggered based on options set in "Schedule Scan"
5 *
6 * @link https://wordpress.org/plugins/broken-link-checker/
7 * @since 2.0.0
8 *
9 * @author WPMUDEV (https://wpmudev.com)
10 * @package WPMUDEV_BLC\App\Options\Settings
11 *
12 * @copyright (c) 2022, Incsub (http://incsub.com)
13 */
14
15 namespace WPMUDEV_BLC\App\Options\Settings;
16
17 // Abort if called directly.
18 defined( 'WPINC' ) || die;
19
20 use WPMUDEV_BLC\Core\Models\Option;
21 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
22 use WPMUDEV_BLC\App\Options\Settings\Model as Settings_Model;
23 use WPMUDEV_BLC\Core\Utils\Utilities;
24
25 /**
26 * Class Controller
27 *
28 * @package WPMUDEV_BLC\App\Options\Settings
29 */
30 class Controller extends Base {
31 /**
32 * Plugin settings
33 */
34 private $settings = null;
35
36 /**
37 * Init Settings
38 *
39 * @since 2.0.0
40 *
41 * @return void
42 */
43 public function init() {
44 $this->settings = new Settings_Model();
45 $this->settings->init();
46
47 add_action( 'wpmudev_blc_plugin_activated', array( $this, 'activation_actions' ), 9 );
48 //add_action( 'wpmudev_blc_plugin_deactivated', array( $this, 'deactivation_actions' ) );
49
50 add_action( 'load-toplevel_page_blc_dash', array( $this, 'blc_pages_init' ) );
51 add_action( 'load-link-checker_page_view-broken-links', array( $this, 'blc_pages_init' ) );
52 add_action( 'load-link-checker_page_link-checker-settings', array( $this, 'blc_pages_init' ) );
53 //add_action( 'delete_user', array( $this,'adapt_schedule_recipients' ), 10, 2 );
54 add_action( 'deleted_user', array( $this, 'adapt_schedule_recipients' ), 10, 2 );
55 add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog' ), 10, 3 );
56 }
57
58 /**
59 * Actions to be done when v2 or legacy blc pages are loaded.
60 * @return void
61 */
62 public function blc_pages_init() {
63 // Activate V2 if there was a request to activate.
64 // 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.
65 if ( $this->settings->get( 'v2_activation_request' ) && Utilities::site_connected() ) {
66 $this->settings->set( array( 'use_legacy_blc_version' => false ) );
67 $this->settings->set( array( 'v2_activation_request' => false ) );
68 $this->settings->save();
69
70 // 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.
71 $parsed_query_params = wp_parse_url( $_SERVER['REQUEST_URI'] );
72
73 if ( ! empty( $parsed_query_params['query'] ) ) {
74 $query_params = $parsed_query_params['query'];
75 wp_safe_redirect( admin_url( "admin.php?{$query_params}" ) );
76 exit;
77 }
78 }
79
80 // Disable the welcome modal after plugin activation when BLC Dash or other BLC screen is visited.
81 $this->settings->set( array( 'activation_modal_shown' => true ) );
82 $this->settings->save();
83 }
84
85 /**
86 * Deleted the settings when plugin gets deactivated.
87 *
88 * @since 2.0.0
89 *
90 * @return void
91 */
92 public function deactivation_actions() {
93 $this->settings->delete();
94 }
95
96 /**
97 * Set the settings when plugin gets activated.
98 *
99 * @since 2.0.0
100 *
101 * @return void
102 */
103 public function activation_actions() {
104 // Check if legacy plugin was installed by checking legacy a`wsblc_options` option.
105 // If installed first time enable V2, else V1.
106 $legacy_option = new Option( array( 'name' => 'wsblc_options' ) );
107
108 if ( empty( $legacy_option->get() ) || ( empty( $this->settings->get( 'use_legacy_blc_version' ) ) && ! empty( $this->settings->get( 'schedule' ) ) && ! empty( $this->settings->get( 'schedule' )['active'] ) ) ) {
109 \WPMUDEV_BLC\App\Scheduled_Events\Scan\Controller::instance()->set_scan_schedule();
110 }
111
112 if ( ! empty( get_option( 'blc_settings' ) ) ) {
113 return;
114 }
115
116 if ( empty( $legacy_option->get() ) ) {
117 $this->settings->set( array( 'use_legacy_blc_version' => false ) );
118 } else {
119 $this->settings->set( array( 'use_legacy_blc_version' => true ) );
120 }
121
122 $this->settings->set( array( 'installation_timestamp' => time() ) );
123 $this->settings->save();
124 }
125
126 /**
127 * Gets triggerred once a user gets removed from current subsite on a multsite network.
128 *
129 * @param int|null $user_id
130 * @param int|null $user_id_reassign
131 *
132 * @return void
133 */
134 public function remove_user_from_blog( int $user_id = null, int $user_id_reassign = null ) {
135 $this->adapt_schedule_recipients( $user_id );
136 }
137
138 /**
139 * Adapts schedule recipients when a user is deleted from admin users.
140 *
141 * @param int|null $user_id
142 * @param int|null $user_id_reassign
143 *
144 * @return void
145 */
146 public function adapt_schedule_recipients( int $user_id = null, int $user_id_reassign = null ) {
147 if ( empty( $user_id ) ) {
148 return;
149 }
150
151 $schedule = $this->settings->get( 'schedule' );
152
153 if ( ! empty( $schedule['recipients'] ) ) {
154 if ( in_array( $user_id, $schedule['recipients'] ) ) {
155 $user_key = array_search(
156 $user_id,
157 $schedule['recipients']
158 );
159
160 if ( intval( $schedule['recipients'][ $user_key ] ) === $user_id ) {
161 unset( $schedule['recipients'][ $user_key ] );
162 $schedule['recipients'] = array_values( $schedule['recipients'] );
163
164 unset( $schedule['registered_recipients_data'][ $user_id ] );
165
166 $this->settings->set( array( 'schedule' => $schedule ) );
167 $this->settings->save();
168 }
169 }
170 }
171 }
172 }
1 <?php
2 /**
3 * The Option model
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Options\Settings
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Options\Settings;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Models\Option;
20 use function array_filter;
21
22 /**
23 * Class Settings
24 *
25 * @package WPMUDEV_BLC\App\Options\Settings
26 */
27 class Model extends Option {
28 /**
29 * Default options. Optional
30 *
31 * @since 2.0.0
32 * @var string|array|null $option_keys
33 */
34 public $default = array(
35 /*
36 * Scan status (scan_status) :
37 * none : Never started
38 * in_progress : Is currently running
39 * completed : Has been completed
40 */
41 'scan_status' => 'none',
42 'site_connected' => false,
43 'activation_modal_shown' => false,
44 'use_legacy_blc_version' => true,
45 'blc_schedule_scan_in_progress' => false,
46 'show_multisite_notice' => true,
47 'installation_timestamp' => null,
48 'v2_activation_request' => false,
49 'schedule' => array(
50 'active' => false,
51 'recipients' => array(),
52 'registered_recipients_data' => array(),
53 'emailRecipients' => array(),
54 'frequency' => 'daily',
55 'days' => array(),
56 'monthdays' => array(),
57 'time' => '00:00',
58 ),
59 'scan_results' => array(
60 /*
61 * List of broken links. Storing to be used in Scan Report Emails. Stores limited number links configured
62 * in `WPMUDEV_BLC\App\Scan_Models\limit_links_number()`
63 */
64 'broken_links_list',
65 'broken_links' => null,
66 'succeeded_urls' => null,
67 'total_urls' => null,
68 'unique_urls' => null,
69 'start_time' => null,
70 'end_time' => null,
71 'duration' => null,
72 ),
73 );
74 /**
75 * The option_name.
76 *
77 * @since 2.0.0
78 * @var string $name
79 */
80 protected $name = 'blc_settings';
81
82 /**
83 * Returns the scheduled scan email recipients that have been confirmed.
84 *
85 * @return array.
86 */
87 public function get_scan_active_email_recipients() {
88 $schedule = $this->get( 'schedule' );
89
90 if ( empty( $schedule ) ) {
91 return array();
92 }
93
94 $email_recipients = (array) $schedule['emailrecipients'] ?? array();
95
96 $active_recipients = array_filter(
97 $email_recipients,
98 function ( array $recipient = array() ) {
99 return isset( $recipient['confirmed'] ) && $recipient['confirmed'];
100 }
101 );
102
103 return $active_recipients;
104 }
105 }
1 <?php
2 /**
3 * Rest endpoint fetching Avatars.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Rest_Endpoints\Avatars
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Rest_Endpoints\Avatars;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WP_Error;
20 use WP_REST_Request;
21 use WP_REST_Server;
22 use WPMUDEV_BLC\Core\Controllers\Rest_Api;
23 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
24
25 /**
26 * Class Controller
27 *
28 * @package WPMUDEV_BLC\App\Rest_Endpoints\Avatars
29 */
30 class Controller extends Rest_Api {
31 /**
32 * Settings keys.
33 *
34 * @var array
35 */
36 protected $settings_keys = array();
37
38 public function init() {
39 $this->settings_keys = array_map(
40 function ( $settings_key ) {
41 return sanitize_key( $settings_key );
42 },
43 array_keys( Settings::instance()->default )
44 );
45
46 $this->namespace = "wpmudev_blc/{$this->version}";
47 $this->rest_base = 'avatars';
48
49 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
50 }
51
52 /**
53 * Register the routes for the objects of the controller.
54 *
55 * @since 2.0.0
56 *
57 * @return void
58 */
59 public function register_routes() {
60 register_rest_route(
61 $this->namespace,
62 '/' . $this->rest_base,
63 array(
64 array(
65 'methods' => WP_REST_Server::EDITABLE,
66 'callback' => array( $this, 'get_avatar' ),
67 'permission_callback' => array( $this, 'get_avatar_permissions' ),
68 ),
69 'schema' => array( $this, 'get_item_schema' ),
70 )
71 );
72 }
73
74 /**
75 * Returns avatar.
76 *
77 * @since 2.0.0
78 *
79 * @param object $request WP_REST_Request get data from request.
80 *
81 * @return mixed WP_REST_Response|WP_Error|WP_HTTP_Response|mixed $response
82 */
83 public function get_avatar( $request ) {
84 $email = $request->get_param( 'email' );
85
86 $response_data = array(
87 'message' => __( 'Avatar url', 'broken-link-checker' ),
88 'status_code' => 200,
89 );
90
91 if ( ! is_email( $email ) ) {
92 $response_data['message'] = __( 'Invalid email address', 'broken-link-checker' );
93 $response_data['status_code'] = 500;
94 } else {
95 $avatar = get_avatar_url( $email, array( 'size' => 24 ) );
96 $response_data['avatar'] = $avatar;
97 }
98
99 $response = $this->prepare_item_for_response( $response_data, $request );
100
101 return rest_ensure_response( $response );
102 }
103
104
105 /**
106 * Check permissions for fetching avatar.
107 *
108 * @since 2.0.0
109 *
110 * @param object $request get data from request.
111 *
112 * @return bool|object Boolean or WP_Error.
113 */
114 public function get_avatar_permissions( WP_REST_Request $request ) {
115 if ( ! current_user_can( 'manage_options' ) ) {
116 return new WP_Error(
117 'rest_forbidden',
118 esc_html__( 'You can not fetch avatars.', 'broken-link-checker' ),
119 array( 'status' => $this->authorization_status_code() )
120 );
121 }
122
123 return true;
124 }
125
126 /**
127 * Retrieves the item's schema, conforming to JSON Schema.
128 *
129 * @since 2.0.0
130 *
131 * @return array Item schema data.
132 */
133 public function get_item_schema() {
134 if ( $this->schema ) {
135 return $this->add_additional_fields_schema( $this->schema );
136 }
137
138 $this->schema = array(
139 '$schema' => 'http://json-schema.org/draft-04/schema#',
140 'title' => isset( $args['rest_base'] ) ? $args['rest_base'] : '',
141 'type' => 'object',
142 'properties' => array(),
143 );
144
145 $this->schema['properties'] = array(
146 'avatar' => array(
147 'description' => esc_html__( 'Avatar by email.', 'broken-link-checker' ),
148 'type' => 'string',
149 ),
150
151 'confirmed' => array(
152 'description' => esc_html__( 'Auto-confirmed when email belongs to user.', 'broken-link-checker' ),
153 'type' => 'boolean',
154 ),
155
156 'message' => array(
157 'description' => esc_html__( 'Response message.', 'broken-link-checker' ),
158 'type' => 'string',
159 ),
160
161 'status_code' => array(
162 'description' => esc_html__( 'Response status code.', 'broken-link-checker' ),
163 'type' => 'string',
164 'context' => array( 'view', 'edit' ),
165 'enum' => array(
166 '200',
167 '400',
168 '401',
169 '403',
170 ),
171 'readonly' => true,
172 ),
173 );
174
175 return $this->add_additional_fields_schema( $this->schema );
176 }
177
178 }
1 <?php
2 /**
3 * Rest endpoint starting new Scan.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Rest_Endpoints\Scan
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Rest_Endpoints\Scan;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WP_Error;
20 use WP_REST_Request;
21 use WP_REST_Server;
22
23 use WPMUDEV_BLC\Core\Controllers\Rest_Api;
24 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
25 use WPMUDEV_BLC\App\Http_Requests\Sync_Scan_Results\Controller as Scan_API;
26
27 /**
28 * Class Controller
29 *
30 * @package WPMUDEV_BLC\App\Rest_Endpoints\Scan
31 */
32 class Controller extends Rest_Api {
33 /**
34 * Settings keys.
35 *
36 * @var array
37 */
38 protected $settings_keys = array();
39
40 public function init() {
41 $this->settings_keys = array_map(
42 function ( $settings_key ) {
43 return sanitize_key( $settings_key );
44 },
45 array_keys( Settings::instance()->default )
46 );
47
48 $this->namespace = "wpmudev_blc/{$this->version}";
49 $this->rest_base = 'scan';
50
51 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
52 }
53
54 /**
55 * Register the routes for the objects of the controller.
56 *
57 * @since 2.0.0
58 *
59 * @return void
60 */
61 public function register_routes() {
62 register_rest_route(
63 $this->namespace,
64 '/' . $this->rest_base,
65 array(
66 array(
67 'methods' => WP_REST_Server::EDITABLE,
68 'callback' => array( $this, 'handle_scan_request' ),
69 'permission_callback' => array( $this, 'get_scan_permissions' ),
70 ),
71 'schema' => array( $this, 'get_item_schema' ),
72 )
73 );
74 }
75
76 public function handle_scan_request( $request ) {
77 $action = $request->get_param( 'action' );
78
79 if ( 'start_scan' === $action ) {
80 return $this->start_scan( $request );
81 }
82
83 // Action is `fetch_scan_data`.
84 return $this->monitor_scan( $request );
85 }
86
87 /**
88 * Returns new scan info.
89 *
90 * @param object $request WP_REST_Request get data from request.
91 *
92 * @since 2.0.0
93 *
94 * @return mixed WP_REST_Response|WP_Error|WP_HTTP_Response|mixed $response
95 */
96 public function start_scan( $request ) {
97 $response_data = array();
98
99 $scan = Scan_API::instance();
100 $scan->start();
101
102 $response_data['status_code'] = $scan->get_response_code();
103 $response_data['message'] = $scan->get_response_message();
104 $response_data['data'] = $scan->get_response_data();
105 $response_data['success'] = 200 === $scan->get_response_code();
106 $response_data['scan_status'] = $scan->get_response_scan_status();
107 $response = $this->prepare_item_for_response( $response_data, $request );
108
109 return rest_ensure_response( $response );
110 }
111
112
113 /**
114 * @param $request
115 *
116 * @return mixed
117 */
118 public function monitor_scan( $request ) {
119 $response_data = array();
120 $scan = Scan_API::instance();
121
122 $scan->start();
123
124 if ( is_wp_error( $scan->get_response() ) ) {
125 $response_data['success'] = false;
126 $response_data['status_code'] = 500;
127 $response_data['message'] = $scan->get_error_message();
128 $response_data['data'] = null;
129 } else {
130 $response_data['status_code'] = $scan->get_response_code();
131 $response_data['message'] = $scan->get_response_message();
132 }
133
134 $response_data['success'] = $scan->get_response_code() === 200;
135 $response_data['scan_status'] = $scan->get_response_scan_status();
136 $response_data['data'] = $scan->get_response_data();
137
138 return rest_ensure_response( $this->prepare_item_for_response( $response_data, $request ) );
139 }
140
141 /**
142 * Check permissions for fetching avatar.
143 *
144 * @param object $request get data from request.
145 *
146 * @since 2.0.0
147 *
148 * @return bool|object Boolean or WP_Error.
149 */
150 public function get_scan_permissions( WP_REST_Request $request ) {
151 if ( ! current_user_can( 'manage_options' ) ) {
152 return new WP_Error(
153 'rest_forbidden',
154 esc_html__( 'You are not allowed to start a new scan.', 'broken-link-checker' ),
155 array( 'status' => $this->authorization_status_code() )
156 );
157 }
158
159 return true;
160 }
161
162 /**
163 * Retrieves the item's schema, conforming to JSON Schema.
164 *
165 * @since 2.0.0
166 *
167 * @return array Item schema data.
168 */
169 public function get_item_schema() {
170 if ( $this->schema ) {
171 return $this->add_additional_fields_schema( $this->schema );
172 }
173
174 $this->schema = array(
175 '$schema' => 'http://json-schema.org/draft-04/schema#',
176 'title' => isset( $args['rest_base'] ) ? $args['rest_base'] : '',
177 'type' => 'object',
178 'properties' => array(),
179 );
180
181 $this->schema['properties'] = array(
182 'success' => array(
183 'description' => esc_html__( 'If true scan has started successfully, else it has not.', 'broken-link-checker' ),
184 'type' => 'boolean',
185 ),
186
187 'scan_status' => array(
188 'description' => esc_html__( 'The scan status.', 'broken-link-checker' ),
189 'type' => 'string',
190 'enum' => array(
191 'completed',
192 'in_progress',
193 'none',
194 ),
195 ),
196
197 'message' => array(
198 'description' => esc_html__( 'Response message.', 'broken-link-checker' ),
199 'type' => 'string',
200 ),
201
202 'data' => array(
203 'description' => esc_html__( 'Scan response data/results.', 'broken-link-checker' ),
204 'type' => 'string',
205 ),
206
207 'status_code' => array(
208 'description' => esc_html__( 'Response status code.', 'broken-link-checker' ),
209 'type' => 'string',
210 'context' => array( 'view', 'edit' ),
211 'enum' => array(
212 '200',
213 '400',
214 '401',
215 '403',
216 '500',
217 ),
218 'readonly' => true,
219 ),
220 );
221
222 return $this->add_additional_fields_schema( $this->schema );
223 }
224
225 }
1 <?php
2 /**
3 * The Schema for Rest endpoint
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Rest_Endpoints\Settings
10 */
11
12 namespace WPMUDEV_BLC\App\Rest_Endpoints\Settings\Includes;
13
14 // Abort if called directly.
15 defined( 'WPINC' ) || die;
16
17 class Schema {
18 /**
19 * Get Schema for Rest Endpoint.
20 *
21 * @since 1.0.0
22 *
23 * @param string $action A string that contains action that endpoint performs.
24 *
25 * @param array $args An array containing several options that we can use for returning specific schema properties.
26 *
27 * @return array An array containing Schema.
28 */
29 public static function get_schema( string $action = null, array $args = array() ) {
30 if ( \is_null( $action ) ) {
31 return array();
32 }
33
34 $poperties_keys = array();
35 $schema = array(
36 '$schema' => 'http://json-schema.org/draft-04/schema#',
37 'title' => isset( $args['rest_base'] ) ? $args['rest_base'] : '',
38 'type' => 'object',
39 'properties' => array(),
40 );
41
42 switch ( $action ) {
43 case 'save':
44 $poperties_keys = array( 'message', 'status_code' );
45 break;
46 case 'get':
47 $poperties_keys = array( 'message', 'status_code', 'settings' );
48 break;
49 }
50
51 $schema['properties'] = self::get_schema_properties( $poperties_keys );
52
53 return $schema;
54 }
55
56 /**
57 * Get Schema properties for Rest Response.
58 *
59 * @since 1.0.0
60 *
61 * @param array $properties_keys An array containing field keys for properties needed.
62 *
63 * @return array An array of schema properties.
64 */
65 protected static function get_schema_properties( array $properties_keys = array() ) {
66 $return_properties = array();
67 $schema_properties = array(
68 'settings' => array(
69 'description' => esc_html__( 'All BLC settings.', 'broken-link-checker' ),
70 'type' => 'object',
71 'properties' => array(
72 'activation_modal_shown' => array(
73 'description' => esc_html__( 'Activation modal shown or not.', 'broken-link-checker' ),
74 'type' => 'string',
75 ),
76
77 'use_legacy_blc_version' => array(
78 'description' => esc_html__( 'Use legacy BLC', 'broken-link-checker' ),
79 'type' => 'string',
80 ),
81
82 'userRolesAllowed' => array(
83 'type' => 'object',
84 'properties' => array(
85 'name' => array(
86 'description' => esc_html__( 'User role name', 'broken-link-checker' ),
87 'type' => 'string',
88 ),
89 'label' => array(
90 'description' => esc_html__( 'User role label', 'broken-link-checker' ),
91 'type' => 'string',
92 ),
93 ),
94 ),
95 ),
96 ),
97
98 'message' => array(
99 'description' => esc_html__( 'Response message.', 'broken-link-checker' ),
100 'type' => 'string',
101 ),
102
103 'status_code' => array(
104 'description' => esc_html__( 'Response status code.', 'broken-link-checker' ),
105 'type' => 'string',
106 'context' => array( 'view', 'edit' ),
107 'enum' => array(
108 '200',
109 '400',
110 '401',
111 '403',
112 ),
113 'readonly' => true,
114 ),
115
116 'instructions' => array(
117 'description' => esc_html__( 'Response instructions.', 'broken-link-checker' ),
118 'type' => 'object',
119 ),
120 );
121
122 if ( empty( $properties_keys ) ) {
123 $return_properties = $schema_properties;
124 } else {
125 $return_properties = \array_filter(
126 $schema_properties,
127 function( string $property_key = '' ) use ( $properties_keys ) {
128 return in_array( $property_key, $properties_keys );
129 },
130 ARRAY_FILTER_USE_KEY
131 );
132 }
133
134 return apply_filters( 'wpmudev_blc_rest_enpoints_settings_schema_properties', $return_properties );
135 }
136 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2 /**
3 * Sets or Inserts fresh scan records in DB.
4 * Used by
5 * Cron that syncs report data (WPMUDEV_BLC\App\Http_Requests\Sync_Scan_Results\Controller)
6 * Hub endpoint the sets report data (WPMUDEV_BLC\App\Hub_Endpoints\Set_Data\Controller)
7 *
8 * @link https://wordpress.org/plugins/broken-link-checker/
9 * @since 2.0.0
10 *
11 * @author WPMUDEV (https://wpmudev.com)
12 * @package WPMUDEV_BLC\App\Scan_Models
13 *
14 * @copyright (c) 2022, Incsub (http://incsub.com)
15 */
16
17 namespace WPMUDEV_BLC\App\Scan_Models;
18
19 // Abort if called directly.
20 defined( 'WPINC' ) || die;
21
22 use stdClass;
23 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
24 use WPMUDEV_BLC\Core\Utils\Utilities;
25 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
26 use function is_multisite;
27 use function property_exists;
28 use function restore_current_blog;
29 use function switch_to_blog;
30
31 /**
32 * Class Scan_Data
33 *
34 * @package WPMUDEV_BLC\App\Scan_Models
35 */
36 class Scan_Data extends Base {
37 /**
38 * Inserts new scan data to DB.
39 *
40 * @param string $json_data The data to insert needs to be in json format.
41 *
42 * @return bool
43 */
44 public function set( string $json_data = '' ) {
45 if ( empty( $json_data ) ) {
46 Utilities::log( 'BLC_SET_SCAN_DATA_ERROR - Exiting because input does not contain any data.' );
47
48 return false;
49 }
50
51 $unformated_input = json_decode( $json_data );
52
53 // Make sure input is valid json.
54 if ( json_last_error() !== JSON_ERROR_NONE ) {
55 Utilities::log( 'BLC_SET_SCAN_DATA_ERROR - Exiting because input was not valid json string.' );
56
57 return false;
58 }
59
60 $params = $unformated_input->params;
61 $url = property_exists( $params, 'url' ) ? $params->url : null;
62 $site_id = property_exists( $params, 'site_id' ) ? intval( $params->site_id ) : null;
63
64 if ( $site_id !== Utilities::site_id() ) {
65 Utilities::log( 'BLC_SET_SCAN_DATA_ERROR - Exiting because BLC HUB API ping does not contain site id.' );
66
67 return false;
68 }
69
70 $input = $this->get_formatted_input( $params );
71 $use_subsite_id = false;
72
73 if ( empty( $input ) ) {
74 return false;
75 }
76
77 if ( is_multisite() ) {
78 $subside_id = Utilities::subsite_id_from_url( $url );
79
80 if ( ! empty( $subside_id ) ) {
81 $use_subsite_id = true;
82
83 switch_to_blog( $subside_id );
84 }
85 }
86
87 Settings::instance()->init();
88 Settings::instance()->set( array( 'scan_results' => $input ) );
89 Settings::instance()->set( array( 'scan_status' => 'completed' ) );
90 Settings::instance()->save();
91
92 if ( $use_subsite_id ) {
93 restore_current_blog();
94 }
95
96 return true;
97 }
98
99 /**
100 * Returns an array of expected scan data values.
101 *
102 * @param stdClass $params The input object.
103 *
104 * @return array|null
105 */
106 public function get_formatted_input( stdClass $params ) {
107 //if ( ! empty( $params->scanning->is_running ) ) {
108 // Utilities::log( 'BLC_SET_SCAN_DATA_ERROR - Exiting because BLC HUB API scan is running currently.' );
109
110 // return null;
111 //}
112
113 $results = $params->last_result;
114
115 return array(
116 'broken_links_list' => $this->limit_links_number( $results->broken_links ),
117 'broken_links' => $results->num_broken_links ?? null,
118 'succeeded_urls' => $results->num_successful_links ?? null,
119 'total_urls' => $results->num_found_links ?? null,
120 'unique_urls' => $results->num_site_unique_links ?? null,
121 'start_time' => $results->start_unix_time_utc ?? null,
122 'end_time' => $results->ended_unix_time_utc ?? null,
123 'duration' => $results->scan_duration ?? null,
124 );
125 }
126
127 /**
128 * Limits the number of links to be stored in db. Limit is set to 20.
129 *
130 * @param array $links_list An array with all broken links sent from Hub API.
131 *
132 * @return array|null
133 */
134 public function limit_links_number( array $links_list = array() ) {
135 if ( empty( $links_list ) ) {
136 return null;
137 }
138
139 $links_limit = apply_filters( 'wpmudev_blc_settings_links_limit', 20, $links_list, $this );
140
141 // Make sure we don't store ignored ones.
142 // We only store the broken links to be sent to be sent from the
143 if ( count( $links_list ) > $links_limit ) {
144 $new_links_list = array();
145
146 foreach( $links_list as $key => $broken_link ) {
147 if ( property_exists( $broken_link, 'is_ignored' ) && ! empty( $broken_link->is_ignored ) ) {
148 continue;
149 }
150
151 $new_links_list[] = $broken_link;
152 }
153
154 $links_list = $new_links_list;
155 }
156
157 return array_slice(
158 $links_list,
159 0,
160 $links_limit
161 );
162 }
163 }
1 <?php
2 /**
3 * Legacy Scheduled event for BLC v1.
4 * Handles legacy cron jobs.
5 *
6 * @link https://wordpress.org/plugins/broken-link-checker/
7 * @since 2.0.0
8 *
9 * @author WPMUDEV (https://wpmudev.com)
10 * @package WPMUDEV_BLC\App\Schedule_Events\Legacy
11 *
12 * @copyright (c) 2022, Incsub (http://incsub.com)
13 */
14
15 namespace WPMUDEV_BLC\App\Scheduled_Events\Legacy;
16
17 // Abort if called directly.
18 defined( 'WPINC' ) || die;
19
20 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
21
22 /**
23 * Class Controller
24 *
25 * @package WPMUDEV_BLC\App\Schedule_Events\Scan
26 */
27 class Controller extends Base {
28 /**
29 * @var array $legacy_crons List of all legacy scheduled events.
30 */
31 private $legacy_crons = array(
32 'blc_cron_check_links',
33 'blc_cron_email_notifications',
34 'blc_cron_database_maintenance',
35 'blc_corn_clear_log_file',
36 'blc_cron_check_news',
37 );
38
39 /**
40 * @var array $v2_crons List of all v2 scheduled events.
41 */
42 private $v2_crons = array(
43 'blc_recipients_activation_email_schedule',
44 'blc_schedule_sync_scan_results',
45 'blc_schedule_scan',
46 );
47
48 /**
49 * Init Schedule
50 *
51 * @since 2.0.0
52 *
53 * @return void
54 */
55 public function init() {
56 add_action( 'wpmudev_blc_plugin_deactivated', array( $this, 'deactivate_legacy_crons' ) );
57 add_action( 'wpmudev_blc_rest_enpoints_switch_version_mode', array( $this, 'switch_version_mode' ) );
58 }
59
60 /**
61 * Switched cron jobs depending on plugin version mode (legacy or v2).
62 *
63 * @since 2.0.0
64 *
65 * @param bool $legacy_active A boolean indicating if legacy mode is set.
66 *
67 * @retun void
68 */
69 public function switch_version_mode( bool $legacy_active = true ) {
70 if ( $legacy_active ) {
71 // Enable legacy crons.
72 $this->activate_legacy_crons();
73
74 // Disable v2 cron events.
75 $this->deactivate_v2_crons();
76
77 } else {
78 // Disable legacy crons.
79 $this->deactivate_legacy_crons();
80
81 // Enable v2 crons.
82 do_action( 'wpmudev_blc_plugin_activated' );
83 \WPMUDEV_BLC\App\Scheduled_Events\Scan\Controller::instance()->set_scan_schedule();
84 \WPMUDEV_BLC\App\Scheduled_Events\Sync_Scan_Results\Controller::instance()->activate_cron();
85 }
86 }
87
88 /**
89 * Deactivates all legacy plugin's scheduled events.
90 *
91 * @since 2.0.0
92 *
93 * @return void
94 */
95 public function deactivate_legacy_crons() {
96 $this->deactivate_crons( $this->legacy_crons );
97
98 // We still need to force clear `blc_schedule_scan` cron.
99 wp_clear_scheduled_hook( 'blc_schedule_scan' );
100
101 add_filter( 'blc_allow_send_email_notification', '__return_false' );
102 }
103
104 /**
105 * Deactivates all v2 plugin's scheduled events.
106 *
107 * @since 2.0.0
108 *
109 * @return void
110 */
111 public function deactivate_v2_crons() {
112 $this->deactivate_crons( $this->v2_crons );
113 }
114
115 /**
116 * Deactivates given list of scheduled events.
117 *
118 * @since 2.0.0
119 *
120 * @param array $crons List of scheduled events to be deactivated.
121 *
122 * @return void
123 */
124 public function deactivate_crons( array $cron_hooks = array() ) {
125 if ( empty( $cron_hooks ) ) {
126 return;
127 }
128
129 foreach ( $cron_hooks as $cron_hook ) {
130 wp_clear_scheduled_hook( $cron_hook );
131 }
132 }
133
134 public function activate_legacy_crons() {
135 global $blc_config_manager;
136
137 $ws_link_checker = null;
138
139 if ( ! class_exists( 'wsBrokenLinkChecker' ) ) {
140 require_once BLC_DIRECTORY_LEGACY . '/core/core.php';
141 }
142
143 if ( $blc_config_manager instanceof \blcConfigurationManager ) {
144 $ws_link_checker = new \wsBrokenLinkChecker( BLC_PLUGIN_FILE_LEGACY, $blc_config_manager );
145 }
146
147 if ( ! is_null( $ws_link_checker ) ) {
148 // Enable legacy cron events.
149 $ws_link_checker->setup_cron_events();
150 }
151 }
152 }
1 <?php
2 /**
3 * Scheduled event for BLC Scan.
4 * A single scheduled event that gets triggered based on options set in "Schedule Scan"
5 *
6 * @link https://wordpress.org/plugins/broken-link-checker/
7 * @since 2.0.0
8 *
9 * @author WPMUDEV (https://wpmudev.com)
10 * @package WPMUDEV_BLC\App\Schedule_Events\Scan
11 *
12 * @copyright (c) 2022, Incsub (http://incsub.com)
13 */
14
15 namespace WPMUDEV_BLC\App\Scheduled_Events\Scan;
16
17 // Abort if called directly.
18 defined( 'WPINC' ) || die;
19
20 use WPMUDEV_BLC\App\Http_Requests\Scan\Controller as Scan_API;
21 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
22 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
23 use WPMUDEV_BLC\Core\Traits\Cron;
24 use WPMUDEV_BLC\Core\Utils\Utilities;
25
26 /**
27 * Class Controller
28 *
29 * @package WPMUDEV_BLC\App\Schedule_Events\Scan
30 */
31 class Controller extends Base {
32 use Cron;
33
34 /**
35 * WP Cron hook to execute when event is run.
36 *
37 * @var string
38 */
39 public $cron_hook_name = 'blc_schedule_scan';
40
41 /**
42 * BLC settings from options table.
43 *
44 * @var array
45 */
46 private $settings = null;
47
48 /**
49 * Init Schedule
50 *
51 * @since 2.0.0
52 *
53 * @return void
54 */
55 public function init() {
56 //if ( wp_doing_ajax() || ! $this->get_schedule( 'active' ) ) {
57 if ( wp_doing_ajax() ) {
58 return;
59 }
60
61 Settings::instance()->init();
62
63 //add_action( 'init', array( $this, 'load' ) );
64 //add_action( 'wpmudev_blc_rest_enpoints_after_save_schedule_settings', array( $this, 'deactivate_cron' ), 10 );
65 add_action( 'wpmudev_blc_rest_enpoints_after_save_schedule_settings', array( $this, 'set_scan_schedule' ) );
66
67 add_action( 'wpmudev_blc_plugin_deactivated', array( $this, 'deactivate_cron' ) );
68 }
69
70 /**
71 * Returns the scheduled event's hook name.
72 * Overriding Trait's method.
73 */
74 public function get_hook_name() {
75 return $this->cron_hook_name;
76 }
77
78 /**
79 * Starts the scheduled scan.
80 */
81 public function process_scheduled_event() {
82 // At his point we're setting the scan status flag to `in_progress`. So if it doesn't get `completed` there
83 // will ba a sync request fired on page load.
84 Settings::instance()->set( array( 'scan_status' => 'in_progress' ) );
85 Settings::instance()->save();
86
87 $scan = Scan_API::instance();
88
89 $scan->start();
90 $this->set_scan_schedule_flag();
91 //$this->deactivate_cron();
92 $this->set_scan_schedule();
93 }
94
95 /**
96 * Sets the scan flag to true. Useful when API sends the SET request, an email about the current schedule should
97 * be sent to schedule receivers.
98 */
99 public function set_scan_schedule_flag( bool $flag = true ) {
100 Settings::instance()->set( array( 'blc_schedule_scan_in_progress' => $flag ) );
101 Settings::instance()->set( array( 'scan_status' => 'in_progress' ) );
102 Settings::instance()->save();
103 }
104
105 /**
106 * Sets new scan schedule.
107 *
108 * @param array $settings The settings param from `wpmudev_blc_rest_enpoints_after_save_schedule_settings` action.
109 *
110 * @return bool
111 */
112 public function set_scan_schedule( array $settings = array() ) {
113 if ( ! $this->get_schedule( 'active' ) ) {
114 return false;
115 }
116
117 // Deactivate cron if is already created, so we will replace it later on.
118 $this->deactivate_cron();
119
120 // As a single event it will be possible to set custom timestamps to run.
121 $this->is_single_event = true;
122 // Set the timestamp based on Schedule options.
123 $this->timestamp = intval( $this->get_timestamp( $settings['schedule'] ?? array() ) );
124 /*
125 * setup_cron() is handled by Cron trait.
126 */
127 //$this->setup_cron();
128 return $this->activate_cron();
129 }
130
131 /**
132 * Returns the schedule from settings, or if a key is set, it returns that key's value
133 *
134 * @param string $key .
135 *
136 * @return array|mixed|null
137 */
138 private function get_schedule( string $key = '' ) {
139 if ( is_null( $this->settings ) ) {
140 $this->settings = Settings::instance()->get( 'schedule' );
141 }
142
143 if ( ! empty( $key ) && is_array( $this->settings ) ) {
144 return $this->settings[ $key ] ?? null;
145 }
146
147 return $this->settings;
148 }
149
150 /**
151 * Returns the timestamp of next scheduled scan.
152 *
153 * @param array $schedule
154 *
155 * @return false|int|null
156 */
157 public function get_timestamp( array $schedule = array() ) {
158 $schedule = ! empty( $schedule['frequency'] ) ? $schedule : $this->get_schedule();
159 $timestamp = null;
160
161 if ( empty( $schedule['frequency'] ) || empty( $schedule['time'] ) ) {
162 return $timestamp;
163 }
164
165 $schedule_time = $schedule['time'];
166 // phpcs:ignore
167 $current_day_num = 'monthly' === $schedule['frequency'] ? date( 'd' ) : date( 'w', time() );
168 $schedule_days = 'monthly' === $schedule['frequency'] ? $schedule['monthdays'] : $schedule['days'];
169
170 if ( 'daily' !== $schedule['frequency'] && empty( $schedule_days ) ) {
171 return $timestamp;
172 }
173
174 sort( $schedule_days );
175
176 switch ( $schedule['frequency'] ) {
177 case 'daily':
178 if ( date_format( date_create( date_i18n( 'H:i' ) ), 'Hi' ) > date_format( date_create( $schedule_time ), 'Hi' ) ) {
179 $timestamp = strtotime( "tomorrow {$schedule_time} " . wp_timezone_string() );
180 } else {
181 $timestamp = strtotime( "today {$schedule_time} " . wp_timezone_string() );
182 }
183 break;
184
185 case 'weekly':
186 case 'monthly':
187 $next_day_num = null;
188 $move_to_next_period = false;
189
190 if ( in_array( $current_day_num, $schedule_days, true ) ) {
191 $day_key = array_keys( $schedule_days, $current_day_num, true )[0];
192
193 if ( date_format( date_create( date_i18n( 'H:i' ) ), 'Hi' ) >= date_format( date_create( $schedule_time ), 'Hi' ) ) {
194 $next_day_num = array_key_exists( ( $day_key + 1 ), $schedule_days ) ? $schedule_days[ ( $day_key + 1 ) ] : null;
195 } else {
196 $timestamp = strtotime( "today {$schedule_time}" . ' ' . wp_timezone_string() );
197 }
198 }
199
200 if ( is_null( $next_day_num ) ) {
201 foreach ( $schedule_days as $day_num ) {
202 if ( intval( $day_num ) > intval( $current_day_num ) ) {
203 $next_day_num = intval( $day_num );
204 break;
205 }
206 }
207 }
208
209 if ( is_null( $next_day_num ) ) {
210 $next_day_num = intval( $schedule_days[0] );
211 $move_to_next_period = true;
212 }
213
214 if ( is_null( $timestamp ) ) {
215 if ( 'weekly' === $schedule['frequency'] ) {
216 // phpcs:ignore
217 $day_name = date( 'l', strtotime( "Sunday +{$next_day_num} days" ) );
218 $timestamp = strtotime( "next {$day_name} {$schedule_time}" . ' ' . wp_timezone_string() );
219 } else {
220 if ( $move_to_next_period ) {
221 // As we're adding $next_day_num as additional days in next month, we need to deduct it by one.
222 -- $next_day_num;
223 $timestamp = strtotime( "+{$next_day_num} days {$schedule_time}" . ' ' . wp_timezone_string(), strtotime( 'first day of next month' ) );
224 } else {
225 $days_diff = $next_day_num - $current_day_num;
226 $timestamp = strtotime( "+{$days_diff} days {$schedule_time}" . ' ' . wp_timezone_string() );
227 }
228 }
229 }
230
231 break;
232
233 default:
234 $timestamp = null;
235 }
236
237 return $timestamp;
238 }
239 }
1 <?php
2 /**
3 * Schedule to sync plugin with latest scan results from BLC API.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Schedule_Events\Sync_Scan_Results
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Scheduled_Events\Sync_Scan_Results;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 // use WPMUDEV_BLC\App\Http_Requests\Sync_Scan_Results\Controller as Scan_API;
20 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
21 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
22 use WPMUDEV_BLC\Core\Traits\Cron;
23 use WPMUDEV_BLC\Core\Utils\Utilities;
24
25 /**
26 * Class Controller
27 *
28 * @package WPMUDEV_BLC\App\Schedule_Events\Sync_Scan_Results
29 */
30 class Controller extends Base {
31 use Cron;
32
33 /**
34 * WP Cron hook to execute when event is run.
35 *
36 * @var string
37 */
38 public $cron_hook_name = 'blc_schedule_sync_scan_results';
39
40 /**
41 * BLC settings from options table.
42 *
43 * @var array
44 */
45 private $settings = null;
46
47 /**
48 * Init Mailer
49 *
50 * @since 2.0.0
51 *
52 * @return void
53 */
54 public function init() {
55 add_action( 'wpmudev_blc_plugin_deactivated', array( $this, 'deactivate_cron' ) );
56
57 if ( wp_doing_ajax() || Settings::instance()->get( 'use_legacy_blc_version' ) ) {
58 return;
59 }
60
61 $this->setup_cron();
62 }
63
64 /**
65 * Prepares vars
66 *
67 * @return void
68 */
69 public function prepare_vars() {
70 $this->cron_interval_title = 'hourly';
71 $this->timestamp = time() + MINUTE_IN_SECONDS;
72 $this->cron_interval_title = 'twicedaily';
73 }
74
75 /**
76 * Gives the cron interval.
77 *
78 * @return string
79 */
80 public function ___get_cron_interval_title() {
81 return 'twicedaily';
82 }
83
84 /**
85 * Returns the scheduled event's hook name.
86 * Overriding Trait's method.
87 */
88 public function get_hook_name() {
89 return $this->cron_hook_name;
90 }
91
92 /**
93 * Starts the scheduled scan.
94 */
95 public function process_scheduled_event() {
96 Utilities::log( 'Scheduled event for sync started' );
97
98 $scan = new \WPMUDEV_BLC\App\Http_Requests\Sync_Scan_Results\Controller();
99 $scan->start();
100 }
101 }
1 <?php
2 /**
3 * Controller for Recipient activation virtual post.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Emails\Recipient_Activation
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Virtual_Posts\Recipient_Activation;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Controllers\Virtual_Post;
20 use WPMUDEV_BLC\App\Webhooks\Recipient_Activation\Controller as Recipient_Activation;
21
22 /**
23 * Class Controller
24 *
25 * @package WPMUDEV_BLC\App\Virtual_Posts\Recipient_Activation
26 */
27 class Controller extends Virtual_Post {
28 public function prepare_vars() {
29 $this->post_title = __( 'Broken links reports', 'broken-link-checker' );
30 }
31
32 /**
33 * @param string $the_content
34 *
35 * @return string
36 */
37 protected function post_content( string $the_content = '' ) {
38 $activated_recipient = Recipient_Activation::instance()->activated_recipient;
39 $action = isset( $_GET['action'] ) && in_array(
40 $_GET['action'],
41 array(
42 'activate',
43 'cancel',
44 )
45 ) ?
46 $_GET['action'] : null;
47
48 if ( empty( $activated_recipient ) || is_null( $action ) ) {
49 global $wp_query;
50
51 $wp_query->set_404();
52 status_header( 404 );
53 nocache_headers();
54 include get_query_template( '404' );
55 die();
56 }
57
58 ob_start();
59 if ( 'activate' === $action ) {
60 View::instance()->render_activation_message( $activated_recipient );
61 } elseif ( 'cancel' === $action ) {
62 View::instance()->render_cancellation_message( $activated_recipient );
63 }
64
65 return ob_get_clean();
66 }
67
68 public function can_load_virtual_post() {
69 global $wp;
70
71 return ! empty( $wp->query_vars ) && array_key_exists( Recipient_Activation::instance()->webhook_tag, $wp->query_vars );
72 }
73 }
1 <?php
2 /**
3 * Virtual post for Recipient Activation view.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Admin_Pages\Settings
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Virtual_Posts\Recipient_Activation;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\Core\Utils\Abstracts\Base;
20
21
22 /**
23 * Class View
24 *
25 * @package WPMUDEV_BLC\App\Virtual_Posts\Recipient_Activation
26 */
27 class View extends Base {
28
29 /**
30 * Render the output.
31 *
32 * @since 2.0.0
33 *
34 * @return void Render the output.
35 */
36 public function render_activation_message( $params = array() ) {
37 $cancellation_link = $params['cancellation_link'] ?? '';
38 $cancelation_message = $this->cancellation_instructions_message( $cancellation_link );
39
40 ?>
41 <div style="width: 100%; margin: auto;" class="blc-vpost blc-activation-message">
42 <h4>
43 <?php esc_html_e( 'You have been added in broken links reports recipients.', 'broken-link-checker' ); ?>
44 </h4>
45 <p>
46 <?php
47 printf(
48 /* translators: 1: Recipient name 2: Opening link tag 3; Closing link tag */
49 esc_html__(
50 'Hi %1$s. You have become a recipient of site\'s broken links reports. You can continue to site from %2$shere%3$s.',
51 'broken-link-checker'
52 ),
53 $params['name'],
54 '<a href="' . site_url() . '">',
55 '</a>'
56 );
57 ?>
58 </p>
59
60 <p>
61 <?php echo $cancelation_message; ?>
62 </p>
63 </div>
64 <?php
65 }
66
67 protected function cancellation_instructions_message( string $cancellation_link = '' ) {
68 $message = '';
69
70 if ( ! empty( $cancellation_link ) ) {
71 $message = sprintf(
72 /* translators: 1: Opening link tag 2; Closing link tag */
73 esc_html__(
74 '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.',
75 'broken-link-checker'
76 ),
77 '<a href="' . esc_html( $cancellation_link ) . '">',
78 '</a>'
79 );
80 } else {
81 $message = esc_html__(
82 'If you do not wish to receive these reports in your email, you can contact site admins.',
83 'broken-link-checker'
84 );
85 }
86
87 return $message;
88 }
89
90 public function render_cancellation_message( $params = array() ) {
91 ?>
92 <div style="width: 100%; margin: auto;" class="blc-vpost blc-activation-message">
93 <h4>
94 <?php
95 esc_html_e( 'Your email has been successfully removed from broken links recipients list.', 'broken-link-checker' );
96 ?>
97 </h4>
98 <p>
99 <?php
100 printf(
101 /* translators: 1: Recipient name 2: Opening link tag 3; Closing link tag */
102 esc_html__(
103 'Hi %1$s. You should stop receiving broken links reports for this site. You can visit site from %2$shere%3$s.',
104 'broken-link-checker'
105 ),
106 $params['name'],
107 '<a href="' . site_url() . '">',
108 '</a>'
109 );
110 ?>
111 </p>
112
113 </div>
114 <?php
115 }
116 }
1 <?php
2 /**
3 * Controller for Recipient activation webhook.
4 *
5 * @link https://wordpress.org/plugins/broken-link-checker/
6 * @since 2.0.0
7 *
8 * @author WPMUDEV (https://wpmudev.com)
9 * @package WPMUDEV_BLC\App\Webhooks\Recipient_Activation
10 *
11 * @copyright (c) 2022, Incsub (http://incsub.com)
12 */
13
14 namespace WPMUDEV_BLC\App\Webhooks\Recipient_Activation;
15
16 // Abort if called directly.
17 defined( 'WPINC' ) || die;
18
19 use WPMUDEV_BLC\App\Options\Settings\Model as Settings;
20 use WPMUDEV_BLC\Core\Controllers\Webhook;
21
22 /**
23 * Class Controller
24 *
25 * @package WPMUDEV_BLC\App\Emails\Recipient_Activation
26 */
27 class Controller extends Webhook {
28 /**
29 * The webhook tag.
30 *
31 * @var string $webhook The webhook tag
32 */
33 public $webhook_tag = 'blc-activate-recipient';
34
35 /**
36 * The activated recipient data in an array. Retrieved by activation code.
37 *
38 * @var array $activated_recipient
39 */
40 public $activated_recipient = array();
41
42 /**
43 * The plugin settings.
44 *
45 * @var array $settings
46 */
47 public $settings = array();
48
49 /**
50 * Prepares the class properties.
51 *
52 * @return void
53 */
54 public function prepare_vars() {
55 $this->webhook_title = __( 'Activate broken links recipient', 'broken-link-checker' );
56 }
57
58 /**
59 * Executes the webhook action(s).
60 *
61 * @param $wp
62 *
63 * @return void
64 */
65 public function webhook_action( &$wp ) {
66 $this->settings = Settings::instance()->get();
67 $activation_key = $_GET['activation_code'] ?? null;
68 $action = isset( $_GET['action'] ) && in_array( $_GET['action'], array(
69 'activate',
70 'cancel'
71 ) ) ?
72 $_GET['action'] : null;
73
74 if ( is_null( $action ) ) {
75 return;
76 }
77
78 $this->activated_recipient = $this->get_recipient_by_key( $activation_key );
79
80 if ( empty( $this->activated_recipient ) ) {
81 return;
82 }
83
84 switch ( $action ) {
85 case 'activate' :
86 if ( $this->set_recipient_status( $this->activated_recipient ) ) {
87 $this->activated_recipient['cancellation_link'] = add_query_arg( array(
88 'action' => 'cancel',
89 'activation_code' => esc_html( $activation_key ),
90 ), $this->webhook_url() );
91 }
92 break;
93 case 'cancel' :
94 if ( isset( $this->activated_recipient['user_id'] ) ) {
95 $this->unset_registered_recipient( intval( $this->activated_recipient['user_id'] ) );
96 } else {
97 $this->set_recipient_status( $this->activated_recipient, false );
98 }
99 break;
100 }
101 }
102
103 protected function get_recipient_by_key( string $key = '' ) {
104 if ( empty( $key ) ) {
105 return false;
106 }
107
108 $key_parts = explode( '_', base64_decode( $key ) );
109
110 if ( count( $key_parts ) < 2 ) {
111 return array();
112 }
113
114 $hashed_email = $key_parts[0];
115 $recipient_key = sanitize_text_field( $key_parts[1] );
116 $schedule = $this->settings['schedule'] ?? array();
117 $recipient = array();
118
119 //if ( empty( $schedule ) || ( empty( $schedule['emailrecipients'] ) && empty( $schedule['registered_recipients_data'] ) ) ) {
120 //return array();
121 //}
122
123 if ( ! empty( $schedule['emailrecipients'] ) ) {
124 $recipient = array_filter(
125 $schedule['emailrecipients'],
126 function ( $recipient_data ) use ( $recipient_key, $hashed_email ) {
127
128 return isset( $recipient_data['key'] ) &&
129 $recipient_data['key'] === $recipient_key &&
130 $hashed_email === md5( $recipient_data['email'] );
131 }
132 );
133 }
134
135 if ( empty( $recipient ) ) {
136 if ( ! empty( $schedule['registered_recipients_data'] ) ) {
137 foreach ( $schedule['registered_recipients_data'] as $user_id => $user_data ) {
138 $user = null;
139
140 if ( md5( $user_data['email'] ) === $hashed_email ) {
141 $user = get_user_by( 'email', sanitize_email( $user_data['email'] ) );
142 }
143
144 if ( $user instanceof \WP_User ) {
145 $recipient = array(
146 'key' => $user_data['key'],
147 'name' => $user->display_name,
148 'email' => $user->user_email,
149 'user_id' => $user->ID,
150 );
151
152 break;
153 }
154 }
155 }
156
157 if ( empty( $recipient ) ) {
158 $user_key_parts = explode( '|', $recipient_key );
159 $user = ! empty( $user_key_parts[1] ) ? get_userdata( intval( $user_key_parts[1] ) ) : null;
160
161 if ( $user instanceof \WP_User && md5( $user->user_email ) === $hashed_email ) {
162 $recipient = array(
163 'key' => $recipient_key,
164 'name' => $user->display_name,
165 'email' => $user->user_email,
166 'user_id' => $user->ID,
167 );
168 }
169 }
170
171 } else {
172 $recipient = array_values( $recipient )[0];
173 }
174
175 return $recipient;
176 }
177
178 /**
179 * Changes the recipient status for recipients added by email.
180 *
181 * @param array $recipient_data
182 * @param bool $new_status
183 *
184 * @return bool
185 */
186 protected function set_recipient_status( array $recipient_data = array(), bool $new_status = true ) {
187 if (
188 empty( $recipient_data ) ||
189 ! isset( $recipient_data['email'] ) ||
190 ! isset( $this->settings['schedule'] ) ||
191 ( empty( $this->settings['schedule']['emailrecipients'] ) && empty( $this->settings['schedule']['registered_recipients_data'] ) )
192 ) {
193 return false;
194 }
195
196 Settings::instance()->init();
197
198 $this->settings['schedule']['emailrecipients'] = array_map(
199 function ( $recipient ) use ( $recipient_data, $new_status ) {
200 if ( isset( $recipient['email'] ) && $recipient['email'] === $recipient_data['email'] ) {
201 $recipient['confirmed'] = boolval( $new_status );
202 }
203
204 return $recipient;
205 },
206 $this->settings['schedule']['emailrecipients']
207 );
208
209 Settings::instance()->set( array( 'schedule' => $this->settings['schedule'] ) );
210 Settings::instance()->save();
211
212 return true;
213 }
214
215 /**
216 * Removes a recipient by user id.
217 *
218 * @param int|null $user_id
219 *
220 * @return false|void
221 */
222 protected function unset_registered_recipient( int $user_id = null ) {
223 if ( empty( $user_id ) ) {
224 return false;
225 }
226
227 $user_key = array_search(
228 $user_id,
229 $this->settings['schedule']['recipients']
230 );
231
232 if (
233 ! is_numeric( $user_key ) ||
234 empty( $this->settings['schedule']['recipients'][ $user_key ] ) ||
235 intval( $this->settings['schedule']['recipients'][ $user_key ] ) !== $user_id
236 ) {
237 return false;
238 }
239
240 unset( $this->settings['schedule']['recipients'][ $user_key ] );
241
242 $this->settings['schedule']['recipients'] = array_values( $this->settings['schedule']['recipients'] );
243
244 unset( $this->settings['schedule']['registered_recipients_data'][ $user_id ] );
245
246 Settings::instance()->set( array( 'schedule' => $this->settings['schedule'] ) );
247 Settings::instance()->save();
248 }
249 }
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.