Skip to content

Commit

Permalink
Merge pull request #2213 from ruohola/description-html
Browse files Browse the repository at this point in the history
Admin: Add settings for disabling HTML in descriptions
  • Loading branch information
tulimaki committed Jul 3, 2020
2 parents 49ea043 + 5b1497a commit 5ed12c2
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 8 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

List all changes after the last release here (newer on top). Each change on a separate bullet point line.

### Added

- Admin: Add settings for controlling allowing HTML in product and vendor descriptions

## [1.11.0] - 2020-07-02

Expand Down
1 change: 1 addition & 0 deletions setup.py
Expand Up @@ -82,6 +82,7 @@

REQUIRES = [
'Babel==2.5.3',
'bleach==3.1.5',
'Django>=1.8,<2',
'django-bootstrap3>=6.1,<10',
'django-countries>=3.3,<5.3',
Expand Down
4 changes: 3 additions & 1 deletion shuup/admin/modules/products/forms/base_forms.py
Expand Up @@ -82,7 +82,9 @@ class Meta:
"keywords": forms.TextInput(),
"sales_unit": QuickAddSalesUnitSelect(editable_model="shuup.SalesUnit"),
"tax_class": QuickAddTaxClassSelect(editable_model="shuup.TaxClass"),
"description": TextEditorWidget(),
"description": (TextEditorWidget()
if settings.SHUUP_ADMIN_ALLOW_HTML_IN_PRODUCT_DESCRIPTION
else forms.Textarea(attrs={"rows": 5})),
"short_description": forms.TextInput(),
}

Expand Down
10 changes: 9 additions & 1 deletion shuup/admin/modules/suppliers/forms.py
Expand Up @@ -7,6 +7,7 @@
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals

import bleach
from django import forms
from django.conf import settings
from django.utils.encoding import force_text
Expand All @@ -25,7 +26,9 @@ class Meta:
exclude = ("module_data", "options", "contact_address", "deleted")
widgets = {
"module_identifier": forms.Select,
"description": TextEditorWidget()
"description": (TextEditorWidget()
if settings.SHUUP_ADMIN_ALLOW_HTML_IN_VENDOR_DESCRIPTION
else forms.Textarea(attrs={"rows": 5})),
}

def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -63,6 +66,11 @@ def clean(self):
if stock_managed and not module_identifier:
self.add_error("stock_managed", _("It is not possible to manage inventory when no module is selected."))

if not settings.SHUUP_ADMIN_ALLOW_HTML_IN_VENDOR_DESCRIPTION:
for key, value in cleaned_data.items():
if key.startswith("description__"):
cleaned_data[key] = bleach.clean(value, tags=[])

return cleaned_data

def save(self, commit=True):
Expand Down
10 changes: 10 additions & 0 deletions shuup/admin/settings.py
Expand Up @@ -107,3 +107,13 @@
#: Whether to require payment method at admin order creator/edit
#:
SHUUP_ADMIN_REQUIRE_PAYMENT_METHOD_AT_ORDER_CREATOR = True

#: Whether to allow vendors and staff to use a rich text editor and HTML for product descriptions.
#: If this is False, only a allow simple text field and sanitize all HTML from it.
#:
SHUUP_ADMIN_ALLOW_HTML_IN_PRODUCT_DESCRIPTION = True

#: Whether to allow vendors to use a rich text editor and HTML for their profile descriptions.
#: If this is False, only a allow simple text field and sanitize all HTML from it.
#:
SHUUP_ADMIN_ALLOW_HTML_IN_VENDOR_DESCRIPTION = True
21 changes: 20 additions & 1 deletion shuup/core/templatetags/shuup_common.py
Expand Up @@ -19,14 +19,15 @@
from datetime import date
from json import dumps as json_dump

import bleach
from babel.dates import format_date, format_datetime, format_time
from babel.numbers import format_decimal
from django.conf import settings
from django.utils import translation
from django.utils.safestring import mark_safe
from django.utils.timezone import localtime
from django_jinja import library
from jinja2.runtime import Undefined
from jinja2 import Undefined
from jinja2.utils import contextfunction

