42 Commits

Author SHA1 Message Date
Dome 282523fb9c test 2026-05-08 00:15:04 +02:00
Dome 71db3f5ff2 Merge branch 'main' of https://github.com/Domoel/Zeitfresser-Wordpress-Theme 2026-05-06 18:33:32 +02:00
Dome 03253bed9b Update style.css 2026-05-06 18:33:24 +02:00
Dome 59fcc487bf fixed social icon behavior on mobile 2026-05-06 18:14:04 +02:00
Dome 04d6ba2079 Update style.css 2026-05-06 17:58:48 +02:00
Dome 7c21552f05 apply various design tweaks 2026-05-06 17:57:59 +02:00
Dome 98039df80e Merge branch 'main' of https://github.com/Domoel/Zeitfresser-Wordpress-Theme 2026-05-03 09:27:09 +02:00
Dome db32925598 design tweaking to align certain elements 2026-05-03 09:27:07 +02:00
Dome 74e4b59a31 Update style.css 2026-05-03 01:15:34 +02:00
Dome 0a2bbcbef8 refactor: modularize theme structure and clean up functions.php
- moved performance-related logic into /inc/performance/performance.php
- relocated image optimization logic into dedicated image-optimizer module
- simplified functions.php to act as bootstrap and theme setup only
- introduced consistent module loading via require_once
- improved file structure with clear separation of concerns (customizer, utilities, tools, performance)

theme setup improvements:
- consolidated editor styles setup into zeitfresser_setup()
- removed redundant hooks and improved readability

assets:
- added script dependency (scripts -> navigation) to prevent load order issues
- improved stylesheet dependency handling

result:
- cleaner architecture
- better maintainability and scalability
- reduced risk of side effects during future feature development
2026-05-03 01:07:15 +02:00
Dome 61da5b15e5 fix file conflict 2026-05-03 00:24:39 +02:00
Dome ca7101489a Update code-block.php 2026-05-03 00:23:21 +02:00
Dome eda333d17a feat: add native code block feature and optimize asset loading
- Implemented custom code block system with Prism.js highlighting (YAML)
- Added Gutenberg block with modal editor for better handling of large code snippets
- Added Classic Editor button with dialog for structured code input
- Implemented copy-to-clipboard functionality with hover-based UI
- Introduced dedicated styling (code.css)

Performance improvements:
- Load Prism.js and code block assets only when a code block is present
- Reduced unnecessary JS and CSS on pages without code snippets
- Improved overall frontend performance and resource efficiency

UI/UX improvements:
- Adapted code block styling to match theme design (rectangular layout, accent border, integrated color scheme)
- Refactored sidebar search styling for consistent appearance
- Removed conflicting wrapper borders causing double border rendering
- Applied single-border input pattern with clean focus state
- Fixed invalid CSS !important syntax issues
- Aligned search input design with comments and code block components

Result:
- Cleaner UI with consistent component styling
- Improved performance through conditional asset loading
- Better authoring experience for structured YAML content
2026-05-03 00:22:55 +02:00
Dome 1ee3574d03 load code-block code only if needed 2026-05-02 17:37:19 +02:00
Dome b51ce9ab3f feat: add native code block feature with Prism highlighting and editor integration
- Implemented custom code block system for frontend and editors
- Integrated Prism.js for syntax highlighting (YAML + HTML support)
- Added copy-to-clipboard functionality with hover-based UI
- Introduced custom Gutenberg block for code input
- Added Classic Editor button for quick code insertion
- Implemented server-side rendering via the_content filter
- Added dedicated styling (code.css) with Dracula-inspired theme
- Added editor preview styling (editor.css) for visual consistency
- Ensured accessibility and keyboard support for copy button
- Optimized asset loading and versioning using filemtime()

This feature provides a lightweight, theme-native alternative to external code highlighting plugins.
2026-05-02 15:50:33 +02:00
Dome 3150f4da51 Merge branch 'main' of https://github.com/Domoel/Zeitfresser-Wordpress-Theme 2026-04-30 21:41:30 +02:00
Dome 1020442c06 refactor(inc, image-optimizer): restructure /inc architecture and standardize image optimizer module
- reorganized /inc directory structure for improved separation of concerns
  - grouped files into customizer, utilities, and tools
  - improved naming consistency across files (e.g. *-settings, template-tags, etc.)
  - simplified functions.php includes for better readability and maintainability

- refactored Customizer structure
  - extracted image optimizer settings from core-settings into dedicated module
  - consolidated settings, UI logic, and styles into a single feature file
  - improved naming consistency for hooks and functions

- standardized Image Optimizer admin tool
  - renamed "Performance Tools" to "Image Optimizer" across UI and hooks
  - updated admin page registration, callback names, and menu labels
  - aligned AJAX nonce naming for consistency and clarity

- preserved all existing logic and behavior (no functional changes)
- improved overall code organization and long-term maintainability

no breaking changes
2026-04-30 21:41:19 +02:00
Dome 81ed7efa1a enforce justify via css 2026-04-30 11:44:58 +02:00
Dome 84b2b85bf6 more robust toc scroll 2026-04-30 00:14:19 +02:00
Dome 3bcf9f47fb fixed toc progressbar 2026-04-29 23:23:31 +02:00
Dome 8ec3992945 fixec comment count alignment 2026-04-29 22:35:24 +02:00
Dome cac17ab39d fixed alignment and spacing, and highlight issues 2026-04-29 22:22:05 +02:00
Dome 042547c98f fixed fonts issue 2026-04-29 19:32:01 +02:00
Dome 33df5bbe2e refactor(css): restructure stylesheet, remove duplicates, and improve maintainability
* reorganized CSS into clear sections (Root, Base, Layout, Typography, Components, Utilities)
* removed redundant and duplicate rules (header, branding, search, media, etc.)
* consolidated repeated selectors into single sources of truth
* unified form styles (comments + CF7)
* cleaned up sidebar/widget styles and resolved conflicting rules
* fixed related posts grid issue (min-width: 0 for grid children)
* improved TOC structure and extracted shared calculations
* simplified social links and hover behavior
* removed unsafe global performance hint (will-change)
* added prefers-reduced-motion support for accessibility
* improved overall cascade predictability and reduced specificity conflicts

No visual changes intended (safe refactor).
2026-04-29 19:24:55 +02:00
Dome 798d3f9e1f Delete assets/community-badge.png 2026-04-27 09:24:12 +02:00
Dome d477c95917 Update readme.md 2026-04-27 09:23:44 +02:00
Dome 6bf38ae05d refactor(toc, customizer): improve TOC architecture and reorganize customizer settings
- Reorganized Customizer structure for improved clarity and maintainability
- Introduced consistent default values for all settings to ensure stable fallbacks
  when no user preferences are defined

- Refactored scroll-driven TOC implementation:
  - Optimized scroll handling using requestAnimationFrame
  - Reduced layout thrashing and unnecessary DOM reads
  - Improved heading detection logic (deterministic viewport trigger)
  - Enhanced positioning logic (responsive alignment + sidebar awareness)
  - Improved footer collision handling for more robust layout behavior

- Added optional IntersectionObserver-based TOC implementation:
  - Event-driven alternative to scroll-based approach
  - Currently not enabled by default
  - May not be supported long-term due to less deterministic behavior

- General cleanup and internal consistency improvements

chore: bump version to 2.4.0
2026-04-26 18:48:28 +02:00
Dome 36cad12351 Update style.css 2026-04-26 14:01:12 +02:00
Dome a9f1e799d4 refactor(toc): fix alignment and improve layout responsiveness
- add heuristic sidebar detection
- improve TOC positioning and width calculation
- enhance layout robustness across themes
2026-04-26 14:00:35 +02:00
Dome dbdaaff9f2 Update style.css 2026-04-26 08:44:15 +02:00
Dome 00341252a1 fix(toc): correct floating TOC alignment by resolving wrong sidebar reference
The floating TOC positioning logic assumed a correct sidebar reference
when calculating the horizontal offset. However, the selector used
(`aside, .sidebar, #secondary`) could match non-layout elements such as
hidden containers, mobile sidebars, or unrelated widgets.

This resulted in incorrect `getBoundingClientRect()` values and caused
the TOC to be positioned too far left or right, depending on which
element was matched.

Solution:
- Introduced a `getRealSidebar()` helper to dynamically detect the
  visually relevant sidebar element.
- Filters out non-visible or irrelevant elements based on size.
- Selects the right-most valid candidate, ensuring correct layout context.
- Uses the actual gap between content and sidebar to position the TOC
  symmetrically on the opposite side of the content.

Additional improvements:
- Cached sidebar lookup to avoid repeated DOM queries during scroll.
- Stabilized gap calculation with clamping to prevent layout drift.

Result:
The TOC now consistently aligns with the content column and mirrors
the sidebar spacing correctly across different layouts and breakpoints.
2026-04-26 07:27:52 +02:00
Dome 05de6f2028 fix(toc): restore correct floating TOC positioning after layout refactor
Fix incorrect TOC alignment caused by outdated DOM selector in toc.js.
The content reference element changed during layout refactor, breaking
position calculations for the floating TOC.

Updated contentColumn selector to use .main-wrapper/.container fallback,
ensuring correct left offset and responsive positioning.

Also adds a more robust fallback chain to prevent future regressions
when layout structure changes.
2026-04-26 04:43:04 +02:00
Dome 4408a738ec Update readme.md 2026-04-26 04:17:10 +02:00
Dome e5c716740c Merge branch 'main' of https://github.com/Domoel/Zeitfresser-Wordpress-Theme 2026-04-26 04:06:20 +02:00
Dome 001390457b Sync 2026-04-26 04:06:01 +02:00
Dome f479530c4c Merge branch 'main' of https://github.com/Domoel/Zeitfresser-Wordpress-Theme 2026-04-26 03:57:36 +02:00
Dome e3302b79a3 Update functions.php 2026-04-26 03:57:22 +02:00
Dome 2a28d94b4d introduce 2026-04-26 03:56:28 +02:00
Dome fafc46e007 up version 2026-04-26 03:17:02 +02:00
Dome 9f92958651 refactor(core): introduce modular architecture and restructure theme into clean, maintainable components
This commit introduces a complete internal refactoring of the theme architecture,
transitioning from a monolithic functions.php structure to a modular, scalable system.

### Key Changes

#### 1. Modular Architecture
- Extracted core logic into dedicated modules under `/inc/`
- Introduced clear separation of concerns:
  - `helpers/` → shared utility functions
  - `performance/` → optimization and media processing logic
  - `customizer/` → fully modularized Customizer structure
  - `template-*` → presentation and template-related logic

#### 2. Customizer Refactor
- Replaced legacy monolithic customizer with modular system:
  - `core.php` → base setup
  - `general.php`, `layout.php`, `toc.php`, `social.php` → feature-based modules
- Improved maintainability and extensibility for future settings

#### 3. Performance Layer Separation
- Moved performance-related logic into `/inc/performance/`
- Clearly separated:
  - frontend performance tweaks (scripts, CSS, cleanup)
  - media optimization tools (batch processing, cleanup UI)
- Reduced coupling between UI, logic, and processing

#### 4. Media Optimization Pipeline Stabilization
- Re-aligned upload pipeline with WordPress native flow
- Restored proper use of `wp_generate_attachment_metadata`
- Ensured compatibility with:
  - format conversion (AVIF/WebP)
  - responsive image generation
  - WordPress core image handling

#### 5. Cleaner Hook Management
- Removed duplicated hooks and redundant filters
- Standardized hook priorities and responsibilities
- Ensured consistent use of feature toggles (`get_theme_mod`)

#### 6. Improved Code Quality
- Reduced function duplication
- Standardized naming conventions
- Improved readability and inline documentation
- Removed legacy patterns and implicit dependencies

#### 7. Asset Handling Improvements
- Introduced centralized asset versioning via `filemtime`
- Improved script loading strategy (defer non-critical JS)
- Cleaner enqueue structure for styles and scripts

#### 8. Foundation for Future Development
This refactor lays the groundwork for:
- easier feature expansion
- better testability
- improved debugging capabilities
- long-term maintainability

### Breaking Changes
- Internal file structure has changed significantly
- Direct modifications to old functions.php logic may no longer apply
- Customizer extensions must now hook into modular structure

---

This is a purely structural and architectural release.
No intentional changes to frontend behavior were introduced,
but the internal system is now significantly more robust and maintainable.
2026-04-26 03:16:00 +02:00
Dome 787cdb31aa up version 2026-04-25 15:09:49 +02:00
Dome 7b5bd18dcd fix(performance): correctly disable cleanup button when no actionable items remain
- Fix initial state where cleanup button stayed enabled after full cleanup
- Extend logic to detect "nothing to clean" cases:
  - no optimized images available for cleanup
  - all originals already deleted
- Align PHP initial render logic with runtime JS behavior
- Improve UX consistency between page load and live updates
2026-04-25 15:07:21 +02:00
82 changed files with 5679 additions and 2801 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

