Skip to content

Commit

Permalink
Merge branch 'addon-collections'
Browse files Browse the repository at this point in the history
  • Loading branch information
potch committed Aug 3, 2010
2 parents e3ec5c8 + 51c1e46 commit 37104c0
Show file tree
Hide file tree
Showing 18 changed files with 500 additions and 79 deletions.
29 changes: 3 additions & 26 deletions apps/addons/templates/addons/details.html
Expand Up @@ -26,7 +26,7 @@ <h4 class="author">{{ _('by') }} {{ users_list(addon.listed_authors) }}</h4>

{# TODO(fwenzel): "add-on has been added to collection" notification #}

<div id="addon" class="primary" role="main">
<div id="addon" class="primary" role="main" data-id="{{ addon.id }}">
<div class="featured">
<div class="featured-inner object-lead inverse">

Expand Down Expand Up @@ -109,6 +109,8 @@ <h4 class="author">{{ _('by') }} {{ users_list(addon.listed_authors) }}</h4>
alt="" width="200" height="150" />
{% endif %}

{% include 'addons/includes/collection_add_widget.html' %}

{{ addon_sharing(addon) }}

</div>{# /secondary #}
Expand Down Expand Up @@ -319,31 +321,6 @@ <h3 class="compact-bottom">{{ _('Related Collections') }}</h3>
</ul>
{% endif %}

{% if user.is_authenticated() %}
{# TODO reverse URLs #}
<form action="{{ remora_url('/collections/addtocollection') }}"
method="post" id="coll_publish"
data-detail-url="{{ remora_url('/collection') }}/"
data-json-url="{{ remora_url('/collections/json') }}">
<div>
<a href="{{ remora_url('/collections/') }}" class="what">
{{ _("What's this?", 'addons_display_a_license_what') }}</a>
<h3>
<label for="publish_to">{{ _('Add to a collection:') }}</label>
</h3>
{{ cake_csrf_token() }}
<input name="data[addon_id]" type="hidden" value="{{ addon.id }}"/>
<select name="data[collection_uuid]" id="publish_to">
<option value="" selected="selected">{{ _('Select a collection...') }}</option>
{% for collection in user_collections %}
<option value="{{ collection.uuid }}">{{ collection.name }}</option>
{% endfor %}
<option value="new">{{ _('New Collection...') }}</option>
</select>
<button>{{ _('Publish') }}</button>
</div>
</form>
{% endif %}
</div>
{# /collections #}

Expand Down
29 changes: 29 additions & 0 deletions apps/addons/templates/addons/includes/collection_add_widget.html
@@ -0,0 +1,29 @@
<div class="collection-add"
data-listurl="{{ url('collections.ajax_list') }}"
data-addurl="{{ url('collections.ajax_add') }}"
data-removeurl="{{ url('collections.ajax_remove') }}"
data-newurl="{{ url('collections.ajax_new') }}"
>
<span><a href="#">Add to collection</a></span>
</div>
<div class="collection-add-dropdown install-note">
{% if user.is_anonymous() %}
<div class="collection-add-login">
<p>
{% trans %}
To create your own collections, you must have an add-ons
account.
{% endtrans %}
</p>
<p class="register-button">
<a class="button" href="{{ remora_url('users/register') }}">{{ _('Create an Add-ons Account') }}</a>
</p>

<p>
{% trans login=login_link() %}
or <a href="{{ login }}">log in to your current account</a>
{% endtrans %}
</p>
</div>
{% endif %}
</div>
4 changes: 3 additions & 1 deletion apps/addons/templates/addons/personas_detail.html
Expand Up @@ -17,7 +17,7 @@ <h2 class="addon"{{ addon.name|locale_html }}>
<h4 class="author">{{ _('by') }} {{ users_list(addon.listed_authors) }}</h4>
</hgroup>

<div id="persona" class="primary" role="main">
<div id="persona" class="primary" role="main" data-id="{{ addon.id }}">
<div class="featured">
<div class="featured-inner object-lead">

Expand Down Expand Up @@ -63,6 +63,8 @@ <h4 class="author">{{ _('by') }} {{ users_list(addon.listed_authors) }}</h4>

{{ big_install_button(addon, show_warning=False) }}

{% include 'addons/includes/collection_add_widget.html' %}

{# TODO(davedash): Remove until zamboni does sharing
{{ addon_sharing(addon) }}
#}
Expand Down
32 changes: 22 additions & 10 deletions apps/addons/tests/test_views.py
Expand Up @@ -199,8 +199,10 @@ def test_ppal_return_url_not_relative(self):
response = self.client.get(reverse('addons.contribute',
args=[592]), follow=True)
redirect_url = response.redirect_chain[0][0]

assert(re.search('\?|&return=https?%3A%2F%2F', redirect_url)), \
"return URL param did not start w/ http%3A%2F%2F (http://) [%s]" % redirect_url
("return URL param did not start w/ "
"http%3A%2F%2F (http://) [%s]" % redirect_url)

def test_redirect_params_type_monthly(self):
"""Test that we have the required ppal param when
Expand Down Expand Up @@ -440,13 +442,18 @@ def test_invalid_version(self):
eq_(response.status_code, 404)

def test_login_links(self):
"""Make sure the login links on this page, redirect back to itself."""
"""
Make sure the login links on this page, redirect back to itself.
Note: This test needs to be changed if you add/remove a login link from
the detail page.
"""
url = reverse('addons.detail', args=[3615])
resp = self.client.get(url, follow=True)

sel = 'a[href$="%s"]' % urlparams(reverse('users.login'), to=url)
doc = pq(resp.content)
eq_(len(doc(sel)), 4) # 4 login links
eq_(len(doc(sel)), 5) # 5 login links

def test_other_author_addons(self):
"""
Expand Down Expand Up @@ -474,7 +481,7 @@ def test_other_author_addons(self):
response = forward_to(u'\u271D')
eq_(response.status_code, 400)

def test_details_collections_dropdown(self):
def test_collections_dropdown(self):

request = Mock()
request.APP.id = 1
Expand Down Expand Up @@ -518,12 +525,6 @@ def test_no_listed_authors(self):
doc = pq(r.content)
eq_(0, len(doc('.avatar')))

def test_collection_detal_url(self):
self.client.login(username='regular@mozilla.com', password='password')
r = self.client.get(reverse('addons.detail', args=[3615]))
url = pq(r.content)('[data-detail-url]').attr('data-detail-url')
eq_(url, '/en-US/firefox/collection/')

def test_search_engine_works_with(self):
"""We don't display works-with info for search engines."""
addon = Addon.objects.filter(type=amo.ADDON_SEARCH)[0]
Expand All @@ -538,6 +539,17 @@ def test_search_engine_works_with(self):
assert any(th.text.strip().lower() == 'works with'
for th in headings)

def test_collections_login_form(self):
# logged out
r = self.client.get(reverse('addons.detail', args=[3615]))
doc = (pq(r.content))
eq_(len(doc('.collection-add-login')), 1)
# logged in
self.client.login(username='regular@mozilla.com', password='password')
r = self.client.get(reverse('addons.detail', args=[3615]))
doc = (pq(r.content))
eq_(len(doc('.collection-add-login')), 0)


class TestTagsBox(amo.test_utils.ExtraSetup, test_utils.TestCase):
fixtures = ['base/addontag', 'base/apps']
Expand Down
77 changes: 39 additions & 38 deletions apps/amo/fixtures/base/fixtures.json
Expand Up @@ -8818,36 +8818,11 @@
"last_login": "2010-04-23 10:33:53",
"groups": [],
"user_permissions": [],
"password": "yermom",
"password": "sha1$a04e0$0512298efb3e6e7dbace3976474151d396078fdd",
"email": "clouserw@gmail.com",
"date_joined": "2007-03-05 13:09:38"
}
},
{
"pk": 80,
"model": "bandwagon.collection",
"fields": {
"description": 394558,
"rating": 0.0,
"uuid": "a5f1b177-c01d-c306-3030-e01598892405",
"default_locale": "en-US",
"created": "2009-06-09 23:35:10",
"monthly_subscribers": 0,
"nickname": "webdev",
"downvotes": 0,
"weekly_subscribers": 0,
"modified": "2009-06-09 23:37:36",
"addon_count": 2,
"application": 1,
"listed": 1,
"subscribers": 1,
"upvotes": 0,
"icontype": "",
"type": 0,
"downloads": 46,
"name": 394557
}
},
{
"pk": 50,
"model": "bandwagon.collection",
Expand All @@ -8873,17 +8848,6 @@
"name": 394487
}
},
{
"pk": 47,
"model": "bandwagon.collectionpromo",
"fields": {
"locale": "",
"collection_feature": 1,
"modified": null,
"collection": 80,
"created": "2010-01-05 18:16:11"
}
},
{
"pk": 10482,
"model": "users.userprofile",
Expand All @@ -8905,7 +8869,7 @@
"lastname": "Clouser",
"emailhidden": 0,
"user": 10482,
"password": "yermom",
"password": "sha1$a04e0$0512298efb3e6e7dbace3976474151d396078fdd",
"nickname": "clouserw",
"resetcode_expires": "2010-01-01 00:00:00",
"resetcode": "",
Expand All @@ -8915,6 +8879,43 @@
"notifyevents": 1
}
},
{
"pk": 80,
"model": "bandwagon.collection",
"fields": {
"description": 394558,
"rating": 0.0,
"uuid": "a5f1b177-c01d-c306-3030-e01598892405",
"default_locale": "en-US",
"created": "2009-06-09 23:35:10",
"monthly_subscribers": 0,
"nickname": "webdev",
"downvotes": 0,
"weekly_subscribers": 0,
"modified": "2009-06-09 23:37:36",
"addon_count": 2,
"application": 1,
"listed": 1,
"subscribers": 1,
"upvotes": 0,
"icontype": "",
"type": 0,
"downloads": 46,
"name": 394557,
"author": 10482
}
},
{
"pk": 47,
"model": "bandwagon.collectionpromo",
"fields": {
"locale": "",
"collection_feature": 1,
"modified": null,
"collection": 80,
"created": "2010-01-05 18:16:11"
}
},
{
"pk": 5,
"model": "bandwagon.collection",
Expand Down
3 changes: 2 additions & 1 deletion apps/bandwagon/forms.py
Expand Up @@ -23,7 +23,8 @@ class CollectionForm(forms.ModelForm):
label=_('Give your collection a name.'))
slug = forms.CharField(label=_('URL:'))
description = forms.CharField(label=_('Describe your collections.'),
widget=forms.Textarea, required=False)
widget=forms.Textarea(attrs={'rows': 3}),
required=False)
listed = forms.ChoiceField(
label=_('Who can view your collection?'),
widget=forms.RadioSelect,
Expand Down
30 changes: 30 additions & 0 deletions apps/bandwagon/models.py
Expand Up @@ -8,6 +8,8 @@
from django.core.cache import cache
from django.db import models, connection

from MySQLdb import IntegrityError

import amo
import amo.models
from amo.utils import sorted_groupby
Expand Down Expand Up @@ -36,6 +38,14 @@ def __set__(self, obj, value):

class CollectionManager(amo.models.ManagerBase):

def manual(self):
"""Only hand-crafted, favorites, and featured collections should appear
in this filter."""
types = (amo.COLLECTION_NORMAL, amo.COLLECTION_FAVORITE,
amo.COLLECTION_FEATURED, )

return self.filter(type__in=types)

def listed(self):
"""Return public collections only."""
return self.filter(listed=True)
Expand Down Expand Up @@ -187,6 +197,26 @@ def is_subscribed(self, user):
"""Determines if the user is subscribed to this collection."""
return self.subscriptions.filter(user=user).exists()

# TODO(davedash): use this when we're on 1.3:
# http://code.djangoproject.com/ticket/13240
def add_addon(self, addon):
"Adds an addon to the collection."
ca = CollectionAddon()
ca.addon = addon
ca.collection = self
try:
ca.save()
except IntegrityError:
pass
self.save() # To invalidate Collection.

def remove_addon(self, addon):
CollectionAddon.objects.filter(addon=addon, collection=self).delete()
self.save() # To invalidate Collection.

def is_owner(self, user):
return (user.id == self.author_id)


class CollectionAddon(amo.models.ModelBase):
addon = models.ForeignKey(Addon)
Expand Down
3 changes: 2 additions & 1 deletion apps/bandwagon/sql/collectionuser.sql
@@ -1,4 +1,5 @@
ALTER TABLE `collections_users` CHANGE COLUMN `id` `id` int(11) NOT NULL;
ALTER TABLE `collections_users`
CHANGE COLUMN `id` `id` int(11) NOT NULL DEFAULT 0;

ALTER TABLE `collections_users` DROP PRIMARY KEY,
ADD PRIMARY KEY(`collection_id`, `user_id`);
12 changes: 12 additions & 0 deletions apps/bandwagon/templates/bandwagon/ajax_list.html
@@ -0,0 +1,12 @@
{% if collections %}
<ul id="ajax_collections_list" class="addon-collections">
{% for collection in collections %}
<li {{ collection.has_addon|class_selected(True) }}
data-id="{{ collection.id }}">
<span class="collectionitem">{{ collection.name }}</span>
</li>
{% endfor %}
</ul>
{% endif %}

<p id="ajax_new_collection"><a href="#">{{ _('Start a new collection...') }}</a></p>
31 changes: 31 additions & 0 deletions apps/bandwagon/templates/bandwagon/ajax_new.html
@@ -0,0 +1,31 @@
<div id="collections-new">
<h3>{{ _('Start a New Collection') }}</h3>

<form>
<fieldset>
{{ field(form.name, _('Name:')) }}
<p id="collection-form-slug">
{{ form.errors['slug']|safe }}
<label>{{ form.slug.label|safe }}</label>
<span id="slug_edit">
{{ url('collections.user', user.get_profile().nickname)|absolutify -}}
{{ form.slug|safe }}
</span>
<span id="slug_readonly">
{{ url('collections.user', user.get_profile().nickname)|absolutify -}}<span id="slug_value"></span>
<a id="edit_slug" href="#">{{ _('Edit') }}</a>
</span>
</p>
<p>
<label for="id_description">{{ form.description.label|safe }}</label> {{ _('(optional)') }}
{{ form.description|safe }}
</p>
<label>{{ form.listed.label }}</label>
{{ form.listed|safe }}
</fieldset>
<p>
<input type="submit" value="{{ _('Create Collection') }}">
{% trans %}or <a id="collections-new-cancel" href="#">Cancel</a>{% endtrans %}
</p>
</form>
</div>

0 comments on commit 37104c0

Please sign in to comment.