| 758 | | Inside your filter, you will need to think about three areas in order to be |
|---|
| 759 | | auto-escaping compliant: |
|---|
| 760 | | |
|---|
| 761 | | 1. If your filter returns a string that is ready for direct output (it should |
|---|
| 762 | | be considered a "safe" string), you should call |
|---|
| 763 | | ``django.utils.safestring.mark_safe()`` on the result prior to returning. |
|---|
| 764 | | This will turn the result into the appropriate ``SafeData`` type. This is |
|---|
| 765 | | often the case when you are returning raw HTML, for example. |
|---|
| 766 | | |
|---|
| 767 | | 2. If your filter is given a "safe" string, is it guaranteed to return a |
|---|
| 768 | | "safe" string? If so, set the ``is_safe`` attribute on the function to be |
|---|
| 769 | | ``True``. For example, a filter that replaced a word consisting only of |
|---|
| 770 | | digits with the number spelt out in words is going to be |
|---|
| 771 | | safe-string-preserving, since it cannot introduce any of the five dangerous |
|---|
| 772 | | characters: <, >, ", ' or &. We can write:: |
|---|
| | 758 | When you are writing a filter, your code will typically fall into one of two |
|---|
| | 759 | situations: |
|---|
| | 760 | |
|---|
| | 761 | 1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``, |
|---|
| | 762 | ``'``, ``"`` or ``&``) into the result that were not already present. In |
|---|
| | 763 | this case, you can let Django take care of all the auto-escaping handling |
|---|
| | 764 | for you. All you need to do is put the ``is_safe`` attribute on your |
|---|
| | 765 | filter function and set it to ``True``. This attribute tells Django that |
|---|
| | 766 | is a "safe" string is passed into your filter, the result will still be |
|---|
| | 767 | "safe" and if a non-safe string is passed in, Django will automatically |
|---|
| | 768 | escape it, if necessary. The reason ``is_safe`` is necessary is because |
|---|
| | 769 | there are plenty of normal string operations that will turn a ``SafeData`` |
|---|
| | 770 | object back into a normal ``str`` or ``unicode`` object and, rather than |
|---|
| | 771 | try to catch them all, which would be very difficult, Django repairs the |
|---|
| | 772 | damage after the filter has completed. |
|---|
| | 773 | |
|---|
| | 774 | For example, suppose you have a filter that adds the string ``xx`` to the |
|---|
| | 775 | end of any input. Since this introduces no dangerous HTML characters into |
|---|
| | 776 | the result (aside from any that were already present), you should mark |
|---|
| | 777 | your filter with ``is_safe``:: |
|---|
| 775 | | def convert_to_words(value): |
|---|
| 776 | | # ... implementation here ... |
|---|
| 777 | | return result |
|---|
| 778 | | |
|---|
| 779 | | convert_to_words.is_safe = True |
|---|
| 780 | | |
|---|
| 781 | | Note that this filter does not return a universally safe result (it does |
|---|
| 782 | | not return ``mark_safe(result)``) because if it is handed a raw string such |
|---|
| 783 | | as '<a>', this will need further escaping in an auto-escape environment. |
|---|
| 784 | | The ``is_safe`` attribute only talks about the the result when a safe |
|---|
| 785 | | string is passed into the filter. |
|---|
| 786 | | |
|---|
| 787 | | 3. Will your filter behave differently depending upon whether auto-escaping |
|---|
| 788 | | is currently in effect or not? This is normally a concern when you are |
|---|
| 789 | | returning mixed content (HTML elements mixed with user-supplied content). |
|---|
| 790 | | For example, the ``ordered_list`` filter that ships with Django needs to |
|---|
| 791 | | know whether to escape its content or not. It will always return a safe |
|---|
| 792 | | string. Since it returns raw HTML, we cannot apply escaping to the |
|---|
| 793 | | result -- it needs to be done in-situ. |
|---|
| 794 | | |
|---|
| 795 | | For these cases, the filter function needs to be told what the current |
|---|
| 796 | | auto-escaping setting is. Set the ``needs_autoescape`` attribute on the |
|---|
| 797 | | filter to ``True`` and have your function take an extra argument called |
|---|
| 798 | | ``autoescape`` with a default value of ``None``. When the filter is called, |
|---|
| 799 | | the ``autoescape`` keyword argument will be ``True`` if auto-escaping is in |
|---|
| 800 | | effect. For example, the ``unordered_list`` filter is written as:: |
|---|
| 801 | | |
|---|
| 802 | | def unordered_list(value, autoescape=None): |
|---|
| 803 | | # ... lots of code here ... |
|---|
| 804 | | |
|---|
| 805 | | return mark_safe(...) |
|---|
| 806 | | |
|---|
| 807 | | unordered_list.is_safe = True |
|---|
| 808 | | unordered_list.needs_autoescape = True |
|---|
| 809 | | |
|---|
| 810 | | By default, both the ``is_safe`` and ``needs_autoescape`` attributes are |
|---|
| 811 | | ``False``. You do not need to specify them if ``False`` is an acceptable |
|---|
| 812 | | value. |
|---|
| | 780 | def add_xx(value): |
|---|
| | 781 | return '%sxx' % value |
|---|
| | 782 | add_xx.is_safe = True |
|---|
| | 783 | |
|---|
| | 784 | When this filter is used in a template where auto-escaping is enabled, |
|---|
| | 785 | Django will escape the output whenever the input is not already marked as |
|---|
| | 786 | "safe". |
|---|
| | 787 | |
|---|
| | 788 | By default, ``is_safe`` defaults to ``False`` and you can omit it from |
|---|
| | 789 | any filters where it isn't required. |
|---|
| | 790 | |
|---|
| | 791 | Be careful when deciding if your filter really does leave safe strings |
|---|
| | 792 | as safe. Sometimes if you are *removing* characters, you can |
|---|
| | 793 | inadvertently leave unbalanced HTML tags or entities in the result. |
|---|
| | 794 | For example, removing a ``>`` from the input might turn ``<a>`` into |
|---|
| | 795 | ``<a``, which would need to be escaped on output to avoid causing |
|---|
| | 796 | problems. Similarly, removing a semicolon (``;``) can turn ``&`` |
|---|
| | 797 | into ``&``, which is no longer a valid entity and thus needs |
|---|
| | 798 | further escaping. Most cases won't be nearly this tricky, but keep an |
|---|
| | 799 | eye out for any problems like that when reviewing your code. |
|---|
| | 800 | |
|---|
| | 801 | 2. Alternatively, your filter code can manually take care of any necessary |
|---|
| | 802 | escaping. This is usually necessary when you are introducing new HTML |
|---|
| | 803 | markup into the result. You want to mark the output as safe from further |
|---|
| | 804 | escaping so that your HTML markup isn't escaped further, so you'll need to |
|---|
| | 805 | handle the input yourself. |
|---|
| | 806 | |
|---|
| | 807 | To mark the output as a safe string, use |
|---|
| | 808 | ``django.utils.safestring.mark_safe()``. |
|---|
| | 809 | |
|---|
| | 810 | Be careful, though. You need to do more than just mark the output as |
|---|
| | 811 | safe. You need to ensure it really *is* safe and what you do will often |
|---|
| | 812 | depend upon whether or not auto-escaping is in effect. The idea is to |
|---|
| | 813 | write filters than can operate in templates where auto-escaping is either |
|---|
| | 814 | on or off in order to make things easier for your template authors. |
|---|
| | 815 | |
|---|
| | 816 | In order for you filter to know the current auto-escaping state, set the |
|---|
| | 817 | ``needs_autoescape`` attribute to ``True`` on your function (if you don't |
|---|
| | 818 | specify this attribute, it defaults to ``False``). This attribute tells |
|---|
| | 819 | Django that your filter function wants to be passed an extra keyword |
|---|
| | 820 | argument, called ``autoescape`` that is ``True`` is auto-escaping is in |
|---|
| | 821 | effect and ``False`` otherwise. |
|---|
| | 822 | |
|---|
| | 823 | An example might make this clearer. Let's write a filter that emphasizes |
|---|
| | 824 | the first character of a string:: |
|---|
| | 825 | |
|---|
| | 826 | from django.utils.html import conditional_escape |
|---|
| | 827 | from django.utils.safestring import mark_safe |
|---|
| | 828 | |
|---|
| | 829 | def initial_letter_filter(text, autoescape=None): |
|---|
| | 830 | first, other = text[0] ,text[1:] |
|---|
| | 831 | if autoescape: |
|---|
| | 832 | esc = conditional_escape |
|---|
| | 833 | else: |
|---|
| | 834 | esc = lambda x: x |
|---|
| | 835 | result = '<strong>%s</strong>%s' % (esc(first), esc(other)) |
|---|
| | 836 | return mark_safe(result) |
|---|
| | 837 | initial_letter_filter.needs_autoescape = True |
|---|
| | 838 | |
|---|
| | 839 | The ``needs_autoescape`` attribute on the filter function and the |
|---|
| | 840 | ``autoescape`` keyword argument mean that our function will know whether |
|---|
| | 841 | or not automatic escaping is in effect when the filter is called. We use |
|---|
| | 842 | ``autoescape`` to decide whether the input data needs to be passed through |
|---|
| | 843 | ``django.utils.html.conditional_escape`` or not (in the latter case, we |
|---|
| | 844 | just use the identity function as the "escape" function). The |
|---|
| | 845 | ``conditional_escape()`` function is like ``escape()`` except it only |
|---|
| | 846 | escapes input that is **not** a ``SafeData`` instance. If a ``SafeData`` |
|---|
| | 847 | instance is passed to ``conditional_escape()``, the data is returned |
|---|
| | 848 | unchanged. |
|---|
| | 849 | |
|---|
| | 850 | Finally, in the above example, we remember to mark the result as safe |
|---|
| | 851 | so that our HTML is inserted directly into the template without further |
|---|
| | 852 | escaping. |
|---|
| | 853 | |
|---|
| | 854 | There is no need to worry about the ``is_safe`` attribute in this case |
|---|
| | 855 | (although including it wouldn't hurt anything). Whenever you are manually |
|---|
| | 856 | handling the auto-escaping issues and returning a safe string, the |
|---|
| | 857 | ``is_safe`` attribute won't change anything either way. |
|---|