diff --git a/apps/addons/views.py b/apps/addons/views.py index 312207d1f9..26e33d09de 100644 --- a/apps/addons/views.py +++ b/apps/addons/views.py @@ -40,7 +40,6 @@ from reviews.models import Review, GroupedRating from sharing.views import share as share_redirect from stats.models import Contribution from translations.query import order_by_translation -from translations.helpers import truncate from versions.models import Version from .models import Addon, Persona, FrozenAddon from .forms import NewPersonaForm @@ -680,8 +679,7 @@ def paypal_result(request, addon, status): @addon_view def share(request, addon): """Add-on sharing""" - return share_redirect(request, addon, name=addon.name, - description=truncate(addon.summary, length=250)) + return share_redirect(request, addon, addon.name, addon.summary) @addon_view diff --git a/apps/sharing/forms.py b/apps/sharing/forms.py new file mode 100644 index 0000000000..ce15389e1a --- /dev/null +++ b/apps/sharing/forms.py @@ -0,0 +1,20 @@ +from django import forms + +from amo.helpers import absolutify +from translations.helpers import truncate + + +class ShareForm(forms.Form): + """Only used for the field clean methods. Doesn't get exposed to user.""" + title = forms.CharField() + url = forms.CharField() + description = forms.CharField(required=False) + + def clean_url(self): + return absolutify(self.cleaned_data.get('url')) + + def clean_description(self): + desc = self.cleaned_data.get('description', '') + if desc: + desc = truncate(desc, 250) + return desc diff --git a/apps/sharing/tests.py b/apps/sharing/tests.py index 3c8d74717d..6b2897d7ac 100644 --- a/apps/sharing/tests.py +++ b/apps/sharing/tests.py @@ -1,11 +1,12 @@ from urlparse import urlparse, parse_qs from django import test +from django.conf import settings from django.contrib.auth.models import User as DjangoUser from django.utils import translation, encoding import jingo -from mock import Mock +from mock import Mock, patch from nose.tools import eq_ from pyquery import PyQuery as pq @@ -13,6 +14,7 @@ from addons.models import Addon import amo import sharing import sharing.views +from sharing.forms import ShareForm from sharing.helpers import sharing_box from sharing import DIGG, FACEBOOK @@ -96,3 +98,16 @@ def test_share_view_for_webapp(): qs = parse_qs(urlparse(res['Location']).query) assert 'Apps Marketplace' in qs['status'][0], ( 'Unexpected status: %s' % qs['status'][0]) + + +@patch.object(settings, 'SITE_URL', 'http://test') +def test_share_form(): + form = ShareForm({ + 'title': 'title', + 'url': '/path/to/nowhere/', + 'description': 'x' * 250 + 'abcdef', + }) + form.full_clean() + eq_(form.cleaned_data['description'], 'x' * 250 + '...') + assert form.cleaned_data['url'].startswith('http'), ( + "Unexpected: URL not absolute") diff --git a/apps/sharing/views.py b/apps/sharing/views.py index b8adf5f7e3..ddc7432c75 100644 --- a/apps/sharing/views.py +++ b/apps/sharing/views.py @@ -2,20 +2,24 @@ from django import http from django.shortcuts import redirect from django.utils.encoding import smart_unicode as u -from amo.helpers import page_title, absolutify -import sharing +from amo.helpers import page_title + +from . import SERVICES +from .forms import ShareForm def share(request, obj, name, description): try: - service = sharing.SERVICES[request.GET['service']] + service = SERVICES[request.GET['service']] except KeyError: raise http.Http404() is_webapp = hasattr(obj, 'is_webapp') and obj.is_webapp() - d = { + + form = ShareForm({ 'title': page_title({'request': request}, name, force_webapps=is_webapp), + 'url': u(obj.get_url_path()), 'description': u(description), - 'url': absolutify(u(obj.get_url_path())), - } - return redirect(service.url.format(**d)) + }) + form.full_clean() + return redirect(service.url.format(**form.cleaned_data)) diff --git a/apps/webapps/models.py b/apps/webapps/models.py index efc7acf9de..1723145c4a 100644 --- a/apps/webapps/models.py +++ b/apps/webapps/models.py @@ -82,6 +82,9 @@ class Webapp(Addon): def can_be_purchased(self): return self.is_premium() + def share_url(self): + return reverse('apps.share', args=[self.app_slug]) + # Pull all translated_fields from Addon over to Webapp. Webapp._meta.translated_fields = Addon._meta.translated_fields diff --git a/apps/webapps/tests/test_views.py b/apps/webapps/tests/test_views.py index b724e15f9e..fdc2e0ad77 100644 --- a/apps/webapps/tests/test_views.py +++ b/apps/webapps/tests/test_views.py @@ -1,9 +1,13 @@ from nose.tools import eq_ from pyquery import PyQuery as pq +from amo.helpers import absolutify, page_title import amo.tests from amo.urlresolvers import reverse from browse.tests import test_listing_sort, test_default_sort +from django.utils.encoding import iri_to_uri +from sharing import SERVICES +from translations.helpers import truncate from webapps.models import Webapp @@ -91,3 +95,18 @@ class TestMobileDetail(amo.tests.MobileTest, WebappTest): def test_no_release_notes(self): r = self.client.get(self.url) eq_(pq(r.content)('.versions').length, 0) + + +class TestSharing(WebappTest): + + def test_redirect_sharing(self): + r = self.client.get(reverse('apps.share', args=['yeah']), + {'service': 'delicious'}) + d = { + 'title': page_title({'request': r}, self.webapp.name, + force_webapps=True), + 'description': truncate(self.webapp.summary, length=250), + 'url': absolutify(self.webapp.get_url_path()), + } + url = iri_to_uri(SERVICES['delicious'].url.format(**d)) + self.assertRedirects(r, url, status_code=302, target_status_code=301) diff --git a/apps/webapps/urls.py b/apps/webapps/urls.py index 03fb46a7e2..952fdbdf53 100644 --- a/apps/webapps/urls.py +++ b/apps/webapps/urls.py @@ -9,6 +9,7 @@ APP_SLUG = r"""(?P[^/<>"']+)""" detail_patterns = patterns('', url('^$', views.app_detail, name='apps.detail'), url('^more$', views.app_detail, name='apps.detail_more'), + url('^share$', views.share, name='apps.share'), ) diff --git a/apps/webapps/views.py b/apps/webapps/views.py index 7e97702379..eeefdf1601 100644 --- a/apps/webapps/views.py +++ b/apps/webapps/views.py @@ -8,8 +8,9 @@ from amo.utils import paginate import addons.views import search.views -from addons.models import Addon, Category +from addons.models import Category from browse.views import addon_listing, category_landing, CategoryLandingFilter +from sharing.views import share as share_redirect from .models import Webapp TYPE = amo.ADDON_WEBAPP @@ -66,3 +67,8 @@ def app_detail(request, app_slug): # TODO: check status. webapp = get_object_or_404(Webapp, app_slug=app_slug) return addons.views.extension_detail(request, webapp) + + +def share(request, app_slug): + webapp = get_object_or_404(Webapp, app_slug=app_slug) + return share_redirect(request, webapp, webapp.name, webapp.summary)