Skip to content

Commit

Permalink
Merge pull request #1418 from glogiotatidis/issue-1414-one-scene-s2d
Browse files Browse the repository at this point in the history
[Fix #1414] Add Send to Device Single Scene Template.
  • Loading branch information
glogiotatidis committed Jul 28, 2020
2 parents 8dee499 + f399e81 commit d1fbeec
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 11 deletions.
51 changes: 51 additions & 0 deletions snippets/base/admin/adminmodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,56 @@ class NewsletterTemplateInline(admin.StackedInline):
)


class SendToDeviceSingleSceneTemplateInline(admin.StackedInline):
model = models.SendToDeviceSingleSceneTemplate
form = forms.SendToDeviceTemplateForm
can_delete = False
classes = [
'inline-template',
'send_to_device_scene2_snippet',
]
raw_id_fields = [
'section_title_icon',
'icon',
]

fieldsets = (
('Section', {
'fields': (
'section_title_icon',
'section_title_text',
'section_title_url',
),
}),
('Main', {
'fields': (
'icon',
'text',

'button_label',
'input_placeholder',
'disclaimer_html',

'locale',
('include_sms', 'message_id_sms',),
'country',
'message_id_email',
'success_title',
'success_text',
'error_text',
'retry_button_label',
)
}),

('Extra', {
'fields': (
'block_button_text',
'do_not_autoblock',
),
})
)


class SendToDeviceTemplateInline(admin.StackedInline):
model = models.SendToDeviceTemplate
form = forms.SendToDeviceTemplateForm
Expand Down Expand Up @@ -514,6 +564,7 @@ class ASRSnippetAdmin(admin.ModelAdmin):
FxASignupTemplateInline,
NewsletterTemplateInline,
SendToDeviceTemplateInline,
SendToDeviceSingleSceneTemplateInline,
SimpleBelowSearchTemplateInline,
]
list_display_links = [
Expand Down
1 change: 1 addition & 0 deletions snippets/base/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ class ASRSnippetAdminForm(forms.ModelForm):
('fxa_signup_snippet', 'Firefox Accounts Sign Up'),
('newsletter_snippet', 'Newsletter Sign Up'),
('send_to_device_snippet', 'Send to Device'),
('send_to_device_scene2_snippet', 'Send to Device Single Scene'),
('simple_below_search_snippet', 'Simple Below Search'),
),
widget=TemplateChooserWidget,
Expand Down
41 changes: 41 additions & 0 deletions snippets/base/migrations/0039_sendtodevicesinglescenetemplate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Generated by Django 2.2.13 on 2020-07-28 13:28

from django.db import migrations, models
import django.db.models.deletion
import snippets.base.fields


class Migration(migrations.Migration):

dependencies = [
('base', '0038_auto_20200624_1213'),
]

