bug 574271, Update collections

This commit is contained in:
Dave Dash 2010-08-03 10:20:22 -07:00
Родитель c80d66d302
Коммит 117f3875a3
17 изменённых файлов: 624 добавлений и 130 удалений

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

@ -45,7 +45,8 @@ def check_ownership(request, obj, require_owner=False):
def check_collection_ownership(request, collection, require_owner=False):
if not request.user.is_authenticated():
return False
if not require_owner and action_allowed(request, 'Admin', '%'):
if action_allowed(request, 'Admin', '%'):
return True
elif request.user.id == collection.author_id:
return True

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

@ -6,25 +6,107 @@ from django.conf import settings
import commonware
from tower import ugettext as _
import amo
from addons.models import Addon
from .models import Collection, CollectionAddon
from translations.widgets import TranslationTextInput, TranslationTextarea
from users.models import UserProfile
from .models import Collection, CollectionAddon, CollectionUser
from . import tasks
privacy_choices = (
(False, _('Only I can view this collection.')),
(True, _('Anybody can view this collection.')))
log = commonware.log.getLogger('z.image')
apps = ((a.id, a.pretty) for a in amo.APP_USAGE)
collection_types = ((k, v) for k, v in amo.COLLECTION_CHOICES.iteritems()
if k not in (amo.COLLECTION_ANONYMOUS, amo.COLLECTION_RECOMMENDED))
class AdminForm(forms.Form):
application = forms.TypedChoiceField(choices=apps, required=False,
coerce=int)
type = forms.TypedChoiceField(choices=collection_types, required=False,
coerce=int)
def save(self, collection):
collection.type = self.cleaned_data['type']
collection.application_id = self.cleaned_data['application']
collection.save()
class AddonsForm(forms.Form):
"""This form is related to adding addons to a collection."""
addon = forms.CharField(widget=forms.MultipleHiddenInput, required=False)
addon_comment = forms.CharField(widget=forms.MultipleHiddenInput,
required=False)
def clean_addon(self):
return self.data.getlist('addon')
def clean_addon_comment(self):
addon_ids = self.data.getlist('addon')
return dict(zip(map(int, addon_ids),
self.data.getlist('addon_comment')))
def save(self, collection):
collection.set_addons(self.cleaned_data['addon'],
self.cleaned_data['addon_comment'])
class ContributorsForm(forms.Form):
"""This form is related to adding contributors to a collection."""
contributor = forms.CharField(widget=forms.MultipleHiddenInput,
required=False)
new_owner = forms.IntegerField(widget=forms.HiddenInput, required=False)
def clean_new_owner(self):
new_owner = self.cleaned_data['new_owner']
if new_owner:
return UserProfile.objects.get(pk=new_owner)
def clean_contributor(self):
contributor_ids = self.data.getlist('contributor')
return UserProfile.objects.filter(pk__in=contributor_ids)
def save(self, collection):
collection.collectionuser_set.all().delete()
for user in self.cleaned_data['contributor']:
CollectionUser(collection=collection, user=user).save()
new_owner = self.cleaned_data['new_owner']
if new_owner:
old_owner = collection.author
collection.author = new_owner
cu, created = CollectionUser.objects.get_or_create(
collection=collection, user=old_owner)
if created:
cu.save()
# Check for duplicate slugs.
slug = collection.slug
while new_owner.collections.filter(slug=slug).count():
slug = slug + '-'
collection.slug = slug
collection.save()
# New owner is no longer a contributor.
collection.collectionuser_set.filter(user=new_owner).delete()
class CollectionForm(forms.ModelForm):
name = forms.CharField(max_length=100,
label=_('Give your collection a name.'))
name = forms.CharField(
label=_('Give your collection a name.'),
widget=TranslationTextInput,
)
slug = forms.CharField(label=_('URL:'))
description = forms.CharField(label=_('Describe your collections.'),
widget=forms.Textarea(attrs={'rows': 3}),
required=False)
description = forms.CharField(
label=_('Describe your collections.'),
widget=TranslationTextarea,
required=False)
listed = forms.ChoiceField(
label=_('Who can view your collection?'),
widget=forms.RadioSelect,
@ -35,19 +117,6 @@ class CollectionForm(forms.ModelForm):
icon = forms.FileField(label=_('Give your collection an icon.'),
required=False)
addon = forms.CharField(widget=forms.MultipleHiddenInput, required=False)
addon_comment = forms.CharField(widget=forms.MultipleHiddenInput,
required=False)
def clean_addon(self):
addon_ids = self.data.getlist('addon')
return Addon.objects.filter(pk__in=addon_ids)
def clean_addon_comment(self):
addon_ids = self.data.getlist('addon')
return dict(zip(map(int, addon_ids),
self.data.getlist('addon_comment')))
def clean_description(self):
description = self.cleaned_data['description']
if description.strip() == '':
@ -58,6 +127,8 @@ class CollectionForm(forms.ModelForm):
def clean_slug(self):
author = self.initial['author']
slug = self.cleaned_data['slug']
if self.instance and self.instance.slug == slug:
return slug
if author.collections.filter(slug=slug).count():
raise forms.ValidationError(
@ -107,15 +178,7 @@ class CollectionForm(forms.ModelForm):
fh.close()
# XXX
# tasks.resize_icon.delay(tmp_destination, destination)
for addon in self.cleaned_data['addon']:
ca = CollectionAddon(collection=c, addon=addon)
comment = self.cleaned_data['addon_comment'].get(addon.id)
if comment:
ca.comments = comment
ca.save()
c.save() # Update counts, etc.
tasks.resize_icon.delay(tmp_destination, destination)
return c

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

@ -178,7 +178,7 @@ class Collection(amo.models.ModelBase):
self.save()
return r
def set_addons(self, addon_ids):
def set_addons(self, addon_ids, comments={}):
"""Replace the current add-ons with a new list of add-on ids."""
order = dict((a, idx) for idx, a in enumerate(addon_ids))
@ -207,6 +207,14 @@ class Collection(amo.models.ModelBase):
for addon, ordering in update:
(CollectionAddon.objects.filter(collection=self.id, addon=addon)
.update(ordering=ordering, modified=now))
for addon, comment in comments.iteritems():
c = CollectionAddon.objects.filter(collection=self.id, addon=addon)
if c:
c[0].comments = comment
c[0].save()
self.save()
def is_subscribed(self, user):
@ -230,6 +238,12 @@ class Collection(amo.models.ModelBase):
CollectionAddon.objects.filter(addon=addon, collection=self).delete()
self.save() # To invalidate Collection.
def owned_by(self, user):
return (user.id == self.author_id)
def publishable_by(self, user):
return (user in self.users.all())
@staticmethod
def transformer(collections):
if not collections:
@ -240,12 +254,6 @@ class Collection(amo.models.ModelBase):
for c in collections:
c.author = authors.get(c.author_id)
def owned_by(self, user):
return (user.id == self.author_id)
def publishable_by(self, user):
return (user in self.users.all())
class CollectionAddon(amo.models.ModelBase):
addon = models.ForeignKey(Addon)
@ -313,7 +321,7 @@ class CollectionPromo(amo.models.ModelBase):
promo_dict[promo_id].collection = collection.next()
class CollectionRecommendation(amo.models.ModelBase):
class CollectionRecommendation(models.Model):
collection = models.ForeignKey(Collection, null=True,
related_name="collection_one")
other_collection = models.ForeignKey(Collection, null=True,

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

@ -11,95 +11,15 @@
<h2>{{ _('Create a New Collection') }}</h2>
</header>
{% if form.errors %}
<p class="error">
{{ _('There are errors in this form. Please correct them below.') }}
</p>
{% endif %}
{% include 'bandwagon/includes/addedit_errors.html' %}
<div>
<form method="post" action="{{ url('collections.add') }}"
enctype="multipart/form-data">
{{ csrf() }}
{% include 'bandwagon/includes/addedit.html' %}
{% include 'bandwagon/includes/addon_selector.html' %}
<h3>{{ _('Collection Description') }}</h3>
<fieldset>
<p>
{{ form.errors['name']|safe }}
{{ form.name.label|safe }}
{{ form.name|safe }}
</p>
<p>
{{ form.errors['slug']|safe }}
{{ form.slug.label|safe }}
{{ url('collections.user', user.get_profile().nickname)|absolutify -}}
{{ form.slug|safe }}
</p>
<p>
{{ form.description.label|safe }} {{ _('(optional)') }}
{{ form.description|safe }}
</p>
<p>
{{ form.listed.label|safe }}
</p>
{{ form.listed|safe }}
<p>
{{ form.errors['icon']|safe }}
{{ form.icon.label|safe }} {{ _('(optional)') }}
{{ form.icon|safe }}
</p>
<p>
{{ _('PNG and JPG supported. Image will be resized to 32x32.') }}
</p>
</fieldset>
<h3>{{ _('Add-ons in Your Collection') }}</h3>
<p>
{% trans %}
To include an add-on in this collection, enter its name
in the box below and hit enter. You can also use the
<strong>Add to Collection</strong> links throughout the
site to include add-ons later.
{% endtrans %}
</p>
<fieldset>
<table>
<thead>
<tr>
<th>Add-on</th>
<th>Comment</th>
<th>Remove</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input id="addon-ac" data-src="{{ url('search.ajax') }}" />
<button id="addon-select">
{{ _('Add to Collection') }}
</button>
</td>
</tr>
{% for addon in addons %}
<tr>
<td>
<input type="hidden" value="{{ addon.id }}" name="addon">
<img src="{{ addon.icon_url }}">
<p>{{ addon.name }}</p>
<p>
<textarea name="addon_comment">{{ comments.get(addon.id) }}
</textarea>
</p>
</td>
<td class="comment">x</td>
<td class="remove">x</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
<p>
<input type="submit" value="{{ _('Create Collection') }}">
</p>

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

@ -0,0 +1,12 @@
{% extends "bandwagon/edit_base.html" %}
{% block form %}
<form method="post" action="{{ url('collections.edit', username, slug) }}"
enctype="multipart/form-data">
{% include 'bandwagon/includes/addedit.html' %}
<p>
<input type="submit" value="{{ _('Save Changes') }}">
</p>
</form>
{% endblock %}

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

@ -0,0 +1,12 @@
{% extends "bandwagon/edit_base.html" %}
{% block form %}
<form method="post"
action="{{ url('collections.edit_addons', username, slug) }}">
{% include 'bandwagon/includes/addon_selector.html' %}
{{ csrf() }}
<p>
<input type="submit" value="{{ _('Save Changes') }}">
</p>
</form>
{% endblock %}

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

@ -0,0 +1,52 @@
{% extends "base.html" %}
{% block title %}
{# L10n: %s is the name of the collection. #}
{{ page_title(_('Edit %s') % collection.name) }}
{% endblock %}
{% block content %}
<header>
{{ breadcrumbs([(remora_url('collections'), _('Collections')),
(collection.get_url_path(), collection.name),
(None, _('Edit'))]) }}
<h2>{{ _('Editing %s') % collection.name }}</h2>
</header>
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>
{{- message }}</li>
{% endfor %}
</ul>
{% endif %}
{% if collection.owned_by(amo_user) %}
<ul class="tabs">
<li>
<a href="{{ url('collections.edit', username, slug) }}">
{{ _('Description') }}</a>
</li>
<li>
<a href="{{ url('collections.edit_addons', username, slug) }}">
{{ _('Add-ons') }}</a>
</li>
<li>
<a href="{{ url('collections.edit_contributors', username, slug) }}">
{{ _('Contributors & More') }}</a>
</li>
</ul>
{% endif %}
{% if form %}
{% include 'bandwagon/includes/addedit_errors.html' %}
{% endif %}
<div>
{% block form %}
{% endblock %}
</div>
{% endblock %}

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

@ -0,0 +1,78 @@
{% macro user_row(user, role) %}
<tr>
<td>
{{ user.name }}
</td>
<td>
{{ user.email }}
</td>
<td>
{{ role }}
{% if role != _('Owner') %}
<input type="hidden" name="contributor" value="{{ user.id }}">
<a>Make Owner</a>
<button name="new_owner" value="{{ user.id }}">POTCH IT!</button>
{% endif %}
</td>
{% if role != _('Owner') %}
<td class="remove">x</td>
{% endif %}
</tr>
{% endmacro %}
{% extends "bandwagon/edit_base.html" %}
{% block bodyclass %}collections-contributors{% endblock %}
{% block form %}
<h3>{{ _('Collection Contributors') }}</h3>
<p>
{% trans %}
You can add multiple contributors to this collection. A contributor can
add and remove add-on from this collection, but cannot change its name or
description. To add a contributor, enter their email in the box below.
Contributors must have a Mozilla Add-ons account.
{% endtrans %}
</p>
<form method="post"
action="{{ url('collections.edit_contributors', username, slug) }}">
{{ csrf() }}
<fieldset>
<table>
<thead>
<tr>
<th>{{ _('User') }}</th>
<th>{{ _('Email') }}</th>
<th>{{ _('Role') }}</th>
<th>{{ _('Remove') }}</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="4">
<input id="contributor-ac" data-src="{{ url('users.ajax') }}"
data-self="{{ amo_user.id }}" />
<button id="contributor-ac-button">
{{ _('Add Contributor') }}
</button>
</td>
</tr>
{{ user_row(collection.author, _('Owner')) }}
{% for user in collection.users.all() %}
{{ user_row(user, _('Contributor')) }}
{% endfor %}
</tbody>
</table>
</fieldset>
{% if is_admin %}
<h3>{{ _('Admin Settings') }}</h3>
{{ admin_form.as_p()|safe }}
{% endif %}
<p>
<input type="submit" value="{{ _('Save Changes') }}">
</p>
</form>
{% endblock %}

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

@ -0,0 +1,38 @@
{{ csrf() }}
<h3>{{ _('Collection Description') }}</h3>
<fieldset>
<p>
{{ form.errors['name']|safe }}
{{ form.name.label|safe }}
{{ form.name|safe }}
</p>
<p>
{{ form.errors['slug']|safe }}
{{ form.slug.label|safe }}
{{ url('collections.user', user.get_profile().nickname)|absolutify -}}
{{ form.slug|safe }}
</p>
<p>
{{ form.description.label|safe }} {{ _('(optional)') }}
{{ form.description|safe }}
</p>
<p>
{{ form.listed.label|safe }}
</p>
{{ form.listed|safe }}
<p>
{% if collection %}
<img src="{{ collection.icon_url }}">
{% endif %}
{{ form.errors['icon']|safe }}
{{ form.icon.label|safe }} {{ _('(optional)') }}
{{ form.icon|safe }}
</p>
<p>
{{ _('PNG and JPG supported. Image will be resized to 32x32.') }}
</p>
</fieldset>

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

@ -0,0 +1,5 @@
{% if form.errors %}
<p class="error">
{{ _('There are errors in this form. Please correct them below.') }}
</p>
{% endif %}

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

@ -0,0 +1,47 @@
<h3>{{ _('Add-ons in Your Collection') }}</h3>
<p>
{% trans %}
To include an add-on in this collection, enter its name
in the box below and hit enter. You can also use the
<strong>Add to Collection</strong> links throughout the
site to include add-ons later.
{% endtrans %}
</p>
<fieldset>
<table>
<thead>
<tr>
<th>{{ _('Add-on') }}</th>
<th>{{ _('Comment') }}</th>
<th>{{ _('Remove') }}</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input id="addon-ac" data-src="{{ url('search.ajax') }}" />
<button id="addon-select">
{{ _('Add to Collection') }}
</button>
</td>
</tr>
{% for addon in addons %}
<tr>
<td>
<input type="hidden" value="{{ addon.id }}" name="addon">
<img src="{{ addon.icon_url }}">
<p>{{ addon.name }}</p>
<p>
<textarea name="addon_comment">{{ comments.get(addon.id) }}
</textarea>
</p>
</td>
<td class="comment">x</td>
<td class="remove">x</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>

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

@ -6,7 +6,7 @@ import test_utils
import amo
from addons.models import Addon, AddonRecommendation
from bandwagon.models import (Collection, SyncedCollection,
from bandwagon.models import (Collection, CollectionUser, SyncedCollection,
RecommendedCollection)
from users.models import UserProfile
@ -94,6 +94,14 @@ class TestCollections(test_utils.TestCase):
eq_(get_addons(c), addons)
eq_(c.addons.count(), len(addons))
def test_is_publisher(self):
c = Collection()
u = UserProfile(nickname='f')
c.save()
u.save()
CollectionUser(collection=c, user=u).save()
eq_(c.is_publisher(u), True)
class TestRecommendations(test_utils.TestCase):
fixtures = ['base/addon-recs']

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

@ -3,9 +3,12 @@ import os
from django.conf import settings
from django.http import QueryDict
from mock import patch
from nose.tools import eq_
from pyquery import PyQuery as pq
import test_utils
from access import acl
from amo.urlresolvers import reverse
from amo.utils import urlparams
from bandwagon.models import Collection, CollectionVote
@ -128,7 +131,7 @@ class TestVotes(test_utils.TestCase):
eq_(r.status_code, 200)
class TestAdd(test_utils.TestCase):
class TestCRUD(test_utils.TestCase):
"""Test the collection form."""
fixtures = ['base/fixtures']
@ -144,6 +147,12 @@ class TestAdd(test_utils.TestCase):
'listed': 'True'
}
def login_regular(self):
self.client.login(username='regular@mozilla.com', password='password')
def create_collection(self):
return self.client.post(self.add_url, self.data, follow=True)
def test_showform(self):
"""Shows form if logged in."""
r = self.client.get(self.add_url)
@ -166,3 +175,91 @@ class TestAdd(test_utils.TestCase):
r = self.client.post(self.add_url, self.data, follow=True)
eq_(r.context['form'].errors['slug'][0],
'This url is already in use by another collection')
def test_reassign(self):
"""
When reassigning an addon make sure we don't give it a duplicate slug.
"""
# Create an addon by user 1.
r = self.client.post(self.add_url, self.data, follow=True)
# Create an addon by user 2 with matching slug.
self.login_regular()
r = self.client.post(self.add_url, self.data, follow=True)
# Add user1 to user 2.
# Make user1 owner of user2s addon.
url = reverse('collections.edit_contributors',
args=['regularuser', 'pornstar'])
r = self.client.post(url,
{'contributor': 4043307, 'new_owner': 4043307},
follow=True)
# verify that user1's addon is slug + '-'
c = Collection.objects.get(slug='pornstar-')
eq_(c.author_id, 4043307)
def test_edit(self):
self.create_collection()
url = reverse('collections.edit', args=['admin', 'pornstar'])
r = self.client.get(url, follow=True)
eq_(r.status_code, 200)
def test_edit_post(self):
"""
Test edit of collection.
"""
r = self.client.post(self.add_url, self.data, follow=True)
url = reverse('collections.edit',
args=['admin', 'pornstar'])
r = self.client.post(url,
{'name': 'HALP', 'slug': 'halp', 'listed': True},
follow=True)
c = Collection.objects.get(slug='halp')
eq_(unicode(c.name), 'HALP')
def test_forbidden_edit(self):
r = self.client.post(self.add_url, self.data, follow=True)
self.login_regular()
url_args = ['admin', 'pornstar']
url = reverse('collections.edit', args=url_args)
r = self.client.get(url)
eq_(r.status_code, 403)
url = reverse('collections.edit_addons', args=url_args)
r = self.client.get(url)
eq_(r.status_code, 403)
url = reverse('collections.edit_contributors', args=url_args)
r = self.client.get(url)
eq_(r.status_code, 403)
def test_edit_addons(self):
self.create_collection()
url = reverse('collections.edit_addons', args=['admin', 'pornstar'])
r = self.client.get(url, follow=True)
eq_(r.status_code, 200)
def test_edit_addons_post(self):
self.create_collection()
url = reverse('collections.edit_addons',
args=['admin', 'pornstar'])
r = self.client.post(url, {'addon': 40}, follow=True)
addon = Collection.objects.filter(slug='pornstar')[0].addons.all()[0]
eq_(addon.id, 40)
@patch('access.acl.action_allowed')
def test_admin(self, f):
f = lambda *args, **kwargs: True
self.create_collection()
url = reverse('collections.edit_contributors',
args=['admin', 'pornstar'])
r = self.client.get(url, follow=True)
doc = pq(r.content)
eq_(doc('form h3').text(), 'Admin Settings')
r = self.client.post(url, dict(application=1, type=0), follow=True)
eq_(r.status_code, 200)

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

@ -2,11 +2,18 @@ from django.conf.urls.defaults import patterns, url, include
from . import views
edit_urls = patterns('',
url('^$', views.edit, name='collections.edit'),
url('^addons$', views.edit_addons, name='collections.edit_addons'),
url('^contributors$', views.edit_contributors,
name='collections.edit_contributors'),
)
detail_urls = patterns('',
url('^$', views.collection_detail, name='collections.detail'),
url('^vote/(?P<direction>up|down)$', views.collection_vote,
name='collections.vote'),
url('^edit/', include(edit_urls)),
)
ajax_urls = patterns('',

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

@ -1,14 +1,19 @@
import functools
from django import http
from django.db.models import Q
from django.contrib import messages
from django.shortcuts import get_object_or_404, redirect
import jingo
from tower import ugettext_lazy as _lazy
from tower import ugettext_lazy as _lazy, ugettext as _
import amo.utils
from amo.decorators import login_required
from amo.urlresolvers import reverse
from access import acl
from amo.decorators import login_required
from amo.urlresolvers import reverse
from addons.models import Addon
from addons.views import BaseFilter
from tags.models import Tag
@ -17,6 +22,30 @@ from .models import Collection, CollectionAddon, CollectionUser, CollectionVote
from . import forms
def owner_required(f=None, require_owner=True):
"""Requires collection to be owner, by someone."""
def decorator(func):
@functools.wraps(func)
def wrapper(request, username, slug, *args, **kw):
collection = get_object_or_404(Collection,
author__nickname=username,
slug=slug)
if acl.check_collection_ownership(request, collection,
require_owner=require_owner):
return func(request, collection, username, slug, *args, **kw)
else:
return http.HttpResponseForbidden(
_("This is not the collection you are looking for."))
return wrapper
if f:
return decorator(f)
else:
return decorator
def legacy_redirect(request, uuid):
# Nicknames have a limit of 30, so len == 36 implies a uuid.
key = 'uuid' if len(uuid) == 36 else 'nickname'
@ -159,6 +188,10 @@ def collection_vote(request, username, slug, direction):
return redirect(c.get_url_path())
def initial_data_from_request(request):
return dict(author=request.amo_user, application_id=request.APP.id)
@login_required
def add(request):
"Displays/processes a form to create a collection."
@ -166,14 +199,17 @@ def add(request):
if request.method == 'POST':
form = forms.CollectionForm(
request.POST, request.FILES,
initial={'author': request.amo_user,
'application_id': request.APP.id})
initial=initial_data_from_request(request))
aform = forms.AddonsForm(request.POST)
if form.is_valid():
collection = form.save()
if aform.is_valid():
aform.save(collection)
return http.HttpResponseRedirect(collection.get_url_path())
else:
data['addons'] = form.clean_addon()
data['comments'] = form.clean_addon_comment()
data['addons'] = aform.clean_addon()
data['comments'] = aform.clean_addon_comment()
else:
form = forms.CollectionForm()
@ -223,7 +259,7 @@ def _ajax_add_remove(request, op):
c = Collection.objects.get(pk=id)
if not c.is_owner(request.amo_user):
if not c.owned_by(request.amo_user):
return http.HttpResponseForbidden()
a = Addon.objects.get(pk=addon_id)
@ -244,3 +280,69 @@ def ajax_add(request):
def ajax_remove(request):
return _ajax_add_remove(request, 'remove')
@login_required
@owner_required
def edit(request, collection, username, slug):
if request.method == 'POST':
form = forms.CollectionForm(request.POST, request.FILES,
initial=initial_data_from_request(request),
instance=collection)
if form.is_valid():
collection = form.save()
return http.HttpResponseRedirect(collection.get_url_path())
else:
form = forms.CollectionForm(instance=collection)
data = dict(collection=collection,
form=form,
username=username,
slug=slug)
return jingo.render(request, 'bandwagon/edit.html', data)
@login_required
@owner_required(require_owner=False)
def edit_addons(request, collection, username, slug):
if request.method == 'POST':
form = forms.AddonsForm(request.POST)
if form.is_valid():
form.save(collection)
return http.HttpResponseRedirect(collection.get_url_path())
data = dict(collection=collection, username=username, slug=slug)
return jingo.render(request, 'bandwagon/edit_addons.html', data)
@login_required
@owner_required
def edit_contributors(request, collection, username, slug):
is_admin = acl.action_allowed(request, 'Admin', '%')
data = dict(collection=collection, username=username, slug=slug,
is_admin=is_admin)
if is_admin:
initial = dict(type=collection.type,
application=collection.application_id)
data['admin_form'] = forms.AdminForm(initial=initial)
if request.method == 'POST':
if is_admin:
admin_form = forms.AdminForm(request.POST)
if admin_form.is_valid():
admin_form.save(collection)
form = forms.ContributorsForm(request.POST)
if form.is_valid():
form.save(collection)
messages.success(request, _('Your collection has been updated.'))
if form.cleaned_data['new_owner']:
return http.HttpResponseRedirect(collection.get_url_path())
return http.HttpResponseRedirect(
reverse('collections.edit_contributors',
args=[username, slug]))
return jingo.render(request, 'bandwagon/edit_contributors.html', data)

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

@ -2456,3 +2456,11 @@ h6.author, .author a {
background-image: url(../../img/zamboni/loading-white.gif);
background-position: left bottom;
}
.collections-contributors tr a {
display: none;
}
.collections-contributors tr:hover a {
display: inline;
}

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

@ -441,7 +441,43 @@ table.delegate(".remove", "click", function() {
row.find('textarea').parent().show();
});
})();
if ($('body.collections-contributors')) {
var user_row = _.template('<tr>' +
'<td>' +
'<input name="contributor" value="{{ id }}" type="hidden">' +
'{{ name }}' +
'</td><td>{{ email }}</td>' +
'<td class="contributor">Contributor</td>' +
'<td class="remove">x</td>' +
'</tr>'
);
$('#contributor-ac-button').click(function(e) {
e.preventDefault();
var email = $('#contributor-ac').val();
var src = $('#contributor-ac').attr('data-src');
var my_id = $('#contributor-ac').attr('data-self');
// TODO(potch): Add a fancy failure case.
$.get(src, {q: email}, function(d) {
// TODO(potch): gently yell at user if they add someone twice.
if ($('input[name=contributor][value='+d.id+']').length == 0 &&
my_id != d.id) {
var str = user_row({id: d.id, name: d.name, email: email});
$('#contributor-ac-button').closest('tbody').append(str);
}
$('#contributor-ac').val('');
});
});
var table = $('#contributor-ac').closest('table');
table.delegate(".remove", "click", function() {
$(this).closest('tr').remove();
})
}