Skip to content
This repository has been archived by the owner on Mar 15, 2018. It is now read-only.

Commit

Permalink
Allow developers to set offline status (bug 1049770)
Browse files Browse the repository at this point in the history
Fixes #2741
  • Loading branch information
mstriemer committed Nov 21, 2014
1 parent 0bca85d commit bf7f64e
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 23 deletions.
2 changes: 2 additions & 0 deletions migrations/873-add-is-offline-webapps.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE `addons`
ADD COLUMN `is_offline` tinyint(1) unsigned NOT NULL DEFAULT 0;
3 changes: 2 additions & 1 deletion mkt/developers/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1107,10 +1107,11 @@ def __init__(self, locale, *args, **kw):

class AppFormTechnical(AddonFormBase):
flash = forms.BooleanField(required=False)
is_offline = forms.BooleanField(required=False)

class Meta:
model = Webapp
fields = ('public_stats',)
fields = ('is_offline', 'public_stats',)

def __init__(self, *args, **kw):
super(AppFormTechnical, self).__init__(*args, **kw)
Expand Down
19 changes: 18 additions & 1 deletion mkt/developers/templates/developers/apps/edit/technical.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ <h2>
<tbody>
<tr>
<th>
<label for="flash">
<label for="id_flash">
{{ _('Uses Flash') }}
{{ tip(None,
_('If your app uses Flash, it will be hidden from the Marketplace on devices without Flash support.')) }}
Expand All @@ -30,6 +30,23 @@ <h2>
{% endif %}
</td>
</tr>
<tr>
<th>
<label for="id_is_offline">
{{ _('Works Offline') }}
{{ tip(None,
_('If your app works without an Internet connection then we will list that your app "Works Offline."')) }}
</label>
</th>
<td>
{% if editable and form %}
{{ form.is_offline }}
{{ form.is_offline.error }}
{% else %}
<input type="checkbox" disabled{{ ' checked' if addon.is_offline }}>
{% endif %}
</td>
</tr>
<tr>
<th>
{{ tip(_("Public Stats?"),
Expand Down
4 changes: 1 addition & 3 deletions mkt/search/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ def check(offline, visible):
check(offline='None', visible=True)

# Mark that app is capable offline.
self.webapp.update(is_packaged=True)
self.webapp.update(is_offline=True)
self.refresh('webapp')

# Should show up in offline.
Expand Down Expand Up @@ -595,7 +595,6 @@ def test_app_type_hosted(self):
obj = res.json['objects'][0]
eq_(obj['slug'], self.webapp.app_slug)
eq_(obj['is_packaged'], False)
eq_(obj['is_offline'], False)
eq_(obj['package_path'], None)

@override_settings(SITE_URL='http://hy.fr')
Expand All @@ -610,7 +609,6 @@ def test_app_type_packaged(self):
obj = res.json['objects'][0]
eq_(obj['slug'], self.webapp.app_slug)
eq_(obj['is_packaged'], True)
eq_(obj['is_offline'], True)
eq_(obj['package_path'],
'%s/downloads/file/%s/%s' % (settings.SITE_URL, f.id, f.filename))

Expand Down
9 changes: 8 additions & 1 deletion mkt/submit/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,11 +324,14 @@ class AppDetailsBasicForm(AppSupportFormMixin, TranslationFormMixin,
label=_lazy(u'Once your app is approved, choose a publishing option:'),
choices=PUBLISH_CHOICES, initial=amo.PUBLISH_IMMEDIATE,
widget=forms.RadioSelect())
is_offline = forms.BooleanField(
label=_lazy(u'My app works without an Internet connection.'),
required=False)

class Meta:
model = Webapp
fields = ('app_slug', 'description', 'privacy_policy', 'homepage',
'support_url', 'support_email', 'publish_type')
'support_url', 'support_email', 'publish_type', 'is_offline')

def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
Expand All @@ -339,6 +342,10 @@ def __init__(self, *args, **kwargs):
privacy_field.help_text = mark_safe(privacy_field.help_text.format(
url=self.PRIVACY_MDN_URL))

if 'instance' in kwargs:
instance = kwargs['instance']
instance.is_offline = instance.guess_is_offline()

super(AppDetailsBasicForm, self).__init__(*args, **kwargs)

def clean_app_slug(self):
Expand Down
1 change: 1 addition & 0 deletions mkt/submit/templates/submit/details.html
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ <h2 class="output">{{ addon.name }}</h2>
</div>
</div>
{{ form_field(form_basic.flash) }}
{{ form_field(form_basic.is_offline) }}
{{ form_field(form_basic.publish_type) }}

{{ form_field(form_basic.notes, hint=True) }}
Expand Down
42 changes: 42 additions & 0 deletions mkt/submit/tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,48 @@ def test_help_text_uses_safetext_and_includes_url(self):
ok_('{url}' not in help_text)
ok_(form.PRIVACY_MDN_URL in help_text)

def test_is_offline_guess_false(self):
app = self.get_app()
app.guess_is_offline = lambda: False
assert not app.is_offline
forms.AppDetailsBasicForm(
self.get_data(),
request=self.request,
instance=app)
assert not app.is_offline

def test_is_offline_guess_false_override(self):
app = self.get_app()
app.guess_is_offline = lambda: False
form = forms.AppDetailsBasicForm(
self.get_data(is_offline=True),
request=self.request,
instance=app)
assert form.is_valid(), form.errors
form.save()
eq_(app.is_offline, True)

def test_is_offline_guess_true(self):
app = self.get_app()
app.guess_is_offline = lambda: True
assert not app.is_offline
forms.AppDetailsBasicForm(
self.get_data(is_offline=None),
request=self.request,
instance=app)
assert app.is_offline

def test_is_offline_guess_true_override(self):
app = self.get_app()
app.guess_is_offline = lambda: True
form = forms.AppDetailsBasicForm(
self.get_data(is_offline=False),
request=self.request,
instance=app)
assert form.is_valid(), form.errors
form.save()
eq_(app.is_offline, False)


class TestAppFeaturesForm(amo.tests.TestCase):
fixtures = fixture('user_999', 'webapp_337141')
Expand Down
11 changes: 11 additions & 0 deletions mkt/webapps/management/commands/process_addons.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from mkt.webapps.models import Webapp
from mkt.webapps.tasks import (add_uuids, clean_apps, dump_apps,
fix_missing_icons, import_manifests,
populate_is_offline,
regenerate_icons_and_thumbnails,
update_manifests, update_supported_locales,
zip_apps)
Expand Down Expand Up @@ -38,6 +39,16 @@
amo.STATUS_PUBLIC,
amo.STATUS_APPROVED],
disabled_by_user=False)]},
'populate_is_offline': {
'method': populate_is_offline,
'qs': [Q(status__in=[amo.STATUS_NULL,
amo.STATUS_PENDING,
amo.STATUS_PUBLIC,
amo.STATUS_REJECTED,
amo.STATUS_APPROVED,
amo.STATUS_UNLISTED],
disabled_by_user=False)]
},
'regenerate_icons_and_thumbnails':
{'method': regenerate_icons_and_thumbnails,
'qs': [Q(status__in=[amo.STATUS_PENDING,
Expand Down
17 changes: 11 additions & 6 deletions mkt/webapps/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ class Webapp(UUIDModelMixin, OnChangeMixin, ModelBase):
# This is the public_id to a Generic Solitude Product
solitude_public_id = models.CharField(max_length=255, null=True,
blank=True)
is_offline = models.BooleanField(default=False)

objects = WebappManager()
with_deleted = WebappManager(include_deleted=True)
Expand Down Expand Up @@ -1493,18 +1494,22 @@ def next_step(self):
'url': self.get_dev_url('payments'),
}

@amo.cached_property(writable=True)
def is_offline(self):
def guess_is_offline(self):
"""
Returns a boolean of whether this is an app that degrades
gracefully offline (i.e., is a packaged app or has an
`appcache_path` defined in its manifest).
"""
if self.is_packaged:
return True
manifest = self.get_manifest_json()
return bool(manifest and 'appcache_path' in manifest)
if self.latest_version:
# Manually find the latest file since `self.get_latest_file()`
# won't be set correctly near the start of the app submission
# process.
manifest = self.get_manifest_json(
file_obj=self.latest_version.all_files[0])
return bool(manifest and 'appcache_path' in manifest)
else:
return False

def mark_done(self):
"""When the submission process is done, update status accordingly."""
Expand Down
8 changes: 8 additions & 0 deletions mkt/webapps/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1029,3 +1029,11 @@ def find_abuse_escalations(addon_id, **kw):
amo.log(amo.LOG.ESCALATED_HIGH_ABUSE, abuse.addon,
abuse.addon.current_version, details={'comments': msg})
task_log.info(u'[app:%s] %s' % (abuse.addon, msg))


@task
@write
def populate_is_offline(ids, **kw):
for webapp in Webapp.objects.filter(pk__in=ids).iterator():
if webapp.guess_is_offline():
webapp.update(is_offline=True)
16 changes: 7 additions & 9 deletions mkt/webapps/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -638,20 +638,24 @@ def test_get_trending(self):
region.adolescent = False
eq_(app.get_trending(region=region), 10.0)

def test_is_offline_when_appcache_path(self):
def test_guess_is_offline_when_appcache_path(self):
app = self.get_app()

# If there's no appcache_path defined, ain't an offline-capable app.
am = AppManifest.objects.get(version=app.current_version)
eq_(app.is_offline, False)
eq_(app.guess_is_offline(), False)

# If there's an appcache_path defined, this is an offline-capable app.
manifest = json.loads(am.manifest)
manifest['appcache_path'] = '/manifest.appcache'
am.update(manifest=json.dumps(manifest))
# reload isn't enough, it doesn't clear cached_property.
app = self.get_app()
eq_(app.is_offline, True)
eq_(app.guess_is_offline(), True)

def test_guess_is_offline_no_manifest(self):
app = Webapp()
eq_(app.guess_is_offline(), False)

@mock.patch('mkt.webapps.models.Webapp.has_payment_account')
def test_payments_complete(self, pay_mock):
Expand Down Expand Up @@ -1052,12 +1056,6 @@ def test_excluded_in_usk_exclude(self):
self.assertSetEqual(get_excluded_in(mkt.regions.BR.id), [])
self.assertSetEqual(get_excluded_in(mkt.regions.DE.id), [app.id])

@mock.patch('mkt.webapps.models.cache.get')
def test_is_offline_when_packaged(self, mock_get):
mock_get.return_value = ''
eq_(Webapp(is_packaged=True).is_offline, True)
eq_(Webapp(is_packaged=False).is_offline, False)

@mock.patch('mkt.webapps.models.Webapp.completion_errors')
def test_completion_errors(self, complete_mock):
app = app_factory()
Expand Down
3 changes: 1 addition & 2 deletions mkt/webapps/tests/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ def test_packaged(self):
eq_(res['is_packaged'], False)
eq_(res['is_offline'], False)

self.app.update(is_packaged=True)
del self.app.is_offline # cached_property, need to be reset.
self.app.update(is_packaged=True, is_offline=True)

res = self.serialize(self.app)
eq_(res['is_packaged'], True)
Expand Down

0 comments on commit bf7f64e

Please sign in to comment.