215 lines
9.2 KiB
Bash
Executable File
215 lines
9.2 KiB
Bash
Executable File
#!/bin/bash
|
|
# verify_article.sh — Prüft Artikel-HTML auf Einhaltung aller redaktionellen Standards
|
|
# Usage: ./verify_article.sh <artikel_datei.html>
|
|
# Exit 0 = alle Checks bestanden, Exit 1 = Fehler gefunden
|
|
#
|
|
# Aufruf aus Cronjobs: bash /home/hermes/workspace/verify_article.sh /home/hermes/workspace/artikel_*.html
|
|
|
|
set -euo pipefail
|
|
|
|
FILE="${1:-}"
|
|
ERRORS=0
|
|
WARNINGS=0
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[0;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
ok() { echo -e "${GREEN}✅ $1${NC}"; }
|
|
fail() { echo -e "${RED}❌ $1${NC}"; ERRORS=$((ERRORS+1)); }
|
|
warn() { echo -e "${YELLOW}⚠️ $1${NC}"; WARNINGS=$((WARNINGS+1)); }
|
|
|
|
if [ -z "$FILE" ] || [ ! -f "$FILE" ]; then
|
|
echo "Usage: $0 <artikel_datei.html>"
|
|
exit 1
|
|
fi
|
|
|
|
echo "=== Verifiziere: $FILE ==="
|
|
echo ""
|
|
|
|
# ── CHECK 1: Exactly one read-more ──────────────────────────────────
|
|
# Nur echte HTML-Tags zählen, nicht <hr...> Referenzen in QC-Zeilen
|
|
READMORE_COUNT=$(grep -c '<hr id="system-readmore"' "$FILE" || true)
|
|
if [ "$READMORE_COUNT" = "1" ]; then
|
|
ok "Read-More: genau 1"
|
|
elif [ "$READMORE_COUNT" = "0" ]; then
|
|
fail "Read-More: KEIN <hr id=\"system-readmore\" /> gefunden"
|
|
else
|
|
fail "Read-More: $READMORE_COUNT Stück (muss genau 1 sein)"
|
|
fi
|
|
|
|
# ── CHECK 2: No <br> immediately after read-more ────────────────────
|
|
if grep -A1 'system-readmore' "$FILE" | grep -q '<br'; then
|
|
fail "<br> direkt nach Read-More (erzeugt doppelte Linie)"
|
|
else
|
|
ok "Kein <br> nach Read-More"
|
|
fi
|
|
|
|
# ── CHECK 3: Kein <div class="meta"> (nur <p class="meta"> erlaubt) ──
|
|
if grep -q '<div class="meta">' "$FILE"; then
|
|
fail "<div class=\"meta\"> statt <p class=\"meta\"> (article_server regex schlägt fehl)"
|
|
else
|
|
ok "<p class=\"meta\"> korrekt (oder nicht vorhanden — System setzt Datum)"
|
|
fi
|
|
|
|
# ── CHECK 4: No emojis in article body (QC line excluded) ──────────
|
|
# Extract body between <article> and </article>, strip everything from QC line onward
|
|
BODY=$(sed -n '/<article>/,/<\/article>/p' "$FILE" | sed '/<p class="qc">/,$d')
|
|
if echo "$BODY" | python3 -c "import sys,re; t=sys.stdin.read(); sys.exit(0 if re.search(r'[\U0001F300-\U0001F9FF\U00002600-\U000027BF\U0001F600-\U0001F64F]',t) else 1)"; then
|
|
fail "Emojis im Artikel-Body gefunden"
|
|
else
|
|
ok "Keine Emojis"
|
|
fi
|
|
|
|
# ── CHECK 5: File ends with </html> ─────────────────────────────────
|
|
LAST_LINE=$(tail -1 "$FILE")
|
|
if echo "$LAST_LINE" | grep -q '</html>'; then
|
|
ok "Datei endet mit </html>"
|
|
else
|
|
fail "Letzte Zeile ist nicht </html> (Fremd-Content nach Artikel-Ende?)"
|
|
fi
|
|
|
|
# ── CHECK 6: No meta-line in body (title, date, reading time) ───────
|
|
if echo "$BODY" | grep -qP '<p[^>]*>.*\d{1,2}\.\s+\w+\s+\d{4}.*·.*Lesezeit'; then
|
|
fail "Meta-Zeile im Body (Datum · Lesezeit) — macht Article-Server selbst!"
|
|
fi
|
|
|
|
# ── CHECK 7: Sources heading uses span-strong, not h2 ───────────────
|
|
if echo "$BODY" | grep -q '<h2>Quellen</h2>'; then
|
|
fail "<h2>Quellen</h2> statt <span><strong>Quellen</strong></span>"
|
|
else
|
|
ok "Quellen-Überschrift korrekt (kein <h2>)"
|
|
fi
|
|
|
|
# ── CHECK 8: All links have target="_blank" ─────────────────────────
|
|
LINKS_TOTAL=$(echo "$BODY" | grep -o '<a ' | wc -l)
|
|
LINKS_BLANK=$(echo "$BODY" | grep -o 'target="_blank"' | wc -l)
|
|
if [ "$LINKS_TOTAL" -gt 0 ] && [ "$LINKS_TOTAL" != "$LINKS_BLANK" ]; then
|
|
MISSING=$((LINKS_TOTAL - LINKS_BLANK))
|
|
fail "$MISSING von $LINKS_TOTAL Links ohne target=\"_blank\""
|
|
else
|
|
ok "Alle $LINKS_TOTAL Links mit target=\"_blank\""
|
|
fi
|
|
|
|
# ── CHECK 9: Meta description present ───────────────────────────────
|
|
if grep -q '<meta name="description" content="[^"]*[a-zA-Z]' "$FILE"; then
|
|
ok "Meta-Description vorhanden"
|
|
else
|
|
fail "Meta-Description fehlt oder leer"
|
|
fi
|
|
|
|
# ── CHECK 10: No QC line outside <p class="qc"> ────────────────────
|
|
QC_COUNT=$(echo "$BODY" | grep -c 'class="qc"' || true)
|
|
QC_RAW=$(echo "$BODY" | grep -cP 'QC:\s*[✓✔]' || true)
|
|
if [ "$QC_RAW" -gt "$QC_COUNT" ]; then
|
|
fail "QC-Hinweis nicht in <p class=\"qc\"> (wird nicht gestrippt)"
|
|
else
|
|
ok "QC-Zeile korrekt in <p class=\"qc\">"
|
|
fi
|
|
|
|
# ── CHECK 11: Autoren gegen BGG verifizieren ─────────────────────────
|
|
echo ""
|
|
echo "--- BGG Autoren-Faktencheck ---"
|
|
AUTHOR_CHECK=$(python3 /home/hermes/workspace/check_authors.py "$FILE" 2>&1)
|
|
AUTHOR_EXIT=$?
|
|
echo "$AUTHOR_CHECK"
|
|
if [ $AUTHOR_EXIT -eq 1 ]; then
|
|
ERRORS=$((ERRORS + 1))
|
|
elif [ $AUTHOR_EXIT -eq 2 ]; then
|
|
WARNINGS=$((WARNINGS + 1))
|
|
fi
|
|
|
|
# ── CHECK 12: Keine XML-API-Referenzen im Artikel ──────────────────
|
|
if echo "$BODY" | grep -qiP 'xml\s*api|XMLAPI|xmlapi|betriebsgeheimnis'; then
|
|
fail "XML-API-Referenz oder 'Betriebsgeheimnis' im Artikel (nicht leser-tauglich)"
|
|
else
|
|
ok "Keine XML-API-Referenzen"
|
|
fi
|
|
|
|
# ── CHECK 13: Quellen-Block muss klickbare Links enthalten ──────────
|
|
# Sucht Quellen-Überschrift (h2, span, strong) und prüft ob danach Links kommen
|
|
SOURCES_SECTION=$(echo "$BODY" | sed -n '/<h2>Quellen<\/h2>/,$p; /<span.*>.*Quellen.*<\/span>/,$p' || echo "")
|
|
if [ -n "$SOURCES_SECTION" ]; then
|
|
SOURCE_LINKS=$(echo "$SOURCES_SECTION" | grep -c '<a href=' || true)
|
|
if [ "$SOURCE_LINKS" -eq 0 ]; then
|
|
fail "Quellen-Block ohne klickbare Links (nur Fließtext)"
|
|
else
|
|
ok "Quellen-Block mit $SOURCE_LINKS klickbaren Links"
|
|
fi
|
|
else
|
|
fail "Kein Quellen-Block gefunden (h2/span>Quellen fehlt)"
|
|
fi
|
|
|
|
# ── CHECK 14: Dachzeile vorhanden (div unter h1 mit border-bottom) ──
|
|
if echo "$BODY" | grep -qP '<div style="[^"]*border-bottom:2px solid #e0e0e0[^"]*"'; then
|
|
ok "Dachzeile vorhanden"
|
|
else
|
|
fail "Dachzeile fehlt (grauer Div-Balken unter h1)"
|
|
fi
|
|
|
|
# ── CHECK 15: Infobox vorhanden (f0f4f8 Hintergrund) ─────────────────
|
|
if echo "$BODY" | grep -q 'background:#f0f4f8'; then
|
|
ok "Infobox vorhanden"
|
|
else
|
|
fail "Infobox fehlt (Fakten-Tabelle mit #f0f4f8 Hintergrund)"
|
|
fi
|
|
|
|
# ── CHECK 16: Read-More NACH erstem Text-Absatz (Daniel 23.06.2026) ──
|
|
# NICHT "vor erstem h2" — sondern: der erste echte <p>-Absatz (nach <p class="meta">)
|
|
# MUSS VOR dem Read-More stehen. Read-More gehört ZWISCHEN Absatz 1 und Absatz 2.
|
|
READMORE_LINE=$(grep -n '<hr id="system-readmore"' "$FILE" | head -1 | cut -d: -f1)
|
|
# Finde den ersten echten <p> (nicht meta, nicht qc)
|
|
FIRST_P=$(grep -n '^<p>' "$FILE" | grep -v 'class="meta"' | grep -v 'class="qc"' | head -1 | cut -d: -f1 || echo "")
|
|
if [ -z "$FIRST_P" ]; then
|
|
warn "Kein <p>-Absatz gefunden — Read-More-Position nicht prüfbar"
|
|
elif [ "$FIRST_P" -lt "$READMORE_LINE" ]; then
|
|
ok "Read-More NACH Absatz 1 — korrekt (Absatz Zeile $FIRST_P, Read-More Zeile $READMORE_LINE)"
|
|
else
|
|
fail "Read-More VOR Absatz 1 (Zeile $READMORE_LINE < $FIRST_P). Intro fehlt — Joomla zeigt leere Vorschau!"
|
|
fi
|
|
|
|
# ── CHECK 17: JEDE Quelle im Quellen-Block hat <a href> ─────────────
|
|
# Extrahiere Quellen-Block (nach "Quellen" bis </ol>)
|
|
SRC_START=$(grep -n 'Quellen' "$FILE" | tail -1 | cut -d: -f1 || echo "")
|
|
if [ -n "$SRC_START" ]; then
|
|
SRC_LI=$(sed -n "$SRC_START,\$p" "$FILE" | grep -c '<li>' || true)
|
|
SRC_LINKED=$(sed -n "$SRC_START,\$p" "$FILE" | grep -c '<li>.*<a href=' || true)
|
|
if [ "$SRC_LI" -eq 0 ]; then
|
|
fail "Keine <li> im Quellen-Block"
|
|
elif [ "$SRC_LI" != "$SRC_LINKED" ]; then
|
|
UNLINKED=$((SRC_LI - SRC_LINKED))
|
|
fail "$UNLINKED von $SRC_LI Quellen ohne <a href> Link"
|
|
else
|
|
ok "Alle $SRC_LI Quellen verlinkt"
|
|
fi
|
|
fi
|
|
|
|
# ── CHECK 18: Recherchestand-Zeile vorhanden UND verlinkt ──────────
|
|
if echo "$BODY" | grep -q 'Recherchestand:'; then
|
|
if echo "$BODY" | grep 'Recherchestand:' | grep -q '<a href='; then
|
|
ok "Recherchestand-Zeile mit klickbaren Links"
|
|
else
|
|
fail "Recherchestand-Zeile OHNE klickbare Links — Fließtext reicht nicht!"
|
|
fi
|
|
else
|
|
fail "Recherchestand-Zeile fehlt"
|
|
fi
|
|
|
|
# ── SUMMARY ─────────────────────────────────────────────────────────
|
|
echo ""
|
|
echo "═══════════════════════════════════════"
|
|
if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then
|
|
echo -e "${GREEN}✅ ALLE CHECKS BESTANDEN${NC}"
|
|
echo "═══════════════════════════════════════"
|
|
exit 0
|
|
elif [ $ERRORS -eq 0 ]; then
|
|
echo -e "${YELLOW}⚠️ $WARNINGS Warnung(en) — KEINE Fehler${NC}"
|
|
echo "═══════════════════════════════════════"
|
|
exit 0
|
|
else
|
|
echo -e "${RED}❌ $ERRORS Fehler, $WARNINGS Warnungen${NC}"
|
|
echo "═══════════════════════════════════════"
|
|
exit 1
|
|
fi
|