PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.9.2
WP 2FA – Two-factor authentication for WordPress v2.9.2
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Admin / Fly-Out / class-flyout.php
wp-2fa / includes / classes / Admin / Fly-Out Last commit date
assets 10 months ago class-flyout.php 10 months ago index.php 10 months ago
class-flyout.php
306 lines
1 <?php
2 /**
3 * Responsible for fly-out menu shown on some of the plugin pages.
4 *
5 * @package wp2fa
6 * @subpackage flyout
7 *
8 * @since 2.8.0
9 *
10 * @copyright 2025 Melapress
11 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
12 *
13 * @see https://wordpress.org/plugins/wp-2fa/
14 */
15
16 declare(strict_types=1);
17
18 namespace WP2FA\Admin\FlyOut;
19
20 use WP2FA\WP2FA;
21 use WP2FA\Admin\Helpers\WP_Helper;
22
23 // Exit if accessed directly.
24 if ( ! defined( 'ABSPATH' ) ) {
25 exit;
26 }
27
28 if ( ! class_exists( '\WP2FA\Admin\FlyOut\FlyOut' ) ) {
29 /**
30 * Generates fly-out menu on the plugin admin screen.
31 *
32 * @since 2.8.0
33 */
34 class FlyOut {
35
36 private const ENQUEUE_NAME = 'mlp_flyout';
37 private const CONFIG_TRANSIENT_NAME = \WP_2FA_PREFIX . 'flyout_config';
38
39 /**
40 * Array with the configuration of the fly-out menu
41 *
42 * @var array
43 *
44 * @since 2.8.0
45 */
46 private static $config = array();
47
48 /**
49 * Class cache for the current screen (if admin is on it)
50 *
51 * @var bool
52 *
53 * @since 2.8.0
54 */
55 private static $screen = null;
56
57 /**
58 * Inits the class and its hooks
59 *
60 * @return void
61 *
62 * @since 2.8.0
63 */
64 public static function init() {
65 if ( ! \is_admin() ) {
66 return;
67 } else {
68 self::load_config();
69 if ( ! empty( self::$config ) ) {
70 \add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
71 \add_action( 'admin_head', array( __CLASS__, 'admin_head' ) );
72 \add_action( 'admin_footer', array( __CLASS__, 'admin_footer' ) );
73 }
74 }
75 }
76
77 /**
78 * Loads the external config for processing
79 *
80 * @return void
81 *
82 * @since 2.8.0
83 */
84 public static function load_config() {
85 $config = array();
86
87 $config = self::read_remote_config();
88
89 if ( false === $config ) {
90 return;
91 }
92
93 $defaults = array(
94 'plugin_screen' => '',
95 'icon_border' => '#0000ff',
96 'icon_right' => '40px',
97 'icon_bottom' => '40px',
98 'icon_image' => '',
99 'icon_padding' => '2px',
100 'icon_size' => '55px',
101 'menu_accent_color' => '#ca4a1f',
102 'custom_css' => '',
103 'menu_items' => array(),
104 );
105
106 $config = array_merge( $defaults, (array) $config );
107 if ( ! is_array( $config['plugin_screen'] ) ) {
108 $config['plugin_screen'] = array( $config['plugin_screen'] );
109 }
110
111 if ( WP_Helper::is_multisite() ) {
112 foreach ( $config['plugin_screen'] as $key => $value ) {
113 $config['plugin_screen'][ $key ] = $value . '-network';
114 }
115 }
116
117 self::$config = $config;
118 }
119
120 /**
121 * Checks the current screen and returns true if it is the plugin one
122 *
123 * @return boolean
124 *
125 * @since 2.8.0
126 */
127 public static function is_plugin_screen(): bool {
128
129 if ( \is_null( self::$screen ) ) {
130
131 $screen = \get_current_screen();
132 self::$screen = false;
133
134 if ( in_array( $screen->id, self::$config['plugin_screen'] ) ) {
135 self::$screen = true;
136 }
137 }
138
139 return self::$screen;
140 }
141
142 /**
143 * Loads the fly-out css and JS files
144 *
145 * @return void
146 *
147 * @since 2.8.0
148 */
149 public static function admin_enqueue_scripts() {
150 if ( false === self::is_plugin_screen() ) {
151 return;
152 }
153
154 \wp_enqueue_style(
155 self::ENQUEUE_NAME,
156 WP_2FA_URL . '/includes/classes/Admin/Fly-Out/assets/css/flyout.css',
157 array(),
158 WP_2FA_VERSION
159 );
160 \wp_enqueue_script(
161 self::ENQUEUE_NAME,
162 WP_2FA_URL . '/includes/classes/Admin/Fly-Out/assets/js/flyout.js',
163 array(),
164 WP_2FA_VERSION,
165 true
166 );
167 }
168
169 /**
170 * Writes additional custom code in the header of the page
171 *
172 * @return void
173 *
174 * @since 2.8.0
175 */
176 public static function admin_head() {
177 if ( false === self::is_plugin_screen() ) {
178 return;
179 }
180
181 $out = '<style type="text/css">';
182 $out .= '#mlp-flyout {
183 right: ' . \sanitize_text_field( self::$config['icon_right'] ) . ';
184 bottom: ' . \sanitize_text_field( self::$config['icon_bottom'] ) . ';
185 }';
186 $out .= '#mlp-flyout #mlp-elements-image-wrapper {
187 border: ' . \sanitize_text_field( self::$config['icon_border'] ) . ';
188 }';
189 $out .= '#mlp-flyout #mlp-elements-button img {
190 padding: ' . \sanitize_text_field( self::$config['icon_padding'] ) . ';
191 width: ' . \sanitize_text_field( self::$config['icon_size'] ) . ';
192 height: ' . \sanitize_text_field( self::$config['icon_size'] ) . ';
193 }';
194 $out .= '#mlp-flyout .mlp-elements-menu-item.accent {
195 background: ' . \sanitize_text_field( self::$config['menu_accent_color'] ) . ';
196 }';
197 $out .= \sanitize_text_field( self::$config['custom_css'] );
198 $out .= '</style>';
199
200 echo $out; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
201 }
202
203 /**
204 * Writes additional custom code in the footer of the page
205 *
206 * @return void
207 *
208 * @since 2.8.0
209 */
210 public static function admin_footer() {
211 if ( false === self::is_plugin_screen() ) {
212 return;
213 }
214
215 $out = '';
216 $icons_url = WP_2FA_URL . 'dist/images/';
217 $default_link_item = array(
218 'class' => '',
219 'href' => '#',
220 'target' => '_blank',
221 'label' => '',
222 'icon' => '',
223 'data' => '',
224 'type' => 'all',
225 );
226
227 $out .= '<div id="mlp-overlay"></div>';
228
229 $out .= '<div id="mlp-flyout">';
230
231 $out .= '<a href="#" id="mlp-elements-button">';
232 $out .= '<span class="mlp-elements-label">Open Quick Links</span>';
233 $out .= '<span id="mlp-elements-image-wrapper">';
234 $out .= '<img src="' . esc_url( $icons_url . self::$config['icon_image'] ) . '" alt="Open Quick Links" title="Open Quick Links">';
235 $out .= '</span>';
236 $out .= '</a>';
237
238 $out .= '<div id="mlp-elements-menu">';
239 $i = 0;
240 foreach ( array_reverse( self::$config['menu_items'] ) as $item ) {
241 $item = array_merge( $default_link_item, $item );
242
243 if ( ( isset( $item['type'] ) && 'all' === $item['type'] ) || ( isset( $item['type'] ) && WP2FA::determine_plugin_type() === $item['type'] ) ) {
244 ++$i;
245
246 if ( ! empty( $item['icon'] ) && \str_starts_with( $item['icon'], 'dashicons' ) ) {
247 $item['class'] .= ' mlp-elements-custom-icon';
248 $item['class'] = trim( $item['class'] );
249 }
250
251 $out .= '<a ' . $item['data'] . ' href="' . esc_url( $item['href'] ) . '" class="mlp-elements-menu-item mlp-elements-menu-item-' . $i . ' ' . esc_attr( $item['class'] ) . '" target="' . esc_attr( $item['target'] ) . '">';
252 $out .= '<span class="mlp-elements-label visible">' . esc_html( $item['label'] ) . '</span>';
253 if ( \str_starts_with( $item['icon'], 'dashicons' ) ) {
254 $out .= '<span class="dashicons ' . sanitize_text_field( $item['icon'] ) . '"></span>';
255 } elseif ( ! empty( $item['icon'] ) ) {
256 $out .= '<span class="mlp-elements-icon"><img src="' . esc_url( $icons_url . $item['icon'] ) . '"></span>';
257 }
258 $out .= '</a>';
259 }
260 } // foreach
261 $out .= '</div>'; // #mlp-elements-menu
262
263 $out .= '</div>'; // #mlp-flyout
264
265 echo $out; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
266 }
267
268 /**
269 * Reads the config file remotely and sets 2 days transient for caching. If for some reason cant read the remote - false is returned
270 *
271 * @return bool|array
272 *
273 * @since 2.8.0
274 */
275 public static function read_remote_config() {
276 $config = \get_transient( self::CONFIG_TRANSIENT_NAME );
277
278 if ( false === $config || empty( $config ) ) {
279
280 $api_response = \wp_remote_request( 'https://melapress.com/downloads/plugins-files/wp-2fa-flyout-config.php', array() );
281
282 $response_code = \wp_remote_retrieve_response_code( $api_response );
283
284 if ( \is_wp_error( $api_response ) || 200 !== (int) $response_code ) {
285
286 return false;
287 } else {
288 $config = \wp_remote_retrieve_body( $api_response );
289
290 \set_transient( self::CONFIG_TRANSIENT_NAME, $config, \DAY_IN_SECONDS * 3 );
291
292 return \json_decode( $config, true );
293 }
294 }
295
296 $config = json_decode( $config, true );
297
298 if ( json_last_error() !== JSON_ERROR_NONE ) {
299 $config = false;
300 }
301
302 return $config;
303 }
304 }
305 }
306