405]);
}
if (!current_user_can('manage_options')) wp_die(esc_html__('You do not have permission to access this page.', 'mkb-glossar'));
check_admin_referer('mkb_glossar_setup_' . $action);
// Only the two names and primary URL base are accepted after nonce validation.
// Other wizard actions need no POST body and never unslash unknown data.
if ($action !== 'names') return [];
return [
'mkb_collection' => isset($_POST['mkb_collection']) && is_string($_POST['mkb_collection'])
? sanitize_text_field(wp_unslash($_POST['mkb_collection'])) : null,
'mkb_item' => isset($_POST['mkb_item']) && is_string($_POST['mkb_item'])
? sanitize_text_field(wp_unslash($_POST['mkb_item'])) : null,
'mkb_base' => isset($_POST['mkb_base']) && is_string($_POST['mkb_base'])
? sanitize_text_field(wp_unslash($_POST['mkb_base'])) : null,
];
}
add_action('admin_post_mkb_glossar_setup_names', static function () {
$request = mkb_glossar_setup_guard('names');
// mkb_glossar_setup_guard() has already unslashed and sanitized both names.
$raw_collection = $request['mkb_collection'] ?? null;
$raw_item = $request['mkb_item'] ?? null;
$base = mkb_glossar_url_base_candidate($request['mkb_base'] ?? null);
if (!is_string($raw_collection) || !is_string($raw_item)) {
wp_safe_redirect(mkb_glossar_setup_url(['setup_error'=>'names'])); exit;
}
$collection = trim($raw_collection);
$item = trim($raw_item);
if ($collection === '' || $item === '' || preg_match_all('/./us', $collection) > 80 || preg_match_all('/./us', $item) > 80) {
wp_safe_redirect(mkb_glossar_setup_url(['setup_error'=>'names'])); exit;
}
if ($base === '') {
wp_safe_redirect(mkb_glossar_setup_url(['setup_error'=>'url'])); exit;
}
$old_base = (string)mkb_glossar_opts()['slug'];
if (($base !== $old_base || mkb_glossar_setup_state() === 'pending') && mkb_glossar_url_base_problem($base) !== null) {
wp_safe_redirect(mkb_glossar_setup_url(['setup_error'=>'conflict', 'setup_base'=>$base])); exit;
}
// Preserve every unrelated setting, especially language URL bases and Elementor templates.
$saved = get_option('mkb_glossar_options', []);
if (!is_array($saved)) $saved = [];
$saved['label'] = $collection;
$saved['singular'] = $item;
$saved['slug'] = $base;
// update_option() executes the registered sanitize_callback, which checks
// all three values again and leaves unrelated options unchanged.
$saved['_section'] = 'setup_names';
update_option('mkb_glossar_options', $saved);
$confirmed = get_option('mkb_glossar_options', []);
if (!is_array($confirmed) || ($confirmed['label'] ?? null) !== $collection
|| ($confirmed['singular'] ?? null) !== $item || ($confirmed['slug'] ?? null) !== $base) {
wp_safe_redirect(mkb_glossar_setup_url(['setup_error'=>'save'])); exit;
}
update_option('mkb_glossar_setup_state', 'in_progress', false);
delete_option('mkb_glossar_setup_redirect');
wp_safe_redirect(mkb_glossar_setup_url(['setup_step'=>1,'setup_saved'=>1])); exit;
});
add_action('admin_post_mkb_glossar_setup_skip', static function () {
mkb_glossar_setup_guard('skip');
update_option('mkb_glossar_setup_state', 'skipped', false);
delete_option('mkb_glossar_setup_redirect');
wp_safe_redirect(admin_url('edit.php?post_type=mkb_glossar&page=mkb-glossar&tab=general&setup_skipped=1')); exit;
});
add_action('admin_post_mkb_glossar_setup_complete', static function () {
mkb_glossar_setup_guard('complete');
// First step must have been explicitly confirmed before completion.
if (mkb_glossar_setup_state() !== 'in_progress') {
wp_safe_redirect(mkb_glossar_setup_url()); exit;
}
update_option('mkb_glossar_setup_state', 'completed', false);
wp_safe_redirect(admin_url('edit.php?post_type=mkb_glossar&page=mkb-glossar&tab=general&setup_completed=1')); exit;
});
add_action('admin_post_mkb_glossar_setup_restart', static function () {
mkb_glossar_setup_guard('restart');
// Restart only the guided process. Existing posts, URLs, and settings are unchanged.
update_option('mkb_glossar_setup_state', 'pending', false);
delete_option('mkb_glossar_setup_redirect');
wp_safe_redirect(mkb_glossar_setup_url()); exit;
});
/** Trusted MKB stroke icons; inline SVG belongs inside the actual action element. */
function mkb_glossar_setup_icon($name) {
$paths = [
'book' => '',
'plus' => '',
'pages' => '',
'link' => '',
'palette' => '',
'copy' => '',
'left' => '',
'right' => '',
'settings' => '',
];
if (!isset($paths[$name])) return '';
return '';
}
function mkb_glossar_setup_page() {
if (!current_user_can('manage_options')) wp_die(esc_html__('You do not have permission to access this page.', 'mkb-glossar'));
$options = mkb_glossar_opts();
$names = mkb_glossar_display_names($options);
$state = mkb_glossar_setup_state();
$ready = $state !== 'pending';
$counts = wp_count_posts('mkb_glossar');
$published = (int)($counts->publish ?? 0);
$pages = get_posts(['post_type'=>'page', 'post_status'=>'publish', 'posts_per_page'=>1, 's'=>$names['collection'], 'fields'=>'ids']);
$steps = [
[
'title'=>__('Choose names and web address', 'mkb-glossar'),
'description'=>__('Choose names for the WordPress interface and a separate public URL base. You may use matching or different names.', 'mkb-glossar'),
'reason'=>__('The names identify your content in WordPress. The URL base determines where its public addresses begin. Existing URLs are never changed without your action.', 'mkb-glossar'),
'icon'=>'settings',
],
[
/* translators: %s is the configured singular name of an item (for example, Artikel). */
'title'=>sprintf(__('Create %s', 'mkb-glossar'), $names['item']),
'description'=>__('Start with a clear title and a short explanation. Add synonyms and a longer description only when they help visitors.', 'mkb-glossar'),
'reason'=>__('Why this matters: good explanations are the foundation for searches, tooltips and internal glossary links.', 'mkb-glossar'),
/* translators: %d is the number of published glossary entries. */
'status'=>$published ? sprintf(__('Published content: %d', 'mkb-glossar'), $published) : __('No published content yet', 'mkb-glossar'),
'primary'=>__('Manage content', 'mkb-glossar'),
'url'=>admin_url('edit.php?post_type=mkb_glossar'),
/* translators: %s is the configured singular name of an item (for example, Artikel). */
'secondary'=>sprintf(__('Create %s', 'mkb-glossar'), $names['item']),
'secondary_url'=>admin_url('post-new.php?post_type=mkb_glossar'),
'icon'=>'book', 'primary_icon'=>'pages', 'secondary_icon'=>'plus',
],
[
/* translators: %s is the site-configured collection or item name (for example, Wiki). */
'title'=>sprintf(__('Overview: %s', 'mkb-glossar'), $names['collection']),
'description'=>mkb_glossar_elementor_available()
? __('Create a page in Elementor with the MKB Glossary widget. Alternatively use a Gutenberg Shortcode block or the shortcode below. Search and alphabet navigation appear automatically.', 'mkb-glossar')
: __('Create a WordPress page with a Shortcode block and paste the code below. Search and alphabet navigation appear automatically.', 'mkb-glossar'),
'reason'=>__('Why this matters: visitors need a clear place where they can browse your published content.', 'mkb-glossar'),
'status'=>$pages ? __('A matching page title exists; check its contents', 'mkb-glossar') : __('Create a glossary page', 'mkb-glossar'),
'primary'=>__('Open pages', 'mkb-glossar'),
'url'=>admin_url('edit.php?post_type=page'),
'secondary'=>__('Add a page', 'mkb-glossar'),
'secondary_url'=>admin_url('post-new.php?post_type=page'),
'code'=>'[mkb_glossar show_topics="true"]',
'icon'=>'pages', 'primary_icon'=>'pages', 'secondary_icon'=>'plus',
],
[
'title'=>__('Choose where automatic links appear', 'mkb-glossar'),
'description'=>__('Automatic links are optional. You choose which articles or pages receive links, how often they appear and where they are excluded.', 'mkb-glossar'),
'reason'=>__('Why this matters: helpful links should support reading, not interrupt it. Nothing is linked automatically until you enable it.', 'mkb-glossar'),
'status'=>!empty($options['auto_link']) ? __('Automatic linking is enabled', 'mkb-glossar') : __('Automatic linking is currently off', 'mkb-glossar'),
'primary'=>__('Open linking settings', 'mkb-glossar'),
'url'=>admin_url('edit.php?post_type=mkb_glossar&page=mkb-glossar&tab=linking'),
'icon'=>'link', 'primary_icon'=>'link',
],
[
'title'=>__('Adjust the appearance and check visibility', 'mkb-glossar'),
'description'=>mkb_glossar_elementor_available()
? __('Style sources and related content with the built-in controls or one shared Elementor Free template, then check the published result.', 'mkb-glossar')
: __('Style sources and related content with the built-in controls, then check the published result.', 'mkb-glossar'),
'reason'=>__('Consistent presentation helps visitors recognize sources and related content. Structured information helps search engines understand published explanations.', 'mkb-glossar'),
'status'=>!empty($options['schema_enabled']) ? __('DefinedTerm data is enabled for published terms', 'mkb-glossar') : __('DefinedTerm data is currently off', 'mkb-glossar'),
'primary'=>__('Open appearance and SEO', 'mkb-glossar'),
'url'=>admin_url('edit.php?post_type=mkb_glossar&page=mkb-glossar&tab=presentation'),
'secondary'=>__('Read the handbook', 'mkb-glossar'),
'secondary_url'=>function_exists('mkb_glossar_help_url') ? mkb_glossar_help_url('display-step-5') : admin_url('edit.php?post_type=mkb_glossar'),
'icon'=>'palette', 'primary_icon'=>'palette', 'secondary_icon'=>'book',
],
];
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately.
$initial = isset($_GET['setup_step']) && is_scalar($_GET['setup_step']) ? absint($_GET['setup_step']) : 0;
if (!$ready) $initial = 0;
$initial = min($initial, count($steps)-1);
echo '
';
echo ''.esc_html__('GETTING STARTED', 'mkb-glossar').'';
/* translators: %s is the site-configured collection or item name (for example, Wiki). */
echo '
'.esc_html($ready
? __('Your names are set. Continue with the remaining steps at your own pace or finish setup when ready.', 'mkb-glossar')
: __('Start by choosing the names shown on your website. You can keep the defaults, complete the remaining steps at your own pace, or skip setup entirely.', 'mkb-glossar')).'
';
echo '';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately.
if (isset($_GET['setup_error'])) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately.
$error = is_string($_GET['setup_error']) ? sanitize_key($_GET['setup_error']) : '';
if ($error === 'save') $message = __('Your settings could not be saved. No changes were confirmed.', 'mkb-glossar');
elseif ($error === 'url') $message = __('Enter a valid URL base using letters, numbers and hyphens.', 'mkb-glossar');
elseif ($error === 'conflict') {
// Read-only GET feedback, bounded by the same scalar slug validator.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET value for escaped notice; all writes use a separate nonce-checked POST action.
$attempt = isset($_GET['setup_base']) && is_string($_GET['setup_base'])
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only attempted URL base is validated, not saved.
? mkb_glossar_url_base_candidate(sanitize_text_field(wp_unslash($_GET['setup_base']))) : '';
$message = $attempt ? (mkb_glossar_url_base_problem($attempt)
?: __('This URL base conflicts with an existing WordPress address. Choose another and save again.', 'mkb-glossar'))
: __('This URL base conflicts with an existing WordPress address. Choose another and save again.', 'mkb-glossar');
}
else $message = __('Enter both names (up to 80 characters each) to continue.', 'mkb-glossar');
echo '
'.esc_html($message).'
';
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately.
if (isset($_GET['setup_saved'])) echo '
'.esc_html__('Names and URL base saved. You can continue with the next steps.', 'mkb-glossar').'
';
echo '';
foreach ($steps as $i=>$step) {
echo '';
/* translators: %1$d is the current setup step number, %2$d is the total number of steps. */
echo ''.esc_html(sprintf(__('Step %1$d of %2$d', 'mkb-glossar'),$i+1,count($steps))).'';
echo '
'.esc_html($step['title']).'
';
echo '
'.esc_html($step['description']).'
';
echo wp_kses(mkb_glossar_info_box($step['reason'], 'mkb-setup-reason'), mkb_glossar_allowed_visual_markup());
if ($i===0) {
echo '';
} else {
if (isset($step['code'])) {
echo '