diff --git a/apps/addons/models.py b/apps/addons/models.py index 3549cd3d57..6dd1c2fac8 100644 --- a/apps/addons/models.py +++ b/apps/addons/models.py @@ -1164,11 +1164,12 @@ class Preview(amo.models.ModelBase): filetype = models.CharField(max_length=25) thumbtype = models.CharField(max_length=25) caption = TranslatedField() - highlight = models.BooleanField(default=False) + + position = models.IntegerField(default=0) class Meta: db_table = 'previews' - ordering = ('-highlight', 'created') + ordering = ('position', 'created') def flush_urls(self): urls = ['*/addon/%d/' % self.addon_id, diff --git a/apps/amo/fixtures/base/addon_4664_twitterbar.json b/apps/amo/fixtures/base/addon_4664_twitterbar.json index 74ccfb8559..53b39f34a4 100644 --- a/apps/amo/fixtures/base/addon_4664_twitterbar.json +++ b/apps/amo/fixtures/base/addon_4664_twitterbar.json @@ -337,7 +337,6 @@ "created": "2008-05-03 10:03:27", "modified": "2008-05-03 10:03:28", "thumbtype": "image/png", - "highlight": 1, "addon": 4664 }, "model": "addons.preview", diff --git a/apps/amo/fixtures/base/previews.json b/apps/amo/fixtures/base/previews.json index 994377f19e..cb1d00b477 100644 --- a/apps/amo/fixtures/base/previews.json +++ b/apps/amo/fixtures/base/previews.json @@ -171,7 +171,6 @@ "created": "2007-03-05 09:54:15", "modified": null, "thumbtype": "image/png", - "highlight": 0, "addon": 159 } } diff --git a/apps/api/templates/api/includes/addon.xml b/apps/api/templates/api/includes/addon.xml index 642cf871a8..ac9d78db9b 100644 --- a/apps/api/templates/api/includes/addon.xml +++ b/apps/api/templates/api/includes/addon.xml @@ -51,7 +51,7 @@ {%- if new_api -%} {%- for preview in addon.previews.all() -%} - + {{ preview.image_url|urlparams(src='api') }} diff --git a/apps/api/tests/test_legacy.py b/apps/api/tests/test_legacy.py index 51797709ee..0a1758270a 100644 --- a/apps/api/tests/test_legacy.py +++ b/apps/api/tests/test_legacy.py @@ -270,7 +270,7 @@ class APITest(TestCase): "%s/en-US/firefox/user/2519/?src=api" % settings.SITE_URL, "", - """""", + """""", "" "TwitterBar places an icon in the address bar.", """""", diff --git a/apps/devhub/forms.py b/apps/devhub/forms.py index eebb429f9c..f3eca3d12a 100644 --- a/apps/devhub/forms.py +++ b/apps/devhub/forms.py @@ -505,7 +505,7 @@ class PreviewForm(happyforms.ModelForm): class Meta: model = Preview - fields = ('caption', 'file_upload', 'upload_hash', 'id') + fields = ('caption', 'file_upload', 'upload_hash', 'id', 'position') class BasePreviewFormSet(BaseModelFormSet): diff --git a/apps/devhub/templates/devhub/includes/addon_edit_media.html b/apps/devhub/templates/devhub/includes/addon_edit_media.html index df896d5fda..9c72b80d12 100644 --- a/apps/devhub/templates/devhub/includes/addon_edit_media.html +++ b/apps/devhub/templates/devhub/includes/addon_edit_media.html @@ -85,6 +85,7 @@
{% for form in preview_form.forms %}
+  
{% if form.instance.id %} @@ -99,6 +100,9 @@ {{ form.caption|safe }} x
+
+ {{ form.position|safe }} +
{{ form.upload_hash|safe }}
diff --git a/apps/devhub/tests/test_views.py b/apps/devhub/tests/test_views.py index 33d51cba6c..516777340e 100644 --- a/apps/devhub/tests/test_views.py +++ b/apps/devhub/tests/test_views.py @@ -1651,7 +1651,8 @@ class TestEdit(test_utils.TestCase): fields = [] for i in range(amount): fields.append(self.formset_new_form(caption='hi', - upload_hash=upload_hash)) + upload_hash=upload_hash, + position=i)) data_formset = self.formset_media(*fields) self.get_url('media', True) @@ -1669,6 +1670,7 @@ class TestEdit(test_utils.TestCase): edited = {'caption': 'bye', 'upload_hash': '', 'id': preview.id, + 'position': preview.position, 'file_upload': None} data_formset = self.formset_media(edited, initial_count=1) @@ -1678,12 +1680,41 @@ class TestEdit(test_utils.TestCase): eq_(str(self.get_addon().previews.all()[0].caption), 'bye') eq_(len(self.get_addon().previews.all()), 1) + def test_edit_media_preview_reorder(self): + self.preview_add(3) + + previews = self.get_addon().previews.all() + + base = dict(upload_hash='', file_upload=None) + + # Three preview forms were generated; mix them up here. + a = dict(caption="first", position=1, id=previews[2].id) + b = dict(caption="second", position=2, id=previews[0].id) + c = dict(caption="third", position=3, id=previews[1].id) + a.update(base) + b.update(base) + c.update(base) + + # Add them in backwards ("third", "second", "first") + data_formset = self.formset_media(c, b, a, initial_count=3) + eq_(data_formset['files-0-caption'], 'third') + eq_(data_formset['files-1-caption'], 'second') + eq_(data_formset['files-2-caption'], 'first') + + self.client.post(self.get_url('media', True), data_formset) + + # They should come out "first", "second", "third" + eq_(self.get_addon().previews.all()[0].caption, 'first') + eq_(self.get_addon().previews.all()[1].caption, 'second') + eq_(self.get_addon().previews.all()[2].caption, 'third') + def test_edit_media_preview_delete(self): self.preview_add() preview = self.get_addon().previews.get() edited = {'DELETE': 'checked', 'upload_hash': '', 'id': preview.id, + 'position': 0, 'file_upload': None} data_formset = self.formset_media(edited, initial_count=1) diff --git a/media/css/zamboni/developers.css b/media/css/zamboni/developers.css index 3de2af965a..eaf1b377d9 100644 --- a/media/css/zamboni/developers.css +++ b/media/css/zamboni/developers.css @@ -349,6 +349,7 @@ form .char-count b { margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px dotted #ADD0DC; + background-color: #fff; } #file-list > div:last-child { @@ -388,7 +389,11 @@ form .char-count b { } .preview-thumb { - text-align: center; + -moz-background-size: contain; + -webkit-background-size: contain; + background-size: contain; + background-position: center center; + background-repeat: no-repeat; border: 3px solid #D2EDF5; float: left; height: 94px; @@ -396,9 +401,9 @@ form .char-count b { width: 125px; } -.preview-thumb img { - max-width: 125px; - max-height: 94px; +.preview-thumb.error-loading { + border-color: #C63717; + opacity: 0.5; } .edit-previews-readonly .preview-thumb { @@ -863,13 +868,18 @@ h3 a.subscribe-feed:hover { #author_list li > select { margin-right: .5em; } -#author_list .handle { +#author_list .handle, +#file-list .handle { width: 20px; height: 16px; display: inline-block; cursor: move; background: url(../../img/zamboni/icons/icon-draggable.png) no-repeat 0 center; } +#file-list .handle { + display: block; + float: left; +} #author_list .blank * { display: none; } diff --git a/media/js/zamboni/devhub.js b/media/js/zamboni/devhub.js index 920ebd55ff..83a8930e5e 100644 --- a/media/js/zamboni/devhub.js +++ b/media/js/zamboni/devhub.js @@ -254,6 +254,7 @@ function addonFormSubmit() { } }); }); + reorderPreviews(); z.refreshL10n(); })(parent_div); } @@ -316,6 +317,31 @@ function create_new_preview_field() { return last; } + +function renumberPreviews() { + previews = $("#file-list").children(".preview:visible"); + previews.each(function(i, el) { + $(this).find(".position input").val(i); + }); + $(previews).find(".handle").toggle(previews.length > 1); +} + +function reorderPreviews() { + var preview_list = $("#file-list"); + + if (preview_list.length) { + preview_list.sortable({ + items: ".preview:visible", + handle: ".handle", + containment: preview_list, + tolerance: "pointer", + update: renumberPreviews + }); + + renumberPreviews(); + } +} + function imageUploadFile(f, url, parent_form, callbacks) { var data = f.getAsBinary(), file = {}, @@ -338,6 +364,7 @@ function imageUploadFile(f, url, parent_form, callbacks) { if(file.type != 'image/jpeg' && file.type != 'image/png') { callbacks.upload_errors([gettext("Icons must be either PNG or JPG.")]); + callbacks.upload_finished(); return; } @@ -413,6 +440,7 @@ var initUploadPreview = (function(){ $('#edit-addon-media .listing-footer button').attr('disabled', false); } $(form).find('.preview-thumb').removeClass('loading'); + renumberPreviews(); }; callbacks.upload_success = function(upload_hash){ @@ -421,9 +449,11 @@ var initUploadPreview = (function(){ callbacks.upload_start = function(){ $(form).find('.preview-thumb').addClass('loading'); - $('').appendTo($('.preview-thumb', form)).attr('src', f.getAsDataURL()); + $('.preview-thumb', form).css('background-image', 'url(' + + f.getAsDataURL() + ')'); $('#edit-addon-media .listing-footer button').attr('disabled', true); outstanding_uploads++; + renumberPreviews(); }; callbacks.upload_errors = function(errors){ @@ -435,7 +465,11 @@ var initUploadPreview = (function(){ $(error_list).append('
  • ' + v + '
  • '); }); - $el.find('.edit-previews-text').addClass('error').html(error).append(error_list); + $el.find('.preview-thumb').addClass('error-loading'); + + $el.find('.edit-previews-text').addClass('error') + .html(error) + .append(error_list); $el.find('[name^=files-]').remove(); }; @@ -450,7 +484,7 @@ var initUploadPreview = (function(){ e.preventDefault(); var row = $(this).closest(".preview"); row.find(".delete input").attr("checked", "checked"); - row.hide(); + row.slideUp(500, renumberPreviews); }); } })(); @@ -1614,3 +1648,4 @@ $(document).ready(function() { $('#addon-validator-suite').trigger('validate'); }); + diff --git a/migrations/127-add-position-to-preview.sql b/migrations/127-add-position-to-preview.sql new file mode 100644 index 0000000000..02babc884c --- /dev/null +++ b/migrations/127-add-position-to-preview.sql @@ -0,0 +1,13 @@ +ALTER TABLE previews + ADD COLUMN position TINYINT(11) unsigned DEFAULT 0 AFTER highlight; + +CREATE INDEX addon_position_created_idx + ON previews (`addon_id`, `position`, `created`); + +UPDATE previews + SET position=1 WHERE highlight=0; + +ALTER TABLE previews + DROP COLUMN highlight; + +