*/
/**
* Defaults in agency mode: everywhere else FeatherDash only changes the look
* (theme, accent, toolbar) until a setting is chosen. Dot path => value.
*/
private const AGENCY_DEFAULTS = [
'admin.footer.hide_version' => true,
'admin.menu.hide' => [
'customizer',
'widgets',
'writing',
'updates',
'wc_extensions',
'wc_separator'
],
'admin_bar.frontend.others' => false,
'network.menu.hide' => ['updates'],
'dashboard.show' => [],
'content.comments' => false,
'content.posts' => false,
'tables.users.add' => ['last_login', 'registration_date'],
'tables.users.track_last_login' => true,
'cleanup.admin_email_check' => true,
'frontend.emoji' => false,
'frontend.feeds' => false,
'frontend.head_links' => false,
'security.xmlrpc' => false,
'security.rest_users' => true,
'security.author_scan' => true,
'security.login_language' => true,
'security.login_logo_url' => true,
];
public static function defaults() : array {
$defaults = [
'admin' => [
'theme' => [
'enabled' => true,
'accent' => '#0fc0c0',
'accordion' => true,
'block_editor' => true,
],
'navigation' => [
'prefetch' => true,
'transitions' => true,
],
'footer' => [
'text' => ( Licensing::agency() ? 'Developed and maintained by the award winning agency.' : '' ),
'hide_version' => false,
],
'menu' => [
'hide' => [],
'labels' => [
'administration' => true,
'development' => true,
],
],
],
'admin_bar' => [
'show' => [
'site-name',
'edit',
'my-account',
'search'
],
'frontend' => [
'administrator' => true,
'others' => true,
],
'replace_howdy' => true,
],
'network' => [
'menu' => [
'hide' => [],
],
],
'dashboard' => [
'show' => self::fieldTypes()['dashboard.show']['options'],
],
'content' => [
'comments' => true,
'posts' => true,
],
'tables' => [
'users' => [
'add' => [],
'remove' => [],
'track_last_login' => false,
],
],
'cleanup' => [
'help_tabs' => false,
'screen_options' => false,
'update_nags' => false,
'admin_email_check' => false,
'toasts' => true,
'auto_updates_off' => Licensing::agency(),
'appearance' => [
'themes' => true,
'design' => true,
'customizer' => true,
'fonts' => true,
'menus' => true,
'widgets' => true,
'patterns' => true,
],
],
'frontend' => [
'emoji' => true,
'feeds' => true,
'head_links' => true,
'version_strings' => false,
'oembed' => true,
'dns_prefetch' => false,
'jquery' => [
'enabled' => true,
'migrate' => true,
],
],
'security' => [
'xmlrpc' => true,
'rest_users' => false,
'author_scan' => false,
'login_language' => false,
'login_logo_url' => false,
],
'login' => [
'enabled' => false,
'logo' => 0,
'background' => '#1e1e1e',
],
'maintenance' => [
'enabled' => false,
'title' => 'We\'ll be back soon',
'subtitle' => 'We\'re doing some scheduled maintenance.',
'body' => '',
'background' => '#1e1e1e',
'text_color' => '#ffffff',
],
'analytics' => [
'tracking' => [
'measurement_id' => '',
'exclude_editors' => true,
'consent_mode' => true,
'verification' => '',
'meta_pixel' => '',
],
],
'welcome' => [
'enabled' => false,
'title' => 'Welcome',
'logo' => 0,
'body' => '',
'button_label' => 'Read our knowledge base',
'button_url' => '',
'links' => '',
],
];
$agency = self::AGENCY_DEFAULTS;
// The agency's own client sites keep the opinionated set-up they were built with.
if ( Licensing::agency() ) {
foreach ( $agency as $path => $value ) {
$node =& $defaults;
foreach ( explode( '.', $path ) as $key ) {
$node =& $node[$key];
}
$node = $value;
unset($node);
}
}
return $defaults;
}
/**
* Field metadata (type and allowed option keys) used by the sanitizer.
*
* Keys absent from this map are inferred from their default value
* (bool => toggle, else text).
*
* @return array}>
*/
public static function fieldTypes() : array {
$types = [
'admin.theme.accent' => [
'type' => 'color',
],
'admin.footer.text' => [
'type' => 'textarea',
],
'admin.menu.hide' => [
'type' => 'checklist',
'options' => [
'customizer',
'widgets',
'writing',
'updates',
'wc_extensions',
'wc_separator'
],
],
'admin_bar.show' => [
'type' => 'checklist',
'options' => [
'wp-logo',
'site-name',
'site-editor',
'comments',
'new-content',
'customize',
'archive',
'edit',
'my-account',
'updates',
'search',
'widgets'
],
],
'network.menu.hide' => [
'type' => 'checklist',
'options' => ['updates'],
],
'dashboard.show' => [
'type' => 'checklist',
'options' => [
'wp_welcome_panel',
'dashboard_site_health',
'dashboard_right_now',
'dashboard_activity',
'dashboard_primary',
'dashboard_quick_press',
'network_dashboard_right_now',
'network_dashboard_wordpress_events_and_news'
],
],
'tables.users.add' => [
'type' => 'checklist',
'options' => ['last_login', 'registration_date'],
],
'tables.users.remove' => [
'type' => 'checklist',
'options' => [
'username',
'name',
'email',
'role',
'posts'
],
],
'analytics.tracking.measurement_id' => [
'type' => 'text',
'sanitize' => [\DigitalBrew\Featherdash\Modules\Front\Tracking::class, 'sanitizeMeasurementId'],
],
'analytics.tracking.verification' => [
'type' => 'text',
'sanitize' => [\DigitalBrew\Featherdash\Modules\Front\Tracking::class, 'sanitizeVerification'],
],
'analytics.tracking.meta_pixel' => [
'type' => 'text',
'sanitize' => [\DigitalBrew\Featherdash\Modules\Front\Tracking::class, 'sanitizePixelId'],
],
'login.logo' => [
'type' => 'image',
],
'login.background' => [
'type' => 'color',
],
'maintenance.body' => [
'type' => 'textarea',
],
'maintenance.background' => [
'type' => 'color',
],
'maintenance.text_color' => [
'type' => 'color',
],
'welcome.logo' => [
'type' => 'image',
],
'welcome.body' => [
'type' => 'textarea',
],
'welcome.button_url' => [
'type' => 'text',
'sanitize' => [self::class, 'sanitizeLink'],
],
'welcome.links' => [
'type' => 'textarea',
'sanitize' => [self::class, 'sanitizeLinks'],
],
];
return $types;
}
/**
* Tabs, cards and fields for the settings screen (translated).
*
* @return array>}>
*/
public static function sections() : array {
$sections = [
'appearance' => [
'icon' => 'dashicons-admin-appearance',
'label' => __( 'Appearance', 'featherdash' ),
'cards' => [[
'title' => __( 'Admin theme', 'featherdash' ),
'desc' => __( 'Applies the custom admin theme to wp-admin.', 'featherdash' ),
'fields' => [
[
'path' => 'admin.theme.enabled',
'type' => 'toggle',
'label' => __( 'Enable admin theme', 'featherdash' ),
'help' => __( 'Turns on the FeatherDash look for wp-admin: dark frame, rounded content card, restyled menu, buttons, forms and tables. Turn it off to get the standard WordPress admin back; every other setting keeps working.', 'featherdash' ),
],
[
'path' => 'admin.theme.accent',
'type' => 'color',
'label' => __( 'Accent colour', 'featherdash' ),
'help' => __( 'The highlight colour used for the current menu item, primary buttons, links, switches and focus rings.', 'featherdash' ),
'depends' => 'admin.theme.enabled',
],
[
'path' => 'admin.theme.accordion',
'type' => 'toggle',
'label' => __( 'Accordion submenus', 'featherdash' ),
'help' => __( 'Clicking a menu item that has a submenu opens it in place instead of loading its first page. Only one section stays open at a time.', 'featherdash' ),
'depends' => 'admin.theme.enabled',
],
[
'path' => 'admin.theme.block_editor',
'type' => 'toggle',
'label' => __( 'Accent colours in the block editor', 'featherdash' ),
'help' => __( 'Uses your accent colour for buttons and highlights inside the block editor. The editor\'s layout is never changed.', 'featherdash' ),
'depends' => 'admin.theme.enabled',
]
],
], [
'title' => __( 'Navigation', 'featherdash' ),
'fields' => [[
'path' => 'admin.navigation.prefetch',
'type' => 'toggle',
'label' => __( 'Load pages on hover', 'featherdash' ),
'help' => __( 'When you hover a link in the admin, the browser quietly fetches that page so it opens almost instantly when clicked. Links that perform an action (trash, delete, activate, log out, anything with a security nonce) and edit screens are never fetched early. Works in Chrome, Edge and other Chromium browsers.', 'featherdash' ),
], [
'path' => 'admin.navigation.transitions',
'type' => 'toggle',
'label' => __( 'Smooth page transitions', 'featherdash' ),
'help' => __( 'Fades between admin pages instead of flashing white, while the toolbar and sidebar stay in place. Supported by Chromium browsers and Safari; others load pages normally. Turned off automatically for people who prefer reduced motion.', 'featherdash' ),
]],
], [
'title' => __( 'Footer', 'featherdash' ),
'fields' => [[
'path' => 'admin.footer.text',
'type' => 'textarea',
'label' => __( 'Admin footer text', 'featherdash' ),
'help' => __( 'Text shown at the bottom of every admin page. Basic HTML such as links is allowed. Leave empty to keep WordPress\' own text.', 'featherdash' ),
], [
'path' => 'admin.footer.hide_version',
'type' => 'toggle',
'label' => __( 'Hide the WordPress version in the footer', 'featherdash' ),
'help' => __( 'Removes the WordPress version number from the bottom-right corner of the admin. When shown, it is always the installed version, never WordPress\' “Get Version …” update link.', 'featherdash' ),
]],
]],
],
'toolbar' => [
'icon' => 'dashicons-table-row-before',
'label' => __( 'Toolbar', 'featherdash' ),
'cards' => [[
'title' => __( 'Toolbar', 'featherdash' ),
'fields' => [
[
'path' => 'admin_bar.show',
'type' => 'checklist',
'label' => __( 'Visible core toolbar items', 'featherdash' ),
'help' => __( 'WordPress\' own toolbar items to show, in the admin and on the front end. Unticked items are removed. The mobile menu button and the Ctrl+K command palette always stay.', 'featherdash' ),
'options' => [
'wp-logo' => __( 'WordPress logo menu', 'featherdash' ),
'site-name' => __( 'Site name', 'featherdash' ),
'site-editor' => __( 'Edit site (block themes)', 'featherdash' ),
'comments' => __( 'Comments', 'featherdash' ),
'new-content' => __( 'New content', 'featherdash' ),
'customize' => __( 'Customize', 'featherdash' ),
'archive' => __( 'Archive / search', 'featherdash' ),
'edit' => __( 'Edit', 'featherdash' ),
'my-account' => __( 'My account', 'featherdash' ),
'updates' => __( 'Updates', 'featherdash' ),
'search' => __( 'Search', 'featherdash' ),
'widgets' => __( 'Widgets', 'featherdash' ),
],
],
[
'path' => 'admin_bar.frontend.administrator',
'type' => 'toggle',
'label' => __( 'Show toolbar on the front end for administrators', 'featherdash' ),
'help' => __( 'Shows the toolbar to administrators while they browse the website. Each user\'s own profile setting can still turn it off.', 'featherdash' ),
],
[
'path' => 'admin_bar.frontend.others',
'type' => 'toggle',
'label' => __( 'Show toolbar on the front end for other roles', 'featherdash' ),
'help' => __( 'Shows the toolbar on the website to logged-in users who are not administrators (editors, shop managers, customers…).', 'featherdash' ),
],
[
'path' => 'admin_bar.replace_howdy',
'type' => 'toggle',
'label' => __( 'Show display name instead of “Howdy, …”', 'featherdash' ),
'help' => __( 'Replaces the “Howdy, name” greeting in the top-right corner with just the user\'s display name.', 'featherdash' ),
]
],
], [
'title' => __( 'Network admin', 'featherdash' ),
'condition' => 'multisite',
'fields' => [[
'path' => 'network.menu.hide',
'type' => 'checklist',
'label' => __( 'Hidden network items', 'featherdash' ),
'help' => __( 'Ticked items are removed from the Network Admin menu on multisite installs.', 'featherdash' ),
'options' => [
'updates' => __( 'Dashboard → Updates', 'featherdash' ),
],
]],
]],
],
'dashboard' => [
'icon' => 'dashicons-dashboard',
'label' => __( 'Dashboard', 'featherdash' ),
'cards' => [[
'title' => __( 'Dashboard Widgets', 'featherdash' ),
'fields' => [[
'path' => 'dashboard.show',
'type' => 'checklist',
'label' => __( 'Visible core widgets', 'featherdash' ),
'help' => __( 'WordPress\' own Dashboard widgets to show, for everyone. Unticked widgets are removed. WordPress\' warnings about an outdated browser or PHP version always show.', 'featherdash' ),
'options' => [
'wp_welcome_panel' => __( 'Welcome panel', 'featherdash' ),
'dashboard_site_health' => __( 'Site Health', 'featherdash' ),
'dashboard_right_now' => __( 'At a Glance', 'featherdash' ),
'dashboard_activity' => __( 'Activity', 'featherdash' ),
'dashboard_primary' => __( 'WordPress Events and News', 'featherdash' ),
'dashboard_quick_press' => __( 'Quick Draft', 'featherdash' ),
'network_dashboard_right_now' => __( 'Network: Right Now', 'featherdash' ),
'network_dashboard_wordpress_events_and_news' => __( 'Network: Events and News', 'featherdash' ),
],
]],
], [
'title' => __( 'Welcome widget', 'featherdash' ),
'desc' => __( 'A dashboard widget you write once for your clients: your logo, a short message, a button to ask for help and useful links.', 'featherdash' ),
'fields' => [
[
'path' => 'welcome.enabled',
'type' => 'toggle',
'label' => __( 'Show the welcome widget', 'featherdash' ),
'help' => __( 'Adds the widget at the top of the Dashboard.', 'featherdash' ),
],
[
'path' => 'welcome.title',
'type' => 'text',
'label' => __( 'Title', 'featherdash' ),
'help' => __( 'The widget\'s heading, e.g. “Welcome to your website”.', 'featherdash' ),
'depends' => 'welcome.enabled',
],
[
'path' => 'welcome.logo',
'type' => 'image',
'label' => __( 'Logo', 'featherdash' ),
'help' => __( 'Your agency\'s logo, shown at the top of the widget.', 'featherdash' ),
'depends' => 'welcome.enabled',
],
[
'path' => 'welcome.body',
'type' => 'textarea',
'label' => __( 'Message', 'featherdash' ),
'help' => __( 'A few lines for your client: who to contact, opening hours, what\'s included. Basic HTML such as links and bold text is allowed.', 'featherdash' ),
'depends' => 'welcome.enabled',
],
[
'path' => 'welcome.button_label',
'type' => 'text',
'label' => __( 'Button label', 'featherdash' ),
'help' => __( 'Text of the main button, e.g. “Read our knowledge base”. Leave the link empty to hide the button.', 'featherdash' ),
'depends' => 'welcome.enabled',
],
[
'path' => 'welcome.button_url',
'type' => 'text',
'label' => __( 'Button link', 'featherdash' ),
'help' => __( 'A web address (e.g. your knowledge base or support form) or an email link such as mailto:support@example.com.', 'featherdash' ),
'depends' => 'welcome.enabled',
],
[
'path' => 'welcome.links',
'type' => 'textarea',
'label' => __( 'Links', 'featherdash' ),
'help' => __( 'One link per line as “Label | address”, e.g. “How to add a blog post | https://…”. Handy for how-to videos and guides.', 'featherdash' ),
'depends' => 'welcome.enabled',
]
],
]],
],
'content' => [
'icon' => 'dashicons-list-view',
'label' => __( 'Content & tables', 'featherdash' ),
'cards' => [[
'title' => __( 'Content', 'featherdash' ),
'fields' => [[
'path' => 'content.comments',
'type' => 'toggle',
'label' => __( 'Enable comments', 'featherdash' ),
'help' => __( 'When off, comments are switched off site-wide: comment forms close, existing comments are hidden, and the Comments menu, toolbar icon and dashboard box are removed.', 'featherdash' ),
], [
'path' => 'content.posts',
'type' => 'toggle',
'label' => __( 'Enable Posts', 'featherdash' ),
'help' => __( 'When off, the blog Posts section (with Categories and Tags) is hidden from the admin menu, toolbar and nav-menu screen. Existing posts stay in the database and still load on the front end.', 'featherdash' ),
]],
], [
'title' => __( 'Users table', 'featherdash' ),
'fields' => [[
'path' => 'tables.users.add',
'type' => 'checklist',
'label' => __( 'Add columns', 'featherdash' ),
'help' => __( 'Extra columns on the Users screen. “Last login” needs “Record last login time” to be on.', 'featherdash' ),
'options' => [
'last_login' => __( 'Last login', 'featherdash' ),
'registration_date' => __( 'Registration date', 'featherdash' ),
],
], [
'path' => 'tables.users.remove',
'type' => 'checklist',
'label' => __( 'Remove columns', 'featherdash' ),
'help' => __( 'Ticked columns are removed from the Users screen.', 'featherdash' ),
'options' => [
'username' => __( 'Username', 'featherdash' ),
'name' => __( 'Name', 'featherdash' ),
'email' => __( 'Email', 'featherdash' ),
'role' => __( 'Role', 'featherdash' ),
'posts' => __( 'Posts', 'featherdash' ),
],
], [
'path' => 'tables.users.track_last_login',
'type' => 'toggle',
'label' => __( 'Record last login time', 'featherdash' ),
'help' => __( 'Saves the date and time each user last logged in, so it can be shown in the Users list.', 'featherdash' ),
]],
]],
],
'cleanup' => [
'icon' => 'dashicons-hidden',
'label' => __( 'Admin clean-up', 'featherdash' ),
'cards' => [[
'title' => __( 'Admin clean-up', 'featherdash' ),
'fields' => [
[
'path' => 'cleanup.help_tabs',
'type' => 'toggle',
'label' => __( 'Hide Help tabs', 'featherdash' ),
'help' => __( 'Removes the “Help” drop-down tab from the top-right of admin screens.', 'featherdash' ),
],
[
'path' => 'cleanup.screen_options',
'type' => 'toggle',
'label' => __( 'Hide Screen Options', 'featherdash' ),
'help' => __( 'Removes the “Screen Options” drop-down tab, so users can\'t change columns or boxes shown on a screen.', 'featherdash' ),
],
[
'path' => 'cleanup.update_nags',
'type' => 'toggle',
'label' => __( 'Hide update notices for non-administrators', 'featherdash' ),
'help' => __( 'Hides “WordPress X is available” and maintenance notices from users who can\'t install updates anyway. Administrators still see them.', 'featherdash' ),
],
[
'path' => 'cleanup.admin_email_check',
'type' => 'toggle',
'label' => __( 'Disable the periodic admin email confirmation screen', 'featherdash' ),
'help' => __( 'Stops WordPress from periodically asking administrators to confirm the site\'s admin email address after logging in.', 'featherdash' ),
],
[
'path' => 'cleanup.toasts',
'type' => 'toggle',
'label' => __( 'Show “saved” messages as pop-ups', 'featherdash' ),
'help' => __( 'Success messages such as “Settings saved” or “Post updated” appear as a small pop-up in the top-right corner that disappears after a few seconds, instead of a box pushing the page down. Errors and warnings always stay on the page. Needs the admin theme.', 'featherdash' ),
'depends' => 'admin.theme.enabled',
]
],
], [
'title' => __( 'Updates', 'featherdash' ),
'condition' => 'not_agency',
'fields' => [[
'path' => 'cleanup.auto_updates_off',
'type' => 'toggle',
'label' => __( 'Turn off automatic updates', 'featherdash' ),
'help' => __( 'Stops WordPress from updating itself, plugins, themes and translations in the background, including security releases, and hides the “Enable auto-updates” links. Only turn this on if you update sites yourself (e.g. with Composer or a management tool).', 'featherdash' ),
]],
], [
'title' => __( 'Appearance menu', 'featherdash' ),
'desc' => __( 'Turn off Appearance screens a site doesn\'t need. A turned-off item is removed from the menu and the toolbar, and opening its screen goes to the Dashboard. Items a theme doesn\'t have (Menus and Widgets with block themes) don\'t appear anyway.', 'featherdash' ),
'fields' => [
[
'path' => 'cleanup.appearance.themes',
'type' => 'toggle',
'label' => __( 'Themes', 'featherdash' ),
'help' => __( 'Appearance → Themes: browse, install and switch themes.', 'featherdash' ),
],
[
'path' => 'cleanup.appearance.design',
'type' => 'toggle',
'label' => __( 'Design', 'featherdash' ),
'help' => __( 'The Site Editor (Appearance → Editor) for block themes: templates, styles and navigation.', 'featherdash' ),
],
[
'path' => 'cleanup.appearance.customizer',
'type' => 'toggle',
'label' => __( 'Customizer', 'featherdash' ),
'help' => __( 'Appearance → Customize, the live theme options screen, and the Customizer itself.', 'featherdash' ),
],
[
'path' => 'cleanup.appearance.fonts',
'type' => 'toggle',
'label' => __( 'Fonts', 'featherdash' ),
'help' => __( 'Appearance → Fonts, the font library.', 'featherdash' ),
],
[
'path' => 'cleanup.appearance.menus',
'type' => 'toggle',
'label' => __( 'Menus', 'featherdash' ),
'help' => __( 'Appearance → Menus (classic themes).', 'featherdash' ),
],
[
'path' => 'cleanup.appearance.widgets',
'type' => 'toggle',
'label' => __( 'Widgets', 'featherdash' ),
'help' => __( 'Appearance → Widgets (classic themes).', 'featherdash' ),
],
[
'path' => 'cleanup.appearance.patterns',
'type' => 'toggle',
'label' => __( 'Patterns', 'featherdash' ),
'help' => __( 'The Patterns screen, for reusable blocks and synced patterns.', 'featherdash' ),
]
],
]],
],
'frontend' => [
'icon' => 'dashicons-desktop',
'label' => __( 'Front end', 'featherdash' ),
'cards' => [[
'title' => __( 'Head clean-up', 'featherdash' ),
'fields' => [
[
'path' => 'frontend.emoji',
'type' => 'toggle',
'label' => __( 'Load emoji scripts', 'featherdash' ),
'help' => __( 'When off, removes WordPress\' emoji detection script and styles from every page. Modern browsers show emoji without them.', 'featherdash' ),
],
[
'path' => 'frontend.feeds',
'type' => 'toggle',
'label' => __( 'Enable RSS feeds', 'featherdash' ),
'help' => __( 'When off, the RSS/Atom feed links are removed from the page head and any feed URL redirects to the homepage.', 'featherdash' ),
],
[
'path' => 'frontend.head_links',
'type' => 'toggle',
'label' => __( 'Keep generator/RSD/WLW/shortlink/REST/oEmbed links in ', 'featherdash' ),
'help' => __( 'When off, removes extra tags from the page head: the WordPress generator/version, RSD and Windows Live Writer links, the shortlink, REST API and oEmbed discovery links.', 'featherdash' ),
],
[
'path' => 'frontend.version_strings',
'type' => 'toggle',
'label' => __( 'Strip ?ver= from asset URLs', 'featherdash' ),
'help' => __( 'Removes the “?ver=” WordPress version number from CSS and JS URLs on the website, so the version isn\'t advertised. Plugin and theme versions are kept.', 'featherdash' ),
],
[
'path' => 'frontend.oembed',
'type' => 'toggle',
'label' => __( 'Enable oEmbed JS', 'featherdash' ),
'help' => __( 'When off, removes the script that lets other WordPress sites embed your posts. Embedding YouTube, X and similar links in your own content still works.', 'featherdash' ),
],
[
'path' => 'frontend.dns_prefetch',
'type' => 'toggle',
'label' => __( 'Remove DNS-prefetch hints', 'featherdash' ),
'help' => __( 'Removes WordPress\' DNS-prefetch hint for its emoji image server from the page head.', 'featherdash' ),
]
],
], [
'title' => __( 'jQuery', 'featherdash' ),
'fields' => [[
'path' => 'frontend.jquery.enabled',
'type' => 'toggle',
'label' => __( 'Load jQuery on the front end', 'featherdash' ),
'help' => __( 'Loads jQuery on the website. Turn off only if your theme and plugins don\'t need it; turning it off can break sliders, forms and similar features.', 'featherdash' ),
], [
'path' => 'frontend.jquery.migrate',
'type' => 'toggle',
'label' => __( 'Load jQuery Migrate', 'featherdash' ),
'help' => __( 'Loads jQuery Migrate, which keeps old jQuery code working. Most modern themes don\'t need it.', 'featherdash' ),
'depends' => 'frontend.jquery.enabled',
]],
]],
],
'security' => [
'icon' => 'dashicons-shield',
'label' => __( 'Security', 'featherdash' ),
'cards' => [[
'title' => __( 'Security', 'featherdash' ),
'fields' => [[
'path' => 'security.xmlrpc',
'type' => 'toggle',
'label' => __( 'Enable XML-RPC', 'featherdash' ),
'help' => __( 'XML-RPC is an old remote-publishing API that bots often use for password guessing. Keep it off unless something (such as the Jetpack plugin or an old mobile app) needs it.', 'featherdash' ),
], [
'path' => 'security.rest_users',
'type' => 'toggle',
'label' => __( 'Hide REST /wp/v2/users from logged-out visitors', 'featherdash' ),
'help' => __( 'Stops logged-out visitors from listing your users (and their login names) through /wp-json/wp/v2/users.', 'featherdash' ),
], [
'path' => 'security.author_scan',
'type' => 'toggle',
'label' => __( 'Block ?author=N enumeration', 'featherdash' ),
'help' => __( 'Redirects “?author=1” style URLs to the homepage so bots can\'t discover usernames. Author archive pages keep working.', 'featherdash' ),
]],
]],
],
'login' => [
'icon' => 'dashicons-lock',
'label' => __( 'Login', 'featherdash' ),
'cards' => [[
'title' => __( 'Login screen', 'featherdash' ),
'desc' => __( 'Brand the wp-login.php screen. Buttons and focus rings use the accent colour from Appearance.', 'featherdash' ),
'fields' => [[
'path' => 'login.enabled',
'type' => 'toggle',
'label' => __( 'Customise the login screen', 'featherdash' ),
'help' => __( 'Applies the settings below to the login, lost password and registration screens: Inter font, rounded form, your logo, background and accent colour. Turn it off to get the standard WordPress login screen back.', 'featherdash' ),
], [
'path' => 'login.logo',
'type' => 'image',
'label' => __( 'Logo', 'featherdash' ),
'help' => __( 'Replaces the WordPress logo above the form. It is scaled to fit 320 × 120 px. Leave empty to keep the WordPress logo.', 'featherdash' ),
], [
'path' => 'login.background',
'type' => 'color',
'label' => __( 'Background colour', 'featherdash' ),
'help' => __( 'Colour behind the form. The links below the form switch between light and dark text to stay readable.', 'featherdash' ),
]],
'footer' => [\DigitalBrew\Featherdash\Modules\Login::class, 'renderPreviewLink'],
], [
'title' => __( 'Login options', 'featherdash' ),
'fields' => [[
'path' => 'security.login_language',
'type' => 'toggle',
'label' => __( 'Hide language switcher on login screen', 'featherdash' ),
'help' => __( 'Removes the language drop-down below the login form.', 'featherdash' ),
], [
'path' => 'security.login_logo_url',
'type' => 'toggle',
'label' => __( 'Login logo links to this site', 'featherdash' ),
'help' => __( 'Makes the logo on the login screen link to your website instead of wordpress.org, with your site name as its text.', 'featherdash' ),
]],
]],
],
'maintenance' => [
'icon' => 'dashicons-hammer',
'label' => __( 'Maintenance', 'featherdash' ),
'cards' => [[
'title' => __( 'Maintenance mode', 'featherdash' ),
'desc' => __( 'Show visitors a holding page while you work on the site. You can preview the page before turning it on.', 'featherdash' ),
'fields' => [
[
'path' => 'maintenance.enabled',
'type' => 'toggle',
'label' => __( 'Turn on maintenance mode', 'featherdash' ),
'help' => __( 'Logged-out visitors (and logged-in users who can\'t edit content, such as customers) see the maintenance page instead of the site. It is sent with a 503 status and a Retry-After header, so search engines come back later instead of dropping your pages. Anyone who can edit posts still sees the site, with a “Maintenance mode” badge in the toolbar. The admin, login screen, REST API and cron keep working.', 'featherdash' ),
],
[
'path' => 'maintenance.title',
'type' => 'text',
'label' => __( 'Title', 'featherdash' ),
'help' => __( 'The main heading of the page, also used as the browser tab title.', 'featherdash' ),
],
[
'path' => 'maintenance.subtitle',
'type' => 'text',
'label' => __( 'Subtitle', 'featherdash' ),
'help' => __( 'A short line under the title.', 'featherdash' ),
],
[
'path' => 'maintenance.body',
'type' => 'textarea',
'label' => __( 'Body', 'featherdash' ),
'help' => __( 'Optional longer text under the subtitle, for example when you expect to be back or how to get in touch. Line breaks are kept and basic HTML such as links is allowed.', 'featherdash' ),
],
[
'path' => 'maintenance.background',
'type' => 'color',
'label' => __( 'Background colour', 'featherdash' ),
'help' => __( 'Page background. With a background image it is laid over the image as a tint, which keeps the text readable.', 'featherdash' ),
],
[
'path' => 'maintenance.text_color',
'type' => 'color',
'label' => __( 'Text colour', 'featherdash' ),
'help' => __( 'Colour of the title, subtitle and body. Pick one that contrasts with the background.', 'featherdash' ),
]
],
'footer' => [\DigitalBrew\Featherdash\Modules\Maintenance::class, 'renderPreviewLink'],
]],
],
'analytics' => [
'icon' => 'dashicons-chart-area',
'label' => __( 'Analytics', 'featherdash' ),
'cards' => [[
'title' => __( 'Tracking', 'featherdash' ),
'desc' => __( 'Adds Google\'s and Meta\'s tags to the website. Works on its own: the reporting connection above isn\'t needed for it.', 'featherdash' ),
'fields' => [
[
'path' => 'analytics.tracking.measurement_id',
'type' => 'text',
'label' => __( 'GA4 Measurement ID', 'featherdash' ),
'help' => __( 'The G-… ID from Google Analytics → Admin → Data streams → your web stream. When set, the Google tag (gtag.js) is added to every page of the website and starts sending visits to that property. Leave empty if another plugin (MonsterInsights, Site Kit) or Google Tag Manager already adds it.', 'featherdash' ),
],
[
'path' => 'analytics.tracking.exclude_editors',
'type' => 'toggle',
'label' => __( 'Don\'t track logged-in editors and admins', 'featherdash' ),
'help' => __( 'Leaves the tag out for anyone who can edit content, so your own visits don\'t count.', 'featherdash' ),
],
[
'path' => 'analytics.tracking.consent_mode',
'type' => 'toggle',
'label' => __( 'Wait for cookie consent (Consent Mode)', 'featherdash' ),
'help' => __( 'Starts Google\'s Consent Mode with analytics and ads storage denied, and holds the Meta Pixel back, so nothing is stored until visitors accept cookies. Cookie banners such as Complianz, Cookiebot or CookieAdmin then grant consent (the Meta Pixel waits for marketing consent). Only turn off if the site has no cookie banner and doesn\'t need one.', 'featherdash' ),
],
[
'path' => 'analytics.tracking.verification',
'type' => 'text',
'label' => __( 'Search Console verification', 'featherdash' ),
'help' => __( 'In Google Search Console choose the “HTML tag” method and paste the tag (or just the code in content="…"). It is added to the homepage head, so Search Console can verify the site.', 'featherdash' ),
],
[
'path' => 'analytics.tracking.meta_pixel',
'type' => 'text',
'label' => __( 'Meta Pixel ID', 'featherdash' ),
'help' => __( 'The Pixel (dataset) ID from Meta Events Manager → Data sources, a number like 123456789012345. When set, the Meta Pixel is added to every page and sends page views. Uses the two settings above: editors aren\'t tracked, and with cookie consent on it waits for marketing consent. Leave empty if another plugin (Meta for WooCommerce, PixelYourSite) already adds it.', 'featherdash' ),
]
],
]],
],
'site' => [
'icon' => 'dashicons-id',
'label' => __( 'Site identity', 'featherdash' ),
'cards' => [[
'title' => __( 'Favicon', 'featherdash' ),
'fields' => [[
'path' => 'site.favicon',
'type' => 'image',
'name' => \DigitalBrew\Featherdash\Modules\Front\Favicon::FIELD,
'value' => [\DigitalBrew\Featherdash\Modules\Front\Favicon::class, 'current'],
'label' => __( 'Favicon', 'featherdash' ),
'help' => __( 'The icon shown in browser tabs, bookmarks and on phone home screens, on the website, in the admin and on the login screen. This is WordPress\' own Site Icon, the same one as in Appearance → Customize → Site Identity: changing it here changes it there too. Use a square image of at least 512 × 512 px.', 'featherdash' ),
]],
]],
],
];
return $sections;
}
/**
* Roles, for `keys` fields.
*
* @return array
*/
public static function roleOptions() : array {
$options = [];
foreach ( wp_roles()->get_names() as $role => $name ) {
$options[(string) $role] = [
'label' => translate_user_role( (string) $name ),
'plugin' => '',
'active' => true,
];
}
return $options;
}
/**
* A web address or a mailto:/tel: link.
*/
public static function sanitizeLink( mixed $value ) : string {
return esc_url_raw( trim( ( is_string( $value ) ? $value : '' ) ), [
'http',
'https',
'mailto',
'tel'
] );
}
/**
* "Label | address" lines; lines without a valid address are dropped.
*/
public static function sanitizeLinks( mixed $value ) : string {
$lines = [];
foreach ( ( preg_split( '/\\R/', ( is_string( $value ) ? $value : '' ) ) ?: [] ) as $line ) {
[$label, $url] = array_pad( array_map( 'trim', explode( '|', $line, 2 ) ), 2, '' );
$url = self::sanitizeLink( $url );
if ( $label !== '' && $url !== '' ) {
$lines[] = sanitize_text_field( $label ) . ' | ' . $url;
}
}
return implode( "\n", $lines );
}
/**
* Parsed "Label | address" lines.
*
* @return array
*/
public static function links( string $value ) : array {
$links = [];
foreach ( ( preg_split( '/\\R/', $value ) ?: [] ) as $line ) {
[$label, $url] = array_pad( array_map( 'trim', explode( '|', $line, 2 ) ), 2, '' );
if ( $label !== '' && $url !== '' ) {
$links[] = [
'label' => $label,
'url' => $url,
];
}
}
return $links;
}
}