PluginProbe
BlockSpare – Gutenberg Blocks, AI Content Generator & Site Builder for News, Magazine & Blogs / 5.0.1
BlockSpare – Gutenberg Blocks, AI Content Generator & Site Builder for News, Magazine & Blogs v5.0.1
5.0.1 4.1.0 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.1.0 1.1.1 1.1.2 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 2.1.2 2.5.0 2.5.3 All 39 releases
blockspare / inc / ai / class-blockspare-ai-settings.php

class-blockspare-ai-settings.php in BlockSpare – Gutenberg Blocks, AI Content Generator & Site Builder for News, Magazine & Blogs 5.0.1, at inc/ai/class-blockspare-ai-settings.php

903 lines 23.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (! defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Handles the "AI" settings screen: default prompts, admin menu,
9 * settings fields, and sanitization.
10 */
11 class Blockspare_AI_Settings
12 {
13
14 /**
15 * Default AI settings.
16 */
17 private $default_settings = array(
18
19 'generate_prompt' => 'Write a high-quality blog post about: {prompt}',
20
21 // Max words the "Content Generation" tool should produce.
22 'generate_word_limit' => 75,
23
24 'summarize_prompt' => 'Summarize the following WordPress blog post.
25
26 Requirements:
27 - Identify the main topic.
28 - Include the most important points.
29 - Remove unnecessary repetition.
30 - Keep important facts and details.
31 - Do not invent or add information.
32 - Make the summary concise and easy to understand.
33 - Return clean HTML.
34 - Do not use Markdown.
35 - Do not wrap the response in a code block.
36
37 Original blog post:
38
39 {post_content}',
40
41 // Max words the "Summarization" tool should produce.
42 'summarize_word_limit' => 25,
43
44 'qa_prompt' => 'Read the following WordPress blog post.
45
46 Generate {qa_count} of the most useful questions and answers that a reader
47 would want to know after reading this article.
48
49 Rules:
50 - Generate exactly {qa_count} questions and answers.
51 - Questions must be directly related to the article.
52 - Answers must be based ONLY on the article.
53 - Do not invent information.
54 - Do not add external information.
55 - Avoid duplicate or very similar questions.
56 - Questions should be clear and useful.
57 - Answers should be concise but informative.
58 - Return ONLY valid JSON.
59 - Do not use Markdown.
60 - Do not use ``` code blocks.
61
62 Return exactly this JSON structure:
63
64 {
65 "items": [
66 {
67 "question": "Question 1",
68 "answer": "Answer 1"
69 }
70 ]
71 }
72
73 Article:
74
75 {post_content}',
76
77 // How many Q&A pairs to generate (max 5).
78 'qa_count' => 2,
79 );
80
81
82 public function __construct()
83 {
84
85 $this->default_settings['language'] = $this->blockspare_get_default_language();
86 add_action('admin_enqueue_scripts', array($this, 'blockspare_enqueue_editor_assets'));
87 add_action('admin_menu', array($this, 'blockspare_ai_settings_menu'));
88 add_action('admin_init', array($this, 'blockspare_ai_settings_init'));
89 }
90
91
92 public static function blockspare_get_language_fallback_map()
93 {
94 return array(
95 'af' => 'Afrikaans',
96 'ar' => 'Arabic',
97 'bg_BG' => 'Bulgarian',
98 'ca' => 'Catalan',
99 'cs_CZ' => 'Czech',
100 'da_DK' => 'Danish',
101 'de_DE' => 'German',
102 'de_CH' => 'German (Switzerland)',
103 'de_AT' => 'German (Austria)',
104 'el' => 'Greek',
105 'en_GB' => 'English (UK)',
106 'es_ES' => 'Spanish (Spain)',
107 'es_MX' => 'Spanish (Mexico)',
108 'fa_IR' => 'Persian',
109 'fi' => 'Finnish',
110 'fr_FR' => 'French (France)',
111 'fr_CA' => 'French (Canada)',
112 'he_IL' => 'Hebrew',
113 'hi_IN' => 'Hindi',
114 'hu_HU' => 'Hungarian',
115 'id_ID' => 'Indonesian',
116 'it_IT' => 'Italian',
117 'ja' => 'Japanese',
118 'ko_KR' => 'Korean',
119 'ne_NP' => 'Nepali',
120 'nl_NL' => 'Dutch',
121 'nb_NO' => 'Norwegian (Bokmål)',
122 'pl_PL' => 'Polish',
123 'pt_BR' => 'Portuguese (Brazil)',
124 'pt_PT' => 'Portuguese (Portugal)',
125 'ro_RO' => 'Romanian',
126 'ru_RU' => 'Russian',
127 'sk_SK' => 'Slovak',
128 'sv_SE' => 'Swedish',
129 'th' => 'Thai',
130 'tr_TR' => 'Turkish',
131 'uk' => 'Ukrainian',
132 'vi' => 'Vietnamese',
133 'zh_CN' => 'Chinese (China)',
134 'zh_TW' => 'Chinese (Taiwan)',
135 );
136 }
137
138 public function blockspare_enqueue_editor_assets($hook_suffix)
139 {
140 $expected_hook = get_plugin_page_hookname('blockspare-ai', 'blockspare');
141
142 if ($hook_suffix !== $expected_hook) {
143 return;
144 }
145 remove_all_actions('admin_notices');
146 remove_all_actions('all_admin_notices');
147 $manual_deps = array(
148 'wp-element',
149 'wp-i18n',
150 );
151
152 wp_enqueue_script(
153 'blockspare-ai-generator-dashboard',
154 BLOCKSPARE_PLUGIN_URL . 'dist/aidashboardSettings.js',
155 $manual_deps,
156 '1.0.0'
157 );
158 }
159
160 public function blockspare_get_default_language()
161 {
162 $locale = get_locale();
163
164 if (empty($locale) || 'en_US' === $locale) {
165 return 'English';
166 }
167
168 $fallback = self::blockspare_get_language_fallback_map();
169
170 return ! empty($fallback[$locale]) ? $fallback[$locale] : 'English';
171 }
172
173
174 /**
175 * Add AI settings menu.
176 */
177 public function blockspare_ai_settings_menu()
178 {
179 $badge_count = __('New', 'blockspare'); // Dynamic count variable
180 add_submenu_page(
181 'blockspare',
182 __('AI Settings', 'blockspare'),
183 sprintf(
184 __('AI Settings %s', 'blockspare'),
185 '<span class="update-plugins count-' . esc_attr($badge_count) . '"><span class="plugin-count">' . esc_html($badge_count) . '</span></span>'
186 ),
187 'manage_options',
188 'blockspare-ai',
189 array($this, 'blockspare_ai_settings_page'),
190 1
191 );
192
193 add_submenu_page(
194 'blockspare-ai',
195 __('AI Settings', 'blockspare'),
196 __('AI Settings', 'blockspare'),
197 'manage_options',
198 'blockspare-ai',
199 array($this, 'blockspare_ai_settings_page')
200 );
201 }
202
203
204 /**
205 * Register AI settings.
206 */
207 public function blockspare_ai_settings_init()
208 {
209 register_setting(
210 'blockspare_ai_settings_group',
211 'blockspare_ai_settings',
212 array(
213 'sanitize_callback' => array(
214 $this,
215 'blockspare_sanitize_ai_settings',
216 ),
217 )
218 );
219
220 /**
221 * General Settings.
222 */
223 add_settings_section(
224 'blockspare_ai_general_section',
225 __('General Settings', 'blockspare'),
226 array(
227 $this,
228 'blockspare_ai_general_section_callback'
229 ),
230 'blockspare-ai-general'
231 );
232
233 add_settings_field(
234 'language',
235 __('Default Language', 'blockspare'),
236 array($this, 'blockspare_language_field'),
237 'blockspare-ai-general',
238 'blockspare_ai_general_section'
239 );
240
241 add_settings_field(
242 'ai_model_selection',
243 __('AI Model', 'blockspare'),
244 array($this, 'blockspare_ai_model_field'),
245 'blockspare-ai-general',
246 'blockspare_ai_general_section'
247 );
248
249
250 /**
251 * Content Generation.
252 */
253 add_settings_section(
254 'blockspare_ai_generation_section',
255 __('AI Content Generation', 'blockspare'),
256 function () {
257 echo '<div class="notice notice-warning inline" style="margin: 0 0 15px 0;"><p>';
258 esc_html_e(
259 'Customizing the Content Generation prompt and word limit is a Pro feature. These fields are read-only in the free version.',
260 'blockspare'
261 );
262 echo '</p></div>';
263 echo '<p>';
264 esc_html_e(
265 'Customize the prompt used when generating new content.',
266 'blockspare'
267 );
268 echo '</p>';
269 },
270 'blockspare-ai-generation'
271 );
272
273 add_settings_field(
274 'generate_prompt',
275 __('Generation Prompt', 'blockspare'),
276 array($this, 'blockspare_generate_prompt_field'),
277 'blockspare-ai-generation',
278 'blockspare_ai_generation_section'
279 );
280
281 add_settings_field(
282 'generate_word_limit',
283 __('Word Limit', 'blockspare'),
284 array($this, 'blockspare_generate_word_limit_field'),
285 'blockspare-ai-generation',
286 'blockspare_ai_generation_section'
287 );
288
289
290 /**
291 * Summarization.
292 */
293 add_settings_section(
294 'blockspare_ai_summary_section',
295 __('AI Summarization', 'blockspare'),
296 function () {
297 echo '<div class="notice notice-warning inline" style="margin: 0 0 15px 0;"><p>';
298 esc_html_e(
299 'Customizing the Summarization prompt and word limit is a Pro feature. These fields are read-only in the free version.',
300 'blockspare'
301 );
302 echo '</p></div>';
303 echo '<p>';
304 esc_html_e(
305 'Customize the prompt used when summarizing existing content.',
306 'blockspare'
307 );
308 echo '</p>';
309 },
310 'blockspare-ai-summary'
311 );
312
313 add_settings_field(
314 'summarize_prompt',
315 __('Summarization Prompt', 'blockspare'),
316 array($this, 'blockspare_summarize_prompt_field'),
317 'blockspare-ai-summary',
318 'blockspare_ai_summary_section'
319 );
320
321 add_settings_field(
322 'summarize_word_limit',
323 __('Word Limit', 'blockspare'),
324 array($this, 'blockspare_summarize_word_limit_field'),
325 'blockspare-ai-summary',
326 'blockspare_ai_summary_section'
327 );
328
329
330 /**
331 * Helpful Q&A.
332 */
333 add_settings_section(
334 'blockspare_ai_qa_section',
335 __('AI Key Q&A', 'blockspare'),
336 function () {
337 echo '<div class="notice notice-warning inline" style="margin: 0 0 15px 0;"><p>';
338 esc_html_e(
339 'Customizing Helpful Q&A prompt and question count is a Pro feature. These fields are read-only in the free version.',
340 'blockspare'
341 );
342 echo '</p></div>';
343 echo '<p>';
344 esc_html_e(
345 'Customize the prompt used to generate helpful questions and answers.',
346 'blockspare'
347 );
348 echo '</p>';
349 },
350 'blockspare-ai-qa'
351 );
352
353 add_settings_field(
354 'qa_prompt',
355 __('Helpful Q&A Prompt', 'blockspare'),
356 array($this, 'blockspare_qa_prompt_field'),
357 'blockspare-ai-qa',
358 'blockspare_ai_qa_section'
359 );
360
361 add_settings_field(
362 'qa_count',
363 __('Number of Questions', 'blockspare'),
364 array($this, 'blockspare_qa_count_field'),
365 'blockspare-ai-qa',
366 'blockspare_ai_qa_section'
367 );
368 }
369
370
371 /**
372 * Get AI settings with defaults.
373 */
374 public function get_ai_settings()
375 {
376 $settings = get_option('blockspare_ai_settings', array());
377
378 if (! is_array($settings)) {
379 $settings = array();
380 }
381
382 return wp_parse_args(
383 $settings,
384 $this->default_settings
385 );
386 }
387
388
389 private function blockspare_get_language_list()
390 {
391 $languages = array(
392 'en_US' => __('English', 'blockspare'),
393 );
394
395 $languages = array_merge($languages, $this->blockspare_get_language_fallback_map());
396
397 if (! function_exists('wp_get_available_translations')) {
398 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
399 }
400
401 $translations = wp_get_available_translations();
402
403 if (is_array($translations)) {
404 foreach ($translations as $locale => $translation) {
405 if (! isset($languages[$locale]) && ! empty($translation['english_name'])) {
406 $languages[$locale] = $translation['english_name'];
407 }
408 }
409 }
410
411 asort($languages);
412
413 return $languages;
414 }
415
416 public function blockspare_ai_general_section_callback()
417 {
418
419 $generation_url = add_query_arg(
420 array(
421 'page' => 'blockspare-ai',
422 'tab' => 'generation',
423 ),
424 admin_url('admin.php')
425 );
426
427 $summarization_url = add_query_arg(
428 array(
429 'page' => 'blockspare-ai',
430 'tab' => 'summary',
431 ),
432 admin_url('admin.php')
433 );
434
435 $qa_url = add_query_arg(
436 array(
437 'page' => 'blockspare-ai',
438 'tab' => 'qa',
439 ),
440 admin_url('admin.php')
441 ); ?>
442 <div class="blockspare-ai-tabs" style="display: flex; gap: 20px;">
443
444 <a href="admin.php?page=blockspare-ai&tab=generation"
445 class="active"
446 style="display: flex; flex-direction: row; gap:15px">
447 <span>
448 <img src="<?php echo esc_url(BLOCKSPARE_PLUGIN_URL . 'assets/icons/generator.svg'); ?>" />
449 </span>
450 <span>
451 <h3>AI Content Generation</h3>
452 <span style="font-size: 12px; font-weight: 400; color: #646970;">
453 Create new content with AI using your preferred provider and model. Simply describe what you want to generate, and let AI help you create engaging, relevant, and high-quality content.
454 </span>
455 </span>
456 </a>
457
458 <a href="admin.php?page=blockspare-ai&tab=summary"
459 style="display: flex; flex-direction: row; gap:15px">
460 <span>
461 <img src="<?php echo esc_url(BLOCKSPARE_PLUGIN_URL . 'assets/icons/summary-check.svg'); ?>" />
462 </span>
463 <span>
464 <h3>AI Summarization</h3>
465 <span style="font-size: 12px; font-weight: 400; color: #646970;">
466 Use AI to quickly summarize your content and extract the most important points, making long articles and posts easier to understand and review.
467 </span>
468 </span>
469 </a>
470
471 <a href="admin.php?page=blockspare-ai&tab=qa"
472 style="display: flex; flex-direction: row; gap:15px">
473 <span>
474 <img src="<?php echo esc_url(BLOCKSPARE_PLUGIN_URL . 'assets/icons/comments-question.svg'); ?>" />
475 </span>
476 <span>
477 <h3>AI Key Q&A</h3>
478 <span style="font-size: 12px; font-weight: 400; color: #646970;">
479 Automatically identify relevant questions and answers from your content with AI. Highlight the most useful information to make your content easier to understand and navigate.
480 </span>
481 </span>
482 </a>
483
484 </div>
485
486 <p>
487 Use your AI connectors to generate content, summarize posts, and find useful questions and answers.
488 </p>
489
490 <p style="margin-top: 15px;">
491 <a href="options-connectors.php" class="button button-primary">
492 Configure Connectors
493 </a>
494 </p>
495
496 <?php }
497
498
499 /**
500 * Default language field.
501 */
502 public function blockspare_language_field()
503 {
504 //$settings = $this->get_ai_settings();
505 $language = $this->blockspare_get_default_language();
506 $languages = $this->blockspare_get_language_list();
507
508 ?>
509
510 <select
511 name="blockspare_ai_settings[language]"
512 class="regular-text"
513 disabled>
514
515 <?php foreach ($languages as $locale => $label) : ?>
516 <option value="<?php echo esc_attr($label); ?>" <?php selected($language, $label); ?>>
517 <?php echo esc_html($label); ?>
518 </option>
519 <?php endforeach; ?>
520
521 </select>
522
523 <p class="description">
524 <?php esc_html_e(
525 'The selected language will be used by the AI content tools. Default: Site Langauge.',
526 'blockspare'
527 ); ?>
528 </p>
529
530 <?php
531 }
532
533 /**
534 * AI Model selection dropdown field.
535 */
536 public function blockspare_ai_model_field()
537 {
538 $settings = $this->get_ai_settings();
539 $current_prov = isset($settings['ai_provider']) ? $settings['ai_provider'] : '';
540 $current_model = isset($settings['ai_model']) ? $settings['ai_model'] : '';
541
542 $options = array('' => __('Select Model', 'blockspare'));
543
544 if (class_exists('WordPress\AiClient\AiClient')) {
545 try {
546 $registry = \WordPress\AiClient\AiClient::defaultRegistry();
547
548 foreach ($registry->getRegisteredProviderIds() as $provider_id) {
549
550 if (!$registry->isProviderConfigured($provider_id)) {
551 continue;
552 }
553
554 $provider_class = $registry->getProviderClassName($provider_id);
555 $provider_metadata = $provider_class::metadata();
556 $provider_label = $provider_metadata->getName();
557
558 $models = $provider_class::modelMetadataDirectory()->listModelMetadata();
559
560 foreach ($models as $model) {
561
562 $supports_text = false;
563 foreach ($model->getSupportedCapabilities() as $capability) {
564 if ($capability->isTextGeneration()) {
565 $supports_text = true;
566 break;
567 }
568 }
569
570 if (!$supports_text) {
571 continue;
572 }
573
574 $m_id = $model->getId();
575 $m_name = $model->getName();
576
577 $combined_key = $provider_id . '|' . $m_id;
578 $options[$combined_key] = sprintf('%s - %s', $provider_label, $m_name);
579 }
580 }
581 } catch (\Throwable $e) {
582 // Log errors if necessary
583 }
584 }
585
586 $selected_value = (! empty($current_prov) && ! empty($current_model))
587 ? $current_prov . '|' . $current_model
588 : '';
589
590 echo '<select name="blockspare_ai_settings[ai_model_selection]" class="regular-text">';
591 foreach ($options as $val => $label) {
592 printf(
593 '<option value="%s" %s>%s</option>',
594 esc_attr($val),
595 selected($selected_value, $val, false),
596 esc_html($label)
597 );
598 }
599 echo '</select>';
600 printf(
601 '<p class="description">%s</p>',
602 esc_html__(
603 'The selected model will be used by the AI content tools.',
604 'blockspare'
605 )
606 );
607 }
608
609
610 /**
611 * Content generation prompt field (DISABLED).
612 */
613 public function blockspare_generate_prompt_field()
614 {
615 $settings = $this->get_ai_settings();
616
617 ?>
618
619 <textarea
620 name="blockspare_ai_settings[generate_prompt]"
621 rows="10"
622 class="large-text"
623 disabled><?php echo esc_textarea($settings['generate_prompt']); ?></textarea>
624
625 <p class="description">
626 <?php esc_html_e(
627 'Available placeholders: {prompt}, {language}',
628 'blockspare'
629 ); ?>
630 </p>
631
632 <?php
633 }
634
635
636 /**
637 * Content generation word limit field (DISABLED).
638 */
639 public function blockspare_generate_word_limit_field()
640 {
641 $settings = $this->get_ai_settings();
642 $limit = (int) $settings['generate_word_limit'];
643
644 ?>
645
646 <input
647 type="number"
648 name="blockspare_ai_settings[generate_word_limit]"
649 value="<?php echo esc_attr($limit); ?>"
650 min="1"
651 max="5000"
652 step="1"
653 class="small-text"
654 disabled />
655
656 <p class="description">
657 <?php esc_html_e(
658 'Maximum number of words to generate. Default: 75.',
659 'blockspare'
660 ); ?>
661 </p>
662
663 <?php
664 }
665
666
667 /**
668 * Summarization prompt field (DISABLED).
669 */
670 public function blockspare_summarize_prompt_field()
671 {
672 $settings = $this->get_ai_settings();
673
674 ?>
675
676 <textarea
677 name="blockspare_ai_settings[summarize_prompt]"
678 rows="15"
679 class="large-text"
680 disabled><?php echo esc_textarea($settings['summarize_prompt']); ?></textarea>
681
682 <p class="description">
683 <?php esc_html_e(
684 'Available placeholders: {language}, {post_content}',
685 'blockspare'
686 ); ?>
687 </p>
688
689 <?php
690 }
691
692
693 /**
694 * Summarization word limit field (DISABLED).
695 */
696 public function blockspare_summarize_word_limit_field()
697 {
698 $settings = $this->get_ai_settings();
699 $limit = (int) $settings['summarize_word_limit'];
700
701 ?>
702
703 <input
704 type="number"
705 name="blockspare_ai_settings[summarize_word_limit]"
706 value="<?php echo esc_attr($limit); ?>"
707 min="1"
708 max="5000"
709 step="1"
710 class="small-text"
711 disabled />
712
713 <p class="description">
714 <?php esc_html_e(
715 'Maximum number of words to generate for the summary. Default: 25.',
716 'blockspare'
717 ); ?>
718 </p>
719
720 <?php
721 }
722
723
724 /**
725 * Helpful Q&A prompt field (DISABLED).
726 */
727 public function blockspare_qa_prompt_field()
728 {
729 $settings = $this->get_ai_settings();
730
731 ?>
732
733 <textarea
734 name="blockspare_ai_settings[qa_prompt]"
735 rows="25"
736 class="large-text"
737 disabled><?php echo esc_textarea($settings['qa_prompt']); ?></textarea>
738
739 <p class="description">
740 <?php esc_html_e(
741 'Available placeholders: {language}, {post_content}, {qa_count}',
742 'blockspare'
743 ); ?>
744 </p>
745
746 <?php
747 }
748
749
750 /**
751 * Helpful Q&A count field (DISABLED).
752 */
753 public function blockspare_qa_count_field()
754 {
755 $settings = $this->get_ai_settings();
756 $count = (int) $settings['qa_count'];
757
758 ?>
759
760 <select
761 name="blockspare_ai_settings[qa_count]"
762 class="regular-text"
763 disabled>
764
765 <?php for ($i = 1; $i <= 5; $i++) : ?>
766 <option value="<?php echo esc_attr($i); ?>" <?php selected($count, $i); ?>>
767 <?php echo esc_html($i); ?>
768 </option>
769 <?php endfor; ?>
770
771 </select>
772
773 <p class="description">
774 <?php esc_html_e(
775 'Number of questions and answers to generate (max 5). Default: 3.',
776 'blockspare'
777 ); ?>
778 </p>
779
780 <?php
781 }
782
783
784 /**
785 * AI settings page rendering.
786 */
787 public function blockspare_ai_settings_page()
788 {
789 if (! current_user_can('manage_options')) {
790
791 echo '<div class="notice notice-error inline">';
792 echo '<p>' . esc_html__('You do not have permission to access this page.', 'blockspare') . '</p>';
793 echo '</div>';
794
795 return;
796 }
797
798 $tabs = array(
799 'general' => __('General', 'blockspare'),
800 'generation' => __('Content Generation', 'blockspare'),
801 'summary' => __('Summarization', 'blockspare'),
802 'qa' => __('Helpful Q&A', 'blockspare'),
803 );
804
805 $active_tab = 'general';
806
807 if (isset($_GET['tab']) && array_key_exists($_GET['tab'], $tabs)) {
808 $active_tab = sanitize_key(wp_unslash($_GET['tab']));
809 }
810
811 ?>
812
813 <div class="wrap">
814
815 <h1>
816 <?php esc_html_e('AI Settings', 'blockspare'); ?>
817 </h1>
818
819 <h2 class="nav-tab-wrapper">
820 <?php foreach ($tabs as $tab_slug => $tab_label) : ?>
821 <a
822 href="<?php echo esc_url(add_query_arg('tab', $tab_slug)); ?>"
823 class="nav-tab <?php echo $active_tab === $tab_slug ? 'nav-tab-active' : ''; ?>">
824 <?php echo esc_html($tab_label); ?>
825 </a>
826 <?php endforeach; ?>
827 </h2>
828
829 <form method="post" action="options.php">
830
831 <?php settings_fields('blockspare_ai_settings_group'); ?>
832
833 <div style="margin-top: 20px;<?php echo $active_tab === 'general' ? '' : ' display:none;'; ?>">
834 <?php do_settings_sections('blockspare-ai-general'); ?>
835 <?php submit_button(__('Save AI Settings', 'blockspare')); ?>
836 </div>
837
838 <div style="margin-top: 20px;<?php echo $active_tab === 'generation' ? '' : ' display:none;'; ?>">
839 <?php do_settings_sections('blockspare-ai-generation'); ?>
840 <?php submit_button(__('Save AI Settings', 'blockspare'), 'primary', 'submit', true, array('disabled' => 'disabled')); ?>
841 </div>
842
843 <div style="margin-top: 20px;<?php echo $active_tab === 'summary' ? '' : ' display:none;'; ?>">
844 <?php do_settings_sections('blockspare-ai-summary'); ?>
845 <?php submit_button(__('Save AI Settings', 'blockspare'), 'primary', 'submit', true, array('disabled' => 'disabled')); ?>
846 </div>
847
848 <div style="margin-top: 20px;<?php echo $active_tab === 'qa' ? '' : ' display:none;'; ?>">
849 <?php do_settings_sections('blockspare-ai-qa'); ?>
850 <?php submit_button(__('Save AI Settings', 'blockspare'), 'primary', 'submit', true, array('disabled' => 'disabled')); ?>
851 </div>
852
853 </form>
854
855 </div>
856
857 <?php
858 }
859
860
861 /**
862 * Sanitize AI settings and restrict modifications on locked fields.
863 */
864 public function blockspare_sanitize_ai_settings($input)
865 {
866 $existing = $this->get_ai_settings();
867 $output = $existing;
868
869 if (! is_array($input)) {
870 return $output;
871 }
872
873 /**
874 * General settings (Allowed to update).
875 */
876 if (isset($input['language'])) {
877 $output['language'] = sanitize_text_field($input['language']);
878 }
879
880 if (! empty($input['ai_model_selection']) && str_contains($input['ai_model_selection'], '|')) {
881 list($provider, $model) = explode('|', $input['ai_model_selection'], 2);
882 $output['ai_provider'] = sanitize_text_field($provider);
883 $output['ai_model'] = sanitize_text_field($model);
884 } else {
885 $output['ai_provider'] = '';
886 $output['ai_model'] = '';
887 }
888
889 /**
890 * Locked settings in Free Plugin (Retain original values).
891 */
892 $output['language'] = $this->blockspare_get_default_language();
893 $output['generate_prompt'] = $existing['generate_prompt'];
894 $output['generate_word_limit'] = $existing['generate_word_limit'];
895 $output['summarize_prompt'] = $existing['summarize_prompt'];
896 $output['summarize_word_limit'] = $existing['summarize_word_limit'];
897 $output['qa_prompt'] = $existing['qa_prompt'];
898 $output['qa_count'] = $existing['qa_count'];
899
900 return $output;
901 }
902 }
903