예제 #1
0
class FormDefinition(models.Model):
    name = models.SlugField(_('name'), max_length=255, unique=True)
    use_djangular = models.BooleanField(
        _('use django-angular form validation'), default=False)
    require_hash = models.BooleanField(
        _('obfuscate URL to this form'),
        default=False,
        help_text=_(
            'If enabled, the form can only be reached via a secret URL.'))
    private_hash = models.CharField(editable=False, max_length=40, default='')
    public_hash = models.CharField(editable=False, max_length=40, default='')
    title = models.CharField(_('title'), max_length=255, blank=True, null=True)
    body = models.TextField(_('body'), blank=True, null=True)
    action = models.URLField(
        _('target URL'),
        help_text=
        _('If you leave this empty, the page where the form resides will be requested, and you can use the mail form and logging features. You can also send data to external sites: For instance, enter "http://www.google.ch/search" to create a search form.'
          ),
        max_length=255,
        blank=True,
        null=True)
    mail_to = TemplateCharField(
        _('send form data to e-mail address'),
        help_text=
        ('Separate several addresses with a comma. Your form fields are available as template context. Example: "[email protected], {{ from_email }}" if you have a field named `from_email`.'
         ),
        max_length=255,
        blank=True,
        null=True)
    mail_from = TemplateCharField(
        _('sender address'),
        max_length=255,
        help_text=
        ('Your form fields are available as template context. Example: "{{ first_name }} {{ last_name }} <{{ from_email }}>" if you have fields named `first_name`, `last_name`, `from_email`.'
         ),
        blank=True,
        null=True)
    mail_subject = TemplateCharField(
        _('email subject'),
        max_length=255,
        help_text=
        ('Your form fields are available as template context. Example: "Contact form {{ subject }}" if you have a field named `subject`.'
         ),
        blank=True,
        null=True)
    mail_uploaded_files = models.BooleanField(
        _('Send uploaded files as email attachments'), default=True)
    method = models.CharField(_('method'),
                              max_length=10,
                              default="POST",
                              choices=(('POST', 'POST'), ('GET', 'GET')))
    success_message = models.CharField(_('success message'),
                                       max_length=255,
                                       blank=True,
                                       null=True)
    error_message = models.CharField(_('error message'),
                                     max_length=255,
                                     blank=True,
                                     null=True)
    submit_label = models.CharField(_('submit button label'),
                                    max_length=255,
                                    blank=True,
                                    null=True)
    log_data = models.BooleanField(
        _('log form data'),
        help_text=_('Logs all form submissions to the database.'),
        default=True)
    save_uploaded_files = models.BooleanField(
        _('save uploaded files'),
        help_text=_('Saves all uploaded files using server storage.'),
        default=True)
    success_redirect = models.BooleanField(
        _('HTTP redirect after successful submission'), default=True)
    success_clear = models.BooleanField(
        _('clear form after successful submission'), default=True)
    allow_get_initial = models.BooleanField(
        _('allow initial values via URL'),
        help_text=
        _('If enabled, you can fill in form fields by adding them to the query string.'
          ),
        default=True)
    message_template = TemplateTextField(
        _('message template'),
        help_text=
        _('Your form fields are available as template context. Example: "{{ message }}" if you have a field named `message`. To iterate over all fields, use the variable `data` (a list containing a dictionary for each form field, each containing the elements `name`, `label`, `value`).'
          ),
        blank=True,
        null=True)
    form_template_name = models.CharField(_('form template'),
                                          max_length=255,
                                          choices=settings.FORM_TEMPLATES,
                                          blank=True,
                                          null=True)
    display_logged = models.BooleanField(
        _('display logged submissions with form'), default=False)

    class Meta:
        verbose_name = _('Form')
        verbose_name_plural = _('Forms')

    def save(self, *args, **kwargs):
        if not self.private_hash:
            self.private_hash = hashlib.sha1(str(uuid.uuid4())).hexdigest()
        if not self.public_hash:
            self.public_hash = hashlib.sha1(str(uuid.uuid4())).hexdigest()
        super(FormDefinition, self).save()

    def get_field_dict(self):
        field_dict = SortedDict()
        names = []
        for field in self.formdefinitionfield_set.all():
            field_dict[field.name] = field
        return field_dict

    @models.permalink
    def get_absolute_url(self):
        if self.require_hash:
            return ('form_designer.views.detail_by_hash',
                    [str(self.public_hash)])
        return ('form_designer.views.detail', [str(self.name)])

    def get_form_data(self, form):
        # TODO: refactor, move to utils or views
        data = []
        field_dict = self.get_field_dict()
        form_keys = form.fields.keys()
        def_keys = field_dict.keys()
        for key in form_keys:
            if key in def_keys and field_dict[key].include_result:
                value = form.cleaned_data[key]
                if getattr(value, '__form_data__', False):
                    value = value.__form_data__()
                data.append(FormValueDict(key, value, form.fields[key].label))
        return data

    def get_form_data_context(self, form_data):
        # TODO: refactor, move to utils
        dict = {}
        if form_data:
            for field in form_data:
                dict[field['name']] = field['value']
        return dict

    def compile_message(self, form_data, template=None):
        # TODO: refactor, move to utils
        from django.template.loader import get_template
        from django.template import Context, Template
        if template:
            t = get_template(template)
        elif not self.message_template:
            t = get_template('txt/formdefinition/data_message.txt')
        else:
            t = Template(self.message_template)
        context = Context(self.get_form_data_context(form_data))
        context['data'] = form_data
        return t.render(context)

    def count_fields(self):
        return self.formdefinitionfield_set.count()

    count_fields.short_description = _('Fields')

    def __unicode__(self):
        return self.title or self.name

    def log(self, form, user=None):
        form_data = self.get_form_data(form)
        created_by = None
        if user and user.is_authenticated():
            created_by = user
        FormLog(form_definition=self, data=form_data,
                created_by=created_by).save()

    def string_template_replace(self, text, context_dict):
        # TODO: refactor, move to utils
        from django.template import Context, Template, TemplateSyntaxError
        try:
            t = Template(text)
            return t.render(Context(context_dict))
        except TemplateSyntaxError:
            return text

    def send_mail(self, form, files=[]):
        # TODO: refactor, move to utils
        form_data = self.get_form_data(form)
        message = self.compile_message(form_data)
        context_dict = self.get_form_data_context(form_data)

        mail_to = re.compile('\s*[,;]+\s*').split(self.mail_to)
        for key, email in enumerate(mail_to):
            mail_to[key] = self.string_template_replace(email, context_dict)

        mail_from = self.mail_from or None
        if mail_from:
            mail_from = self.string_template_replace(mail_from, context_dict)

        if self.mail_subject:
            mail_subject = self.string_template_replace(
                self.mail_subject, context_dict)
        else:
            mail_subject = self.title

        from django.core.mail import EmailMessage
        message = EmailMessage(mail_subject, message, mail_from or None,
                               mail_to)

        if self.mail_uploaded_files:
            for file_path in files:
                message.attach_file(file_path)

        message.send(fail_silently=False)

    @property
    def submit_flag_name(self):
        name = settings.SUBMIT_FLAG_NAME % self.name
        # make sure we are not overriding one of the actual form fields
        while self.formdefinitionfield_set.filter(
                name__exact=name).count() > 0:
            name += '_'
        return name
