.. _forms:
.. module:: contact_form.forms

Contact form classes
====================

There are two contact-form classes included in django-contact-form;
one provides all the infrastructure for a contact form, and will
usually be the base class for subclasses which want to extend or
modify functionality. The other is a subclass which adds spam
filtering to the contact form.


The ContactForm class
---------------------

.. class:: ContactForm

    The base contact form class from which all contact form classes
    should inherit.

    If you don't need any customization, you can use this form to
    provide basic contact-form functionality; it will collect name,
    email address and message.

    The :class:`~contact_form.views.ContactFormView` included in this
    application knows how to work with this form and can handle many
    types of subclasses as well (see below for a discussion of the
    important points), so in many cases it will be all that you
    need. If you'd like to use this form or a subclass of it from one
    of your own views, here's how:

    1. When you instantiate the form, pass the current
       :class:`~django.http.HttpRequest` object as the keyword
       argument `request`; this is used internally by the base
       implementation, and also made available so that subclasses can
       add functionality which relies on inspecting the request (such
       as spam filtering).

    2. To send the message, call the form's :meth:`save` method, which
       accepts the keyword argument `fail_silently` and defaults it to
       `False`. This argument is passed directly to Django's
       :func:`~django.core.mail.send_mail` function, and allows you to
       suppress or raise exceptions as needed for debugging. The
       :meth:`save` method has no return value.

    Other than that, treat it like any other form; validity checks and
    validated data are handled normally, through the
    :meth:`~django.forms.Form.is_valid` method and the
    :attr:`~django.forms.Form.cleaned_data` dictionary.

    Under the hood, this form uses a somewhat abstracted interface in
    order to make it easier to subclass and add functionality.

    The following attributes play a role in determining behavior, and
    any of them can be implemented as an attribute or as a method (for
    example, if you wish to have :attr:`from_email` be dynamic, you
    can implement a method named :meth:`from_email` instead of setting
    the attribute :attr:`from_email`).

    .. attribute:: from_email

       The email address (:class:`str`) to use in the `From:` header
       of the message. By default, this is the value of the Django
       setting :data:`~django.conf.settings.DEFAULT_FROM_EMAIL`.

    .. attribute:: recipient_list

       A :class:`list` of recipients for the message. By default, this
       is the email addresses specified in the setting
       :data:`~django.conf.settings.MANAGERS`.

    .. attribute:: subject_template_name

       A :class:`str`, the name of the template to use when rendering
       the subject line of the message. By default, this is
       `contact_form/contact_form_subject.txt`.

    .. attribute:: template_name

       A :class:`str`, the name of the template to use when rendering
       the body of the message. By default, this is
       `contact_form/contact_form.txt`.

    And two methods are involved in producing the contents of the
    message to send:

    .. method:: message

       Returns the body of the message to send. By default, this is
       accomplished by rendering the template name specified in
       :attr:`template_name`.

       :rtype: str

    .. method:: subject

       Returns the subject line of the message to send. By default,
       this is accomplished by rendering the template name specified
       in :attr:`subject_template_name`.

       :rtype: str

    .. warning:: **Subject must be a single line**

       The subject of an email is sent in a header (named
       `Subject:`). Because email uses newlines as a separator between
       headers, newlines in the subject can cause it to be interpreted
       as multiple headers; this is the `header injection attack
       <https://en.wikipedia.org/wiki/Email_injection>`_. To prevent
       this, :meth:`subject` will always force the subject to a single
       line of text, stripping all newline characters. If you override
       :meth:`subject`, be sure to either do this manually, or use
       :func:`super` to call the parent implementation.

    Finally, the message itself is generated by the following two
    methods:

    .. method:: get_message_dict

       This method loops through :attr:`from_email`,
       :attr:`recipient_list`, :meth:`message` and :meth:`subject`,
       collecting those parts into a dictionary with keys
       corresponding to the arguments to Django's `send_mail`
       function, then returns the dictionary. Overriding this allows
       essentially unlimited customization of how the message is
       generated. Note that for compatibility, implementations which
       override this should support callables for the values of
       :attr:`from_email` and :attr:`recipient_list`.

       :rtype: dict

    .. method:: get_context

       For methods which render portions of the message using
       templates (by default, :meth:`message` and :meth:`subject`),
       generates the context used by those templates. The default
       context will be a :class:`~django.template.RequestContext`
       (using the current HTTP request, so user information is
       available), plus the contents of the form's
       :attr:`~django.forms.Form.cleaned_data` dictionary, and one
       additional variable:

       `site`
         If `django.contrib.sites` is installed, the currently-active
         :class:`~django.contrib.sites.models.Site` object. Otherwise,
         a :class:`~django.contrib.sites.requests.RequestSite` object
         generated from the request.

       :rtype: dict

    Meanwhile, the following attributes/methods generally should not
    be overridden; doing so may interfere with functionality, may not
    accomplish what you want, and generally any desired customization
    can be accomplished in a more straightforward way through
    overriding one of the attributes/methods listed above.

    .. attribute:: request

       The :class:`~django.http.HttpRequest` object representing the
       current request. This is set automatically in `__init__()`, and
       is used both to generate a
       :class:`~django.template.RequestContext` for the templates and
       to allow subclasses to engage in request-specific behavior.

    .. method:: save

       If the form has data and is valid, will send the email, by
       calling :meth:`get_message_dict` and passing the result to
       Django's :func:`~django.core.mail.send_mail` function.

    Note that subclasses which override `__init__` or :meth:`save`
    need to accept `*args` and `**kwargs`, and pass them via
    :func:`super`, in order to preserve behavior (each of those
    methods accepts at least one additional argument, and this
    application expects and requires them to do so).


The Akismet (spam-filtering) contact form class
-----------------------------------------------

.. class:: AkismetContactForm

   A subclass of :class:`ContactForm` which adds spam filtering, via
   `the Wordpress Akismet spam-detection service
   <https://akismet.com/>`_.

   Use of this class requires you to provide configuration for the
   Akismet web service; you'll need to obtain an Akismet API key, and
   you'll need to associate it with the site you'll use the contact
   form on. You can do this at <https://akismet.com/>. Once you have,
   you can configure in either of two ways:

   1. Put your Akismet API key in the Django setting
      :data:`~django.conf.settings.AKISMET_API_KEY`, and the URL it's
      associated with in the setting
      :class:`~django.conf.settings.AKISMET_BLOG_URL`, or

   2. Put your Akismet API key in the environment variable
      `PYTHON_AKISMET_API_KEY`, and the URL it's associated with in
      the environment variable `PYTHON_AKISMET_BLOG_URL`.

   You will also need `the Python Akismet module
   <http://akismet.readthedocs.io/>`_ to communicate with the Akismet
   web service. You can install it by running `pip install akismet`,
   or django-contact-form can install it automatically for you if you
   run `pip install django-contact-form[akismet]`.

   Once you have an Akismet API key and URL configured, and the
   `akismet` module installed, you can drop in
   :class:`AkismetContactForm` anywhere you would have used
   :class:`ContactForm`. A URLconf is provided in django-contact-form,
   at `contact_form.akismet_urls`, which will correctly configure
   :class:`AkismetContactForm` for you.
