Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73852c8241 | ||
|
|
d1039413ba | ||
|
|
5d90e28914 | ||
|
|
06e3d59ab9 | ||
|
|
7e6250d45f | ||
|
|
4209a832bc |
+201
-5
@@ -64,6 +64,32 @@ function wp_multi_display_guest_author($author_name) {
|
||||
}
|
||||
add_filter('the_author', 'wp_multi_display_guest_author');
|
||||
|
||||
// Link hinter dem Autornamen: zur Gastautor-Seite statt zum (redirecteten) WordPress-Benutzer
|
||||
function wp_multi_guest_author_link($link, $author_id) {
|
||||
if (is_admin() || !(is_single() || is_archive() || is_home() || is_search())) {
|
||||
return $link;
|
||||
}
|
||||
|
||||
$post = get_post();
|
||||
if (!$post || (int) $post->post_author !== (int) $author_id) {
|
||||
return $link;
|
||||
}
|
||||
|
||||
$guest_author = get_post_meta($post->ID, '_guest_author', true);
|
||||
if (empty($guest_author)) {
|
||||
return $link;
|
||||
}
|
||||
|
||||
$page_id = (int) get_option('wp_multi_guest_author_page_id');
|
||||
$page_url = $page_id ? get_permalink($page_id) : '';
|
||||
if (!$page_url) {
|
||||
return $link;
|
||||
}
|
||||
|
||||
return add_query_arg('guest_author', rawurlencode($guest_author), $page_url);
|
||||
}
|
||||
add_filter('author_link', 'wp_multi_guest_author_link', 10, 2);
|
||||
|
||||
// Anzeige des Gast-Autors in der Beitragsübersicht (Backend)
|
||||
function wp_multi_add_guest_author_column($columns) {
|
||||
if (isset($columns['author'])) {
|
||||
@@ -1728,6 +1754,175 @@ function wp_multi_get_guest_author_overview_url() {
|
||||
return $cached ? get_permalink($cached) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Datum · Kategorie · Altersfreigabe (Age Gate) einer Geschichte als HTML.
|
||||
* Es wird nie Text der Geschichte ausgegeben – gesperrte Beiträge bleiben verborgen.
|
||||
*/
|
||||
function wp_multi_guest_author_post_meta($post_id, $post_date) {
|
||||
$bits = [esc_html(mysql2date(get_option('date_format'), $post_date))];
|
||||
|
||||
$names = [];
|
||||
foreach ((array) get_the_category((int) $post_id) as $category) {
|
||||
$names[] = esc_html($category->name);
|
||||
}
|
||||
if ($names) {
|
||||
$bits[] = implode(', ', $names);
|
||||
}
|
||||
|
||||
$html = implode(' · ', $bits);
|
||||
|
||||
if (function_exists('zst_profile_for_post')) {
|
||||
$profile = zst_profile_for_post((int) $post_id);
|
||||
if (is_array($profile) && !empty($profile['title'])) {
|
||||
$html .= ' <span class="ga-badge" title="' . esc_attr__('Hinter einer Altersfreigabe verborgen', 'wp-multi') . '">' . esc_html($profile['title']) . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alle Teile eines Autors je Serie, nach Teilnummer sortiert.
|
||||
*
|
||||
* @param string $guest_author Name des Gast-Autors.
|
||||
* @param array $series Serienzeilen aus wp_multi_get_guest_author_series().
|
||||
* @return array term_id => [ ['id','title','date','part'], … ]
|
||||
*/
|
||||
function wp_multi_get_guest_author_series_parts($guest_author, $series) {
|
||||
$source = wp_multi_detect_series_source();
|
||||
|
||||
if (!$series || !$source || $guest_author === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$term_ids = array_map('intval', wp_list_pluck($series, 'term_id'));
|
||||
$in = implode(',', $term_ids);
|
||||
$parent_sql = $source['parent'] ? $wpdb->prepare('AND tt.parent = %d', $source['parent']) : '';
|
||||
|
||||
$rows = $wpdb->get_results($wpdb->prepare("
|
||||
SELECT DISTINCT p.ID, p.post_title, p.post_date, tt.term_id
|
||||
FROM {$wpdb->posts} p
|
||||
INNER JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = '_guest_author' AND pm.meta_value = %s
|
||||
INNER JOIN {$wpdb->term_relationships} tr ON tr.object_id = p.ID
|
||||
INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id AND tt.taxonomy = %s {$parent_sql}
|
||||
WHERE p.post_status = 'publish' AND tt.term_id IN ({$in})
|
||||
", $guest_author, $source['taxonomy']));
|
||||
|
||||
$default_key = defined('SERIES_PART_KEY') ? SERIES_PART_KEY : '_series_part';
|
||||
$out = [];
|
||||
|
||||
foreach ((array) $rows as $row) {
|
||||
$term_id = (int) $row->term_id;
|
||||
$key = apply_filters('orgseries_part_key', $default_key, $term_id);
|
||||
$out[$term_id][] = [
|
||||
'id' => (int) $row->ID,
|
||||
'title' => $row->post_title,
|
||||
'date' => $row->post_date,
|
||||
'part' => (int) get_post_meta((int) $row->ID, $key, true),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($out as &$list) {
|
||||
usort($list, function ($a, $b) {
|
||||
if ($a['part'] !== $b['part']) {
|
||||
return $a['part'] <=> $b['part'];
|
||||
}
|
||||
return strcmp($a['date'], $b['date']);
|
||||
});
|
||||
}
|
||||
unset($list);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Einklappbare Teile-Liste einer Serie.
|
||||
*
|
||||
* @param array $parts Teile des Autors in dieser Serie.
|
||||
* @param int $total_parts Gesamtzahl der Teile der Serie.
|
||||
*/
|
||||
function wp_multi_guest_author_render_parts($parts, $total_parts) {
|
||||
if (!$parts) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$summary = sprintf(
|
||||
/* translators: 1: Anzahl Teile des Autors, 2: Gesamtzahl der Teile */
|
||||
_n('%1$d Teil von %2$d anzeigen', '%1$d Teile von %2$d anzeigen', count($parts), 'wp-multi'),
|
||||
count($parts),
|
||||
max((int) $total_parts, count($parts))
|
||||
);
|
||||
|
||||
$html = '<details class="ga-parts"><summary>' . esc_html($summary) . '</summary><ol>';
|
||||
|
||||
foreach ($parts as $part) {
|
||||
$label = $part['part'] > 0
|
||||
? sprintf(__('Teil %d', 'wp-multi'), $part['part'])
|
||||
: '';
|
||||
|
||||
$html .= '<li>';
|
||||
if ($label) {
|
||||
$html .= '<span class="ga-part-no">' . esc_html($label) . '</span> ';
|
||||
}
|
||||
$html .= '<a href="' . esc_url(get_permalink($part['id'])) . '">' . esc_html(get_the_title($part['id'])) . '</a>'
|
||||
. ' <span class="ga-part-meta">' . wp_multi_guest_author_post_meta($part['id'], $part['date']) . '</span></li>';
|
||||
}
|
||||
|
||||
return $html . '</ol></details>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Eigenes CSS für Autorenübersicht und -profil (ersetzt das frühere Tailwind-CDN).
|
||||
* Farben werden vom Theme geerbt, damit helle und dunkle Designs funktionieren.
|
||||
*/
|
||||
function wp_multi_guest_author_styles() {
|
||||
static $printed = false;
|
||||
|
||||
if ($printed) {
|
||||
return;
|
||||
}
|
||||
$printed = true;
|
||||
?>
|
||||
<style id="wp-multi-guest-author-css">
|
||||
/* Breite zwingend an die Spalte binden: das Theme gibt der Klasse "container" feste Breiten. */
|
||||
#guest-author-section, #guest-author-posts { width: 100%; min-width: 0; max-width: 100%; float: none; margin: 0; padding: 2rem 0; box-sizing: border-box; overflow-wrap: anywhere; }
|
||||
#guest-author-section *, #guest-author-posts * { box-sizing: border-box; }
|
||||
#guest-author-section .grid, #guest-author-posts .grid { display: grid; grid-template-columns: 1fr; gap: 1.5rem; margin-bottom: 2rem; }
|
||||
@media (min-width: 768px) { #guest-author-section .grid, #guest-author-posts .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
||||
#guest-author-section .guest-author-card, #guest-author-posts .post-card {
|
||||
display: block; padding: 1.25rem 1.5rem; border-radius: .6rem; text-decoration: none; color: inherit;
|
||||
background: rgba(127,127,127,.09); border: 1px solid rgba(127,127,127,.28);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.12); transition: box-shadow .15s, border-color .15s;
|
||||
}
|
||||
#guest-author-section .guest-author-card:hover, #guest-author-posts .post-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,.22); border-color: rgba(127,127,127,.55); }
|
||||
#guest-author-section h3, #guest-author-posts h2, #guest-author-posts h3, #guest-author-posts h4 { margin: 0 0 .4rem; color: inherit; }
|
||||
#guest-author-posts h2 { font-size: 1.6rem; }
|
||||
#guest-author-posts h3 { font-size: 1.25rem; margin-top: 2rem; }
|
||||
#guest-author-posts h4 { font-size: 1.05rem; }
|
||||
#guest-author-section p, #guest-author-posts p { margin: 0 0 .5rem; }
|
||||
#guest-author-section .guest-author-sort { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-bottom: 1.5rem; }
|
||||
#guest-author-section .guest-author-sort-label { font-size: 16px !important; }
|
||||
#guest-author-section .guest-author-sort-link { padding: .4rem 1rem; border: 1px solid rgba(127,127,127,.45); border-radius: .4rem; text-decoration: none; font-size: 16px !important; line-height: 1.3; }
|
||||
#guest-author-section .guest-author-sort-link.is-active { font-weight: 600; border-color: currentColor; }
|
||||
#guest-author-section .text-sm, #guest-author-posts .text-sm { font-size: 15px !important; }
|
||||
#guest-author-section .guest-author-last-post { font-size: 15px !important; }
|
||||
#guest-author-section .text-gray-500, #guest-author-posts .text-gray-500,
|
||||
#guest-author-section .text-gray-600, #guest-author-posts .text-gray-600 { opacity: .75; }
|
||||
#guest-author-posts .guest-author-popular { padding-left: 1.25rem; list-style: disc; }
|
||||
#guest-author-posts .guest-author-popular li { margin-bottom: .25rem; }
|
||||
#guest-author-posts .guest-author-milestone-badge { font-size: 1.1rem; font-weight: 600; }
|
||||
.ga-badge { display: inline-block; padding: 0 .45rem; border-radius: 999px; font-size: .75rem; background: rgba(120,80,200,.22); }
|
||||
.ga-parts { margin-top: .6rem; }
|
||||
.ga-parts summary { cursor: pointer; font-size: .9rem; }
|
||||
.ga-parts ol { margin: .5rem 0 0; padding-left: 0; list-style: none; max-height: 18rem; overflow: auto; }
|
||||
.ga-parts li { padding: .2rem 0; border-top: 1px solid rgba(127,127,127,.18); font-size: .9rem; }
|
||||
.ga-part-no { font-weight: 600; margin-right: .25rem; }
|
||||
.ga-part-meta { opacity: .7; font-size: .8rem; }
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
// Shortcode für Gastautoren-Liste
|
||||
function wp_multi_guest_author_shortcode($atts = []) {
|
||||
ob_start();
|
||||
@@ -1847,9 +2042,8 @@ function wp_multi_guest_author_shortcode($atts = []) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tailwind CSS CDN -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<?php
|
||||
wp_multi_guest_author_styles();
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
@@ -1879,6 +2073,7 @@ function wp_multi_guest_author_posts_shortcode() {
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
wp_multi_guest_author_styles();
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
@@ -1889,6 +2084,7 @@ function wp_multi_guest_author_posts_shortcode() {
|
||||
$series_data = wp_multi_get_guest_author_series($guest_author);
|
||||
$series = $series_data['series'];
|
||||
$series_post_ids = $series_data['post_ids'];
|
||||
$series_parts = wp_multi_get_guest_author_series_parts($guest_author, $series);
|
||||
|
||||
// Neueste Einzelgeschichten (Serienteile stehen im eigenen Abschnitt)
|
||||
$exclude_sql = $series_post_ids ? 'AND p.ID NOT IN (' . implode(',', $series_post_ids) . ')' : '';
|
||||
@@ -1998,6 +2194,7 @@ function wp_multi_guest_author_posts_shortcode() {
|
||||
·
|
||||
<?php printf(esc_html__('zuletzt %s', 'wp-multi'), esc_html(mysql2date(get_option('date_format'), $row->last_part_date))); ?>
|
||||
</p>
|
||||
<?php echo wp_multi_guest_author_render_parts(isset($series_parts[(int) $row->term_id]) ? $series_parts[(int) $row->term_id] : [], (int) $row->total_parts); // phpcs:ignore WordPress.Security.EscapeOutput ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
@@ -2022,16 +2219,15 @@ function wp_multi_guest_author_posts_shortcode() {
|
||||
<h4 class="text-lg font-medium text-gray-800">
|
||||
<a href="<?php echo esc_url(get_permalink((int) $row->ID)); ?>" class="hover:text-blue-600"><?php echo esc_html(get_the_title((int) $row->ID)); ?></a>
|
||||
</h4>
|
||||
<p class="text-gray-500 text-sm"><?php echo esc_html(mysql2date(get_option('date_format'), $row->post_date)); ?></p>
|
||||
<p class="text-gray-500 text-sm"><?php echo wp_multi_guest_author_post_meta((int) $row->ID, $row->post_date); // phpcs:ignore WordPress.Security.EscapeOutput ?></p>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Tailwind CSS CDN -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<?php
|
||||
wp_multi_guest_author_styles();
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
@@ -205,6 +205,16 @@ function statistik_manager_get_statistics() {
|
||||
// Banner an der richtigen Stelle einhängen – abhängig von der konfigurierten
|
||||
// Position. Nur wenn das Banner auch wirklich aktiviert ist.
|
||||
function statistik_manager_register_banner() {
|
||||
// Abstand oben für fixe Leisten (ShiftNav-Menüleiste usw.) wird IMMER
|
||||
// berechnet – auch ohne Banner –, sonst liegt der Seitenkopf darunter.
|
||||
add_action('wp_head', 'statistik_manager_banner_offset');
|
||||
|
||||
// Am Seitenende sofort nachrechnen – dann sind Banner und fixe Leisten
|
||||
// (z. B. ShiftNav) garantiert im DOM, auch wenn DOMContentLoaded spät kommt.
|
||||
add_action('wp_footer', function() {
|
||||
echo '<script>window.statistikManagerBannerAdjust&&window.statistikManagerBannerAdjust();</script>';
|
||||
}, 999);
|
||||
|
||||
if (!get_option('statistik_manager_banner_enabled', 1)) {
|
||||
return;
|
||||
}
|
||||
@@ -226,58 +236,47 @@ function statistik_manager_get_statistics() {
|
||||
statistik_manager_display_banner('top');
|
||||
}, 5);
|
||||
}
|
||||
|
||||
// Abstand für den Seiteninhalt, damit das fixe Banner nichts verdeckt
|
||||
add_action('wp_head', 'statistik_manager_banner_offset');
|
||||
|
||||
// Am Seitenende sofort nachrechnen – dann sind Banner und fixe Leisten
|
||||
// (z. B. ShiftNav) garantiert im DOM, auch wenn DOMContentLoaded spät kommt.
|
||||
add_action('wp_footer', function() {
|
||||
echo '<script>window.statistikManagerBannerAdjust&&window.statistikManagerBannerAdjust();</script>';
|
||||
}, 999);
|
||||
}
|
||||
add_action('wp', 'statistik_manager_register_banner');
|
||||
|
||||
// Fügt NUR bei aktivem Banner den passenden Abstand hinzu – auf der richtigen
|
||||
// Seite (oben/unten) und mit Rücksicht auf die WordPress-Admin-Leiste.
|
||||
// Sorgt dafür, dass fixe Leisten oben (ShiftNav-Menüleiste) und – falls aktiv –
|
||||
// das Banner keinen Inhalt verdecken. Die WordPress-Admin-Leiste ist per
|
||||
// html-margin bereits berücksichtigt und wird nicht doppelt gezählt.
|
||||
function statistik_manager_banner_offset() {
|
||||
if (!get_option('statistik_manager_banner_enabled', 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$enabled = (bool) get_option('statistik_manager_banner_enabled', 1);
|
||||
$pos = get_option('statistik_manager_banner_position', 'top');
|
||||
$h = 56; // ungefähre Bannerhöhe – nur für den No-JS-Fallback
|
||||
|
||||
// Fallback ohne JavaScript (WordPress verschiebt die Seite für die
|
||||
// Admin-Leiste bereits selbst per html-margin, daher hier nicht addieren).
|
||||
echo '<noscript><style>';
|
||||
if ($pos === 'bottom') {
|
||||
echo 'body { padding-bottom: ' . $h . 'px !important; }';
|
||||
} else {
|
||||
echo 'body { padding-top: ' . $h . 'px !important; }';
|
||||
if (is_admin_bar_showing()) {
|
||||
echo '#statistik-manager-banner { top: 32px !important; }';
|
||||
echo '@media screen and (max-width: 782px) { #statistik-manager-banner { top: 46px !important; } }';
|
||||
if ($enabled) {
|
||||
$h = 56; // ungefähre Bannerhöhe – nur für den No-JS-Fallback
|
||||
echo '<noscript><style>';
|
||||
if ($pos === 'bottom') {
|
||||
echo 'body { padding-bottom: ' . $h . 'px !important; }';
|
||||
} else {
|
||||
echo 'body { padding-top: ' . $h . 'px !important; }';
|
||||
if (is_admin_bar_showing()) {
|
||||
echo '#statistik-manager-banner { top: 32px !important; }';
|
||||
echo '@media screen and (max-width: 782px) { #statistik-manager-banner { top: 46px !important; } }';
|
||||
}
|
||||
}
|
||||
echo '</style></noscript>';
|
||||
}
|
||||
echo '</style></noscript>';
|
||||
|
||||
// Mit JavaScript: Banner exakt unter fixe Leisten oben setzen (WP-Admin-Leiste,
|
||||
// ShiftNav-Menüleiste), sonst verdecken diese es. Der Inhalt wird nur um die
|
||||
// tatsächliche Bannerhöhe verschoben – zusätzlich zum vorhandenen Abstand
|
||||
// des Themes, damit das Layout ansonsten unverändert bleibt.
|
||||
// Mit JavaScript: Unterkante der fixen Leisten oben messen, ein Top-Banner
|
||||
// direkt darunter setzen und den Inhalt um Leisten + Banner nach unten
|
||||
// schieben (zusätzlich zum vorhandenen Abstand des Themes).
|
||||
$is_bottom = ($pos === 'bottom') ? 'true' : 'false';
|
||||
echo '<script>(function(){'
|
||||
. 'var isBottom=' . $is_bottom . ';'
|
||||
. 'var bars=["wpadminbar","shiftnav-toggle-main"];'
|
||||
. 'var setTop=false,setBottom=false;'
|
||||
. 'function adjust(){'
|
||||
. 'if(!document.body){return;}'
|
||||
. 'var b=document.getElementById("statistik-manager-banner");'
|
||||
. 'if(!b){return;}'
|
||||
. 'var s=document.body.style,prop=isBottom?"padding-bottom":"padding-top";'
|
||||
. 's.removeProperty(prop);'
|
||||
. 'var base=parseFloat(getComputedStyle(document.body)[isBottom?"paddingBottom":"paddingTop"])||0;'
|
||||
. 'var extra=0;'
|
||||
. 'if(!isBottom){'
|
||||
. 'var s=document.body.style;'
|
||||
. 'if(setTop){s.removeProperty("padding-top");setTop=false;}'
|
||||
. 'if(setBottom){s.removeProperty("padding-bottom");setBottom=false;}'
|
||||
. 'var cs=getComputedStyle(document.body);'
|
||||
. 'var baseTop=parseFloat(cs.paddingTop)||0,baseBottom=parseFloat(cs.paddingBottom)||0;'
|
||||
. 'var off=0;'
|
||||
. 'bars.forEach(function(id){'
|
||||
. 'var e=document.getElementById(id);if(!e){return;}'
|
||||
@@ -286,13 +285,11 @@ function statistik_manager_get_statistics() {
|
||||
. 'var r=e.getBoundingClientRect();'
|
||||
. 'if(r.height>0&&r.bottom>off){off=r.bottom;}'
|
||||
. '});'
|
||||
. 'b.style.setProperty("top",off+"px","important");'
|
||||
// Inhalt komplett unter Leisten + Banner beginnen lassen; die Admin-Leiste
|
||||
// hat WordPress per html-margin bereits berücksichtigt.
|
||||
. 'var hm=parseFloat(getComputedStyle(document.documentElement).marginTop)||0;'
|
||||
. 'extra=Math.max(0,off-hm);'
|
||||
. '}'
|
||||
. 's.setProperty(prop,(base+extra+b.offsetHeight)+"px","important");'
|
||||
. 'var top=Math.max(0,off-hm);'
|
||||
. 'if(b&&!isBottom){b.style.setProperty("top",off+"px","important");top+=b.offsetHeight;}'
|
||||
. 'if(top>0){s.setProperty("padding-top",(baseTop+top)+"px","important");setTop=true;}'
|
||||
. 'if(b&&isBottom){s.setProperty("padding-bottom",(baseBottom+b.offsetHeight)+"px","important");setBottom=true;}'
|
||||
. '}'
|
||||
. 'window.statistikManagerBannerAdjust=adjust;'
|
||||
. 'function watch(){'
|
||||
|
||||
Reference in New Issue
Block a user