Performed summary-ectomy (bug 862603)
This commit is contained in:
Родитель
e83198abf2
Коммит
c9542a1f10
|
@ -11,10 +11,10 @@ from amo.utils import chunked
|
|||
from devhub.tasks import convert_purified, flag_binary, get_preview_sizes
|
||||
from market.tasks import check_paypal, check_paypal_multiple
|
||||
|
||||
from mkt.webapps.tasks import (add_uuids, dump_apps, fix_missing_icons,
|
||||
update_developer_name, update_features,
|
||||
update_manifests, update_supported_locales,
|
||||
zip_apps)
|
||||
from mkt.webapps.tasks import (add_uuids, collapse_summary, dump_apps,
|
||||
fix_missing_icons, update_developer_name,
|
||||
update_features, update_manifests,
|
||||
update_supported_locales, zip_apps)
|
||||
|
||||
|
||||
tasks = {
|
||||
|
@ -70,6 +70,10 @@ tasks = {
|
|||
amo.STATUS_PUBLIC,
|
||||
amo.STATUS_PUBLIC_WAITING],
|
||||
disabled_by_user=False)]},
|
||||
'collapse_summary': {'method': collapse_summary,
|
||||
'qs': [Q(type=amo.ADDON_WEBAPP,
|
||||
disabled_by_user=False,
|
||||
summary__isnull=False)]},
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -266,7 +266,7 @@ class WebAppParser(object):
|
|||
'type': amo.ADDON_WEBAPP,
|
||||
'name': {default_locale: data['name']},
|
||||
'developer_name': developer_name,
|
||||
'summary': self.trans_all_locales(localized_descr),
|
||||
'description': self.trans_all_locales(localized_descr),
|
||||
'version': data.get('version', '1.0'),
|
||||
'default_locale': default_locale,
|
||||
'origin': data.get('origin')}
|
||||
|
|
|
@ -128,7 +128,7 @@ class AppResource(CORSResource, MarketplaceModelResource):
|
|||
queryset = Webapp.objects.all() # Gets overriden in dispatch.
|
||||
fields = ['categories', 'description', 'device_types', 'homepage',
|
||||
'id', 'name', 'payment_account', 'premium_type',
|
||||
'status', 'summary', 'support_email', 'support_url']
|
||||
'status', 'support_email', 'support_url']
|
||||
list_allowed_methods = ['get', 'post']
|
||||
detail_allowed_methods = ['get', 'put', 'delete']
|
||||
always_return_data = True
|
||||
|
@ -406,9 +406,11 @@ class StatusResource(MarketplaceModelResource):
|
|||
def hydrate_status(self, bundle):
|
||||
return amo.STATUS_CHOICES_API_LOOKUP[int(bundle.data['status'])]
|
||||
|
||||
|
||||
class CategorySerializer(ModelSerializer):
|
||||
name = CharField('name')
|
||||
resource_uri = HyperlinkedIdentityField(view_name='app-category-detail')
|
||||
|
||||
class Meta:
|
||||
model = Category
|
||||
fields = ('name', 'id', 'resource_uri', 'slug')
|
||||
|
|
|
@ -13,16 +13,13 @@ from addons.models import (Addon, AddonDeviceType, AddonUpsell,
|
|||
AddonUser, Category, Preview)
|
||||
from amo.tests import AMOPaths, app_factory
|
||||
from files.models import FileUpload
|
||||
from market.models import AddonPremium, Price, PriceCurrency
|
||||
from market.models import Price, PriceCurrency
|
||||
from users.models import UserProfile
|
||||
|
||||
from mkt.api.tests.test_oauth import BaseOAuth, OAuthClient, RestOAuth, get_absolute_url
|
||||
from mkt.api.base import get_url, list_url
|
||||
from mkt.api.models import Access, generate
|
||||
from mkt.api.tests.test_oauth import BaseOAuth, OAuthClient, RestOAuth
|
||||
from mkt.constants import APP_IMAGE_SIZES, carriers, regions
|
||||
from mkt.developers.models import (AddonPaymentAccount, PaymentAccount,
|
||||
SolitudeSeller)
|
||||
from mkt.developers.tests.test_api import payment_data
|
||||
from mkt.site.fixtures import fixture
|
||||
from mkt.webapps.models import ContentRating, ImageAsset, Webapp
|
||||
from reviews.models import Review
|
||||
|
@ -427,7 +424,7 @@ class TestAppCreateHandler(CreateHandler, AMOPaths):
|
|||
'homepage': 'http://www.whatever.com',
|
||||
'name': 'mozball',
|
||||
'categories': [c.pk for c in self.categories],
|
||||
'summary': 'wat...',
|
||||
'description': 'wat...',
|
||||
'premium_type': 'free',
|
||||
'regions': ['us'],
|
||||
'device_types': amo.DEVICE_TYPES.keys()}
|
||||
|
@ -749,14 +746,14 @@ class TestListHandler(CreateHandler, AMOPaths):
|
|||
eq_(pks, set([str(app.pk) for app in apps]))
|
||||
|
||||
def test_lang(self):
|
||||
app = app_factory(summary={'fr': 'Le blah', 'en-US': 'Blah'})
|
||||
app = app_factory(description={'fr': 'Le blah', 'en-US': 'Blah'})
|
||||
url = get_url('app', app.pk)
|
||||
|
||||
res = self.client.get(url, HTTP_ACCEPT_LANGUAGE='en-US')
|
||||
eq_(json.loads(res.content)['summary'], 'Blah')
|
||||
eq_(json.loads(res.content)['description'], 'Blah')
|
||||
|
||||
res = self.client.get(url, HTTP_ACCEPT_LANGUAGE='fr')
|
||||
eq_(json.loads(res.content)['summary'], 'Le blah')
|
||||
eq_(json.loads(res.content)['description'], 'Le blah')
|
||||
|
||||
|
||||
class TestAppStatusHandler(CreateHandler, AMOPaths):
|
||||
|
|
|
@ -482,12 +482,14 @@ class AppFormBasic(addons.forms.AddonFormBase):
|
|||
"""Form to edit basic app info."""
|
||||
slug = forms.CharField(max_length=30, widget=forms.TextInput)
|
||||
manifest_url = forms.URLField(verify_exists=False)
|
||||
summary = TransField(widget=TransTextarea(attrs={'rows': 4}),
|
||||
max_length=1024)
|
||||
description = TransField(required=True,
|
||||
label=_lazy(u'Provide a detailed description of your app'),
|
||||
help_text=_lazy(u'This description will appear on the details page.'),
|
||||
widget=TransTextarea)
|
||||
|
||||
class Meta:
|
||||
model = Addon
|
||||
fields = ('slug', 'manifest_url', 'summary')
|
||||
fields = ('slug', 'manifest_url', 'description')
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
# Force the form to use app_slug if this is a webapp. We want to keep
|
||||
|
@ -546,10 +548,6 @@ class AppFormBasic(addons.forms.AddonFormBase):
|
|||
|
||||
|
||||
class AppFormDetails(addons.forms.AddonFormBase):
|
||||
description = TransField(required=False,
|
||||
label=_lazy(u'Provide a more detailed description of your app'),
|
||||
help_text=_lazy(u'This description will appear on the details page.'),
|
||||
widget=TransTextarea)
|
||||
default_locale = forms.TypedChoiceField(required=False,
|
||||
choices=Addon.LOCALES)
|
||||
homepage = TransField.adapt(forms.URLField)(required=False,
|
||||
|
@ -559,12 +557,11 @@ class AppFormDetails(addons.forms.AddonFormBase):
|
|||
|
||||
class Meta:
|
||||
model = Addon
|
||||
fields = ('description', 'default_locale', 'homepage',
|
||||
'privacy_policy')
|
||||
fields = ('default_locale', 'homepage', 'privacy_policy')
|
||||
|
||||
def clean(self):
|
||||
# Make sure we have the required translations in the new locale.
|
||||
required = ['name', 'summary', 'description']
|
||||
required = ['name', 'description']
|
||||
data = self.cleaned_data
|
||||
if not self.errors and 'default_locale' in self.changed_data:
|
||||
fields = dict((k, getattr(self.instance, k + '_id'))
|
||||
|
@ -575,13 +572,10 @@ class AppFormDetails(addons.forms.AddonFormBase):
|
|||
localized_string__isnull=False)
|
||||
.values_list('id', flat=True))
|
||||
missing = [k for k, v in fields.items() if v not in qs]
|
||||
# They might be setting description right now.
|
||||
if 'description' in missing and locale in data['description']:
|
||||
missing.remove('description')
|
||||
if missing:
|
||||
raise forms.ValidationError(
|
||||
_('Before changing your default locale you must have a '
|
||||
'name, summary, and description in that locale. '
|
||||
'name and description in that locale. '
|
||||
'You are missing %s.') % ', '.join(map(repr, missing)))
|
||||
return data
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{% from 'developers/includes/macros.html' import empty_unless, required, select_cats, tip, trans_readonly %}
|
||||
{% from 'developers/includes/macros.html' import empty_unless, required, select_cats, some_html_tip, tip, trans_readonly %}
|
||||
{% set req_if_edit = required() if editable %}
|
||||
<form method="post" action="{{ url('mkt.developers.apps.section', valid_slug, 'basic', 'edit') }}"
|
||||
id="addon-edit-basic"
|
||||
|
@ -99,33 +99,26 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label data-for="summary">
|
||||
{{ _("Summary") }}
|
||||
{% if webapp %}
|
||||
{# TODO(apps): Finalize copy. #}
|
||||
{{ tip(None,
|
||||
_("A short explanation of your app's basic
|
||||
functionality that is displayed in search and browse
|
||||
listings, as well as at the top of your app's
|
||||
details page.")) }}
|
||||
{% else %}
|
||||
{{ tip(None,
|
||||
_("A short explanation of your add-on's basic
|
||||
functionality that is displayed in search and browse
|
||||
listings, as well as at the top of your add-on's
|
||||
details page.")) }}
|
||||
{% endif %}
|
||||
<label data-for="description">
|
||||
{{ _('Description') }}
|
||||
{{ tip(None,
|
||||
_("An explanation of features, functionality, and
|
||||
other relevant information. This field is displayed
|
||||
on the app's details page.")) }}
|
||||
</label>
|
||||
{{ req_if_edit }}
|
||||
</th>
|
||||
<td>
|
||||
{% if editable %}
|
||||
{{ form.summary }}
|
||||
{{ form.summary.errors }}
|
||||
<div class="char-count"
|
||||
data-for-startswith="{{ form.summary.auto_id }}_"
|
||||
data-maxlength="{{ form.summary.field.max_length }}"></div>
|
||||
{{ form.description }}
|
||||
{{ form.description.errors }}
|
||||
{{ some_html_tip() }}
|
||||
{% else %}
|
||||
{{ addon|all_locales('summary') }}
|
||||
{% call empty_unless(addon.description) %}
|
||||
<div id="addon-description" class="prose">
|
||||
{{ addon|all_locales('description', nl2br=True) }}
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -12,31 +12,6 @@
|
|||
{% if editable %}{{ form.non_field_errors() }}{% endif %}
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<label data-for="description">
|
||||
{{ _('Description') }}
|
||||
{{ tip(None,
|
||||
_("A longer explanation of features, functionality, and
|
||||
other relevant information. This field is displayed
|
||||
only on the app's details page.")) }}
|
||||
</label>
|
||||
{{ req_if_edit }}
|
||||
</th>
|
||||
<td>
|
||||
{% if editable %}
|
||||
{{ form.description }}
|
||||
{{ form.description.errors }}
|
||||
{{ some_html_tip() }}
|
||||
{% else %}
|
||||
{% call empty_unless(addon.description) %}
|
||||
<div id="addon-description" class="prose">
|
||||
{{ addon|all_locales('description', nl2br=True) }}
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
{{ tip(_("Default Locale"),
|
||||
|
|
|
@ -439,14 +439,15 @@ class TestAppFormBasic(amo.tests.TestCase):
|
|||
self.data = {
|
||||
'slug': 'yolo',
|
||||
'manifest_url': 'https://omg.org/yes.webapp',
|
||||
'summary': 'You Only Live Once'
|
||||
'description': 'You Only Live Once'
|
||||
}
|
||||
self.request = mock.Mock()
|
||||
self.request.groups = ()
|
||||
|
||||
def post(self):
|
||||
self.form = forms.AppFormBasic(self.data,
|
||||
instance=Webapp.objects.create(app_slug='yolo'), request=self.request)
|
||||
self.form = forms.AppFormBasic(
|
||||
self.data, instance=Webapp.objects.create(app_slug='yolo'),
|
||||
request=self.request)
|
||||
|
||||
def test_success(self):
|
||||
self.post()
|
||||
|
|
|
@ -22,7 +22,6 @@ from amo.helpers import absolutify
|
|||
from amo.tests import assert_required, formset, initial
|
||||
from amo.tests.test_helpers import get_image_path
|
||||
from amo.urlresolvers import reverse
|
||||
from addons.forms import AddonFormBasic
|
||||
from addons.models import (Addon, AddonCategory, AddonDeviceType, AddonUser,
|
||||
Category)
|
||||
from constants.applications import DEVICE_TYPES
|
||||
|
@ -86,7 +85,7 @@ class TestEdit(amo.tests.TestCase):
|
|||
def get_dict(self, **kw):
|
||||
fs = formset(self.cat_initial, initial_count=1)
|
||||
result = {'name': 'new name', 'slug': 'test_slug',
|
||||
'summary': 'new summary'}
|
||||
'description': 'new description'}
|
||||
result.update(**kw)
|
||||
result.update(fs)
|
||||
return result
|
||||
|
@ -189,7 +188,7 @@ class TestEditBasic(TestEdit):
|
|||
|
||||
def get_dict(self, **kw):
|
||||
result = {'device_types': self.dtype, 'slug': 'test_slug',
|
||||
'summary': 'new summary',
|
||||
'description': 'New description with <em>html</em>!',
|
||||
'manifest_url': self.get_webapp().manifest_url,
|
||||
'categories': [self.cat.id]}
|
||||
result.update(**kw)
|
||||
|
@ -232,6 +231,15 @@ class TestEditBasic(TestEdit):
|
|||
eq_(webapp.slug, self.webapp.slug)
|
||||
eq_(webapp.app_slug, self.webapp.app_slug)
|
||||
|
||||
def test_edit_xss(self):
|
||||
self.webapp.description = ("This\n<b>IS</b>"
|
||||
"<script>alert('awesome')</script>")
|
||||
self.webapp.save()
|
||||
r = self.client.get(self.url)
|
||||
eq_(pq(r.content)('#addon-description span[lang]').html(),
|
||||
"This<br/><b>IS</b><script>alert('awesome')"
|
||||
'</script>')
|
||||
|
||||
@mock.patch('devhub.tasks.urllib2.urlopen')
|
||||
@mock.patch('devhub.tasks.validator')
|
||||
def test_view_manifest_url_default(self, mock_urlopen, validator):
|
||||
|
@ -386,18 +394,11 @@ class TestEditBasic(TestEdit):
|
|||
eq_(r.context['cat_form'].errors['categories'],
|
||||
['You can have only 2 categories.'])
|
||||
|
||||
def test_edit_summary_max_length(self):
|
||||
r = self.client.post(self.edit_url, self.get_dict(summary='x' * 1025))
|
||||
eq_(list(r.context['form'].errors['summary']),
|
||||
[('en-us',
|
||||
'Ensure this value has at most 1024 characters (it has 1025).')])
|
||||
|
||||
def test_edit_check_description(self):
|
||||
# Make sure bug 629779 doesn't return.
|
||||
r = self.client.post(self.edit_url, self.get_dict())
|
||||
eq_(r.status_code, 200)
|
||||
|
||||
eq_(self.get_webapp().description, self.webapp.description)
|
||||
eq_(self.get_webapp().description, self.get_dict()['description'])
|
||||
|
||||
def test_edit_slug_valid(self):
|
||||
old_edit = self.edit_url
|
||||
|
@ -406,22 +407,6 @@ class TestEditBasic(TestEdit):
|
|||
doc = pq(r.content)
|
||||
assert doc('form').attr('action') != old_edit
|
||||
|
||||
def test_edit_summary_escaping(self):
|
||||
data = self.get_dict(summary='<b>oh my</b>')
|
||||
r = self.client.post(self.edit_url, data)
|
||||
eq_(r.status_code, 200)
|
||||
|
||||
# Fetch the page so the LinkifiedTranslation gets in cache.
|
||||
webapp = self.get_webapp()
|
||||
r = self.client.get(webapp.get_dev_url('edit'))
|
||||
eq_(pq(r.content)('[data-name=summary]').html().strip(),
|
||||
'<span lang="en-us"><b>oh my</b></span>')
|
||||
|
||||
# Now make sure we don't have escaped content in the rendered form.
|
||||
form = AddonFormBasic(instance=webapp, request=object())
|
||||
eq_(pq('<body>%s</body>' % form['summary'])('[lang="en-us"]').html(),
|
||||
data['summary'])
|
||||
|
||||
def test_edit_as_developer(self):
|
||||
self.client.login(username='regular@mozilla.com', password='password')
|
||||
data = self.get_dict()
|
||||
|
@ -436,7 +421,7 @@ class TestEditBasic(TestEdit):
|
|||
webapp = self.get_webapp()
|
||||
|
||||
eq_(unicode(webapp.app_slug), data['slug'])
|
||||
eq_(unicode(webapp.summary), data['summary'])
|
||||
eq_(unicode(webapp.description), data['description'])
|
||||
|
||||
def get_l10n_urls(self):
|
||||
return [self.webapp.get_dev_url(p) for p in ('edit', 'profile')]
|
||||
|
@ -1003,8 +988,7 @@ class TestEditDetails(TestEdit):
|
|||
self.edit_url = self.get_url('details', edit=True)
|
||||
|
||||
def get_dict(self, **kw):
|
||||
data = dict(description='New description with <em>html</em>!',
|
||||
default_locale='en-US',
|
||||
data = dict(default_locale='en-US',
|
||||
homepage='http://twitter.com/fligtarsmom',
|
||||
privacy_policy="fligtar's mom does <em>not</em> share "
|
||||
"your data with third parties.")
|
||||
|
@ -1020,19 +1004,6 @@ class TestEditDetails(TestEdit):
|
|||
self.assertNoFormErrors(r)
|
||||
self.compare(data)
|
||||
|
||||
def test_edit_xss(self):
|
||||
"""
|
||||
Let's try to put xss in our description, and safe html, and verify
|
||||
that we are playing safe.
|
||||
"""
|
||||
self.webapp.description = ("This\n<b>IS</b>"
|
||||
"<script>alert('awesome')</script>")
|
||||
self.webapp.save()
|
||||
r = self.client.get(self.url)
|
||||
eq_(pq(r.content)('#addon-description span[lang]').html(),
|
||||
"This<br/><b>IS</b><script>alert('awesome')"
|
||||
'</script>')
|
||||
|
||||
def test_privacy_policy_xss(self):
|
||||
self.webapp.privacy_policy = ("We\n<b>own</b>your"
|
||||
"<script>alert('soul')</script>")
|
||||
|
@ -1044,8 +1015,7 @@ class TestEditDetails(TestEdit):
|
|||
|
||||
def test_edit_exclude_optional_fields(self):
|
||||
data = self.get_dict()
|
||||
data.update(description='New description with <em>html</em>!',
|
||||
default_locale='en-US', homepage='',
|
||||
data.update(default_locale='en-US', homepage='',
|
||||
privacy_policy='we sell your data to everyone')
|
||||
|
||||
r = self.client.post(self.edit_url, data)
|
||||
|
@ -1053,16 +1023,14 @@ class TestEditDetails(TestEdit):
|
|||
self.compare(data)
|
||||
|
||||
def test_edit_default_locale_required_trans(self):
|
||||
# name, summary, and description are required in the new locale.
|
||||
# name and description are required in the new locale.
|
||||
data = self.get_dict()
|
||||
data.update(description='bullocks',
|
||||
homepage='http://omg.org/yes',
|
||||
privacy_policy='your data is delicious')
|
||||
# TODO: description should get fixed up with the form.
|
||||
fields = ['description', 'name', 'summary']
|
||||
error = ('Before changing your default locale you must have a name, '
|
||||
'summary, and description in that locale. '
|
||||
'You are missing %s.')
|
||||
fields = ['name', 'description']
|
||||
error = ('Before changing your default locale you must have a name '
|
||||
'and description in that locale. You are missing %s.')
|
||||
missing = lambda f: error % ', '.join(map(repr, f))
|
||||
|
||||
data.update(default_locale='pt-BR')
|
||||
|
@ -1076,18 +1044,6 @@ class TestEditDetails(TestEdit):
|
|||
r = self.client.post(self.edit_url, data)
|
||||
self.assertFormError(r, 'form', None, missing(fields))
|
||||
|
||||
# Now we have a summary.
|
||||
self.webapp.summary = {'pt-BR': 'pt-BR summary'}
|
||||
self.webapp.save()
|
||||
fields.remove('summary')
|
||||
r = self.client.post(self.edit_url, data)
|
||||
self.assertFormError(r, 'form', None, missing(fields))
|
||||
|
||||
# Now we're sending an pt-BR description with the form.
|
||||
data['description_pt-BR'] = 'pt-BR description'
|
||||
r = self.client.post(self.edit_url, data)
|
||||
self.assertNoFormErrors(r)
|
||||
|
||||
def test_edit_default_locale_frontend_error(self):
|
||||
data = self.get_dict()
|
||||
data.update(description='xx', homepage='http://google.com',
|
||||
|
|
|
@ -52,11 +52,11 @@ class TestWebApps(amo.tests.TestCase, amo.tests.AMOPaths):
|
|||
wp = WebAppParser().parse(self.webapp_path)
|
||||
eq_(wp['guid'], None)
|
||||
eq_(wp['type'], amo.ADDON_WEBAPP)
|
||||
eq_(wp['summary']['en-US'], u'Exciting Open Web development action!')
|
||||
eq_(wp['description']['en-US'], u'Exciting Open Web development action!')
|
||||
# UTF-8 byte string decoded to unicode.
|
||||
eq_(wp['summary']['es'],
|
||||
eq_(wp['description']['es'],
|
||||
u'\xa1Acci\xf3n abierta emocionante del desarrollo del Web!')
|
||||
eq_(wp['summary']['it'],
|
||||
eq_(wp['description']['it'],
|
||||
u'Azione aperta emozionante di sviluppo di fotoricettore!')
|
||||
eq_(wp['version'], '1.0')
|
||||
eq_(wp['default_locale'], 'en-US')
|
||||
|
@ -66,10 +66,10 @@ class TestWebApps(amo.tests.TestCase, amo.tests.AMOPaths):
|
|||
eq_(wp['guid'], None)
|
||||
eq_(wp['type'], amo.ADDON_WEBAPP)
|
||||
eq_(wp['name']['en-US'], u'Packaged MozillaBall ょ')
|
||||
eq_(wp['summary']['en-US'], u'Exciting Open Web development action!')
|
||||
eq_(wp['summary']['es'],
|
||||
eq_(wp['description']['en-US'], u'Exciting Open Web development action!')
|
||||
eq_(wp['description']['es'],
|
||||
u'¡Acción abierta emocionante del desarrollo del Web!')
|
||||
eq_(wp['summary']['it'],
|
||||
eq_(wp['description']['it'],
|
||||
u'Azione aperta emozionante di sviluppo di fotoricettore!')
|
||||
eq_(wp['version'], '1.0')
|
||||
eq_(wp['default_locale'], 'en-US')
|
||||
|
@ -79,9 +79,9 @@ class TestWebApps(amo.tests.TestCase, amo.tests.AMOPaths):
|
|||
eq_(wp['guid'], None)
|
||||
eq_(wp['type'], amo.ADDON_WEBAPP)
|
||||
eq_(wp['name']['en-US'], u'Packaged MozBOM ょ')
|
||||
eq_(wp['summary']['en-US'], u'Exciting BOM action!')
|
||||
eq_(wp['summary']['es'], u'¡Acción BOM!')
|
||||
eq_(wp['summary']['it'], u'Azione BOM!')
|
||||
eq_(wp['description']['en-US'], u'Exciting BOM action!')
|
||||
eq_(wp['description']['es'], u'¡Acción BOM!')
|
||||
eq_(wp['description']['it'], u'Azione BOM!')
|
||||
eq_(wp['version'], '1.0')
|
||||
eq_(wp['default_locale'], 'en-US')
|
||||
|
||||
|
@ -95,15 +95,15 @@ class TestWebApps(amo.tests.TestCase, amo.tests.AMOPaths):
|
|||
|
||||
def test_no_locales(self):
|
||||
wp = WebAppParser().parse(self.webapp(dict(name='foo', version='1.0',
|
||||
description='summary',
|
||||
description='description',
|
||||
developer=dict(name='bar'))))
|
||||
eq_(wp['summary']['en-US'], u'summary')
|
||||
eq_(wp['description']['en-US'], u'description')
|
||||
|
||||
def test_no_description(self):
|
||||
wp = WebAppParser().parse(self.webapp(dict(name='foo',
|
||||
version='1.0',
|
||||
developer=dict(name='bar'))))
|
||||
eq_(wp['summary'], {})
|
||||
eq_(wp['description'], {})
|
||||
|
||||
def test_syntax_error(self):
|
||||
with self.assertRaises(forms.ValidationError) as exc:
|
||||
|
|
|
@ -102,7 +102,6 @@ class TestApi(BaseOAuth, ESTestCase):
|
|||
eq_(obj['ratings'], {'average': 0.0, 'count': 0})
|
||||
eq_(obj['resource_uri'], '/api/v1/apps/app/337141/')
|
||||
eq_(obj['slug'], self.webapp.app_slug)
|
||||
eq_(obj['summary'], u'')
|
||||
eq_(obj['supported_locales'], ['en-US', 'es', 'pt-BR'])
|
||||
|
||||
# These only exists if requested by a reviewer.
|
||||
|
|
|
@ -56,17 +56,12 @@ def name_query(q):
|
|||
"""
|
||||
more = {
|
||||
'description__text': {'query': q, 'boost': 0.8, 'type': 'phrase'},
|
||||
# TODO: Remove summary when bug 862603 lands.
|
||||
'summary__text': {'query': q, 'boost': 0.3, 'type': 'phrase'},
|
||||
}
|
||||
|
||||
analyzer = _get_locale_analyzer()
|
||||
if analyzer:
|
||||
more['description_%s__text' % analyzer] = {
|
||||
'query': q, 'boost': 0.6, 'type': 'phrase', 'analyzer': analyzer}
|
||||
# TODO: Remove summary when bug 862603 lands.
|
||||
more['summary_%s__text' % analyzer] = {
|
||||
'query': q, 'boost': 0.1, 'type': 'phrase', 'analyzer': analyzer}
|
||||
|
||||
return dict(more, **name_only_query(q))
|
||||
|
||||
|
|
|
@ -335,13 +335,8 @@ class AppDetailsBasicForm(TranslationFormMixin, happyforms.ModelForm):
|
|||
|
||||
app_slug = forms.CharField(max_length=30,
|
||||
widget=forms.TextInput(attrs={'class': 'm'}))
|
||||
summary = TransField(max_length=1024,
|
||||
label=_lazy(u"Brief Summary:"),
|
||||
help_text=_lazy(u'This summary will be shown in listings and '
|
||||
'searches.'),
|
||||
widget=TransTextarea(attrs={'rows': 2, 'class': 'full'}))
|
||||
description = TransField(required=False,
|
||||
label=_lazy(u'Additional Information:'),
|
||||
description = TransField(required=True,
|
||||
label=_lazy(u'Description:'),
|
||||
help_text=_lazy(u'This description will appear on the details page.'),
|
||||
widget=TransTextarea(attrs={'rows': 4}))
|
||||
privacy_policy = TransField(widget=TransTextarea(attrs={'rows': 6}),
|
||||
|
@ -384,8 +379,8 @@ class AppDetailsBasicForm(TranslationFormMixin, happyforms.ModelForm):
|
|||
|
||||
class Meta:
|
||||
model = Addon
|
||||
fields = ('app_slug', 'summary', 'description', 'privacy_policy',
|
||||
'homepage', 'support_url', 'support_email')
|
||||
fields = ('app_slug', 'description', 'privacy_policy', 'homepage',
|
||||
'support_url', 'support_email')
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
self.request = kw.pop('request')
|
||||
|
|
|
@ -89,9 +89,7 @@
|
|||
<ul class="icon-errorlist errorlist"></ul>
|
||||
</header>
|
||||
|
||||
{{ form_field(form_basic.summary, hint=True,
|
||||
cc_startswith=form_basic.summary.auto_id + '_',
|
||||
cc_maxlength=form_basic.summary.field.max_length) }}
|
||||
{{ form_field(form_basic.description, hint=True, some_html=True) }}
|
||||
|
||||
<div id="addon-categories-edit"
|
||||
data-max-categories="{{ form_cats.max_categories() }}">
|
||||
|
@ -100,7 +98,6 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
|
||||
{{ form_field(form_basic.description, hint=True, some_html=True) }}
|
||||
{{ form_field(form_basic.privacy_policy, hint=True) }}
|
||||
|
||||
{# TODO: Remove since Privacy Policy is required.
|
||||
|
|
|
@ -334,10 +334,10 @@ class TestCreateWebApp(BaseWebAppTest):
|
|||
eq_(unicode(addon.name), u'MozillaBall ょ')
|
||||
eq_(addon.slug, 'app-%s' % addon.id)
|
||||
eq_(addon.app_slug, u'mozillaball-ょ')
|
||||
eq_(addon.summary, u'Exciting Open Web development action!')
|
||||
eq_(addon.description, u'Exciting Open Web development action!')
|
||||
eq_(addon.manifest_url, u'http://allizom.org/mozball.webapp')
|
||||
eq_(addon.app_domain, u'http://allizom.org')
|
||||
eq_(Translation.objects.get(id=addon.summary.id, locale='it'),
|
||||
eq_(Translation.objects.get(id=addon.description.id, locale='it'),
|
||||
u'Azione aperta emozionante di sviluppo di fotoricettore!')
|
||||
eq_(addon.current_version.developer_name, 'Mozilla Labs')
|
||||
|
||||
|
@ -520,10 +520,10 @@ class TestCreatePackagedApp(BasePackagedAppTest):
|
|||
eq_(unicode(addon.name), u'Packaged MozillaBall ょ')
|
||||
eq_(addon.slug, 'app-%s' % addon.id)
|
||||
eq_(addon.app_slug, u'packaged-mozillaball-ょ')
|
||||
eq_(addon.summary, u'Exciting Open Web development action!')
|
||||
eq_(addon.description, u'Exciting Open Web development action!')
|
||||
eq_(addon.manifest_url, None)
|
||||
eq_(addon.app_domain, 'app://hy.fr')
|
||||
eq_(Translation.objects.get(id=addon.summary.id, locale='it'),
|
||||
eq_(Translation.objects.get(id=addon.description.id, locale='it'),
|
||||
u'Azione aperta emozionante di sviluppo di fotoricettore!')
|
||||
eq_(addon.current_version.developer_name, 'Mozilla Labs')
|
||||
|
||||
|
@ -638,7 +638,6 @@ class TestDetails(TestSubmit):
|
|||
def get_dict(self, **kw):
|
||||
data = {
|
||||
'app_slug': 'testname',
|
||||
'summary': 'Hello!',
|
||||
'description': 'desc',
|
||||
'privacy_policy': 'XXX <script>alert("xss")</script>',
|
||||
'homepage': 'http://www.goodreads.com/user/show/7595895-krupa',
|
||||
|
@ -666,7 +665,6 @@ class TestDetails(TestSubmit):
|
|||
# Build a dictionary of expected results.
|
||||
expected_data = {
|
||||
'app_slug': 'testname',
|
||||
'summary': 'Hello!',
|
||||
'description': 'desc',
|
||||
'privacy_policy': 'XXX <script>alert("xss")</script>',
|
||||
'uses_flash': True,
|
||||
|
@ -836,25 +834,13 @@ class TestDetails(TestSubmit):
|
|||
self.assertFormError(r, 'form_basic', 'app_slug',
|
||||
'This field is required.')
|
||||
|
||||
def test_summary_required(self):
|
||||
def test_description_required(self):
|
||||
self._step()
|
||||
r = self.client.post(self.url, self.get_dict(summary=''))
|
||||
r = self.client.post(self.url, self.get_dict(description=''))
|
||||
eq_(r.status_code, 200)
|
||||
self.assertFormError(r, 'form_basic', 'summary',
|
||||
self.assertFormError(r, 'form_basic', 'description',
|
||||
'This field is required.')
|
||||
|
||||
def test_summary_length(self):
|
||||
self._step()
|
||||
r = self.client.post(self.url, self.get_dict(summary='a' * 1025))
|
||||
eq_(r.status_code, 200)
|
||||
self.assertFormError(r, 'form_basic', 'summary',
|
||||
'Ensure this value has at most 1024 characters (it has 1025).')
|
||||
|
||||
def test_description_optional(self):
|
||||
self._step()
|
||||
r = self.client.post(self.url, self.get_dict(description=None))
|
||||
self.assertNoFormErrors(r)
|
||||
|
||||
def test_privacy_policy_required(self):
|
||||
self._step()
|
||||
r = self.client.post(self.url, self.get_dict(privacy_policy=None))
|
||||
|
|
|
@ -135,8 +135,8 @@ def manifest(request):
|
|||
@dev_required
|
||||
@submit_step('details')
|
||||
def details(request, addon_id, addon):
|
||||
# Name, Slug, Summary, Description, Privacy Policy,
|
||||
# Homepage URL, Support URL, Support Email.
|
||||
# Name, Slug, Description, Privacy Policy, Homepage URL, Support URL,
|
||||
# Support Email.
|
||||
form_basic = AppDetailsBasicForm(request.POST or None, instance=addon,
|
||||
request=request)
|
||||
form_cats = CategoryForm(request.POST or None, product=addon,
|
||||
|
@ -148,7 +148,7 @@ def details(request, addon_id, addon):
|
|||
|
||||
# For empty webapp-locale (or no-locale) fields that have
|
||||
# form-locale values, duplicate them to satisfy the requirement.
|
||||
form_locale = request.COOKIES.get("current_locale", "")
|
||||
form_locale = request.COOKIES.get('current_locale', '')
|
||||
app_locale = to_language(addon.default_locale)
|
||||
for name, value in request.POST.items():
|
||||
if value:
|
||||
|
|
|
@ -1012,8 +1012,6 @@ class WebappIndexer(MappingType, Indexable):
|
|||
},
|
||||
'region_exclusions': {'type': 'short'},
|
||||
'status': {'type': 'byte'},
|
||||
# TODO: Remove when bug 862603 lands.
|
||||
'summary': {'type': 'string', 'analyzer': 'snowball'},
|
||||
'support_email': {'type': 'string',
|
||||
'index': 'not_analyzed'},
|
||||
'support_url': {'type': 'string',
|
||||
|
@ -1050,8 +1048,6 @@ class WebappIndexer(MappingType, Indexable):
|
|||
_locale_field_mapping('name', analyzer))
|
||||
mapping[doc_type]['properties'].update(
|
||||
_locale_field_mapping('description', analyzer))
|
||||
mapping[doc_type]['properties'].update(
|
||||
_locale_field_mapping('summary', analyzer))
|
||||
|
||||
# TODO: reviewer flags (bug 848446)
|
||||
|
||||
|
@ -1141,8 +1137,6 @@ class WebappIndexer(MappingType, Indexable):
|
|||
}
|
||||
d['region_exclusions'] = list(
|
||||
obj.addonexcludedregion.values_list('region', flat=True))
|
||||
# TODO: Remove when bug 862603 lands.
|
||||
d['summary'] = list(set(s for _, s in translations[obj.summary_id]))
|
||||
d['support_email'] = (unicode(obj.support_email)
|
||||
if obj.support_email else None)
|
||||
d['support_url'] = (unicode(obj.support_url)
|
||||
|
@ -1191,10 +1185,6 @@ class WebappIndexer(MappingType, Indexable):
|
|||
set(string for locale, string
|
||||
in translations[obj.description_id]
|
||||
if locale.lower() in languages))
|
||||
d['summary_' + analyzer] = list(
|
||||
set(string for locale, string
|
||||
in translations[obj.summary_id]
|
||||
if locale.lower() in languages))
|
||||
|
||||
return d
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ from editors.models import RereviewQueue
|
|||
from files.models import FileUpload
|
||||
from files.utils import WebAppParser
|
||||
from lib.es.utils import get_indices
|
||||
from translations.models import delete_translation, Translation
|
||||
from users.utils import get_task_user
|
||||
|
||||
from mkt.constants.regions import WORLDWIDE
|
||||
|
@ -33,6 +34,7 @@ from mkt.developers.tasks import (fetch_icon, _fetch_manifest, run_validator,
|
|||
from mkt.webapps.models import Webapp, WebappIndexer
|
||||
from mkt.webapps.utils import get_locale_properties
|
||||
|
||||
|
||||
task_log = logging.getLogger('z.task')
|
||||
|
||||
|
||||
|
@ -182,9 +184,9 @@ def _update_manifest(id, check_hash, failed_fetches):
|
|||
old.get('name'), new.get('name')))
|
||||
|
||||
new_version = webapp.versions.latest()
|
||||
# Compare developer_name between old and new version using the property that
|
||||
# fallbacks to the author name instead of using the db field directly. This
|
||||
# allows us to avoid forcing a re-review on old apps which didn't have
|
||||
# Compare developer_name between old and new version using the property
|
||||
# that fallbacks to the author name instead of using the db field directly.
|
||||
# This allows us to avoid forcing a re-review on old apps which didn't have
|
||||
# developer name in their manifest initially and upload a new version that
|
||||
# does, providing that it matches the original author name.
|
||||
if version.developer_name != new_version.developer_name:
|
||||
|
@ -465,3 +467,68 @@ def _fix_missing_icons(id):
|
|||
def fix_missing_icons(ids, **kw):
|
||||
for id in ids:
|
||||
_fix_missing_icons(id)
|
||||
|
||||
|
||||
# TODO: Remove the collapse_summary calls when bug 862603 is completed.
|
||||
@task
|
||||
@write
|
||||
def _collapse_summary(app):
|
||||
|
||||
task_log.info('[Webapp:%s] Collapsing summary.' % app.id)
|
||||
|
||||
# No prior summary so do nothing.
|
||||
if app.summary is None:
|
||||
return
|
||||
|
||||
# Handle no prior description.
|
||||
if app.description is None:
|
||||
# These should be translation ids and copy easily.
|
||||
app.description_id = app.summary_id
|
||||
app.summary_id = None
|
||||
app.save()
|
||||
task_log.info('[Webapp:%s] No description, copied translation %s.' % (
|
||||
app.id, app.description_id))
|
||||
return
|
||||
|
||||
# The other cases require looking at the localized strings in the
|
||||
# translation table in all locales.
|
||||
for summary in Translation.objects.filter(id=app.summary_id):
|
||||
try:
|
||||
descr = Translation.objects.get(id=app.description_id,
|
||||
locale=summary.locale)
|
||||
except Translation.DoesNotExist:
|
||||
# We have a summary in this locale but not a description.
|
||||
Translation.objects.create(
|
||||
id=app.description_id, locale=summary.locale,
|
||||
localized_string=summary.localized_string,
|
||||
localized_string_clean=summary.localized_string_clean)
|
||||
task_log.info('[Webapp:%s] Created description in locale %s with '
|
||||
'translation %s.' % (app.id, summary.locale,
|
||||
app.description_id))
|
||||
continue
|
||||
|
||||
# If summary is a truncated description, delete the summary.
|
||||
if descr.localized_string.startswith(summary.localized_string):
|
||||
task_log.info('[Webapp:%s] Description starts with summary for '
|
||||
'translation %s and locale %s.' % (
|
||||
app.id, summary.id, summary.locale))
|
||||
continue
|
||||
|
||||
# Otherwise, concat summary and description together.
|
||||
descr.localized_string = u'%s\n%s' % (
|
||||
summary.localized_string, descr.localized_string)
|
||||
descr.localized_string_clean = u'%s\n%s' % (
|
||||
summary.localized_string_clean, descr.localized_string_clean)
|
||||
descr.save()
|
||||
task_log.info('[Webapp:%s] Concatenated summary and description for '
|
||||
'translation %s and locale %s' % (app.id, descr.id,
|
||||
descr.locale))
|
||||
|
||||
delete_translation(app, 'summary')
|
||||
|
||||
|
||||
@task
|
||||
def collapse_summary(ids, **kw):
|
||||
for chunk in chunked(ids, 50):
|
||||
for app in Webapp.objects.filter(id__in=chunk):
|
||||
_collapse_summary.delay(app)
|
||||
|
|
|
@ -166,7 +166,6 @@ class TestESAppToDict(amo.tests.ESTestCase):
|
|||
},
|
||||
'slug': 'something-something',
|
||||
'status': 4,
|
||||
'summary': u'',
|
||||
'support_email': None,
|
||||
'support_url': None,
|
||||
'user': {
|
||||
|
|
|
@ -199,7 +199,6 @@ def es_app_to_dict(obj, region=None, profile=None):
|
|||
GenericObject({'pk': obj._id})
|
||||
),
|
||||
'public_stats': obj.has_public_stats,
|
||||
'summary': get_attr_lang(src, 'summary', obj.default_locale),
|
||||
'supported_locales': src.get('supported_locales', ''),
|
||||
'slug': obj.app_slug,
|
||||
})
|
||||
|
|
Загрузка…
Ссылка в новой задаче