5 Commits

Author SHA1 Message Date
9df41ca85c chore: Bump version to 1.6.4 and update OAuth redirect URI descriptions 2026-01-26 19:50:38 +00:00
934cbf0f73 chore: Bump version to 1.6.3 2026-01-26 19:26:20 +00:00
5cc6e869bf Refactor code structure for improved readability and maintainability 2026-01-26 19:25:42 +00:00
79d411f35a feat: Register plugin settings with WordPress 2026-01-23 19:02:11 +00:00
58a9b37ccf Add bulk term generation functionality and enhance logging
- Introduced a new JavaScript file for handling bulk term generation in the admin interface.
- Implemented AJAX requests for generating terms and handling responses with appropriate logging.
- Enhanced the Groq_AI_Ajax_Controller to support new options for term generation, including origin and force parameters.
- Improved error handling and logging for term generation events.
- Updated the user interface to reflect the status of term generation and provide feedback to the user.
2026-01-23 18:55:52 +00:00
8 changed files with 1501 additions and 1551 deletions

View File

@@ -33,7 +33,7 @@ class SitiWebUpdater {
require_once ABSPATH . 'wp-admin/includes/plugin.php'; require_once ABSPATH . 'wp-admin/includes/plugin.php';
} }
$this->plugin = get_plugin_data( $this->file ); $this->plugin = get_plugin_data( $this->file, false, false );
$this->basename = plugin_basename( $this->file ); $this->basename = plugin_basename( $this->file );
$this->active = is_plugin_active( $this->basename ); $this->active = is_plugin_active( $this->basename );
} }

View File

@@ -41,6 +41,14 @@
margin-top: 8px; margin-top: 8px;
} }
.groq-ai-bulk-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
margin-top: 8px;
}
#groq-ai-bulk-status { #groq-ai-bulk-status {
margin-top: 8px; margin-top: 8px;
} }
@@ -89,3 +97,12 @@
background-color: transparent; background-color: transparent;
} }
} }
.groq-ai-term-actions {
width: 180px;
}
.groq-ai-regenerate-term.is-busy {
opacity: 0.6;
pointer-events: none;
}

View File