operations = [
migrations.CreateModel(
name='SendToDeviceSingleSceneTemplate',
fields=[
('template_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='base.Template')),
('section_title_text', models.CharField(blank=True, help_text='Section title text. section_title_icon must also be specified to display.', max_length=255, verbose_name='Section Title Text')),
('section_title_url', snippets.base.fields.URLField(blank=True, help_text='A url, section_title_text links to this', max_length=500, verbose_name='Section Title URL')),
('text', models.TextField(help_text='Main text.', verbose_name='Text')),
('button_label', models.CharField(default='Send', help_text='Label for form submit button.', max_length=50, verbose_name='Button Label')),
('input_placeholder', models.CharField(default='Your email here', help_text='Placeholder text for email / phone number field.', max_length=255, verbose_name='Input Placeholder')),
('disclaimer_html', models.TextField(help_text='Text and link underneath the input box. HTML subset allowed: i, b, u, strong, em, br.', verbose_name='Disclaimer HTML')),
('locale', models.CharField(default='EN', help_text='Two to five character string for the locale code. Default "EN".', max_length=10)),
('country', models.CharField(default='us', help_text='Two character string for the country code (used for SMS). Default "us".', max_length=10)),
('include_sms', models.BooleanField(blank=True, default=False, help_text='Defines whether SMS is available.', verbose_name='Include SMS')),
('message_id_sms', models.CharField(blank=True, help_text='Newsletter/basket id representing the SMS message to be sent.', max_length=100, verbose_name='Message ID for SMS')),
('message_id_email', models.CharField(help_text='Newsletter/basket id representing the email message to be sent. Must be a value from the "Slug" column here: https://basket.mozilla.org/news/.', max_length=100, verbose_name='Message ID for Email')),
('success_title', models.TextField(help_text='Title of success message after form submission.', verbose_name='Success Title')),
('success_text', models.TextField(help_text='Text of success message after form submission.', verbose_name='Success Text')),
('error_text', models.TextField(help_text='Text of error message if form submission fails.', verbose_name='Error Text')),
('retry_button_label', models.CharField(default='Try again', help_text='Button label after a failed form submission', max_length=50, verbose_name='Retry Button Label')),
('block_button_text', models.CharField(default='Remove this', help_text='Tooltip text used for dismiss button.', max_length=50, verbose_name='Block Button Text')),
('do_not_autoblock', models.BooleanField(blank=True, default=False, help_text='Used to prevent blocking the snippet after the CTA (link or button) has been clicked.', verbose_name='Do Not Autoblock')),
('icon', models.ForeignKey(help_text='Image to display above the form. 192x192px PNG.', on_delete=django.db.models.deletion.PROTECT, related_name='sendtodevicesinglescene_icons', to='base.Icon', verbose_name='Icon')),
('section_title_icon', models.ForeignKey(blank=True, help_text='Section title icon. 64x64px. PNG. section_title_text must also be specified to display.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='sendtodevicesinglescene_section_icons', to='base.Icon', verbose_name='Section Title Icon')),
],
bases=('base.template',),
),
]
193 changes: 184 additions & 9 deletions snippets/base/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,8 @@ def clean(self):


class Template(models.Model):
TARGETING = ''

snippet = models.OneToOneField('ASRSnippet', related_name='template_relation',
on_delete=models.CASCADE)

Expand Down Expand Up @@ -742,6 +744,10 @@ def render(self):
def version(self):
return self.VERSION

@property
def targeting(self):
return self.TARGETING

def get_main_body(self, bleached=False):
body = self.text
if bleached:
Expand Down Expand Up @@ -1662,6 +1668,169 @@ def get_main_body(self, bleached=False):
return body


class SendToDeviceSingleSceneTemplate(Template):
VERSION = '1.0.0'
NAME = 'Send to Device Single Scene'
TARGETING = 'firefoxVersion >= 80'

section_title_icon = models.ForeignKey(
Icon,
blank=True,
null=True,
on_delete=models.PROTECT,
verbose_name='Section Title Icon',
related_name='sendtodevicesinglescene_section_icons',
help_text=('Section title icon. 64x64px. PNG. '
'section_title_text must also be specified to display.'),
)
section_title_text = models.CharField(
verbose_name='Section Title Text',
blank=True,
max_length=255,
help_text='Section title text. section_title_icon must also be specified to display.',
)
section_title_url = snippet_fields.URLField(
verbose_name='Section Title URL',
blank=True,
max_length=500,
help_text='A url, section_title_text links to this',
)

text = models.TextField(
verbose_name='Text',
help_text='Main text.',
)
icon = models.ForeignKey(
Icon,
on_delete=models.PROTECT,
verbose_name='Icon',
related_name='sendtodevicesinglescene_icons',
help_text='Image to display above the form. 192x192px PNG.'
)
button_label = models.CharField(
verbose_name='Button Label',
max_length=50,
default='Send',
help_text='Label for form submit button.',
)

input_placeholder = models.CharField(
verbose_name='Input Placeholder',
max_length=255,
default='Your email here',
help_text='Placeholder text for email / phone number field.',
)
disclaimer_html = models.TextField(
verbose_name='Disclaimer HTML',
help_text=(
'Text and link underneath the input box. HTML subset allowed: i, b, u, strong, em, br.'
),
)

locale = models.CharField(
max_length=10,
default='EN',
help_text='Two to five character string for the locale code. Default "EN".',
)
country = models.CharField(
max_length=10,
default='us',
help_text='Two character string for the country code (used for SMS). Default "us".',
)
include_sms = models.BooleanField(
verbose_name='Include SMS',
blank=True,
default=False,
help_text='Defines whether SMS is available.',
)
message_id_sms = models.CharField(
verbose_name='Message ID for SMS',
max_length=100,
blank=True,
help_text='Newsletter/basket id representing the SMS message to be sent.',
)
message_id_email = models.CharField(
verbose_name='Message ID for Email',
max_length=100,
help_text=('Newsletter/basket id representing the email message to be sent. Must be '
'a value from the "Slug" column here: https://basket.mozilla.org/news/.'),
)

success_title = models.TextField(
verbose_name='Success Title',
help_text='Title of success message after form submission.',
)
success_text = models.TextField(
verbose_name='Success Text',
help_text='Text of success message after form submission.',
)
error_text = models.TextField(
verbose_name='Error Text',
help_text='Text of error message if form submission fails.',
)
retry_button_label = models.CharField(
verbose_name='Retry Button Label',
max_length=50,
default='Try again',
help_text='Button label after a failed form submission'
)

###
# Extras
###
block_button_text = models.CharField(
verbose_name='Block Button Text',
max_length=50, default='Remove this',
help_text='Tooltip text used for dismiss button.'
)
do_not_autoblock = models.BooleanField(
verbose_name='Do Not Autoblock',
default=False, blank=True,
help_text=('Used to prevent blocking the snippet after the '
'CTA (link or button) has been clicked.'),
)

@property
def code_name(self):
return 'send_to_device_scene2_snippet'

def render(self):
data = {
'section_title_icon': self.section_title_icon.url if self.section_title_icon else '',
'section_title_text': self.section_title_text,
'section_title_url': self.section_title_url,
'scene2_text': self.text,
'scene2_icon': self.icon.url if self.icon else '',
'scene2_button_label': self.button_label,
'scene2_input_placeholder': self.input_placeholder,
'scene2_disclaimer_html': self.disclaimer_html,
'locale': self.locale,
'country': self.country,
'include_sms': self.include_sms,
'message_id_sms': self.message_id_sms,
'message_id_email': self.message_id_email,
'success_title': self.success_title,
'success_text': self.success_text,
'error_text': self.error_text,
'block_button_text': self.block_button_text,
'do_not_autoblock': self.do_not_autoblock,
'retry_button_label': self.retry_button_label,
}
data = self._process_rendered_data(data)
return data

def get_rich_text_fields(self):
return [
'disclaimer_html',
]

def get_main_body(self, bleached=False):
body = self.text
if bleached:
body = bleach.clean(body, tags=[], strip=True).strip()
return body


class SimpleBelowSearchTemplate(Template):
VERSION = '1.0.1'
NAME = 'Simple below Search Bar'
Expand Down Expand Up @@ -1933,16 +2102,21 @@ def render(self, always_eval_to_false=False):
CHANNELS_MAP[channel] for channel in CHANNELS_MAP if channel in self.channels
])
rendered_snippet = util.deep_search_and_replace(rendered_snippet, '[[channels]]', channels)
# Add JEXL targeting
rendered_snippet['targeting'] = ' && '.join(
[target.jexl_expr for
target in self.targets.all().order_by('id') if
target.jexl_expr]
)

# Add Targets
targeting = []
if rendered_snippet.get('targeting'):
targeting.append(rendered_snippet['targeting'])

targeting.extend([target.jexl_expr for
target in self.targets.all().order_by('id') if
target.jexl_expr])

# Make targeting always fail. Used for Nightly debuging.
if always_eval_to_false:
if rendered_snippet['targeting']:
rendered_snippet['targeting'] += ' && '
rendered_snippet['targeting'] += 'false'
targeting.append('false')

rendered_snippet['targeting'] = ' && '.join(targeting)

# Add Client Limits
frequency = {}
Expand Down Expand Up @@ -2095,6 +2269,7 @@ def render(self, preview=False):
'template': template_code_name,
'template_version': template_version,
'content': data,
'targeting': self.template_ng.targeting,
}

