options = $options;
$this->init_hooks();
}
/**
* Initialize hooks.
*/
private function init_hooks() {
add_filter( 'the_content', array( $this, 'process_content' ), 15 );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
}
/**
* Enqueue frontend assets.
*/
public function enqueue_assets() {
if ( ! is_singular( 'page' ) ) {
return;
}
global $post;
if ( ! $post ) {
return;
}
// Check if this is a glossary page or child of one.
$is_glossary = $this->options->is_glossary_page( $post->ID );
$is_entry = $post->post_parent && $this->options->is_glossary_page( $post->post_parent );
if ( ! $is_glossary && ! $is_entry ) {
return;
}
wp_enqueue_style(
'azgl-frontend',
AZGL_PLUGIN_URL . 'assets/css/frontend.css',
array(),
AZGL_VERSION
);
// Add color scheme and dark mode classes.
$color_scheme = $this->options->get( 'color_scheme', 'emerald' );
$dark_mode = $this->options->get( 'dark_mode', 'auto' );
// Inline CSS for color scheme variables.
$inline_css = $this->get_color_scheme_css( $color_scheme, $dark_mode );
wp_add_inline_style( 'azgl-frontend', $inline_css );
}
/**
* Process content.
*
* @param string $content Post content.
* @return string Processed content.
*/
public function process_content( $content ) {
// Only on frontend single pages.
if ( is_admin() || ! is_singular( 'page' ) ) {
return $content;
}
global $post;
if ( ! $post ) {
return $content;
}
// Check if this is a glossary page.
$glossary = $this->options->get_glossary( $post->ID );
if ( $glossary && ! empty( $glossary['active'] ) ) {
return $this->render_glossary_page( $post->ID, $glossary, $content );
}
// Check if this is a child of a glossary page.
if ( $post->post_parent ) {
$parent_glossary = $this->options->get_glossary( $post->post_parent );
if ( $parent_glossary && ! empty( $parent_glossary['active'] ) ) {
return $this->render_entry_page( $post->post_parent, $parent_glossary, $content );
}
}
return $content;
}
/**
* Render glossary page with A-Z navigation.
*
* @param int $page_id Glossary page ID.
* @param array $glossary Glossary settings.
* @param string $content Original content.
* @return string Modified content.
*/
private function render_glossary_page( $page_id, $glossary, $content ) {
$entries = azgl()->get_glossary_entries( $page_id );
if ( empty( $entries ) ) {
$output = '
';
$output .= '
' . esc_html__( 'No glossary entries yet. Create child pages under this page to add entries.', '3task-glossary' ) . '
';
$output .= '
';
return $content . $output;
}
$alphabetic_list = $this->get_alphabetic_list( $entries );
$nav_style = $this->options->get( 'nav_style', 'buttons' );
$inactive_mode = $this->options->get( 'inactive_letters', 'accessible' );
$color_scheme = $this->options->get( 'color_scheme', 'emerald' );
// Migrate old boolean values.
if ( true === $inactive_mode || '1' === $inactive_mode ) {
$inactive_mode = 'show';
} elseif ( false === $inactive_mode || '' === $inactive_mode ) {
$inactive_mode = 'none';
}
$output = '';
// A-Z Navigation.
if ( ! empty( $glossary['navigation'] ) ) {
$output .= $this->render_navigation( $alphabetic_list, $nav_style, $color_scheme, $inactive_mode );
}
// Entry list.
$output .= '';
foreach ( $alphabetic_list as $letter => $letter_entries ) {
if ( empty( $letter_entries ) ) {
continue;
}
$output .= '
';
$output .= '' . esc_html( $letter ) . '
';
$output .= '';
$output .= '';
}
$output .= '
';
// Credit link (only if supporter mode enabled).
$output .= $this->render_credit_link();
return $content . $output;
}
/**
* Render entry page with navigation back to glossary.
*
* @param int $glossary_id Glossary page ID.
* @param array $glossary Glossary settings.
* @param string $content Original content.
* @return string Modified content.
*/
private function render_entry_page( $glossary_id, $glossary, $content ) {
$output = '';
// Add sub-navigation.
if ( ! empty( $glossary['navigation'] ) ) {
$entries = azgl()->get_glossary_entries( $glossary_id );
$alphabetic_list = $this->get_alphabetic_list( $entries );
$nav_style = $this->options->get( 'nav_style', 'buttons' );
$color_scheme = $this->options->get( 'color_scheme', 'emerald' );
$output .= $this->render_sub_navigation( $glossary_id, $alphabetic_list, $nav_style, $color_scheme );
}
return $output . $content;
}
/**
* Render A-Z navigation.
*
* @param array $alphabetic_list Alphabetic list of entries.
* @param string $nav_style Navigation style.
* @param string $color_scheme Color scheme.
* @param string $inactive_mode Inactive letters display mode (accessible, hidden, show, none).
* @return string HTML.
*/
private function render_navigation( $alphabetic_list, $nav_style, $color_scheme, $inactive_mode ) {
$output = '';
return $output;
}
/**
* Render an inactive letter based on accessibility mode.
*
* @param string $letter The letter to render.
* @param string $inactive_mode Display mode (accessible, hidden, show, none).
* @return string HTML.
*/
private function render_inactive_letter( $letter, $inactive_mode ) {
switch ( $inactive_mode ) {
case 'accessible':
// Screen reader announces "X, no entries" - best for accessibility.
return '' . esc_html( $letter ) . '';
case 'hidden':
// Visible but hidden from screen readers.
return '' .
esc_html( $letter ) . '';
case 'show':
// Legacy mode - visual only, may cause accessibility issues.
return '' .
esc_html( $letter ) . '';
case 'none':
default:
// Don't render inactive letters at all.
return '';
}
}
/**
* Render sub-navigation for entry pages.
*
* @param int $glossary_id Glossary page ID.
* @param array $alphabetic_list Alphabetic list of entries.
* @param string $nav_style Navigation style.
* @param string $color_scheme Color scheme.
* @return string HTML.
*/
private function render_sub_navigation( $glossary_id, $alphabetic_list, $nav_style, $color_scheme ) {
$permalink = get_permalink( $glossary_id );
$output = '';
return $output;
}
/**
* Get alphabetic list from entries.
*
* @param array $entries Glossary entries.
* @return array Alphabetic list.
*/
private function get_alphabetic_list( $entries ) {
// Initialize alphabet.
$letters = array_merge( array( '#' ), range( 'A', 'Z' ) );
$list = array_fill_keys( $letters, array() );
// Sort entries by first letter.
foreach ( $entries as $entry ) {
$initial = $this->get_initial( $entry->post_title );
$list[ $initial ][] = array(
'id' => $entry->ID,
'title' => $entry->post_title,
'url' => get_permalink( $entry->ID ),
);
}
return $list;
}
/**
* Get initial letter with umlaut handling.
*
* @param string $string Input string.
* @return string Initial letter.
*/
private function get_initial( $string ) {
$string = trim( $string );
if ( empty( $string ) ) {
return '#';
}
// German umlaut replacements.
$replacements = array(
'Ä' => 'A',
'ä' => 'A',
'Ö' => 'O',
'ö' => 'O',
'Ü' => 'U',
'ü' => 'U',
'ß' => 'S',
);
$first_char = mb_substr( $string, 0, 1, 'UTF-8' );
if ( isset( $replacements[ $first_char ] ) ) {
return $replacements[ $first_char ];
}
$first_char = strtoupper( $first_char );
if ( preg_match( '/^[A-Z]$/', $first_char ) ) {
return $first_char;
}
return '#';
}
/**
* Render optional credit link.
*
* Only shown if user explicitly opts in via supporter mode.
*
* @return string HTML.
*/
private function render_credit_link() {
if ( ! azgl()->is_supporter() ) {
return '';
}
$credit_url = $this->options->get( 'credit_link_url', 'https://watchguide.net' );
return '' .
'' . esc_html__( 'Glossary by', '3task-glossary' ) . ' ' .
'WatchGuide' .
'
';
}
/**
* Get color scheme CSS variables.
*
* @param string $scheme Color scheme name.
* @param string $dark_mode Dark mode setting.
* @return string CSS.
*/
private function get_color_scheme_css( $scheme, $dark_mode ) {
$schemes = $this->get_color_schemes();
$colors = isset( $schemes[ $scheme ] ) ? $schemes[ $scheme ] : $schemes['emerald'];
// Auto scheme: use theme colors if available.
if ( 'auto' === $scheme ) {
$css = ':root {
--azgl-primary: var(--wp--preset--color--primary, #10b981);
--azgl-hover: var(--wp--preset--color--secondary, #059669);
--azgl-light: var(--wp--preset--color--tertiary, #d1fae5);
--azgl-dark-text: var(--wp--preset--color--contrast, #065f46);
}';
} else {
$css = ':root {
--azgl-primary: ' . $colors['primary'] . ';
--azgl-hover: ' . $colors['hover'] . ';
--azgl-light: ' . $colors['light'] . ';
--azgl-dark-text: ' . $colors['dark-text'] . ';
}';
}
// Dark mode.
$dark_css = '
--azgl-bg: #1e293b;
--azgl-bg-inactive: #334155;
--azgl-text: #f1f5f9;
--azgl-text-muted: #94a3b8;
--azgl-border: #475569;
';
if ( 'dark' === $dark_mode ) {
$css .= ':root {' . $dark_css . '}';
} elseif ( 'auto' === $dark_mode ) {
$css .= '@media (prefers-color-scheme: dark) {:root {' . $dark_css . '}}';
}
return $css;
}
/**
* Get available color schemes.
*
* Lite: 2 schemes (Emerald, Slate)
* Pro: 6 schemes + Custom colors
*
* @return array Color schemes.
*/
private function get_color_schemes() {
return array(
'emerald' => array(
'primary' => '#10b981',
'hover' => '#059669',
'light' => '#d1fae5',
'dark-text' => '#065f46',
),
'slate' => array(
'primary' => '#64748b',
'hover' => '#475569',
'light' => '#f1f5f9',
'dark-text' => '#334155',
),
);
}
}