Skip to content

Commit 554c23c

Browse files
author
Jeff Balogh
committed
Merge branch 'versions'
2 parents fb265b0 + b9d00ec commit 554c23c

File tree

6 files changed

+162
-10
lines changed

6 files changed

+162
-10
lines changed

apps/devhub/forms.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import paypal
1212
from addons.models import Addon, AddonUser, Charity
1313
from applications.models import Application, AppVersion
14+
from files.models import File
1415
from translations.widgets import TranslationTextarea, TranslationTextInput
1516
from translations.models import delete_translation
1617
from versions.models import License, Version, ApplicationsVersions
@@ -273,3 +274,41 @@ def clean(self):
273274
CompatFormSet = modelformset_factory(
274275
ApplicationsVersions, formset=BaseCompatFormSet,
275276
form=CompatForm, can_delete=True, extra=0)
277+
278+
279+
class FileForm(happyforms.ModelForm):
280+
_choices = [(k, amo.STATUS_CHOICES[k]) for k in
281+
(amo.STATUS_BETA, amo.STATUS_UNREVIEWED)]
282+
status = forms.TypedChoiceField(coerce=int, choices=_choices)
283+
platform = File._meta.get_field('platform').formfield(empty_label=None)
284+
285+
class Meta:
286+
model = File
287+
fields = ('status', 'platform')
288+
289+
def __init__(self, *args, **kw):
290+
super(FileForm, self).__init__(*args, **kw)
291+
# Make sure the current status is in the status <select>.
292+
status = kw['instance'].status
293+
field = self.fields['status']
294+
if status not in dict(field.choices).keys():
295+
# Rebind and add so the original choices aren't changed.
296+
field.choices = (field.choices +
297+
[(status, amo.STATUS_CHOICES[status])])
298+
299+
300+
class BaseFileFormSet(BaseModelFormSet):
301+
302+
def clean(self):
303+
if any(self.errors):
304+
return
305+
files = [f.cleaned_data for f in self.forms
306+
if not f.cleaned_data.get('DELETE', False)]
307+
platforms = [f['platform'] for f in files]
308+
if sorted(platforms) != sorted(set(platforms)):
309+
raise forms.ValidationError(
310+
_('A platform can only be chosen once.'))
311+
312+
313+
FileFormSet = modelformset_factory(File, formset=BaseFileFormSet,
314+
form=FileForm, can_delete=True, extra=0)

apps/devhub/templates/devhub/versions/edit.html

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,24 @@ <h3>{{ title }}</h3>
2424
<div class="featured-inner">
2525
<form method="post" action="">
2626
{{ csrf() }}
27+
<table>
28+
{{ file_form.management_form|safe }}
29+
{% for form in file_form.forms %}
30+
{% with file = form.instance %}
31+
<tr>
32+
<td><a href="{{ file.get_url_path(amo.FIREFOX, 'devhub') }}">
33+
{{ file.filename }}</a></td>
34+
<td>{{ (file.size * 1000)|filesizeformat }}</td>
35+
<td>{{ form.platform|safe }}</td>
36+
<td>{{ form.status|safe }}</td>
37+
<td>
38+
{{ form.DELETE|safe }}
39+
{{ form.id|safe }}
40+
</td>
41+
</tr>
42+
{% endwith %}
43+
{% endfor %}
44+
</table>
2745
<table>
2846
{{ compat_form.management_form|safe }}
2947
{% for form in compat_form.initial_forms %}

apps/devhub/tests/test_views.py

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
from amo.urlresolvers import reverse
1616
from addons.models import Addon, AddonUser, Charity
1717
from applications.models import AppVersion
18-
from devhub.forms import ContribForm, ProfileForm
18+
from devhub.forms import ContribForm
19+
from files.models import File, Platform
1920
from users.models import UserProfile
2021
from versions.models import License, Version
2122

