From d12ee4e0ec425e7f508c93ad1295a21a67ae1a30 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Wed, 20 Feb 2013 11:49:53 +0100 Subject: [PATCH] Contact forms. --- .../lesson/appendix/lesson_detail.html | 6 ++ .../catalogue/lesson/lesson_detail.html | 9 ++ chunks/templatetags/chunks.py | 59 ++++------- contact/__init__.py | 52 ++++++++++ contact/admin.py | 94 ++++++++++++++++++ contact/forms.py | 85 ++++++++++++++++ contact/locale/pl/LC_MESSAGES/django.mo | Bin 0 -> 1537 bytes contact/locale/pl/LC_MESSAGES/django.po | 81 +++++++++++++++ contact/migrations/0001_initial.py | 40 ++++++++ .../migrations/0002_auto__add_attachment.py | 51 ++++++++++ contact/migrations/__init__.py | 0 contact/models.py | 35 +++++++ contact/templates/contact/form.html | 24 +++++ contact/templates/contact/mail_body.txt | 6 ++ .../templates/contact/mail_managers_body.txt | 12 +++ .../contact/mail_managers_subject.txt | 1 + contact/templates/contact/mail_subject.txt | 1 + contact/templates/contact/thanks.html | 14 +++ contact/urls.py | 9 ++ contact/views.py | 40 ++++++++ edumed/contact_forms.py | 48 +++++++++ edumed/settings.d/30-apps.py | 3 +- edumed/settings.d/40-middleware.py | 1 + edumed/settings.d/50-contrib.py | 2 + edumed/settings.d/50-static.py | 1 + edumed/settings.d/60-custom.py | 1 + edumed/static/css/form.css | 14 +++ edumed/static/css/form.scss | 20 ++++ .../templates/contact/konkurs/mail_body.txt | 10 ++ .../contact/konkurs/mail_subject.txt | 1 + edumed/templates/contact/konkurs/thanks.html | 5 + .../templates/contact/sugestie/mail_body.txt | 10 ++ .../contact/sugestie/mail_subject.txt | 1 + edumed/templates/contact/sugestie/thanks.html | 5 + edumed/urls.py | 1 + requirements.txt | 2 + 36 files changed, 701 insertions(+), 43 deletions(-) create mode 100644 contact/__init__.py create mode 100644 contact/admin.py create mode 100644 contact/forms.py create mode 100644 contact/locale/pl/LC_MESSAGES/django.mo create mode 100644 contact/locale/pl/LC_MESSAGES/django.po create mode 100644 contact/migrations/0001_initial.py create mode 100644 contact/migrations/0002_auto__add_attachment.py create mode 100644 contact/migrations/__init__.py create mode 100644 contact/models.py create mode 100644 contact/templates/contact/form.html create mode 100644 contact/templates/contact/mail_body.txt create mode 100644 contact/templates/contact/mail_managers_body.txt create mode 100644 contact/templates/contact/mail_managers_subject.txt create mode 100644 contact/templates/contact/mail_subject.txt create mode 100644 contact/templates/contact/thanks.html create mode 100644 contact/urls.py create mode 100644 contact/views.py create mode 100644 edumed/contact_forms.py create mode 100644 edumed/static/css/form.css create mode 100755 edumed/static/css/form.scss create mode 100644 edumed/templates/contact/konkurs/mail_body.txt create mode 100644 edumed/templates/contact/konkurs/mail_subject.txt create mode 100644 edumed/templates/contact/konkurs/thanks.html create mode 100644 edumed/templates/contact/sugestie/mail_body.txt create mode 100644 edumed/templates/contact/sugestie/mail_subject.txt create mode 100644 edumed/templates/contact/sugestie/thanks.html diff --git a/catalogue/templates/catalogue/lesson/appendix/lesson_detail.html b/catalogue/templates/catalogue/lesson/appendix/lesson_detail.html index b4bbc94..fae3bcb 100755 --- a/catalogue/templates/catalogue/lesson/appendix/lesson_detail.html +++ b/catalogue/templates/catalogue/lesson/appendix/lesson_detail.html @@ -17,3 +17,9 @@ {% endblock %} + +{% block suggest-link %} + + Zgłoś swoją uwagę na temat tej strony. + +{% endblock %} diff --git a/catalogue/templates/catalogue/lesson/lesson_detail.html b/catalogue/templates/catalogue/lesson/lesson_detail.html index 8eec0da..dfd844e 100755 --- a/catalogue/templates/catalogue/lesson/lesson_detail.html +++ b/catalogue/templates/catalogue/lesson/lesson_detail.html @@ -102,6 +102,15 @@ {% endif %}
+ +

