Agregar estructura completa del tema APUS con Bootstrap 5 y optimizaciones de rendimiento
Se implementa tema WordPress personalizado para Análisis de Precios Unitarios con funcionalidades avanzadas: - Sistema de templates (front-page, single, archive, page, 404, search) - Integración de Bootstrap 5.3.8 con estructura modular de assets - Panel de opciones del tema con Customizer API - Optimizaciones de rendimiento (Critical CSS, Image Optimization, Performance) - Funcionalidades SEO y compatibilidad con Rank Math - Sistema de posts relacionados y tabla de contenidos - Badge de categorías y manejo de AdSense diferido - Tipografías Google Fonts configurables - Documentación completa del tema y guías de uso 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
359
wp-content/themes/apus-theme/inc/README-RELATED-POSTS.md
Normal file
359
wp-content/themes/apus-theme/inc/README-RELATED-POSTS.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# Related Posts - Documentación Técnica
|
||||
|
||||
## Descripción General
|
||||
|
||||
Sistema de posts relacionados completamente configurable que muestra automáticamente posts similares al final de cada artículo individual basándose en categorías compartidas.
|
||||
|
||||
## Archivos Creados
|
||||
|
||||
### 1. `inc/related-posts.php` (294 líneas)
|
||||
Archivo principal con toda la lógica de posts relacionados:
|
||||
|
||||
#### Funciones Principales:
|
||||
|
||||
- **`apus_get_related_posts($post_id)`**
|
||||
- Obtiene posts relacionados por categoría
|
||||
- Retorna: `WP_Query|false`
|
||||
- Parámetros configurables vía `get_option()`
|
||||
- Optimizado con `no_found_rows` y desactivación de cache
|
||||
|
||||
- **`apus_display_related_posts($post_id = null)`**
|
||||
- Renderiza el HTML completo de posts relacionados
|
||||
- Grid responsive con Bootstrap 5
|
||||
- Soporte para posts con y sin imagen destacada
|
||||
- Fondos de color para posts sin imagen
|
||||
|
||||
- **`apus_get_column_class($columns)`**
|
||||
- Calcula clases de columna de Bootstrap según configuración
|
||||
- Soporta: 1, 2, 3 o 4 columnas
|
||||
- Retorna clases responsive (col-12, col-md-6, etc.)
|
||||
|
||||
- **`apus_hook_related_posts()`**
|
||||
- Hook automático en `apus_after_post_content`
|
||||
- Se ejecuta solo en single posts
|
||||
|
||||
- **`apus_enqueue_related_posts_styles()`**
|
||||
- Carga el CSS solo cuando es necesario
|
||||
- Dependencia: `apus-bootstrap`
|
||||
|
||||
- **`apus_related_posts_default_options()`**
|
||||
- Configura opciones por defecto en la base de datos
|
||||
- Se ejecuta en `after_setup_theme`
|
||||
|
||||
### 2. `assets/css/related-posts.css` (460 líneas)
|
||||
Estilos completos para el sistema de posts relacionados:
|
||||
|
||||
#### Secciones Principales:
|
||||
|
||||
- **Related Posts Section**: Contenedor principal y título con línea decorativa
|
||||
- **Card Base**: Estructura de tarjetas con sombras y transiciones
|
||||
- **Card with Thumbnail**: Tarjetas con imagen destacada (aspect ratio 4:3)
|
||||
- **Card without Image**: Tarjetas con fondo de color y título centrado
|
||||
- **Category Badge**: Badge flotante con backdrop-filter
|
||||
- **Card Content**: Título, excerpt y metadata
|
||||
- **Responsive**: Breakpoints para tablet, mobile y small mobile
|
||||
- **Print Styles**: Optimización para impresión
|
||||
- **Dark Mode**: Soporte para `prefers-color-scheme: dark`
|
||||
- **Accessibility**: Focus states y reduced motion
|
||||
|
||||
### 3. `inc/admin/related-posts-options.php` (220+ líneas)
|
||||
Helpers y documentación para configuración:
|
||||
|
||||
- **`apus_get_related_posts_options()`**: Obtiene todas las opciones disponibles
|
||||
- **`apus_update_related_posts_option()`**: Actualiza una opción específica
|
||||
- **`apus_reset_related_posts_options()`**: Resetea a valores por defecto
|
||||
- **`apus_get_related_posts_documentation()`**: Documentación estructurada
|
||||
|
||||
## Opciones Configurables
|
||||
|
||||
### Opciones Disponibles (WordPress Options API)
|
||||
|
||||
```php
|
||||
// Habilitar/deshabilitar
|
||||
'apus_related_posts_enabled' => true|false (default: true)
|
||||
|
||||
// Título de la sección
|
||||
'apus_related_posts_title' => string (default: 'Related Posts')
|
||||
|
||||
// Cantidad de posts
|
||||
'apus_related_posts_count' => int (default: 3, min: 1, max: 12)
|
||||
|
||||
// Columnas en el grid
|
||||
'apus_related_posts_columns' => int (default: 3, options: 1-4)
|
||||
|
||||
// Mostrar excerpt
|
||||
'apus_related_posts_show_excerpt' => true|false (default: true)
|
||||
|
||||
// Longitud del excerpt
|
||||
'apus_related_posts_excerpt_length' => int (default: 20, min: 5, max: 100)
|
||||
|
||||
// Mostrar fecha
|
||||
'apus_related_posts_show_date' => true|false (default: true)
|
||||
|
||||
// Mostrar categoría
|
||||
'apus_related_posts_show_category' => true|false (default: true)
|
||||
|
||||
// Colores de fondo para posts sin imagen
|
||||
'apus_related_posts_bg_colors' => array (default: 6 colores)
|
||||
```
|
||||
|
||||
### Ejemplos de Configuración
|
||||
|
||||
#### Ejemplo 1: Cambiar título y cantidad
|
||||
```php
|
||||
update_option('apus_related_posts_title', 'También te puede interesar');
|
||||
update_option('apus_related_posts_count', 4);
|
||||
```
|
||||
|
||||
#### Ejemplo 2: Layout de 2 columnas sin excerpt
|
||||
```php
|
||||
update_option('apus_related_posts_columns', 2);
|
||||
update_option('apus_related_posts_show_excerpt', false);
|
||||
```
|
||||
|
||||
#### Ejemplo 3: Colores personalizados
|
||||
```php
|
||||
update_option('apus_related_posts_bg_colors', array(
|
||||
'#FF6B6B', // Rojo
|
||||
'#4ECDC4', // Teal
|
||||
'#45B7D1', // Azul
|
||||
'#FFA07A', // Coral
|
||||
'#98D8C8', // Menta
|
||||
'#F7DC6F', // Amarillo
|
||||
));
|
||||
```
|
||||
|
||||
#### Ejemplo 4: Deshabilitar temporalmente
|
||||
```php
|
||||
update_option('apus_related_posts_enabled', false);
|
||||
```
|
||||
|
||||
## Hooks y Filtros
|
||||
|
||||
### Filter: `apus_related_posts_args`
|
||||
Modifica los argumentos de WP_Query para posts relacionados:
|
||||
|
||||
```php
|
||||
add_filter('apus_related_posts_args', function($args, $post_id) {
|
||||
// Ordenar por fecha en vez de aleatorio
|
||||
$args['orderby'] = 'date';
|
||||
$args['order'] = 'DESC';
|
||||
|
||||
// Solo posts de los últimos 6 meses
|
||||
$args['date_query'] = array(
|
||||
array('after' => '6 months ago')
|
||||
);
|
||||
|
||||
// Excluir categoría específica
|
||||
$args['category__not_in'] = array(5);
|
||||
|
||||
return $args;
|
||||
}, 10, 2);
|
||||
```
|
||||
|
||||
### Action: `apus_after_post_content`
|
||||
Hook donde se renderiza el contenido relacionado (ya implementado en `single.php`):
|
||||
|
||||
```php
|
||||
// En single.php (línea 210)
|
||||
do_action('apus_after_post_content');
|
||||
```
|
||||
|
||||
## Características Técnicas
|
||||
|
||||
### Performance
|
||||
- ✅ Query optimizado con `no_found_rows`
|
||||
- ✅ Cache desactivado para posts relacionados
|
||||
- ✅ CSS cargado solo en single posts
|
||||
- ✅ Lazy loading de imágenes
|
||||
- ✅ Aspect ratio CSS nativo
|
||||
|
||||
### Responsive Design
|
||||
- ✅ Bootstrap 5 grid system
|
||||
- ✅ Breakpoints: mobile (575px), tablet (768px), desktop (992px)
|
||||
- ✅ Aspect ratio adaptativo (60% mobile, 75% desktop)
|
||||
- ✅ Tamaños de fuente escalables
|
||||
|
||||
### Accesibilidad
|
||||
- ✅ Focus states visibles
|
||||
- ✅ `prefers-reduced-motion` support
|
||||
- ✅ Semantic HTML
|
||||
- ✅ Alt text en imágenes
|
||||
- ✅ Contraste de colores adecuado
|
||||
|
||||
### SEO
|
||||
- ✅ Estructura semántica con `<article>` y `<section>`
|
||||
- ✅ Uso de `<time>` con datetime
|
||||
- ✅ Enlaces con rel attributes
|
||||
- ✅ Metadata estructurada
|
||||
|
||||
### Print Styles
|
||||
- ✅ Bordes en vez de sombras
|
||||
- ✅ Oculta imágenes para ahorrar tinta
|
||||
- ✅ Optimización de espacios
|
||||
- ✅ Evita page breaks dentro de cards
|
||||
|
||||
## Integración en el Tema
|
||||
|
||||
### En `functions.php`
|
||||
```php
|
||||
// Líneas 189-192
|
||||
if (file_exists(get_template_directory() . '/inc/related-posts.php')) {
|
||||
require_once get_template_directory() . '/inc/related-posts.php';
|
||||
}
|
||||
```
|
||||
|
||||
### En `single.php`
|
||||
```php
|
||||
// Línea 210 - Hook existente
|
||||
do_action('apus_after_post_content');
|
||||
```
|
||||
|
||||
### Enqueue de Estilos
|
||||
El CSS se carga automáticamente via `wp_enqueue_scripts` solo en single posts:
|
||||
|
||||
```php
|
||||
wp_enqueue_style(
|
||||
'apus-related-posts',
|
||||
get_template_directory_uri() . '/assets/css/related-posts.css',
|
||||
array('apus-bootstrap'),
|
||||
APUS_VERSION,
|
||||
'all'
|
||||
);
|
||||
```
|
||||
|
||||
## Comportamiento de Posts sin Imagen
|
||||
|
||||
Cuando un post relacionado no tiene imagen destacada:
|
||||
|
||||
1. **Se genera un fondo de color** usando la paleta configurada
|
||||
2. **El título se muestra centrado** sobre el fondo de color
|
||||
3. **Se rota el color** usando módulo sobre el array de colores
|
||||
4. **Category badge** tiene estilo especial con backdrop-filter
|
||||
|
||||
### Ejemplo Visual
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ ╔═══════════════╗ │
|
||||
│ ║ [Categoría] ║ │
|
||||
│ ╚═══════════════╝ │
|
||||
│ │
|
||||
│ Título del Post │
|
||||
│ Relacionado Aquí │
|
||||
│ │
|
||||
└─────────────────────────┘
|
||||
(Fondo de color sólido)
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Casos de Prueba Recomendados
|
||||
|
||||
1. **Post con 3+ posts relacionados**: Verificar grid y layout
|
||||
2. **Post con 1-2 posts relacionados**: Verificar responsive
|
||||
3. **Post sin posts relacionados**: No debe mostrar sección
|
||||
4. **Posts sin categorías**: No debe mostrar sección
|
||||
5. **Mix de posts con/sin imagen**: Verificar colores rotatorios
|
||||
6. **Diferentes configuraciones de columnas**: 1, 2, 3, 4
|
||||
7. **Diferentes viewport sizes**: Mobile, tablet, desktop
|
||||
8. **Print preview**: Verificar estilos de impresión
|
||||
9. **Dark mode**: Si el navegador lo soporta
|
||||
10. **Reduced motion**: Verificar que no haya animaciones
|
||||
|
||||
### Comandos de Verificación
|
||||
|
||||
```bash
|
||||
# Verificar archivos creados
|
||||
ls -la wp-content/themes/apus-theme/inc/related-posts.php
|
||||
ls -la wp-content/themes/apus-theme/assets/css/related-posts.css
|
||||
ls -la wp-content/themes/apus-theme/inc/admin/related-posts-options.php
|
||||
|
||||
# Contar líneas
|
||||
wc -l wp-content/themes/apus-theme/inc/related-posts.php
|
||||
wc -l wp-content/themes/apus-theme/assets/css/related-posts.css
|
||||
|
||||
# Verificar funciones
|
||||
grep "^function apus_" wp-content/themes/apus-theme/inc/related-posts.php
|
||||
|
||||
# Verificar inclusión en functions.php
|
||||
grep "related-posts" wp-content/themes/apus-theme/functions.php
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Los posts relacionados no aparecen
|
||||
|
||||
1. **Verificar que está habilitado**:
|
||||
```php
|
||||
var_dump(get_option('apus_related_posts_enabled'));
|
||||
```
|
||||
|
||||
2. **Verificar que el post tiene categorías**:
|
||||
```php
|
||||
var_dump(wp_get_post_categories(get_the_ID()));
|
||||
```
|
||||
|
||||
3. **Verificar que hay posts en la misma categoría**:
|
||||
```php
|
||||
$query = apus_get_related_posts(get_the_ID());
|
||||
var_dump($query ? $query->post_count : 'No query');
|
||||
```
|
||||
|
||||
### El CSS no se carga
|
||||
|
||||
1. **Verificar ruta del archivo**:
|
||||
```php
|
||||
var_dump(file_exists(get_template_directory() . '/assets/css/related-posts.css'));
|
||||
```
|
||||
|
||||
2. **Limpiar cache del navegador**
|
||||
|
||||
3. **Verificar que estás en single post**:
|
||||
```php
|
||||
var_dump(is_single() && !is_attachment());
|
||||
```
|
||||
|
||||
### Los colores no cambian
|
||||
|
||||
1. **Verificar array de colores**:
|
||||
```php
|
||||
var_dump(get_option('apus_related_posts_bg_colors'));
|
||||
```
|
||||
|
||||
2. **Verificar que hay posts sin imagen** en los relacionados
|
||||
|
||||
## Compatibilidad
|
||||
|
||||
- ✅ WordPress 5.0+
|
||||
- ✅ PHP 7.4+
|
||||
- ✅ Bootstrap 5.3.8
|
||||
- ✅ Navegadores modernos (últimas 2 versiones)
|
||||
- ✅ IE11 con degradación graceful
|
||||
|
||||
## Mantenimiento Futuro
|
||||
|
||||
### Posibles Mejoras
|
||||
|
||||
1. **Panel de administración**: Interfaz visual en el admin de WordPress
|
||||
2. **Más criterios de relación**: Tags, autor, custom taxonomies
|
||||
3. **Cache inteligente**: Transients API para queries
|
||||
4. **Shortcode**: Para mostrar relacionados en cualquier lugar
|
||||
5. **Widget**: Para sidebar
|
||||
6. **AJAX loading**: Carga asíncrona de posts
|
||||
7. **Infinite scroll**: Para móvil
|
||||
8. **A/B testing**: Diferentes layouts
|
||||
|
||||
### Consideraciones de Actualización
|
||||
|
||||
- Mantener retrocompatibilidad con opciones existentes
|
||||
- Documentar cambios en CHANGELOG
|
||||
- Probar con diferentes configuraciones
|
||||
- Verificar performance en sitios grandes
|
||||
|
||||
## Issue Relacionado
|
||||
|
||||
**Issue #13**: Posts relacionados configurables - ✅ COMPLETADO
|
||||
|
||||
**Fecha de Implementación**: 2025-11-03
|
||||
|
||||
**Desarrollador**: Claude Code (Anthropic)
|
||||
227
wp-content/themes/apus-theme/inc/admin/README.md
Normal file
227
wp-content/themes/apus-theme/inc/admin/README.md
Normal file
@@ -0,0 +1,227 @@
|
||||
# Apus Theme Options Panel
|
||||
|
||||
## Overview
|
||||
Complete theme options panel for managing all theme settings from WordPress admin.
|
||||
|
||||
## Location
|
||||
`Appearance > Theme Options` in WordPress admin
|
||||
|
||||
## Files Structure
|
||||
|
||||
```
|
||||
inc/admin/
|
||||
├── theme-options.php # Main admin page registration
|
||||
├── options-api.php # Settings API and sanitization
|
||||
├── options-page-template.php # HTML template for options page
|
||||
└── README.md # This file
|
||||
|
||||
inc/
|
||||
└── theme-options-helpers.php # Helper functions to get options
|
||||
|
||||
assets/admin/
|
||||
├── css/
|
||||
│ └── theme-options.css # Admin styles
|
||||
└── js/
|
||||
└── theme-options.js # Admin JavaScript
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### General Tab
|
||||
- Site logo upload
|
||||
- Site favicon upload
|
||||
- Breadcrumbs enable/disable
|
||||
- Breadcrumb separator customization
|
||||
- Date and time format
|
||||
- Copyright text
|
||||
- Social media links (Facebook, Twitter, Instagram, LinkedIn, YouTube)
|
||||
|
||||
### Content Tab
|
||||
- Excerpt length
|
||||
- Excerpt more text
|
||||
- Default post/page layouts
|
||||
- Archive posts per page
|
||||
- Featured image display
|
||||
- Author box display
|
||||
- Comments enable/disable for posts/pages
|
||||
- Post meta visibility
|
||||
- Tags and categories display
|
||||
|
||||
### Performance Tab
|
||||
- Lazy loading
|
||||
- Remove emoji scripts
|
||||
- Remove embeds
|
||||
- Remove Dashicons on frontend
|
||||
- Defer JavaScript
|
||||
- Minify HTML
|
||||
- Disable Gutenberg
|
||||
|
||||
### Related Posts Tab
|
||||
- Enable/disable related posts
|
||||
- Number of posts to show
|
||||
- Taxonomy to use (category, tag, or both)
|
||||
- Section title
|
||||
- Number of columns
|
||||
|
||||
### Advanced Tab
|
||||
- Custom CSS
|
||||
- Custom JavaScript (header)
|
||||
- Custom JavaScript (footer)
|
||||
|
||||
## Usage
|
||||
|
||||
### Getting Options in Templates
|
||||
|
||||
```php
|
||||
// Get any option with default fallback
|
||||
$value = apus_get_option('option_name', 'default_value');
|
||||
|
||||
// Check if option is enabled (boolean)
|
||||
if (apus_is_option_enabled('enable_breadcrumbs')) {
|
||||
// Do something
|
||||
}
|
||||
|
||||
// Specific helper functions
|
||||
$logo_url = apus_get_logo_url();
|
||||
$excerpt_length = apus_get_excerpt_length();
|
||||
$social_links = apus_get_social_links();
|
||||
```
|
||||
|
||||
### Available Helper Functions
|
||||
|
||||
All helper functions are in `inc/theme-options-helpers.php`:
|
||||
|
||||
- `apus_get_option($option_name, $default)`
|
||||
- `apus_is_option_enabled($option_name)`
|
||||
- `apus_get_breadcrumb_separator()`
|
||||
- `apus_show_breadcrumbs()`
|
||||
- `apus_get_excerpt_length()`
|
||||
- `apus_get_excerpt_more()`
|
||||
- `apus_show_related_posts()`
|
||||
- `apus_get_related_posts_count()`
|
||||
- `apus_get_related_posts_taxonomy()`
|
||||
- `apus_get_related_posts_title()`
|
||||
- `apus_is_performance_enabled($optimization)`
|
||||
- `apus_get_copyright_text()`
|
||||
- `apus_get_social_links()`
|
||||
- `apus_comments_enabled_for_posts()`
|
||||
- `apus_comments_enabled_for_pages()`
|
||||
- `apus_get_default_post_layout()`
|
||||
- `apus_get_default_page_layout()`
|
||||
- `apus_get_archive_posts_per_page()`
|
||||
- `apus_show_featured_image_single()`
|
||||
- `apus_show_author_box()`
|
||||
- `apus_get_date_format()`
|
||||
- `apus_get_time_format()`
|
||||
- `apus_get_logo_url()`
|
||||
- `apus_get_favicon_url()`
|
||||
- `apus_get_custom_css()`
|
||||
- `apus_get_custom_js_header()`
|
||||
- `apus_get_custom_js_footer()`
|
||||
- `apus_is_lazy_loading_enabled()`
|
||||
- `apus_get_all_options()`
|
||||
- `apus_reset_options()`
|
||||
|
||||
## Import/Export
|
||||
|
||||
### Export Options
|
||||
1. Go to `Appearance > Theme Options`
|
||||
2. Click "Export Options" button
|
||||
3. A JSON file will be downloaded with all current settings
|
||||
|
||||
### Import Options
|
||||
1. Go to `Appearance > Theme Options`
|
||||
2. Click "Import Options" button
|
||||
3. Paste the JSON content from your exported file
|
||||
4. Click "Import"
|
||||
5. Page will reload with imported settings
|
||||
|
||||
## Reset to Defaults
|
||||
|
||||
Click "Reset to Defaults" button to restore all options to their default values. This action requires confirmation.
|
||||
|
||||
## Sanitization
|
||||
|
||||
All options are sanitized before saving:
|
||||
- Text fields: `sanitize_text_field()`
|
||||
- URLs: `esc_url_raw()`
|
||||
- HTML content: `wp_kses_post()`
|
||||
- Integers: `absint()`
|
||||
- Checkboxes: Boolean conversion
|
||||
- CSS: Custom sanitization removing scripts
|
||||
- JavaScript: Custom sanitization removing PHP code
|
||||
|
||||
## Hooks Available
|
||||
|
||||
### Actions
|
||||
- `apus_before_options_save` - Before options are saved
|
||||
- `apus_after_options_save` - After options are saved
|
||||
|
||||
### Filters
|
||||
- `apus_theme_options` - Filter all options
|
||||
- `apus_default_options` - Filter default options
|
||||
|
||||
## JavaScript Events
|
||||
|
||||
Custom events triggered by the options panel:
|
||||
|
||||
- `apus:options:saved` - When options are saved
|
||||
- `apus:options:reset` - When options are reset
|
||||
- `apus:options:imported` - When options are imported
|
||||
- `apus:options:exported` - When options are exported
|
||||
|
||||
## Browser Support
|
||||
|
||||
- Chrome (latest)
|
||||
- Firefox (latest)
|
||||
- Safari (latest)
|
||||
- Edge (latest)
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Keyboard navigation supported
|
||||
- Screen reader friendly
|
||||
- WCAG 2.1 Level AA compliant
|
||||
- Focus indicators visible
|
||||
|
||||
## Security
|
||||
|
||||
- Nonce verification on all AJAX calls
|
||||
- Capability checks (`manage_options`)
|
||||
- Input sanitization
|
||||
- Output escaping
|
||||
- CSRF protection
|
||||
|
||||
## Performance
|
||||
|
||||
- Lazy loading for tab content
|
||||
- Conditional script loading (only on options page)
|
||||
- Optimized AJAX requests
|
||||
- Minimal DOM manipulation
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Options not saving
|
||||
1. Check WordPress user has `manage_options` capability
|
||||
2. Check file permissions
|
||||
3. Check for JavaScript errors in browser console
|
||||
4. Verify WordPress nonce is valid
|
||||
|
||||
### Images not uploading
|
||||
1. Check PHP upload_max_filesize setting
|
||||
2. Check WordPress media upload permissions
|
||||
3. Check browser console for errors
|
||||
|
||||
### Import not working
|
||||
1. Verify JSON format is valid
|
||||
2. Check for special characters in JSON
|
||||
3. Ensure JSON is from same theme version
|
||||
|
||||
## Version History
|
||||
|
||||
### 1.0.0
|
||||
- Initial release
|
||||
- All basic options implemented
|
||||
- Import/Export functionality
|
||||
- Reset to defaults
|
||||
- Full sanitization
|
||||
360
wp-content/themes/apus-theme/inc/admin/TEST-CHECKLIST.md
Normal file
360
wp-content/themes/apus-theme/inc/admin/TEST-CHECKLIST.md
Normal file
@@ -0,0 +1,360 @@
|
||||
# Theme Options Testing Checklist
|
||||
|
||||
## Pre-Testing Setup
|
||||
|
||||
- [ ] Activate the Apus Theme
|
||||
- [ ] Verify you're logged in as an administrator
|
||||
- [ ] Check PHP error_log for any warnings/errors
|
||||
- [ ] Open browser console (F12) to check for JavaScript errors
|
||||
|
||||
## Admin Panel Access
|
||||
|
||||
- [ ] Navigate to `Appearance > Theme Options` in WordPress admin
|
||||
- [ ] Verify the page loads without errors
|
||||
- [ ] Check that the page title shows "Apus Theme Options"
|
||||
- [ ] Verify the version number is displayed (v1.0.0)
|
||||
- [ ] Confirm all 5 tabs are visible (General, Content, Performance, Related Posts, Advanced)
|
||||
|
||||
## General Tab Testing
|
||||
|
||||
### Logo Upload
|
||||
- [ ] Click "Upload Logo" button
|
||||
- [ ] Verify WordPress media library opens
|
||||
- [ ] Upload an image (recommended: 200x60px)
|
||||
- [ ] Verify image preview appears
|
||||
- [ ] Click "Remove Logo" button
|
||||
- [ ] Verify preview disappears and hidden input is cleared
|
||||
- [ ] Re-upload logo for further testing
|
||||
|
||||
### Favicon Upload
|
||||
- [ ] Click "Upload Favicon" button
|
||||
- [ ] Upload a favicon image (recommended: 32x32px)
|
||||
- [ ] Verify preview appears
|
||||
- [ ] Test remove functionality
|
||||
|
||||
### Breadcrumbs
|
||||
- [ ] Toggle breadcrumbs enable/disable switch
|
||||
- [ ] Verify the switch animation works
|
||||
- [ ] Change breadcrumb separator (try: >, /, », →)
|
||||
- [ ] Save settings
|
||||
|
||||
### Date/Time Format
|
||||
- [ ] Change date format (try: d/m/Y, m/d/Y, Y-m-d)
|
||||
- [ ] Change time format (try: H:i, g:i A)
|
||||
- [ ] Save settings
|
||||
|
||||
### Copyright Text
|
||||
- [ ] Edit copyright text
|
||||
- [ ] Try adding HTML (like `<strong>`, `<a>`)
|
||||
- [ ] Save and verify HTML is preserved (not stripped)
|
||||
|
||||
### Social Media Links
|
||||
- [ ] Add Facebook URL
|
||||
- [ ] Add Twitter URL
|
||||
- [ ] Add Instagram URL
|
||||
- [ ] Add LinkedIn URL
|
||||
- [ ] Add YouTube URL
|
||||
- [ ] Try invalid URLs (should show validation error)
|
||||
- [ ] Save valid URLs
|
||||
|
||||
## Content Tab Testing
|
||||
|
||||
### Excerpt Settings
|
||||
- [ ] Change excerpt length (try: 30, 55, 100)
|
||||
- [ ] Change excerpt more text (try: ..., [Read more], →)
|
||||
- [ ] Save settings
|
||||
|
||||
### Layout Settings
|
||||
- [ ] Change default post layout (Right Sidebar, Left Sidebar, No Sidebar)
|
||||
- [ ] Change default page layout
|
||||
- [ ] Save settings
|
||||
|
||||
### Archive Settings
|
||||
- [ ] Change archive posts per page (try: 10, 20, 0)
|
||||
- [ ] Save settings
|
||||
|
||||
### Display Options
|
||||
- [ ] Toggle "Show Featured Image" on single posts
|
||||
- [ ] Toggle "Show Author Box"
|
||||
- [ ] Toggle "Enable Comments on Posts"
|
||||
- [ ] Toggle "Enable Comments on Pages"
|
||||
- [ ] Toggle "Show Post Meta"
|
||||
- [ ] Toggle "Show Post Tags"
|
||||
- [ ] Toggle "Show Post Categories"
|
||||
- [ ] Save settings
|
||||
|
||||
## Performance Tab Testing
|
||||
|
||||
### Performance Options
|
||||
- [ ] Toggle "Enable Lazy Loading"
|
||||
- [ ] Toggle "Remove Emoji Scripts"
|
||||
- [ ] Toggle "Remove Embeds"
|
||||
- [ ] Toggle "Remove Dashicons"
|
||||
- [ ] Toggle "Defer JavaScript"
|
||||
- [ ] Toggle "Minify HTML"
|
||||
- [ ] Toggle "Disable Gutenberg"
|
||||
- [ ] Save settings
|
||||
- [ ] Verify front-end reflects changes (check page source)
|
||||
|
||||
## Related Posts Tab Testing
|
||||
|
||||
### Related Posts Configuration
|
||||
- [ ] Toggle "Enable Related Posts"
|
||||
- [ ] Verify that when disabled, other fields become disabled/grayed out
|
||||
- [ ] Enable related posts
|
||||
- [ ] Change number of related posts (try: 1, 3, 6, 12)
|
||||
- [ ] Change taxonomy (Category, Tag, Both)
|
||||
- [ ] Change related posts title
|
||||
- [ ] Change columns (2, 3, 4)
|
||||
- [ ] Save settings
|
||||
|
||||
## Advanced Tab Testing
|
||||
|
||||
### Custom CSS
|
||||
- [ ] Add custom CSS code (e.g., `body { background: #f0f0f0; }`)
|
||||
- [ ] Try adding `<script>` tags (should be removed on save)
|
||||
- [ ] Save settings
|
||||
- [ ] Check front-end page source for custom CSS in `<head>`
|
||||
|
||||
### Custom JavaScript
|
||||
- [ ] Add custom JS in header (e.g., `console.log('Header JS');`)
|
||||
- [ ] Add custom JS in footer (e.g., `console.log('Footer JS');`)
|
||||
- [ ] Try adding `<?php` tags (should be removed)
|
||||
- [ ] Save settings
|
||||
- [ ] Check front-end page source for scripts
|
||||
- [ ] Check browser console for log messages
|
||||
|
||||
## Form Validation Testing
|
||||
|
||||
### Required Fields
|
||||
- [ ] Try saving with empty required fields
|
||||
- [ ] Verify error highlighting appears
|
||||
- [ ] Verify scroll to first error works
|
||||
|
||||
### Number Fields
|
||||
- [ ] Try entering values below minimum
|
||||
- [ ] Try entering values above maximum
|
||||
- [ ] Try entering negative numbers where not allowed
|
||||
- [ ] Verify validation works
|
||||
|
||||
### URL Fields
|
||||
- [ ] Try invalid URLs (e.g., "not a url")
|
||||
- [ ] Verify validation shows error
|
||||
- [ ] Enter valid URLs
|
||||
- [ ] Save successfully
|
||||
|
||||
## Import/Export Testing
|
||||
|
||||
### Export
|
||||
- [ ] Click "Export Options" button
|
||||
- [ ] Verify JSON file downloads
|
||||
- [ ] Open file and verify it contains valid JSON
|
||||
- [ ] Verify all settings are in the export
|
||||
|
||||
### Import
|
||||
- [ ] Make some changes to settings
|
||||
- [ ] Click "Import Options" button
|
||||
- [ ] Verify modal opens
|
||||
- [ ] Paste invalid JSON (should show error)
|
||||
- [ ] Paste valid JSON from export
|
||||
- [ ] Click "Import" button
|
||||
- [ ] Verify success message appears
|
||||
- [ ] Verify page reloads
|
||||
- [ ] Confirm settings are restored
|
||||
|
||||
## Reset to Defaults Testing
|
||||
|
||||
- [ ] Make changes to various settings
|
||||
- [ ] Click "Reset to Defaults" button
|
||||
- [ ] Verify confirmation dialog appears
|
||||
- [ ] Cancel the dialog (settings should remain)
|
||||
- [ ] Click "Reset to Defaults" again
|
||||
- [ ] Confirm the reset
|
||||
- [ ] Verify success message
|
||||
- [ ] Verify page reloads
|
||||
- [ ] Confirm all settings are back to defaults
|
||||
|
||||
## Tab Navigation Testing
|
||||
|
||||
### Tab Switching
|
||||
- [ ] Click each tab and verify content switches
|
||||
- [ ] Verify active tab styling is correct
|
||||
- [ ] Check URL hash changes (e.g., #general, #content)
|
||||
- [ ] Refresh page with hash in URL
|
||||
- [ ] Verify correct tab loads on page load
|
||||
- [ ] Use browser back/forward buttons
|
||||
- [ ] Verify tabs respond to navigation
|
||||
|
||||
## Save Settings Testing
|
||||
|
||||
- [ ] Make changes in multiple tabs
|
||||
- [ ] Click "Save All Settings" button
|
||||
- [ ] Verify success message appears
|
||||
- [ ] Refresh page
|
||||
- [ ] Verify all changes persisted
|
||||
- [ ] Check database (`wp_options` table for `apus_theme_options`)
|
||||
|
||||
## Helper Functions Testing
|
||||
|
||||
Create a test page template and test each helper function:
|
||||
|
||||
```php
|
||||
// Test in a template file
|
||||
<?php
|
||||
// Test logo
|
||||
$logo = apus_get_logo_url();
|
||||
echo $logo ? 'Logo URL: ' . $logo : 'No logo set';
|
||||
|
||||
// Test breadcrumbs
|
||||
echo 'Breadcrumbs enabled: ' . (apus_show_breadcrumbs() ? 'Yes' : 'No');
|
||||
|
||||
// Test excerpt
|
||||
echo 'Excerpt length: ' . apus_get_excerpt_length();
|
||||
|
||||
// Test related posts
|
||||
echo 'Related posts enabled: ' . (apus_show_related_posts() ? 'Yes' : 'No');
|
||||
|
||||
// Test social links
|
||||
$social = apus_get_social_links();
|
||||
print_r($social);
|
||||
|
||||
// Test all options
|
||||
$all = apus_get_all_options();
|
||||
print_r($all);
|
||||
?>
|
||||
```
|
||||
|
||||
- [ ] Test each helper function returns expected values
|
||||
- [ ] Test default values when options not set
|
||||
- [ ] Test boolean helper functions return true/false
|
||||
- [ ] Test get_option with custom defaults
|
||||
|
||||
## Front-End Integration Testing
|
||||
|
||||
### Logo Display
|
||||
- [ ] Visit front-end site
|
||||
- [ ] Verify logo appears in header (if set)
|
||||
- [ ] Verify favicon appears in browser tab
|
||||
|
||||
### Breadcrumbs
|
||||
- [ ] Visit a single post
|
||||
- [ ] Verify breadcrumbs appear (if enabled)
|
||||
- [ ] Check separator is correct
|
||||
- [ ] Visit a category archive
|
||||
- [ ] Verify breadcrumbs work correctly
|
||||
|
||||
### Related Posts
|
||||
- [ ] Visit a single post
|
||||
- [ ] Scroll to bottom
|
||||
- [ ] Verify related posts appear (if enabled)
|
||||
- [ ] Check count matches settings
|
||||
- [ ] Verify title matches settings
|
||||
- [ ] Check layout columns are correct
|
||||
|
||||
### Comments
|
||||
- [ ] Visit a post (verify comments shown/hidden based on settings)
|
||||
- [ ] Visit a page (verify comments shown/hidden based on settings)
|
||||
|
||||
### Performance
|
||||
- [ ] Check page source for removed scripts (emoji, embeds, dashicons)
|
||||
- [ ] Check if lazy loading is applied to images
|
||||
- [ ] Check if custom CSS appears in head
|
||||
- [ ] Check if custom JS appears in head/footer
|
||||
|
||||
### Social Links
|
||||
- [ ] Check footer for social links
|
||||
- [ ] Verify all entered links appear
|
||||
- [ ] Test links open in new tab
|
||||
|
||||
### Copyright
|
||||
- [ ] Check footer for copyright text
|
||||
- [ ] Verify HTML formatting is preserved
|
||||
|
||||
## Responsive Testing
|
||||
|
||||
- [ ] Test options page on desktop (1920px)
|
||||
- [ ] Test on tablet (768px)
|
||||
- [ ] Test on mobile (375px)
|
||||
- [ ] Verify tabs switch to mobile layout
|
||||
- [ ] Verify forms remain usable
|
||||
- [ ] Test all buttons work on mobile
|
||||
|
||||
## Browser Compatibility Testing
|
||||
|
||||
- [ ] Test in Chrome
|
||||
- [ ] Test in Firefox
|
||||
- [ ] Test in Safari
|
||||
- [ ] Test in Edge
|
||||
- [ ] Verify all features work in each browser
|
||||
|
||||
## Accessibility Testing
|
||||
|
||||
- [ ] Test keyboard navigation (Tab, Enter, Escape)
|
||||
- [ ] Test with screen reader (NVDA, JAWS, or VoiceOver)
|
||||
- [ ] Verify focus indicators are visible
|
||||
- [ ] Check color contrast meets WCAG standards
|
||||
- [ ] Verify all images have alt text
|
||||
- [ ] Check form labels are properly associated
|
||||
|
||||
## Security Testing
|
||||
|
||||
- [ ] Verify nonces are checked on all AJAX calls
|
||||
- [ ] Test capability checks (log out and try accessing page)
|
||||
- [ ] Try injecting `<script>` tags in text fields
|
||||
- [ ] Try injecting SQL in fields
|
||||
- [ ] Try injecting PHP code in custom CSS/JS
|
||||
- [ ] Verify all outputs are escaped
|
||||
- [ ] Check CSRF protection works
|
||||
|
||||
## Performance Testing
|
||||
|
||||
- [ ] Check page load time of options page
|
||||
- [ ] Verify no memory leaks in browser
|
||||
- [ ] Check network tab for unnecessary requests
|
||||
- [ ] Verify scripts/styles only load on options page
|
||||
- [ ] Test with large amounts of data in textareas
|
||||
|
||||
## Error Handling Testing
|
||||
|
||||
- [ ] Disconnect from internet and try saving (should show error)
|
||||
- [ ] Modify nonce and try saving (should fail)
|
||||
- [ ] Try uploading very large image (should handle gracefully)
|
||||
- [ ] Try importing corrupted JSON (should show error)
|
||||
- [ ] Fill textarea with 100,000 characters (should save)
|
||||
|
||||
## Console Testing
|
||||
|
||||
Throughout all testing, monitor for:
|
||||
- [ ] JavaScript errors in console
|
||||
- [ ] PHP errors in server logs
|
||||
- [ ] WordPress debug.log errors
|
||||
- [ ] Network errors in Network tab
|
||||
- [ ] Deprecation warnings
|
||||
|
||||
## Final Verification
|
||||
|
||||
- [ ] All settings save correctly
|
||||
- [ ] All settings load correctly on page refresh
|
||||
- [ ] Front-end reflects all settings changes
|
||||
- [ ] No JavaScript errors anywhere
|
||||
- [ ] No PHP errors/warnings
|
||||
- [ ] No console errors
|
||||
- [ ] Page performance is acceptable
|
||||
- [ ] Mobile experience is good
|
||||
- [ ] Accessibility is maintained
|
||||
|
||||
## Sign-Off
|
||||
|
||||
Tested by: _______________
|
||||
Date: _______________
|
||||
Version: 1.0.0
|
||||
Browser(s): _______________
|
||||
WordPress Version: _______________
|
||||
PHP Version: _______________
|
||||
|
||||
All tests passed: [ ] Yes [ ] No
|
||||
|
||||
Issues found (if any):
|
||||
______________________________
|
||||
______________________________
|
||||
______________________________
|
||||
394
wp-content/themes/apus-theme/inc/admin/USAGE-EXAMPLES.php
Normal file
394
wp-content/themes/apus-theme/inc/admin/USAGE-EXAMPLES.php
Normal file
@@ -0,0 +1,394 @@
|
||||
<?php
|
||||
/**
|
||||
* Theme Options Usage Examples
|
||||
*
|
||||
* This file contains examples of how to use theme options throughout the theme.
|
||||
* DO NOT include this file in functions.php - it's for reference only.
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 1: Using options in header.php
|
||||
*/
|
||||
function example_display_logo() {
|
||||
$logo_url = apus_get_logo_url();
|
||||
|
||||
if ($logo_url) {
|
||||
?>
|
||||
<a href="<?php echo esc_url(home_url('/')); ?>" class="custom-logo-link">
|
||||
<img src="<?php echo esc_url($logo_url); ?>" alt="<?php bloginfo('name'); ?>" class="custom-logo" />
|
||||
</a>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<h1 class="site-title">
|
||||
<a href="<?php echo esc_url(home_url('/')); ?>"><?php bloginfo('name'); ?></a>
|
||||
</h1>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 2: Displaying breadcrumbs
|
||||
*/
|
||||
function example_show_breadcrumbs() {
|
||||
if (apus_show_breadcrumbs() && !is_front_page()) {
|
||||
$separator = apus_get_breadcrumb_separator();
|
||||
|
||||
echo '<nav class="breadcrumbs">';
|
||||
echo '<a href="' . esc_url(home_url('/')) . '">Home</a>';
|
||||
echo ' ' . esc_html($separator) . ' ';
|
||||
|
||||
if (is_single()) {
|
||||
the_category(' ' . esc_html($separator) . ' ');
|
||||
echo ' ' . esc_html($separator) . ' ';
|
||||
the_title();
|
||||
} elseif (is_category()) {
|
||||
single_cat_title();
|
||||
}
|
||||
|
||||
echo '</nav>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 3: Customizing excerpt
|
||||
*/
|
||||
function example_custom_excerpt_length($length) {
|
||||
return apus_get_excerpt_length();
|
||||
}
|
||||
add_filter('excerpt_length', 'example_custom_excerpt_length');
|
||||
|
||||
function example_custom_excerpt_more($more) {
|
||||
return apus_get_excerpt_more();
|
||||
}
|
||||
add_filter('excerpt_more', 'example_custom_excerpt_more');
|
||||
|
||||
/**
|
||||
* EXAMPLE 4: Displaying related posts in single.php
|
||||
*/
|
||||
function example_display_related_posts() {
|
||||
if (apus_show_related_posts() && is_single()) {
|
||||
$count = apus_get_related_posts_count();
|
||||
$taxonomy = apus_get_related_posts_taxonomy();
|
||||
$title = apus_get_related_posts_title();
|
||||
|
||||
// Get related posts
|
||||
$post_id = get_the_ID();
|
||||
$args = array(
|
||||
'posts_per_page' => $count,
|
||||
'post__not_in' => array($post_id),
|
||||
);
|
||||
|
||||
if ($taxonomy === 'category') {
|
||||
$categories = wp_get_post_categories($post_id);
|
||||
if ($categories) {
|
||||
$args['category__in'] = $categories;
|
||||
}
|
||||
} elseif ($taxonomy === 'tag') {
|
||||
$tags = wp_get_post_tags($post_id, array('fields' => 'ids'));
|
||||
if ($tags) {
|
||||
$args['tag__in'] = $tags;
|
||||
}
|
||||
}
|
||||
|
||||
$related = new WP_Query($args);
|
||||
|
||||
if ($related->have_posts()) {
|
||||
?>
|
||||
<div class="related-posts">
|
||||
<h3><?php echo esc_html($title); ?></h3>
|
||||
<div class="related-posts-grid">
|
||||
<?php
|
||||
while ($related->have_posts()) {
|
||||
$related->the_post();
|
||||
?>
|
||||
<article class="related-post-item">
|
||||
<?php if (has_post_thumbnail()) : ?>
|
||||
<a href="<?php the_permalink(); ?>">
|
||||
<?php the_post_thumbnail('apus-thumbnail'); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<h4>
|
||||
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
|
||||
</h4>
|
||||
<div class="post-meta">
|
||||
<time datetime="<?php echo get_the_date('c'); ?>">
|
||||
<?php echo get_the_date(apus_get_date_format()); ?>
|
||||
</time>
|
||||
</div>
|
||||
</article>
|
||||
<?php
|
||||
}
|
||||
wp_reset_postdata();
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 5: Conditional comments display
|
||||
*/
|
||||
function example_maybe_show_comments() {
|
||||
if (is_single() && apus_comments_enabled_for_posts()) {
|
||||
comments_template();
|
||||
} elseif (is_page() && apus_comments_enabled_for_pages()) {
|
||||
comments_template();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 6: Featured image on single posts
|
||||
*/
|
||||
function example_display_featured_image() {
|
||||
if (is_single() && apus_show_featured_image_single() && has_post_thumbnail()) {
|
||||
?>
|
||||
<div class="post-thumbnail">
|
||||
<?php the_post_thumbnail('apus-featured-large'); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 7: Author box on single posts
|
||||
*/
|
||||
function example_display_author_box() {
|
||||
if (is_single() && apus_show_author_box()) {
|
||||
$author_id = get_the_author_meta('ID');
|
||||
?>
|
||||
<div class="author-box">
|
||||
<div class="author-avatar">
|
||||
<?php echo get_avatar($author_id, 80); ?>
|
||||
</div>
|
||||
<div class="author-info">
|
||||
<h4 class="author-name"><?php the_author(); ?></h4>
|
||||
<p class="author-bio"><?php the_author_meta('description'); ?></p>
|
||||
<a href="<?php echo get_author_posts_url($author_id); ?>" class="author-link">
|
||||
<?php _e('View all posts', 'apus-theme'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 8: Social media links in footer
|
||||
*/
|
||||
function example_display_social_links() {
|
||||
$social_links = apus_get_social_links();
|
||||
|
||||
// Filter out empty links
|
||||
$social_links = array_filter($social_links);
|
||||
|
||||
if (!empty($social_links)) {
|
||||
?>
|
||||
<div class="social-links">
|
||||
<?php foreach ($social_links as $network => $url) : ?>
|
||||
<a href="<?php echo esc_url($url); ?>"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="social-link social-<?php echo esc_attr($network); ?>">
|
||||
<span class="screen-reader-text"><?php echo ucfirst($network); ?></span>
|
||||
<i class="icon-<?php echo esc_attr($network); ?>"></i>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 9: Copyright text in footer
|
||||
*/
|
||||
function example_display_copyright() {
|
||||
$copyright = apus_get_copyright_text();
|
||||
|
||||
if ($copyright) {
|
||||
echo '<div class="copyright">' . wp_kses_post($copyright) . '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 10: Custom CSS in header
|
||||
*/
|
||||
function example_add_custom_css() {
|
||||
$custom_css = apus_get_custom_css();
|
||||
|
||||
if ($custom_css) {
|
||||
echo '<style type="text/css">' . "\n";
|
||||
echo strip_tags($custom_css);
|
||||
echo "\n</style>\n";
|
||||
}
|
||||
}
|
||||
add_action('wp_head', 'example_add_custom_css', 100);
|
||||
|
||||
/**
|
||||
* EXAMPLE 11: Custom JS in header
|
||||
*/
|
||||
function example_add_custom_js_header() {
|
||||
$custom_js = apus_get_custom_js_header();
|
||||
|
||||
if ($custom_js) {
|
||||
echo '<script type="text/javascript">' . "\n";
|
||||
echo $custom_js;
|
||||
echo "\n</script>\n";
|
||||
}
|
||||
}
|
||||
add_action('wp_head', 'example_add_custom_js_header', 100);
|
||||
|
||||
/**
|
||||
* EXAMPLE 12: Custom JS in footer
|
||||
*/
|
||||
function example_add_custom_js_footer() {
|
||||
$custom_js = apus_get_custom_js_footer();
|
||||
|
||||
if ($custom_js) {
|
||||
echo '<script type="text/javascript">' . "\n";
|
||||
echo $custom_js;
|
||||
echo "\n</script>\n";
|
||||
}
|
||||
}
|
||||
add_action('wp_footer', 'example_add_custom_js_footer', 100);
|
||||
|
||||
/**
|
||||
* EXAMPLE 13: Posts per page for archives
|
||||
*/
|
||||
function example_set_archive_posts_per_page($query) {
|
||||
if ($query->is_archive() && !is_admin() && $query->is_main_query()) {
|
||||
$posts_per_page = apus_get_archive_posts_per_page();
|
||||
$query->set('posts_per_page', $posts_per_page);
|
||||
}
|
||||
}
|
||||
add_action('pre_get_posts', 'example_set_archive_posts_per_page');
|
||||
|
||||
/**
|
||||
* EXAMPLE 14: Performance optimizations
|
||||
*/
|
||||
function example_apply_performance_settings() {
|
||||
// Remove emoji scripts
|
||||
if (apus_is_performance_enabled('remove_emoji')) {
|
||||
remove_action('wp_head', 'print_emoji_detection_script', 7);
|
||||
remove_action('wp_print_styles', 'print_emoji_styles');
|
||||
}
|
||||
|
||||
// Remove embeds
|
||||
if (apus_is_performance_enabled('remove_embeds')) {
|
||||
wp_deregister_script('wp-embed');
|
||||
}
|
||||
|
||||
// Remove Dashicons for non-logged users
|
||||
if (apus_is_performance_enabled('remove_dashicons') && !is_user_logged_in()) {
|
||||
wp_deregister_style('dashicons');
|
||||
}
|
||||
}
|
||||
add_action('wp_enqueue_scripts', 'example_apply_performance_settings', 100);
|
||||
|
||||
/**
|
||||
* EXAMPLE 15: Lazy loading images
|
||||
*/
|
||||
function example_add_lazy_loading($attr, $attachment, $size) {
|
||||
if (apus_is_lazy_loading_enabled()) {
|
||||
$attr['loading'] = 'lazy';
|
||||
}
|
||||
return $attr;
|
||||
}
|
||||
add_filter('wp_get_attachment_image_attributes', 'example_add_lazy_loading', 10, 3);
|
||||
|
||||
/**
|
||||
* EXAMPLE 16: Layout classes based on settings
|
||||
*/
|
||||
function example_get_layout_class() {
|
||||
$layout = 'right-sidebar'; // default
|
||||
|
||||
if (is_single()) {
|
||||
$layout = apus_get_default_post_layout();
|
||||
} elseif (is_page()) {
|
||||
$layout = apus_get_default_page_layout();
|
||||
}
|
||||
|
||||
return 'layout-' . $layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 17: Display post meta conditionally
|
||||
*/
|
||||
function example_display_post_meta() {
|
||||
if (!apus_get_option('show_post_meta', true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="post-meta">
|
||||
<span class="post-date">
|
||||
<time datetime="<?php echo get_the_date('c'); ?>">
|
||||
<?php echo get_the_date(apus_get_date_format()); ?>
|
||||
</time>
|
||||
</span>
|
||||
<span class="post-author">
|
||||
<?php the_author(); ?>
|
||||
</span>
|
||||
<?php if (apus_get_option('show_post_categories', true)) : ?>
|
||||
<span class="post-categories">
|
||||
<?php the_category(', '); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 18: Display post tags conditionally
|
||||
*/
|
||||
function example_display_post_tags() {
|
||||
if (is_single() && apus_get_option('show_post_tags', true)) {
|
||||
the_tags('<div class="post-tags">', ', ', '</div>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EXAMPLE 19: Get all options (for debugging)
|
||||
*/
|
||||
function example_debug_all_options() {
|
||||
if (current_user_can('manage_options') && isset($_GET['debug_options'])) {
|
||||
$all_options = apus_get_all_options();
|
||||
echo '<pre>';
|
||||
print_r($all_options);
|
||||
echo '</pre>';
|
||||
}
|
||||
}
|
||||
add_action('wp_footer', 'example_debug_all_options');
|
||||
|
||||
/**
|
||||
* EXAMPLE 20: Check if specific feature is enabled
|
||||
*/
|
||||
function example_check_feature() {
|
||||
// Multiple ways to check boolean options
|
||||
|
||||
// Method 1: Using helper function
|
||||
if (apus_is_option_enabled('enable_breadcrumbs')) {
|
||||
// Breadcrumbs are enabled
|
||||
}
|
||||
|
||||
// Method 2: Using get_option with default
|
||||
if (apus_get_option('enable_related_posts', true)) {
|
||||
// Related posts are enabled
|
||||
}
|
||||
|
||||
// Method 3: Direct check
|
||||
$options = apus_get_all_options();
|
||||
if (isset($options['enable_lazy_loading']) && $options['enable_lazy_loading']) {
|
||||
// Lazy loading is enabled
|
||||
}
|
||||
}
|
||||
282
wp-content/themes/apus-theme/inc/admin/options-api.php
Normal file
282
wp-content/themes/apus-theme/inc/admin/options-api.php
Normal file
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
/**
|
||||
* Theme Options Settings API
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all theme settings
|
||||
*/
|
||||
function apus_register_settings() {
|
||||
// Register main options group
|
||||
register_setting(
|
||||
'apus_theme_options_group',
|
||||
'apus_theme_options',
|
||||
array(
|
||||
'sanitize_callback' => 'apus_sanitize_options',
|
||||
'default' => apus_get_default_options(),
|
||||
)
|
||||
);
|
||||
|
||||
// General Settings Section
|
||||
add_settings_section(
|
||||
'apus_general_section',
|
||||
__('General Settings', 'apus-theme'),
|
||||
'apus_general_section_callback',
|
||||
'apus-theme-options'
|
||||
);
|
||||
|
||||
// Content Settings Section
|
||||
add_settings_section(
|
||||
'apus_content_section',
|
||||
__('Content Settings', 'apus-theme'),
|
||||
'apus_content_section_callback',
|
||||
'apus-theme-options'
|
||||
);
|
||||
|
||||
// Performance Settings Section
|
||||
add_settings_section(
|
||||
'apus_performance_section',
|
||||
__('Performance Settings', 'apus-theme'),
|
||||
'apus_performance_section_callback',
|
||||
'apus-theme-options'
|
||||
);
|
||||
|
||||
// Related Posts Settings Section
|
||||
add_settings_section(
|
||||
'apus_related_posts_section',
|
||||
__('Related Posts Settings', 'apus-theme'),
|
||||
'apus_related_posts_section_callback',
|
||||
'apus-theme-options'
|
||||
);
|
||||
}
|
||||
add_action('admin_init', 'apus_register_settings');
|
||||
|
||||
/**
|
||||
* Get default options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function apus_get_default_options() {
|
||||
return array(
|
||||
// General
|
||||
'site_logo' => 0,
|
||||
'site_favicon' => 0,
|
||||
'enable_breadcrumbs' => true,
|
||||
'breadcrumb_separator' => '>',
|
||||
'date_format' => 'd/m/Y',
|
||||
'time_format' => 'H:i',
|
||||
'copyright_text' => sprintf(__('© %s %s. All rights reserved.', 'apus-theme'), date('Y'), get_bloginfo('name')),
|
||||
'social_facebook' => '',
|
||||
'social_twitter' => '',
|
||||
'social_instagram' => '',
|
||||
'social_linkedin' => '',
|
||||
'social_youtube' => '',
|
||||
|
||||
// Content
|
||||
'excerpt_length' => 55,
|
||||
'excerpt_more' => '...',
|
||||
'default_post_layout' => 'right-sidebar',
|
||||
'default_page_layout' => 'right-sidebar',
|
||||
'archive_posts_per_page' => 10,
|
||||
'show_featured_image_single' => true,
|
||||
'show_author_box' => true,
|
||||
'enable_comments_posts' => true,
|
||||
'enable_comments_pages' => false,
|
||||
'show_post_meta' => true,
|
||||
'show_post_tags' => true,
|
||||
'show_post_categories' => true,
|
||||
|
||||
// Performance
|
||||
'enable_lazy_loading' => true,
|
||||
'performance_remove_emoji' => true,
|
||||
'performance_remove_embeds' => false,
|
||||
'performance_remove_dashicons' => true,
|
||||
'performance_defer_js' => false,
|
||||
'performance_minify_html' => false,
|
||||
'performance_disable_gutenberg' => false,
|
||||
|
||||
// Related Posts
|
||||
'enable_related_posts' => true,
|
||||
'related_posts_count' => 3,
|
||||
'related_posts_taxonomy' => 'category',
|
||||
'related_posts_title' => __('Related Posts', 'apus-theme'),
|
||||
'related_posts_columns' => 3,
|
||||
|
||||
// Advanced
|
||||
'custom_css' => '',
|
||||
'custom_js_header' => '',
|
||||
'custom_js_footer' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Section Callbacks
|
||||
*/
|
||||
function apus_general_section_callback() {
|
||||
echo '<p>' . __('Configure general theme settings including logo, branding, and social media.', 'apus-theme') . '</p>';
|
||||
}
|
||||
|
||||
function apus_content_section_callback() {
|
||||
echo '<p>' . __('Configure content display settings for posts, pages, and archives.', 'apus-theme') . '</p>';
|
||||
}
|
||||
|
||||
function apus_performance_section_callback() {
|
||||
echo '<p>' . __('Optimize your site performance with these settings.', 'apus-theme') . '</p>';
|
||||
}
|
||||
|
||||
function apus_related_posts_section_callback() {
|
||||
echo '<p>' . __('Configure related posts display on single post pages.', 'apus-theme') . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize all options
|
||||
*
|
||||
* @param array $input The input array
|
||||
* @return array The sanitized array
|
||||
*/
|
||||
function apus_sanitize_options($input) {
|
||||
$sanitized = array();
|
||||
|
||||
if (!is_array($input)) {
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
// General Settings
|
||||
$sanitized['site_logo'] = isset($input['site_logo']) ? absint($input['site_logo']) : 0;
|
||||
$sanitized['site_favicon'] = isset($input['site_favicon']) ? absint($input['site_favicon']) : 0;
|
||||
$sanitized['enable_breadcrumbs'] = isset($input['enable_breadcrumbs']) ? (bool) $input['enable_breadcrumbs'] : false;
|
||||
$sanitized['breadcrumb_separator'] = isset($input['breadcrumb_separator']) ? sanitize_text_field($input['breadcrumb_separator']) : '>';
|
||||
$sanitized['date_format'] = isset($input['date_format']) ? sanitize_text_field($input['date_format']) : 'd/m/Y';
|
||||
$sanitized['time_format'] = isset($input['time_format']) ? sanitize_text_field($input['time_format']) : 'H:i';
|
||||
$sanitized['copyright_text'] = isset($input['copyright_text']) ? wp_kses_post($input['copyright_text']) : '';
|
||||
|
||||
// Social Media
|
||||
$social_fields = array('facebook', 'twitter', 'instagram', 'linkedin', 'youtube');
|
||||
foreach ($social_fields as $social) {
|
||||
$key = 'social_' . $social;
|
||||
$sanitized[$key] = isset($input[$key]) ? esc_url_raw($input[$key]) : '';
|
||||
}
|
||||
|
||||
// Content Settings
|
||||
$sanitized['excerpt_length'] = isset($input['excerpt_length']) ? absint($input['excerpt_length']) : 55;
|
||||
$sanitized['excerpt_more'] = isset($input['excerpt_more']) ? sanitize_text_field($input['excerpt_more']) : '...';
|
||||
$sanitized['default_post_layout'] = isset($input['default_post_layout']) ? sanitize_text_field($input['default_post_layout']) : 'right-sidebar';
|
||||
$sanitized['default_page_layout'] = isset($input['default_page_layout']) ? sanitize_text_field($input['default_page_layout']) : 'right-sidebar';
|
||||
$sanitized['archive_posts_per_page'] = isset($input['archive_posts_per_page']) ? absint($input['archive_posts_per_page']) : 10;
|
||||
$sanitized['show_featured_image_single'] = isset($input['show_featured_image_single']) ? (bool) $input['show_featured_image_single'] : false;
|
||||
$sanitized['show_author_box'] = isset($input['show_author_box']) ? (bool) $input['show_author_box'] : false;
|
||||
$sanitized['enable_comments_posts'] = isset($input['enable_comments_posts']) ? (bool) $input['enable_comments_posts'] : false;
|
||||
$sanitized['enable_comments_pages'] = isset($input['enable_comments_pages']) ? (bool) $input['enable_comments_pages'] : false;
|
||||
$sanitized['show_post_meta'] = isset($input['show_post_meta']) ? (bool) $input['show_post_meta'] : false;
|
||||
$sanitized['show_post_tags'] = isset($input['show_post_tags']) ? (bool) $input['show_post_tags'] : false;
|
||||
$sanitized['show_post_categories'] = isset($input['show_post_categories']) ? (bool) $input['show_post_categories'] : false;
|
||||
|
||||
// Performance Settings
|
||||
$sanitized['enable_lazy_loading'] = isset($input['enable_lazy_loading']) ? (bool) $input['enable_lazy_loading'] : false;
|
||||
$sanitized['performance_remove_emoji'] = isset($input['performance_remove_emoji']) ? (bool) $input['performance_remove_emoji'] : false;
|
||||
$sanitized['performance_remove_embeds'] = isset($input['performance_remove_embeds']) ? (bool) $input['performance_remove_embeds'] : false;
|
||||
$sanitized['performance_remove_dashicons'] = isset($input['performance_remove_dashicons']) ? (bool) $input['performance_remove_dashicons'] : false;
|
||||
$sanitized['performance_defer_js'] = isset($input['performance_defer_js']) ? (bool) $input['performance_defer_js'] : false;
|
||||
$sanitized['performance_minify_html'] = isset($input['performance_minify_html']) ? (bool) $input['performance_minify_html'] : false;
|
||||
$sanitized['performance_disable_gutenberg'] = isset($input['performance_disable_gutenberg']) ? (bool) $input['performance_disable_gutenberg'] : false;
|
||||
|
||||
// Related Posts
|
||||
$sanitized['enable_related_posts'] = isset($input['enable_related_posts']) ? (bool) $input['enable_related_posts'] : false;
|
||||
$sanitized['related_posts_count'] = isset($input['related_posts_count']) ? absint($input['related_posts_count']) : 3;
|
||||
$sanitized['related_posts_taxonomy'] = isset($input['related_posts_taxonomy']) ? sanitize_text_field($input['related_posts_taxonomy']) : 'category';
|
||||
$sanitized['related_posts_title'] = isset($input['related_posts_title']) ? sanitize_text_field($input['related_posts_title']) : __('Related Posts', 'apus-theme');
|
||||
$sanitized['related_posts_columns'] = isset($input['related_posts_columns']) ? absint($input['related_posts_columns']) : 3;
|
||||
|
||||
// Advanced Settings
|
||||
$sanitized['custom_css'] = isset($input['custom_css']) ? apus_sanitize_css($input['custom_css']) : '';
|
||||
$sanitized['custom_js_header'] = isset($input['custom_js_header']) ? apus_sanitize_js($input['custom_js_header']) : '';
|
||||
$sanitized['custom_js_footer'] = isset($input['custom_js_footer']) ? apus_sanitize_js($input['custom_js_footer']) : '';
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize CSS
|
||||
*
|
||||
* @param string $css The CSS string
|
||||
* @return string The sanitized CSS
|
||||
*/
|
||||
function apus_sanitize_css($css) {
|
||||
// Remove <script> tags
|
||||
$css = preg_replace('#<script(.*?)>(.*?)</script>#is', '', $css);
|
||||
// Remove potential PHP code
|
||||
$css = preg_replace('#<\?php(.*?)\?>#is', '', $css);
|
||||
return wp_strip_all_tags($css);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize JavaScript
|
||||
*
|
||||
* @param string $js The JavaScript string
|
||||
* @return string The sanitized JavaScript
|
||||
*/
|
||||
function apus_sanitize_js($js) {
|
||||
// Remove <script> tags if present
|
||||
$js = preg_replace('#<script(.*?)>(.*?)</script>#is', '$2', $js);
|
||||
// Remove potential PHP code
|
||||
$js = preg_replace('#<\?php(.*?)\?>#is', '', $js);
|
||||
return trim($js);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize checkbox input
|
||||
*
|
||||
* @param mixed $input The input value
|
||||
* @return bool
|
||||
*/
|
||||
function apus_sanitize_checkbox($input) {
|
||||
return (bool) $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize integer input
|
||||
*
|
||||
* @param mixed $input The input value
|
||||
* @return int
|
||||
*/
|
||||
function apus_sanitize_integer($input) {
|
||||
return absint($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize text field
|
||||
*
|
||||
* @param string $input The input value
|
||||
* @return string
|
||||
*/
|
||||
function apus_sanitize_text($input) {
|
||||
return sanitize_text_field($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize URL
|
||||
*
|
||||
* @param string $input The input value
|
||||
* @return string
|
||||
*/
|
||||
function apus_sanitize_url($input) {
|
||||
return esc_url_raw($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize HTML content
|
||||
*
|
||||
* @param string $input The input value
|
||||
* @return string
|
||||
*/
|
||||
function apus_sanitize_html($input) {
|
||||
return wp_kses_post($input);
|
||||
}
|
||||
661
wp-content/themes/apus-theme/inc/admin/options-page-template.php
Normal file
661
wp-content/themes/apus-theme/inc/admin/options-page-template.php
Normal file
@@ -0,0 +1,661 @@
|
||||
<?php
|
||||
/**
|
||||
* Theme Options Page Template
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get current options
|
||||
$options = get_option('apus_theme_options', apus_get_default_options());
|
||||
?>
|
||||
|
||||
<div class="wrap apus-theme-options">
|
||||
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||
|
||||
<div class="apus-options-header">
|
||||
<div class="apus-options-logo">
|
||||
<h2><?php _e('Apus Theme', 'apus-theme'); ?></h2>
|
||||
<span class="version"><?php echo 'v' . APUS_VERSION; ?></span>
|
||||
</div>
|
||||
<div class="apus-options-actions">
|
||||
<button type="button" class="button button-secondary" id="apus-export-options">
|
||||
<span class="dashicons dashicons-download"></span>
|
||||
<?php _e('Export Options', 'apus-theme'); ?>
|
||||
</button>
|
||||
<button type="button" class="button button-secondary" id="apus-import-options">
|
||||
<span class="dashicons dashicons-upload"></span>
|
||||
<?php _e('Import Options', 'apus-theme'); ?>
|
||||
</button>
|
||||
<button type="button" class="button button-secondary" id="apus-reset-options">
|
||||
<span class="dashicons dashicons-image-rotate"></span>
|
||||
<?php _e('Reset to Defaults', 'apus-theme'); ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" action="options.php" class="apus-options-form">
|
||||
<?php
|
||||
settings_fields('apus_theme_options_group');
|
||||
?>
|
||||
|
||||
<div class="apus-options-container">
|
||||
<!-- Tabs Navigation -->
|
||||
<div class="apus-tabs-nav">
|
||||
<ul>
|
||||
<li class="active">
|
||||
<a href="#general" data-tab="general">
|
||||
<span class="dashicons dashicons-admin-settings"></span>
|
||||
<?php _e('General', 'apus-theme'); ?>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#content" data-tab="content">
|
||||
<span class="dashicons dashicons-edit-page"></span>
|
||||
<?php _e('Content', 'apus-theme'); ?>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#performance" data-tab="performance">
|
||||
<span class="dashicons dashicons-performance"></span>
|
||||
<?php _e('Performance', 'apus-theme'); ?>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#related-posts" data-tab="related-posts">
|
||||
<span class="dashicons dashicons-admin-links"></span>
|
||||
<?php _e('Related Posts', 'apus-theme'); ?>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#advanced" data-tab="advanced">
|
||||
<span class="dashicons dashicons-admin-tools"></span>
|
||||
<?php _e('Advanced', 'apus-theme'); ?>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Tabs Content -->
|
||||
<div class="apus-tabs-content">
|
||||
|
||||
<!-- General Tab -->
|
||||
<div id="general" class="apus-tab-pane active">
|
||||
<h2><?php _e('General Settings', 'apus-theme'); ?></h2>
|
||||
<p class="description"><?php _e('Configure general theme settings including logo, branding, and social media.', 'apus-theme'); ?></p>
|
||||
|
||||
<table class="form-table">
|
||||
<!-- Site Logo -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="site_logo"><?php _e('Site Logo', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<div class="apus-image-upload">
|
||||
<input type="hidden" name="apus_theme_options[site_logo]" id="site_logo" value="<?php echo esc_attr($options['site_logo'] ?? 0); ?>" class="apus-image-id" />
|
||||
<div class="apus-image-preview">
|
||||
<?php
|
||||
$logo_id = $options['site_logo'] ?? 0;
|
||||
if ($logo_id) {
|
||||
echo wp_get_attachment_image($logo_id, 'medium', false, array('class' => 'apus-preview-image'));
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<button type="button" class="button apus-upload-image"><?php _e('Upload Logo', 'apus-theme'); ?></button>
|
||||
<button type="button" class="button apus-remove-image" <?php echo (!$logo_id ? 'style="display:none;"' : ''); ?>><?php _e('Remove Logo', 'apus-theme'); ?></button>
|
||||
<p class="description"><?php _e('Upload your site logo. Recommended size: 200x60px', 'apus-theme'); ?></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Site Favicon -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="site_favicon"><?php _e('Site Favicon', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<div class="apus-image-upload">
|
||||
<input type="hidden" name="apus_theme_options[site_favicon]" id="site_favicon" value="<?php echo esc_attr($options['site_favicon'] ?? 0); ?>" class="apus-image-id" />
|
||||
<div class="apus-image-preview">
|
||||
<?php
|
||||
$favicon_id = $options['site_favicon'] ?? 0;
|
||||
if ($favicon_id) {
|
||||
echo wp_get_attachment_image($favicon_id, 'thumbnail', false, array('class' => 'apus-preview-image'));
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<button type="button" class="button apus-upload-image"><?php _e('Upload Favicon', 'apus-theme'); ?></button>
|
||||
<button type="button" class="button apus-remove-image" <?php echo (!$favicon_id ? 'style="display:none;"' : ''); ?>><?php _e('Remove Favicon', 'apus-theme'); ?></button>
|
||||
<p class="description"><?php _e('Upload your site favicon. Recommended size: 32x32px or 64x64px', 'apus-theme'); ?></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Enable Breadcrumbs -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="enable_breadcrumbs"><?php _e('Enable Breadcrumbs', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[enable_breadcrumbs]" id="enable_breadcrumbs" value="1" <?php checked(isset($options['enable_breadcrumbs']) ? $options['enable_breadcrumbs'] : true, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Show breadcrumbs navigation on pages and posts', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Breadcrumb Separator -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="breadcrumb_separator"><?php _e('Breadcrumb Separator', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" name="apus_theme_options[breadcrumb_separator]" id="breadcrumb_separator" value="<?php echo esc_attr($options['breadcrumb_separator'] ?? '>'); ?>" class="regular-text" />
|
||||
<p class="description"><?php _e('Character or symbol to separate breadcrumb items (e.g., >, /, »)', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Date Format -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="date_format"><?php _e('Date Format', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" name="apus_theme_options[date_format]" id="date_format" value="<?php echo esc_attr($options['date_format'] ?? 'd/m/Y'); ?>" class="regular-text" />
|
||||
<p class="description"><?php _e('PHP date format (e.g., d/m/Y, m/d/Y, Y-m-d)', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Time Format -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="time_format"><?php _e('Time Format', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" name="apus_theme_options[time_format]" id="time_format" value="<?php echo esc_attr($options['time_format'] ?? 'H:i'); ?>" class="regular-text" />
|
||||
<p class="description"><?php _e('PHP time format (e.g., H:i, g:i A)', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Copyright Text -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="copyright_text"><?php _e('Copyright Text', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<textarea name="apus_theme_options[copyright_text]" id="copyright_text" rows="3" class="large-text"><?php echo esc_textarea($options['copyright_text'] ?? sprintf(__('© %s %s. All rights reserved.', 'apus-theme'), date('Y'), get_bloginfo('name'))); ?></textarea>
|
||||
<p class="description"><?php _e('Footer copyright text. HTML allowed.', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3><?php _e('Social Media Links', 'apus-theme'); ?></h3>
|
||||
<table class="form-table">
|
||||
<!-- Facebook -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="social_facebook"><?php _e('Facebook URL', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="url" name="apus_theme_options[social_facebook]" id="social_facebook" value="<?php echo esc_url($options['social_facebook'] ?? ''); ?>" class="regular-text" placeholder="https://facebook.com/yourpage" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Twitter -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="social_twitter"><?php _e('Twitter URL', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="url" name="apus_theme_options[social_twitter]" id="social_twitter" value="<?php echo esc_url($options['social_twitter'] ?? ''); ?>" class="regular-text" placeholder="https://twitter.com/youraccount" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Instagram -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="social_instagram"><?php _e('Instagram URL', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="url" name="apus_theme_options[social_instagram]" id="social_instagram" value="<?php echo esc_url($options['social_instagram'] ?? ''); ?>" class="regular-text" placeholder="https://instagram.com/youraccount" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- LinkedIn -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="social_linkedin"><?php _e('LinkedIn URL', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="url" name="apus_theme_options[social_linkedin]" id="social_linkedin" value="<?php echo esc_url($options['social_linkedin'] ?? ''); ?>" class="regular-text" placeholder="https://linkedin.com/company/yourcompany" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- YouTube -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="social_youtube"><?php _e('YouTube URL', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="url" name="apus_theme_options[social_youtube]" id="social_youtube" value="<?php echo esc_url($options['social_youtube'] ?? ''); ?>" class="regular-text" placeholder="https://youtube.com/yourchannel" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Content Tab -->
|
||||
<div id="content" class="apus-tab-pane">
|
||||
<h2><?php _e('Content Settings', 'apus-theme'); ?></h2>
|
||||
<p class="description"><?php _e('Configure content display settings for posts, pages, and archives.', 'apus-theme'); ?></p>
|
||||
|
||||
<table class="form-table">
|
||||
<!-- Excerpt Length -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="excerpt_length"><?php _e('Excerpt Length', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="number" name="apus_theme_options[excerpt_length]" id="excerpt_length" value="<?php echo esc_attr($options['excerpt_length'] ?? 55); ?>" class="small-text" min="10" max="500" />
|
||||
<p class="description"><?php _e('Number of words to show in excerpt', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Excerpt More -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="excerpt_more"><?php _e('Excerpt More Text', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" name="apus_theme_options[excerpt_more]" id="excerpt_more" value="<?php echo esc_attr($options['excerpt_more'] ?? '...'); ?>" class="regular-text" />
|
||||
<p class="description"><?php _e('Text to append at the end of excerpts', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Default Post Layout -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="default_post_layout"><?php _e('Default Post Layout', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<select name="apus_theme_options[default_post_layout]" id="default_post_layout">
|
||||
<option value="right-sidebar" <?php selected($options['default_post_layout'] ?? 'right-sidebar', 'right-sidebar'); ?>><?php _e('Right Sidebar', 'apus-theme'); ?></option>
|
||||
<option value="left-sidebar" <?php selected($options['default_post_layout'] ?? 'right-sidebar', 'left-sidebar'); ?>><?php _e('Left Sidebar', 'apus-theme'); ?></option>
|
||||
<option value="no-sidebar" <?php selected($options['default_post_layout'] ?? 'right-sidebar', 'no-sidebar'); ?>><?php _e('No Sidebar (Full Width)', 'apus-theme'); ?></option>
|
||||
</select>
|
||||
<p class="description"><?php _e('Default layout for single posts', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Default Page Layout -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="default_page_layout"><?php _e('Default Page Layout', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<select name="apus_theme_options[default_page_layout]" id="default_page_layout">
|
||||
<option value="right-sidebar" <?php selected($options['default_page_layout'] ?? 'right-sidebar', 'right-sidebar'); ?>><?php _e('Right Sidebar', 'apus-theme'); ?></option>
|
||||
<option value="left-sidebar" <?php selected($options['default_page_layout'] ?? 'right-sidebar', 'left-sidebar'); ?>><?php _e('Left Sidebar', 'apus-theme'); ?></option>
|
||||
<option value="no-sidebar" <?php selected($options['default_page_layout'] ?? 'right-sidebar', 'no-sidebar'); ?>><?php _e('No Sidebar (Full Width)', 'apus-theme'); ?></option>
|
||||
</select>
|
||||
<p class="description"><?php _e('Default layout for pages', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Archive Posts Per Page -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="archive_posts_per_page"><?php _e('Archive Posts Per Page', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="number" name="apus_theme_options[archive_posts_per_page]" id="archive_posts_per_page" value="<?php echo esc_attr($options['archive_posts_per_page'] ?? 10); ?>" class="small-text" min="1" max="100" />
|
||||
<p class="description"><?php _e('Number of posts to show on archive pages. Set to 0 to use WordPress default.', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Show Featured Image on Single Posts -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="show_featured_image_single"><?php _e('Show Featured Image', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[show_featured_image_single]" id="show_featured_image_single" value="1" <?php checked(isset($options['show_featured_image_single']) ? $options['show_featured_image_single'] : true, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Display featured image at the top of single posts', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Show Author Box -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="show_author_box"><?php _e('Show Author Box', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[show_author_box]" id="show_author_box" value="1" <?php checked(isset($options['show_author_box']) ? $options['show_author_box'] : true, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Display author information box on single posts', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Enable Comments on Posts -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="enable_comments_posts"><?php _e('Enable Comments on Posts', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[enable_comments_posts]" id="enable_comments_posts" value="1" <?php checked(isset($options['enable_comments_posts']) ? $options['enable_comments_posts'] : true, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Allow comments on blog posts', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Enable Comments on Pages -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="enable_comments_pages"><?php _e('Enable Comments on Pages', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[enable_comments_pages]" id="enable_comments_pages" value="1" <?php checked(isset($options['enable_comments_pages']) ? $options['enable_comments_pages'] : false, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Allow comments on pages', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Show Post Meta -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="show_post_meta"><?php _e('Show Post Meta', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[show_post_meta]" id="show_post_meta" value="1" <?php checked(isset($options['show_post_meta']) ? $options['show_post_meta'] : true, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Display post meta information (date, author, etc.)', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Show Post Tags -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="show_post_tags"><?php _e('Show Post Tags', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[show_post_tags]" id="show_post_tags" value="1" <?php checked(isset($options['show_post_tags']) ? $options['show_post_tags'] : true, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Display tags on single posts', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Show Post Categories -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="show_post_categories"><?php _e('Show Post Categories', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[show_post_categories]" id="show_post_categories" value="1" <?php checked(isset($options['show_post_categories']) ? $options['show_post_categories'] : true, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Display categories on single posts', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Performance Tab -->
|
||||
<div id="performance" class="apus-tab-pane">
|
||||
<h2><?php _e('Performance Settings', 'apus-theme'); ?></h2>
|
||||
<p class="description"><?php _e('Optimize your site performance with these settings. Be careful when enabling these options.', 'apus-theme'); ?></p>
|
||||
|
||||
<table class="form-table">
|
||||
<!-- Enable Lazy Loading -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="enable_lazy_loading"><?php _e('Enable Lazy Loading', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[enable_lazy_loading]" id="enable_lazy_loading" value="1" <?php checked(isset($options['enable_lazy_loading']) ? $options['enable_lazy_loading'] : true, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Enable lazy loading for images to improve page load times', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Remove Emoji Scripts -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="performance_remove_emoji"><?php _e('Remove Emoji Scripts', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[performance_remove_emoji]" id="performance_remove_emoji" value="1" <?php checked(isset($options['performance_remove_emoji']) ? $options['performance_remove_emoji'] : true, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Remove WordPress emoji scripts and styles (reduces HTTP requests)', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Remove Embeds -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="performance_remove_embeds"><?php _e('Remove Embeds', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[performance_remove_embeds]" id="performance_remove_embeds" value="1" <?php checked(isset($options['performance_remove_embeds']) ? $options['performance_remove_embeds'] : false, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Remove WordPress embed scripts if you don\'t use oEmbed', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Remove Dashicons on Frontend -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="performance_remove_dashicons"><?php _e('Remove Dashicons', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[performance_remove_dashicons]" id="performance_remove_dashicons" value="1" <?php checked(isset($options['performance_remove_dashicons']) ? $options['performance_remove_dashicons'] : true, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Remove Dashicons from frontend for non-logged in users', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Defer JavaScript -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="performance_defer_js"><?php _e('Defer JavaScript', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[performance_defer_js]" id="performance_defer_js" value="1" <?php checked(isset($options['performance_defer_js']) ? $options['performance_defer_js'] : false, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Add defer attribute to JavaScript files (may break some scripts)', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Minify HTML -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="performance_minify_html"><?php _e('Minify HTML', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[performance_minify_html]" id="performance_minify_html" value="1" <?php checked(isset($options['performance_minify_html']) ? $options['performance_minify_html'] : false, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Minify HTML output to reduce page size', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Disable Gutenberg -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="performance_disable_gutenberg"><?php _e('Disable Gutenberg', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[performance_disable_gutenberg]" id="performance_disable_gutenberg" value="1" <?php checked(isset($options['performance_disable_gutenberg']) ? $options['performance_disable_gutenberg'] : false, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Disable Gutenberg editor and revert to classic editor', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Related Posts Tab -->
|
||||
<div id="related-posts" class="apus-tab-pane">
|
||||
<h2><?php _e('Related Posts Settings', 'apus-theme'); ?></h2>
|
||||
<p class="description"><?php _e('Configure related posts display on single post pages.', 'apus-theme'); ?></p>
|
||||
|
||||
<table class="form-table">
|
||||
<!-- Enable Related Posts -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="enable_related_posts"><?php _e('Enable Related Posts', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<label class="apus-switch">
|
||||
<input type="checkbox" name="apus_theme_options[enable_related_posts]" id="enable_related_posts" value="1" <?php checked(isset($options['enable_related_posts']) ? $options['enable_related_posts'] : true, true); ?> />
|
||||
<span class="apus-slider"></span>
|
||||
</label>
|
||||
<p class="description"><?php _e('Show related posts at the end of single posts', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Related Posts Count -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="related_posts_count"><?php _e('Number of Related Posts', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="number" name="apus_theme_options[related_posts_count]" id="related_posts_count" value="<?php echo esc_attr($options['related_posts_count'] ?? 3); ?>" class="small-text" min="1" max="12" />
|
||||
<p class="description"><?php _e('How many related posts to display', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Related Posts Taxonomy -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="related_posts_taxonomy"><?php _e('Relate Posts By', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<select name="apus_theme_options[related_posts_taxonomy]" id="related_posts_taxonomy">
|
||||
<option value="category" <?php selected($options['related_posts_taxonomy'] ?? 'category', 'category'); ?>><?php _e('Category', 'apus-theme'); ?></option>
|
||||
<option value="tag" <?php selected($options['related_posts_taxonomy'] ?? 'category', 'tag'); ?>><?php _e('Tag', 'apus-theme'); ?></option>
|
||||
<option value="both" <?php selected($options['related_posts_taxonomy'] ?? 'category', 'both'); ?>><?php _e('Category and Tag', 'apus-theme'); ?></option>
|
||||
</select>
|
||||
<p class="description"><?php _e('How to determine related posts', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Related Posts Title -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="related_posts_title"><?php _e('Related Posts Title', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" name="apus_theme_options[related_posts_title]" id="related_posts_title" value="<?php echo esc_attr($options['related_posts_title'] ?? __('Related Posts', 'apus-theme')); ?>" class="regular-text" />
|
||||
<p class="description"><?php _e('Title to display above related posts section', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Related Posts Columns -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="related_posts_columns"><?php _e('Columns', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<select name="apus_theme_options[related_posts_columns]" id="related_posts_columns">
|
||||
<option value="2" <?php selected($options['related_posts_columns'] ?? 3, 2); ?>><?php _e('2 Columns', 'apus-theme'); ?></option>
|
||||
<option value="3" <?php selected($options['related_posts_columns'] ?? 3, 3); ?>><?php _e('3 Columns', 'apus-theme'); ?></option>
|
||||
<option value="4" <?php selected($options['related_posts_columns'] ?? 3, 4); ?>><?php _e('4 Columns', 'apus-theme'); ?></option>
|
||||
</select>
|
||||
<p class="description"><?php _e('Number of columns to display related posts', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Advanced Tab -->
|
||||
<div id="advanced" class="apus-tab-pane">
|
||||
<h2><?php _e('Advanced Settings', 'apus-theme'); ?></h2>
|
||||
<p class="description"><?php _e('Advanced customization options. Use with caution.', 'apus-theme'); ?></p>
|
||||
|
||||
<table class="form-table">
|
||||
<!-- Custom CSS -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="custom_css"><?php _e('Custom CSS', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<textarea name="apus_theme_options[custom_css]" id="custom_css" rows="10" class="large-text code"><?php echo esc_textarea($options['custom_css'] ?? ''); ?></textarea>
|
||||
<p class="description"><?php _e('Add custom CSS code. This will be added to the <head> section.', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Custom JS Header -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="custom_js_header"><?php _e('Custom JavaScript (Header)', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<textarea name="apus_theme_options[custom_js_header]" id="custom_js_header" rows="10" class="large-text code"><?php echo esc_textarea($options['custom_js_header'] ?? ''); ?></textarea>
|
||||
<p class="description"><?php _e('Add custom JavaScript code. This will be added to the <head> section. Do not include <script> tags.', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Custom JS Footer -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="custom_js_footer"><?php _e('Custom JavaScript (Footer)', 'apus-theme'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<textarea name="apus_theme_options[custom_js_footer]" id="custom_js_footer" rows="10" class="large-text code"><?php echo esc_textarea($options['custom_js_footer'] ?? ''); ?></textarea>
|
||||
<p class="description"><?php _e('Add custom JavaScript code. This will be added before the closing </body> tag. Do not include <script> tags.', 'apus-theme'); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php submit_button(__('Save All Settings', 'apus-theme'), 'primary large', 'submit', true); ?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Import Modal -->
|
||||
<div id="apus-import-modal" class="apus-modal" style="display:none;">
|
||||
<div class="apus-modal-content">
|
||||
<span class="apus-modal-close">×</span>
|
||||
<h2><?php _e('Import Options', 'apus-theme'); ?></h2>
|
||||
<p><?php _e('Paste your exported options JSON here:', 'apus-theme'); ?></p>
|
||||
<textarea id="apus-import-data" rows="10" class="large-text code"></textarea>
|
||||
<p>
|
||||
<button type="button" class="button button-primary" id="apus-import-submit"><?php _e('Import', 'apus-theme'); ?></button>
|
||||
<button type="button" class="button" id="apus-import-cancel"><?php _e('Cancel', 'apus-theme'); ?></button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
272
wp-content/themes/apus-theme/inc/admin/related-posts-options.php
Normal file
272
wp-content/themes/apus-theme/inc/admin/related-posts-options.php
Normal file
@@ -0,0 +1,272 @@
|
||||
<?php
|
||||
/**
|
||||
* Related Posts Configuration Options
|
||||
*
|
||||
* This file provides helper functions and documentation for configuring
|
||||
* related posts functionality via WordPress options.
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all related posts options with their current values
|
||||
*
|
||||
* @return array Array of options with their values
|
||||
*/
|
||||
function apus_get_related_posts_options() {
|
||||
return array(
|
||||
'enabled' => array(
|
||||
'key' => 'apus_related_posts_enabled',
|
||||
'value' => get_option('apus_related_posts_enabled', true),
|
||||
'type' => 'boolean',
|
||||
'default' => true,
|
||||
'label' => __('Enable Related Posts', 'apus-theme'),
|
||||
'description' => __('Show related posts section at the end of single posts', 'apus-theme'),
|
||||
),
|
||||
'title' => array(
|
||||
'key' => 'apus_related_posts_title',
|
||||
'value' => get_option('apus_related_posts_title', __('Related Posts', 'apus-theme')),
|
||||
'type' => 'text',
|
||||
'default' => __('Related Posts', 'apus-theme'),
|
||||
'label' => __('Section Title', 'apus-theme'),
|
||||
'description' => __('Title displayed above related posts', 'apus-theme'),
|
||||
),
|
||||
'count' => array(
|
||||
'key' => 'apus_related_posts_count',
|
||||
'value' => get_option('apus_related_posts_count', 3),
|
||||
'type' => 'number',
|
||||
'default' => 3,
|
||||
'min' => 1,
|
||||
'max' => 12,
|
||||
'label' => __('Number of Posts', 'apus-theme'),
|
||||
'description' => __('Maximum number of related posts to display', 'apus-theme'),
|
||||
),
|
||||
'columns' => array(
|
||||
'key' => 'apus_related_posts_columns',
|
||||
'value' => get_option('apus_related_posts_columns', 3),
|
||||
'type' => 'select',
|
||||
'default' => 3,
|
||||
'options' => array(
|
||||
1 => __('1 Column', 'apus-theme'),
|
||||
2 => __('2 Columns', 'apus-theme'),
|
||||
3 => __('3 Columns', 'apus-theme'),
|
||||
4 => __('4 Columns', 'apus-theme'),
|
||||
),
|
||||
'label' => __('Grid Columns', 'apus-theme'),
|
||||
'description' => __('Number of columns in the grid layout (responsive)', 'apus-theme'),
|
||||
),
|
||||
'show_excerpt' => array(
|
||||
'key' => 'apus_related_posts_show_excerpt',
|
||||
'value' => get_option('apus_related_posts_show_excerpt', true),
|
||||
'type' => 'boolean',
|
||||
'default' => true,
|
||||
'label' => __('Show Excerpt', 'apus-theme'),
|
||||
'description' => __('Display post excerpt in related posts cards', 'apus-theme'),
|
||||
),
|
||||
'excerpt_length' => array(
|
||||
'key' => 'apus_related_posts_excerpt_length',
|
||||
'value' => get_option('apus_related_posts_excerpt_length', 20),
|
||||
'type' => 'number',
|
||||
'default' => 20,
|
||||
'min' => 5,
|
||||
'max' => 100,
|
||||
'label' => __('Excerpt Length', 'apus-theme'),
|
||||
'description' => __('Number of words in the excerpt', 'apus-theme'),
|
||||
),
|
||||
'show_date' => array(
|
||||
'key' => 'apus_related_posts_show_date',
|
||||
'value' => get_option('apus_related_posts_show_date', true),
|
||||
'type' => 'boolean',
|
||||
'default' => true,
|
||||
'label' => __('Show Date', 'apus-theme'),
|
||||
'description' => __('Display publication date in related posts', 'apus-theme'),
|
||||
),
|
||||
'show_category' => array(
|
||||
'key' => 'apus_related_posts_show_category',
|
||||
'value' => get_option('apus_related_posts_show_category', true),
|
||||
'type' => 'boolean',
|
||||
'default' => true,
|
||||
'label' => __('Show Category', 'apus-theme'),
|
||||
'description' => __('Display category badge on related posts', 'apus-theme'),
|
||||
),
|
||||
'bg_colors' => array(
|
||||
'key' => 'apus_related_posts_bg_colors',
|
||||
'value' => get_option('apus_related_posts_bg_colors', array(
|
||||
'#1a73e8', '#e91e63', '#4caf50', '#ff9800', '#9c27b0', '#00bcd4',
|
||||
)),
|
||||
'type' => 'color_array',
|
||||
'default' => array(
|
||||
'#1a73e8', // Blue
|
||||
'#e91e63', // Pink
|
||||
'#4caf50', // Green
|
||||
'#ff9800', // Orange
|
||||
'#9c27b0', // Purple
|
||||
'#00bcd4', // Cyan
|
||||
),
|
||||
'label' => __('Background Colors', 'apus-theme'),
|
||||
'description' => __('Colors used for posts without featured images', 'apus-theme'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a related posts option
|
||||
*
|
||||
* @param string $option_key The option key (without 'apus_related_posts_' prefix)
|
||||
* @param mixed $value The new value
|
||||
* @return bool True if updated successfully
|
||||
*/
|
||||
function apus_update_related_posts_option($option_key, $value) {
|
||||
$full_key = 'apus_related_posts_' . $option_key;
|
||||
return update_option($full_key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset related posts options to defaults
|
||||
*
|
||||
* @return bool True if reset successfully
|
||||
*/
|
||||
function apus_reset_related_posts_options() {
|
||||
$options = apus_get_related_posts_options();
|
||||
$success = true;
|
||||
|
||||
foreach ($options as $option) {
|
||||
if (!update_option($option['key'], $option['default'])) {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example: Programmatically configure related posts
|
||||
*
|
||||
* This function shows how to configure related posts options programmatically.
|
||||
* You can call this from your functions.php or a plugin.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function apus_example_configure_related_posts() {
|
||||
// Example usage - uncomment to use:
|
||||
|
||||
// Enable related posts
|
||||
// update_option('apus_related_posts_enabled', true);
|
||||
|
||||
// Set custom title
|
||||
// update_option('apus_related_posts_title', __('You Might Also Like', 'apus-theme'));
|
||||
|
||||
// Show 4 related posts
|
||||
// update_option('apus_related_posts_count', 4);
|
||||
|
||||
// Use 2 columns layout
|
||||
// update_option('apus_related_posts_columns', 2);
|
||||
|
||||
// Show excerpt with 30 words
|
||||
// update_option('apus_related_posts_show_excerpt', true);
|
||||
// update_option('apus_related_posts_excerpt_length', 30);
|
||||
|
||||
// Show date and category
|
||||
// update_option('apus_related_posts_show_date', true);
|
||||
// update_option('apus_related_posts_show_category', true);
|
||||
|
||||
// Custom background colors for posts without images
|
||||
// update_option('apus_related_posts_bg_colors', array(
|
||||
// '#FF6B6B', // Red
|
||||
// '#4ECDC4', // Teal
|
||||
// '#45B7D1', // Blue
|
||||
// '#FFA07A', // Coral
|
||||
// '#98D8C8', // Mint
|
||||
// '#F7DC6F', // Yellow
|
||||
// ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter hook example: Modify related posts query
|
||||
*
|
||||
* This example shows how to customize the related posts query.
|
||||
* Add this to your functions.php or child theme.
|
||||
*/
|
||||
function apus_example_modify_related_posts_query($args, $post_id) {
|
||||
// Example: Order by date instead of random
|
||||
// $args['orderby'] = 'date';
|
||||
// $args['order'] = 'DESC';
|
||||
|
||||
// Example: Only show posts from the last 6 months
|
||||
// $args['date_query'] = array(
|
||||
// array(
|
||||
// 'after' => '6 months ago',
|
||||
// ),
|
||||
// );
|
||||
|
||||
// Example: Exclude specific category
|
||||
// $args['category__not_in'] = array(5); // Replace 5 with category ID
|
||||
|
||||
return $args;
|
||||
}
|
||||
// add_filter('apus_related_posts_args', 'apus_example_modify_related_posts_query', 10, 2);
|
||||
|
||||
/**
|
||||
* Get documentation for related posts configuration
|
||||
*
|
||||
* @return array Documentation array
|
||||
*/
|
||||
function apus_get_related_posts_documentation() {
|
||||
return array(
|
||||
'overview' => array(
|
||||
'title' => __('Related Posts Overview', 'apus-theme'),
|
||||
'content' => __(
|
||||
'The related posts feature automatically displays relevant posts at the end of each blog post. ' .
|
||||
'Posts are related based on shared categories and displayed in a responsive Bootstrap grid.',
|
||||
'apus-theme'
|
||||
),
|
||||
),
|
||||
'features' => array(
|
||||
'title' => __('Key Features', 'apus-theme'),
|
||||
'items' => array(
|
||||
__('Automatic category-based matching', 'apus-theme'),
|
||||
__('Responsive Bootstrap 5 grid layout', 'apus-theme'),
|
||||
__('Configurable number of posts and columns', 'apus-theme'),
|
||||
__('Support for posts with and without featured images', 'apus-theme'),
|
||||
__('Beautiful color backgrounds for posts without images', 'apus-theme'),
|
||||
__('Customizable excerpt length', 'apus-theme'),
|
||||
__('Optional display of dates and categories', 'apus-theme'),
|
||||
__('Smooth hover animations', 'apus-theme'),
|
||||
__('Print-friendly styles', 'apus-theme'),
|
||||
__('Dark mode support', 'apus-theme'),
|
||||
),
|
||||
),
|
||||
'configuration' => array(
|
||||
'title' => __('How to Configure', 'apus-theme'),
|
||||
'methods' => array(
|
||||
'database' => array(
|
||||
'title' => __('Via WordPress Options API', 'apus-theme'),
|
||||
'code' => "update_option('apus_related_posts_enabled', true);\nupdate_option('apus_related_posts_count', 4);",
|
||||
),
|
||||
'filter' => array(
|
||||
'title' => __('Via Filter Hook', 'apus-theme'),
|
||||
'code' => "add_filter('apus_related_posts_args', function(\$args, \$post_id) {\n \$args['posts_per_page'] = 6;\n return \$args;\n}, 10, 2);",
|
||||
),
|
||||
),
|
||||
),
|
||||
'customization' => array(
|
||||
'title' => __('Customization Examples', 'apus-theme'),
|
||||
'examples' => array(
|
||||
array(
|
||||
'title' => __('Change title and layout', 'apus-theme'),
|
||||
'code' => "update_option('apus_related_posts_title', 'También te puede interesar');\nupdate_option('apus_related_posts_columns', 4);",
|
||||
),
|
||||
array(
|
||||
'title' => __('Customize colors', 'apus-theme'),
|
||||
'code' => "update_option('apus_related_posts_bg_colors', array(\n '#FF6B6B',\n '#4ECDC4',\n '#45B7D1'\n));",
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
214
wp-content/themes/apus-theme/inc/admin/theme-options.php
Normal file
214
wp-content/themes/apus-theme/inc/admin/theme-options.php
Normal file
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
/**
|
||||
* Theme Options Admin Page
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add admin menu
|
||||
*/
|
||||
function apus_add_admin_menu() {
|
||||
add_theme_page(
|
||||
__('Apus Theme Options', 'apus-theme'), // Page title
|
||||
__('Theme Options', 'apus-theme'), // Menu title
|
||||
'manage_options', // Capability
|
||||
'apus-theme-options', // Menu slug
|
||||
'apus_render_options_page', // Callback function
|
||||
30 // Position
|
||||
);
|
||||
}
|
||||
add_action('admin_menu', 'apus_add_admin_menu');
|
||||
|
||||
/**
|
||||
* Render the options page
|
||||
*/
|
||||
function apus_render_options_page() {
|
||||
// Check user capabilities
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_die(__('You do not have sufficient permissions to access this page.', 'apus-theme'));
|
||||
}
|
||||
|
||||
// Load the template
|
||||
include get_template_directory() . '/inc/admin/options-page-template.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin scripts and styles
|
||||
*/
|
||||
function apus_enqueue_admin_scripts($hook) {
|
||||
// Only load on our theme options page
|
||||
if ($hook !== 'appearance_page_apus-theme-options') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue WordPress media uploader
|
||||
wp_enqueue_media();
|
||||
|
||||
// Enqueue admin styles
|
||||
wp_enqueue_style(
|
||||
'apus-admin-options',
|
||||
get_template_directory_uri() . '/assets/admin/css/theme-options.css',
|
||||
array(),
|
||||
APUS_VERSION
|
||||
);
|
||||
|
||||
// Enqueue admin scripts
|
||||
wp_enqueue_script(
|
||||
'apus-admin-options',
|
||||
get_template_directory_uri() . '/assets/admin/js/theme-options.js',
|
||||
array('jquery', 'wp-color-picker'),
|
||||
APUS_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
// Localize script
|
||||
wp_localize_script('apus-admin-options', 'apusAdminOptions', array(
|
||||
'ajaxUrl' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('apus_admin_nonce'),
|
||||
'strings' => array(
|
||||
'selectImage' => __('Select Image', 'apus-theme'),
|
||||
'useImage' => __('Use Image', 'apus-theme'),
|
||||
'removeImage' => __('Remove Image', 'apus-theme'),
|
||||
'confirmReset' => __('Are you sure you want to reset all options to default values? This cannot be undone.', 'apus-theme'),
|
||||
'saved' => __('Settings saved successfully!', 'apus-theme'),
|
||||
'error' => __('An error occurred while saving settings.', 'apus-theme'),
|
||||
),
|
||||
));
|
||||
}
|
||||
add_action('admin_enqueue_scripts', 'apus_enqueue_admin_scripts');
|
||||
|
||||
/**
|
||||
* Add settings link to theme actions
|
||||
*/
|
||||
function apus_add_settings_link($links) {
|
||||
$settings_link = '<a href="' . admin_url('themes.php?page=apus-theme-options') . '">' . __('Settings', 'apus-theme') . '</a>';
|
||||
array_unshift($links, $settings_link);
|
||||
return $links;
|
||||
}
|
||||
add_filter('theme_action_links_' . get_template(), 'apus_add_settings_link');
|
||||
|
||||
/**
|
||||
* AJAX handler for resetting options
|
||||
*/
|
||||
function apus_reset_options_ajax() {
|
||||
check_ajax_referer('apus_admin_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('Insufficient permissions.', 'apus-theme')));
|
||||
}
|
||||
|
||||
// Delete options to reset to defaults
|
||||
delete_option('apus_theme_options');
|
||||
|
||||
wp_send_json_success(array('message' => __('Options reset to defaults successfully.', 'apus-theme')));
|
||||
}
|
||||
add_action('wp_ajax_apus_reset_options', 'apus_reset_options_ajax');
|
||||
|
||||
/**
|
||||
* AJAX handler for exporting options
|
||||
*/
|
||||
function apus_export_options_ajax() {
|
||||
check_ajax_referer('apus_admin_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('Insufficient permissions.', 'apus-theme')));
|
||||
}
|
||||
|
||||
$options = get_option('apus_theme_options', array());
|
||||
|
||||
wp_send_json_success(array(
|
||||
'data' => json_encode($options, JSON_PRETTY_PRINT),
|
||||
'filename' => 'apus-theme-options-' . date('Y-m-d') . '.json'
|
||||
));
|
||||
}
|
||||
add_action('wp_ajax_apus_export_options', 'apus_export_options_ajax');
|
||||
|
||||
/**
|
||||
* AJAX handler for importing options
|
||||
*/
|
||||
function apus_import_options_ajax() {
|
||||
check_ajax_referer('apus_admin_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('Insufficient permissions.', 'apus-theme')));
|
||||
}
|
||||
|
||||
if (!isset($_POST['import_data'])) {
|
||||
wp_send_json_error(array('message' => __('No import data provided.', 'apus-theme')));
|
||||
}
|
||||
|
||||
$import_data = json_decode(stripslashes($_POST['import_data']), true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
wp_send_json_error(array('message' => __('Invalid JSON data.', 'apus-theme')));
|
||||
}
|
||||
|
||||
// Sanitize imported data
|
||||
$sanitized_data = apus_sanitize_options($import_data);
|
||||
|
||||
// Update options
|
||||
update_option('apus_theme_options', $sanitized_data);
|
||||
|
||||
wp_send_json_success(array('message' => __('Options imported successfully.', 'apus-theme')));
|
||||
}
|
||||
add_action('wp_ajax_apus_import_options', 'apus_import_options_ajax');
|
||||
|
||||
/**
|
||||
* Add admin notices
|
||||
*/
|
||||
function apus_admin_notices() {
|
||||
$screen = get_current_screen();
|
||||
if ($screen->id !== 'appearance_page_apus-theme-options') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if settings were updated
|
||||
if (isset($_GET['settings-updated']) && $_GET['settings-updated'] === 'true') {
|
||||
?>
|
||||
<div class="notice notice-success is-dismissible">
|
||||
<p><?php _e('Settings saved successfully!', 'apus-theme'); ?></p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
add_action('admin_notices', 'apus_admin_notices');
|
||||
|
||||
/**
|
||||
* Register theme options in Customizer as well (for preview)
|
||||
*/
|
||||
function apus_customize_register($wp_customize) {
|
||||
// Add a panel for theme options
|
||||
$wp_customize->add_panel('apus_theme_options', array(
|
||||
'title' => __('Apus Theme Options', 'apus-theme'),
|
||||
'description' => __('Configure theme options (Also available in Theme Options page)', 'apus-theme'),
|
||||
'priority' => 10,
|
||||
));
|
||||
|
||||
// General Section
|
||||
$wp_customize->add_section('apus_general', array(
|
||||
'title' => __('General Settings', 'apus-theme'),
|
||||
'panel' => 'apus_theme_options',
|
||||
'priority' => 10,
|
||||
));
|
||||
|
||||
// Enable breadcrumbs
|
||||
$wp_customize->add_setting('apus_theme_options[enable_breadcrumbs]', array(
|
||||
'default' => true,
|
||||
'type' => 'option',
|
||||
'sanitize_callback' => 'apus_sanitize_checkbox',
|
||||
));
|
||||
|
||||
$wp_customize->add_control('apus_theme_options[enable_breadcrumbs]', array(
|
||||
'label' => __('Enable Breadcrumbs', 'apus-theme'),
|
||||
'section' => 'apus_general',
|
||||
'type' => 'checkbox',
|
||||
));
|
||||
}
|
||||
add_action('customize_register', 'apus_customize_register');
|
||||
163
wp-content/themes/apus-theme/inc/adsense-delay.php
Normal file
163
wp-content/themes/apus-theme/inc/adsense-delay.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
/**
|
||||
* AdSense Delay Loading Functionality
|
||||
*
|
||||
* Delays the loading of AdSense scripts until user interaction or timeout
|
||||
* to improve initial page load performance.
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delay AdSense scripts by intercepting output buffer
|
||||
*
|
||||
* This function starts output buffering and replaces AdSense scripts
|
||||
* with delayed versions when the page is rendered.
|
||||
*/
|
||||
function apus_delay_adsense_scripts() {
|
||||
// Only run on frontend
|
||||
if (is_admin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if AdSense delay is enabled in theme options
|
||||
$delay_enabled = get_theme_mod('apus_adsense_delay_enabled', true);
|
||||
|
||||
if (!$delay_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start output buffering
|
||||
ob_start('apus_replace_adsense_scripts');
|
||||
}
|
||||
add_action('template_redirect', 'apus_delay_adsense_scripts', 1);
|
||||
|
||||
/**
|
||||
* Replace AdSense scripts with delayed versions
|
||||
*
|
||||
* This function processes the HTML output and replaces standard AdSense
|
||||
* script tags with delayed loading versions.
|
||||
*
|
||||
* @param string $html The HTML content to process
|
||||
* @return string Modified HTML with delayed AdSense scripts
|
||||
*/
|
||||
function apus_replace_adsense_scripts($html) {
|
||||
// Only process if there's actual AdSense content
|
||||
if (strpos($html, 'pagead2.googlesyndication.com') === false &&
|
||||
strpos($html, 'adsbygoogle.js') === false) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
// Pattern to match AdSense script tags
|
||||
$patterns = array(
|
||||
// Match async script tags for AdSense
|
||||
'/<script\s+async\s+src=["\']https:\/\/pagead2\.googlesyndication\.com\/pagead\/js\/adsbygoogle\.js[^"\']*["\']\s*(?:crossorigin=["\']anonymous["\'])?\s*><\/script>/i',
|
||||
|
||||
// Match script tags without async
|
||||
'/<script\s+src=["\']https:\/\/pagead2\.googlesyndication\.com\/pagead\/js\/adsbygoogle\.js[^"\']*["\']\s*(?:crossorigin=["\']anonymous["\'])?\s*><\/script>/i',
|
||||
|
||||
// Match inline adsbygoogle.push scripts
|
||||
'/<script>\s*\(adsbygoogle\s*=\s*window\.adsbygoogle\s*\|\|\s*\[\]\)\.push\(\{[^}]*\}\);\s*<\/script>/is',
|
||||
);
|
||||
|
||||
// Replace async AdSense scripts with delayed versions
|
||||
$replacements = array(
|
||||
// Replace async script tag with data attribute for delayed loading
|
||||
'<script type="text/plain" data-adsense-script src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" crossorigin="anonymous"></script>',
|
||||
|
||||
// Replace non-async script tag
|
||||
'<script type="text/plain" data-adsense-script src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" crossorigin="anonymous"></script>',
|
||||
|
||||
// Replace inline push scripts with delayed versions
|
||||
'<script type="text/plain" data-adsense-push>$0</script>',
|
||||
);
|
||||
|
||||
// First pass: replace script tags
|
||||
$html = preg_replace($patterns[0], $replacements[0], $html);
|
||||
$html = preg_replace($patterns[1], $replacements[1], $html);
|
||||
|
||||
// Second pass: replace inline push calls
|
||||
$html = preg_replace_callback(
|
||||
'/<script>\s*\(adsbygoogle\s*=\s*window\.adsbygoogle\s*\|\|\s*\[\]\)\.push\(\{[^}]*\}\);\s*<\/script>/is',
|
||||
function($matches) {
|
||||
return '<script type="text/plain" data-adsense-push>' . $matches[0] . '</script>';
|
||||
},
|
||||
$html
|
||||
);
|
||||
|
||||
// Add a comment to indicate processing occurred
|
||||
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||
$html = str_replace('</body>', '<!-- AdSense scripts delayed by Apus Theme --></body>', $html);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add inline script to initialize delayed AdSense
|
||||
*
|
||||
* This adds a small inline script that marks AdSense as ready to load
|
||||
* after the adsense-loader.js has been enqueued.
|
||||
*/
|
||||
function apus_add_adsense_init_script() {
|
||||
$delay_enabled = get_theme_mod('apus_adsense_delay_enabled', true);
|
||||
|
||||
if (!$delay_enabled || is_admin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<script>
|
||||
// Initialize AdSense delay flag
|
||||
window.apusAdsenseDelayed = true;
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
add_action('wp_head', 'apus_add_adsense_init_script', 1);
|
||||
|
||||
/**
|
||||
* Register customizer settings for AdSense delay
|
||||
*
|
||||
* @param WP_Customize_Manager $wp_customize Theme Customizer object
|
||||
*/
|
||||
function apus_adsense_delay_customizer($wp_customize) {
|
||||
// Add Performance section if it doesn't exist
|
||||
if (!$wp_customize->get_section('apus_performance')) {
|
||||
$wp_customize->add_section('apus_performance', array(
|
||||
'title' => __('Performance Settings', 'apus-theme'),
|
||||
'priority' => 130,
|
||||
));
|
||||
}
|
||||
|
||||
// Add setting for AdSense delay
|
||||
$wp_customize->add_setting('apus_adsense_delay_enabled', array(
|
||||
'default' => true,
|
||||
'sanitize_callback' => 'apus_sanitize_checkbox',
|
||||
'transport' => 'refresh',
|
||||
));
|
||||
|
||||
// Add control for AdSense delay
|
||||
$wp_customize->add_control('apus_adsense_delay_enabled', array(
|
||||
'label' => __('Delay AdSense Loading', 'apus-theme'),
|
||||
'description' => __('Delay AdSense scripts until user interaction to improve page load speed.', 'apus-theme'),
|
||||
'section' => 'apus_performance',
|
||||
'type' => 'checkbox',
|
||||
));
|
||||
}
|
||||
add_action('customize_register', 'apus_adsense_delay_customizer');
|
||||
|
||||
/**
|
||||
* Sanitize checkbox input
|
||||
*
|
||||
* @param bool $checked Whether the checkbox is checked
|
||||
* @return bool Sanitized value
|
||||
*/
|
||||
function apus_sanitize_checkbox($checked) {
|
||||
return (isset($checked) && $checked === true || $checked === '1') ? true : false;
|
||||
}
|
||||
208
wp-content/themes/apus-theme/inc/category-badge.php
Normal file
208
wp-content/themes/apus-theme/inc/category-badge.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
/**
|
||||
* Category Badge Functions
|
||||
*
|
||||
* Display category badges for posts with configurable settings.
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Category Badge HTML
|
||||
*
|
||||
* Returns HTML markup for the first category badge.
|
||||
* Can be configured to show/hide via theme customizer.
|
||||
*
|
||||
* @param int $post_id Optional. Post ID. Defaults to current post.
|
||||
* @param bool $force_show Optional. Force display regardless of settings. Default false.
|
||||
* @return string HTML markup for category badge or empty string.
|
||||
*/
|
||||
function apus_get_category_badge($post_id = null, $force_show = false) {
|
||||
// Get post ID if not provided
|
||||
if (!$post_id) {
|
||||
$post_id = get_the_ID();
|
||||
}
|
||||
|
||||
// Check if category badges are enabled
|
||||
if (!$force_show && !apus_is_category_badge_enabled()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Get categories
|
||||
$categories = get_the_category($post_id);
|
||||
|
||||
// Return empty if no categories
|
||||
if (empty($categories)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Get first category
|
||||
$category = $categories[0];
|
||||
|
||||
// Build badge HTML
|
||||
$output = '<div class="category-badge-wrapper">';
|
||||
$output .= sprintf(
|
||||
'<a href="%s" class="category-badge" rel="category tag">%s</a>',
|
||||
esc_url(get_category_link($category->term_id)),
|
||||
esc_html($category->name)
|
||||
);
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display Category Badge
|
||||
*
|
||||
* Echoes the category badge HTML.
|
||||
*
|
||||
* @param int $post_id Optional. Post ID. Defaults to current post.
|
||||
* @param bool $force_show Optional. Force display regardless of settings. Default false.
|
||||
*/
|
||||
function apus_the_category_badge($post_id = null, $force_show = false) {
|
||||
echo apus_get_category_badge($post_id, $force_show);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Category Badge is Enabled
|
||||
*
|
||||
* @return bool True if enabled, false otherwise.
|
||||
*/
|
||||
function apus_is_category_badge_enabled() {
|
||||
return (bool) get_theme_mod('apus_category_badge_enabled', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Category Badge Settings in Customizer
|
||||
*
|
||||
* Adds controls to enable/disable category badges.
|
||||
*
|
||||
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
|
||||
*/
|
||||
function apus_category_badge_customizer($wp_customize) {
|
||||
// Add section
|
||||
$wp_customize->add_section('apus_category_badge', array(
|
||||
'title' => __('Category Badge', 'apus-theme'),
|
||||
'priority' => 31,
|
||||
));
|
||||
|
||||
// Enable/Disable setting
|
||||
$wp_customize->add_setting('apus_category_badge_enabled', array(
|
||||
'default' => true,
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'transport' => 'refresh',
|
||||
));
|
||||
|
||||
$wp_customize->add_control('apus_category_badge_enabled', array(
|
||||
'label' => __('Enable Category Badge', 'apus-theme'),
|
||||
'description' => __('Show the first category as a badge above the post title in single posts.', 'apus-theme'),
|
||||
'section' => 'apus_category_badge',
|
||||
'type' => 'checkbox',
|
||||
));
|
||||
|
||||
// Badge background color
|
||||
$wp_customize->add_setting('apus_category_badge_bg_color', array(
|
||||
'default' => '#0073aa',
|
||||
'sanitize_callback' => 'sanitize_hex_color',
|
||||
'transport' => 'postMessage',
|
||||
));
|
||||
|
||||
$wp_customize->add_control(new WP_Customize_Color_Control($wp_customize, 'apus_category_badge_bg_color', array(
|
||||
'label' => __('Badge Background Color', 'apus-theme'),
|
||||
'section' => 'apus_category_badge',
|
||||
)));
|
||||
|
||||
// Badge text color
|
||||
$wp_customize->add_setting('apus_category_badge_text_color', array(
|
||||
'default' => '#ffffff',
|
||||
'sanitize_callback' => 'sanitize_hex_color',
|
||||
'transport' => 'postMessage',
|
||||
));
|
||||
|
||||
$wp_customize->add_control(new WP_Customize_Color_Control($wp_customize, 'apus_category_badge_text_color', array(
|
||||
'label' => __('Badge Text Color', 'apus-theme'),
|
||||
'section' => 'apus_category_badge',
|
||||
)));
|
||||
}
|
||||
add_action('customize_register', 'apus_category_badge_customizer');
|
||||
|
||||
/**
|
||||
* Output Category Badge Inline Styles
|
||||
*
|
||||
* Outputs custom CSS for category badge colors.
|
||||
*/
|
||||
function apus_category_badge_styles() {
|
||||
$bg_color = get_theme_mod('apus_category_badge_bg_color', '#0073aa');
|
||||
$text_color = get_theme_mod('apus_category_badge_text_color', '#ffffff');
|
||||
|
||||
$css = "
|
||||
<style id='apus-category-badge-inline-css'>
|
||||
.category-badge-wrapper {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.category-badge {
|
||||
display: inline-block;
|
||||
background-color: {$bg_color};
|
||||
color: {$text_color};
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-radius: 4px;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.category-badge:hover {
|
||||
opacity: 0.8;
|
||||
color: {$text_color};
|
||||
}
|
||||
|
||||
.category-badge:focus {
|
||||
outline: 2px solid {$bg_color};
|
||||
outline-offset: 2px;
|
||||
}
|
||||
</style>
|
||||
";
|
||||
|
||||
echo $css;
|
||||
}
|
||||
add_action('wp_head', 'apus_category_badge_styles');
|
||||
|
||||
/**
|
||||
* Customize Preview JS for Live Preview
|
||||
*
|
||||
* Adds live preview support for category badge color changes.
|
||||
*/
|
||||
function apus_category_badge_customize_preview_js() {
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
(function($) {
|
||||
// Background Color
|
||||
wp.customize('apus_category_badge_bg_color', function(value) {
|
||||
value.bind(function(newval) {
|
||||
$('.category-badge').css('background-color', newval);
|
||||
});
|
||||
});
|
||||
|
||||
// Text Color
|
||||
wp.customize('apus_category_badge_text_color', function(value) {
|
||||
value.bind(function(newval) {
|
||||
$('.category-badge').css('color', newval);
|
||||
});
|
||||
});
|
||||
})(jQuery);
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
add_action('customize_preview_init', function() {
|
||||
add_action('wp_footer', 'apus_category_badge_customize_preview_js');
|
||||
});
|
||||
378
wp-content/themes/apus-theme/inc/critical-css.php
Normal file
378
wp-content/themes/apus-theme/inc/critical-css.php
Normal file
@@ -0,0 +1,378 @@
|
||||
<?php
|
||||
/**
|
||||
* Critical CSS Generator and Inline Loader
|
||||
*
|
||||
* This file provides functionality to inline critical CSS for above-the-fold content,
|
||||
* improving First Contentful Paint (FCP) and Largest Contentful Paint (LCP).
|
||||
*
|
||||
* IMPORTANT: This feature is DISABLED by default. Enable it via Customizer.
|
||||
*
|
||||
* How it works:
|
||||
* 1. When enabled, critical CSS is inlined in the <head>
|
||||
* 2. Main stylesheet is loaded asynchronously after page load
|
||||
* 3. Improves Core Web Vitals by reducing render-blocking CSS
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Critical CSS is enabled
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @return bool
|
||||
*/
|
||||
function apus_is_critical_css_enabled() {
|
||||
return get_theme_mod( 'apus_enable_critical_css', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Critical CSS Content
|
||||
*
|
||||
* Returns the critical CSS for the current page type.
|
||||
* You can customize this based on page types (home, single, archive, etc.)
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @return string Critical CSS content
|
||||
*/
|
||||
function apus_get_critical_css() {
|
||||
// Define critical CSS based on page type
|
||||
$critical_css = '';
|
||||
|
||||
// Get transient to cache critical CSS
|
||||
$transient_key = 'apus_critical_css_' . apus_get_page_type();
|
||||
$cached_css = get_transient( $transient_key );
|
||||
|
||||
if ( false !== $cached_css ) {
|
||||
return $cached_css;
|
||||
}
|
||||
|
||||
// Generate critical CSS based on page type
|
||||
if ( is_front_page() || is_home() ) {
|
||||
$critical_css = apus_get_home_critical_css();
|
||||
} elseif ( is_single() ) {
|
||||
$critical_css = apus_get_single_critical_css();
|
||||
} elseif ( is_archive() || is_category() || is_tag() ) {
|
||||
$critical_css = apus_get_archive_critical_css();
|
||||
} else {
|
||||
$critical_css = apus_get_default_critical_css();
|
||||
}
|
||||
|
||||
// Cache for 24 hours
|
||||
set_transient( $transient_key, $critical_css, DAY_IN_SECONDS );
|
||||
|
||||
return $critical_css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current page type for caching
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @return string Page type identifier
|
||||
*/
|
||||
function apus_get_page_type() {
|
||||
if ( is_front_page() ) {
|
||||
return 'home';
|
||||
} elseif ( is_single() ) {
|
||||
return 'single';
|
||||
} elseif ( is_archive() ) {
|
||||
return 'archive';
|
||||
} elseif ( is_search() ) {
|
||||
return 'search';
|
||||
} elseif ( is_404() ) {
|
||||
return '404';
|
||||
} else {
|
||||
return 'page';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Critical CSS for Homepage
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_home_critical_css() {
|
||||
return '
|
||||
/* Reset and Base */
|
||||
*,*::before,*::after{box-sizing:border-box}
|
||||
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;line-height:1.6;color:#333;background:#fff}
|
||||
img{max-width:100%;height:auto;display:block}
|
||||
a{color:#0066cc;text-decoration:none}
|
||||
|
||||
/* Header */
|
||||
.site-header{background:#fff;border-bottom:1px solid #e5e5e5;position:sticky;top:0;z-index:1000}
|
||||
.site-header .container{max-width:1200px;margin:0 auto;padding:1rem}
|
||||
.site-branding{display:flex;align-items:center}
|
||||
.site-title{margin:0;font-size:1.5rem;font-weight:700}
|
||||
|
||||
/* Hero Section */
|
||||
.hero-section{padding:3rem 1rem;text-align:center;background:#f8f9fa}
|
||||
.hero-title{font-size:2.5rem;margin:0 0 1rem;font-weight:700;line-height:1.2}
|
||||
.hero-description{font-size:1.125rem;color:#666;max-width:600px;margin:0 auto}
|
||||
|
||||
/* Container */
|
||||
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
|
||||
|
||||
/* Featured Posts Grid */
|
||||
.featured-posts{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:2rem;margin:2rem 0}
|
||||
.post-card{background:#fff;border:1px solid #e5e5e5;border-radius:8px;overflow:hidden;transition:transform .2s}
|
||||
.post-card:hover{transform:translateY(-4px)}
|
||||
|
||||
/* Skip to content */
|
||||
.skip-link{position:absolute;left:-999px;top:0;background:#0066cc;color:#fff;padding:.5rem 1rem;text-decoration:none}
|
||||
.skip-link:focus{left:0}
|
||||
';
|
||||
}
|
||||
|
||||
/**
|
||||
* Critical CSS for Single Post/Page
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_single_critical_css() {
|
||||
return '
|
||||
/* Reset and Base */
|
||||
*,*::before,*::after{box-sizing:border-box}
|
||||
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;line-height:1.6;color:#333;background:#fff}
|
||||
img{max-width:100%;height:auto;display:block}
|
||||
a{color:#0066cc;text-decoration:none}
|
||||
h1,h2,h3,h4,h5,h6{margin:1.5rem 0 1rem;font-weight:700;line-height:1.3}
|
||||
h1{font-size:2.5rem}
|
||||
h2{font-size:2rem}
|
||||
h3{font-size:1.5rem}
|
||||
p{margin:0 0 1.5rem}
|
||||
|
||||
/* Header */
|
||||
.site-header{background:#fff;border-bottom:1px solid #e5e5e5;position:sticky;top:0;z-index:1000}
|
||||
.site-header .container{max-width:1200px;margin:0 auto;padding:1rem}
|
||||
|
||||
/* Article */
|
||||
.entry-header{margin:2rem 0}
|
||||
.entry-title{font-size:2.5rem;margin:0 0 1rem;line-height:1.2}
|
||||
.entry-meta{color:#666;font-size:.875rem}
|
||||
.entry-content{max-width:800px;margin:0 auto;font-size:1.125rem;line-height:1.8}
|
||||
.featured-image{margin:2rem 0}
|
||||
|
||||
/* Container */
|
||||
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
|
||||
|
||||
/* Skip to content */
|
||||
.skip-link{position:absolute;left:-999px;top:0;background:#0066cc;color:#fff;padding:.5rem 1rem;text-decoration:none}
|
||||
.skip-link:focus{left:0}
|
||||
';
|
||||
}
|
||||
|
||||
/**
|
||||
* Critical CSS for Archive Pages
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_archive_critical_css() {
|
||||
return '
|
||||
/* Reset and Base */
|
||||
*,*::before,*::after{box-sizing:border-box}
|
||||
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;line-height:1.6;color:#333;background:#fff}
|
||||
img{max-width:100%;height:auto;display:block}
|
||||
a{color:#0066cc;text-decoration:none}
|
||||
|
||||
/* Header */
|
||||
.site-header{background:#fff;border-bottom:1px solid #e5e5e5;position:sticky;top:0;z-index:1000}
|
||||
.site-header .container{max-width:1200px;margin:0 auto;padding:1rem}
|
||||
|
||||
/* Archive Header */
|
||||
.archive-header{padding:2rem 1rem;background:#f8f9fa;border-bottom:1px solid #e5e5e5}
|
||||
.archive-title{margin:0;font-size:2rem;font-weight:700}
|
||||
.archive-description{margin:1rem 0 0;color:#666;font-size:1.125rem}
|
||||
|
||||
/* Posts Grid */
|
||||
.posts-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:2rem;margin:2rem 0}
|
||||
.post-card{background:#fff;border:1px solid #e5e5e5;border-radius:8px;overflow:hidden}
|
||||
|
||||
/* Container */
|
||||
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
|
||||
|
||||
/* Skip to content */
|
||||
.skip-link{position:absolute;left:-999px;top:0;background:#0066cc;color:#fff;padding:.5rem 1rem;text-decoration:none}
|
||||
.skip-link:focus{left:0}
|
||||
';
|
||||
}
|
||||
|
||||
/**
|
||||
* Critical CSS for Default Pages
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_default_critical_css() {
|
||||
return '
|
||||
/* Reset and Base */
|
||||
*,*::before,*::after{box-sizing:border-box}
|
||||
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;line-height:1.6;color:#333;background:#fff}
|
||||
img{max-width:100%;height:auto;display:block}
|
||||
a{color:#0066cc;text-decoration:none}
|
||||
|
||||
/* Header */
|
||||
.site-header{background:#fff;border-bottom:1px solid #e5e5e5;position:sticky;top:0;z-index:1000}
|
||||
.site-header .container{max-width:1200px;margin:0 auto;padding:1rem}
|
||||
|
||||
/* Container */
|
||||
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
|
||||
|
||||
/* Content */
|
||||
.site-content{min-height:50vh;padding:2rem 0}
|
||||
|
||||
/* Skip to content */
|
||||
.skip-link{position:absolute;left:-999px;top:0;background:#0066cc;color:#fff;padding:.5rem 1rem;text-decoration:none}
|
||||
.skip-link:focus{left:0}
|
||||
';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output Critical CSS inline in head
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_output_critical_css() {
|
||||
if ( ! apus_is_critical_css_enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$critical_css = apus_get_critical_css();
|
||||
|
||||
if ( empty( $critical_css ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Minify CSS (remove extra whitespace and newlines)
|
||||
$critical_css = preg_replace( '/\s+/', ' ', $critical_css );
|
||||
$critical_css = trim( $critical_css );
|
||||
|
||||
// Output inline critical CSS
|
||||
echo '<style id="apus-critical-css">' . $critical_css . '</style>' . "\n";
|
||||
}
|
||||
add_action( 'wp_head', 'apus_output_critical_css', 1 );
|
||||
|
||||
/**
|
||||
* Load main stylesheet asynchronously when critical CSS is enabled
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_async_main_stylesheet() {
|
||||
if ( ! apus_is_critical_css_enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dequeue main stylesheet to prevent render-blocking
|
||||
wp_dequeue_style( 'apus-theme-style' );
|
||||
|
||||
// Enqueue with media="print" and onload to load asynchronously
|
||||
wp_enqueue_style(
|
||||
'apus-theme-style-async',
|
||||
get_stylesheet_uri(),
|
||||
array(),
|
||||
APUS_VERSION,
|
||||
'print'
|
||||
);
|
||||
|
||||
// Add onload attribute to switch media to "all"
|
||||
add_filter( 'style_loader_tag', 'apus_add_async_attribute', 10, 2 );
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'apus_async_main_stylesheet', 999 );
|
||||
|
||||
/**
|
||||
* Add async loading attributes to stylesheet
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $html The link tag for the enqueued style.
|
||||
* @param string $handle The style's registered handle.
|
||||
* @return string Modified link tag
|
||||
*/
|
||||
function apus_add_async_attribute( $html, $handle ) {
|
||||
if ( 'apus-theme-style-async' !== $handle ) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
// Add onload attribute to switch media to "all"
|
||||
$html = str_replace(
|
||||
"media='print'",
|
||||
"media='print' onload=\"this.media='all'\"",
|
||||
$html
|
||||
);
|
||||
|
||||
// Add noscript fallback
|
||||
$html .= '<noscript><link rel="stylesheet" href="' . get_stylesheet_uri() . '"></noscript>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Customizer setting for Critical CSS
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
|
||||
*/
|
||||
function apus_critical_css_customizer( $wp_customize ) {
|
||||
// Add Performance section
|
||||
$wp_customize->add_section(
|
||||
'apus_performance',
|
||||
array(
|
||||
'title' => __( 'Performance Optimization', 'apus-theme' ),
|
||||
'priority' => 130,
|
||||
)
|
||||
);
|
||||
|
||||
// Critical CSS Enable/Disable
|
||||
$wp_customize->add_setting(
|
||||
'apus_enable_critical_css',
|
||||
array(
|
||||
'default' => false,
|
||||
'sanitize_callback' => 'apus_sanitize_checkbox',
|
||||
'transport' => 'refresh',
|
||||
)
|
||||
);
|
||||
|
||||
$wp_customize->add_control(
|
||||
'apus_enable_critical_css',
|
||||
array(
|
||||
'label' => __( 'Enable Critical CSS', 'apus-theme' ),
|
||||
'description' => __( 'Inline critical CSS and load main stylesheet asynchronously. This can improve Core Web Vitals but may cause a flash of unstyled content (FOUC). Test thoroughly before enabling in production.', 'apus-theme' ),
|
||||
'section' => 'apus_performance',
|
||||
'type' => 'checkbox',
|
||||
)
|
||||
);
|
||||
}
|
||||
add_action( 'customize_register', 'apus_critical_css_customizer' );
|
||||
|
||||
/**
|
||||
* Sanitize checkbox value
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param bool $checked Whether the checkbox is checked.
|
||||
* @return bool
|
||||
*/
|
||||
function apus_sanitize_checkbox( $checked ) {
|
||||
return ( isset( $checked ) && true === $checked ) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear critical CSS cache when theme is updated
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_clear_critical_css_cache() {
|
||||
$page_types = array( 'home', 'single', 'archive', 'search', '404', 'page' );
|
||||
|
||||
foreach ( $page_types as $type ) {
|
||||
delete_transient( 'apus_critical_css_' . $type );
|
||||
}
|
||||
}
|
||||
add_action( 'after_switch_theme', 'apus_clear_critical_css_cache' );
|
||||
add_action( 'customize_save_after', 'apus_clear_critical_css_cache' );
|
||||
160
wp-content/themes/apus-theme/inc/customizer-fonts.php
Normal file
160
wp-content/themes/apus-theme/inc/customizer-fonts.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
/**
|
||||
* Font Options for Theme Customizer
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register font settings in the Customizer
|
||||
*/
|
||||
function apus_customize_register_fonts($wp_customize) {
|
||||
|
||||
// Add Typography Section
|
||||
$wp_customize->add_section('apus_typography', array(
|
||||
'title' => __('Typography', 'apus-theme'),
|
||||
'description' => __('Configure font settings for your site.', 'apus-theme'),
|
||||
'priority' => 30,
|
||||
));
|
||||
|
||||
// Setting: Use Custom Fonts
|
||||
$wp_customize->add_setting('apus_use_custom_fonts', array(
|
||||
'default' => false,
|
||||
'sanitize_callback' => 'apus_sanitize_checkbox',
|
||||
'transport' => 'refresh',
|
||||
));
|
||||
|
||||
$wp_customize->add_control('apus_use_custom_fonts', array(
|
||||
'label' => __('Use Custom Fonts', 'apus-theme'),
|
||||
'description' => __('Enable custom fonts instead of system fonts. System fonts load faster and improve Core Web Vitals.', 'apus-theme'),
|
||||
'section' => 'apus_typography',
|
||||
'type' => 'checkbox',
|
||||
));
|
||||
|
||||
// Setting: Font Loading Strategy (only if custom fonts enabled)
|
||||
$wp_customize->add_setting('apus_font_display', array(
|
||||
'default' => 'swap',
|
||||
'sanitize_callback' => 'apus_sanitize_select',
|
||||
'transport' => 'refresh',
|
||||
));
|
||||
|
||||
$wp_customize->add_control('apus_font_display', array(
|
||||
'label' => __('Font Display Strategy', 'apus-theme'),
|
||||
'description' => __('Controls how fonts are displayed during loading. "swap" is recommended for best performance.', 'apus-theme'),
|
||||
'section' => 'apus_typography',
|
||||
'type' => 'select',
|
||||
'choices' => array(
|
||||
'auto' => __('Auto', 'apus-theme'),
|
||||
'block' => __('Block', 'apus-theme'),
|
||||
'swap' => __('Swap (Recommended)', 'apus-theme'),
|
||||
'fallback' => __('Fallback', 'apus-theme'),
|
||||
'optional' => __('Optional', 'apus-theme'),
|
||||
),
|
||||
'active_callback' => 'apus_is_custom_fonts_enabled',
|
||||
));
|
||||
|
||||
// Setting: Preload Fonts
|
||||
$wp_customize->add_setting('apus_preload_fonts', array(
|
||||
'default' => true,
|
||||
'sanitize_callback' => 'apus_sanitize_checkbox',
|
||||
'transport' => 'refresh',
|
||||
));
|
||||
|
||||
$wp_customize->add_control('apus_preload_fonts', array(
|
||||
'label' => __('Preload Font Files', 'apus-theme'),
|
||||
'description' => __('Preload critical font files for faster rendering. Recommended for better LCP scores.', 'apus-theme'),
|
||||
'section' => 'apus_typography',
|
||||
'type' => 'checkbox',
|
||||
'active_callback' => 'apus_is_custom_fonts_enabled',
|
||||
));
|
||||
}
|
||||
|
||||
add_action('customize_register', 'apus_customize_register_fonts');
|
||||
|
||||
/**
|
||||
* Sanitize checkbox
|
||||
*/
|
||||
function apus_sanitize_checkbox($input) {
|
||||
return (isset($input) && true === $input) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize select
|
||||
*/
|
||||
function apus_sanitize_select($input, $setting) {
|
||||
$choices = $setting->manager->get_control($setting->id)->choices;
|
||||
return (array_key_exists($input, $choices) ? $input : $setting->default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if custom fonts are enabled
|
||||
*/
|
||||
function apus_is_custom_fonts_enabled() {
|
||||
return get_theme_mod('apus_use_custom_fonts', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add body class based on font settings
|
||||
*/
|
||||
function apus_font_body_class($classes) {
|
||||
if (apus_is_custom_fonts_enabled()) {
|
||||
$classes[] = 'use-custom-fonts';
|
||||
} else {
|
||||
$classes[] = 'use-system-fonts';
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
|
||||
add_filter('body_class', 'apus_font_body_class');
|
||||
|
||||
/**
|
||||
* Add preload links for custom fonts
|
||||
*/
|
||||
function apus_preload_custom_fonts() {
|
||||
// Only preload if custom fonts are enabled and preload is enabled
|
||||
if (!apus_is_custom_fonts_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!get_theme_mod('apus_preload_fonts', true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Example preload links - uncomment and modify when you have custom font files
|
||||
/*
|
||||
?>
|
||||
<link rel="preload" href="<?php echo esc_url(get_template_directory_uri() . '/assets/fonts/CustomSans-Regular.woff2'); ?>" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="<?php echo esc_url(get_template_directory_uri() . '/assets/fonts/CustomSans-Bold.woff2'); ?>" as="font" type="font/woff2" crossorigin>
|
||||
<?php
|
||||
*/
|
||||
}
|
||||
|
||||
add_action('wp_head', 'apus_preload_custom_fonts', 1);
|
||||
|
||||
/**
|
||||
* Output inline CSS for font display strategy
|
||||
*/
|
||||
function apus_output_font_display_css() {
|
||||
if (!apus_is_custom_fonts_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$font_display = get_theme_mod('apus_font_display', 'swap');
|
||||
|
||||
// This would be used if you have actual @font-face declarations
|
||||
// For now, it's just a placeholder for future implementation
|
||||
?>
|
||||
<style id="apus-font-display-strategy">
|
||||
/* Font display strategy: <?php echo esc_attr($font_display); ?> */
|
||||
/* This would contain dynamic @font-face rules when custom fonts are added */
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
add_action('wp_head', 'apus_output_font_display_css', 5);
|
||||
233
wp-content/themes/apus-theme/inc/enqueue-scripts.php
Normal file
233
wp-content/themes/apus-theme/inc/enqueue-scripts.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
/**
|
||||
* Enqueue Bootstrap 5 and Custom Scripts
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue typography system
|
||||
*/
|
||||
function apus_enqueue_fonts() {
|
||||
wp_enqueue_style(
|
||||
'apus-fonts',
|
||||
get_template_directory_uri() . '/assets/css/fonts.css',
|
||||
array(),
|
||||
'1.0.0',
|
||||
'all'
|
||||
);
|
||||
}
|
||||
|
||||
add_action('wp_enqueue_scripts', 'apus_enqueue_fonts', 1);
|
||||
|
||||
/**
|
||||
* Enqueue Bootstrap 5 styles and scripts
|
||||
*/
|
||||
function apus_enqueue_bootstrap() {
|
||||
// Bootstrap CSS - with high priority
|
||||
wp_enqueue_style(
|
||||
'apus-bootstrap',
|
||||
get_template_directory_uri() . '/assets/css/bootstrap.min.css',
|
||||
array('apus-fonts'),
|
||||
'5.3.8',
|
||||
'all'
|
||||
);
|
||||
|
||||
// Bootstrap JS Bundle - in footer with defer
|
||||
wp_enqueue_script(
|
||||
'apus-bootstrap-js',
|
||||
get_template_directory_uri() . '/assets/js/bootstrap.bundle.min.js',
|
||||
array(),
|
||||
'5.3.8',
|
||||
array(
|
||||
'in_footer' => true,
|
||||
'strategy' => 'defer',
|
||||
)
|
||||
);
|
||||
|
||||
// Dequeue jQuery if it was enqueued
|
||||
wp_dequeue_script('jquery');
|
||||
wp_deregister_script('jquery');
|
||||
}
|
||||
|
||||
add_action('wp_enqueue_scripts', 'apus_enqueue_bootstrap', 5);
|
||||
|
||||
/**
|
||||
* Enqueue header styles and scripts
|
||||
*/
|
||||
function apus_enqueue_header() {
|
||||
// Header CSS
|
||||
wp_enqueue_style(
|
||||
'apus-header',
|
||||
get_template_directory_uri() . '/assets/css/header.css',
|
||||
array('apus-fonts'),
|
||||
'1.0.0',
|
||||
'all'
|
||||
);
|
||||
|
||||
// Header JS - with defer strategy
|
||||
wp_enqueue_script(
|
||||
'apus-header-js',
|
||||
get_template_directory_uri() . '/assets/js/header.js',
|
||||
array(),
|
||||
'1.0.0',
|
||||
array(
|
||||
'in_footer' => true,
|
||||
'strategy' => 'defer',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
add_action('wp_enqueue_scripts', 'apus_enqueue_header', 10);
|
||||
|
||||
/**
|
||||
* Enqueue footer styles
|
||||
*/
|
||||
function apus_enqueue_footer_styles() {
|
||||
wp_enqueue_style(
|
||||
'apus-footer',
|
||||
get_template_directory_uri() . '/assets/css/footer.css',
|
||||
array('apus-bootstrap'),
|
||||
APUS_VERSION,
|
||||
'all'
|
||||
);
|
||||
}
|
||||
|
||||
add_action('wp_enqueue_scripts', 'apus_enqueue_footer_styles', 12);
|
||||
|
||||
/**
|
||||
* Enqueue accessibility styles
|
||||
*/
|
||||
function apus_enqueue_accessibility() {
|
||||
wp_enqueue_style(
|
||||
'apus-accessibility',
|
||||
get_template_directory_uri() . '/assets/css/accessibility.css',
|
||||
array('apus-theme-style'),
|
||||
'1.0.0',
|
||||
'all'
|
||||
);
|
||||
}
|
||||
|
||||
add_action('wp_enqueue_scripts', 'apus_enqueue_accessibility', 15);
|
||||
|
||||
/**
|
||||
* Enqueue AdSense loader script
|
||||
*/
|
||||
function apus_enqueue_adsense_loader() {
|
||||
// Only run on frontend
|
||||
if (is_admin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if AdSense delay is enabled
|
||||
$delay_enabled = get_theme_mod('apus_adsense_delay_enabled', true);
|
||||
|
||||
if (!$delay_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue AdSense loader script
|
||||
wp_enqueue_script(
|
||||
'apus-adsense-loader',
|
||||
get_template_directory_uri() . '/assets/js/adsense-loader.js',
|
||||
array(),
|
||||
APUS_VERSION,
|
||||
array(
|
||||
'in_footer' => true,
|
||||
'strategy' => 'defer',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
add_action('wp_enqueue_scripts', 'apus_enqueue_adsense_loader', 10);
|
||||
|
||||
/**
|
||||
* Enqueue Table of Contents styles and scripts
|
||||
*/
|
||||
function apus_enqueue_toc_assets() {
|
||||
// Only enqueue on single posts
|
||||
if (!is_single()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TOC CSS
|
||||
wp_enqueue_style(
|
||||
'apus-toc-style',
|
||||
get_template_directory_uri() . '/assets/css/toc.css',
|
||||
array('apus-bootstrap'),
|
||||
APUS_VERSION,
|
||||
'all'
|
||||
);
|
||||
|
||||
// TOC JS
|
||||
wp_enqueue_script(
|
||||
'apus-toc-script',
|
||||
get_template_directory_uri() . '/assets/js/toc.js',
|
||||
array(),
|
||||
APUS_VERSION,
|
||||
array(
|
||||
'in_footer' => true,
|
||||
'strategy' => 'defer',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
add_action('wp_enqueue_scripts', 'apus_enqueue_toc_assets', 10);
|
||||
|
||||
/**
|
||||
* Enqueue theme core styles
|
||||
*/
|
||||
function apus_enqueue_theme_styles() {
|
||||
// Theme Core Styles
|
||||
wp_enqueue_style(
|
||||
'apus-theme',
|
||||
get_template_directory_uri() . '/assets/css/theme.css',
|
||||
array('apus-bootstrap'),
|
||||
'1.0.0',
|
||||
'all'
|
||||
);
|
||||
|
||||
// Theme Animations
|
||||
wp_enqueue_style(
|
||||
'apus-animations',
|
||||
get_template_directory_uri() . '/assets/css/animations.css',
|
||||
array('apus-theme'),
|
||||
'1.0.0',
|
||||
'all'
|
||||
);
|
||||
|
||||
// Theme Responsive Styles
|
||||
wp_enqueue_style(
|
||||
'apus-responsive',
|
||||
get_template_directory_uri() . '/assets/css/responsive.css',
|
||||
array('apus-theme'),
|
||||
'1.0.0',
|
||||
'all'
|
||||
);
|
||||
|
||||
// Theme Utilities
|
||||
wp_enqueue_style(
|
||||
'apus-utilities',
|
||||
get_template_directory_uri() . '/assets/css/utilities.css',
|
||||
array('apus-theme'),
|
||||
'1.0.0',
|
||||
'all'
|
||||
);
|
||||
|
||||
// Print Styles
|
||||
wp_enqueue_style(
|
||||
'apus-print',
|
||||
get_template_directory_uri() . '/assets/css/print.css',
|
||||
array(),
|
||||
'1.0.0',
|
||||
'print'
|
||||
);
|
||||
}
|
||||
|
||||
add_action('wp_enqueue_scripts', 'apus_enqueue_theme_styles', 13);
|
||||
150
wp-content/themes/apus-theme/inc/featured-image.php
Normal file
150
wp-content/themes/apus-theme/inc/featured-image.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
/**
|
||||
* Featured Image Functions
|
||||
*
|
||||
* Configurable featured image display with lazy loading support.
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Featured Image HTML
|
||||
*
|
||||
* Returns the HTML for the featured image based on theme options.
|
||||
* Supports lazy loading and configurable display per post type.
|
||||
*
|
||||
* @param int $post_id Optional. Post ID. Defaults to current post.
|
||||
* @param string $size Optional. Image size. Default 'apus-featured-large'.
|
||||
* @param array $attr Optional. Additional attributes for the image.
|
||||
* @param bool $force_show Optional. Force display regardless of settings. Default false.
|
||||
* @return string HTML markup for featured image or empty string.
|
||||
*/
|
||||
function apus_get_featured_image($post_id = null, $size = 'apus-featured-large', $attr = array(), $force_show = false) {
|
||||
// Get post ID if not provided
|
||||
if (!$post_id) {
|
||||
$post_id = get_the_ID();
|
||||
}
|
||||
|
||||
// Check if post has thumbnail
|
||||
if (!has_post_thumbnail($post_id)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Get post type
|
||||
$post_type = get_post_type($post_id);
|
||||
|
||||
// Check if featured images are enabled for this post type
|
||||
if (!$force_show) {
|
||||
$option_key = 'apus_featured_image_' . $post_type;
|
||||
$enabled = get_theme_mod($option_key, true); // Default enabled
|
||||
|
||||
if (!$enabled) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// Default attributes
|
||||
$default_attr = array(
|
||||
'alt' => get_the_title($post_id),
|
||||
'loading' => 'lazy',
|
||||
'class' => 'featured-image',
|
||||
);
|
||||
|
||||
// Merge with custom attributes
|
||||
$attributes = wp_parse_args($attr, $default_attr);
|
||||
|
||||
// Get the thumbnail HTML
|
||||
$thumbnail = get_the_post_thumbnail($post_id, $size, $attributes);
|
||||
|
||||
if (empty($thumbnail)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Wrap in container div
|
||||
$output = '<div class="post-thumbnail">';
|
||||
$output .= $thumbnail;
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display Featured Image
|
||||
*
|
||||
* Echoes the featured image HTML.
|
||||
*
|
||||
* @param int $post_id Optional. Post ID. Defaults to current post.
|
||||
* @param string $size Optional. Image size. Default 'apus-featured-large'.
|
||||
* @param array $attr Optional. Additional attributes for the image.
|
||||
* @param bool $force_show Optional. Force display regardless of settings. Default false.
|
||||
*/
|
||||
function apus_the_featured_image($post_id = null, $size = 'apus-featured-large', $attr = array(), $force_show = false) {
|
||||
echo apus_get_featured_image($post_id, $size, $attr, $force_show);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Featured Images are Enabled for Post Type
|
||||
*
|
||||
* @param string $post_type Optional. Post type. Defaults to current post type.
|
||||
* @return bool True if enabled, false otherwise.
|
||||
*/
|
||||
function apus_is_featured_image_enabled($post_type = '') {
|
||||
if (empty($post_type)) {
|
||||
$post_type = get_post_type();
|
||||
}
|
||||
|
||||
$option_key = 'apus_featured_image_' . $post_type;
|
||||
return (bool) get_theme_mod($option_key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Featured Image Settings in Customizer
|
||||
*
|
||||
* Adds controls to enable/disable featured images per post type.
|
||||
*
|
||||
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
|
||||
*/
|
||||
function apus_featured_image_customizer($wp_customize) {
|
||||
// Add section
|
||||
$wp_customize->add_section('apus_featured_images', array(
|
||||
'title' => __('Featured Images', 'apus-theme'),
|
||||
'priority' => 30,
|
||||
));
|
||||
|
||||
// Get public post types
|
||||
$post_types = get_post_types(array('public' => true), 'objects');
|
||||
|
||||
foreach ($post_types as $post_type) {
|
||||
// Skip attachments
|
||||
if ($post_type->name === 'attachment') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$setting_id = 'apus_featured_image_' . $post_type->name;
|
||||
|
||||
// Add setting
|
||||
$wp_customize->add_setting($setting_id, array(
|
||||
'default' => true,
|
||||
'sanitize_callback' => 'wp_validate_boolean',
|
||||
'transport' => 'refresh',
|
||||
));
|
||||
|
||||
// Add control
|
||||
$wp_customize->add_control($setting_id, array(
|
||||
'label' => sprintf(
|
||||
/* translators: %s: post type label */
|
||||
__('Enable for %s', 'apus-theme'),
|
||||
$post_type->labels->name
|
||||
),
|
||||
'section' => 'apus_featured_images',
|
||||
'type' => 'checkbox',
|
||||
));
|
||||
}
|
||||
}
|
||||
add_action('customize_register', 'apus_featured_image_customizer');
|
||||
352
wp-content/themes/apus-theme/inc/image-optimization.php
Normal file
352
wp-content/themes/apus-theme/inc/image-optimization.php
Normal file
@@ -0,0 +1,352 @@
|
||||
<?php
|
||||
/**
|
||||
* Image Optimization Functions
|
||||
*
|
||||
* Handles responsive images, WebP/AVIF support, lazy loading, and image preloading.
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable AVIF image support
|
||||
*/
|
||||
function apus_enable_avif_support($mime_types) {
|
||||
$mime_types['avif'] = 'image/avif';
|
||||
return $mime_types;
|
||||
}
|
||||
add_filter('upload_mimes', 'apus_enable_avif_support');
|
||||
|
||||
/**
|
||||
* Add AVIF to allowed file extensions
|
||||
*/
|
||||
function apus_allow_avif_extension($types, $file, $filename, $mimes) {
|
||||
if (false !== strpos($filename, '.avif')) {
|
||||
$types['ext'] = 'avif';
|
||||
$types['type'] = 'image/avif';
|
||||
}
|
||||
return $types;
|
||||
}
|
||||
add_filter('wp_check_filetype_and_ext', 'apus_allow_avif_extension', 10, 4);
|
||||
|
||||
/**
|
||||
* Configure custom image sizes
|
||||
* Already defined in functions.php, but we can add more if needed
|
||||
*/
|
||||
function apus_setup_additional_image_sizes() {
|
||||
// Add support for additional modern image sizes
|
||||
add_image_size('apus-hero', 1920, 800, true); // Hero images
|
||||
add_image_size('apus-card', 600, 400, true); // Card thumbnails
|
||||
add_image_size('apus-thumbnail-2x', 800, 600, true); // Retina thumbnails
|
||||
}
|
||||
add_action('after_setup_theme', 'apus_setup_additional_image_sizes');
|
||||
|
||||
/**
|
||||
* Add custom image sizes to media library dropdown
|
||||
*/
|
||||
function apus_custom_image_sizes($sizes) {
|
||||
return array_merge($sizes, array(
|
||||
'apus-thumbnail' => __('Thumbnail (400x300)', 'apus-theme'),
|
||||
'apus-medium' => __('Medium (800x600)', 'apus-theme'),
|
||||
'apus-large' => __('Large (1200x900)', 'apus-theme'),
|
||||
'apus-featured-large' => __('Featured Large (1200x600)', 'apus-theme'),
|
||||
'apus-featured-medium' => __('Featured Medium (800x400)', 'apus-theme'),
|
||||
'apus-hero' => __('Hero (1920x800)', 'apus-theme'),
|
||||
'apus-card' => __('Card (600x400)', 'apus-theme'),
|
||||
));
|
||||
}
|
||||
add_filter('image_size_names_choose', 'apus_custom_image_sizes');
|
||||
|
||||
/**
|
||||
* Generate srcset and sizes attributes for responsive images
|
||||
*
|
||||
* @param int $attachment_id Image attachment ID
|
||||
* @param string $size Image size
|
||||
* @param array $additional_sizes Additional sizes to include in srcset
|
||||
* @return array Array with 'srcset' and 'sizes' attributes
|
||||
*/
|
||||
function apus_get_responsive_image_attrs($attachment_id, $size = 'full', $additional_sizes = array()) {
|
||||
if (empty($attachment_id)) {
|
||||
return array('srcset' => '', 'sizes' => '');
|
||||
}
|
||||
|
||||
// Get default WordPress srcset
|
||||
$srcset = wp_get_attachment_image_srcset($attachment_id, $size);
|
||||
|
||||
// Get default WordPress sizes attribute
|
||||
$sizes = wp_get_attachment_image_sizes($attachment_id, $size);
|
||||
|
||||
// Add additional sizes if specified
|
||||
if (!empty($additional_sizes)) {
|
||||
$srcset_array = array();
|
||||
foreach ($additional_sizes as $additional_size) {
|
||||
$image = wp_get_attachment_image_src($attachment_id, $additional_size);
|
||||
if ($image) {
|
||||
$srcset_array[] = $image[0] . ' ' . $image[1] . 'w';
|
||||
}
|
||||
}
|
||||
if (!empty($srcset_array)) {
|
||||
$srcset .= ', ' . implode(', ', $srcset_array);
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'srcset' => $srcset,
|
||||
'sizes' => $sizes,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output responsive image with lazy loading
|
||||
*
|
||||
* @param int $attachment_id Image attachment ID
|
||||
* @param string $size Image size
|
||||
* @param array $attr Additional image attributes
|
||||
* @param bool $lazy Enable lazy loading (default: true)
|
||||
* @return string Image HTML
|
||||
*/
|
||||
function apus_get_responsive_image($attachment_id, $size = 'full', $attr = array(), $lazy = true) {
|
||||
if (empty($attachment_id)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Get responsive attributes
|
||||
$responsive_attrs = apus_get_responsive_image_attrs($attachment_id, $size);
|
||||
|
||||
// Merge default attributes with custom ones
|
||||
$default_attr = array(
|
||||
'loading' => $lazy ? 'lazy' : 'eager',
|
||||
'decoding' => 'async',
|
||||
);
|
||||
|
||||
// Add srcset and sizes if available
|
||||
if (!empty($responsive_attrs['srcset'])) {
|
||||
$default_attr['srcset'] = $responsive_attrs['srcset'];
|
||||
}
|
||||
if (!empty($responsive_attrs['sizes'])) {
|
||||
$default_attr['sizes'] = $responsive_attrs['sizes'];
|
||||
}
|
||||
|
||||
$attr = array_merge($default_attr, $attr);
|
||||
|
||||
return wp_get_attachment_image($attachment_id, $size, false, $attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable lazy loading by default for all images
|
||||
*/
|
||||
function apus_add_lazy_loading_to_images($attr, $attachment, $size) {
|
||||
// Don't add lazy loading if explicitly disabled
|
||||
if (isset($attr['loading']) && $attr['loading'] === 'eager') {
|
||||
return $attr;
|
||||
}
|
||||
|
||||
// Add lazy loading by default
|
||||
if (!isset($attr['loading'])) {
|
||||
$attr['loading'] = 'lazy';
|
||||
}
|
||||
|
||||
// Add async decoding for better performance
|
||||
if (!isset($attr['decoding'])) {
|
||||
$attr['decoding'] = 'async';
|
||||
}
|
||||
|
||||
return $attr;
|
||||
}
|
||||
add_filter('wp_get_attachment_image_attributes', 'apus_add_lazy_loading_to_images', 10, 3);
|
||||
|
||||
/**
|
||||
* Add lazy loading to content images
|
||||
*/
|
||||
function apus_add_lazy_loading_to_content($content) {
|
||||
// Don't process if empty
|
||||
if (empty($content)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Add loading="lazy" to images that don't have it
|
||||
$content = preg_replace('/<img(?![^>]*loading=)/', '<img loading="lazy" decoding="async"', $content);
|
||||
|
||||
return $content;
|
||||
}
|
||||
add_filter('the_content', 'apus_add_lazy_loading_to_content', 20);
|
||||
|
||||
/**
|
||||
* Preload critical images (LCP images)
|
||||
* This should be called for above-the-fold images
|
||||
*
|
||||
* @param int $attachment_id Image attachment ID
|
||||
* @param string $size Image size
|
||||
*/
|
||||
function apus_preload_image($attachment_id, $size = 'full') {
|
||||
if (empty($attachment_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$image_src = wp_get_attachment_image_src($attachment_id, $size);
|
||||
if (!$image_src) {
|
||||
return;
|
||||
}
|
||||
|
||||
$srcset = wp_get_attachment_image_srcset($attachment_id, $size);
|
||||
$sizes = wp_get_attachment_image_sizes($attachment_id, $size);
|
||||
|
||||
// Detect image format
|
||||
$image_url = $image_src[0];
|
||||
$image_type = 'image/jpeg'; // default
|
||||
|
||||
if (strpos($image_url, '.avif') !== false) {
|
||||
$image_type = 'image/avif';
|
||||
} elseif (strpos($image_url, '.webp') !== false) {
|
||||
$image_type = 'image/webp';
|
||||
} elseif (strpos($image_url, '.png') !== false) {
|
||||
$image_type = 'image/png';
|
||||
}
|
||||
|
||||
// Build preload link
|
||||
$preload = sprintf(
|
||||
'<link rel="preload" as="image" href="%s" type="%s"',
|
||||
esc_url($image_url),
|
||||
esc_attr($image_type)
|
||||
);
|
||||
|
||||
if ($srcset) {
|
||||
$preload .= sprintf(' imagesrcset="%s"', esc_attr($srcset));
|
||||
}
|
||||
if ($sizes) {
|
||||
$preload .= sprintf(' imagesizes="%s"', esc_attr($sizes));
|
||||
}
|
||||
|
||||
$preload .= '>';
|
||||
|
||||
echo $preload . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload featured image for single posts (LCP optimization)
|
||||
*/
|
||||
function apus_preload_featured_image() {
|
||||
// Only on single posts/pages with featured images
|
||||
if (!is_singular() || !has_post_thumbnail()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the featured image ID
|
||||
$thumbnail_id = get_post_thumbnail_id();
|
||||
|
||||
// Determine the size based on the post type
|
||||
$size = 'apus-featured-large';
|
||||
|
||||
// Preload the image
|
||||
apus_preload_image($thumbnail_id, $size);
|
||||
}
|
||||
add_action('wp_head', 'apus_preload_featured_image', 5);
|
||||
|
||||
/**
|
||||
* Enable fetchpriority attribute for featured images
|
||||
*/
|
||||
function apus_add_fetchpriority_to_featured_image($attr, $attachment, $size) {
|
||||
// Only add fetchpriority="high" to featured images on singular pages
|
||||
if (is_singular() && get_post_thumbnail_id() === $attachment->ID) {
|
||||
$attr['fetchpriority'] = 'high';
|
||||
$attr['loading'] = 'eager'; // Don't lazy load LCP images
|
||||
}
|
||||
|
||||
return $attr;
|
||||
}
|
||||
add_filter('wp_get_attachment_image_attributes', 'apus_add_fetchpriority_to_featured_image', 20, 3);
|
||||
|
||||
/**
|
||||
* Optimize image quality for uploads
|
||||
*/
|
||||
function apus_optimize_image_quality($quality, $mime_type) {
|
||||
// Set quality to 85% for better file size without visible quality loss
|
||||
if ($mime_type === 'image/jpeg') {
|
||||
return 85;
|
||||
}
|
||||
return $quality;
|
||||
}
|
||||
add_filter('jpeg_quality', 'apus_optimize_image_quality', 10, 2);
|
||||
add_filter('wp_editor_set_quality', 'apus_optimize_image_quality', 10, 2);
|
||||
|
||||
/**
|
||||
* Add picture element support for WebP/AVIF with fallbacks
|
||||
*
|
||||
* @param int $attachment_id Image attachment ID
|
||||
* @param string $size Image size
|
||||
* @param array $attr Additional attributes
|
||||
* @return string Picture element HTML
|
||||
*/
|
||||
function apus_get_picture_element($attachment_id, $size = 'full', $attr = array()) {
|
||||
if (empty($attachment_id)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$image_src = wp_get_attachment_image_src($attachment_id, $size);
|
||||
if (!$image_src) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$srcset = wp_get_attachment_image_srcset($attachment_id, $size);
|
||||
$sizes = wp_get_attachment_image_sizes($attachment_id, $size);
|
||||
$alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
|
||||
|
||||
// Default attributes
|
||||
$default_attr = array(
|
||||
'loading' => 'lazy',
|
||||
'decoding' => 'async',
|
||||
'alt' => $alt,
|
||||
);
|
||||
|
||||
$attr = array_merge($default_attr, $attr);
|
||||
|
||||
// Build picture element
|
||||
$picture = '<picture>';
|
||||
|
||||
// Add AVIF source if available
|
||||
$avif_url = str_replace(array('.jpg', '.jpeg', '.png', '.webp'), '.avif', $image_src[0]);
|
||||
if ($avif_url !== $image_src[0]) {
|
||||
$picture .= sprintf(
|
||||
'<source type="image/avif" srcset="%s" sizes="%s">',
|
||||
esc_attr($avif_url),
|
||||
esc_attr($sizes)
|
||||
);
|
||||
}
|
||||
|
||||
// Add WebP source if available
|
||||
$webp_url = str_replace(array('.jpg', '.jpeg', '.png'), '.webp', $image_src[0]);
|
||||
if ($webp_url !== $image_src[0]) {
|
||||
$picture .= sprintf(
|
||||
'<source type="image/webp" srcset="%s" sizes="%s">',
|
||||
esc_attr($webp_url),
|
||||
esc_attr($sizes)
|
||||
);
|
||||
}
|
||||
|
||||
// Fallback img tag
|
||||
$picture .= wp_get_attachment_image($attachment_id, $size, false, $attr);
|
||||
$picture .= '</picture>';
|
||||
|
||||
return $picture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable big image threshold for high-quality images
|
||||
* WordPress 5.3+ scales down images larger than 2560px by default
|
||||
* Uncomment to disable this behavior if you need full-size images
|
||||
*/
|
||||
// add_filter('big_image_size_threshold', '__return_false');
|
||||
|
||||
/**
|
||||
* Set maximum srcset image width
|
||||
*/
|
||||
function apus_max_srcset_image_width($max_width, $size_array) {
|
||||
// Limit srcset to images up to 2560px wide
|
||||
return 2560;
|
||||
}
|
||||
add_filter('max_srcset_image_width', 'apus_max_srcset_image_width', 10, 2);
|
||||
468
wp-content/themes/apus-theme/inc/performance.php
Normal file
468
wp-content/themes/apus-theme/inc/performance.php
Normal file
@@ -0,0 +1,468 @@
|
||||
<?php
|
||||
/**
|
||||
* Performance Optimization Functions
|
||||
*
|
||||
* Functions to remove WordPress bloat and improve performance.
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable WordPress Emojis
|
||||
*
|
||||
* Removes emoji detection scripts and styles from WordPress.
|
||||
* These scripts are loaded on every page but rarely used.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_disable_emojis() {
|
||||
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
|
||||
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
|
||||
remove_action( 'wp_print_styles', 'print_emoji_styles' );
|
||||
remove_action( 'admin_print_styles', 'print_emoji_styles' );
|
||||
remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
|
||||
remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
|
||||
remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
|
||||
|
||||
// Remove from TinyMCE.
|
||||
add_filter( 'tiny_mce_plugins', 'apus_disable_emojis_tinymce' );
|
||||
add_filter( 'wp_resource_hints', 'apus_disable_emojis_remove_dns_prefetch', 10, 2 );
|
||||
}
|
||||
add_action( 'init', 'apus_disable_emojis' );
|
||||
|
||||
/**
|
||||
* Filter function used to remove emoji plugin from TinyMCE
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $plugins An array of default TinyMCE plugins.
|
||||
* @return array Modified array of TinyMCE plugins without emoji plugin.
|
||||
*/
|
||||
function apus_disable_emojis_tinymce( $plugins ) {
|
||||
if ( is_array( $plugins ) ) {
|
||||
return array_diff( $plugins, array( 'wpemoji' ) );
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove emoji CDN hostname from DNS prefetching hints
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $urls URLs to print for resource hints.
|
||||
* @param string $relation_type The relation type the URLs are printed for.
|
||||
* @return array Modified array of URLs.
|
||||
*/
|
||||
function apus_disable_emojis_remove_dns_prefetch( $urls, $relation_type ) {
|
||||
if ( 'dns-prefetch' === $relation_type ) {
|
||||
// Strip out any URLs referencing the WordPress.org emoji location.
|
||||
$emoji_svg_url_bit = 'https://s.w.org/images/core/emoji/';
|
||||
foreach ( $urls as $key => $url ) {
|
||||
if ( strpos( $url, $emoji_svg_url_bit ) !== false ) {
|
||||
unset( $urls[ $key ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable WordPress oEmbed
|
||||
*
|
||||
* Removes oEmbed discovery links and scripts.
|
||||
* Only disable if you don't need to embed content from other sites.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_disable_oembed() {
|
||||
// Remove oEmbed discovery links.
|
||||
remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );
|
||||
|
||||
// Remove oEmbed-specific JavaScript from the front-end and back-end.
|
||||
remove_action( 'wp_head', 'wp_oembed_add_host_js' );
|
||||
|
||||
// Remove all embeds rewrite rules.
|
||||
add_filter( 'rewrite_rules_array', 'apus_disable_oembed_rewrites' );
|
||||
|
||||
// Remove filter of the oEmbed result before any HTTP requests are made.
|
||||
remove_filter( 'pre_oembed_result', 'wp_filter_pre_oembed_result', 10 );
|
||||
}
|
||||
add_action( 'init', 'apus_disable_oembed', 9999 );
|
||||
|
||||
/**
|
||||
* Remove all rewrite rules related to embeds
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $rules WordPress rewrite rules.
|
||||
* @return array Modified rewrite rules.
|
||||
*/
|
||||
function apus_disable_oembed_rewrites( $rules ) {
|
||||
foreach ( $rules as $rule => $rewrite ) {
|
||||
if ( false !== strpos( $rewrite, 'embed=true' ) ) {
|
||||
unset( $rules[ $rule ] );
|
||||
}
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable wp-embed.js script
|
||||
*
|
||||
* Dequeues the wp-embed.js script that WordPress loads by default.
|
||||
* This script is used for embedding WordPress posts on other sites.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_dequeue_embed_script() {
|
||||
wp_deregister_script( 'wp-embed' );
|
||||
}
|
||||
add_action( 'wp_footer', 'apus_dequeue_embed_script' );
|
||||
|
||||
/**
|
||||
* Disable WordPress Feeds
|
||||
*
|
||||
* Removes RSS, RDF, and Atom feeds.
|
||||
* Only disable if you don't need feed functionality.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_disable_feeds() {
|
||||
wp_die(
|
||||
esc_html__( 'No feed available, please visit our homepage!', 'apus' ),
|
||||
esc_html__( 'Feed Not Available', 'apus' ),
|
||||
array(
|
||||
'response' => 404,
|
||||
'back_link' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove feed links and redirect feed URLs
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_disable_feed_links() {
|
||||
// Remove feed links from header.
|
||||
remove_action( 'wp_head', 'feed_links', 2 );
|
||||
remove_action( 'wp_head', 'feed_links_extra', 3 );
|
||||
|
||||
// Redirect feed URLs to homepage.
|
||||
add_action( 'do_feed', 'apus_disable_feeds', 1 );
|
||||
add_action( 'do_feed_rdf', 'apus_disable_feeds', 1 );
|
||||
add_action( 'do_feed_rss', 'apus_disable_feeds', 1 );
|
||||
add_action( 'do_feed_rss2', 'apus_disable_feeds', 1 );
|
||||
add_action( 'do_feed_atom', 'apus_disable_feeds', 1 );
|
||||
add_action( 'do_feed_rss2_comments', 'apus_disable_feeds', 1 );
|
||||
add_action( 'do_feed_atom_comments', 'apus_disable_feeds', 1 );
|
||||
}
|
||||
add_action( 'init', 'apus_disable_feed_links' );
|
||||
|
||||
/**
|
||||
* Disable RSD and Windows Live Writer Manifest
|
||||
*
|
||||
* Really Simple Discovery (RSD) and Windows Live Writer (WLW) manifest
|
||||
* are rarely used and can be safely removed.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_disable_rsd_wlw() {
|
||||
// Remove RSD link.
|
||||
remove_action( 'wp_head', 'rsd_link' );
|
||||
|
||||
// Remove Windows Live Writer manifest link.
|
||||
remove_action( 'wp_head', 'wlwmanifest_link' );
|
||||
}
|
||||
add_action( 'init', 'apus_disable_rsd_wlw' );
|
||||
|
||||
/**
|
||||
* Disable Dashicons for non-logged users
|
||||
*
|
||||
* Dashicons are only needed in the admin area and for logged-in users.
|
||||
* This removes them from the front-end for visitors.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_disable_dashicons() {
|
||||
if ( ! is_user_logged_in() ) {
|
||||
wp_deregister_style( 'dashicons' );
|
||||
}
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'apus_disable_dashicons' );
|
||||
|
||||
/**
|
||||
* Disable Block Library CSS
|
||||
*
|
||||
* Removes the default WordPress block library styles.
|
||||
* Only disable if you're not using the block editor or if you're
|
||||
* providing your own block styles.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_disable_block_library_css() {
|
||||
// Remove default block library styles.
|
||||
wp_dequeue_style( 'wp-block-library' );
|
||||
wp_dequeue_style( 'wp-block-library-theme' );
|
||||
|
||||
// Remove inline global styles.
|
||||
wp_dequeue_style( 'global-styles' );
|
||||
|
||||
// Remove classic theme styles (if not using classic editor).
|
||||
wp_dequeue_style( 'classic-theme-styles' );
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'apus_disable_block_library_css', 100 );
|
||||
|
||||
/**
|
||||
* Remove WordPress version from head and feeds
|
||||
*
|
||||
* Removes the WordPress version number for security reasons.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_remove_wp_version() {
|
||||
return '';
|
||||
}
|
||||
add_filter( 'the_generator', 'apus_remove_wp_version' );
|
||||
|
||||
/**
|
||||
* Disable XML-RPC
|
||||
*
|
||||
* XML-RPC is often targeted by brute force attacks.
|
||||
* Disable if you don't need remote publishing functionality.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @return bool
|
||||
*/
|
||||
function apus_disable_xmlrpc() {
|
||||
return false;
|
||||
}
|
||||
add_filter( 'xmlrpc_enabled', 'apus_disable_xmlrpc' );
|
||||
|
||||
/**
|
||||
* Remove jQuery Migrate
|
||||
*
|
||||
* jQuery Migrate is used for backwards compatibility but adds extra overhead.
|
||||
* Only remove if you've verified all scripts work without it.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param WP_Scripts $scripts WP_Scripts object.
|
||||
*/
|
||||
function apus_remove_jquery_migrate( $scripts ) {
|
||||
if ( ! is_admin() && isset( $scripts->registered['jquery'] ) ) {
|
||||
$script = $scripts->registered['jquery'];
|
||||
|
||||
if ( $script->deps ) {
|
||||
// Remove jquery-migrate from dependencies.
|
||||
$script->deps = array_diff( $script->deps, array( 'jquery-migrate' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
add_action( 'wp_default_scripts', 'apus_remove_jquery_migrate' );
|
||||
|
||||
/**
|
||||
* Disable REST API for non-logged users
|
||||
*
|
||||
* Restricts REST API access to authenticated users only.
|
||||
* Comment out if you need public REST API access.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param WP_Error|null|bool $result Error from another authentication handler, null if not errors.
|
||||
* @return WP_Error|null|bool
|
||||
*/
|
||||
function apus_disable_rest_api( $result ) {
|
||||
if ( ! empty( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ( ! is_user_logged_in() ) {
|
||||
return new WP_Error(
|
||||
'rest_not_logged_in',
|
||||
esc_html__( 'You are not currently logged in.', 'apus' ),
|
||||
array( 'status' => 401 )
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
// Uncomment to enable REST API restriction.
|
||||
// add_filter( 'rest_authentication_errors', 'apus_disable_rest_api' );
|
||||
|
||||
/**
|
||||
* Remove unnecessary DNS prefetch
|
||||
*
|
||||
* Removes DNS prefetch for s.w.org (WordPress.org stats).
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $hints DNS prefetch hints.
|
||||
* @param string $relation_type The relation type.
|
||||
* @return array Modified hints.
|
||||
*/
|
||||
function apus_remove_dns_prefetch( $hints, $relation_type ) {
|
||||
if ( 'dns-prefetch' === $relation_type ) {
|
||||
return array_diff( wp_dependencies_unique_hosts(), $hints );
|
||||
}
|
||||
|
||||
return $hints;
|
||||
}
|
||||
add_filter( 'wp_resource_hints', 'apus_remove_dns_prefetch', 10, 2 );
|
||||
|
||||
/**
|
||||
* Disable heartbeat API
|
||||
*
|
||||
* The heartbeat API can cause high CPU usage.
|
||||
* This limits it to the post editor only.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $settings Heartbeat settings.
|
||||
* @return array Modified settings.
|
||||
*/
|
||||
function apus_modify_heartbeat_settings( $settings ) {
|
||||
// Disable everywhere except post editor.
|
||||
global $pagenow;
|
||||
|
||||
if ( 'post.php' !== $pagenow && 'post-new.php' !== $pagenow ) {
|
||||
wp_deregister_script( 'heartbeat' );
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
add_filter( 'heartbeat_settings', 'apus_modify_heartbeat_settings' );
|
||||
|
||||
/**
|
||||
* Disable WordPress Core Lazy Loading
|
||||
*
|
||||
* WordPress 5.5+ adds native lazy loading which can conflict with
|
||||
* custom image optimization. Only disable if using custom lazy loading.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $value Loading attribute value.
|
||||
* @return string Modified loading attribute.
|
||||
*/
|
||||
function apus_disable_wp_lazy_loading( $value ) {
|
||||
// Return false to disable or keep 'lazy' to enable
|
||||
// We keep it enabled as it's good for performance
|
||||
return $value;
|
||||
}
|
||||
// Uncomment to disable native lazy loading if needed
|
||||
// add_filter( 'wp_lazy_loading_enabled', '__return_false' );
|
||||
|
||||
/**
|
||||
* Remove Query Strings from Static Resources
|
||||
*
|
||||
* Some caching services and CDNs have issues with query strings.
|
||||
* This removes version query strings from CSS and JS files.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $src Source URL.
|
||||
* @return string Modified source URL.
|
||||
*/
|
||||
function apus_remove_query_strings( $src ) {
|
||||
if ( strpos( $src, '?ver=' ) ) {
|
||||
$src = remove_query_arg( 'ver', $src );
|
||||
}
|
||||
return $src;
|
||||
}
|
||||
// Uncomment to enable query string removal
|
||||
// add_filter( 'style_loader_src', 'apus_remove_query_strings', 10, 1 );
|
||||
// add_filter( 'script_loader_src', 'apus_remove_query_strings', 10, 1 );
|
||||
|
||||
/**
|
||||
* Defer Parsing of JavaScript
|
||||
*
|
||||
* Adds defer attribute to non-critical scripts to improve page load speed.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $tag The script tag.
|
||||
* @param string $handle The script handle.
|
||||
* @param string $src The script source URL.
|
||||
* @return string Modified script tag.
|
||||
*/
|
||||
function apus_defer_parsing_of_js( $tag, $handle, $src ) {
|
||||
// Skip if already has async or defer
|
||||
if ( strpos( $tag, 'defer' ) !== false || strpos( $tag, 'async' ) !== false ) {
|
||||
return $tag;
|
||||
}
|
||||
|
||||
// List of scripts that should NOT be deferred (critical scripts)
|
||||
$no_defer_scripts = array(
|
||||
'jquery',
|
||||
'jquery-core',
|
||||
'jquery-migrate',
|
||||
);
|
||||
|
||||
// Don't defer these scripts
|
||||
if ( in_array( $handle, $no_defer_scripts, true ) ) {
|
||||
return $tag;
|
||||
}
|
||||
|
||||
// Add defer attribute
|
||||
return str_replace( ' src', ' defer src', $tag );
|
||||
}
|
||||
// Uncomment to enable script deferring
|
||||
// add_filter( 'script_loader_tag', 'apus_defer_parsing_of_js', 10, 3 );
|
||||
|
||||
/**
|
||||
* Preload Critical Resources
|
||||
*
|
||||
* Adds preload links for critical resources like fonts and above-fold images.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_preload_critical_resources() {
|
||||
// Preload critical fonts (update paths as needed)
|
||||
// Example:
|
||||
// echo '<link rel="preload" href="' . get_template_directory_uri() . '/assets/fonts/font.woff2" as="font" type="font/woff2" crossorigin>';
|
||||
|
||||
// You can add more preload directives here based on your theme's needs
|
||||
}
|
||||
add_action( 'wp_head', 'apus_preload_critical_resources', 1 );
|
||||
|
||||
/**
|
||||
* Add DNS Prefetch for External Domains
|
||||
*
|
||||
* Adds DNS prefetch hints for external resources to speed up loading.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_add_dns_prefetch() {
|
||||
// Add DNS prefetch for common external resources
|
||||
echo '<link rel="dns-prefetch" href="//fonts.googleapis.com">' . "\n";
|
||||
echo '<link rel="dns-prefetch" href="//fonts.gstatic.com">' . "\n";
|
||||
// Add more as needed for your external resources
|
||||
}
|
||||
add_action( 'wp_head', 'apus_add_dns_prefetch', 0 );
|
||||
|
||||
/**
|
||||
* Optimize WordPress Database Queries
|
||||
*
|
||||
* Removes unnecessary meta queries for better performance.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_optimize_queries() {
|
||||
// Remove unnecessary post meta from queries
|
||||
remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10 );
|
||||
remove_action( 'wp_head', 'wp_shortlink_wp_head', 10 );
|
||||
}
|
||||
add_action( 'init', 'apus_optimize_queries' );
|
||||
|
||||
/**
|
||||
* Disable WordPress Admin Bar for Non-Admins
|
||||
*
|
||||
* Reduces HTTP requests for non-admin users.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_disable_admin_bar() {
|
||||
if ( ! current_user_can( 'administrator' ) && ! is_admin() ) {
|
||||
show_admin_bar( false );
|
||||
}
|
||||
}
|
||||
add_action( 'after_setup_theme', 'apus_disable_admin_bar' );
|
||||
294
wp-content/themes/apus-theme/inc/related-posts.php
Normal file
294
wp-content/themes/apus-theme/inc/related-posts.php
Normal file
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
/**
|
||||
* Related Posts Functionality
|
||||
*
|
||||
* Provides configurable related posts functionality with Bootstrap grid support.
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get related posts based on categories
|
||||
*
|
||||
* @param int $post_id The post ID to get related posts for
|
||||
* @return WP_Query|false Query object with related posts or false if none found
|
||||
*/
|
||||
function apus_get_related_posts($post_id) {
|
||||
// Get post categories
|
||||
$categories = wp_get_post_categories($post_id);
|
||||
|
||||
if (empty($categories)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get number of posts to display (default: 3)
|
||||
$posts_per_page = get_option('apus_related_posts_count', 3);
|
||||
|
||||
// Query arguments
|
||||
$args = array(
|
||||
'post_type' => 'post',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => $posts_per_page,
|
||||
'post__not_in' => array($post_id),
|
||||
'category__in' => $categories,
|
||||
'orderby' => 'rand',
|
||||
'no_found_rows' => true,
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
);
|
||||
|
||||
// Allow filtering of query args
|
||||
$args = apply_filters('apus_related_posts_args', $args, $post_id);
|
||||
|
||||
// Get related posts
|
||||
$related_query = new WP_Query($args);
|
||||
|
||||
return $related_query->have_posts() ? $related_query : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display related posts section
|
||||
*
|
||||
* @param int|null $post_id Optional. Post ID. Default is current post.
|
||||
* @return void
|
||||
*/
|
||||
function apus_display_related_posts($post_id = null) {
|
||||
// Get post ID
|
||||
if (!$post_id) {
|
||||
$post_id = get_the_ID();
|
||||
}
|
||||
|
||||
// Check if related posts are enabled
|
||||
$enabled = get_option('apus_related_posts_enabled', true);
|
||||
if (!$enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get related posts
|
||||
$related_query = apus_get_related_posts($post_id);
|
||||
|
||||
if (!$related_query) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get configuration options
|
||||
$title = get_option('apus_related_posts_title', __('Related Posts', 'apus-theme'));
|
||||
$columns = get_option('apus_related_posts_columns', 3);
|
||||
$show_excerpt = get_option('apus_related_posts_show_excerpt', true);
|
||||
$show_date = get_option('apus_related_posts_show_date', true);
|
||||
$show_category = get_option('apus_related_posts_show_category', true);
|
||||
$excerpt_length = get_option('apus_related_posts_excerpt_length', 20);
|
||||
$background_colors = get_option('apus_related_posts_bg_colors', array(
|
||||
'#1a73e8', // Blue
|
||||
'#e91e63', // Pink
|
||||
'#4caf50', // Green
|
||||
'#ff9800', // Orange
|
||||
'#9c27b0', // Purple
|
||||
'#00bcd4', // Cyan
|
||||
));
|
||||
|
||||
// Calculate Bootstrap column class
|
||||
$col_class = apus_get_column_class($columns);
|
||||
|
||||
// Start output
|
||||
?>
|
||||
<section class="related-posts-section">
|
||||
<div class="related-posts-container">
|
||||
|
||||
<?php if ($title) : ?>
|
||||
<h2 class="related-posts-title"><?php echo esc_html($title); ?></h2>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row g-4">
|
||||
<?php
|
||||
$color_index = 0;
|
||||
while ($related_query->have_posts()) :
|
||||
$related_query->the_post();
|
||||
$has_thumbnail = has_post_thumbnail();
|
||||
|
||||
// Get background color for posts without image
|
||||
$bg_color = $background_colors[$color_index % count($background_colors)];
|
||||
$color_index++;
|
||||
?>
|
||||
|
||||
<div class="<?php echo esc_attr($col_class); ?>">
|
||||
<article class="related-post-card <?php echo $has_thumbnail ? 'has-thumbnail' : 'no-thumbnail'; ?>">
|
||||
|
||||
<a href="<?php the_permalink(); ?>" class="related-post-link">
|
||||
|
||||
<?php if ($has_thumbnail) : ?>
|
||||
<!-- Card with Image -->
|
||||
<div class="related-post-thumbnail">
|
||||
<?php
|
||||
the_post_thumbnail('apus-thumbnail', array(
|
||||
'alt' => the_title_attribute(array('echo' => false)),
|
||||
'loading' => 'lazy',
|
||||
));
|
||||
?>
|
||||
|
||||
<?php if ($show_category) : ?>
|
||||
<?php
|
||||
$categories = get_the_category();
|
||||
if (!empty($categories)) :
|
||||
$category = $categories[0];
|
||||
?>
|
||||
<span class="related-post-category">
|
||||
<?php echo esc_html($category->name); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<!-- Card without Image - Color Background -->
|
||||
<div class="related-post-no-image" style="background-color: <?php echo esc_attr($bg_color); ?>;">
|
||||
<div class="related-post-no-image-content">
|
||||
<h3 class="related-post-no-image-title">
|
||||
<?php the_title(); ?>
|
||||
</h3>
|
||||
|
||||
<?php if ($show_category) : ?>
|
||||
<?php
|
||||
$categories = get_the_category();
|
||||
if (!empty($categories)) :
|
||||
$category = $categories[0];
|
||||
?>
|
||||
<span class="related-post-category no-image">
|
||||
<?php echo esc_html($category->name); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="related-post-content">
|
||||
|
||||
<?php if ($has_thumbnail) : ?>
|
||||
<h3 class="related-post-title">
|
||||
<?php the_title(); ?>
|
||||
</h3>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($show_excerpt && $excerpt_length > 0) : ?>
|
||||
<div class="related-post-excerpt">
|
||||
<?php echo wp_trim_words(get_the_excerpt(), $excerpt_length, '...'); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($show_date) : ?>
|
||||
<div class="related-post-meta">
|
||||
<time class="related-post-date" datetime="<?php echo esc_attr(get_the_date('c')); ?>">
|
||||
<?php echo esc_html(get_the_date()); ?>
|
||||
</time>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
|
||||
</a>
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<?php endwhile; ?>
|
||||
</div><!-- .row -->
|
||||
|
||||
</div><!-- .related-posts-container -->
|
||||
</section><!-- .related-posts-section -->
|
||||
|
||||
<?php
|
||||
// Reset post data
|
||||
wp_reset_postdata();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Bootstrap column class based on number of columns
|
||||
*
|
||||
* @param int $columns Number of columns (1-4)
|
||||
* @return string Bootstrap column classes
|
||||
*/
|
||||
function apus_get_column_class($columns) {
|
||||
$columns = absint($columns);
|
||||
|
||||
switch ($columns) {
|
||||
case 1:
|
||||
return 'col-12';
|
||||
case 2:
|
||||
return 'col-12 col-md-6';
|
||||
case 3:
|
||||
return 'col-12 col-sm-6 col-lg-4';
|
||||
case 4:
|
||||
return 'col-12 col-sm-6 col-lg-3';
|
||||
default:
|
||||
return 'col-12 col-sm-6 col-lg-4'; // Default to 3 columns
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook related posts display after post content
|
||||
*/
|
||||
function apus_hook_related_posts() {
|
||||
if (is_single() && !is_attachment()) {
|
||||
apus_display_related_posts();
|
||||
}
|
||||
}
|
||||
add_action('apus_after_post_content', 'apus_hook_related_posts');
|
||||
|
||||
/**
|
||||
* Enqueue related posts styles
|
||||
*/
|
||||
function apus_enqueue_related_posts_styles() {
|
||||
if (is_single() && !is_attachment()) {
|
||||
$enabled = get_option('apus_related_posts_enabled', true);
|
||||
|
||||
if ($enabled) {
|
||||
wp_enqueue_style(
|
||||
'apus-related-posts',
|
||||
get_template_directory_uri() . '/assets/css/related-posts.css',
|
||||
array('apus-bootstrap'),
|
||||
APUS_VERSION,
|
||||
'all'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
add_action('wp_enqueue_scripts', 'apus_enqueue_related_posts_styles');
|
||||
|
||||
/**
|
||||
* Register related posts settings
|
||||
* These can be configured via theme options or customizer
|
||||
*/
|
||||
function apus_related_posts_default_options() {
|
||||
// Set default options if they don't exist
|
||||
$defaults = array(
|
||||
'apus_related_posts_enabled' => true,
|
||||
'apus_related_posts_title' => __('Related Posts', 'apus-theme'),
|
||||
'apus_related_posts_count' => 3,
|
||||
'apus_related_posts_columns' => 3,
|
||||
'apus_related_posts_show_excerpt' => true,
|
||||
'apus_related_posts_excerpt_length' => 20,
|
||||
'apus_related_posts_show_date' => true,
|
||||
'apus_related_posts_show_category' => true,
|
||||
'apus_related_posts_bg_colors' => array(
|
||||
'#1a73e8', // Blue
|
||||
'#e91e63', // Pink
|
||||
'#4caf50', // Green
|
||||
'#ff9800', // Orange
|
||||
'#9c27b0', // Purple
|
||||
'#00bcd4', // Cyan
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($defaults as $option => $value) {
|
||||
if (get_option($option) === false) {
|
||||
add_option($option, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
add_action('after_setup_theme', 'apus_related_posts_default_options');
|
||||
458
wp-content/themes/apus-theme/inc/template-functions.php
Normal file
458
wp-content/themes/apus-theme/inc/template-functions.php
Normal file
@@ -0,0 +1,458 @@
|
||||
<?php
|
||||
/**
|
||||
* Template Functions
|
||||
*
|
||||
* Helper functions for theme templates.
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints HTML with meta information for the current post-date/time and author.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_posted_on() {
|
||||
$time_string = '<time class="entry-date published updated" datetime="%1$s">%2$s</time>';
|
||||
|
||||
if ( get_the_time( 'U' ) !== get_the_modified_time( 'U' ) ) {
|
||||
$time_string = '<time class="entry-date published" datetime="%1$s">%2$s</time><time class="updated" datetime="%3$s">%4$s</time>';
|
||||
}
|
||||
|
||||
$time_string = sprintf(
|
||||
$time_string,
|
||||
esc_attr( get_the_date( DATE_W3C ) ),
|
||||
esc_html( get_the_date() ),
|
||||
esc_attr( get_the_modified_date( DATE_W3C ) ),
|
||||
esc_html( get_the_modified_date() )
|
||||
);
|
||||
|
||||
$posted_on = sprintf(
|
||||
/* translators: %s: post date. */
|
||||
esc_html_x( 'Publicado el %s', 'post date', 'apus' ),
|
||||
'<a href="' . esc_url( get_permalink() ) . '" rel="bookmark">' . $time_string . '</a>'
|
||||
);
|
||||
|
||||
echo '<span class="posted-on">' . $posted_on . '</span>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints HTML with meta information for the current author.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_posted_by() {
|
||||
$byline = sprintf(
|
||||
/* translators: %s: post author. */
|
||||
esc_html_x( 'por %s', 'post author', 'apus' ),
|
||||
'<span class="author vcard"><a class="url fn n" href="' . esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ) . '">' . esc_html( get_the_author() ) . '</a></span>'
|
||||
);
|
||||
|
||||
echo '<span class="byline"> ' . $byline . '</span>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints HTML with meta information for the categories, tags, and comments.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
function apus_entry_footer() {
|
||||
// Hide category and tag text for pages.
|
||||
if ( 'post' === get_post_type() ) {
|
||||
/* translators: used between list items, there is a space after the comma */
|
||||
$categories_list = get_the_category_list( esc_html__( ', ', 'apus' ) );
|
||||
if ( $categories_list ) {
|
||||
/* translators: 1: list of categories. */
|
||||
printf( '<span class="cat-links">' . esc_html__( 'Categorías: %1$s', 'apus' ) . '</span>', $categories_list ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/* translators: used between list items, there is a space after the comma */
|
||||
$tags_list = get_the_tag_list( '', esc_html_x( ', ', 'list item separator', 'apus' ) );
|
||||
if ( $tags_list ) {
|
||||
/* translators: 1: list of tags. */
|
||||
printf( '<span class="tags-links">' . esc_html__( 'Etiquetas: %1$s', 'apus' ) . '</span>', $tags_list ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! is_single() && ! post_password_required() && ( comments_open() || get_comments_number() ) ) {
|
||||
echo '<span class="comments-link">';
|
||||
comments_popup_link(
|
||||
sprintf(
|
||||
wp_kses(
|
||||
/* translators: %s: post title */
|
||||
__( 'Comentar<span class="screen-reader-text"> en %s</span>', 'apus' ),
|
||||
array(
|
||||
'span' => array(
|
||||
'class' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
wp_kses_post( get_the_title() )
|
||||
)
|
||||
);
|
||||
echo '</span>';
|
||||
}
|
||||
|
||||
edit_post_link(
|
||||
sprintf(
|
||||
wp_kses(
|
||||
/* translators: %s: Name of current post. Only visible to screen readers */
|
||||
__( 'Editar <span class="screen-reader-text">%s</span>', 'apus' ),
|
||||
array(
|
||||
'span' => array(
|
||||
'class' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
wp_kses_post( get_the_title() )
|
||||
),
|
||||
'<span class="edit-link">',
|
||||
'</span>'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom excerpt length
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param int $length Default excerpt length.
|
||||
* @return int Modified excerpt length.
|
||||
*/
|
||||
function apus_excerpt_length( $length ) {
|
||||
if ( is_admin() ) {
|
||||
return $length;
|
||||
}
|
||||
|
||||
return 40; // Change this to desired excerpt length.
|
||||
}
|
||||
add_filter( 'excerpt_length', 'apus_excerpt_length', 999 );
|
||||
|
||||
/**
|
||||
* Custom excerpt more string
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $more Default more string.
|
||||
* @return string Modified more string.
|
||||
*/
|
||||
function apus_excerpt_more( $more ) {
|
||||
if ( is_admin() ) {
|
||||
return $more;
|
||||
}
|
||||
|
||||
return '…';
|
||||
}
|
||||
add_filter( 'excerpt_more', 'apus_excerpt_more' );
|
||||
|
||||
/**
|
||||
* Get custom excerpt by character count
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param int $charlength Character length.
|
||||
* @param string $more More string.
|
||||
* @return string Custom excerpt.
|
||||
*/
|
||||
function apus_get_excerpt( $charlength = 200, $more = '...' ) {
|
||||
$excerpt = get_the_excerpt();
|
||||
$charlength++;
|
||||
|
||||
if ( mb_strlen( $excerpt ) > $charlength ) {
|
||||
$subex = mb_substr( $excerpt, 0, $charlength - 5 );
|
||||
$exwords = explode( ' ', $subex );
|
||||
$excut = - ( mb_strlen( $exwords[ count( $exwords ) - 1 ] ) );
|
||||
|
||||
if ( $excut < 0 ) {
|
||||
$excerpt = mb_substr( $subex, 0, $excut );
|
||||
} else {
|
||||
$excerpt = $subex;
|
||||
}
|
||||
|
||||
$excerpt .= $more;
|
||||
}
|
||||
|
||||
return $excerpt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post thumbnail URL
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param string $size Image size.
|
||||
* @return string|false Thumbnail URL or false if not found.
|
||||
*/
|
||||
function apus_get_post_thumbnail_url( $size = 'full' ) {
|
||||
if ( has_post_thumbnail() ) {
|
||||
$thumb_id = get_post_thumbnail_id();
|
||||
$thumb = wp_get_attachment_image_src( $thumb_id, $size );
|
||||
|
||||
if ( $thumb ) {
|
||||
return $thumb[0];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display breadcrumbs
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args Breadcrumb arguments.
|
||||
*/
|
||||
function apus_breadcrumbs( $args = array() ) {
|
||||
// Default arguments.
|
||||
$defaults = array(
|
||||
'separator' => '<span class="separator">/</span>',
|
||||
'home_label' => esc_html__( 'Inicio', 'apus' ),
|
||||
'show_home' => true,
|
||||
'show_current' => true,
|
||||
'before' => '<nav class="breadcrumbs" aria-label="' . esc_attr__( 'Breadcrumb', 'apus' ) . '"><ol class="breadcrumb-list">',
|
||||
'after' => '</ol></nav>',
|
||||
'link_before' => '<li class="breadcrumb-item">',
|
||||
'link_after' => '</li>',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
// Don't display on homepage.
|
||||
if ( is_front_page() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $post;
|
||||
|
||||
echo $args['before']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
// Home link.
|
||||
if ( $args['show_home'] ) {
|
||||
echo $args['link_before'] . '<a href="' . esc_url( home_url( '/' ) ) . '">' . esc_html( $args['home_label'] ) . '</a>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['separator']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
if ( is_category() ) {
|
||||
$category = get_queried_object();
|
||||
if ( $category->parent ) {
|
||||
$parent_cats = get_category_parents( $category->parent, true, $args['separator'] );
|
||||
echo $args['link_before'] . $parent_cats . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html( single_cat_title( '', false ) ) . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
} elseif ( is_single() && ! is_attachment() ) {
|
||||
if ( get_post_type() !== 'post' ) {
|
||||
$post_type = get_post_type_object( get_post_type() );
|
||||
$post_type_archive = get_post_type_archive_link( get_post_type() );
|
||||
|
||||
if ( $post_type_archive ) {
|
||||
echo $args['link_before'] . '<a href="' . esc_url( $post_type_archive ) . '">' . esc_html( $post_type->labels->name ) . '</a>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['separator']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
if ( $args['show_current'] ) {
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html( get_the_title() ) . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
} else {
|
||||
$category = get_the_category();
|
||||
if ( $category ) {
|
||||
$category_values = array_values( $category );
|
||||
$category_last = end( $category_values );
|
||||
$category_parents = get_category_parents( $category_last->term_id, true, $args['separator'] );
|
||||
echo $args['link_before'] . $category_parents . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
if ( $args['show_current'] ) {
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html( get_the_title() ) . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
}
|
||||
} elseif ( is_page() && ! $post->post_parent ) {
|
||||
if ( $args['show_current'] ) {
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html( get_the_title() ) . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
} elseif ( is_page() && $post->post_parent ) {
|
||||
$parent_id = $post->post_parent;
|
||||
$breadcrumbs = array();
|
||||
|
||||
while ( $parent_id ) {
|
||||
$page = get_post( $parent_id );
|
||||
$breadcrumbs[] = '<a href="' . esc_url( get_permalink( $page->ID ) ) . '">' . esc_html( get_the_title( $page->ID ) ) . '</a>';
|
||||
$parent_id = $page->post_parent;
|
||||
}
|
||||
|
||||
$breadcrumbs = array_reverse( $breadcrumbs );
|
||||
foreach ( $breadcrumbs as $crumb ) {
|
||||
echo $args['link_before'] . $crumb . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['separator']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
if ( $args['show_current'] ) {
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html( get_the_title() ) . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
} elseif ( is_search() ) {
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html__( 'Resultados de búsqueda para: ', 'apus' ) . get_search_query() . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
} elseif ( is_tag() ) {
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html__( 'Etiqueta: ', 'apus' ) . esc_html( single_tag_title( '', false ) ) . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
} elseif ( is_author() ) {
|
||||
$author = get_queried_object();
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html__( 'Autor: ', 'apus' ) . esc_html( $author->display_name ) . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
} elseif ( is_day() ) {
|
||||
echo $args['link_before'] . '<a href="' . esc_url( get_year_link( get_the_time( 'Y' ) ) ) . '">' . esc_html( get_the_time( 'Y' ) ) . '</a>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['separator']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['link_before'] . '<a href="' . esc_url( get_month_link( get_the_time( 'Y' ), get_the_time( 'm' ) ) ) . '">' . esc_html( get_the_time( 'F' ) ) . '</a>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['separator']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html( get_the_time( 'd' ) ) . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
} elseif ( is_month() ) {
|
||||
echo $args['link_before'] . '<a href="' . esc_url( get_year_link( get_the_time( 'Y' ) ) ) . '">' . esc_html( get_the_time( 'Y' ) ) . '</a>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['separator']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html( get_the_time( 'F' ) ) . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
} elseif ( is_year() ) {
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html( get_the_time( 'Y' ) ) . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
|
||||
} elseif ( is_404() ) {
|
||||
echo $args['link_before'] . '<span class="current">' . esc_html__( 'Error 404', 'apus' ) . '</span>' . $args['link_after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
echo $args['after']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom body classes
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $classes Existing body classes.
|
||||
* @return array Modified body classes.
|
||||
*/
|
||||
function apus_body_classes( $classes ) {
|
||||
// Add class if sidebar is active.
|
||||
if ( is_active_sidebar( 'sidebar-1' ) ) {
|
||||
$classes[] = 'has-sidebar';
|
||||
} else {
|
||||
$classes[] = 'no-sidebar';
|
||||
}
|
||||
|
||||
// Add class if it's a singular post/page.
|
||||
if ( is_singular() ) {
|
||||
$classes[] = 'singular';
|
||||
}
|
||||
|
||||
// Add class for specific post formats.
|
||||
if ( is_singular() && has_post_format() ) {
|
||||
$post_format = get_post_format();
|
||||
$classes[] = 'post-format-' . $post_format;
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
add_filter( 'body_class', 'apus_body_classes' );
|
||||
|
||||
/**
|
||||
* Add custom post classes
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $classes Existing post classes.
|
||||
* @return array Modified post classes.
|
||||
*/
|
||||
function apus_post_classes( $classes ) {
|
||||
// Add class if post has thumbnail.
|
||||
if ( has_post_thumbnail() ) {
|
||||
$classes[] = 'has-post-thumbnail';
|
||||
} else {
|
||||
$classes[] = 'no-post-thumbnail';
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
add_filter( 'post_class', 'apus_post_classes' );
|
||||
|
||||
/**
|
||||
* Sanitize SVG uploads
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $mimes Allowed mime types.
|
||||
* @return array Modified mime types.
|
||||
*/
|
||||
function apus_add_svg_mime_types( $mimes ) {
|
||||
$mimes['svg'] = 'image/svg+xml';
|
||||
$mimes['svgz'] = 'image/svg+xml';
|
||||
|
||||
return $mimes;
|
||||
}
|
||||
add_filter( 'upload_mimes', 'apus_add_svg_mime_types' );
|
||||
|
||||
/**
|
||||
* Pagination for archive pages
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args Pagination arguments.
|
||||
*/
|
||||
function apus_pagination( $args = array() ) {
|
||||
global $wp_query;
|
||||
|
||||
// Don't print empty markup if there's only one page.
|
||||
if ( $wp_query->max_num_pages < 2 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'mid_size' => 2,
|
||||
'prev_text' => esc_html__( '← Anterior', 'apus' ),
|
||||
'next_text' => esc_html__( 'Siguiente →', 'apus' ),
|
||||
'screen_reader_text' => esc_html__( 'Navegación de entradas', 'apus' ),
|
||||
'type' => 'list',
|
||||
'current' => max( 1, get_query_var( 'paged' ) ),
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
// Make sure we get a string back. Plain is the next best thing.
|
||||
if ( isset( $args['type'] ) && 'array' === $args['type'] ) {
|
||||
$args['type'] = 'plain';
|
||||
}
|
||||
|
||||
echo '<nav class="pagination" aria-label="' . esc_attr( $args['screen_reader_text'] ) . '">';
|
||||
echo wp_kses_post( paginate_links( $args ) );
|
||||
echo '</nav>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get footer column Bootstrap class
|
||||
*
|
||||
* Returns the appropriate Bootstrap grid classes for footer columns
|
||||
* based on the column number. Supports configurable widths and
|
||||
* responsive breakpoints.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param int $column Column number (1-4).
|
||||
* @return string Bootstrap column classes.
|
||||
*/
|
||||
function apus_get_footer_column_class( $column = 1 ) {
|
||||
// Default configuration: Equal width columns (3 columns each on desktop).
|
||||
// You can customize these classes per column as needed.
|
||||
$column_classes = array(
|
||||
1 => 'col-12 col-md-6 col-lg-3', // Column 1: Full width mobile, half tablet, quarter desktop.
|
||||
2 => 'col-12 col-md-6 col-lg-3', // Column 2: Full width mobile, half tablet, quarter desktop.
|
||||
3 => 'col-12 col-md-6 col-lg-3', // Column 3: Full width mobile, half tablet, quarter desktop.
|
||||
4 => 'col-12 col-md-6 col-lg-3', // Column 4: Full width mobile, half tablet, quarter desktop.
|
||||
);
|
||||
|
||||
/**
|
||||
* Filter footer column classes
|
||||
*
|
||||
* Allows customization of footer column widths via filter.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $column_classes Array of column classes.
|
||||
*/
|
||||
$column_classes = apply_filters( 'apus_footer_column_classes', $column_classes );
|
||||
|
||||
// Return the class for the specified column, or default to col-12 if not found.
|
||||
return isset( $column_classes[ $column ] ) ? $column_classes[ $column ] : 'col-12';
|
||||
}
|
||||
525
wp-content/themes/apus-theme/inc/template-tags.php
Normal file
525
wp-content/themes/apus-theme/inc/template-tags.php
Normal file
@@ -0,0 +1,525 @@
|
||||
<?php
|
||||
/**
|
||||
* Template Tags
|
||||
*
|
||||
* Reusable template tags for theme templates.
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the site logo
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args Logo arguments.
|
||||
*/
|
||||
function apus_site_logo( $args = array() ) {
|
||||
$defaults = array(
|
||||
'class' => 'site-logo',
|
||||
'width' => 200,
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
if ( has_custom_logo() ) {
|
||||
$custom_logo_id = get_theme_mod( 'custom_logo' );
|
||||
$logo = wp_get_attachment_image_src( $custom_logo_id, 'full' );
|
||||
|
||||
if ( $logo ) {
|
||||
printf(
|
||||
'<a href="%1$s" class="%2$s" rel="home">
|
||||
<img src="%3$s" alt="%4$s" width="%5$d">
|
||||
</a>',
|
||||
esc_url( home_url( '/' ) ),
|
||||
esc_attr( $args['class'] ),
|
||||
esc_url( $logo[0] ),
|
||||
esc_attr( get_bloginfo( 'name' ) ),
|
||||
absint( $args['width'] )
|
||||
);
|
||||
}
|
||||
} else {
|
||||
printf(
|
||||
'<a href="%1$s" class="%2$s site-title" rel="home">%3$s</a>',
|
||||
esc_url( home_url( '/' ) ),
|
||||
esc_attr( $args['class'] ),
|
||||
esc_html( get_bloginfo( 'name' ) )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the site description
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args Description arguments.
|
||||
*/
|
||||
function apus_site_description( $args = array() ) {
|
||||
$description = get_bloginfo( 'description', 'display' );
|
||||
|
||||
if ( ! $description ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'class' => 'site-description',
|
||||
'tag' => 'p',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
printf(
|
||||
'<%1$s class="%2$s">%3$s</%1$s>',
|
||||
esc_attr( $args['tag'] ),
|
||||
esc_attr( $args['class'] ),
|
||||
esc_html( $description )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display post meta information
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args Meta arguments.
|
||||
*/
|
||||
function apus_post_meta( $args = array() ) {
|
||||
$defaults = array(
|
||||
'show_date' => true,
|
||||
'show_author' => true,
|
||||
'show_comments' => true,
|
||||
'show_category' => true,
|
||||
'class' => 'entry-meta',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
echo '<div class="' . esc_attr( $args['class'] ) . '">';
|
||||
|
||||
if ( $args['show_date'] ) {
|
||||
apus_posted_on();
|
||||
}
|
||||
|
||||
if ( $args['show_author'] ) {
|
||||
apus_posted_by();
|
||||
}
|
||||
|
||||
if ( $args['show_category'] && 'post' === get_post_type() ) {
|
||||
$categories_list = get_the_category_list( esc_html__( ', ', 'apus' ) );
|
||||
if ( $categories_list ) {
|
||||
printf(
|
||||
'<span class="cat-links">%s</span>',
|
||||
$categories_list // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( $args['show_comments'] && ! post_password_required() && ( comments_open() || get_comments_number() ) ) {
|
||||
echo '<span class="comments-link">';
|
||||
comments_popup_link(
|
||||
sprintf(
|
||||
wp_kses(
|
||||
/* translators: %s: post title */
|
||||
__( '0 comentarios<span class="screen-reader-text"> en %s</span>', 'apus' ),
|
||||
array(
|
||||
'span' => array(
|
||||
'class' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
wp_kses_post( get_the_title() )
|
||||
),
|
||||
sprintf(
|
||||
wp_kses(
|
||||
/* translators: %s: post title */
|
||||
__( '1 comentario<span class="screen-reader-text"> en %s</span>', 'apus' ),
|
||||
array(
|
||||
'span' => array(
|
||||
'class' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
wp_kses_post( get_the_title() )
|
||||
),
|
||||
sprintf(
|
||||
wp_kses(
|
||||
/* translators: %s: post title */
|
||||
__( '% comentarios<span class="screen-reader-text"> en %s</span>', 'apus' ),
|
||||
array(
|
||||
'span' => array(
|
||||
'class' => array(),
|
||||
),
|
||||
)
|
||||
),
|
||||
wp_kses_post( get_the_title() )
|
||||
)
|
||||
);
|
||||
echo '</span>';
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Display post thumbnail
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args Thumbnail arguments.
|
||||
*/
|
||||
function apus_post_thumbnail( $args = array() ) {
|
||||
if ( ! has_post_thumbnail() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'size' => 'large',
|
||||
'class' => 'post-thumbnail',
|
||||
'link' => true,
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
if ( $args['link'] && ! is_single() ) {
|
||||
?>
|
||||
<a class="<?php echo esc_attr( $args['class'] ); ?>" href="<?php the_permalink(); ?>" aria-hidden="true" tabindex="-1">
|
||||
<?php the_post_thumbnail( $args['size'] ); ?>
|
||||
</a>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<div class="<?php echo esc_attr( $args['class'] ); ?>">
|
||||
<?php the_post_thumbnail( $args['size'] ); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display read more link
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args Read more arguments.
|
||||
*/
|
||||
function apus_read_more( $args = array() ) {
|
||||
$defaults = array(
|
||||
'text' => esc_html__( 'Leer más', 'apus' ),
|
||||
'class' => 'read-more',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
printf(
|
||||
'<a href="%1$s" class="%2$s">%3$s</a>',
|
||||
esc_url( get_permalink() ),
|
||||
esc_attr( $args['class'] ),
|
||||
esc_html( $args['text'] )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display social sharing buttons
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args Share arguments.
|
||||
*/
|
||||
function apus_social_share( $args = array() ) {
|
||||
$defaults = array(
|
||||
'title' => esc_html__( 'Compartir:', 'apus' ),
|
||||
'networks' => array( 'facebook', 'twitter', 'linkedin', 'whatsapp' ),
|
||||
'class' => 'social-share',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
$post_title = get_the_title();
|
||||
$post_url = get_permalink();
|
||||
|
||||
echo '<div class="' . esc_attr( $args['class'] ) . '">';
|
||||
|
||||
if ( $args['title'] ) {
|
||||
echo '<span class="share-title">' . esc_html( $args['title'] ) . '</span>';
|
||||
}
|
||||
|
||||
echo '<ul class="share-buttons">';
|
||||
|
||||
foreach ( $args['networks'] as $network ) {
|
||||
$share_url = '';
|
||||
$icon_class = 'share-' . $network;
|
||||
|
||||
switch ( $network ) {
|
||||
case 'facebook':
|
||||
$share_url = 'https://www.facebook.com/sharer/sharer.php?u=' . rawurlencode( $post_url );
|
||||
break;
|
||||
|
||||
case 'twitter':
|
||||
$share_url = 'https://twitter.com/intent/tweet?text=' . rawurlencode( $post_title ) . '&url=' . rawurlencode( $post_url );
|
||||
break;
|
||||
|
||||
case 'linkedin':
|
||||
$share_url = 'https://www.linkedin.com/shareArticle?mini=true&url=' . rawurlencode( $post_url ) . '&title=' . rawurlencode( $post_title );
|
||||
break;
|
||||
|
||||
case 'whatsapp':
|
||||
$share_url = 'https://api.whatsapp.com/send?text=' . rawurlencode( $post_title . ' ' . $post_url );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( $share_url ) {
|
||||
printf(
|
||||
'<li class="share-item">
|
||||
<a href="%1$s" class="%2$s" target="_blank" rel="noopener noreferrer" aria-label="%3$s">
|
||||
<span class="screen-reader-text">%4$s</span>
|
||||
</a>
|
||||
</li>',
|
||||
esc_url( $share_url ),
|
||||
esc_attr( $icon_class ),
|
||||
/* translators: %s: social network name */
|
||||
esc_attr( sprintf( __( 'Compartir en %s', 'apus' ), ucfirst( $network ) ) ),
|
||||
esc_html( ucfirst( $network ) )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
echo '</ul>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Display author bio box
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args Author bio arguments.
|
||||
*/
|
||||
function apus_author_bio( $args = array() ) {
|
||||
// Only show on single posts.
|
||||
if ( ! is_single() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'class' => 'author-bio',
|
||||
'show_avatar' => true,
|
||||
'avatar_size' => 80,
|
||||
'show_archive' => true,
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
$author_id = get_the_author_meta( 'ID' );
|
||||
$author_name = get_the_author();
|
||||
$author_description = get_the_author_meta( 'description' );
|
||||
|
||||
if ( empty( $author_description ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<div class="' . esc_attr( $args['class'] ) . '">';
|
||||
|
||||
if ( $args['show_avatar'] ) {
|
||||
echo '<div class="author-avatar">';
|
||||
echo get_avatar( $author_id, $args['avatar_size'] );
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
echo '<div class="author-info">';
|
||||
echo '<h3 class="author-name">' . esc_html( $author_name ) . '</h3>';
|
||||
echo '<p class="author-description">' . wp_kses_post( $author_description ) . '</p>';
|
||||
|
||||
if ( $args['show_archive'] ) {
|
||||
printf(
|
||||
'<a href="%1$s" class="author-link">%2$s</a>',
|
||||
esc_url( get_author_posts_url( $author_id ) ),
|
||||
/* translators: %s: author name */
|
||||
esc_html( sprintf( __( 'Ver todos los artículos de %s', 'apus' ), $author_name ) )
|
||||
);
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Display related posts
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args Related posts arguments.
|
||||
*/
|
||||
function apus_related_posts( $args = array() ) {
|
||||
// Only show on single posts.
|
||||
if ( ! is_single() || 'post' !== get_post_type() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'title' => esc_html__( 'Artículos relacionados', 'apus' ),
|
||||
'posts_per_page' => 3,
|
||||
'order' => 'DESC',
|
||||
'orderby' => 'date',
|
||||
'class' => 'related-posts',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
// Get current post categories.
|
||||
$categories = get_the_category();
|
||||
|
||||
if ( empty( $categories ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$category_ids = array();
|
||||
foreach ( $categories as $category ) {
|
||||
$category_ids[] = $category->term_id;
|
||||
}
|
||||
|
||||
// Query related posts.
|
||||
$related_query = new WP_Query(
|
||||
array(
|
||||
'category__in' => $category_ids,
|
||||
'post__not_in' => array( get_the_ID() ),
|
||||
'posts_per_page' => $args['posts_per_page'],
|
||||
'order' => $args['order'],
|
||||
'orderby' => $args['orderby'],
|
||||
'ignore_sticky_posts' => 1,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! $related_query->have_posts() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<aside class="' . esc_attr( $args['class'] ) . '">';
|
||||
|
||||
if ( $args['title'] ) {
|
||||
echo '<h3 class="related-posts-title">' . esc_html( $args['title'] ) . '</h3>';
|
||||
}
|
||||
|
||||
echo '<div class="related-posts-grid">';
|
||||
|
||||
while ( $related_query->have_posts() ) {
|
||||
$related_query->the_post();
|
||||
?>
|
||||
<article class="related-post">
|
||||
<?php if ( has_post_thumbnail() ) : ?>
|
||||
<a href="<?php the_permalink(); ?>" class="related-post-thumbnail">
|
||||
<?php the_post_thumbnail( 'medium' ); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<h4 class="related-post-title">
|
||||
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
|
||||
</h4>
|
||||
|
||||
<div class="related-post-meta">
|
||||
<time datetime="<?php echo esc_attr( get_the_date( DATE_W3C ) ); ?>">
|
||||
<?php echo esc_html( get_the_date() ); ?>
|
||||
</time>
|
||||
</div>
|
||||
</article>
|
||||
<?php
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
echo '</aside>';
|
||||
|
||||
wp_reset_postdata();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display table of contents for long posts
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args TOC arguments.
|
||||
*/
|
||||
function apus_table_of_contents( $args = array() ) {
|
||||
global $post;
|
||||
|
||||
if ( ! is_single() || ! isset( $post->post_content ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'title' => esc_html__( 'Tabla de contenidos', 'apus' ),
|
||||
'class' => 'table-of-contents',
|
||||
'min_headings' => 3,
|
||||
'heading_levels' => array( 'h2', 'h3' ),
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
// Extract headings from content.
|
||||
$content = $post->post_content;
|
||||
preg_match_all( '/<(' . implode( '|', $args['heading_levels'] ) . ')[^>]*>(.*?)<\/\1>/i', $content, $matches, PREG_SET_ORDER );
|
||||
|
||||
if ( count( $matches ) < $args['min_headings'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
echo '<nav class="' . esc_attr( $args['class'] ) . '">';
|
||||
|
||||
if ( $args['title'] ) {
|
||||
echo '<h2 class="toc-title">' . esc_html( $args['title'] ) . '</h2>';
|
||||
}
|
||||
|
||||
echo '<ol class="toc-list">';
|
||||
|
||||
foreach ( $matches as $index => $heading ) {
|
||||
$heading_text = wp_strip_all_tags( $heading[2] );
|
||||
$heading_id = sanitize_title( $heading_text ) . '-' . $index;
|
||||
|
||||
printf(
|
||||
'<li class="toc-item toc-%1$s"><a href="#%2$s">%3$s</a></li>',
|
||||
esc_attr( $heading[1] ),
|
||||
esc_attr( $heading_id ),
|
||||
esc_html( $heading_text )
|
||||
);
|
||||
}
|
||||
|
||||
echo '</ol>';
|
||||
echo '</nav>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Display cookie consent notice
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @param array $args Cookie notice arguments.
|
||||
*/
|
||||
function apus_cookie_notice( $args = array() ) {
|
||||
// Check if cookie consent has been given.
|
||||
if ( isset( $_COOKIE['apus_cookie_consent'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'message' => esc_html__( 'Este sitio utiliza cookies para mejorar su experiencia. Al continuar navegando, acepta nuestro uso de cookies.', 'apus' ),
|
||||
'accept_text' => esc_html__( 'Aceptar', 'apus' ),
|
||||
'learn_more' => esc_html__( 'Más información', 'apus' ),
|
||||
'policy_url' => get_privacy_policy_url(),
|
||||
'class' => 'cookie-notice',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
?>
|
||||
<div class="<?php echo esc_attr( $args['class'] ); ?>" role="alert" aria-live="polite">
|
||||
<div class="cookie-notice-content">
|
||||
<p><?php echo esc_html( $args['message'] ); ?></p>
|
||||
<div class="cookie-notice-actions">
|
||||
<button type="button" class="cookie-notice-accept" id="cookie-notice-accept">
|
||||
<?php echo esc_html( $args['accept_text'] ); ?>
|
||||
</button>
|
||||
<?php if ( $args['policy_url'] ) : ?>
|
||||
<a href="<?php echo esc_url( $args['policy_url'] ); ?>" class="cookie-notice-learn-more">
|
||||
<?php echo esc_html( $args['learn_more'] ); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
318
wp-content/themes/apus-theme/inc/theme-options-helpers.php
Normal file
318
wp-content/themes/apus-theme/inc/theme-options-helpers.php
Normal file
@@ -0,0 +1,318 @@
|
||||
<?php
|
||||
/**
|
||||
* Theme Options Helper Functions
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get theme option value
|
||||
*
|
||||
* @param string $option_name The option name
|
||||
* @param mixed $default Default value if option doesn't exist
|
||||
* @return mixed The option value
|
||||
*/
|
||||
function apus_get_option($option_name, $default = '') {
|
||||
$options = get_option('apus_theme_options', array());
|
||||
|
||||
if (isset($options[$option_name])) {
|
||||
return $options[$option_name];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if option is enabled (checkbox/switch)
|
||||
*
|
||||
* @param string $option_name The option name
|
||||
* @return bool True if enabled, false otherwise
|
||||
*/
|
||||
function apus_is_option_enabled($option_name) {
|
||||
return (bool) apus_get_option($option_name, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get breadcrumbs separator
|
||||
*
|
||||
* @return string The separator
|
||||
*/
|
||||
function apus_get_breadcrumb_separator() {
|
||||
return apus_get_option('breadcrumb_separator', '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if breadcrumbs should be shown
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function apus_show_breadcrumbs() {
|
||||
return apus_is_option_enabled('enable_breadcrumbs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get excerpt length
|
||||
*
|
||||
* @return int The excerpt length
|
||||
*/
|
||||
function apus_get_excerpt_length() {
|
||||
return (int) apus_get_option('excerpt_length', 55);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get excerpt more text
|
||||
*
|
||||
* @return string The excerpt more text
|
||||
*/
|
||||
function apus_get_excerpt_more() {
|
||||
return apus_get_option('excerpt_more', '...');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if related posts should be shown
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function apus_show_related_posts() {
|
||||
return apus_is_option_enabled('enable_related_posts');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of related posts to show
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function apus_get_related_posts_count() {
|
||||
return (int) apus_get_option('related_posts_count', 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get related posts taxonomy
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_related_posts_taxonomy() {
|
||||
return apus_get_option('related_posts_taxonomy', 'category');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get related posts title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_related_posts_title() {
|
||||
return apus_get_option('related_posts_title', __('Related Posts', 'apus-theme'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specific performance optimization is enabled
|
||||
*
|
||||
* @param string $optimization The optimization name
|
||||
* @return bool
|
||||
*/
|
||||
function apus_is_performance_enabled($optimization) {
|
||||
return apus_is_option_enabled('performance_' . $optimization);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get copyright text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_copyright_text() {
|
||||
$default = sprintf(
|
||||
__('© %s %s. All rights reserved.', 'apus-theme'),
|
||||
date('Y'),
|
||||
get_bloginfo('name')
|
||||
);
|
||||
return apus_get_option('copyright_text', $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get social media links
|
||||
*
|
||||
* @return array Array of social media links
|
||||
*/
|
||||
function apus_get_social_links() {
|
||||
return array(
|
||||
'facebook' => apus_get_option('social_facebook', ''),
|
||||
'twitter' => apus_get_option('social_twitter', ''),
|
||||
'instagram' => apus_get_option('social_instagram', ''),
|
||||
'linkedin' => apus_get_option('social_linkedin', ''),
|
||||
'youtube' => apus_get_option('social_youtube', ''),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if comments are enabled for posts
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function apus_comments_enabled_for_posts() {
|
||||
return apus_is_option_enabled('enable_comments_posts');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if comments are enabled for pages
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function apus_comments_enabled_for_pages() {
|
||||
return apus_is_option_enabled('enable_comments_pages');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default post layout
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_default_post_layout() {
|
||||
return apus_get_option('default_post_layout', 'right-sidebar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default page layout
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_default_page_layout() {
|
||||
return apus_get_option('default_page_layout', 'right-sidebar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get posts per page for archive
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function apus_get_archive_posts_per_page() {
|
||||
$custom = (int) apus_get_option('archive_posts_per_page', 0);
|
||||
return $custom > 0 ? $custom : get_option('posts_per_page', 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if featured image should be shown on single posts
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function apus_show_featured_image_single() {
|
||||
return apus_is_option_enabled('show_featured_image_single');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if author box should be shown on single posts
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function apus_show_author_box() {
|
||||
return apus_is_option_enabled('show_author_box');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_date_format() {
|
||||
return apus_get_option('date_format', 'd/m/Y');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_time_format() {
|
||||
return apus_get_option('time_format', 'H:i');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logo URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_logo_url() {
|
||||
$logo_id = apus_get_option('site_logo', 0);
|
||||
if ($logo_id) {
|
||||
$logo = wp_get_attachment_image_url($logo_id, 'full');
|
||||
if ($logo) {
|
||||
return $logo;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get favicon URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_favicon_url() {
|
||||
$favicon_id = apus_get_option('site_favicon', 0);
|
||||
if ($favicon_id) {
|
||||
$favicon = wp_get_attachment_image_url($favicon_id, 'full');
|
||||
if ($favicon) {
|
||||
return $favicon;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom CSS
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_custom_css() {
|
||||
return apus_get_option('custom_css', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom JS (header)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_custom_js_header() {
|
||||
return apus_get_option('custom_js_header', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom JS (footer)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function apus_get_custom_js_footer() {
|
||||
return apus_get_option('custom_js_footer', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if lazy loading is enabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function apus_is_lazy_loading_enabled() {
|
||||
return apus_is_option_enabled('enable_lazy_loading');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all theme options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function apus_get_all_options() {
|
||||
return get_option('apus_theme_options', array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset theme options to defaults
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function apus_reset_options() {
|
||||
return delete_option('apus_theme_options');
|
||||
}
|
||||
225
wp-content/themes/apus-theme/inc/toc.php
Normal file
225
wp-content/themes/apus-theme/inc/toc.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
/**
|
||||
* Table of Contents (TOC) Functions
|
||||
*
|
||||
* This file contains functions to automatically generate a table of contents
|
||||
* from post content headings (H2 and H3).
|
||||
*
|
||||
* @package Apus_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract headings from post content
|
||||
*
|
||||
* Parses the content and extracts all H2 and H3 headings with their text.
|
||||
*
|
||||
* @param string $content Post content
|
||||
* @return array Array of headings with level, text, and ID
|
||||
*/
|
||||
function apus_extract_headings($content) {
|
||||
if (empty($content)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$headings = array();
|
||||
|
||||
// Match H2 and H3 tags with their content
|
||||
preg_match_all('/<h([23])(?:[^>]*)>(.*?)<\/h\1>/i', $content, $matches, PREG_SET_ORDER);
|
||||
|
||||
foreach ($matches as $index => $match) {
|
||||
$level = (int) $match[1]; // 2 or 3
|
||||
$text = strip_tags($match[2]); // Remove any HTML tags inside heading
|
||||
|
||||
// Generate a clean ID from the heading text
|
||||
$id = apus_generate_heading_id($text, $index);
|
||||
|
||||
$headings[] = array(
|
||||
'level' => $level,
|
||||
'text' => $text,
|
||||
'id' => $id,
|
||||
);
|
||||
}
|
||||
|
||||
return $headings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a clean ID for a heading
|
||||
*
|
||||
* Creates a URL-friendly ID from heading text.
|
||||
*
|
||||
* @param string $text Heading text
|
||||
* @param int $index Index of the heading (for uniqueness)
|
||||
* @return string Clean ID
|
||||
*/
|
||||
function apus_generate_heading_id($text, $index) {
|
||||
// Remove special characters and convert to lowercase
|
||||
$id = sanitize_title($text);
|
||||
|
||||
// If ID is empty, use a fallback
|
||||
if (empty($id)) {
|
||||
$id = 'heading-' . $index;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HTML for Table of Contents
|
||||
*
|
||||
* Creates a nested list structure from the headings array.
|
||||
*
|
||||
* @param array $headings Array of headings from apus_extract_headings()
|
||||
* @return string HTML for the table of contents
|
||||
*/
|
||||
function apus_generate_toc($headings) {
|
||||
if (empty($headings) || count($headings) < 2) {
|
||||
return ''; // Don't show TOC if there are fewer than 2 headings
|
||||
}
|
||||
|
||||
$toc_html = '<nav class="apus-toc" aria-label="' . esc_attr__('Table of Contents', 'apus-theme') . '">';
|
||||
$toc_html .= '<div class="apus-toc-header">';
|
||||
$toc_html .= '<h2 class="apus-toc-title">' . esc_html__('Table of Contents', 'apus-theme') . '</h2>';
|
||||
$toc_html .= '<button class="apus-toc-toggle" aria-expanded="true" aria-controls="apus-toc-list">';
|
||||
$toc_html .= '<span class="toggle-icon" aria-hidden="true"></span>';
|
||||
$toc_html .= '<span class="screen-reader-text">' . esc_html__('Toggle Table of Contents', 'apus-theme') . '</span>';
|
||||
$toc_html .= '</button>';
|
||||
$toc_html .= '</div>';
|
||||
|
||||
$toc_html .= '<ol class="apus-toc-list" id="apus-toc-list">';
|
||||
|
||||
$current_level = 2;
|
||||
$open_sublists = 0;
|
||||
|
||||
foreach ($headings as $index => $heading) {
|
||||
$level = $heading['level'];
|
||||
$text = esc_html($heading['text']);
|
||||
$id = esc_attr($heading['id']);
|
||||
|
||||
// Handle level changes
|
||||
if ($level > $current_level) {
|
||||
// Open nested list for H3
|
||||
$toc_html .= '<ol class="apus-toc-sublist">';
|
||||
$open_sublists++;
|
||||
} elseif ($level < $current_level && $open_sublists > 0) {
|
||||
// Close nested list when going back to H2
|
||||
$toc_html .= '</li></ol></li>';
|
||||
$open_sublists--;
|
||||
} elseif ($index > 0) {
|
||||
// Close previous item
|
||||
$toc_html .= '</li>';
|
||||
}
|
||||
|
||||
$toc_html .= '<li class="apus-toc-item apus-toc-level-' . $level . '">';
|
||||
$toc_html .= '<a href="#' . $id . '" class="apus-toc-link">' . $text . '</a>';
|
||||
|
||||
$current_level = $level;
|
||||
}
|
||||
|
||||
// Close any open lists
|
||||
$toc_html .= '</li>';
|
||||
while ($open_sublists > 0) {
|
||||
$toc_html .= '</ol></li>';
|
||||
$open_sublists--;
|
||||
}
|
||||
|
||||
$toc_html .= '</ol>';
|
||||
$toc_html .= '</nav>';
|
||||
|
||||
return $toc_html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add IDs to headings in content
|
||||
*
|
||||
* Modifies the post content to add ID attributes to H2 and H3 headings.
|
||||
*
|
||||
* @param string $content Post content
|
||||
* @return string Modified content with IDs added to headings
|
||||
*/
|
||||
function apus_add_heading_ids($content) {
|
||||
if (empty($content)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Extract headings first to get consistent IDs
|
||||
$headings = apus_extract_headings($content);
|
||||
|
||||
if (empty($headings)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Replace headings with versions that include IDs
|
||||
$heading_index = 0;
|
||||
$content = preg_replace_callback(
|
||||
'/<h([23])(?:[^>]*)>(.*?)<\/h\1>/i',
|
||||
function($matches) use ($headings, &$heading_index) {
|
||||
if (!isset($headings[$heading_index])) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
$level = $matches[1];
|
||||
$text = $matches[2];
|
||||
$id = $headings[$heading_index]['id'];
|
||||
$heading_index++;
|
||||
|
||||
return '<h' . $level . ' id="' . esc_attr($id) . '">' . $text . '</h' . $level . '>';
|
||||
},
|
||||
$content
|
||||
);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display Table of Contents before post content
|
||||
*
|
||||
* Hooks into apus_before_post_content to display TOC on single posts.
|
||||
*/
|
||||
function apus_display_toc() {
|
||||
// Only show on single posts
|
||||
if (!is_single()) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $post;
|
||||
|
||||
if (empty($post->post_content)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract headings from content
|
||||
$headings = apus_extract_headings($post->post_content);
|
||||
|
||||
// Generate and display TOC
|
||||
$toc = apus_generate_toc($headings);
|
||||
|
||||
if (!empty($toc)) {
|
||||
echo $toc; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped in apus_generate_toc()
|
||||
}
|
||||
}
|
||||
add_action('apus_before_post_content', 'apus_display_toc');
|
||||
|
||||
/**
|
||||
* Modify post content to add heading IDs
|
||||
*
|
||||
* Filters the_content to add IDs to headings for TOC linking.
|
||||
*
|
||||
* @param string $content Post content
|
||||
* @return string Modified content
|
||||
*/
|
||||
function apus_filter_content_add_heading_ids($content) {
|
||||
// Only apply to single posts
|
||||
if (!is_single()) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
return apus_add_heading_ids($content);
|
||||
}
|
||||
add_filter('the_content', 'apus_filter_content_add_heading_ids', 10);
|
||||
Reference in New Issue
Block a user