Features ======== Direct write to --------------- In the pages of your site, you can put links containing the recipient name(s). Example:: write to {{ username }} Separate multiple usernames with a ``:`` character. Example:: write to admins Prefilled fields ---------------- You may prefill the contents of some fields by providing a query string in the link. Example:: ask for details Recipients Min/Max ------------------ If you need to constraint the maximum number of recipients in the forms, you can pass the optional ``max`` parameter to the view. There is no parameter for a minimum number, but you can code a custom form and pass a ``min`` parameter to the recipient field (see Advanced Usage below for details). Views supporting the parameter are: ``WriteView``, ``ReplyView``. But this parameter does not apply to the default ``AnonymousWriteForm`` for visitors: The maximum is enforced to 1 (see Advanced Usage below for knowing how), in order to keep the features available to anonymous users to a strict minimum. Example:: urlpatterns = patterns('postman.views', # ... url(r'^write/(?:(?P[^/#]+)/)?$', WriteView.as_view(max=3), name='write'), # ... ) Advanced usage ~~~~~~~~~~~~~~ If you define your own custom form, you may specify a ``min`` parameter and a ``max`` parameter to the recipients field. For example:: from postman.forms import WriteForm class MyWriteForm(WriteForm): recipients = CommaSeparatedUserField(label="Recipients", min=2, max=5) If you do not want the fixed ``max`` parameter of the recipients field in your custom form, to be superseded by the parameter passed to the view, set the ``can_overwrite_limits`` form attribute to ``False``. For example:: class MyThreeAnonymousWriteForm(MyBaseAnonymousWriteForm): can_overwrite_limits = False recipients = CommaSeparatedUserField(label="Recipients", max=3) See also: * the ``POSTMAN_DISALLOW_MULTIRECIPIENTS`` setting in :ref:`optional_settings` User filter ----------- If there are some situations where a user should not be a recipient, you can write a filter and pass it to the view. Views supporting a user filter are: ``WriteView``, ``ReplyView``. Example:: def my_user_filter(user): if user.get_profile().is_absent: return "is away" return None urlpatterns = patterns('postman.views', # ... url(r'^write/(?:(?P[^/#]+)/)?$', WriteView.as_view(user_filter=my_user_filter), name='write'), # ... ) The filter will be called for each recipient, for validation. *Input*: * ``user``: a User instance, as the recipient of the message *Output*: If the recipient is allowed, just return ``None``. To forbid the message, use one of these means: * return ``False`` or ``''``, if you do not want to give a reason for the refusal. The error message will be: "Some usernames are rejected: foo, bar." * return a string, as a reason for the refusal. The error message will be: "Some usernames are rejected: foo (reason), bar (reason)." * raise a ``ValidationError`` with an error message to your liking. Advanced usage ~~~~~~~~~~~~~~ If you define your own custom form, you may specify a user filter inside. For example:: def my_user_filter(user): # ... return None from postman.forms import WriteForm class MyWriteForm(WriteForm): recipients = CommaSeparatedUserField(label="Recipients", user_filter=my_user_filter) Exchange filter --------------- If there are some situations where an exchange should not take place, you can write a filter and pass it to the view. Typical usages would be: blacklists, users that do not want solicitation from visitors. Views supporting an exchange filter are: ``WriteView``, ``ReplyView``. An example, with the django-relationships application:: def my_exchange_filter(sender, recipient, recipients_list): if recipient.relationships.exists(sender, RelationshipStatus.objects.blocking()): return "has blacklisted you" return None urlpatterns = patterns('postman.views', # ... url(r'^write/(?:(?P[^/#]+)/)?$', WriteView.as_view(exchange_filter=my_exchange_filter), name='write'), # ... ) The filter will be called for each couple, to validate that the exchange is possible. *New in version 3.3.0* In the case of a reply, there is an additional call for the implicit recipient when it is a User. The value of the ``recipients_list`` parameter allows to differentiate the context. *Inputs*: * ``sender``: a User instance, as the sender of the message, or None if the writer is not authenticated * ``recipient``: a User instance, as the recipient of the message * ``recipients_list``: the full list of recipients or (*New in version 3.3.0*) None in the case of the implicit recipient for a reply. Provided as a convenient additional element of decision. *Output*: If the exchange is allowed, just return ``None``. To forbid the exchange, use one of these means: * return ``False`` or ``''``, if you do not want to give a reason for the refusal. The error message will be: "Writing to some users is not possible: foo, bar." * return a string, as a reason for the refusal. The error message will be: "Writing to some users is not possible: foo (reason), bar (reason)." * raise a ``ValidationError`` with an error message to your liking. Advanced usage ~~~~~~~~~~~~~~ If you define your own custom form, you may specify an exchange filter inside. For example:: def my_exchange_filter(sender, recipient, recipients_list): # ... return None from postman.forms import WriteForm class MyWriteForm(WriteForm): exchange_filter = staticmethod(my_exchange_filter) Auto-complete field ------------------- An auto-complete functionality may be useful on the recipients field. To activate the option, set at least the ``arg_default`` key in the ``POSTMAN_AUTOCOMPLETER_APP`` dictionary. If the default ``ajax_select`` application is used, define a matching entry in the ``AJAX_LOOKUP_CHANNELS`` dictionary. Example:: AJAX_LOOKUP_CHANNELS = { 'postman_users': dict(model='auth.user', search_field='username'), } POSTMAN_AUTOCOMPLETER_APP = { 'arg_default': 'postman_users', } Don't forget that not-custom channels are restricted to users having the ``is_staff`` property. In case of version 1.1.4/5 of django-ajax-selects: Support for multiple recipients is not turned on by default by `django-ajax-selects`_. To allow this capability, you have to pass the option ``multiple: true`` to jquery-plugin-autocomplete. .. _`django-ajax-selects`: https://github.com/crucialfelix/django-ajax-selects Make your own templates, based on these two files, given as implementation examples: * :file:`postman/templates/autocomplete_postman_multiple_as1-1.html` * :file:`postman/templates/autocomplete_postman_single_as1-1.html` These examples include a correction necessary for the support of the 'multiple' option. In case of version 1.2.x of django-ajax-selects: Refer to the installation guide of this application, in particular the use of AJAX_SELECT_BOOTSTRAP and AJAX_SELECT_INLINES. Support for multiple recipients is not as simple as an option: see the examples in the `jQuery UI demos`_. .. _`jQuery UI demos`: http://jqueryui.com/resources/demos/autocomplete/multiple-remote.html You can use the following working implementation example: * :file:`postman/templates/autocomplete_postman_multiple_as1-2.html` *New in version 3.3.0* In case of version 1.3.x of django-ajax-selects: To make your own :file:`templates/autocomplete.html` or :file:`templates/autocomplete_.html`, you can use the following working implementation example: * :file:`postman/templates/autocomplete_postman_multiple_as1-3.html` Customization ~~~~~~~~~~~~~ You may attach a specific channel, different from the default one, to a particular view. Views supporting an auto-complete parameter are: ``WriteView``, ``ReplyView``. For the ``WriteView`` view, the parameter is named ``autocomplete_channels`` (note the plural). It supports two variations: * a 2-tuple of channels names: the first one for authenticated users, the second for visitors. Specify ``None`` if you let the default channel name for one of the tuple parts. * a single channel name: the same for users and visitors For the ``ReplyView`` view, the parameter is named ``autocomplete_channel`` (note the singular). The value is the channel name. Example:: urlpatterns = patterns('postman.views', # ... url(r'^write/(?:(?P[^/#]+)/)?$', WriteView.as_view(autocomplete_channels=(None,'anonymous_ac')), name='write'), url(r'^reply/(?P[\d]+)/$', ReplyView.as_view(autocomplete_channel='reply_ac'), name='reply'), # ... ) Example:: urlpatterns = patterns('postman.views', # ... url(r'^write/(?:(?P[^/#]+)/)?$', WriteView.as_view(autocomplete_channels='write_ac'), name='write'), # ... ) Advanced usage ~~~~~~~~~~~~~~ If you define your own custom form, you may specify an autocomplete channel inside. For example:: from postman.forms import WriteForm class MyWriteForm(WriteForm): recipients = CommaSeparatedUserField(label="Recipients", channel='my_channel')