+ {% block suggest-link %} + + Zgłoś swoją uwagę na temat tej lekcji. + + {% endblock %} +

+ diff --git a/chunks/templatetags/chunks.py b/chunks/templatetags/chunks.py index 083c48a..fd24b02 100644 --- a/chunks/templatetags/chunks.py +++ b/chunks/templatetags/chunks.py @@ -1,53 +1,29 @@ from django import template from django.db import models from django.core.cache import cache +from ..models import Chunk, Attachment register = template.Library() -Chunk = models.get_model('chunks', 'chunk') -Attachment = models.get_model('chunks', 'attachment') - - -def do_get_chunk(parser, token): - # split_contents() knows not to split quoted strings. - tokens = token.split_contents() - if len(tokens) < 2 or len(tokens) > 3: - raise template.TemplateSyntaxError, "%r tag should have either 2 or 3 arguments" % (tokens[0],) - if len(tokens) == 2: - tag_name, key = tokens - cache_time = 0 - if len(tokens) == 3: - tag_name, key, cache_time = tokens - # Check to see if the key is properly double/single quoted - if not (key[0] == key[-1] and key[0] in ('"', "'")): - raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name - # Send key without quotes and caching time - return ChunkNode(key[1:-1], cache_time) - - -class ChunkNode(template.Node): - def __init__(self, key, cache_time=0): - self.key = key - self.cache_time = cache_time - - def render(self, context): - try: - cache_key = 'chunk_' + self.key - c = cache.get(cache_key) - if c is None: - c = Chunk.objects.get(key=self.key) - cache.set(cache_key, c, int(self.cache_time)) - content = c.content - except Chunk.DoesNotExist: - n = Chunk(key=self.key) - n.save() - return '' - return content - -register.tag('chunk', do_get_chunk) + +@register.simple_tag +def chunk(key, cache_time=0): + try: + cache_key = 'chunk_' + key + c = cache.get(cache_key) + if c is None: + c = Chunk.objects.get(key=key) + cache.set(cache_key, c, int(cache_time)) + content = c.content + except Chunk.DoesNotExist: + n = Chunk(key=key) + n.save() + return '' + return content +@register.simple_tag def attachment(key, cache_time=0): try: cache_key = 'attachment_' + key @@ -59,5 +35,4 @@ def attachment(key, cache_time=0): except Attachment.DoesNotExist: return '' -register.simple_tag(attachment) diff --git a/contact/__init__.py b/contact/__init__.py new file mode 100644 index 0000000..0f043ed --- /dev/null +++ b/contact/__init__.py @@ -0,0 +1,52 @@ +""" +Generic app for creating contact forms. + +0. Add 'contact' to your INSTALLED_APPS and include 'contact.urls' somewhere +in your urls.py, like: + url(r'^contact/', + include('contact.urls')) + +1. Migrate. + +2. Create somewhere in your project a module with some subclasses of +contact.forms.ContactForm, specyfing form_tag and some fields in each. + +3. Set CONTACT_FORMS_MODULE in your settings to point to the module. + +4. Link to the form with {% url 'contact_form' form_tag %}. + +5. Optionally override some templates in form-specific template directories +(/contact//...). + +6. Receive submitted forms by email and read them in admin. + + +Example: +======== + +settings.py: + CONTACT_FORMS_MODULE = 'myproject.contact_forms' + +myproject/contact_forms.py: + from django import forms + from contact.forms import ContactForm + from django.utils.translation import ugettext_lazy as _ + + class RegistrationForm(ContactForm): + form_tag = 'register' + name = forms.CharField(label=_('Name'), max_length=128) + presentation = forms.FileField(label=_('Presentation')) + +some_template.html: + {% url 'contact:form' 'register' %} + +""" + +from fnpdjango.utils.app import AppSettings + + +class Settings(AppSettings): + FORMS_MODULE = "contact_forms" + + +app_settings = Settings('CONTACT') diff --git a/contact/admin.py b/contact/admin.py new file mode 100644 index 0000000..3776fc4 --- /dev/null +++ b/contact/admin.py @@ -0,0 +1,94 @@ +from django.contrib import admin +from django.forms import ModelForm +from .models import Contact +from django.utils.translation import ugettext as _ +from .forms import contact_forms, admin_list_width +from django.template import Template +from django.utils.safestring import mark_safe + + +class ContactAdminMeta(admin.ModelAdmin.__metaclass__): + def __getattr__(cls, name): + if name.startswith('admin_list_'): + return lambda self: "" + raise AttributeError, name + + +class ContactAdmin(admin.ModelAdmin): + __metaclass__ = ContactAdminMeta + date_hierarchy = 'created_at' + list_display = ['created_at', 'contact', 'form_tag'] + \ + ["admin_list_%d" % i for i in range(admin_list_width)] + fields = ['form_tag', 'created_at', 'contact', 'ip'] + readonly_fields = ['form_tag', 'created_at', 'contact', 'ip'] + list_filter = ['form_tag'] + + def admin_list(self, obj, nr): + try: + field_name = contact_forms[obj.form_tag].admin_list[nr] + except BaseException, e: + return '' + else: + return obj.body.get(field_name, '') + + def __getattr__(self, name): + if name.startswith('admin_list_'): + nr = int(name[len('admin_list_'):]) + return lambda obj: self.admin_list(obj, nr) + raise AttributeError, name + + def change_view(self, request, object_id, extra_context=None): + if object_id: + try: + instance = Contact.objects.get(pk=object_id) + assert isinstance(instance.body, dict) + except (Contact.DoesNotExist, AssertionError): + pass + else: + # Create readonly fields from the body JSON. + body_fields = ['body__%s' % k for k in instance.body.keys()] + attachments = list(instance.attachment_set.all()) + body_fields += ['body__%s' % a.tag for a in attachments] + self.readonly_fields.extend(body_fields) + + # Find the original form. + try: + orig_fields = contact_forms[instance.form_tag]().fields + except KeyError: + orig_fields = {} + + # Try to preserve the original order. + admin_fields = [] + orig_keys = list(orig_fields.keys()) + while orig_keys: + key = orig_keys.pop(0) + key = "body__%s" % key + if key in body_fields: + admin_fields.append(key) + body_fields.remove(key) + admin_fields.extend(body_fields) + + self.fieldsets = [ + (None, {'fields': self.fields}), + (_('Body'), {'fields': admin_fields}), + ] + + # Create field getters for fields and attachments. + for k, v in instance.body.items(): + f = (lambda v: lambda self: v)(v) + f.short_description = orig_fields[k].label if k in orig_fields else _(k) + setattr(self, "body__%s" % k, f) + + download_link = "%(url)s" + for attachment in attachments: + k = attachment.tag + link = mark_safe(download_link % { + 'url': attachment.get_absolute_url()}) + f = (lambda v: lambda self: v)(link) + f.short_description = orig_fields[k].label if k in orig_fields else _(k) + setattr(self, "body__%s" % k, f) + return super(ContactAdmin, self).change_view(request, object_id, + extra_context=extra_context) + + +admin.site.register(Contact, ContactAdmin) diff --git a/contact/forms.py b/contact/forms.py new file mode 100644 index 0000000..8e78eed --- /dev/null +++ b/contact/forms.py @@ -0,0 +1,85 @@ +from django.contrib.sites.models import Site +from django.core.files.uploadedfile import UploadedFile +from django.core.mail import send_mail, mail_managers +from django.core.validators import email_re +from django import forms +from django.template.loader import render_to_string +from django.template import RequestContext +from django.utils.translation import ugettext_lazy as _ + + +contact_forms = {} +admin_list_width = 0 +class ContactFormMeta(forms.Form.__metaclass__): + def __new__(cls, *args, **kwargs): + global admin_list_width + model = super(ContactFormMeta, cls).__new__(cls, *args, **kwargs) + assert model.form_tag not in contact_forms, 'Duplicate form_tag.' + if model.admin_list: + admin_list_width = max(admin_list_width, len(model.admin_list)) + contact_forms[model.form_tag] = model + return model + + +class ContactForm(forms.Form): + """Subclass and define some fields.""" + __metaclass__ = ContactFormMeta + + form_tag = None + form_title = _('Contact form') + submit_label = _('Submit') + admin_list = None + + required_css_class = 'required' + contact = forms.CharField(max_length=128) + + def save(self, request): + from .models import Attachment, Contact + body = {} + for name, value in self.cleaned_data.items(): + if not isinstance(value, UploadedFile) and name != 'contact': + body[name] = value + contact = Contact.objects.create(body=body, + ip=request.META['REMOTE_ADDR'], + contact=self.cleaned_data['contact'], + form_tag=self.form_tag) + for name, value in self.cleaned_data.items(): + if isinstance(value, UploadedFile): + attachment = Attachment(contact=contact, tag=name) + attachment.file.save(value.name, value) + attachment.save() + + site = Site.objects.get_current() + dictionary = { + 'form_tag': self.form_tag, + 'site_name': site.name, + 'site_domain': site.domain, + 'contact': contact, + } + context = RequestContext(request) + mail_managers_subject = render_to_string([ + 'contact/%s/mail_managers_subject.txt' % self.form_tag, + 'contact/mail_managers_subject.txt', + ], dictionary, context).strip() + mail_managers_body = render_to_string([ + 'contact/%s/mail_managers_body.txt' % self.form_tag, + 'contact/mail_managers_body.txt', + ], dictionary, context) + mail_managers(mail_managers_subject, mail_managers_body, + fail_silently=True) + + if email_re.match(contact.contact): + mail_subject = render_to_string([ + 'contact/%s/mail_subject.txt' % self.form_tag, + 'contact/mail_subject.txt', + ], dictionary, context).strip() + mail_body = render_to_string([ + 'contact/%s/mail_body.txt' % self.form_tag, + 'contact/mail_body.txt', + ], dictionary, context) + send_mail(mail_subject, mail_body, + 'no-reply@%s' % site.domain, + [contact.contact], + fail_silently=True) + + return contact diff --git a/contact/locale/pl/LC_MESSAGES/django.mo b/contact/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..985b0c0f584c959cdaab7dec71ab592201cc2b00 GIT binary patch literal 1537 zcmZ{jy^kA37{&((UnW3=Kr|F@ljsiEW$lX~a_o@H-6gWPORS5Nh$LrKBE!1J!1_!Nne*T0$BXWp-!_t6`t9teyx zc;3PD9iF#&{tW&w4!~1j0iFgw2W#M0;D_MX;0xgI;LG42U;sV>-vv)UD?}Uo2xL1s z_##*?*N5QiSRa9}f!{3GKY+i&#?K(@4{-QR@C)!I@B!EYzX$pKU*Id?-{2PbFL)l@ zcwUHYa0s&Q$0dIPIo@AD_WK*i_WuOm0{;QwUci0j56h}XK$S0(&v7kyyov`ds~WLg z9-P;6)oc+P`+qX7n4}O}Mtd21x@YV}bh;#y#46{+9{%J&kyF}}%)OaNpT;uFijcaQ zl1@=#NE=U9O|yc$q11=sX5OEqUi5}ikEt;EYQ`QYUU*COfO1FD)7dqbdUaRJiCTBz zQ)*Wk^Q`tm_4sfVYA8N6xur_sTxv88rK7%58u8py)~bX<;$u^5MhY3m#@Hm)(iZq6orZUwxLc z@ⅅq1%D zj66OH#hqgQFiS^byG)P29p@u8DNfF*l;oZ`m3-VmNF|{kQv8wg*6{XIZF0gM+nAx; z^F!!Z%D1&jN3QZw1B2e0p%17W%nyw#D`{z*mslc!QjKLPHR5-s4=ZDCmO|&F<1b@1 nTJ+Eu{q2`UTSi^APUTIYX*iOqLa7umxtJHLh*p~?!c^ivlZLdq literal 0 HcmV?d00001 diff --git a/contact/locale/pl/LC_MESSAGES/django.po b/contact/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000..7805958 --- /dev/null +++ b/contact/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,81 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-10-10 13:12+0200\n" +"PO-Revision-Date: 2012-10-10 13:12+0100\n" +"Last-Translator: Radek Czajka \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" + +#: admin.py:49 +msgid "Body" +msgstr "Treść" + +#: models.py:10 +msgid "submission date" +msgstr "data wysłania" + +#: models.py:11 +msgid "IP address" +msgstr "adres IP" + +#: models.py:12 +msgid "contact" +msgstr "kontakt" + +#: models.py:13 +msgid "form" +msgstr "formularz" + +#: models.py:14 +msgid "body" +msgstr "treść" + +#: models.py:18 +msgid "submitted form" +msgstr "przysłany formularz" + +#: models.py:19 +msgid "submitted forms" +msgstr "przysłane formularze" + +#: templates/contact/form.html:5 +msgid "Contact form" +msgstr "Formularz kontaktowy" + +#: templates/contact/form.html:14 +msgid "Submit" +msgstr "Wyślij" + +#: templates/contact/mail_body.txt:2 +#: templates/contact/mail_subject.txt:1 +#, python-format +msgid "Thank you for contacting us at %(site_name)s." +msgstr "Dziękujemy za skontaktowanie się z nami na stronie %(site_name)s." + +#: templates/contact/mail_body.txt:3 +msgid "Your submission has been referred to the project coordinator." +msgstr "Twoje zgłoszenie zostało przekazane osobie koordynującej projekt." + +#: templates/contact/mail_body.txt:6 +msgid "Message sent automatically. Please do not reply to it." +msgstr "Wiadomość wysłana automatycznie, prosimy nie odpowiadać." + +#: templates/contact/thanks.html:5 +msgid "Thank you" +msgstr "Dziękujemy" + +#: templates/contact/thanks.html:8 +msgid "Thank you for submitting the contact form." +msgstr "Dziękujemy za wypełnienie formularza kontaktowego." + diff --git a/contact/migrations/0001_initial.py b/contact/migrations/0001_initial.py new file mode 100644 index 0000000..d16bf4c --- /dev/null +++ b/contact/migrations/0001_initial.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Contact' + db.create_table('contact_contact', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('ip', self.gf('django.db.models.fields.IPAddressField')(max_length=15)), + ('contact', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('form_tag', self.gf('django.db.models.fields.CharField')(max_length=32)), + ('body', self.gf('jsonfield.fields.JSONField')()), + )) + db.send_create_signal('contact', ['Contact']) + + + def backwards(self, orm): + # Deleting model 'Contact' + db.delete_table('contact_contact') + + + models = { + 'contact.contact': { + 'Meta': {'ordering': "('-created_at',)", 'object_name': 'Contact'}, + 'body': ('jsonfield.fields.JSONField', [], {}), + 'contact': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'form_tag': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}) + } + } + + complete_apps = ['contact'] \ No newline at end of file diff --git a/contact/migrations/0002_auto__add_attachment.py b/contact/migrations/0002_auto__add_attachment.py new file mode 100644 index 0000000..8dc031d --- /dev/null +++ b/contact/migrations/0002_auto__add_attachment.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Attachment' + db.create_table('contact_attachment', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('contact', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contact.Contact'])), + ('tag', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('file', self.gf('django.db.models.fields.files.FileField')(max_length=100)), + )) + db.send_create_signal('contact', ['Attachment']) + + # Adding index on 'Contact', fields ['form_tag'] + db.create_index('contact_contact', ['form_tag']) + + + def backwards(self, orm): + # Removing index on 'Contact', fields ['form_tag'] + db.delete_index('contact_contact', ['form_tag']) + + # Deleting model 'Attachment' + db.delete_table('contact_attachment') + + + models = { + 'contact.attachment': { + 'Meta': {'object_name': 'Attachment'}, + 'contact': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contact.Contact']"}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '64'}) + }, + 'contact.contact': { + 'Meta': {'ordering': "('-created_at',)", 'object_name': 'Contact'}, + 'body': ('jsonfield.fields.JSONField', [], {}), + 'contact': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'form_tag': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}) + } + } + + complete_apps = ['contact'] \ No newline at end of file diff --git a/contact/migrations/__init__.py b/contact/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/contact/models.py b/contact/models.py new file mode 100644 index 0000000..21d8405 --- /dev/null +++ b/contact/models.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from django.core.files.storage import FileSystemStorage +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from jsonfield import JSONField +from . import app_settings + + +class Contact(models.Model): + created_at = models.DateTimeField(_('submission date'), auto_now_add=True) + ip = models.IPAddressField(_('IP address')) + contact = models.CharField(_('contact'), max_length=128) + form_tag = models.CharField(_('form'), max_length=32, db_index=True) + body = JSONField(_('body')) + + class Meta: + ordering = ('-created_at',) + verbose_name = _('submitted form') + verbose_name_plural = _('submitted forms') + + def __unicode__(self): + return unicode(self.created_at) + + +class Attachment(models.Model): + contact = models.ForeignKey(Contact) + tag = models.CharField(max_length=64) + file = models.FileField(upload_to='contact/attachment') + + @models.permalink + def get_absolute_url(self): + return ('contact_attachment', [self.contact_id, self.tag]) + + +__import__(app_settings.FORMS_MODULE) diff --git a/contact/templates/contact/form.html b/contact/templates/contact/form.html new file mode 100644 index 0000000..963742e --- /dev/null +++ b/contact/templates/contact/form.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} +{% load chunks %} + +{% block title %}{{ form.form_title }}{% endblock %} + +{% block body %} + +

