500) return false; foreach ($input['term_topics'] as $id) if (!is_int($id) && !(is_string($id) && ctype_digit($id))) return false; } if (isset($input['term_new_topics'])) { if (!is_array($input['term_new_topics']) || count($input['term_new_topics']) > 10) return false; foreach ($input['term_new_topics'] as $name) if (!is_string($name) || strlen($name)>180) return false; } if (isset($input['term_sources'])) { if (!is_array($input['term_sources']) || count($input['term_sources'])>12) return false; foreach ($input['term_sources'] as $row) { if (!is_array($row) || array_diff(array_keys($row),['name','url','newtab','nofollow','sponsored','ugc'])) return false; foreach ($row as $value) if (!is_scalar($value) || strlen((string)$value)>2048) return false; } } return true; } /** Build a one-time restore snapshot from an explicitly supplied, shape-validated * request. This helper never reads PHP superglobals or authorizes requests. * Only the nonce/capability-gated admin-post handler may call the redirect writer. */ function mkb_glossar_editor_submission_snapshot($input,$message='') { if (!mkb_glossar_editor_submission_shape_valid($input)) return null; $new_topics = isset($input['term_new_topics']) && is_array($input['term_new_topics']) ? wp_unslash($input['term_new_topics']) : []; $source_rows = isset($input['term_sources']) && is_array($input['term_sources']) ? wp_unslash($input['term_sources']) : []; $form=[ 'term_id'=>absint($input['term_id']??0), 'error_message'=>sanitize_text_field($message), 'title'=>sanitize_text_field(wp_unslash($input['term_title']??'')), 'summary'=>sanitize_textarea_field(wp_unslash($input['term_summary']??'')), 'synonyms'=>sanitize_textarea_field(wp_unslash($input['term_synonyms']??'')), 'content'=>isset($input['term_content'])?wp_kses_post(wp_unslash($input['term_content'])):null, 'status'=>sanitize_key(wp_unslash($input['term_status']??'draft')), 'topics'=>array_values(array_filter(array_map('absint',(array)($input['term_topics']??[])))), 'new_topics'=>array_values(array_filter(array_map('sanitize_text_field', array_filter($new_topics,'is_string')))), 'editorial'=>[ 'mkb_glossar_sources_form'=>array_map(static function($row){return is_array($row)?array_map(static fn($v)=>is_scalar($v)?sanitize_text_field((string)$v):'',$row):[];},$source_rows), 'mkb_glossar_references'=>isset($input['term_references']) && is_scalar($input['term_references']) ? sanitize_textarea_field(wp_unslash((string)$input['term_references'])) : '', 'mkb_glossar_external_url'=>isset($input['term_external_url']) && is_scalar($input['term_external_url']) ? sanitize_text_field(wp_unslash((string)$input['term_external_url'])) : '', 'mkb_glossar_related_ids'=>isset($input['term_related_ids']) && is_scalar($input['term_related_ids']) ? sanitize_text_field(wp_unslash((string)$input['term_related_ids'])) : '', 'mkb_glossar_use_external'=>isset($input['term_use_external']) && (string)$input['term_use_external'] === '1' ? '1' : '0', 'mkb_glossar_external_newtab'=>isset($input['term_external_newtab']) && (string)$input['term_external_newtab'] === '1' ? '1' : '0', 'mkb_glossar_external_nofollow'=>isset($input['term_external_nofollow']) && (string)$input['term_external_nofollow'] === '1' ? '1' : '0', 'mkb_glossar_external_sponsored'=>isset($input['term_external_sponsored']) && (string)$input['term_external_sponsored'] === '1' ? '1' : '0', 'mkb_glossar_external_ugc'=>isset($input['term_external_ugc']) && (string)$input['term_external_ugc'] === '1' ? '1' : '0', 'mkb_glossar_auto_details'=>isset($input['term_auto_details']) && $input['term_auto_details'] === '0' ? '0' : '1', 'mkb_glossar_case_sensitive'=>isset($input['term_case_sensitive']) && (string)$input['term_case_sensitive'] === '1' ? '1' : '0' ] ]; return $form; } /** Store a rejected, already-authorized editor submission for a single redirect. * The caller must pass the request it validated after checking nonce/capability. */ function mkb_glossar_editor_preserve_submission($redirect,$error,$validated_input,$extra=[],$message='') { $form=mkb_glossar_editor_submission_snapshot($validated_input,$message); if (!is_array($form)) wp_die('Ungültige Eingabedaten.',400); $token=wp_generate_password(24,false,false); $key=mkb_glossar_editor_submission_key($token); if ($key) set_transient($key,$form,5*MINUTE_IN_SECONDS); wp_safe_redirect(add_query_arg(array_merge(['mkb_error'=>$error,'mkb_restore'=>$token],$extra),$redirect)); exit; } function mkb_glossar_editor_restore_submission($id,$token) { $key=mkb_glossar_editor_submission_key($token); if (!$key) return null; $form=get_transient($key); if (!is_array($form) || (int)($form['term_id']??-1)!==(int)$id) return null; delete_transient($key); return $form; } function mkb_glossar_editor_url($id=0) { return add_query_arg(array_filter(['page'=>'mkb-glossar-term-editor','term_id'=>absint($id)]),admin_url('admin.php')); } /** Keep translation context when Polylang/WPML send users to the native post editor. */ function mkb_glossar_editor_context($input) { $language=mkb_glossar_request_key($input,'mkb_lang'); if (!$language) $language=mkb_glossar_request_key($input,'new_lang'); if (!$language) $language=mkb_glossar_request_key($input,'lang'); $source = is_array($input) && array_key_exists('mkb_source',$input) ? mkb_glossar_request_id($input,'mkb_source') : mkb_glossar_request_id($input,'from_post'); if ($source && get_post_type($source)!=='mkb_glossar') $source=0; if ($language && !preg_match('/^[a-z][a-z0-9_-]{1,19}$/i',$language)) $language=''; return ['language'=>$language,'source'=>$source]; } function mkb_glossar_editor_languages() { if (mkb_glossar_polylang_enabled() && function_exists('pll_languages_list')) { $codes=pll_languages_list(['fields'=>'slug']); return is_array($codes)?array_values(array_filter($codes,'is_string')):[]; } if (mkb_glossar_wpml_enabled()) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Required third-party WPML/Elementor API hook name; changing it would break integration. $languages=apply_filters('wpml_active_languages',null,['skip_missing'=>0]); return is_array($languages)?array_keys($languages):[]; } return []; } function mkb_glossar_editor_language_valid($language) { return is_string($language) && $language!=='' && in_array($language,mkb_glossar_editor_languages(),true); } /** One effective language for the editor UI and its AJAX identity check. */ function mkb_glossar_editor_active_language($id=0,$context_language='') { $languages=mkb_glossar_editor_languages(); if (!$languages) return ''; $language=$id?mkb_glossar_post_language($id):$context_language; if (!$language && mkb_glossar_polylang_enabled() && function_exists('pll_default_language')) $language=pll_default_language('slug'); if (!$language) $language=mkb_glossar_current_language(); return mkb_glossar_editor_language_valid($language)?$language:$languages[0]; } function mkb_glossar_editor_language_label($language) { if (mkb_glossar_polylang_enabled() && function_exists('pll_the_languages')) { $items=pll_the_languages(['raw'=>1]); if (is_array($items) && isset($items[$language]['name'])) return $items[$language]['name']; } if (mkb_glossar_wpml_enabled()) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Required third-party WPML/Elementor API hook name; changing it would break integration. $items=apply_filters('wpml_active_languages',null,['skip_missing'=>0]); if (is_array($items) && isset($items[$language]['translated_name'])) return $items[$language]['translated_name']; } return strtoupper($language); } function mkb_glossar_editor_translations($id) { if (!$id) return []; if (mkb_glossar_polylang_enabled() && function_exists('pll_get_post_translations')) { $items=pll_get_post_translations($id); return is_array($items)?$items:[]; } if (mkb_glossar_wpml_enabled()) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Required third-party WPML/Elementor API hook name; changing it would break integration. $trid=apply_filters('wpml_element_trid',null,$id,'post_mkb_glossar'); if (!$trid) return []; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Required third-party WPML/Elementor API hook name; changing it would break integration. $items=apply_filters('wpml_get_element_translations',null,$trid,'post_mkb_glossar'); $out=[]; foreach ((array)$items as $code=>$item) if (isset($item->element_id)) $out[$code]=(int)$item->element_id; return $out; } return []; } function mkb_glossar_editor_translation_url($source,$language) { return add_query_arg(['mkb_lang'=>$language,'mkb_source'=>$source],mkb_glossar_editor_url()); } function mkb_glossar_editor_assign_translation($id,$language,$source=0) { if (!mkb_glossar_editor_language_valid($language)) return false; if (mkb_glossar_polylang_enabled() && function_exists('pll_set_post_language')) { pll_set_post_language($id,$language); if ($source && function_exists('pll_save_post_translations')) { $translations=mkb_glossar_editor_translations($source); if (!isset($translations[$language]) || (int)$translations[$language]===$id) { $translations[$language]=$id; pll_save_post_translations($translations); } } return true; } if (mkb_glossar_wpml_enabled()) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Required third-party WPML/Elementor API hook name; changing it would break integration. $trid=$source?apply_filters('wpml_element_trid',null,$source,'post_mkb_glossar'):null; $source_language=$source?mkb_glossar_post_language($source):null; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Required third-party WPML/Elementor API hook name; changing it would break integration. do_action('wpml_set_element_language_details',['element_id'=>$id,'element_type'=>'post_mkb_glossar','trid'=>$trid,'language_code'=>$language,'source_language_code'=>$source_language]); return true; } return false; } function mkb_glossar_editor_native_url($id) { return add_query_arg('mkb_native','1',get_edit_post_link($id,'raw')); } add_action('admin_menu',function(){ /* translators: %s is the configured singular name of an item (for example, Artikel). */ add_submenu_page('edit.php?post_type=mkb_glossar',sprintf(__('Create %s','mkb-glossar'),mkb_glossar_display_names()['item']),sprintf(__('Create %s','mkb-glossar'),mkb_glossar_display_names()['item']),'edit_posts','mkb-glossar-term-editor','mkb_glossar_render_term_editor'); },20); // The submenu action creates a term, but the browser tab must show the actual screen state. // Scope the title change to an existing Glossar item being edited in our custom editor. add_filter('admin_title', static function($admin_title, $title) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. if (mkb_glossar_request_key($_GET,'page')!=='mkb-glossar-term-editor') return $admin_title; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. $id=mkb_glossar_request_id($_GET,'term_id'); if (!$id || get_post_type($id)!=='mkb_glossar') return $admin_title; $edit_title=__('Edit Term','mkb-glossar'); if (str_starts_with((string)$admin_title,(string)$title)) { return $edit_title.substr((string)$admin_title,strlen((string)$title)); } return $edit_title.' – '.(string)$admin_title; }, 10, 2); // Put the primary create action immediately after the native "All terms" link. add_action('admin_menu',function(){ global $submenu; $parent='edit.php?post_type=mkb_glossar'; if (empty($submenu[$parent]) || !is_array($submenu[$parent])) return; $items=array_values($submenu[$parent]); $create=null; foreach ($items as $index=>$item) { if (($item[2]??'')==='mkb-glossar-term-editor') { $create=$item; unset($items[$index]); break; } } if ($create===null) return; $items=array_values($items); // The CPT "All terms" is at index zero. Keep it there. array_splice($items,1,0,[$create]); $submenu[$parent]=$items; },999); // Hide only the duplicate native Add New submenu; keep native edit URLs usable. add_action('admin_menu',function(){ remove_submenu_page('edit.php?post_type=mkb_glossar','post-new.php?post_type=mkb_glossar'); },99); // Keep the standard WP edit screen available through an explicit opt-in link. add_action('admin_init',function(){ global $pagenow; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. if (!in_array($pagenow,['post-new.php','post.php'],true) || isset($_GET['mkb_native'])) return; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. if ($pagenow==='post-new.php' && mkb_glossar_request_key($_GET,'post_type')==='mkb_glossar') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. if (current_user_can('edit_posts')) wp_safe_redirect(add_query_arg(array_filter(['mkb_lang'=>mkb_glossar_editor_context($_GET)['language'],'mkb_source'=>mkb_glossar_editor_context($_GET)['source']]),mkb_glossar_editor_url())); exit; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. if ($pagenow==='post.php' && mkb_glossar_request_key($_GET,'action')==='edit') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. $id=mkb_glossar_request_id($_GET,'post'); if ($id && get_post_type($id)==='mkb_glossar' && current_user_can('edit_post',$id)) { wp_safe_redirect(mkb_glossar_editor_url($id));exit; } } },20); add_action('admin_enqueue_scripts',function($hook){ // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. if ($hook!=='glossar_page_mkb-glossar-term-editor' && !(isset($_GET['page']) && $_GET['page']==='mkb-glossar-term-editor')) return; $editor_css_path = MKB_GLOSSAR_PATH . 'assets/admin-term-editor.css'; $editor_css_version = is_file($editor_css_path) ? (string) filemtime($editor_css_path) : MKB_GLOSSAR_VERSION; wp_enqueue_style('mkb-glossar-term-editor',MKB_GLOSSAR_URL.'assets/admin-term-editor.css',[],$editor_css_version); $editor_js_path = MKB_GLOSSAR_PATH . 'assets/admin-term-editor.js'; $editor_js_version = is_file($editor_js_path) ? (string)filemtime($editor_js_path) : MKB_GLOSSAR_VERSION; wp_enqueue_script('mkb-glossar-term-editor',MKB_GLOSSAR_URL.'assets/admin-term-editor.js',[],$editor_js_version,true); wp_add_inline_script('mkb-glossar-term-editor','window.mkbTermEditor='.wp_json_encode([ 'ajax'=>admin_url('admin-ajax.php'), 'nonce'=>wp_create_nonce('mkb_glossar_identity'), // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. 'postId'=>mkb_glossar_request_id($_GET,'term_id'), 'language'=>mkb_glossar_editor_active_language(mkb_glossar_request_id($_GET,'term_id'),mkb_glossar_editor_context($_GET)['language']) ]).';','before'); wp_add_inline_script('mkb-glossar-term-editor','window.mkbTermTopics='.wp_json_encode(['remove'=>__('Remove','mkb-glossar'), 'limit'=>__('Add no more than ten new topics at once.', 'mkb-glossar'), 'pending'=>__('The new topic is selected and will be created when you save.', 'mkb-glossar')]).';','before'); }); function mkb_glossar_render_term_editor(){ $names=mkb_glossar_display_names(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. $id=mkb_glossar_request_id($_GET,'term_id'); $post=$id?get_post($id):null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. $context=mkb_glossar_editor_context($_GET); $languages=mkb_glossar_editor_languages(); $active_language=mkb_glossar_editor_active_language($id,$context['language']); $source=(!$id && $context['source'])?$context['source']:0; $translations=$id?mkb_glossar_editor_translations($id):($source?mkb_glossar_editor_translations($source):[]); if ($source) $translations[mkb_glossar_post_language($source)]=$source; if ($id && (!$post || $post->post_type!=='mkb_glossar')) wp_die(esc_html__('The content could not be found.','mkb-glossar')); if (!current_user_can($id?'edit_post':'edit_posts',$id?:null)) wp_die(esc_html__('Keine Berechtigung.','mkb-glossar')); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. $error=mkb_glossar_request_key($_GET,'mkb_error'); $conflicts=[]; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. $conflict_id=mkb_glossar_request_id($_GET,'mkb_conflict'); if ($conflict_id) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. if ($conflict_id && get_post_type($conflict_id)==='mkb_glossar') $conflicts[]=['id'=>$conflict_id,'kind'=>mkb_glossar_request_key($_GET,'mkb_kind','title'),'label'=>get_the_title($conflict_id)]; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only GET/navigation parameter; no state is changed and input is validated separately. $restore_token=isset($_GET['mkb_restore']) && is_scalar($_GET['mkb_restore']) ? sanitize_text_field(wp_unslash((string)$_GET['mkb_restore'])) : ''; $restored=$restore_token!=='' ? mkb_glossar_editor_restore_submission($id,$restore_token) : null; $title=$post?$post->post_title:''; $summary=$id?get_post_meta($id,'mkb_glossar_summary',true):''; $synonyms=$id?get_post_meta($id,'mkb_glossar_synonyms',true):''; $content=$post?$post->post_content:''; $elementor_active=$id && get_post_meta($id,'_elementor_edit_mode',true)==='builder'; $blocked=($post && function_exists('has_blocks') && has_blocks($content)) || $elementor_active; $selected=$id?wp_get_object_terms($id,'mkb_glossar_topic',['fields'=>'ids']):[]; if (is_wp_error($selected)) $selected=[]; $current_status=$post?$post->post_status:'draft'; $restored_editorial=[]; $restored_new_topics=[]; $preview_url=$post?get_preview_post_link($post):''; $public_url=($post && $post->post_status==='publish')?get_permalink($post):''; if ($restored!==null) { $title=$restored['title']; $summary=$restored['summary']; $synonyms=$restored['synonyms']; if ($restored['content']!==null) $content=$restored['content']; $selected=$restored['topics']; $restored_new_topics=(array)($restored['new_topics']??[]); $restored_editorial=(array)($restored['editorial']??[]); if (in_array($restored['status'],['draft','publish','pending','private'],true)) $current_status=$restored['status']; } $topics=get_terms(['taxonomy'=>'mkb_glossar_topic','hide_empty'=>false]); if (is_wp_error($topics)) $topics=[]; if ($languages && mkb_glossar_polylang_enabled() && function_exists('pll_get_term_language')) $topics=array_values(array_filter($topics,function($topic)use($active_language){return pll_get_term_language($topic->term_id,'slug')===$active_language;})); ?>