from shuup.utils.i18n import (
Expand Down Expand Up @@ -139,6 +140,24 @@ def json(value):
return mark_safe(json_dump(value, cls=ExtendedJSONEncoder))


@library.filter
def safe_product_description(value):
if isinstance(value, Undefined):
return value
if not settings.SHUUP_ADMIN_ALLOW_HTML_IN_PRODUCT_DESCRIPTION:
value = bleach.clean(value, tags=[])
return mark_safe(value)


@library.filter
def safe_vendor_description(value):
if isinstance(value, Undefined):
return value
if not settings.SHUUP_ADMIN_ALLOW_HTML_IN_VENDOR_DESCRIPTION:
value = bleach.clean(value, tags=[])
return mark_safe(value)


@library.global_function
@contextfunction
def get_shop_configuration(context, name, default=None):
Expand Down
2 changes: 1 addition & 1 deletion shuup/front/templates/shuup/front/macros/product.jinja
Expand Up @@ -53,7 +53,7 @@ Inheritance order for product macors:
{% endif %}
</div>
{% if show_description and product.short_description %}
<p class="description">{{ product.short_description|safe }}</p>
<p class="description">{{ product.short_description|safe_product_description }}</p>
{% endif %}
</div>
</a>
Expand Down
Expand Up @@ -7,7 +7,7 @@
{% if show_supplier_info %}{{ render_supplier_info(supplier) }}{% endif %}
<p class="text-muted product-sku"><small>{{ product.sku }}</small></p>
{% if product.short_description %}
<p class="product-short-description">{{ product.short_description|safe }}</p>
<p class="product-short-description">{{ product.short_description|safe_product_description }}</p>
{% endif %}
{% if show_prices() and not xtheme.get("hide_prices") %}
<div class="product-price">
Expand Down Expand Up @@ -69,7 +69,7 @@
{% if xtheme.should_render_product_detail_tab("description") %}
<div role="tabpanel" class="product-description tab-pane fade in {% if active_tab == 'description' %}in active{% endif %}" id="description">
{% if product.description or package_children %}
{{ product.description|safe }}
{{ product.description|safe_product_description }}
{% if package_children %}
{% set quantity_map = product.get_package_child_to_quantity_map() %}
<p><strong>{% trans %}Includes these products{% endtrans %}</strong></p>
Expand Down
Expand Up @@ -40,7 +40,7 @@
{% if show_supplier_info %}{{ render_supplier_info(supplier, render_link=False) }}{% endif %}
{% if product.short_description %}
<p class="description">
{{ product.short_description|safe }}
{{ product.short_description|safe_product_description }}
</p>
{% endif %}
{% if show_prices() and not xtheme.get("hide_prices") %}
Expand Down
24 changes: 23 additions & 1 deletion shuup_tests/core/test_template_tags.py
Expand Up @@ -8,14 +8,16 @@
from __future__ import unicode_literals

from decimal import Decimal

from django.conf import settings
from mock import patch

from django.utils import translation
from jinja2 import Template

from shuup.core.models import Shop
from shuup.core.templatetags.shuup_common import (
get_global_configuration, get_shop_configuration, money, number, percent
get_global_configuration, get_shop_configuration, money, number, percent, safe_product_description, safe_vendor_description
)
from shuup.utils.money import Money

Expand Down Expand Up @@ -213,3 +215,23 @@ def test_get_global_configuration(conf_get_mock, rf):
def test_get_global_configuration_with_default(conf_get_mock, rf):
get_global_configuration('some_variable', 'default')
conf_get_mock.assert_called_once_with(None, 'some_variable', 'default')


def test_safe_product_description():
text = "<p>product description</p>"

settings.SHUUP_ADMIN_ALLOW_HTML_IN_PRODUCT_DESCRIPTION = True
assert safe_product_description(text) == text

settings.SHUUP_ADMIN_ALLOW_HTML_IN_PRODUCT_DESCRIPTION = False
assert safe_product_description(text) == "&lt;p&gt;product description&lt;/p&gt;"


def test_safe_vendor_description():
text = "<p>vendor description</p>"

settings.SHUUP_ADMIN_ALLOW_HTML_IN_VENDOR_DESCRIPTION = True
assert safe_vendor_description(text) == text

settings.SHUUP_ADMIN_ALLOW_HTML_IN_VENDOR_DESCRIPTION = False
assert safe_vendor_description(text) == "&lt;p&gt;vendor description&lt;/p&gt;"

0 comments on commit 5ed12c2

Please sign in to comment.