{% block contact_form_title %}{{ form.form_title }}{% endblock %}

+ +
+ {% block contact_form_description %} + {% chunk "contact_form__"|add:form.form_tag %} + {% endblock %} +
+ +
+ {% csrf_token %} + + {{ form.as_table }} + +
+
+ +{% endblock %} diff --git a/contact/templates/contact/mail_body.txt b/contact/templates/contact/mail_body.txt new file mode 100644 index 0000000..5015757 --- /dev/null +++ b/contact/templates/contact/mail_body.txt @@ -0,0 +1,6 @@ +{% load i18n %} +{% blocktrans %}Thank you for contacting us at {{ site_name }}.{% endblocktrans %} +{% trans "Your submission has been referred to the project coordinator." %} + +-- +{% trans "Message sent automatically. Please do not reply to it." %} diff --git a/contact/templates/contact/mail_managers_body.txt b/contact/templates/contact/mail_managers_body.txt new file mode 100644 index 0000000..386c9cd --- /dev/null +++ b/contact/templates/contact/mail_managers_body.txt @@ -0,0 +1,12 @@ +{% load url from future %}Wypełniono formularz {{ form_tag }} na stronie {{ site_name }}. + +http://{{ site_domain }}{% url 'admin:contact_contact_change' contact.pk %} + +{% for k, v in contact.body.items %} +{{ k }}: +{{ v }} +{% endfor %} +{% for attachment in contact.attachment_set.all %} +{{ attachment.tag }}: +http://{{ site_domain }}{{ attachment.get_absolute_url }} +{% endfor %} diff --git a/contact/templates/contact/mail_managers_subject.txt b/contact/templates/contact/mail_managers_subject.txt new file mode 100644 index 0000000..12d2c8e --- /dev/null +++ b/contact/templates/contact/mail_managers_subject.txt @@ -0,0 +1 @@ +Wypełniono formularz {{ form_tag }} na stronie {{ site_name }}. \ No newline at end of file diff --git a/contact/templates/contact/mail_subject.txt b/contact/templates/contact/mail_subject.txt new file mode 100644 index 0000000..b8f586e --- /dev/null +++ b/contact/templates/contact/mail_subject.txt @@ -0,0 +1 @@ +{% load i18n %}{% blocktrans %}Thank you for contacting us at {{ site_name }}.{% endblocktrans %} \ No newline at end of file diff --git a/contact/templates/contact/thanks.html b/contact/templates/contact/thanks.html new file mode 100644 index 0000000..cc061ed --- /dev/null +++ b/contact/templates/contact/thanks.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block title %}{% trans "Thank you" %}{% endblock %} + +{% block body %} + +

