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
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