@@ -1,212 +0,0 @@
(function () {
const data = window.GroqAICategoryBulk || {};
const startButton = document.getElementById('groq-ai-bulk-generate');
const stopButton = document.getElementById('groq-ai-bulk-cancel');
const statusField = document.getElementById('groq-ai-bulk-status');
const logList = document.getElementById('groq-ai-bulk-log');
if (!startButton || !data.ajaxUrl) {
return;
}
let queue = [];
let totalCount = 0;
let processed = 0;
let successes = 0;
let isRunning = false;
let abortRequested = false;
function formatString(template, values) {
if (!template) {
return '';
}
let autoIndex = 0;
return template.replace(/%(\d+\$)?[sd]/g, (match, position) => {
let valueIndex;
if (position) {
valueIndex = parseInt(position, 10) - 1;
} else {
valueIndex = autoIndex;
autoIndex += 1;
}
const replacement = values[valueIndex];
return typeof replacement === 'undefined' ? '' : String(replacement);
});
}
function setStatus(message, type) {
if (!statusField) {
return;
}
statusField.textContent = message || '';
statusField.dataset.status = type || '';
}
function appendLog(message, type) {
if (!logList || !message) {
return;
}
const item = document.createElement('li');
item.textContent = message;
item.dataset.status = type || '';
logList.appendChild(item);
}
function resetLog() {
if (!logList) {
return;
}
logList.innerHTML = '';
}
function toggleButtons(running) {
isRunning = running;
startButton.disabled = running;
if (stopButton) {
stopButton.hidden = !running;
}
}
function getPendingTerms() {
if (!Array.isArray(data.terms)) {
return [];
}
return data.terms.filter((term) => !term.processed);
}
function updateRow(termId, words) {
const row = document.querySelector('[data-groq-ai-term-id="' + termId + '"]');
if (!row) {
return;
}
row.classList.remove('groq-ai-term-missing');
row.classList.add('groq-ai-term-updated');
const wordCell = row.querySelector('.groq-ai-word-count');
if (wordCell) {
wordCell.textContent = String(typeof words === 'number' ? words : wordCell.textContent);
}
}
function finish(state) {
const summaryTemplate =
state === 'done'
? data.strings && data.strings.statusDone
: state === 'stopped'
? data.strings && data.strings.statusStopped
: '';
const summary = summaryTemplate
? formatString(summaryTemplate, [successes])
: '';
const statusType = state === 'done' ? 'success' : state === 'stopped' ? 'info' : '';
setStatus(summary, statusType);
toggleButtons(false);
queue = [];
totalCount = 0;
abortRequested = false;
}
function processNext() {
if (abortRequested) {
finish('stopped');
return;
}
if (!queue.length) {
finish('done');
return;
}
const term = queue.shift();
const position = processed + 1;
const progressTemplate = data.strings && data.strings.statusProgress;
if (progressTemplate) {
setStatus(formatString(progressTemplate, [position, totalCount, term.name || '']), 'loading');
}
const payload = new URLSearchParams();
payload.append('action', 'groq_ai_bulk_generate_terms');
payload.append('nonce', data.nonce || '');
payload.append('taxonomy', data.taxonomy || 'product_cat');
payload.append('term_id', term.id);
fetch(data.ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
body: payload.toString(),
})
.then((response) => response.json())
.then((json) => {
if (!json.success) {
const errorMessage = (json.data && json.data.message) || 'Onbekende fout';
appendLog(formatString((data.strings && data.strings.logError) || '%1$s: %2$s', [term.name || term.id, errorMessage]), 'error');
return;
}
term.processed = true;
successes += 1;
const words = json.data && typeof json.data.words !== 'undefined' ? json.data.words : 0;
updateRow(term.id, words);
appendLog(formatString((data.strings && data.strings.logSuccess) || '%1$s gevuld.', [term.name || term.id, words]), 'success');
})
.catch((error) => {
appendLog(
formatString((data.strings && data.strings.logError) || '%1$s: %2$s', [term.name || term.id, error && error.message ? error.message : 'Onbekende fout']),
'error'
);
})
.finally(() => {
processed += 1;
if (abortRequested) {
finish('stopped');
} else {
processNext();
}
});
}
function startBulk() {
if (isRunning) {
return;
}
const pending = getPendingTerms();
if (!pending.length) {
setStatus((data.strings && data.strings.statusEmpty) || '', 'info');
return;
}
queue = pending.slice();
totalCount = queue.length;
processed = 0;
successes = 0;
abortRequested = false;
resetLog();
toggleButtons(true);
setStatus((data.strings && data.strings.statusIdle) || '', 'info');
processNext();
}
startButton.addEventListener('click', startBulk);
if (stopButton) {
stopButton.addEventListener('click', () => {
if (!isRunning) {
return;
}
const confirmation = ! (data.strings && data.strings.confirmStop)
? window.confirm('Stoppen?')
: window.confirm(data.strings.confirmStop);
if (confirmation) {
abortRequested = true;
}
});
}
if (!Array.isArray(data.terms) || !data.terms.length) {
setStatus((data.strings && data.strings.statusEmpty) || '', 'info');
}
})();

288
assets/js/term-bulk.js Normal file
View File