+173
View File
@@ -0,0 +1,173 @@
/**
* Code block styles
*
* Frontend and editor preview styles for the Zeitfresser code block feature.
* The selectors are scoped to avoid leaking into unrelated blocks or plugins.
*/
/* -------------------------------------------------------------------------
Code block wrapper
------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------
Code block wrapper
------------------------------------------------------------------------- */
.ztfr-code {
position: relative;
background-color: var(--footer-color);
color: var(--light-color);
padding: 1rem 1.2rem;
margin: 1rem 0;
border-radius: 0;
border: 1px solid rgba(248, 248, 242, 0.08);
border-left: 4px solid var(--hover-color);
max-width: 100%;
overflow-x: auto;
font-family: var(--secondary-font);
font-size: 0.85rem;
line-height: 1.6;
font-weight: 400;
box-shadow: none;
}
/* -------------------------------------------------------------------------
Prism overrides
------------------------------------------------------------------------- */
.ztfr-code pre,
.ztfr-code code {
background: transparent;
color: inherit;
}
.ztfr-code pre[class*="language-"],
.ztfr-code code[class*="language-"] {
background: transparent !important;
padding: 0 !important;
margin: 0 !important;
white-space: pre-wrap !important;
word-break: break-word !important;
overflow-wrap: break-word !important;
}
.ztfr-code pre[class*="language-"] {
overflow-x: visible !important;
}
.ztfr-code pre code {
display: block;
padding: 1rem;
overflow-x: auto;
white-space: pre-wrap;
word-break: break-word;
overflow-wrap: break-word;
}
/* -------------------------------------------------------------------------
Copy button
------------------------------------------------------------------------- */
.ztfr-code__copy {
position: absolute;
top: 0.75rem;
right: 0.75rem;
background-color: var(--footer-color);
color: var(--light-color);
border: 1px solid rgba(248, 248, 242, 0.12);
border-radius: 4px;
font-size: 0.72rem;
line-height: 1;
padding: 0.45rem 0.65rem;
cursor: pointer;
opacity: 0;
pointer-events: none;
transition:
opacity 0.2s ease,
background-color 0.2s ease,
color 0.2s ease,
border-color 0.2s ease;
}
.ztfr-code:hover .ztfr-code__copy {
opacity: 1;
pointer-events: auto;
}
.ztfr-code__copy:hover {
background-color: var(--hover-color);
color: #f8f8f2;
}
.ztfr-code__copy:focus {
opacity: 1;
pointer-events: auto;
outline: 2px solid #bd93f9;
outline-offset: 2px;
}
/* -------------------------------------------------------------------------
Dracula token colors
------------------------------------------------------------------------- */
.ztfr-code .token.comment,
.ztfr-code .token.prolog,
.ztfr-code .token.doctype,
.ztfr-code .token.cdata {
color: #6272a4;
}
.ztfr-code .token.punctuation {
color: #f8f8f2;
}
.ztfr-code .token.tag,
.ztfr-code .token.constant,
.ztfr-code .token.symbol,
.ztfr-code .token.deleted {
color: #ff79c6;
}
.ztfr-code .token.attr-name,
.ztfr-code .token.property,
.ztfr-code .token.selector,
.ztfr-code .token.important,
.ztfr-code .token.atrule {
color: #8be9fd;
}
.ztfr-code .token.attr-value,
.ztfr-code .token.string,
.ztfr-code .token.char,
.ztfr-code .token.inserted {
color: #f1fa8c;
}
.ztfr-code .token.keyword,
.ztfr-code .token.boolean,
.ztfr-code .token.number {
color: #bd93f9;
}
.ztfr-code .token.operator,
.ztfr-code .token.entity,
.ztfr-code .token.url {
color: #f8f8f2;
}
.ztfr-code .token.function,
.ztfr-code .token.class-name {
color: #50fa7b;
}
.block-editor-block-list__layout .ztfr-code,
.editor-styles-wrapper .ztfr-code {
margin: 0;
}
+11
View File
@@ -0,0 +1,11 @@
:root {
--light-color: #f7f7fa;
--dark-color: #1e1f29;
--footer-color: #2f313d;
--hover-color: #bd93f9;
}
body {
background-color: #1e1f29;
}
+116
View File
@@ -0,0 +1,116 @@
/**
* Editor styles
*
* Scoped styles for Gutenberg and Classic Editor.
*/
/* -------------------------------------------------------------------------
Editor base
------------------------------------------------------------------------- */
.editor-styles-wrapper,
.mce-content-body {
font-family: var(--secondary-font);
line-height: 1.7;
}
/* -------------------------------------------------------------------------
Shared code block preview
------------------------------------------------------------------------- */
.editor-styles-wrapper .ztfr-code,
.editor-styles-wrapper pre.language-yaml,
.mce-content-body pre.language-yaml {
position: relative;
background-color: #282a36;
color: #f8f8f2;
border: 1px solid rgba(248, 248, 242, 0.08);
border-radius: 6px;
padding: 1rem 1.2rem;
margin: 1rem 0;
max-width: 100%;
overflow-x: auto;
font-family: var(--secondary-font);
font-size: 0.95rem;
line-height: 1.7;
box-sizing: border-box;
}
/* -------------------------------------------------------------------------
Gutenberg preview
------------------------------------------------------------------------- */
.editor-styles-wrapper .ztfr-code pre,
.editor-styles-wrapper .ztfr-code code {
background: transparent;
color: inherit;
}
.editor-styles-wrapper .ztfr-code pre.language-yaml,
.editor-styles-wrapper .ztfr-code code.language-yaml {
background: transparent !important;
padding: 0 !important;
margin: 0 !important;
white-space: pre-wrap !important;
word-break: break-word !important;
overflow-wrap: break-word !important;
}
.editor-styles-wrapper .ztfr-code pre code {
display: block;
white-space: pre-wrap;
word-break: break-word;
overflow-wrap: break-word;
}
/* -------------------------------------------------------------------------
Classic Editor preview
------------------------------------------------------------------------- */
.mce-content-body pre.language-yaml {
white-space: pre-wrap;
word-break: break-word;
overflow-wrap: break-word;
}
.mce-content-body pre.language-yaml code.language-yaml {
display: block;
background: transparent;
color: inherit;
padding: 0;
margin: 0;
white-space: pre-wrap;
word-break: break-word;
overflow-wrap: break-word;
}
/* -------------------------------------------------------------------------
Editor label
------------------------------------------------------------------------- */
.editor-styles-wrapper .ztfr-code.is-editor-preview::before,
.mce-content-body pre.language-yaml::before {
content: "YAML";
display: inline-block;
margin-bottom: 0.75rem;
padding: 0.2rem 0.45rem;
border-radius: 4px;
background: #44475a;
color: #f8f8f2;
font-size: 0.72rem;
line-height: 1;
letter-spacing: 0.03em;
}
/* -------------------------------------------------------------------------
Lists
------------------------------------------------------------------------- */
.editor-styles-wrapper ul,
.editor-styles-wrapper ol,
.mce-content-body ul,
.mce-content-body ol {
margin-left: 0;
padding-left: 1.25em;
list-style-position: outside;
}
+2 -2
View File
@@ -59,8 +59,8 @@
========================= */ ========================= */
:root { :root {
--primary-font: 'Oswald', var(--zeitfresser-heading-fallback); --primary-font: 'Oswald', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--secondary-font: 'Roboto', var(--zeitfresser-body-fallback); --secondary-font: 'Roboto', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--site-identity-font-size: 40px; --site-identity-font-size: 40px;

Before

Width:  |  Height:  |  Size: 264 B

After

Width:  |  Height:  |  Size: 264 B

Before

Width:  |  Height:  |  Size: 264 B

After

Width:  |  Height:  |  Size: 264 B

+94
View File
@@ -0,0 +1,94 @@
/**
* Code Block UI Enhancements
*
* Handles:
* - Copy button
* - Prism highlight trigger
*/
(function () {
'use strict';
function copyText(text, onSuccess, onError) {
if (!text) {
return;
}
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text).then(onSuccess).catch(onError);
return;
}
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', 'readonly');
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
textarea.style.pointerEvents = 'none';
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
try {
document.execCommand('copy');
onSuccess();
} catch (error) {
onError();
}
document.body.removeChild(textarea);
}
function initCodeUI() {
const wrappers = document.querySelectorAll('.ztfr-code');
if (!wrappers.length) {
return;
}
wrappers.forEach(function (wrapper) {
const code = wrapper.querySelector('code');
if (!code) {
return;
}
if (!wrapper.querySelector('.ztfr-code__copy')) {
const button = document.createElement('button');
button.className = 'ztfr-code__copy';
button.type = 'button';
button.setAttribute('aria-label', 'Copy code');
button.textContent = 'Copy';
button.addEventListener('click', function () {
const text = code.textContent;
copyText(
text,
function () {
button.textContent = 'Copied';
window.setTimeout(function () {
button.textContent = 'Copy';
}, 2000);
},
function () {
button.textContent = 'Error';
window.setTimeout(function () {
button.textContent = 'Copy';
}, 2000);
}
);
});
wrapper.appendChild(button);
}
});
if (typeof Prism !== 'undefined') {
Prism.highlightAll();
}
}
document.addEventListener('DOMContentLoaded', initCodeUI);
})();
+219
View File
@@ -0,0 +1,219 @@
(function () {
'use strict';
/**
* Escape HTML before saving it into the code block markup.
*
* @param {string} text Raw code.
* @returns {string} Escaped code.
*/
function escapeHtml(text) {
return String(text)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
/**
* Gutenberg block
*/
if (
window.wp &&
window.wp.blocks &&
window.wp.element &&
window.wp.i18n &&
window.wp.components &&
window.wp.blockEditor
) {
const blocks = window.wp.blocks;
const element = window.wp.element;
const i18n = window.wp.i18n;
const blockEditor = window.wp.blockEditor;
const components = window.wp.components;
const el = element.createElement;
const __ = i18n.__;
const PlainText = blockEditor.PlainText;
const Button = components.Button;
const Modal = components.Modal;
const TextareaControl = components.TextareaControl;
const useState = element.useState;
const useEffect = element.useEffect;
const Fragment = element.Fragment;
function EditCodeBlock(props) {
const content = props.attributes.content || '';
const setAttributes = props.setAttributes;
const state = useState(false);
const isDialogOpen = state[0];
const setDialogOpen = state[1];
function updateCode(value) {
setAttributes({ content: value });
}
useEffect(function () {
if (!isDialogOpen) {
return;
}
window.setTimeout(function () {
const textarea = document.querySelector('.ztfr-code__modal-textarea textarea');
if (textarea) {
textarea.focus();
textarea.setSelectionRange(textarea.value.length, textarea.value.length);
}
}, 50);
}, [isDialogOpen]);
return el(
Fragment,
null,
el(
'div',
{
className: 'ztfr-code is-editor-preview',
'data-language': 'yaml'
},
el(
'div',
{ className: 'ztfr-code__editor-actions' },
el(
Button,
{
variant: 'secondary',
onClick: function () {
setDialogOpen(true);
}
},
__('Edit code', 'zeitfresser')
)
),
el(
'pre',
{ className: 'language-yaml' },
el(PlainText, {
tagName: 'code',
className: 'language-yaml',
value: content,
placeholder: __('Write or paste YAML code here…', 'zeitfresser'),
onChange: updateCode
})
)
),
isDialogOpen &&
el(
Modal,
{
title: __('Edit code block', 'zeitfresser'),
onRequestClose: function () {
setDialogOpen(false);
},
className: 'ztfr-code__modal'
},
el(TextareaControl, {
label: __('Code', 'zeitfresser'),
value: content,
onChange: updateCode,
help: __('Paste your YAML code here. Indentation and line breaks are preserved.', 'zeitfresser'),
rows: 18,
className: 'ztfr-code__modal-textarea'
}),
el(
'div',
{ className: 'ztfr-code__modal-actions' },
el(
Button,
{
variant: 'primary',
onClick: function () {
setDialogOpen(false);
}
},
__('Done', 'zeitfresser')
)
)
)
);
}
blocks.registerBlockType('ztfr/code-block', {
title: __('Code', 'zeitfresser'),
icon: 'editor-code',
category: 'formatting',
description: __('Insert a styled YAML code block.', 'zeitfresser'),
supports: {
html: false
},
attributes: {
content: {
type: 'string',
default: ''
}
},
edit: EditCodeBlock,
save: function (props) {
const content = props.attributes.content || '';
return el(
'pre',
{ className: 'language-yaml' },
el(
'code',
{ className: 'language-yaml' },
content
)
);
}
});
}
/**
* Classic Editor TinyMCE button
*/
if (window.tinymce && window.tinymce.PluginManager) {
window.tinymce.PluginManager.add('ztfr_code_block', function (editor) {
function insertCodeBlockFromDialog() {
editor.windowManager.open({
title: 'Insert code block',
body: [
{
type: 'textbox',
name: 'code',
label: 'Code',
multiline: true,
minWidth: 700,
minHeight: 350,
value: ''
}
],
onsubmit: function (event) {
const code = event.data.code || 'your_key: your_value';
const safeCode = escapeHtml(code);
editor.insertContent(
'<pre class="language-yaml"><code class="language-yaml">' +
safeCode +
'</code></pre><p></p>'
);
editor.focus();
return true;
}
});
}
editor.addButton('ztfr_code_block', {
text: 'Code',
icon: false,
onclick: insertCodeBlockFromDialog
});
});
}
})();
File diff suppressed because one or more lines are too long
@@ -0,0 +1,325 @@
/**
* Floating TOC (Scroll-Driven Implementation)
*
* This implementation uses a scroll-based approach combined with
* requestAnimationFrame to determine the currently active heading.
* The active section is calculated based on a fixed viewport trigger
* (offset from the top), ensuring predictable and stable behavior.
*
* Design Goals:
* - Deterministic highlighting (no competing states)
* - Visual stability (no flickering or race conditions)
* - Theme compatibility (no reliance on experimental APIs)
* - Maintainable and debuggable logic
*
* Characteristics:
* - Uses a cached list of headings for efficient iteration
* - Throttles scroll handling via requestAnimationFrame
* - Minimizes DOM writes and layout recalculations
* - Separates layout (positioning) from state (active link)
*
* Trade-offs:
* - Runs continuously during scroll (minor CPU overhead)
* - Relies on getBoundingClientRect for visibility detection
* - Less "event-driven" than IntersectionObserver-based approaches
*
* Notes:
* This approach was chosen over IntersectionObserver to guarantee
* consistent ordering, eliminate flickering edge cases, and provide
* fully deterministic behavior across all layouts and browsers.
*/
document.addEventListener('DOMContentLoaded', function () {
var toc = document.getElementById('zeitfresser-floating-toc');
var title = document.querySelector(
'.zeitfresser-article-heading .page-title, ' +
'.zeitfresser-article-heading .entry-title, ' +
'.entry-header .entry-title'
);
var progressBar = document.getElementById('zeitfresser-floating-toc-progress');
var nav = toc ? toc.querySelector('.zeitfresser-floating-toc__nav') : null;
if (!toc || !title) {
return;
}
var links = Array.prototype.slice.call(toc.querySelectorAll('a[data-target]'));
var desktopQuery = window.matchMedia('(min-width: 1500px)');
var stickyTop = 100;
var headingOffset = 88;
var ticking = false;
var tocBottomOffset = null;
var cachedSidebar = null;
var headings = getHeadings();
function isDesktop() {
return desktopQuery.matches;
}
function getTarget(link) {
var id = link.getAttribute('data-target');
return id ? document.getElementById(id) : null;
}
function getHeadings() {
return links
.map(function (link) {
return { link: link, target: getTarget(link) };
})
.filter(function (item) {
return !!item.target;
});
}
function getArticleElement() {
return document.querySelector(
'.single-post .post-content article, ' +
'.single-post .post-content, ' +
'article.post, article, ' +
'.entry-content'
);
}
function getTocBottomOffset() {
if (tocBottomOffset !== null) return tocBottomOffset;
var value = getComputedStyle(document.documentElement)
.getPropertyValue('--toc-bottom-offset')
.trim();
tocBottomOffset = parseInt(value, 10) || 12;
return tocBottomOffset;
}
function getRealSidebar() {
if (cachedSidebar) return cachedSidebar;
var candidates = Array.prototype.slice.call(
document.querySelectorAll('aside, .sidebar, #secondary')
);
cachedSidebar = candidates
.filter(function (el) {
var rect = el.getBoundingClientRect();
return rect.width > 200 && rect.height > 200;
})
.sort(function (a, b) {
var rectA = a.getBoundingClientRect();
var rectB = b.getBoundingClientRect();
return rectB.left - rectA.left;
})[0] || null;
return cachedSidebar;
}
function syncPosition() {
if (!isDesktop()) {
document.documentElement.style.setProperty('--zeitfresser-toc-top', stickyTop + 'px');
document.documentElement.style.setProperty('--zeitfresser-toc-left', '24px');
document.documentElement.style.setProperty('--zeitfresser-toc-width', '220px');
return;
}
var scrollTop = window.scrollY || window.pageYOffset || 0;
var titleRect = title.getBoundingClientRect();
// 🔥 bessere Content-Erkennung
var contentColumn =
document.querySelector('.inside-page .main-wrapper > section') ||
document.querySelector('#primary') ||
document.querySelector('.content-area') ||
title;
if (!contentColumn) return;
var sidebar = getRealSidebar();
var contentRect = contentColumn.getBoundingClientRect();
var sidebarRect = sidebar ? sidebar.getBoundingClientRect() : null;
var gap = 48;
if (sidebarRect) {
gap = Math.abs(sidebarRect.left - contentRect.right);
gap = Math.max(32, Math.min(gap, 120));
}
var maxWidth = Math.max(Math.round(contentRect.left - gap - 24), 180);
var tocWidth = Math.max(220, Math.min(260, maxWidth));
var tocLeft = Math.max(
24,
Math.round(contentRect.left - gap - tocWidth)
);
var tocTop = Math.max(
stickyTop,
Math.round(titleRect.top + scrollTop + 14)
);
document.documentElement.style.setProperty('--zeitfresser-toc-top', tocTop + 'px');
document.documentElement.style.setProperty('--zeitfresser-toc-left', tocLeft + 'px');
document.documentElement.style.setProperty('--zeitfresser-toc-width', tocWidth + 'px');
}
function handleFooterCollision() {
if (!isDesktop()) {
toc.style.transform = '';
return;
}
var article = getArticleElement();
if (!article) {
toc.style.transform = '';
return;
}
toc.style.transform = '';
var scrollTop = window.scrollY || window.pageYOffset;
var articleRect = article.getBoundingClientRect();
var articleBottom = articleRect.top + scrollTop + articleRect.height;
var tocRect = toc.getBoundingClientRect();
var tocTop = tocRect.top + scrollTop;
var tocHeight = tocRect.height;
var tocBottom = tocTop + tocHeight;
var offset = getTocBottomOffset();
var maxBottom = articleBottom - offset;
var overflow = Math.ceil(tocBottom - maxBottom);
if (overflow > 0) {
toc.style.transform = 'translateY(-' + overflow + 'px)';
}
}
function setActiveLink(id) {
links.forEach(function (link) {
var active = link.getAttribute('data-target') === id;
link.classList.toggle('is-active', active);
if (active) {
link.setAttribute('aria-current', 'true');
} else {
link.removeAttribute('aria-current');
}
});
}
function updateProgress() {
if (!progressBar) return;
var article = getArticleElement();
if (!article) {
progressBar.style.width = '0%';
return;
}
var rect = article.getBoundingClientRect();
var total = Math.max(
Math.max(article.scrollHeight, article.offsetHeight) - window.innerHeight,
1
);
var progress = Math.min(
Math.max((-rect.top / total) * 100, 0),
100
);
progressBar.style.width = progress + '%';
}
function updateActiveHeading() {
if (!headings.length) return;
var currentId = headings[0].target.id;
var triggerY = headingOffset + 24;
for (var i = 0; i < headings.length; i++) {
var rectTop = headings[i].target.getBoundingClientRect().top;
if (rectTop <= triggerY) {
currentId = headings[i].target.id;
} else {
break;
}
}
setActiveLink(currentId);
}
function onViewportChange() {
if (ticking) return;
ticking = true;
window.requestAnimationFrame(function () {
syncPosition();
handleFooterCollision();
updateProgress();
updateActiveHeading();
ticking = false;
});
}
links.forEach(function (link) {
link.addEventListener('click', function (event) {
var target = getTarget(link);
if (!target) return;
event.preventDefault();
var top =
target.getBoundingClientRect().top +
window.scrollY -
headingOffset;
window.scrollTo({
top: top,
behavior: 'smooth'
});
setActiveLink(target.id);
});
});
if (nav) {
nav.addEventListener(
'wheel',
function (event) {
var canScroll = nav.scrollHeight > nav.clientHeight;
if (!canScroll) return;
var atTop = nav.scrollTop <= 0;
var atBottom =
Math.ceil(nav.scrollTop + nav.clientHeight) >= nav.scrollHeight;
if (
(event.deltaY < 0 && !atTop) ||
(event.deltaY > 0 && !atBottom)
) {
event.preventDefault();
nav.scrollTop += event.deltaY;
}
},
{ passive: false }
);
}
// Initial run
syncPosition();
handleFooterCollision();
updateProgress();
updateActiveHeading();
requestAnimationFrame(function () {
toc.classList.add('is-visible');
});
window.addEventListener('scroll', onViewportChange, { passive: true });
window.addEventListener('resize', onViewportChange, { passive: true });
window.addEventListener('load', function () {
syncPosition();
});
});
+97 -41
View File
@@ -1,3 +1,19 @@
/**
* Floating TOC (IntersectionObserver Version)
*
* This implementation relies on the IntersectionObserver API to detect which
* headings are currently visible in the viewport. It is event-driven and
* more efficient, as updates occur only when visibility changes.
*
* Pros:
* - Lower CPU usage (no continuous polling)
* - Native browser optimization
*
* Cons:
* - Can produce multiple competing active states
* - May require tuning to avoid flickering or reordering
*/
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
var toc = document.getElementById('zeitfresser-floating-toc'); var toc = document.getElementById('zeitfresser-floating-toc');
var title = document.querySelector('.zeitfresser-article-heading .page-title, .zeitfresser-article-heading .entry-title, .entry-header .entry-title'); var title = document.querySelector('.zeitfresser-article-heading .page-title, .zeitfresser-article-heading .entry-title, .entry-header .entry-title');
@@ -13,7 +29,7 @@ document.addEventListener('DOMContentLoaded', function () {
var stickyTop = 100; var stickyTop = 100;
var headingOffset = 88; var headingOffset = 88;
var ticking = false; var ticking = false;
var cachedSidebar = null;
var tocBottomOffset = null; var tocBottomOffset = null;
function isDesktop() { function isDesktop() {
@@ -56,6 +72,25 @@ document.addEventListener('DOMContentLoaded', function () {
return tocBottomOffset; return tocBottomOffset;
} }
function getRealSidebar() {
if (cachedSidebar) return cachedSidebar;
var candidates = Array.prototype.slice.call(
document.querySelectorAll('aside, .sidebar, #secondary')
);
cachedSidebar = candidates
.filter(function (el) {
var rect = el.getBoundingClientRect();
return rect.width > 200 && rect.height > 200;
})
.sort(function (a, b) {
return b.getBoundingClientRect().left - a.getBoundingClientRect().left;
})[0] || null;
return cachedSidebar;
}
function syncPosition() { function syncPosition() {
if (!isDesktop()) { if (!isDesktop()) {
document.documentElement.style.setProperty('--zeitfresser-toc-top', stickyTop + 'px'); document.documentElement.style.setProperty('--zeitfresser-toc-top', stickyTop + 'px');
@@ -64,36 +99,43 @@ document.addEventListener('DOMContentLoaded', function () {
return; return;
} }
var titleRect = title.getBoundingClientRect();
var scrollTop = window.scrollY || window.pageYOffset || 0; var scrollTop = window.scrollY || window.pageYOffset || 0;
var titleRect = title.getBoundingClientRect();
var contentColumn = document.querySelector( // 🔥 bessere Content-Erkennung
'.inside-page .main-wrapper > *:first-child, ' + var contentColumn =
'.inside-page .main-wrapper .primary-content, ' + document.querySelector('.inside-page .main-wrapper > section') ||
'.inside-page .main-wrapper #primary, ' + document.querySelector('#primary') ||
'.inside-page .main-wrapper main' document.querySelector('.content-area') ||
); title;
var sidebar = document.querySelector( if (!contentColumn) return;
'.inside-page .main-wrapper > aside, ' +
'.inside-page .main-wrapper .widget-area, ' +
'.inside-page .main-wrapper #secondary, ' +
'.inside-page .main-wrapper .sidebar'
);
var contentRect = contentColumn ? contentColumn.getBoundingClientRect() : titleRect; var sidebar = getRealSidebar();
var contentRect = contentColumn.getBoundingClientRect();
var sidebarRect = sidebar ? sidebar.getBoundingClientRect() : null; var sidebarRect = sidebar ? sidebar.getBoundingClientRect() : null;
var mirroredGap = 56; var gap = 48;
if (sidebarRect) { if (sidebarRect) {
mirroredGap = Math.max(Math.round(sidebarRect.left - contentRect.right), 40); gap = Math.abs(sidebarRect.left - contentRect.right);
gap = Math.max(32, Math.min(gap, 120));
} }
var maxWidth = Math.max(Math.round(contentRect.left - mirroredGap - 24), 180); // Toc Content Breite
var tocWidth = Math.max(190, Math.min(250, maxWidth)); var maxWidth = Math.max(Math.round(contentRect.left - gap - 24), 180);
var tocLeft = Math.max(24, Math.round(contentRect.left - mirroredGap - tocWidth)); var tocWidth = Math.max(220, Math.min(260, maxWidth));
var tocTop = Math.max(stickyTop, Math.round(titleRect.top + scrollTop + 14));
var tocLeft = Math.max(
24,
Math.round(contentRect.left - gap - tocWidth)
);
var tocTop = Math.max(
stickyTop,
Math.round(titleRect.top + scrollTop + 14)
);
document.documentElement.style.setProperty('--zeitfresser-toc-top', tocTop + 'px'); document.documentElement.style.setProperty('--zeitfresser-toc-top', tocTop + 'px');
document.documentElement.style.setProperty('--zeitfresser-toc-left', tocLeft + 'px'); document.documentElement.style.setProperty('--zeitfresser-toc-left', tocLeft + 'px');
@@ -165,22 +207,6 @@ document.addEventListener('DOMContentLoaded', function () {
progressBar.style.width = progress + '%'; progressBar.style.width = progress + '%';
} }
function updateActiveHeading() {
var headings = getHeadings();
if (!headings.length) return;
var currentId = headings[0].target.id;
var triggerY = headingOffset + 24;
headings.forEach(function (item) {
if (item.target.getBoundingClientRect().top <= triggerY) {
currentId = item.target.id;
}
});
setActiveLink(currentId);
}
function onViewportChange() { function onViewportChange() {
if (ticking) return; if (ticking) return;
@@ -190,11 +216,43 @@ document.addEventListener('DOMContentLoaded', function () {
syncPosition(); syncPosition();
handleFooterCollision(); handleFooterCollision();
updateProgress(); updateProgress();
updateActiveHeading();
ticking = false; ticking = false;
}); });
} }
// 🔥 NEW: IntersectionObserver for active headings
let currentActiveId = null;
function initIntersectionObserver() {
const headings = getHeadings();
if (!headings.length) return;
const observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
const id = entry.target.id;
if (id !== currentActiveId) {
currentActiveId = id;
setActiveLink(id);
}
}
});
}, {
root: null,
rootMargin: `-${headingOffset}px 0px -70% 0px`,
threshold: 0
});
headings.forEach(function (item) {
if (item.target) {
observer.observe(item.target);
}
});
}
links.forEach(function (link) { links.forEach(function (link) {
link.addEventListener('click', function (event) { link.addEventListener('click', function (event) {
var target = getTarget(link); var target = getTarget(link);
@@ -208,8 +266,6 @@ document.addEventListener('DOMContentLoaded', function () {
top: top, top: top,
behavior: 'smooth' behavior: 'smooth'
}); });
setActiveLink(target.id);
}); });
}); });
@@ -232,7 +288,7 @@ document.addEventListener('DOMContentLoaded', function () {
syncPosition(); syncPosition();
handleFooterCollision(); handleFooterCollision();
updateProgress(); updateProgress();
updateActiveHeading(); initIntersectionObserver();
requestAnimationFrame(function () { requestAnimationFrame(function () {
toc.classList.add('is-visible'); toc.classList.add('is-visible');
+325
View File
@@ -0,0 +1,325 @@
/**
* Floating TOC (Scroll-Driven Implementation)
*
* This implementation uses a scroll-based approach combined with
* requestAnimationFrame to determine the currently active heading.
* The active section is calculated based on a fixed viewport trigger
* (offset from the top), ensuring predictable and stable behavior.
*
* Design Goals:
* - Deterministic highlighting (no competing states)
* - Visual stability (no flickering or race conditions)
* - Theme compatibility (no reliance on experimental APIs)
* - Maintainable and debuggable logic
*
* Characteristics:
* - Uses a cached list of headings for efficient iteration
* - Throttles scroll handling via requestAnimationFrame
* - Minimizes DOM writes and layout recalculations
* - Separates layout (positioning) from state (active link)
*
* Trade-offs:
* - Runs continuously during scroll (minor CPU overhead)
* - Relies on getBoundingClientRect for visibility detection
* - Less "event-driven" than IntersectionObserver-based approaches
*
* Notes:
* This approach was chosen over IntersectionObserver to guarantee
* consistent ordering, eliminate flickering edge cases, and provide
* fully deterministic behavior across all layouts and browsers.
*/
document.addEventListener('DOMContentLoaded', function () {
var toc = document.getElementById('zeitfresser-floating-toc');
var title = document.querySelector(
'.zeitfresser-article-heading .page-title, ' +
'.zeitfresser-article-heading .entry-title, ' +
'.entry-header .entry-title'
);
var progressBar = document.getElementById('zeitfresser-floating-toc-progress');
var nav = toc ? toc.querySelector('.zeitfresser-floating-toc__nav') : null;
if (!toc || !title) {
return;
}
var links = Array.prototype.slice.call(toc.querySelectorAll('a[data-target]'));
var desktopQuery = window.matchMedia('(min-width: 1500px)');
var stickyTop = 100;
var headingOffset = 88;
var ticking = false;
var tocBottomOffset = null;
var cachedSidebar = null;
var headings = getHeadings();
function isDesktop() {
return desktopQuery.matches;
}
function getTarget(link) {
var id = link.getAttribute('data-target');
return id ? document.getElementById(id) : null;
}
function getHeadings() {
return links
.map(function (link) {
return { link: link, target: getTarget(link) };
})
.filter(function (item) {
return !!item.target;
});
}
function getArticleElement() {
return document.querySelector(
'.single-post .post-content article, ' +
'.single-post .post-content, ' +
'article.post, article, ' +
'.entry-content'
);
}
function getTocBottomOffset() {
if (tocBottomOffset !== null) return tocBottomOffset;
var value = getComputedStyle(document.documentElement)
.getPropertyValue('--toc-bottom-offset')
.trim();
tocBottomOffset = parseInt(value, 10) || 12;
return tocBottomOffset;
}
function getRealSidebar() {
if (cachedSidebar) return cachedSidebar;
var candidates = Array.prototype.slice.call(
document.querySelectorAll('aside, .sidebar, #secondary')
);
cachedSidebar = candidates
.filter(function (el) {
var rect = el.getBoundingClientRect();
return rect.width > 200 && rect.height > 200;
})
.sort(function (a, b) {
var rectA = a.getBoundingClientRect();
var rectB = b.getBoundingClientRect();
return rectB.left - rectA.left;
})[0] || null;
return cachedSidebar;
}
function syncPosition() {
if (!isDesktop()) {
document.documentElement.style.setProperty('--zeitfresser-toc-top', stickyTop + 'px');
document.documentElement.style.setProperty('--zeitfresser-toc-left', '24px');
document.documentElement.style.setProperty('--zeitfresser-toc-width', '220px');
return;
}
var scrollTop = window.scrollY || window.pageYOffset || 0;
var titleRect = title.getBoundingClientRect();
// 🔥 bessere Content-Erkennung
var contentColumn =
document.querySelector('.inside-page .main-wrapper > section') ||
document.querySelector('#primary') ||
document.querySelector('.content-area') ||
title;
if (!contentColumn) return;
var sidebar = getRealSidebar();
var contentRect = contentColumn.getBoundingClientRect();
var sidebarRect = sidebar ? sidebar.getBoundingClientRect() : null;
var gap = 48;
if (sidebarRect) {
gap = Math.abs(sidebarRect.left - contentRect.right);
gap = Math.max(32, Math.min(gap, 120));
}
var maxWidth = Math.max(Math.round(contentRect.left - gap - 24), 180);
var tocWidth = Math.max(220, Math.min(260, maxWidth));
var tocLeft = Math.max(
24,
Math.round(contentRect.left - gap - tocWidth)
);
var tocTop = Math.max(
stickyTop,
Math.round(titleRect.top + scrollTop + 14)
);
document.documentElement.style.setProperty('--zeitfresser-toc-top', tocTop + 'px');
document.documentElement.style.setProperty('--zeitfresser-toc-left', tocLeft + 'px');
document.documentElement.style.setProperty('--zeitfresser-toc-width', tocWidth + 'px');
}
function handleFooterCollision() {
if (!isDesktop()) {
toc.style.transform = '';
return;
}
var article = getArticleElement();
if (!article) {
toc.style.transform = '';
return;
}
toc.style.transform = '';
var scrollTop = window.scrollY || window.pageYOffset;
var articleRect = article.getBoundingClientRect();
var articleBottom = articleRect.top + scrollTop + articleRect.height;
var tocRect = toc.getBoundingClientRect();
var tocTop = tocRect.top + scrollTop;
var tocHeight = tocRect.height;
var tocBottom = tocTop + tocHeight;
var offset = getTocBottomOffset();
var maxBottom = articleBottom - offset;
var overflow = Math.ceil(tocBottom - maxBottom);
if (overflow > 0) {
toc.style.transform = 'translateY(-' + overflow + 'px)';
}
}
function setActiveLink(id) {
links.forEach(function (link) {
var active = link.getAttribute('data-target') === id;
link.classList.toggle('is-active', active);
if (active) {
link.setAttribute('aria-current', 'true');
} else {
link.removeAttribute('aria-current');
}
});
}
function updateProgress() {
if (!progressBar) return;
var article = getArticleElement();
if (!article) {
progressBar.style.width = '0%';
return;
}
var rect = article.getBoundingClientRect();
var total = Math.max(
Math.max(article.scrollHeight, article.offsetHeight) - window.innerHeight,
1
);
var progress = Math.min(
Math.max((-rect.top / total) * 100, 0),
100
);
progressBar.style.width = progress + '%';
}
function updateActiveHeading() {
if (!headings.length) return;
var currentId = headings[0].target.id;
var triggerY = headingOffset + 24;
for (var i = 0; i < headings.length; i++) {
var rectTop = headings[i].target.getBoundingClientRect().top;
if (rectTop <= triggerY) {
currentId = headings[i].target.id;
} else {
break;
}
}
setActiveLink(currentId);
}
function onViewportChange() {
if (ticking) return;
ticking = true;
window.requestAnimationFrame(function () {
syncPosition();
handleFooterCollision();
updateProgress();
updateActiveHeading();
ticking = false;
});
}
links.forEach(function (link) {
link.addEventListener('click', function (event) {
var target = getTarget(link);
if (!target) return;
event.preventDefault();
var top =
target.getBoundingClientRect().top +
window.scrollY -
headingOffset;
window.scrollTo({
top: top,
behavior: 'smooth'
});
setActiveLink(target.id);
});
});
if (nav) {
nav.addEventListener(
'wheel',
function (event) {
var canScroll = nav.scrollHeight > nav.clientHeight;
if (!canScroll) return;
var atTop = nav.scrollTop <= 0;
var atBottom =
Math.ceil(nav.scrollTop + nav.clientHeight) >= nav.scrollHeight;
if (
(event.deltaY < 0 && !atTop) ||
(event.deltaY > 0 && !atBottom)
) {
event.preventDefault();
nav.scrollTop += event.deltaY;
}
},
{ passive: false }
);
}
// Initial run
syncPosition();
handleFooterCollision();
updateProgress();
updateActiveHeading();
requestAnimationFrame(function () {
toc.classList.add('is-visible');
});
window.addEventListener('scroll', onViewportChange, { passive: true });
window.addEventListener('resize', onViewportChange, { passive: true });
window.addEventListener('load', function () {
syncPosition();
});
});
-12
View File
@@ -1,12 +0,0 @@
:root {
--site-title-color: #f7f7fa;
--primary-color: #f7f7fa;
--secondary-color: #f7f7fa;
--light-color: #1e1f29;
--grey-color: #f7f7fa;
--dark-color: #f7f7fa;
}
body {
background-color: #1e1f29;
}
-7
View File
@@ -1,7 +0,0 @@
/* Align lists in the Classic Editor */
ul, ol {
margin-left: 0 !important;
padding-left: 1.0em !important;
list-style-position: inside !important;
}
+4 -1
View File
@@ -5,7 +5,10 @@
* @package zeitfresser * @package zeitfresser
*/ */
$copyright = get_theme_mod( 'footer_copyright_text', zeitfresser_get_default_footer_copyright() ); $copyright = get_theme_mod(
'footer_copyright_text',
'© ' . date('Y') . ' Zeitfresser'
);
?> ?>
<footer id="colophon" class="site-footer"> <footer id="colophon" class="site-footer">
+109 -687
View File
@@ -9,90 +9,75 @@ if ( ! defined( 'ABSPATH' ) ) {
exit; exit;
} }
/**
* ------------------------------------------------------------------------
* Theme Constants
* ------------------------------------------------------------------------
*/
if ( ! defined( 'ZEITFRESSER_VERSION' ) ) { if ( ! defined( 'ZEITFRESSER_VERSION' ) ) {
define( 'ZEITFRESSER_VERSION', '2.3.6' ); define( 'ZEITFRESSER_VERSION', '2.3.6' );
} }
if ( ! defined( 'DAISY_BLOG_VERSION' ) ) {
define( 'DAISY_BLOG_VERSION', ZEITFRESSER_VERSION );
}
if ( ! defined( 'ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION' ) ) { if ( ! defined( 'ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION' ) ) {
define( 'ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION', '1.0' ); define( 'ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION', '1.0' );
} }
require get_template_directory() . '/inc/zeitfresser-helpers.php';
require get_template_directory() . '/inc/performance-tools.php';
require get_template_directory() . '/inc/zeitfresser-toc.php';
/** /**
* Upload Handler * ------------------------------------------------------------------------
* Customizer
* ------------------------------------------------------------------------
*/ */
add_filter('wp_handle_upload', 'zeitfresser_capture_original_upload', 10, 2); require_once get_template_directory() . '/inc/customizer/core-settings.php';
require_once get_template_directory() . '/inc/customizer/general-settings.php';
require_once get_template_directory() . '/inc/customizer/layout-settings.php';
require_once get_template_directory() . '/inc/customizer/toc-settings.php';
require_once get_template_directory() . '/inc/customizer/social-settings.php';
require_once get_template_directory() . '/inc/customizer/image-optimizer-settings.php';
/** /**
* Capture original upload path safely * ------------------------------------------------------------------------
* Utilities
* ------------------------------------------------------------------------
*/ */
function zeitfresser_capture_original_upload( $upload, $context ) { require_once get_template_directory() . '/inc/utilities/helpers.php';
require_once get_template_directory() . '/inc/utilities/template-tags.php';
if ( empty( $upload['file'] ) ) { require_once get_template_directory() . '/inc/utilities/template-functions.php';
return $upload; require_once get_template_directory() . '/inc/utilities/pagination.php';
} require_once get_template_directory() . '/inc/utilities/toc.php';
// Store temporarily (request-scoped)
$GLOBALS['zeitfresser_last_uploaded_file'] = $upload['file'];
return $upload;
}
add_filter( 'wp_handle_upload', 'zeitfresser_capture_original_upload', 10, 2 );
/** /**
* Persist original file path to attachment meta * ------------------------------------------------------------------------
* Tools
* ------------------------------------------------------------------------
*/ */
function zeitfresser_store_original_file( $attachment_id ) { require_once get_template_directory() . '/inc/tools/image-optimizer.php';
require_once get_template_directory() . '/inc/tools/code-block.php';
if ( ! wp_attachment_is_image( $attachment_id ) ) {
return;
}
if ( empty( $GLOBALS['zeitfresser_last_uploaded_file'] ) ) {
return;
}
$file = $GLOBALS['zeitfresser_last_uploaded_file'];
// Safety: ensure file still exists
if ( ! file_exists( $file ) ) {
return;
}
// Prevent overwrite if already set
if ( get_post_meta( $attachment_id, '_zeitfresser_original_file', true ) ) {
return;
}
update_post_meta(
$attachment_id,
'_zeitfresser_original_file',
$file
);
}
add_action( 'add_attachment', 'zeitfresser_store_original_file' );
/** /**
* Theme setup. * ------------------------------------------------------------------------
* * Performance
* @return void * ------------------------------------------------------------------------
*/
require_once get_template_directory() . '/inc/performance/performance.php';
/**
* ------------------------------------------------------------------------
* Theme Setup
* ------------------------------------------------------------------------
*/ */
function zeitfresser_setup() { function zeitfresser_setup() {
load_theme_textdomain( 'zeitfresser', get_template_directory() . '/languages' ); load_theme_textdomain( 'zeitfresser', get_template_directory() . '/languages' );
add_theme_support( 'automatic-feed-links' ); add_theme_support( 'automatic-feed-links' );
add_theme_support( 'title-tag' ); add_theme_support( 'title-tag' );
add_theme_support( 'post-thumbnails' ); add_theme_support( 'post-thumbnails' );
add_theme_support(
'html5', // Editor Styles
array( add_theme_support( 'editor-styles' );
add_editor_style( 'assets/css/editor.css' );
add_theme_support( 'html5', array(
'search-form', 'search-form',
'comment-form', 'comment-form',
'comment-list', 'comment-list',
@@ -100,49 +85,42 @@ function zeitfresser_setup() {
'caption', 'caption',
'style', 'style',
'script', 'script',
) ));
);
add_theme_support( 'customize-selective-refresh-widgets' ); add_theme_support( 'customize-selective-refresh-widgets' );
add_theme_support(
'custom-logo', add_theme_support( 'custom-logo', array(
array(
'height' => 250, 'height' => 250,
'width' => 250, 'width' => 250,
'flex-width' => true, 'flex-width' => true,
'flex-height' => true, 'flex-height' => true,
) ));
);
add_theme_support( 'align-wide' ); add_theme_support( 'align-wide' );
add_theme_support( 'wp-block-styles' ); add_theme_support( 'wp-block-styles' );
add_theme_support( 'responsive-embeds' ); add_theme_support( 'responsive-embeds' );
register_nav_menus( register_nav_menus( array(
array(
'menu-1' => esc_html__( 'Primary', 'zeitfresser' ), 'menu-1' => esc_html__( 'Primary', 'zeitfresser' ),
) ));
);
add_editor_style( 'editor-style.css' );
} }
add_action( 'after_setup_theme', 'zeitfresser_setup' ); add_action( 'after_setup_theme', 'zeitfresser_setup' );
/** /**
* Register optimized image sizes * ------------------------------------------------------------------------
* Image Sizes
* ------------------------------------------------------------------------
*/ */
function zeitfresser_custom_image_sizes() { function zeitfresser_custom_image_sizes() {
// Content images (main article)
add_image_size( 'zeitfresser-content', 720, 0, false ); add_image_size( 'zeitfresser-content', 720, 0, false );
// Archive / card layout
add_image_size( 'zeitfresser-card', 480, 0, false ); add_image_size( 'zeitfresser-card', 480, 0, false );
} }
add_action( 'after_setup_theme', 'zeitfresser_custom_image_sizes' ); add_action( 'after_setup_theme', 'zeitfresser_custom_image_sizes' );
/** /**
* Set the content width in pixels. * ------------------------------------------------------------------------
* * Content Width
* @return void * ------------------------------------------------------------------------
*/ */
function zeitfresser_content_width() { function zeitfresser_content_width() {
$GLOBALS['content_width'] = apply_filters( 'zeitfresser_content_width', 640 ); $GLOBALS['content_width'] = apply_filters( 'zeitfresser_content_width', 640 );
@@ -150,13 +128,12 @@ function zeitfresser_content_width() {
add_action( 'after_setup_theme', 'zeitfresser_content_width', 0 ); add_action( 'after_setup_theme', 'zeitfresser_content_width', 0 );
/** /**
* Register widget area. * ------------------------------------------------------------------------
* * Widgets
* @return void * ------------------------------------------------------------------------
*/ */
function zeitfresser_widgets_init() { function zeitfresser_widgets_init() {
register_sidebar( register_sidebar( array(
array(
'name' => esc_html__( 'Sidebar', 'zeitfresser' ), 'name' => esc_html__( 'Sidebar', 'zeitfresser' ),
'id' => 'sidebar-1', 'id' => 'sidebar-1',
'description' => esc_html__( 'Add widgets here.', 'zeitfresser' ), 'description' => esc_html__( 'Add widgets here.', 'zeitfresser' ),
@@ -164,631 +141,76 @@ function zeitfresser_widgets_init() {
'after_widget' => '</section>', 'after_widget' => '</section>',
'before_title' => '<h4 class="widget-title">', 'before_title' => '<h4 class="widget-title">',
'after_title' => '</h4>', 'after_title' => '</h4>',
) ));
);
} }
add_action( 'widgets_init', 'zeitfresser_widgets_init' ); add_action( 'widgets_init', 'zeitfresser_widgets_init' );
/** /**
* Enqueue floating TOC assets on single posts. * ------------------------------------------------------------------------
* * Assets
* @return void * ------------------------------------------------------------------------
*/
function zeitfresser_enqueue_toc_assets() {
if ( is_singular( 'post' ) && zeitfresser_has_floating_toc() ) {
wp_enqueue_script(
'zeitfresser-toc',
get_template_directory_uri() . '/js/toc.js',
array(),
ZEITFRESSER_VERSION,
true
);
}
}
add_action( 'wp_enqueue_scripts', 'zeitfresser_enqueue_toc_assets', 20 );
/**
* Return file version using filemtime in production-safe form.
*
* @param string $relative_path Relative file path inside the theme.
* @return string
*/
function zeitfresser_asset_version( $relative_path ) {
$path = get_template_directory() . $relative_path;
return file_exists( $path ) ? (string) filemtime( $path ) : ZEITFRESSER_VERSION;
}
/**
* Enqueue scripts and styles.
*
* @return void
*/ */
function zeitfresser_scripts() { function zeitfresser_scripts() {
/**
* Base stylesheet
*/
wp_enqueue_style( wp_enqueue_style(
'zeitfresser', 'zeitfresser',
get_template_directory_uri() . '/style.css', get_template_directory_uri() . '/style.css',
array(), [],
zeitfresser_asset_version( '/style.css' ) file_exists( get_template_directory() . '/style.css' )
? filemtime( get_template_directory() . '/style.css' )
: ZEITFRESSER_VERSION
); );
/**
* Additional styles (versioned helper)
*/
$fonts = zeitfresser_asset_versioned('/css/fonts.css');
$colors = zeitfresser_asset_versioned('/css/colors.css');
wp_enqueue_style(
'zeitfresser-fonts',
$fonts['url'],
['zeitfresser'],
$fonts['version']
);
wp_enqueue_style(
'zeitfresser-colors',
$colors['url'],
['zeitfresser'],
$colors['version']
);
/**
* Scripts
*/
$nav = zeitfresser_asset_versioned('/js/navigation.js');
$scripts = zeitfresser_asset_versioned('/js/scripts.js');
wp_enqueue_script( wp_enqueue_script(
'zeitfresser-navigation', 'zeitfresser-navigation',
get_template_directory_uri() . '/js/navigation.js', $nav['url'],
array(), [],
zeitfresser_asset_version( '/js/navigation.js' ), $nav['version'],
true true
); );
if ( is_home() || is_front_page() || is_archive() || is_search() ) {
wp_enqueue_script(
'zeitfresser-masonry',
get_template_directory_uri() . '/js/masonry.pkgd.min.js',
array(),
zeitfresser_asset_version( '/js/masonry.pkgd.min.js' ),
true
);
}
wp_enqueue_script( wp_enqueue_script(
'zeitfresser-scripts', 'zeitfresser-scripts',
get_template_directory_uri() . '/js/scripts.js', $scripts['url'],
array(), ['zeitfresser-navigation'],
zeitfresser_asset_version( '/js/scripts.js' ), $scripts['version'],
true true
); );
/**
* WordPress native threaded comments
*/
if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) { if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
wp_enqueue_script( 'comment-reply' ); wp_enqueue_script( 'comment-reply' );
} }
} }
add_action( 'wp_enqueue_scripts', 'zeitfresser_scripts', 10 ); add_action( 'wp_enqueue_scripts', 'zeitfresser_scripts', 10 );
/**
* Load RTL stylesheet from /css folder
*/
function zeitfresser_rtl_styles() {
if ( is_rtl() ) {
wp_enqueue_style(
'zeitfresser-rtl',
get_template_directory_uri() . '/css/style-rtl.css',
array('zeitfresser'),
filemtime(get_template_directory() . '/css/style-rtl.css')
);
}
}
add_action( 'wp_enqueue_scripts', 'zeitfresser_rtl_styles', 11 );
function zeitfresser_enqueue_static_colors() {
wp_enqueue_style(
'zeitfresser-colors',
get_template_directory_uri() . '/css/colors.css',
array('zeitfresser'),
file_exists(get_template_directory() . '/css/colors.css')
? filemtime(get_template_directory() . '/css/colors.css')
: ZEITFRESSER_VERSION
);
}
add_action( 'wp_enqueue_scripts', 'zeitfresser_enqueue_static_colors', 20 );
function zeitfresser_enqueue_static_fonts() {
wp_enqueue_style(
'zeitfresser-fonts',
get_template_directory_uri() . '/css/fonts.css',
array(),
file_exists(get_template_directory() . '/css/fonts.css')
? filemtime(get_template_directory() . '/css/fonts.css')
: ZEITFRESSER_VERSION
);
}
add_action( 'wp_enqueue_scripts', 'zeitfresser_enqueue_static_fonts', 15 );
/**
* Theme package marker kept for compatibility with the original premium controls.
*
* @return string
*/
function zeitfresser_free_pro() {
return 'pro';
}
require get_template_directory() . '/inc/template-tags.php';
require get_template_directory() . '/inc/template-functions.php';
require get_template_directory() . '/inc/customizer.php';
if ( defined( 'JETPACK__VERSION' ) ) {
require get_template_directory() . '/inc/jetpack.php';
}
require get_template_directory() . '/inc/blocks/blocks.php';
require get_template_directory() . '/inc/pagination.php';
/**
* Add safe front-end performance optimizations.
*
* @return void
*/
function zeitfresser_performance_setup() {
// Disable the legacy embeds script on the front end. Native iframes still work.
if ( ! is_admin() ) {
wp_deregister_script( 'wp-embed' );
}
}
add_action( 'wp_enqueue_scripts', 'zeitfresser_performance_setup', 100 );
/**
* Remove unnecessary head output for a leaner front end.
*
* @return void
*/
function zeitfresser_cleanup_wp_head() {
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
remove_action( 'admin_print_styles', 'print_emoji_styles' );
remove_action( 'wp_head', 'rsd_link' );
remove_action( 'wp_head', 'wlwmanifest_link' );
remove_action( 'wp_head', 'wp_generator' );
remove_action( 'wp_head', 'rest_output_link_wp_head', 10 );
remove_action( 'wp_head', 'wp_shortlink_wp_head', 10 );
remove_action( 'wp_head', 'wp_oembed_add_discovery_links', 10 );
remove_action( 'wp_head', 'wp_oembed_add_host_js' );
remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10 );
}
add_action( 'init', 'zeitfresser_cleanup_wp_head' );
/**
* Preload critical local fonts only
*/
function zeitfresser_preload_fonts() {
?>
<!-- Critical Fonts Only -->
<link rel="preload" href="<?php echo get_template_directory_uri(); ?>/fonts/oswald-400.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="<?php echo get_template_directory_uri(); ?>/fonts/oswald-700.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="<?php echo get_template_directory_uri(); ?>/fonts/roboto-400.woff2" as="font" type="font/woff2" crossorigin>
<?php
}
add_action('wp_head', 'zeitfresser_preload_fonts', 1);
/**
* Optimize font loading with preconnect
*/
add_filter( 'wp_resource_hints', function( $urls, $relation_type ) {
if ( 'preconnect' === $relation_type ) {
$urls[] = [
'href' => get_template_directory_uri(),
'crossorigin' => 'anonymous',
];
}
return $urls;
}, 10, 2 );
/**
* Remove front-end dashicons for visitors.
*
* @return void
*/
function zeitfresser_maybe_dequeue_dashicons() {
if ( ! is_user_logged_in() ) {
wp_deregister_style( 'dashicons' );
}
}
add_action( 'wp_enqueue_scripts', 'zeitfresser_maybe_dequeue_dashicons', 100 );
/**
* Improve image decoding defaults without changing visual output.
*
* @param array $attr Image markup attributes.
* @param WP_Post $attachment Attachment post object.
* @param string|array $size Requested image size.
* @return array
*/
function zeitfresser_optimize_image_attributes( $attr, $attachment, $size ) {
if ( empty( $attr['decoding'] ) ) {
$attr['decoding'] = 'async';
}
if ( empty( $attr['loading'] ) && ! is_admin() ) {
$attr['loading'] = 'lazy';
}
return $attr;
}
add_filter( 'wp_get_attachment_image_attributes', 'zeitfresser_optimize_image_attributes', 10, 3 );
/**
* Lower the threshold for WordPress scaled originals when auto optimization is enabled.
*
* When automatic optimization is disabled, original uploads should remain untouched.
*
* @return int|false
*/
function zeitfresser_big_image_size_threshold() {
if ( ! get_theme_mod( 'ztfr_auto_optimize', true ) ) {
return false;
}
return 1800;
}
add_filter( 'big_image_size_threshold', 'zeitfresser_big_image_size_threshold' );
/**
* Skip generating oversized core intermediate sizes we do not use.
*
* @param array $sizes Registered intermediate sizes.
* @return array
*/
function zeitfresser_filter_intermediate_image_sizes( $sizes ) {
// Remove oversized defaults
unset(
$sizes['1536x1536'],
$sizes['2048x2048']
);
return $sizes;
}
add_filter( 'intermediate_image_sizes_advanced', 'zeitfresser_filter_intermediate_image_sizes' );
/**
* Convert generated JPEG and PNG files to AVIF/WebP when enabled.
*
* Auto optimization can be disabled for uploads via Customizer.
* Manual optimization may still force conversion through a request-scoped flag.
*
* @param array $formats Output format map.
* @return array
*/
function zeitfresser_image_output_format( $formats ) {
$auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
$force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
if ( ! $auto_enabled && ! $force_enabled ) {
return $formats;
}
if ( function_exists( 'wp_image_editor_supports' ) ) {
// Prefer AVIF if supported.
if ( wp_image_editor_supports( array( 'mime_type' => 'image/avif' ) ) ) {
$formats['image/jpeg'] = 'image/avif';
$formats['image/png'] = 'image/avif';
// Fallback to WebP.
} elseif ( wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
$formats['image/jpeg'] = 'image/webp';
$formats['image/png'] = 'image/webp';
}
}
return $formats;
}
add_filter( 'image_editor_output_format', 'zeitfresser_image_output_format' );
/**
* Mark images as optimized only when optimization is actually active.
*
* @param array $metadata Attachment metadata.
* @param int $attachment_id Attachment ID.
* @return array
*/
function zeitfresser_mark_new_images_optimized( $metadata, $attachment_id ) {
if ( ! wp_attachment_is_image( $attachment_id ) ) {
return $metadata;
}
$auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
$force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
if ( ! $auto_enabled && ! $force_enabled ) {
return $metadata;
}
update_post_meta(
$attachment_id,
'_zeitfresser_media_optimized_version',
ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION
);
return $metadata;
}
add_filter( 'wp_generate_attachment_metadata', 'zeitfresser_mark_new_images_optimized', 20, 2 );
/**
* Auto Optimize Hook
*/
add_filter(
'wp_generate_attachment_metadata',
'zeitfresser_auto_optimize_on_upload',
15,
2
);
function zeitfresser_auto_optimize_on_upload( $metadata, $attachment_id ) {
// 🔒 only images
if ( ! wp_attachment_is_image( $attachment_id ) ) {
return $metadata;
}
// 🔒 feature toggle
if ( ! get_theme_mod( 'ztfr_auto_optimize', true ) ) {
return $metadata;
}
$file = get_attached_file( $attachment_id );
if ( ! $file || ! file_exists( $file ) ) {
return $metadata;
}
// 🔥 DO NOT overwrite upload-captured original
if ( ! get_post_meta( $attachment_id, '_zeitfresser_original_file', true ) ) {
// fallback only
update_post_meta( $attachment_id, '_zeitfresser_original_file', $file );
}
// mark as optimized
update_post_meta(
$attachment_id,
'_zeitfresser_media_optimized_version',
ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION
);
return $metadata;
}
/**
* Auto Delete Hook
*/
add_filter(
'wp_generate_attachment_metadata',
'zeitfresser_auto_delete_original_after_upload',
30,
2
);
function zeitfresser_auto_delete_original_after_upload( $metadata, $attachment_id ) {
if ( ! wp_attachment_is_image( $attachment_id ) ) {
return $metadata;
}
if ( ! get_theme_mod( 'ztfr_auto_optimize', true ) ) {
return $metadata;
}
if ( ! get_theme_mod( 'ztfr_auto_delete', false ) ) {
return $metadata;
}
$original = get_post_meta(
$attachment_id,
'_zeitfresser_original_file',
true
);
if ( ! $original ) {
return $metadata;
}
$ext = strtolower( pathinfo( $original, PATHINFO_EXTENSION ) );
if ( in_array( $ext, [ 'webp', 'avif' ], true ) ) {
update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
return $metadata;
}
// If nothing remains, mark as completed.
if ( ! zeitfresser_original_family_exists( $attachment_id, $original ) ) {
update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
return $metadata;
}
zeitfresser_delete_original_family_files( $attachment_id, $original );
// Only mark as deleted when all original-format files are gone.
if ( ! zeitfresser_original_family_exists( $attachment_id, $original ) ) {
update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
}
return $metadata;
}
/**
* Keep generated image quality balanced for file size and visual fidelity.
*
* @param int $quality Proposed image quality.
* @param string $mime_type Image mime type.
* @return int
*/
function zeitfresser_image_quality( $quality, $mime_type = 'image/jpeg' ) {
switch ( $mime_type ) {
case 'image/avif':
return 50;
case 'image/webp':
return 75;
case 'image/jpeg':
default:
return 82;
}
}
add_filter( 'wp_editor_set_quality', 'zeitfresser_image_quality', 10, 2 );
/**
* Improve attachment image attributes for layout stability and fetch priority.
*
* @param array $attr Image markup attributes.
* @param WP_Post $attachment Attachment post object.
* @param string|array $size Requested image size.
* @return array
*/
function zeitfresser_improve_attachment_dimensions( $attr, $attachment, $size ) {
if ( empty( $attr['width'] ) || empty( $attr['height'] ) ) {
$metadata = wp_get_attachment_metadata( $attachment->ID );
if ( is_array( $metadata ) && ! empty( $metadata['width'] ) && ! empty( $metadata['height'] ) ) {
if ( empty( $attr['width'] ) ) {
$attr['width'] = (int) $metadata['width'];
}
if ( empty( $attr['height'] ) ) {
$attr['height'] = (int) $metadata['height'];
}
}
}
if ( empty( $attr['fetchpriority'] ) && ! is_admin() && ! is_feed() ) {
static $did_set_high_priority = false;
if ( ! $did_set_high_priority && is_singular() ) {
$attr['fetchpriority'] = 'high';
$did_set_high_priority = true;
}
}
return $attr;
}
add_filter( 'wp_get_attachment_image_attributes', 'zeitfresser_improve_attachment_dimensions', 11, 3 );
/**
* Improve responsive image sizes attribute.
*
* @param string $sizes Existing sizes attribute.
* @param array $size Requested image size.
* @return string
*/
function zeitfresser_responsive_image_sizes( $sizes, $size ) {
// Single post content
if ( is_singular() ) {
return '(max-width: 768px) 100vw, (max-width: 1200px) 720px, 720px';
}
// Archive / blog overview
if ( is_home() || is_front_page() || is_archive() || is_search() ) {
return '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 400px';
}
return $sizes;
}
add_filter( 'wp_calculate_image_sizes', 'zeitfresser_responsive_image_sizes', 10, 2 );
/**
* Determine the most likely LCP image URL.
*
* @return string
*/
function zeitfresser_get_lcp_image_url() {
// Single posts/pages → featured image
if ( is_singular() ) {
$post_id = get_queried_object_id();
if ( $post_id && has_post_thumbnail( $post_id ) ) {
return get_the_post_thumbnail_url( $post_id, 'large' );
}
}
// Archives / homepage → first post with thumbnail
if ( is_home() || is_front_page() || is_archive() || is_search() ) {
global $wp_query;
if ( ! empty( $wp_query->posts ) ) {
foreach ( $wp_query->posts as $post ) {
if ( has_post_thumbnail( $post->ID ) ) {
return get_the_post_thumbnail_url( $post->ID, 'medium_large' );
}
}
}
}
return '';
}
/**
* Preload the most likely LCP image for archive and singular views.
*
* @param array $resources Existing preload resources.
* @return array
*/
function zeitfresser_preload_resources( $resources ) {
if ( is_admin() || is_feed() || is_embed() ) {
return $resources;
}
// 🔥 NEW: use smart detection
$image_url = zeitfresser_get_lcp_image_url();
if ( empty( $image_url ) ) {
return $resources;
}
// Robust extension detection
$extension = strtolower(
pathinfo(
wp_parse_url( $image_url, PHP_URL_PATH ),
PATHINFO_EXTENSION
)
);
// MIME fallback
$image_type = 'image/jpeg';
if ( 'png' === $extension ) {
$image_type = 'image/png';
} elseif ( 'webp' === $extension ) {
$image_type = 'image/webp';
} elseif ( 'avif' === $extension ) {
$image_type = 'image/avif';
}
$resources[] = array_filter(
array(
'href' => esc_url( $image_url ),
'as' => 'image',
'type' => $image_type,
'fetchpriority' => 'high',
)
);
return $resources;
}
add_filter( 'wp_preload_resources', 'zeitfresser_preload_resources' );
/**
* Improve script loading strategy for non-critical assets.
*
* @param array $tag Script tag markup.
* @param string $handle Script handle.
* @param string $src Script source URL.
* @return string
*/
function zeitfresser_defer_non_critical_scripts( $tag, $handle, $src ) {
$deferred_handles = array(
'zeitfresser-navigation',
'zeitfresser-masonry',
'zeitfresser-scripts',
);
if ( in_array( $handle, $deferred_handles, true ) && false === strpos( $tag, ' defer' ) ) {
return str_replace( ' src=', ' defer src=', $tag );
}
return $tag;
}
add_filter( 'script_loader_tag', 'zeitfresser_defer_non_critical_scripts', 10, 3 );
+25 -22
View File
@@ -2,13 +2,8 @@
/** /**
* The header for our theme * The header for our theme
* *
* This is the template that displays all of the <head> section and everything up until <div id="content">
*
* @link https://developer.wordpress.org/themes/basics/template-files/#template-partials
*
* @package zeitfresser * @package zeitfresser
*/ */
?> ?>
<!doctype html> <!doctype html>
<html <?php language_attributes(); ?>> <html <?php language_attributes(); ?>>
@@ -22,47 +17,52 @@
</head> </head>
<body <?php body_class(); ?>> <body <?php body_class(); ?>>
<?php wp_body_open(); ?> <?php wp_body_open(); ?>
<a class="skip-link screen-reader-text" href="#primary"><?php esc_html_e( 'Skip to content', 'zeitfresser' ); ?></a>
<a class="skip-link screen-reader-text" href="#primary">
<?php esc_html_e( 'Skip to content', 'zeitfresser' ); ?>
</a>
<header id="masthead" class="site-header"> <header id="masthead" class="site-header">
<div class="header-wrapper"> <div class="header-wrapper">
<div class="container"> <div class="container">
<div class="site-header-wrapper"> <div class="site-header-wrapper">
<div class="site-branding"> <div class="site-branding">
<?php the_custom_logo(); ?> <?php the_custom_logo(); ?>
<div class="site-identity"> <div class="site-identity">
<?php if( get_theme_mod( 'show_hide_site_title', zeitfresser_get_default_site_title_show_hide() ) ) { ?> <?php if ( get_theme_mod( 'show_hide_site_title', true ) ) : ?>
<div class="site-title"> <div class="site-title">
<a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home" <a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home" class="logo">
class="logo"><?php bloginfo( 'name' ); ?></a> <?php bloginfo( 'name' ); ?>
</a>
</div> </div>
<?php } ?> <?php endif; ?>
<?php if ( get_theme_mod( 'show_hide_site_tagline', true ) ) : ?>
<div class="site-description">
<?php bloginfo( 'description' ); ?>
</div>
<?php endif; ?>
<?php $daisy_blog_description = get_bloginfo( 'description' ); ?>
<?php if( get_theme_mod( 'show_hide_site_tagline', zeitfresser_get_default_site_tagline_show_hide() ) ) { ?>
<div class="site-description"><?php echo $daisy_blog_description; ?></div>
<?php } ?>
</div> </div>
</div><!-- .site-branding --> </div><!-- .site-branding -->
<div class="nav-social-links"> <div class="nav-social-links">
<nav id="site-navigation" class="main-navigation">
<button id="nav-icon3" class="menu-toggle" aria-controls="primary-menu"
aria-expanded="false">
<nav id="site-navigation" class="main-navigation">
<button id="nav-icon3" class="menu-toggle" aria-controls="primary-menu" aria-expanded="false">
<span></span> <span></span>
<span></span> <span></span>
<span></span> <span></span>
<span></span> <span></span>
</button> </button>
<?php <?php
wp_nav_menu( wp_nav_menu(
array( array(
@@ -71,11 +71,14 @@
) )
); );
?> ?>
</nav><!-- #site-navigation --> </nav>
<?php get_template_part( 'template-parts/social', 'links' ); ?> <?php get_template_part( 'template-parts/social', 'links' ); ?>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
</header><!-- #masthead --> </header><!-- #masthead -->
-20
View File
@@ -1,20 +0,0 @@
<?php
/**
* Register customizer blocks used by the Zeitfresser theme.
*/
if ( ! defined( 'ZEITFRESSER_BLOCKS_DIR_PATH' ) ) {
define( 'ZEITFRESSER_BLOCKS_DIR_PATH', dirname( __FILE__ ) );
}
if ( ! defined( 'DAISY_BLOG_BLOCKS_DIR_PATH' ) ) {
define( 'DAISY_BLOG_BLOCKS_DIR_PATH', ZEITFRESSER_BLOCKS_DIR_PATH );
}
require dirname( __FILE__ ) . '/includes/sanitize.php';
require dirname( __FILE__ ) . '/includes/register-controls.php';
require dirname( __FILE__ ) . '/site-identity/site-identity.php';
require dirname( __FILE__ ) . '/general/general.php';
require dirname( __FILE__ ) . '/post-detail/post-detail.php';
require dirname( __FILE__ ) . '/footer-copyright/footer-copyright.php';
@@ -1,5 +0,0 @@
<?php
function zeitfresser_get_default_footer_copyright() {
return "Zeitfresser ©";
}
@@ -1,26 +0,0 @@
<?php
/* Add Default Copyright Text */
require dirname( __FILE__ ) . '/default-footer-copyright.php';
add_action( 'customize_register', 'zeitfresser_customize_register_footer_copyright' );
function zeitfresser_customize_register_footer_copyright( $wp_customize ) {
$wp_customize->add_section( 'daisy_blog_footer_copyright_section', array(
'title' => esc_html__( 'Footer Copyright', 'zeitfresser' ),
'priority' => 24
) );
$wp_customize->add_setting( 'footer_copyright_text', array(
'sanitize_callback' => 'wp_kses_post',
'default' => zeitfresser_get_default_footer_copyright()
) );
$wp_customize->add_control( 'footer_copyright_text', array(
'label' => esc_html__( 'Copyright Text', 'zeitfresser' ),
'section' => 'daisy_blog_footer_copyright_section',
'settings' => 'footer_copyright_text',
'type'=> 'textarea',
) );
}
@@ -1,64 +0,0 @@
<?php
add_action( 'customize_register', 'zeitfresser_container_width' );
function zeitfresser_container_width( $wp_customize ) {
$general_title = '<hr/><h2>' . esc_html__( 'General:', 'zeitfresser' ) . '</h2>';
$wp_customize->add_setting(
'zeitfresser_general_title',
array(
'default' => '',
'sanitize_callback' => 'wp_kses_post',
)
);
$wp_customize->add_control(
new Daisy_Blog_Custom_Text(
$wp_customize,
'zeitfresser_general_title',
array(
'section' => 'daisy_blog_general_customization_section',
'label' => $general_title,
'priority' => 5,
)
)
);
$wp_customize->add_setting('container_width', array(
'default' => zeitfresser_get_default_container_width(),
'sanitize_callback' => 'absint',
'transport' => 'postMessage',
));
$wp_customize->add_control( 'container_width', array(
'label' => esc_html__('Container Width', 'zeitfresser'),
'section' => 'daisy_blog_general_customization_section',
'type' => 'number',
'priority' => 10,
'input_attrs' => array(
'min' => 1000,
'max' => 1600
) )
);
}
add_action( 'customize_preview_init', 'zeitfresser_container_width_enqueue_scripts' );
function zeitfresser_container_width_enqueue_scripts() {
wp_enqueue_script( 'graphthemes-container-width-customizer', get_template_directory_uri() . '/inc/blocks/general/container-width/customizer-container-width.js', array('jquery'), '', true );
}
add_action( 'wp_enqueue_scripts', 'zeitfresser_container_width_dynamic_css' );
function zeitfresser_container_width_dynamic_css() {
$container_width = esc_attr( get_theme_mod( 'container_width', zeitfresser_get_default_container_width() ) );
$container_width .= 'px';
$dynamic_css = ":root { --container-width: $container_width; }";
wp_add_inline_style( 'zeitfresser', $dynamic_css );
}
@@ -1,10 +0,0 @@
jQuery( function( $ ) {
wp.customize('container_width',function ( value ) {
value.bind(function ( to ) {
document.body.style.setProperty('--container-width', to+"px");
}
);
} );
} );
-25
View File
@@ -1,25 +0,0 @@
<?php
/* Default General Customization */
function zeitfresser_get_default_breadcrumbs() {
return false;
}
function zeitfresser_get_default_sticky_menu() {
return false;
}
function zeitfresser_get_default_container_width() {
return 1400;
}
function zeitfresser_get_default_show_article_toc() {
return true;
}
function zeitfresser_get_default_article_toc_min_headlines() {
return 3;
}
-30
View File
@@ -1,30 +0,0 @@
<?php
/**
* General customizer section.
*/
add_action( 'customize_register', 'zeitfresser_register_general_customization_section' );
/**
* Register the general options section.
*
* @param WP_Customize_Manager $wp_customize Customizer manager.
* @return void
*/
function zeitfresser_register_general_customization_section( $wp_customize ) {
$wp_customize->add_section(
'daisy_blog_general_customization_section',
array(
'title' => esc_html__( 'General Options', 'zeitfresser' ),
'priority' => 21,
)
);
}
require dirname( __FILE__ ) . '/default-general.php';
require dirname( __FILE__ ) . '/container-width/container-width.php';
require dirname( __FILE__ ) . '/social-links/social-links.php';
require dirname( __FILE__ ) . '/toc-options.php';
require dirname( __FILE__ ) . '/../post-snippet/default-post-snippet.php';
require dirname( __FILE__ ) . '/../post-snippet/excerpt/excerpt.php';
@@ -1,83 +0,0 @@
<?php
/**
* Social links customizer settings.
*
* @package zeitfresser
*/
if ( ! function_exists( 'zeitfresser_get_social_links' ) ) {
/**
* Return supported social network labels.
*
* @return array<string,string>
*/
function zeitfresser_get_social_links() {
return array(
'facebook' => esc_html__( 'Facebook', 'zeitfresser' ),
'instagram' => esc_html__( 'Instagram', 'zeitfresser' ),
'youtube' => esc_html__( 'Youtube', 'zeitfresser' ),
'linkedin' => esc_html__( 'LinkedIn', 'zeitfresser' ),
'twitter' => esc_html__( 'Twitter', 'zeitfresser' ),
'pinterest' => esc_html__( 'Pinterest', 'zeitfresser' ),
'tiktok' => esc_html__( 'TikTok', 'zeitfresser' ),
'mastodon' => esc_html__( 'Mastodon', 'zeitfresser' ),
'github' => esc_html__( 'GitHub', 'zeitfresser' ),
'matrix' => esc_html__( 'Matrix.org', 'zeitfresser' ),
);
}
}
add_action( 'customize_register', 'zeitfresser_social_links' );
/**
* Register social link customizer controls.
*
* @param WP_Customize_Manager $wp_customize Customizer manager.
* @return void
*/
function zeitfresser_social_links( $wp_customize ) {
$social_links = zeitfresser_get_social_links();
$social_links_title = '<hr/><h2>' . esc_html__( 'Social Links:', 'zeitfresser' ) . '</h2>';
$wp_customize->add_setting(
'social_links_title',
array(
'default' => '',
'sanitize_callback' => 'wp_kses_post',
)
);
$wp_customize->add_control(
new Daisy_Blog_Custom_Text(
$wp_customize,
'social_links_title',
array(
'section' => 'daisy_blog_general_customization_section',
'label' => $social_links_title,
'priority' => 20,
)
)
);
$social_priority = 21;
foreach ( $social_links as $social_key => $social_label ) {
$wp_customize->add_setting(
'social_links_' . $social_key,
array(
'default' => zeitfresser_get_social_link_default( $social_key ),
'sanitize_callback' => 'esc_url_raw',
)
);
$wp_customize->add_control(
'social_links_' . $social_key,
array(
'label' => $social_label,
'section' => 'daisy_blog_general_customization_section',
'type' => 'url',
'priority' => $social_priority++,
)
);
}
}
-99
View File
@@ -1,99 +0,0 @@
<?php
/**
* TOC related general settings.
*/
add_action( 'customize_register', 'zeitfresser_register_article_toc_options' );
/**
* Sanitize the minimum heading threshold for the article TOC.
*
* @param mixed $value Submitted control value.
* @return int
*/
function zeitfresser_sanitize_article_toc_min_headlines( $value ) {
$value = absint( $value );
if ( $value < 1 ) {
$value = zeitfresser_get_default_article_toc_min_headlines();
}
return min( 50, $value );
}
/**
* Register article TOC options in General Options.
*
* @param WP_Customize_Manager $wp_customize Customizer manager.
* @return void
*/
function zeitfresser_register_article_toc_options( $wp_customize ) {
$article_toc_title = '<hr/><h2>' . esc_html__( 'Article TOC:', 'zeitfresser' ) . '</h2>';
$wp_customize->add_setting(
'article_toc_options_heading',
array(
'sanitize_callback' => 'wp_kses_post',
)
);
$wp_customize->add_control(
new Daisy_Blog_Custom_Text(
$wp_customize,
'article_toc_options_heading',
array(
'section' => 'daisy_blog_general_customization_section',
'label' => $article_toc_title,
'priority' => 12,
)
)
);
$wp_customize->add_setting(
'show_article_toc',
array(
'sanitize_callback' => 'zeitfresser_sanitize_checkbox',
'default' => zeitfresser_get_default_show_article_toc(),
)
);
$wp_customize->add_control(
new Graphthemes_Toggle_Control(
$wp_customize,
'show_article_toc',
array(
'settings' => 'show_article_toc',
'section' => 'daisy_blog_general_customization_section',
'label' => esc_html__( 'Show Article TOC', 'zeitfresser' ),
'description' => esc_html__( 'Enable the floating article TOC on single posts. Default: enabled.', 'zeitfresser' ),
'type' => 'toggle',
'priority' => 13,
)
)
);
$wp_customize->add_setting(
'article_toc_min_headlines',
array(
'sanitize_callback' => 'zeitfresser_sanitize_article_toc_min_headlines',
'default' => zeitfresser_get_default_article_toc_min_headlines(),
)
);
$wp_customize->add_control(
'article_toc_min_headlines',
array(
'settings' => 'article_toc_min_headlines',
'type' => 'number',
'section' => 'daisy_blog_general_customization_section',
'label' => esc_html__( 'Number of Headlines to Start TOC', 'zeitfresser' ),
'description' => esc_html__( 'The article TOC is shown only when this number of headings or more is found. Default: 3.', 'zeitfresser' ),
'priority' => 14,
'input_attrs' => array(
'min' => 1,
'max' => 50,
'step' => 1,
),
)
);
}
-18
View File
@@ -1,18 +0,0 @@
#customize-controls .control-section-button .accordion-section-title:hover,
#customize-controls .control-section-button .accordion-section-title:focus {
background-color: #fff;
}
.control-section-button .accordion-section-title .button {
margin-top: -4px;
font-weight: 400;
margin-left: 8px;
background: #e45157;
border: none;
color: #fff;
}
.rtl .control-section-button .accordion-section-title .button {
margin-left: 0;
margin-right: 8px;
}
-15
View File
@@ -1,15 +0,0 @@
(function (api) {
if (api != null) {
// Extends our custom "upgrade-to-pro" section.
api.sectionConstructor["button"] = api.Section.extend({
// No events for this type of section.
attachEvents: function () {},
// Always make the section active.
isContextuallyActive: function () {
return true;
},
});
} else {
}
})(wp.customize);
@@ -1,74 +0,0 @@
<?php
/**
* Pro customizer section.
*
* @since 1.0.0
* @access public
*/
class Daisy_Blog_Button_Control extends WP_Customize_Section {
/**
* The type of customize section being rendered.
*
* @since 1.0.0
* @access public
* @var string
*/
public $type = 'button';
/**
* Custom button text to output.
*
* @since 1.0.0
* @access public
* @var string
*/
public $pro_text = '';
/**
* Custom pro button URL.
*
* @since 1.0.0
* @access public
* @var string
*/
public $pro_url = '';
/**
* Add custom parameters to pass to the JS via JSON.
*
* @since 1.0.0
* @access public
* @return void
*/
public function json() {
$json = parent::json();
$json['pro_text'] = $this->pro_text;
$json['pro_url'] = esc_url( $this->pro_url );
return $json;
}
/**
* Outputs the Underscore.js template.
*
* @since 1.0.0
* @access public
* @return void
*/
protected function render_template() { ?>
<li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }} cannot-expand">
<h3 class="accordion-section-title">
{{ data.title }}
<# if ( data.pro_text && data.pro_url ) { #>
<a href="{{ data.pro_url }}" class="button button-secondary alignright" target="_blank">{{ data.pro_text }}</a>
<# } #>
</h3>
</li>
<?php }
}
@@ -1,24 +0,0 @@
<?php
if( class_exists( 'WP_Customize_control' ) ){
class Daisy_Blog_Custom_Text extends WP_Customize_Control
{
/**
* Render the content on the theme customizer page
*/
public function render_content()
{
?>
<label>
<strong class="customize-text_editor"><?php echo wp_kses_post( $this->label ); ?></strong>
<br />
<span class="customize-text_editor_desc">
<?php echo wp_kses_post( $this->description ); ?>
</span>
</label>
<?php
}
}//editor close
}//class close
@@ -1,86 +0,0 @@
<?php
/**
* Customizer Control: multi-check
*
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'Graphthemes_Multi_Check_Control' ) ) {
/**
* Adds a multicheck control.
*/
class Graphthemes_Multi_Check_Control extends Wp_Customize_Control {
/**
* The control type.
*
* @access public
* @var string
*/
public $type = 'multi-check';
public $tooltip = '';
public function to_json() {
parent::to_json();
if ( isset( $this->default ) ) {
$this->json['default'] = $this->default;
} else {
$this->json['default'] = $this->setting->default;
}
$this->json['value'] = $this->value();
$this->json['choices'] = $this->choices;
$this->json['link'] = $this->get_link();
$this->json['id'] = $this->id;
$this->json['tooltip'] = $this->tooltip;
$this->json['inputAttrs'] = '';
foreach ( $this->input_attrs as $attr => $value ) {
$this->json['inputAttrs'] .= $attr . '="' . esc_attr( $value ) . '" ';
}
}
public function enqueue() {
wp_enqueue_script( 'graphthemes-multi-check', get_template_directory_uri() . '/inc/blocks/includes/multicheck/multi-check.js', array( 'jquery' ), false, true );
}
protected function content_template() {
?>
<# if ( ! data.choices ) { return; } #>
<# if ( data.tooltip ) { #>
<a href="#" class="tooltip hint--left" data-hint="{{ data.tooltip }}"><span class='dashicons dashicons-info'></span></a>
<# } #>
<# if ( data.label ) { #>
<span class="customize-control-title">{{ data.label }}</span>
<# } #>
<# if ( data.description ) { #>
<span class="description customize-control-description">{{{ data.description }}}</span>
<# } #>
<ul>
<# for ( key in data.choices ) { #>
<li>
<label>
<input type="checkbox" value="{{ key }}"<# if ( _.contains( data.value, key ) ) { #> checked<# } #> />
{{ data.choices[ key ] }}
</label>
</li>
<# } #>
</ul>
<?php
}
}
}
@@ -1,30 +0,0 @@
wp.customize.controlConstructor['multi-check'] = wp.customize.Control.extend({
// When we're finished loading continue processing.
ready: function() {
'use strict';
var control = this;
// Save the value
control.container.on( 'change', 'input', function() {
var value = [],
i = 0;
// Build the value as an object using the sub-values from individual checkboxes.
jQuery.each( control.params.choices, function( key, subValue ) {
if ( control.container.find( 'input[value="' + key + '"]' ).is( ':checked' ) ) {
value[ i ] = key;
i++;
}
});
// Update the value in the customizer.
control.setting.set( value );
});
}
});
-17
View File
@@ -1,17 +0,0 @@
<?php
add_action( 'customize_register', 'zeitfresser_register_custom_controls' );
function zeitfresser_register_custom_controls( $wp_customize ) {
require_once ZEITFRESSER_BLOCKS_DIR_PATH . '/includes/button/class-button-control.php';
$wp_customize->register_control_type( 'Daisy_Blog_Button_Control' );
require_once ZEITFRESSER_BLOCKS_DIR_PATH . '/includes//custom-html/class-custom-html.php';
require_once ZEITFRESSER_BLOCKS_DIR_PATH . '/includes/toggle/class-toggle-control.php';
$wp_customize->register_control_type( 'Graphthemes_Toggle_Control' );
require_once ZEITFRESSER_BLOCKS_DIR_PATH . '/includes//multicheck/class-multi-check-control.php';
$wp_customize->register_control_type( 'Graphthemes_Multi_Check_Control' );
}
-50
View File
@@ -1,50 +0,0 @@
<?php
/* Sanitize Google Fonts */
function zeitfresser_sanitize_google_fonts( $input, $setting ) {
// Get list of choices from the control associated with the setting.
$choices = $setting->manager->get_control( $setting->id )->choices;
// If the input is a valid key, return it; otherwise, return the default.
return ( array_key_exists( $input, $choices ) ? $input : $setting->default );
}
function zeitfresser_sanitize_float( $input ) {
return filter_var($input, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
}
//checkbox sanitization function
function zeitfresser_sanitize_checkbox( $input ) {
//returns true if checkbox is checked
return ( ( isset( $input ) && true == $input ) ? true : false );
}
function zeitfresser_sanitize_select( $input, $setting ) {
// Ensure input is a slug.
$input = sanitize_key( $input );
// Get list of choices from the control associated with the setting.
$choices = $setting->manager->get_control( $setting->id )->choices;
// If the input is a valid key, return it; otherwise, return the default.
return ( array_key_exists( $input, $choices ) ? $input : $setting->default );
}
function zeitfresser_sanitize_array( $value ){
if ( is_array( $value ) ) {
foreach ( $value as $key => $subvalue ) {
$value[ $key ] = esc_attr( $subvalue );
}
return $value;
}
return esc_attr( $value );
}
@@ -1,56 +0,0 @@
<script type="text/javascript">
var fb = '<?php echo esc_html__( "Facebook", 'zeitfresser' ); ?>';
var twitter = '<?php echo esc_html__( "Twitter", 'zeitfresser' ); ?>';
var pinterest = '<?php echo esc_html__( "Pinterest", 'zeitfresser' ); ?>';
var linkedin = '<?php echo esc_html__( "Linkedin", 'zeitfresser' ); ?>';
</script>
<?php
$url = urlencode(get_the_permalink());
$title = html_entity_decode(get_the_title(), ENT_COMPAT, 'UTF-8');
$media = urlencode(get_the_post_thumbnail_url(get_the_ID(), 'full'));
$twitter_id = get_theme_mod('twitter_id');
$social_share = $args;
?>
<ul class="list-inline">
<li><?php esc_html_e( "Share", 'zeitfresser' ); ?>:</li>
<?php if( in_array( 'facebook', $social_share ) ) { ?>
<li><a href="https://www.facebook.com/sharer/sharer.php?u=<?php echo $url; ?>" onclick="return ! window.open( this.href, fb, 'width=500, height=500' )">
<svg version="1.1" viewBox="0 0 56.693 56.693" width="56.693px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M40.43,21.739h-7.645v-5.014c0-1.883,1.248-2.322,2.127-2.322c0.877,0,5.395,0,5.395,0V6.125l-7.43-0.029 c-8.248,0-10.125,6.174-10.125,10.125v5.518h-4.77v8.53h4.77c0,10.947,0,24.137,0,24.137h10.033c0,0,0-13.32,0-24.137h6.77 L40.43,21.739z"/></svg></a></li>
<?php } ?>
<?php if( in_array( 'twitter', $social_share ) ) { ?>
<li><a href="https://twitter.com/intent/tweet?text=<?php echo $title; ?>&amp;url=<?php echo $url; ?>&amp;via=<?php echo esc_html( $twitter_id ); ?>" onclick="return ! window.open( this.href, twitter, 'width=500, height=500' )">
<svg version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M492,109.5c-17.4,7.7-36,12.9-55.6,15.3c20-12,35.4-31,42.6-53.6c-18.7,11.1-39.4,19.2-61.5,23.5 C399.8,75.8,374.6,64,346.8,64c-53.5,0-96.8,43.4-96.8,96.9c0,7.6,0.8,15,2.5,22.1C172,179,100.6,140.4,52.9,81.7 c-8.3,14.3-13.1,31-13.1,48.7c0,33.6,17.1,63.3,43.1,80.7C67,210.7,52,206.3,39,199c0,0.4,0,0.8,0,1.2c0,47,33.4,86.1,77.7,95 c-8.1,2.2-16.7,3.4-25.5,3.4c-6.2,0-12.3-0.6-18.2-1.8c12.3,38.5,48.1,66.5,90.5,67.3c-33.1,26-74.9,41.5-120.3,41.5 c-7.8,0-15.5-0.5-23.1-1.4C62.9,432,113.8,448,168.4,448C346.6,448,444,300.3,444,172.2c0-4.2-0.1-8.4-0.3-12.5 C462.6,146,479,128.9,492,109.5z"/></svg>
</a>
</li>
<?php } ?>
<?php if( in_array( 'pinterest', $social_share ) ) { ?>
<li><a href="http://pinterest.com/pin/create/button/?url=<?php echo $url; ?>&amp;media=<?php echo $media; ?>&amp;description=<?php echo esc_html( $title ); ?>" onclick="return ! window.open( this.href, pinterest, 'width=500, height=500' )">
<svg viewBox="0 0 24 24" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><path d="M12.486,4.771c-4.23,0-6.363,3.033-6.363,5.562c0,1.533,0.581,2.894,1.823,3.401c0.205,0.084,0.387,0.004,0.446-0.221 c0.041-0.157,0.138-0.553,0.182-0.717c0.061-0.221,0.037-0.3-0.127-0.495c-0.359-0.422-0.588-0.972-0.588-1.747 c0-2.25,1.683-4.264,4.384-4.264c2.392,0,3.706,1.463,3.706,3.412c0,2.568-1.137,4.734-2.824,4.734 c-0.932,0-1.629-0.77-1.405-1.715c0.268-1.13,0.786-2.347,0.786-3.16c0-0.729-0.392-1.336-1.2-1.336 c-0.952,0-1.718,0.984-1.718,2.304c0,0.841,0.286,1.409,0.286,1.409s-0.976,4.129-1.146,4.852c-0.34,1.44-0.051,3.206-0.025,3.385 c0.013,0.104,0.149,0.131,0.21,0.051c0.088-0.115,1.223-1.517,1.607-2.915c0.111-0.396,0.627-2.445,0.627-2.445 c0.311,0.589,1.213,1.108,2.175,1.108c2.863,0,4.804-2.608,4.804-6.103C18.123,7.231,15.886,4.771,12.486,4.771z"/></g></svg>
</a>
</li>
<?php } ?>
<?php if( in_array( 'linkedin', $social_share ) ) { ?>
<li><a href="https://www.linkedin.com/shareArticle?mini=true&url=<?php echo $url; ?>&title=<?php echo esc_html( $title ); ?>" onclick="return ! window.open( this.href, linkedin, 'width=500, height=500' )">
<svg baseProfile="tiny" version="1.2" viewBox="0 0 24 24" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><path d="M8,19H5V9h3V19z M19,19h-3v-5.342c0-1.392-0.496-2.085-1.479-2.085c-0.779,0-1.273,0.388-1.521,1.165C13,14,13,19,13,19h-3 c0,0,0.04-9,0-10h2.368l0.183,2h0.062c0.615-1,1.598-1.678,2.946-1.678c1.025,0,1.854,0.285,2.487,1.001 C18.683,11.04,19,12.002,19,13.353V19z"/></g><g><ellipse cx="6.5" cy="6.5" rx="1.55" ry="1.5"/></g></svg>
</a>
</li>
<?php } ?>
<?php if( in_array( 'email', $social_share ) ) { ?>
<li><a href="mailto:?subject=<?php echo esc_attr( $title ); ?>&body=<?php echo $title . " " . $url; ?>" target="_blank">
<svg version="1.1" viewBox="0 0 100.354 100.352" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M93.09,76.224c0.047-0.145,0.079-0.298,0.079-0.459V22.638c0-0.162-0.032-0.316-0.08-0.462 c-0.007-0.02-0.011-0.04-0.019-0.06c-0.064-0.171-0.158-0.325-0.276-0.46c-0.008-0.009-0.009-0.02-0.017-0.029 c-0.005-0.005-0.011-0.007-0.016-0.012c-0.126-0.134-0.275-0.242-0.442-0.323c-0.013-0.006-0.023-0.014-0.036-0.02 c-0.158-0.071-0.33-0.111-0.511-0.123c-0.018-0.001-0.035-0.005-0.053-0.005c-0.017-0.001-0.032-0.005-0.049-0.005H8.465 c-0.017,0-0.033,0.004-0.05,0.005c-0.016,0.001-0.032,0.004-0.048,0.005c-0.183,0.012-0.358,0.053-0.518,0.125 c-0.01,0.004-0.018,0.011-0.028,0.015c-0.17,0.081-0.321,0.191-0.448,0.327c-0.005,0.005-0.011,0.006-0.016,0.011 c-0.008,0.008-0.009,0.019-0.017,0.028c-0.118,0.135-0.213,0.29-0.277,0.461c-0.008,0.02-0.012,0.04-0.019,0.061 c-0.048,0.146-0.08,0.3-0.08,0.462v53.128c0,0.164,0.033,0.32,0.082,0.468c0.007,0.02,0.011,0.039,0.018,0.059 c0.065,0.172,0.161,0.327,0.28,0.462c0.007,0.008,0.009,0.018,0.016,0.026c0.006,0.007,0.014,0.011,0.021,0.018 c0.049,0.051,0.103,0.096,0.159,0.14c0.025,0.019,0.047,0.042,0.073,0.06c0.066,0.046,0.137,0.083,0.21,0.117 c0.018,0.008,0.034,0.021,0.052,0.028c0.181,0.077,0.38,0.121,0.589,0.121h83.204c0.209,0,0.408-0.043,0.589-0.121 c0.028-0.012,0.054-0.03,0.081-0.044c0.062-0.031,0.124-0.063,0.181-0.102c0.03-0.021,0.057-0.048,0.086-0.071 c0.051-0.041,0.101-0.082,0.145-0.129c0.008-0.008,0.017-0.014,0.025-0.022c0.008-0.009,0.01-0.021,0.018-0.03 c0.117-0.134,0.211-0.288,0.275-0.458C93.078,76.267,93.083,76.246,93.09,76.224z M9.965,26.04l25.247,23.061L9.965,72.346V26.04z M61.711,47.971c-0.104,0.068-0.214,0.125-0.301,0.221c-0.033,0.036-0.044,0.083-0.073,0.121l-11.27,10.294L12.331,24.138h75.472 L61.711,47.971z M37.436,51.132l11.619,10.613c0.287,0.262,0.649,0.393,1.012,0.393s0.725-0.131,1.011-0.393l11.475-10.481 l25.243,23.002H12.309L37.436,51.132z M64.778,49.232L90.169,26.04v46.33L64.778,49.232z"/></svg></a>
</li>
<?php } ?>
</ul>
@@ -1,65 +0,0 @@
<?php
/**
* Customizer Control: toggle.
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'Graphthemes_Toggle_Control' ) ) {
/**
* Toggle control (modified checkbox).
*/
class Graphthemes_Toggle_Control extends Wp_Customize_Control {
public $type = 'toggle';
public $tooltip = '';
public function to_json() {
parent::to_json();
if ( isset( $this->default ) ) {
$this->json['default'] = $this->default;
} else {
$this->json['default'] = $this->setting->default;
}
$this->json['value'] = $this->value();
$this->json['link'] = $this->get_link();
$this->json['id'] = $this->id;
$this->json['tooltip'] = $this->tooltip;
$this->json['inputAttrs'] = '';
foreach ( $this->input_attrs as $attr => $value ) {
$this->json['inputAttrs'] .= $attr . '="' . esc_attr( $value ) . '" ';
}
}
public function enqueue() {
wp_enqueue_style( 'graphthemes-toggle', get_template_directory_uri() . '/inc/blocks/includes/toggle/toggle.css', null );
wp_enqueue_script( 'graphthemes-toggle', get_template_directory_uri() . '/inc/blocks/includes/toggle/toggle.js', array( 'jquery' ), false, true ); //for toggle
}
protected function content_template() {
?>
<# if ( data.tooltip ) { #>
<a href="#" class="tooltip hint--left" data-hint="{{ data.tooltip }}"><span class='dashicons dashicons-info'></span></a>
<# } #>
<label for="toggle_{{ data.id }}">
<span class="customize-control-title">
{{{ data.label }}}
</span>
<# if ( data.description ) { #>
<span class="description customize-control-description">{{{ data.description }}}</span>
<# } #>
<input {{{ data.inputAttrs }}} name="toggle_{{ data.id }}" id="toggle_{{ data.id }}" type="checkbox" value="{{ data.value }}" {{{ data.link }}}<# if ( '1' == data.value ) { #> checked<# } #> hidden />
<span class="switch"></span>
</label>
<?php
}
}
}
-64
View File
@@ -1,64 +0,0 @@
.customize-control-toggle label {
display: flex;
flex-wrap: wrap;
}
.customize-control-toggle label .customize-control-title {
width: calc(100% - 55px);
}
.customize-control-toggle label .description {
order: 99;
}
.customize-control-toggle input[type="checkbox"] {
display: none;
}
.customize-control-toggle .switch {
border: 1px solid rgba(0, 0, 0, 0.1);
display: inline-block;
width: 35px;
height: 12px;
border-radius: 8px;
background: #ccc;
vertical-align: middle;
position: relative;
cursor: pointer;
user-select: none;
transition: background 350ms ease;
}
.customize-control-toggle .switch:before, .customize-control-toggle .switch:after {
content: "";
display: block;
width: 20px;
height: 20px;
border-radius: 50%;
position: absolute;
top: 50%;
left: -3px;
transition: all 350ms cubic-bezier(0, 0.95, 0.38, 0.98), background 150ms ease;
}
.customize-control-toggle .switch:before {
background: rgba(0, 0, 0, 0.2);
transform: translate3d(0, -50%, 0) scale(0);
}
.customize-control-toggle .switch:after {
background: #999;
border: 1px solid rgba(0, 0, 0, 0.1);
transform: translate3d(0, -50%, 0);
}
.customize-control-toggle .switch:active:before {
transform: translate3d(0, -50%, 0) scale(3);
}
.customize-control-toggle input:checked + .switch {
background: rgba(52, 152, 222, 0.3);
}
.customize-control-toggle input:checked + .switch:before {
background: rgba(52, 152, 222, 0.075);
transform: translate3d(100%, -50%, 0) scale(1);
}
.customize-control-toggle input:checked + .switch:after {
background: #3498DE;
transform: translate3d(100%, -50%, 0);
}
.customize-control-toggle input:checked + .switch:active:before {
background: rgba(52, 152, 222, 0.075);
transform: translate3d(100%, -50%, 0) scale(3);
}
-18
View File
@@ -1,18 +0,0 @@
wp.customize.controlConstructor['toggle'] = wp.customize.Control.extend({
ready: function() {
'use strict';
var control = this,
checkboxValue = control.setting._value;
// Save the value
this.container.on( 'change', 'input', function() {
checkboxValue = ( jQuery( this ).is( ':checked' ) ) ? true : false;
control.setting.set( checkboxValue );
});
}
});
@@ -1,61 +0,0 @@
<?php
/* Default Post Detail Author*/
function zeitfresser_get_default_post_detail_author() {
return false;
}
/* Default Post Detail Category*/
function zeitfresser_get_default_post_detail_category() {
return false;
}
/* Default Post Detail Comment*/
function zeitfresser_get_default_post_detail_comment() {
return true;
}
/* Default Post Detail Date*/
function zeitfresser_get_default_post_detail_date() {
return true;
}
/* Default Post Detail Featured Image*/
function zeitfresser_get_default_post_detail_featured_image() {
return false;
}
/* Default Post Detail Tag*/
function zeitfresser_get_default_post_detail_featured_image_size() {
return esc_html__( 'large', 'zeitfresser' );
}
/* Default Post Detail Tag*/
function zeitfresser_get_default_post_detail_tag() {
return false;
}
/* Default Post Detail Social Share */
function zeitfresser_get_default_post_detail_social_share() {
return false;
}
/* Default Post Detail Social Share Options*/
function zeitfresser_get_default_post_detail_social_share_options() {
return array();
}
/* Default Post Detail Author Block Options */
function zeitfresser_get_default_post_detail_author_block() {
return false;
}
/* Default Post Detail Related Articles Options*/
function zeitfresser_get_default_post_detail_related_articles() {
return true;
}
/* Default Post Detail Related Articles Title */
function zeitfresser_get_default_post_detail_related_articles_title() {
return esc_html__( "Related Articles", 'zeitfresser' );
}
-9
View File
@@ -1,9 +0,0 @@
<?php
/**
* Post detail defaults only.
*
* The frontend still uses these defaults, but the related controls are intentionally
* hidden to keep the customizer lean for the Zeitfresser setup.
*/
require dirname( __FILE__ ) . '/default-post-detail.php';
@@ -1,61 +0,0 @@
<?php
/* Default Post Snippet Author*/
function zeitfresser_get_default_post_snippet_author() {
return false;
}
/* Default Post Snippet Category*/
function zeitfresser_get_default_post_snippet_category() {
return false;
}
/* Default Post Snippet Comment*/
function zeitfresser_get_default_post_snippet_comment() {
return false;
}
/* Default Post Snippet Date*/
function zeitfresser_get_default_post_snippet_date() {
return false;
}
/* Default Post Snippet Featured Image*/
function zeitfresser_get_default_post_snippet_featured_image() {
return true;
}
/* Default Post Snippet Tag*/
function zeitfresser_get_default_post_snippet_featured_image_size() {
return esc_html__( 'thumbnail', 'zeitfresser' );
}
/* Default Post Snippet Tag*/
function zeitfresser_get_default_post_snippet_tag() {
return false;
}
/* Default Post Snippet Excerpt Szie */
function zeitfresser_get_default_post_snippet_excerpt_size() {
return 20;
}
/* Default Post Snippet Read More */
function zeitfresser_get_default_post_snippet_show_hide_read_more() {
return false;
}
/* Default Post Snippet Read More Text */
function zeitfresser_get_default_post_snippet_read_more_text() {
return esc_html__( "Read More", 'zeitfresser' );
}
/* Default Post Snippet Social Share */
function zeitfresser_get_default_post_snippet_social_share() {
return false;
}
/* Default Post Snippet Social Share Options*/
function zeitfresser_get_default_post_snippet_social_share_options() {
return array();
}
@@ -1,23 +0,0 @@
<?php
add_action( 'customize_register', 'zeitfresser_post_snippet_excerpt_size' );
function zeitfresser_post_snippet_excerpt_size( $wp_customize ) {
$wp_customize->add_setting( 'post_snippet_excerpt_size', array(
'sanitize_callback' => 'absint',
'default' => zeitfresser_get_default_post_snippet_excerpt_size()
) );
$wp_customize->add_control( 'post_snippet_excerpt_size', array(
'settings' => 'post_snippet_excerpt_size',
'type' => 'number',
'section' => 'daisy_blog_general_customization_section',
'label' => esc_html__( 'Excerpt Size', 'zeitfresser' ),
'priority' => 11,
'input_attrs' => array(
'min' => 5,
'max' => 80,
),
) );
}
-25
View File
@@ -1,25 +0,0 @@
<?php
/**
* Post snippet customizer section.
*/
add_action( 'customize_register', 'zeitfresser_register_post_snippet_customization_section' );
/**
* Register the post snippet section.
*
* @param WP_Customize_Manager $wp_customize Customizer manager.
* @return void
*/
function zeitfresser_register_post_snippet_customization_section( $wp_customize ) {
$wp_customize->add_section(
'daisy_blog_post_snippet_customization_section',
array(
'title' => esc_html__( 'Post Snippet', 'zeitfresser' ),
'priority' => 22,
)
);
}
require dirname( __FILE__ ) . '/default-post-snippet.php';
require dirname( __FILE__ ) . '/excerpt/excerpt.php';
@@ -1,11 +0,0 @@
<?php
/* Default Site Title Show Hide Option */
function zeitfresser_get_default_site_title_show_hide() {
return true;
}
/* Default Site Tagline Show Hide Option */
function zeitfresser_get_default_site_tagline_show_hide() {
return true;
}
@@ -1,9 +0,0 @@
<?php
/* Default Site Identity Settings */
require dirname( __FILE__ ) . '/default-site-identity.php';
require dirname( __FILE__ ) . '/site-title/site-title.php';
require dirname( __FILE__ ) . '/site-tagline/site-tagline.php';
@@ -1,18 +0,0 @@
<?php
add_action( 'customize_register', 'zeitfresser_show_hide_site_tagline' );
function zeitfresser_show_hide_site_tagline( $wp_customize ) {
$wp_customize->add_setting( 'show_hide_site_tagline', array(
'sanitize_callback' => 'zeitfresser_sanitize_checkbox',
'default' => zeitfresser_get_default_site_tagline_show_hide()
) );
$wp_customize->add_control( new Graphthemes_Toggle_Control( $wp_customize, 'show_hide_site_tagline', array(
'label' => esc_html__( 'Show/Hide Site Tagline','zeitfresser' ),
'section' => 'title_tagline',
'settings' => 'show_hide_site_tagline',
'type'=> 'toggle',
) ) );
}
@@ -1,18 +0,0 @@
<?php
add_action( 'customize_register', 'zeitfresser_show_hide_site_title' );
function zeitfresser_show_hide_site_title( $wp_customize ) {
$wp_customize->add_setting( 'show_hide_site_title', array(
'sanitize_callback' => 'zeitfresser_sanitize_checkbox',
'default' => zeitfresser_get_default_site_title_show_hide(),
) );
$wp_customize->add_control( new Graphthemes_Toggle_Control( $wp_customize, 'show_hide_site_title', array(
'label' => esc_html__( 'Show/Hide Site Title','zeitfresser' ),
'section' => 'title_tagline',
'settings' => 'show_hide_site_title',
'type'=> 'toggle',
) ) );
}
-238
View File
@@ -1,238 +0,0 @@
<?php
/**
* daisy blog Theme Customizer
*
* @package zeitfresser
*/
/**
* Add postMessage support for site title and description for the Theme Customizer.
*
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
* @return void
*/
function zeitfresser_customize_register( $wp_customize ) {
$wp_customize->get_setting( 'blogname' )->transport = 'postMessage';
$wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage';
$wp_customize->get_setting( 'header_textcolor' )->transport = 'postMessage';
if ( isset( $wp_customize->selective_refresh ) ) {
$wp_customize->selective_refresh->add_partial(
'blogname',
array(
'selector' => '.site-title a',
'render_callback' => 'zeitfresser_customize_partial_blogname',
)
);
$wp_customize->selective_refresh->add_partial(
'blogdescription',
array(
'selector' => '.site-description',
'render_callback' => 'zeitfresser_customize_partial_blogdescription',
)
);
}
/**
* Performance Tools section
*/
$wp_customize->add_section(
'ztfr_performance_tools',
array(
'title' => 'Performance Tools Settings',
'priority' => 160,
)
);
/**
* Auto optimize uploaded images.
*/
$wp_customize->add_setting(
'ztfr_auto_optimize',
array(
'default' => true,
'sanitize_callback' => 'wp_validate_boolean',
)
);
$wp_customize->add_control(
'ztfr_auto_optimize',
array(
'type' => 'checkbox',
'section' => 'ztfr_performance_tools',
'label' => 'Auto Optimize Pictures on Upload',
'description' => 'Automatically converts uploaded images to modern formats (AVIF/WebP) for improved performance.',
)
);
/**
* Auto delete originals after successful optimization.
*/
$wp_customize->add_setting(
'ztfr_auto_delete',
array(
'default' => false,
'sanitize_callback' => 'wp_validate_boolean',
)
);
$wp_customize->add_control(
'ztfr_auto_delete',
array(
'type' => 'checkbox',
'section' => 'ztfr_performance_tools',
'label' => 'Auto Delete Original Pictures on Upload',
'description' => 'Automatically deletes original images after optimization. ⚠️ This action cannot be undone.',
)
);
}
add_action( 'customize_register', 'zeitfresser_customize_register' );
/**
* Render the site title for the selective refresh partial.
*
* @return void
*/
function zeitfresser_customize_partial_blogname() {
bloginfo( 'name' );
}
/**
* Render the site tagline for the selective refresh partial.
*
* @return void
*/
function zeitfresser_customize_partial_blogdescription() {
bloginfo( 'description' );
}
/**
* Bind JS handlers to make Theme Customizer preview reload changes asynchronously.
*
* @return void
*/
function zeitfresser_customize_preview_js() {
wp_enqueue_script(
'zeitfresser-customizer',
get_template_directory_uri() . '/js/customizer.js',
array( 'customize-preview' ),
ZEITFRESSER_VERSION,
true
);
}
add_action( 'customize_preview_init', 'zeitfresser_customize_preview_js' );
/**
* Add dependency logic and status box for Performance Tools settings.
*
* @return void
*/
function zeitfresser_customize_controls_dependency_js() {
?>
<script>
(function() {
function getOptimizeInput() {
return document.querySelector('#customize-control-ztfr_auto_optimize input[type="checkbox"]');
}
function getDeleteInput() {
return document.querySelector('#customize-control-ztfr_auto_delete input[type="checkbox"]');
}
function getDeleteControl() {
return document.getElementById('customize-control-ztfr_auto_delete');
}
function ensureStatusBox() {
let box = document.getElementById('ztfr-auto-status-box');
if (box) {
return box;
}
const optimizeControl = document.getElementById('customize-control-ztfr_auto_optimize');
if (!optimizeControl || !optimizeControl.parentNode) {
return null;
}
box = document.createElement('li');
box.id = 'ztfr-auto-status-box';
box.className = 'customize-control';
box.innerHTML =
'<span style="display:block;font-weight:600;margin-bottom:6px;">Current Mode</span>' +
'<span id="ztfr-auto-status-text">Checking...</span>';
optimizeControl.parentNode.insertBefore(box, optimizeControl);
return box;
}
function updateState() {
const optimizeInput = getOptimizeInput();
const deleteInput = getDeleteInput();
const deleteControl = getDeleteControl();
const statusBox = ensureStatusBox();
const statusText = document.getElementById('ztfr-auto-status-text');
if (!optimizeInput || !deleteInput || !deleteControl || !statusBox || !statusText) {
return;
}
if (!optimizeInput.checked) {
deleteInput.checked = false;
deleteInput.disabled = true;
deleteControl.style.opacity = '0.5';
statusText.textContent = '⚪ Manual Mode (no automation)';
} else {
deleteInput.disabled = false;
deleteControl.style.opacity = '1';
if (deleteInput.checked) {
statusText.textContent = '🟢 Full Auto Mode (optimize + delete)';
} else {
statusText.textContent = '🟡 Auto Optimize enabled (originals kept)';
}
}
}
function init() {
updateState();
document.addEventListener('change', function(event) {
const target = event.target;
if (
target &&
(
target.matches('#customize-control-ztfr_auto_optimize input[type="checkbox"]') ||
target.matches('#customize-control-ztfr_auto_delete input[type="checkbox"]')
)
) {
updateState();
}
});
let tries = 0;
const interval = setInterval(function() {
updateState();
tries++;
if (tries > 20) {
clearInterval(interval);
}
}, 300);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
<?php
}
add_action( 'customize_controls_enqueue_scripts', 'zeitfresser_customize_controls_dependency_js' );
+58
View File
@@ -0,0 +1,58 @@
<?php
/**
* Theme Customizer Core
*
* @package zeitfresser
*/
function zeitfresser_customize_register( $wp_customize ) {
// Live Preview support
$wp_customize->get_setting( 'blogname' )->transport = 'postMessage';
$wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage';
$wp_customize->get_setting( 'header_textcolor' )->transport = 'postMessage';
if ( isset( $wp_customize->selective_refresh ) ) {
$wp_customize->selective_refresh->add_partial(
'blogname',
array(
'selector' => '.site-title a',
'render_callback' => 'zeitfresser_customize_partial_blogname',
)
);
$wp_customize->selective_refresh->add_partial(
'blogdescription',
array(
'selector' => '.site-description',
'render_callback' => 'zeitfresser_customize_partial_blogdescription',
)
);
}
}
add_action( 'customize_register', 'zeitfresser_customize_register' );
/**
* Partial refresh helpers
*/
function zeitfresser_customize_partial_blogname() {
bloginfo( 'name' );
}
function zeitfresser_customize_partial_blogdescription() {
bloginfo( 'description' );
}
/**
* Live preview JS
*/
function zeitfresser_customize_preview_js() {
wp_enqueue_script(
'zeitfresser-customizer',
get_template_directory_uri() . '/js/customizer.js',
array( 'customize-preview' ),
ZEITFRESSER_VERSION,
true
);
}
add_action( 'customize_preview_init', 'zeitfresser_customize_preview_js' );
+152
View File
@@ -0,0 +1,152 @@
<?php
/**
* General Theme Options
*
* @package zeitfresser
*/
add_action( 'customize_register', 'zeitfresser_general_options' );
function zeitfresser_general_options( $wp_customize ) {
/**
* 🔥 Custom Heading Control
*/
if ( class_exists( 'WP_Customize_Control' ) ) {
class ZTFR_Customize_Heading_Control extends WP_Customize_Control {
public $type = 'ztfr-heading';
public function render_content() {
?>
<span style="display:block; font-weight:600; font-size:14px; margin:15px 0 5px;">
<?php echo esc_html( $this->label ); ?>
</span>
<?php
}
}
}
/**
* General Section
*/
if ( ! $wp_customize->get_section( 'ztfr_general' ) ) {
$wp_customize->add_section(
'ztfr_general',
array(
'title' => 'General Options',
'priority' => 30,
)
);
}
/**
* ------------------------
* HEADER
* ------------------------
*/
$wp_customize->add_setting( 'ztfr_header_heading', array(
'sanitize_callback' => 'sanitize_text_field',
));
$wp_customize->add_control(
new ZTFR_Customize_Heading_Control(
$wp_customize,
'ztfr_header_heading',
array(
'label' => 'Header',
'section' => 'ztfr_general',
'priority' => 1,
)
)
);
/**
* Show Site Title
*/
$wp_customize->add_setting(
'show_hide_site_title',
array(
'default' => true,
'sanitize_callback' => 'wp_validate_boolean',
)
);
$wp_customize->add_control(
'show_hide_site_title',
array(
'type' => 'checkbox',
'section' => 'ztfr_general',
'label' => 'Show Site Title',
'priority' => 2,
)
);
/**
* Show Tagline
*/
$wp_customize->add_setting(
'show_hide_site_tagline',
array(
'default' => true,
'sanitize_callback' => 'wp_validate_boolean',
)
);
$wp_customize->add_control(
'show_hide_site_tagline',
array(
'type' => 'checkbox',
'section' => 'ztfr_general',
'label' => 'Show Tagline',
'priority' => 3,
)
);
/**
* ------------------------
* GRID
* ------------------------
*/
$wp_customize->add_setting( 'ztfr_grid_heading', array(
'sanitize_callback' => 'sanitize_text_field',
));
$wp_customize->add_control(
new ZTFR_Customize_Heading_Control(
$wp_customize,
'ztfr_grid_heading',
array(
'label' => 'Grid',
'section' => 'ztfr_general',
'priority' => 20,
)
)
);
/**
* Excerpt Length
*/
$wp_customize->add_setting(
'post_snippet_excerpt_size',
array(
'default' => 25,
'sanitize_callback' => 'absint',
)
);
$wp_customize->add_control(
'post_snippet_excerpt_size',
array(
'type' => 'number',
'section' => 'ztfr_general',
'label' => 'Excerpt Length (Post Cards)',
'description' => 'Number of words shown in post previews.',
'priority' => 21,
'input_attrs' => array(
'min' => 5,
'max' => 100,
'step' => 1,
),
)
);
}
+210
View File
@@ -0,0 +1,210 @@
<?php
/**
* Image Optimizer (Settings + UI)
*
* @package zeitfresser
*/
/**
* ------------------------------------------------------------------------
* Settings
* ------------------------------------------------------------------------
*/
function zeitfresser_customize_image_optimizer_settings( $wp_customize ) {
/**
* Image Optimizer Section
*/
$wp_customize->add_section(
'ztfr_image_optimizer',
array(
'title' => 'Image Optimizer',
'priority' => 160,
)
);
/**
* Auto Optimize
*/
$wp_customize->add_setting(
'ztfr_auto_optimize',
array(
'default' => true,
'sanitize_callback' => 'wp_validate_boolean',
)
);
$wp_customize->add_control(
'ztfr_auto_optimize',
array(
'type' => 'checkbox',
'section' => 'ztfr_image_optimizer',
'label' => 'Auto Optimize Pictures on Upload',
'description' => 'Automatically converts images to AVIF/WebP.',
)
);
/**
* Auto Delete
*/
$wp_customize->add_setting(
'ztfr_auto_delete',
array(
'default' => false,
'sanitize_callback' => 'wp_validate_boolean',
)
);
$wp_customize->add_control(
'ztfr_auto_delete',
array(
'type' => 'checkbox',
'section' => 'ztfr_image_optimizer',
'label' => 'Auto Delete Original Pictures',
'description' => 'Deletes originals after optimization.',
)
);
}
add_action( 'customize_register', 'zeitfresser_customize_image_optimizer_settings' );
/**
* ------------------------------------------------------------------------
* UI Logic (JS)
* ------------------------------------------------------------------------
*/
function zeitfresser_customize_image_optimizer_ui() {
?>
<script>
(function() {
function getOptimizeInput() {
return document.querySelector('#customize-control-ztfr_auto_optimize input');
}
function getDeleteInput() {
return document.querySelector('#customize-control-ztfr_auto_delete input');
}
function getDeleteControl() {
return document.getElementById('customize-control-ztfr_auto_delete');
}
function ensureStatusBox() {
let box = document.getElementById('ztfr-auto-status-box');
if (box) return box;
const optimizeControl = document.getElementById('customize-control-ztfr_auto_optimize');
if (!optimizeControl || !optimizeControl.parentNode) return null;
box = document.createElement('li');
box.id = 'ztfr-auto-status-box';
box.className = 'customize-control';
box.innerHTML =
'<span style="display:block;font-weight:600;margin-bottom:6px;">Current Mode</span>' +
'<span id="ztfr-auto-status-text">Checking...</span>';
optimizeControl.parentNode.insertBefore(box, optimizeControl);
return box;
}
function updateState() {
const optimizeInput = getOptimizeInput();
const deleteInput = getDeleteInput();
const deleteControl = getDeleteControl();
const statusBox = ensureStatusBox();
const statusText = document.getElementById('ztfr-auto-status-text');
if (!optimizeInput || !deleteInput || !deleteControl || !statusBox || !statusText) {
return;
}
if (!optimizeInput.checked) {
deleteInput.checked = false;
deleteInput.disabled = true;
deleteControl.style.opacity = '0.5';
statusText.textContent = '⚪ Manual Mode (no automation)';
} else {
deleteInput.disabled = false;
deleteControl.style.opacity = '1';
if (deleteInput.checked) {
statusText.textContent = '🟢 Full Auto Mode (optimize + delete)';
} else {
statusText.textContent = '🟡 Auto Optimize enabled (originals kept)';
}
}
}
function init() {
let attempts = 0;
function tryInit() {
const optimize = getOptimizeInput();
const del = getDeleteInput();
if (optimize && del) {
updateState();
return;
}
if (attempts < 10) {
attempts++;
setTimeout(tryInit, 200);
}
}
tryInit();
document.addEventListener('change', function(e) {
if (
e.target &&
(
e.target.matches('#customize-control-ztfr_auto_optimize input') ||
e.target.matches('#customize-control-ztfr_auto_delete input')
)
) {
updateState();
}
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
<?php
}
add_action( 'customize_controls_enqueue_scripts', 'zeitfresser_customize_image_optimizer_ui' );
/**
* ------------------------------------------------------------------------
* UI Styles
* ------------------------------------------------------------------------
*/
function zeitfresser_customize_image_optimizer_ui_styles() {
?>
<style>
#customize-control-ztfr_auto_optimize > label,
#customize-control-ztfr_auto_delete > label {
display:flex;
align-items:flex-start;
gap:6px;
}
</style>
<?php
}
add_action(
'customize_controls_enqueue_scripts',
'zeitfresser_customize_image_optimizer_ui_styles'
);
+54
View File
@@ -0,0 +1,54 @@
<?php
/**
* Layout / Container Settings
*
* @package zeitfresser
*/
add_action( 'customize_register', 'zeitfresser_layout_options' );
function zeitfresser_layout_options( $wp_customize ) {
/**
* Container Width
*/
$wp_customize->add_setting(
'container_width',
array(
'default' => 1400,
'sanitize_callback' => 'absint',
)
);
$wp_customize->add_control(
'container_width',
array(
'type' => 'number',
'section' => 'ztfr_general',
'label' => esc_html__( 'Container Width', 'zeitfresser' ),
'description' => esc_html__( 'Maximum width of the content container in pixels.', 'zeitfresser' ),
'priority' => 22,
'input_attrs' => array(
'min' => 800,
'max' => 2000,
'step' => 10,
),
)
);
}
/**
* Apply container width via CSS variable
*/
add_action( 'wp_head', 'zeitfresser_container_width_dynamic_css' );
function zeitfresser_container_width_dynamic_css() {
$container_width = (int) get_theme_mod( 'container_width' );
if ( $container_width <= 0 ) {
$container_width = 1400;
}
echo '<style>:root{--container-width:' . esc_attr( $container_width ) . 'px;}</style>';
}
+97
View File
@@ -0,0 +1,97 @@
<?php
/**
* Social Links Customizer Options
*
* @package zeitfresser
*/
if ( ! function_exists( 'zeitfresser_get_social_links' ) ) {
function zeitfresser_get_social_links() {
return array(
'facebook' => esc_html__( 'Facebook', 'zeitfresser' ),
'instagram' => esc_html__( 'Instagram', 'zeitfresser' ),
'youtube' => esc_html__( 'YouTube', 'zeitfresser' ),
'linkedin' => esc_html__( 'LinkedIn', 'zeitfresser' ),
'twitter' => esc_html__( 'Twitter', 'zeitfresser' ),
'pinterest' => esc_html__( 'Pinterest', 'zeitfresser' ),
'tiktok' => esc_html__( 'TikTok', 'zeitfresser' ),
'mastodon' => esc_html__( 'Mastodon', 'zeitfresser' ),
'github' => esc_html__( 'GitHub', 'zeitfresser' ),
'matrix' => esc_html__( 'Matrix.org', 'zeitfresser' ),
);
}
}
add_action( 'customize_register', 'zeitfresser_social_links' );
function zeitfresser_social_links( $wp_customize ) {
/**
* 🔥 Heading Control (falls noch nicht vorhanden)
*/
if ( class_exists( 'WP_Customize_Control' ) && ! class_exists( 'ZTFR_Customize_Heading_Control' ) ) {
class ZTFR_Customize_Heading_Control extends WP_Customize_Control {
public $type = 'ztfr-heading';
public function render_content() {
?>
<span style="display:block; font-weight:600; font-size:14px; margin:15px 0 5px;">
<?php echo esc_html( $this->label ); ?>
</span>
<?php
}
}
}
/**
* ------------------------
* SOCIAL HEADING
* ------------------------
*/
$wp_customize->add_setting(
'ztfr_social_heading',
array(
'sanitize_callback' => 'sanitize_text_field',
)
);
$wp_customize->add_control(
new ZTFR_Customize_Heading_Control(
$wp_customize,
'ztfr_social_heading',
array(
'label' => esc_html__( 'Social Links', 'zeitfresser' ),
'section' => 'ztfr_general',
'priority' => 30,
)
)
);
/**
* Social URLs
*/
$social_links = zeitfresser_get_social_links();
$priority = 31;
foreach ( $social_links as $key => $label ) {
$wp_customize->add_setting(
'social_links_' . $key,
array(
'default' => '',
'sanitize_callback' => 'esc_url_raw',
)
);
$wp_customize->add_control(
'social_links_' . $key,
array(
'type' => 'url',
'section' => 'ztfr_general',
'label' => $label,
'priority' => $priority++,
)
);
}
}
+101
View File
@@ -0,0 +1,101 @@
<?php
/**
* TOC Customizer Options
*
* @package zeitfresser
*/
add_action( 'customize_register', 'zeitfresser_toc_options' );
function zeitfresser_toc_options( $wp_customize ) {
/**
* 🔥 Heading Control (falls noch nicht vorhanden)
*/
if ( class_exists( 'WP_Customize_Control' ) && ! class_exists( 'ZTFR_Customize_Heading_Control' ) ) {
class ZTFR_Customize_Heading_Control extends WP_Customize_Control {
public $type = 'ztfr-heading';
public function render_content() {
?>
<span style="display:block; font-weight:600; font-size:14px; margin:15px 0 5px;">
<?php echo esc_html( $this->label ); ?>
</span>
<?php
}
}
}
/**
* ------------------------
* TOC HEADING
* ------------------------
*/
$wp_customize->add_setting(
'ztfr_toc_heading',
array(
'sanitize_callback' => 'sanitize_text_field',
)
);
$wp_customize->add_control(
new ZTFR_Customize_Heading_Control(
$wp_customize,
'ztfr_toc_heading',
array(
'label' => esc_html__( 'TOC', 'zeitfresser' ),
'section' => 'ztfr_general',
'priority' => 10,
)
)
);
/**
* Show TOC
*/
$wp_customize->add_setting(
'show_article_toc',
array(
'default' => true,
'sanitize_callback' => 'wp_validate_boolean',
)
);
$wp_customize->add_control(
'show_article_toc',
array(
'type' => 'checkbox',
'section' => 'ztfr_general',
'label' => esc_html__( 'Show Article TOC', 'zeitfresser' ),
'description' => esc_html__( 'Enable floating TOC on single posts.', 'zeitfresser' ),
'priority' => 11,
)
);
/**
* Minimum headlines threshold
*/
$wp_customize->add_setting(
'article_toc_min_headlines',
array(
'default' => 3,
'sanitize_callback' => 'absint',
)
);
$wp_customize->add_control(
'article_toc_min_headlines',
array(
'type' => 'number',
'section' => 'ztfr_general',
'label' => esc_html__( 'Minimum Headlines', 'zeitfresser' ),
'description' => esc_html__( 'TOC appears only if this number of headings is reached.', 'zeitfresser' ),
'priority' => 12,
'input_attrs' => array(
'min' => 1,
'max' => 50,
'step' => 1,
),
)
);
}
-67
View File
@@ -1,67 +0,0 @@
<?php
/**
* Jetpack Compatibility File
*
* @link https://jetpack.com/
*
* @package zeitfresser
*/
/**
* Jetpack setup function.
*
* See: https://jetpack.com/support/infinite-scroll/
* See: https://jetpack.com/support/responsive-videos/
* See: https://jetpack.com/support/content-options/
*/
function zeitfresser_jetpack_setup() {
// Add theme support for Infinite Scroll.
add_theme_support(
'infinite-scroll',
array(
'container' => 'main',
'render' => 'zeitfresser_infinite_scroll_render',
'footer' => 'page',
)
);
// Add theme support for Responsive Videos.
add_theme_support( 'jetpack-responsive-videos' );
// Add theme support for Content Options.
add_theme_support(
'jetpack-content-options',
array(
'post-details' => array(
'stylesheet' => 'zeitfresser',
'date' => '.posted-on',
'categories' => '.cat-links',
'tags' => '.tags-links',
'author' => '.byline',
'comment' => '.comments-link',
),
'featured-images' => array(
'archive' => true,
'post' => true,
'page' => true,
),
)
);
}
add_action( 'after_setup_theme', 'zeitfresser_jetpack_setup' );
if ( ! function_exists( 'zeitfresser_infinite_scroll_render' ) ) :
/**
* Custom render function for Infinite Scroll.
*/
function zeitfresser_infinite_scroll_render() {
while ( have_posts() ) {
the_post();
if ( is_search() ) :
get_template_part( 'template-parts/content', 'search' );
else :
get_template_part( 'template-parts/content', get_post_type() );
endif;
}
}
endif;
+236
View File
@@ -0,0 +1,236 @@
<?php
/**
* Performance tweaks.
*
* @package Zeitfresser
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* ------------------------------------------------------------------------
* Defer non-critical JavaScript
* ------------------------------------------------------------------------
*
* Prevents JS from blocking page rendering.
*/
function zeitfresser_defer_scripts( $tag, $handle, $src ) {
$defer_scripts = array(
'zeitfresser-navigation',
'zeitfresser-scripts',
);
if ( in_array( $handle, $defer_scripts, true ) ) {
return str_replace( ' src=', ' defer src=', $tag );
}
return $tag;
}
add_filter( 'script_loader_tag', 'zeitfresser_defer_scripts', 10, 3 );
/**
* ------------------------------------------------------------------------
* Image Loading Optimization (LCP + Lazy Loading)
* ------------------------------------------------------------------------
*
* Ensures the first visible image loads immediately (LCP),
* while all other images are lazy-loaded for performance.
*/
function zeitfresser_optimize_image_attributes( $attr, $attachment, $size ) {
static $is_first = true;
if ( ! is_admin() ) {
if ( $is_first ) {
// First image (critical for LCP)
$attr['loading'] = 'eager';
$is_first = false;
} else {
// All other images
$attr['loading'] = 'lazy';
}
// Improve decoding performance
$attr['decoding'] = 'async';
}
return $attr;
}
add_filter( 'wp_get_attachment_image_attributes', 'zeitfresser_optimize_image_attributes', 10, 3 );
/**
* Lower the threshold for WordPress scaled originals when auto optimization is enabled.
*
* When automatic optimization is disabled, original uploads should remain untouched.
*
* @return int|false
*/
function zeitfresser_big_image_size_threshold() {
$auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
$force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
if ( ! $auto_enabled && ! $force_enabled ) {
return false;
}
return 1800;
}
add_filter( 'big_image_size_threshold', 'zeitfresser_big_image_size_threshold' );
/**
* Skip generating oversized core intermediate sizes we do not use.
*
* @param array $sizes Registered intermediate sizes.
* @return array
*/
function zeitfresser_filter_intermediate_image_sizes( $sizes ) {
$auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
$force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
if ( ! $auto_enabled && ! $force_enabled ) {
return $sizes;
}
unset(
$sizes['1536x1536'],
$sizes['2048x2048']
);
return $sizes;
}
add_filter( 'intermediate_image_sizes_advanced', 'zeitfresser_filter_intermediate_image_sizes' );
/**
* Improve attachment image attributes for layout stability and fetch priority.
*
* @param array $attr Image markup attributes.
* @param WP_Post $attachment Attachment post object.
* @param string|array $size Requested image size.
* @return array
*/
function zeitfresser_improve_attachment_dimensions( $attr, $attachment, $size ) {
if ( empty( $attr['width'] ) || empty( $attr['height'] ) ) {
$metadata = wp_get_attachment_metadata( $attachment->ID );
if ( is_array( $metadata ) && ! empty( $metadata['width'] ) && ! empty( $metadata['height'] ) ) {
if ( empty( $attr['width'] ) ) {
$attr['width'] = (int) $metadata['width'];
}
if ( empty( $attr['height'] ) ) {
$attr['height'] = (int) $metadata['height'];
}
}
}
if ( empty( $attr['fetchpriority'] ) && ! is_admin() && ! is_feed() ) {
static $did_set_high_priority = false;
if ( ! $did_set_high_priority && is_singular() ) {
$attr['fetchpriority'] = 'high';
$did_set_high_priority = true;
}
}
return $attr;
}
add_filter( 'wp_get_attachment_image_attributes', 'zeitfresser_improve_attachment_dimensions', 11, 3 );
/**
* ------------------------------------------------------------------------
* Preload critical fonts
* ------------------------------------------------------------------------
*
* Preloads only the fonts that are needed for initial rendering
* (headings + body text). This improves LCP and avoids render delays.
*/
function zeitfresser_preload_fonts() {
?>
<!-- Critical Fonts Only -->
<link rel="preload" href="<?php echo zeitfresser_asset('/fonts/oswald-400.woff2'); ?>" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="<?php echo zeitfresser_asset('/fonts/oswald-700.woff2'); ?>" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="<?php echo zeitfresser_asset('/fonts/roboto-400.woff2'); ?>" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="<?php echo zeitfresser_asset('/fonts/roboto-500.woff2'); ?>" as="font" type="font/woff2" crossorigin>
<?php
}
add_action('wp_head', 'zeitfresser_preload_fonts', 0);
/**
* Optimize font loading with preconnect
*/
add_filter( 'wp_resource_hints', function( $urls, $relation_type ) {
if ( 'preconnect' === $relation_type ) {
$urls[] = [
'href' => get_template_directory_uri(),
'crossorigin' => 'anonymous',
];
}
return $urls;
}, 10, 2 );
/**
* ------------------------------------------------------------------------
* Critical CSS (inline for faster first render)
* ------------------------------------------------------------------------
*
* We inline only the minimal CSS required for initial layout.
* This ensures the page structure renders immediately without
* waiting for the full stylesheet.
*/
function zeitfresser_inline_critical_css() {
?>
<style>
body {
margin: 0;
background: #1e1f29;
}
.container {
max-width: var(--container-width, 1140px);
margin: 0 auto;
padding: 0 70px;
}
@media (max-width: 800px) {
.container {
padding: 0 20px;
}
}
.custom-grid-view {
display: grid;
}
header.site-header {
background: var(--light-color);
}
</style>
<?php
}
add_action('wp_head', 'zeitfresser_inline_critical_css', 1);
function zeitfresser_performance_setup() {
if ( ! is_admin() ) {
wp_deregister_script( 'wp-embed' );
}
}
add_action( 'wp_enqueue_scripts', 'zeitfresser_performance_setup', 100 );
function zeitfresser_cleanup_wp_head() {
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
remove_action( 'wp_head', 'rsd_link' );
remove_action( 'wp_head', 'wlwmanifest_link' );
remove_action( 'wp_head', 'wp_generator' );
}
add_action( 'init', 'zeitfresser_cleanup_wp_head' );
+243
View File
@@ -0,0 +1,243 @@
<?php
/**
* Simplified Code Block Feature
*
* Handles:
* - Frontend assets
* - Editor assets
* - Classic Editor button
* - Gutenberg block
* - Server-side wrapping for code blocks
*
* @package Zeitfresser
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Check if current post content contains code blocks.
*
* @return bool
*/
function ztfr_has_code_block() {
if ( is_admin() ) {
return false;
}
if ( ! is_singular() ) {
return false;
}
global $post;
if ( ! isset( $post ) || empty( $post->post_content ) ) {
return false;
}
if ( has_block( 'ztfr/code-block', $post ) ) {
return true;
}
return false !== strpos( $post->post_content, '<pre' );
}
/**
* Get asset version from file modification time.
*
* @param string $relative_path Relative path inside the theme directory.
* @return string
*/
function ztfr_code_asset_version( $relative_path ) {
$file_path = get_template_directory() . $relative_path;
if ( file_exists( $file_path ) ) {
return (string) filemtime( $file_path );
}
return (string) ZEITFRESSER_VERSION;
}
/**
* Enqueue frontend assets.
*/
function ztfr_enqueue_code_assets() {
if ( is_admin() ) {
return;
}
if ( ! ztfr_has_code_block() ) {
return;
}
$code_css_version = ztfr_code_asset_version( '/assets/css/code.css' );
$prism_version = ztfr_code_asset_version( '/assets/js/prism.js' );
$code_js_version = ztfr_code_asset_version( '/assets/js/code-block.js' );
wp_enqueue_style(
'ztfr-code',
get_template_directory_uri() . '/assets/css/code.css',
[],
$code_css_version
);
wp_enqueue_script(
'ztfr-prism',
get_template_directory_uri() . '/assets/js/prism.js',
[],
$prism_version,
true
);
/**
* Disable Prism auto-highlighting.
*
* Prism auto-runs on DOM ready unless Prism.manual is set before the core
* script executes. We disable auto-run so highlighting happens only once
* in our custom script after the DOM is ready.
*/
wp_add_inline_script(
'ztfr-prism',
'window.Prism = window.Prism || {}; window.Prism.manual = true;',
'before'
);
wp_enqueue_script(
'ztfr-code-block',
get_template_directory_uri() . '/assets/js/code-block.js',
[ 'ztfr-prism' ],
$code_js_version,
true
);
}
add_action( 'wp_enqueue_scripts', 'ztfr_enqueue_code_assets' );
/**
* Enqueue block editor assets.
*/
function ztfr_enqueue_code_block_editor_assets() {
$editor_js_version = ztfr_code_asset_version( '/assets/js/editor.js' );
$code_css_version = ztfr_code_asset_version( '/assets/css/code.css' );
$editor_css_version = ztfr_code_asset_version( '/assets/css/editor.css' );
wp_enqueue_script(
'ztfr-code-editor',
get_template_directory_uri() . '/assets/js/editor.js',
[
'wp-blocks',
'wp-element',
'wp-i18n',
'wp-components',
'wp-block-editor',
],
$editor_js_version,
true
);
wp_enqueue_style(
'ztfr-code-editor-preview',
get_template_directory_uri() . '/assets/css/code.css',
[],
$code_css_version
);
wp_enqueue_style(
'ztfr-editor',
get_template_directory_uri() . '/assets/css/editor.css',
[],
$editor_css_version
);
}
add_action( 'enqueue_block_editor_assets', 'ztfr_enqueue_code_block_editor_assets' );
/**
* Register Classic Editor button only for users who can edit and use rich text.
*/
function ztfr_register_classic_code_button() {
if ( ! current_user_can( 'edit_posts' ) && ! current_user_can( 'edit_pages' ) ) {
return;
}
if ( 'true' !== get_user_option( 'rich_editing' ) ) {
return;
}
add_filter( 'mce_external_plugins', 'ztfr_add_classic_code_plugin' );
add_filter( 'mce_buttons', 'ztfr_add_classic_code_button' );
}
add_action( 'admin_init', 'ztfr_register_classic_code_button' );
/**
* Add TinyMCE plugin script.
*
* @param array $plugins TinyMCE plugins.
* @return array
*/
function ztfr_add_classic_code_plugin( $plugins ) {
$plugins['ztfr_code_block'] = get_template_directory_uri() . '/assets/js/editor.js';
return $plugins;
}
/**
* Add TinyMCE toolbar button.
*
* @param array $buttons TinyMCE buttons.
* @return array
*/
function ztfr_add_classic_code_button( $buttons ) {
$buttons[] = 'ztfr_code_block';
return $buttons;
}
/**
* Wrap code blocks server-side.
*
* This keeps Classic Editor, Gutenberg and pasted raw code blocks
* compatible with the frontend styling without runtime DOM manipulation.
*
* @param string $content Post content.
* @return string
*/
function ztfr_wrap_code_blocks( $content ) {
if ( is_admin() ) {
return $content;
}
if ( false === strpos( $content, '<pre' ) ) {
return $content;
}
$content = preg_replace_callback(
'/<pre([^>]*)><code([^>]*)>(.*?)<\/code><\/pre>/s',
function ( $matches ) {
$pre_attrs = $matches[1];
$code_attrs = $matches[2];
$code = $matches[3];
if ( false === strpos( $pre_attrs, 'language-' ) ) {
$pre_attrs .= ' class="language-yaml"';
}
if ( false === strpos( $code_attrs, 'language-' ) ) {
$code_attrs .= ' class="language-yaml"';
}
return sprintf(
'<div class="ztfr-code"><pre%s><code%s>%s</code></pre></div>',
$pre_attrs,
$code_attrs,
$code
);
},
$content
);
return $content;
}
add_filter( 'the_content', 'ztfr_wrap_code_blocks', 20 );
@@ -1,6 +1,6 @@
<?php <?php
/** /**
* Performance tools for existing media. * Image Optimizer
* *
* @package zeitfresser * @package zeitfresser
*/ */
@@ -10,18 +10,284 @@ if ( ! defined( 'ABSPATH' ) ) {
} }
/** /**
* Register admin page * ------------------------------------------------------------------------
* Upload Handling (Original File Tracking)
* ------------------------------------------------------------------------
*/ */
function zeitfresser_register_performance_tools_page() { function zeitfresser_capture_original_upload( $upload, $context ) {
add_theme_page(
'Performance Tools', if ( empty( $upload['file'] ) ) {
'Performance Tools', return $upload;
'manage_options', }
'zeitfresser-performance-tools',
'zeitfresser_render_performance_tools_page' // Store temporarily (request-scoped)
$GLOBALS['zeitfresser_last_uploaded_file'] = $upload['file'];
return $upload;
}
add_filter( 'wp_handle_upload', 'zeitfresser_capture_original_upload', 10, 2 );
/**
* Persist original file path to attachment meta
*/
function zeitfresser_store_original_file( $attachment_id ) {
if ( ! wp_attachment_is_image( $attachment_id ) ) {
return;
}
if ( empty( $GLOBALS['zeitfresser_last_uploaded_file'] ) ) {
return;
}
$file = $GLOBALS['zeitfresser_last_uploaded_file'];
// Safety: ensure file still exists
if ( ! file_exists( $file ) ) {
return;
}
// Prevent overwrite if already set
if ( get_post_meta( $attachment_id, '_zeitfresser_original_file', true ) ) {
return;
}
update_post_meta(
$attachment_id,
'_zeitfresser_original_file',
$file
); );
} }
add_action( 'admin_menu', 'zeitfresser_register_performance_tools_page' ); add_action( 'add_attachment', 'zeitfresser_store_original_file' );
/**
* Convert generated JPEG and PNG files to AVIF/WebP when enabled.
*
* Auto optimization can be disabled for uploads via Customizer.
* Manual optimization may still force conversion through a request-scoped flag.
*
* @param array $formats Output format map.
* @return array
*/
function zeitfresser_image_output_format( $formats ) {
$auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
$force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
if ( ! $auto_enabled && ! $force_enabled ) {
return $formats;
}
if ( function_exists( 'wp_image_editor_supports' ) ) {
// Prefer AVIF if supported.
if ( wp_image_editor_supports( array( 'mime_type' => 'image/avif' ) ) ) {
$formats['image/jpeg'] = 'image/avif';
$formats['image/png'] = 'image/avif';
// Fallback to WebP.
} elseif ( wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
$formats['image/jpeg'] = 'image/webp';
$formats['image/png'] = 'image/webp';
}
}
return $formats;
}
add_filter( 'image_editor_output_format', 'zeitfresser_image_output_format' );
/**
* Mark images as optimized only when optimization is actually active.
*
* @param array $metadata Attachment metadata.
* @param int $attachment_id Attachment ID.
* @return array
*/
function zeitfresser_mark_new_images_optimized( $metadata, $attachment_id ) {
if ( ! wp_attachment_is_image( $attachment_id ) ) {
return $metadata;
}
$auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
$force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
if ( ! $auto_enabled && ! $force_enabled ) {
return $metadata;
}
update_post_meta(
$attachment_id,
'_zeitfresser_media_optimized_version',
ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION
);
return $metadata;
}
add_filter( 'wp_generate_attachment_metadata', 'zeitfresser_mark_new_images_optimized', 20, 2 );
/**
* Auto Optimize Hook
*/
add_filter(
'wp_generate_attachment_metadata',
'zeitfresser_auto_optimize_on_upload',
15,
2
);
function zeitfresser_auto_optimize_on_upload( $metadata, $attachment_id ) {
// Only images
if ( ! wp_attachment_is_image( $attachment_id ) ) {
return $metadata;
}
// Feature toggle
if ( ! get_theme_mod( 'ztfr_auto_optimize', true ) ) {
return $metadata;
}
$file = get_attached_file( $attachment_id );
if ( ! $file || ! file_exists( $file ) ) {
return $metadata;
}
// DO NOT overwrite captured original
if ( ! get_post_meta( $attachment_id, '_zeitfresser_original_file', true ) ) {
update_post_meta( $attachment_id, '_zeitfresser_original_file', $file );
}
// Mark optimized (IMPORTANT: no re-trigger here)
update_post_meta(
$attachment_id,
'_zeitfresser_media_optimized_version',
ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION
);
return $metadata;
}
/**
* Auto Delete Hook
*/
add_filter(
'wp_generate_attachment_metadata',
'zeitfresser_auto_delete_original_after_upload',
30,
2
);
function zeitfresser_auto_delete_original_after_upload( $metadata, $attachment_id ) {
if ( ! wp_attachment_is_image( $attachment_id ) ) {
return $metadata;
}
$auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
$delete_enabled = get_theme_mod( 'ztfr_auto_delete', false );
if ( ! $auto_enabled || ! $delete_enabled ) {
return $metadata;
}
$original = get_post_meta(
$attachment_id,
'_zeitfresser_original_file',
true
);
if ( ! $original ) {
return $metadata;
}
$ext = strtolower( pathinfo( $original, PATHINFO_EXTENSION ) );
// Skip modern formats
if ( in_array( $ext, [ 'webp', 'avif' ], true ) ) {
update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
return $metadata;
}
// 🔥 Wenn nichts mehr existiert → fertig
if ( ! zeitfresser_original_family_exists( $attachment_id, $original ) ) {
update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
return $metadata;
}
// 🔥 HIER passiert der Fix
zeitfresser_delete_original_family_files( $attachment_id, $original );
// Final check
if ( ! zeitfresser_original_family_exists( $attachment_id, $original ) ) {
update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
}
return $metadata;
}
/**
* Keep generated image quality balanced for file size and visual fidelity.
*
* @param int $quality Proposed image quality.
* @param string $mime_type Image mime type.
* @return int
*/
function zeitfresser_image_quality( $quality, $mime_type = 'image/jpeg' ) {
$auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
$force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
if ( ! $auto_enabled && ! $force_enabled ) {
return $quality;
}
return match ($mime_type) {
'image/avif' => 50,
'image/webp' => 75,
default => 82,
};
}
add_filter( 'wp_editor_set_quality', 'zeitfresser_image_quality', 10, 2 );
/**
* Improve responsive image sizes attribute.
*
* @param string $sizes Existing sizes attribute.
* @param array $size Requested image size.
* @return string
*/
function zeitfresser_responsive_image_sizes( $sizes, $size ) {
// Single post content
if ( is_singular() ) {
return '(max-width: 768px) 100vw, (max-width: 1200px) 720px, 720px';
}
// Archive / blog overview
if ( is_home() || is_front_page() || is_archive() || is_search() ) {
return '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 400px';
}
return $sizes;
}
add_filter( 'wp_calculate_image_sizes', 'zeitfresser_responsive_image_sizes', 10, 2 );
/**
* Register admin page
*/
function zeitfresser_register_image_optimizer_page() {
add_theme_page(
'Image Optimizer',
'Image Optimizer',
'manage_options',
'zeitfresser-image-optimizer',
'zeitfresser_render_image_optimizer_page'
);
}
add_action( 'admin_menu', 'zeitfresser_register_image_optimizer_page' );
/** /**
* Count pending images * Count pending images
@@ -87,18 +353,66 @@ function zeitfresser_get_total_originals_count() {
function zeitfresser_get_remaining_originals_count() { function zeitfresser_get_remaining_originals_count() {
$query = new WP_Query([ $query = new WP_Query([
'post_type'=>'attachment', 'post_type' => 'attachment',
'post_status'=>'inherit', 'post_status' => 'inherit',
'post_mime_type'=>'image', 'post_mime_type' => 'image',
'posts_per_page'=>1, 'posts_per_page' => 1,
'fields'=>'ids', 'fields' => 'ids',
'meta_query'=>[ 'meta_query' => [
'relation'=>'AND', 'relation' => 'AND',
['key'=>'_zeitfresser_original_file','compare'=>'EXISTS'], [
['key'=>'_zeitfresser_original_deleted','compare'=>'NOT EXISTS'] 'key' => '_zeitfresser_original_file',
'compare' => 'EXISTS',
], ],
'no_found_rows'=>false [
'key' => '_zeitfresser_original_deleted',
'compare' => 'NOT EXISTS',
],
[
'key' => '_zeitfresser_media_optimized_version',
'value' => ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION,
'compare' => '=',
],
],
'no_found_rows' => false,
]); ]);
return (int) $query->found_posts;
}
function zeitfresser_get_unoptimized_originals_count() {
$query = new WP_Query([
'post_type' => 'attachment',
'post_status' => 'inherit',
'post_mime_type' => 'image',
'posts_per_page' => 1,
'fields' => 'ids',
'meta_query' => [
'relation' => 'AND',
[
'key' => '_zeitfresser_original_file',
'compare' => 'EXISTS',
],
[
'key' => '_zeitfresser_original_deleted',
'compare' => 'NOT EXISTS',
],
[
'relation' => 'OR',
[
'key' => '_zeitfresser_media_optimized_version',
'compare' => 'NOT EXISTS',
],
[
'key' => '_zeitfresser_media_optimized_version',
'value' => ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION,
'compare' => '!=',
],
],
],
'no_found_rows' => false,
]);
return (int) $query->found_posts; return (int) $query->found_posts;
} }
@@ -123,19 +437,25 @@ function zeitfresser_get_original_family_files( $attachment_id, $original ) {
} }
$original_ext = strtolower( pathinfo( $original, PATHINFO_EXTENSION ) ); $original_ext = strtolower( pathinfo( $original, PATHINFO_EXTENSION ) );
$base_name = pathinfo( $original, PATHINFO_FILENAME );
$dir = dirname( $original );
if ( empty( $original_ext ) ) { if ( empty( $original_ext ) ) {
return $files; return $files;
} }
// Original
$files[] = $original; $files[] = $original;
// --- ORIGINAL LOGIC (metadata-based) ---
$metadata = wp_get_attachment_metadata( $attachment_id ); $metadata = wp_get_attachment_metadata( $attachment_id );
$upload_dir = wp_get_upload_dir(); $upload_dir = wp_get_upload_dir();
if ( ! empty( $metadata['file'] ) ) { if ( ! empty( $metadata['file'] ) ) {
$current_main_absolute = trailingslashit( $upload_dir['basedir'] ) . $metadata['file']; $current_main_absolute = trailingslashit( $upload_dir['basedir'] ) . $metadata['file'];
// scaled/main
$files[] = preg_replace( $files[] = preg_replace(
'/\.[^.]+$/', '/\.[^.]+$/',
'.' . $original_ext, '.' . $original_ext,
@@ -159,9 +479,12 @@ function zeitfresser_get_original_family_files( $attachment_id, $original ) {
} }
} }
$files = array_unique( array_filter( $files ) ); // --- NEW: FILESYSTEM FALLBACK (CRITICAL FIX) ---
foreach ( glob( $dir . '/' . $base_name . '*.' . $original_ext ) as $file ) {
$files[] = $file;
}
return array_values( $files ); return array_values( array_unique( array_filter( $files ) ) );
} }
/** /**
@@ -370,15 +693,31 @@ function zeitfresser_ajax_optimize_images() {
wp_send_json_error(); wp_send_json_error();
} }
check_ajax_referer( 'zeitfresser_performance_tools', 'nonce' ); check_ajax_referer( 'zeitfresser_image_optimizer', 'nonce' );
$results = zeitfresser_process_legacy_images_batch( 25 ); $results = zeitfresser_process_legacy_images_batch( 25 );
$pending = zeitfresser_get_pending_legacy_images_count();
$total = zeitfresser_get_total_images_count();
$cleanup_total = zeitfresser_get_total_originals_count();
$cleanup_remaining = zeitfresser_get_remaining_originals_count();
$cleanup_unoptimized = zeitfresser_get_unoptimized_originals_count();
$cleanup_deleted = $cleanup_total - ( $cleanup_remaining + $cleanup_unoptimized );
$cleanup_progress = $cleanup_total > 0
? round( ( $cleanup_deleted / $cleanup_total ) * 100 )
: 0;
wp_send_json_success([ wp_send_json_success([
'processed' => $results['processed'], 'processed' => $results['processed'],
'updated' => $results['updated'], 'updated' => $results['updated'],
'pending' => zeitfresser_get_pending_legacy_images_count(), 'pending' => $pending,
'total' => zeitfresser_get_total_images_count(), 'total' => $total,
'cleanup_total' => $cleanup_total,
'cleanup_remaining' => $cleanup_remaining,
'cleanup_unoptimized' => $cleanup_unoptimized,
'cleanup_deleted' => $cleanup_deleted,
'cleanup_progress' => $cleanup_progress,
]); ]);
} }
add_action( 'wp_ajax_zeitfresser_optimize_images', 'zeitfresser_ajax_optimize_images' ); add_action( 'wp_ajax_zeitfresser_optimize_images', 'zeitfresser_ajax_optimize_images' );
@@ -392,18 +731,20 @@ function zeitfresser_ajax_delete_originals() {
wp_send_json_error(); wp_send_json_error();
} }
check_ajax_referer( 'zeitfresser_performance_tools', 'nonce' ); check_ajax_referer( 'zeitfresser_image_optimizer', 'nonce' );
$deleted = zeitfresser_delete_originals_batch( 10 ); $deleted = zeitfresser_delete_originals_batch( 10 );
$total = zeitfresser_get_total_originals_count(); $total = zeitfresser_get_total_originals_count();
$remaining = zeitfresser_get_remaining_originals_count(); $remaining = zeitfresser_get_remaining_originals_count();
$unoptimized = zeitfresser_get_unoptimized_originals_count();
$deleted_total = $total - ( $remaining + $unoptimized );
wp_send_json_success([ wp_send_json_success([
'deleted' => $deleted, 'deleted' => $deleted,
'total' => $total, 'total' => $total,
'remaining' => $remaining, 'remaining' => $remaining,
'deleted_total' => $total - $remaining, 'unoptimized' => $unoptimized,
'deleted_total' => $deleted_total,
]); ]);
} }
add_action( 'wp_ajax_zeitfresser_delete_originals', 'zeitfresser_ajax_delete_originals' ); add_action( 'wp_ajax_zeitfresser_delete_originals', 'zeitfresser_ajax_delete_originals' );
@@ -411,7 +752,7 @@ add_action( 'wp_ajax_zeitfresser_delete_originals', 'zeitfresser_ajax_delete_ori
/** /**
* Render UI * Render UI
*/ */
function zeitfresser_render_performance_tools_page() { function zeitfresser_render_image_optimizer_page() {
if ( ! current_user_can( 'manage_options' ) ) { if ( ! current_user_can( 'manage_options' ) ) {
return; return;
@@ -425,12 +766,26 @@ function zeitfresser_render_performance_tools_page() {
// 🔥 NEW: Cleanup counters // 🔥 NEW: Cleanup counters
$cleanup_total = zeitfresser_get_total_originals_count(); $cleanup_total = zeitfresser_get_total_originals_count();
$cleanup_remaining = zeitfresser_get_remaining_originals_count(); $cleanup_remaining = zeitfresser_get_remaining_originals_count();
$cleanup_deleted = $cleanup_total - $cleanup_remaining; $cleanup_unoptimized = zeitfresser_get_unoptimized_originals_count();
$cleanup_deleted = $cleanup_total - ( $cleanup_remaining + $cleanup_unoptimized );
$cleanup_progress = $cleanup_total > 0 ? round(($cleanup_deleted / $cleanup_total) * 100) : 0; $cleanup_progress = $cleanup_total > 0 ? round(($cleanup_deleted / $cleanup_total) * 100) : 0;
$cleanup_nothing_to_do = (
0 === $cleanup_remaining &&
(
$cleanup_unoptimized > 0 ||
$cleanup_deleted >= $cleanup_total
)
);
$cleanup_button_disabled = $cleanup_nothing_to_do;
$cleanup_button_label = $cleanup_nothing_to_do
? '🧹 Nothing to clean yet'
: '🧹 Delete Originals';
?> ?>
<div class="wrap"> <div class="wrap">
<h1>Zeitfresser Performance Tools</h1> <h1>Zeitfresser Image Optimizer</h1>
<div class="notice notice-info" style="max-width:800px;margin-top:20px;"> <div class="notice notice-info" style="max-width:800px;margin-top:20px;">
<p> <p>
@@ -443,7 +798,7 @@ function zeitfresser_render_performance_tools_page() {
Once optimized, original images can be deleted to save disk space.<br><br> Once optimized, original images can be deleted to save disk space.<br><br>
<strong>Automation:</strong><br> <strong>Automation:</strong><br>
You can enable automatic optimization on upload in the Customizer under <em>Performance Tools Settings</em>.<br> You can enable automatic optimization on upload in the Customizer under <em>Image Optimizer Settings</em>.<br>
Optionally, original images can also be deleted automatically after successful optimization.<br><br> Optionally, original images can also be deleted automatically after successful optimization.<br><br>
<strong>Safety:</strong><br> <strong>Safety:</strong><br>
@@ -484,7 +839,8 @@ function zeitfresser_render_performance_tools_page() {
<div style="margin-bottom:20px;"> <div style="margin-bottom:20px;">
<p><strong>Total Originals:</strong> <span id="cleanup-total"><?php echo $cleanup_total; ?></span></p> <p><strong>Total Originals:</strong> <span id="cleanup-total"><?php echo $cleanup_total; ?></span></p>
<p><strong>Deleted:</strong> <span id="cleanup-deleted"><?php echo $cleanup_deleted; ?></span></p> <p><strong>Deleted:</strong> <span id="cleanup-deleted"><?php echo $cleanup_deleted; ?></span></p>
<p><strong>Remaining:</strong> <span id="cleanup-remaining"><?php echo $cleanup_remaining; ?></span></p> <p><strong>Ready for Cleanup:</strong> <span id="cleanup-remaining"><?php echo $cleanup_remaining; ?></span></p>
<p><strong>Not Optimized Yet:</strong> <span id="cleanup-unoptimized"><?php echo $cleanup_unoptimized; ?></span></p>
</div> </div>
<div style="margin-bottom:20px;"> <div style="margin-bottom:20px;">
@@ -494,7 +850,9 @@ function zeitfresser_render_performance_tools_page() {
<p><strong>Cleanup Progress:</strong> <span id="cleanup-progress"><?php echo $cleanup_progress; ?></span>%</p> <p><strong>Cleanup Progress:</strong> <span id="cleanup-progress"><?php echo $cleanup_progress; ?></span>%</p>
</div> </div>
<button id="delete-btn" class="button">🧹 Delete Originals</button> <button id="delete-btn" class="button" <?php disabled( $cleanup_button_disabled ); ?>>
<?php echo esc_html( $cleanup_button_label ); ?>
</button>
</div> </div>
<!-- STATUS --> <!-- STATUS -->
@@ -561,7 +919,7 @@ function deleteBatch() {
headers: {'Content-Type': 'application/x-www-form-urlencoded'}, headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({ body: new URLSearchParams({
action: 'zeitfresser_delete_originals', action: 'zeitfresser_delete_originals',
nonce: '<?php echo wp_create_nonce('zeitfresser_performance_tools'); ?>' nonce: '<?php echo wp_create_nonce('zeitfresser_image_optimizer'); ?>'
}) })
}) })
.then(res => res.json()) .then(res => res.json())
@@ -571,6 +929,7 @@ function deleteBatch() {
let remaining = data.data.remaining; let remaining = data.data.remaining;
let deleted = data.data.deleted_total; let deleted = data.data.deleted_total;
let batch = data.data.deleted; let batch = data.data.deleted;
let unoptimized = data.data.unoptimized;
let progress = total > 0 ? Math.round((deleted / total) * 100) : 0; let progress = total > 0 ? Math.round((deleted / total) * 100) : 0;
@@ -578,10 +937,10 @@ function deleteBatch() {
document.getElementById('cleanup-total').innerText = total; document.getElementById('cleanup-total').innerText = total;
document.getElementById('cleanup-deleted').innerText = deleted; document.getElementById('cleanup-deleted').innerText = deleted;
document.getElementById('cleanup-remaining').innerText = remaining; document.getElementById('cleanup-remaining').innerText = remaining;
document.getElementById('cleanup-unoptimized').innerText = unoptimized;
document.getElementById('cleanup-progress').innerText = progress; document.getElementById('cleanup-progress').innerText = progress;
let bar = document.getElementById('cleanup-bar'); let bar = document.getElementById('cleanup-bar');
bar.style.width = progress + '%'; bar.style.width = progress + '%';
if (progress >= 100) { if (progress >= 100) {
@@ -590,7 +949,23 @@ function deleteBatch() {
bar.style.background = '#ff9800'; bar.style.background = '#ff9800';
} }
if (batch > 0) {
document.getElementById('status-clean').innerText = '🧹 Deleted: ' + batch; document.getElementById('status-clean').innerText = '🧹 Deleted: ' + batch;
}
// Nothing cleanup-eligible remains, but originals still exist and are not optimized
if (remaining === 0 && unoptimized > 0) {
deleting = false;
document.getElementById('start-btn').disabled = false;
document.getElementById('delete-btn').disabled = true;
document.getElementById('delete-btn').innerText = '🧹 Nothing to clean yet';
document.getElementById('status-clean').innerText =
'⚠ Available images are not optimized yet. Please optimize them before starting cleanup.';
return;
}
if (remaining > 0) { if (remaining > 0) {
setTimeout(deleteBatch, 400); setTimeout(deleteBatch, 400);
@@ -598,9 +973,17 @@ function deleteBatch() {
deleting = false; deleting = false;
document.getElementById('start-btn').disabled = false; document.getElementById('start-btn').disabled = false;
document.getElementById('delete-btn').disabled = false;
if (remaining === 0 && unoptimized > 0) {
document.getElementById('delete-btn').disabled = true;
document.getElementById('delete-btn').innerText = '🧹 Nothing to clean yet';
} else if (remaining === 0 && total === deleted) {
document.getElementById('delete-btn').disabled = true;
document.getElementById('delete-btn').innerText = '🧹 Nothing to clean yet';
} else {
document.getElementById('delete-btn').disabled = false;
document.getElementById('delete-btn').innerText = '🧹 Delete Originals'; document.getElementById('delete-btn').innerText = '🧹 Delete Originals';
}
document.getElementById('status-clean').innerText = '✔ Cleanup complete'; document.getElementById('status-clean').innerText = '✔ Cleanup complete';
} }
@@ -615,7 +998,7 @@ function processBatch() {
headers: {'Content-Type': 'application/x-www-form-urlencoded'}, headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({ body: new URLSearchParams({
action: 'zeitfresser_optimize_images', action: 'zeitfresser_optimize_images',
nonce: '<?php echo wp_create_nonce('zeitfresser_performance_tools'); ?>' nonce: '<?php echo wp_create_nonce('zeitfresser_image_optimizer'); ?>'
}) })
}) })
.then(res => res.json()) .then(res => res.json())
@@ -631,10 +1014,35 @@ function processBatch() {
document.getElementById('optimized').innerText = optimized; document.getElementById('optimized').innerText = optimized;
document.getElementById('remaining').innerText = pending; document.getElementById('remaining').innerText = pending;
// Live update progress UI // Live update optimization progress
document.getElementById('progress').innerText = progress; document.getElementById('progress').innerText = progress;
document.getElementById('progress-bar').style.width = progress + '%'; document.getElementById('progress-bar').style.width = progress + '%';
// Live update cleanup counters
document.getElementById('cleanup-total').innerText = data.data.cleanup_total;
document.getElementById('cleanup-deleted').innerText = data.data.cleanup_deleted;
document.getElementById('cleanup-remaining').innerText = data.data.cleanup_remaining;
document.getElementById('cleanup-unoptimized').innerText = data.data.cleanup_unoptimized;
document.getElementById('cleanup-progress').innerText = data.data.cleanup_progress;
const cleanupBar = document.getElementById('cleanup-bar');
cleanupBar.style.width = data.data.cleanup_progress + '%';
if (data.data.cleanup_progress >= 100) {
cleanupBar.style.background = '#4caf50';
} else if (data.data.cleanup_progress > 0) {
cleanupBar.style.background = '#ff9800';
}
// Update cleanup button state based on fresh server data
if (data.data.cleanup_remaining === 0 && data.data.cleanup_unoptimized > 0) {
document.getElementById('delete-btn').disabled = true;
document.getElementById('delete-btn').innerText = '🧹 Nothing to clean yet';
} else {
document.getElementById('delete-btn').disabled = false;
document.getElementById('delete-btn').innerText = '🧹 Delete Originals';
}
document.getElementById('status-opt').innerText = document.getElementById('status-opt').innerText =
'🚀 Processed: ' + data.data.processed + '🚀 Processed: ' + data.data.processed +
' | Updated: ' + data.data.updated; ' | Updated: ' + data.data.updated;
@@ -645,10 +1053,10 @@ function processBatch() {
running = false; running = false;
document.getElementById('start-btn').disabled = false; document.getElementById('start-btn').disabled = false;
document.getElementById('delete-btn').disabled = false; document.getElementById('delete-btn').disabled =
(data.data.cleanup_remaining === 0 && data.data.cleanup_unoptimized > 0);
document.getElementById('start-btn').innerText = '🚀 Optimize Images'; document.getElementById('start-btn').innerText = '🚀 Optimize Images';
document.getElementById('status-opt').innerText = '✔ Optimization complete'; document.getElementById('status-opt').innerText = '✔ Optimization complete';
} }
}); });
@@ -9,6 +9,48 @@ if ( ! defined( 'ABSPATH' ) ) {
exit; exit;
} }
/**
* Return the fixed thumbnail size used for post cards and snippets.
*
* This keeps thumbnail selection deterministic while still allowing
* a single central code-level switch if the size ever needs to change.
*
* @return string
*/
if ( ! function_exists( 'zeitfresser_get_post_card_thumbnail_size' ) ) {
function zeitfresser_get_post_card_thumbnail_size() {
return 'thumbnail';
}
}
/**
* Determine whether post card featured images should be displayed.
*
* This replaces the old Customizer-based toggle with a static,
* deterministic decision.
*
* @return bool
*/
if ( ! function_exists( 'zeitfresser_show_post_card_featured_image' ) ) {
function zeitfresser_show_post_card_featured_image() {
return true;
}
}
/**
* Return the excerpt length for post cards.
*
* This remains configurable via Theme Customizer, as it is a
* meaningful editorial control.
*
* @return int
*/
if ( ! function_exists( 'zeitfresser_get_post_card_excerpt_length' ) ) {
function zeitfresser_get_post_card_excerpt_length() {
return (int) get_theme_mod( 'post_snippet_excerpt_size', 20 );
}
}
if ( ! function_exists( 'zeitfresser_fs' ) ) { if ( ! function_exists( 'zeitfresser_fs' ) ) {
/** /**
* Lightweight Freemius compatibility stub. * Lightweight Freemius compatibility stub.
@@ -121,9 +163,9 @@ if ( ! function_exists( 'zeitfresser_social_icon_svg' ) ) {
'twitter' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" focusable="false"><path d="M459.37 151.72c.32 4.54.32 9.1.32 13.63c0 138.72-105.58 298.56-298.56 298.56c-59.45 0-114.68-17.22-161.14-47.11c8.45 1 16.57 1.32 25.34 1.32c49.06 0 94.21-16.57 130.27-44.84c-46.13-1-84.79-31.24-98.11-72.77c6.5 1 12.99 1.63 19.81 1.63c9.42 0 18.84-1.32 27.61-3.57c-48.08-9.74-84.14-51.98-84.14-102.98v-1.32c13.97 7.8 30.21 12.67 47.43 13.3c-28.3-18.84-46.78-51.02-46.78-87.39c0-19.49 5.2-37.36 14.29-52.95c51.98 63.67 129.3 105.26 216.36 109.75c-1.62-7.8-2.6-15.92-2.6-24.04c0-57.83 46.78-104.93 104.93-104.93c30.21 0 57.5 12.67 76.67 33.14c23.72-4.55 46.46-13.3 66.59-25.34c-7.8 24.37-24.37 44.84-46.13 57.83c21.12-2.27 41.58-8.12 60.42-16.24c-14.29 20.79-32.16 39.31-52.63 54.25z"/></svg>', 'twitter' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" focusable="false"><path d="M459.37 151.72c.32 4.54.32 9.1.32 13.63c0 138.72-105.58 298.56-298.56 298.56c-59.45 0-114.68-17.22-161.14-47.11c8.45 1 16.57 1.32 25.34 1.32c49.06 0 94.21-16.57 130.27-44.84c-46.13-1-84.79-31.24-98.11-72.77c6.5 1 12.99 1.63 19.81 1.63c9.42 0 18.84-1.32 27.61-3.57c-48.08-9.74-84.14-51.98-84.14-102.98v-1.32c13.97 7.8 30.21 12.67 47.43 13.3c-28.3-18.84-46.78-51.02-46.78-87.39c0-19.49 5.2-37.36 14.29-52.95c51.98 63.67 129.3 105.26 216.36 109.75c-1.62-7.8-2.6-15.92-2.6-24.04c0-57.83 46.78-104.93 104.93-104.93c30.21 0 57.5 12.67 76.67 33.14c23.72-4.55 46.46-13.3 66.59-25.34c-7.8 24.37-24.37 44.84-46.13 57.83c21.12-2.27 41.58-8.12 60.42-16.24c-14.29 20.79-32.16 39.31-52.63 54.25z"/></svg>',
'pinterest' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" aria-hidden="true" focusable="false"><path d="M204 6.7C93.4 6.7 0 100.1 0 210.7c0 69.2 40.5 128.9 101.6 161.4c-1.4-13.7-2.6-34.8.5-49.8c2.8-15.9 18.1-101.2 18.1-101.2s-4.6-9.3-4.6-23c0-21.6 12.5-37.8 28.1-37.8c13.2 0 19.6 9.9 19.6 21.8c0 13.2-8.4 33-12.7 51.3c-3.6 15.6 7.7 28.4 23 28.4c27.6 0 48.9-29.1 48.9-71.1c0-37.2-26.8-63.2-65-63.2c-44.3 0-70.3 33.1-70.3 67.5c0 13.3 5.1 27.5 11.6 35.2c1.3 1.6 1.5 3 .9 4.7c-1 5.1-3.2 16-3.6 18.2c-.6 3-2.1 3.6-4.8 2.2c-17.8-8.3-28.9-34.1-28.9-54.8c0-44.6 32.4-85.6 93.4-85.6c49 0 87.1 34.9 87.1 81.6c0 48.7-30.7 87.9-73.2 87.9c-14.3 0-27.7-7.4-32.3-16.2l-8.8 33.5c-3.2 12.2-11.8 27.5-17.6 36.8c13.2 4.1 27.1 6.2 41.5 6.2c110.6 0 204-93.4 204-204S314.6 6.7 204 6.7z"/></svg>', 'pinterest' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" aria-hidden="true" focusable="false"><path d="M204 6.7C93.4 6.7 0 100.1 0 210.7c0 69.2 40.5 128.9 101.6 161.4c-1.4-13.7-2.6-34.8.5-49.8c2.8-15.9 18.1-101.2 18.1-101.2s-4.6-9.3-4.6-23c0-21.6 12.5-37.8 28.1-37.8c13.2 0 19.6 9.9 19.6 21.8c0 13.2-8.4 33-12.7 51.3c-3.6 15.6 7.7 28.4 23 28.4c27.6 0 48.9-29.1 48.9-71.1c0-37.2-26.8-63.2-65-63.2c-44.3 0-70.3 33.1-70.3 67.5c0 13.3 5.1 27.5 11.6 35.2c1.3 1.6 1.5 3 .9 4.7c-1 5.1-3.2 16-3.6 18.2c-.6 3-2.1 3.6-4.8 2.2c-17.8-8.3-28.9-34.1-28.9-54.8c0-44.6 32.4-85.6 93.4-85.6c49 0 87.1 34.9 87.1 81.6c0 48.7-30.7 87.9-73.2 87.9c-14.3 0-27.7-7.4-32.3-16.2l-8.8 33.5c-3.2 12.2-11.8 27.5-17.6 36.8c13.2 4.1 27.1 6.2 41.5 6.2c110.6 0 204-93.4 204-204S314.6 6.7 204 6.7z"/></svg>',
'tiktok' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" aria-hidden="true" focusable="false"><path d="M448,209.9a210.06,210.06,0,0,1-122.77-39.25V349.38A162.55,162.55,0,1,1,185,188.31V278.2a74.62,74.62,0,1,0,52.23,71.18V0h88a121.18,121.18,0,0,0,1.86,22.17h0A122.18,122.18,0,0,0,448,142.3Z"/></svg>', 'tiktok' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" aria-hidden="true" focusable="false"><path d="M448,209.9a210.06,210.06,0,0,1-122.77-39.25V349.38A162.55,162.55,0,1,1,185,188.31V278.2a74.62,74.62,0,1,0,52.23,71.18V0h88a121.18,121.18,0,0,0,1.86,22.17h0A122.18,122.18,0,0,0,448,142.3Z"/></svg>',
'mastodon' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" aria-hidden="true" focusable="false"><path d="M433 179.11c0-97.2-63.91-125.7-63.91-125.7C336.42 38.4 279.2 32 224.14 32h-.27c-55.06 0-112.28 6.4-144.95 21.41c0 0-63.92 28.5-63.92 125.7c0 22.34-.43 49.13.27 81.78c2.31 109.32 20.05 217.01 121.08 241.82c46.64 11.45 86.68 13.84 119.46 12.06c59.08-3.2 92.27-20.62 92.27-20.62l-1.96-43.47s-42.3 13.33-89.78 11.72c-47.03-1.6-96.73-5.09-104.41-63.16a116.85 116.85 0 0 1-1.06-15.65s46.27 11.3 104.92 13.99c35.27 1.62 68.32-2.06 101.96-6.15c64.49-7.83 120.53-48.23 127.62-85.36c11.18-58.65 10.26-143.5 10.26-143.5zm-80.32 145.27h-50.54V200.59c0-26.15-11.06-39.43-33.17-39.43c-24.46 0-36.72 15.84-36.72 47.1v67.82H181.8V208.26c0-31.26-12.26-47.1-36.73-47.1c-22.1 0-33.16 13.28-33.16 39.43v123.79H61.37V196.83c0-26.15 6.68-46.92 20.03-62.31c13.75-15.39 31.75-23.14 54.09-23.14c25.82 0 45.34 9.92 58.74 29.76l12.69 21.11l12.69-21.11c13.4-19.84 32.92-29.76 58.75-29.76c22.33 0 40.34 7.75 54.08 23.14c13.36 15.39 20.04 36.16 20.04 62.31z"/></svg>', 'mastodon' => '<svg viewBox="0 0 448 512" aria-hidden="true"><path d="M433 179.11c0-97.2-63.91-125.7-63.91-125.7C336.42 38.4 279.2 32 224.14 32h-.27c-55.06 0-112.28 6.4-144.95 21.41c0 0-63.92 28.5-63.92 125.7c0 22.34-.43 49.13.27 81.78c2.31 109.32 20.05 217.01 121.08 241.82c46.64 11.45 86.68 13.84 119.46 12.06c59.08-3.2 92.27-20.62 92.27-20.62l-1.96-43.47s-42.3 13.33-89.78 11.72c-47.03-1.6-96.73-5.09-104.41-63.16a116.85 116.85 0 0 1-1.06-15.65s46.27 11.3 104.92 13.99c35.27 1.62 68.32-2.06 101.96-6.15c64.49-7.83 120.53-48.23 127.62-85.36c11.18-58.65 10.26-143.5 10.26-143.5zm-80.32 145.27h-50.54V200.59c0-26.15-11.06-39.43-33.17-39.43c-24.46 0-36.72 15.84-36.72 47.1v67.82H181.8V208.26c0-31.26-12.26-47.1-36.73-47.1c-22.1 0-33.16 13.28-33.16 39.43v123.79H61.37V196.83c0-26.15 6.68-46.92 20.03-62.31c13.75-15.39 31.75-23.14 54.09-23.14c25.82 0 45.34 9.92 58.74 29.76l12.69 21.11l12.69-21.11c13.4-19.84 32.92-29.76 58.75-29.76c22.33 0 40.34 7.75 54.08 23.14c13.36 15.39 20.04 36.16 20.04 62.31z"/></svg>',
'github' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512" aria-hidden="true" focusable="false"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6c-3.3.3-5.6-1.3-5.6-3.6c0-2 2.3-3.6 5.2-3.6c3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9c2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9c.3 2 2.6 3.3 5.6 2.6c2.9-.7 4.9-2.6 4.6-4.6c-.3-2.3-2.6-3.6-5.6-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 71.8 205 171.6 238.2c12.8 2.3 17.5-5.6 17.5-12.1c0-6.2-.3-40.4-.3-61.4c0 0-70 15-84.7-29.8c0 0-11.4-29.1-27.8-36.9c0 0-22.9-15.7 1.6-15.4c0 0 24.9 2 38.6 25.8c21.9 38.6 58.6 27.5 72.9 20.9c2.3-16 8.8-27.5 16-33.7c-55.9-6.2-112.3-13.9-112.3-110.5c0-27.5 7.8-41.4 20.6-55.2c-2-5-8.8-25.7 2-53.6c16.1-5 52.6 20.6 52.6 20.6c15.2-4.3 31.4-6.5 47.6-6.5c16.3 0 32.8 2.3 47.7 6.5c0 0 36.6-25.8 52.6-20.6c10.8 27.8 4 48.6 2 53.6c12.8 13.8 20.6 27.8 20.6 55.2c0 96.9-58.8 104.2-114.9 110.5c9.2 7.8 17.2 22.9 17.2 46.4c0 33.7-.3 75.4-.3 83.1c0 6.5 4.6 14.4 17.5 12.1C424.2 457 496 362.9 496 252C496 113.3 383.5 8 244.8 8z"/></svg>', 'github' => '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 .297a12 12 0 0 0-3.794 23.403c.6.113.82-.26.82-.577v-2.234c-3.338.726-4.042-1.61-4.042-1.61a3.18 3.18 0 0 0-1.335-1.754c-1.09-.745.083-.73.083-.73a2.52 2.52 0 0 1 1.84 1.24 2.555 2.555 0 0 0 3.49.997 2.56 2.56 0 0 1 .763-1.606c-2.665-.303-5.466-1.334-5.466-5.933a4.64 4.64 0 0 1 1.235-3.216 4.31 4.31 0 0 1 .117-3.172s1.008-.322 3.301 1.23a11.46 11.46 0 0 1 6.003 0c2.293-1.552 3.3-1.23 3.3-1.23a4.31 4.31 0 0 1 .118 3.172 4.64 4.64 0 0 1 1.234 3.216c0 4.61-2.804 5.627-5.476 5.922a2.865 2.865 0 0 1 .815 2.222v3.293c0 .32.218.694.825.576A12.001 12.001 0 0 0 12 .297"/></svg>',
'matrix' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" aria-hidden="true" focusable="false"><path d="M0 32v448h60V0H0v32zm580 0v448h60V0h-60v32zM120 96v320h70V206.8l77.7 117.8h4.6L350 206.8V416h70V96h-68l-81.4 122.6L189 96h-69z"/></svg>', 'matrix' => '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2 2v20h3V5.5l5 7.5h1l5-7.5V22h3V2h-3l-5.5 8-5.5-8H2z"/></svg>',
); );
return isset( $icons[ $key ] ) ? $icons[ $key ] : ''; return isset( $icons[ $key ] ) ? $icons[ $key ] : '';
@@ -150,3 +192,43 @@ function db_fs() {
function graphthemes_get_social_link_default( $social_key ) { function graphthemes_get_social_link_default( $social_key ) {
return zeitfresser_get_social_link_default( $social_key ); return zeitfresser_get_social_link_default( $social_key );
} }
/**
* Get asset URL with version (cache busting).
*
* @param string $path Relative path inside /assets (must start with /)
* @return array{url:string, version:string}
*/
if ( ! function_exists( 'zeitfresser_asset_versioned' ) ) {
function zeitfresser_asset_versioned( $path ) {
$full_path = '/assets' . $path;
return [
'url' => get_template_directory_uri() . $full_path,
'version' => file_exists( get_template_directory() . $full_path )
? (string) filemtime( get_template_directory() . $full_path )
: ZEITFRESSER_VERSION,
];
}
}
/**
* Get asset URL only (no version).
*
* @param string $path
* @return string
*/
if ( ! function_exists( 'zeitfresser_asset' ) ) {
function zeitfresser_asset( $path ) {
return get_template_directory_uri() . '/assets/' . ltrim($path, '/');
}
}
/**
* Delete Cookie Button
*/
add_filter( 'comment_form_default_fields', function( $fields ) {
unset( $fields['cookies'] );
return $fields;
});
@@ -9,13 +9,38 @@ if ( ! defined( 'ABSPATH' ) ) {
exit; exit;
} }
/**
* Enqueue TOC script when needed.
*/
function zeitfresser_enqueue_toc_assets() {
if ( ! is_singular( 'post' ) ) {
return;
}
if ( ! zeitfresser_has_floating_toc() ) {
return;
}
$asset = zeitfresser_asset_versioned('/js/toc.js');
wp_enqueue_script(
'zeitfresser-toc',
$asset['url'],
[],
$asset['version'],
true
);
}
add_action( 'wp_enqueue_scripts', 'zeitfresser_enqueue_toc_assets', 20 );
/** /**
* Return whether article TOC output is enabled. * Return whether article TOC output is enabled.
* *
* @return bool * @return bool
*/ */
function zeitfresser_show_article_toc() { function zeitfresser_show_article_toc() {
return (bool) get_theme_mod( 'show_article_toc', zeitfresser_get_default_show_article_toc() ); return (bool) get_theme_mod( 'show_article_toc', true );
} }
/** /**
@@ -24,11 +49,10 @@ function zeitfresser_show_article_toc() {
* @return int * @return int
*/ */
function zeitfresser_get_article_toc_min_headlines() { function zeitfresser_get_article_toc_min_headlines() {
$threshold = absint( get_theme_mod( 'article_toc_min_headlines', zeitfresser_get_default_article_toc_min_headlines() ) ); $threshold = absint( get_theme_mod( 'article_toc_min_headlines', 3 ) );
return max( 1, $threshold ); return max( 1, $threshold );
} }
/** /**
* Build a processed single post content payload with TOC metadata. * Build a processed single post content payload with TOC metadata.
* *
@@ -36,8 +60,8 @@ function zeitfresser_get_article_toc_min_headlines() {
* @return array{content:string,items:array<int,array<string,mixed>>} * @return array{content:string,items:array<int,array<string,mixed>>}
*/ */
function zeitfresser_build_toc_payload( $post_id ) { function zeitfresser_build_toc_payload( $post_id ) {
static $cache = array();
static $cache = array();
$post_id = (int) $post_id; $post_id = (int) $post_id;
if ( isset( $cache[ $post_id ] ) ) { if ( isset( $cache[ $post_id ] ) ) {
@@ -49,21 +73,15 @@ function zeitfresser_build_toc_payload( $post_id ) {
'items' => array(), 'items' => array(),
); );
// Early exit conditions
if ( ! $post_id || ! is_singular( 'post' ) || ! zeitfresser_show_article_toc() ) { if ( ! $post_id || ! is_singular( 'post' ) || ! zeitfresser_show_article_toc() ) {
$cache[ $post_id ] = $payload; return $cache[ $post_id ] = $payload;
return $payload;
} }
$content = trim( (string) $payload['content'] ); $content = trim( (string) $payload['content'] );
if ( '' === $content ) { if ( '' === $content || ! class_exists( 'DOMDocument' ) ) {
$cache[ $post_id ] = $payload; return $cache[ $post_id ] = $payload;
return $payload;
}
if ( ! class_exists( 'DOMDocument' ) ) {
$cache[ $post_id ] = $payload;
return $payload;
} }
libxml_use_internal_errors( true ); libxml_use_internal_errors( true );
@@ -76,16 +94,14 @@ function zeitfresser_build_toc_payload( $post_id ) {
if ( ! $loaded ) { if ( ! $loaded ) {
libxml_clear_errors(); libxml_clear_errors();
$cache[ $post_id ] = $payload; return $cache[ $post_id ] = $payload;
return $payload;
} }
$container = $dom->getElementById( 'zeitfresser-toc-root' ); $container = $dom->getElementById( 'zeitfresser-toc-root' );
if ( ! $container ) { if ( ! $container ) {
libxml_clear_errors(); libxml_clear_errors();
$cache[ $post_id ] = $payload; return $cache[ $post_id ] = $payload;
return $payload;
} }
$index = 1; $index = 1;
@@ -95,8 +111,8 @@ function zeitfresser_build_toc_payload( $post_id ) {
if ( $headings instanceof DOMNodeList ) { if ( $headings instanceof DOMNodeList ) {
foreach ( $headings as $heading ) { foreach ( $headings as $heading ) {
$text = trim( wp_strip_all_tags( $heading->textContent ) );
$text = trim( wp_strip_all_tags( $heading->textContent ) );
if ( '' === $text ) { if ( '' === $text ) {
continue; continue;
} }
@@ -128,20 +144,18 @@ function zeitfresser_build_toc_payload( $post_id ) {
libxml_clear_errors(); libxml_clear_errors();
// Respect minimum threshold
if ( count( $toc_items ) < zeitfresser_get_article_toc_min_headlines() ) { if ( count( $toc_items ) < zeitfresser_get_article_toc_min_headlines() ) {
$cache[ $post_id ] = array( return $cache[ $post_id ] = array(
'content' => zeitfresser_extract_toc_inner_html( $container ), 'content' => zeitfresser_extract_toc_inner_html( $container ),
'items' => array(), 'items' => array(),
); );
return $cache[ $post_id ];
} }
$cache[ $post_id ] = array( return $cache[ $post_id ] = array(
'content' => zeitfresser_extract_toc_inner_html( $container ), 'content' => zeitfresser_extract_toc_inner_html( $container ),
'items' => $toc_items, 'items' => $toc_items,
); );
return $cache[ $post_id ];
} }
/** /**
@@ -151,6 +165,7 @@ function zeitfresser_build_toc_payload( $post_id ) {
* @return string * @return string
*/ */
function zeitfresser_extract_toc_inner_html( $node ) { function zeitfresser_extract_toc_inner_html( $node ) {
$html = ''; $html = '';
if ( ! $node || ! $node->hasChildNodes() ) { if ( ! $node || ! $node->hasChildNodes() ) {
@@ -171,6 +186,7 @@ function zeitfresser_extract_toc_inner_html( $node ) {
* @return bool * @return bool
*/ */
function zeitfresser_has_floating_toc( $post_id = null ) { function zeitfresser_has_floating_toc( $post_id = null ) {
$post_id = $post_id ? (int) $post_id : get_the_ID(); $post_id = $post_id ? (int) $post_id : get_the_ID();
if ( ! $post_id ) { if ( ! $post_id ) {
@@ -189,6 +205,7 @@ function zeitfresser_has_floating_toc( $post_id = null ) {
* @return void * @return void
*/ */
function zeitfresser_render_floating_toc( $post_id = null ) { function zeitfresser_render_floating_toc( $post_id = null ) {
$post_id = $post_id ? (int) $post_id : get_the_ID(); $post_id = $post_id ? (int) $post_id : get_the_ID();
if ( ! $post_id ) { if ( ! $post_id ) {
@@ -201,13 +218,16 @@ function zeitfresser_render_floating_toc( $post_id = null ) {
return; return;
} }
?> ?>
<aside class="zeitfresser-floating-toc" id="zeitfresser-floating-toc" aria-label="<?php echo esc_attr__( 'Table of contents', 'zeitfresser' ); ?>"> <aside class="zeitfresser-floating-toc" id="zeitfresser-floating-toc" aria-label="<?php echo esc_attr__( 'Table of contents', 'zeitfresser' ); ?>">
<div class="zeitfresser-floating-toc__header"> <div class="zeitfresser-floating-toc__header">
<span class="zeitfresser-floating-toc__title"><?php echo esc_html__( 'Content', 'zeitfresser' ); ?></span> <span class="zeitfresser-floating-toc__title"><?php echo esc_html__( 'Content', 'zeitfresser' ); ?></span>
</div> </div>
<div class="zeitfresser-floating-toc__progress" aria-hidden="true"> <div class="zeitfresser-floating-toc__progress" aria-hidden="true">
<span class="zeitfresser-floating-toc__progress-bar" id="zeitfresser-floating-toc-progress"></span> <span class="zeitfresser-floating-toc__progress-bar" id="zeitfresser-floating-toc-progress"></span>
</div> </div>
<nav class="zeitfresser-floating-toc__nav"> <nav class="zeitfresser-floating-toc__nav">
<ol class="zeitfresser-floating-toc__list"> <ol class="zeitfresser-floating-toc__list">
<?php foreach ( $payload['items'] as $item ) : ?> <?php foreach ( $payload['items'] as $item ) : ?>
@@ -220,5 +240,6 @@ function zeitfresser_render_floating_toc( $post_id = null ) {
</ol> </ol>
</nav> </nav>
</aside> </aside>
<?php <?php
} }
+2 -4
View File
@@ -1,6 +1,6 @@
<p align="center"> <p align="center">
<a href="https://ztfr.eu/matrix"> <a href="https://ztfr.eu/matrix">
<img src="assets/community-badge.png" alt="Join Zeitfresser Matrix Community" height="70" /> <img src="screenshot.png" alt="Join Zeitfresser Matrix Community" height="70" />
</a> </a>
</p> </p>
@@ -130,9 +130,7 @@ Using caching, a CDN, and optimized hosting will further improve performance, es
## 🛠 Development & Support ## 🛠 Development & Support
If you want to get support or participate in development, you can join the If you want to get support or participate in development, you can join the <a href="https://ztfr.eu/matrix">Zeitfresser Matrix Community</a> or the <a href="https://look.ztfr.eu/#/#support:ztfr.eu">Development & Support Channel</a>.
<a href="https://ztfr.eu/matrix">Zeitfresser Matrix Community</a> or the
<a href="https://look.ztfr.eu/#/#support:ztfr.eu">Development & Support Channel</a>.
## 📄 License ## 📄 License
+21 -15
View File
@@ -2,42 +2,48 @@
/** /**
* The template for displaying all single posts * The template for displaying all single posts
* *
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/#single-post
*
* @package zeitfresser * @package zeitfresser
*/ */
get_header(); get_header();
?>
<?php $show_hide_related_posts = get_theme_mod( 'post_detail_hide_show_related_articles', zeitfresser_get_default_post_detail_related_articles() ); ?>
// CLEAN: no default function anymore
$show_hide_related_posts = get_theme_mod(
'post_detail_hide_show_related_articles',
true
);
?>
<div id="primary" class="inside-page content-area"> <div id="primary" class="inside-page content-area">
<div class="container"> <div class="container">
<div class="main-wrapper"> <div class="main-wrapper">
<?php zeitfresser_render_floating_toc(); ?>
<section class="page-section full-width-view"> <section class="page-section full-width-view">
<div class="detail-content"> <div class="detail-content">
<?php while ( have_posts() ) : the_post(); ?> <?php while ( have_posts() ) : the_post(); ?>
<?php get_template_part( 'template-parts/content', 'single' ); ?> <?php get_template_part( 'template-parts/content', 'single' ); ?>
<?php endwhile; // End of the loop. ?> <?php endwhile; ?>
<?php the_post_navigation(); ?> <?php the_post_navigation(); ?>
<?php comments_template(); ?> <?php comments_template(); ?>
</div><!-- /.end of deatil-content --> </div>
<?php <?php if ( $show_hide_related_posts ) : ?>
if( $show_hide_related_posts ) { <?php get_template_part( 'template-parts/related', 'articles' ); ?>
get_template_part('template-parts/related', 'articles'); <?php endif; ?>
}
?>
</section> <!-- /.end of section -->
<div class="sidebar"><?php get_sidebar();?></div> </section>
<div class="sidebar">
<?php get_sidebar(); ?>
</div>
</div> </div>
</div> </div>
</div> </div>
<?php <?php get_footer(); ?>
get_footer();
+2226 -4
View File
File diff suppressed because one or more lines are too long
+14 -115
View File
@@ -2,137 +2,36 @@
/** /**
* Template part for displaying results in search pages * Template part for displaying results in search pages
* *
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*
* @package zeitfresser * @package zeitfresser
*/ */
?>
<?php
$show_hide_image = get_theme_mod( 'post_snippet_hide_show_featured_image', zeitfresser_get_default_post_snippet_featured_image() );
$show_hide_date = get_theme_mod( 'post_snippet_hide_show_date', zeitfresser_get_default_post_snippet_date() );
$show_hide_author = get_theme_mod( 'post_snippet_hide_show_author', zeitfresser_get_default_post_snippet_author() );
$show_hide_comment = get_theme_mod( 'post_snippet_hide_show_comment', zeitfresser_get_default_post_snippet_comment() );
$show_hide_categories = get_theme_mod( 'post_snippet_hide_show_category', zeitfresser_get_default_post_snippet_category() );
$show_hide_tags = get_theme_mod( 'post_snippet_hide_show_tag', zeitfresser_get_default_post_snippet_tag() );
$show_hide_social_share = get_theme_mod( 'post_snippet_hide_show_social_share', zeitfresser_get_default_post_snippet_social_share() );
$social_share = get_theme_mod( 'post_snippet_social_share_options', zeitfresser_get_default_post_snippet_social_share_options() );
?> ?>
<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>> <div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<div class="news-snippet"> <div class="news-snippet">
<?php if ( $show_hide_image && has_post_thumbnail() ) : ?>
<?php $thumbnail_size = get_theme_mod( 'post_snippet_featured_image_size', zeitfresser_get_default_post_snippet_featured_image_size() ); ?> <?php if ( zeitfresser_show_post_card_featured_image() && has_post_thumbnail() ) : ?>
<a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark" class="featured-image"> <a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark" class="featured-image">
<?php the_post_thumbnail( $thumbnail_size ); ?> <?php the_post_thumbnail( zeitfresser_get_post_card_thumbnail_size() ); ?>
</a> </a>
<?php endif; ?> <?php endif; ?>
<div class="summary"> <div class="summary">
<?php if( $show_hide_categories ) { ?>
<?php $categories = get_the_category(); <h3 class="news-title">
if( ! empty( $categories ) ) : ?> <a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark">
<div class="category"> <?php the_title(); ?>
<?php foreach ( $categories as $category ) { ?>
<a
href="<?php echo esc_url( get_category_link( $category->term_id ) ); ?>"><?php echo esc_html( $category->name ); ?></a>
<?php } ?>
</div>
<?php endif; ?>
<?php } ?>
<h3 class="news-title"><a href="<?php echo esc_url( get_permalink() ); ?>"
rel="bookmark"><?php the_title(); ?></a></h3>
<div class="ihead info">
<ul class="list-inline">
<?php if( $show_hide_date ) { ?>
<?php $archive_year = get_the_time('Y'); $archive_month = get_the_time('m'); $archive_day = get_the_time('d'); ?>
<li><a
href="<?php echo esc_url( get_day_link( $archive_year, $archive_month, $archive_day ) ); ?>"><?php echo get_the_date(); ?></a>
</li>
<?php } ?>
<?php
if( $show_hide_author ) { ?>
<li class="post-author">
<a class="url fn n"
href="<?php echo esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ); ?>">
<?php $avatar = get_avatar( get_the_author_meta( 'ID' ), $size = 60 ); ?>
<?php if( $avatar ) : ?>
<div class="author-image">
<?php echo esc_url($avatar); ?>
</div>
<?php endif; ?>
<?php echo esc_html( get_the_author() ); ?>
</a> </a>
</li> </h3>
<?php } ?>
</ul>
<?php if( $show_hide_comment ) { ?>
<span class="comments">
<svg width="20px" height="20px" viewBox="0 0 24 24" id="magicoon-Regular"
xmlns="http://www.w3.org/2000/svg">
<g id="comment-Regular">
<path id="comment-Regular-2" data-name="comment-Regular" class="cls-1"
d="M17,3.25H7A4.756,4.756,0,0,0,2.25,8V21a.75.75,0,0,0,1.28.53l2.414-2.414a1.246,1.246,0,0,1,.885-.366H17A4.756,4.756,0,0,0,21.75,14V8A4.756,4.756,0,0,0,17,3.25ZM20.25,14A3.254,3.254,0,0,1,17,17.25H6.829a2.73,2.73,0,0,0-1.945.806L3.75,19.189V8A3.254,3.254,0,0,1,7,4.75H17A3.254,3.254,0,0,1,20.25,8Z" />
</g>
</svg>
<?php comments_popup_link( __( '0', 'zeitfresser' ), __( '1', 'zeitfresser' ), __( '%', 'zeitfresser' ) ); ?>
</span>
<?php } ?>
</div>
<div class="excerpt"> <div class="excerpt">
<?php $excerpt_length = get_theme_mod( 'post_snippet_excerpt_size', zeitfresser_get_default_post_snippet_excerpt_size() ); ?> <?php echo esc_html(
wp_trim_words(
<?php echo wp_trim_words( get_the_excerpt(), $excerpt_length ); ?> get_the_excerpt(),
zeitfresser_get_post_card_excerpt_length()
)
); ?>
</div> </div>
<?php $readmore_show_hide = get_theme_mod( 'post_snippet_hide_show_readmore', zeitfresser_get_default_post_snippet_show_hide_read_more() ); ?>
<?php $readmore_text = get_theme_mod( 'post_snippet_readmore_text', zeitfresser_get_default_post_snippet_read_more_text() ); ?>
<div class="ifoot info">
<?php if( $readmore_show_hide ) { ?>
<a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark" title=""
class="readmore"><?php echo esc_html( $readmore_text ); ?></a>
<?php } ?>
<?php if( $show_hide_social_share && $social_share ) { ?>
<div class="social-share">
<?php get_template_part( 'inc/blocks/includes/template', 'social-share', $social_share ); ?>
</div>
<?php } ?>
</div>
<?php if( $show_hide_tags ) { ?>
<div class="tags">
<?php $tags = get_the_tags(); ?>
<?php if( ! empty( $tags ) ) : ?>
<?php foreach ( $tags as $post_tag ) { ?>
<a
href="<?php echo esc_url( get_category_link( $post_tag->term_id ) ); ?>"><?php echo esc_html( $post_tag->name ); ?></a>
<?php } ?>
<?php endif; ?>
</div>
<?php } ?>
</div> </div>
</div> </div>
</div> </div>
+23 -135
View File
@@ -4,21 +4,9 @@
* *
* @package zeitfresser * @package zeitfresser
*/ */
?>
$toc_payload = zeitfresser_build_toc_payload( get_the_ID() );
<?php $has_floating_toc = ! empty( $toc_payload['items'] );
$show_hide_image = get_theme_mod( 'post_detail_hide_show_featured_image', zeitfresser_get_default_post_detail_featured_image() );
$show_hide_date = get_theme_mod( 'post_detail_hide_show_date', zeitfresser_get_default_post_detail_date() );
$show_hide_author = get_theme_mod( 'post_detail_hide_show_author', zeitfresser_get_default_post_detail_author() );
$show_hide_comment = get_theme_mod( 'post_detail_hide_show_comment', zeitfresser_get_default_post_detail_comment() );
$show_hide_categories = get_theme_mod( 'post_detail_hide_show_category', zeitfresser_get_default_post_detail_category() );
$show_hide_tags = get_theme_mod( 'post_detail_hide_show_tag', zeitfresser_get_default_post_detail_tag() );
$show_hide_author_block = get_theme_mod( 'post_detail_hide_show_author_block', zeitfresser_get_default_post_detail_author_block() );
$show_hide_social_share = get_theme_mod( 'post_detail_hide_show_social_share', zeitfresser_get_default_post_detail_social_share() );
$social_share = get_theme_mod( 'post_detail_social_share_options', zeitfresser_get_default_post_detail_social_share_options() );
$toc_payload = zeitfresser_build_toc_payload( get_the_ID() );
$has_floating_toc = ! empty( $toc_payload['items'] );
?> ?>
<div class="zeitfresser-article-heading"> <div class="zeitfresser-article-heading">
@@ -29,97 +17,46 @@
<?php zeitfresser_render_floating_toc( get_the_ID() ); ?> <?php zeitfresser_render_floating_toc( get_the_ID() ); ?>
<?php endif; ?> <?php endif; ?>
<div class="single-post"> <div class="single-post">
<div class="post-content"> <div class="post-content">
<!-- Meta Info -->
<div class="ihead info"> <div class="ihead info">
<ul class="list-inline"> <ul class="list-inline">
<?php
if( $show_hide_date ) { ?>
<?php $archive_year = get_the_time('Y'); $archive_month = get_the_time('m'); $archive_day = get_the_time('d'); ?>
<li class="post-date"><i class="icon-calendar"></i> <a
href="<?php echo esc_url( get_day_link( $archive_year, $archive_month, $archive_day ) ); ?>"><?php echo get_the_date(); ?></a>
</li>
<?php } ?>
<!-- Date (TRUE) -->
<?php <?php
if( $show_hide_author ) { ?> $archive_year = get_the_time('Y');
<li class="post-author"><i class="icon-user"></i> $archive_month = get_the_time('m');
<a class="url fn n" $archive_day = get_the_time('d');
href="<?php echo esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ); ?>"> ?>
<?php $avatar = get_avatar( get_the_author_meta( 'ID' ), $size = 60 ); ?> <li class="post-date">
<?php if( $avatar ) : ?> <i class="icon-calendar"></i>
<div class="author-image"> <a href="<?php echo esc_url( get_day_link( $archive_year, $archive_month, $archive_day ) ); ?>">
<?php echo esc_url($avatar); ?> <?php echo esc_html( get_the_date() ); ?>
</div>
<?php endif; ?>
<?php echo esc_html( get_the_author() ); ?>
</a> </a>
</li> </li>
<?php } ?>
</ul> </ul>
<!-- Comments (TRUE) -->
<?php if( $show_hide_comment ) { ?>
<span class="comments"> <span class="comments">
<svg width="20px" height="20px" viewBox="0 0 24 24" id="magicoon-Regular" <svg width="20px" height="20px" viewBox="0 0 24 24">
xmlns="http://www.w3.org/2000/svg"> <g>
<g id="comment-Regular"> <path d="M17,3.25H7A4.756,4.756,0,0,0,2.25,8V21a.75.75,0,0,0,1.28.53l2.414-2.414a1.246,1.246,0,0,1,.885-.366H17A4.756,4.756,0,0,0,21.75,14V8A4.756,4.756,0,0,0,17,3.25Z"/>
<path id="comment-Regular-2" data-name="comment-Regular" class="cls-1"
d="M17,3.25H7A4.756,4.756,0,0,0,2.25,8V21a.75.75,0,0,0,1.28.53l2.414-2.414a1.246,1.246,0,0,1,.885-.366H17A4.756,4.756,0,0,0,21.75,14V8A4.756,4.756,0,0,0,17,3.25ZM20.25,14A3.254,3.254,0,0,1,17,17.25H6.829a2.73,2.73,0,0,0-1.945.806L3.75,19.189V8A3.254,3.254,0,0,1,7,4.75H17A3.254,3.254,0,0,1,20.25,8Z" />
</g> </g>
</svg> </svg>
<?php comments_popup_link( __( '0', 'zeitfresser' ), __( '1', 'zeitfresser' ), __( '%', 'zeitfresser' ) ); ?> <?php comments_popup_link( __( '0', 'zeitfresser' ), __( '1', 'zeitfresser' ), __( '%', 'zeitfresser' ) ); ?>
</span> </span>
<?php } ?>
</div> </div>
<!-- Featured Image (FALSE → entfernt) -->
<?php if( $show_hide_categories ) { ?> <!-- Article Content -->
<?php $categories = get_the_category();
if( ! empty( $categories ) ) : ?>
<div class="category">
<?php foreach ( $categories as $category ) { ?>
<a
href="<?php echo esc_url( get_category_link( $category->term_id ) ); ?>"><?php echo esc_html( $category->name ); ?></a>
<?php } ?>
</div>
<?php endif; ?>
<?php } ?>
<?php
$page_template = get_page_template_slug( get_queried_object_id() );
if($page_template != 'single-thumbnail.php'){
?>
<?php if ( $show_hide_image && has_post_thumbnail() ) : ?>
<?php $thumbnail_size = get_theme_mod( 'post_detail_featured_image_size', zeitfresser_get_default_post_detail_featured_image_size() ); ?>
<a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark" class="featured-image">
<?php the_post_thumbnail( $thumbnail_size ); ?>
</a>
<?php endif; ?>
<?php } ?>
<article> <article>
<div class="inner-article-content"> <div class="inner-article-content">
<?php echo $toc_payload['content']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> <?php echo $toc_payload['content']; // phpcs:ignore ?>
</div> </div>
<?php <?php
@@ -132,58 +69,9 @@
</div> </div>
<!-- Author Block (FALSE → entfernt) -->
<!-- Categories (FALSE → entfernt) -->
<!-- Tags (FALSE → entfernt) -->
<div class="ifoot info"> <!-- Social Share (FALSE → entfernt) -->
<?php if( $show_hide_social_share && $social_share ) { ?>
<div class="social-share">
<?php get_template_part( 'inc/blocks/includes/template', 'social-share', $social_share ); ?>
</div>
<?php } ?>
<?php if( $show_hide_tags ) { ?>
<div class="tags">
<?php $tags = get_the_tags();
if( ! empty( $tags ) ) : ?>
<?php foreach ( $tags as $post_tag ) { ?>
<a
href="<?php echo esc_url( get_category_link( $post_tag->term_id ) ); ?>"><?php echo esc_html( $post_tag->name ); ?></a>
<?php }
endif; ?>
</div>
<?php } ?>
</div>
<?php if( $show_hide_author_block ) : ?>
<div class="author-post clearfix">
<?php $avatar = get_avatar( get_the_author_meta( 'ID' ), $size = 75 ); ?>
<?php if( $avatar ) : ?>
<div class="author-image">
<a
href="<?php echo esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ); ?>"><?php echo $avatar; ?></a>
</div>
<?php endif; ?>
<div class="author-details">
<h4><a
href="<?php echo esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ); ?>"><?php echo esc_html( get_the_author() ); ?></a>
</h4>
<p><?php echo esc_html( get_the_author_meta('description') ); ?></p>
</div>
</div>
<?php endif; ?>
</div> </div>
+15 -113
View File
@@ -6,133 +6,35 @@
*/ */
?> ?>
<?php
$show_hide_image = get_theme_mod( 'post_snippet_hide_show_featured_image', zeitfresser_get_default_post_snippet_featured_image() );
$show_hide_date = get_theme_mod( 'post_snippet_hide_show_date', zeitfresser_get_default_post_snippet_date() );
$show_hide_author = get_theme_mod( 'post_snippet_hide_show_author', zeitfresser_get_default_post_snippet_author() );
$show_hide_comment = get_theme_mod( 'post_snippet_hide_show_comment', zeitfresser_get_default_post_snippet_comment() );
$show_hide_categories = get_theme_mod( 'post_snippet_hide_show_category', zeitfresser_get_default_post_snippet_category() );
$show_hide_tags = get_theme_mod( 'post_snippet_hide_show_tag', zeitfresser_get_default_post_snippet_tag() );
$show_hide_social_share = get_theme_mod( 'post_snippet_hide_show_social_share', zeitfresser_get_default_post_snippet_social_share() );
$social_share = get_theme_mod( 'post_snippet_social_share_options', zeitfresser_get_default_post_snippet_social_share_options() );
?>
<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>> <div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<div class="news-snippet"> <div class="news-snippet">
<?php if ( $show_hide_image && has_post_thumbnail() ) : ?>
<?php $thumbnail_size = get_theme_mod( 'post_snippet_featured_image_size', zeitfresser_get_default_post_snippet_featured_image_size() ); ?> <?php if ( zeitfresser_show_post_card_featured_image() && has_post_thumbnail() ) : ?>
<a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark" class="featured-image"> <a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark" class="featured-image">
<?php <?php the_post_thumbnail( zeitfresser_get_post_card_thumbnail_size() ); ?>
$size = ! empty( $thumbnail_size ) ? $thumbnail_size : 'zeitfresser-card';
the_post_thumbnail( $size );
?>
</a> </a>
<?php endif; ?> <?php endif; ?>
<div class="summary"> <div class="summary">
<?php if( $show_hide_categories ) { ?>
<?php $categories = get_the_category(); <h3 class="news-title">
if( ! empty( $categories ) ) : ?> <a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark">
<div class="category"> <?php the_title(); ?>
<?php foreach ( $categories as $category ) { ?>
<a
href="<?php echo esc_url( get_category_link( $category->term_id ) ); ?>"><?php echo esc_html( $category->name ); ?></a>
<?php } ?>
</div>
<?php endif; ?>
<?php } ?>
<h3 class="news-title"><a href="<?php echo esc_url( get_permalink() ); ?>"
rel="bookmark"><?php the_title(); ?></a></h3>
<div class="ihead info">
<ul class="list-inline">
<?php if( $show_hide_date ) { ?>
<?php $archive_year = get_the_time('Y'); $archive_month = get_the_time('m'); $archive_day = get_the_time('d'); ?>
<li><a
href="<?php echo esc_url( get_day_link( $archive_year, $archive_month, $archive_day ) ); ?>"><?php echo get_the_date(); ?></a>
</li>
<?php } ?>
<?php
if( $show_hide_author ) { ?>
<li class="post-author">
<a class="url fn n"
href="<?php echo esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ); ?>">
<?php $avatar = get_avatar( get_the_author_meta( 'ID' ), $size = 60 ); ?>
<?php if( $avatar ) : ?>
<div class="author-image">
<?php echo esc_url($avatar); ?>
</div>
<?php endif; ?>
<?php echo esc_html( get_the_author() ); ?>
</a> </a>
</li> </h3>
<?php } ?>
</ul>
<?php if( $show_hide_comment ) { ?>
<span class="comments">
<svg width="20px" height="20px" viewBox="0 0 24 24" id="magicoon-Regular"
xmlns="http://www.w3.org/2000/svg">
<g id="comment-Regular">
<path id="comment-Regular-2" data-name="comment-Regular" class="cls-1"
d="M17,3.25H7A4.756,4.756,0,0,0,2.25,8V21a.75.75,0,0,0,1.28.53l2.414-2.414a1.246,1.246,0,0,1,.885-.366H17A4.756,4.756,0,0,0,21.75,14V8A4.756,4.756,0,0,0,17,3.25ZM20.25,14A3.254,3.254,0,0,1,17,17.25H6.829a2.73,2.73,0,0,0-1.945.806L3.75,19.189V8A3.254,3.254,0,0,1,7,4.75H17A3.254,3.254,0,0,1,20.25,8Z" />
</g>
</svg>
<?php comments_popup_link( __( '0', 'zeitfresser' ), __( '1', 'zeitfresser' ), __( '%', 'zeitfresser' ) ); ?>
</span>
<?php } ?>
</div>
<div class="excerpt"> <div class="excerpt">
<?php $excerpt_length = get_theme_mod( 'post_snippet_excerpt_size', zeitfresser_get_default_post_snippet_excerpt_size() ); ?> <?php echo esc_html(
wp_trim_words(
<?php echo wp_trim_words( get_the_excerpt(), $excerpt_length ); ?> get_the_excerpt(),
zeitfresser_get_post_card_excerpt_length()
)
); ?>
</div> </div>
<?php $readmore_show_hide = get_theme_mod( 'post_snippet_hide_show_readmore', zeitfresser_get_default_post_snippet_show_hide_read_more() ); ?>
<?php $readmore_text = get_theme_mod( 'post_snippet_readmore_text', zeitfresser_get_default_post_snippet_read_more_text() ); ?>
<div class="ifoot info">
<?php if( $readmore_show_hide ) { ?>
<a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark" title=""
class="readmore"><?php echo esc_html( $readmore_text ); ?></a>
<?php } ?>
<?php if( $show_hide_social_share && $social_share ) { ?>
<div class="social-share">
<?php get_template_part( 'inc/blocks/includes/template', 'social-share', $social_share ); ?>
</div>
<?php } ?>
</div> </div>
<?php if( $show_hide_tags ) { ?>
<div class="tags">
<?php $tags = get_the_tags(); ?>
<?php if( ! empty( $tags ) ) : ?>
<?php foreach ( $tags as $post_tag ) { ?>
<a
href="<?php echo esc_url( get_category_link( $post_tag->term_id ) ); ?>"><?php echo esc_html( $post_tag->name ); ?></a>
<?php } ?>
<?php endif; ?>
</div> </div>
<?php } ?>
</div>
</div>
</div> </div>
+15 -31
View File
@@ -3,13 +3,6 @@ $page_template = get_page_template_slug( get_queried_object_id() );
$post_count = 3; $post_count = 3;
?> ?>
<?php
$readmore_show_hide = get_theme_mod( 'post_snippet_hide_show_readmore', zeitfresser_get_default_post_snippet_show_hide_read_more() );
$readmore_text = get_theme_mod( 'post_snippet_readmore_text', zeitfresser_get_default_post_snippet_read_more_text() );
$related_articles_title = get_theme_mod( 'post_detail_related_articles_title', zeitfresser_get_default_post_detail_related_articles_title() );
?>
<div class="related-posts"> <div class="related-posts">
<?php <?php
$args = array( $args = array(
@@ -23,25 +16,24 @@ $post_count = 3;
'update_post_term_cache' => false, 'update_post_term_cache' => false,
); );
$query = new WP_Query( $args ); $query = new WP_Query( $args );
if( $query->have_posts() ) {
if ( $query->have_posts() ) :
?> ?>
<h2 class="main-title"><?php echo esc_html( $related_articles_title ); ?></h2> <h2 class="main-title">Related Posts</h2>
<div class="post-holder"> <div class="post-holder">
<?php while ( $query->have_posts() ) : $query->the_post(); ?> <?php while ( $query->have_posts() ) : $query->the_post(); ?>
<div class="news-snippet"> <div class="news-snippet">
<?php if( get_theme_mod( 'post_snippet_hide_show_featured_image', zeitfresser_get_default_post_snippet_featured_image() ) && has_post_thumbnail() ) : ?> <?php if ( zeitfresser_show_post_card_featured_image() && has_post_thumbnail() ) : ?>
<?php $thumbnail_size = get_theme_mod( 'post_snippet_featured_image_size', zeitfresser_get_default_post_snippet_featured_image_size() ); ?>
<a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark" class="featured-image"> <a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark" class="featured-image">
<?php the_post_thumbnail( $thumbnail_size ); ?> <?php the_post_thumbnail( zeitfresser_get_post_card_thumbnail_size() ); ?>
</a> </a>
<?php endif; ?> <?php endif; ?>
<div class="summary"> <div class="summary">
<h5 class="news-title"> <h5 class="news-title">
<a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark"> <a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark">
<?php the_title(); ?> <?php the_title(); ?>
@@ -49,27 +41,19 @@ $post_count = 3;
</h5> </h5>
<div class="excerpt"> <div class="excerpt">
<?php $excerpt_length = get_theme_mod( 'post_snippet_excerpt_size', zeitfresser_get_default_post_snippet_excerpt_size() ); ?> <?php echo esc_html(
wp_trim_words(
<?php echo wp_trim_words( get_the_excerpt(), $excerpt_length ); ?> get_the_excerpt(),
zeitfresser_get_post_card_excerpt_length()
)
); ?>
</div> </div>
<?php if( $readmore_show_hide ) { ?>
<div class="ifoot info">
<a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark" title=""
class="readmore"><?php echo esc_html( $readmore_text ); ?> </a>
<?php if( get_theme_mod( 'post_snippet_hide_show_social_share', zeitfresser_get_default_post_snippet_social_share() ) ) { ?>
<div class="social-share">
<?php get_template_part( 'inc/blocks/includes/template', 'social-share' ); ?>
</div>
<?php } ?>
</div>
<?php } ?>
</div> </div>
</div> </div>
<?php endwhile; ?> <?php endwhile; ?>
<?php wp_reset_postdata(); ?> <?php wp_reset_postdata(); ?>
</div> </div>
<?php } ?>
<?php endif; ?>
</div> </div>