PluginProbe ʕ •ᴥ•ʔ
Admin and Site Enhancements (ASE) / 8.6.2
Admin and Site Enhancements (ASE) v8.6.2
9.1.0 9.0.2 9.0.1 9.0.0 8.9.2 8.9.1 8.9.0 8.8.8 8.8.7 8.8.6 8.8.5 8.8.4 8.8.3 8.8.2 8.8.1 8.8.0 8.7.3 8.7.2 8.7.1 8.2.1 8.2.2 8.2.3 8.3.0 8.3.1 8.3.2 8.4.0 8.4.1 8.4.2 8.5.0 8.5.1 8.5.2 8.6.0 8.6.1 8.6.2 8.7.0 5.0.1 5.0.2 5.0.2.1 5.0.2.2 5.0.2.3 5.0.2.4 5.1.0 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.8.0 5.8.1 6.0.0 6.0.3 6.0.4 6.0.5 6.0.5.1 6.0.6 6.0.7 6.0.8.1 6.1.0 6.1.3 6.2.0 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.2.6 6.2.7 6.3.0 6.3.1 6.3.2 6.4.0 6.5.0 6.5.1 6.6.0 6.7.0 6.8.0 6.8.2 6.8.3 6.9.0 6.9.1 6.9.10 6.9.11 6.9.12 6.9.13.1 6.9.13.2 6.9.2 6.9.3 6.9.4 6.9.5 6.9.6 6.9.7 6.9.8 6.9.9 7.0.0 7.0.2 7.0.2.1 7.0.2.2 7.0.2.3 7.0.3 7.1.0 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2.0 7.2.1 7.3.0 7.3.1 7.3.2 7.3.3 7.4.0 7.4.2 7.4.4 7.4.5 7.4.6 7.4.7 7.4.8 7.5.0 7.5.1 7.5.2 7.5.3 7.5.4 7.6.0 7.6.1 7.6.1.1 7.6.10 7.6.11 7.6.2 7.6.3 7.6.4 7.6.5 7.6.6 7.6.7 7.6.7.1 7.6.8 7.6.9 7.7.0 7.8.0 7.8.1 7.8.10 7.8.11 7.8.12 7.8.13 7.8.14 7.8.15 7.8.16 7.8.17 7.8.18 7.8.2 7.8.3 7.8.4 7.8.5 7.8.5.1 7.8.6 7.8.7 7.8.8 7.8.9 7.9.0 7.9.1 7.9.10 7.9.11 7.9.2 7.9.3 7.9.4 7.9.5 7.9.6 7.9.7 7.9.8 7.9.9 8.0.0 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.0.6 8.0.7 8.0.8 8.1.0 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 trunk 1.0.0 1.1.0 1.2.0 1.3.0 1.4.0 1.5.0 1.6.0 1.7.0 1.8.0 1.9.0 2.0.0 2.1.0 2.2.0 2.3.0 2.4.0 2.5.0 2.6.0 2.7.0 2.8.0 2.8.1 2.8.2 2.8.3 2.9.0 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.2.0 3.3.0 3.4.0 3.5.0 3.6.1 3.7.0 3.8.0 3.9.0 3.9.1 3.9.2 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.1 4.8.2 4.8.3 4.9.0 4.9.1 4.9.2 4.9.3 5.0.0
admin-site-enhancements / classes / class-settings-fields-render.php
admin-site-enhancements / classes Last commit date
class-activation.php 4 months ago class-admin-menu-organizer.php 4 months ago class-admin-menu-svg-icon-mask.php 4 months ago class-auto-publish-posts-with-missed-schedule.php 4 months ago class-avif-upload.php 4 months ago class-captcha-protection.php 4 months ago class-change-login-url.php 4 months ago class-cleanup-admin-bar.php 4 months ago class-common-methods.php 4 months ago class-content-duplication.php 4 months ago class-content-order.php 4 months ago class-custom-admin-footer-text.php 4 months ago class-custom-body-class.php 4 months ago class-custom-css.php 4 months ago class-custom-nav-menu-items-in-new-tab.php 4 months ago class-deactivation.php 4 months ago class-disable-author-archives.php 4 months ago class-disable-comments.php 4 months ago class-disable-dashboard-widgets.php 4 months ago class-disable-embeds.php 4 months ago class-disable-feeds.php 4 months ago class-disable-gutenberg.php 4 months ago class-disable-rest-api.php 4 months ago class-disable-smaller-components.php 4 months ago class-disable-updates.php 4 months ago class-disable-xml-rpc.php 4 months ago class-display-system-summary.php 4 months ago class-email-address-obfuscator.php 4 months ago class-email-delivery.php 4 months ago class-enhance-list-tables.php 4 months ago class-external-permalinks.php 4 months ago class-heartbeat-control.php 4 months ago class-hide-admin-bar.php 4 months ago class-hide-admin-notices.php 4 months ago class-image-sizes-panel.php 4 months ago class-image-upload-control.php 4 months ago class-insert-head-body-footer-code.php 4 months ago class-last-login-column.php 4 months ago class-limit-login-attempts.php 4 months ago class-login-id-type.php 4 months ago class-login-logout-menu.php 4 months ago class-maintenance-mode.php 4 months ago class-manage-ads-appads-txt.php 4 months ago class-manage-robots-txt.php 4 months ago class-media-files-visibility-control.php 4 months ago class-media-replacement.php 4 months ago class-multiple-user-roles.php 4 months ago class-obfuscate-author-slugs.php 4 months ago class-open-external-links-in-new-tab.php 4 months ago class-password-protection.php 4 months ago class-redirect-after-login.php 4 months ago class-redirect-after-logout.php 4 months ago class-redirect-fourofour.php 4 months ago class-registration-date-column.php 4 months ago class-revisions-control.php 4 months ago class-search-engines-visibility.php 4 months ago class-settings-fields-render.php 4 months ago class-settings-sanitization.php 4 months ago class-settings-sections-fields.php 4 months ago class-show-custom-taxonomy-filters.php 4 months ago class-site-identity-on-login-page.php 4 months ago class-svg-upload.php 4 months ago class-various-admin-ui-enhancements.php 4 months ago class-view-admin-as-role.php 4 months ago class-wider-admin-menu.php 4 months ago class-wp-config-transformer.php 4 months ago
class-settings-fields-render.php
1358 lines
1 <?php
2
3 namespace ASENHA\Classes;
4
5 /**
6 * Class related to rendering of settings fields on the admin page
7 *
8 * @since 2.2.0
9 */
10 class Settings_Fields_Render {
11 /**
12 * Render checkbox field as a toggle/switcher
13 *
14 * @since 1.0.0
15 */
16 function render_checkbox_toggle( $args ) {
17 $option_name = ( isset( $args['option_name'] ) ? $args['option_name'] : '' );
18 if ( !empty( $option_name ) ) {
19 $options = get_option( $option_name, array() );
20 } else {
21 $options = get_option( ASENHA_SLUG_U, array() );
22 }
23 $field_name = $args['field_name'];
24 $field_title = ( isset( $args['field_title'] ) ? $args['field_title'] : '' );
25 $field_description = $args['field_description'];
26 $field_option_value = ( array_key_exists( $args['field_id'], $options ) ? $options[$args['field_id']] : false );
27 echo '<input type="checkbox" id="' . esc_attr( $field_name ) . '" class="asenha-field-checkbox" name="' . esc_attr( $field_name ) . '" ' . checked( $field_option_value, true, false ) . '>';
28 echo '<label for="' . esc_attr( $field_name ) . '"></label>';
29 // For field with additional options / sub-fields, we add a wrapper to enclose field descriptions
30 if ( array_key_exists( 'field_options_wrapper', $args ) && $args['field_options_wrapper'] ) {
31 // For when the options / sub-fields occupy lengthy vertical space, we add show all / less toggler
32 if ( array_key_exists( 'field_options_moreless', $args ) && $args['field_options_moreless'] ) {
33 echo '<div class="asenha-field-with-options field-show-more">';
34 echo '<a id="' . esc_attr( $args['field_slug'] ) . '-show-moreless" class="show-more-less show-more" href="#">' . __( 'Expand', 'admin-site-enhancements' ) . ' &#9660;</a>';
35 echo '<div class="asenha-field-options-wrapper wrapper-show-more">';
36 } else {
37 echo '<div class="asenha-field-with-options">';
38 echo '<div class="asenha-field-options-wrapper">';
39 }
40 }
41 echo '<div class="asenha-field-description" data-search-filter data-module-info="' . esc_attr( strtolower( $field_title ) ) . '">' . wp_kses_post( $field_description ) . '</div>';
42 // For field with additional options / sub-fields, we add wrapper for them
43 if ( array_key_exists( 'field_options_wrapper', $args ) && $args['field_options_wrapper'] ) {
44 echo '<div class="asenha-subfields" style="display:none"></div>';
45 }
46 // For field with additional options / sub-fields, we add a wrapper to enclose field descriptions
47 if ( array_key_exists( 'field_options_wrapper', $args ) && $args['field_options_wrapper'] ) {
48 echo '</div>';
49 echo '</div>';
50 }
51 }
52
53 /**
54 * Render checkbox field as sub-field of a toggle/switcher checkbox
55 *
56 * @since 1.9.0
57 */
58 function render_checkbox_plain( $args ) {
59 $option_name = $args['option_name'];
60 if ( !empty( $option_name ) ) {
61 $options = get_option( $option_name, array() );
62 } else {
63 $options = get_option( ASENHA_SLUG_U, array() );
64 }
65 $field_name = $args['field_name'];
66 $field_label = $args['field_label'];
67 $default_value = false;
68 switch ( $args['field_id'] ) {
69 case 'login_page_disable_registration':
70 $default_value = ( 1 == get_option( 'users_can_register' ) ? false : true );
71 break;
72 }
73 $field_option_value = ( isset( $options[$args['field_id']] ) ? $options[$args['field_id']] : $default_value );
74 $display_none_on_load = ( isset( $args['display_none_on_load'] ) ? $args['display_none_on_load'] : false );
75 if ( $display_none_on_load ) {
76 $inline_style = 'display:none;';
77 } else {
78 $inline_style = '';
79 }
80 echo '<input type="checkbox" id="' . esc_attr( $field_name ) . '" class="asenha-subfield-checkbox" style="' . esc_attr( $inline_style ) . '" name="' . esc_attr( $field_name ) . '" ' . checked( $field_option_value, true, false ) . '>';
81 echo '<label for="' . esc_attr( $field_name ) . '" class="asenha-subfield-checkbox-label" style="' . esc_attr( $inline_style ) . '">' . wp_kses_post( $field_label ) . '</label>';
82 }
83
84 /**
85 * Render checkbox field as sub-field of a toggle/switcher checkbox
86 *
87 * @since 1.3.0
88 */
89 function render_checkbox_subfield( $args ) {
90 $option_name = $args['option_name'];
91 if ( !empty( $option_name ) ) {
92 $options = get_option( $option_name, array() );
93 } else {
94 $options = get_option( ASENHA_SLUG_U, array() );
95 }
96 $field_name = $args['field_name'];
97 $field_label = $args['field_label'];
98 $parent_field_id = ( isset( $args['parent_field_id'] ) ? $args['parent_field_id'] : '' );
99 $sub_field_id = ( isset( $args['sub_field_id'] ) ? $args['sub_field_id'] : '' );
100 if ( in_array( $parent_field_id, array('enable_duplication_for', 'enable_rest_api_for') ) ) {
101 // Default is true/enabled. Usually for options introduced at a later date where the previous default is true/enabled.
102 $default_value = true;
103 } else {
104 // Default is false / checked
105 $default_value = false;
106 }
107 if ( !empty( $parent_field_id ) && !empty( $sub_field_id ) && isset( $options[$parent_field_id][$sub_field_id][$args['field_id']] ) ) {
108 $field_option_value = $options[$parent_field_id][$sub_field_id][$args['field_id']];
109 } elseif ( in_array( $parent_field_id, array('redirect_after_login_for_separate', 'redirect_after_logout_for_separate'), true ) && !empty( $sub_field_id ) ) {
110 $field_option_value = ( isset( $options[$sub_field_id][$args['field_id']] ) ? $options[$sub_field_id][$args['field_id']] : $default_value );
111 } elseif ( empty( $parent_field_id ) && !empty( $args['field_id'] ) ) {
112 $field_option_value = ( isset( $options[$args['field_id']] ) ? $options[$args['field_id']] : $default_value );
113 } else {
114 $field_option_value = ( isset( $options[$parent_field_id][$args['field_id']] ) ? $options[$parent_field_id][$args['field_id']] : $default_value );
115 }
116 echo '<input type="checkbox" id="' . esc_attr( $field_name ) . '" class="asenha-subfield-checkbox" name="' . esc_attr( $field_name ) . '" ' . checked( $field_option_value, true, false ) . '>';
117 echo '<label for="' . esc_attr( $field_name ) . '" class="asenha-subfield-checkbox-label">' . wp_kses_post( $field_label ) . '</label>';
118 if ( !empty( $args['field_description'] ) ) {
119 $desc_display = ( isset( $args['field_description_display'] ) ? $args['field_description_display'] : 'inline' );
120 if ( 'tooltip' === $desc_display ) {
121 echo '<span class="asenha-info-tooltip-wrap">';
122 echo '<button type="button" class="asenha-info-tooltip-toggle" aria-label="' . esc_attr__( 'More information', 'admin-site-enhancements' ) . '">';
123 echo '<span class="dashicons dashicons-info-outline" aria-hidden="true"></span>';
124 echo '</button>';
125 echo '<span class="asenha-info-tooltip" role="tooltip">' . esc_html( $args['field_description'] ) . '</span>';
126 echo '</span>';
127 } else {
128 echo '<div class="asenha-subfield-description">' . wp_kses_post( $args['field_description'] ) . '</div>';
129 }
130 }
131 }
132
133 /**
134 * Render radio buttons field as sub-field of a toggle/switcher checkbox
135 *
136 * @since 1.3.0
137 */
138 function render_radio_buttons_subfield( $args ) {
139 $option_name = $args['option_name'];
140 if ( !empty( $option_name ) ) {
141 $options = get_option( $option_name, array() );
142 } else {
143 $options = get_option( ASENHA_SLUG_U, array() );
144 }
145 $field_id = $args['field_id'];
146 $field_name = $args['field_name'];
147 $field_radios = $args['field_radios'];
148 if ( !empty( $args['field_default'] ) ) {
149 $default_value = $args['field_default'];
150 } else {
151 $default_value = false;
152 }
153 $field_description = ( isset( $args['field_description'] ) ? $args['field_description'] : '' );
154 $field_option_value = ( isset( $options[$field_id] ) ? $options[$field_id] : $default_value );
155 echo '<div class="asenha-subfield-radio-button-wrapper">';
156 foreach ( $field_radios as $radio_label => $radio_value ) {
157 echo '<input type="radio" id="' . esc_attr( $field_id . '_' . $radio_value ) . '" class="asenha-subfield-radio-button" name="' . esc_attr( $field_name ) . '" value="' . esc_attr( $radio_value ) . '" ' . checked( $radio_value, $field_option_value, false ) . '>';
158 echo '<label for="' . esc_attr( $field_id . '_' . $radio_value ) . '" class="asenha-subfield-radio-button-label">' . wp_kses_post( $radio_label ) . '</label>';
159 }
160 echo '</div>';
161 if ( !empty( $field_description ) ) {
162 echo '<div class="asenha-subfield-description">' . wp_kses_post( $field_description ) . '</div>';
163 }
164 }
165
166 /**
167 * Render checkboxes field as sub-field of a toggle/switcher checkbox
168 *
169 * @since 6.9.2
170 */
171 function render_checkboxes_subfield( $args ) {
172 $options = get_option( ASENHA_SLUG_U, array() );
173 $field_id = $args['field_id'];
174 $field_name = $args['field_name'];
175 $field_options = $args['field_options'];
176 $layout = ( !empty( $args['layout'] ) ? $args['layout'] : 'horizontal' );
177 $default_value = ( !empty( $args['field_default'] ) ? $args['field_default'] : array() );
178 $parent_field_id = ( isset( $args['parent_field_id'] ) ? $args['parent_field_id'] : '' );
179 $sub_field_id = ( isset( $args['sub_field_id'] ) ? $args['sub_field_id'] : '' );
180 if ( !empty( $parent_field_id ) && !empty( $sub_field_id ) && isset( $options[$parent_field_id][$sub_field_id][$field_id] ) ) {
181 $field_option_value = (array) $options[$parent_field_id][$sub_field_id][$field_id];
182 } else {
183 $field_option_value = ( isset( $options[$field_id] ) ? (array) $options[$field_id] : $default_value );
184 }
185 // Back-compat: legacy naming for Two-Factor Recovery Codes.
186 if ( in_array( $field_id, array('two_factor_available_providers', 'available_providers'), true ) && is_array( $field_option_value ) ) {
187 $field_option_value = array_map( static function ( $provider_key ) {
188 return ( 'backup_codes' === $provider_key ? 'recovery_codes' : $provider_key );
189 }, $field_option_value );
190 }
191 echo '<div class="wrapper-for-checkboxes ' . esc_attr( $layout ) . '">';
192 foreach ( $field_options as $option_label => $option_value ) {
193 $checkbox_id = ( !empty( $parent_field_id ) && !empty( $sub_field_id ) ? $parent_field_id . '_' . $sub_field_id . '_' . $field_id . '_' . $option_value : $field_id . '_' . $option_value );
194 echo '<div>';
195 echo '<input type="checkbox" id="' . esc_attr( $checkbox_id ) . '" class="asenha-subfield-radio-button" name="' . esc_attr( $field_name ) . '" value="' . esc_attr( $option_value ) . '" ' . checked( in_array( $option_value, $field_option_value ), 1, false ) . '>';
196 echo '<label for="' . esc_attr( $checkbox_id ) . '" class="asenha-subfield-radio-button-label">' . wp_kses_post( $option_label ) . '</label>';
197 echo '</div>';
198 }
199 echo '</div>';
200 }
201
202 /**
203 * Render text field as sub-field of a toggle/switcher checkbox
204 *
205 * @since 1.4.0
206 */
207 function render_text_subfield( $args ) {
208 $option_name = $args['option_name'];
209 if ( !empty( $option_name ) ) {
210 $options = get_option( $option_name, array() );
211 } else {
212 $options = get_option( ASENHA_SLUG_U, array() );
213 }
214 $field_id = $args['field_id'];
215 $field_slug = str_replace( '_', '-', $field_id );
216 $field_name = $args['field_name'];
217 $field_width_classname = ( isset( $args['field_width_classname'] ) ? $args['field_width_classname'] : '' );
218 $field_type = $args['field_type'];
219 $field_prefix = $args['field_prefix'];
220 $field_suffix = $args['field_suffix'];
221 $field_is_read_only = ( isset( $args['read_only'] ) ? $args['read_only'] : false );
222 $field_is_read_only_output = ( $field_is_read_only ? ' readonly="readonly"' : '' );
223 $field_placeholder = ( isset( $args['field_placeholder'] ) ? $args['field_placeholder'] : '' );
224 $field_description = $args['field_description'];
225 if ( isset( $options[$field_id] ) ) {
226 if ( 'live_site_url' == $field_id ) {
227 if ( false !== strpos( $options[$field_id], 'http' ) ) {
228 $field_option_value = $options[$field_id];
229 } else {
230 // Legacy support for when base64 encoding was used prior to v7.3.1
231 $field_option_value = base64_decode( $options[$field_id] );
232 }
233 } elseif ( 'login_page_logo_image_width' == $field_id || 'login_page_logo_image_height' == $field_id ) {
234 if ( isset( $options[$field_id] ) ) {
235 if ( is_numeric( $options[$field_id] ) ) {
236 $field_option_value = $options[$field_id];
237 } else {
238 $field_option_value = str_replace( 'px', '', $options[$field_id] );
239 }
240 }
241 } else {
242 $field_option_value = $options[$field_id];
243 }
244 } else {
245 if ( 'altcha_secret_key' == $field_id ) {
246 $field_option_value = '';
247 } else {
248 $field_option_value = '';
249 }
250 }
251 if ( !empty( $field_prefix ) && !empty( $field_suffix ) ) {
252 $field_classname = ' with-prefix with-suffix';
253 } elseif ( !empty( $field_prefix ) && empty( $field_suffix ) ) {
254 $field_classname = ' with-prefix';
255 } elseif ( empty( $field_prefix ) && !empty( $field_suffix ) ) {
256 $field_classname = ' with-suffix';
257 } else {
258 $field_classname = '';
259 }
260 if ( !empty( $field_width_classname ) ) {
261 $field_classname .= ' ' . $field_width_classname;
262 }
263 echo wp_kses_post( $field_prefix ) . '<input type="text" id="' . esc_attr( $field_name ) . '" class="asenha-subfield-text' . esc_attr( $field_classname ) . '" name="' . esc_attr( $field_name ) . '" placeholder="' . esc_attr( $field_placeholder ) . '" value="' . esc_attr( $field_option_value ) . '"' . esc_html( $field_is_read_only_output ) . '>' . wp_kses_post( $field_suffix );
264 echo '<label for="' . esc_attr( $field_name ) . '" class="asenha-subfield-checkbox-label">' . esc_html( $field_description ) . '</label>';
265 }
266
267 /**
268 * Render text field as sub-field of a checkbox field. e.g. in Redirect After Login module
269 *
270 * @since 7.3.3
271 */
272 function render_checkbox_field_text_subfield( $args ) {
273 $option_name = $args['option_name'];
274 if ( !empty( $option_name ) ) {
275 $options = get_option( $option_name, array() );
276 } else {
277 $options = get_option( ASENHA_SLUG_U, array() );
278 }
279 $field_id = $args['field_id'];
280 $field_name = $args['field_name'];
281 $field_width_classname = ( isset( $args['field_width_classname'] ) ? $args['field_width_classname'] : '' );
282 $field_type = $args['field_type'];
283 $field_prefix = $args['field_prefix'];
284 $field_suffix = $args['field_suffix'];
285 $field_placeholder = ( isset( $args['field_placeholder'] ) ? $args['field_placeholder'] : '' );
286 $field_description = $args['field_description'];
287 $parent_field_id = ( isset( $args['parent_field_id'] ) ? $args['parent_field_id'] : '' );
288 $sub_field_id = ( isset( $args['sub_field_id'] ) ? $args['sub_field_id'] : '' );
289 if ( 'redirect_after_login_for_separate' == $parent_field_id && !empty( $sub_field_id ) ) {
290 $field_option_value = ( isset( $options[$sub_field_id][$field_id] ) ? $options[$sub_field_id][$field_id] : '' );
291 } else {
292 $field_option_value = ( isset( $options[$parent_field_id][$field_id] ) ? $options[$parent_field_id][$field_id] : '' );
293 }
294 // $field_option_value = ( isset( $options[$field_id] ) ) ? $options[$field_id] : '';
295 $field_option_value = ( isset( $options[$parent_field_id . '_slug'][$args['field_id']] ) ? $options[$parent_field_id . '_slug'][$field_id] : '' );
296 if ( !empty( $field_prefix ) && !empty( $field_suffix ) ) {
297 $field_classname = ' with-prefix with-suffix';
298 } elseif ( !empty( $field_prefix ) && empty( $field_suffix ) ) {
299 $field_classname = ' with-prefix';
300 } elseif ( empty( $field_prefix ) && !empty( $field_suffix ) ) {
301 $field_classname = ' with-suffix';
302 } else {
303 $field_classname = '';
304 }
305 if ( !empty( $field_width_classname ) ) {
306 $field_classname .= ' ' . $field_width_classname;
307 }
308 echo wp_kses_post( $field_prefix ) . '<input type="text" id="' . esc_attr( $field_name ) . '" class="asenha-subfield-text' . esc_attr( $field_classname ) . '" name="' . esc_attr( $field_name ) . '" placeholder="' . esc_attr( $field_placeholder ) . '" value="' . esc_attr( $field_option_value ) . '">' . wp_kses_post( $field_suffix );
309 echo '<label for="' . esc_attr( $field_name ) . '" class="asenha-subfield-checkbox-label">' . esc_html( $field_description ) . '</label>';
310 }
311
312 /**
313 * Render description field as sub-field of a toggle/switcher checkbox
314 *
315 * @since 4.6.0
316 */
317 function render_description_subfield( $args ) {
318 $field_description = $args['field_description'];
319 echo '<div class="asenha-subfield-description">' . wp_kses( $field_description, get_kses_with_style_src_svg_ruleset() ) . '</div>';
320 }
321
322 /**
323 * Render heading for sub-fields of a toggle/switcher checkbox
324 *
325 * @since 5.0.0
326 */
327 function render_subfields_heading( $args ) {
328 $subfields_heading = $args['subfields_heading'];
329 echo '<div class="asenha-subfields-heading">' . wp_kses_post( $subfields_heading ) . '</div>';
330 }
331
332 /**
333 * Render password field as sub-field of a toggle/switcher checkbox
334 *
335 * @since 4.1.0
336 */
337 function render_password_subfield( $args ) {
338 $option_name = $args['option_name'];
339 if ( !empty( $option_name ) ) {
340 $options = get_option( $option_name, array() );
341 } else {
342 $options = get_option( ASENHA_SLUG_U, array() );
343 }
344 $field_id = $args['field_id'];
345 $field_name = $args['field_name'];
346 $field_type = $args['field_type'];
347 $field_prefix = $args['field_prefix'];
348 $field_suffix = $args['field_suffix'];
349 $field_description = $args['field_description'];
350 $field_option_value = ( isset( $options[$args['field_id']] ) ? $options[$args['field_id']] : '' );
351 $hide_stored_value = ( isset( $args['hide_stored_value'] ) ? (bool) $args['hide_stored_value'] : false );
352 if ( !empty( $field_prefix ) && !empty( $field_suffix ) ) {
353 $field_classname = ' with-prefix with-suffix';
354 } elseif ( !empty( $field_prefix ) && empty( $field_suffix ) ) {
355 $field_classname = ' with-prefix';
356 } elseif ( empty( $field_prefix ) && !empty( $field_suffix ) ) {
357 $field_classname = ' with-suffix';
358 } else {
359 $field_classname = '';
360 }
361 if ( $hide_stored_value ) {
362 $field_option_value = '';
363 }
364 $placeholder = ( isset( $args['field_placeholder'] ) ? $args['field_placeholder'] : '' );
365 echo wp_kses_post( $field_prefix ) . '<input type="password" id="' . esc_attr( $field_name ) . '" class="asenha-subfield-password' . esc_attr( $field_classname ) . '" name="' . esc_attr( $field_name ) . '" placeholder="' . esc_attr( $placeholder ) . '" size="24" autocomplete="off" value="' . esc_attr( $field_option_value ) . '">' . wp_kses_post( $field_suffix );
366 echo '<label for="' . esc_attr( $field_name ) . '" class="asenha-subfield-checkbox-label">' . esc_html( $field_description ) . '</label>';
367 }
368
369 /**
370 * Render number field as sub-field of a toggle/switcher checkbox
371 *
372 * @since 1.4.0
373 */
374 function render_number_subfield( $args ) {
375 $option_name = $args['option_name'];
376 if ( !empty( $option_name ) ) {
377 $options = get_option( $option_name, array() );
378 } else {
379 $options = get_option( ASENHA_SLUG_U, array() );
380 }
381 $field_id = $args['field_id'];
382 $field_name = $args['field_name'];
383 $field_type = $args['field_type'];
384 $field_prefix = $args['field_prefix'];
385 $field_suffix = $args['field_suffix'];
386 $field_intro = $args['field_intro'];
387 $field_placeholder = ( isset( $args['field_placeholder'] ) ? $args['field_placeholder'] : '' );
388 $field_min = ( isset( $args['field_min'] ) ? $args['field_min'] : 1 );
389 $field_max = ( isset( $args['field_max'] ) ? $args['field_max'] : 10 );
390 $field_description = ( isset( $args['field_description'] ) ? $args['field_description'] : '' );
391 $field_option_value = ( isset( $options[$args['field_id']] ) ? $options[$args['field_id']] : '' );
392 if ( !empty( $field_prefix ) && !empty( $field_suffix ) ) {
393 $field_classname = ' with-prefix with-suffix';
394 } elseif ( !empty( $field_prefix ) && empty( $field_suffix ) ) {
395 $field_classname = ' with-prefix';
396 } elseif ( empty( $field_prefix ) && !empty( $field_suffix ) ) {
397 $field_classname = ' with-suffix';
398 } else {
399 $field_classname = '';
400 }
401 echo '<div class="asenha-subfield-number-wrapper">';
402 if ( !empty( $field_intro ) ) {
403 echo '<div class="asenha-subfield-number-intro">' . wp_kses_post( $field_intro ) . '</div>';
404 }
405 echo '<div>' . wp_kses_post( $field_prefix ) . '<input type="number" id="' . esc_attr( $field_name ) . '" class="asenha-subfield-number' . esc_attr( $field_classname ) . '" name="' . esc_attr( $field_name ) . '" placeholder="' . esc_attr( $field_placeholder ) . '" step="1" min="' . esc_attr( $field_min ) . '" max="' . esc_attr( $field_max ) . '" value="' . esc_attr( $field_option_value ) . '">' . wp_kses_post( $field_suffix ) . '</div>';
406 if ( !empty( $field_description ) ) {
407 echo '<div class="asenha-subfield-number-description">' . wp_kses_post( $field_description ) . '</div>';
408 }
409 echo '</div>';
410 }
411
412 /**
413 * Render select field as sub-field of a toggle/switcher checkbox
414 *
415 * @since 1.4.0
416 */
417 function render_select_subfield( $args ) {
418 $option_name = $args['option_name'];
419 if ( !empty( $option_name ) ) {
420 $options = get_option( $option_name, array() );
421 } else {
422 $options = get_option( ASENHA_SLUG_U, array() );
423 }
424 $field_id = $args['field_id'];
425 $field_name = $args['field_name'];
426 $field_type = $args['field_type'];
427 $field_prefix = $args['field_prefix'];
428 $field_suffix = $args['field_suffix'];
429 $field_select_options = $args['field_select_options'];
430 $field_select_default = $args['field_select_default'];
431 $field_intro = $args['field_intro'];
432 $field_description = $args['field_description'];
433 $parent_field_id = ( isset( $args['parent_field_id'] ) ? $args['parent_field_id'] : '' );
434 $sub_field_id = ( isset( $args['sub_field_id'] ) ? $args['sub_field_id'] : '' );
435 if ( !empty( $field_select_default ) ) {
436 $default_value = $field_select_default;
437 } else {
438 $default_value = false;
439 }
440 if ( !empty( $parent_field_id ) && !empty( $sub_field_id ) && isset( $options[$parent_field_id][$sub_field_id][$field_id] ) ) {
441 $field_option_value = $options[$parent_field_id][$sub_field_id][$field_id];
442 } else {
443 $field_option_value = ( isset( $options[$field_id] ) ? $options[$field_id] : $default_value );
444 }
445 if ( !empty( $field_prefix ) && !empty( $field_suffix ) ) {
446 $field_classname = ' with-prefix with-suffix';
447 } elseif ( !empty( $field_prefix ) && empty( $field_suffix ) ) {
448 $field_classname = ' with-prefix';
449 } elseif ( empty( $field_prefix ) && !empty( $field_suffix ) ) {
450 $field_classname = ' with-suffix';
451 } else {
452 $field_classname = '';
453 }
454 $display_none_on_load = ( isset( $args['display_none_on_load'] ) ? $args['display_none_on_load'] : false );
455 if ( $display_none_on_load ) {
456 $inline_style = 'display:none;';
457 } else {
458 $inline_style = '';
459 }
460 echo '<div class="asenha-subfield-select-wrapper">';
461 if ( !empty( $field_intro ) ) {
462 echo '<div class="asenha-subfield-select-intro">' . wp_kses_post( $field_intro ) . '</div>';
463 }
464 echo '<div style="' . esc_attr( $inline_style ) . '" class="asenha-subfield-select-inner">' . wp_kses_post( $field_prefix );
465 echo '<select name="' . esc_attr( $field_name ) . '" class="asenha-subfield-select' . esc_attr( $field_classname ) . '">';
466 foreach ( $field_select_options as $label => $value ) {
467 echo '<option value="' . esc_attr( $value ) . '" ' . selected( $value, $field_option_value, false ) . '>' . esc_html( $label ) . '</option>';
468 }
469 echo '</select>';
470 echo wp_kses_post( $field_suffix ) . '</div>';
471 if ( !empty( $field_description ) ) {
472 echo '<div class="asenha-subfield-select-description">' . wp_kses_post( $field_description ) . '</div>';
473 }
474 echo '</div>';
475 }
476
477 /**
478 * Render textarea field as sub-field of a toggle/switcher checkbox
479 *
480 * @since 2.3.0
481 */
482 function render_textarea_subfield( $args ) {
483 $option_name = $args['option_name'];
484 if ( !empty( $option_name ) ) {
485 $options = get_option( $option_name, array() );
486 } else {
487 $options = get_option( ASENHA_SLUG_U, array() );
488 }
489 $field_id = $args['field_id'];
490 $field_slug = $args['field_slug'];
491 $field_name = $args['field_name'];
492 $field_type = $args['field_type'];
493 $field_rows = $args['field_rows'];
494 $field_intro = $args['field_intro'];
495 $field_description = $args['field_description'];
496 $field_placeholder = ( isset( $args['field_placeholder'] ) ? $args['field_placeholder'] : '' );
497 // Always load textarea content from robots.txt URL, whether it's a real, custom-made robots.txt file or a virtual one generated by WordPress
498 if ( 'robots_txt_content' == $field_id ) {
499 if ( array_key_exists( 'manage_robots_txt', $options ) ) {
500 // Manage robots.txt feature has been enabled before
501 if ( !$options['manage_robots_txt'] ) {
502 // Manage robots.txt feature is NOT enabled
503 if ( array_key_exists( 'robots_txt_content', $options ) && $options['robots_txt_content'] ) {
504 $field_option_value = $options['robots_txt_content'];
505 } else {
506 $robots_txt_content = wp_remote_get( get_site_url() . '/robots.txt' );
507 $robots_txt_content = esc_textarea( trim( wp_remote_retrieve_body( $robots_txt_content ) ) );
508 $field_option_value = $robots_txt_content;
509 }
510 } else {
511 // Manage robots.txt feature is enabled
512 if ( array_key_exists( 'robots_txt_content', $options ) && $options['robots_txt_content'] ) {
513 $field_option_value = $options['robots_txt_content'];
514 } else {
515 $robots_txt_content = wp_remote_get( get_site_url() . '/robots.txt' );
516 $robots_txt_content = esc_textarea( trim( wp_remote_retrieve_body( $robots_txt_content ) ) );
517 $field_option_value = $robots_txt_content;
518 }
519 }
520 } else {
521 // Manage robots.txt feature has never been enabled yet
522 $robots_txt_content = wp_remote_get( get_site_url() . '/robots.txt' );
523 $robots_txt_content = esc_textarea( trim( wp_remote_retrieve_body( $robots_txt_content ) ) );
524 $field_option_value = $robots_txt_content;
525 }
526 } else {
527 $field_option_value = ( isset( $options[$args['field_id']] ) ? $options[$args['field_id']] : '' );
528 }
529 echo '<div class="asenha-subfield-textarea-wrapper">';
530 if ( !empty( $field_intro ) ) {
531 echo '<div class="asenha-subfield-textarea-intro">' . wp_kses_post( $field_intro ) . '</div>';
532 }
533 echo '<textarea rows="' . esc_attr( $field_rows ) . '" class="asenha-subfield-textarea" id="' . esc_attr( $field_name ) . '" name="' . esc_attr( $field_name ) . '" placeholder="' . esc_attr( $field_placeholder ) . '">' . esc_textarea( $field_option_value ) . '</textarea>';
534 if ( !empty( $field_description ) ) {
535 echo '<div class="asenha-subfield-textarea-description">' . wp_kses_post( $field_description ) . '</div>';
536 }
537 echo '</div>';
538 }
539
540 /**
541 * Render textarea field as sub-field of a toggle/switcher checkbox
542 *
543 * @since 2.3.0
544 */
545 function render_wpeditor_subfield( $args ) {
546 $option_name = $args['option_name'];
547 if ( !empty( $option_name ) ) {
548 $options = get_option( $option_name, array() );
549 } else {
550 $options = get_option( ASENHA_SLUG_U, array() );
551 }
552 $field_id = $args['field_id'];
553 $field_slug = $args['field_slug'];
554 $field_name = $args['field_name'];
555 $field_type = $args['field_type'];
556 $field_intro = $args['field_intro'];
557 $field_description = $args['field_description'];
558 $field_placeholder = ( isset( $args['field_placeholder'] ) ? $args['field_placeholder'] : '' );
559 $field_option_value = ( isset( $options[$args['field_id']] ) ? $options[$args['field_id']] : '' );
560 $editor_settings = $args['editor_settings'];
561 // https://developer.wordpress.org/reference/classes/_wp_editors/parse_settings/
562 echo '<div class="asenha-subfield-wpeditor-wrapper">';
563 if ( !empty( $field_intro ) ) {
564 echo '<div class="asenha-subfield-wpeditor-intro">' . wp_kses_post( $field_intro ) . '</div>';
565 }
566 $content = $field_option_value;
567 $editor_id = str_replace( array('[', ']'), array('--', ''), $field_name );
568 // vi( $editor_id, '', 'for ' . $field_name );
569 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
570 echo wp_editor( $content, $editor_id, $editor_settings );
571 if ( !empty( $field_description ) ) {
572 echo '<div class="asenha-subfield-wpeditor-description">' . wp_kses_post( $field_description ) . '</div>';
573 }
574 echo '</div>';
575 }
576
577 /**
578 * Render custom HTML subfield
579 *
580 * @since 5.3.0
581 */
582 function render_custom_html( $args ) {
583 echo wp_kses( $args['html'], get_kses_with_custom_html_ruleset() );
584 }
585
586 /**
587 * Render media subfield
588 *
589 * @since 6.2.2
590 */
591 function render_media_subfield( $args ) {
592 $field_id = $args['field_id'];
593 $field_slug = $args['field_slug'];
594 $field_name = $args['field_name'];
595 $field_media_frame_title = $args['field_media_frame_title'];
596 $field_media_frame_multiple = $args['field_media_frame_multiple'];
597 $field_media_frame_library_type = $args['field_media_frame_library_type'];
598 $field_media_frame_button_text = $args['field_media_frame_button_text'];
599 $field_intro = $args['field_intro'];
600 $field_description = $args['field_description'];
601 $options = get_option( $args['option_name'], array() );
602 $field_option_value = ( isset( $options[$field_id] ) ? $options[$field_id] : '' );
603 ?>
604 <div class="media-subfield-wrapper">
605 <input id="<?php
606 echo esc_attr( $field_slug );
607 ?>" class="image-picker" type="text" size="40" name="<?php
608 echo esc_attr( $field_name );
609 ?>" value="<?php
610 echo esc_url( $field_option_value );
611 ?>" />
612 <button id="<?php
613 echo esc_attr( $field_slug );
614 ?>-button" class="image-picker-button button-secondary"><?php
615 echo __( 'Select an Image', 'admin-site-enhancements' );
616 ?></button>
617 <?php
618 if ( array_key_exists( 'field_hidden_inputs', $args ) && is_array( $args['field_hidden_inputs'] ) ) {
619 foreach ( $args['field_hidden_inputs'] as $hidden_input ) {
620 $hidden_option_key = ( isset( $hidden_input['option_key'] ) ? sanitize_key( $hidden_input['option_key'] ) : '' );
621 $hidden_id = ( isset( $hidden_input['field_id'] ) ? (string) $hidden_input['field_id'] : '' );
622 $hidden_name = ( isset( $hidden_input['field_name'] ) ? (string) $hidden_input['field_name'] : '' );
623 if ( empty( $hidden_option_key ) || empty( $hidden_id ) || empty( $hidden_name ) ) {
624 continue;
625 }
626 $hidden_value = ( isset( $options[$hidden_option_key] ) ? absint( $options[$hidden_option_key] ) : 0 );
627 echo '<input type="hidden" id="' . esc_attr( $hidden_id ) . '" class="asenha-hidden-media-dim" name="' . esc_attr( $hidden_name ) . '" value="' . esc_attr( $hidden_value ) . '" />';
628 }
629 }
630 ?>
631 <?php
632 if ( !empty( $field_description ) ) {
633 echo '<div class="asenha-subfield-description media-subfield">' . wp_kses_post( $field_description ) . '</div>';
634 }
635 ?>
636 </div>
637 <?php
638 }
639
640 /**
641 * Render media subfield
642 *
643 * @since 6.2.2
644 */
645 function render_color_picker_subfield( $args ) {
646 $common_methods = new Common_Methods();
647 $field_id = $args['field_id'];
648 $field_slug = $args['field_slug'];
649 $field_name = $args['field_name'];
650 $field_intro = $args['field_intro'];
651 $field_description = $args['field_description'];
652 $field_default_value = $args['field_default_value'];
653 $options = get_option( $args['option_name'], array() );
654 $field_option_value = ( isset( $options[$field_id] ) ? $options[$field_id] : $field_default_value );
655 ?>
656 <div class="color-subfield-wrapper">
657 <input type="text" id="<?php
658 echo esc_attr( $field_slug );
659 ?>" name="<?php
660 echo esc_attr( $field_name );
661 ?>" value="<?php
662 echo esc_attr( $common_methods->sanitize_hex_color( $field_option_value ) );
663 ?>" data-default-color="<?php
664 echo esc_attr( $common_methods->sanitize_hex_color( $field_default_value ) );
665 ?>" class="color-picker"/>
666 </div>
667 <?php
668 }
669
670 /**
671 * Render text field as sub-field of a toggle/switcher checkbox
672 *
673 * @since 1.4.0
674 */
675 function render_content_toggler( $args ) {
676 $field_id = $args['field_id'];
677 $field_slug = $args['field_slug'];
678 $field_slug = str_replace( '_', '-', $field_id );
679 $show_text = ( isset( $args['show_text'] ) ? $args['show_text'] : '' );
680 $hide_text = ( isset( $args['hide_text'] ) ? $args['hide_text'] : '' );
681 $content_selector = ( isset( $args['content_selector'] ) ? $args['content_selector'] : '' );
682 ?>
683 <div class="subfield-content-toggler <?php
684 echo esc_attr( $field_slug );
685 ?>"><a href="#" data-show-text="<?php
686 echo esc_attr( $show_text );
687 ?>" data-hide-text="<?php
688 echo esc_attr( $hide_text );
689 ?>" data-target-selector="<?php
690 echo esc_attr( $content_selector );
691 ?>" class="asenha-content-toggler" data-expanded="no"><?php
692 echo esc_html( $show_text );
693 ?> <span></span></a></div>
694 <?php
695 }
696
697 /**
698 * Human-readable label fragment for an admin menu separator slug (AMO sortable UI).
699 *
700 * WordPress core and plugins use slugs like separator1, separator-last, separator-woocommerce.
701 * Internal slugs stay unchanged; this output is display-only (Spacer-* wording).
702 *
703 * @since 8.5.3
704 *
705 * @param string $menu_slug Separator menu slug from global $menu item index 2.
706 * @return string Middle segment only (without surrounding ~~); safe for esc_html when wrapped.
707 */
708 private function format_admin_menu_separator_slug_for_display( $menu_slug ) {
709 $name = str_replace( 'separator', 'Spacer-', $menu_slug );
710 $name = str_replace( '--last', '-Last', $name );
711 $name = str_replace( '--woocommerce', '--WooCommerce', $name );
712 return $name;
713 }
714
715 /**
716 * Render sortable menu field
717 *
718 * @since 2.0.0
719 */
720 function render_sortable_menu() {
721 $triangle_right_icon = '<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 16 16"><path fill="currentColor" d="M14.222 6.687a1.5 1.5 0 0 1 0 2.629l-10 5.499A1.5 1.5 0 0 1 2 13.5V2.502a1.5 1.5 0 0 1 2.223-1.314z"/></svg>';
722 ?>
723 <ul id="custom-admin-menu" class="menu ui-sortable">
724 <?php
725 global $menu, $submenu;
726 $common_methods = new Common_Methods();
727 $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
728 $options = ( isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array() );
729 // Parsed early so Pro merge of custom spacers and render loops can use the same list.
730 $deleted_separators = ( isset( $options['custom_menu_deleted_separators'] ) ? $options['custom_menu_deleted_separators'] : '' );
731 $deleted_separators_array = array();
732 if ( is_string( $deleted_separators ) && !empty( $deleted_separators ) ) {
733 $deleted_separators_array = json_decode( $deleted_separators, true );
734 if ( !is_array( $deleted_separators_array ) ) {
735 $deleted_separators_array = array();
736 }
737 }
738 // Set menu items to be excluded from title renaming. These are from WordPress core.
739 $renaming_not_allowed = array(
740 'menu-dashboard',
741 'menu-pages',
742 // 'menu-posts',
743 'menu-media',
744 'menu-comments',
745 'menu-appearance',
746 'menu-plugins',
747 'menu-users',
748 'menu-tools',
749 'menu-settings',
750 );
751 // Get custom menu item titles
752 if ( array_key_exists( 'custom_menu_titles', $options ) ) {
753 $custom_menu_titles = $options['custom_menu_titles'];
754 $custom_menu_titles = explode( ',', $custom_menu_titles );
755 } else {
756 $custom_menu_titles = array();
757 }
758 // Get menu items hidden by toggle
759 $menu_hidden_by_toggle = $common_methods->get_menu_hidden_by_toggle();
760 $i = 1;
761 // Check if there's an existing custom menu order data stored in options
762 if ( array_key_exists( 'custom_menu_order', $options ) ) {
763 $custom_menu = $options['custom_menu_order'];
764 $custom_menu = explode( ',', $custom_menu );
765 $menu_key_in_use = array();
766 // Render sortables with data in custom menu order
767 foreach ( $custom_menu as $custom_menu_item ) {
768 foreach ( $menu as $menu_key => $menu_info ) {
769 // Skip deleted built-in separators only for real separator rows; Pro distinguishes ASE additional-separator rows.
770 $skip_deleted_separator = false;
771 if ( isset( $menu_info[2], $menu_info[4] ) && false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
772 $skip_deleted_separator = in_array( $menu_info[2], $deleted_separators_array, true );
773 }
774 if ( $skip_deleted_separator ) {
775 continue;
776 }
777 if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
778 $menu_item_title = $menu_info[2];
779 $menu_item_id = $menu_info[2];
780 } else {
781 $menu_item_title = $menu_info[0];
782 $menu_item_id = $menu_info[5];
783 }
784 $menu_url_fragment = '';
785 if ( $custom_menu_item == $menu_item_id ) {
786 $menu_item_id_transformed = $common_methods->transform_menu_item_id( $menu_item_id );
787 $is_custom_menu = 'no';
788 $menu_required_capability = ( isset( $menu_info[1] ) ? $menu_info[1] : '' );
789 ?>
790 <li id="<?php
791 echo esc_attr( $menu_item_id );
792 ?>" class="menu-item parent-menu-item menu-item-depth-0" data-custom-menu-item="<?php
793 echo esc_attr( $is_custom_menu );
794 ?>" data-menu-slug="<?php
795 echo esc_attr( $menu_info[2] );
796 ?>">
797 <div class="menu-item-bar">
798 <div class="menu-item-handle">
799 <span class="dashicons dashicons-menu"></span>
800 <div class="item-title">
801 <div class="title-wrapper">
802 <span class="menu-item-title">
803 <?php
804 if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
805 echo '~~ ' . esc_html( $this->format_admin_menu_separator_slug_for_display( $menu_info[2] ) ) . ' ~~';
806 } else {
807 if ( in_array( $menu_item_id, $renaming_not_allowed ) ) {
808 $menu_item_title = $menu_info[0];
809 echo wp_kses_post( $common_methods->strip_html_tags_and_content( $menu_item_title ) );
810 } else {
811 // Get defaul/custom menu item title
812 foreach ( $custom_menu_titles as $custom_menu_title ) {
813 // At this point, $custom_menu_title value looks like toplevel_page_snippets__Code Snippets
814 $custom_menu_title = explode( '__', $custom_menu_title );
815 if ( $custom_menu_title[0] == $menu_item_id ) {
816 $menu_item_title = $common_methods->strip_html_tags_and_content( $custom_menu_title[1] );
817 // e.g. Code Snippets
818 break;
819 // stop foreach loop so $menu_item_title is not overwritten in the next iteration
820 } else {
821 $menu_item_title = $common_methods->strip_html_tags_and_content( $menu_info[0] );
822 }
823 }
824 ?>
825 <input type="text" value="<?php
826 echo wp_kses_post( wp_unslash( $menu_item_title ) );
827 ?>" class="menu-item-custom-title" data-menu-item-id="<?php
828 echo esc_attr( $menu_item_id );
829 ?>">
830 <?php
831 }
832 }
833 ?>
834 </span><!-- end of .menu-item-title -->
835 <?php
836 ?>
837 </div><!-- end of .title-wrapper -->
838 <div class="options-for-hiding">
839 <?php
840 $hide_text = __( 'Hide until toggled', 'admin-site-enhancements' );
841 $checkbox_class = 'parent-menu-hide-checkbox';
842 ?>
843 <label class="menu-item-checkbox-label">
844 <?php
845 if ( in_array( $custom_menu_item, $menu_hidden_by_toggle ) ) {
846 ?>
847 <input type="checkbox" id="hide-status-for-<?php
848 echo esc_attr( $menu_item_id_transformed );
849 ?>" class="<?php
850 echo esc_attr( $checkbox_class );
851 ?>" data-menu-item-title="<?php
852 echo esc_attr( $common_methods->strip_html_tags_and_content( $menu_item_title ) );
853 ?>" data-menu-item-id="<?php
854 echo esc_attr( $menu_item_id_transformed );
855 ?>" data-menu-item-id-ori="<?php
856 echo esc_attr( $menu_item_id );
857 ?>" data-menu-url-fragment="<?php
858 echo esc_attr( $menu_url_fragment );
859 ?>" data-required-capability="<?php
860 echo esc_attr( $menu_required_capability );
861 ?>" checked>
862 <span><?php
863 echo esc_html( $hide_text );
864 ?></span>
865 <?php
866 } else {
867 ?>
868 <input type="checkbox" id="hide-status-for-<?php
869 echo esc_attr( $menu_item_id_transformed );
870 ?>" class="<?php
871 echo esc_attr( $checkbox_class );
872 ?>" data-menu-item-title="<?php
873 echo esc_attr( $common_methods->strip_html_tags_and_content( $menu_item_title ) );
874 ?>" data-menu-item-id="<?php
875 echo esc_attr( $menu_item_id_transformed );
876 ?>" data-menu-item-id-ori="<?php
877 echo esc_attr( $menu_item_id );
878 ?>" data-menu-url-fragment="<?php
879 echo esc_attr( $menu_url_fragment );
880 ?>" data-required-capability="<?php
881 echo esc_attr( $menu_required_capability );
882 ?>">
883 <span><?php
884 echo esc_html( $hide_text );
885 ?></span>
886 <?php
887 }
888 ?>
889 </label>
890 <?php
891 ?>
892 </div><!-- end of .options-for-hiding -->
893 </div><!-- end of .item-title -->
894 </div><!-- end of .menu-item-handle -->
895 </div><!-- end of .menu-item-bar -->
896 <?php
897 $i = 1;
898 ?>
899 </li>
900 <?php
901 $menu_key_in_use[] = $menu_key;
902 }
903 }
904 }
905 // Render the rest of the current menu towards the end of the sortables
906 foreach ( $menu as $menu_key => $menu_info ) {
907 if ( !in_array( $menu_key, $menu_key_in_use ) ) {
908 // Skip deleted built-in separators only for real separator rows; Pro distinguishes ASE additional-separator rows.
909 $skip_deleted_separator = false;
910 if ( isset( $menu_info[2], $menu_info[4] ) && false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
911 $skip_deleted_separator = in_array( $menu_info[2], $deleted_separators_array, true );
912 }
913 if ( $skip_deleted_separator ) {
914 continue;
915 }
916 if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
917 $menu_item_id = $menu_info[2];
918 } else {
919 $menu_item_id = $menu_info[5];
920 }
921 $menu_item_title = $menu_info[0];
922 $menu_url_fragment = '';
923 // Strip tags
924 $menu_item_title = $common_methods->strip_html_tags_and_content( $menu_item_title );
925 // Exclude Show All/Less toggles
926 if ( false === strpos( $menu_item_id, 'toplevel_page_asenha_' ) ) {
927 $menu_item_id_transformed = $common_methods->transform_menu_item_id( $menu_item_id );
928 $is_custom_menu = 'no';
929 $menu_required_capability = ( isset( $menu_info[1] ) ? $menu_info[1] : '' );
930 ?>
931 <li id="<?php
932 echo esc_attr( $menu_item_id );
933 ?>" class="menu-item parent-menu-item menu-item-depth-0" data-custom-menu-item="<?php
934 echo esc_attr( $is_custom_menu );
935 ?>">
936 <div class="menu-item-bar">
937 <div class="menu-item-handle">
938 <span class="dashicons dashicons-menu"></span>
939 <div class="item-title">
940 <div class="title-wrapper">
941 <span class="menu-item-title">
942 <?php
943 if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
944 echo '~~ ' . esc_html( $this->format_admin_menu_separator_slug_for_display( $menu_info[2] ) ) . ' ~~';
945 } else {
946 ?>
947 <input type="text" value="<?php
948 echo wp_kses_post( wp_unslash( $menu_item_title ) );
949 ?>" class="menu-item-custom-title" data-menu-item-id="<?php
950 echo esc_attr( $menu_item_id );
951 ?>">
952 <?php
953 }
954 ?>
955 </span>
956 <?php
957 ?>
958 </div>
959 <div class="options-for-hiding">
960 <?php
961 $hide_text = __( 'Hide until toggled', 'admin-site-enhancements' );
962 $checkbox_class = 'parent-menu-hide-checkbox';
963 ?>
964 <label class="menu-item-checkbox-label">
965 <input type="checkbox" id="hide-status-for-<?php
966 echo esc_attr( $menu_item_id_transformed );
967 ?>" class="<?php
968 echo esc_attr( $checkbox_class );
969 ?>" data-menu-item-title="<?php
970 echo esc_attr( $common_methods->strip_html_tags_and_content( $menu_item_title ) );
971 ?>" data-menu-item-id="<?php
972 echo esc_attr( $menu_item_id_transformed );
973 ?>" data-menu-item-id-ori="<?php
974 echo esc_attr( $menu_item_id );
975 ?>" data-menu-url-fragment="<?php
976 echo esc_attr( $menu_url_fragment );
977 ?>" data-required-capability="<?php
978 echo esc_attr( $menu_required_capability );
979 ?>">
980 <span><?php
981 echo esc_html( $hide_text );
982 ?></span>
983 </label>
984 <?php
985 ?>
986 </div><!-- end of .options-for-hiding -->
987 </div><!-- end of .item-title -->
988 </div><!-- end of .menu-item-handle -->
989 </div><!-- end of .menu-item-bar -->
990 <?php
991 $i = 1;
992 // Add delete icon for saved custom menu items (not separators)
993 if ( $is_custom_menu === 'yes' && strpos( $menu_info[2], 'separator' ) === false ) {
994 ?>
995 <div class="remove-custom-menu-item-saved" data-menu-item-id="<?php
996 echo esc_attr( $menu_info[2] );
997 ?>">
998 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
999 <path fill="#bbbbbb" d="M24 2.4L21.6 0L12 9.6L2.4 0L0 2.4L9.6 12L0 21.6L2.4 24l9.6-9.6l9.6 9.6l2.4-2.4l-9.6-9.6z"/>
1000 </svg>
1001 </div>
1002 <?php
1003 }
1004 ?>
1005 </li><!-- end of .menu-item -->
1006 <?php
1007 }
1008 }
1009 }
1010 } else {
1011 // No custom menu order has been saved yet
1012 // Render sortables with existing items in the admin menu
1013 foreach ( $menu as $menu_key => $menu_info ) {
1014 // Skip deleted built-in separators only for real separator rows; Pro distinguishes ASE additional-separator rows.
1015 $skip_deleted_separator = false;
1016 if ( isset( $menu_info[2], $menu_info[4] ) && false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
1017 $skip_deleted_separator = in_array( $menu_info[2], $deleted_separators_array, true );
1018 }
1019 if ( $skip_deleted_separator ) {
1020 continue;
1021 }
1022 if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
1023 $menu_item_id = $menu_info[2];
1024 } else {
1025 $menu_item_id = $menu_info[5];
1026 }
1027 $menu_url_fragment = '';
1028 $menu_item_title = $menu_info[0];
1029 $menu_item_id_transformed = $common_methods->transform_menu_item_id( $menu_item_id );
1030 // Strip tags
1031 $menu_item_title = $common_methods->strip_html_tags_and_content( $menu_item_title );
1032 $is_custom_menu = 'no';
1033 $menu_required_capability = ( isset( $menu_info[1] ) ? $menu_info[1] : '' );
1034 $custom_menu_data = null;
1035 $menu_item_handle_classes = 'menu-item-handle';
1036 ?>
1037 <li id="<?php
1038 echo esc_attr( $menu_item_id );
1039 ?>" class="menu-item parent-menu-item menu-item-depth-0" data-custom-menu-item="<?php
1040 echo esc_attr( $is_custom_menu );
1041 ?>" data-menu-slug="<?php
1042 echo esc_attr( $menu_info[2] );
1043 ?>">
1044 <div class="menu-item-bar">
1045 <div class="<?php
1046 echo esc_attr( $menu_item_handle_classes );
1047 ?>">
1048 <span class="dashicons dashicons-menu"></span>
1049 <div class="item-title">
1050 <div class="title-wrapper">
1051 <span class="menu-item-title">
1052 <?php
1053 if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
1054 echo '~~ ' . esc_html( $this->format_admin_menu_separator_slug_for_display( $menu_info[2] ) ) . ' ~~';
1055 } else {
1056 if ( in_array( $menu_item_id, $renaming_not_allowed ) ) {
1057 echo wp_kses_post( $menu_item_title );
1058 } else {
1059 ?>
1060 <input type="text" value="<?php
1061 echo wp_kses_post( wp_unslash( $menu_item_title ) );
1062 ?>" class="menu-item-custom-title" data-menu-item-id="<?php
1063 echo esc_attr( $menu_item_id );
1064 ?>">
1065 <?php
1066 }
1067 }
1068 ?>
1069 </span>
1070 <?php
1071 ?>
1072 </div><!-- end of .title-wrapper -->
1073 <div class="options-for-hiding">
1074 <?php
1075 $hide_text = __( 'Hide until toggled', 'admin-site-enhancements' );
1076 $checkbox_class = 'parent-menu-hide-checkbox';
1077 ?>
1078 <label class="menu-item-checkbox-label">
1079 <input type="checkbox" id="hide-status-for-<?php
1080 echo esc_attr( $menu_item_id_transformed );
1081 ?>" class="<?php
1082 echo esc_attr( $checkbox_class );
1083 ?>" data-menu-item-title="<?php
1084 echo esc_attr( $common_methods->strip_html_tags_and_content( $menu_item_title ) );
1085 ?>" data-menu-item-id="<?php
1086 echo esc_attr( $menu_item_id_transformed );
1087 ?>" data-menu-item-id-ori="<?php
1088 echo esc_attr( $menu_item_id );
1089 ?>" data-menu-url-fragment="<?php
1090 echo esc_attr( $menu_url_fragment );
1091 ?>" data-required-capability="<?php
1092 echo esc_attr( $menu_required_capability );
1093 ?>">
1094 <span><?php
1095 echo esc_html( $hide_text );
1096 ?></span>
1097 </label>
1098 <?php
1099 ?>
1100 </div><!-- end of .options-for-hiding -->
1101 </div><!-- end of .item-title -->
1102 </div><!-- end of .menu-item-handle -->
1103 </div><!-- end of .menu-item-bar -->
1104 <?php
1105 $i = 1;
1106 // Add delete icon for saved custom menu items (not separators)
1107 if ( $is_custom_menu === 'yes' && strpos( $menu_info[2], 'separator' ) === false ) {
1108 ?>
1109 <div class="remove-custom-menu-item-saved" data-menu-item-id="<?php
1110 echo esc_attr( $menu_info[2] );
1111 ?>">
1112 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
1113 <path fill="#bbbbbb" d="M24 2.4L21.6 0L12 9.6L2.4 0L0 2.4L9.6 12L0 21.6L2.4 24l9.6-9.6l9.6 9.6l2.4-2.4l-9.6-9.6z"/>
1114 </svg>
1115 </div>
1116 <?php
1117 }
1118 ?>
1119 </li>
1120 <?php
1121 }
1122 }
1123 ?>
1124 </ul>
1125 <?php
1126 // Hidden input field to store custom menu order (from options as is, or sortupdate) upon clicking Save Changes.
1127 $field_id = 'custom_menu_order';
1128 $field_option_value = ( isset( $options[$field_id] ) ? $options[$field_id] : '' );
1129 echo '<input type="hidden" id="' . esc_attr( $field_id ) . '" class="asenha-subfield-text" name="' . esc_attr( $field_id ) . '" value="' . esc_attr( $field_option_value ) . '">';
1130 // Hidden input field to store hidden menu items (from options as is, or 'Hide' checkbox clicks) upon clicking Save Changes.
1131 $this->output_admin_menu_organizer_hidden_field( 'custom_menu_hidden' );
1132 // Hidden input field to store custom menu titles (from options as is, or custom values entered on each non-WP-default menu items.
1133 $this->output_admin_menu_organizer_hidden_field( 'custom_menu_titles' );
1134 }
1135
1136 /**
1137 * Decode and re-encode JSON option values for AMO hidden fields so WordPress-slashed or legacy
1138 * strings become valid for JavaScript JSON.parse(), without applying stripslashes() to raw strings
1139 * (which would break JSON \\u Unicode escapes).
1140 *
1141 * @since 8.5.3
1142 *
1143 * @param string $value Raw option value.
1144 * @return string
1145 */
1146 private function normalize_admin_menu_json_option_for_output( $value ) {
1147 if ( !is_string( $value ) || '' === $value ) {
1148 return $value;
1149 }
1150 // Use objects (not associative arrays) so JSON objects stay objects and arrays stay arrays.
1151 $decoded = json_decode( $value, false );
1152 if ( JSON_ERROR_NONE === json_last_error() ) {
1153 return wp_json_encode( $decoded );
1154 }
1155 $decoded = json_decode( wp_unslash( $value ), false );
1156 if ( JSON_ERROR_NONE === json_last_error() ) {
1157 return wp_json_encode( $decoded );
1158 }
1159 return $value;
1160 }
1161
1162 /**
1163 * Output hidden field
1164 *
1165 * @since 6.9.13
1166 */
1167 public function output_admin_menu_organizer_hidden_field( $field_id ) {
1168 $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
1169 $options = ( isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array() );
1170 $field_name = $field_id;
1171 $field_option_value = ( isset( $options[$field_id] ) ? $options[$field_id] : '' );
1172 $json_option_field_ids = array(
1173 'custom_menu_new_separators',
1174 'custom_menu_deleted_separators',
1175 'custom_submenus_order',
1176 'custom_menu_always_hidden',
1177 'custom_submenu_always_hidden',
1178 'custom_menu_new_items'
1179 );
1180 if ( in_array( $field_id, $json_option_field_ids, true ) ) {
1181 $field_option_value = $this->normalize_admin_menu_json_option_for_output( $field_option_value );
1182 }
1183 echo '<input type="hidden" id="' . esc_attr( $field_name ) . '" class="asenha-subfield-text" name="' . esc_attr( $field_name ) . '" value="' . esc_attr( $field_option_value ) . '">';
1184 }
1185
1186 /**
1187 * Render textarea field as sub-field of a toggle/switcher checkbox
1188 *
1189 * @since 2.3.0
1190 */
1191 function render_datatable( $args ) {
1192 global $wpdb;
1193 $option_name = $args['option_name'];
1194 if ( !empty( $option_name ) ) {
1195 $options = get_option( $option_name, array() );
1196 } else {
1197 $options = get_option( ASENHA_SLUG_U, array() );
1198 }
1199 $field_id = $args['field_id'];
1200 $field_slug = $args['field_slug'];
1201 $field_name = $args['field_name'];
1202 $field_type = $args['field_type'];
1203 $field_description = $args['field_description'];
1204 $table_title = $args['table_title'];
1205 $table_name = $args['table_name'];
1206 $field_option_value = ( isset( $options[$args['field_id']] ) ? $options[$args['field_id']] : '' );
1207 $login_fails_allowed = ( isset( $options['login_fails_allowed'] ) ? (int) $options['login_fails_allowed'] : 3 );
1208 $login_lockout_maxcount = ( isset( $options['login_lockout_maxcount'] ) ? (int) $options['login_lockout_maxcount'] : 3 );
1209 $login_fails_allowed = ( $login_fails_allowed > 0 ? $login_fails_allowed : 3 );
1210 $login_lockout_maxcount = ( $login_lockout_maxcount > 0 ? $login_lockout_maxcount : 3 );
1211 $default_lockout_period = 60 * 15;
1212 // 15 minutes in seconds.
1213 $extended_lockout_period = 24 * 60 * 60;
1214 // 24 hours in seconds.
1215 $now = time();
1216 $ip_address_whitelist = array();
1217 ?>
1218 <table id="login-attempts-log" class="wp-list-table widefat striped datatable">
1219 <thead>
1220 <tr class="datatable-tr">
1221 <th class="datatable-th"><?php
1222 _e( 'IP Address<br />Last Username', 'admin-site-enhancements' );
1223 ?></th>
1224 <th class="datatable-th"><?php
1225 _e( 'Attempts<br />Lockouts', 'admin-site-enhancements' );
1226 ?></th>
1227 <th class="datatable-th"><?php
1228 _e( 'Last Attempt On', 'admin-site-enhancements' );
1229 ?></th>
1230 <th class="datatable-th"><?php
1231 _e( 'Actions', 'admin-site-enhancements' );
1232 ?></th>
1233 </tr>
1234 </thead>
1235 <tbody>
1236 <?php
1237 $limit = 1000;
1238 $sql = $wpdb->prepare( "SELECT * FROM {$table_name} ORDER BY unixtime DESC LIMIT %d", array($limit) );
1239 $entries = $wpdb->get_results( $sql, ARRAY_A );
1240 foreach ( $entries as $entry ) {
1241 $unixtime = $entry['unixtime'];
1242 $ip_address = ( isset( $entry['ip_address'] ) ? (string) $entry['ip_address'] : '' );
1243 $fail_count = ( isset( $entry['fail_count'] ) ? (int) $entry['fail_count'] : 0 );
1244 $lockout_count = ( isset( $entry['lockout_count'] ) ? (int) $entry['lockout_count'] : 0 );
1245 $locked_condition = $fail_count > 0 && $fail_count % $login_fails_allowed === 0;
1246 $is_extended_lockout = $lockout_count >= $login_lockout_maxcount;
1247 $lockout_period = ( $is_extended_lockout ? $extended_lockout_period : $default_lockout_period );
1248 $is_locked_out = $locked_condition && $now - (int) $unixtime <= $lockout_period;
1249 $is_whitelisted = in_array( $ip_address, $ip_address_whitelist, true );
1250 if ( function_exists( 'wp_date' ) ) {
1251 $date = wp_date( 'F j, Y', $unixtime );
1252 $time = wp_date( 'H:i:s', $unixtime );
1253 } else {
1254 $date = date_i18n( 'F j, Y', $unixtime );
1255 $time = date_i18n( 'H:i:s', $unixtime );
1256 }
1257 ?>
1258 <tr class="datatable-tr">
1259 <td class="datatable-td"><?php
1260 echo esc_html( $ip_address );
1261 ?><br /><?php
1262 echo esc_html( $entry['username'] );
1263 ?></td>
1264 <td class="datatable-td"><?php
1265 echo esc_html( $fail_count );
1266 ?><br /><?php
1267 echo esc_html( $lockout_count );
1268 ?></td>
1269 <td class="datatable-td"><span class="unixtime"><?php
1270 echo esc_html( $entry['unixtime'] );
1271 ?></span><?php
1272 echo esc_html( $date );
1273 ?><br /><?php
1274 echo esc_html( $time );
1275 ?></td>
1276 <td class="datatable-td">
1277 <?php
1278 if ( $is_locked_out && !$is_whitelisted ) {
1279 ?>
1280 <a href="#" class="button button-secondary button-small asenha-release-login-lock" data-ip-address="<?php
1281 echo esc_attr( $ip_address );
1282 ?>"><?php
1283 _e( 'Release Lock', 'admin-site-enhancements' );
1284 ?></a>
1285 <?php
1286 }
1287 ?>
1288 <?php
1289 ?>
1290 </td>
1291 </tr>
1292 <?php
1293 }
1294 ?>
1295 </tbody>
1296 </table>
1297 <?php
1298 echo '<div class="asenha-subfield-description">' . esc_html( $field_description ) . '</div>';
1299 echo '<input type="hidden" id="' . esc_attr( $field_name ) . '" class="asenha-subfield-datatable" name="' . esc_attr( $field_name ) . '" value="' . esc_attr( $field_option_value ) . '">';
1300 }
1301
1302 /**
1303 * Render checks and status for AVIF support
1304 *
1305 * @link https://php.watch/versions/8.1/gd-avif
1306 * @since 5.7.0
1307 */
1308 public function render_avif_support_status() {
1309 // Check status of GD extension and it's AVIF support
1310 if ( extension_loaded( 'gd' ) && function_exists( 'gd_info' ) ) {
1311 $is_gd_enabled = true;
1312 $gd_info = gd_info();
1313 $gd_version = $gd_info['GD Version'];
1314 $gd_avif_support = ( isset( $gd_info['AVIF Support'] ) ? isset( $gd_info['AVIF Support'] ) : false );
1315 if ( $gd_avif_support ) {
1316 $gd_status = $gd_version . ' <span class="supported">' . __( 'with AVIF support', 'admin-site-enhancements' ) . '</span>';
1317 } else {
1318 $gd_status = $gd_version . ' <span class="unsupported">' . __( 'without AVIF support', 'admin-site-enhancements' ) . '</span>';
1319 }
1320 } else {
1321 $is_gd_enabled = false;
1322 $gd_avif_support = false;
1323 $gd_status = __( 'Not available', 'admin-site-enhancements' );
1324 }
1325 // Check status of ImageMagick library and it's AVIF support
1326 if ( extension_loaded( 'imagick' ) && class_exists( 'Imagick' ) ) {
1327 $is_imagick_enabled = true;
1328 $imagick_version = \Imagick::getVersion();
1329 if ( preg_match( '/((?:[0-9]+\\.?)+)/', $imagick_version['versionString'], $matches ) ) {
1330 $imagick_version = $matches[0];
1331 } else {
1332 $imagick_version = $imagick_version['versionString'];
1333 }
1334 if ( version_compare( $imagick_version, '7.0.25', '>=' ) ) {
1335 $imagick_avif_support = true;
1336 $imagick_status = $imagick_version . ' <span class="supported">with AVIF support</span>';
1337 } else {
1338 $imagick_avif_support = false;
1339 $imagick_status = $imagick_version . ' <span class="unsupported">without AVIF support</span>';
1340 }
1341 } else {
1342 $is_imagick_enabled = false;
1343 $imagick_avif_support = false;
1344 $imagick_status = __( 'Not available', 'admin-site-enhancements' );
1345 }
1346 echo '<div class="asenha-subfield-status">';
1347 echo '<div class="status-title">' . __( 'AVIF Support Status', 'admin-site-enhancements' ) . '</div>';
1348 echo '<div class="status-body">';
1349 echo '<div class="status-item"><span class="status-item-title">PHP</span> : ' . wp_kses_post( phpversion() ) . '</div>';
1350 echo '<div class="status-item"><span class="status-item-title">GD</span> : ' . wp_kses_post( $gd_status ) . '</div>';
1351 echo '<div class="status-item"><span class="status-item-title">ImageMagick</span> : ' . wp_kses_post( $imagick_status ) . '</div>';
1352 echo '</div>';
1353 echo '<div class="status-footer">' . __( 'Full AVIF support requires that your server / hosting has <a href="https://php.watch/versions/8.1/gd-avif" target="_blank">GD extension</a> compiled with AVIF support in PHP 8.1 or greater, or, <a href="https://web.archive.org/web/20240104012918/https://avif.io/blog/tutorials/imagemagick/" target="_blank">ImageMagick 7.0.25 or greater</a> installed. Without either, you can still upload AVIF files but without auto-generation of the smaller thumbnail sizes. A majority of <a href="https://web.archive.org/web/20231207174740/https://avif.io/blog/articles/avif-browser-support/" target="_blank">modern desktop and mobile browsers</a> support the display of AVIF files.', 'admin-site-enhancements' ) . '</div>';
1354 echo '</div>';
1355 }
1356
1357 }
1358