@@ -0,0 +1,288 @@
(function () {
const data = window.GroqAITermBulk || {};
const startButton = document.getElementById('groq-ai-bulk-generate');
const stopButton = document.getElementById('groq-ai-bulk-cancel');
const statusField = document.getElementById('groq-ai-bulk-status');
const logList = document.getElementById('groq-ai-bulk-log');
if (!data.ajaxUrl || !startButton || !statusField || !logList) {
return;
}
const strings = data.strings || {};
const allowRegenerate = !!data.allowRegenerate;
const terms = (Array.isArray(data.terms) ? data.terms : [])
.map((term) => {
const id = parseInt(term.id, 10);
if (!Number.isFinite(id)) {
return null;
}
const words = typeof term.words === 'number' ? term.words : parseInt(term.words, 10) || 0;
const hasDescription = !!term.hasDescription;
return {
id,
name: term.name || '',
slug: term.slug || '',
count: typeof term.count === 'number' ? term.count : parseInt(term.count, 10) || 0,
words,
hasDescription,
needsGeneration: !hasDescription,
};
})
.filter(Boolean);
const termMap = new Map();
terms.forEach((term) => termMap.set(term.id, term));
let queue = [];
let totalCount = 0;
let processed = 0;
let successes = 0;
let isRunning = false;
let abortRequested = false;
function formatString(template, values) {
if (!template) {
return '';
}
let autoIndex = 0;
return template.replace(/%(\d+\$)?[sd]/g, (match, position) => {
let valueIndex;
if (position) {
valueIndex = parseInt(position, 10) - 1;
} else {
valueIndex = autoIndex;
autoIndex += 1;
}
const replacement = values[valueIndex];
return typeof replacement === 'undefined' ? '' : String(replacement);
});
}
function setStatus(message, type) {
statusField.textContent = message || '';
statusField.dataset.status = type || '';
}
function appendLog(message, type) {
if (!message) {
return;
}
const item = document.createElement('li');
item.textContent = message;
item.dataset.status = type || '';
logList.appendChild(item);
}
function resetLog() {
logList.innerHTML = '';
}
function toggleButtons(running) {
isRunning = running;
startButton.disabled = running;
if (stopButton) {
stopButton.hidden = !running;
}
}
function getPendingTerms() {
return terms.filter((term) => term.needsGeneration);
}
function updateRow(term) {
const row = document.querySelector('[data-groq-ai-term-id="' + term.id + '"]');
if (!row) {
return;
}
row.classList.remove('groq-ai-term-missing');
row.classList.add('groq-ai-term-updated');
const wordCell = row.querySelector('.groq-ai-word-count');
if (wordCell) {
wordCell.textContent = String(term.words);
}
}
function markTermCompleted(term, words) {
term.hasDescription = true;
term.needsGeneration = false;
if (Number.isFinite(words)) {
term.words = words;
}
updateRow(term);
}
function finish(state) {
const summaryTemplate = state === 'done' ? strings.statusDone : state === 'stopped' ? strings.statusStopped : '';
const summary = summaryTemplate ? formatString(summaryTemplate, [successes]) : '';
const statusType = state === 'done' ? 'success' : state === 'stopped' ? 'info' : '';
setStatus(summary, statusType);
toggleButtons(false);
queue = [];
totalCount = 0;
processed = 0;
successes = 0;
abortRequested = false;
}
function sendRequest(term, options = {}) {
const payload = new URLSearchParams();
payload.append('action', 'groq_ai_bulk_generate_terms');
payload.append('nonce', data.nonce || '');
payload.append('taxonomy', data.taxonomy || '');
payload.append('term_id', term.id);
if (options.force) {
payload.append('force', '1');
}
return fetch(data.ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
body: payload.toString(),
}).then((response) => response.json());
}
function handleResponse(term, json, context) {
if (!json || !json.success) {
const errorMessage = (json && json.data && json.data.message) || 'Onbekende fout';
appendLog(formatString(strings.logError || '%1$s: %2$s', [term.name || term.id, errorMessage]), 'error');
if (context === 'single') {
setStatus(formatString(strings.regenerateError || '%1$s mislukt: %2$s', [term.name || term.id, errorMessage]), 'error');
}
return false;
}
const words = json.data && typeof json.data.words !== 'undefined' ? parseInt(json.data.words, 10) : term.words;
markTermCompleted(term, Number.isFinite(words) ? words : term.words);
appendLog(formatString(strings.logSuccess || '%1$s gevuld.', [term.name || term.id, term.words]), 'success');
if (context === 'single') {
setStatus(formatString(strings.regenerateDone || '%s is bijgewerkt.', [term.name || term.id]), 'success');
}
return true;
}
function processNext() {
if (abortRequested) {
finish('stopped');
return;
}
if (!queue.length) {
finish('done');
return;
}
const term = queue.shift();
const progressTemplate = strings.statusProgress;
if (progressTemplate) {
setStatus(formatString(progressTemplate, [processed + 1, totalCount, term.name || '']), 'loading');
}
sendRequest(term)
.then((json) => {
if (handleResponse(term, json, 'bulk')) {
successes += 1;
}
})
.catch((error) => {
appendLog(
formatString(strings.logError || '%1$s: %2$s', [term.name || term.id, error && error.message ? error.message : 'Onbekende fout']),
'error'
);
})
.finally(() => {
processed += 1;
if (abortRequested) {
finish('stopped');
} else {
processNext();
}
});
}
function startBulk() {
if (isRunning) {
return;
}
const pending = getPendingTerms();
if (!pending.length) {
setStatus(strings.statusEmpty || '', 'info');
return;
}
queue = pending.slice();
totalCount = queue.length;
processed = 0;
successes = 0;
abortRequested = false;
resetLog();
toggleButtons(true);
if (strings.statusIdle) {
setStatus(strings.statusIdle, 'info');
}
processNext();
}
startButton.addEventListener('click', startBulk);
if (stopButton) {
stopButton.addEventListener('click', () => {
if (!isRunning) {
return;
}
const confirmation = strings.confirmStop ? window.confirm(strings.confirmStop) : window.confirm('Stoppen?');
if (confirmation) {
abortRequested = true;
}
});
}
if (allowRegenerate) {
const buttons = document.querySelectorAll('.groq-ai-regenerate-term');
buttons.forEach((button) => {
button.addEventListener('click', () => {
if (isRunning) {
setStatus(strings.regenerateBlocked || '', 'error');
return;
}
const termId = parseInt(button.getAttribute('data-term-id'), 10);
const term = termMap.get(termId);
if (!term) {
setStatus('Onbekende term.', 'error');
return;
}
if (strings.confirmRegenerate) {
const confirmed = window.confirm(formatString(strings.confirmRegenerate, [term.name || term.id]));
if (!confirmed) {
return;
}
}
button.classList.add('is-busy');
button.disabled = true;
if (strings.regenerateProgress) {
setStatus(formatString(strings.regenerateProgress, [term.name || term.id]), 'loading');
}
sendRequest(term, { force: true })
.then((json) => {
handleResponse(term, json, 'single');
})
.catch((error) => {
const message = error && error.message ? error.message : 'Onbekende fout';
appendLog(formatString(strings.logError || '%1$s: %2$s', [term.name || term.id, message]), 'error');
setStatus(formatString(strings.regenerateError || '%1$s mislukt: %2$s', [term.name || term.id, message]), 'error');
})
.finally(() => {
button.disabled = false;
button.classList.remove('is-busy');
});
});
});
}
if (getPendingTerms().length === 0) {
setStatus(strings.statusEmpty || '', 'info');
}
})();

