User:Nikki/ItemUsage.js

From Wikidata
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/* This script runs on the delete page and on the page information page.
   It checks whether an item is in use in a variety of places
   to help you avoid deleting items which are actually being used somewhere.
   
   Note: This script makes requests to overpass-api.de to look for usage in OSM.

   To use it, add the following to your common.js:
   mw.loader.load("//www.wikidata.org/w/index.php?title=User:Nikki/ItemUsage.js&action=raw&ctype=text/javascript");
   
   Known issues:
   * Doesn't work properly for Structured Data on Commons - often finds false
	 positives, probably fails sometimes
   * Can't detect usage in lemma/form language codes on lexemes
   * Colours should probably be configurable
   * So many messages... it would be possible to simplify it a bit
*/

(function (mw, wb, $) {

var messages = (function () {
    var translations = {
        en: {
			"heading-usage-check": "Usage check",
			"checking-sitelinks": "Checking sitelinks...",
			"checking-items": "Checking items...",
			"checking-properties": "Checking properties...",
			"checking-lexemes": "Checking lexemes...",
			"checking-sdoc": "Checking structured data on Commons...",
			"checking-wikidata-other": "Checking other Wikidata pages...",
			"checking-wikimedia-other": "Checking other Wikimedia projects...",
			"checking-notability-exemptions": "Checking for notability policy exemptions...",
			"checking-osm": "Checking OSM...",
			"checking-usage": "Checking usage:",
			"check-usage": "check usage",
			"item-sitelinks": "Item has sitelinks",
			"item-no-sitelinks": "Item has no sitelinks",
			"item-wikidata": "Item is used by Wikidata pages",
			"item-no-wikidata": "Item is not used by any other Wikidata pages",
			"item-wikimedia-other": "Item is used by other Wikimedia projects",
			"item-no-wikimedia-other": "Item is not used by any other Wikimedia projects",
			"item-osm": "Item is linked from objects in OSM",
			"item-no-osm": "Item is not used by OSM",
			"item-items": "Item is used by {{PLURAL:$1|an item|$1 items}}",
			"item-items-more": "Item is used by $1+ items",
			"item-no-items": "Item is not used by any items",
			"item-properties": "Item is used by {{PLURAL:$1|a property|$1 properties}}",
			"item-properties-more": "Item is used by $1+ properties",
			"item-no-properties": "Item is not used by any properties",
			"item-lexemes": "Item is used by {{PLURAL:$1|a lexeme|$1 lexemes}}",
			"item-lexemes-more": "Item is used by $1+ lexemes",
			"item-no-lexemes": "Item is not used by any lexemes",
			"item-sdoc": "Item is used in structured data by {{PLURAL:$1|a file|$1 files}} on Commons",
			"item-no-sdoc": "Item is not used in structured data on Commons",
			"item-notability-exemptions": "Item is a notability policy exemption",
			"item-no-notability-exemptions": "Item is not a notability policy exemption",
			"what-links-here": "check what links here",
			"osm-view-results": "view query results",
			"not-actually-used": " - not actually used (see T206857)",
			"more": "and more..."
		},
		de: {
			"heading-usage-check": "Überprüfung der Objektverwendung",
			"checking-sitelinks": "Nach Interwikilinks suchen ...",
			"checking-items": "Nach Links von anderen Datenobjekten suchen ...",
			"checking-properties": "Nach Links von Eigenschaften suchen ...",
			"checking-lexemes": "Nach Links von Lexemen suchen ...",
			"checking-wikidata-other": "Nach Benutzung auf anderen Wikidata-Seiten suchen ...",
			"checking-wikimedia-other": "Nach Benutzung durch andere Wikimedia-Projekte suchen ...",
			"checking-notability-exemptions": "Nach Ausnahmen der Relevanzkriterien suchen ...",
			"checking-osm": "Nach Links von OSM suchen ...",
			"checking-usage": "Überprüfung der Objektverwendung:",
			"check-usage": "Objektverwendung ansehen",
			"item-sitelinks": "Datenobjekt hat Interwikilinks",
			"item-no-sitelinks": "Datenobjekt hat keine Interwikilinks",
			"item-wikidata": "Datenobjekt wird von anderen Wikidata-Seiten verwendet",
			"item-no-wikidata": "Datenobjekt wird nicht von anderen Wikidata-Seiten verwendet",
			"item-wikimedia-other": "Datenobjekt wird durch andere Wikimedia-Projekte verwendet",
			"item-no-wikimedia-other": "Datenobjekt wird nicht durch andere Wikimedia-Projekte verwendet",
			"item-osm": "Datenobjekt wird von Objekten in OSM verlinkt",
			"item-no-osm": "Datenobjekt wird nicht von Objekten in OSM verlinkt",
			"item-items": "Datenobjekt wird von {{PLURAL:$1|einem anderen Datenobjekt|$1 anderen Datenobjekten}} verlinkt",
			"item-items-more": "Datenobjekt wird von mindestens $1 anderen Datenobjekten verlinkt",
			"item-no-items": "Datenobjekt wird nicht von anderen Datenobjekten verlinkt",
			"item-properties": "Datenobjekt wird von {{PLURAL:$1|einer Eigenschaft|$1 Eigenschaften}} verlinkt",
			"item-properties-more": "Datenobjekt wird von mindestens $1 Eigenschaften verlinkt",
			"item-no-properties": "Datenobjekt wird nicht von Eigenschaften verlinkt",
			"item-lexemes": "Datenobjekt wird von {{PLURAL:$1|einem Lexem|$1 Lexemen}} verlinkt",
			"item-lexemes-more": "Datenobjekt wird von mindestens $1 Lexemen verlinkt",
			"item-no-lexemes": "Datenobjekt wird nicht von Lexemen verlinkt",
			"item-notability-exemptions": "Datenobjekt ist eine Ausnahme der Relevanzkriterien",
			"item-no-notability-exemptions": "Datenobjekt ist keine Ausnahme der Relevanzkriterien",
			"what-links-here": "Links auf diese Seite ansehen",
			"osm-view-results": "Suchergebnisse ansehen",
			"not-actually-used": " - wird eigentlich nicht verwendet (siehe T206857)",
			"more": "und noch mehr ..."
		}
	},
    chain = mw.language.getFallbackLanguageChain(),
    len = chain.length,
    ret = {},
    i = len - 1;
    while (i >= 0) {
        if (translations.hasOwnProperty(chain[i])) {
            $.extend(ret, translations[chain[i]]);
        }
        i = i - 1;
    }
    return ret;
}());
mw.messages.set(messages);

var text_colour_good = "green";
var text_colour_bad = "#c00";
var icon_good = "//upload.wikimedia.org/wikipedia/commons/thumb/4/41/Symbol_confirmed.svg/23px-Symbol_confirmed.svg.png";
var icon_bad = "//upload.wikimedia.org/wikipedia/commons/thumb/8/85/Symbol_unrelated.svg/23px-Symbol_unrelated.svg.png";

var osm_api_url = "https://overpass-api.de/api/interpreter";
var osm_keys = ["wikidata", "operator:wikidata", "brand:wikidata", "architect:wikidata", "artist:wikidata", "subject:wikidata", "name:etymology:wikidata"];

var h2 = "<h2>" + messages["heading-usage-check"] + "</h2>";
var html = "<div style=\"clear:both; padding:5px\" id=\"usage-check\">\
	<ul>\
	<li id=\"usage-sitelinks\">" + messages["checking-sitelinks"] + "</li>\
	<li id=\"usage-items\">" + messages["checking-items"] + "</li>\
	<li id=\"usage-properties\">" + messages["checking-properties"] + "</li>\
	<li id=\"usage-lexemes\">" + messages["checking-lexemes"] + "</li>\
	<li id=\"usage-sdoc\">" + messages["checking-sdoc"] + "</li>\
	<li id=\"usage-wikidataother\">" + messages["checking-wikidata-other"] + "</li>\
	<li id=\"usage-otherwikimedia\">" + messages["checking-wikimedia-other"] + "</li>\
	<li id=\"usage-exception\">" + messages["checking-notability-exemptions"] + "</li>\
	<li id=\"usage-osm\">" + messages["checking-osm"] + "</li>\
	</ul>\
	</div>";

if (document.location.href.match(/\/w\/index.php\?title=Q[0-9]+&action=info/)) {
	$(".mw-page-info").last().after(h2 + html);
} else if (document.location.href.match(/\/w\/index.php\?title=Q[0-9]+&action=delete/) && $("#deleteconfirm").length > 0) {
	$("#mw-content-text").prepend(html);
	$("#usage-check").css("border", "1px solid lightgrey");
	$("#usage-check").prepend(messages["checking-usage"]);
	$(".mw-warning").css("display", "none");
}

function update_status(id, message, is_good) {
	if (is_good) {
		image = "<img src=\"" + icon_good + "\" width=\"15\" height=\"15\" />";
		text_colour = text_colour_good;
	} else {
		image = "<img src=\"" + icon_bad + "\" width=\"15\" height=\"15\" />";
		text_colour = text_colour_bad;
	}

	$(id).html(image + " " + message);
	$(id).css("color", text_colour);
}

function update_backlink_info (ns) {
	$.getJSON("https://www.wikidata.org/w/api.php?format=json&action=query&list=backlinks&bltitle=" + qid + "&bllimit=" + limit + "&blnamespace=" + ns.id + "&blfilterredir=nonredirects").done(function (data) {
		if (data.query.backlinks.length > 0) {
			var results = data.query.backlinks.length;
			var message = (results == limit) ? mw.msg("item-"+ns.key+"-more", results) : mw.msg("item-"+ns.key, results);

			update_status("#usage-"+ns.key, message +
				"<ul><li><a href=\"/wiki/Special:WhatLinksHere/" + qid + "?namespace=" + ns.id + "\">" + mw.msg("what-links-here") + "</a></li></ul>", false);
		} else {
			update_status("#usage-"+ns.key, mw.msg("item-no-"+ns.key), true);
		}
	});
}

function verify_wikimedia_entity_usage (qid, site, api_hostname) {
	api_hostname = api_hostname.replace(/(https:\/\/[^\/]+)\/.*/, "$1");
	$.getJSON(api_hostname + "/w/api.php?format=json&origin=https://www.wikidata.org&action=query&list=wblistentityusage&wbeuentities=" + qid + "&wbeuprop=url").done(function (data) {
		if (!(data.hasOwnProperty("query") && data.query.hasOwnProperty("pages")))
			$("#usage-"+site).append(mw.msg("not-actually-used"));
	});
}

function verify_wikidata_entity_usage (qid) {
	var pages_to_ignore = [
		"User:Pasleim/Items for deletion/Page deleted",
		"User:Pasleim/notability",
		"Wikidata:Database reports",
		"Wikidata:Requests for deletions"
	];
	$.getJSON("https://www.wikidata.org/w/api.php?format=json&action=query&list=wblistentityusage&wbeuentities=" + qid + "&wbeuprop=url").done(function (data) {
		var skipped = 0;
		if (data.query) {
			for (var page_id in data.query.pages) {
				for (var page of pages_to_ignore) {
					if (data.query.pages[page_id].title.startsWith(page))
						skipped = skipped + 1;
				}
			}
		}

		if (data.query && Object.keys(data.query.pages).length !== skipped) {
			update_status("#usage-wikidataother", messages["item-wikidata"] + "<ul><li><a href=\"/wiki/Special:EntityUsage/" + qid + "\">" + messages["check-usage"] + "</a></li></ul>", false);
		} else {
			update_status("#usage-wikidataother", messages["item-no-wikidata"], true);
		}
	});
}


if (document.location.href.match(/\/w\/index.php\?title=Q[0-9]+&action=info/)
	|| document.location.href.match(/\/w\/index.php\?title=Q[0-9]+&action=delete/) && $("#deleteconfirm").length > 0) {


var qid = mw.config.get("wgPageName");
if (qid && mw.config.get("wgNamespaceNumber") === 0) {

/* Sitelinks */
$.getJSON("https://www.wikidata.org/wiki/Special:EntityData/" + qid + ".json").done(function (data) {
	if (Object.keys(data.entities[qid].sitelinks).length > 0) {
		update_status("#usage-sitelinks", messages["item-sitelinks"], false);
	} else {
		update_status("#usage-sitelinks", messages["item-no-sitelinks"], true);
	}
});

/* Structured data on Commons */
$.getJSON("https://commons.wikimedia.org/w/api.php?action=query&list=search&format=json&callback=?&srnamespace=6&srsearch=" + qid).done(function (data) {
	if (data.query.searchinfo.totalhits > 0) {
		update_status("#usage-sdoc", mw.msg("item-sdoc", data.query.searchinfo.totalhits), false);
	} else {
		update_status("#usage-sdoc", messages["item-no-sdoc"], true);
	}
});

/* Data usage */
$.getJSON("https://www.wikidata.org/w/api.php?format=json&action=query&list=wbsubscribers&wblsentities=" + qid + "&wblslimit=500&wblsprop=url").done(function (data) {
	var subscribers = [];
	if (data.query.subscribers)
		subscribers = data.query.subscribers[qid].subscribers;

	var inwikidata = false;
	var wikis = $.map(subscribers, function (v) {
		if (v.site == "wikidatawiki")
			inwikidata = true;
		else
			return v;
	});

	if (inwikidata) {
		verify_wikidata_entity_usage(qid);
	} else {
		update_status("#usage-wikidataother", messages["item-no-wikidata"], true);
	}
	
	if (wikis.length > 0) {
		var count = 0;
		update_status("#usage-otherwikimedia", messages["item-wikimedia-other"] + "<ul></ul>", false);
		$.each(wikis, function (i, v) {
			if (count >= 10)
				return;
			count = count + 1;
			$("#usage-otherwikimedia ul").append("<li id=\"usage-" + v.site + "\"><a href=\"" + v.url + "\">" + v.site + "</a></li>");
			verify_wikimedia_entity_usage(qid, v.site, v.url);
		});
		if (count >= 10 || data.hasOwnProperty("continue"))
			$("#usage-otherwikimedia ul").append("<li>" + mw.msg("more") + "</li>");
	} else {
		update_status("#usage-otherwikimedia", messages["item-no-wikimedia-other"], true);
	}
});

/* OSM */
var rows = [];
rows.push("[out:json][timeout:25]; (");
for (var i = 0; i < osm_keys.length; i++) {
	rows.push("nwr[\"" + osm_keys[i] + "\"=\"" + qid + "\"]; ");
}
rows.push("); out body; out skel qt;");
var query = rows.join("\n");

$.ajax({
	method: "POST",
	url: osm_api_url,
	data: query
}).done(function (data) {
	if (data.elements.length > 0) {
		$("#usage-osm").html("<img src=\"" + icon_bad + "\" width=\"15\" height=\"15\" /> " + messages["item-osm"] +
			"<ul><li><a href=\"https://overpass-turbo.eu/?R&Q=" + encodeURIComponent(query.replace(/out body;/, "out body; (._;>;);")) + "\">" + messages["osm-view-results"] + "</a></li></ul>");
		$("#usage-osm").css("color", "#c00");
	} else {
		update_status("#usage-osm", messages["item-no-osm"], true);
	}
});

/* Backlinks from items, properties and lexemes */
var limit = 50;
var namespaces = [
	{ id: 0, key: "items" },
	{ id: 120, key: "properties" },
	{ id: 146, key: "lexemes" }
];

for (var ns of namespaces) {
	update_backlink_info(ns);
}

/* Notability policy exemptions */
$.getJSON("/w/api.php?format=json&action=query&prop=categories&titles=Talk:" + qid + "&clcategories=Category:Notability%20policy%20exemptions").done(function (data) {
	$.each(data.query.pages, function (i, v) {
		if (v.categories && v.categories.length > 0) {
			update_status("#usage-exception", messages["item-notability-exemptions"] + "<ul><li><a href=\"/wiki/Talk:" + qid + "\">Talk:" + qid + "</a>", false);
		} else {
			update_status("#usage-exception", messages["item-no-notability-exemptions"], true);
		}
	});
});

}

}

}(mediaWiki, wikibase, jQuery));