Version Upload Modal
This commit is contained in:
Родитель
1546523077
Коммит
7e7fbc60bc
|
@ -93,6 +93,48 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="add-file-modal upload-file modal hidden">
|
||||
<form method="post" id="upload-file" action="{{ url('devhub.upload') }}"
|
||||
enctype="multipart/form-data">
|
||||
<h3>{{ _('Upload a New File') }}</h3>
|
||||
<div class="upload-file-box">
|
||||
<p>
|
||||
{% trans %}
|
||||
Files added to reviewed versions must be reviewed before they
|
||||
will be available for download.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
|
||||
{{ csrf() }}
|
||||
|
||||
<label for="upload-file-input" class="brform">
|
||||
{{ _('Select the add-on file to upload.') }}
|
||||
</label>
|
||||
<input type="file" id="upload-file-input" name="upload" />
|
||||
<div class="upload-details">
|
||||
{{ _('Your add-on should end with .xpi or .jar.') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="upload-status">
|
||||
<strong id="upload-status-text"></strong>
|
||||
<div id="upload-status-bar">
|
||||
<div></div>
|
||||
</div>
|
||||
{# TODO(gkoberger): Add %, kb's uploaded, cancel link #}
|
||||
<div id="upload-status-results"></div>
|
||||
</div>
|
||||
|
||||
<div class="upload-status-button-add">
|
||||
<button class="button">{{ _('Add File') }}</button> or
|
||||
<a href="#" class="upload-file-cancel">{{ _('Cancel') }}</a>
|
||||
</div>
|
||||
<div class="upload-status-button-close">
|
||||
<a href="#" class="upload-file-reset button"></a> or
|
||||
<a href="#" class="upload-file-cancel"></a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
{% include "devhub/includes/addons_edit_nav.html" %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -47,5 +47,5 @@ urlpatterns = decorate(write, patterns('',
|
|||
url('^addons/activity$', views.activity,
|
||||
name='devhub.addons.activity'),
|
||||
url('^upload$', views.upload, name='devhub.upload'),
|
||||
url('^upload/([^/]+)$', views.upload_detail,
|
||||
url('^upload/([^/]+)(?:/([^/]+))?$', views.upload_detail,
|
||||
name='devhub.upload_detail')))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import codecs
|
||||
import functools
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
|
||||
|
@ -230,22 +231,23 @@ def upload(request):
|
|||
user = getattr(request, 'amo_user', None)
|
||||
fu = FileUpload.objects.create(path=loc, name=upload.name, user=user)
|
||||
tasks.validator.delay(fu.pk)
|
||||
return redirect('devhub.upload_detail', fu.pk)
|
||||
return redirect('devhub.upload_detail', fu.pk, 'json')
|
||||
|
||||
return jingo.render(request, 'devhub/upload.html')
|
||||
|
||||
|
||||
@json_view
|
||||
def json_upload_detail(upload):
|
||||
r = dict(upload=upload.uuid, validation=upload.validation,
|
||||
validation = json.loads(upload.validation) if upload.validation else ""
|
||||
r = dict(upload=upload.uuid, validation=validation,
|
||||
error=upload.task_error)
|
||||
return r
|
||||
|
||||
|
||||
def upload_detail(request, uuid):
|
||||
def upload_detail(request, uuid, format='html'):
|
||||
upload = get_object_or_404(FileUpload.uncached, uuid=uuid)
|
||||
|
||||
if request.is_ajax():
|
||||
if format == 'json' or request.is_ajax():
|
||||
return json_upload_detail(upload)
|
||||
|
||||
return jingo.render(request, 'devhub/validation.html',
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.1 KiB |
Двоичные данные
media/img/zamboni/icons/checks.png
Двоичные данные
media/img/zamboni/icons/checks.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 1.1 KiB После Ширина: | Высота: | Размер: 379 B |
|
@ -81,6 +81,247 @@ function addonFormSubmit() {
|
|||
$("#user-form-template .email-autocomplete")
|
||||
.attr("placeholder", gettext("Enter a new author's email address"));
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
//Ownership
|
||||
if ($("#author_list").length) {
|
||||
initAuthorFields();
|
||||
}
|
||||
|
||||
//Payments
|
||||
if ($('.payments').length) {
|
||||
initPayments();
|
||||
}
|
||||
|
||||
// Edit Versions
|
||||
if($('#upload-file').length) {
|
||||
initEditVersions();
|
||||
}
|
||||
|
||||
// View versions
|
||||
if($('#version-list').length) {
|
||||
initVersions();
|
||||
}
|
||||
});
|
||||
|
||||
function initVersions() {
|
||||
$('#modals').hide();
|
||||
|
||||
$('#modal-delete-version').modal('.version-delete .remove',
|
||||
{ width: 400,
|
||||
callback:function(d){
|
||||
$('.version_id', this).val($(d.click_target).attr('data-version'));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
$('#modal-cancel').modal('#cancel-review',
|
||||
{ width: 400
|
||||
});
|
||||
|
||||
|
||||
$('#modal-delete').modal('#delete-addon',
|
||||
{ width: 400,
|
||||
});
|
||||
|
||||
|
||||
$('#modal-disable').modal('#disable-addon',
|
||||
{ width: 400,
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
function initEditVersions() {
|
||||
// Hide the modal
|
||||
$('.upload-status').hide();
|
||||
|
||||
// Modal box
|
||||
$modal = $(".add-file-modal").modal(".add-file", {
|
||||
width: '450px',
|
||||
hideme: false,
|
||||
callback:resetModal
|
||||
});
|
||||
|
||||
// Reset link
|
||||
$('.upload-file-reset').click(function(e) {
|
||||
e.preventDefault();
|
||||
resetModal();
|
||||
});
|
||||
|
||||
// Cancel link
|
||||
$('.upload-file-cancel').click(function(e) {
|
||||
e.preventDefault();
|
||||
$modal.hideMe();
|
||||
});
|
||||
|
||||
// Upload form submit
|
||||
$('#upload-file').submit(function(e){
|
||||
e.preventDefault();
|
||||
|
||||
$('.upload-status-button-add, .upload-status-button-close').hide();
|
||||
|
||||
fileUpload($('#upload-file-input'), $(this).attr('action'));
|
||||
|
||||
$('.upload-file-box').hide();
|
||||
$('.upload-status').show();
|
||||
});
|
||||
|
||||
function fileUpload(img, url) {
|
||||
|
||||
var file = img[0].files[0],
|
||||
fileName = file.name,
|
||||
fileSize = file.size,
|
||||
fileData = '';
|
||||
|
||||
var boundary = "BoUnDaRyStRiNg";
|
||||
|
||||
text = format(gettext('Preparing {0}'), [fileName]);
|
||||
$('#upload-status-text').text(text);
|
||||
$('#upload-status-bar').addClass('progress-idle');
|
||||
|
||||
// Wrap in a setTimeout so it doesn't freeze the browser before
|
||||
// the status above can be set.
|
||||
|
||||
setTimeout(function(){
|
||||
fileData = file.getAsBinary();
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.upload.addEventListener("progress", function(e) {
|
||||
if (e.lengthComputable) {
|
||||
var pct = Math.round((e.loaded * 100) / e.total) + "%";
|
||||
$('#upload-status-bar div').animate({'width': pct}, 500);
|
||||
}
|
||||
}, false);
|
||||
|
||||
xhr.open("POST", url, true);
|
||||
|
||||
xhr.onreadystatechange = function(){ onupload(xhr, fileName) };
|
||||
|
||||
xhr.setRequestHeader("Content-Type", "multipart/form-data;" +
|
||||
"boundary="+boundary);
|
||||
xhr.setRequestHeader("Content-Length", fileSize);
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
|
||||
var token = $("#upload-file input[name=csrfmiddlewaretoken]").val();
|
||||
|
||||
// Things like spacing and quotes need to be exactly as follows, so
|
||||
// edit with care.
|
||||
|
||||
var body = 'Content-Type: multipart/form-data; ';
|
||||
body += format('boundary={0}\r\n', [boundary]);
|
||||
body += format('Content-Length: {0}\r\n', [fileSize]);
|
||||
|
||||
body += format("--{0}\r\n", [boundary]);
|
||||
|
||||
body += 'Content-Disposition: form-data; '
|
||||
body += 'name="csrfmiddlewaretoken"\r\n\r\n';
|
||||
body += format('{0}\r\n', [token]);
|
||||
|
||||
body += format("--{0}\r\n", [boundary]);
|
||||
|
||||
body += 'Content-Disposition: form-data; name="upload"; ';
|
||||
body += format('filename="{0}"\r\n', [fileName]);
|
||||
body += 'Content-Type: application/octet-stream\r\n\r\n';
|
||||
|
||||
body += fileData + '\r\n';
|
||||
body += format('--{0}--', [boundary]);
|
||||
|
||||
text = format(gettext('Uploading {0}'), [fileName]);
|
||||
$('#upload-status-text').text(text);
|
||||
$('#upload-status-bar').removeClass('progress-idle');
|
||||
|
||||
xhr.sendAsBinary(body);
|
||||
}, 10);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function onupload(xhr, fileName) {
|
||||
if (xhr.readyState == 4 && xhr.responseText &&
|
||||
(xhr.status == 200 || xhr.status == 304)) {
|
||||
|
||||
$('#upload-status-bar div').animate({'width': '100%'}, 500, function() {
|
||||
text = format(gettext('Validating {0}'), [fileName]);
|
||||
$('#upload-status-text').text(text);
|
||||
|
||||
$(this).parent().addClass('progress-idle');
|
||||
|
||||
json = JSON.parse(xhr.responseText);
|
||||
addonUploaded(json, fileName);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addonUploaded(json, fileName) {
|
||||
v = json.validation;
|
||||
|
||||
if(!v) {
|
||||
setTimeout(function(){
|
||||
$.getJSON( $('#upload-file').attr('action') + "/" + json.upload,
|
||||
function(d){ addonUploaded(d, fileName); })
|
||||
}, 1000);
|
||||
} else {
|
||||
$('#upload-status-bar').removeClass('progress-idle')
|
||||
.addClass(v.errors ? 'bar-fail' : 'bar-success');
|
||||
$('#upload-status-bar div').fadeOut();
|
||||
|
||||
text = format(gettext('Validated {0}'), [fileName]);
|
||||
$('#upload-status-text').text(text);
|
||||
|
||||
// TODO(gkoberger): Use templates here, rather than +'s
|
||||
|
||||
body = "<strong>";
|
||||
if(!v.errors) {
|
||||
body += format(ngettext(
|
||||
"Your add-on passed validation with no errors and {0} warning.",
|
||||
"Your add-on passed validation with no errors and {0} warnings.",
|
||||
v.warnings), [v.warnings]);
|
||||
} else {
|
||||
body += format(ngettext(
|
||||
"Your add-on failed validation with {0} error.",
|
||||
"Your add-on failed validation with {0} errors.",
|
||||
v.errors), [v.errors]);
|
||||
}
|
||||
body += "</strong>";
|
||||
|
||||
body += "<ul>";
|
||||
if(v.errors) {
|
||||
$.each(v.messages, function(k, t) {
|
||||
if(t.type == "error") {
|
||||
body += "<li>" + t.message + "</li>";
|
||||
}
|
||||
});
|
||||
}
|
||||
body += "</ul>";
|
||||
|
||||
// TODO(gkoberger): Add a link when it becomes available
|
||||
|
||||
body += "<a href='#'>";
|
||||
body += gettext('See full validation report');
|
||||
body += '</a>';
|
||||
|
||||
statusclass = v.errors ? 'status-fail' : 'status-pass';
|
||||
$('#upload-status-results').html(body).addClass(statusclass);
|
||||
|
||||
$('.upload-status-button-add').hide();
|
||||
$('.upload-status-button-close').show();
|
||||
|
||||
inputDiv = $('.upload-status-button-close');
|
||||
|
||||
if(v.errors) {
|
||||
text_reset = gettext('Try Again');
|
||||
text_cancel = gettext('Cancel');
|
||||
} else {
|
||||
text_reset = gettext('Upload Another');
|
||||
text_cancel = gettext('Finish Uploading');
|
||||
}
|
||||
|
||||
$('.upload-file-reset', inputDiv).text(text_reset);
|
||||
$('.upload-file-cancel', inputDiv).text(text_cancel);
|
||||
}
|
||||
}
|
||||
|
||||
function initPayments() {
|
||||
var previews = [
|
||||
|
@ -254,3 +495,24 @@ function initAuthorFields() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resetModal(obj) {
|
||||
|
||||
upload = $("<input type='file'>").attr('name', 'upload')
|
||||
.attr('id', 'upload-file-input');
|
||||
|
||||
$('.upload-file-box').show();
|
||||
$('.upload-status').hide();
|
||||
|
||||
$('.upload-status-button-add').show();
|
||||
$('.upload-status-button-close').hide();
|
||||
|
||||
$('#upload-status-bar').attr('class', '');
|
||||
$('#upload-status-text').text("");
|
||||
$('#upload-file-input').replaceWith(upload); // Clear file input
|
||||
$('#upload-status-results').text("").attr("class", "");
|
||||
$('#upload-status-bar div').css('width', 0).show();
|
||||
$('#upload-status-bar').removeClass('progress-idle');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -194,3 +194,110 @@ $.fn.popup = function(click_target, o) {
|
|||
|
||||
return $popup;
|
||||
};
|
||||
|
||||
// makes an element into a modal.
|
||||
// click_target defines the element/elements that trigger the popup.
|
||||
// currently presumes the given element uses the '.modal' style
|
||||
// o takes the following optional fields:
|
||||
// callback: a function to run before displaying the popup. Returning
|
||||
// false from the function cancels the popup.
|
||||
// container: if set the popup will be appended to the container before
|
||||
// being displayed.
|
||||
// width: the width of the popup.
|
||||
// delegate: delegates the click handling of the click_target to the
|
||||
// specified parent element.
|
||||
// hideme: defaults to true, if set to false, popup will not be hidden
|
||||
// when the user clicks outside of it.
|
||||
// note: all options may be overridden and modified by returning them in an
|
||||
// object from the callback.
|
||||
$.fn.modal = function(click_target, o) {
|
||||
o = o || {};
|
||||
|
||||
var $ct = $(click_target),
|
||||
$modal = this;
|
||||
|
||||
$modal.o = $.extend({
|
||||
delegate: false,
|
||||
callback: false,
|
||||
onresize: function(){$modal.setPos();},
|
||||
hideme: true,
|
||||
offset: {},
|
||||
width: 300
|
||||
}, o);
|
||||
|
||||
$modal.setWidth = function(w) {
|
||||
$modal.css({width: w});
|
||||
return $modal;
|
||||
}
|
||||
|
||||
$modal.setPos = function(offset) {
|
||||
offset = offset || $modal.o.offset;
|
||||
|
||||
$modal.detach().appendTo("body");
|
||||
var toX = ($(window).width() - $modal.outerWidth()) / 2,
|
||||
toY = ($(window).height() - $modal.outerHeight()) / 2;
|
||||
$modal.css({
|
||||
'left': toX,
|
||||
'top': toY,
|
||||
'right': 'inherit',
|
||||
'bottom': 'inherit',
|
||||
'position': 'fixed'
|
||||
});
|
||||
return $modal;
|
||||
};
|
||||
|
||||
$modal.hideMe = function() {
|
||||
$modal.hide();
|
||||
$modal.unbind();
|
||||
$modal.undelegate();
|
||||
$(document.body).unbind('click newmodal', $modal.hider);
|
||||
$(window).bind('resize', $modal.o.onresize);
|
||||
return $modal;
|
||||
};
|
||||
|
||||
function handler(e) {
|
||||
e.preventDefault();
|
||||
var resp = o.callback ? (o.callback.call($modal, {
|
||||
click_target: this,
|
||||
evt: e
|
||||
})) : true;
|
||||
$modal.o = $.extend({click_target: this}, $modal.o, resp);
|
||||
if (resp) {
|
||||
$modal.render();
|
||||
}
|
||||
}
|
||||
|
||||
$modal.render = function() {
|
||||
var p = $modal.o;
|
||||
$modal.hider = makeBlurHideCallback($modal);
|
||||
if (p.hideme) {
|
||||
setTimeout(function(){
|
||||
$(document.body).bind('click modal', $modal.hider);
|
||||
}, 0);
|
||||
}
|
||||
$modal.delegate('.close', 'click', function(e){
|
||||
e.preventDefault();
|
||||
$modal.hideMe();
|
||||
});
|
||||
$ct.trigger("modal_show", [$modal]);
|
||||
if (p.container && p.container.length)
|
||||
$modal.detach().appendTo(p.container);
|
||||
$modal.setPos();
|
||||
setTimeout(function(){
|
||||
$modal.show();
|
||||
}, 0);
|
||||
|
||||
$(window).bind('resize', p.onresize);
|
||||
return $modal;
|
||||
};
|
||||
|
||||
if ($modal.o.delegate) {
|
||||
$($modal.o.delegate).delegate(click_target, "click", handler);
|
||||
} else {
|
||||
$ct.click(handler);
|
||||
}
|
||||
|
||||
$modal.setWidth($modal.o.width);
|
||||
|
||||
return $modal;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче