mise-en-production
authorPatrick Hetu <patrick.hetu@auf.org>
Wed, 9 Sep 2015 19:29:53 +0000 (15:29 -0400)
committerPatrick Hetu <patrick.hetu@auf.org>
Wed, 9 Sep 2015 19:29:53 +0000 (15:29 -0400)
68 files changed:
project/aldryn_search/__init__.py [deleted file]
project/aldryn_search/base.py [deleted file]
project/aldryn_search/cms_app.py [deleted file]
project/aldryn_search/conf.py [deleted file]
project/aldryn_search/helpers.py [deleted file]
project/aldryn_search/locale/de/LC_MESSAGES/django.mo [deleted file]
project/aldryn_search/locale/de/LC_MESSAGES/django.po [deleted file]
project/aldryn_search/locale/en/LC_MESSAGES/django.mo [deleted file]
project/aldryn_search/locale/en/LC_MESSAGES/django.po [deleted file]
project/aldryn_search/models.py [deleted file]
project/aldryn_search/router.py [deleted file]
project/aldryn_search/search_indexes.py [deleted file]
project/aldryn_search/templates/aldryn_search/base.html [deleted file]
project/aldryn_search/templates/aldryn_search/includes/pagination.html [deleted file]
project/aldryn_search/templates/aldryn_search/includes/search_items.html [deleted file]
project/aldryn_search/templates/aldryn_search/search_results.html [deleted file]
project/aldryn_search/tests.py [deleted file]
project/aldryn_search/utils.py [deleted file]
project/aldryn_search/views.py [deleted file]
project/cmsplugin_contact_plus/__init__.py [new file with mode: 0755]
project/cmsplugin_contact_plus/actions.py [new file with mode: 0644]
project/cmsplugin_contact_plus/admin.py [new file with mode: 0755]
project/cmsplugin_contact_plus/cms_plugins.py [new file with mode: 0755]
project/cmsplugin_contact_plus/forms.py [new file with mode: 0755]
project/cmsplugin_contact_plus/inline_ordering/__init__.py [new file with mode: 0644]
project/cmsplugin_contact_plus/inline_ordering/admin.py [new file with mode: 0644]
project/cmsplugin_contact_plus/inline_ordering/media [new symlink]
project/cmsplugin_contact_plus/inline_ordering/models.py [new file with mode: 0644]
project/cmsplugin_contact_plus/inline_ordering/settings.py [new file with mode: 0644]
project/cmsplugin_contact_plus/inline_ordering/static/inline_ordering.js [new file with mode: 0644]
project/cmsplugin_contact_plus/jsonfield/__init__.py [new file with mode: 0644]
project/cmsplugin_contact_plus/jsonfield/encoder.py [new file with mode: 0644]
project/cmsplugin_contact_plus/jsonfield/fields.py [new file with mode: 0644]
project/cmsplugin_contact_plus/jsonfield/models.py [new file with mode: 0644]
project/cmsplugin_contact_plus/jsonfield/subclassing.py [new file with mode: 0644]
project/cmsplugin_contact_plus/jsonfield/tests.py [new file with mode: 0644]
project/cmsplugin_contact_plus/local_settings.py [new file with mode: 0644]
project/cmsplugin_contact_plus/locale/es/LC_MESSAGES/django.mo [new file with mode: 0644]
project/cmsplugin_contact_plus/locale/es/LC_MESSAGES/django.po [new file with mode: 0644]
project/cmsplugin_contact_plus/migrations/0001_initial.py [new file with mode: 0644]
project/cmsplugin_contact_plus/migrations/__init__.py [new file with mode: 0644]
project/cmsplugin_contact_plus/models.py [new file with mode: 0755]
project/cmsplugin_contact_plus/signals.py [new file with mode: 0644]
project/cmsplugin_contact_plus/simplemathcaptcha/__init__.py [new file with mode: 0644]
project/cmsplugin_contact_plus/simplemathcaptcha/fields.py [new file with mode: 0644]
project/cmsplugin_contact_plus/simplemathcaptcha/locale/es/LC_MESSAGES/django.mo [new file with mode: 0644]
project/cmsplugin_contact_plus/simplemathcaptcha/locale/es/LC_MESSAGES/django.po [new file with mode: 0644]
project/cmsplugin_contact_plus/simplemathcaptcha/utils.py [new file with mode: 0644]
project/cmsplugin_contact_plus/simplemathcaptcha/widgets.py [new file with mode: 0644]
project/cmsplugin_contact_plus/south_migrations/__init__.py [new file with mode: 0644]
project/cmsplugin_contact_plus/templates/cmsplugin_contact_plus/contact.html [new file with mode: 0755]
project/cmsplugin_contact_plus/templates/cmsplugin_contact_plus/email.txt [new file with mode: 0755]
project/cmsplugin_contact_plus/utils.py [new file with mode: 0644]
project/framonde/models.py
project/templates/accueil.html
project/templates/archives.html
project/templates/base.html
project/templates/detail.html
project/templates/pageAppel.html
project/templates/pageCom.html
project/templates/pageContri.html
project/templates/search.html [deleted file]
project/templates/search/indexes/framonde/communication_text.txt [new file with mode: 0644]
project/templates/search/indexes/framonde/contribution_text.txt [new file with mode: 0644]
project/templates/search/indexes/framonde/offre_text.txt [new file with mode: 0644]
project/templates/search/search.html [new file with mode: 0644]
project/urls/60-framonde.py
project/views.py

diff --git a/project/aldryn_search/__init__.py b/project/aldryn_search/__init__.py
deleted file mode 100644 (file)
index 44b1806..0000000
+++ /dev/null
@@ -1 +0,0 @@
-__version__ = '0.2.6'
diff --git a/project/aldryn_search/base.py b/project/aldryn_search/base.py
deleted file mode 100644 (file)
index c667dec..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-# -*- coding: utf-8 -*-
-import warnings
-from django.utils.translation import override
-
-from haystack import indexes
-
-from .conf import settings
-from .helpers import get_request
-from .utils import clean_join, _get_language_from_alias_func
-
-
-language_from_alias = _get_language_from_alias_func()
-
-
-class AbstractIndex(indexes.SearchIndex):
-    text = indexes.CharField(document=True, use_template=False)
-
-    def _get_backend(self, using):
-        """
-        We set the backend to allow easy access for things like document search.
-        """
-        self._backend = super(AbstractIndex, self)._get_backend(using)
-        self._backend_alias = using
-        return self._backend
-
-    def index_queryset(self, using=None):
-        self._get_backend(using)
-        language = self.get_current_language(using)
-        filter_kwargs = self.get_index_kwargs(language)
-        qs = self.get_index_queryset(language)
-        return (qs.filter(**filter_kwargs))
-
-    def get_index_queryset(self, language):
-        return self.get_model().objects.all()
-
-    def prepare(self, obj):
-        current_language = self.get_current_language(
-            using=self._backend_alias, obj=obj)
-
-        with override(current_language):
-            request = self.get_request_instance(obj, current_language)
-            self.prepared_data = super(AbstractIndex, self).prepare(obj)
-            self.prepared_data['text'] = self.get_search_data(
-                obj, current_language, request)
-            self.prepare_fields(obj, current_language, request)
-            return self.prepared_data
-
-    def get_request_instance(self, obj, language):
-        return get_request(language)
-
-    def get_language(self, obj):
-        """
-        Equivalent to self.prepare_language.
-        """
-        return None
-
-    def get_current_language(self, using=None, obj=None):
-        """
-        Helper method bound to ALWAYS return a language.
-
-        When obj is not None, this calls self.get_language to try and get a language from obj,
-        this is useful when the object itself defines it's language in a "language" field.
-
-        If no language was found or obj is None, then we call self.get_default_language to try and get a fallback language.
-        """
-        language = self.get_language(obj) if obj else None
-        return language or self.get_default_language(using)
-
-    def get_default_language(self, using):
-        """
-        When using multiple languages, this allows us to specify a fallback based on the
-        backend being used.
-        """
-        language = None
-
-        if using and language_from_alias:
-            language = language_from_alias(using)
-        return language or settings.ALDRYN_SEARCH_DEFAULT_LANGUAGE
-
-    def get_index_kwargs(self, language):
-        """
-        This is called to filter the index queryset.
-        """
-        return {}
-
-    def get_search_data(self, obj, language, request):
-        """
-        Returns a string that will be used to populate the text field (primary field).
-        """
-        return ''
-
-    def prepare_fields(self, obj, language, request):
-        """
-        This is called to prepare any extra fields.
-        """
-        pass
-
-
-class AldrynIndexBase(AbstractIndex):
-    # For some apps it makes sense to turn on the title indexing.
-    index_title = False
-
-    language = indexes.CharField()
-    description = indexes.CharField(indexed=False, stored=True, null=True)
-    pub_date = indexes.DateTimeField(null=True)
-    login_required = indexes.BooleanField(default=False)
-    url = indexes.CharField(stored=True, indexed=False)
-    title = indexes.CharField(stored=True, indexed=False)
-    site_id = indexes.IntegerField(stored=True, indexed=True, null=True)
-
-    def __init__(self):
-        if hasattr(self, 'INDEX_TITLE'):
-            warning_message = 'AldrynIndexBase.INDEX_TITLE is deprecated; use AldrynIndexBase.index_title instead'
-            warnings.warn(warning_message, PendingDeprecationWarning)
-        super(AldrynIndexBase, self).__init__()
-
-    def get_url(self, obj):
-        """
-        Equivalent to self.prepare_url.
-        """
-        if not obj.get_absolute_url():
-            print obj
-        return obj.get_absolute_url()
-
-    def get_title(self, obj):
-        """
-        Equivalent to self.prepare_title.
-        """
-        return None
-
-    def get_description(self, obj):
-        """
-        Equivalent to self.prepare_description.
-        """
-        return None
-
-    def prepare_fields(self, obj, language, request):
-        self.prepared_data['language'] = language
-        # We set the following fields here because on some models,
-        # the value of these fields is dependent on the active language
-        # this being the case we extrapolate the language hacks.
-        self.prepared_data['url'] = self.get_url(obj)
-        self.prepared_data['title'] = self.get_title(obj)
-        self.prepared_data['description'] = self.get_description(obj)
-
-        if self.index_title or getattr(self, 'INDEX_TITLE', False):
-            prepared_text = self.prepared_data['text']
-            prepared_title = self.prepared_data['title']
-            self.prepared_data['text'] = clean_join(
-                ' ', [prepared_title, prepared_text])
diff --git a/project/aldryn_search/cms_app.py b/project/aldryn_search/cms_app.py
deleted file mode 100644 (file)
index c6366b1..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-from django.conf.urls import patterns, url
-from django.utils.translation import ugettext_lazy as _
-
-from cms.app_base import CMSApp
-from cms.apphook_pool import apphook_pool
-
-from .views import AldrynSearchView
-
-from .conf import settings
-
-
-class AldrynSearchApphook(CMSApp):
-    name = _("aldryn search")
-    urls = [patterns('',
-        url('^$', AldrynSearchView.as_view(), name='aldryn-search'),
-    ),]
-
-
-if settings.ALDRYN_SEARCH_REGISTER_APPHOOK:
-    apphook_pool.register(AldrynSearchApphook)
diff --git a/project/aldryn_search/conf.py b/project/aldryn_search/conf.py
deleted file mode 100644 (file)
index c293732..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- coding: utf-8 -*-
-from django.conf import settings
-
-from appconf import AppConf
-
-
-class AldrynSearchAppConf(AppConf):
-
-    CMS_PAGE = True
-    DEFAULT_LANGUAGE = settings.LANGUAGE_CODE
-    INDEX_BASE_CLASS = 'aldryn_search.base.AldrynIndexBase'
-    LANGUAGE_FROM_ALIAS = 'aldryn_search.utils.language_from_alias'
-    PAGINATION = 10
-    REGISTER_APPHOOK = True
-
-    class Meta:
-        prefix = 'ALDRYN_SEARCH'
diff --git a/project/aldryn_search/helpers.py b/project/aldryn_search/helpers.py
deleted file mode 100644 (file)
index f6a4638..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- coding: utf-8 -*-
-from django.contrib.auth.models import AnonymousUser
-from django.template import RequestContext
-from django.test import RequestFactory
-from django.utils.text import smart_split
-try:
-    from django.utils.encoding import force_unicode
-except ImportError:
-    from django.utils.encoding import force_text as force_unicode
-
-from .conf import settings
-from .utils import get_field_value, strip_tags
-
-
-def get_cleaned_bits(data):
-    decoded = force_unicode(data)
-    stripped = strip_tags(decoded)
-    return smart_split(stripped)
-
-
-def get_plugin_index_data(base_plugin, request):
-    text_bits = []
-
-    instance, plugin_type = base_plugin.get_plugin_instance()
-
-    if instance is None:
-        # this is an empty plugin
-        return text_bits
-
-    search_fields = getattr(instance, 'search_fields', [])
-
-    if hasattr(instance, 'search_fulltext'):
-        # check if the plugin instance has search enabled
-        search_contents = instance.search_fulltext
-    elif hasattr(base_plugin, 'search_fulltext'):
-        # now check in the base plugin instance (CMSPlugin)
-        search_contents = base_plugin.search_fulltext
-    elif hasattr(plugin_type, 'search_fulltext'):
-        # last check in the plugin class (CMSPluginBase)
-        search_contents = plugin_type.search_fulltext
-    else:
-        # disabled if there's search fields defined,
-        # otherwise it's enabled.
-        search_contents = not bool(search_fields)
-
-    if search_contents:
-        plugin_contents = instance.render_plugin(
-            context=RequestContext(request))
-
-        if plugin_contents:
-            text_bits = get_cleaned_bits(plugin_contents)
-    else:
-        values = (get_field_value(instance, field) for field in search_fields)
-
-        for value in values:
-            cleaned_bits = get_cleaned_bits(value or '')
-            text_bits.extend(cleaned_bits)
-    return text_bits
-
-
-def get_request(language=None):
-    """
-    Returns a Request instance populated with cms specific attributes.
-    """
-    request_factory = RequestFactory(HTTP_HOST=settings.ALLOWED_HOSTS[0])
-    request = request_factory.get("/")
-    request.session = {}
-    request.LANGUAGE_CODE = language or settings.LANGUAGE_CODE
-    # Needed for plugin rendering.
-    request.current_page = None
-    request.user = AnonymousUser()
-    return request
diff --git a/project/aldryn_search/locale/de/LC_MESSAGES/django.mo b/project/aldryn_search/locale/de/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 39bab21..0000000
Binary files a/project/aldryn_search/locale/de/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/project/aldryn_search/locale/de/LC_MESSAGES/django.po b/project/aldryn_search/locale/de/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 5d53494..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-# 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 <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-07-24 16:57+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: cms_app.py:13
-msgid "aldryn search"
-msgstr "Aldryn Suche"
-
-#: templates/aldryn_search/search_results.html:6
-msgid "enter your search term"
-msgstr "Suchbegriffe eingeben"
-
-#: templates/aldryn_search/search_results.html:16
-msgid "results"
-msgstr "Resultaten"
-
-#: templates/aldryn_search/includes/pagination.html:7
-#, python-format
-msgid "%(from)s – %(until)s of %(count)s %(obj_string)s are displayed"
-msgstr "%(from)s – %(until)s von %(count)s %(obj_string)s werden angezeigt"
-
-#: templates/aldryn_search/includes/pagination.html:11
-msgid "previous"
-msgstr ""
-
-#: templates/aldryn_search/includes/pagination.html:25
-msgid "next"
-msgstr ""
-
-#: templates/aldryn_search/includes/search_items.html:12
-msgid "No results found for"
-msgstr ""
-
-#~ msgid "Displaying all %(count)s %(obj_string)s"
-#~ msgstr "Alle %(count)s %(obj_string)s werden angezeigt"
-
-#~ msgid "Displaying all %(count)s objects"
-#~ msgstr "Alle %(count)s Objekte werden angezeigt"
diff --git a/project/aldryn_search/locale/en/LC_MESSAGES/django.mo b/project/aldryn_search/locale/en/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 31092e8..0000000
Binary files a/project/aldryn_search/locale/en/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/project/aldryn_search/locale/en/LC_MESSAGES/django.po b/project/aldryn_search/locale/en/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 1b400f8..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-# 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 <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-07-24 16:57+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: cms_app.py:13
-msgid "aldryn search"
-msgstr ""
-
-#: templates/aldryn_search/search_results.html:6
-msgid "enter your search term"
-msgstr ""
-
-#: templates/aldryn_search/search_results.html:16
-msgid "results"
-msgstr ""
-
-#: templates/aldryn_search/includes/pagination.html:7
-#, python-format
-msgid "%(from)s – %(until)s of %(count)s %(obj_string)s are displayed"
-msgstr ""
-
-#: templates/aldryn_search/includes/pagination.html:11
-msgid "previous"
-msgstr ""
-
-#: templates/aldryn_search/includes/pagination.html:25
-msgid "next"
-msgstr ""
-
-#: templates/aldryn_search/includes/search_items.html:12
-msgid "No results found for"
-msgstr ""
diff --git a/project/aldryn_search/models.py b/project/aldryn_search/models.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/project/aldryn_search/router.py b/project/aldryn_search/router.py
deleted file mode 100644 (file)
index 9da94c2..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-from django.conf import settings
-from django.utils.translation import get_language
-
-from haystack import routers
-from haystack.constants import DEFAULT_ALIAS
-
-
-class LanguageRouter(routers.BaseRouter):
-
-    def for_read(self, **hints):
-        language = get_language()
-        if language not in settings.HAYSTACK_CONNECTIONS:
-            return DEFAULT_ALIAS
-        return language
-
-    def for_write(self, **hints):
-        language = get_language()
-        if language not in settings.HAYSTACK_CONNECTIONS:
-            return DEFAULT_ALIAS
-        return language
diff --git a/project/aldryn_search/search_indexes.py b/project/aldryn_search/search_indexes.py
deleted file mode 100644 (file)
index 8e671d0..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-from django.db.models import Q
-from django.utils import timezone
-
-from cms.models import CMSPlugin, Title
-
-from .conf import settings
-from .helpers import get_plugin_index_data
-from .utils import clean_join, get_index_base, strip_tags
-
-
-# Backwards compatibility
-_strip_tags = strip_tags
-
-
-class TitleIndex(get_index_base()):
-    index_title = True
-
-    haystack_use_for_indexing = settings.ALDRYN_SEARCH_CMS_PAGE
-
-    def prepare_pub_date(self, obj):
-        return obj.page.publication_date
-
-    def prepare_login_required(self, obj):
-        return obj.page.login_required
-
-    def prepare_site_id(self, obj):
-        return obj.page.site_id
-
-    def get_language(self, obj):
-        return obj.language
-
-    def get_url(self, obj):
-        return obj.page.get_absolute_url()
-
-    def get_title(self, obj):
-        return obj.title
-
-    def get_description(self, obj):
-        return obj.meta_description or None
-
-    def get_plugin_queryset(self, language):
-        queryset = CMSPlugin.objects.filter(language=language)
-        return queryset
-
-    def get_search_data(self, obj, language, request):
-        current_page = obj.page
-        placeholders = current_page.placeholders.all()
-        plugins = self.get_plugin_queryset(
-            language).filter(placeholder__in=placeholders)
-        text_bits = []
-
-        for base_plugin in plugins:
-            plugin_text_content = self.get_plugin_search_text(
-                base_plugin, request)
-            text_bits.append(plugin_text_content)
-
-        page_meta_description = current_page.get_meta_description(
-            fallback=False, language=language)
-
-        if page_meta_description:
-            text_bits.append(page_meta_description)
-
-        page_meta_keywords = getattr(current_page, 'get_meta_keywords', None)
-
-        if callable(page_meta_keywords):
-            text_bits.append(page_meta_keywords())
-
-        return clean_join(' ', text_bits)
-
-    def get_plugin_search_text(self, base_plugin, request):
-        try:
-            plugin_content_bits = get_plugin_index_data(base_plugin, request)
-        except:
-            return ''
-        return clean_join(' ', plugin_content_bits)
-
-    def get_model(self):
-        return Title
-
-    def get_index_queryset(self, language):
-        queryset = Title.objects.public().filter(
-            Q(page__publication_date__lt=timezone.now()) | Q(
-                page__publication_date__isnull=True),
-            Q(page__publication_end_date__gte=timezone.now()) | Q(
-                page__publication_end_date__isnull=True),
-            Q(redirect__exact='') | Q(redirect__isnull=True),
-            language=language
-        ).select_related('page').distinct()
-        return queryset
diff --git a/project/aldryn_search/templates/aldryn_search/base.html b/project/aldryn_search/templates/aldryn_search/base.html
deleted file mode 100644 (file)
index 527e7de..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "base.html" %}
-
-{% block content %}
-<div class="plugin-search">
-       {% block content_search %}{% endblock %}
-</div>
-{% endblock %}
\ No newline at end of file
diff --git a/project/aldryn_search/templates/aldryn_search/includes/pagination.html b/project/aldryn_search/templates/aldryn_search/includes/pagination.html
deleted file mode 100644 (file)
index 942b4f7..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-{% load i18n spurl %}
-
-{% if page_obj.has_other_pages %}
-
-<div class="pagenav">
-       {% with from=page_obj.start_index until=page_obj.end_index count=page_obj.paginator.count %}
-       <p>{% blocktrans %}{{ from }} – {{ until }} of {{ count }} {{ obj_string }} are displayed{% endblocktrans %}</p>
-       {% endwith %}
-       <ul>
-               {% if page_obj.has_previous %}
-               <li class="prev"><a href="{% spurl base='' query=request.GET set_query='page={{ page_obj.previous_page_number }}' %}">&laquo; {% trans "previous" %}</a></li>
-               {% endif %}
-               {% for page_num in page_obj.page_range %}
-               {% if page_num %}
-               {% if page_obj.number == page_num %}
-               <li class="page active"><span>{{ page_num }}</span></li>
-               {% else %}
-               <li class="page"><a href="{% spurl base='' query=request.GET set_query='page={{ page_num }}' %}">{{ page_num }}</a></li>
-               {% endif %}
-               {% else %}
-               <li class="jumper"><span>...</span></li>
-               {% endif %}
-               {% endfor %}
-               {% if page_obj.has_next %}
-               <li class="prev"><a href="{% spurl base='' query=request.GET set_query='page={{ page_obj.next_page_number }}' %}">{% trans "next" %} &raquo;</a></li>
-               {% endif %}
-       </ul>
-</div>
-{% endif %}
\ No newline at end of file
diff --git a/project/aldryn_search/templates/aldryn_search/includes/search_items.html b/project/aldryn_search/templates/aldryn_search/includes/search_items.html
deleted file mode 100644 (file)
index 0d65bff..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-{% load i18n highlight %}
-
-{% for result in page_obj.object_list %}
-<li>
-       <h3><a href="{{ result.url }}">{{ result.title }}</a></h3>
-       {% if result.text %}
-       <p>{% highlight result.text with request.GET.q %}</p>
-       {% endif %}
-       <p><a href="{{ result.url }}">{{ result.url }}</a></p>
-</li>
-{% empty %}
-<li>{% trans "No results found for" %} "{{ request.GET.q }}"</li>
-{% endfor %}
\ No newline at end of file
diff --git a/project/aldryn_search/templates/aldryn_search/search_results.html b/project/aldryn_search/templates/aldryn_search/search_results.html
deleted file mode 100644 (file)
index ce5b5a1..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends "aldryn_search/base.html" %}
-{% load i18n standard_form %}
-
-{% block content_search %}
-<form action="." method="get" class="frm frm-search">
-       {% standard_widget form.q options custom_class='input-large' placeholder=_('enter your search term') %}
-       {% standard_submit %}
-</form>
-
-<div class="search-results">
-       <ul>
-               {% include "aldryn_search/includes/search_items.html" %}
-       </ul>
-</div>
-
-{% include "aldryn_search/includes/pagination.html" with obj_string=_('results') %}
-{% endblock %}
\ No newline at end of file
diff --git a/project/aldryn_search/tests.py b/project/aldryn_search/tests.py
deleted file mode 100644 (file)
index dabb191..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-from django.template import Template
-from django.test import TestCase
-from django.test.utils import override_settings
-
-from cms.plugin_base import CMSPluginBase
-from cms.plugin_pool import plugin_pool
-from cms.models.placeholdermodel import Placeholder
-from cms.models import CMSPlugin
-
-from aldryn_search.search_indexes import TitleIndex
-from .helpers import get_plugin_index_data, get_request
-
-
-test_settings = {
-    'ALLOWED_HOSTS': ['localhost'],
-    'CMS_LANGUAGES': {1: [{'code': 'en', 'name': 'English'}]},
-    'CMS_TEMPLATES': (("whee.html", "Whee Template"),),
-    'LANGUAGES': (('en', 'English'),),
-    'LANGUAGE_CODE': 'en',
-    'TEMPLATE_LOADERS': ('aldryn_search.tests.FakeTemplateLoader',),
-}
-
-
-class FakeTemplateLoader(object):
-    is_usable = True
-
-    def __init__(self, name, dirs):
-        pass
-
-    def __iter__(self):
-        yield self.__class__
-        yield "{{baz}}"
-
-
-class NotIndexedPlugin(CMSPluginBase):
-    model = CMSPlugin
-    plugin_content = 'rendered plugin content'
-    render_template = Template(plugin_content)
-
-    def render(self, context, instance, placeholder):
-        return context
-
-plugin_pool.register_plugin(NotIndexedPlugin)
-
-
-@override_settings(**test_settings)
-class PluginIndexingTests(TestCase):
-
-    def setUp(self):
-        self.index = TitleIndex()
-        self.request = get_request(language='en')
-
-    def get_plugin(self):
-        instance = CMSPlugin(
-            language='en',
-            plugin_type="NotIndexedPlugin",
-            placeholder=Placeholder(id=1235)
-        )
-        instance.cmsplugin_ptr = instance
-        instance.pk = 1234  # otherwise plugin_meta_context_processor() crashes
-        return instance
-
-    def test_plugin_indexing_is_enabled_by_default(self):
-        cms_plugin = self.get_plugin()
-        indexed_content = self.index.get_plugin_search_text(
-            cms_plugin, self.request)
-        self.assertEqual(NotIndexedPlugin.plugin_content, indexed_content)
-
-    def test_plugin_indexing_can_be_disabled_on_model(self):
-        cms_plugin = self.get_plugin()
-        cms_plugin.search_fulltext = False
-        indexed_content = self.index.get_plugin_search_text(
-            cms_plugin, self.request)
-        self.assertEqual('', indexed_content)
-
-    def test_plugin_indexing_can_be_disabled_on_plugin(self):
-        NotIndexedPlugin.search_fulltext = False
-
-        try:
-            self.assertEqual(
-                '', self.index.get_plugin_search_text(self.get_plugin(), self.request))
-        finally:
-            del NotIndexedPlugin.search_fulltext
-
-    def test_page_title_is_indexed_using_prepare(self):
-        """This tests the indexing path way used by update_index mgmt command"""
-        from cms.api import create_page
-        page = create_page(
-            title="Whoopee", template="whee.html", language="en")
-
-        from haystack import connections
-        from haystack.constants import DEFAULT_ALIAS
-        search_conn = connections[DEFAULT_ALIAS]
-        unified_index = search_conn.get_unified_index()
-
-        from cms.models import Title
-        index = unified_index.get_index(Title)
-
-        title = Title.objects.get(pk=page.title_set.all()[0].pk)
-        index.index_queryset(DEFAULT_ALIAS)  # initialises index._backend_alias
-        indexed = index.prepare(title)
-        self.assertEqual('Whoopee', indexed['title'])
-        self.assertEqual('Whoopee', indexed['text'])
-
-    def test_page_title_is_indexed_using_update_object(self):
-        """This tests the indexing path way used by the RealTimeSignalProcessor"""
-        from cms.api import create_page
-        page = create_page(
-            title="Whoopee", template="whee.html", language="en")
-
-        from haystack import connections
-        from haystack.constants import DEFAULT_ALIAS
-        search_conn = connections[DEFAULT_ALIAS]
-        unified_index = search_conn.get_unified_index()
-
-        from cms.models import Title
-        index = unified_index.get_index(Title)
-
-        title = Title.objects.get(pk=page.title_set.all()[0].pk)
-        index.update_object(title, using=DEFAULT_ALIAS)
-        indexed = index.prepared_data
-        self.assertEqual('Whoopee', indexed['title'])
-        self.assertEqual('Whoopee', indexed['text'])
diff --git a/project/aldryn_search/utils.py b/project/aldryn_search/utils.py
deleted file mode 100644 (file)
index e0a720a..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import six
-
-from lxml.html.clean import Cleaner as LxmlCleaner
-
-from django.core.exceptions import ImproperlyConfigured
-from django.db import models
-try:
-    from django.utils.encoding import force_unicode
-except ImportError:
-    from django.utils.encoding import force_text as force_unicode
-from django.utils.importlib import import_module
-from django.utils.html import strip_tags as _strip_tags
-
-from haystack import DEFAULT_ALIAS
-from haystack.indexes import SearchIndex
-
-from cms.utils.i18n import get_language_code
-
-from .conf import settings
-
-
-def alias_from_language(language):
-    """
-    Returns alias if alias is a valid language.
-    """
-    language = get_language_code(language)
-
-    if language == settings.ALDRYN_SEARCH_DEFAULT_LANGUAGE:
-        return DEFAULT_ALIAS
-    return language
-
-
-def clean_join(separator, iterable):
-    """
-    Filters out iterable to only join non empty items.
-    """
-    return separator.join(filter(None, iterable))
-
-
-def get_callable(string_or_callable):
-    """
-    If given a callable then it returns it, otherwise it resolves the path
-    and returns an object.
-    """
-    if callable(string_or_callable):
-        return string_or_callable
-    else:
-        module_name, object_name = string_or_callable.rsplit('.', 1)
-        if module_name.startswith('aldryn_search'):
-            module_name = "project." + module_name
-        module = import_module(module_name)
-        return getattr(module, object_name)
-
-
-def _get_language_from_alias_func():
-    path_or_callable = settings.ALDRYN_SEARCH_LANGUAGE_FROM_ALIAS
-
-    if path_or_callable:
-        try:
-            func = get_callable(path_or_callable)
-        except AttributeError as error:
-            raise ImproperlyConfigured(
-                'ALDRYN_SEARCH_LANGUAGE_FROM_ALIAS: %s' % (str(error)))
-        if not callable(func):
-            raise ImproperlyConfigured(
-                'ALDRYN_SEARCH_LANGUAGE_FROM_ALIAS: %s is not callable' % func)
-    else:
-        func = None
-    return func
-
-
-def get_index_base():
-    index_string = settings.ALDRYN_SEARCH_INDEX_BASE_CLASS
-    try:
-        BaseClass = get_callable(index_string)
-    except AttributeError as error:
-        raise ImproperlyConfigured(
-            'ALDRYN_SEARCH_INDEX_BASE_CLASS: %s' % (str(error)))
-
-    if not issubclass(BaseClass, SearchIndex):
-        raise ImproperlyConfigured(
-            'ALDRYN_SEARCH_INDEX_BASE_CLASS: %s is not a subclass of haystack.indexes.SearchIndex' % index_string)
-
-    required_fields = ['text', 'language']
-
-    if not all(field in BaseClass.fields for field in required_fields):
-        raise ImproperlyConfigured('ALDRYN_SEARCH_INDEX_BASE_CLASS: %s must contain at least these fields: %s' % (
-            index_string, required_fields))
-    return BaseClass
-
-
-def language_from_alias(alias):
-    """
-    Returns alias if alias is a valid language.
-    """
-    languages = [language[0] for language in settings.LANGUAGES]
-
-    return alias if alias in languages else None
-
-
-def get_field_value(obj, name):
-    """
-    Given a model instance and a field name (or attribute),
-    returns the value of the field or an empty string.
-    """
-    fields = name.split('__')
-
-    name = fields[0]
-
-    try:
-        obj._meta.get_field(name)
-    except (AttributeError, models.FieldDoesNotExist):
-        # we catch attribute error because obj will not always be a model
-        # specially when going through multiple relationships.
-        value = getattr(obj, name, None) or ''
-    else:
-        value = getattr(obj, name)
-
-    if len(fields) > 1:
-        remaining = '__'.join(fields[1:])
-        return get_field_value(value, remaining)
-    return value
-
-
-def get_model_path(model_or_string):
-    if not isinstance(model_or_string, six.string_types):
-        # it's a model class
-        app_label = model_or_string._meta.app_label
-        model_name = model_or_string._meta.object_name
-        model_or_string = '{0}.{1}'.format(app_label, model_name)
-    return model_or_string.lower()
-
-
-def strip_tags(value):
-    """
-    Returns the given HTML with all tags stripped.
-    We use lxml to strip all js tags and then hand the result to django's strip tags.
-    """
-    # strip any new lines
-    value = value.strip()
-
-    if value:
-        partial_strip = LxmlCleaner().clean_html(value)
-        value = _strip_tags(partial_strip)
-    return value
diff --git a/project/aldryn_search/views.py b/project/aldryn_search/views.py
deleted file mode 100644 (file)
index ea4d75b..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-
-from django.views.generic import ListView
-from django.views.generic.edit import FormMixin
-
-from django import forms
-
-from haystack.forms import SearchForm
-from haystack.query import SearchQuerySet
-
-
-from aldryn_common.paginator import DiggPaginator
-
-from .conf import settings
-
-
-class AldrynFacetedSearchForm(SearchForm):
-    selected_facets = forms.CharField(required=False, widget=forms.HiddenInput)
-    courant = forms.BooleanField(required=False, widget=forms.HiddenInput)
-    cloture = forms.BooleanField(required=False, widget=forms.HiddenInput)
-
-    def search(self):
-        sqs = SearchQuerySet()
-        sqs = sqs.facet('bureaux').facet(
-            'section').facet('annee').facet('partenaire')
-
-        if self.q:
-            sqs = sqs.filter(content=sqs.query.clean(self.q))
-
-        if self.courant:
-            sqs = sqs.filter(date_fin__gte=datetime.date.today())
-        if self.cloture:
-            sqs = sqs.filter(date_fin__lt=datetime.date.today())
-
-        self.selected_facets = list(
-            set(self.selected_facets.split('&') + self.selected_facets_get))
-
-        for facet in self.selected_facets:
-            if "__" not in facet:
-                continue
-
-            field, value = facet.split("__", 1)
-
-            if value:
-                sqs = sqs.narrow(u'%s:"%s"' % (field, sqs.query.clean(value)))
-
-        return sqs
-
-
-class AldrynSearchView(FormMixin, ListView):
-    form_class = AldrynFacetedSearchForm
-
-    paginate_by = settings.ALDRYN_SEARCH_PAGINATION
-    paginator_class = DiggPaginator
-
-    template_name = 'aldryn_search/search_results.html'
-
-    def get(self, request, *args, **kwargs):
-        self.form = AldrynFacetedSearchForm(self.request.GET)
-        self.form.q = self.request.GET.get('q', '')
-        self.form.courant = self.request.GET.get('courant', '')
-        self.form.cloture = self.request.GET.get('cloture', '')
-        self.form.selected_facets = self.request.GET.get('selected_facets', '')
-        self.form.selected_facets_get = self.request.GET.getlist(
-            'selected_facets', [])
-        return super(AldrynSearchView, self).get(request, *args, **kwargs)
-
-    def get_queryset(self):
-        self.queryset = self.form.search()
-        if not self.request.user.is_authenticated():
-            self.queryset = self.queryset.exclude(login_required=True)
-        self.facet_counts = self.queryset.facet_counts()
-        return self.queryset.order_by('-date_pub')
-
-    def get_context_data(self, **kwargs):
-        context = super(AldrynSearchView, self).get_context_data(**kwargs)
-
-        context['form'] = self.form
-        context['facets'] = self.facet_counts
-        context['selected_facets'] = self.request.GET.getlist(
-            'selected_facets', [])
-        if self.object_list.query.backend.include_spelling:
-            context['suggestion'] = self.form.get_suggestion()
-        return context
diff --git a/project/cmsplugin_contact_plus/__init__.py b/project/cmsplugin_contact_plus/__init__.py
new file mode 100755 (executable)
index 0000000..923b987
--- /dev/null
@@ -0,0 +1 @@
+__version__ = '1.2.2'
diff --git a/project/cmsplugin_contact_plus/actions.py b/project/cmsplugin_contact_plus/actions.py
new file mode 100644 (file)
index 0000000..51ed0cb
--- /dev/null
@@ -0,0 +1,104 @@
+import csv 
+from django.http import HttpResponse
+#from setuptools.compat import unicode
+
+class LUT(object):
+        
+    def __init__(self):
+        self.lut = []
+            
+    def add_field(self, field_name):
+        """ """
+        if field_name not in self.lut:
+            self.lut.append(field_name)
+            
+    def get_idx(self, field_name):
+        """ """
+        return self.lut.index(field_name)
+    
+    
+    
+def export_as_csv_action(description="Export selected objects as CSV file",
+                         fields=None, exclude=None, header=True, json_fields=None):
+    """
+    This function returns an export csv action
+    'fields' and 'exclude' work like in django ModelForm
+    'header' is whether or not to output the column names as the first row
+    
+    json_fields will be exploaded to rows.
+    """
+
+    from itertools import chain
+
+    def export_as_csv(modeladmin, request, queryset):
+        """
+        Generic csv export admin action.
+        based on http://djangosnippets.org/snippets/2369/
+        """
+        opts = modeladmin.model._meta
+        field_names = set([field.name for field in opts.fields])
+        many_to_many_field_names = set([many_to_many_field.name for many_to_many_field in opts.many_to_many])
+        if fields:
+            fieldset = set(fields)
+            field_names = field_names & fieldset
+        elif exclude:
+            excludeset = set(exclude)
+            field_names = field_names - excludeset
+
+        response = HttpResponse(content_type='text/csv')
+        response['Content-Disposition'] = 'attachment; filename=%s.csv' % unicode(opts).replace('.', '_')
+        
+        writer = csv.writer(response)
+        """
+        if header:
+            writer.writerow(list(chain(field_names, many_to_many_field_names)))
+        # default: does not split json_fields to rows. (json is in one row) 
+        if not json_fields:
+            for obj in queryset:
+                row = []
+                for field in field_names:
+                    row.append(unicode(getattr(obj, field)))
+                for field in many_to_many_field_names:
+                    row.append(unicode(getattr(obj, field).all()))
+                writer.writerow(row)
+        else:
+        """
+        # build LUT
+        lut = LUT()
+        for obj in queryset:
+            for field in field_names:
+                if field in json_fields:
+                    j = getattr(obj, field)
+                    for l in j:
+                        try:
+                            for k, v in l.iteritems(): 
+                                lut.add_field(k)
+                        except AttributeError:
+                            pass
+                else:
+                    lut.add_field(field)
+        
+        if header:
+            writer.writerow(lut.lut)
+        for obj in queryset:
+            row = ['']*len(lut.lut)
+            for field in field_names:
+                if field in json_fields:
+                    j = getattr(obj, field)
+                    for l in j:
+                        for k, v in l.iteritems(): 
+                            try:
+                                row[lut.get_idx(k)] = v.encode('utf-8')
+                            except AttributeError:
+                                pass
+                else:
+                    row[lut.get_idx(field)] = unicode(getattr(obj, field))
+            for field in many_to_many_field_names:
+                row[lut.get_idx(field)] = unicode(getattr(obj, field).all())
+            writer.writerow(row)
+            
+            
+        return response
+    export_as_csv.short_description = description
+    return export_as_csv
diff --git a/project/cmsplugin_contact_plus/admin.py b/project/cmsplugin_contact_plus/admin.py
new file mode 100755 (executable)
index 0000000..a4ff8d0
--- /dev/null
@@ -0,0 +1,30 @@
+from django.contrib import admin
+from django.http import HttpResponse
+
+from inline_ordering.admin import OrderableStackedInline
+from .models import ExtraField, ContactPlus, ContactRecord
+
+from .actions import export_as_csv_action
+
+class ExtraFieldInline(OrderableStackedInline):
+    model = ExtraField
+
+
+class ContactFormPlusAdmin(admin.ModelAdmin):
+    model = ContactPlus
+    inlines = (ExtraFieldInline, )
+
+
+class ContactRecordAdmin(admin.ModelAdmin):
+    model = ContactRecord
+    actions = [export_as_csv_action("CSV Export", 
+        fields = ['contact_form', 'date_of_entry', 'date_processed', 'data'],
+        header = True,
+        json_fields = ['data']), # 
+    ]
+
+
+admin.site.register(ExtraField)
+
+admin.site.register(ContactRecord, ContactRecordAdmin)
+admin.site.register(ContactPlus, ContactFormPlusAdmin)
diff --git a/project/cmsplugin_contact_plus/cms_plugins.py b/project/cmsplugin_contact_plus/cms_plugins.py
new file mode 100755 (executable)
index 0000000..738ea6d
--- /dev/null
@@ -0,0 +1,68 @@
+from django.utils.translation import ugettext_lazy as _
+from django.conf import settings
+from cms.plugin_base import CMSPluginBase
+from cms.plugin_pool import plugin_pool
+
+from .admin import ExtraFieldInline
+from .models import ContactPlus
+from .forms import ContactFormPlus
+
+
+import time
+
+def handle_uploaded_file(f, ts):    
+    destination = open('%s/%s' % (settings.MEDIA_ROOT, ts + '-' + f.name), 'wb+')
+
+    for chunk in f.chunks():
+        destination.write(chunk)
+    destination.close()
+    
+    
+class CMSContactPlusPlugin(CMSPluginBase):
+    """ 
+    """
+    model = ContactPlus
+    inlines = [ExtraFieldInline, ]
+    name = _('Contact Form')
+    render_template = "cmsplugin_contact_plus/contact.html"
+    cache = False
+
+    def render(self, context, instance, placeholder):
+        request = context['request']
+
+        if instance and instance.template:
+            self.render_template = instance.template
+
+        if request.method == "POST":
+            form = ContactFormPlus(contactFormInstance=instance, 
+                    request=request, 
+                    data=request.POST, 
+                    files=request.FILES)
+            if form.is_valid():
+                ts = str(int(time.time()))
+
+                for fl in request.FILES:
+                    for f in request.FILES.getlist(fl):
+                        handle_uploaded_file(f, ts)
+
+                form.send(instance.recipient_email, request, ts, instance, form.is_multipart)
+                context.update({
+                    'contact': instance,
+                })
+                return context
+            else:
+                context.update({
+                    'contact': instance,
+                    'form': form,
+                })
+
+        else:
+            form = ContactFormPlus(contactFormInstance=instance, request=request)
+            context.update({
+                    'contact': instance,
+                    'form': form,
+            })
+        return context
+
+
+plugin_pool.register_plugin(CMSContactPlusPlugin)
diff --git a/project/cmsplugin_contact_plus/forms.py b/project/cmsplugin_contact_plus/forms.py
new file mode 100755 (executable)
index 0000000..90651bd
--- /dev/null
@@ -0,0 +1,129 @@
+from django import forms
+from django.core.mail import EmailMessage
+from django.template.loader import render_to_string
+from django.contrib.sites.models import Site
+from django.template.defaultfilters import slugify
+from django.conf import settings
+from django.utils.translation import ugettext_lazy as _
+
+from simplemathcaptcha.fields import MathCaptchaField
+from .models import ContactPlus, ContactRecord
+from .signals import contact_message_sent
+
+
+class ContactFormPlus(forms.Form):
+
+    def __init__(self, contactFormInstance, request, *args, **kwargs):
+        super(ContactFormPlus, self).__init__(*args, **kwargs)
+        if 'instance' not in kwargs:
+            for extraField in contactFormInstance.extrafield_set.all():
+                if extraField.fieldType == 'CharField':
+                    self.fields[slugify(extraField.label)] = forms.CharField(label=extraField.label,
+                            initial=extraField.initial,
+                            required=extraField.required)
+                elif extraField.fieldType == 'BooleanField':
+                    self.fields[slugify(extraField.label)] = forms.BooleanField(label=extraField.label,
+                            initial=extraField.initial,
+                            required=extraField.required)
+                elif extraField.fieldType == 'EmailField':
+                    self.fields[slugify(extraField.label)] = forms.EmailField(label=extraField.label,
+                            initial=extraField.initial,
+                            required=extraField.required)
+                elif extraField.fieldType == 'DecimalField':
+                    self.fields[slugify(extraField.label)] = forms.DecimalField(label=extraField.label,
+                            initial=extraField.initial,
+                            required=extraField.required)
+                elif extraField.fieldType == 'FloatField':
+                    self.fields[slugify(extraField.label)] = forms.FloatField(label=extraField.label,
+                            initial=extraField.initial,
+                            required=extraField.required)
+                elif extraField.fieldType == 'FileField': 
+                    self.fields[slugify(extraField.label)] = forms.FileField(label=extraField.label,
+                            initial=extraField.initial,
+                            required=extraField.required)
+                elif extraField.fieldType == 'ImageField': 
+                    self.fields[slugify(extraField.label)] = forms.ImageField(label=extraField.label,
+                            initial=extraField.initial,
+                            required=extraField.required)
+                elif extraField.fieldType == 'IntegerField':
+                    self.fields[slugify(extraField.label)] = forms.IntegerField(label=extraField.label,
+                            initial=extraField.initial,
+                            required=extraField.required)
+                elif extraField.fieldType == 'IPAddressField':
+                    self.fields[slugify(extraField.label)] = forms.IPAddressField(label=extraField.label,
+                            initial=extraField.initial,
+                            required=extraField.required)
+                elif extraField.fieldType == 'auto_Textarea':
+                    self.fields[slugify(extraField.label)] = forms.CharField(label=extraField.label,
+                            initial=extraField.initial,
+                            widget=forms.Textarea,
+                            required=extraField.required)
+                elif extraField.fieldType == 'auto_hidden_input':
+                    self.fields[slugify(extraField.label)] = forms.CharField(label=extraField.label,
+                            initial=extraField.initial,
+                            widget=forms.HiddenInput,
+                            required=False)
+                elif extraField.fieldType == 'auto_referral_page':
+                    lInitial = _("No referral available.")
+                    if request:
+                        lInitial = request.META.get('HTTP_REFERER', _('No referral available.'))
+                    self.fields[slugify(extraField.label)] = forms.CharField(label=extraField.label,
+                            initial=lInitial,  # NOTE: This overwrites extraField.initial!
+                            widget=forms.HiddenInput,
+                            required=False)
+                elif extraField.fieldType == 'MathCaptcha':
+                    self.fields[slugify(extraField.label)] = MathCaptchaField(
+                                                label=extraField.label,
+                                                initial=extraField.initial,
+                                                required=True)
+                elif extraField.fieldType == 'auto_GET_parameter':
+                    lInitial = _("Key/value parameter not available.")
+                    if request:
+                        lInitial = request.GET.get(slugify(extraField.label), 'n/a')
+                    self.fields[slugify(extraField.label)] = forms.CharField(label=extraField.label,
+                            initial=lInitial,  # NOTE: This overwrites extraField.initial!
+                            widget=forms.HiddenInput,
+                            required=False)
+
+    def send(self, recipient_email, request, ts, instance=None, multipart=False):
+        current_site = Site.objects.get_current()
+        if instance:
+            order = ContactPlus.objects.get(id=instance.id).extrafield_set.order_by('inline_ordering_position')
+            ordered_dic_list = []
+            for field in order:
+                key = slugify(field.label)
+                value = self.cleaned_data.get(key, '(no input)')
+                # redefine value for files... 
+                if field.fieldType in ["FileField", "ImageField"]:
+                    val = ts + '-' + str(value)
+                    if settings.MEDIA_URL.startswith("http"):
+                        value = "%s%s" % (settings.MEDIA_URL, val)
+                    else:
+                        value = "http://%s%s%s" % (current_site, settings.MEDIA_URL, val)
+                ordered_dic_list.append({field.label: value})
+
+        # Automatically match reply-to email adress in form
+        tmp_headers = {}
+        try:
+            reply_email_label = getattr(settings, 'CONTACT_PLUS_REPLY_EMAIL_LABEL', None)
+            if reply_email_label is not None:
+                tmp_headers.update({'Reply-To': self.cleaned_data[reply_email_label]})
+        except:
+            pass
+
+        email_message = EmailMessage(
+            "[" + current_site.domain.upper() + "]",
+                render_to_string("cmsplugin_contact_plus/email.txt", {'data': self.cleaned_data,
+                                                                      'ordered_data': ordered_dic_list,
+                                                                      'instance': instance,
+                                                                      }),
+                    from_email=getattr(settings, 'DEFAULT_FROM_EMAIL', None),
+                    to=[recipient_email, ],
+                    headers=tmp_headers,)
+        email_message.send(fail_silently=True)
+
+        if instance.collect_records:# and not multipart:
+            record = ContactRecord(contact_form=instance, data=ordered_dic_list)#self.cleaned_data)
+            record.save()
+
+        contact_message_sent.send(sender=self, data=self.cleaned_data)
diff --git a/project/cmsplugin_contact_plus/inline_ordering/__init__.py b/project/cmsplugin_contact_plus/inline_ordering/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/project/cmsplugin_contact_plus/inline_ordering/admin.py b/project/cmsplugin_contact_plus/inline_ordering/admin.py
new file mode 100644 (file)
index 0000000..5735067
--- /dev/null
@@ -0,0 +1,21 @@
+from django.contrib.admin import TabularInline, StackedInline
+from django.conf import settings
+
+INLINE_ORDERING_JS = getattr(settings,
+                             'INLINE_ORDERING_JS', 'inline_ordering.js')
+
+
+class OrderableStackedInline(StackedInline):
+    
+    """Adds necessary media files to regular Django StackedInline"""
+    
+    class Media:
+        js = (INLINE_ORDERING_JS,)
+
+
+class OrderableTabularInline(TabularInline):
+    
+    """Adds necessary media files to regular Django TabularInline"""
+    
+    class Media:
+        js = (INLINE_ORDERING_JS,)
diff --git a/project/cmsplugin_contact_plus/inline_ordering/media b/project/cmsplugin_contact_plus/inline_ordering/media
new file mode 120000 (symlink)
index 0000000..93a464b
--- /dev/null
@@ -0,0 +1 @@
+static/
\ No newline at end of file
diff --git a/project/cmsplugin_contact_plus/inline_ordering/models.py b/project/cmsplugin_contact_plus/inline_ordering/models.py
new file mode 100644 (file)
index 0000000..1b773aa
--- /dev/null
@@ -0,0 +1,23 @@
+from django.db import models
+
+class Orderable(models.Model):
+    
+    """Add extra field and default ordering column for and inline orderable model"""
+    
+    inline_ordering_position = models.IntegerField(blank = True, 
+                                                   null = True, 
+                                                   editable = True)
+    
+    class Meta:
+        abstract = True 
+        ordering = ('inline_ordering_position',)
+    
+    def save(self, force_insert=False, force_update=False, using=None):
+        """Calculate position (max+1) for new records"""
+        if not self.inline_ordering_position:
+            max = self.__class__.objects.filter().aggregate(models.Max('inline_ordering_position'))
+            try: 
+                self.inline_ordering_position = max['inline_ordering_position__max'] + 1
+            except TypeError:
+                self.inline_ordering_position = 1
+        return super(Orderable, self).save(force_insert=force_insert, force_update=force_update, using=using)
\ No newline at end of file
diff --git a/project/cmsplugin_contact_plus/inline_ordering/settings.py b/project/cmsplugin_contact_plus/inline_ordering/settings.py
new file mode 100644 (file)
index 0000000..c8b07b8
--- /dev/null
@@ -0,0 +1,4 @@
+from django.conf import settings
+
+# path to inline_ordering.js
+INLINE_ORDERING_JS = getattr(settings, 'INLINE_ORDERING_JS', settings.MEDIA_URL + 'inline_ordering.js')
diff --git a/project/cmsplugin_contact_plus/inline_ordering/static/inline_ordering.js b/project/cmsplugin_contact_plus/inline_ordering/static/inline_ordering.js
new file mode 100644 (file)
index 0000000..63ccf93
--- /dev/null
@@ -0,0 +1,73 @@
+var InlineOrdering = {
+
+    /**
+     * Get list of elements that can be reordered
+     *
+     * At this point, only already existent records can be reordered (ie. where pk != '')
+     *
+     * @return Array
+     * @todo Check if given record changed, and if so, make it reorderable
+     * @todo Primary key might not be 'id' - better selector needed
+     *
+     */
+    getOrderables: function () {
+        var allInlineRows = InlineOrdering.jQuery('.inline-related'),
+            i = 0,
+            ids = [];
+        
+        for (i = 0; i < allInlineRows.length; i = i + 1) {
+            if (InlineOrdering.jQuery('.inline_ordering_position input, .field-inline_ordering_position input', allInlineRows[i]).val()) {
+                ids.push('#' + allInlineRows[i].id);
+            }
+        }
+        
+        // this redundant way is required, so that proper order is maintained, 
+        // otherwise orderables were returned in more or less random order
+        return InlineOrdering.jQuery(ids.join(', ')); 
+    },
+    
+    /**
+     * Inits the jQuery UI D&D
+     *
+     */
+    init: function (jQuery) {
+        InlineOrdering.jQuery = jQuery;
+        InlineOrdering.jQuery("div.inline-group").sortable({
+            axis: 'y',
+            placeholder: 'ui-state-highlight',
+            forcePlaceholderSize: 'true',
+            items: InlineOrdering.getOrderables(),
+            update: InlineOrdering.update
+        });
+        //jQuery("div.inline-group").disableSelection();
+        
+        InlineOrdering.jQuery('div.field-inline_ordering_position').hide();
+        InlineOrdering.jQuery('div.inline_ordering_position').hide();
+        InlineOrdering.jQuery('td.inline_ordering_position input').hide();
+        
+        InlineOrdering.jQuery('.add-row a').click(InlineOrdering.update);
+        
+        InlineOrdering.getOrderables().css('cursor', 'move');
+
+        InlineOrdering.update();
+    },
+    
+    jQuery: null,
+    
+    /**
+     * Updates the position field
+     *
+     */
+    update: function () {
+        InlineOrdering.getOrderables().each(function (i) {
+            InlineOrdering.jQuery('input[id$=inline_ordering_position]', this).val(i + 1);
+            InlineOrdering.jQuery(this).find('h3 > span.position').remove();
+            InlineOrdering.jQuery(this).find('h3').append('<span class="position">#' + (i + 1).toFixed() + '</span>');
+        });
+    }
+    
+};
+
+django.jQuery(function () {
+    InlineOrdering.init(django.jQuery);
+});
diff --git a/project/cmsplugin_contact_plus/jsonfield/__init__.py b/project/cmsplugin_contact_plus/jsonfield/__init__.py
new file mode 100644 (file)
index 0000000..9c2a0ed
--- /dev/null
@@ -0,0 +1 @@
+from .fields import JSONField, JSONCharField
diff --git a/project/cmsplugin_contact_plus/jsonfield/encoder.py b/project/cmsplugin_contact_plus/jsonfield/encoder.py
new file mode 100644 (file)
index 0000000..2b41e34
--- /dev/null
@@ -0,0 +1,59 @@
+from django.db.models.query import QuerySet
+from django.utils import six, timezone
+from django.utils.encoding import force_text
+from django.utils.functional import Promise
+import datetime
+import decimal
+import types
+import json
+import uuid
+
+
+class JSONEncoder(json.JSONEncoder):
+    """
+    JSONEncoder subclass that knows how to encode date/time/timedelta,
+    decimal types, generators and other basic python objects.
+
+    Taken from https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/utils/encoders.py
+    """
+    def default(self, obj):
+        # For Date Time string spec, see ECMA 262
+        # http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
+        if isinstance(obj, Promise):
+            return force_text(obj)
+        elif isinstance(obj, datetime.datetime):
+            representation = obj.isoformat()
+            if obj.microsecond:
+                representation = representation[:23] + representation[26:]
+            if representation.endswith('+00:00'):
+                representation = representation[:-6] + 'Z'
+            return representation
+        elif isinstance(obj, datetime.date):
+            return obj.isoformat()
+        elif isinstance(obj, datetime.time):
+            if timezone and timezone.is_aware(obj):
+                raise ValueError("JSON can't represent timezone-aware times.")
+            representation = obj.isoformat()
+            if obj.microsecond:
+                representation = representation[:12]
+            return representation
+        elif isinstance(obj, datetime.timedelta):
+            return six.text_type(total_seconds(obj))
+        elif isinstance(obj, decimal.Decimal):
+            # Serializers will coerce decimals to strings by default.
+            return float(obj)
+        elif isinstance(obj, uuid.UUID):
+            return six.text_type(obj)
+        elif isinstance(obj, QuerySet):
+            return tuple(obj)
+        elif hasattr(obj, 'tolist'):
+            # Numpy arrays and array scalars.
+            return obj.tolist()
+        elif hasattr(obj, '__getitem__'):
+            try:
+                return dict(obj)
+            except:
+                pass
+        elif hasattr(obj, '__iter__'):
+            return tuple(item for item in obj)
+        return super(JSONEncoder, self).default(obj)
diff --git a/project/cmsplugin_contact_plus/jsonfield/fields.py b/project/cmsplugin_contact_plus/jsonfield/fields.py
new file mode 100644 (file)
index 0000000..01dd6bc
--- /dev/null
@@ -0,0 +1,170 @@
+import copy
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+try:
+    from django.utils import six
+except ImportError:
+    import six
+
+try:
+    import json
+except ImportError:
+    from django.utils import simplejson as json
+
+from django.forms import fields
+try:
+    from django.forms.utils import ValidationError
+except ImportError:
+    from django.forms.util import ValidationError
+
+from .subclassing import SubfieldBase
+from .encoder import JSONEncoder
+
+
+class JSONFormFieldBase(object):
+
+    def to_python(self, value):
+        if isinstance(value, six.string_types):
+            try:
+                return json.loads(value, **self.load_kwargs)
+            except ValueError:
+                raise ValidationError(_("Enter valid JSON"))
+        return value
+
+    def clean(self, value):
+
+        if not value and not self.required:
+            return None
+
+        # Trap cleaning errors & bubble them up as JSON errors
+        try:
+            return super(JSONFormFieldBase, self).clean(value)
+        except TypeError:
+            raise ValidationError(_("Enter valid JSON"))
+
+
+class JSONFormField(JSONFormFieldBase, fields.CharField):
+    pass
+
+class JSONCharFormField(JSONFormFieldBase, fields.CharField):
+    pass
+
+
+class JSONFieldBase(six.with_metaclass(SubfieldBase, models.Field)):
+
+    def __init__(self, *args, **kwargs):
+        self.dump_kwargs = kwargs.pop('dump_kwargs', {
+            'cls': JSONEncoder,
+            'separators': (',', ':')
+        })
+        self.load_kwargs = kwargs.pop('load_kwargs', {})
+
+        super(JSONFieldBase, self).__init__(*args, **kwargs)
+
+    def pre_init(self, value, obj):
+        """Convert a string value to JSON only if it needs to be deserialized.
+
+        SubfieldBase metaclass has been modified to call this method instead of
+        to_python so that we can check the obj state and determine if it needs to be
+        deserialized"""
+
+        try:
+            if obj._state.adding:
+                # Make sure the primary key actually exists on the object before
+                # checking if it's empty. This is a special case for South datamigrations
+                # see: https://github.com/bradjasper/django-jsonfield/issues/52
+                if getattr(obj, "pk", None) is not None:
+                    if isinstance(value, six.string_types):
+                        try:
+                            return json.loads(value, **self.load_kwargs)
+                        except ValueError:
+                            raise ValidationError(_("Enter valid JSON"))
+
+        except AttributeError:
+            # south fake meta class doesn't create proper attributes
+            # see this:
+            # https://github.com/bradjasper/django-jsonfield/issues/52
+            pass
+
+        return value
+
+    def to_python(self, value):
+        """The SubfieldBase metaclass calls pre_init instead of to_python, however to_python
+        is still necessary for Django's deserializer"""
+        return value
+
+    def get_db_prep_value(self, value, connection, prepared=False):
+        """Convert JSON object to a string"""
+        if self.null and value is None:
+            return None
+        return json.dumps(value, **self.dump_kwargs)
+
+    def value_to_string(self, obj):
+        value = self._get_val_from_obj(obj)
+        return self.get_db_prep_value(value, None)
+
+    def value_from_object(self, obj):
+        value = super(JSONFieldBase, self).value_from_object(obj)
+        if self.null and value is None:
+            return None
+        return self.dumps_for_display(value)
+
+    def dumps_for_display(self, value):
+        return json.dumps(value, **self.dump_kwargs)
+
+    def formfield(self, **kwargs):
+
+        if "form_class" not in kwargs:
+            kwargs["form_class"] = self.form_class
+
+        field = super(JSONFieldBase, self).formfield(**kwargs)
+
+        if isinstance(field, JSONFormFieldBase):
+            field.load_kwargs = self.load_kwargs
+
+        if not field.help_text:
+            field.help_text = "Enter valid JSON"
+
+        return field
+
+    def get_default(self):
+        """
+        Returns the default value for this field.
+
+        The default implementation on models.Field calls force_unicode
+        on the default, which means you can't set arbitrary Python
+        objects as the default. To fix this, we just return the value
+        without calling force_unicode on it. Note that if you set a
+        callable as a default, the field will still call it. It will
+        *not* try to pickle and encode it.
+
+        """
+        if self.has_default():
+            if callable(self.default):
+                return self.default()
+            return copy.deepcopy(self.default)
+        # If the field doesn't have a default, then we punt to models.Field.
+        return super(JSONFieldBase, self).get_default()
+
+class JSONField(JSONFieldBase, models.TextField):
+    """JSONField is a generic textfield that serializes/deserializes JSON objects"""
+    form_class = JSONFormField
+
+    def dumps_for_display(self, value):
+        kwargs = { "indent": 2 }
+        kwargs.update(self.dump_kwargs)
+        return json.dumps(value, **kwargs)
+
+
+class JSONCharField(JSONFieldBase, models.CharField):
+    """JSONCharField is a generic textfield that serializes/deserializes JSON objects,
+    stored in the database like a CharField, which enables it to be used
+    e.g. in unique keys"""
+    form_class = JSONCharFormField
+
+
+try:
+    from south.modelsinspector import add_introspection_rules
+    add_introspection_rules([], ["^jsonfield\.fields\.(JSONField|JSONCharField)"])
+except ImportError:
+    pass
diff --git a/project/cmsplugin_contact_plus/jsonfield/models.py b/project/cmsplugin_contact_plus/jsonfield/models.py
new file mode 100644 (file)
index 0000000..e5faf1b
--- /dev/null
@@ -0,0 +1 @@
+# Django needs this to see it as a project
diff --git a/project/cmsplugin_contact_plus/jsonfield/subclassing.py b/project/cmsplugin_contact_plus/jsonfield/subclassing.py
new file mode 100644 (file)
index 0000000..21d0fc6
--- /dev/null
@@ -0,0 +1,60 @@
+## This file was copied from django.db.models.fields.subclassing so that we could
+## change the Creator.__set__ behavior. Read the comment below for full details.
+
+"""
+Convenience routines for creating non-trivial Field subclasses, as well as
+backwards compatibility utilities.
+
+Add SubfieldBase as the __metaclass__ for your Field subclass, implement
+to_python() and the other necessary methods and everything will work seamlessly.
+"""
+
+class SubfieldBase(type):
+    """
+    A metaclass for custom Field subclasses. This ensures the model's attribute
+    has the descriptor protocol attached to it.
+    """
+    def __new__(cls, name, bases, attrs):
+        new_class = super(SubfieldBase, cls).__new__(cls, name, bases, attrs)
+        new_class.contribute_to_class = make_contrib(
+            new_class, attrs.get('contribute_to_class')
+        )
+        return new_class
+
+class Creator(object):
+    """
+    A placeholder class that provides a way to set the attribute on the model.
+    """
+    def __init__(self, field):
+        self.field = field
+
+    def __get__(self, obj, type=None):
+        if obj is None:
+            raise AttributeError('Can only be accessed via an instance.')
+        return obj.__dict__[self.field.name]
+
+    def __set__(self, obj, value):
+        # Usually this would call to_python, but we've changed it to pre_init
+        # so that we can tell which state we're in. By passing an obj,
+        # we can definitively tell if a value has already been deserialized
+        # More: https://github.com/bradjasper/django-jsonfield/issues/33
+        obj.__dict__[self.field.name] = self.field.pre_init(value, obj)
+
+
+def make_contrib(superclass, func=None):
+    """
+    Returns a suitable contribute_to_class() method for the Field subclass.
+
+    If 'func' is passed in, it is the existing contribute_to_class() method on
+    the subclass and it is called before anything else. It is assumed in this
+    case that the existing contribute_to_class() calls all the necessary
+    superclass methods.
+    """
+    def contribute_to_class(self, cls, name):
+        if func:
+            func(self, cls, name)
+        else:
+            super(superclass, self).contribute_to_class(cls, name)
+        setattr(cls, self.name, Creator(self))
+
+    return contribute_to_class
diff --git a/project/cmsplugin_contact_plus/jsonfield/tests.py b/project/cmsplugin_contact_plus/jsonfield/tests.py
new file mode 100644 (file)
index 0000000..5faae20
--- /dev/null
@@ -0,0 +1,273 @@
+from decimal import Decimal
+from django.core.serializers import deserialize, serialize
+from django.core.serializers.base import DeserializationError
+from django.db import models
+from django.test import TestCase
+try:
+    import json
+except ImportError:
+    from django.utils import simplejson as json
+
+from .fields import JSONField, JSONCharField
+from django.forms.util import ValidationError
+
+from collections import OrderedDict
+
+class JsonModel(models.Model):
+    json = JSONField()
+    default_json = JSONField(default={"check":12})
+    complex_default_json = JSONField(default=[{"checkcheck": 1212}])
+    empty_default = JSONField(default={})
+
+class JsonCharModel(models.Model):
+    json = JSONCharField(max_length=100)
+    default_json = JSONCharField(max_length=100, default={"check":34})
+
+class ComplexEncoder(json.JSONEncoder):
+    def default(self, obj):
+        if isinstance(obj, complex):
+            return {
+                '__complex__': True,
+                'real': obj.real,
+                'imag': obj.imag,
+            }
+
+        return json.JSONEncoder.default(self, obj)
+
+def as_complex(dct):
+    if '__complex__' in dct:
+        return complex(dct['real'], dct['imag'])
+    return dct
+
+class JSONModelCustomEncoders(models.Model):
+    # A JSON field that can store complex numbers
+    json = JSONField(
+        dump_kwargs={'cls': ComplexEncoder, "indent": 4},
+        load_kwargs={'object_hook': as_complex},
+    )
+
+class JSONFieldTest(TestCase):
+    """JSONField Wrapper Tests"""
+
+    json_model = JsonModel
+
+    def test_json_field_create(self):
+        """Test saving a JSON object in our JSONField"""
+        json_obj = {
+            "item_1": "this is a json blah",
+            "blergh": "hey, hey, hey"}
+
+        obj = self.json_model.objects.create(json=json_obj)
+        new_obj = self.json_model.objects.get(id=obj.id)
+
+        self.assertEqual(new_obj.json, json_obj)
+
+    def test_string_in_json_field(self):
+        """Test saving an ordinary Python string in our JSONField"""
+        json_obj = 'blah blah'
+        obj = self.json_model.objects.create(json=json_obj)
+        new_obj = self.json_model.objects.get(id=obj.id)
+
+        self.assertEqual(new_obj.json, json_obj)
+
+    def test_float_in_json_field(self):
+        """Test saving a Python float in our JSONField"""
+        json_obj = 1.23
+        obj = self.json_model.objects.create(json=json_obj)
+        new_obj = self.json_model.objects.get(id=obj.id)
+
+        self.assertEqual(new_obj.json, json_obj)
+
+    def test_int_in_json_field(self):
+        """Test saving a Python integer in our JSONField"""
+        json_obj = 1234567
+        obj = self.json_model.objects.create(json=json_obj)
+        new_obj = self.json_model.objects.get(id=obj.id)
+
+        self.assertEqual(new_obj.json, json_obj)
+
+    def test_decimal_in_json_field(self):
+        """Test saving a Python Decimal in our JSONField"""
+        json_obj = Decimal(12.34)
+        obj = self.json_model.objects.create(json=json_obj)
+        new_obj = self.json_model.objects.get(id=obj.id)
+
+        # here we must know to convert the returned string back to Decimal,
+        # since json does not support that format
+        self.assertEqual(Decimal(new_obj.json), json_obj)
+
+    def test_json_field_modify(self):
+        """Test modifying a JSON object in our JSONField"""
+        json_obj_1 = {'a': 1, 'b': 2}
+        json_obj_2 = {'a': 3, 'b': 4}
+
+        obj = self.json_model.objects.create(json=json_obj_1)
+        self.assertEqual(obj.json, json_obj_1)
+        obj.json = json_obj_2
+
+        self.assertEqual(obj.json, json_obj_2)
+        obj.save()
+        self.assertEqual(obj.json, json_obj_2)
+
+        self.assertTrue(obj)
+
+    def test_json_field_load(self):
+        """Test loading a JSON object from the DB"""
+        json_obj_1 = {'a': 1, 'b': 2}
+        obj = self.json_model.objects.create(json=json_obj_1)
+        new_obj = self.json_model.objects.get(id=obj.id)
+
+        self.assertEqual(new_obj.json, json_obj_1)
+
+    def test_json_list(self):
+        """Test storing a JSON list"""
+        json_obj = ["my", "list", "of", 1, "objs", {"hello": "there"}]
+
+        obj = self.json_model.objects.create(json=json_obj)
+        new_obj = self.json_model.objects.get(id=obj.id)
+        self.assertEqual(new_obj.json, json_obj)
+
+    def test_empty_objects(self):
+        """Test storing empty objects"""
+        for json_obj in [{}, [], 0, '', False]:
+            obj = self.json_model.objects.create(json=json_obj)
+            new_obj = self.json_model.objects.get(id=obj.id)
+            self.assertEqual(json_obj, obj.json)
+            self.assertEqual(json_obj, new_obj.json)
+
+    def test_custom_encoder(self):
+        """Test encoder_cls and object_hook"""
+        value = 1 + 3j  # A complex number
+
+        obj = JSONModelCustomEncoders.objects.create(json=value)
+        new_obj = JSONModelCustomEncoders.objects.get(pk=obj.pk)
+        self.assertEqual(value, new_obj.json)
+
+    def test_django_serializers(self):
+        """Test serializing/deserializing jsonfield data"""
+        for json_obj in [{}, [], 0, '', False, {'key': 'value', 'num': 42,
+                                                'ary': list(range(5)),
+                                                'dict': {'k': 'v'}}]:
+            obj = self.json_model.objects.create(json=json_obj)
+            new_obj = self.json_model.objects.get(id=obj.id)
+            self.assert_(new_obj)
+
+        queryset = self.json_model.objects.all()
+        ser = serialize('json', queryset)
+        for dobj in deserialize('json', ser):
+            obj = dobj.object
+            pulled = self.json_model.objects.get(id=obj.pk)
+            self.assertEqual(obj.json, pulled.json)
+
+    def test_default_parameters(self):
+        """Test providing a default value to the model"""
+        model = JsonModel()
+        model.json = {"check": 12}
+        self.assertEqual(model.json, {"check": 12})
+        self.assertEqual(type(model.json), dict)
+
+        self.assertEqual(model.default_json, {"check": 12})
+        self.assertEqual(type(model.default_json), dict)
+
+    def test_invalid_json(self):
+        # invalid json data {] in the json and default_json fields
+        ser = '[{"pk": 1, "model": "jsonfield.jsoncharmodel", ' \
+            '"fields": {"json": "{]", "default_json": "{]"}}]'
+        with self.assertRaises(DeserializationError) as cm:
+            next(deserialize('json', ser))
+        inner = cm.exception.args[0]
+        self.assertTrue(isinstance(inner, ValidationError))
+        self.assertEqual('Enter valid JSON', inner.messages[0])
+
+    def test_integer_in_string_in_json_field(self):
+        """Test saving the Python string '123' in our JSONField"""
+        json_obj = '123'
+        obj = self.json_model.objects.create(json=json_obj)
+        new_obj = self.json_model.objects.get(id=obj.id)
+
+        self.assertEqual(new_obj.json, json_obj)
+
+    def test_boolean_in_string_in_json_field(self):
+        """Test saving the Python string 'true' in our JSONField"""
+        json_obj = 'true'
+        obj = self.json_model.objects.create(json=json_obj)
+        new_obj = self.json_model.objects.get(id=obj.id)
+
+        self.assertEqual(new_obj.json, json_obj)
+
+
+    def test_pass_by_reference_pollution(self):
+        """Make sure the default parameter is copied rather than passed by reference"""
+        model = JsonModel()
+        model.default_json["check"] = 144
+        model.complex_default_json[0]["checkcheck"] = 144
+        self.assertEqual(model.default_json["check"], 144)
+        self.assertEqual(model.complex_default_json[0]["checkcheck"], 144)
+
+        # Make sure when we create a new model, it resets to the default value
+        # and not to what we just set it to (it would be if it were passed by reference)
+        model = JsonModel()
+        self.assertEqual(model.default_json["check"], 12)
+        self.assertEqual(model.complex_default_json[0]["checkcheck"], 1212)
+
+    def test_normal_regex_filter(self):
+        """Make sure JSON model can filter regex"""
+
+        JsonModel.objects.create(json={"boom": "town"})
+        JsonModel.objects.create(json={"move": "town"})
+        JsonModel.objects.create(json={"save": "town"})
+
+        self.assertEqual(JsonModel.objects.count(), 3)
+
+        self.assertEqual(JsonModel.objects.filter(json__regex=r"boom").count(), 1)
+        self.assertEqual(JsonModel.objects.filter(json__regex=r"town").count(), 3)
+
+    def test_save_blank_object(self):
+        """Test that JSON model can save a blank object as none"""
+
+        model = JsonModel()
+        self.assertEqual(model.empty_default, {})
+
+        model.save()
+        self.assertEqual(model.empty_default, {})
+
+        model1 = JsonModel(empty_default={"hey": "now"})
+        self.assertEqual(model1.empty_default, {"hey": "now"})
+
+        model1.save()
+        self.assertEqual(model1.empty_default, {"hey": "now"})
+
+
+
+class JSONCharFieldTest(JSONFieldTest):
+    json_model = JsonCharModel
+
+
+class OrderedJsonModel(models.Model):
+    json = JSONField(load_kwargs={'object_pairs_hook': OrderedDict})
+
+
+class OrderedDictSerializationTest(TestCase):
+    ordered_dict = OrderedDict([
+        ('number', [1, 2, 3, 4]),
+        ('notes', True),
+    ])
+    expected_key_order = ['number', 'notes']
+
+    def test_ordered_dict_differs_from_normal_dict(self):
+        self.assertEqual(list(self.ordered_dict.keys()), self.expected_key_order)
+        self.assertNotEqual(dict(self.ordered_dict).keys(), self.expected_key_order)
+
+    def test_default_behaviour_loses_sort_order(self):
+        mod = JsonModel.objects.create(json=self.ordered_dict)
+        self.assertEqual(list(mod.json.keys()), self.expected_key_order)
+        mod_from_db = JsonModel.objects.get(id=mod.id)
+
+        # mod_from_db lost ordering information during json.loads()
+        self.assertNotEqual(mod_from_db.json.keys(), self.expected_key_order)
+
+    def test_load_kwargs_hook_does_not_lose_sort_order(self):
+        mod = OrderedJsonModel.objects.create(json=self.ordered_dict)
+        self.assertEqual(list(mod.json.keys()), self.expected_key_order)
+        mod_from_db = OrderedJsonModel.objects.get(id=mod.id)
+        self.assertEqual(list(mod_from_db.json.keys()), self.expected_key_order)
diff --git a/project/cmsplugin_contact_plus/local_settings.py b/project/cmsplugin_contact_plus/local_settings.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/project/cmsplugin_contact_plus/locale/es/LC_MESSAGES/django.mo b/project/cmsplugin_contact_plus/locale/es/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..21581f3
Binary files /dev/null and b/project/cmsplugin_contact_plus/locale/es/LC_MESSAGES/django.mo differ
diff --git a/project/cmsplugin_contact_plus/locale/es/LC_MESSAGES/django.po b/project/cmsplugin_contact_plus/locale/es/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..11d87fc
--- /dev/null
@@ -0,0 +1,126 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Luis Zárate <luisza14@gmail.com>, 2014.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-11-14 17:02+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: Luis Zárate <luisza14@gmail.com>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: cmsplugin_contact_plus/cms_plugins.py:14
+#: cmsplugin_contact_plus/models.py:83 cmsplugin_contact_plus/models.py:100
+msgid "Contact Form"
+msgstr "Formulario de contacto"
+
+#: cmsplugin_contact_plus/forms.py:58 cmsplugin_contact_plus/forms.py:60
+msgid "No referral available."
+msgstr "Remisión no disponible"
+
+#: cmsplugin_contact_plus/forms.py:71
+msgid "Key/value parameter not available."
+msgstr "Parametro para clave/valor no disponible"
+
+#: cmsplugin_contact_plus/models.py:31
+msgid "Contact form message from {}"
+msgstr "Mensaje de formulario para {}"
+
+#: cmsplugin_contact_plus/models.py:34
+msgid "Title"
+msgstr "Título"
+
+#: cmsplugin_contact_plus/models.py:34
+msgid "Title for the Contact Form."
+msgstr "Título para el formulario de contacto"
+
+#: cmsplugin_contact_plus/models.py:36
+msgid "Email subject"
+msgstr "Asunto del correo electrónico"
+
+#: cmsplugin_contact_plus/models.py:39
+msgid "Email of recipients"
+msgstr "Correo de los destinatarios "
+
+#: cmsplugin_contact_plus/models.py:41
+msgid "Collect Records"
+msgstr "Recolección de registros"
+
+#: cmsplugin_contact_plus/models.py:41
+msgid "If active, all records for this Form will be stored in the Database."
+msgstr "Si está activo, todos los registros de este formulario serán guardados en la base de datos"
+
+#: cmsplugin_contact_plus/models.py:43
+msgid "Message displayed after submitting the contact form."
+msgstr "Mensaje mostrado despúes de enviar el formulario"
+
+#: cmsplugin_contact_plus/models.py:45
+msgid "Text for the Submit button."
+msgstr "Texto para el botón de enviar"
+
+#: cmsplugin_contact_plus/models.py:65
+#, python-format
+msgid "Contact Plus Form for %s"
+msgstr "Contact Plus Form para %s"
+
+#: cmsplugin_contact_plus/models.py:75
+msgid "CharField as Textarea"
+msgstr "Campo de caracteres como un área de texto"
+
+#: cmsplugin_contact_plus/models.py:76
+msgid "CharField as HiddenInput"
+msgstr "Campo de caracteres oculto"
+
+#: cmsplugin_contact_plus/models.py:77
+msgid "Referral page as HiddenInput"
+msgstr "Página de remisión oculta"
+
+#: cmsplugin_contact_plus/models.py:78
+msgid "GET parameter as HiddenInput"
+msgstr "Parámetros GET ocultos"
+
+#: cmsplugin_contact_plus/models.py:84
+msgid "Label"
+msgstr "Etiqueta"
+
+#: cmsplugin_contact_plus/models.py:87
+msgid "Inital Value"
+msgstr "Valor inicial"
+
+#: cmsplugin_contact_plus/models.py:89
+msgid "Mandatory field"
+msgstr "Campo Obligatorio"
+
+#: cmsplugin_contact_plus/models.py:91
+msgid "Widget"
+msgstr ""
+
+#: cmsplugin_contact_plus/models.py:92
+msgid "Will be ignored in the current version."
+msgstr "Será ignorado en la versión actual"
+
+#: cmsplugin_contact_plus/models.py:102
+msgid "Date the Record was processed."
+msgstr "Fecha de registro fue procesada"
+
+#: cmsplugin_contact_plus/models.py:107
+msgid "Contact Record"
+msgstr "Registro de contacto"
+
+#: cmsplugin_contact_plus/models.py:108
+msgid "Contact Records"
+msgstr "Registros de contacto"
+
+#: cmsplugin_contact_plus/models.py:118
+#, python-format
+msgid "Record for %(contact)s recorded on %(date)s"
+msgstr "Registro para %(contact)s registrados el %(date)s"
diff --git a/project/cmsplugin_contact_plus/migrations/0001_initial.py b/project/cmsplugin_contact_plus/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..63168f8
--- /dev/null
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+import django.db.models.deletion
+import cmsplugin_contact_plus.models
+import jsonfield.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('cms', '0003_auto_20140926_2347'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ContactPlus',
+            fields=[
+                ('cmsplugin_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='cms.CMSPlugin')),
+                ('title', models.CharField(help_text='Title for the Contact Form.', max_length=100, null=True, verbose_name='Title', blank=True)),
+                ('email_subject', models.CharField(default=cmsplugin_contact_plus.models.get_current_site, max_length=256, verbose_name='Email subject')),
+                ('recipient_email', models.EmailField(default=b'', max_length=75, verbose_name='Email of recipients')),
+                ('collect_records', models.BooleanField(default=True, help_text='If active, all records for this Form will be stored in the Database.', verbose_name='Collect Records')),
+                ('thanks', models.TextField(verbose_name='Message displayed after submitting the contact form.')),
+                ('submit', models.CharField(max_length=30, verbose_name='Text for the Submit button.', blank=True)),
+                ('template', models.CharField(default=b'cmsplugin_contact_plus/contact.html', max_length=255, editable=False, choices=[(b'cmsplugin_contact_plus/contact.html', b'contact.html')])),
+            ],
+            options={
+                'verbose_name': 'Contact Plus Form',
+                'verbose_name_plural': 'Contact Plus Forms',
+            },
+            bases=('cms.cmsplugin',),
+        ),
+        migrations.CreateModel(
+            name='ContactRecord',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('date_of_entry', models.DateTimeField(auto_now_add=True)),
+                ('date_processed', models.DateTimeField(help_text=b'Date the Record was processed.', null=True, blank=True)),
+                ('data', jsonfield.fields.JSONField(default={}, null=True, blank=True)),
+                ('contact_form', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, verbose_name='Contact Form', to='cmsplugin_contact_plus.ContactPlus', null=True)),
+            ],
+            options={
+                'ordering': ['date_of_entry', 'contact_form'],
+                'verbose_name': 'Contact Record',
+                'verbose_name_plural': 'Contact Records',
+            },
+            bases=(models.Model,),
+        ),
+        migrations.CreateModel(
+            name='ExtraField',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('inline_ordering_position', models.IntegerField(null=True, blank=True)),
+                ('label', models.CharField(max_length=100, verbose_name='Label')),
+                ('fieldType', models.CharField(max_length=100, choices=[(b'CharField', b'CharField'), (b'BooleanField', b'BooleanField'), (b'EmailField', b'EmailField'), (b'DecimalField', b'DecimalField'), (b'FloatField', b'FloatField'), (b'IntegerField', b'IntegerField'), (b'IPAddressField', b'IPAddressField'), (b'auto_Textarea', b'CharField as Textarea'), (b'auto_hidden_input', b'CharField as HiddenInput'), (b'auto_referral_page', b'Referral page as HiddenInput'), (b'auto_GET_parameter', b'GET parameter as HiddenInput')])),
+                ('initial', models.CharField(max_length=250, null=True, verbose_name='Inital Value', blank=True)),
+                ('required', models.BooleanField(default=True, verbose_name='Mandatory field')),
+                ('widget', models.CharField(help_text='Will be ignored in the current version.', max_length=250, null=True, verbose_name='Widget', blank=True)),
+                ('form', models.ForeignKey(verbose_name='Contact Form', to='cmsplugin_contact_plus.ContactPlus')),
+            ],
+            options={
+                'ordering': ('inline_ordering_position',),
+                'abstract': False,
+            },
+            bases=(models.Model,),
+        ),
+    ]
diff --git a/project/cmsplugin_contact_plus/migrations/__init__.py b/project/cmsplugin_contact_plus/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/project/cmsplugin_contact_plus/models.py b/project/cmsplugin_contact_plus/models.py
new file mode 100755 (executable)
index 0000000..ee2df83
--- /dev/null
@@ -0,0 +1,134 @@
+import threading
+
+from django.conf import settings
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+from django.contrib.sites.models import Site
+from django.utils.encoding import python_2_unicode_compatible
+from django.db.models import Model
+
+from cms.models import CMSPlugin
+from inline_ordering.models import Orderable
+from jsonfield import JSONField
+
+try:
+    DEFAULT_FROM_EMAIL_ADDRESS = settings.ADMINS[0][1]
+except:
+    DEFAULT_FROM_EMAIL_ADDRESS = ''
+
+from . import utils
+
+
+localdata = threading.local()
+localdata.TEMPLATE_CHOICES = utils.autodiscover_templates()
+TEMPLATE_CHOICES = localdata.TEMPLATE_CHOICES
+
+def get_current_site():
+    try:
+        current_site = Site.objects.get(id=1).name
+    except:
+        current_site = 'example.com'
+    return _('Contact form message from %s') % (current_site)
+@python_2_unicode_compatible
+class ContactPlus(CMSPlugin):
+    title = models.CharField(_('Title'), 
+            null=True, 
+            blank=True, 
+            max_length=100, 
+            help_text=_("Title for the Contact Form."))
+    email_subject = models.CharField(
+            max_length=256, 
+            verbose_name=_("Email subject"),
+            default=get_current_site)
+    recipient_email = models.EmailField(_("Email of recipients"), 
+            default=DEFAULT_FROM_EMAIL_ADDRESS)
+    collect_records = models.BooleanField(_('Collect Records'), 
+            default=True, 
+            help_text=_("If active, all records for this Form will be stored in the Database."))
+    thanks = models.TextField(_('Message displayed after submitting the contact form.'))
+    submit = models.CharField(_('Text for the Submit button.'), 
+            blank=True, 
+            max_length=30)
+    template = models.CharField(
+            max_length=255,
+            choices=TEMPLATE_CHOICES,
+            default='cmsplugin_contact_plus/contact.html',
+            editable=len(TEMPLATE_CHOICES) > 1)
+            
+    class Meta:
+        verbose_name = "Contact Plus Form"
+        verbose_name_plural = "Contact Plus Forms"
+            
+    def copy_relations(self, oldinstance):
+        for extrafield in ExtraField.objects.filter(form__pk=oldinstance.pk):
+            extrafield.pk = None
+            extrafield.save()
+            self.extrafield_set.add(
+                extrafield)
+                
+    def __str__(self):
+        if self.title:
+            return self.title
+        return _("Contact Plus Form for %s") % self.recipient_email
+
+
+FIELD_TYPE = (('CharField', 'CharField'),
+              ('BooleanField', 'BooleanField'),
+              ('EmailField', 'EmailField'),
+              ('DecimalField', 'DecimalField'),
+              ('FloatField', 'FloatField'),
+              ('IntegerField', 'IntegerField'),
+              ('FileField', 'FileField'),
+              ('ImageField', 'ImageField'),
+              ('IPAddressField', 'IPAddressField'),
+              ('MathCaptcha','Math Captcha'),
+              ('auto_Textarea', _('CharField as Textarea')),
+              ('auto_hidden_input', _('CharField as HiddenInput')),
+              ('auto_referral_page', _('Referral page as HiddenInput')),
+              ('auto_GET_parameter', _('GET parameter as HiddenInput')))
+
+
+@python_2_unicode_compatible
+class ExtraField(Orderable):
+    """
+    """
+    form = models.ForeignKey(ContactPlus, verbose_name=_("Contact Form"))
+    label = models.CharField(_('Label'), max_length=100)
+    fieldType = models.CharField(max_length=100, choices=FIELD_TYPE)
+    initial = models.CharField(
+        _('Inital Value'), max_length=250, blank=True, null=True)
+    required = models.BooleanField(
+        _('Mandatory field'), default=True)
+    widget = models.CharField(
+        _('Widget'), max_length=250, blank=True, null=True,
+        help_text=_("Will be ignored in the current version."))
+
+    def __str__(self):
+        return self.label
+
+
+@python_2_unicode_compatible
+class ContactRecord(Model):
+    """
+    """
+    contact_form = models.ForeignKey(ContactPlus, verbose_name=_("Contact Form"), null=True, on_delete=models.SET_NULL)
+    date_of_entry = models.DateTimeField(auto_now_add=True)
+    date_processed = models.DateTimeField(null=True, blank=True, help_text=_("Date the Record was processed."))
+    data = JSONField(null=True, blank=True, default={})
+
+    class Meta():
+        ordering = ['date_of_entry', 'contact_form', ]
+        verbose_name = _("Contact Record")
+        verbose_name_plural = _("Contact Records")
+
+    @property
+    def is_processed(self):
+        if self.date_processed:
+            return True
+        else:
+            return False
+
+    def __str__(self):
+        return _("Record for %(contact)s recorded on %(date)s") % {'contact':self.contact_form, 
+                                                                   'date': self.date_of_entry.strftime('%d. %b %Y') }
diff --git a/project/cmsplugin_contact_plus/signals.py b/project/cmsplugin_contact_plus/signals.py
new file mode 100644 (file)
index 0000000..6bfd831
--- /dev/null
@@ -0,0 +1,3 @@
+from django.dispatch import Signal
+
+contact_message_sent = Signal()
diff --git a/project/cmsplugin_contact_plus/simplemathcaptcha/__init__.py b/project/cmsplugin_contact_plus/simplemathcaptcha/__init__.py
new file mode 100644 (file)
index 0000000..6a6cd9a
--- /dev/null
@@ -0,0 +1,16 @@
+VERSION = (1, 0, 5, "f", 0)  # following PEP 386\r
+DEV_N = None\r
+\r
+\r
+def get_version():  # pragma: nocover\r
+    version = "%s.%s" % (VERSION[0], VERSION[1])\r
+    if VERSION[2]:\r
+        version = "%s.%s" % (version, VERSION[2])\r
+    if VERSION[3] != "f":\r
+        version = "%s%s%s" % (version, VERSION[3], VERSION[4])\r
+        if DEV_N:\r
+            version = "%s.dev%s" % (version, DEV_N)\r
+    return version\r
+\r
+\r
+__version__ = get_version()\r
diff --git a/project/cmsplugin_contact_plus/simplemathcaptcha/fields.py b/project/cmsplugin_contact_plus/simplemathcaptcha/fields.py
new file mode 100644 (file)
index 0000000..4328b9c
--- /dev/null
@@ -0,0 +1,63 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+from django.core.exceptions import ValidationError
+
+from .widgets import MathCaptchaWidget
+from .utils import hash_answer
+
+
+class MathCaptchaField(forms.MultiValueField):
+    default_error_messages = {
+        'invalid': _('Please check your math and try again.'),
+        'invalid_number': _('Enter a whole number.'),
+    }
+
+    def __init__(self, *args, **kwargs):
+        self._ensure_widget(kwargs)
+        kwargs['required'] = True
+        # we skip MultiValueField handling of fields and setup ourselves
+        super(MathCaptchaField, self).__init__((), *args, **kwargs)
+        self._setup_fields()
+
+    def compress(self, data_list):
+        """Compress takes the place of clean with MultiValueFields"""
+        if data_list:
+            answer = data_list[0]
+            real_hashed_answer = data_list[1]
+            hashed_answer = hash_answer(answer)
+            if hashed_answer != real_hashed_answer:
+                raise ValidationError(self.error_messages['invalid'])
+        return None
+
+    def _ensure_widget(self, kwargs):
+        widget_params = self._extract_widget_params(kwargs)
+
+        if 'widget' not in kwargs or not kwargs['widget']:
+            kwargs['widget'] = MathCaptchaWidget(**widget_params)
+        elif widget_params:
+            msg = '%s must be omitted when widget is provided for %s.'
+            msg = msg % (' and '.join(list(widget_params)),
+                         self.__class__.__name__)
+            raise TypeError(msg)
+
+    def _extract_widget_params(self, kwargs):
+        params = {}
+        for key in ('start_int', 'end_int'):
+            if key in kwargs:
+                params[key] = kwargs.pop(key)
+        return params
+
+    def _setup_fields(self):
+        error_messages = {'invalid': self.error_messages['invalid_number']}
+        # set fields
+        fields = (
+            forms.IntegerField(error_messages=error_messages,
+                               localize=self.localize),
+            forms.CharField()
+        )
+        for field in fields:
+            field.required = False
+        self.fields = fields
diff --git a/project/cmsplugin_contact_plus/simplemathcaptcha/locale/es/LC_MESSAGES/django.mo b/project/cmsplugin_contact_plus/simplemathcaptcha/locale/es/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..901b006
Binary files /dev/null and b/project/cmsplugin_contact_plus/simplemathcaptcha/locale/es/LC_MESSAGES/django.mo differ
diff --git a/project/cmsplugin_contact_plus/simplemathcaptcha/locale/es/LC_MESSAGES/django.po b/project/cmsplugin_contact_plus/simplemathcaptcha/locale/es/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..ccb239b
--- /dev/null
@@ -0,0 +1,45 @@
+# 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 <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-10-14 15:09-0600\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: fields.py:14
+msgid "Please check your math and try again."
+msgstr "Por favor revise la ecuación y trate de nuevo"
+
+#: fields.py:15
+msgid "Enter a whole number."
+msgstr "Ingrese un número entero"
+
+#: fields.py:41
+#, python-format
+msgid "%(params)s must be omitted when widget is provided for %(classname)s."
+msgstr "%(params)s serán omitidos cuando se proporcione un widget para %(classname)s."
+
+#: widgets.py:17
+#, python-format
+msgid "What is %(num1)i %(operator)s %(num2)i?"
+msgstr "¿Cuánto es %(num1)i %(operator)s %(num2)i?"
+
+#: widgets.py:75
+msgid "MathCaptchaWidget requires positive integers for start_int and end_int."
+msgstr "MathCaptchaWidget requiere un número entero positivo para start_int and end_int."
+
+#: widgets.py:78
+msgid "MathCaptchaWidget requires end_int be greater than start_int."
+msgstr "MathCaptchaWidget end_int debe ser mayor que start_int."
diff --git a/project/cmsplugin_contact_plus/simplemathcaptcha/utils.py b/project/cmsplugin_contact_plus/simplemathcaptcha/utils.py
new file mode 100644 (file)
index 0000000..5122010
--- /dev/null
@@ -0,0 +1,45 @@
+from __future__ import absolute_import\r
+from __future__ import unicode_literals\r
+\r
+from random import randint, choice\r
+from hashlib import sha1\r
+\r
+from django.conf import settings\r
+from django.utils import six\r
+\r
+MULTIPLY = '*'\r
+ADD = '+'\r
+SUBTRACT = '-'\r
+CALCULATIONS = {\r
+    MULTIPLY: lambda a, b: a * b,\r
+    ADD: lambda a, b: a + b,\r
+    SUBTRACT: lambda a, b: a - b,\r
+}\r
+OPERATORS = tuple(CALCULATIONS)\r
+\r
+\r
+def hash_answer(value):\r
+    answer = six.text_type(value)\r
+    to_encode = (settings.SECRET_KEY + answer).encode('utf-8')\r
+    return sha1(to_encode).hexdigest()\r
+\r
+\r
+def get_operator():\r
+    return choice(OPERATORS)\r
+\r
+\r
+def get_numbers(start_int, end_int, operator):\r
+    x = randint(start_int, end_int)\r
+    y = randint(start_int, end_int)\r
+\r
+    #avoid negative results for subtraction\r
+    if y > x and operator == SUBTRACT:\r
+        x, y = y, x\r
+\r
+    return x, y\r
+\r
+\r
+def calculate(x, y, operator):\r
+    func = CALCULATIONS[operator]\r
+    total = func(x, y)\r
+    return total\r
diff --git a/project/cmsplugin_contact_plus/simplemathcaptcha/widgets.py b/project/cmsplugin_contact_plus/simplemathcaptcha/widgets.py
new file mode 100644 (file)
index 0000000..84a09cc
--- /dev/null
@@ -0,0 +1,80 @@
+from __future__ import absolute_import
+from __future__ import unicode_literals
+
+from django import forms
+from django.template.defaultfilters import mark_safe
+from django.utils.translation import ugettext_lazy as _
+
+from .utils import hash_answer, get_operator, get_numbers, calculate
+
+
+class MathCaptchaWidget(forms.MultiWidget):
+    def __init__(self, start_int=1, end_int=10, question_tmpl=None,
+                 question_class=None, attrs=None):
+        self.start_int, self.end_int = self.verify_numbers(start_int, end_int)
+        self.question_class = question_class or 'captcha-question'
+        self.question_tmpl = (
+            question_tmpl or _('What is %(num1)i %(operator)s %(num2)i? '))
+        self.question_html = None
+        widget_attrs = {'size': '5'}
+        widget_attrs.update(attrs or {})
+        widgets = (
+            # this is the answer input field
+            forms.TextInput(attrs=widget_attrs),
+
+            # this is the hashed answer field to compare to
+            forms.HiddenInput()
+        )
+        super(MathCaptchaWidget, self).__init__(widgets, attrs)
+
+    def decompress(self, value):
+        return [None, None]
+
+    def format_output(self, rendered_widgets):
+        output = super(MathCaptchaWidget, self).format_output(rendered_widgets)
+        output = '%s%s' % (self.question_html, output)
+        return output
+
+    def render(self, name, value, attrs=None):
+        # hash answer and set as the hidden value of form
+        hashed_answer = self.generate_captcha()
+        value = ['', hashed_answer]
+
+        return super(MathCaptchaWidget, self).render(name, value, attrs=attrs)
+
+    def generate_captcha(self):
+        # get operator for calculation
+        operator = get_operator()
+
+        # get integers for calculation
+        x, y = get_numbers(self.start_int, self.end_int, operator)
+
+        # set question to display in output
+        self.set_question(x, y, operator)
+
+        # preform the calculation
+        total = calculate(x, y, operator)
+
+        return hash_answer(total)
+
+    def set_question(self, x, y, operator):
+        # make multiplication operator more human-readable
+        operator_for_label = '&times;' if operator == '*' else operator
+        question = self.question_tmpl % {
+            'num1': x,
+            'operator': operator_for_label,
+            'num2': y
+        }
+
+        html = '<span class="%s">%s</span>' % (self.question_class, question)
+        self.question_html = mark_safe(html)
+
+    def verify_numbers(self, start_int, end_int):
+        start_int, end_int = int(start_int), int(end_int)
+        if start_int < 0 or end_int < 0:
+            raise Warning('MathCaptchaWidget requires positive integers '
+                          'for start_int and end_int.')
+        elif end_int < start_int:
+            raise Warning('MathCaptchaWidget requires end_int be greater '
+                          'than start_int.')
+        return start_int, end_int
diff --git a/project/cmsplugin_contact_plus/south_migrations/__init__.py b/project/cmsplugin_contact_plus/south_migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/project/cmsplugin_contact_plus/templates/cmsplugin_contact_plus/contact.html b/project/cmsplugin_contact_plus/templates/cmsplugin_contact_plus/contact.html
new file mode 100755 (executable)
index 0000000..43ddcbd
--- /dev/null
@@ -0,0 +1,16 @@
+{% if form %}
+    {% if form.is_multipart %}
+        <form enctype="multipart/form-data" method="POST" action="">
+    {% else %}
+       <form method="POST" action="">
+    {% endif %}
+       {% csrf_token %}
+               {{ form.as_p }}
+               <input type="submit"{% if contact.submit %} value="{{ contact.submit }}"{% endif %} />
+       </form>
+{% else %}
+       {{ contact.thanks|safe }}
+{% endif %}
+
+
+    
\ No newline at end of file
diff --git a/project/cmsplugin_contact_plus/templates/cmsplugin_contact_plus/email.txt b/project/cmsplugin_contact_plus/templates/cmsplugin_contact_plus/email.txt
new file mode 100755 (executable)
index 0000000..ce48522
--- /dev/null
@@ -0,0 +1,5 @@
+New message from the Web
+
+{% for dic in ordered_data %}{% for key, value in dic.items %}
+    {{ key }}: {{ value }}
+{% endfor %}{% endfor %}
diff --git a/project/cmsplugin_contact_plus/utils.py b/project/cmsplugin_contact_plus/utils.py
new file mode 100644 (file)
index 0000000..a5d8d44
--- /dev/null
@@ -0,0 +1,69 @@
+import glob
+import os
+import threading
+
+from django.conf import settings
+
+localdata = threading.local()
+localdata.TEMPLATES = tuple()
+TEMPLATES = localdata.TEMPLATES
+
+
+def autodiscover_templates():
+    '''
+    Autodiscovers cmsplugin_contact_plus templates the way
+    'django.template.loaders.filesystem.Loader' and
+    'django.template.loaders.app_directories.Loader' work.
+    '''
+    def sorted_templates(templates):
+        '''
+        Sorts templates
+        '''
+        TEMPLATES = sorted(templates, key=lambda template: template[1])
+        return TEMPLATES
+
+    # obviously, cache for better performance
+    global TEMPLATES
+    if TEMPLATES:
+        return TEMPLATES
+
+    # override templates from settings
+    override_dir = getattr(settings, 'CMSPLUGIN_CONTACT_PLUS_TEMPLATES', None)
+    if override_dir:
+        return sorted_templates(override_dir)
+
+    templates = []
+#    templates = [
+#        ('cmsplugin_contact_plus/hello.html', 'hello.html'),
+#    ]
+
+    dirs_to_scan = []
+    if 'django.template.loaders.app_directories.Loader' in settings.TEMPLATE_LOADERS:
+        for app in settings.INSTALLED_APPS:
+            _ = __import__(app)
+            dir = os.path.dirname(_.__file__)
+            if not dir in dirs_to_scan:
+                # append 'templates' for app directories
+                dirs_to_scan.append(os.path.join(dir, 'templates'))
+
+    if 'django.template.loaders.filesystem.Loader' in settings.TEMPLATE_LOADERS:
+        for dir in settings.TEMPLATE_DIRS:
+            if not dir in dirs_to_scan:
+                # filesystem loader assumes our templates in 'templates'
+                # already
+                dirs_to_scan.append(dir)
+
+    for dir in dirs_to_scan:
+        found = glob.glob(os.path.join(dir, 'cmsplugin_contact_plus/*.html'))
+        for file in found:
+            dir, file = os.path.split(file)
+            key, value = os.path.join(dir.split('/')[-1], file), file
+            f = False
+            for _, template in templates:
+                if template == file:
+                    f = True
+            if not f:
+                templates.append((key, value,))
+            # print os.path.basename(file)
+
+    return sorted_templates(templates)
index a66b96d..61b89f9 100755 (executable)
@@ -179,7 +179,7 @@ class Offre(models.Model):
     texte = models.TextField()
     image = models.ImageField(null=True, blank=True, upload_to='offres')
     date_pub = models.DateField('date de publication')
-    date_limite = models.DateField('date limite')
+    date_limite = models.DateField('date limite', blank=True, upload_to='offres')
     date_event = models.DateField('date de l\'événement')
     date_mod = models.DateTimeField(
         'date de derniere modification',
index 2ce82a1..478c798 100644 (file)
@@ -2,7 +2,7 @@
 {% load i18n cms_tags%}
 {% block Contenu %}
                         
-<p>{% show_placeholder "Texter" "accueil"%}</p>
+<p>{# show_placeholder "Texter" "accueil"#}</p>
 
                        <!-- Bloc appel a communication -->
                        <div class="titre_bloc"><p>Derniers appels à communications</p></div>
@@ -10,7 +10,7 @@
                                {% if comm_list %}
                                        {% for item in comm_list %}
                                        <h4><a href="{{item.get_absolute_url}}">{{item.titre}}</a></h4>
-                                       <p>{{item.lieu}}, le {{item.date_event|date:"d F Y"}}</p>
+                                       <p>{{item.lieu}} {{item.date_event}}</p>
                                        <p><span>Date Limite:</span> {{item.date_limite|date:"d F Y"}}</p>
                                        <hr />
                                        {% endfor %}
@@ -26,7 +26,7 @@
                                {% if contri_list %}
                                        {% for item in contri_list %}
                                        <h4><a href="{{item.get_absolute_url}}">{{item.titre}}</a></h4>
-                                       <p>{{item.lieu}}, {{item.date_event|date:"d F Y"}}</p>
+                                       <p>{{item.lieu}} {{item.date_event}}</p>
                                        <p><span>Date Limite:</span> {{item.date_limite|date:"d F Y"}}</p>
                                        <hr />
                                        {% endfor %}
@@ -42,7 +42,7 @@
                                {% if offre_list %}
                                        {% for item in offre_list %}
                                        <h4><a href="{{item.get_absolute_url}}">{{item.titre}}</a></h4>
-                                       <p>{{item.lieu}}, {{item.date_event|date:"d F Y"}}</p>
+                                       <p>{{item.lieu}} {{item.date_event}}</p>
                                        <p><span>Date Limite:</span> {{item.date_limite|date:"d F Y"}}</p>
                                        <hr />
                                        {% endfor %}
index 72d0e27..99a2bc6 100644 (file)
@@ -5,16 +5,17 @@
 <h6>Archives Framonde</h6>
 <p>{% show_placeholder "Texter" "archives"%}</p>
 
-                       <!-- Bloc appel a communication -->
-                               {% if archives_list %}
-                                       <table>
-                                       <tr><td><strong>Numéro</strong></td><td><strong>Date</strong></td><td><strong>Titre</strong></td><td><strong>Télécharger</strong></td></tr>
-                                       {% for item in archives_list %}
-                                       <tr><td><a href="{{item.get_absolute_url}}">{{item.numero}}</a></td><td>{{item.date_pub|date:"d F Y"}}</td><td>{{item.titre}}</td><td style="text-align: center"><a href="{{ MEDIA_URL }}{{ item.fichier }}" style="text-decoration: none"><li class="icon-file-alt icon-2x" style="margin-right: 10px;"></li></a></tr>
-                                       {% endfor %}
-                                       </table>
-                               {% else%}
-                               <p>Aucune archives</p>
-                               {% endif %}
+<!-- Bloc appel a communication -->
+{% if archives_list %}
+       <p>Pour les archives antérieures à l'année 2015, merci de consulter cette adresse : <a href="https://listes.auf.org/pipermail/liste-framonde/">https://listes.auf.org/pipermail/liste-framonde/</a></p>
+       <table>
+       <tr><td><strong>Titre</strong></td><td><strong>Télécharger</strong></td></tr>
+       {% for item in archives_list %}
+       <tr><td>{{item.titre}}</td><td style="text-align: center"><a href="{{ MEDIA_URL }}{{ item.fichier }}" style="text-decoration: none"><li class="icon-file-alt icon-2x" style="margin-right: 10px;"></li></a></tr>
+       {% endfor %}
+       </table>
+{% else%}
+<p>Aucune archives</p>
+{% endif %}
         
 {% endblock %}
index a7732bc..429f18b 100644 (file)
@@ -1,4 +1,4 @@
-{% load cms_tags%}
+{% load cms_tags sekizai_tags menu_tags %}
 <!DOCTYPE html>
 <html>
 <head>
@@ -8,6 +8,7 @@
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <meta name="description" content="" />
        <!-- CSS -->
+        {% render_block "css" %}
        <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/kickstart.css" media="all" />
        <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}style.css" media="all" /> 
        <!-- Javascript -->
@@ -15,6 +16,8 @@
        <script type="text/javascript" src="{{ STATIC_URL }}js/kickstart.js"></script>
 </head>
 
+<body>
+    {% cms_toolbar %}
 <!-- CONTENEUR GLOBAL -->
 <div class="grid" style="max-width: 1000px;">
        
@@ -30,7 +33,7 @@
                <div class="col_12" style="margin-top: 0px;position: relative;">
                        <ul class="menu">
                                <li class="current bleu"><a href="/"><i class="icon-home"></i></a></li>
-                               <li class="jaune"><a href="{% page_url 'propos' %}">A propos</a></li>
+                               <li class="jaune"><a href="{% page_url 'propos' %}">À propos</a></li>
                                <li class="vert"><a href="/appels-communications/">Appels à communications</a></li>
                                <li class="violet"><a href="/appels-contributions/">Appels à contributions</a></li>
                                <li class="rouge"><a href="/appels-offres/">Appels d'offres</a></li>
 
                <!-- COLONNE DE DROITE-->
                <div class="col_4 droite">
+                       <div class="titre_bloc" style="margin-top:12px;"><p>Dernier numéro</p></div>
+                       <div class="bloc">
+                                 <a href="{{ MEDIA_URL }}{{ archive_side.0.fichier }}" style="text-decoration: none">
+                                       <img style="margin-left: 20%;" src="{{ STATIC_URL }}img/derniernumero.png"/></br>
+                                       {{ archive_side.0.titre }} ({{ archive_side.0.fichier.size|filesizeformat }})
+                                  </a>
+                       </div>
 
                        <!-- Bloc 1 droite -->
                        <div class="titre_bloc" style="margin-top:12px;"><p>S'abonner</p></div>
        
 </div>
 
+{% render_block "js" %}
 </body>
 </html>
index a7673b7..da94a07 100644 (file)
@@ -5,10 +5,10 @@
 <h6>{{item.titre}}</h6>
 
 <div class="contenu_texte">
-<p>{% if item.sous_titre %}<span>Sous titre:</span> {{item.sous_titre}}<br>{% endif %}
+{% if item.sous_titre %}{{item.sous_titre}}<br>{% endif %}
 {% if item.lieu %}<span>Lieu:</span> {{item.lieu}}<br>{% endif %}
-{% if item.date_limite %}<span>Date limite:</span> {{item.date_limite|date:"d F Y"}}<br>{% endif %}
-{% if item.date_event %}<span>Date de l'événement:</span> {{item.date_event|date:"d F Y"}}{% endif %}</p>
+{% if item.date_event %}<span>Date de l'événement:</span> {{item.date_event}}{% endif %}</p>
+{% if item.date_limite %}<h4><span>Date limite:</span> {{item.date_limite|date:"d F Y"}}</h4><br>{% endif %}
 
 {% if item.image %}<img src="{{ MEDIA_URL }}{{ item.image }}"/>{% endif %}        
 <p>{{ item.texte|safe}}</p>  
index 08a52c0..ba4543f 100644 (file)
@@ -11,8 +11,9 @@
                                {% if offre_list %}
                                        {% for item in offre_list %}
                                        <h4><a href="{{item.get_absolute_url}}">{{item.titre}}</a></h4>
-                                       <p>{{item.lieu}}, le {{item.date_event|date:"d F Y"}}</p>
-                                       <p><span>Date Limite:</span> {{item.date_limite|date:"d F Y"}}</p>
+                                       {% if item.sous_titre %}<p>{{item.sous_titre}}</p>{% endif %}
+                                       <p>{{item.lieu}} {{item.date_event}}</p>
+                                       <p><h4><i><span>Date Limite:</span> {{item.date_limite|date:"d F Y"}}</i></h4></p>
                                        <hr />
                                        {% endfor %}
                                        <p class="center">{% paginate %}</p>
index 6fd1b46..8513bd0 100644 (file)
@@ -11,8 +11,9 @@
                                {% if comm_list %}
                                        {% for item in comm_list %}
                                        <h4><a href="{{item.get_absolute_url}}">{{item.titre}}</a></h4>
-                                       <p>{{item.lieu}}, le {{item.date_event|date:"d F Y"}}</p>
-                                       <p><span>Date Limite:</span> {{item.date_limite|date:"d F Y"}}</p>
+                                       {% if item.sous_titre %}<p>{{item.sous_titre}}</p>{% endif %}
+                                       <p>{{item.lieu}} {{item.date_event}}</p>
+                                       <p><h4><i><span>Date Limite:</span> {{item.date_limite|date:"d F Y"}}</i></h4></p>
                                        <hr />
                                        {% endfor %}
                                        <p class="center">{% paginate %}</p>
index c11f43a..92122c1 100644 (file)
@@ -11,8 +11,9 @@
                                {% if contri_list %}
                                        {% for item in contri_list %}
                                        <h4><a href="{{item.get_absolute_url}}">{{item.titre}}</a></h4>
-                                       <p>{{item.lieu}}, le {{item.date_event|date:"d F Y"}}</p>
-                                       <p><span>Date Limite:</span> {{item.date_limite|date:"d F Y"}}</p>
+                                       {% if item.sous_titre %}<p>{{item.sous_titre}}</p>{% endif %}
+                                       <p>{{item.lieu}} {{item.date_event }}</p>
+                                       <p><h4><i><span>Date Limite:</span> {{item.date_limite|date:"d F Y"}}</i></h4></p>
                                        <hr />
                                        {% endfor %}
                                        <p class="center">{% paginate %}</p>
diff --git a/project/templates/search.html b/project/templates/search.html
deleted file mode 100644 (file)
index d9165c4..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-{% extends "base.html" %}
-{% load cms_tags%}
-
-{% block Contenu %}
-        
-               <h6>Recherche</h6>
-
-                    <form action="/recherche/" method="get">
-                    
-                    <p>La recherche "{{ query_string }}" a donnée les résultats suivants:</p>
-                    
-                    {% if found_entries %}
-                       <div class="bloc" style="border-top: 1px solid #d9d9d9;">
-                       {% for item in found_entries%}
-                                               {% if item.image %}
-                                                    <div class="photo">
-                                                        <a href="{% if item.path %}../{{ item.path }}{% else %}{{ item.get_absolute_url }}{% endif %}">
-                                                              
-                                                              <img src="{{ MEDIA_URL }}{{ item.image }}" width="80" height="65" alt="{{ item.titre }}" title="{{ item.titre }}"/>
-                                                        </a>
-                                                    </div>
-                                       {% endif %}
-                                       <h4><a href="{{item.get_absolute_url}}">{{item.titre}}</a></h4>
-                                       <p>{{item.lieu}}, le {{item.date_event|date:"d F Y"}}</p>
-                                       <p><span>Date Limite:</span> {{item.date_limite|date:"d F Y"}}</p>
-                                       <hr />
-                        {% endfor %}
-                       </div> 
-                    {% else%}
-                              <p style="margin-top: 20px;">Pas de résultat</p>                
-                    {% endif %}
-                    
-                                       </form>
-        
-{% endblock %}
diff --git a/project/templates/search/indexes/framonde/communication_text.txt b/project/templates/search/indexes/framonde/communication_text.txt
new file mode 100644 (file)
index 0000000..8bca816
--- /dev/null
@@ -0,0 +1 @@
+{{ object.texte|safe }}
diff --git a/project/templates/search/indexes/framonde/contribution_text.txt b/project/templates/search/indexes/framonde/contribution_text.txt
new file mode 100644 (file)
index 0000000..2c2eb92
--- /dev/null
@@ -0,0 +1 @@
+{{ object.texte|safe }}
diff --git a/project/templates/search/indexes/framonde/offre_text.txt b/project/templates/search/indexes/framonde/offre_text.txt
new file mode 100644 (file)
index 0000000..8bca816
--- /dev/null
@@ -0,0 +1 @@
+{{ object.texte|safe }}
diff --git a/project/templates/search/search.html b/project/templates/search/search.html
new file mode 100644 (file)
index 0000000..c2ab79d
--- /dev/null
@@ -0,0 +1,66 @@
+{% extends "base.html" %}
+{% load cms_tags auf %}
+
+{% block Contenu %}
+        
+               <h6>Recherche</h6>
+
+    <form method="get" action=".">
+        <h3>Filtres:</h3>
+            <p>Régions:</p>
+           {% show_facet "region" %}
+            <p>Sections:</p>
+           {% show_facet "section" %}
+            <p>Année de l'événement</p>
+           {% show_facet "annee_limite" %}
+        <table>
+            {{ form.as_table }}
+
+            <tr>
+                <td>&nbsp;</td>
+                <td>
+                    <input type="submit" value="Rechercher">
+                </td>
+            </tr>
+        </table>
+                    
+{% if query %}
+            <h3>Résultats</h3>
+
+               <div class="bloc" style="border-top: 1px solid #d9d9d9;">
+            {% for result in page.object_list %}
+                <p>
+                {% if result.object.image %}
+                            <div class="photo">
+                                <a href="{{ result.object.get_absolute_url }}">
+                                      
+                                      <img src="{{ MEDIA_URL }}{{ result.object.image }}" width="80" height="65" alt="{{ result.object.titre }}" title="{{ result.object.titre }}"/>
+                                </a>
+                            </div>
+               {% endif %}
+               <h4><a href="{{result.object.get_absolute_url}}">{{result.object.titre}}</a></h4>
+                {% if result.object.sous_titre %}<p>{{result.object.sous_titre}}</p>{% endif %}
+               <p>{{result.object.lieu}} {{result.object.date_event}}</p>
+               <p><span>Date Limite:</span> {{result.object.date_limite|date:"d F Y"}}</p>
+               <hr />
+                </p>
+            {% empty %}
+                <p>Pas de résultat</p>
+            {% endfor %}
+               </div> 
+
+            {% if page.has_previous or page.has_next %}
+                <div>
+                    {% if page.has_previous %}<a href="?q={{ query }}&amp;page={{ page.previous_page_number }}">{% endif %}&laquo; Précédent{% if page.has_previous %}</a>{% endif %}
+                    |
+                    {% if page.has_next %}<a href="?q={{ query }}&amp;page={{ page.next_page_number }}">{% endif %}Suivant &raquo;{% if page.has_next %}</a>{% endif %}
+                </div>
+            {% endif %}
+        {% else %}
+            {# Show some example queries to run, maybe query syntax, something else? #}
+        {% endif %}
+    </form>
+
+
+        
+{% endblock %}
index 3084904..af0cc8d 100644 (file)
@@ -2,12 +2,20 @@ from cms.sitemaps import CMSSitemap
 #from filebrowser.sites import site
 
 
-urlpatterns = patterns('',
+urlpatterns += patterns('',
 #    (r'^admin/filebrowser/', include(site.urls)),
+    (r'^tinymce/', include('tinymce.urls')),
     (r'^', include('project.framonde.urls')),  
 )
 
 # Search
-urlpatterns += patterns('views',
-    (r'^recherche/$', 'search'),
+from haystack.views import FacetedSearchView
+from haystack.query import SearchQuerySet
+from haystack.forms import FacetedSearchForm
+urlpatterns += patterns('',
+#    (r'^recherche/', include('haystack.urls')),
+    (r'^recherche/', FacetedSearchView(
+       searchqueryset=SearchQuerySet()\
+           .facet('region').facet('section').facet('annee_limite'),
+       form_class=FacetedSearchForm)),
 )
index 3909922..b8e52c8 100755 (executable)
@@ -1,5 +1,5 @@
 import re
-from framonde.models import *
+from project.framonde.models import *
 from django.shortcuts import render_to_response
 from django.template import Context, RequestContext
 from django.db.models import Q