diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..0740d1a1 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,15 @@ +name: Close stale issues and pull requests + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' # Run every day at midnight + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v8 + with: + days-before-stale: 30 + exempt-issue-labels: in-progress,help-wanted,pinned,security,enhancement diff --git a/post_office/admin.py b/post_office/admin.py index 71e967ab..83d4bf0d 100644 --- a/post_office/admin.py +++ b/post_office/admin.py @@ -16,6 +16,7 @@ from django.utils.text import Truncator from django.utils.translation import gettext_lazy as _ +from . import settings as post_office_settings from .fields import CommaSeparatedEmailField from .mail import send from .models import STATUS, Attachment, Email, EmailTemplate, Log @@ -259,24 +260,54 @@ def __init__(self, *args, **kwargs): self.fields['language'].disabled = True +def _create_iframe(src, height): + return format_html(''' + + ''', height=height, src=src) + + class EmailTemplateInline(admin.StackedInline): form = EmailTemplateAdminForm formset = EmailTemplateAdminFormSet model = EmailTemplate extra = 0 - fields = ('language', 'subject', 'content', 'html_content',) + fields = ('language', 'subject', 'content', 'html_content', + 'rendered_content', 'rendered_html_content',) + readonly_fields = ('rendered_content', 'rendered_html_content',) formfield_overrides = { models.CharField: {'widget': SubjectField} } + def rendered_content(self, instance): + if instance.content: + src = '?preview=text&language={}'.format(instance.language) + height = instance.content.count('\n') * 25 + return _create_iframe(src, height) + else: + return '' + + def rendered_html_content(self, instance): + if instance.html_content: + src = '?preview=text&language={}'.format(instance.language) + return _create_iframe(src, 800) + else: + return '' + def get_max_num(self, request, obj=None, **kwargs): return len(settings.LANGUAGES) class EmailTemplateAdmin(admin.ModelAdmin): form = EmailTemplateAdminForm - list_display = ('name', 'description_shortened', 'subject', 'languages_compact', 'created') + list_display = ( + 'name', 'description_shortened', 'subject', 'languages_compact', + 'created') search_fields = ('name', 'description', 'subject') + readonly_fields = ('rendered_content', 'rendered_html_content') fieldsets = [ (None, { 'fields': ('name', 'description'), @@ -284,11 +315,56 @@ class EmailTemplateAdmin(admin.ModelAdmin): (_("Default Content"), { 'fields': ('subject', 'content', 'html_content'), }), + (_("Preview"), { + 'fields': ('example_context', 'rendered_content', + 'rendered_html_content'), + }), ] inlines = (EmailTemplateInline,) if settings.USE_I18N else () formfield_overrides = { models.CharField: {'widget': SubjectField} } + change_form_template = 'admin/post_office/EmailTemplate/change_form.html' + + def change_view(self, request, object_id, form_url='', extra_context=None): + if request.GET.get('preview'): + instance = self.model.objects.get(id=object_id) + engine = post_office_settings.get_template_engine() + + if request.GET.get('language'): + template_instance = instance.translated_templates.filter( + language=request.GET.get('language'), + ).first() + else: + template_instance = instance + + if request.GET.get('preview') == 'html': + template = engine.from_string( + template_instance.html_content + .replace('inline_image', 'static') + .replace(' post_office ', ' static ')) + else: + template = engine.from_string( + '
%s
' % template_instance.content) + + return HttpResponse(clean_html(template.render( + instance.example_context))) + + return super().change_view(request, object_id, form_url, extra_context) + + def rendered_content(self, instance): + if instance.content: + src = '?preview=text' + height = instance.content.count('\n') * 25 + return _create_iframe(src, height) + else: + return '' + + def rendered_html_content(self, instance): + if instance.html_content: + return _create_iframe('?preview=html', 800) + else: + return '' def get_queryset(self, request): return self.model.objects.filter(default_template__isnull=True) diff --git a/post_office/migrations/0012_add_example_context.py b/post_office/migrations/0012_add_example_context.py new file mode 100644 index 00000000..492cb536 --- /dev/null +++ b/post_office/migrations/0012_add_example_context.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.10 on 2021-12-12 02:17 + +from django.db import migrations, models +import jsonfield.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('post_office', '0011_models_help_text'), + ] + + operations = [ + migrations.AddField( + model_name='emailtemplate', + name='example_context', + field=jsonfield.fields.JSONField(blank=True, null=True, verbose_name='Context'), + ), + ] diff --git a/post_office/models.py b/post_office/models.py index d736d48e..1033d6f0 100644 --- a/post_office/models.py +++ b/post_office/models.py @@ -269,6 +269,7 @@ class EmailTemplate(models.Model): default='', blank=True) default_template = models.ForeignKey('self', related_name='translated_templates', null=True, default=None, verbose_name=_('Default template'), on_delete=models.CASCADE) + example_context = context_field_class(_('Context'), blank=True, null=True) objects = EmailTemplateManager()