maybe_save();
$id = isset($_GET['display']) ? absint(wp_unslash($_GET['display'])) : 0; // phpcs:ignore WordPress.Security.NonceVerification
$this->display = $id ? SRPP_Display::get($id) : null;
if (! $this->display) {
$this->display = new SRPP_Display();
$this->display->title = __('New display', 'advanced-css3-related-posts-widget');
}
$this->c = $this->display->config;
echo '
';
SRPP_Admin::header(
$this->display->id ? __('Edit display', 'advanced-css3-related-posts-widget') : __('Create a related-content display', 'advanced-css3-related-posts-widget'),
__('Design → Content → Placement → Layout', 'advanced-css3-related-posts-widget'),
sprintf(
'
%s ',
esc_url(add_query_arg('page', 'srpp-displays', admin_url('admin.php'))),
esc_html__('Back to displays', 'advanced-css3-related-posts-widget')
),
array('icon' => 'wand')
);
echo '
';
}
/* ---------------------------------------------------------------------
* Saving
* ------------------------------------------------------------------ */
/**
* Persist the form.
*/
protected function maybe_save()
{
if (! isset($_POST['srpp_nonce']) || ! SRPP_Admin::can()) {
return;
}
if (! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['srpp_nonce'])), 'srpp_save_display')) {
return;
}
$raw = isset($_POST['srpp']) ? wp_unslash($_POST['srpp']) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
$raw = self::normalize_input((array) $raw);
$id = SRPP_Display::save(
isset($_POST['display_id']) ? absint(wp_unslash($_POST['display_id'])) : 0,
array(
'title' => isset($_POST['srpp_title']) ? sanitize_text_field(wp_unslash($_POST['srpp_title'])) : __('Untitled display', 'advanced-css3-related-posts-widget'),
'status' => (isset($_POST['srpp_status']) && 'draft' === $_POST['srpp_status']) ? 'draft' : 'publish',
'config' => $raw,
)
);
if (is_wp_error($id)) {
return;
}
wp_safe_redirect(
add_query_arg(
array('page' => 'srpp-edit', 'display' => $id, 'srpp_msg' => 'saved'),
admin_url('admin.php')
)
);
exit;
}
/**
* Turn the flat form shape into the config shape.
*
* Taxonomy rules arrive as taxonomy => [term ids]; the model stores a list of
* { taxonomy, terms } rules.
*
* @param array $raw Raw POST data.
* @return array
*/
public static function normalize_input($raw)
{
foreach (array('cond_include', 'cond_exclude', 'ex_terms') as $key) {
if (empty($raw[$key]) || ! is_array($raw[$key])) {
$raw[$key] = array();
continue;
}
$rules = array();
foreach ($raw[$key] as $taxonomy => $terms) {
$terms = srpp_int_list($terms);
if ($terms) {
$rules[] = array('taxonomy' => $taxonomy, 'terms' => $terms);
}
}
$raw[$key] = $rules;
}
return $raw;
}
/* ---------------------------------------------------------------------
* Field helpers
* ------------------------------------------------------------------ */
/**
* Config value.
*
* @param string $key Key.
* @return mixed
*/
protected function v($key)
{
return isset($this->c[$key]) ? $this->c[$key] : '';
}
/**
* Field name attribute.
*
* @param string $key Key.
* @param string $sub Sub key.
* @return string
*/
protected function n($key, $sub = '')
{
return 'srpp[' . $key . ']' . ('' !== $sub ? '[' . $sub . ']' : '');
}
/**
* Open a step panel.
*
* @param string $step Step number/label.
* @param string $title Title.
* @param string $intro Intro copy.
* @param string $id DOM id.
*/
protected function open_step($step, $title, $intro = '', $id = '', $open = false)
{
printf(
'%s
%s %s',
$id ? 'id="' . esc_attr($id) . '"' : '',
$open ? 'open' : '',
esc_html($step),
esc_html($title),
$intro ? '
' . esc_html($intro) . '
' : ''
);
}
/**
* Close a step panel.
*/
protected function close_step()
{
echo '
';
}
/**
* A labelled row.
*
* @param string $label Label.
* @param string $help Help text.
* @param string $class Extra class.
*/
protected function open_row($label, $help = '', $class = '')
{
printf(
'%s %s
',
esc_attr($class),
esc_html($label),
$help ? '' . esc_html($help) . ' ' : ''
);
}
/**
* Close row.
*/
protected function close_row()
{
echo '
';
}
/**
* Checkbox pill group.
*
* @param string $key Config key.
* @param array $choices value => label.
*/
protected function checkbox_group($key, $choices)
{
$selected = (array) $this->v($key);
echo '';
foreach ($choices as $value => $label) {
printf(
'%s ',
esc_attr($this->n($key, '') . '[]'),
esc_attr($value),
checked(in_array((string) $value, array_map('strval', $selected), true), true, false),
esc_html($label)
);
}
echo '
';
}
/**
* Radio card group.
*
* @param string $key Config key.
* @param array $choices value => [label, desc].
*/
protected function radio_cards($key, $choices)
{
$current = (string) $this->v($key);
echo '';
foreach ($choices as $value => $data) {
$label = is_array($data) ? $data[0] : $data;
$desc = is_array($data) && isset($data[1]) ? $data[1] : '';
printf(
'%s %s ',
$current === (string) $value ? 'is-selected' : '',
esc_attr($this->n($key)),
esc_attr($value),
checked($current, (string) $value, false),
esc_html($label),
$desc ? '' . esc_html($desc) . ' ' : ''
);
}
echo '
';
}
/**
* Select field.
*
* @param string $key Config key.
* @param array $choices value => label.
* @param array $atts Extra attributes.
*/
protected function select($key, $choices, $atts = array())
{
$current = (string) $this->v($key);
$extra = '';
foreach ($atts as $attr => $value) {
$extra .= sprintf(' %s="%s"', esc_attr($attr), esc_attr($value));
}
printf('', esc_attr($this->n($key)), $extra); // phpcs:ignore WordPress.Security.EscapeOutput
foreach ($choices as $value => $label) {
if (is_array($label)) {
printf('', esc_attr($value));
foreach ($label as $sub_value => $sub_label) {
printf(
'%s ',
esc_attr($sub_value),
selected($current, (string) $sub_value, false),
esc_html($sub_label)
);
}
echo ' ';
continue;
}
printf(
'%s ',
esc_attr($value),
selected($current, (string) $value, false),
esc_html($label)
);
}
echo ' ';
}
/**
* Number input.
*
* @param string $key Config key.
* @param int $min Min.
* @param int $max Max.
* @param string $unit Suffix.
*/
protected function number($key, $min = 0, $max = 100, $unit = '')
{
printf(
' %s ',
esc_attr($this->n($key)),
esc_attr($this->v($key)),
(int) $min,
(int) $max,
$unit ? '' . esc_html($unit) . ' ' : ''
);
}
/**
* Text input.
*
* @param string $key Config key.
* @param string $placeholder Placeholder.
*/
protected function text($key, $placeholder = '')
{
printf(
' ',
esc_attr($this->n($key)),
esc_attr($this->v($key)),
esc_attr($placeholder)
);
}
/**
* Toggle switch.
*
* @param string $key Config key.
* @param string $label Label.
*/
protected function toggle($key, $label)
{
printf(
'%3$s ',
esc_attr($this->n($key)),
checked((int) $this->v($key), 1, false),
esc_html($label)
);
}
/**
* Post picker (search + chips).
*
* @param string $key Config key.
* @param string $types Comma separated post types.
*/
protected function post_picker($key, $types = '')
{
$ids = srpp_int_list($this->v($key));
printf(
'',
esc_attr__('Search posts, pages or products…', 'advanced-css3-related-posts-widget')
);
}
/**
* Taxonomy/term rule picker.
*
* @param string $key Config key.
* @param array $post_types Limit taxonomies to these post types.
*/
protected function term_rules($key, $post_types = array())
{
$rules = (array) $this->v($key);
$current = array();
foreach ($rules as $rule) {
if (! empty($rule['taxonomy'])) {
$current[$rule['taxonomy']] = array_map('intval', (array) $rule['terms']);
}
}
$taxonomies = srpp_taxonomies($post_types);
echo '';
foreach ($taxonomies as $taxonomy => $label) {
$terms = get_terms(
array(
'taxonomy' => $taxonomy,
'hide_empty' => false,
'number' => 300,
)
);
if (is_wp_error($terms) || empty($terms)) {
continue;
}
printf(
'
%s ',
esc_html($label),
esc_attr('srpp[' . $key . '][' . $taxonomy . '][]')
);
$chosen = isset($current[$taxonomy]) ? $current[$taxonomy] : array();
foreach ($terms as $term) {
printf(
'%s ',
absint($term->term_id),
selected(in_array((int) $term->term_id, $chosen, true), true, false),
esc_html($term->name)
);
}
echo '
';
}
echo '
';
}
/* ---------------------------------------------------------------------
* Steps
* ------------------------------------------------------------------ */
/**
* Name + status bar.
*/
protected function title_bar()
{
?>
open_step('2', __('What should be shown?', 'advanced-css3-related-posts-widget'), __('Pick the content to recommend and how relatedness is decided.', 'advanced-css3-related-posts-widget'), 'srpp-step-content');
$this->open_row(__('Content type', 'advanced-css3-related-posts-widget'), __('Every public post type on this site is available — including custom ones.', 'advanced-css3-related-posts-widget'));
$this->checkbox_group('target_types', srpp_post_types());
$this->close_row();
$this->open_row(__('Selection mode', 'advanced-css3-related-posts-widget'));
$this->radio_cards(
'selection_mode',
array(
'auto' => array(__('Automatic', 'advanced-css3-related-posts-widget'), __('Let the relation engine choose.', 'advanced-css3-related-posts-widget')),
'manual' => array(__('Manual', 'advanced-css3-related-posts-widget'), __('Only the items you pick.', 'advanced-css3-related-posts-widget')),
'auto_manual' => array(__('Manual + automatic', 'advanced-css3-related-posts-widget'), __('Your picks first, the rest filled automatically.', 'advanced-css3-related-posts-widget')),
)
);
$this->close_row();
$this->open_row(__('Manual picks', 'advanced-css3-related-posts-widget'), __('Shown first, in this order.', 'advanced-css3-related-posts-widget'), 'srpp-when-manual');
$this->post_picker('manual_ids');
$this->close_row();
$this->open_row(__('Related by', 'advanced-css3-related-posts-widget'), __('Shared terms in these taxonomies make two items related.', 'advanced-css3-related-posts-widget'), 'srpp-when-auto');
$this->checkbox_group('relate_by', srpp_taxonomies());
$this->close_row();
$this->open_row(__('Relation engine', 'advanced-css3-related-posts-widget'), '', 'srpp-when-auto');
$this->radio_cards(
'engine',
array(
'basic' => array(__('Basic', 'advanced-css3-related-posts-widget'), __('Any shared term counts the same.', 'advanced-css3-related-posts-widget')),
'smart' => array(__('Smart (weighted)', 'advanced-css3-related-posts-widget'), __('Score candidates, then rank by score.', 'advanced-css3-related-posts-widget')),
)
);
$this->close_row();
$this->open_row(__('Weights', 'advanced-css3-related-posts-widget'), __('Two posts sharing a category and three tags outrank one sharing only a category.', 'advanced-css3-related-posts-widget'), 'srpp-when-smart');
echo '';
$labels = array(
'primary_term' => __('Primary category', 'advanced-css3-related-posts-widget'),
'category' => __('Same category', 'advanced-css3-related-posts-widget'),
'post_tag' => __('Each matching tag', 'advanced-css3-related-posts-widget'),
'taxonomy' => __('Custom taxonomy', 'advanced-css3-related-posts-widget'),
'attribute' => __('Product attribute', 'advanced-css3-related-posts-widget'),
'author' => __('Same author', 'advanced-css3-related-posts-widget'),
'recency' => __('Recently published', 'advanced-css3-related-posts-widget'),
);
$weights = wp_parse_args((array) $this->v('weights'), srpp_default_weights());
foreach ($labels as $key => $label) {
printf(
'%s ',
esc_html($label),
esc_attr($this->n('weights', $key)),
(int) $weights[$key]
);
}
echo '
';
printf(
'%s
',
esc_html__('related_score = category + tags + taxonomies + attributes + author + recency', 'advanced-css3-related-posts-widget')
);
$this->close_row();
$this->open_row(__('Recency window', 'advanced-css3-related-posts-widget'), __('Items published inside this window get the recency bonus.', 'advanced-css3-related-posts-widget'), 'srpp-when-smart');
$this->number('recency_days', 0, 3650, __('days', 'advanced-css3-related-posts-widget'));
$this->close_row();
$this->open_row(__('Cross-content matching', 'advanced-css3-related-posts-widget'), __('Lets a blog post recommend products (and the reverse) by matching term slugs across taxonomies.', 'advanced-css3-related-posts-widget'), 'srpp-when-auto');
$this->toggle('cross_match', __('Match terms across content types', 'advanced-css3-related-posts-widget'));
$this->close_row();
$this->open_row(__('If there are not enough matches', 'advanced-css3-related-posts-widget'), '', 'srpp-when-auto');
$this->select(
'fallback',
array(
'recent' => __('Fill with the most recent items', 'advanced-css3-related-posts-widget'),
'random' => __('Fill with random items', 'advanced-css3-related-posts-widget'),
'popular' => __('Fill with most-commented items', 'advanced-css3-related-posts-widget'),
'none' => __('Show fewer items', 'advanced-css3-related-posts-widget'),
)
);
$this->close_row();
$this->open_row(__('Order', 'advanced-css3-related-posts-widget'));
$this->select(
'order',
array(
'score' => __('Relevance score', 'advanced-css3-related-posts-widget'),
'date' => __('Newest first', 'advanced-css3-related-posts-widget'),
'modified' => __('Recently updated', 'advanced-css3-related-posts-widget'),
'title' => __('Title (A→Z)', 'advanced-css3-related-posts-widget'),
'rand' => __('Random', 'advanced-css3-related-posts-widget'),
)
);
$this->close_row();
$this->close_step();
}
/**
* Step 2 — where it appears.
*/
protected function step_placement()
{
$this->open_step('3', __('Where should it appear?', 'advanced-css3-related-posts-widget'), __('Insertion is HTML-aware: a block is only ever placed between top-level elements, never inside a list, table or shortcode.', 'advanced-css3-related-posts-widget'), 'srpp-step-placement');
$this->open_row(__('Placement', 'advanced-css3-related-posts-widget'));
$this->select('placement', SRPP_Placement::grouped_placements(), array('id' => 'srpp-placement'));
$this->close_row();
$this->open_row(__('Position value', 'advanced-css3-related-posts-widget'), __('Paragraph number, or percentage of the article.', 'advanced-css3-related-posts-widget'), 'srpp-when-position');
$this->number('placement_value', 0, 100);
$this->close_row();
$this->open_row(__('Repeat interval', 'advanced-css3-related-posts-widget'), __('Insert another block every X paragraphs or words.', 'advanced-css3-related-posts-widget'), 'srpp-when-repeat');
$this->number('repeat_every', 0, 50);
$this->close_row();
$this->open_row(__('Maximum blocks', 'advanced-css3-related-posts-widget'), '', 'srpp-when-repeat');
$this->number('repeat_max', 1, 10);
$this->close_row();
$this->open_row(__('Run on these post types', 'advanced-css3-related-posts-widget'), __('Where the display is generated from.', 'advanced-css3-related-posts-widget'));
$this->checkbox_group('cond_types', srpp_post_types());
$this->close_row();
$this->open_row(__('Only when the post has', 'advanced-css3-related-posts-widget'), __('Leave empty to run everywhere.', 'advanced-css3-related-posts-widget'));
$this->term_rules('cond_include');
$this->close_row();
$this->open_row(__('Rule logic', 'advanced-css3-related-posts-widget'));
$this->radio_cards(
'cond_logic',
array(
'and' => array(__('AND', 'advanced-css3-related-posts-widget'), __('Every rule must match.', 'advanced-css3-related-posts-widget')),
'or' => array(__('OR', 'advanced-css3-related-posts-widget'), __('Any rule may match.', 'advanced-css3-related-posts-widget')),
)
);
$this->close_row();
$this->open_row(__('Never when the post has', 'advanced-css3-related-posts-widget'));
$this->term_rules('cond_exclude');
$this->close_row();
$this->open_row(__('Only on these items', 'advanced-css3-related-posts-widget'));
$this->post_picker('cond_ids_include');
$this->close_row();
$this->open_row(__('Never on these items', 'advanced-css3-related-posts-widget'));
$this->post_picker('cond_ids_exclude');
$this->close_row();
if (srpp_is_woo_active()) {
$this->open_row(__('Product price range', 'advanced-css3-related-posts-widget'), __('Only run on products inside this range.', 'advanced-css3-related-posts-widget'));
printf(
' ',
esc_attr($this->n('cond_price_min')),
esc_attr($this->v('cond_price_min')),
esc_attr__('Min', 'advanced-css3-related-posts-widget'),
esc_attr($this->n('cond_price_max')),
esc_attr($this->v('cond_price_max')),
esc_attr__('Max', 'advanced-css3-related-posts-widget')
);
$this->close_row();
}
$this->open_row(__('Visitors', 'advanced-css3-related-posts-widget'));
$this->select(
'cond_users',
array(
'all' => __('Everyone', 'advanced-css3-related-posts-widget'),
'logged_in' => __('Logged-in visitors only', 'advanced-css3-related-posts-widget'),
'logged_out' => __('Logged-out visitors only', 'advanced-css3-related-posts-widget'),
)
);
$this->close_row();
$this->open_row(__('Priority', 'advanced-css3-related-posts-widget'), __('Lower numbers are placed first when several displays run on one article.', 'advanced-css3-related-posts-widget'));
$this->number('priority', 1, 999);
$this->close_row();
$this->close_step();
}
/**
* Step 3 — layout.
*/
protected function step_layout()
{
$this->open_step('4', __('How many, and how do they flow?', 'advanced-css3-related-posts-widget'), __('The number of items is deliberately separate from the number of columns.', 'advanced-css3-related-posts-widget'), 'srpp-step-layout');
$this->open_row(__('Layout', 'advanced-css3-related-posts-widget'));
$choices = array();
foreach (srpp_layouts() as $key => $label) {
$choices[$key] = $label;
}
$this->radio_cards('layout', $choices);
$this->close_row();
$this->open_row(__('Total items', 'advanced-css3-related-posts-widget'));
$this->number('items', 1, 60);
$this->close_row();
$this->open_row(__('Columns', 'advanced-css3-related-posts-widget'), __('Responsive control is a core setting, not an advanced one.', 'advanced-css3-related-posts-widget'));
echo '';
foreach (
array(
'cols_d' => __('Desktop', 'advanced-css3-related-posts-widget'),
'cols_t' => __('Tablet', 'advanced-css3-related-posts-widget'),
'cols_m' => __('Mobile', 'advanced-css3-related-posts-widget'),
) as $key => $label
) {
printf(
'%s ',
esc_html($label),
esc_attr($this->n($key)),
(int) $this->v($key)
);
}
echo '
';
$this->close_row();
$this->open_row(__('Gap', 'advanced-css3-related-posts-widget'));
$this->number('gap', 0, 80, 'px');
$this->close_row();
$this->close_step();
}
/**
* Step 4 — design.
*/
protected function step_design()
{
$this->open_step('1', __('How should it look?', 'advanced-css3-related-posts-widget'), __('Choose a template, then fine-tune only if you want to.', 'advanced-css3-related-posts-widget'), 'srpp-step-design', true);
$this->open_row(__('Template', 'advanced-css3-related-posts-widget'));
echo '';
$current = (string) $this->v('template');
foreach (SRPP_Templates::all() as $id => $template) {
printf(
'%s %s ',
$current === $id ? 'is-selected' : '',
esc_attr($template['layout']),
esc_attr($this->n('template')),
esc_attr($id),
checked($current, $id, false),
esc_attr($id),
esc_html($template['name']),
esc_html($template['desc'])
);
}
echo '
';
$this->close_row();
$this->open_row(__('Heading', 'advanced-css3-related-posts-widget'), __('Leave empty for no heading.', 'advanced-css3-related-posts-widget'));
$this->text('heading', __('You may also like', 'advanced-css3-related-posts-widget'));
$this->close_row();
$this->open_row(__('Heading tag & alignment', 'advanced-css3-related-posts-widget'));
$this->select('heading_tag', array('h2' => 'H2', 'h3' => 'H3', 'h4' => 'H4', 'h5' => 'H5', 'div' => 'div', 'span' => 'span'));
$this->select('heading_align', array('left' => __('Left', 'advanced-css3-related-posts-widget'), 'center' => __('Center', 'advanced-css3-related-posts-widget'), 'right' => __('Right', 'advanced-css3-related-posts-widget')));
$this->close_row();
$this->open_row(__('Show', 'advanced-css3-related-posts-widget'), __('Fields a template can render. Unsupported fields are ignored.', 'advanced-css3-related-posts-widget'));
$this->checkbox_group('fields', srpp_fields());
$this->close_row();
$this->open_row(__('Excerpt length', 'advanced-css3-related-posts-widget'), __('0 hides the excerpt entirely.', 'advanced-css3-related-posts-widget'));
$this->number('excerpt_length', 0, 80, __('words', 'advanced-css3-related-posts-widget'));
$this->close_row();
$this->open_row(__('Image ratio', 'advanced-css3-related-posts-widget'));
$this->select('image_ratio', srpp_ratios());
$this->close_row();
$this->open_row(__('Links', 'advanced-css3-related-posts-widget'));
$this->select('link_target', array('_self' => __('Open in same tab', 'advanced-css3-related-posts-widget'), '_blank' => __('Open in new tab', 'advanced-css3-related-posts-widget')));
$this->close_row();
$this->open_row(__('Button text', 'advanced-css3-related-posts-widget'));
$this->text('button_text', __('Read more', 'advanced-css3-related-posts-widget'));
$this->close_row();
echo '' . esc_html__('Customize template', 'advanced-css3-related-posts-widget') . ' ';
$style = wp_parse_args((array) $this->v('style'), srpp_default_style());
$colors = array(
'accent' => __('Accent', 'advanced-css3-related-posts-widget'),
'card_bg' => __('Card background', 'advanced-css3-related-posts-widget'),
'title_color' => __('Title', 'advanced-css3-related-posts-widget'),
'text_color' => __('Text', 'advanced-css3-related-posts-widget'),
'meta_color' => __('Meta', 'advanced-css3-related-posts-widget'),
'border' => __('Border', 'advanced-css3-related-posts-widget'),
);
echo '
';
foreach ($colors as $key => $label) {
printf(
'%s ',
esc_html($label),
esc_attr($this->n('style', $key)),
esc_attr($style[$key]),
esc_attr($style[$key])
);
}
echo '
';
$sliders = array(
'radius' => array(__('Corner radius', 'advanced-css3-related-posts-widget'), 0, 40),
'img_radius' => array(__('Image radius', 'advanced-css3-related-posts-widget'), 0, 40),
'padding' => array(__('Card padding', 'advanced-css3-related-posts-widget'), 0, 48),
'title_size' => array(__('Title size', 'advanced-css3-related-posts-widget'), 10, 40),
'text_size' => array(__('Text size', 'advanced-css3-related-posts-widget'), 10, 24),
);
foreach ($sliders as $key => $data) {
printf(
'%s %d ',
esc_html($data[0]),
(int) $data[1],
(int) $data[2],
esc_attr($this->n('style', $key)),
(int) $style[$key],
(int) $style[$key]
);
}
echo '
';
$this->open_row(__('Shadow', 'advanced-css3-related-posts-widget'));
printf('
', esc_attr($this->n('style', 'shadow')));
foreach (
array(
'none' => __('None', 'advanced-css3-related-posts-widget'),
'sm' => __('Subtle', 'advanced-css3-related-posts-widget'),
'md' => __('Medium', 'advanced-css3-related-posts-widget'),
'lg' => __('Large', 'advanced-css3-related-posts-widget'),
) as $value => $label
) {
printf(
'%s ',
esc_attr($value),
selected($style['shadow'], $value, false),
esc_html($label)
);
}
echo ' ';
$this->close_row();
echo '
';
$this->close_step();
}
/**
* Advanced panel — exclusions and performance.
*/
protected function step_advanced()
{
printf(
'5
%s ',
esc_html__('Exclusions, duplicates & performance', 'advanced-css3-related-posts-widget')
);
$this->open_row(__('Never recommend', 'advanced-css3-related-posts-widget'));
echo '
';
$this->toggle('ex_current', __('The current post or product', 'advanced-css3-related-posts-widget'));
$this->toggle('ex_password', __('Password protected content', 'advanced-css3-related-posts-widget'));
$this->toggle('ex_private', __('Private content', 'advanced-css3-related-posts-widget'));
$this->toggle('ex_noindex', __('Content marked noindex (Yoast / Rank Math)', 'advanced-css3-related-posts-widget'));
if (srpp_is_woo_active()) {
$this->toggle('ex_outofstock', __('Out-of-stock products', 'advanced-css3-related-posts-widget'));
}
$this->toggle('no_duplicates', __('The same item twice on one page', 'advanced-css3-related-posts-widget'));
echo '
';
$this->close_row();
$this->open_row(__('Excluded terms', 'advanced-css3-related-posts-widget'));
$this->term_rules('ex_terms');
$this->close_row();
$this->open_row(__('Excluded items', 'advanced-css3-related-posts-widget'));
$this->post_picker('ex_ids');
$this->close_row();
$this->open_row(__('Excluded authors', 'advanced-css3-related-posts-widget'));
$users = get_users(array('fields' => array('ID', 'display_name'), 'number' => 200));
$chosen = srpp_int_list($this->v('ex_authors'));
printf('
', esc_attr($this->n('ex_authors') . '[]'));
foreach ($users as $user) {
printf(
'%s ',
absint($user->ID),
selected(in_array((int) $user->ID, $chosen, true), true, false),
esc_html($user->display_name)
);
}
echo ' ';
$this->close_row();
$this->open_row(__('Cache lifetime', 'advanced-css3-related-posts-widget'), __('Resolved recommendation IDs are stored per post and regenerated when the post, its terms or this display change.', 'advanced-css3-related-posts-widget'));
$this->select(
'cache_ttl',
array(
0 => __('Disabled (query on every request)', 'advanced-css3-related-posts-widget'),
HOUR_IN_SECONDS => __('1 hour', 'advanced-css3-related-posts-widget'),
DAY_IN_SECONDS => __('1 day', 'advanced-css3-related-posts-widget'),
WEEK_IN_SECONDS => __('1 week', 'advanced-css3-related-posts-widget'),
MONTH_IN_SECONDS => __('1 month', 'advanced-css3-related-posts-widget'),
)
);
$this->close_row();
echo '
'; // srpp-step-advanced
}
/**
* Live preview column.
*/
protected function preview_pane()
{
?>