:

'term_content','textarea_rows'=>12,'media_buttons'=>true,'teeny'=>false,'quicktags'=>true]); ?>

cap->manage_terms)): ?>

$language,'mkb_source'=>$source]),mkb_glossar_editor_url()); if (!$title) mkb_glossar_editor_preserve_submission($redirect,'missing',$validated_submission); $conflicts=mkb_glossar_identity_conflicts($title,$synonyms,$id,$language); if ($conflicts) { mkb_glossar_editor_preserve_submission($redirect,'duplicate',$validated_submission,['mkb_conflict'=>$conflicts[0]['id'],'mkb_kind'=>$conflicts[0]['kind']]); } $editorial=mkb_glossar_editorial_prepare($validated_submission,$id,$language); if (is_wp_error($editorial)) mkb_glossar_editor_preserve_submission($redirect,'editorial',$validated_submission,['mkb_detail'=>$editorial->get_error_code()],$editorial->get_error_message()); $fields=['post_type'=>'mkb_glossar','post_title'=>$title,'post_status'=>$status]; if ($id) $fields['ID']=$id; // Never overwrite Gutenberg block markup with an absent WYSIWYG field. if (isset($validated_submission['term_content']) && (!$id || (!has_blocks(get_post_field('post_content',$id)) && get_post_meta($id,'_elementor_edit_mode',true)!=='builder'))) $fields['post_content']=wp_kses_post(wp_unslash($validated_submission['term_content'])); $saved=wp_insert_post(wp_slash($fields),true); if (is_wp_error($saved)) mkb_glossar_editor_preserve_submission($redirect,'save',$validated_submission); if (!$id && mkb_glossar_editor_languages()) mkb_glossar_editor_assign_translation($saved,$language,$source); update_post_meta($saved,'mkb_glossar_summary',$summary); update_post_meta($saved,'mkb_glossar_synonyms',$synonyms); mkb_glossar_editorial_save($saved,$editorial); $topics=array_filter(array_map('absint',(array)($validated_submission['term_topics']??[]))); $new_topics=isset($validated_submission['term_new_topics']) && is_array($validated_submission['term_new_topics']) ? array_map('sanitize_text_field', wp_unslash($validated_submission['term_new_topics'])) : []; $taxonomy=get_taxonomy('mkb_glossar_topic'); if ($new_topics && $taxonomy && current_user_can($taxonomy->cap->manage_terms)) { // Names were unslashed and sanitized once above; do not strip literal backslashes again. foreach (array_unique($new_topics) as $name) { $name=trim($name); if ($name==='' || strlen($name)>100) continue; $existing=term_exists($name,'mkb_glossar_topic'); $result=$existing ?: wp_insert_term($name,'mkb_glossar_topic'); if (is_wp_error($result)) {set_transient('mkb_glossar_topic_notice_'.get_current_user_id(),$result->get_error_message(),60);continue;} $topic_id=(int)(is_array($result)?$result['term_id']:$result); if (!$topic_id) continue; if (!$existing && $language && mkb_glossar_polylang_enabled() && function_exists('pll_set_term_language')) pll_set_term_language($topic_id,$language); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Required third-party WPML/Elementor API hook name; changing it would break integration. if (!$existing && $language && mkb_glossar_wpml_enabled()) do_action('wpml_set_element_language_details',['element_id'=>$topic_id,'element_type'=>'tax_mkb_glossar_topic','trid'=>null,'language_code'=>$language,'source_language_code'=>null]); if ($language && mkb_glossar_polylang_enabled() && function_exists('pll_get_term_language') && pll_get_term_language($topic_id,'slug')!==$language) continue; $topics[]=$topic_id; } } $topics=array_values(array_filter($topics,function($topic)use($language){ if (!term_exists($topic,'mkb_glossar_topic')) return false; if (mkb_glossar_polylang_enabled() && function_exists('pll_get_term_language')) return pll_get_term_language($topic,'slug')===$language; return true; })); wp_set_object_terms($saved,$topics,'mkb_glossar_topic'); wp_safe_redirect(add_query_arg('mkb_saved','1',mkb_glossar_editor_url($saved)));exit; });