This commit is contained in:
Gregory Koberger 2010-11-02 16:35:23 -07:00
Родитель 1546523077
Коммит 7e7fbc60bc
7 изменённых файлов: 418 добавлений и 5 удалений

Просмотреть файл

@ -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',

Двоичные данные
media/img/developers/progress.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Двоичные данные
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;
};