@@ -157,7 +158,7 @@ def formset(*args, **kw):
157158
prefix and initial_count can be set in **kw.
158159
"""
159160
prefix = kw.pop('prefix', 'form')
160-
initial_count = kw.pop('initial_count', 0)
161+
initial_count = kw.pop('initial_count', len(args))
161162
data = {prefix + '-TOTAL_FORMS': len(args),
162163
prefix + '-INITIAL_FORMS': initial_count}
163164
for idx, d in enumerate(args):
@@ -353,8 +354,10 @@ def test_add_user_twice(self):
353354
['An author can only be listed once.'])
354355

355356
def test_success_delete_user(self):
357+
# Add a new user so we have one to delete.
356358
data = self.formset(dict(user='regular@mozilla.com', listed=True,
357-
role=amo.AUTHOR_ROLE_OWNER, position=1))
359+
role=amo.AUTHOR_ROLE_OWNER, position=1),
360+
initial_count=0)
358361
self.client.post(self.url, data)
359362

360363
one, two = self.client.get(self.url).context['user_form'].initial_forms
@@ -839,16 +842,90 @@ def formset(self, *args, **kw):
839842
defaults.update(kw)
840843
return formset(*args, **defaults)
841844

845+
846+
class TestVersionEditDetails(TestVersionEdit):
847+
848+
def setUp(self):
849+
super(TestVersionEditDetails, self).setUp()
850+
ctx = self.client.get(self.url).context
851+
compat = initial(ctx['compat_form'].forms[0])
852+
files = initial(ctx['file_form'].forms[0])
853+
self.initial = formset(compat, **formset(files, prefix='files'))
854+
855+
def formset(self, *args, **kw):
856+
defaults = dict(self.initial)
857+
defaults.update(kw)
858+
return super(TestVersionEditDetails, self).formset(*args, **defaults)
859+
842860
def test_edit_notes(self):
843-
f = self.client.get(self.url).context['compat_form'].initial_forms[0]
844-
d = formset(initial(f), releasenotes='xx', approvalnotes='yy',
845-
initial_count=1)
861+
d = self.formset(releasenotes='xx', approvalnotes='yy')
846862
r = self.client.post(self.url, d)
847863
eq_(r.status_code, 302)
848864
version = self.get_version()
849865
eq_(unicode(version.releasenotes), 'xx')
850866
eq_(unicode(version.approvalnotes), 'yy')
851867

868+
def test_version_number_redirect(self):
869+
url = self.url.replace(str(self.version.id), self.version.version)
870+
r = self.client.get(url, follow=True)
871+
self.assertRedirects(r, self.url)
872+
873+
874+
class TestVersionEditFiles(TestVersionEdit):
875+
876+
def setUp(self):
877+
super(TestVersionEditFiles, self).setUp()
878+
f = self.client.get(self.url).context['compat_form'].initial_forms[0]
879+
self.compat = initial(f)
880+
881+
def formset(self, *args, **kw):
882+
compat = formset(self.compat, initial_count=1)
883+
compat.update(kw)
884+
return super(TestVersionEditFiles, self).formset(*args, **compat)
885+
886+
def test_edit_status(self):
887+
f = self.client.get(self.url).context['file_form'].forms[0]
888+
# Public is one of the choices since the file is currently public.
889+
eq_([x[0] for x in f.fields['status'].choices],
890+
[amo.STATUS_BETA, amo.STATUS_UNREVIEWED, amo.STATUS_PUBLIC])
891+
# Switch the status to Beta.
892+
data = initial(f)
893+
data['status'] = amo.STATUS_BETA
894+
r = self.client.post(self.url, self.formset(data, prefix='files'))
895+
eq_(r.status_code, 302)
896+
eq_(self.version.files.get().status, amo.STATUS_BETA)
897+
898+
# Beta and unreviewed are the only choices.
899+
f = self.client.get(self.url).context['file_form'].forms[0]
900+
eq_([x[0] for x in f.fields['status'].choices],
901+
[amo.STATUS_BETA, amo.STATUS_UNREVIEWED])
902+
903+
def test_unique_platforms(self):
904+
for platform in amo.PLATFORMS:
905+
k, _ = Platform.objects.get_or_create(id=platform)
906+
# Move the existing file to Linux.
907+
f = self.version.files.get()
908+
f.update(platform=Platform.objects.get(id=amo.PLATFORM_LINUX.id))
909+
# And make a new file for Mac.
910+
File.objects.create(version=self.version,
911+
platform_id=amo.PLATFORM_MAC.id)
912+
913+
forms = map(initial,
914+
self.client.get(self.url).context['file_form'].forms)
915+
forms[1]['platform'] = forms[0]['platform']
916+
r = self.client.post(self.url, self.formset(*forms, prefix='files'))
917+
eq_(r.status_code, 200)
918+
eq_(r.context['file_form'].non_form_errors(),
919+
['A platform can only be chosen once.'])
920+
921+
922+
class TestVersionEditCompat(TestVersionEdit):
923+
924+
def formset(self, *args, **kw):
925+
defaults = formset(prefix='files')
926+
defaults.update(kw)
927+
return super(TestVersionEditCompat, self).formset(*args, **defaults)
928+
852929
def test_add_appversion(self):
853930
f = self.client.get(self.url).context['compat_form'].initial_forms[0]
854931
d = self.formset(initial(f), dict(application=18, min=28, max=29),

apps/devhub/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@
2121
views.addons_section, name='devhub.addons.section'),
2222

2323
url('^versions/$', views.version_list, name='devhub.versions'),
24-
url('^versions/(?P<version_id>[^/]+)$', views.version_edit,
24+
url('^versions/(?P<version_id>\d+)$', views.version_edit,
2525
name='devhub.versions.edit'),
26+
url('^versions/(?P<version>[^/]+)$', views.version_bounce),
2627
)
2728

2829
urlpatterns = decorate(write, patterns('',

apps/devhub/views.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,18 +288,32 @@ def version_edit(request, addon_id, addon, version_id):
288288
version_form = forms.VersionForm(request.POST or None, instance=version)
289289
compat_form = forms.CompatFormSet(request.POST or None,
290290
queryset=version.apps.all())
291-
fs = [version_form, compat_form]
291+
file_form = forms.FileFormSet(request.POST or None, prefix='files',
292+
queryset=version.files.all())
293+
fs = [version_form, compat_form, file_form]
292294
if request.method == 'POST' and all([form.is_valid() for form in fs]):
293295
version_form.save()
296+
file_form.save()
294297
for compat in compat_form.save(commit=False):
295298
compat.version = version
296299
compat.save()
297300
return redirect('devhub.versions.edit', addon_id, version_id)
298301
return jingo.render(request, 'devhub/versions/edit.html',
299302
dict(addon=addon, version=version, version_form=version_form,
300-
compat_form=compat_form))
303+
compat_form=compat_form, file_form=file_form))
301304

302305

303306
@dev_required
304307
def version_list(request, addon_id, addon):
305308
return http.HttpResponse('All right then!')
309+
310+
311+
@dev_required
312+
def version_bounce(request, addon_id, addon, version):
313+
# Use filter since there could be dupes.
314+
vs = (Version.objects.filter(version=version, addon=addon)
315+
.order_by('-created'))
316+
if vs:
317+
return redirect('devhub.versions.edit', addon_id, vs[0].id)
318+
else:
319+
raise http.Http404()

apps/files/models.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class File(amo.models.ModelBase):
1919
version = models.ForeignKey('versions.Version', related_name='files')
2020
platform = models.ForeignKey('Platform')
2121
filename = models.CharField(max_length=255, default='')
22-
size = models.PositiveIntegerField(default=0)
22+
size = models.PositiveIntegerField(default=0) # kilobytes
2323
hash = models.CharField(max_length=255, default='')
2424
codereview = models.BooleanField(default=False)
2525
jetpack = models.BooleanField(default=False)
@@ -107,6 +107,9 @@ class Platform(amo.models.ModelBase):
107107
class Meta(amo.models.ModelBase.Meta):
108108
db_table = 'platforms'
109109

110+
def __unicode__(self):
111+
return unicode(amo.PLATFORMS[self.id].name)
112+
110113

111114
class FileUpload(amo.models.ModelBase):
112115
"""Created when a file is uploaded for validation/submission."""

0 commit comments

Comments
 (0)