MediaWiki:EnhancedStash.js
Jump to navigation
Jump to search
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 MediaWiki:EnhancedStash. |
/**
* Enhances [[Special:UploadStash]] adding a button that
* when clicked will add thumbnails and imageinfo. On top of that,
* adds buttons to publish completed uploads.
*
* @author Rainer Rillke
* <nowiki>
*/
/*
* Copyright (C) 2013 Rainer Rillke and others
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*global mediaWiki:false, jQuery: false*/
(function($, mw) {
'use strict';
if (mw.config.get('wgCanonicalSpecialPageName') !== 'UploadStash') return;
var us,
stashMaxServeSize = 1048576,
id = 'commons-upload-stash-enhance-button',
$ul = $('#mw-content-text ul').first(),
rtl = $(document.body).hasClass('rtl'),
right = rtl ? 'left' : 'right',
left = rtl ? 'right' : 'left',
e = rtl ? 'w' : 'e';
us = mw.libs.enhancedStash = {
fileKeyMap: {},
fileKeys: [],
queue: null,
$exs: $(),
init: function() {
this.queue = new mw.libs.JStack(this, null, {
logPrefix: 'stash',
app: 'MediaWiki:EnhancedStash.js',
reportPage: 'MediaWiki talk:EnhancedStash.js',
stuckTimeout: 2 * 61 * 1000
});
},
loadAndContinue: function(deps, cont, args) {
var us = this;
args = Array.prototype.slice.call(args, 0);
args.unshift(cont);
mw.loader.using(deps, function() {
us.queue.secureCall.apply(us.queue, args);
});
},
blockPage: function() {
this.loadAndContinue(['ext.gadget.jquery.blockUI'], '__blockPage', arguments);
},
__blockPage: function() {
$ul.parent().block({
message: '<img src="//upload.wikimedia.org/wikipedia/commons/1/10/Loading-special.gif" height="15" width="128"/>',
css: {
border: 'none',
background: 'none'
}
});
us.queue.nextTask();
},
unblockPage: function() {
$ul.parent().unblock();
us.queue.nextTask();
},
parse: function() {
$('#mw-content-text').find('li').each(function(i, el) {
var $el = $(el),
key = $el.find('a').first().text();
us.fileKeys.push(key);
us.fileKeyMap[key] = {
$el: $el
};
});
if (!us.fileKeys.length) {
return us.queue.emptyQueue();
}
us.queue.nextTask();
},
query: function() {
this.loadAndContinue(['ext.gadget.libAPI'], '__query', arguments);
},
__query: function() {
var isDone = false;
setTimeout(function() {
if (!isDone) {
us.queue.nextTask();
}
isDone = true;
}, 1 * 60 * 1000);
mw.libs.commons.api.$autoQuery({
action: 'query',
prop: 'stashimageinfo',
siifilekey: us.fileKeys.join('|'),
siiprop: 'timestamp|url|size|dimensions|sha1|mime|thumbmime|metadata|extmetadata|bitdepth',
siiurlwidth: 120,
siiurlheight: 120
})
.progress(function(result, params) {
$.each(result.query.stashimageinfo, function(i, sii) {
var key = sii.url.match(/Special\:UploadStash\/file\/(.+)$/)[1];
us.fileKeyMap[key].sii = sii;
});
})
.done(function(fullResult) {
if (!isDone) {
us.queue.nextTask();
}
isDone = true;
});
},
getPublishButton: function(key, $img) {
return $('<button>')
.text('Publish')
.button({
icons: {
primary: 'ui-icon-circle-arrow-n'
}
})
.addClass('ui-button-blue')
.click(function() {
us.describe(key, $img);
});
},
render: function() {
var $detailsCont = $('<div>')
.css({
'float': right,
'width': '49%',
'box-shadow': '3px 3px 5px 6px #ccc',
'visibility': 'hidden',
'background': 'white',
'border': '1px solid #EEE'
})
.insertBefore($ul).first(),
$details = $('<div>')
.css({
overflow: 'auto'
})
.appendTo($detailsCont),
$detailsClose = $('<button type="button"></button>')
.text('close')
.button({
icons: {
primary: 'ui-icon-circle-close'
},
text: false
})
.css({
'position': 'absolute',
'top': '.1em'
})
.attr('title', 'close')
.prependTo($detailsCont)
.click(function() {
$detailsCont.css('visibility', 'hidden');
us.$exs.button('option', 'disabled', false);
});
$detailsClose.css(right, '1.8em');
$.each(us.fileKeyMap, function(key, m) {
m.$el.css({
'list-style': 'none',
'display': 'block',
'height': '120px'
});
// Do not process broken uploads
if ( !m.sii ) {
if ( m.$el ) {
us.getPublishButton(key, $('<img>')).appendTo( m.$el );
}
return;
}
var ___imgLoadError = function () {
if (m.sii.size < stashMaxServeSize) {
$(this).attr('src', m.sii.url);
}
};
var $info = $('<div>')
.css({
'width': '49%',
'background': '#ECF4FF',
'min-height': '115px'
}),
$datetime = $('<div>')
.css({
color: '#808080',
'font-size': '.9em'
})
// TODO use a formatter!
.text(m.sii.timestamp.replace('T', ' ').replace('Z', ''))
.appendTo($info),
$hwz = $('<div>')
.css({
color: '#008000',
'font-size': '.9em'
})
// TODO use a formatter!
.text(Math.round(m.sii.size / (1024 * 1024)) + ' MiB ' + m.sii.width + ' × ' + m.sii.height + 'px')
.appendTo($info),
$imgCont = $('<div>')
.css('margin-' + right, '1em')
.css({
height: 120,
width: 120,
'float': left
}).prependTo($info),
$img = $('<img>')
.error(___imgLoadError)
.attr({
src: m.sii.thumburl,
title: key
})
.css({
width: m.sii.thumbwidth,
height: m.sii.thumbheight
})
.appendTo($imgCont),
$ex = $('<button>')
.css({
'height': '100px',
'width': '30px',
'float': right
})
.button({
icons: {
primary: 'ui-icon-circle-triangle-' + e
},
text: false
}).prependTo($info);
us.getPublishButton(key, $img).appendTo($info);
var t = $ul.offset().top;
us.$exs = us.$exs.add($ex);
$detailsCont.css({
'position': 'fixed',
'top': t,
'width': $detailsCont.width(),
'font-family': 'Courier,monospace',
'white-space': 'pre-wrap'
});
$detailsCont.css(right, '5px');
$details.css({
'max-height': $(window).height() - t - 45
});
$ex.click(function() {
us.$exs.button('option', 'disabled', false);
$ex.button('option', 'disabled', true);
$details.text(JSON.stringify(m.sii, null, ' '));
$detailsCont.css('visibility', 'visible');
});
$info.prepend(m.$el.find('a').first());
m.$el.empty().append($info);
});
us.queue.nextTask();
},
run: function() {
$('#' + id).hide().off();
us.queue.addTask('blockPage');
us.queue.addTask('parse');
us.queue.addTask('query');
us.queue.addTask('render');
us.queue.addTask('unblockPage');
us.queue.nextTask();
},
describe: function() {
us.loadAndContinue(['jquery.ui', 'mediawiki.user'], '__describe', arguments);
},
__describe: function(filekey, $img) {
var d = new Date(),
y = d.getFullYear(),
m = (d.getMonth()+1).toString(),
day = d.getDate();
if (m.length < 2) m = "0"+m;
if (day.length < 2) day = "0"+day;
var $dlg = $('<div>')
.prepend($img.clone().css('float', right)),
$token = $('<input>')
.val("Edit token")
.attr('title', 'edit token')
.attr('readonly', 'readonly')
.css({
'max-width': '25em',
'width': '50%'
})
.appendTo($dlg),
$sW = $('<div>').css('clear', 'both').appendTo($dlg),
$sL = $('<label for="commons-enh-stash-sum"></label>')
.text("Upload summary")
.appendTo($sW),
$s = $('<input>')
.attr('placeholder', "Upload summary")
.attr('id', 'commons-enh-stash-sum')
.css({
width: '98%'
})
.appendTo($sW),
$tW = $('<div>').css('clear', 'both').appendTo($dlg),
$tL = $('<label for="commons-enh-stash-title"></label>')
.text("Target filename (existing files under that name will be overwritten)")
.appendTo($tW),
$t = $('<input>')
.val(filekey)
.attr('id', 'commons-enh-stash-title')
.css({
width: '98%'
})
.appendTo($tW),
$cW = $('<div>').css('clear', 'both').appendTo($dlg),
$cL = $('<label for="commons-enh-stash-desc"></label>')
.text("File description")
.appendTo($cW),
$c = $('<textarea>')
.attr('id', 'commons-enh-stash-desc')
.css({
width: '98%',
height: '20em',
'font-family': 'Courier,monospace'
})
.val("== {{int:filedesc}} ==\n{{Information\n |Description=\n" +
"{{en|1=}}\n{{" + mw.user.options.get('language') + "|1=}}\n |Source={{own}}\n |Date=" + d.getFullYear() + "-" + m + "-" + day +
"\n |Author=[[User:" + mw.user.getName() + "|" + mw.user.getName() + "]]\n |Permission=\n |other_versions=\n}}\n\n== {{int:license-header}} ==\n{{subst:nld}}")
.appendTo($cW),
$pb = $('<button type="button"></button>')
.text('publish')
.button({
icons: {
primary: 'ui-icon-circle-check'
}
})
.addClass('ui-button-large ui-button-green')
.css({
'text-transform': 'uppercase',
'font-weight': 'bold'
})
.click(function() {
$(this).button('option', 'disabled', true);
var t = $.trim($t.val()),
s = $.trim($s.val()),
c = $.trim($c.val());
if (!t) return;
us.publish(filekey, t, s, c, $dlg, $pb);
})
.appendTo($dlg);
var iv = setInterval(function() {
us.refreshToken($token);
}, 2*60*1000);
us.refreshToken($token);
$dlg.dialog({
title: 'Publish ' + filekey,
modal: true,
width: Math.min($(window).width(), 650),
close: function() {
clearInterval(iv);
$(this).remove();
}
});
},
refreshToken: function() {
us.loadAndContinue(['ext.gadget.libAPI'], '__refreshToken', arguments);
},
__refreshToken: function($target) {
mw.libs.commons.api.$query({
'action': 'query',
'meta': 'tokens',
'type': 'csrf'
}).done(function(r) {
var t = r.query.tokens.csrftoken;
$target.val(r.query.tokens.csrftoken === '+\\' ? 'WARNING: SESSION EXPIRED! LogIn again!' : t);
}).fail(function(x) {
$target.val(x);
});
},
publish: function() {
us.loadAndContinue(['ext.gadget.libAPI', 'ext.gadget.jquery.blockUI'], '__publish', arguments);
},
__publish: function(filekey, target, summary, text, $dlg, $btn) {
var ext = filekey.match(/\.(\w{2,9})$/),
extW = target.match(/\.(\w{2,9})$/);
if (!ext && !extW) return;
if (!extW) target += '.' + ext[1];
$dlg.block();
mw.libs.commons.api.$query({
'action': 'query',
'meta': 'tokens',
'type': 'csrf'
}).done(function(r) {
var t = r.query.tokens.csrftoken;
target = $.ucFirst(target.replace(/File:/, '').replace(/_/g, ' '));
$.ajax({
url: mw.util.wikiScript('api'),
dataType: 'json',
data: {
'format': 'json',
'action': 'upload',
'filename': target,
'comment': (summary || 'Publishing upload from [[Special:UploadStash]]') + ' using [[MediaWiki:EnhancedStash.js]]',
'text': text,
'filekey': filekey,
'ignorewarnings': 1,
'async': 1,
'leavemessage': 1,
'tags': 'EnhancedStash',
'token': t
},
type: 'POST'
// TODO: Implement in libAPI
}).done(function(r) {
$dlg.unblock();
$btn.button('option', 'disabled', false);
if (!r || !r.upload) {
throw new Error('Empty or wrong API response. ' + JSON.stringify(r));
}
if (r.upload.result === "Success") {
// TODO: ...
mw.notify($('<div>').text('Uploaded ' + filekey + ' successfully to ').append( $('<a>', {
// TODO: ...
href: mw.util.getUrl('File:' + target),
target: '_blank'
}).text(target) ));
$dlg.dialog('close');
us.fileKeyMap[filekey].$el.hide();
}
if (r.upload.result === "Poll") {
// TODO: ...
mw.notify($('<div>').text('THIS MAY TAKE A WHILE. CHECK BACK TOMORROW: Uploaded ' + filekey + ' pending to ').append( $('<a>', {
// TODO: ...
href: mw.util.getUrl('File:' + target),
target: '_blank'
}).text(target) ));
$dlg.dialog('close');
us.fileKeyMap[filekey].$el.hide();
}
}).fail(function(x) {
$btn.button('option', 'disabled', false);
$dlg.unblock();
throw new Error(x);
});
}).fail(function(x) {
$btn.button('option', 'disabled', false);
$dlg.unblock();
throw new Error(x);
});
},
install: function() {
var $button = $('<button type="button" id="' + id + '"></button>')
.text('Further Information')
.attr('title', 'Displays thumbnails and other information for stashed files and allows you to publish completed uploads.')
.button({
icons: { primary: 'ui-icon-image' }
})
.css('float', rtl ? 'left' : 'right')
.click(function() {
us.run();
});
if ($('#' + id).length) return;
$button.appendTo('#contentSub');
}
};
mw.loader.using([
'ext.gadget.JStack',
'mediawiki.util',
'jquery.ui'
], function() {
us.init();
us.install();
if (mw.util.getParamValue('withJS') === 'MediaWiki:EnhancedStash.js') {
us.run();
}
});
// Prefetch
mw.loader.load(['ext.gadget.jquery.blockUI', 'ext.gadget.libAPI', 'jquery.ui', 'mediawiki.user']);
}(jQuery, mediaWiki));
// </nowiki>