if preview:
Expand Down
5 changes: 3 additions & 2 deletions snippets/base/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,11 +514,12 @@ def test_render(self):
'<a href="https://example.com/[[snippet_id]]/foo">link</a> in '
'[[channels]] channels'),
)
snippet.template_ng.TARGETING = 'true'
generated_result = snippet.render()
self.maxDiff = None
expected_result = {
'template': snippet.template_ng.code_name,
'template_version': snippet.template_ng.version,
'targeting': 'true',
'content': {
'text': ('snippet id {} and with campaign [[campaign_slug]] and '
'<link0>link</link0> in [[channels]] channels').format(snippet.id),
Expand All @@ -544,6 +545,7 @@ def test_render_preview_only(self):
'id': 'preview-{}'.format(snippet.id),
'template': snippet.template_ng.code_name,
'template_version': snippet.template_ng.version,
'targeting': '',
'content': {
'do_not_autoblock': True,
# snippet_id, campaign_slug and channels must be replaced with empty string.
Expand Down Expand Up @@ -749,7 +751,6 @@ def test_render(self):
jexl_expr='foo==bar'),
]
)

snippet_render = {
'template': 'simple_snippet',
'template_version': 'xx.xx',
Expand Down

0 comments on commit d1fbeec

Please sign in to comment.