'',
'mail' => '',
'bell' => '',
'send' => '',
'shield' => '',
'database' => '',
'filter' => '',
'search' => '',
'chevron' => '',
'chart' => '',
'bolt' => '',
'target' => '',
'grid' => '',
'wand' => '',
'clock' => '',
'sliders' => '',
'refresh' => '',
'plus' => '',
'code' => '',
'sparkle' => '',
'desktop' => '',
'tablet' => '',
'mobile' => '',
);
$path = isset( $paths[ $name ] ) ? $paths[ $name ] : $paths['layers'];
return '';
}
/**
* A pastel icon tile.
*
* @param string $name Icon name.
* @param string $tone Colour tone.
* @param bool $large Large variant.
* @return string
*/
public static function tile( $name, $tone = 'amber', $large = false ) {
return sprintf(
'%s',
$large ? ' srpp-tile-lg' : '',
esc_attr( $tone ),
self::icon( $name )
);
}
/**
* Initial-based avatar, coloured deterministically from the label.
*
* @param string $text Label.
* @return string
*/
public static function avatar( $text ) {
$words = preg_split( '/\s+/', trim( wp_strip_all_tags( $text ) ) );
$initials = '';
foreach ( (array) $words as $word ) {
if ( '' === $word ) {
continue;
}
$initials .= function_exists( 'mb_substr' ) ? mb_substr( $word, 0, 1 ) : substr( $word, 0, 1 );
if ( strlen( $initials ) >= 2 ) {
break;
}
}
$initials = $initials ? strtoupper( $initials ) : 'RC';
$tones = array( 'teal', 'coral', 'amber', 'violet', 'blue', 'pink' );
$tone = $tones[ abs( crc32( $text ) ) % count( $tones ) ];
return sprintf(
'%s',
esc_attr( $tone ),
esc_html( $initials )
);
}
/**
* The three-up explainer strip.
*
* @param array $items Each: icon, tone, title, text.
*/
public static function features( $items ) {
echo '
';
foreach ( $items as $item ) {
printf(
'
',
self::tile( $item['icon'], $item['tone'], true ), // phpcs:ignore WordPress.Security.EscapeOutput
esc_html( $item['title'] ),
esc_html( $item['text'] )
);
}
echo '
';
}
/**
* The cream footer band.
*
* @param string $title Title.
* @param string $text Copy.
* @param string $action Button markup.
* @param string $icon Icon name.
* @param string $tone Tone.
*/
public static function footnote( $title, $text, $action = '', $icon = 'mail', $tone = 'amber' ) {
printf(
'',
self::tile( $icon, $tone, true ), // phpcs:ignore WordPress.Security.EscapeOutput
esc_html( $title ),
esc_html( $text ),
$action ? '' . wp_kses_post( $action ) . '
' : ''
);
}
/**
* Capability check helper.
*
* @return bool
*/
public static function can() {
return current_user_can( self::CAP );
}
/**
* Register menus.
*/
public function menu() {
$this->screens['main'] = add_menu_page(
__( 'Related Content', 'advanced-css3-related-posts-widget' ),
__( 'Related Content', 'advanced-css3-related-posts-widget' ),
self::CAP,
'srpp-displays',
array( $this, 'page_displays' ),
'dashicons-screenoptions',
58
);
add_submenu_page( 'srpp-displays', __( 'Displays', 'advanced-css3-related-posts-widget' ), __( 'Displays', 'advanced-css3-related-posts-widget' ), self::CAP, 'srpp-displays', array( $this, 'page_displays' ) );
$this->screens['edit'] = add_submenu_page( 'srpp-displays', __( 'Add Display', 'advanced-css3-related-posts-widget' ), __( 'Add Display', 'advanced-css3-related-posts-widget' ), self::CAP, 'srpp-edit', array( $this, 'page_edit' ) );
$this->screens['templates'] = add_submenu_page( 'srpp-displays', __( 'Templates', 'advanced-css3-related-posts-widget' ), __( 'Templates', 'advanced-css3-related-posts-widget' ), self::CAP, 'srpp-templates', array( $this, 'page_templates' ) );
$this->screens['analytics'] = add_submenu_page( 'srpp-displays', __( 'Analytics', 'advanced-css3-related-posts-widget' ), __( 'Analytics', 'advanced-css3-related-posts-widget' ), self::CAP, 'srpp-analytics', array( $this, 'page_analytics' ) );
$this->screens['settings'] = add_submenu_page( 'srpp-displays', __( 'Settings', 'advanced-css3-related-posts-widget' ), __( 'Settings', 'advanced-css3-related-posts-widget' ), self::CAP, 'srpp-settings', array( 'SRPP_Settings', 'render' ) );
$this->screens['tools'] = add_submenu_page( 'srpp-displays', __( 'Tools', 'advanced-css3-related-posts-widget' ), __( 'Tools', 'advanced-css3-related-posts-widget' ), self::CAP, 'srpp-tools', array( 'SRPP_Tools', 'render' ) );
}
/**
* Is the current screen one of ours?
*
* @return bool
*/
public static function is_plugin_screen() {
if ( ! isset( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
return false;
}
return 0 === strpos( sanitize_key( wp_unslash( $_GET['page'] ) ), 'srpp-' ); // phpcs:ignore WordPress.Security.NonceVerification
}
/**
* Admin assets.
*
* @param string $hook Hook suffix.
*/
public function assets( $hook ) {
$is_editor_screen = in_array( $hook, array( 'post.php', 'post-new.php' ), true );
if ( ! self::is_plugin_screen() && ! $is_editor_screen ) {
return;
}
wp_enqueue_style( 'srpp-admin', SRPP_URL . 'admin/css/admin.css', array( 'wp-color-picker' ), SRPP_VERSION );
wp_enqueue_style( 'srpp', SRPP_URL . 'public/css/srpp.css', array(), SRPP_VERSION );
wp_enqueue_script(
'srpp-admin',
SRPP_URL . 'admin/js/admin.js',
array( 'jquery', 'wp-color-picker', 'jquery-ui-sortable' ),
SRPP_VERSION,
true
);
wp_localize_script(
'srpp-admin',
'SRPP_ADMIN',
array(
'ajax' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'srpp_admin' ),
'i18n' => array(
'search' => __( 'Type to search…', 'advanced-css3-related-posts-widget' ),
'noResults' => __( 'Nothing found.', 'advanced-css3-related-posts-widget' ),
'remove' => __( 'Remove', 'advanced-css3-related-posts-widget' ),
'confirm' => __( 'Delete this display? This cannot be undone.', 'advanced-css3-related-posts-widget' ),
'loading' => __( 'Rendering preview…', 'advanced-css3-related-posts-widget' ),
),
'templates' => SRPP_Templates::all(),
)
);
// The live preview can render the carousel template.
wp_enqueue_script( 'srpp-carousel', SRPP_URL . 'public/js/carousel.js', array(), SRPP_VERSION, true );
}
/**
* Handle row actions (delete / duplicate / toggle).
*/
public function handle_actions() {
if ( ! self::can() || ! isset( $_GET['srpp_action'], $_GET['display'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
return;
}
$action = sanitize_key( wp_unslash( $_GET['srpp_action'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
$id = absint( wp_unslash( $_GET['display'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
$nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';
if ( ! wp_verify_nonce( $nonce, 'srpp_row_' . $id ) ) {
return;
}
$message = '';
switch ( $action ) {
case 'delete':
SRPP_Display::delete( $id );
$message = 'deleted';
break;
case 'duplicate':
SRPP_Display::duplicate( $id );
$message = 'duplicated';
break;
case 'toggle':
$display = SRPP_Display::get( $id );
if ( $display ) {
SRPP_Display::save(
$id,
array(
'title' => $display->title,
'status' => $display->is_active() ? 'draft' : 'publish',
)
);
}
$message = 'toggled';
break;
}
wp_safe_redirect( add_query_arg( array( 'page' => 'srpp-displays', 'srpp_msg' => $message ), admin_url( 'admin.php' ) ) );
exit;
}
/**
* Admin notices.
*/
public function notices() {
if ( ! self::is_plugin_screen() || ! isset( $_GET['srpp_msg'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
return;
}
$messages = array(
'saved' => __( 'Display saved.', 'advanced-css3-related-posts-widget' ),
'deleted' => __( 'Display deleted.', 'advanced-css3-related-posts-widget' ),
'duplicated' => __( 'Display duplicated.', 'advanced-css3-related-posts-widget' ),
'toggled' => __( 'Status updated.', 'advanced-css3-related-posts-widget' ),
'settings' => __( 'Settings saved.', 'advanced-css3-related-posts-widget' ),
'cache' => __( 'Recommendation cache cleared.', 'advanced-css3-related-posts-widget' ),
'stats' => __( 'Statistics reset.', 'advanced-css3-related-posts-widget' ),
'imported' => __( 'Displays imported.', 'advanced-css3-related-posts-widget' ),
);
$key = sanitize_key( wp_unslash( $_GET['srpp_msg'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
if ( isset( $messages[ $key ] ) ) {
printf( '', esc_html( $messages[ $key ] ) );
}
}
/**
* Shared page header — the warm hero band.
*
* @param string $title Title.
* @param string $subtitle Subtitle.
* @param string $action Optional button HTML.
* @param array $args icon, highlights.
*/
public static function header( $title, $subtitle = '', $action = '', $args = array() ) {
$args = wp_parse_args(
$args,
array(
'icon' => 'layers',
'highlights' => array(),
)
);
?>
render();
}
/**
* Display editor screen.
*/
public function page_edit() {
if ( ! self::can() ) {
return;
}
$editor = new SRPP_Editor();
$editor->render();
}
/**
* Template gallery screen.
*/
public function page_templates() {
if ( ! self::can() ) {
return;
}
echo '';
self::header(
__( 'Template library', 'advanced-css3-related-posts-widget' ),
__( 'Layout controls the structure, the template controls the visual DNA. Mix them freely.', 'advanced-css3-related-posts-widget' ),
'',
array( 'icon' => 'grid' )
);
echo '
';
foreach ( SRPP_Templates::all() as $id => $template ) {
$fields = array();
foreach ( $template['supports'] as $field ) {
$all = srpp_fields();
if ( isset( $all[ $field ] ) ) {
$fields[] = $all[ $field ];
}
}
printf(
'
%1$s
%2$s
%3$s
%4$s %5$s
%6$s %7$s
',
esc_attr( $id ),
esc_html( $template['name'] ),
esc_html( $template['desc'] ),
esc_html__( 'Default layout:', 'advanced-css3-related-posts-widget' ),
esc_html( $template['layout'] ),
esc_html__( 'Supports:', 'advanced-css3-related-posts-widget' ),
esc_html( implode( ', ', $fields ) )
);
}
echo '
';
}
/**
* Analytics screen.
*/
public function page_analytics() {
if ( ! self::can() ) {
return;
}
$days = isset( $_GET['days'] ) ? absint( wp_unslash( $_GET['days'] ) ) : 30; // phpcs:ignore WordPress.Security.NonceVerification
$days = in_array( $days, array( 7, 30, 90, 365 ), true ) ? $days : 30;
$totals = SRPP_Analytics::totals( $days );
$by_tpl = SRPP_Analytics::by( 'template', $days );
$by_plc = SRPP_Analytics::by( 'placement', $days );
$by_display = SRPP_Analytics::by( 'display_id', $days );
$by_target = SRPP_Analytics::by( 'target_id', $days, 10 );
echo '';
self::header(
__( 'Related content performance', 'advanced-css3-related-posts-widget' ),
__( 'Which recommendation strategy actually earns clicks.', 'advanced-css3-related-posts-widget' ),
'',
array(
'icon' => 'chart',
'highlights' => array(
array(
'icon' => 'target',
'tone' => 'amber',
'title' => __( 'Per template', 'advanced-css3-related-posts-widget' ),
'text' => __( 'See which visual style pulls readers through.', 'advanced-css3-related-posts-widget' ),
),
array(
'icon' => 'bolt',
'tone' => 'teal',
'title' => __( 'Per placement', 'advanced-css3-related-posts-widget' ),
'text' => __( 'Mid-article usually beats the footer. Confirm it.', 'advanced-css3-related-posts-widget' ),
),
array(
'icon' => 'shield',
'tone' => 'violet',
'title' => __( 'Privacy-safe', 'advanced-css3-related-posts-widget' ),
'text' => __( 'Daily aggregates only. No cookies, no personal data.', 'advanced-css3-related-posts-widget' ),
),
),
)
);
if ( ! SRPP_Analytics::enabled() ) {
printf(
'
',
esc_html__( 'Tracking is currently disabled in Settings, so no new data is being collected.', 'advanced-css3-related-posts-widget' )
);
}
echo '
';
foreach ( array( 7, 30, 90, 365 ) as $range ) {
printf(
'%s',
$range === $days ? 'is-active' : '',
esc_url( add_query_arg( array( 'page' => 'srpp-analytics', 'days' => $range ), admin_url( 'admin.php' ) ) ),
/* translators: %d: number of days */
esc_html( sprintf( _n( 'Last %d day', 'Last %d days', $range, 'advanced-css3-related-posts-widget' ), $range ) )
);
}
echo '
';
printf(
'
',
self::tile( 'target', 'blue', true ), // phpcs:ignore WordPress.Security.EscapeOutput
esc_html__( 'Impressions', 'advanced-css3-related-posts-widget' ),
esc_html( number_format_i18n( $totals['impressions'] ) ),
self::tile( 'bolt', 'teal', true ), // phpcs:ignore WordPress.Security.EscapeOutput
esc_html__( 'Clicks', 'advanced-css3-related-posts-widget' ),
esc_html( number_format_i18n( $totals['clicks'] ) ),
self::tile( 'chart', 'amber', true ), // phpcs:ignore WordPress.Security.EscapeOutput
esc_html__( 'Click-through rate', 'advanced-css3-related-posts-widget' ),
esc_html( number_format_i18n( $totals['ctr'], 2 ) )
);
$this->stats_table( __( 'Per template', 'advanced-css3-related-posts-widget' ), $by_tpl, 'template' );
$this->stats_table( __( 'Per placement', 'advanced-css3-related-posts-widget' ), $by_plc, 'placement' );
$this->stats_table( __( 'Per display', 'advanced-css3-related-posts-widget' ), $by_display, 'display' );
$this->stats_table( __( 'Most clicked recommendations', 'advanced-css3-related-posts-widget' ), $by_target, 'post' );
echo '
';
}
/**
* Render one analytics table.
*
* @param string $title Title.
* @param array $rows Rows.
* @param string $type Label type.
*/
protected function stats_table( $title, $rows, $type ) {
$tones = array(
'template' => array( 'grid', 'violet' ),
'placement' => array( 'target', 'teal' ),
'display' => array( 'layers', 'amber' ),
'post' => array( 'sparkle', 'coral' ),
);
$tone = isset( $tones[ $type ] ) ? $tones[ $type ] : array( 'chart', 'amber' );
echo '';
printf(
'
',
self::tile( $tone[0], $tone[1] ), // phpcs:ignore WordPress.Security.EscapeOutput
esc_html( $title )
);
if ( empty( $rows ) ) {
echo '
' . esc_html__( 'No data for this period yet.', 'advanced-css3-related-posts-widget' ) . '
';
return;
}
echo '';
echo '| ' . esc_html__( 'Item', 'advanced-css3-related-posts-widget' ) . ' | ';
echo '' . esc_html__( 'Impressions', 'advanced-css3-related-posts-widget' ) . ' | ';
echo '' . esc_html__( 'Clicks', 'advanced-css3-related-posts-widget' ) . ' | ';
echo '' . esc_html__( 'CTR', 'advanced-css3-related-posts-widget' ) . ' | ';
echo '
';
foreach ( $rows as $row ) {
$label = $row->label;
if ( 'template' === $type ) {
$template = SRPP_Templates::get( $row->label );
$label = $template['name'];
} elseif ( 'display' === $type ) {
$display = SRPP_Display::get( $row->label );
$label = $display ? $display->title : sprintf( '#%d', (int) $row->label );
} elseif ( 'post' === $type ) {
$label = get_the_title( (int) $row->label );
$label = $label ? $label : sprintf( '#%d', (int) $row->label );
} elseif ( 'placement' === $type ) {
$all = array_merge( SRPP_Placement::content_placements(), srpp_is_woo_active() ? SRPP_Woo::placements() : array() );
$label = isset( $all[ $row->label ] ) ? $all[ $row->label ] : $row->label;
}
printf(
'| %s | %s | %s | %s%% |
',
esc_html( $label ),
esc_html( number_format_i18n( $row->impressions ) ),
esc_html( number_format_i18n( $row->clicks ) ),
esc_html( number_format_i18n( $row->ctr, 2 ) )
);
}
echo '
';
}
}