Bug 614337 - Screen Shot Uploader

This commit is contained in:
Gregory Koberger 2010-12-30 20:02:31 -05:00
Родитель 1953e89cb5
Коммит d6dda89846
15 изменённых файлов: 518 добавлений и 76 удалений

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

@ -180,7 +180,7 @@ class AddonFormMedia(AddonFormBase):
fields = ('icon_upload', 'icon_type')
def save(self, addon, commit=True):
if self.request.FILES:
if 'icon_upload' in self.request.FILES:
icon = self.request.FILES['icon_upload']
icon.seek(0)
dirname = addon.get_icon_dir()

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

@ -1129,15 +1129,16 @@ class Preview(amo.models.ModelBase):
self.image_url, ]
return urls
def _image_url(self, thumb=True):
def _image_url(self, url_template):
if self.modified is not None:
modified = int(time.mktime(self.modified.timetuple()))
else:
modified = 0
url_template = (thumb and settings.PREVIEW_THUMBNAIL_URL or
settings.PREVIEW_FULL_URL)
return url_template % (self.id / 1000, self.id, modified)
def _image_path(self, url_template):
return url_template % (self.id / 1000, self.id)
def as_dict(self, src=None):
d = {'full': urlparams(self.image_url, src=src),
'thumbnail': urlparams(self.thumbnail_url, src=src),
@ -1146,11 +1147,19 @@ class Preview(amo.models.ModelBase):
@property
def thumbnail_url(self):
return self._image_url(thumb=True)
return self._image_url(settings.PREVIEW_THUMBNAIL_URL)
@property
def image_url(self):
return self._image_url(thumb=False)
return self._image_url(settings.PREVIEW_FULL_URL)
@property
def thumbnail_path(self):
return self._image_path(settings.PREVIEW_THUMBNAIL_PATH)
@property
def image_path(self):
return self._image_path(settings.PREVIEW_FULL_PATH)
class AppSupport(amo.models.ModelBase):

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

@ -206,6 +206,9 @@ MAX_CATEGORIES = 2
# Icon upload sizes
ADDON_ICON_SIZES = [32, 48, 64]
# Preview upload sizes [thumb, full]
ADDON_PREVIEW_SIZES = [(200, 150), (700, 525)]
# These types don't maintain app compatibility in the db. Instead, we look at
# APP.types and APP_TYPE_SUPPORT to figure out where they are compatible.
NO_COMPAT = (ADDON_SEARCH, ADDON_PERSONA)

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

@ -291,6 +291,11 @@ def resize_image(src, dst, size, remove_src=True):
"""Resizes and image from src, to dst."""
if src == dst:
raise Exception("src and dst can't be the same: %s" % src)
dirname = os.path.dirname(dst)
if not os.path.exists(dirname):
os.makedirs(dirname)
im = Image.open(src)
im = im.convert('RGBA')
im = processors.scale_and_crop(im, size)

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

@ -1,3 +1,4 @@
import path
import socket
from django import forms
@ -12,7 +13,7 @@ from tower import ugettext as _, ugettext_lazy as _lazy
import amo
import addons.forms
import paypal
from addons.models import Addon, AddonUser, Charity
from addons.models import Addon, AddonUser, Charity, Preview
from amo.forms import AMOModelForm
from applications.models import Application, AppVersion
from files.models import File, FileUpload, Platform
@ -22,6 +23,7 @@ from translations.fields import TransTextarea, TransField
from translations.models import delete_translation
from translations.forms import TranslationFormMixin
from versions.models import License, Version, ApplicationsVersions
from . import tasks
class AuthorForm(happyforms.ModelForm):
@ -454,3 +456,39 @@ class ReviewTypeForm(forms.Form):
class Step3Form(addons.forms.AddonFormBasic):
description = TransField(widget=TransTextarea, required=False)
class PreviewForm(happyforms.ModelForm):
caption = TransField(widget=TransTextarea, required=False)
file_upload = forms.FileField(required=False)
upload_hash = forms.CharField(required=False)
def save(self, addon, commit=True):
if self.cleaned_data:
self.instance.addon = addon
super(PreviewForm, self).save(commit=commit)
if self.cleaned_data['upload_hash']:
upload_hash = self.cleaned_data['upload_hash']
settings_path = settings.PREVIEWS_PATH
upload_path = path.path(settings_path) / 'temp' / upload_hash
tasks.resize_preview.delay(str(upload_path),
self.instance.thumbnail_path,
self.instance.image_path)
class Meta:
model = Preview
fields = ('caption', 'file_upload', 'upload_hash', 'id')
class BasePreviewFormSet(BaseModelFormSet):
def clean(self):
if any(self.errors):
return
PreviewFormSet = modelformset_factory(Preview, formset=BasePreviewFormSet,
form=PreviewForm, can_delete=True,
extra=1)

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

@ -1,4 +1,3 @@
import json
import logging
import os
import sys
@ -8,6 +7,7 @@ from django.conf import settings
from django.core.management import call_command
from celeryutils import task
import amo
from amo.decorators import write
from amo.utils import resize_image
from files.models import FileUpload, File, FileValidation
@ -81,3 +81,21 @@ def resize_icon(src, dst, size, **kw):
except Exception, e:
log.error("Error saving addon icon: %s" % e)
@task(queue='images')
def resize_preview(src, thumb_dst, full_dst, **kw):
"""Resizes preview images."""
log.info('[1@None] Resizing preview: %s' % thumb_dst)
try:
# Generate the thumb.
size = amo.ADDON_PREVIEW_SIZES[0]
resize_image(src, thumb_dst, size, remove_src=False)
# Resize the original.
size = amo.ADDON_PREVIEW_SIZES[1]
resize_image(src, full_dst, size, remove_src=True)
except Exception, e:
log.error("Error saving preview: %s" % e)

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

@ -5,6 +5,7 @@
{% block title %}{{ dev_page_title(_('Step 4'), addon) }}{% endblock %}
{% block primary %}
<section class="addon-submission-process" id="submit-media" role="main">
<h3>{{ _('Step 4. Add Images') }}</h3>
<p>
{% trans %}
@ -54,6 +55,35 @@
<li id="edit-icon-error"></li>
</ul>
</div>
{% if preview_form %}
{{ preview_form.management_form|safe }}
{{ preview_form.non_form_errors()|safe }}
<div id="file-list">
{% for form in preview_form.forms %}
<div class="preview">
<div class="preview-thumb">
{% if form.instance.id %}
<img src="{{ form.instance.thumbnail_url }}">
{% endif %}
</div>
{{ form.id|safe }}
<label>{{ _('Please provide a caption for this screen shot:') }}</label>
{{ form.caption|safe }}
<div class="preview_extra">
{{ form.upload_hash|safe }}
</div>
{{ form.errors|safe }}
</div>
{% endfor %}
</div>
<div class="invisible-upload">
<a class="button" href="#">{{ _('Add a screenshot...') }}</a>
<input type="file" id="screenshot_upload" name="uploads" multiple
data-upload-url="{{ url('devhub.addons.upload_preview', addon.slug) }}">
</div>
{% endif %}
<div class="submission-buttons addon-submission-field">
<button type="submit">
{{ _('Continue') }}

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

@ -70,6 +70,48 @@
{% endif %}
</td>
</tr>
<tr>
<th>
<label>{{ _("Screenshots and videos") }}</label>
</th>
<td class="edit-previews-readonly">
{% if editable %}
{% if preview_form: %}
{{ preview_form.management_form|safe }}
{{ preview_form.non_form_errors()|safe }}
<div id="file-list">
{% for form in preview_form.forms %}
<div class="preview">
<div class="preview-thumb">
{% if form.instance.id %}
<img src="{{ form.instance.thumbnail_url }}">
{% endif %}
</div>
{{ form.id|safe }}
<label>{{ _('Please provide a caption for this screen shot:') }}</label>
{{ form.caption|safe }}
<div class="preview_extra">
{{ form.upload_hash|safe }}
</div>
{{ form.errors|safe }}
</div>
{% endfor %}
</div>
<div class="invisible-upload">
<a class="button" href="#">{{ _('Add a screenshot...') }}</a>
<input type="file" id="screenshot_upload" name="uploads" multiple
data-upload-url="{{ url('devhub.addons.upload_preview', addon.slug) }}">
</div>
{% endif %}
{% else %}
{% for preview in addon.previews.all() %}
<div class="preview-thumb">
<img src="{{ preview.thumbnail_url }}">
</div>
{% endfor %}
{% endif %}
</td>
</tr>
</tbody>
</table>
</div>

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

@ -1,4 +1,5 @@
import os
import path
import shutil
import tempfile
@ -64,7 +65,7 @@ def _uploader(resize_size, final_size):
if isinstance(final_size, list):
for rsize, fsize in zip(resize_size, final_size):
dest_name = '1234'
dest_name = str(path.path(settings.ADDON_ICONS_PATH) / '1234')
resize_icon(src.name, dest_name, resize_size)
dest_image = Image.open("%s-%s.png" % (dest_name, rsize))

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

@ -6,7 +6,6 @@ import shutil
import socket
import tempfile
from decimal import Decimal
import shutil
from django import forms
from django.conf import settings
@ -71,7 +70,7 @@ class HubTest(test_utils.TestCase):
def clone_addon(self, num, addon_id=57132):
ids = []
for i in xrange(num):
for i in range(num):
addon = Addon.objects.get(id=addon_id)
addon.id = addon.guid = None
addon.save()
@ -271,8 +270,9 @@ def formset(*args, **kw):
prefix and initial_count can be set in **kw.
"""
prefix = kw.pop('prefix', 'form')
total_count = kw.pop('total_count', len(args))
initial_count = kw.pop('initial_count', len(args))
data = {prefix + '-TOTAL_FORMS': len(args),
data = {prefix + '-TOTAL_FORMS': total_count,
prefix + '-INITIAL_FORMS': initial_count}
for idx, d in enumerate(args):
data.update(('%s-%s-%s' % (prefix, idx, k), v)
@ -949,6 +949,20 @@ class TestEdit(test_utils.TestCase):
self.addon = self.get_addon()
def formset_new_form(self, *args, **kw):
ctx = self.client.get(self.get_url('media', True)).context
blank = initial(ctx['preview_form'].forms[-1])
blank.update(**kw)
return blank
def formset_media(self, *args, **kw):
kw.setdefault('initial_count', 0)
kw.setdefault('prefix', 'files')
fs = formset(*[a for a in args] + [self.formset_new_form()], **kw)
return dict([(k, '' if v is None else v) for k, v in fs.items()])
def tearDown(self):
reset_redis(self._redis)
@ -1396,8 +1410,9 @@ class TestEdit(test_utils.TestCase):
def test_edit_media_defaulticon(self):
data = dict(icon_type='')
data_formset = self.formset_media(**data)
r = self.client.post(self.get_url('media', True), data)
r = self.client.post(self.get_url('media', True), data_formset)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
@ -1408,8 +1423,9 @@ class TestEdit(test_utils.TestCase):
def test_edit_media_preuploadedicon(self):
data = dict(icon_type='icon/appearance')
data_formset = self.formset_media(**data)
r = self.client.post(self.get_url('media', True), data)
r = self.client.post(self.get_url('media', True), data_formset)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
@ -1424,8 +1440,9 @@ class TestEdit(test_utils.TestCase):
data = dict(icon_type='image/png',
icon_upload=src_image)
data_formset = self.formset_media(**data)
r = self.client.post(self.get_url('media', True), data)
r = self.client.post(self.get_url('media', True), data_formset)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
@ -1454,8 +1471,9 @@ class TestEdit(test_utils.TestCase):
data = dict(icon_type='image/png',
icon_upload=src_image)
data_formset = self.formset_media(**data)
r = self.client.post(self.get_url('media', True), data)
r = self.client.post(self.get_url('media', True), data_formset)
eq_(r.context['form'].errors, {})
addon = self.get_addon()
@ -1478,8 +1496,9 @@ class TestEdit(test_utils.TestCase):
data = dict(icon_type='image/png',
icon_upload=src_image)
data_formset = self.formset_media(**data)
r = self.client.post(self.get_url('media', True), data)
r = self.client.post(self.get_url('media', True), data_formset)
error = 'Icons must be either PNG or JPG.'
self.assertFormError(r, 'form', 'icon_upload', error)
@ -1506,10 +1525,65 @@ class TestEdit(test_utils.TestCase):
def test_icon_animated(self):
filehandle = open(get_image_path('animated.png'), 'rb')
data = {'icon_type': 'image/png', 'icon_upload': filehandle}
res = self.client.post(self.get_url('media', True), data)
data_formset = self.formset_media(**data)
res = self.client.post(self.get_url('media', True), data_formset)
eq_(res.context['form'].errors['icon_upload'][0],
u'Icons cannot be animated.')
def preview_add(self, amount=1):
img = "%s/img/amo2009/tab-mozilla.png" % settings.MEDIA_ROOT
src_image = open(img, 'rb')
data = dict(upload_preview=src_image)
data_formset = self.formset_media(**data)
url = reverse('devhub.addons.upload_preview', args=['a3615'])
r = self.client.post(url, data_formset)
details = json.loads(r.content)
upload_hash = details['upload_hash']
# Create and post with the formset.
fields = []
for i in range(amount):
fields.append(self.formset_new_form(caption='hi',
upload_hash=upload_hash))
data_formset = self.formset_media(*fields)
self.get_url('media', True)
r = self.client.post(self.get_url('media', True), data_formset)
def test_edit_media_preview_add(self):
self.preview_add()
eq_(str(self.get_addon().previews.all()[0].caption), 'hi')
def test_edit_media_preview_edit(self):
self.preview_add()
preview = self.get_addon().previews.all()[0]
edited = {'caption': 'bye',
'upload_hash': '',
'id': preview.id,
'file_upload': None}
data_formset = self.formset_media(edited, initial_count=1)
self.client.post(self.get_url('media', True), data_formset)
eq_(str(self.get_addon().previews.all()[0].caption), 'bye')
eq_(len(self.get_addon().previews.all()), 1)
def test_edit_media_preview_add_another(self):
self.preview_add()
self.preview_add()
eq_(len(self.get_addon().previews.all()), 2)
def test_edit_media_preview_add_two(self):
self.preview_add(2)
eq_(len(self.get_addon().previews.all()), 2)
def test_log(self):
data = {'developer_comments': 'This is a test'}
o = ActivityLog.objects
@ -2566,13 +2640,30 @@ class TestSubmitStep4(TestSubmitBase):
def test_post(self):
data = dict(icon_type='')
r = self.client.post(self.url, data)
data_formset = self.formset_media(**data)
r = self.client.post(self.url, data_formset)
eq_(r.status_code, 302)
eq_(self.get_step().step, 5)
def formset_new_form(self, *args, **kw):
ctx = self.client.get(self.url).context
blank = initial(ctx['preview_form'].forms[-1])
blank.update(**kw)
return blank
def formset_media(self, *args, **kw):
kw.setdefault('initial_count', 0)
kw.setdefault('prefix', 'files')
fs = formset(*[a for a in args] + [self.formset_new_form()], **kw)
return dict([(k, '' if v is None else v) for k, v in fs.items()])
def test_edit_media_defaulticon(self):
data = dict(icon_type='')
self.client.post(self.url, data)
data_formset = self.formset_media(**data)
self.client.post(self.url, data_formset)
addon = self.get_addon()
@ -2584,7 +2675,8 @@ class TestSubmitStep4(TestSubmitBase):
def test_edit_media_preuploadedicon(self):
data = dict(icon_type='icon/appearance')
self.client.post(self.url, data)
data_formset = self.formset_media(**data)
self.client.post(self.url, data_formset)
addon = self.get_addon()
@ -2600,10 +2692,10 @@ class TestSubmitStep4(TestSubmitBase):
data = dict(icon_type='image/png',
icon_upload=src_image)
data_formset = self.formset_media(**data)
self.client.post(self.url, data_formset)
self.client.post(self.url, data)
addon = self.get_addon()
eq_('/'.join(addon.get_icon_url(64).split('/')[-3:-1]),
'addon_icon/%s' % addon.id)
@ -2624,8 +2716,9 @@ class TestSubmitStep4(TestSubmitBase):
data = dict(icon_type='image/png',
icon_upload=src_image)
data_formset = self.formset_media(**data)
self.client.post(self.url, data)
self.client.post(self.url, data_formset)
addon = self.get_addon()
eq_('/'.join(addon.get_icon_url(64).split('/')[-3:-1]),
@ -2645,21 +2738,25 @@ class TestSubmitStep4(TestSubmitBase):
def test_client_lied(self):
filehandle = open(get_image_path('non-animated.gif'), 'rb')
data = {'icon_type': 'image/png', 'icon_upload': filehandle}
res = self.client.post(self.url, data)
data_formset = self.formset_media(**data)
res = self.client.post(self.url, data_formset)
eq_(res.context['form'].errors['icon_upload'][0],
u'Icons must be either PNG or JPG.')
def test_icon_animated(self):
filehandle = open(get_image_path('animated.png'), 'rb')
data = {'icon_type': 'image/png', 'icon_upload': filehandle}
res = self.client.post(self.url, data)
data_formset = self.formset_media(**data)
res = self.client.post(self.url, data_formset)
eq_(res.context['form'].errors['icon_upload'][0],
u'Icons cannot be animated.')
def test_icon_non_animated(self):
filehandle = open(get_image_path('non-animated.png'), 'rb')
data = {'icon_type': 'image/png', 'icon_upload': filehandle}
res = self.client.post(self.url, data)
data_formset = self.formset_media(**data)
res = self.client.post(self.url, data_formset)
eq_(res.status_code, 302)
eq_(self.get_step().step, 5)

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

@ -39,6 +39,9 @@ detail_patterns = patterns('',
url('^edit_(?P<section>[^/]+)(?:/(?P<editable>[^/]+))?$',
views.addons_section, name='devhub.addons.section'),
url('^upload_preview$', views.upload_preview,
name='devhub.addons.upload_preview'),
url('^versions/$', views.version_list, name='devhub.versions'),
url('^versions/delete$', views.version_delete,
name='devhub.versions.delete'),

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

@ -3,12 +3,14 @@ import collections
import functools
import json
import os
import path
import sys
import traceback
import uuid
from django import http
from django.db.models import Count
from django.conf import settings
from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect
from django.utils.http import urlquote
from django.views.decorators.cache import never_cache
@ -574,12 +576,22 @@ def addons_section(request, addon_id, addon, section, editable=False):
if section not in models:
return http.HttpResponseNotFound()
previews = []
if section == 'media':
previews = forms.PreviewFormSet(request.POST or None,
prefix='files', queryset=addon.previews.all())
if editable:
if request.method == 'POST':
form = models[section](request.POST, request.FILES,
instance=addon, request=request)
if form.is_valid():
if form.is_valid() and (not previews or previews.is_valid()):
addon = form.save(addon)
if previews:
for preview in previews.forms:
preview.save(addon)
editable = False
if section == 'media':
amo.log(amo.LOG.CHANGE_ICON, addon)
@ -601,7 +613,8 @@ def addons_section(request, addon_id, addon, section, editable=False):
'form': form,
'editable': editable,
'categories': categories,
'tags': tags}
'tags': tags,
'preview_form': previews}
return jingo.render(request,
'devhub/includes/addon_edit_%s.html' % section, data)
@ -615,6 +628,27 @@ def icon_status(request, addon_id, addon):
return os.path.exists(destination)
@json_view
@dev_required
def upload_preview(request, addon_id, addon):
if 'upload_preview' in request.FILES:
upload_preview = request.FILES['upload_preview']
upload_preview.seek(0)
upload_hash = uuid.uuid4().hex
loc = path.path(settings.PREVIEWS_PATH) / 'temp' / upload_hash
if not loc.dirname().exists():
loc.dirname().makedirs()
with open(loc, 'wb') as fd:
for chunk in upload_preview:
fd.write(chunk)
return {'upload_hash': upload_hash, 'errors': False}
return {'errors': [_('There was an error uploading your preview.')]}
@dev_required
def version_edit(request, addon_id, addon, version_id):
version = get_object_or_404(Version, pk=version_id, addon=addon)
@ -812,16 +846,24 @@ def submit_describe(request, addon_id, addon, step):
@dev_required
@submit_step(4)
def submit_media(request, addon_id, addon, step):
form = addon_forms.AddonFormMedia(request.POST or None, request.FILES,
instance=addon, request=request)
form_icon = addon_forms.AddonFormMedia(request.POST or None,
request.FILES, instance=addon, request=request)
form_previews = forms.PreviewFormSet(request.POST or None,
prefix='files', queryset=addon.previews.all())
if (request.method == 'POST' and
form_icon.is_valid() and form_previews.is_valid()):
addon = form_icon.save(addon)
for preview in form_previews.forms:
preview.save(addon)
if request.method == 'POST' and form.is_valid():
addon = form.save(addon)
SubmitStep.objects.filter(addon=addon).update(step=5)
return redirect('devhub.submit.5', addon.slug)
return jingo.render(request, 'devhub/addons/submit/media.html',
{'form': form, 'addon': addon, 'step': step})
{'form': form_icon, 'addon': addon, 'step': step,
'preview_form': form_previews})
@dev_required

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

@ -274,6 +274,53 @@ Bug 622030- TODO (potch) fix this later
width: 16px;
}
#file-list .preview {
overflow: auto;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px dotted #ADD0DC;
}
#file-list div:last-child {
display: none;
}
#file-list .preview .preview_extra {
display: none;
}
#file-list .preview label {
color: #666666;
display: block;
font-size: 0.9em;
font-weight: bold;
padding-bottom: 0;
}
#file-list .preview textarea {
font-size: 0.9em;
height: 34px;
line-height: 1.1em;
width: 375px;
}
.preview-thumb {
border: 3px solid #D2EDF5;
float: left;
height: 94px;
margin-right: 15px;
width: 125px;
}
.preview-thumb img {
max-width: 125px;
}
.edit-previews-readonly .preview-thumb {
margin-bottom: 10px;
margin-right: 10px;
}
#icons_default {
border: 1px solid #6A89AC;
max-height: 86px;

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

@ -47,6 +47,7 @@ $(document).ready(function() {
// Submission > Media
if($('#submit-media').length) {
initUploadIcon();
initUploadPreview();
}
if ($(".version-upload").length) {
@ -210,16 +211,120 @@ function initEditAddon() {
hideSameSizedIcons();
initUploadIcon();
initUploadPreview();
}
function create_new_preview_field() {
var forms_count = $('#id_files-TOTAL_FORMS').val(),
last = $('#file-list .preview').last(),
last_clone = last.clone();
$('input, textarea, div', last_clone).each(function(){
var re = new RegExp(format("-{0}-", [forms_count-1])),
new_count = "-"+forms_count+"-",
el = $(this);
$.each(['id','name','data-name'], function(k,v){
if(el.attr(v)) {
el.attr(v, el.attr(v).replace(re, new_count));
}
});
});
$(last).after(last_clone);
$('#id_files-TOTAL_FORMS').val(parseInt(forms_count) + 1);
return last;
}
function initUploadPreview() {
$('#edit-addon-media, #submit-media').delegate('#screenshot_upload', 'change', function(e){
url = $(this).attr('data-upload-url');
// TODO(gkoberger): Make sure this works on non-Fx browsers that don't
//support multiple files
$.each($('#screenshot_upload')[0].files, function(k, f){
var data = f.getAsBinary(),
form = create_new_preview_field(),
file = {},
xhr = new XMLHttpRequest(),
output = "",
boundary = "BoUnDaRyStRiNg";
file.name = f.name || f.fileName;
file.size = f.size;
file.data = '';
file.aborted = false;
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Length", file.size);
xhr.setRequestHeader('Content-Disposition', 'file; name="upload";');
xhr.setRequestHeader("X-File-Name", file.name);
xhr.setRequestHeader("X-File-Size", file.size);
xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
xhr.setRequestHeader('Content-length', false);
xhr.setRequestHeader("Content-Type", "multipart/form-data;" +
"boundary=" + boundary);
output += "--" + boundary + "\r\n";
output += "Content-Disposition: form-data; name=\"csrfmiddlewaretoken\";";
parent_form = $('#screenshot_upload').closest('form')
output += "\r\n\r\n";
output += parent_form.find('input[name=csrfmiddlewaretoken]').val();
output += "\r\n";
output += "--" + boundary + "\r\n";
output += "Content-Disposition: form-data; name=\"upload_preview\";";
output += " filename=\"new-upload\";\r\n";
output += "Content-Type: " + f.type;
output += "\r\n\r\n";
output += data;
output += "\r\n";
output += "--" + boundary + "--";
xhr.onreadystatechange = function(){
if (xhr.readyState == 4 && xhr.responseText &&
(xhr.status == 200 || xhr.status == 304)) {
try {
json = JSON.parse(xhr.responseText);
} catch(err) {
return false;
}
form.find('[name$=upload_hash]').val(json.upload_hash);
}
}
xhr.sendAsBinary(output);
});
$('#screenshot_upload').val("");
});
}
function initUploadIcon() {
$('#edit-addon-media').delegate('form', 'submit', function(e) {
e.preventDefault();
multipartUpload($(this), function(e, xhr){
if (xhr.readyState == 4 && xhr.responseText &&
(xhr.status == 200 || xhr.status == 304)) {
$('#edit-addon-media').html(xhr.responseText);
hideSameSizedIcons();
}
});
if($('input[name=icon_type]:checked').val().match(/^image\//)) {
setTimeout(checkIconStatus, 1000);
}
});
$('#edit-addon-media').delegate('form', 'submit', multipartUpload);
$('#edit-addon-media, #submit-media').delegate('#icons_default a', 'click', function(e){
e.preventDefault();
@ -907,60 +1012,59 @@ function checkIconStatus() {
);
}
function multipartUpload(e) {
e.preventDefault();
function multipartUpload(form, onreadystatechange) {
var xhr = new XMLHttpRequest(),
boundary = "BoUnDaRyStRiNg";
boundary = "BoUnDaRyStRiNg",
form = $(form),
serialized = form.serializeArray(),
submit_items = [],
output = "";
xhr.open("POST", $(this).attr('action'), true)
xhr.open("POST", form.attr('action'), true)
xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
xhr.setRequestHeader('Content-length', false);
xhr.setRequestHeader("Content-Type", "multipart/form-data;" +
"boundary=" + boundary);
// Sorry this is so ugly.
content = [
"Content-Type: multipart/form-data; boundary=" + boundary,
"",
"--" + boundary,
"Content-Disposition: form-data; name=\"icon_type\"",
"",
$('input[name="icon_type"]:checked', $('#icons_default')).val(),
$('input[type=file]', form).each(function(){
var files = $(this)[0].files,
file_field = $(this);
"--" + boundary,
"Content-Disposition: form-data; name=\"csrfmiddlewaretoken\"",
"",
$('input[name="csrfmiddlewaretoken"]', $('#edit-addon-media')).val()];
$.each(files, function(k, file) {
var data = file.getAsBinary();
if($('input[name=icon_type]:checked').val().match(/^image\//)) {
// There's a file to be uploaded.
serialized.push({
'name': $(file_field).attr('name'),
'value': data,
'file_type': file.type,
'file_name': file.name || file.fileName
});
});
var file = $('#id_icon_upload')[0].files[0],
data = file.getAsBinary();
});
image = [
"--" + boundary,
"Content-Disposition: form-data; name=\"icon_upload\";" +
"filename=\"new-icon\"",
"Content-Type: " + file.type,
"",
data,
"--" + boundary + "--"];
$.each(serialized, function(k, v){
output += "--" + boundary + "\r\n";
output += "Content-Disposition: form-data; name=\"" + v.name + "\";";
content = $.merge(content, image);
if(v.file_name != undefined) {
output += " filename=\"new-upload\";\r\n";
output += "Content-Type: " + v.file_type;
}
output += "\r\n\r\n";
output += v.value;
output += "\r\n";
});
output += "--" + boundary + "--";
if(onreadystatechange) {
xhr.onreadystatechange = function(e){ onreadystatechange(e, xhr); }
}
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.responseText &&
(xhr.status == 200 || xhr.status == 304)) {
$('#edit-addon-media').html(xhr.responseText);
hideSameSizedIcons();
}
};
xhr.sendAsBinary(content.join('\r\n'));
xhr.sendAsBinary(output);
}
function hideSameSizedIcons() {

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

@ -462,6 +462,9 @@ PREVIEWS_PATH = UPLOADS_PATH + '/previews'
USERPICS_PATH = UPLOADS_PATH + '/userpics'
ADDON_ICONS_DEFAULT_PATH = os.path.join(MEDIA_ROOT, 'img/addon-icons')
PREVIEW_THUMBNAIL_PATH = (PREVIEWS_PATH + '/thumbs/%s/%d.png')
PREVIEW_FULL_PATH = (PREVIEWS_PATH + '/full/%s/%d.png')
# URL paths
# paths for images, e.g. mozcdn.com/amo or '/static'
STATIC_URL = SITE_URL