diff --git a/contrib/bonde/models.py b/contrib/bonde/models.py index 878b0c33..f047511b 100644 --- a/contrib/bonde/models.py +++ b/contrib/bonde/models.py @@ -6,6 +6,8 @@ # * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table # Feel free to rename the models, but don't rename db_table values or field names. from django.db import models +from django.conf import settings +from django.contrib.sites.models import Site class User(models.Model): @@ -91,7 +93,9 @@ class Meta: class DnsHostedZone(models.Model): - community = models.ForeignKey(Community, on_delete=models.CASCADE, blank=True, null=True) + community = models.ForeignKey( + Community, on_delete=models.CASCADE, blank=True, null=True + ) domain_name = models.CharField(unique=True, max_length=100, blank=True, null=True) # comment = models.TextField(blank=True, null=True) created_at = models.DateTimeField() @@ -103,4 +107,119 @@ class DnsHostedZone(models.Model): class Meta: managed = False - db_table = 'dns_hosted_zones' \ No newline at end of file + db_table = "dns_hosted_zones" + + +class RequestManager(models.Manager): + + def __init__(self, lookup_field=None): + self.lookup_field = lookup_field + super(RequestManager, self).__init__() + + def on_site(self, request=None): + site = Site.objects.get(id=settings.SITE_ID) + if request: + site = request.current_site + + prefix = '' if not self.lookup_field else f'{self.lookup_field}__' + + params = { + f'{prefix}community__dnshostedzone__domain_name': site.domain + } + + return self.get_queryset().filter(**params) + + +class MobilizationStatus(models.TextChoices): + archived = "archived", "Arquivada" + active = "active", "Ativa" + + +class Mobilization(models.Model): + name = models.CharField(max_length=266, blank=True, null=True) + created_at = models.DateTimeField() + # user_id = models.IntegerField(blank=True, null=True) + # color_scheme = models.CharField(max_length=-1, blank=True, null=True) + # google_analytics_code = models.CharField(max_length=-1, blank=True, null=True) + # goal = models.TextField(blank=True, null=True) + # header_font = models.CharField(max_length=-1, blank=True, null=True) + # body_font = models.CharField(max_length=-1, blank=True, null=True) + # facebook_share_title = models.CharField(max_length=-1, blank=True, null=True) + # facebook_share_description = models.TextField(blank=True, null=True) + # facebook_share_image = models.CharField(max_length=-1, blank=True, null=True) + # slug = models.CharField(unique=True, max_length=-1, blank=True, null=True) + custom_domain = models.CharField(unique=True, max_length=255, blank=True, null=True) + # twitter_share_text = models.CharField(max_length=300, blank=True, null=True) + community = models.ForeignKey(Community, models.DO_NOTHING, blank=True, null=True) + # favicon = models.CharField(max_length=-1, blank=True, null=True) + # deleted_at = models.DateTimeField(blank=True, null=True) + status = models.CharField( + choices=MobilizationStatus.choices, max_length=30, blank=True, null=True + ) + # traefik_host_rule = models.CharField(max_length=-1, blank=True, null=True) + # traefik_backend_address = models.CharField(max_length=-1, blank=True, null=True) + language = models.CharField(max_length=5, blank=True, null=True) + updated_at = models.DateTimeField(blank=True, null=True) + # theme = models.ForeignKey('Themes', models.DO_NOTHING, blank=True, null=True) + + objects = RequestManager() + + class Meta: + managed = False + db_table = "mobilizations" + + +class Block(models.Model): + mobilization = models.ForeignKey( + Mobilization, models.DO_NOTHING, blank=True, null=True + ) + created_at = models.DateTimeField() + updated_at = models.DateTimeField() + # bg_class = models.CharField(max_length=-1, blank=True, null=True) + # position = models.IntegerField(blank=True, null=True) + # hidden = models.BooleanField(blank=True, null=True) + # bg_image = models.TextField(blank=True, null=True) + # name = models.CharField(max_length=-1, blank=True, null=True) + # menu_hidden = models.BooleanField(blank=True, null=True) + deleted_at = models.DateTimeField(blank=True, null=True) + + class Meta: + managed = False + db_table = "blocks" + + +class WidgetKind(models.TextChoices): + content = "content", "Conteúdo" + donation = "donation", "Doação" + draft = "draft", "Rascunho" + form = "form", "Formulário" + phone = "phone", "Pressão por telefone" + plip = "plip", "PLIP" + pressure = "pressure", "Pressão por email" + + +class Widget(models.Model): + block = models.ForeignKey(Block, models.DO_NOTHING, blank=True, null=True) + # settings = models.JSONField(blank=True, null=True) + kind = models.CharField( + max_length=50, choices=WidgetKind.choices, blank=True, null=True + ) + created_at = models.DateTimeField() + updated_at = models.DateTimeField() + # sm_size = models.IntegerField(blank=True, null=True) + # md_size = models.IntegerField(blank=True, null=True) + # lg_size = models.IntegerField(blank=True, null=True) + # mailchimp_segment_id = models.CharField(max_length=-1, blank=True, null=True) + # action_community = models.BooleanField(blank=True, null=True) + # exported_at = models.DateTimeField(blank=True, null=True) + # mailchimp_unique_segment_id = models.CharField(max_length=-1, blank=True, null=True) + # mailchimp_recurring_active_segment_id = models.CharField(max_length=-1, blank=True, null=True) + # mailchimp_recurring_inactive_segment_id = models.CharField(max_length=-1, blank=True, null=True) + # goal = models.DecimalField(max_digits=12, decimal_places=2, blank=True, null=True) + deleted_at = models.DateTimeField(blank=True, null=True) + + objects = RequestManager(lookup_field='block__mobilization') + + class Meta: + managed = False + db_table = "widgets" diff --git a/contrib/bonde/templates/bonde/widgets/bonde_widget.html b/contrib/bonde/templates/bonde/widgets/bonde_widget.html new file mode 100644 index 00000000..fe8fd8ae --- /dev/null +++ b/contrib/bonde/templates/bonde/widgets/bonde_widget.html @@ -0,0 +1,3 @@ +{% for subwidget in widget.subwidgets %} + {% include subwidget.template_name with widget=subwidget %} +{% endfor %} \ No newline at end of file diff --git a/contrib/bonde/views.py b/contrib/bonde/views.py index 91ea44a2..32c99898 100644 --- a/contrib/bonde/views.py +++ b/contrib/bonde/views.py @@ -1,3 +1,6 @@ from django.shortcuts import render -# Create your views here. + +# def select_widget(request): +# if request.is_ajax(): +# pass \ No newline at end of file diff --git a/contrib/bonde/widgets.py b/contrib/bonde/widgets.py new file mode 100644 index 00000000..1b2b3f23 --- /dev/null +++ b/contrib/bonde/widgets.py @@ -0,0 +1,20 @@ +from django.forms import MultiWidget, Select + + +class BondeWidget(MultiWidget): + description = "A Bonde widget reference" + template_name = "bonde/widgets/bonde_widget.html" + + def __init__(self, *args, **kwargs): + kwargs["widgets"] = { + "mobilization_id": Select, + "widget_id": Select + } + + super(BondeWidget, self).__init__(*args, **kwargs) + + + def decompress(self, value): + if value: + return [value.mobilization_id, value.widget_id] + return [] \ No newline at end of file diff --git a/contrib/campaign/cms_plugins.py b/contrib/campaign/cms_plugins.py index 52b413a1..0c12fef3 100644 --- a/contrib/campaign/cms_plugins.py +++ b/contrib/campaign/cms_plugins.py @@ -1,74 +1,115 @@ -from django.db.models import Q +from django.contrib import admin from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool -from .models import ActionButton, Row, Column -from .forms import PressureForm +from contrib.bonde.models import Widget +from .models import Pressure, TargetGroup +from .forms import PressureForm, PressureSettingsForm -# @plugin_pool.register_plugin -# class BlockPlugin(CMSPluginBase): -# model = Block -# name = "Bloco" -# render_template = "campaign/plugins/block.html" -# allow_children = True -# child_classes = [ -# "PicturePlugin", -# "TextPlugin", -# "ActionButtonPlugin", -# "RowPlugin", -# ] - -# @plugin_pool.register_plugin -# class GridBlockPlugin(CMSPluginBase): -# model = Block -# name = "Grid" -# render_template = "campaign/plugins/grid-block.html" -# allow_children = True -# child_classes = ["TextPlugin", "PressurePlugin"] - - -@plugin_pool.register_plugin -class ActionButtonPlugin(CMSPluginBase): - model = ActionButton - name = "Botão de ação" - render_template = "campaign/plugins/action-button.html" - allow_children = False +class TargetGroupInline(admin.StackedInline): + fieldset = "Alvos" + model = TargetGroup + template = "campaign/admin/edit_inline/stacked.html" + extra = 1 @plugin_pool.register_plugin class PressurePlugin(CMSPluginBase): name = "Pressão" render_template = "campaign/plugins/pressure.html" + change_form_template = "campaign/admin/change_form.html" + model = Pressure + # model = Dummy + form = PressureSettingsForm + inlines = [ + TargetGroupInline, + ] + fieldsets = [ + ("Alvos", {"fields": [], "classes": ["tab-active"]}), + ("Email", {"fields": ["email_subject", "email_body", "disable_editing"]}), + ("Envio", {"fields": ["submissions_limit", "submissions_interval"]}), + ("Agradecimento", {"fields": ["thank_email_subject", "sender_name", "sender_email", "thank_email_body"]}), + ("Pós-ação", {"fields": ["sharing", "whatsapp_text"]}) + ] + # fieldsets = [ + # ("Alvos", {"fields": ["is_group", "email_subject"], "classes": ["tab-active"]}), + # ("Email", {"fields": ["disable_editing"]}), + # ( + # "Envio", + # { + # "fields": ["submissions_limit", "submissions_interval"], + # }, + # ), + # ( + # "Agradecimento", + # { + # "fields": [ + # "email_subject", + # "sender_name", + # "sender_email", + # "email_body", + # ], + # }, + # ), + # ("Pós-ação", {"fields": ["sharing", "whatsapp_text"]}), + # ] + + # return super().save_form(request, form, change) + + # def get_form(self, request, obj=None, change=False, **kwargs): + # form = super(PressurePlugin, self).get_form(request, obj, change, **kwargs) + + # qs = Widget.objects.on_site(request=request).filter(kind="pressure") + + # choices = list( + # map(lambda x: (x.id, f"{x.block.mobilization.name} {x.kind} {x.id}"), qs) + # ) + + # form.base_fields["widget"].widget.choices = choices + + # return form + + def render_change_form( + self, request, context, add=False, change=False, form_url="", obj=None + ): + sorted_inline_formsets = {} + inline_admin_formsets = context["inline_admin_formsets"] + formsets_to_remove = [] + + for inline_formset in inline_admin_formsets: + if hasattr(inline_formset.opts, "fieldset"): + fieldset = inline_formset.opts.fieldset + if fieldset in sorted_inline_formsets: + sorted_inline_formsets[fieldset].append(inline_formset) + else: + sorted_inline_formsets.update( + { + fieldset: [ + inline_formset, + ] + } + ) + formsets_to_remove.append(inline_formset) + + for inline_formset in formsets_to_remove: + inline_admin_formsets.remove(inline_formset) + + context.update( + { + "sorted_inline_formsets": sorted_inline_formsets, + "inline_admin_formsets": inline_admin_formsets, + } + ) + + # import ipdb;ipdb.set_trace() + return super(PressurePlugin, self).render_change_form( + request, context, add, change, form_url, obj + ) def render(self, context, instance, placeholder): context = super(PressurePlugin, self).render(context, instance, placeholder) context.update({"form": PressureForm()}) return context - - -@plugin_pool.register_plugin -class RowPlugin(CMSPluginBase): - model = Row - name = "Linha" - render_template = "campaign/plugins/row.html" - allow_children = True - child_classes = ["ColumnPlugin", "PicturePlugin"] - - -# @plugin_pool.register_plugin -# class ColumnPlugin(CMSPluginBase): -# model = Column -# name = "Coluna" -# render_template = "campaign/plugins/column.html" -# allow_children = True -# parent_classes = ["RowPlugin"] - - -@plugin_pool.register_plugin -class FooterPlugin(CMSPluginBase): - name = "Rodapé" - render_template = "campaign/plugins/footer.html" - allow_children = False diff --git a/contrib/campaign/cms_wizards.py b/contrib/campaign/cms_wizards.py index e0212a4d..2d33dde2 100644 --- a/contrib/campaign/cms_wizards.py +++ b/contrib/campaign/cms_wizards.py @@ -1,68 +1,68 @@ -from django import forms +# from django import forms -from cms.forms.wizards import CreateCMSPageForm -# from cms.admin.forms import AddPageForm -from cms.wizards.wizard_base import Wizard -from cms.wizards.wizard_pool import wizard_pool -from cms.models import Page -from cms.utils.page_permissions import user_can_add_page, user_can_add_subpage +# from cms.forms.wizards import CreateCMSPageForm +# # from cms.admin.forms import AddPageForm +# from cms.wizards.wizard_base import Wizard +# from cms.wizards.wizard_pool import wizard_pool +# from cms.models import Page +# from cms.utils.page_permissions import user_can_add_page, user_can_add_subpage -# from .forms.wizards import CreateCMSPageForm, CreateCMSSubPageForm -# from .wizards.wizard_base import Wizard -# from .wizards.wizard_pool import wizard_pool -from .forms import CreatePressureForm +# # from .forms.wizards import CreateCMSPageForm, CreateCMSSubPageForm +# # from .wizards.wizard_base import Wizard +# # from .wizards.wizard_pool import wizard_pool +# from .forms import CreatePressureForm -class CMSPressureWizard(Wizard): +# class CMSPressureWizard(Wizard): - def user_has_add_permission(self, user, page=None, **kwargs): - if page: - parent_page = page.get_parent_page() - else: - parent_page = None +# def user_has_add_permission(self, user, page=None, **kwargs): +# if page: +# parent_page = page.get_parent_page() +# else: +# parent_page = None - if page and page.get_parent_page(): - # User is adding a page which will be a right - # sibling to the current page. - has_perm = user_can_add_subpage(user, target=parent_page) - else: - has_perm = user_can_add_page(user) - return has_perm +# if page and page.get_parent_page(): +# # User is adding a page which will be a right +# # sibling to the current page. +# has_perm = user_can_add_subpage(user, target=parent_page) +# else: +# has_perm = user_can_add_page(user) +# return has_perm -class CMSDonationWizard(Wizard): +# class CMSDonationWizard(Wizard): - def user_has_add_permission(self, user, page=None, **kwargs): - if page: - parent_page = page.get_parent_page() - else: - parent_page = None +# def user_has_add_permission(self, user, page=None, **kwargs): +# if page: +# parent_page = page.get_parent_page() +# else: +# parent_page = None - if page and page.get_parent_page(): - # User is adding a page which will be a right - # sibling to the current page. - has_perm = user_can_add_subpage(user, target=parent_page) - else: - has_perm = user_can_add_page(user) - return has_perm +# if page and page.get_parent_page(): +# # User is adding a page which will be a right +# # sibling to the current page. +# has_perm = user_can_add_subpage(user, target=parent_page) +# else: +# has_perm = user_can_add_page(user) +# return has_perm -cms_pressure_wizard = CMSPressureWizard( - title="Campanha de Pressão", - weight=100, - form=CreatePressureForm, - model=Page, - description="Pressão por e-mail Explica tática" -) +# cms_pressure_wizard = CMSPressureWizard( +# title="Campanha de Pressão", +# weight=100, +# form=CreatePressureForm, +# model=Page, +# description="Pressão por e-mail Explica tática" +# ) -cms_donation_wizard = CMSDonationWizard( - title="Campanha de Doação", - weight=100, - form=CreatePressureForm, - model=Page, - description="Doação Explica tática" -) +# cms_donation_wizard = CMSDonationWizard( +# title="Campanha de Doação", +# weight=100, +# form=CreatePressureForm, +# model=Page, +# description="Doação Explica tática" +# ) -wizard_pool.register(cms_pressure_wizard) -wizard_pool.register(cms_donation_wizard) \ No newline at end of file +# wizard_pool.register(cms_pressure_wizard) +# wizard_pool.register(cms_donation_wizard) \ No newline at end of file diff --git a/contrib/campaign/forms.py b/contrib/campaign/forms.py index 93ad06a3..ed8c47d5 100644 --- a/contrib/campaign/forms.py +++ b/contrib/campaign/forms.py @@ -1,106 +1,16 @@ +from typing import Any, Dict from django import forms from django.db import transaction -from cms.forms.wizards import CreateCMSPageForm +# from cms.forms.wizards import CreateCMSPageForm from djangocms_text_ckeditor.widgets import TextEditorWidget +from tailwind.widgets import RadioSelect, CheckboxSelectMultiple +from tailwind.fields import InputArrayField -class CreatePressureForm(CreateCMSPageForm): - content = None - storytelling = forms.CharField(label="Narrativa", widget=TextEditorWidget) - - def get_template(self): - return "campaign/modelo1.html" - - @transaction.atomic - def save(self, content=None, **kwargs): - from cms.api import add_plugin - - new_page = super(CreatePressureForm, self).save(**kwargs) - new_page.rescan_placeholders() - - slot = "content" - - placeholder = self.get_placeholder(new_page, slot=slot) - - # Trabalhando bloco principal - hero_block_plugin = add_plugin( - placeholder=placeholder, - plugin_type="BlockPlugin", - language=self.language_code, - title="Hero", - slug="hero", - background="#e40523", - ) - - # Trabalhando bloco de pressão - pressure_block_plugin = add_plugin( - placeholder=placeholder, - plugin_type="GridBlockPlugin", - language=self.language_code, - title="Action", - slug="action", - background="black", - ) - - row_plugin = add_plugin( - placeholder=placeholder, - language=self.language_code, - target=pressure_block_plugin, - plugin_type="RowPlugin", - ) - - col_left_plugin = add_plugin( - placeholder=placeholder, - language=self.language_code, - target=row_plugin, - plugin_type="ColumnPlugin", - ) - storytelling = self.cleaned_data.get("storytelling") - add_plugin( - placeholder=placeholder, - language=self.language_code, - target=col_left_plugin, - plugin_type="TextPlugin", - body=storytelling, - ) - - col_right_plugin = add_plugin( - placeholder=placeholder, - language=self.language_code, - target=row_plugin, - plugin_type="ColumnPlugin", - ) - add_plugin( - placeholder=placeholder, - language=self.language_code, - target=col_right_plugin, - plugin_type="PressurePlugin", - ) - - # Trabalhando bloco de assinatura - signature_block_plugin = add_plugin( - placeholder=placeholder, - plugin_type="BlockPlugin", - language=self.language_code, - title="Signature", - slug="signature", - ) - - # content = self.cleaned_data.get("storytelling") - # add_plugin( - # placeholder=placeholder, - # plugin_type="TextPlugin", - # language=self.language_code, - # target=hero_block_plugin, - # body=content - # ) - - return new_page +from contrib.bonde.widgets import BondeWidget - # import ipdb;ipdb.set_trace() - # Criar página padrão para tipo de form - # pass +from .models import Pressure, SharingChoices class PressureForm(forms.Form): @@ -147,3 +57,41 @@ def __init__(self, *args, **kwargs): if isinstance(visible.field.widget, forms.Textarea): visible.field.widget.attrs["class"] += " h-28" + + +class PressureSettingsForm(forms.ModelForm): + # widget = forms.IntegerField(widget=forms.Select) + + email_subject = InputArrayField( + label="Assunto do e-mail para os alvos", + num_widgets=10 + ) + + disable_editing = forms.ChoiceField( + label="Desabilitar edição do e-mail e do assunto pelos ativistas?", + choices=( + (True, "Desabilitar"), + (False, "Habilitar") + ), + widget=RadioSelect, + initial=True + ) + + sharing = forms.MultipleChoiceField( + label="Opções de compartilhamento", + choices=SharingChoices.choices, + widget=CheckboxSelectMultiple + ) + + class Meta: + model = Pressure + exclude = ["widget"] + + def clean(self): + cleaned_data = super(PressureSettingsForm, self).clean() + # import ipdb;ipdb.set_trace() + return cleaned_data + + def save(self, commit): + import ipdb;ipdb.set_trace() + return super(PressureSettingsForm, self).save(commit) \ No newline at end of file diff --git a/contrib/campaign/migrations/0008_auto_20230605_1417.py b/contrib/campaign/migrations/0008_auto_20230605_1417.py new file mode 100644 index 00000000..c8fce21f --- /dev/null +++ b/contrib/campaign/migrations/0008_auto_20230605_1417.py @@ -0,0 +1,43 @@ +# Generated by Django 3.2 on 2023-06-05 14:17 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0022_auto_20180620_1551'), + ('campaign', '0007_delete_block'), + ] + + operations = [ + migrations.CreateModel( + name='Pressure', + fields=[ + ('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='campaign_pressure', serialize=False, to='cms.cmsplugin')), + ('widget', models.IntegerField(blank=True, null=True)), + ], + options={ + 'abstract': False, + }, + bases=('cms.cmsplugin',), + ), + migrations.RemoveField( + model_name='column', + name='cmsplugin_ptr', + ), + migrations.RemoveField( + model_name='row', + name='cmsplugin_ptr', + ), + migrations.DeleteModel( + name='ActionButton', + ), + migrations.DeleteModel( + name='Column', + ), + migrations.DeleteModel( + name='Row', + ), + ] diff --git a/contrib/campaign/migrations/0009_auto_20230605_1704.py b/contrib/campaign/migrations/0009_auto_20230605_1704.py new file mode 100644 index 00000000..7b539e5f --- /dev/null +++ b/contrib/campaign/migrations/0009_auto_20230605_1704.py @@ -0,0 +1,85 @@ +# Generated by Django 3.2 on 2023-06-05 17:04 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('campaign', '0008_auto_20230605_1417'), + ] + + operations = [ + migrations.CreateModel( + name='Target', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='Nome do alvo')), + ('email', models.EmailField(blank=True, max_length=100, null=True, verbose_name='Email do alvo')), + ('phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Telefone do alvo')), + ], + ), + migrations.AddField( + model_name='pressure', + name='disable_editing', + field=models.BooleanField(default=True, verbose_name='Desabilitar edição do e-mail e do assunto pelos ativistas?'), + ), + migrations.AddField( + model_name='pressure', + name='email_body', + field=models.TextField(default='', verbose_name='Corpo do e-mail de agradecimento'), + preserve_default=False, + ), + migrations.AddField( + model_name='pressure', + name='email_subject', + field=models.CharField(default='', max_length=120, verbose_name='Assunto do e-mail de agradecimento para quem vai pressionar'), + preserve_default=False, + ), + migrations.AddField( + model_name='pressure', + name='sender_email', + field=models.EmailField(default='', max_length=254, verbose_name='Email de resposta'), + preserve_default=False, + ), + migrations.AddField( + model_name='pressure', + name='sender_name', + field=models.CharField(default='', max_length=120, verbose_name='Remetente'), + preserve_default=False, + ), + migrations.AddField( + model_name='pressure', + name='sharing', + field=models.CharField(choices=[('whatsapp', 'Whatsapp'), ('twitter', 'Twitter'), ('facebook', 'Facebook')], default='', max_length=50, verbose_name='Opções de compartilhamento'), + preserve_default=False, + ), + migrations.AddField( + model_name='pressure', + name='submissions_interval', + field=models.IntegerField(blank=True, null=True, verbose_name='Intervalo de envio'), + ), + migrations.AddField( + model_name='pressure', + name='submissions_limit', + field=models.IntegerField(blank=True, null=True, verbose_name='Limite de envios únicos'), + ), + migrations.AddField( + model_name='pressure', + name='whatsapp_text', + field=models.TextField(default='', verbose_name='Mensagem para o whatsapp'), + preserve_default=False, + ), + migrations.CreateModel( + name='TargetGroup', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50, verbose_name='Nome do grupo')), + ('email_subject', models.CharField(blank=True, max_length=120, null=True, verbose_name='Assunto do e-mail para os alvos')), + ('email_body', models.TextField(blank=True, null=True, verbose_name='Corpo do e-mail para os alvos')), + ('pressure', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='campaign.pressure')), + ('targets', models.ManyToManyField(to='campaign.Target')), + ], + ), + ] diff --git a/contrib/campaign/migrations/0010_auto_20230607_1917.py b/contrib/campaign/migrations/0010_auto_20230607_1917.py new file mode 100644 index 00000000..94ab9a7c --- /dev/null +++ b/contrib/campaign/migrations/0010_auto_20230607_1917.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2 on 2023-06-07 19:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('campaign', '0009_auto_20230605_1704'), + ] + + operations = [ + migrations.RemoveField( + model_name='targetgroup', + name='email_body', + ), + migrations.RemoveField( + model_name='targetgroup', + name='email_subject', + ), + ] diff --git a/contrib/campaign/migrations/0011_auto_20230607_2053.py b/contrib/campaign/migrations/0011_auto_20230607_2053.py new file mode 100644 index 00000000..cc5bf2a5 --- /dev/null +++ b/contrib/campaign/migrations/0011_auto_20230607_2053.py @@ -0,0 +1,36 @@ +# Generated by Django 3.2 on 2023-06-07 20:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('campaign', '0010_auto_20230607_1917'), + ] + + operations = [ + migrations.AlterModelOptions( + name='target', + options={'verbose_name': 'Alvo', 'verbose_name_plural': 'Alvos'}, + ), + migrations.AlterModelOptions( + name='targetgroup', + options={'verbose_name': 'Grupo de alvos', 'verbose_name_plural': 'Grupos de alvos'}, + ), + migrations.RenameField( + model_name='pressure', + old_name='email_body', + new_name='thank_email_body', + ), + migrations.RenameField( + model_name='pressure', + old_name='email_subject', + new_name='thank_email_subject', + ), + migrations.AlterField( + model_name='targetgroup', + name='targets', + field=models.ManyToManyField(to='campaign.Target', verbose_name='Alvos'), + ), + ] diff --git a/contrib/campaign/migrations/0012_auto_20230607_2056.py b/contrib/campaign/migrations/0012_auto_20230607_2056.py new file mode 100644 index 00000000..10b5144f --- /dev/null +++ b/contrib/campaign/migrations/0012_auto_20230607_2056.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2 on 2023-06-07 20:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('campaign', '0011_auto_20230607_2053'), + ] + + operations = [ + migrations.AddField( + model_name='pressure', + name='email_body', + field=models.TextField(default='', verbose_name='Corpo do e-mail para os alvos'), + preserve_default=False, + ), + migrations.AddField( + model_name='pressure', + name='email_subject', + field=models.JSONField(default='', verbose_name='Assunto do e-mail para os alvos'), + preserve_default=False, + ), + ] diff --git a/contrib/campaign/models.py b/contrib/campaign/models.py index 26bb3526..a9c8e01a 100644 --- a/contrib/campaign/models.py +++ b/contrib/campaign/models.py @@ -3,93 +3,92 @@ from cms.models import CMSPlugin -class BlockBase(CMSPlugin): - title = models.CharField("título", max_length=80, blank=True) - slug = models.SlugField( - verbose_name="slug", - max_length=80, - blank=True, - help_text="a parte do título que é usada na URL", - ) - menu_title = models.CharField("título do menu", max_length=50, blank=True) - menu_hidden = models.BooleanField("esconder menu?", default=False) - - # Styles - background = models.CharField( - "background", max_length=200, default="white", blank=True +class Thank(models.Model): + # Agradecimento + thank_email_subject = models.CharField( + verbose_name="Assunto do e-mail de agradecimento para quem vai pressionar", + max_length=120, ) + thank_email_body = models.TextField(verbose_name="Corpo do e-mail de agradecimento") + sender_name = models.CharField(verbose_name="Remetente", max_length=120) + sender_email = models.EmailField(verbose_name="Email de resposta") class Meta: abstract = True - def __str__(self): - return self.title - def get_menu_title(self): - return self.menu_title or self.title +class SharingChoices(models.TextChoices): + whatsapp = "whatsapp", "Whatsapp" + twitter = "twitter", "Twitter" + facebook = "facebook", "Facebook" - def get_background(self): - if self.background.startswith("url"): - return f"bg-[{self.background}] bg-no-repeat bg-cover" - - return f"bg-[{self.background}]" +class PostAction(models.Model): + # Pós ação + sharing = models.CharField( + verbose_name="Opções de compartilhamento", + choices=SharingChoices.choices, + max_length=50, + ) + whatsapp_text = models.TextField(verbose_name="Mensagem para o whatsapp") + + class Meta: + abstract = True -# class Block(BlockBase): -# pass -class ActionButton(CMSPlugin): - title = models.CharField("título", max_length=80) - action_url = models.CharField( - "endereço da ação", max_length=80, help_text="slug do bloco usado na URL" +class Target(models.Model): + name = models.CharField(verbose_name="Nome do alvo", max_length=100) + email = models.EmailField( + verbose_name="Email do alvo", max_length=100, null=True, blank=True ) - bg_color = models.CharField( - "cor do fundo", max_length=100, default="blue", blank=True + phone = models.CharField( + verbose_name="Telefone do alvo", max_length=20, null=True, blank=True ) - def get_bg_color(self): - if self.bg_color.startswith("bg-"): - return self.bg_color - - return f"bg-{{self.bg_color}}-800 hover:bg-{{self.bg_color}}-900" + class Meta: + verbose_name ="Alvo" + verbose_name_plural = "Alvos" -class RowStyles(models.TextChoices): - flex = ("flex", "Flex") - wrap = ("wrap", "Wrap") +class Pressure(PostAction, Thank, CMSPlugin): + widget = models.IntegerField(null=True, blank=True) + email_subject = models.JSONField( + verbose_name="Assunto do e-mail para os alvos" + ) + email_body = models.TextField( + verbose_name="Corpo do e-mail para os alvos" + ) -class Row(CMSPlugin): - styled = models.CharField( - "Estilo da linha", - max_length=20, - choices=RowStyles.choices, - default=RowStyles.flex + # Envio + submissions_limit = models.IntegerField( + verbose_name="Limite de envios únicos", null=True, blank=True + ) + submissions_interval = models.IntegerField( + verbose_name="Intervalo de envio", null=True, blank=True ) - def classnames(self, attrs=None): - if self.styled == RowStyles.flex: - return 'flex flex-row items-center gap-8' - elif self.styled == RowStyles.wrap: - return 'flex flex-wrap gap-8 justify-center' + disable_editing = models.BooleanField( + verbose_name="Desabilitar edição do e-mail e do assunto pelos ativistas?", + default=True, + ) - return '' + def copy_relations(self, old_instance): + # https://docs.django-cms.org/en/latest/how_to/custom_plugins.html#handling-relations + self.targetgroup_set.delete() + for item in old_instance.targetgroup_set.all(): + item.pk = None + item.plugin = self + item.save() -class ColumnStyles(models.TextChoices): - auto = ("auto", "Auto") +class TargetGroup(models.Model): + name = models.CharField(verbose_name="Nome do grupo", max_length=50) + targets = models.ManyToManyField(Target, verbose_name="Alvos") -class Column(CMSPlugin): - styled = models.CharField( - "Estilho da coluna", - max_length=20, - choices=ColumnStyles.choices, - default=ColumnStyles.auto - ) + pressure = models.ForeignKey(Pressure, on_delete=models.CASCADE) - def classnames(self, attrs=None): - if self.styled == ColumnStyles.auto: - return 'flex-auto' - - return '' \ No newline at end of file + class Meta: + verbose_name = "Grupo de alvos" + verbose_name_plural = "Grupos de alvos" diff --git a/contrib/campaign/templates/campaign/admin/change_form.html b/contrib/campaign/templates/campaign/admin/change_form.html new file mode 100644 index 00000000..54450855 --- /dev/null +++ b/contrib/campaign/templates/campaign/admin/change_form.html @@ -0,0 +1,133 @@ +{% extends "admin/change_form.html" %} +{% load i18n admin_urls static admin_modify %} + +{% block extrastyle %} +{{ block.super }} + + + +{% endblock %} + +{% block content %} +