css
21 hours ago
js
21 hours ago
admin-menu.php
21 hours ago
documentation.php
21 hours ago
faq.php
21 hours ago
import.php
21 hours ago
migrate.php
21 hours ago
settings.php
21 hours ago
support.php
21 hours ago
settings.php
1268 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Settings Tab Loader |
| 4 | */ |
| 5 | |
| 6 | namespace PluginRx\AdminHelpDocs; |
| 7 | |
| 8 | if ( ! defined( 'ABSPATH' ) ) exit; |
| 9 | |
| 10 | class Settings { |
| 11 | |
| 12 | |
| 13 | /** |
| 14 | * Returns all settings boxes. |
| 15 | */ |
| 16 | public static function setting_boxes() : array { |
| 17 | $boxes = [ |
| 18 | 'interface' => __( 'Interface', 'admin-help-docs' ), |
| 19 | 'content_output' => __( 'Content & Output', 'admin-help-docs' ), |
| 20 | 'access_control' => __( 'Access Control', 'admin-help-docs' ), |
| 21 | 'advanced' => __( 'Advanced', 'admin-help-docs' ), |
| 22 | ]; |
| 23 | |
| 24 | return apply_filters( 'helpdocs_settings_boxes', $boxes ); |
| 25 | } // End setting_boxes() |
| 26 | |
| 27 | |
| 28 | /** |
| 29 | * Returns all settings fields. |
| 30 | */ |
| 31 | public static function setting_fields( $defaults_only = false ) : array { |
| 32 | $themes = []; |
| 33 | foreach ( Colors::themes() as $key => $theme ) { |
| 34 | $themes[ $key ] = $theme[ 'label' ]; |
| 35 | } |
| 36 | $themes[ 'custom' ] = __( 'Custom (Use the color pickers below to customize)', 'admin-help-docs' ); |
| 37 | |
| 38 | $top_placements = []; |
| 39 | foreach ( self::top_placements() as $key => $label ) { |
| 40 | $top_placements[] = [ |
| 41 | 'value' => $key, |
| 42 | 'label' => $label, |
| 43 | ]; |
| 44 | } |
| 45 | |
| 46 | $legacy_cap = get_option( 'helpdocs_user_view_cap' ); |
| 47 | $current_type = get_option( 'helpdocs_user_view_type' ); |
| 48 | $view_default = ( false !== $legacy_cap && false === $current_type ) ? 'capability' : 'role'; |
| 49 | |
| 50 | $fields = [ |
| 51 | // Interface |
| 52 | [ |
| 53 | 'name' => 'menu_title', |
| 54 | 'label' => __( 'Menu Title', 'admin-help-docs' ), |
| 55 | 'type' => 'text', |
| 56 | 'sanitize' => 'sanitize_text_field', |
| 57 | 'box' => 'interface', |
| 58 | 'default' => 'Help Docs', |
| 59 | ], |
| 60 | [ |
| 61 | 'name' => 'dashicon', |
| 62 | 'label' => __( 'Menu Icon', 'admin-help-docs' ) . ' — <a id="view-dashicons-link" href="https://developer.wordpress.org/resource/dashicons/#editor-help" target="_blank">View Dashicons <span class="dashicons dashicons-external"></span></a>', |
| 63 | 'type' => 'select', |
| 64 | 'choices' => Helpers::get_dashicons(), |
| 65 | 'sanitize' => 'sanitize_text_field', |
| 66 | 'box' => 'interface', |
| 67 | 'default' => 'editor-help', |
| 68 | ], |
| 69 | [ |
| 70 | 'name' => 'menu_position', |
| 71 | 'label' => __( 'Menu Position', 'admin-help-docs' ), |
| 72 | 'desc' => __( '1 = Above Dashboard, 2 = Under Dashboard, 999 = Bottom, etc.', 'admin-help-docs' ), |
| 73 | 'type' => 'number', |
| 74 | 'sanitize' => 'absint', |
| 75 | 'box' => 'interface', |
| 76 | 'default' => Menu::$default_menu_position, |
| 77 | ], |
| 78 | [ |
| 79 | 'name' => 'page_title', |
| 80 | 'label' => __( 'Page Title', 'admin-help-docs' ), |
| 81 | 'type' => 'text', |
| 82 | 'sanitize' => 'sanitize_text_field', |
| 83 | 'box' => 'interface', |
| 84 | 'default' => Bootstrap::name(), |
| 85 | ], |
| 86 | [ |
| 87 | 'name' => 'logo', |
| 88 | 'label' => __( 'Page Logo', 'admin-help-docs' ), |
| 89 | 'desc' => __( 'Preferred size: 100x100 pixels. Accepted formats: jpg | jpeg | png | webp', 'admin-help-docs' ), |
| 90 | 'type' => 'text', |
| 91 | 'sanitize' => 'sanitize_text_field', |
| 92 | 'box' => 'interface', |
| 93 | 'default' => Helpers::get_default_logo_url(), |
| 94 | ], |
| 95 | [ |
| 96 | 'name' => 'doc_logo', |
| 97 | 'label' => __( 'Help Doc Logo', 'admin-help-docs' ), |
| 98 | 'desc' => __( 'You can also change the logo on all of the help docs, which may differ from the page logo if the background is contrasted. For example a light logo on dark background at the top of the page, and a dark logo on a light background on the help docs themselves. You can also disable the help doc logo from the Content & Output settings on the right. The preferred size of the image is 100x100 pixels. Accepted formats: jpg | jpeg | png | webp', 'admin-help-docs' ), |
| 99 | 'type' => 'text', |
| 100 | 'sanitize' => 'sanitize_text_field', |
| 101 | 'box' => 'interface', |
| 102 | 'default' => Helpers::get_default_logo_url(), |
| 103 | ], |
| 104 | [ |
| 105 | 'name' => 'themes', |
| 106 | 'label' => __( 'Color Theme', 'admin-help-docs' ), |
| 107 | 'type' => 'select', |
| 108 | 'choices' => $themes, |
| 109 | 'sanitize' => 'sanitize_text_field', |
| 110 | 'box' => 'interface', |
| 111 | 'default' => 'custom', |
| 112 | ], |
| 113 | [ |
| 114 | 'name' => 'contact_form', |
| 115 | 'label' => __( 'Enable Support Contact Form', 'admin-help-docs' ), |
| 116 | 'desc' => sprintf( |
| 117 | /* translators: %s is the current site admin email address. */ |
| 118 | __( 'Adds a simple contact form within the Help Docs menu, allowing clients to reach out for support directly. Emails will be sent from %s. This feature utilizes wp_mail() for delivery. To ensure reliable inbox placement, using a dedicated provider like WP Mail SMTP (with Brevo) is advised, along with WP Mail Logging for tracking sent messages.', 'admin-help-docs' ), |
| 119 | '<strong>' . esc_html( get_option( 'admin_email' ) ) . '</strong>' |
| 120 | ), |
| 121 | 'type' => 'checkbox', |
| 122 | 'sanitize' => 'sanitize_checkbox', |
| 123 | 'box' => 'interface', |
| 124 | 'default' => false, |
| 125 | 'has_condition' => true, |
| 126 | ], |
| 127 | [ |
| 128 | 'name' => 'contact_name', |
| 129 | 'label' => __( 'Support Contact Name', 'admin-help-docs' ), |
| 130 | 'desc' => __( 'Enter the name that clients can use to reach you. This will be displayed on the contact form page. Can be a business or personal name.', 'admin-help-docs' ), |
| 131 | 'type' => 'text', |
| 132 | 'sanitize' => 'sanitize_text_field', |
| 133 | 'box' => 'interface', |
| 134 | 'default' => '', |
| 135 | 'condition' => [ 'contact_form' => true ], |
| 136 | ], |
| 137 | [ |
| 138 | 'name' => 'contact_emails', |
| 139 | 'label' => __( 'Support Contact Email(s)', 'admin-help-docs' ), |
| 140 | 'desc' => __( 'Enter the email address(es) that will receive messages from the contact form. Separate multiple emails with commas.', 'admin-help-docs' ), |
| 141 | 'type' => 'text', |
| 142 | 'sanitize' => 'sanitize_text_field', |
| 143 | 'box' => 'interface', |
| 144 | 'default' => implode( ', ', Helpers::get_all_admin_emails() ), |
| 145 | 'condition' => [ 'contact_form' => true ], |
| 146 | ], |
| 147 | [ |
| 148 | 'name' => 'contact_phone', |
| 149 | 'label' => __( 'Support Contact Phone Number', 'admin-help-docs' ), |
| 150 | 'desc' => __( 'Enter a phone number that clients can use to reach you. This will be displayed on the contact form page.', 'admin-help-docs' ), |
| 151 | 'type' => 'text', |
| 152 | 'sanitize' => 'sanitize_text_field', |
| 153 | 'box' => 'interface', |
| 154 | 'default' => '', |
| 155 | 'condition' => [ 'contact_form' => true ], |
| 156 | ], |
| 157 | [ |
| 158 | 'name' => 'admin_bar', |
| 159 | 'label' => __( 'Enable Admin Bar Menu for Backend', 'admin-help-docs' ), |
| 160 | 'type' => 'checkbox', |
| 161 | 'sanitize' => 'sanitize_checkbox', |
| 162 | 'box' => 'interface', |
| 163 | 'default' => false, |
| 164 | ], |
| 165 | [ |
| 166 | 'name' => 'admin_bar_frontend', |
| 167 | 'label' => __( 'Enable Admin Bar Menu for Frontend', 'admin-help-docs' ), |
| 168 | 'type' => 'checkbox', |
| 169 | 'sanitize' => 'sanitize_checkbox', |
| 170 | 'box' => 'interface', |
| 171 | 'default' => false, |
| 172 | ], |
| 173 | [ |
| 174 | 'name' => 'replace_dashboard', |
| 175 | 'label' => __( 'Replace WordPress Dashboard with a Help Doc', 'admin-help-docs' ), |
| 176 | 'desc' => __( 'Replace the default WordPress dashboard with a custom help docs page. To add docs, set their locations to "WordPress Dashboard (Replaces Dashboard Entirely)". Warning: Replacing the WordPress dashboard entirely may affect other plugins and functionality. No other widgets or dashboard elements will be displayed.', 'admin-help-docs' ), |
| 177 | 'type' => 'checkbox', |
| 178 | 'sanitize' => 'sanitize_checkbox', |
| 179 | 'box' => 'interface', |
| 180 | 'default' => false, |
| 181 | ], |
| 182 | [ |
| 183 | 'name' => 'dashboard_toc', |
| 184 | 'label' => __( 'Enable Dashboard Table of Contents Widget', 'admin-help-docs' ), |
| 185 | 'desc' => __( 'Adds a dashboard widget with a table of contents for the docs on the Main Documentation Page.', 'admin-help-docs' ), |
| 186 | 'type' => 'checkbox', |
| 187 | 'sanitize' => 'sanitize_checkbox', |
| 188 | 'box' => 'interface', |
| 189 | 'default' => false, |
| 190 | ], |
| 191 | [ |
| 192 | 'name' => 'gutenberg_editor', |
| 193 | 'label' => __( 'Use Gutenberg Editor', 'admin-help-docs' ), |
| 194 | 'desc' => __( 'Adds support for the Gutenberg editor for the documentation. Default is the classic editor.', 'admin-help-docs' ), |
| 195 | 'type' => 'checkbox', |
| 196 | 'sanitize' => 'sanitize_checkbox', |
| 197 | 'box' => 'interface', |
| 198 | 'default' => false, |
| 199 | ], |
| 200 | |
| 201 | // Content & Output |
| 202 | [ |
| 203 | 'name' => 'admin_bar_include_content', |
| 204 | 'label' => __( 'Include Doc Content in Admin Bar', 'admin-help-docs' ), |
| 205 | 'desc' => __( 'Includes a snippet of the document content in the admin bar.', 'admin-help-docs' ), |
| 206 | 'type' => 'checkbox', |
| 207 | 'sanitize' => 'sanitize_checkbox', |
| 208 | 'box' => 'content_output', |
| 209 | 'default' => false, |
| 210 | ], |
| 211 | [ |
| 212 | 'name' => 'default_doc', |
| 213 | 'label' => __( 'Default Document on Main Docs Page', 'admin-help-docs' ), |
| 214 | 'type' => 'select', |
| 215 | 'choices' => Helpers::get_main_helpdoc_options(), |
| 216 | 'sanitize' => 'sanitize_text_field', |
| 217 | 'box' => 'content_output', |
| 218 | ], |
| 219 | [ |
| 220 | 'name' => 'hide_doc_meta', |
| 221 | 'label' => __( 'Hide Document Meta on Main Docs Page', 'admin-help-docs' ), |
| 222 | 'desc' => __( 'Includes created and last modified dates and authors.', 'admin-help-docs' ), |
| 223 | 'type' => 'checkbox', |
| 224 | 'sanitize' => 'sanitize_checkbox', |
| 225 | 'box' => 'content_output', |
| 226 | 'default' => false, |
| 227 | ], |
| 228 | [ |
| 229 | 'name' => 'auto_htoc', |
| 230 | 'label' => __( 'Auto-Generate Table of Contents on Main Docs Page', 'admin-help-docs' ), |
| 231 | 'desc' => __( 'Automatically generate a table of contents from headings (H2–H6) at the top of each documentation page.', 'admin-help-docs' ), |
| 232 | 'type' => 'checkbox', |
| 233 | 'sanitize' => 'sanitize_checkbox', |
| 234 | 'box' => 'content_output', |
| 235 | 'default' => false, |
| 236 | ], |
| 237 | [ |
| 238 | 'name' => 'curly_quotes', |
| 239 | 'label' => __( 'Disable Curly Quotes on Main Docs Page', 'admin-help-docs' ), |
| 240 | 'desc' => __( 'WP automatically converts straight quotes (") to curly quotes (”), which makes sharing code difficult.', 'admin-help-docs' ), |
| 241 | 'type' => 'checkbox', |
| 242 | 'sanitize' => 'sanitize_checkbox', |
| 243 | 'box' => 'content_output', |
| 244 | 'default' => false, |
| 245 | ], |
| 246 | [ |
| 247 | 'name' => 'enqueue_frontend_styles', |
| 248 | 'label' => __( 'Use Frontend Stylesheets on Main Docs Page', 'admin-help-docs' ), |
| 249 | 'desc' => __( 'Adds support for your frontend styles in the backend by enqueueing them while on the Main Docs Page.', 'admin-help-docs' ), |
| 250 | 'type' => 'checkbox', |
| 251 | 'sanitize' => 'sanitize_checkbox', |
| 252 | 'box' => 'content_output', |
| 253 | 'default' => false, |
| 254 | ], |
| 255 | [ |
| 256 | 'name' => 'main_docs_css', |
| 257 | 'label' => __( 'Main Docs Page CSS', 'admin-help-docs' ), |
| 258 | 'desc' => __( 'Add CSS here to style the Main Documentation Page specifically. You must use CSS selectors.', 'admin-help-docs' ), |
| 259 | 'type' => 'textarea', |
| 260 | 'sanitize' => 'sanitize_custom_css', |
| 261 | 'box' => 'content_output', |
| 262 | 'default' => '', |
| 263 | ], |
| 264 | [ |
| 265 | 'name' => 'include_logo_on_docs', |
| 266 | 'label' => __( 'Include Logo on Help Docs', 'admin-help-docs' ), |
| 267 | 'desc' => __( 'Includes the site logo on individual help docs that are not on the Main Docs Page.', 'admin-help-docs' ), |
| 268 | 'type' => 'checkbox', |
| 269 | 'sanitize' => 'sanitize_checkbox', |
| 270 | 'box' => 'content_output', |
| 271 | 'default' => true, |
| 272 | ], |
| 273 | [ |
| 274 | 'name' => 'top_location_type', |
| 275 | 'label' => __( 'Top Location Placement', 'admin-help-docs' ), |
| 276 | 'type' => 'select', |
| 277 | 'choices' => $top_placements, |
| 278 | 'sanitize' => 'sanitize_key', |
| 279 | 'box' => 'content_output', |
| 280 | 'default' => 'admin_notices', |
| 281 | ], |
| 282 | [ |
| 283 | 'name' => 'footer_left', |
| 284 | 'label' => __( 'Left Footer Text', 'admin-help-docs' ), |
| 285 | 'desc' => __( 'Supports HTML. Default: "Thank you for creating with WordPress."', 'admin-help-docs' ), |
| 286 | 'type' => 'textarea', |
| 287 | 'sanitize' => 'wp_kses_post', |
| 288 | 'box' => 'content_output', |
| 289 | 'default' => '<span id="footer-thankyou">Thank you for creating with <a href="https://wordpress.org/">WordPress</a>.</span>', |
| 290 | ], |
| 291 | [ |
| 292 | 'name' => 'footer_right', |
| 293 | 'label' => __( 'Right Footer Text', 'admin-help-docs' ), |
| 294 | 'desc' => __( 'Use <code>{version}</code> to display the current WordPress version', 'admin-help-docs' ), |
| 295 | 'type' => 'textarea', |
| 296 | 'sanitize' => 'wp_kses_post', |
| 297 | 'box' => 'content_output', |
| 298 | 'default' => 'Version {version}', |
| 299 | ], |
| 300 | |
| 301 | // Access & Permissions |
| 302 | [ |
| 303 | 'name' => 'api', |
| 304 | 'label' => __( 'Allow Public by Default', 'admin-help-docs' ), |
| 305 | 'type' => 'checkbox', |
| 306 | 'sanitize' => 'sanitize_checkbox', |
| 307 | 'box' => 'access_control', |
| 308 | 'default' => false, |
| 309 | ], |
| 310 | [ |
| 311 | 'name' => 'api_key', |
| 312 | 'label' => __( 'Public Access API Key (Optional)', 'admin-help-docs' ), |
| 313 | 'desc' => __( 'If you enable "Allow Public" above or on individual docs, you can optionally require an API key for access. This adds a layer of security by ensuring that only users with the key can import your documentation. To use, generate a key here and enter it into the import on your other site where you are importing the docs. Leave empty to allow public access without a key.', 'admin-help-docs' ), |
| 314 | 'type' => 'api_key', |
| 315 | 'sanitize' => 'sanitize_text_field', |
| 316 | 'box' => 'access_control', |
| 317 | 'default' => false, |
| 318 | ], |
| 319 | [ |
| 320 | 'name' => 'user_view_type', |
| 321 | 'label' => __( 'Requirement Type to View Docs', 'admin-help-docs' ), |
| 322 | 'type' => 'select', |
| 323 | 'choices' => [ |
| 324 | 'capability' => __( 'Capability', 'admin-help-docs' ), |
| 325 | 'role' => __( 'Role', 'admin-help-docs' ), |
| 326 | ], |
| 327 | 'sanitize' => 'sanitize_text_field', |
| 328 | 'box' => 'access_control', |
| 329 | 'default' => $view_default, |
| 330 | 'has_condition' => true, |
| 331 | ], |
| 332 | [ |
| 333 | 'name' => 'user_view_cap', |
| 334 | 'label' => __( 'Default Capability Required to View Docs', 'admin-help-docs' ) . ' — <a href="https://wordpress.org/documentation/article/roles-and-capabilities/" target="_blank">View a list of capabilities. <span class="dashicons dashicons-external"></span></a>', |
| 335 | 'desc' => __( 'Use <code>manage_options</code> for admins only. You can also override this setting on a per-document basis.', 'admin-help-docs' ), |
| 336 | 'type' => 'text', |
| 337 | 'sanitize' => 'sanitize_text_field', |
| 338 | 'box' => 'access_control', |
| 339 | 'default' => 'manage_options', |
| 340 | 'condition' => [ 'user_view_type' => 'capability' ], |
| 341 | ], |
| 342 | [ |
| 343 | 'name' => 'view_roles', |
| 344 | 'label' => __( 'Additional Default Roles Required to View Docs', 'admin-help-docs' ), |
| 345 | 'desc' => __( 'Admins can view all docs regardless of this setting. You can also override this setting on a per-document basis.', 'admin-help-docs' ), |
| 346 | 'type' => 'checkboxes', |
| 347 | 'choices' => Helpers::get_role_options(), |
| 348 | 'sanitize' => 'sanitize_checkboxes', |
| 349 | 'box' => 'access_control', |
| 350 | 'condition' => [ 'user_view_type' => 'role' ], |
| 351 | ], |
| 352 | [ |
| 353 | 'name' => 'user_edit_type', |
| 354 | 'label' => __( 'Requirement Type to Edit Docs', 'admin-help-docs' ), |
| 355 | 'type' => 'select', |
| 356 | 'choices' => [ |
| 357 | 'capability' => __( 'Capability', 'admin-help-docs' ), |
| 358 | 'role' => __( 'Role', 'admin-help-docs' ), |
| 359 | ], |
| 360 | 'sanitize' => 'sanitize_text_field', |
| 361 | 'box' => 'access_control', |
| 362 | 'default' => 'role', |
| 363 | 'has_condition' => true, |
| 364 | ], |
| 365 | [ |
| 366 | 'name' => 'user_edit_cap', |
| 367 | 'label' => __( 'Capability Required to Edit Docs', 'admin-help-docs' ) . ' — <a href="https://wordpress.org/documentation/article/roles-and-capabilities/" target="_blank">View a list of capabilities. <span class="dashicons dashicons-external"></span></a>', |
| 368 | 'desc' => __( 'Use <code>manage_options</code> for admins only.', 'admin-help-docs' ), |
| 369 | 'type' => 'text', |
| 370 | 'sanitize' => 'sanitize_text_field', |
| 371 | 'box' => 'access_control', |
| 372 | 'default' => 'manage_options', |
| 373 | 'condition' => [ 'user_edit_type' => 'capability' ], |
| 374 | ], |
| 375 | [ |
| 376 | 'name' => 'edit_roles', |
| 377 | 'label' => __( 'Additional Roles Required to Edit Docs', 'admin-help-docs' ), |
| 378 | 'desc' => __( 'Admins can edit all docs regardless of this setting.', 'admin-help-docs' ), |
| 379 | 'type' => 'checkboxes', |
| 380 | 'choices' => Helpers::get_role_options(), |
| 381 | 'sanitize' => 'sanitize_checkboxes', |
| 382 | 'box' => 'access_control', |
| 383 | 'condition' => [ 'user_edit_type' => 'role' ], |
| 384 | ], |
| 385 | |
| 386 | // Advanced |
| 387 | [ |
| 388 | 'name' => 'remove_on_uninstall', |
| 389 | 'label' => __( 'Remove All Plugin Data on Uninstall', 'admin-help-docs' ), |
| 390 | 'desc' => __( 'Deletes all plugin settings and documentation permanently when the plugin is deleted.', 'admin-help-docs' ), |
| 391 | 'type' => 'checkbox', |
| 392 | 'sanitize' => 'sanitize_checkbox', |
| 393 | 'box' => 'advanced', |
| 394 | 'default' => false, |
| 395 | ], |
| 396 | [ |
| 397 | 'name' => 'flush_cache', |
| 398 | 'label' => __( 'System Cache Management', 'admin-help-docs' ), |
| 399 | 'type' => 'html', |
| 400 | 'box' => 'advanced', |
| 401 | 'content' => '<button type="button" id="helpdocs-flush-cache" class="helpdocs-button">' . __( 'Clear Stored Data', 'admin-help-docs' ) . '</button>', |
| 402 | ], |
| 403 | [ |
| 404 | 'name' => 'upload_download_colors', |
| 405 | 'label' => __( 'Upload/Download Colors', 'admin-help-docs' ), |
| 406 | 'desc' => __( 'Export your current color settings as a JSON file for backup or transfer to another site. You can also import color settings from a JSON file exported from this plugin. Note: Importing settings will overwrite your current color settings.', 'admin-help-docs' ), |
| 407 | 'type' => 'html', |
| 408 | 'box' => 'advanced', |
| 409 | 'content' => '<button type="button" id="helpdocs-download-colors-btn" class="helpdocs-button">' . __( 'Download Colors', 'admin-help-docs' ) . '</button> <div id="helpdocs-upload-colors-button"><label for="helpdocs-upload-colors"><span class="helpdocs-button">' . __( 'Upload Colors', 'admin-help-docs' ) . '</span></label><input type="file" id="helpdocs-upload-colors" name="helpdocs-upload-colors" accept=".json"></div> <div id="helpdocs-upload-colors-filename"></div>', |
| 410 | ], |
| 411 | [ |
| 412 | 'name' => 'upload_download_settings', |
| 413 | 'label' => __( 'Upload/Download Settings', 'admin-help-docs' ), |
| 414 | 'desc' => __( 'Export your current settings as a JSON file for backup or transfer to another site. You can also import settings from a JSON file exported from this plugin. Note: Importing settings will overwrite your current settings.', 'admin-help-docs' ), |
| 415 | 'type' => 'html', |
| 416 | 'box' => 'advanced', |
| 417 | 'content' => '<button type="button" id="helpdocs-download-settings-btn" class="helpdocs-button">' . __( 'Download Settings', 'admin-help-docs' ) . '</button> <div id="helpdocs-upload-button"><label for="helpdocs-upload-settings"><span class="helpdocs-button">' . __( 'Upload Settings', 'admin-help-docs' ) . '</span></label><input type="file" id="helpdocs-upload-settings" name="helpdocs-upload-settings" accept=".json"></div> <div id="helpdocs-upload-filename"></div>', |
| 418 | ], |
| 419 | [ |
| 420 | 'name' => 'reset_settings', |
| 421 | 'label' => __( 'Reset Settings', 'admin-help-docs' ), |
| 422 | 'desc' => __( 'This will not delete any documentation, but it will reset all settings to their defaults.', 'admin-help-docs' ), |
| 423 | 'type' => 'html', |
| 424 | 'box' => 'advanced', |
| 425 | 'content' => '<button type="button" id="helpdocs-reset-colors" class="helpdocs-button">' . __( 'Reset All Colors', 'admin-help-docs' ) . '</button> <button type="button" id="helpdocs-reset-settings" class="helpdocs-button">' . __( 'Reset All Settings', 'admin-help-docs' ) . '</button>', |
| 426 | ], |
| 427 | ]; |
| 428 | |
| 429 | // Colors |
| 430 | $color_fields = []; |
| 431 | $colors = Colors::defaults(); |
| 432 | foreach ( $colors as $key => $color ) { |
| 433 | if ( strpos( $key, 'subheader' ) !== false ) { |
| 434 | continue; |
| 435 | } |
| 436 | $color_fields[] = [ |
| 437 | 'name' => 'color_' . $key, |
| 438 | 'label' => $color[ 'label' ], |
| 439 | 'type' => 'color', |
| 440 | 'sanitize' => 'sanitize_text_field', |
| 441 | 'box' => 'interface', |
| 442 | 'default' => $color[ 'color' ], |
| 443 | ]; |
| 444 | } |
| 445 | |
| 446 | $themes_index = false; |
| 447 | foreach ( $fields as $index => $field ) { |
| 448 | if ( isset( $field[ 'name' ] ) && 'themes' === $field[ 'name' ] ) { |
| 449 | $themes_index = $index; |
| 450 | break; |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | if ( false !== $themes_index ) { |
| 455 | array_splice( $fields, $themes_index + 1, 0, $color_fields ); |
| 456 | } else { |
| 457 | $fields = array_merge( $fields, $color_fields ); |
| 458 | } |
| 459 | |
| 460 | // Only get the name, type and default for each field |
| 461 | if ( $defaults_only ) { |
| 462 | foreach ( $fields as $index => $field ) { |
| 463 | $fields[ $index ] = [ |
| 464 | 'name' => $field[ 'name' ], |
| 465 | 'type' => $field[ 'type' ], |
| 466 | 'default' => $field[ 'default' ] ?? null, |
| 467 | ]; |
| 468 | } |
| 469 | |
| 470 | } |
| 471 | |
| 472 | return apply_filters( 'helpdocs_settings_fields', $fields ); |
| 473 | } // End setting_fields() |
| 474 | |
| 475 | |
| 476 | /** |
| 477 | * Returns available placements for top docs. |
| 478 | * |
| 479 | * @return array |
| 480 | */ |
| 481 | public static function top_placements() : array { |
| 482 | return [ |
| 483 | 'admin_notices' => __( 'Just Above Page Title', 'admin-help-docs' ), |
| 484 | 'in_admin_header' => __( 'Very Top of Page (Above All Content)', 'admin-help-docs' ), |
| 485 | ]; |
| 486 | } // End top_placements() |
| 487 | |
| 488 | |
| 489 | /** |
| 490 | * The single instance of the class |
| 491 | * |
| 492 | * @var self|null |
| 493 | */ |
| 494 | private static ?Settings $instance = null; |
| 495 | |
| 496 | |
| 497 | /** |
| 498 | * Get the singleton instance |
| 499 | * |
| 500 | * @return self |
| 501 | */ |
| 502 | public static function instance() : self { |
| 503 | return self::$instance ??= new self(); |
| 504 | } // End instance() |
| 505 | |
| 506 | |
| 507 | /** |
| 508 | * Constructor |
| 509 | */ |
| 510 | private function __construct() { |
| 511 | add_action( 'helpdocs_subheader_left', [ $this, 'render_save_reminder' ] ); |
| 512 | add_action( 'wp_ajax_helpdocs_save_settings', [ $this, 'ajax_save_settings' ] ); |
| 513 | add_action( 'wp_ajax_helpdocs_flush_cache', [ $this, 'ajax_flush_cache' ] ); |
| 514 | } // End __construct() |
| 515 | |
| 516 | |
| 517 | /** |
| 518 | * Render a reminder to save settings after changes are made |
| 519 | * |
| 520 | * @param string $current_tab The current active tab |
| 521 | */ |
| 522 | public function render_save_reminder( $current_tab ) { |
| 523 | if ( $current_tab === 'settings' ) { |
| 524 | echo '<span id="helpdocs-save-reminder">' . esc_html__( 'Remember to click "Save" after making changes to your settings.', 'admin-help-docs' ) . '</span>'; |
| 525 | } |
| 526 | } // End render_save_reminder() |
| 527 | |
| 528 | |
| 529 | /** |
| 530 | * Render the tab |
| 531 | */ |
| 532 | public function render_tab() { |
| 533 | $fields = $this->setting_fields(); |
| 534 | $boxes = []; |
| 535 | $box_labels = $this->setting_boxes(); |
| 536 | |
| 537 | // Group fields by box |
| 538 | foreach ( $fields as $field ) { |
| 539 | $boxes[ $field[ 'box' ] ][] = $field; |
| 540 | } |
| 541 | |
| 542 | echo '<div class="helpdocs-settings-grid">'; |
| 543 | foreach ( $boxes as $box_id => $fields ) { |
| 544 | echo '<div class="helpdocs-settings-box">'; |
| 545 | echo '<div class="helpdocs-settings-header">'; |
| 546 | echo '<h2>' . esc_html( $box_labels[ $box_id ] ?? 'Unknown Box' ) . '</h2>'; |
| 547 | echo '</div>'; |
| 548 | echo '<div class="helpdocs-settings-body">'; |
| 549 | |
| 550 | $in_color_group = false; |
| 551 | foreach ( $fields as $index => $field ) { |
| 552 | $is_color = ( $field[ 'type' ] === 'color' ); |
| 553 | $render_fn = "render_field_{$field[ 'type' ]}"; |
| 554 | |
| 555 | if ( $is_color && ! $in_color_group ) { |
| 556 | echo '<div class="helpdocs-color-grid">'; |
| 557 | $in_color_group = true; |
| 558 | } |
| 559 | |
| 560 | if ( ! $is_color && $in_color_group ) { |
| 561 | echo '</div>'; |
| 562 | $in_color_group = false; |
| 563 | } |
| 564 | |
| 565 | if ( method_exists( $this, $render_fn ) ) { |
| 566 | $this->{$render_fn}( $field ); |
| 567 | } |
| 568 | |
| 569 | $is_last = ( $index === array_key_last( $fields ) ); |
| 570 | |
| 571 | if ( $is_last && $in_color_group ) { |
| 572 | echo '</div>'; |
| 573 | $in_color_group = false; |
| 574 | } |
| 575 | } |
| 576 | echo '</div></div>'; |
| 577 | } |
| 578 | echo '</div>'; |
| 579 | } // End render_tab() |
| 580 | |
| 581 | |
| 582 | /** |
| 583 | * Get the value of a field, falling back to the default if not set |
| 584 | * |
| 585 | * @param string $field_name The name of the field |
| 586 | * @return mixed The value of the field or its default |
| 587 | */ |
| 588 | public static function get_field_value( $field_name ) { |
| 589 | $option = get_option( "helpdocs_{$field_name}", null ); |
| 590 | |
| 591 | if ( null !== $option ) { |
| 592 | return $option; |
| 593 | } |
| 594 | |
| 595 | $all_fields = self::setting_fields(); |
| 596 | foreach ( $all_fields as $field ) { |
| 597 | if ( $field[ 'name' ] === $field_name ) { |
| 598 | return $field[ 'default' ] ?? ''; |
| 599 | } |
| 600 | } |
| 601 | |
| 602 | return ''; |
| 603 | } // End get_field_value() |
| 604 | |
| 605 | |
| 606 | /** |
| 607 | * Sanitize a field value based on the field's specified sanitize callback |
| 608 | * |
| 609 | * @param array $field The field definition array |
| 610 | * @param mixed $value The value to sanitize |
| 611 | * @return mixed The sanitized value |
| 612 | */ |
| 613 | public function sanitize_field( $field, $value ) { |
| 614 | $sanitize_callback = $field[ 'sanitize' ] ?? null; |
| 615 | |
| 616 | if ( $sanitize_callback && is_callable( [ $this, $sanitize_callback ] ) ) { |
| 617 | return $this->$sanitize_callback( $value ); |
| 618 | } elseif ( is_callable( $sanitize_callback ) ) { |
| 619 | return $sanitize_callback( $value ); |
| 620 | } else { |
| 621 | return sanitize_text_field( $value ); |
| 622 | } |
| 623 | } // End sanitize_field() |
| 624 | |
| 625 | |
| 626 | /** |
| 627 | * Render a field label with optional description tooltip |
| 628 | * |
| 629 | * @param array $field The field definition array |
| 630 | * @param string $for Optional ID of the associated input for the label's "for" attribute |
| 631 | */ |
| 632 | public function render_field_label( $field, $for = '' ) { |
| 633 | ?> |
| 634 | <label <?php echo $for ? 'for="' . esc_attr( $for ) . '"' : ''; ?> class="helpdocs-field-label"> |
| 635 | <?php echo wp_kses_post( $field[ 'label' ] ); ?> |
| 636 | |
| 637 | <?php if ( ! empty( $field[ 'required' ] ) ) : ?> |
| 638 | <span class="helpdocs-required-field" title="<?php esc_attr_e( 'Required', 'admin-help-docs' ); ?>">*</span> |
| 639 | <?php endif; ?> |
| 640 | |
| 641 | <?php if ( ! empty( $field[ 'desc' ] ) ) : ?> |
| 642 | <span class="helpdocs-tooltip"> |
| 643 | <span class="dashicons dashicons-editor-help"></span> |
| 644 | <span class="helpdocs-tooltip-text"><?php echo wp_kses_post( $field[ 'desc' ] ); ?></span> |
| 645 | </span> |
| 646 | <?php endif; ?> |
| 647 | </label> |
| 648 | <?php |
| 649 | } // End render_field_label() |
| 650 | |
| 651 | |
| 652 | /** |
| 653 | * Render a text field |
| 654 | * |
| 655 | * @param array $field The field definition array |
| 656 | */ |
| 657 | public function render_field_text( $field ) { |
| 658 | $value = $this->get_field_value( $field[ 'name' ] ); |
| 659 | |
| 660 | // If the value is empty/null, use the default if provided |
| 661 | if ( ( is_null( $value ) || $value === '' ) && isset( $field[ 'default' ] ) ) { |
| 662 | $value = $field[ 'default' ]; |
| 663 | } |
| 664 | |
| 665 | $value = $this->sanitize_field( $field, $value ); |
| 666 | |
| 667 | $conditional_class = ''; |
| 668 | $data_condition_field = ''; |
| 669 | $data_condition_value = ''; |
| 670 | |
| 671 | if ( ! empty( $field[ 'condition' ] ) ) { |
| 672 | $condition_field = key( $field[ 'condition' ] ); |
| 673 | $expected_value = current( $field[ 'condition' ] ); |
| 674 | $target_value = $this->get_field_value( $condition_field ); |
| 675 | |
| 676 | if ( $target_value != $expected_value ) { |
| 677 | $conditional_class = ' condition-hide'; |
| 678 | } |
| 679 | |
| 680 | $data_condition_field = $condition_field; |
| 681 | $data_condition_value = $expected_value; |
| 682 | } |
| 683 | |
| 684 | $required = ! empty( $field[ 'required' ] ) ? ' required' : ''; |
| 685 | ?> |
| 686 | <div id="helpdocs_field_<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 687 | class="helpdocs-field<?php echo esc_attr( $conditional_class ); ?>" |
| 688 | <?php if ( $data_condition_field && $data_condition_value ) : ?> |
| 689 | data-condition-field="<?php echo esc_attr( $data_condition_field ); ?>" |
| 690 | data-condition-value="<?php echo esc_attr( $data_condition_value ); ?>" |
| 691 | <?php endif; ?>> |
| 692 | <?php $this->render_field_label( $field, $field[ 'name' ] ); ?> |
| 693 | <input type="text" |
| 694 | id="<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 695 | name="helpdocs_<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 696 | value="<?php echo esc_attr( $value ); ?>" |
| 697 | <?php echo esc_attr( $required ); ?> |
| 698 | placeholder="<?php echo isset( $field[ 'default' ] ) ? esc_attr( $field[ 'default' ] ) : ''; ?>"> |
| 699 | </div> |
| 700 | <?php |
| 701 | } // End render_field_text() |
| 702 | |
| 703 | |
| 704 | /** |
| 705 | * Render a textarea field |
| 706 | * |
| 707 | * @param array $field The field definition array |
| 708 | */ |
| 709 | public function render_field_textarea( $field ) { |
| 710 | $value = $this->get_field_value( $field[ 'name' ] ); |
| 711 | $value = $this->sanitize_field( $field, $value ); |
| 712 | |
| 713 | $conditional_class = ''; |
| 714 | $data_condition_field = ''; |
| 715 | $data_condition_value = ''; |
| 716 | |
| 717 | if ( ! empty( $field[ 'condition' ] ) ) { |
| 718 | $condition_field = key( $field[ 'condition' ] ); |
| 719 | $expected_value = current( $field[ 'condition' ] ); |
| 720 | $target_value = $this->get_field_value( $condition_field ); |
| 721 | |
| 722 | if ( $target_value != $expected_value ) { |
| 723 | $conditional_class = ' condition-hide'; |
| 724 | } |
| 725 | |
| 726 | $data_condition_field = $condition_field; |
| 727 | $data_condition_value = $expected_value; |
| 728 | } |
| 729 | |
| 730 | $required = ! empty( $field[ 'required' ] ) ? ' required' : ''; |
| 731 | |
| 732 | $field_id = str_starts_with( $field[ 'name' ], 'footer' ) ? 'helpdocs_' . $field[ 'name' ] : $field[ 'name' ]; |
| 733 | ?> |
| 734 | <div id="helpdocs_field_<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 735 | class="helpdocs-field<?php echo esc_attr( $conditional_class ); ?>" |
| 736 | <?php if ( $data_condition_field && $data_condition_value ) : ?> |
| 737 | data-condition-field="<?php echo esc_attr( $data_condition_field ); ?>" |
| 738 | data-condition-value="<?php echo esc_attr( $data_condition_value ); ?>" |
| 739 | <?php endif; ?>> |
| 740 | <?php $this->render_field_label( $field, $field[ 'name' ] ); ?> |
| 741 | <textarea id="<?php echo esc_attr( $field_id ); ?>" |
| 742 | name="helpdocs_<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 743 | <?php echo esc_attr( $required ); ?> |
| 744 | placeholder="<?php if ( isset( $field[ 'default' ] ) ) { echo esc_attr( $field[ 'default' ] ); } ?>"><?php echo esc_textarea( $value ); ?></textarea> |
| 745 | </div> |
| 746 | <?php |
| 747 | } // End render_field_textarea() |
| 748 | |
| 749 | |
| 750 | /** |
| 751 | * Render a color field |
| 752 | * |
| 753 | * @param array $field The field definition array |
| 754 | */ |
| 755 | public function render_field_color( $field ) { |
| 756 | $value = Colors::get( str_replace( 'color_', '', $field[ 'name' ] ) ); |
| 757 | $value = $this->sanitize_field( $field, $value ); |
| 758 | ?> |
| 759 | <div id="helpdocs_field_<?php echo esc_attr( $field[ 'name' ] ); ?>" class="helpdocs-field color-picker-field"> |
| 760 | <label for="<?php echo esc_attr( $field[ 'name' ] ); ?>"> |
| 761 | <?php echo esc_html( $field[ 'label' ] ); ?> |
| 762 | </label> |
| 763 | <input type="color" |
| 764 | id="<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 765 | name="helpdocs_<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 766 | value="<?php echo esc_attr( $value ); ?>"> |
| 767 | </div> |
| 768 | <?php |
| 769 | } // End render_field_color() |
| 770 | |
| 771 | |
| 772 | /** |
| 773 | * Render a select field |
| 774 | * |
| 775 | * @param array $field The field definition array, which should include a 'choices' key with an array of options |
| 776 | */ |
| 777 | public function render_field_select( $field ) { |
| 778 | $option = get_option( "helpdocs_{$field[ 'name' ]}", null ); |
| 779 | |
| 780 | $value = ( null === $option || '' === $option ) |
| 781 | ? ( $field[ 'default' ] ?? '' ) |
| 782 | : $option; |
| 783 | |
| 784 | $value = $this->sanitize_field( $field, $value ); |
| 785 | |
| 786 | // Strip dashicons |
| 787 | $value = str_replace( 'dashicons-', '', $value ); |
| 788 | |
| 789 | $has_condition = isset( $field[ 'has_condition' ] ) && $field[ 'has_condition' ]; |
| 790 | $conditional_class = $has_condition ? ' has-condition' : ''; |
| 791 | |
| 792 | $required = ! empty( $field[ 'required' ] ) ? ' required' : ''; |
| 793 | ?> |
| 794 | <div id="helpdocs_field_<?php echo esc_attr( $field[ 'name' ] ); ?>" class="helpdocs-field"> |
| 795 | <?php $this->render_field_label( $field, $field[ 'name' ] ); ?> |
| 796 | <select id="<?php echo esc_attr( $field[ 'name' ] ); ?>" name="helpdocs_<?php echo esc_attr( $field[ 'name' ] ); ?>" class="<?php echo esc_attr( $conditional_class ); ?>"<?php echo esc_attr( $required ); ?>> |
| 797 | <?php |
| 798 | if ( isset( $field[ 'choices' ] ) && is_array( $field[ 'choices' ] ) ) { |
| 799 | |
| 800 | foreach ( $field[ 'choices' ] as $key => $choice ) { |
| 801 | |
| 802 | if ( is_array( $choice ) ) { |
| 803 | $option_value = $choice[ 'value' ] ?? ''; |
| 804 | $option_label = $choice[ 'label' ] ?? ''; |
| 805 | } else { |
| 806 | $option_value = is_string( $key ) ? $key : $choice; |
| 807 | $option_label = $choice; |
| 808 | } |
| 809 | ?> |
| 810 | <option value="<?php echo esc_attr( $option_value ); ?>" <?php selected( $value, $option_value ); ?>> |
| 811 | <?php echo esc_html( $option_label ); ?> |
| 812 | </option> |
| 813 | <?php |
| 814 | } |
| 815 | } |
| 816 | ?> |
| 817 | </select> |
| 818 | </div> |
| 819 | <?php |
| 820 | } // End render_field_select() |
| 821 | |
| 822 | |
| 823 | /** |
| 824 | * Render a checkbox field |
| 825 | * |
| 826 | * @param array $field The field definition array |
| 827 | */ |
| 828 | public function render_field_checkbox( $field ) { |
| 829 | $option = get_option( "helpdocs_{$field[ 'name' ]}", null ); |
| 830 | |
| 831 | $value = ( null === $option ) |
| 832 | ? ( $field[ 'default' ] ?? '' ) |
| 833 | : $option; |
| 834 | |
| 835 | $value = $this->sanitize_field( $field, $value ); |
| 836 | |
| 837 | $has_condition = isset( $field[ 'has_condition' ] ) && $field[ 'has_condition' ]; |
| 838 | $conditional_class = $has_condition ? ' has-condition' : ''; |
| 839 | |
| 840 | $required = ! empty( $field[ 'required' ] ) ? ' required' : ''; |
| 841 | ?> |
| 842 | <div id="helpdocs_field_<?php echo esc_attr( $field[ 'name' ] ); ?>" class="helpdocs-field helpdocs-field-checkbox<?php echo esc_attr( $conditional_class ); ?>"> |
| 843 | <label class="helpdocs-checkbox-label"> |
| 844 | <input type="checkbox" |
| 845 | id="<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 846 | name="helpdocs_<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 847 | value="1" <?php checked( $value, '1' ); ?> <?php echo esc_attr( $required ); ?>> |
| 848 | <span><?php echo esc_html( $field[ 'label' ] ); ?></span> |
| 849 | <?php if ( ! empty( $field[ 'desc' ] ) ) : ?> |
| 850 | <span class="helpdocs-tooltip"> |
| 851 | <span class="dashicons dashicons-editor-help"></span> |
| 852 | <span class="helpdocs-tooltip-text"><?php echo wp_kses_post( $field[ 'desc' ] ); ?></span> |
| 853 | </span> |
| 854 | <?php endif; ?> |
| 855 | </label> |
| 856 | </div> |
| 857 | <?php |
| 858 | } // End render_field_checkbox() |
| 859 | |
| 860 | |
| 861 | /** |
| 862 | * Sanitize checkbox |
| 863 | * |
| 864 | * @param mixed $value The value to sanitize |
| 865 | * @return string '1' if checked, '' if not |
| 866 | */ |
| 867 | public function sanitize_checkbox( $value ) { |
| 868 | return ( ( $value == '1' || $value == 'yes' ) ? '1' : '' ); |
| 869 | } // End sanitize_checkbox() |
| 870 | |
| 871 | |
| 872 | /** |
| 873 | * Render a checkboxes field |
| 874 | * |
| 875 | * @param array $field The field definition array, which should include a 'choices' key with an array of options |
| 876 | */ |
| 877 | public function render_field_checkboxes( $field ) { |
| 878 | $values = (array) $this->get_field_value( $field[ 'name' ] ); |
| 879 | $values = (array) $this->sanitize_field( $field, $values ); |
| 880 | |
| 881 | $conditional_class = ''; |
| 882 | $data_condition_field = ''; |
| 883 | $data_condition_value = ''; |
| 884 | |
| 885 | if ( ! empty( $field[ 'condition' ] ) ) { |
| 886 | $condition_field = key( $field[ 'condition' ] ); |
| 887 | $expected_value = current( $field[ 'condition' ] ); |
| 888 | $target_value = $this->get_field_value( $condition_field ); |
| 889 | |
| 890 | if ( $target_value != $expected_value ) { |
| 891 | $conditional_class = ' condition-hide'; |
| 892 | } |
| 893 | |
| 894 | $data_condition_field = $condition_field; |
| 895 | $data_condition_value = $expected_value; |
| 896 | } |
| 897 | ?> |
| 898 | <div id="helpdocs_field_<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 899 | class="helpdocs-field<?php echo esc_attr( $conditional_class ); ?>" |
| 900 | <?php if ( $data_condition_field && $data_condition_value ) : ?> |
| 901 | data-condition-field="<?php echo esc_attr( $data_condition_field ); ?>" |
| 902 | data-condition-value="<?php echo esc_attr( $data_condition_value ); ?>" |
| 903 | <?php endif; ?>> |
| 904 | <?php $this->render_field_label( $field, $field[ 'name' ] ); ?> |
| 905 | <div class="helpdocs-checkboxes"> |
| 906 | <?php |
| 907 | if ( isset( $field[ 'choices' ] ) && is_array( $field[ 'choices' ] ) ) { |
| 908 | foreach ( $field[ 'choices' ] as $choice ) : |
| 909 | $key = is_array( $choice ) ? ( $choice[ 'value' ] ?? '' ) : ( $choice[ 0 ] ?? '' ); |
| 910 | $label = is_array( $choice ) ? ( $choice[ 'label' ] ?? '' ) : ( $choice[ 1 ] ?? '' ); |
| 911 | ?> |
| 912 | <label> |
| 913 | <input type="checkbox" |
| 914 | name="helpdocs_<?php echo esc_attr( $field[ 'name' ] ); ?>[]" |
| 915 | value="<?php echo esc_attr( $key ); ?>" <?php checked( in_array( $key, $values, true ) || in_array( $key, array_keys( $values ), true ) ); ?>> |
| 916 | <?php echo esc_html( $label ); ?> |
| 917 | </label> |
| 918 | <?php |
| 919 | endforeach; |
| 920 | } |
| 921 | ?> |
| 922 | </div> |
| 923 | </div> |
| 924 | <?php |
| 925 | } // End render_field_checkboxes() |
| 926 | |
| 927 | |
| 928 | /** |
| 929 | * Sanitize checkboxes |
| 930 | * |
| 931 | * @param mixed $values The values to sanitize, expected to be an array of selected keys |
| 932 | * @return array An array of sanitized values that are valid choices |
| 933 | */ |
| 934 | private function sanitize_checkboxes( $values ) { |
| 935 | if ( ! is_array( $values ) ) { |
| 936 | return []; |
| 937 | } |
| 938 | return array_map( 'strval', $values ); |
| 939 | } // End sanitize_checkboxes() |
| 940 | |
| 941 | |
| 942 | /** |
| 943 | * Render a file upload field |
| 944 | * |
| 945 | * @param array $field The field definition array |
| 946 | */ |
| 947 | public function render_field_file( $field ) { |
| 948 | $conditional_class = ''; |
| 949 | $data_condition_field = ''; |
| 950 | $data_condition_value = ''; |
| 951 | |
| 952 | if ( ! empty( $field[ 'condition' ] ) ) { |
| 953 | $condition_field = key( $field[ 'condition' ] ); |
| 954 | $expected_value = current( $field[ 'condition' ] ); |
| 955 | $target_value = $this->get_field_value( $condition_field ); |
| 956 | |
| 957 | if ( $target_value != $expected_value ) { |
| 958 | $conditional_class = ' condition-hide'; |
| 959 | } |
| 960 | |
| 961 | $data_condition_field = $condition_field; |
| 962 | $data_condition_value = $expected_value; |
| 963 | } |
| 964 | |
| 965 | // Allow file types to be filterable |
| 966 | $default_types = '.jpg,.jpeg,.png,.pdf,.zip'; |
| 967 | $accepted_types = apply_filters( 'helpdocs_support_file_types', $default_types, $field ); |
| 968 | |
| 969 | $required = ! empty( $field[ 'required' ] ) ? ' required' : ''; |
| 970 | ?> |
| 971 | <div id="helpdocs_field_<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 972 | class="helpdocs-field helpdocs-field-file<?php echo esc_attr( $conditional_class ); ?>" |
| 973 | <?php if ( $data_condition_field && $data_condition_value ) : ?> |
| 974 | data-condition-field="<?php echo esc_attr( $data_condition_field ); ?>" |
| 975 | data-condition-value="<?php echo esc_attr( $data_condition_value ); ?>" |
| 976 | <?php endif; ?>> |
| 977 | <?php $this->render_field_label( $field, $field[ 'name' ] ); ?> |
| 978 | <input type="file" |
| 979 | id="helpdocs_<?php echo esc_attr( $field[ 'name' ] ); ?>" // Added prefix to ID |
| 980 | name="helpdocs_<?php echo esc_attr( $field[ 'name' ] ); ?>[]" |
| 981 | accept="<?php echo esc_attr( $accepted_types ); ?>" |
| 982 | <?php echo esc_attr( $required ); ?> |
| 983 | multiple> |
| 984 | </div> |
| 985 | <?php |
| 986 | } // End render_field_file() |
| 987 | |
| 988 | |
| 989 | /** |
| 990 | * Sanitize and process file uploads |
| 991 | * |
| 992 | * @param string $field_name The name of the file input |
| 993 | * @return array List of local file paths for wp_mail attachments |
| 994 | */ |
| 995 | public function sanitize_files( $field_name, $max_total_mb = 10 ) { |
| 996 | if ( empty( $_FILES[ "helpdocs_$field_name" ] ) ) { // phpcs:ignore |
| 997 | return []; |
| 998 | } |
| 999 | |
| 1000 | $files = $_FILES[ "helpdocs_$field_name" ]; // phpcs:ignore |
| 1001 | $attachments = []; |
| 1002 | $total_size = 0; |
| 1003 | $max_total_size = $max_total_mb * 1024 * 1024; // Convert MB to bytes |
| 1004 | |
| 1005 | if ( is_array( $files[ 'name' ] ) ) { |
| 1006 | // First Pass: Check total size |
| 1007 | foreach ( $files[ 'size' ] as $size ) { |
| 1008 | $total_size += $size; |
| 1009 | } |
| 1010 | |
| 1011 | if ( $total_size > $max_total_size ) { |
| 1012 | wp_send_json_error( sprintf( __( 'The total size of attachments exceeds %dMB.', 'admin-help-docs' ), $max_total_mb ) ); |
| 1013 | } |
| 1014 | |
| 1015 | // Second Pass: Process uploads |
| 1016 | foreach ( $files[ 'name' ] as $key => $value ) { |
| 1017 | if ( $files[ 'name' ][ $key ] ) { |
| 1018 | $file = [ |
| 1019 | 'name' => $files[ 'name' ][ $key ], |
| 1020 | 'type' => $files[ 'type' ][ $key ], |
| 1021 | 'tmp_name' => $files[ 'tmp_name' ][ $key ], |
| 1022 | 'error' => $files[ 'error' ][ $key ], |
| 1023 | 'size' => $files[ 'size' ][ $key ], |
| 1024 | ]; |
| 1025 | |
| 1026 | require_once( ABSPATH . 'wp-admin/includes/file.php' ); |
| 1027 | |
| 1028 | $upload_overrides = [ 'test_form' => false ]; |
| 1029 | $movefile = wp_handle_upload( $file, $upload_overrides ); |
| 1030 | |
| 1031 | if ( $movefile && ! isset( $movefile[ 'error' ] ) ) { |
| 1032 | $attachments[] = $movefile[ 'file' ]; |
| 1033 | } |
| 1034 | } |
| 1035 | } |
| 1036 | } |
| 1037 | |
| 1038 | return $attachments; |
| 1039 | } // End sanitize_files() |
| 1040 | |
| 1041 | |
| 1042 | /** |
| 1043 | * Render an API Key field with Generate, Copy, and Clear actions |
| 1044 | * * @param array $field The field definition array |
| 1045 | */ |
| 1046 | public function render_field_api_key( $field ) { |
| 1047 | $value = $this->get_field_value( $field[ 'name' ] ); |
| 1048 | |
| 1049 | if ( ( is_null( $value ) || $value === '' ) && isset( $field[ 'default' ] ) ) { |
| 1050 | $value = $field[ 'default' ]; |
| 1051 | } |
| 1052 | |
| 1053 | $value = $this->sanitize_field( $field, $value ); |
| 1054 | |
| 1055 | $conditional_class = ''; |
| 1056 | $data_condition_field = ''; |
| 1057 | $data_condition_value = ''; |
| 1058 | |
| 1059 | if ( ! empty( $field[ 'condition' ] ) ) { |
| 1060 | $condition_field = key( $field[ 'condition' ] ); |
| 1061 | $expected_value = current( $field[ 'condition' ] ); |
| 1062 | $target_value = $this->get_field_value( $condition_field ); |
| 1063 | |
| 1064 | if ( $target_value != $expected_value ) { |
| 1065 | $conditional_class = ' condition-hide'; |
| 1066 | } |
| 1067 | |
| 1068 | $data_condition_field = $condition_field; |
| 1069 | $data_condition_value = $expected_value; |
| 1070 | } |
| 1071 | ?> |
| 1072 | <div id="helpdocs_field_<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 1073 | class="helpdocs-field<?php echo esc_attr( $conditional_class ); ?>" |
| 1074 | <?php if ( $data_condition_field && $data_condition_value ) : ?> |
| 1075 | data-condition-field="<?php echo esc_attr( $data_condition_field ); ?>" |
| 1076 | data-condition-value="<?php echo esc_attr( $data_condition_value ); ?>" |
| 1077 | <?php endif; ?>> |
| 1078 | |
| 1079 | <?php $this->render_field_label( $field, $field[ 'name' ] ); ?> |
| 1080 | |
| 1081 | <div class="helpdocs-api-key-wrapper"> |
| 1082 | <div id="helpdocs_api_key_display" class="helpdocs-api-key-box <?php echo $value ? 'has-key' : 'no-key'; ?>"> |
| 1083 | <?php echo $value ? esc_html( $value ) : '<em>' . esc_html__( 'No API Key Generated', 'admin-help-docs' ) . '</em>'; ?> |
| 1084 | </div> |
| 1085 | |
| 1086 | <input type="hidden" |
| 1087 | id="<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 1088 | name="helpdocs_<?php echo esc_attr( $field[ 'name' ] ); ?>" |
| 1089 | value="<?php echo esc_attr( $value ); ?>"> |
| 1090 | |
| 1091 | <div class="helpdocs-api-key-actions"> |
| 1092 | <button type="button" class="helpdocs-button helpdocs-generate-api-key"><?php esc_html_e( 'Generate API Key', 'admin-help-docs' ); ?></button> |
| 1093 | <button type="button" class="helpdocs-button helpdocs-copy-api-key" <?php echo ! $value ? 'disabled' : ''; ?>><?php esc_html_e( 'Copy', 'admin-help-docs' ); ?></button> |
| 1094 | <button type="button" class="helpdocs-button helpdocs-clear-api-key" <?php echo ! $value ? 'disabled' : ''; ?>><?php esc_html_e( 'Clear', 'admin-help-docs' ); ?></button> |
| 1095 | </div> |
| 1096 | </div> |
| 1097 | </div> |
| 1098 | <?php |
| 1099 | } // End render_field_api_key() |
| 1100 | |
| 1101 | |
| 1102 | /** |
| 1103 | * Sanitize custom CSS input |
| 1104 | * |
| 1105 | * @param string $css The CSS input to sanitize |
| 1106 | * @return string The sanitized CSS |
| 1107 | */ |
| 1108 | public function sanitize_custom_css( $css ) { |
| 1109 | $css = wp_unslash( $css ); |
| 1110 | $css = wp_strip_all_tags( $css ); |
| 1111 | return $css; |
| 1112 | } // End sanitize_custom_css() |
| 1113 | |
| 1114 | |
| 1115 | /** |
| 1116 | * Render an HTML field (for custom content like buttons) |
| 1117 | * |
| 1118 | * @param array $field The field definition array, which should include a 'content' key with the HTML to render |
| 1119 | */ |
| 1120 | public function render_field_html( $field ) { |
| 1121 | ?> |
| 1122 | <div id="helpdocs_field_<?php echo esc_attr( $field[ 'name' ] ); ?>" class="helpdocs-field helpdocs-field-html"> |
| 1123 | <label> |
| 1124 | <?php echo wp_kses_post( $field[ 'label' ] ); ?> |
| 1125 | <?php if ( ! empty( $field[ 'desc' ] ) ) : ?> |
| 1126 | <span class="helpdocs-tooltip"> |
| 1127 | <span class="dashicons dashicons-editor-help"></span> |
| 1128 | <span class="helpdocs-tooltip-text"><?php echo wp_kses_post( $field[ 'desc' ] ); ?></span> |
| 1129 | </span> |
| 1130 | <?php endif; ?> |
| 1131 | </label> |
| 1132 | <div class="helpdocs-html-content"> |
| 1133 | <?php |
| 1134 | $allowed_html = wp_kses_allowed_html( 'post' ); |
| 1135 | |
| 1136 | $allowed_html[ 'button' ] = [ |
| 1137 | 'type' => true, |
| 1138 | 'id' => true, |
| 1139 | 'class' => true, |
| 1140 | ]; |
| 1141 | $allowed_html[ 'input' ] = [ |
| 1142 | 'type' => true, |
| 1143 | 'id' => true, |
| 1144 | 'name' => true, |
| 1145 | 'accept' => true, |
| 1146 | 'style' => true, |
| 1147 | 'value' => true, |
| 1148 | 'class' => true, |
| 1149 | ]; |
| 1150 | $allowed_html[ 'label' ][ 'for' ] = true; |
| 1151 | |
| 1152 | echo wp_kses( ( $field[ 'content' ] ?? '' ), $allowed_html ); |
| 1153 | ?> |
| 1154 | </div> |
| 1155 | </div> |
| 1156 | <?php |
| 1157 | } // End render_field_html() |
| 1158 | |
| 1159 | |
| 1160 | /** |
| 1161 | * AJAX handler to save settings |
| 1162 | */ |
| 1163 | public function ajax_save_settings() { |
| 1164 | check_ajax_referer( 'helpdocs_settings_nonce', 'nonce' ); |
| 1165 | if ( ! current_user_can( Helpers::admin_role() ) ) { |
| 1166 | wp_send_json_error( 'Insufficient permissions.' ); |
| 1167 | } |
| 1168 | |
| 1169 | $fields = self::setting_fields(); |
| 1170 | |
| 1171 | $colors_to_save = []; |
| 1172 | $errors = []; |
| 1173 | |
| 1174 | foreach ( $fields as $field ) { |
| 1175 | $name = $field[ 'name' ]; |
| 1176 | $post_key = 'helpdocs_' . $name; |
| 1177 | |
| 1178 | if ( strpos( $name, 'color_' ) === 0 ) { |
| 1179 | $key = str_replace( 'color_', '', $name ); |
| 1180 | if ( isset( $_POST[ 'settings' ][ $post_key ] ) ) { |
| 1181 | $raw_value = wp_unslash( $_POST[ 'settings' ][ $post_key ] ); // phpcs:ignore |
| 1182 | $colors_to_save[ $key ] = $this->sanitize_field( $field, $raw_value ); |
| 1183 | } |
| 1184 | continue; |
| 1185 | } |
| 1186 | |
| 1187 | if ( isset( $_POST[ 'settings' ][ $post_key ] ) ) { |
| 1188 | // Sanitize below based on field type, but first get the raw value for checkboxes |
| 1189 | $raw_value = wp_unslash( $_POST[ 'settings' ][ $post_key ] ); // phpcs:ignore |
| 1190 | } else { |
| 1191 | // If it's a checkbox group and nothing is checked, default to empty array |
| 1192 | if ( $field[ 'type' ] === 'checkboxes' ) { |
| 1193 | $raw_value = []; |
| 1194 | } elseif ( $field[ 'type' ] === 'checkbox' ) { |
| 1195 | $raw_value = 0; |
| 1196 | } else { |
| 1197 | continue; |
| 1198 | } |
| 1199 | } |
| 1200 | |
| 1201 | // Determine type and sanitize accordingly |
| 1202 | if ( $field[ 'type' ] === 'checkbox' ) { |
| 1203 | $value = $this->sanitize_checkbox( $raw_value ); |
| 1204 | } elseif ( $field[ 'type' ] === 'checkboxes' ) { |
| 1205 | $value = $this->sanitize_checkboxes( (array) $raw_value ); |
| 1206 | } elseif ( $field[ 'sanitize' ] === 'sanitize_custom_css' ) { |
| 1207 | $value = $this->sanitize_custom_css( $raw_value ); |
| 1208 | } else { |
| 1209 | $value = $this->sanitize_field( $field, $raw_value ); |
| 1210 | |
| 1211 | // If text field is empty, fall back to default |
| 1212 | if ( $field[ 'type' ] === 'text' && $value === '' && isset( $field[ 'default' ] ) ) { |
| 1213 | $value = $field[ 'default' ]; |
| 1214 | } |
| 1215 | } |
| 1216 | |
| 1217 | $updated = update_option( $post_key, $value ); |
| 1218 | if ( $updated === false && get_option( $post_key ) != $value ) { |
| 1219 | $errors[] = $name; |
| 1220 | } |
| 1221 | } |
| 1222 | |
| 1223 | // Before saving all colors, update the CSS variable storage if needed |
| 1224 | Colors::convert_color_storage(); |
| 1225 | |
| 1226 | if ( ! empty( $colors_to_save ) ) { |
| 1227 | $updated_colors = update_option( 'helpdocs_colors', $colors_to_save ); |
| 1228 | if ( $updated_colors === false && get_option( 'helpdocs_colors' ) !== $colors_to_save ) { |
| 1229 | $errors[] = 'colors'; |
| 1230 | } |
| 1231 | } |
| 1232 | |
| 1233 | // Flush the cache so changes show immediately |
| 1234 | Helpers::flush_permissions_cache(); |
| 1235 | |
| 1236 | if ( empty( $errors ) ) { |
| 1237 | wp_send_json_success(); |
| 1238 | } else { |
| 1239 | wp_send_json_error( 'Failed to save: ' . implode( ', ', $errors ) ); |
| 1240 | } |
| 1241 | } // End ajax_save_settings() |
| 1242 | |
| 1243 | |
| 1244 | /** |
| 1245 | * AJAX handler to flush system cache |
| 1246 | */ |
| 1247 | public function ajax_flush_cache() { |
| 1248 | check_ajax_referer( 'helpdocs_settings_nonce', 'nonce' ); |
| 1249 | if ( ! Helpers::user_can_edit() ) { |
| 1250 | wp_send_json_error( __( 'You do not have permission to perform this action.', 'admin-help-docs' ) ); |
| 1251 | } |
| 1252 | |
| 1253 | Helpers::flush_location_cache(); |
| 1254 | |
| 1255 | wp_send_json_success( __( 'Cache cleared successfully.', 'admin-help-docs' ) ); |
| 1256 | } // End ajax_flush_cache() |
| 1257 | |
| 1258 | |
| 1259 | /** |
| 1260 | * Prevent cloning and unserializing |
| 1261 | */ |
| 1262 | public function __clone() {} |
| 1263 | public function __wakeup() {} |
| 1264 | |
| 1265 | } |
| 1266 | |
| 1267 | |
| 1268 | Settings::instance(); |