Jump to content

User:Dr. Thomas Liptak/common-new.js

From Wikimedia Commons, the free media repository
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
/* 
   Persönliches SDC‑Werkzeug für Dr. Thomas Liptak
   Version 2026‑01‑26‑D (Vollversion, Block 1/2)
   – Keine doppelten Statements
   – P170‑Block mit Qualifikatoren stabil
   – Depicted‑Template zuverlässig eingefügt
   – Manuelles Feld für P1071
   – P6216 = Q50423863
   – P571: EXIF > Wikitext, bei Fehler Fallback „somevalue“
*/

mw.loader.using(['mediawiki.api']).then(function () {
    $(function () {

        if (mw.config.get('wgNamespaceNumber') !== 6) return;

        const api = new mw.Api();
        const pageName = mw.config.get('wgPageName');
        const pageId = mw.config.get('wgArticleId');
        const entityId = 'M' + pageId;

        /* ---------------- Debug-Fenster ---------------- */

        const $debug = $('<div>').css({
            border: '1px solid #aaa',
            padding: '0.5em',
            marginTop: '1em',
            background: '#f0f0f0',
            fontSize: '0.9em',
            maxHeight: '200px',
            overflowY: 'auto'
        }).append('<b>Debug-Ausgabe:</b><br>');

        function log(msg, color = 'black') {
            $debug.append('<div style="color:' + color + '">' + msg + '</div>');
        }

        /* ---------------- Hilfsfunktionen ---------------- */

        function getPageWikitext() {
            return api.get({
                action: 'query',
                prop: 'revisions',
                rvprop: 'content',
                formatversion: 2,
                titles: pageName
            }).then(d => d.query.pages[0].revisions[0].content || '');
        }

        function getExistingClaims() {
            return api.get({
                action: 'wbgetentities',
                ids: entityId,
                props: 'claims'
            }).then(d => d.entities[entityId].claims || {});
        }

        function getExifDate() {
            const title = pageName.replace(/_/g, ' ');
            return api.get({
                action: 'query',
                prop: 'imageinfo',
                titles: title,
                iiprop: 'metadata'
            }).then(data => {
                const page = Object.values(data.query.pages)[0];
                const info = page.imageinfo && page.imageinfo[0];
                if (!info || !info.metadata) return null;

                let exif = null;
                info.metadata.forEach(m => {
                    if (['DateTimeOriginal', 'DateTimeDigitized', 'DateTime'].includes(m.name)) {
                        exif = m.value;
                    }
                });
                if (!exif) return null;

                const m = exif.match(/^(\d{4}):(\d{2}):(\d{2})/);
                return m ? `${m[1]}-${m[2]}-${m[3]}` : null;
            });
        }

        function getDateFromWikitext(wikitext) {
            const m = wikitext.match(/\|date\s*=\s*([0-9]{4}(-[0-9]{2})?(-[0-9]{2})?)/);
            return m ? m[1] : null;
        }

        function getLocationFromWikitext(wikitext) {
            const m = wikitext.match(/\{\{Location\|([0-9.\-]+)\|([0-9.\-]+)\}\}/);
            return m ? { lat: parseFloat(m[1]), lon: parseFloat(m[2]) } : null;
        }

        function reverseGeocode(lat, lon) {
            const sparql = `
SELECT ?place WHERE {
  ?place wdt:P625 ?coord .
  SERVICE wikibase:around {
    ?place wdt:P625 ?coord .
    bd:serviceParam wikibase:center "Point(${lon} ${lat})"^^geo:wktLiteral .
    bd:serviceParam wikibase:radius "5" .
  }
} LIMIT 1`;

            return $.ajax({
                url: "https://query.wikidata.org/sparql",
                data: { query: sparql, format: "json" },
                headers: { "Accept": "application/sparql-results+json" }
            }).then(res => {
                const b = res.results.bindings;
                if (!b.length) return null;
                const q = b[0].place.value.match(/Q\d+/);
                return q ? q[0] : null;
            }).catch(() => null);
        }

        function getCategoryQIDs() {
            const qids = [];
            $('#mw-normal-catlinks a').each(function (i, el) {
                if (i === 0) return;
                const t = $(el).text().trim();
                if (/^Q\d+$/.test(t)) qids.push(t);
            });
            return qids;
        }

        /* ---------------- SDC: Depicts (P180) ---------------- */

        async function setDepicts(existing, depictsInput) {
            const qids = getCategoryQIDs();

            if (depictsInput) {
                depictsInput.split(',').forEach(q => qids.push(q.trim()));
            }

            for (const q of qids) {
                if (!/^Q\d+$/.test(q)) continue;
                if (existing.P180 && existing.P180.some(c => c.mainsnak.datavalue.value.id === q)) continue;

                log(`Setze P180: ${q}`);
                try {
                    await api.postWithToken("csrf", {
                        action: "wbcreateclaim",
                        entity: entityId,
                        property: "P180",
                        snaktype: "value",
                        value: JSON.stringify({ "entity-type": "item", id: q })
                    });
                    log(`P180 ${q} gesetzt`, "green");
                } catch {
                    log(`Fehler bei P180 ${q}`, "red");
                }
            }
        }

        /* ---------------- SDC: Ort (P276) ---------------- */

        async function setLocation(existing, qid) {
            if (!qid || !/^Q\d+$/.test(qid)) return;
            if (existing.P276 && existing.P276.length > 0) return;

            log(`Setze P276: ${qid}`);
            try {
                await api.postWithToken("csrf", {
                    action: "wbcreateclaim",
                    entity: entityId,
                    property: "P276",
                    snaktype: "value",
                    value: JSON.stringify({ "entity-type": "item", id: qid })
                });
                log("P276 gesetzt", "green");
            } catch {
                log("Fehler bei P276", "red");
            }
        }

        /* ---------------- SDC: Koordinaten (P1259) ---------------- */

        async function setCoordinates(existing, coords) {
            if (!coords) return;
            if (existing.P1259 && existing.P1259.length > 0) return;

            log(`Setze P1259: ${coords.lat}, ${coords.lon}`);
            try {
                await api.postWithToken("csrf", {
                    action: "wbcreateclaim",
                    entity: entityId,
                    property: "P1259",
                    snaktype: "value",
                    value: JSON.stringify({
                        latitude: coords.lat,
                        longitude: coords.lon,
                        precision: 0.0001,
                        globe: "http://www.wikidata.org/entity/Q2"
                    })
                });
                log("P1259 gesetzt", "green");
            } catch {
                log("Fehler bei P1259", "red");
            }
        }

        /* ---------------- SDC: Herstellungsort (P1071) ---------------- */

        async function setOrigin(existing, manualQ, coords) {
            if (existing.P1071 && existing.P1071.length > 0) return;

            if (manualQ && /^Q\d+$/.test(manualQ)) {
                log(`Setze P1071 (manuell): ${manualQ}`);
                try {
                    await api.postWithToken("csrf", {
                        action: "wbcreateclaim",
                        entity: entityId,
                        property: "P1071",
                        snaktype: "value",
                        value: JSON.stringify({ "entity-type": "item", id: manualQ })
                    });
                    log("P1071 (manuell) gesetzt", "green");
                } catch {
                    log("Fehler bei P1071 (manuell)", "red");
                }
                return;
            }

            if (!coords) return;

            log("Reverse-Geocoding für P1071…");
            const qid = await reverseGeocode(coords.lat, coords.lon);
            if (!qid) {
                log("Kein Ort gefunden", "red");
                return;
            }

            log(`Setze P1071 (auto): ${qid}`);
            try {
                await api.postWithToken("csrf", {
                    action: "wbcreateclaim",
                    entity: entityId,
                    property: "P1071",
                    snaktype: "value",
                    value: JSON.stringify({ "entity-type": "item", id: qid })
                });
                log("P1071 (auto) gesetzt", "green");
            } catch {
                log("Fehler bei P1071 (auto)", "red");
            }
        }
        /* ---------------- SDC: Datum (P571) ---------------- */

        async function setDate(existing, exifDate, wikiDate) {
            if (existing.P571 && existing.P571.length > 0) return;

            const date = exifDate || wikiDate;
            if (!date) return;

            log(`Setze P571: ${date}`);

            try {
                await api.postWithToken("csrf", {
                    action: "wbcreateclaim",
                    entity: entityId,
                    property: "P571",
                    snaktype: "value",
                    value: JSON.stringify({
                        time: `+${date}T00:00:00Z`,
                        precision: 11,
                        timezone: 0,
                        calendarmodel: "http://www.wikidata.org/entity/Q1985727"
                    })
                });
                log("P571 gesetzt", "green");
                return;
            } catch {
                log("API akzeptiert Datum nicht – Fallback somevalue", "orange");
            }

            try {
                await api.postWithToken("csrf", {
                    action: "wbcreateclaim",
                    entity: entityId,
                    property: "P571",
                    snaktype: "somevalue"
                });
                log("P571 (somevalue) gesetzt", "green");
            } catch {
                log("Fehler bei P571 (somevalue)", "red");
            }
        }

        /* ---------------- SDC: Lizenz (P275) ---------------- */

        async function setLicense(existing) {
            if (existing.P275 && existing.P275.length > 0) return;

            const license = "Q18199165";

            log(`Setze P275: ${license}`);
            try {
                await api.postWithToken("csrf", {
                    action: "wbcreateclaim",
                    entity: entityId,
                    property: "P275",
                    snaktype: "value",
                    value: JSON.stringify({ "entity-type": "item", id: license })
                });
                log("P275 gesetzt", "green");
            } catch {
                log("Fehler bei P275", "red");
            }
        }

        /* ---------------- SDC: Copyright-Status (P6216) ---------------- */

        async function setCopyright(existing) {
            if (existing.P6216 && existing.P6216.length > 0) return;

            const status = "Q50423863";

            log(`Setze P6216: ${status}`);
            try {
                await api.postWithToken("csrf", {
                    action: "wbcreateclaim",
                    entity: entityId,
                    property: "P6216",
                    snaktype: "value",
                    value: JSON.stringify({ "entity-type": "item", id: status })
                });
                log("P6216 gesetzt", "green");
            } catch {
                log("Fehler bei P6216", "red");
            }
        }

        /* ---------------- Wikitext: Depicted-Template ---------------- */

        function buildDepictedTemplate(qid) {
            return `{{Depicted|${qid}|infofield=yes}}`;
        }

        function findInsertionPoint(wikitext) {
            const patterns = [
                "|description=",
                "|Description=",
                "description =",
                "{{Information"
            ];

            for (const p of patterns) {
                const idx = wikitext.indexOf(p);
                if (idx !== -1) {
                    const lineEnd = wikitext.indexOf("\n", idx);
                    return lineEnd === -1 ? wikitext.length : lineEnd + 1;
                }
            }

            const lower = wikitext.toLowerCase();
            const catIndex = lower.indexOf("\n[[category:");
            if (catIndex !== -1) return catIndex;

            return wikitext.length;
        }

        async function updateWikitextDepicted(wikitext, originalQ) {
            if (!originalQ) return;
            if (/\{\{Depicted\|/i.test(wikitext)) return;

            const template = buildDepictedTemplate(originalQ);
            const pos = findInsertionPoint(wikitext);
            const newText = wikitext.slice(0, pos) + template + "\n" + wikitext.slice(pos);

            log(`Schreibe Depicted-Template: ${originalQ}`);
            try {
                await api.postWithToken("csrf", {
                    action: "edit",
                    title: pageName,
                    text: newText,
                    summary: "Depicted-Template aus SDC ergänzt",
                    minor: true
                });
                log("Wikitext aktualisiert", "green");
            } catch {
                log("Fehler beim Wikitext-Update", "red");
            }
        }

        /* ---------------- SDC: Urheber (P170) – MediaInfo-kompatibel ---------------- */

async function setCreatorP170(existing) {
    if (existing.P170 && existing.P170.length > 0) {
        log("P170 existiert bereits.");
        return;
    }

    log("Setze P170…");

    let claimId = null;

    try {
        const res = await api.postWithToken("csrf", {
            action: "wbcreateclaim",
            entity: entityId,
            property: "P170",
            snaktype: "value",
            value: JSON.stringify({ "entity-type": "item", id: "Q2500638" })
        });

        // MediaInfo liefert claim-id hier:
        claimId = res.claim?.id || res.claimId || res["claim-id"];

        if (!claimId) {
            // Fallback: Claim-ID aus wbgetentities neu laden
            const updated = await getExistingClaims();
            const p170 = updated.P170?.[0];
            claimId = p170?.id;
        }

        if (!claimId) {
            log("Konnte Claim-ID für P170 nicht ermitteln.", "red");
            return;
        }

        log("P170 gesetzt: " + claimId, "green");

    } catch (e) {
        log("Fehler bei P170", "red");
        return;
    }

    /* Qualifikatoren */
    const qualifiers = [
        { property: "P2093", value: JSON.stringify("Dr. Thomas Liptak") },
        { property: "P4174", value: JSON.stringify("Dr._Thomas_Liptak") },
        { property: "P2699", value: JSON.stringify("https://commons.wikimedia.org/wiki/User:Dr._Thomas_Liptak") },
        { property: "P3831", value: JSON.stringify({ "entity-type": "item", id: "Q33231" }) }
    ];

    for (const q of qualifiers) {
        try {
            await api.postWithToken("csrf", {
                action: "wbsetqualifier",
                claim: claimId,
                property: q.property,
                snaktype: "value",
                value: q.value
            });
            log(`Qualifikator ${q.property} gesetzt`, "green");
        } catch {
            log(`Fehler bei Qualifikator ${q.property}`, "red");
        }
    }

    log("Urheber-Block (P170 + Qualifikatoren) fertig.", "green");
}


        /* ---------------- UI ---------------- */

        const $box = $('<div>').css({
            border: '2px solid #ccc',
            padding: '1em',
            marginTop: '1em',
            background: '#f8f8f8'
        }).append('<h3>Meine SDC‑Werkzeuge</h3>');

        const $depictsInput = $('<input>')
            .attr('placeholder', 'Zusätzliche Depicts (Q-ID)')
            .css({ width: '100%', marginBottom: '0.5em' });

        const $locationInput = $('<input>')
            .attr('placeholder', 'Aufnahmeort (P276, Q-ID, optional)')
            .css({ width: '100%', marginBottom: '0.5em' });

        const $originInput = $('<input>')
            .attr('placeholder', 'Herstellungsort (P1071, Q-ID, optional)')
            .css({ width: '100%', marginBottom: '0.5em' });

        const $button = $('<button>')
            .text('Meine SDC-Standarddaten setzen')
            .css({
                padding: '0.5em 1em',
                background: '#36c',
                color: 'white',
                border: 'none',
                cursor: 'pointer'
            });

        $box.append($depictsInput, $locationInput, $originInput, $button, $debug);
        $('.mw-parser-output').prepend($box);

        /* ---------------- Hauptlogik ---------------- */

        $button.on('click', function () {
            $button.prop('disabled', true).text('Bitte warten…');
            $debug.html('<b>Debug-Ausgabe:</b><br>');

            $.when(getPageWikitext(), getExifDate(), getExistingClaims())
                .done(async function (wikitext, exifDate, existing) {

                    const coords = getLocationFromWikitext(wikitext);
                    const wikiDate = getDateFromWikitext(wikitext);

                    /* P180 */
                    log("Setze Depicts…");
                    await setDepicts(existing, $depictsInput.val());
                    existing = await getExistingClaims();

                    /* P276 */
                    log("Setze Ort (P276)…");
                    await setLocation(existing, $locationInput.val());
                    existing = await getExistingClaims();

                    /* P1259 */
                    log("Setze Koordinaten (P1259)…");
                    await setCoordinates(existing, coords);
                    existing = await getExistingClaims();

                    /* P1071 */
                    log("Setze Herstellungsort (P1071)…");
                    await setOrigin(existing, $originInput.val().trim(), coords);
                    existing = await getExistingClaims();

                    /* P571 */
                    log("Setze Datum (P571)…");
                    await setDate(existing, exifDate, wikiDate);
                    existing = await getExistingClaims();

                    /* P275 */
                    log("Setze Lizenz (P275)…");
                    await setLicense(existing);
                    existing = await getExistingClaims();

                    /* P6216 */
                    log("Setze Copyright (P6216)…");
                    await setCopyright(existing);
                    existing = await getExistingClaims();

                    /* P170 */
                    log("Setze Urheber (P170)…");
                    await setCreatorP170(existing);
                    existing = await getExistingClaims();

                    /* Depicted-Template */
                    let originalDepictsQ = null;
                    if (existing.P180 && existing.P180.length > 0) {
                        const first = existing.P180[0];
                        if (first.mainsnak && first.mainsnak.datavalue && first.mainsnak.datavalue.value) {
                            originalDepictsQ = first.mainsnak.datavalue.value.id;
                        }
                    }

                    log("Aktualisiere Wikitext (Depicted)…");
                    await updateWikitextDepicted(wikitext, originalDepictsQ);

                    log("Fertig!", "green");
                    $button.prop('disabled', false).text('Meine SDC-Standarddaten setzen');
                });
        });

    });
});