User:TheDJ/RemoveGPS.js
Appearance
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.
| Documentation for this user script can be added at User:TheDJ/RemoveGPS. |
/**
* RemoveGPS.js - upload a new version of an image file that has GPS (coordinate)
* data removed from its EXIF metadata, then remove that data from the file page's wikitext
* and structured data as well.
*
* @author Yaron Koren
* @license MIT
*/
( function( mw, $ ) {
const removeGPSMessages = {
en: {
"remove-gps-upload-summary": "Uploaded new version of image, with GPS metadata removed, via $1",
"remove-gps-edit-summary": "Removed {{location}} template from page via $1",
"remove-gps-tasks-header": "Doing the following tasks:",
"remove-gps-upload-task": "Uploading new version of the file with GPS EXIF data removed",
"remove-gps-edit-task": "Replacing {{location}} template in the page text with {{location withheld}}",
"remove-gps-structured-data-task": "Removing the \"coordinates of the point of view\" (P1259) value from this page's structured data",
"remove-gps-notify-admins-task": "Notifying Commons administrators to hide old revisions of the file and wiki page",
"remove-gps-page-will-reload": "The page will reload on completion.",
"remove-gps-notify-admins":
"Request Wikimedia Commons administrators to delete previous revisions of the file and wiki page that still contain the GPS data",
"remove-gps-notify-admins-warning": "(note that this will allow administrators to see your email address, if you have one set)",
"remove-gps-button-label": "Upload a new version of this file with GPS metadata removed"
},
};
const rgpsUserLang = mw.config.get("wgUserLanguage") || "en";
mw.messages.set(removeGPSMessages[rgpsUserLang] || removeGPSMessages.en);
// Make sure OOUI is loaded, because we are using its CSS.
mw.loader.using( [ 'oojs-ui-core', 'oojs-ui-widgets' ] ).then( function () {
// There is a chained set of commands once the button is pressed, in the following order:
// 1. Get the new, GPS-less version of the file (and possibly notify the Commons admins).
// 2. Upload the new version of the file.
// 3. Get the current wikitext of the page.
// 4. Save the page, with the {{location}} call in the wikitext replaced with {{location withheld}}.
// 5. Get the claim ID(s) for the "coordinates of the point of view" value in the page's structured data.
// 6. Remove the claim for each such ID.
// 7. Reload the page.
const api = new mw.Api({ userAgent: 'RemoveGPS', parameters: { formatversion: 2 } });
const removeGPS = {};
removeGPS.namespaceName = mw.config.get('wgCanonicalNamespace');
removeGPS.pageName = mw.config.get('wgTitle');
removeGPS.gpsRemovalToolURL = "https://exif-gps-removal.toolforge.org/?file=" + encodeURIComponent(removeGPS.pageName);
removeGPS.toolLink = '[[Commons:RemoveGPS|RemoveGPS]]'
// Step 1
removeGPS.getNewVersionOfFile = async function() {
const notifyAdmins = $('#gps-removal-checkbox').is(':checked');
if ( notifyAdmins ) {
removeGPS.gpsRemovalToolURL += '¬ify=yes&user=' + encodeURIComponent(mw.config.get('wgUserName'));
const response = await api.get({
action: 'query',
meta: 'userinfo',
uiprop: 'email'
});
const emailAddress = response.query.userinfo.email;
removeGPS.gpsRemovalToolURL += '&email=' + encodeURIComponent(emailAddress);
}
removeGPS.replaceButtonWithText( notifyAdmins );
$.ajax({
url: removeGPS.gpsRemovalToolURL,
method: "GET",
xhrFields: {
responseType: 'blob'
},
success: function(result) {
removeGPS.uploadNewVersionOfFile(result);
}
});
}
// Step 2
removeGPS.uploadNewVersionOfFile = function(newFileContent) {
api.upload( newFileContent, {
filename: removeGPS.pageName,
comment: mw.msg("remove-gps-upload-summary", removeGPS.toolLink),
ignorewarnings: true // Ignore warnings like duplicate filename
}).done(function(data) {
removeGPS.getPageText();
}).fail(function(error, details) {
if ( details.upload && details.upload.result == 'Success') {
// The upload "failed", but actually succeeded, so keep going.
// The "failure" is presumably because of the "duplicate filename" warning -
// why isn't "ignorewarnings" taking effect here?
removeGPS.getPageText();
} else {
console.error('Upload failed:', error);
console.log(details);
}
});
}
// Step 3
removeGPS.getPageText = function() {
$.get('https://commons.wikimedia.org/wiki/File:' + removeGPS.pageName + '?action=raw', function(data) {
const textWithoutLocationTemplate = data.replace( /\{\{[Ll]ocation\b[^}]*\}\}/g, '{{location withheld}}');
if ( data != textWithoutLocationTemplate ) {
removeGPS.replaceLocationTemplate(textWithoutLocationTemplate);
} else {
removeGPS.getClaimIDForGPSData();
}
});
}
// Step 4
removeGPS.replaceLocationTemplate = function(textWithoutLocationTemplate) {
api.postWithToken('csrf', {
action: 'edit',
title: 'File:' + removeGPS.pageName,
summary: mw.msg("remove-gps-edit-summary", removeGPS.toolLink),
text: textWithoutLocationTemplate
}).then(function(data) {
removeGPS.getClaimIDForGPSData();
});
}
// Step 5
removeGPS.getClaimIDForGPSData = function() {
const entityId = mw.config.get( 'wbEntityId' );
if ( !entityId ) {
// No Wikibase entity on this page, so there's no structured data to remove.
location.reload();
return;
}
// P1259 = "coordinates of the point of view"
api.get({
action: 'wbgetclaims',
entity: entityId,
property: 'P1259'
}).then(function(data) {
if ( data.claims.P1259 != null ) {
for (const claim of data.claims.P1259) {
removeGPS.removeGPSFromStructuredData(claim.id);
}
} else {
location.reload();
}
});
}
// Step 6
removeGPS.removeGPSFromStructuredData = function(claimID) {
api.postWithToken('csrf', {
action: 'wbremoveclaims',
claim: claimID,
summary: 'via ' + removeGPS.toolLink
}).then(function(data) {
// Step 7
// Reload the page so the user can see the new version of the metadata.
location.reload();
});
}
removeGPS.replaceButtonWithText = function( notifyAdmins ) {
// Inject CSS for spinner animation
// Copied from https://commons.wikimedia.org/wiki/User:TechieNK/Cat2Data.js
const style = document.createElement("style");
style.textContent = `
.spinner-ring {
display: inline-block;
position: relative;
width: 24px;
height: 24px;
margin-left: 0.5rem;
vertical-align: middle;
}
.spinner-ring div {
transform-origin: 12px 12px;
animation: spinner-ring 1.2s linear infinite;
}
.spinner-ring div:after {
content: " ";
display: block;
position: absolute;
top: 1px;
left: 11px;
width: 2px;
height: 6px;
border-radius: 20%;
background: #1f1f1f;
}
${[...Array(12)]
.map(
(_, i) =>
`.spinner-ring div:nth-child(${i + 1}) {
transform: rotate(${i * 30}deg);
animation-delay: -${(11 - i) * 0.1}s;
}`
)
.join("\n")}
@keyframes spinner-ring {
0% { opacity: 1; }
100% { opacity: 0; }
}
.cdx-menu-item__text__description {
font-size: 0.85em;
white-space: normal;
overflow: hidden;
text-overflow: ellipsis;
}
`;
document.head.appendChild(style);
let newText = '<div class="wbmi-link-notice oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement oo-ui-flaggedElement-warning oo-ui-iconElement oo-ui-messageWidget-block oo-ui-messageWidget">' +
mw.msg('remove-gps-tasks-header') +
'<ul>' +
'<li>' + mw.msg('remove-gps-upload-task') +
'<li>' + mw.msg('remove-gps-edit-task') +
'<li>' + mw.msg('remove-gps-structured-data-task');
if ( notifyAdmins ) {
newText += '<li>' + mw.msg('remove-gps-notify-admins-task');
}
newText += '</ul>';
newText += '<p>' + mw.msg('remove-gps-page-will-reload') + '</p>';
newText += `
<div class="spinner-ring" style="width: 40px; height: 40px;">
<div></div><div></div><div></div><div></div><div></div><div></div>
<div></div><div></div><div></div><div></div><div></div><div></div>
</div>
</div>
`;
$('#gps-removal-button-wrapper').html(newText);
}
removeGPS.injectWrapperStyle = function() {
const style = document.createElement("style");
style.textContent = `
#overall-wrapper {
background-color: var( --background-color-neutral, #eaecf0 );
color: var( --color-base, #202122 );
border: 1px solid var( --border-color-subtle, #c8ccd1 );
border-radius: 5px;
padding: 1.5em;
margin: 2em 5em;
}
@media ( max-width: 640px ) {
#overall-wrapper {
margin: 1em 0.5em;
padding: 1em;
}
}
`;
document.head.appendChild(style);
}
removeGPS.displayGPSRemovalButton = function() {
removeGPS.injectWrapperStyle();
$('.mw-imagepage-section-metadata').after('<div id="overall-wrapper">' +
'<span id="gps-removal-checkbox-wrapper">' +
'<input type="checkbox" id="gps-removal-checkbox" checked /><span id="gps-removal-post-checkbox-span"></span>' +
'<label for="gps-removal-checkbox" style="white-space: normal;"> ' +
mw.msg('remove-gps-notify-admins') + ' ' + mw.msg('remove-gps-notify-admins-warning') + '</label> ' +
'</span>' +
'<span id="gps-removal-button-wrapper">' +
'<input type="button" value="' + mw.msg('remove-gps-button-label') + '" id="gps-removal-button">' +
'</span></div>');
$('#gps-removal-checkbox-wrapper')
.addClass("oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-checkboxInputWidget")
.css('padding-bottom', '20px');
$('#gps-removal-checkbox')
.addClass("oo-ui-inputWidget-input");
$('#gps-removal-post-checkbox-span')
.addClass("oo-ui-checkboxInputWidget-checkIcon oo-ui-widget oo-ui-widget-enabled oo-ui-iconElement-icon oo-ui-icon-check oo-ui-iconElement oo-ui-labelElement-invisible oo-ui-iconWidget oo-ui-image-invert");
$('#gps-removal-button-wrapper')
.addClass( "oo-ui-widget oo-ui-widget-enabled oo-ui-inputWidget oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-labelElement oo-ui-flaggedElement-progressive oo-ui-flaggedElement-primary oo-ui-buttonInputWidget");
$('#gps-removal-button')
.addClass("oo-ui-inputWidget-input oo-ui-buttonElement-button")
.css('white-space', 'normal')
.click(removeGPS.getNewVersionOfFile);
}
// Only show this button if it's a file, and if it holds GPS metadata, and the user uploaded the photo (or is
// an administrator).
if (removeGPS.namespaceName === 'File' &&
($('#mw_metadata .exif-gpslatitude').length > 0 || $('#mw_metadata .exif-gpslongitude').length > 0 || $('#mw_metadata .exif-altitude').length > 0)) {
$.ajax({
url: removeGPS.gpsRemovalToolURL + "&check_user=" + mw.config.get('wgUserId'),
method: "GET",
success: function(result) {
if ( result === '1' ) {
removeGPS.displayGPSRemovalButton();
}
}
});
}
});
}( mediaWiki, jQuery ) );