Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b51ce9ab3f | |||
| 3150f4da51 | |||
| 1020442c06 | |||
| 81ed7efa1a | |||
| 84b2b85bf6 | |||
| 3bcf9f47fb | |||
| 8ec3992945 | |||
| cac17ab39d | |||
| 042547c98f | |||
| 33df5bbe2e | |||
| 798d3f9e1f | |||
| d477c95917 | |||
| 6bf38ae05d | |||
| 36cad12351 | |||
| a9f1e799d4 | |||
| dbdaaff9f2 | |||
| 00341252a1 | |||
| 05de6f2028 | |||
| 4408a738ec | |||
| e5c716740c | |||
| 001390457b | |||
| f479530c4c | |||
| e3302b79a3 | |||
| 2a28d94b4d | |||
| fafc46e007 | |||
| 9f92958651 | |||
| 787cdb31aa | |||
| 7b5bd18dcd | |||
| 937b36b27f | |||
| 76902390e4 | |||
| e1f7db91f8 | |||
| 05c9a70e01 | |||
| af8a9447b0 | |||
| b0eb1d9526 |
Binary file not shown.
|
Before Width: | Height: | Size: 277 KiB |
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 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
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
.ztfr-code {
|
||||
position: relative;
|
||||
background-color: var(--footer-color);
|
||||
color: var(--light-color);
|
||||
padding: 1.1rem 1.2rem;
|
||||
margin: 1rem 0;
|
||||
border-radius: 6px;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
font-family: var(--secondary-font);
|
||||
font-size: 0.870rem;
|
||||
line-height: 1.6;
|
||||
font-weight: 400;
|
||||
border: 1px solid rgba(248, 248, 242, 0.08);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
:root {
|
||||
--light-color: #f7f7fa;
|
||||
--dark-color: #1e1f29;
|
||||
--footer-color: #2f313d;
|
||||
--hover-color: #bd93f9;
|
||||
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #1e1f29;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -59,8 +59,8 @@
|
||||
========================= */
|
||||
|
||||
:root {
|
||||
--primary-font: 'Oswald', var(--zeitfresser-heading-fallback);
|
||||
--secondary-font: 'Roboto', var(--zeitfresser-body-fallback);
|
||||
--primary-font: 'Oswald', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
--secondary-font: 'Roboto', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
|
||||
--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 |
@@ -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);
|
||||
})();
|
||||
@@ -0,0 +1,113 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* 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 el = element.createElement;
|
||||
const __ = i18n.__;
|
||||
const PlainText = blockEditor.PlainText;
|
||||
|
||||
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: function (props) {
|
||||
const content = props.attributes.content || '';
|
||||
|
||||
return el(
|
||||
'div',
|
||||
{
|
||||
className: 'ztfr-code is-editor-preview',
|
||||
'data-language': 'yaml'
|
||||
},
|
||||
el(
|
||||
'pre',
|
||||
{ className: 'language-yaml' },
|
||||
el(PlainText, {
|
||||
tagName: 'code',
|
||||
className: 'language-yaml',
|
||||
value: content,
|
||||
placeholder: __('Write or paste YAML code here…', 'zeitfresser'),
|
||||
onChange: function (value) {
|
||||
props.setAttributes({ content: value });
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
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) {
|
||||
function escapeHtml(text) {
|
||||
return String(text)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
window.tinymce.PluginManager.add('ztfr_code_block', function (editor) {
|
||||
function insertCodeBlock() {
|
||||
const selectedText = editor.selection.getContent({ format: 'text' });
|
||||
const code = selectedText || 'your_key: your_value';
|
||||
const safeCode = escapeHtml(code);
|
||||
|
||||
editor.insertContent(
|
||||
'<pre class="language-yaml"><code class="language-yaml">' +
|
||||
safeCode +
|
||||
'</code></pre><p></p>'
|
||||
);
|
||||
}
|
||||
|
||||
editor.addButton('ztfr_code_block', {
|
||||
text: 'Code',
|
||||
icon: false,
|
||||
onclick: insertCodeBlock
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
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();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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 () {
|
||||
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');
|
||||
@@ -13,7 +29,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
var stickyTop = 100;
|
||||
var headingOffset = 88;
|
||||
var ticking = false;
|
||||
|
||||
var cachedSidebar = null;
|
||||
var tocBottomOffset = null;
|
||||
|
||||
function isDesktop() {
|
||||
@@ -56,6 +72,25 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
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() {
|
||||
if (!isDesktop()) {
|
||||
document.documentElement.style.setProperty('--zeitfresser-toc-top', stickyTop + 'px');
|
||||
@@ -64,36 +99,43 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
return;
|
||||
}
|
||||
|
||||
var titleRect = title.getBoundingClientRect();
|
||||
var scrollTop = window.scrollY || window.pageYOffset || 0;
|
||||
var titleRect = title.getBoundingClientRect();
|
||||
|
||||
var contentColumn = document.querySelector(
|
||||
'.inside-page .main-wrapper > *:first-child, ' +
|
||||
'.inside-page .main-wrapper .primary-content, ' +
|
||||
'.inside-page .main-wrapper #primary, ' +
|
||||
'.inside-page .main-wrapper main'
|
||||
);
|
||||
// 🔥 bessere Content-Erkennung
|
||||
var contentColumn =
|
||||
document.querySelector('.inside-page .main-wrapper > section') ||
|
||||
document.querySelector('#primary') ||
|
||||
document.querySelector('.content-area') ||
|
||||
title;
|
||||
|
||||
var sidebar = document.querySelector(
|
||||
'.inside-page .main-wrapper > aside, ' +
|
||||
'.inside-page .main-wrapper .widget-area, ' +
|
||||
'.inside-page .main-wrapper #secondary, ' +
|
||||
'.inside-page .main-wrapper .sidebar'
|
||||
);
|
||||
if (!contentColumn) return;
|
||||
|
||||
var contentRect = contentColumn ? contentColumn.getBoundingClientRect() : titleRect;
|
||||
var sidebar = getRealSidebar();
|
||||
|
||||
var contentRect = contentColumn.getBoundingClientRect();
|
||||
var sidebarRect = sidebar ? sidebar.getBoundingClientRect() : null;
|
||||
|
||||
var mirroredGap = 56;
|
||||
var gap = 48;
|
||||
|
||||
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);
|
||||
var tocWidth = Math.max(190, Math.min(250, maxWidth));
|
||||
var tocLeft = Math.max(24, Math.round(contentRect.left - mirroredGap - tocWidth));
|
||||
var tocTop = Math.max(stickyTop, Math.round(titleRect.top + scrollTop + 14));
|
||||
// Toc Content Breite
|
||||
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');
|
||||
@@ -165,22 +207,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
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() {
|
||||
if (ticking) return;
|
||||
|
||||
@@ -190,11 +216,43 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
syncPosition();
|
||||
handleFooterCollision();
|
||||
updateProgress();
|
||||
updateActiveHeading();
|
||||
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) {
|
||||
link.addEventListener('click', function (event) {
|
||||
var target = getTarget(link);
|
||||
@@ -208,8 +266,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
top: top,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
setActiveLink(target.id);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -232,7 +288,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
syncPosition();
|
||||
handleFooterCollision();
|
||||
updateProgress();
|
||||
updateActiveHeading();
|
||||
initIntersectionObserver();
|
||||
|
||||
requestAnimationFrame(function () {
|
||||
toc.classList.add('is-visible');
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -5,7 +5,10 @@
|
||||
* @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">
|
||||
|
||||
+512
-300
@@ -9,32 +9,115 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Theme Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
if ( ! defined( 'ZEITFRESSER_VERSION' ) ) {
|
||||
define( 'ZEITFRESSER_VERSION', '2.3.6' );
|
||||
}
|
||||
|
||||
if ( ! defined( 'DAISY_BLOG_VERSION' ) ) {
|
||||
define( 'DAISY_BLOG_VERSION', ZEITFRESSER_VERSION );
|
||||
if ( ! defined( 'ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION' ) ) {
|
||||
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';
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Customizer
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
require get_template_directory() . '/inc/customizer/core-settings.php';
|
||||
require get_template_directory() . '/inc/customizer/general-settings.php';
|
||||
require get_template_directory() . '/inc/customizer/layout-settings.php';
|
||||
require get_template_directory() . '/inc/customizer/toc-settings.php';
|
||||
require get_template_directory() . '/inc/customizer/social-settings.php';
|
||||
require get_template_directory() . '/inc/customizer/image-optimizer-settings.php';
|
||||
|
||||
/**
|
||||
* Theme setup.
|
||||
*
|
||||
* @return void
|
||||
* ------------------------------------------------------------------------
|
||||
* Utilities
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
require get_template_directory() . '/inc/utilities/helpers.php';
|
||||
require get_template_directory() . '/inc/utilities/template-tags.php';
|
||||
require get_template_directory() . '/inc/utilities/template-functions.php';
|
||||
require get_template_directory() . '/inc/utilities/pagination.php';
|
||||
require get_template_directory() . '/inc/utilities/toc.php';
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Tools
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
require get_template_directory() . '/inc/tools/image-optimizer.php';
|
||||
require get_template_directory() . '/inc/tools/code-block.php';
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Upload Handling (Original File Tracking)
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
function zeitfresser_capture_original_upload( $upload, $context ) {
|
||||
|
||||
if ( empty( $upload['file'] ) ) {
|
||||
return $upload;
|
||||
}
|
||||
|
||||
// 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( 'add_attachment', 'zeitfresser_store_original_file' );
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Theme Setup
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
function zeitfresser_setup() {
|
||||
|
||||
load_theme_textdomain( 'zeitfresser', get_template_directory() . '/languages' );
|
||||
|
||||
add_theme_support( 'automatic-feed-links' );
|
||||
add_theme_support( 'title-tag' );
|
||||
add_theme_support( 'post-thumbnails' );
|
||||
add_theme_support(
|
||||
'html5',
|
||||
array(
|
||||
|
||||
add_theme_support( 'html5', array(
|
||||
'search-form',
|
||||
'comment-form',
|
||||
'comment-list',
|
||||
@@ -42,36 +125,42 @@ function zeitfresser_setup() {
|
||||
'caption',
|
||||
'style',
|
||||
'script',
|
||||
)
|
||||
);
|
||||
));
|
||||
|
||||
add_theme_support( 'customize-selective-refresh-widgets' );
|
||||
add_theme_support(
|
||||
'custom-logo',
|
||||
array(
|
||||
|
||||
add_theme_support( 'custom-logo', array(
|
||||
'height' => 250,
|
||||
'width' => 250,
|
||||
'flex-width' => true,
|
||||
'flex-height' => true,
|
||||
)
|
||||
);
|
||||
));
|
||||
|
||||
add_theme_support( 'align-wide' );
|
||||
add_theme_support( 'wp-block-styles' );
|
||||
add_theme_support( 'responsive-embeds' );
|
||||
|
||||
register_nav_menus(
|
||||
array(
|
||||
register_nav_menus( array(
|
||||
'menu-1' => esc_html__( 'Primary', 'zeitfresser' ),
|
||||
)
|
||||
);
|
||||
|
||||
add_editor_style( 'editor-style.css' );
|
||||
));
|
||||
}
|
||||
add_action( 'after_setup_theme', 'zeitfresser_setup' );
|
||||
|
||||
/**
|
||||
* Set the content width in pixels.
|
||||
*
|
||||
* @return void
|
||||
* ------------------------------------------------------------------------
|
||||
* Image Sizes
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
function zeitfresser_custom_image_sizes() {
|
||||
add_image_size( 'zeitfresser-content', 720, 0, false );
|
||||
add_image_size( 'zeitfresser-card', 480, 0, false );
|
||||
}
|
||||
add_action( 'after_setup_theme', 'zeitfresser_custom_image_sizes' );
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Content Width
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
function zeitfresser_content_width() {
|
||||
$GLOBALS['content_width'] = apply_filters( 'zeitfresser_content_width', 640 );
|
||||
@@ -79,13 +168,12 @@ function zeitfresser_content_width() {
|
||||
add_action( 'after_setup_theme', 'zeitfresser_content_width', 0 );
|
||||
|
||||
/**
|
||||
* Register widget area.
|
||||
*
|
||||
* @return void
|
||||
* ------------------------------------------------------------------------
|
||||
* Widgets
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
function zeitfresser_widgets_init() {
|
||||
register_sidebar(
|
||||
array(
|
||||
register_sidebar( array(
|
||||
'name' => esc_html__( 'Sidebar', 'zeitfresser' ),
|
||||
'id' => 'sidebar-1',
|
||||
'description' => esc_html__( 'Add widgets here.', 'zeitfresser' ),
|
||||
@@ -93,223 +181,132 @@ function zeitfresser_widgets_init() {
|
||||
'after_widget' => '</section>',
|
||||
'before_title' => '<h4 class="widget-title">',
|
||||
'after_title' => '</h4>',
|
||||
)
|
||||
);
|
||||
));
|
||||
}
|
||||
add_action( 'widgets_init', 'zeitfresser_widgets_init' );
|
||||
|
||||
/**
|
||||
* Enqueue floating TOC assets on single posts.
|
||||
*
|
||||
* @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
|
||||
* ------------------------------------------------------------------------
|
||||
* Assets
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
function zeitfresser_scripts() {
|
||||
|
||||
// Base stylesheet (theme root)
|
||||
wp_enqueue_style(
|
||||
'zeitfresser',
|
||||
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
|
||||
);
|
||||
|
||||
// Styles
|
||||
$fonts = zeitfresser_asset_versioned('/css/fonts.css');
|
||||
$colors = zeitfresser_asset_versioned('/css/colors.css');
|
||||
|
||||
wp_enqueue_style(
|
||||
'zeitfresser-fonts',
|
||||
$fonts['url'],
|
||||
[],
|
||||
$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(
|
||||
'zeitfresser-navigation',
|
||||
get_template_directory_uri() . '/js/navigation.js',
|
||||
array(),
|
||||
zeitfresser_asset_version( '/js/navigation.js' ),
|
||||
$nav['url'],
|
||||
[],
|
||||
$nav['version'],
|
||||
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(
|
||||
'zeitfresser-scripts',
|
||||
get_template_directory_uri() . '/js/scripts.js',
|
||||
array(),
|
||||
zeitfresser_asset_version( '/js/scripts.js' ),
|
||||
$scripts['url'],
|
||||
[],
|
||||
$scripts['version'],
|
||||
true
|
||||
);
|
||||
|
||||
// WordPress native threaded comments
|
||||
if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
|
||||
wp_enqueue_script( 'comment-reply' );
|
||||
}
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'zeitfresser_scripts', 10 );
|
||||
|
||||
// Editor Styles
|
||||
function zeitfresser_editor_styles_setup() {
|
||||
add_theme_support( 'editor-styles' );
|
||||
add_editor_style( 'assets/css/editor.css' );
|
||||
}
|
||||
add_action( 'after_setup_theme', 'zeitfresser_editor_styles_setup' );
|
||||
|
||||
/**
|
||||
* Load RTL stylesheet from /css folder
|
||||
* ------------------------------------------------------------------------
|
||||
* Performance Tweaks
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
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')
|
||||
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Defer non-critical JavaScript
|
||||
* ------------------------------------------------------------------------
|
||||
*
|
||||
* Prevents JS from blocking page rendering.
|
||||
*/
|
||||
function zeitfresser_defer_scripts( $tag, $handle, $src ) {
|
||||
|
||||
$defer_scripts = array(
|
||||
'zeitfresser-navigation',
|
||||
'zeitfresser-scripts',
|
||||
);
|
||||
}
|
||||
}
|
||||
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
|
||||
);
|
||||
if ( in_array( $handle, $defer_scripts, true ) ) {
|
||||
return str_replace( ' src=', ' defer src=', $tag );
|
||||
}
|
||||
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
|
||||
);
|
||||
return $tag;
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'zeitfresser_enqueue_static_fonts', 15 );
|
||||
add_filter( 'script_loader_tag', 'zeitfresser_defer_scripts', 10, 3 );
|
||||
|
||||
/**
|
||||
* Theme package marker kept for compatibility with the original premium controls.
|
||||
* ------------------------------------------------------------------------
|
||||
* Image Loading Optimization (LCP + Lazy Loading)
|
||||
* ------------------------------------------------------------------------
|
||||
*
|
||||
* @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 local fonts for better performance
|
||||
*/
|
||||
function zeitfresser_preload_fonts() {
|
||||
?>
|
||||
<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-500.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>
|
||||
<link rel="preload" href="<?php echo get_template_directory_uri(); ?>/fonts/roboto-500.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="<?php echo get_template_directory_uri(); ?>/fonts/roboto-700.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<?php
|
||||
}
|
||||
add_action('wp_head', 'zeitfresser_preload_fonts', 1);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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 ) {
|
||||
if ( empty( $attr['decoding'] ) ) {
|
||||
$attr['decoding'] = 'async';
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
if ( empty( $attr['loading'] ) && ! is_admin() ) {
|
||||
$attr['loading'] = 'lazy';
|
||||
// Improve decoding performance
|
||||
$attr['decoding'] = 'async';
|
||||
}
|
||||
|
||||
return $attr;
|
||||
@@ -317,13 +314,21 @@ function zeitfresser_optimize_image_attributes( $attr, $attachment, $size ) {
|
||||
add_filter( 'wp_get_attachment_image_attributes', 'zeitfresser_optimize_image_attributes', 10, 3 );
|
||||
|
||||
/**
|
||||
* Lower the threshold for WordPress scaled originals.
|
||||
* Lower the threshold for WordPress scaled originals when auto optimization is enabled.
|
||||
*
|
||||
* This prevents very large uploads from shipping oversized source images.
|
||||
* When automatic optimization is disabled, original uploads should remain untouched.
|
||||
*
|
||||
* @return int
|
||||
* @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' );
|
||||
@@ -335,44 +340,23 @@ add_filter( 'big_image_size_threshold', 'zeitfresser_big_image_size_threshold' )
|
||||
* @return array
|
||||
*/
|
||||
function zeitfresser_filter_intermediate_image_sizes( $sizes ) {
|
||||
unset( $sizes['1536x1536'], $sizes['2048x2048'] );
|
||||
|
||||
$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' );
|
||||
|
||||
/**
|
||||
* Convert generated JPEG and PNG sub-sizes to WebP when supported by the server.
|
||||
*
|
||||
* @param array $formats Output format map.
|
||||
* @return array
|
||||
*/
|
||||
function zeitfresser_image_output_format( $formats ) {
|
||||
if ( function_exists( 'wp_image_editor_supports' ) && 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' );
|
||||
|
||||
/**
|
||||
* 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' ) {
|
||||
if ( 'image/png' === $mime_type ) {
|
||||
return $quality;
|
||||
}
|
||||
|
||||
return 82;
|
||||
}
|
||||
add_filter( 'wp_editor_set_quality', 'zeitfresser_image_quality', 10, 2 );
|
||||
|
||||
/**
|
||||
* Improve attachment image attributes for layout stability and fetch priority.
|
||||
*
|
||||
@@ -382,6 +366,7 @@ add_filter( 'wp_editor_set_quality', 'zeitfresser_image_quality', 10, 2 );
|
||||
* @return array
|
||||
*/
|
||||
function zeitfresser_improve_attachment_dimensions( $attr, $attachment, $size ) {
|
||||
|
||||
if ( empty( $attr['width'] ) || empty( $attr['height'] ) ) {
|
||||
$metadata = wp_get_attachment_metadata( $attachment->ID );
|
||||
|
||||
@@ -399,7 +384,7 @@ function zeitfresser_improve_attachment_dimensions( $attr, $attachment, $size )
|
||||
if ( empty( $attr['fetchpriority'] ) && ! is_admin() && ! is_feed() ) {
|
||||
static $did_set_high_priority = false;
|
||||
|
||||
if ( ! $did_set_high_priority && ( is_home() || is_front_page() || is_archive() || is_search() || is_singular() ) ) {
|
||||
if ( ! $did_set_high_priority && is_singular() ) {
|
||||
$attr['fetchpriority'] = 'high';
|
||||
$did_set_high_priority = true;
|
||||
}
|
||||
@@ -409,81 +394,308 @@ function zeitfresser_improve_attachment_dimensions( $attr, $attachment, $size )
|
||||
}
|
||||
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);
|
||||
|
||||
/**
|
||||
* Preload the most likely LCP image for archive and singular views.
|
||||
* 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)
|
||||
* ------------------------------------------------------------------------
|
||||
*
|
||||
* @param array $resources Existing preload resources.
|
||||
* 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' );
|
||||
|
||||
/**
|
||||
* 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_preload_resources( $resources ) {
|
||||
if ( is_admin() || is_feed() || is_embed() ) {
|
||||
return $resources;
|
||||
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;
|
||||
}
|
||||
|
||||
$image_url = '';
|
||||
$image_type = '';
|
||||
if ( function_exists( 'wp_image_editor_supports' ) ) {
|
||||
|
||||
if ( is_singular() ) {
|
||||
$object_id = get_queried_object_id();
|
||||
// Prefer AVIF if supported.
|
||||
if ( wp_image_editor_supports( array( 'mime_type' => 'image/avif' ) ) ) {
|
||||
$formats['image/jpeg'] = 'image/avif';
|
||||
$formats['image/png'] = 'image/avif';
|
||||
|
||||
if ( $object_id && has_post_thumbnail( $object_id ) ) {
|
||||
$image_url = get_the_post_thumbnail_url( $object_id, 'large' );
|
||||
}
|
||||
} elseif ( is_home() || is_front_page() || is_archive() || is_search() ) {
|
||||
global $wp_query;
|
||||
|
||||
if ( isset( $wp_query->posts[0]->ID ) && has_post_thumbnail( $wp_query->posts[0]->ID ) ) {
|
||||
$image_url = get_the_post_thumbnail_url( $wp_query->posts[0]->ID, 'thumbnail' );
|
||||
// Fallback to WebP.
|
||||
} elseif ( wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
|
||||
$formats['image/jpeg'] = 'image/webp';
|
||||
$formats['image/png'] = 'image/webp';
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $image_url ) ) {
|
||||
return $resources;
|
||||
return $formats;
|
||||
}
|
||||
|
||||
$extension = strtolower( pathinfo( wp_parse_url( $image_url, PHP_URL_PATH ), PATHINFO_EXTENSION ) );
|
||||
|
||||
if ( 'jpg' === $extension || 'jpeg' === $extension ) {
|
||||
$image_type = 'image/jpeg';
|
||||
} elseif ( 'png' === $extension ) {
|
||||
$image_type = 'image/png';
|
||||
} elseif ( 'webp' === $extension ) {
|
||||
$image_type = 'image/webp';
|
||||
}
|
||||
|
||||
$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' );
|
||||
add_filter( 'image_editor_output_format', 'zeitfresser_image_output_format' );
|
||||
|
||||
/**
|
||||
* Improve script loading strategy for non-critical assets.
|
||||
* Mark images as optimized only when optimization is actually active.
|
||||
*
|
||||
* @param array $tag Script tag markup.
|
||||
* @param string $handle Script handle.
|
||||
* @param string $src Script source URL.
|
||||
* @return string
|
||||
* @param array $metadata Attachment metadata.
|
||||
* @param int $attachment_id Attachment ID.
|
||||
* @return array
|
||||
*/
|
||||
function zeitfresser_defer_non_critical_scripts( $tag, $handle, $src ) {
|
||||
$deferred_handles = array(
|
||||
'zeitfresser-navigation',
|
||||
'zeitfresser-masonry',
|
||||
'zeitfresser-scripts',
|
||||
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
|
||||
);
|
||||
|
||||
if ( in_array( $handle, $deferred_handles, true ) && false === strpos( $tag, ' defer' ) ) {
|
||||
return str_replace( ' src=', ' defer src=', $tag );
|
||||
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;
|
||||
}
|
||||
|
||||
return $tag;
|
||||
// Feature toggle
|
||||
if ( ! get_theme_mod( 'ztfr_auto_optimize', true ) ) {
|
||||
return $metadata;
|
||||
}
|
||||
add_filter( 'script_loader_tag', 'zeitfresser_defer_non_critical_scripts', 10, 3 );
|
||||
|
||||
$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 );
|
||||
|
||||
+22
-19
@@ -2,13 +2,8 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
?>
|
||||
<!doctype html>
|
||||
<html <?php language_attributes(); ?>>
|
||||
@@ -23,46 +18,51 @@
|
||||
|
||||
<body <?php body_class(); ?>>
|
||||
<?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">
|
||||
|
||||
<div class="header-wrapper">
|
||||
<div class="container">
|
||||
<div class="site-header-wrapper">
|
||||
|
||||
<div class="site-branding">
|
||||
|
||||
<?php the_custom_logo(); ?>
|
||||
|
||||
<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">
|
||||
<a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home"
|
||||
class="logo"><?php bloginfo( 'name' ); ?></a>
|
||||
<a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home" class="logo">
|
||||
<?php bloginfo( 'name' ); ?>
|
||||
</a>
|
||||
</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><!-- .site-branding -->
|
||||
|
||||
<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>
|
||||
</button>
|
||||
|
||||
<?php
|
||||
wp_nav_menu(
|
||||
array(
|
||||
@@ -71,11 +71,14 @@
|
||||
)
|
||||
);
|
||||
?>
|
||||
</nav><!-- #site-navigation -->
|
||||
</nav>
|
||||
|
||||
<?php get_template_part( 'template-parts/social', 'links' ); ?>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</header><!-- #masthead -->
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
);
|
||||
} );
|
||||
|
||||
} );
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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++,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 );
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
@@ -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' );
|
||||
|
||||
}
|
||||
@@ -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; ?>&url=<?php echo $url; ?>&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; ?>&media=<?php echo $media; ?>&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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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' );
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
) );
|
||||
|
||||
}
|
||||
@@ -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',
|
||||
) ) );
|
||||
|
||||
}
|
||||
@@ -1,16 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* daisy blog Theme Customizer
|
||||
* Theme Customizer Core
|
||||
*
|
||||
* @package zeitfresser
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add postMessage support for site title and description for the Theme Customizer.
|
||||
*
|
||||
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
|
||||
*/
|
||||
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';
|
||||
@@ -23,6 +20,7 @@ function zeitfresser_customize_register( $wp_customize ) {
|
||||
'render_callback' => 'zeitfresser_customize_partial_blogname',
|
||||
)
|
||||
);
|
||||
|
||||
$wp_customize->selective_refresh->add_partial(
|
||||
'blogdescription',
|
||||
array(
|
||||
@@ -35,27 +33,26 @@ function zeitfresser_customize_register( $wp_customize ) {
|
||||
add_action( 'customize_register', 'zeitfresser_customize_register' );
|
||||
|
||||
/**
|
||||
* Render the site title for the selective refresh partial.
|
||||
*
|
||||
* @return void
|
||||
* Partial refresh helpers
|
||||
*/
|
||||
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' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds JS handlers to make Theme Customizer preview reload changes asynchronously.
|
||||
* 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 );
|
||||
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' );
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -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'
|
||||
);
|
||||
@@ -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>';
|
||||
}
|
||||
@@ -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++,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -1,272 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Performance tools for existing media and webfonts.
|
||||
*
|
||||
* @package zeitfresser
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the performance tools admin page.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function zeitfresser_register_performance_tools_page() {
|
||||
add_theme_page(
|
||||
esc_html__( 'Performance Tools', 'zeitfresser' ),
|
||||
esc_html__( 'Performance Tools', 'zeitfresser' ),
|
||||
'manage_options',
|
||||
'zeitfresser-performance-tools',
|
||||
'zeitfresser_render_performance_tools_page'
|
||||
);
|
||||
}
|
||||
add_action( 'admin_menu', 'zeitfresser_register_performance_tools_page' );
|
||||
|
||||
/**
|
||||
* Count attachments still waiting for one-time optimization.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function zeitfresser_get_pending_legacy_images_count() {
|
||||
$query = new WP_Query(
|
||||
array(
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => 'inherit',
|
||||
'post_mime_type' => 'image',
|
||||
'fields' => 'ids',
|
||||
'posts_per_page' => 1,
|
||||
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||
array(
|
||||
'key' => '_zeitfresser_media_optimized',
|
||||
'compare' => 'NOT EXISTS',
|
||||
),
|
||||
),
|
||||
'no_found_rows' => false,
|
||||
'cache_results' => false,
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
)
|
||||
);
|
||||
|
||||
return (int) $query->found_posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a batch of legacy images with current thumbnail/webp rules.
|
||||
*
|
||||
* @param int $batch_size Number of images to process.
|
||||
* @return array
|
||||
*/
|
||||
function zeitfresser_process_legacy_images_batch( $batch_size = 20 ) {
|
||||
$results = array(
|
||||
'processed' => 0,
|
||||
'updated' => 0,
|
||||
'skipped' => 0,
|
||||
'errors' => 0,
|
||||
);
|
||||
|
||||
$query = new WP_Query(
|
||||
array(
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => 'inherit',
|
||||
'post_mime_type' => 'image',
|
||||
'fields' => 'ids',
|
||||
'posts_per_page' => absint( $batch_size ),
|
||||
'orderby' => 'ID',
|
||||
'order' => 'ASC',
|
||||
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||
array(
|
||||
'key' => '_zeitfresser_media_optimized',
|
||||
'compare' => 'NOT EXISTS',
|
||||
),
|
||||
),
|
||||
'no_found_rows' => true,
|
||||
'cache_results' => false,
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
)
|
||||
);
|
||||
|
||||
if ( empty( $query->posts ) ) {
|
||||
return $results;
|
||||
}
|
||||
|
||||
foreach ( $query->posts as $attachment_id ) {
|
||||
$results['processed']++;
|
||||
|
||||
$file = get_attached_file( $attachment_id );
|
||||
|
||||
if ( empty( $file ) || ! file_exists( $file ) ) {
|
||||
update_post_meta( $attachment_id, '_zeitfresser_media_optimized', 'missing' );
|
||||
$results['skipped']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$metadata = wp_generate_attachment_metadata( $attachment_id, $file );
|
||||
|
||||
if ( is_wp_error( $metadata ) || empty( $metadata ) ) {
|
||||
$results['errors']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
wp_update_attachment_metadata( $attachment_id, $metadata );
|
||||
update_post_meta( $attachment_id, '_zeitfresser_media_optimized', current_time( 'mysql' ) );
|
||||
$results['updated']++;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle admin actions for performance tools.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function zeitfresser_handle_performance_tools_actions() {
|
||||
if ( ! is_admin() || ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $_GET['page'] ) || 'zeitfresser-performance-tools' !== $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $_GET['zeitfresser_action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
return;
|
||||
}
|
||||
|
||||
check_admin_referer( 'zeitfresser_performance_tools' );
|
||||
|
||||
$action = sanitize_key( wp_unslash( $_GET['zeitfresser_action'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
if ( 'optimize_legacy_images' === $action ) {
|
||||
$results = zeitfresser_process_legacy_images_batch( 25 );
|
||||
$args = array(
|
||||
'page' => 'zeitfresser-performance-tools',
|
||||
'processed' => $results['processed'],
|
||||
'updated' => $results['updated'],
|
||||
'skipped' => $results['skipped'],
|
||||
'errors' => $results['errors'],
|
||||
);
|
||||
|
||||
wp_safe_redirect( add_query_arg( $args, admin_url( 'themes.php' ) ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( 'reset_legacy_images' === $action ) {
|
||||
$query = new WP_Query(
|
||||
array(
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => 'inherit',
|
||||
'post_mime_type' => 'image',
|
||||
'fields' => 'ids',
|
||||
'posts_per_page' => -1,
|
||||
'meta_key' => '_zeitfresser_media_optimized', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
'no_found_rows' => true,
|
||||
'cache_results' => false,
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $query->posts as $attachment_id ) {
|
||||
delete_post_meta( $attachment_id, '_zeitfresser_media_optimized' );
|
||||
}
|
||||
|
||||
wp_safe_redirect(
|
||||
add_query_arg(
|
||||
array(
|
||||
'page' => 'zeitfresser-performance-tools',
|
||||
'reset' => count( $query->posts ),
|
||||
),
|
||||
admin_url( 'themes.php' )
|
||||
)
|
||||
);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
add_action( 'admin_init', 'zeitfresser_handle_performance_tools_actions' );
|
||||
|
||||
/**
|
||||
* Render the performance tools page.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function zeitfresser_render_performance_tools_page() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pending = zeitfresser_get_pending_legacy_images_count();
|
||||
$local = function_exists( 'zeitfresser_get_local_webfonts_css' ) ? zeitfresser_get_local_webfont_urls( zeitfresser_get_local_webfonts_css() ) : array();
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e( 'Zeitfresser Performance Tools', 'zeitfresser' ); ?></h1>
|
||||
<p><?php esc_html_e( 'Use these tools after major performance updates to warm local fonts and reprocess older uploads with the current image rules.', 'zeitfresser' ); ?></p>
|
||||
|
||||
<?php if ( isset( $_GET['updated'] ) || isset( $_GET['reset'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>
|
||||
<div class="notice notice-success is-dismissible">
|
||||
<p>
|
||||
<?php
|
||||
if ( isset( $_GET['reset'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
printf(
|
||||
esc_html__( 'Reset complete. %d legacy image markers removed.', 'zeitfresser' ),
|
||||
absint( $_GET['reset'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
);
|
||||
} else {
|
||||
printf(
|
||||
esc_html__( 'Batch finished. Processed: %1$d, updated: %2$d, skipped: %3$d, errors: %4$d.', 'zeitfresser' ),
|
||||
absint( $_GET['processed'] ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
absint( $_GET['updated'] ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
absint( $_GET['skipped'] ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
absint( $_GET['errors'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
);
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card" style="max-width: 880px; padding: 20px;">
|
||||
<h2><?php esc_html_e( 'Legacy Image Optimization', 'zeitfresser' ); ?></h2>
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
esc_html__( '%d image attachments have not been reprocessed with the current size, quality and WebP rules yet.', 'zeitfresser' ),
|
||||
(int) $pending
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
<p><?php esc_html_e( 'Run the batch button repeatedly until the counter reaches zero. This keeps each request small and safe on shared hosting.', 'zeitfresser' ); ?></p>
|
||||
<p>
|
||||
<a class="button button-primary" href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'page' => 'zeitfresser-performance-tools', 'zeitfresser_action' => 'optimize_legacy_images' ), admin_url( 'themes.php' ) ), 'zeitfresser_performance_tools' ) ); ?>">
|
||||
<?php esc_html_e( 'Process Next 25 Images', 'zeitfresser' ); ?>
|
||||
</a>
|
||||
<a class="button" href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'page' => 'zeitfresser-performance-tools', 'zeitfresser_action' => 'reset_legacy_images' ), admin_url( 'themes.php' ) ), 'zeitfresser_performance_tools' ) ); ?>">
|
||||
<?php esc_html_e( 'Reset Progress', 'zeitfresser' ); ?>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card" style="max-width: 880px; padding: 20px; margin-top: 20px;">
|
||||
<h2><?php esc_html_e( 'Local Font Warmup', 'zeitfresser' ); ?></h2>
|
||||
<p><?php esc_html_e( 'Zeitfresser now prefers locally cached Google font files for the currently selected font families. When the cache is available, the theme preloads the local files and no longer needs the external stylesheet request.', 'zeitfresser' ); ?></p>
|
||||
<p>
|
||||
<?php
|
||||
if ( empty( $local ) ) {
|
||||
esc_html_e( 'Local font files are not cached yet. Visit the front-end once after saving your font settings to warm the cache.', 'zeitfresser' );
|
||||
} else {
|
||||
printf(
|
||||
esc_html__( 'Local font files ready: %d preloadable assets detected.', 'zeitfresser' ),
|
||||
count( $local )
|
||||
);
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
<?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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
$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 );
|
||||
@@ -0,0 +1,801 @@
|
||||
<?php
|
||||
/**
|
||||
* Image Optimizer
|
||||
*
|
||||
* @package zeitfresser
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
function zeitfresser_get_pending_legacy_images_count() {
|
||||
$query = new WP_Query([
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => 'inherit',
|
||||
'post_mime_type' => 'image',
|
||||
'fields' => 'ids',
|
||||
'posts_per_page' => 1,
|
||||
'meta_query' => [
|
||||
'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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count total images
|
||||
*/
|
||||
function zeitfresser_get_total_images_count() {
|
||||
$query = new WP_Query([
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => 'inherit',
|
||||
'post_mime_type' => 'image',
|
||||
'fields' => 'ids',
|
||||
'posts_per_page' => 1,
|
||||
'no_found_rows' => false,
|
||||
]);
|
||||
|
||||
return (int) $query->found_posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* NEW: Cleanup counters (ONLY ADDITIVE)
|
||||
*/
|
||||
function zeitfresser_get_total_originals_count() {
|
||||
$query = new WP_Query([
|
||||
'post_type'=>'attachment',
|
||||
'post_status'=>'inherit',
|
||||
'post_mime_type'=>'image',
|
||||
'posts_per_page'=>1,
|
||||
'fields'=>'ids',
|
||||
'meta_query'=>[
|
||||
['key'=>'_zeitfresser_original_file','compare'=>'EXISTS']
|
||||
],
|
||||
'no_found_rows'=>false
|
||||
]);
|
||||
return (int) $query->found_posts;
|
||||
}
|
||||
|
||||
function zeitfresser_get_remaining_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',
|
||||
],
|
||||
[
|
||||
'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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of original-format files belonging to one attachment.
|
||||
*
|
||||
* This includes:
|
||||
* - the original uploaded file
|
||||
* - the original-format main generated file (e.g. scaled JPG)
|
||||
* - original-format sub-size files derived from attachment metadata
|
||||
*
|
||||
* @param int $attachment_id Attachment ID.
|
||||
* @param string $original Absolute path to the original uploaded file.
|
||||
* @return array
|
||||
*/
|
||||
function zeitfresser_get_original_family_files( $attachment_id, $original ) {
|
||||
|
||||
$files = [];
|
||||
|
||||
if ( empty( $original ) ) {
|
||||
return $files;
|
||||
}
|
||||
|
||||
$original_ext = strtolower( pathinfo( $original, PATHINFO_EXTENSION ) );
|
||||
$base_name = pathinfo( $original, PATHINFO_FILENAME );
|
||||
$dir = dirname( $original );
|
||||
|
||||
if ( empty( $original_ext ) ) {
|
||||
return $files;
|
||||
}
|
||||
|
||||
// Original
|
||||
$files[] = $original;
|
||||
|
||||
// --- ORIGINAL LOGIC (metadata-based) ---
|
||||
$metadata = wp_get_attachment_metadata( $attachment_id );
|
||||
$upload_dir = wp_get_upload_dir();
|
||||
|
||||
if ( ! empty( $metadata['file'] ) ) {
|
||||
|
||||
$current_main_absolute = trailingslashit( $upload_dir['basedir'] ) . $metadata['file'];
|
||||
|
||||
// scaled/main
|
||||
$files[] = preg_replace(
|
||||
'/\.[^.]+$/',
|
||||
'.' . $original_ext,
|
||||
$current_main_absolute
|
||||
);
|
||||
|
||||
$current_dir = dirname( $current_main_absolute );
|
||||
|
||||
if ( ! empty( $metadata['sizes'] ) && is_array( $metadata['sizes'] ) ) {
|
||||
foreach ( $metadata['sizes'] as $size ) {
|
||||
if ( empty( $size['file'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$files[] = $current_dir . '/' . preg_replace(
|
||||
'/\.[^.]+$/',
|
||||
'.' . $original_ext,
|
||||
$size['file']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- NEW: FILESYSTEM FALLBACK (CRITICAL FIX) ---
|
||||
foreach ( glob( $dir . '/' . $base_name . '*.' . $original_ext ) as $file ) {
|
||||
$files[] = $file;
|
||||
}
|
||||
|
||||
return array_values( array_unique( array_filter( $files ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether any original-format family files still exist.
|
||||
*
|
||||
* @param int $attachment_id Attachment ID.
|
||||
* @param string $original Absolute path to the original uploaded file.
|
||||
* @return bool
|
||||
*/
|
||||
function zeitfresser_original_family_exists( $attachment_id, $original ) {
|
||||
|
||||
$files = zeitfresser_get_original_family_files( $attachment_id, $original );
|
||||
|
||||
foreach ( $files as $file ) {
|
||||
if ( file_exists( $file ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all original-format files belonging to one attachment.
|
||||
*
|
||||
* @param int $attachment_id Attachment ID.
|
||||
* @param string $original Absolute path to the original uploaded file.
|
||||
* @return int Number of deleted files.
|
||||
*/
|
||||
function zeitfresser_delete_original_family_files( $attachment_id, $original ) {
|
||||
|
||||
$deleted_files = 0;
|
||||
$files = zeitfresser_get_original_family_files( $attachment_id, $original );
|
||||
|
||||
foreach ( $files as $file ) {
|
||||
if ( ! file_exists( $file ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! is_writable( $file ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( unlink( $file ) ) {
|
||||
$deleted_files++;
|
||||
}
|
||||
}
|
||||
|
||||
return $deleted_files;
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE ORIGINALS (manual batch)
|
||||
*/
|
||||
function zeitfresser_delete_originals_batch( $batch_size = 10 ) {
|
||||
|
||||
$deleted = 0;
|
||||
|
||||
$query = new WP_Query([
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => 'inherit',
|
||||
'post_mime_type' => 'image',
|
||||
'fields' => 'ids',
|
||||
'posts_per_page' => $batch_size,
|
||||
'meta_query' => [
|
||||
'relation' => 'AND',
|
||||
[
|
||||
'key' => '_zeitfresser_original_file',
|
||||
'compare' => 'EXISTS',
|
||||
],
|
||||
[
|
||||
'key' => '_zeitfresser_original_deleted',
|
||||
'compare' => 'NOT EXISTS',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
foreach ( $query->posts as $attachment_id ) {
|
||||
|
||||
$original = get_post_meta( $attachment_id, '_zeitfresser_original_file', true );
|
||||
|
||||
if ( ! $original ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$optimized_version = get_post_meta(
|
||||
$attachment_id,
|
||||
'_zeitfresser_media_optimized_version',
|
||||
true
|
||||
);
|
||||
|
||||
if ( ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION !== $optimized_version ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ext = strtolower( pathinfo( $original, PATHINFO_EXTENSION ) );
|
||||
|
||||
if ( in_array( $ext, [ 'webp', 'avif' ], true ) ) {
|
||||
update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Nothing left to delete -> mark as done.
|
||||
if ( ! zeitfresser_original_family_exists( $attachment_id, $original ) ) {
|
||||
update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
|
||||
continue;
|
||||
}
|
||||
|
||||
zeitfresser_delete_original_family_files( $attachment_id, $original );
|
||||
|
||||
// Only mark as deleted when the full original family is gone.
|
||||
if ( ! zeitfresser_original_family_exists( $attachment_id, $original ) ) {
|
||||
$deleted++;
|
||||
update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
|
||||
}
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimizer batch for manual processing.
|
||||
*
|
||||
* Manual optimization must work independently of the auto-optimize upload toggle.
|
||||
*
|
||||
* @param int $batch_size Number of images per batch.
|
||||
* @return array
|
||||
*/
|
||||
function zeitfresser_process_legacy_images_batch( $batch_size = 25 ) {
|
||||
|
||||
$results = [
|
||||
'processed' => 0,
|
||||
'updated' => 0,
|
||||
'skipped' => 0,
|
||||
'errors' => 0,
|
||||
];
|
||||
|
||||
$query = new WP_Query([
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => 'inherit',
|
||||
'post_mime_type' => 'image',
|
||||
'fields' => 'ids',
|
||||
'posts_per_page' => $batch_size,
|
||||
'meta_query' => [
|
||||
'relation' => 'OR',
|
||||
[
|
||||
'key' => '_zeitfresser_media_optimized_version',
|
||||
'compare' => 'NOT EXISTS',
|
||||
],
|
||||
[
|
||||
'key' => '_zeitfresser_media_optimized_version',
|
||||
'value' => ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION,
|
||||
'compare' => '!=',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// Force optimization for manual tool runs, regardless of upload automation setting.
|
||||
$GLOBALS['zeitfresser_force_image_optimization'] = true;
|
||||
|
||||
foreach ( $query->posts as $attachment_id ) {
|
||||
|
||||
$results['processed']++;
|
||||
|
||||
$file = get_attached_file( $attachment_id );
|
||||
|
||||
if ( empty( $file ) || ! file_exists( $file ) ) {
|
||||
update_post_meta( $attachment_id, '_zeitfresser_media_optimized_version', 'missing' );
|
||||
$results['skipped']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! get_post_meta( $attachment_id, '_zeitfresser_original_file', true ) ) {
|
||||
update_post_meta( $attachment_id, '_zeitfresser_original_file', $file );
|
||||
}
|
||||
|
||||
$metadata = wp_generate_attachment_metadata( $attachment_id, $file );
|
||||
|
||||
if ( is_wp_error( $metadata ) || empty( $metadata ) ) {
|
||||
$results['errors']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
wp_update_attachment_metadata( $attachment_id, $metadata );
|
||||
|
||||
update_post_meta(
|
||||
$attachment_id,
|
||||
'_zeitfresser_media_optimized_version',
|
||||
ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION
|
||||
);
|
||||
|
||||
$results['updated']++;
|
||||
}
|
||||
|
||||
unset( $GLOBALS['zeitfresser_force_image_optimization'] );
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Optimizer (extended output only)
|
||||
*/
|
||||
function zeitfresser_ajax_optimize_images() {
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
check_ajax_referer( 'zeitfresser_image_optimizer', 'nonce' );
|
||||
|
||||
$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([
|
||||
'processed' => $results['processed'],
|
||||
'updated' => $results['updated'],
|
||||
'pending' => $pending,
|
||||
'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' );
|
||||
|
||||
/**
|
||||
* AJAX: Delete (extended ONLY)
|
||||
*/
|
||||
function zeitfresser_ajax_delete_originals() {
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
check_ajax_referer( 'zeitfresser_image_optimizer', 'nonce' );
|
||||
|
||||
$deleted = zeitfresser_delete_originals_batch( 10 );
|
||||
$total = zeitfresser_get_total_originals_count();
|
||||
$remaining = zeitfresser_get_remaining_originals_count();
|
||||
$unoptimized = zeitfresser_get_unoptimized_originals_count();
|
||||
$deleted_total = $total - ( $remaining + $unoptimized );
|
||||
|
||||
wp_send_json_success([
|
||||
'deleted' => $deleted,
|
||||
'total' => $total,
|
||||
'remaining' => $remaining,
|
||||
'unoptimized' => $unoptimized,
|
||||
'deleted_total' => $deleted_total,
|
||||
]);
|
||||
}
|
||||
add_action( 'wp_ajax_zeitfresser_delete_originals', 'zeitfresser_ajax_delete_originals' );
|
||||
|
||||
/**
|
||||
* Render UI
|
||||
*/
|
||||
function zeitfresser_render_image_optimizer_page() {
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pending = zeitfresser_get_pending_legacy_images_count();
|
||||
$total = zeitfresser_get_total_images_count();
|
||||
$optimized = $total - $pending;
|
||||
$progress = $total > 0 ? round(($optimized / $total) * 100) : 0;
|
||||
|
||||
// 🔥 NEW: Cleanup counters
|
||||
$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;
|
||||
|
||||
$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">
|
||||
<h1>Zeitfresser Image Optimizer</h1>
|
||||
|
||||
<div class="notice notice-info" style="max-width:800px;margin-top:20px;">
|
||||
<p>
|
||||
<strong>How this tool works</strong><br><br>
|
||||
|
||||
This tool helps you optimize your existing media library for better performance.<br><br>
|
||||
|
||||
• Images are converted to modern formats (AVIF/WebP) for smaller file sizes.<br>
|
||||
• The original file path is safely stored before optimization.<br>
|
||||
• Once optimized, original images can be deleted to save disk space.<br><br>
|
||||
|
||||
<strong>Automation:</strong><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>
|
||||
|
||||
<strong>Safety:</strong><br>
|
||||
• Images are only processed once per version.<br>
|
||||
• Original files are only deleted when safe.<br>
|
||||
• The tool can be run multiple times without side effects.<br><br>
|
||||
|
||||
<em><strong>Tip:</strong> You can either automate the process via the Customizer or use this tool manually for full control.</em>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- OPTIMIZATION -->
|
||||
<div class="card" style="max-width:800px;padding:24px;margin-bottom:20px;">
|
||||
|
||||
<h2 style="margin-top:0;">🚀 Image Optimization</h2>
|
||||
|
||||
<div style="margin-bottom:20px;">
|
||||
<p><strong>Total Images:</strong> <span id="total"><?php echo $total; ?></span></p>
|
||||
<p><strong>Optimized:</strong> <span id="optimized"><?php echo $optimized; ?></span></p>
|
||||
<p><strong>Pending:</strong> <span id="remaining"><?php echo $pending; ?></span></p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom:20px;">
|
||||
<div style="background:#e0e0e0;border-radius:6px;height:12px;">
|
||||
<div id="progress-bar" style="width:<?php echo $progress; ?>%;background:#4caf50;height:100%;"></div>
|
||||
</div>
|
||||
<p><strong>Progress:</strong> <span id="progress"><?php echo $progress; ?></span>%</p>
|
||||
</div>
|
||||
|
||||
<button id="start-btn" class="button button-primary">🚀 Optimize Images</button>
|
||||
</div>
|
||||
|
||||
<!-- CLEANUP -->
|
||||
<div class="card" style="max-width:800px;padding:24px;">
|
||||
|
||||
<h2>🧹 Original Cleanup</h2>
|
||||
|
||||
<div style="margin-bottom:20px;">
|
||||
<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>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 style="margin-bottom:20px;">
|
||||
<div style="background:#e0e0e0;border-radius:6px;height:12px;">
|
||||
<div id="cleanup-bar" style="width:<?php echo $cleanup_progress; ?>%;background:<?php echo $cleanup_progress === 100 ? '#4caf50' : '#ff9800'; ?>;height:100%;"></div>
|
||||
</div>
|
||||
<p><strong>Cleanup Progress:</strong> <span id="cleanup-progress"><?php echo $cleanup_progress; ?></span>%</p>
|
||||
</div>
|
||||
|
||||
<button id="delete-btn" class="button" <?php disabled( $cleanup_button_disabled ); ?>>
|
||||
<?php echo esc_html( $cleanup_button_label ); ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- STATUS -->
|
||||
<div style="padding:12px;background:#f6f7f7;border-radius:6px;">
|
||||
<p id="status-opt">🚀 Optimizer: Idle</p>
|
||||
<p id="status-clean">🧹 Cleanup: Idle</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#progress-bar,
|
||||
#cleanup-bar {
|
||||
transition: width 0.35s ease, background 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
let running = false;
|
||||
let deleting = false;
|
||||
|
||||
(function initCleanupBar() {
|
||||
const bar = document.getElementById('cleanup-bar');
|
||||
const progress = parseInt(document.getElementById('cleanup-progress').innerText, 10);
|
||||
|
||||
if (!bar || isNaN(progress)) return;
|
||||
|
||||
if (progress >= 100) {
|
||||
bar.style.background = '#4caf50';
|
||||
} else if (progress > 0) {
|
||||
bar.style.background = '#ff9800';
|
||||
}
|
||||
})();
|
||||
|
||||
document.getElementById('start-btn').onclick = () => {
|
||||
running = true;
|
||||
|
||||
document.getElementById('start-btn').disabled = true;
|
||||
document.getElementById('delete-btn').disabled = true;
|
||||
|
||||
document.getElementById('start-btn').innerText = '⏳ Running...';
|
||||
document.getElementById('status-opt').innerText = '🚀 Optimizing images...';
|
||||
|
||||
processBatch();
|
||||
};
|
||||
|
||||
document.getElementById('delete-btn').onclick = () => {
|
||||
deleting = true;
|
||||
|
||||
document.getElementById('start-btn').disabled = true;
|
||||
document.getElementById('delete-btn').disabled = true;
|
||||
|
||||
document.getElementById('delete-btn').innerText = '⏳ Running...';
|
||||
document.getElementById('status-clean').innerText = '🧹 Cleaning originals...';
|
||||
|
||||
deleteBatch();
|
||||
};
|
||||
|
||||
function deleteBatch() {
|
||||
if (!deleting) return;
|
||||
|
||||
fetch(ajaxurl, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: new URLSearchParams({
|
||||
action: 'zeitfresser_delete_originals',
|
||||
nonce: '<?php echo wp_create_nonce('zeitfresser_image_optimizer'); ?>'
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
let total = data.data.total;
|
||||
let remaining = data.data.remaining;
|
||||
let deleted = data.data.deleted_total;
|
||||
let batch = data.data.deleted;
|
||||
let unoptimized = data.data.unoptimized;
|
||||
|
||||
let progress = total > 0 ? Math.round((deleted / total) * 100) : 0;
|
||||
|
||||
// Update Cleanup UI
|
||||
document.getElementById('cleanup-total').innerText = total;
|
||||
document.getElementById('cleanup-deleted').innerText = deleted;
|
||||
document.getElementById('cleanup-remaining').innerText = remaining;
|
||||
document.getElementById('cleanup-unoptimized').innerText = unoptimized;
|
||||
document.getElementById('cleanup-progress').innerText = progress;
|
||||
|
||||
let bar = document.getElementById('cleanup-bar');
|
||||
bar.style.width = progress + '%';
|
||||
|
||||
if (progress >= 100) {
|
||||
bar.style.background = '#4caf50';
|
||||
} else if (progress > 0) {
|
||||
bar.style.background = '#ff9800';
|
||||
}
|
||||
|
||||
if (batch > 0) {
|
||||
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) {
|
||||
setTimeout(deleteBatch, 400);
|
||||
} else {
|
||||
deleting = false;
|
||||
|
||||
document.getElementById('start-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('status-clean').innerText = '✔ Cleanup complete';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function processBatch() {
|
||||
if (!running) return;
|
||||
|
||||
fetch(ajaxurl, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: new URLSearchParams({
|
||||
action: 'zeitfresser_optimize_images',
|
||||
nonce: '<?php echo wp_create_nonce('zeitfresser_image_optimizer'); ?>'
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
let pending = data.data.pending;
|
||||
let total = data.data.total;
|
||||
let optimized = total - pending;
|
||||
let progress = total > 0 ? Math.round((optimized / total) * 100) : 0;
|
||||
|
||||
// Live update optimization counters
|
||||
document.getElementById('total').innerText = total;
|
||||
document.getElementById('optimized').innerText = optimized;
|
||||
document.getElementById('remaining').innerText = pending;
|
||||
|
||||
// Live update optimization progress
|
||||
document.getElementById('progress').innerText = 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 =
|
||||
'🚀 Processed: ' + data.data.processed +
|
||||
' | Updated: ' + data.data.updated;
|
||||
|
||||
if (pending > 0) {
|
||||
setTimeout(processBatch, 400);
|
||||
} else {
|
||||
running = false;
|
||||
|
||||
document.getElementById('start-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('status-opt').innerText = '✔ Optimization complete';
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php
|
||||
}
|
||||
@@ -9,6 +9,48 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
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' ) ) {
|
||||
/**
|
||||
* 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>',
|
||||
'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>',
|
||||
'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>',
|
||||
'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>',
|
||||
'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>',
|
||||
'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 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 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 ] : '';
|
||||
@@ -150,3 +192,43 @@ function db_fs() {
|
||||
function graphthemes_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 bool
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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>>}
|
||||
*/
|
||||
function zeitfresser_build_toc_payload( $post_id ) {
|
||||
static $cache = array();
|
||||
|
||||
static $cache = array();
|
||||
$post_id = (int) $post_id;
|
||||
|
||||
if ( isset( $cache[ $post_id ] ) ) {
|
||||
@@ -49,21 +73,15 @@ function zeitfresser_build_toc_payload( $post_id ) {
|
||||
'items' => array(),
|
||||
);
|
||||
|
||||
// Early exit conditions
|
||||
if ( ! $post_id || ! is_singular( 'post' ) || ! zeitfresser_show_article_toc() ) {
|
||||
$cache[ $post_id ] = $payload;
|
||||
return $payload;
|
||||
return $cache[ $post_id ] = $payload;
|
||||
}
|
||||
|
||||
$content = trim( (string) $payload['content'] );
|
||||
|
||||
if ( '' === $content ) {
|
||||
$cache[ $post_id ] = $payload;
|
||||
return $payload;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'DOMDocument' ) ) {
|
||||
$cache[ $post_id ] = $payload;
|
||||
return $payload;
|
||||
if ( '' === $content || ! class_exists( 'DOMDocument' ) ) {
|
||||
return $cache[ $post_id ] = $payload;
|
||||
}
|
||||
|
||||
libxml_use_internal_errors( true );
|
||||
@@ -76,16 +94,14 @@ function zeitfresser_build_toc_payload( $post_id ) {
|
||||
|
||||
if ( ! $loaded ) {
|
||||
libxml_clear_errors();
|
||||
$cache[ $post_id ] = $payload;
|
||||
return $payload;
|
||||
return $cache[ $post_id ] = $payload;
|
||||
}
|
||||
|
||||
$container = $dom->getElementById( 'zeitfresser-toc-root' );
|
||||
|
||||
if ( ! $container ) {
|
||||
libxml_clear_errors();
|
||||
$cache[ $post_id ] = $payload;
|
||||
return $payload;
|
||||
return $cache[ $post_id ] = $payload;
|
||||
}
|
||||
|
||||
$index = 1;
|
||||
@@ -95,8 +111,8 @@ function zeitfresser_build_toc_payload( $post_id ) {
|
||||
|
||||
if ( $headings instanceof DOMNodeList ) {
|
||||
foreach ( $headings as $heading ) {
|
||||
$text = trim( wp_strip_all_tags( $heading->textContent ) );
|
||||
|
||||
$text = trim( wp_strip_all_tags( $heading->textContent ) );
|
||||
if ( '' === $text ) {
|
||||
continue;
|
||||
}
|
||||
@@ -128,20 +144,18 @@ function zeitfresser_build_toc_payload( $post_id ) {
|
||||
|
||||
libxml_clear_errors();
|
||||
|
||||
// Respect minimum threshold
|
||||
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 ),
|
||||
'items' => array(),
|
||||
);
|
||||
return $cache[ $post_id ];
|
||||
}
|
||||
|
||||
$cache[ $post_id ] = array(
|
||||
return $cache[ $post_id ] = array(
|
||||
'content' => zeitfresser_extract_toc_inner_html( $container ),
|
||||
'items' => $toc_items,
|
||||
);
|
||||
|
||||
return $cache[ $post_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,6 +165,7 @@ function zeitfresser_build_toc_payload( $post_id ) {
|
||||
* @return string
|
||||
*/
|
||||
function zeitfresser_extract_toc_inner_html( $node ) {
|
||||
|
||||
$html = '';
|
||||
|
||||
if ( ! $node || ! $node->hasChildNodes() ) {
|
||||
@@ -171,6 +186,7 @@ function zeitfresser_extract_toc_inner_html( $node ) {
|
||||
* @return bool
|
||||
*/
|
||||
function zeitfresser_has_floating_toc( $post_id = null ) {
|
||||
|
||||
$post_id = $post_id ? (int) $post_id : get_the_ID();
|
||||
|
||||
if ( ! $post_id ) {
|
||||
@@ -189,6 +205,7 @@ function zeitfresser_has_floating_toc( $post_id = null ) {
|
||||
* @return void
|
||||
*/
|
||||
function zeitfresser_render_floating_toc( $post_id = null ) {
|
||||
|
||||
$post_id = $post_id ? (int) $post_id : get_the_ID();
|
||||
|
||||
if ( ! $post_id ) {
|
||||
@@ -201,13 +218,16 @@ function zeitfresser_render_floating_toc( $post_id = null ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
|
||||
<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">
|
||||
<span class="zeitfresser-floating-toc__title"><?php echo esc_html__( 'Content', 'zeitfresser' ); ?></span>
|
||||
</div>
|
||||
|
||||
<div class="zeitfresser-floating-toc__progress" aria-hidden="true">
|
||||
<span class="zeitfresser-floating-toc__progress-bar" id="zeitfresser-floating-toc-progress"></span>
|
||||
</div>
|
||||
|
||||
<nav class="zeitfresser-floating-toc__nav">
|
||||
<ol class="zeitfresser-floating-toc__list">
|
||||
<?php foreach ( $payload['items'] as $item ) : ?>
|
||||
@@ -220,5 +240,6 @@ function zeitfresser_render_floating_toc( $post_id = null ) {
|
||||
</ol>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<?php
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<p align="center">
|
||||
<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>
|
||||
</p>
|
||||
|
||||
@@ -28,134 +28,91 @@ A performance-optimized, minimalist dark blog theme for WordPress, built for fas
|
||||
<br>
|
||||
<img width="1800" height="1271" alt="preview" src="https://github.com/user-attachments/assets/aefff842-8189-48ae-965c-77799667b2a9" />
|
||||
|
||||
|
||||
## ✨ Overview
|
||||
|
||||
Zeitfresser is a custom-built WordPress theme designed with a clear focus:
|
||||
**fast, readable, and distraction-free technical blogging**.
|
||||
|
||||
Originally based on the popular [Daisy Blog](https://wordpress.org/themes/daisy-blog/) theme, Zeitfresser has evolved into a fully independent and heavily optimized codebase. Every part of the system has been refactored with performance, clarity, and maintainability in mind.
|
||||
Originally based on the popular [Daisy Blog](https://wordpress.org/themes/daisy-blog/) theme, Zeitfresser has evolved into a fully independent and heavily optimized codebase. Over time, every layer of the theme has been reworked with performance, clarity, and maintainability in mind.
|
||||
|
||||
The design follows a minimalist dark aesthetic inspired by Dracula, with a strong emphasis on typography, structure, and reading flow.
|
||||
The visual identity follows a minimalist dark aesthetic inspired by Dracula, where typography, spacing, and structure take priority over decoration. The result is a calm, highly readable environment tailored for long-form technical content.
|
||||
|
||||
## 🚀 Performance & Architecture
|
||||
|
||||
Performance is a core design principle.
|
||||
Performance is not an afterthought in Zeitfresser — it is the foundation of the entire system.
|
||||
|
||||
- No unnecessary dependencies or heavy libraries
|
||||
- Minimal JavaScript footprint
|
||||
- Deferred and optimized script loading
|
||||
- Lean CSS architecture with reduced redundancy
|
||||
- Clean DOM structure for predictable rendering
|
||||
Instead of relying on heavy abstractions or third-party dependencies, the theme focuses on a lean architecture with minimal JavaScript, carefully structured CSS, and predictable rendering behavior. Assets are only loaded when needed, avoiding unnecessary overhead and reducing the risk of render-blocking resources.
|
||||
|
||||
Assets are loaded only when needed, avoiding common bottlenecks such as render-blocking scripts or excessive CSS overhead.
|
||||
The DOM structure remains intentionally simple, which helps ensure consistent rendering across browsers and improves maintainability over time.
|
||||
|
||||
### Key Benefits
|
||||
## 🖼️ Image Optimization System (v1.7)
|
||||
|
||||
- No inline styles or dynamically injected CSS
|
||||
- No dependency on the WordPress Customizer for color rendering
|
||||
- Consistent color usage across all components
|
||||
- Easy to maintain and extend
|
||||
- Fully compatible with modern browsers and rendering pipelines
|
||||
With version **1.7**, Zeitfresser introduces a fully integrated **image optimization pipeline**.
|
||||
|
||||
### Design Approach
|
||||
Uploaded images can now be automatically converted to modern formats such as **AVIF** or **WebP**, depending on server capabilities. This significantly reduces file sizes while preserving visual quality, leading to faster page loads and improved Core Web Vitals.
|
||||
|
||||
The theme follows a dark-first design with carefully selected contrast values:
|
||||
The system is designed to be both flexible and safe:
|
||||
|
||||
- Background and surface colors are optimized for readability
|
||||
- Accent colors are used sparingly to guide attention
|
||||
- Typography and spacing work together with color to create hierarchy
|
||||
- Optimization can run automatically on upload or manually via the dashboard
|
||||
- Original file paths are preserved before any transformation
|
||||
- Cleanup of original files is optional and only performed when safe
|
||||
- All operations are versioned and idempotent
|
||||
|
||||
This approach ensures a clean, stable, and performant visual system without unnecessary complexity.
|
||||
A dedicated **Performance Tools dashboard** allows you to process existing media libraries in batches, monitor progress in real time, and optionally remove original files after successful optimization.
|
||||
|
||||
For full control, automation can be configured via the WordPress Customizer. You can choose between manual processing, automatic optimization, or a fully automated workflow that includes cleanup of original images.
|
||||
|
||||
## 🔤 Local Fonts & Typography System
|
||||
|
||||
Zeitfresser uses a fully self-hosted font system:
|
||||
Zeitfresser uses a fully self-hosted typography system built around Oswald and Roboto.
|
||||
|
||||
- **Oswald** (400, 500, 700) for headings
|
||||
- **Roboto** (400, 500, 700) for body text
|
||||
All fonts are served locally using optimized `.woff2` files, eliminating external requests and improving privacy and performance. Critical font assets are preloaded to ensure fast rendering, and the entire system is built using CSS variables for consistency and maintainability.
|
||||
|
||||
Key improvements:
|
||||
|
||||
- No external font requests (Google Fonts removed)
|
||||
- Fonts served locally via optimized `.woff2` files
|
||||
- Critical font assets are **preloaded** for faster rendering
|
||||
- Consistent typography across all environments
|
||||
- Full control over font loading and rendering behavior
|
||||
|
||||
The typography system is based on CSS variables and designed to be predictable, maintainable, and visually consistent.
|
||||
The result is a predictable and visually stable typography layer across all environments.
|
||||
|
||||
## 🎨 CSS-Based Color System
|
||||
|
||||
Zeitfresser uses a fully static, CSS variable-driven color system.
|
||||
The theme relies entirely on a static, CSS variable-driven color system.
|
||||
|
||||
All colors are defined using native CSS custom properties (`:root`) and applied consistently across the entire theme. This replaces traditional PHP-driven or dynamically generated styles with a simpler and more predictable approach.
|
||||
Instead of dynamically generated styles or PHP-driven color logic, all values are defined using native CSS custom properties. This approach simplifies the styling layer, improves performance, and ensures consistent rendering without runtime overhead.
|
||||
|
||||
## ⚡ Core Web Vitals
|
||||
|
||||
The theme is optimized for real-world performance:
|
||||
Zeitfresser is optimized for real-world performance.
|
||||
|
||||
- Stable layout with no unexpected shifts (CLS-safe)
|
||||
- Optimized Largest Contentful Paint (LCP)
|
||||
- Reduced render-blocking resources
|
||||
- Early font availability through preload strategy
|
||||
|
||||
Even long-form articles render quickly and consistently.
|
||||
Layouts are stable and free of unexpected shifts, ensuring a solid CLS score. Critical assets are loaded early, and render-blocking resources are minimized. Even long-form articles with complex structures remain responsive and fast.
|
||||
|
||||
## 📑 Floating Table of Contents
|
||||
|
||||
A core feature of the theme is its editorial-style floating Table of Contents.
|
||||
A key editorial feature of the theme is the floating Table of Contents.
|
||||
|
||||
- Automatically generated from headings
|
||||
- Positioned outside the main content flow
|
||||
- Highlights the active section
|
||||
- Includes a subtle progress indicator
|
||||
- Smooth scroll behavior
|
||||
|
||||
Designed to enhance navigation without adding visual noise.
|
||||
It is generated automatically from headings and positioned outside the main content flow, allowing readers to navigate long articles without distraction. The active section is highlighted, and a subtle progress indicator provides orientation within the document.
|
||||
|
||||
## ⚙️ Customization
|
||||
|
||||
The theme integrates cleanly with the WordPress Customizer.
|
||||
Zeitfresser integrates cleanly with the WordPress Customizer, offering a small but focused set of options.
|
||||
|
||||
- Enable/disable TOC globally
|
||||
- Configure heading thresholds for TOC visibility
|
||||
- Adjust layout behavior without adding complexity
|
||||
|
||||
All options are intentionally minimal and focused.
|
||||
Instead of overwhelming users with configuration, the theme provides only what is necessary to adapt behavior without introducing complexity. This includes layout-related options and performance settings such as the image optimization pipeline.
|
||||
|
||||
## 🎨 Design Philosophy
|
||||
|
||||
Zeitfresser follows a strict philosophy:
|
||||
The guiding principle behind Zeitfresser is simple:
|
||||
**clarity over decoration**.
|
||||
|
||||
- Minimal dark UI with subtle accent colors
|
||||
- Typography-driven hierarchy
|
||||
- Clean spacing instead of visual clutter
|
||||
- Focus on long-form readability
|
||||
|
||||
The result is a calm, distraction-free reading experience.
|
||||
Every design decision is made to support readability and structure. Colors are used sparingly, spacing is intentional, and typography defines hierarchy. The goal is not to impress visually, but to support sustained reading without fatigue.
|
||||
|
||||
## 🧹 Code Quality
|
||||
|
||||
The codebase has been systematically refactored:
|
||||
The codebase has been systematically refactored to remove legacy patterns and reduce complexity.
|
||||
|
||||
- Legacy components removed
|
||||
- CSS conflicts minimized
|
||||
- Modular structure
|
||||
- No unnecessary abstractions
|
||||
- No technical debt patterns
|
||||
|
||||
The theme is designed for long-term maintainability.
|
||||
The theme avoids unnecessary abstractions and focuses on a modular, predictable structure. This makes it easier to maintain, extend, and reason about over time, without accumulating technical debt.
|
||||
|
||||
## 📱 Responsiveness
|
||||
|
||||
The layout adapts cleanly across devices.
|
||||
Zeitfresser adapts naturally across devices.
|
||||
|
||||
- Desktop: full editorial experience
|
||||
- Mobile: simplified, focused layout
|
||||
- Feature-aware behavior (e.g. TOC disabled when not useful)
|
||||
|
||||
All core functionality remains accessible.
|
||||
On desktop, it provides a full editorial experience with structured navigation. On mobile, the layout simplifies while preserving readability and access to core features. Components such as the Table of Contents adjust dynamically based on context.
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
@@ -167,17 +124,13 @@ To install the theme:
|
||||
|
||||
## ⚡ Recommended Setup
|
||||
|
||||
For best performance:
|
||||
While the theme performs well out of the box, it benefits from a modern setup.
|
||||
|
||||
- Enable caching (server or plugin)
|
||||
- Use a CDN for global delivery
|
||||
- Optimize existing media assets
|
||||
|
||||
The theme is designed to perform well out of the box, but benefits from a modern hosting setup.
|
||||
Using caching, a CDN, and optimized hosting will further improve performance, especially for larger sites with extensive media libraries.
|
||||
|
||||
## 🛠 Development & Support
|
||||
|
||||
If you need to get support or want to participate in the active development of this software, you can <a href="https://ztfr.eu/matrix">join our Zeitfresser Matrix Community</a> or the <a href="https://look.ztfr.eu/#/#support:ztfr.eu">Development & Support Channel</a> on Matrix.
|
||||
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>.
|
||||
|
||||
## 📄 License
|
||||
|
||||
@@ -187,10 +140,9 @@ Originally based on the [Daisy Blog](https://wordpress.org/themes/daisy-blog/) t
|
||||
|
||||
## 💬 Final Note
|
||||
|
||||
Zeitfresser is built for developers and writers who value:
|
||||
Zeitfresser is built for developers and writers who care about:
|
||||
|
||||
- performance
|
||||
- readability
|
||||
- clean engineering
|
||||
performance, readability, and clean engineering.
|
||||
|
||||
It avoids unnecessary complexity and focuses on doing a few things exceptionally well: **presenting content clearly, loading fast, and staying maintainable.**
|
||||
It avoids unnecessary complexity and focuses on doing a few things exceptionally well:
|
||||
**presenting content clearly, loading fast, and remaining maintainable over time.**
|
||||
|
||||
+21
-15
@@ -2,42 +2,48 @@
|
||||
/**
|
||||
* The template for displaying all single posts
|
||||
*
|
||||
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/#single-post
|
||||
*
|
||||
* @package zeitfresser
|
||||
*/
|
||||
|
||||
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 class="container">
|
||||
<div class="main-wrapper">
|
||||
|
||||
<?php zeitfresser_render_floating_toc(); ?>
|
||||
|
||||
<section class="page-section full-width-view">
|
||||
<div class="detail-content">
|
||||
|
||||
<?php while ( have_posts() ) : the_post(); ?>
|
||||
<?php get_template_part( 'template-parts/content', 'single' ); ?>
|
||||
<?php endwhile; // End of the loop. ?>
|
||||
<?php endwhile; ?>
|
||||
|
||||
<?php the_post_navigation(); ?>
|
||||
<?php comments_template(); ?>
|
||||
|
||||
</div><!-- /.end of deatil-content -->
|
||||
</div>
|
||||
|
||||
<?php
|
||||
if( $show_hide_related_posts ) {
|
||||
get_template_part('template-parts/related', 'articles');
|
||||
}
|
||||
?>
|
||||
</section> <!-- /.end of section -->
|
||||
<?php if ( $show_hide_related_posts ) : ?>
|
||||
<?php get_template_part( 'template-parts/related', 'articles' ); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="sidebar"><?php get_sidebar();?></div>
|
||||
</section>
|
||||
|
||||
<div class="sidebar">
|
||||
<?php get_sidebar(); ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
<?php get_footer(); ?>
|
||||
|
||||
@@ -2,137 +2,36 @@
|
||||
/**
|
||||
* Template part for displaying results in search pages
|
||||
*
|
||||
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
|
||||
*
|
||||
* @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 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">
|
||||
<?php the_post_thumbnail( $thumbnail_size ); ?>
|
||||
<?php the_post_thumbnail( zeitfresser_get_post_card_thumbnail_size() ); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="summary">
|
||||
<?php if( $show_hide_categories ) { ?>
|
||||
|
||||
<?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 } ?>
|
||||
|
||||
<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() ); ?>
|
||||
<h3 class="news-title">
|
||||
<a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark">
|
||||
<?php the_title(); ?>
|
||||
</a>
|
||||
</li>
|
||||
<?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>
|
||||
</h3>
|
||||
|
||||
<div class="excerpt">
|
||||
<?php $excerpt_length = get_theme_mod( 'post_snippet_excerpt_size', zeitfresser_get_default_post_snippet_excerpt_size() ); ?>
|
||||
|
||||
<?php echo wp_trim_words( get_the_excerpt(), $excerpt_length ); ?>
|
||||
<?php echo esc_html(
|
||||
wp_trim_words(
|
||||
get_the_excerpt(),
|
||||
zeitfresser_get_post_card_excerpt_length()
|
||||
)
|
||||
); ?>
|
||||
</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>
|
||||
@@ -4,19 +4,7 @@
|
||||
*
|
||||
* @package zeitfresser
|
||||
*/
|
||||
?>
|
||||
|
||||
|
||||
<?php
|
||||
$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'] );
|
||||
?>
|
||||
@@ -29,97 +17,46 @@
|
||||
<?php zeitfresser_render_floating_toc( get_the_ID() ); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<div class="single-post">
|
||||
|
||||
|
||||
|
||||
<div class="post-content">
|
||||
|
||||
<!-- Meta Info -->
|
||||
<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 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
|
||||
if( $show_hide_author ) { ?>
|
||||
<li class="post-author"><i class="icon-user"></i>
|
||||
<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() ); ?>
|
||||
$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 esc_html( get_the_date() ); ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
<?php if( $show_hide_comment ) { ?>
|
||||
<!-- Comments (TRUE) -->
|
||||
<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" />
|
||||
<svg width="20px" height="20px" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<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"/>
|
||||
</g>
|
||||
</svg>
|
||||
<?php comments_popup_link( __( '0', 'zeitfresser' ), __( '1', 'zeitfresser' ), __( '%', 'zeitfresser' ) ); ?>
|
||||
</span>
|
||||
|
||||
|
||||
<?php } ?>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Featured Image (FALSE → entfernt) -->
|
||||
|
||||
<?php if( $show_hide_categories ) { ?>
|
||||
|
||||
<?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 Content -->
|
||||
<article>
|
||||
|
||||
<div class="inner-article-content">
|
||||
<?php echo $toc_payload['content']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
<?php echo $toc_payload['content']; // phpcs:ignore ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
@@ -132,58 +69,9 @@
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="ifoot info">
|
||||
|
||||
|
||||
|
||||
<?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; ?>
|
||||
<!-- Author Block (FALSE → entfernt) -->
|
||||
<!-- Categories (FALSE → entfernt) -->
|
||||
<!-- Tags (FALSE → entfernt) -->
|
||||
<!-- Social Share (FALSE → entfernt) -->
|
||||
|
||||
</div>
|
||||
+15
-110
@@ -6,130 +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 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">
|
||||
<?php the_post_thumbnail( $thumbnail_size ); ?>
|
||||
<?php the_post_thumbnail( zeitfresser_get_post_card_thumbnail_size() ); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="summary">
|
||||
<?php if( $show_hide_categories ) { ?>
|
||||
|
||||
<?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 } ?>
|
||||
|
||||
<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() ); ?>
|
||||
<h3 class="news-title">
|
||||
<a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark">
|
||||
<?php the_title(); ?>
|
||||
</a>
|
||||
</li>
|
||||
<?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>
|
||||
</h3>
|
||||
|
||||
<div class="excerpt">
|
||||
<?php $excerpt_length = get_theme_mod( 'post_snippet_excerpt_size', zeitfresser_get_default_post_snippet_excerpt_size() ); ?>
|
||||
|
||||
<?php echo wp_trim_words( get_the_excerpt(), $excerpt_length ); ?>
|
||||
<?php echo esc_html(
|
||||
wp_trim_words(
|
||||
get_the_excerpt(),
|
||||
zeitfresser_get_post_card_excerpt_length()
|
||||
)
|
||||
); ?>
|
||||
</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>
|
||||
@@ -3,13 +3,6 @@ $page_template = get_page_template_slug( get_queried_object_id() );
|
||||
$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">
|
||||
<?php
|
||||
$args = array(
|
||||
@@ -23,25 +16,24 @@ $post_count = 3;
|
||||
'update_post_term_cache' => false,
|
||||
);
|
||||
|
||||
|
||||
$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">
|
||||
<?php while ( $query->have_posts() ) : $query->the_post(); ?>
|
||||
<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 $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">
|
||||
<?php the_post_thumbnail( $thumbnail_size ); ?>
|
||||
<?php the_post_thumbnail( zeitfresser_get_post_card_thumbnail_size() ); ?>
|
||||
</a>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
<h5 class="news-title">
|
||||
<a href="<?php echo esc_url( get_permalink() ); ?>" rel="bookmark">
|
||||
<?php the_title(); ?>
|
||||
@@ -49,27 +41,19 @@ $post_count = 3;
|
||||
</h5>
|
||||
|
||||
<div class="excerpt">
|
||||
<?php $excerpt_length = get_theme_mod( 'post_snippet_excerpt_size', zeitfresser_get_default_post_snippet_excerpt_size() ); ?>
|
||||
|
||||
<?php echo wp_trim_words( get_the_excerpt(), $excerpt_length ); ?>
|
||||
<?php echo esc_html(
|
||||
wp_trim_words(
|
||||
get_the_excerpt(),
|
||||
zeitfresser_get_post_card_excerpt_length()
|
||||
)
|
||||
); ?>
|
||||
</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>
|
||||
<?php endwhile; ?>
|
||||
<?php wp_reset_postdata(); ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user