/**
* 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, $ ) {
// 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. Get the CSRF token.
// 3. Upload the new version of the file.
// 4. Get the current wikitext of the page.
// 5. Save the page, with the {{location}} call in the wikitext replaced with {{location withheld}}.
// 6. Get the claim ID(s) for the "coordinates of the point of view" value in the page's structured data.
// 7. Remove the claim for each such ID.
// 8. Reload the page.
var removeGPS = {};
removeGPS.namespaceName = mw.config.get('wgCanonicalNamespace');
removeGPS.pageName = mw.config.get('wgTitle');
removeGPS.gpsRemovalToolURL = "https://exif-gps-removal.toolforge.org/?file=" + removeGPS.pageName;
removeGPS.apiURL = 'https://commons.wikimedia.org/w/api.php';
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=' + mw.config.get('wgUserName');
const response = await $.ajax({
url: removeGPS.apiURL + '?action=query&meta=userinfo&uiprop=email&format=json',
method: 'GET'
});
const emailAddress = response.query.userinfo.email;
removeGPS.gpsRemovalToolURL += '&email=' + emailAddress;
}
removeGPS.replaceButtonWithText( notifyAdmins );
$.ajax({
url: removeGPS.gpsRemovalToolURL,
method: "GET",
xhrFields: {
responseType: 'blob'
},
success: function(result) {
removeGPS.getCSRFToken(result);
}
});
}
// Step 2
removeGPS.getCSRFToken = function(newFileContent) {
$.get( removeGPS.apiURL + '?action=query&meta=tokens&type=csrf&format=json', function(data) {
const csrfToken = data.query.tokens.csrftoken;
removeGPS.uploadNewVersionOfFile(newFileContent, csrfToken);
});
}
// Step 3
removeGPS.uploadNewVersionOfFile = function(newFileContent, csrfToken) {
const formData = new FormData();
formData.append('action', 'upload');
formData.append('filename', removeGPS.pageName);
formData.append('file', newFileContent);
formData.append('token', csrfToken);
formData.append('comment', 'Uploaded new version of image, with GPS metadata removed, via ' + removeGPS.toolLink)
formData.append('ignorewarnings', 1); // Optional: ignore some warnings like duplicate filename
$.ajax({
url: removeGPS.apiURL,
method: 'POST',
data: formData,
processData: false, // Prevents jQuery from trying to stringify the data
contentType: false, // Allows the browser to set the correct multipart/form-data header
success: function(result) {
removeGPS.getPageText(csrfToken);
}
});
}
// Step 4
removeGPS.getPageText = function(csrfToken) {
$.get('https://commons.wikimedia.org/wiki/File:' + removeGPS.pageName + '?action=raw', function(data) {
console.log(data);
const textWithoutLocationTemplate = data.replace( /{{[L|l]ocation.*}}/g, '{{location withheld}}');
if ( data != textWithoutLocationTemplate ) {
removeGPS.replaceLocationTemplate(textWithoutLocationTemplate, csrfToken);
} else {
removeGPS.getClaimIDForGPSData(csrfToken);
}
});
}
// Step 5
removeGPS.replaceLocationTemplate = function(textWithoutLocationTemplate, csrfToken) {
$.ajax({
url: removeGPS.apiURL,
method: 'POST',
data: {
action: 'edit',
title: 'File:' + removeGPS.pageName,
summary: 'Removed {{location}} template from page via ' + removeGPS.toolLink,
text: textWithoutLocationTemplate,
token: csrfToken
},
success: function(data) {
removeGPS.getClaimIDForGPSData(csrfToken);
}
});
}
// Step 6
removeGPS.getClaimIDForGPSData = function(csrfToken) {
// P1259 = "coordinates of the point of view"
$.get( removeGPS.apiURL + '?action=wbgetclaims&entity=' + mw.config.get( 'wbEntityId' ) +
'&property=P1259&format=json', function(data) {
if ( data.claims.P1259 != null ) {
for (const claim of data.claims.P1259) {
removeGPS.removeGPSFromStructuredData(claim.id, csrfToken);
}
} else {
location.reload();
}
});
}
// Step 7
removeGPS.removeGPSFromStructuredData = function(claimID, csrfToken) {
$.ajax({
url: removeGPS.apiURL,
method: 'POST',
data: {
action: 'wbremoveclaims',
claim: claimID,
summary: 'via ' + removeGPS.toolLink,
token: csrfToken
},
success: function(data) {
// Step 8
// 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">
Doing the following tasks:
<ul>
<li>Uploading new version of the file with GPS EXIF data removed
<li>Replacing {{location}} template in the page text with {{location withheld}}
<li>Removing the "coordinates of the point of view" (P1259) value from this page's structured data
`;
if ( notifyAdmins ) {
newText += '<li>Notifying Commons administrators to hide old revisions of the file and wiki page';
}
newText += `
</ul>
<p>The page will reload on completion.</p>
<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.displayGPSRemovalButton = function() {
$('.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;"> ' +
'Request Wikimedia Commons administrators to delete previous revisions of the file and wiki page that still contain the GPS data</label> ' +
'</span>' +
'<span id="gps-removal-button-wrapper">' +
'<input type="button" value="Upload a new version of this file with GPS metadata removed" id="gps-removal-button">' +
'</span></div>');
$('#overall-wrapper')
.css('background-color', '#eaecf0')
.css('border', '1px #c8ccd1 solid')
.css('border-radius', '5px')
.css('padding', '1.5em')
.css('margin', '2em 5em')
$('#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 ) );