#!/bin/bash # verify_article.sh — Prüft Artikel-HTML auf Einhaltung aller redaktionellen Standards # Usage: ./verify_article.sh # 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 " 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 '
gefunden" else fail "Read-More: $READMORE_COUNT Stück (muss genau 1 sein)" fi # ── CHECK 2: No
immediately after read-more ──────────────────── if grep -A1 'system-readmore' "$FILE" | grep -q ' direkt nach Read-More (erzeugt doppelte Linie)" else ok "Kein
nach Read-More" fi # ── CHECK 3: Kein
(nur

erlaubt) ── if grep -q '

' "$FILE"; then fail "
statt

(article_server regex schlägt fehl)" else ok "

korrekt (oder nicht vorhanden — System setzt Datum)" fi # ── CHECK 4: No emojis in article body (QC line excluded) ────────── # Extract body between

and
, strip everything from QC line onward BODY=$(sed -n '/
/,/<\/article>/p' "$FILE" | sed '/

/,$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 ───────────────────────────────── LAST_LINE=$(tail -1 "$FILE") if echo "$LAST_LINE" | grep -q ''; then ok "Datei endet mit " else fail "Letzte Zeile ist nicht (Fremd-Content nach Artikel-Ende?)" fi # ── CHECK 6: No meta-line in body (title, date, reading time) ─────── if echo "$BODY" | grep -qP ']*>.*\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 '

Quellen

'; then fail "

Quellen

statt Quellen" else ok "Quellen-Überschrift korrekt (kein

)" fi # ── CHECK 8: All links have target="_blank" ───────────────────────── LINKS_TOTAL=$(echo "$BODY" | grep -o 'Quellen<\/h2>/,$p; /.*Quellen.*<\/span>/,$p' || echo "") if [ -n "$SOURCES_SECTION" ]; then SOURCE_LINKS=$(echo "$SOURCES_SECTION" | grep -c '-Absatz (nach

) # MUSS VOR dem Read-More stehen. Read-More gehört ZWISCHEN Absatz 1 und Absatz 2. READMORE_LINE=$(grep -n '


(nicht meta, nicht qc) FIRST_P=$(grep -n '^

' "$FILE" | grep -v 'class="meta"' | grep -v 'class="qc"' | head -1 | cut -d: -f1 || echo "") if [ -z "$FIRST_P" ]; then warn "Kein

-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 ───────────── # Extrahiere Quellen-Block (nach "Quellen" bis ) 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 '

  • ' || true) SRC_LINKED=$(sed -n "$SRC_START,\$p" "$FILE" | grep -c '
  • .*