View File

@@ -2,7 +2,7 @@
/** /**
* Plugin Name: SitiAI Product Teksten * Plugin Name: SitiAI Product Teksten
* Description: Genereer productteksten met diverse AI-aanbieders rechtstreeks vanuit WooCommerce. * Description: Genereer productteksten met diverse AI-aanbieders rechtstreeks vanuit WooCommerce.
* Version: 1.6.0 * Version: 1.6.4
* Author: SitiAI * Author: SitiAI
* Text Domain: siti-ai-product-content-generator * Text Domain: siti-ai-product-content-generator
* Domain Path: /languages * Domain Path: /languages
@@ -108,7 +108,6 @@ final class Groq_AI_Product_Text_Plugin {
$this->settings_page = new Groq_AI_Product_Text_Settings_Page( $this, $this->get_provider_manager() ); $this->settings_page = new Groq_AI_Product_Text_Settings_Page( $this, $this->get_provider_manager() );
$this->product_ui = new Groq_AI_Product_Text_Product_UI( $this ); $this->product_ui = new Groq_AI_Product_Text_Product_UI( $this );
add_action( 'plugins_loaded', [ $this, 'maybe_load_textdomain_early' ], 0 );
add_action( 'init', [ $this, 'load_textdomain' ] ); add_action( 'init', [ $this, 'load_textdomain' ] );
add_action( 'plugins_loaded', [ $this, 'maybe_create_logs_table' ] ); add_action( 'plugins_loaded', [ $this, 'maybe_create_logs_table' ] );
add_action( 'load-plugins.php', [ $this, 'maybe_deactivate_if_woocommerce_missing' ] ); add_action( 'load-plugins.php', [ $this, 'maybe_deactivate_if_woocommerce_missing' ] );
@@ -131,14 +130,6 @@ final class Groq_AI_Product_Text_Plugin {
$this->textdomain_loaded = true; $this->textdomain_loaded = true;
} }
public function maybe_load_textdomain_early() {
if ( did_action( 'init' ) ) {
return;
}
$this->load_textdomain();
}
private function register_services() { private function register_services() {
$this->container = new Groq_AI_Service_Container(); $this->container = new Groq_AI_Service_Container();

View File

@@ -136,28 +136,15 @@ class Groq_AI_Logs_Table extends WP_List_Table {
protected function column_created_at( $item ) { protected function column_created_at( $item ) {
$date = esc_html( mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $item['created_at'] ) ); $date = esc_html( mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $item['created_at'] ) );
$usage = $this->get_usage_meta( $item ); $url = add_query_arg(
$payload = [ [
'created_at' => $item['created_at'], 'page' => 'groq-ai-product-text-log',
'user' => $this->column_default( $item, 'user_id' ), 'log_id' => isset( $item['id'] ) ? (int) $item['id'] : 0,
'post_title' => $item['post_title'], ],
'provider' => $item['provider'], admin_url( 'options-general.php' )
'model' => $item['model'],
'status' => $item['status'],
'tokens_prompt' => isset( $item['tokens_prompt'] ) ? (int) $item['tokens_prompt'] : null,
'tokens_completion' => isset( $item['tokens_completion'] ) ? (int) $item['tokens_completion'] : null,
'tokens_total' => isset( $item['tokens_total'] ) ? (int) $item['tokens_total'] : null,
'prompt' => $item['prompt'],
'response' => $item['response'],
'error_message' => $item['error_message'],
'image_context' => isset( $usage['image_context'] ) ? $usage['image_context'] : null,
];
$encoded = esc_attr( wp_json_encode( $payload ) );
return sprintf(
'<a href="#" class="groq-ai-log-row" data-groq-log="%s">%s</a>',
$encoded,
$date
); );
return sprintf( '<a href="%s">%s</a>', esc_url( $url ), $date );
} }
private function get_usage_meta( $item ) { private function get_usage_meta( $item ) {

File diff suppressed because it is too large Load Diff

View File

@@ -42,6 +42,7 @@ class Groq_AI_Ajax_Controller {
[ [
'include_top_products' => $include_top_products, 'include_top_products' => $include_top_products,
'top_products_limit' => $top_products_limit, 'top_products_limit' => $top_products_limit,
'origin' => 'term_manual',
] ]
); );
@@ -92,6 +93,9 @@ class Groq_AI_Ajax_Controller {
$term $term
); );
$options['origin'] = $force ? 'term_force_regenerate' : 'term_bulk_auto';
$options['force'] = $force;
$result = $this->run_term_generation( $term, $this->get_term_prompt_text( $term ), $options ); $result = $this->run_term_generation( $term, $this->get_term_prompt_text( $term ), $options );
if ( is_wp_error( $result ) ) { if ( is_wp_error( $result ) ) {
wp_send_json_error( [ 'message' => $result->get_error_message() ], 500 ); wp_send_json_error( [ 'message' => $result->get_error_message() ], 500 );
@@ -119,18 +123,26 @@ class Groq_AI_Ajax_Controller {
return new WP_Error( 'groq_ai_invalid_term', __( 'Term niet gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) ); return new WP_Error( 'groq_ai_invalid_term', __( 'Term niet gevonden.', GROQ_AI_PRODUCT_TEXT_DOMAIN ) );
} }
$taxonomy = isset( $term->taxonomy ) ? sanitize_key( (string) $term->taxonomy ) : '';
$term_id = isset( $term->term_id ) ? absint( $term->term_id ) : 0;
$options = wp_parse_args( $options = wp_parse_args(
$options, $options,
[ [
'include_top_products' => true, 'include_top_products' => true,
'top_products_limit' => 10, 'top_products_limit' => 10,
'origin' => 'term_manual',
'force' => false,
] ]
); );
$origin = isset( $options['origin'] ) ? sanitize_key( (string) $options['origin'] ) : 'term_manual';
$force_run = ! empty( $options['force'] );
$include_top_products = ! empty( $options['include_top_products'] ); $include_top_products = ! empty( $options['include_top_products'] );
$top_products_limit = isset( $options['top_products_limit'] ) ? absint( $options['top_products_limit'] ) : 10; $top_products_limit = isset( $options['top_products_limit'] ) ? absint( $options['top_products_limit'] ) : 10;
$top_products_limit = max( 1, min( 25, $top_products_limit ) ); $top_products_limit = max( 1, min( 25, $top_products_limit ) );
$logger = $this->plugin->get_generation_logger();
$settings = $this->plugin->get_settings(); $settings = $this->plugin->get_settings();
$provider_manager = $this->plugin->get_provider_manager(); $provider_manager = $this->plugin->get_provider_manager();
$provider_key = $settings['provider']; $provider_key = $settings['provider'];
@@ -162,6 +174,19 @@ class Groq_AI_Ajax_Controller {
? $prompt_builder->prepend_term_context_to_prompt( $prompt, $context_block ) ? $prompt_builder->prepend_term_context_to_prompt( $prompt, $context_block )
: $prompt_builder->prepend_context_to_prompt( $prompt, $context_block ); : $prompt_builder->prepend_context_to_prompt( $prompt, $context_block );
$usage_meta = [
'term_context' => [
'taxonomy' => $taxonomy,
'term_id' => $term_id,
'origin' => $origin,
],
'term_options' => [
'include_top_products' => $include_top_products,
'top_products_limit' => $top_products_limit,
'force' => $force_run,
],
];
$response_format = null; $response_format = null;
$use_response_format = $this->plugin->should_use_response_format( $provider, $settings ); $use_response_format = $this->plugin->should_use_response_format( $provider, $settings );
if ( $use_response_format && method_exists( $prompt_builder, 'get_term_response_format_definition' ) ) { if ( $use_response_format && method_exists( $prompt_builder, 'get_term_response_format_definition' ) ) {
@@ -187,20 +212,71 @@ class Groq_AI_Ajax_Controller {
); );
if ( is_wp_error( $result ) ) { if ( is_wp_error( $result ) ) {
if ( $logger ) {
$logger->log_generation_event(
[
'provider' => $provider_key,
'model' => $model,
'prompt' => $final_prompt,
'response' => '',
'usage' => $usage_meta,
'status' => 'error',
'error_message' => $result->get_error_message(),
'post_id' => 0,
]
);
}
return $result; return $result;
} }
$response_text = $this->extract_content_text( $result ); $response_text = $this->extract_content_text( $result );
$response_usage = is_array( $result ) && isset( $result['usage'] ) ? $result['usage'] : [];
if ( ! is_array( $response_usage ) ) {
$response_usage = [];
}
$response_usage['term_context'] = $usage_meta['term_context'];
$response_usage['term_options'] = $usage_meta['term_options'];
$parsed = null; $parsed = null;
if ( method_exists( $prompt_builder, 'parse_term_structured_response' ) ) { if ( method_exists( $prompt_builder, 'parse_term_structured_response' ) ) {
$parsed = $prompt_builder->parse_term_structured_response( $response_text, $settings ); $parsed = $prompt_builder->parse_term_structured_response( $response_text, $settings );
} }
if ( is_wp_error( $parsed ) ) {
if ( $logger ) {
$logger->log_generation_event(
[
'provider' => $provider_key,
'model' => $model,
'prompt' => $final_prompt,
'response' => $response_text,
'usage' => $response_usage,
'status' => 'error',
'error_message' => $parsed->get_error_message(),
'post_id' => 0,
]
);
}
return $parsed;
}
if ( ! is_array( $parsed ) ) { if ( ! is_array( $parsed ) ) {
$parsed = [ $parsed = [
'description' => trim( (string) $response_text ), 'description' => trim( (string) $response_text ),
]; ];
} }
if ( $logger ) {
$logger->log_generation_event(
[
'provider' => $provider_key,
'model' => $model,
'prompt' => $final_prompt,
'response' => $response_text,
'usage' => $response_usage,
'status' => 'success',
'post_id' => 0,
]
);
}
return [ return [
'top_description' => isset( $parsed['top_description'] ) ? $parsed['top_description'] : ( isset( $parsed['description'] ) ? $parsed['description'] : '' ), 'top_description' => isset( $parsed['top_description'] ) ? $parsed['top_description'] : ( isset( $parsed['description'] ) ? $parsed['description'] : '' ),
'bottom_description' => isset( $parsed['bottom_description'] ) ? $parsed['bottom_description'] : '', 'bottom_description' => isset( $parsed['bottom_description'] ) ? $parsed['bottom_description'] : '',