예제 #2
0
class FormDefinition(models.Model):
    name = models.SlugField(_('name'), max_length=255, unique=True)
    require_hash = models.BooleanField(_('obfuscate URL to this form'), default=False, help_text=_('If enabled, the form can only be reached via a secret URL.'))
    private_hash = models.CharField(editable=False, max_length=40, default='')
    public_hash = models.CharField(editable=False, max_length=40, default='')
    title = models.CharField(_('title'), max_length=255, blank=True, null=True)
    body = models.TextField(_('body'), help_text=_('Form description. Display on form after title.'), blank=True, null=True)
    action = models.URLField(_('target URL'), help_text=_('If you leave this empty, the page where the form resides will be requested, and you can use the mail form and logging features. You can also send data to external sites: For instance, enter "http://www.google.ch/search" to create a search form.'), max_length=255, blank=True, null=True)
    mail_cover_text = models.TextField(_('email cover text'), help_text=_('Email cover text which can be included in the default email template and in the message template.'), blank=True, null=True)
    mail_to = TemplateCharField(_('send form data to e-mail address'), help_text=_('Separate several addresses with a comma. Your form fields are available as template context. Example: "[email protected], {{ from_email }}" if you have a field named `from_email`.'), max_length=255, blank=True, null=True)
    mail_from = TemplateCharField(_('sender address'), max_length=255, help_text=MAIL_TEMPLATE_CONTEXT_HELP_TEXT, blank=True, null=True)
    mail_reply_to = TemplateCharField(_('reply-to address'), max_length=255, help_text=MAIL_TEMPLATE_CONTEXT_HELP_TEXT, blank=True)
    mail_subject = TemplateCharField(_('email subject'), max_length=255, help_text=_('Your form fields are available as template context. Example: "Contact form {{ subject }}" if you have a field named `subject`.'), blank=True, null=True)
    mail_uploaded_files = models.BooleanField(_('Send uploaded files as email attachments'), default=True)
    method = models.CharField(_('method'), max_length=10, default="POST", choices=(('POST', 'POST'), ('GET', 'GET')))
    success_message = models.CharField(_('success message'), max_length=255, blank=True, null=True)
    error_message = models.CharField(_('error message'), max_length=255, blank=True, null=True)
    submit_label = models.CharField(_('submit button label'), max_length=255, blank=True, null=True)
    log_data = models.BooleanField(_('log form data'), help_text=_('Logs all form submissions to the database.'), default=True)
    save_uploaded_files = models.BooleanField(_('save uploaded files'), help_text=_('Saves all uploaded files using server storage.'), default=True)
    success_redirect = models.BooleanField(_('HTTP redirect after successful submission'), default=True)
    success_clear = models.BooleanField(_('clear form after successful submission'), default=True)
    allow_get_initial = models.BooleanField(_('allow initial values via URL'), help_text=_('If enabled, you can fill in form fields by adding them to the query string.'), default=True)
    message_template = TemplateTextField(_('message template'), help_text=_('Your form fields are available as template context. Example: "{{ message }}" if you have a field named `message`. To iterate over all fields, use the variable `data` (a list containing a dictionary for each form field, each containing the elements `name`, `label`, `value`). If you have set up email cover text, you can use {{ mail_cover_text }} to access it.'), blank=True, null=True)
    form_template_name = models.CharField(_('form template'), max_length=255, blank=True, null=True)
    display_logged = models.BooleanField(_('display logged submissions with form'), default=False)

    class Meta:
        verbose_name = _('Form')
        verbose_name_plural = _('Forms')

    def save(self, *args, **kwargs):
        if not self.private_hash:
            self.private_hash = get_random_hash()
        if not self.public_hash:
            self.public_hash = get_random_hash()
        super().save()

    def get_field_dict(self):
        field_dict = OrderedDict()
        for field in self.formdefinitionfield_set.all():
            field_dict[field.name] = field
        return field_dict

    def get_absolute_url(self):
        if self.require_hash:
            return reverse('form_designer.views.detail_by_hash', [str(self.public_hash)])
        return reverse('form_designer.views.detail', [str(self.name)])

    def get_form_data(self, form):
        # TODO: refactor, move to utils or views
        data = []
        field_dict = self.get_field_dict()
        form_keys = form.fields.keys()
        def_keys = field_dict.keys()
        for key in form_keys:
            if key in def_keys and field_dict[key].include_result:
                value = form.cleaned_data[key]
                if getattr(value, '__form_data__', False):
                    value = value.__form_data__()
                data.append(FormValueDict(key, value, form.fields[key].label))
        return data

    def get_form_data_context(self, form_data):
        # TODO: refactor, move to utils
        dict = {}
        for field in form_data or ():
            dict[field['name']] = field['value']
        return dict

    def compile_message(self, form_data, template=None):
        # TODO: refactor, move to utils
        if template:
            t = get_template(template)
        elif not self.message_template:
            t = get_template(settings.EMAIL_TEMPLATE)
        else:
            t = get_django_template_from_string(self.message_template)
        context = self.get_form_data_context(form_data)
        context['data'] = form_data
        context['mail_cover_text'] = self.mail_cover_text or ''
        return t.render(context)

    def count_fields(self):
        return self.formdefinitionfield_set.count()

    count_fields.short_description = _('Fields')

    def __str__(self):
        return self.title or self.name

    def log(self, form, user=None):
        form_data = self.get_form_data(form)
        created_by = None

        if user and user.is_authenticated:
            created_by = user

        flog = FormLog(form_definition=self, data=form_data, created_by=created_by)
        flog.save()
        return flog

    @warn_about_renamed_method(
        'FormDefinition', 'string_template_replace', 'form_designer.utils.string_template_replace',
        DeprecationWarning
    )
    def string_template_replace(self, text, context_dict):
        return string_template_replace(text, context_dict)

    def send_mail(self, form, files=None):
        if not self.mail_to:
            return
        from form_designer.email import build_form_mail
        message = build_form_mail(form_definition=self, form=form, files=files)
        message.send(fail_silently=False)
        return message

    @property
    def is_template_html(self):
        template = self.message_template
        if template:  # We have a custom inline template string?
            # Assume the template string is HTML-ish if it has at least one opening
            # and closing HTML tag:
            return (re.search("<[^>]+>", template) and re.search("</[^>]+>", template))

        # If there is no custom inline template, see if the `EMAIL_TEMPLATE`
        # setting points to a `.html` file:
        return settings.EMAIL_TEMPLATE.lower().endswith('.html')

    @property
    def submit_flag_name(self):
        name = settings.SUBMIT_FLAG_NAME % self.name
        # make sure we are not overriding one of the actual form fields
        while self.formdefinitionfield_set.filter(name__exact=name).count() > 0:
            name += '_'
        return name