{% block contact_form_title %}{% trans "Thank you" %}{% endblock %}

+ + {% block contact_form_description %} +

{% trans "Thank you for submitting the contact form." %}

+ {% endblock %} + +{% endblock %} diff --git a/contact/urls.py b/contact/urls.py new file mode 100644 index 0000000..de72e82 --- /dev/null +++ b/contact/urls.py @@ -0,0 +1,9 @@ +from django.conf.urls.defaults import * +from . import views + +urlpatterns = patterns('contact.views', + url(r'^(?P[^/]+)/$', views.form, name='contact_form'), + url(r'^(?P[^/]+)/thanks/$', views.thanks, name='contact_thanks'), + url(r'^attachment/(?P\d+)/(?P[^/]+)/$', + views.attachment, name='contact_attachment'), +) diff --git a/contact/views.py b/contact/views.py new file mode 100644 index 0000000..d18598c --- /dev/null +++ b/contact/views.py @@ -0,0 +1,40 @@ +from django.contrib.auth.decorators import permission_required +from django.http import Http404, HttpResponse +from django.shortcuts import get_object_or_404, redirect, render +from django.utils.translation import ugettext_lazy as _ +from fnpdjango.utils.views import serve_file +from .forms import contact_forms +from .models import Attachment + + +def form(request, form_tag): + try: + form_class = contact_forms[form_tag] + except KeyError: + raise Http404 + if request.method == 'POST': + form = form_class(request.POST, request.FILES) + if form.is_valid(): + form.save(request) + return redirect('contact_thanks', form_tag) + else: + form = form_class(initial=request.GET) + return render(request, + ['contact/%s/form.html' % form_tag, 'contact/form.html'], + {'form': form} + ) + + +def thanks(request, form_tag): + if form_tag not in contact_forms: + raise Http404 + + return render(request, + ['contact/%s/thanks.html' % form_tag, 'contact/thanks.html'] + ) + + +@permission_required('contact.change_attachment') +def attachment(request, contact_id, tag): + attachment = get_object_or_404(Attachment, contact_id=contact_id, tag=tag) + return serve_file(attachment.file.url) diff --git a/edumed/contact_forms.py b/edumed/contact_forms.py new file mode 100644 index 0000000..7286509 --- /dev/null +++ b/edumed/contact_forms.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +from django import forms +from contact.forms import ContactForm + + +class RegistrationForm(ContactForm): + form_tag = 'sugestie' + form_title = u"Zgłoś sugestię" + admin_list = ['podpis', 'contact', 'temat'] + + contact = forms.EmailField(label=u'E-mail', max_length=128, required=False) + podpis = forms.CharField(label=u'Podpis', max_length=128, required=False) + temat = forms.CharField(label=u'Temat zgłoszenia', max_length=255) + tresc = forms.CharField(label=u'Treść', widget=forms.Textarea, max_length=1800) + + +class ContestForm(ContactForm): + form_tag = 'konkurs' + form_title = u"Zgłoś się do konkursu" + admin_list = ['name', 'organization', 'title'] + + nazwisko = forms.CharField(label=u'Imię i nazwisko', max_length=128) + contact = forms.EmailField(label=u'Adres e-mail', max_length=128) + instytucja = forms.CharField(label=u'Instytucja (nazwa, adres)', + widget=forms.Textarea, max_length=1000) + tytul = forms.CharField(label=u'Tytuł przeprowadzonej lekcji', + help_text=u'proszę wymienić wszystkie, jeśli zostały przeprowadzone więcej niż jedne zajęcia', + widget=forms.Textarea, max_length=1000) + uczestnicy = forms.CharField(label=u'Liczba uczestników', max_length=64) + trudnosci = forms.CharField(label=u'Czy w trakcie zajęć pojawiły się jakieś trudności? Jeśli tak, to jakie?', + widget=forms.Textarea, max_length=2000) + pomocne = forms.CharField(label=u'Co w materiałach okazało się najbardziej pomocne w przygotowaniu i prowadzeniu lekcji?', + widget=forms.Textarea, max_length=2000) + nieprzydatne = forms.CharField(label=u'Co w materiałach okazało się nieprzydatne w przygotowaniu i prowadzeniu lekcji?', + widget=forms.Textarea, max_length=2000) + poprawic = forms.CharField(label=u'Jak możemy poprawić serwis edukacjamedialna.edu.pl?', + widget=forms.Textarea, max_length=2000) + inne = forms.CharField(label=u'Inne uwagi i komentarze', + widget=forms.Textarea, max_length=2000, + required=False) + zgoda_regulamin = forms.BooleanField( + label=u'Znam i akceptuję regulamin konkursu Medialog.', + help_text=u'Zobacz regulamin konkursu MediaLog.' + ) + zgoda_informacje = forms.BooleanField( + label=u'Wyrażam zgodę na otrzymywanie informacji od Fundacji Nowoczesna Polska związanych z edukacją medialną.', + required=False + ) diff --git a/edumed/settings.d/30-apps.py b/edumed/settings.d/30-apps.py index 0ba0726..e98a3ee 100644 --- a/edumed/settings.d/30-apps.py +++ b/edumed/settings.d/30-apps.py @@ -9,11 +9,12 @@ INSTALLED_APPS = ( # Disable, if not using Piwik. 'piwik.django', # Disable, if not using CAS. + 'honeypot', 'django_cas', 'sponsors', 'haystack', 'chunks', - + 'contact', 'django.contrib.auth', 'django.contrib.contenttypes', diff --git a/edumed/settings.d/40-middleware.py b/edumed/settings.d/40-middleware.py index b211d42..5700161 100644 --- a/edumed/settings.d/40-middleware.py +++ b/edumed/settings.d/40-middleware.py @@ -6,6 +6,7 @@ MIDDLEWARE_CLASSES = tuple(x for x in ( 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', + 'honeypot.middleware.HoneypotMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware' if "django.contrib.auth" in INSTALLED_APPS else None, 'django_cas.middleware.CASMiddleware' diff --git a/edumed/settings.d/50-contrib.py b/edumed/settings.d/50-contrib.py index 2da1448..9291ce5 100644 --- a/edumed/settings.d/50-contrib.py +++ b/edumed/settings.d/50-contrib.py @@ -3,3 +3,5 @@ CAS_VERSION = '1' SPONSORS_THUMB_WIDTH = 100 SPONSORS_THUMB_HEIGHT = 56 + +HONEYPOT_FIELD_NAME='miut' diff --git a/edumed/settings.d/50-static.py b/edumed/settings.d/50-static.py index a32c709..aa2a203 100644 --- a/edumed/settings.d/50-static.py +++ b/edumed/settings.d/50-static.py @@ -17,6 +17,7 @@ PIPELINE_CSS = { 'source_filenames': ( 'css/base.scss', 'css/main.scss', + 'css/form.scss', 'catalogue/css/carousel.scss', 'catalogue/css/layout.scss', 'catalogue/css/lesson.scss', diff --git a/edumed/settings.d/60-custom.py b/edumed/settings.d/60-custom.py index da1b7eb..758819b 100644 --- a/edumed/settings.d/60-custom.py +++ b/edumed/settings.d/60-custom.py @@ -1,2 +1,3 @@ CATALOGUE_PACKAGE = "catalogue/edukacjamedialna.zip" CATALOGUE_PACKAGE_STUDENT = "catalogue/edukacjamedialna_uczen.zip" +CONTACT_FORMS_MODULE = 'edumed.contact_forms' diff --git a/edumed/static/css/form.css b/edumed/static/css/form.css new file mode 100644 index 0000000..878de75 --- /dev/null +++ b/edumed/static/css/form.css @@ -0,0 +1,14 @@ +.submit-form th, .submit-form td { + padding: .3em; + vertical-align: top; + text-align: left; } +.submit-form th { + max-width: 16em; + font-weight: normal; } +.submit-form .required label:before { + content: "* "; + color: red; } +.submit-form .errorlist { + padding: 0 0 0 1em; + margin: 0; + color: red; } diff --git a/edumed/static/css/form.scss b/edumed/static/css/form.scss new file mode 100755 index 0000000..83c7938 --- /dev/null +++ b/edumed/static/css/form.scss @@ -0,0 +1,20 @@ +.submit-form { + th, td { + padding: .3em; + vertical-align: top; + text-align: left; + } + th { + max-width: 16em; + font-weight: normal; + } + .required label:before { + content: "* "; + color: red; + } + .errorlist { + padding: 0 0 0 1em; + margin: 0; + color: red; + } +} diff --git a/edumed/templates/contact/konkurs/mail_body.txt b/edumed/templates/contact/konkurs/mail_body.txt new file mode 100644 index 0000000..10b2826 --- /dev/null +++ b/edumed/templates/contact/konkurs/mail_body.txt @@ -0,0 +1,10 @@ +Dziękujemy, + +Zgłoszenie na konkurs MediaLog na stronie {{ site_name }} +zostało zarejestrowane. + +W razie jakichkolwiek wątpliwości prosimy o kontakt: +http://edukacjamedialna.edu.pl/info/kontakt/ + +-- +Wiadomość wysłana automatycznie. Prosimy na nią nie odpowiadać. diff --git a/edumed/templates/contact/konkurs/mail_subject.txt b/edumed/templates/contact/konkurs/mail_subject.txt new file mode 100644 index 0000000..386c1e6 --- /dev/null +++ b/edumed/templates/contact/konkurs/mail_subject.txt @@ -0,0 +1 @@ +Zgłoszenie na konkurs MediaLog zostało zarejestrowane. diff --git a/edumed/templates/contact/konkurs/thanks.html b/edumed/templates/contact/konkurs/thanks.html new file mode 100644 index 0000000..357e398 --- /dev/null +++ b/edumed/templates/contact/konkurs/thanks.html @@ -0,0 +1,5 @@ +{% extends "contact/thanks.html" %} + +{% block contact_form_description %} +

Dziękujemy, zgłoszenie na konkurs MediaLog zostało zarejestrowane.

+{% endblock %} diff --git a/edumed/templates/contact/sugestie/mail_body.txt b/edumed/templates/contact/sugestie/mail_body.txt new file mode 100644 index 0000000..ad38005 --- /dev/null +++ b/edumed/templates/contact/sugestie/mail_body.txt @@ -0,0 +1,10 @@ +Dziękujemy, + +Zgłoszenie na stronie {{ site_name }} +zostało przekazane koordynatorce projektu. + +W razie jakichkolwiek dodatkowych wątpliwości prosimy o kontakt: +http://edukacjamedialna.edu.pl/info/kontakt/ + +-- +Wiadomość wysłana automatycznie. Prosimy na nią nie odpowiadać. diff --git a/edumed/templates/contact/sugestie/mail_subject.txt b/edumed/templates/contact/sugestie/mail_subject.txt new file mode 100644 index 0000000..19fcbfa --- /dev/null +++ b/edumed/templates/contact/sugestie/mail_subject.txt @@ -0,0 +1 @@ +Zgłoszenie na stronie {{ site_name }} zostało zarejestrowane. diff --git a/edumed/templates/contact/sugestie/thanks.html b/edumed/templates/contact/sugestie/thanks.html new file mode 100644 index 0000000..5c89bba --- /dev/null +++ b/edumed/templates/contact/sugestie/thanks.html @@ -0,0 +1,5 @@ +{% extends "contact/thanks.html" %} + +{% block contact_form_description %} +

Dziękujemy, zgłoszenie zostało zarejestrowane.

+{% endblock %} diff --git a/edumed/urls.py b/edumed/urls.py index 303e011..3e99b30 100644 --- a/edumed/urls.py +++ b/edumed/urls.py @@ -11,6 +11,7 @@ urlpatterns = patterns('', url(r'^info/(?P.*)$', 'django.contrib.flatpages.views.flatpage', name="info"), url(r'^szukaj/', include('haystack.urls')), + url(r'^zglos/', include('contact.urls')), ) diff --git a/requirements.txt b/requirements.txt index ea7ea79..a8b89af 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,8 @@ fnpdjango<0.2 Feedparser +django-honeypot + # Librarian lxml texml -- 2.20.1