diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 0ad6c1071d..85554a9a5a 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -16,6 +16,7 @@ Changelog
* Renamed `target_model` argument on `PageChooserBlock` to `page_type` (Loic Teixeira)
* `edit_handler` and `panels` can now be defined on a `ModelAdmin` definition (Thomas Kremmel)
* Add Learn Wagtail to third-party tutorials in documentation (Matt Westcott)
+ * Add a Django setting `TAG_LIMIT` to limit number of tags that can be added to any taggit model (Mani)
* Fix: Set `SERVER_PORT` to 443 in `Page.dummy_request()` for HTTPS sites (Sergey Fedoseev)
* Fix: Include port number in `Host` header of `Page.dummy_request()` (Sergey Fedoseev)
* Fix: Validation error messages in `InlinePanel` no longer count towards `max_num` when disabling the 'add' button (Todd Dembrey, Thibaud Colas)
diff --git a/docs/advanced_topics/settings.rst b/docs/advanced_topics/settings.rst
index b4752ef18a..47751f6a16 100644
--- a/docs/advanced_topics/settings.rst
+++ b/docs/advanced_topics/settings.rst
@@ -384,6 +384,15 @@ Multi-word tags
Tags can only consist of a single word, no spaces allowed. The default setting is ``True`` (spaces in tags are allowed).
+Tag limit
+---------
+
+.. code-block:: python
+
+ TAG_LIMIT = 5
+
+Limit the number of tags that can be added to (django-taggit) Tag model. Default setting is ``None``, meaning no limit on tags.
+
Unicode Page Slugs
------------------
diff --git a/docs/releases/2.5.rst b/docs/releases/2.5.rst
index b4cf7cf027..89b37fdee7 100644
--- a/docs/releases/2.5.rst
+++ b/docs/releases/2.5.rst
@@ -26,6 +26,7 @@ Other features
* Renamed ``target_model`` argument on ``PageChooserBlock`` to ``page_type`` (Loic Teixeira)
* ``edit_handler`` and ``panels`` can now be defined on a ``ModelAdmin`` definition (Thomas Kremmel)
* Add Learn Wagtail to third-party tutorials in documentation (Matt Westcott)
+ * Add a Django setting ``TAG_LIMIT`` to limit number of tags that can be added to any taggit model (Mani)
Bug fixes
diff --git a/wagtail/admin/static_src/wagtailadmin/js/core.js b/wagtail/admin/static_src/wagtailadmin/js/core.js
index 587195abc4..79eedd60c9 100644
--- a/wagtail/admin/static_src/wagtailadmin/js/core.js
+++ b/wagtail/admin/static_src/wagtailadmin/js/core.js
@@ -21,7 +21,7 @@ function escapeHtml(text) {
});
}
-function initTagField(id, autocompleteUrl, allowSpaces) {
+function initTagField(id, autocompleteUrl, allowSpaces, tagLimit) {
$('#' + id).tagit({
autocomplete: {source: autocompleteUrl},
preprocessTag: function(val) {
@@ -34,7 +34,8 @@ function initTagField(id, autocompleteUrl, allowSpaces) {
return val;
},
- allowSpaces: allowSpaces
+ allowSpaces: allowSpaces,
+ tagLimit: tagLimit,
});
}
diff --git a/wagtail/admin/templates/wagtailadmin/widgets/tag_widget.html b/wagtail/admin/templates/wagtailadmin/widgets/tag_widget.html
index 075b3a8e90..fb730fbff2 100644
--- a/wagtail/admin/templates/wagtailadmin/widgets/tag_widget.html
+++ b/wagtail/admin/templates/wagtailadmin/widgets/tag_widget.html
@@ -1 +1,9 @@
-{% include 'django/forms/widgets/text.html' %}
\ No newline at end of file
+{% include 'django/forms/widgets/text.html' %}
+
\ No newline at end of file
diff --git a/wagtail/admin/tests/test_widgets.py b/wagtail/admin/tests/test_widgets.py
index a26a687b64..251b33bfc3 100644
--- a/wagtail/admin/tests/test_widgets.py
+++ b/wagtail/admin/tests/test_widgets.py
@@ -157,3 +157,58 @@ class TestAdminDateTimeInput(TestCase):
'"format": "d.m.Y. H:i"',
html,
)
+
+
+class TestAdminTagWidget(TestCase):
+
+ def get_js_init_params(self, html):
+ """Returns a list of the params passed in to initTagField from the supplied HTML"""
+ # Eg. ["'test\\u002Did'", "'/admin/tag\\u002Dautocomplete/'", 'true', 'null']
+ start = 'initTagField('
+ end = ');'
+ items_after_init = html.split(start)[1]
+ if items_after_init:
+ params_raw = items_after_init.split(end)[0]
+ if params_raw:
+ return [part.strip() for part in params_raw.split(',')]
+ return []
+
+
+ def test_render_js_init_basic(self):
+ """Chekcs that the 'initTagField' is correctly added to the inline script for tag widgets"""
+ widget = widgets.AdminTagWidget()
+
+ html = widget.render('tags', None, attrs={'id': 'alpha'})
+ params = self.get_js_init_params(html)
+
+ self.assertEqual(len(params), 4)
+ self.assertEqual(params[0], "'alpha'") # id
+ self.assertEqual(params[1], "'/admin/tag\\u002Dautocomplete/'") # autocomplete url
+ self.assertEqual(params[2], 'true') # tag_spaces_allowed
+ self.assertEqual(params[3], 'null') # tag_limit
+
+
+ @override_settings(TAG_SPACES_ALLOWED=False)
+ def test_render_js_init_no_spaces_allowed(self):
+ """Chekcs that the 'initTagField' includes the correct value based on TAG_SPACES_ALLOWED in settings"""
+ widget = widgets.AdminTagWidget()
+
+ html = widget.render('tags', None, attrs={'id': 'alpha'})
+ params = self.get_js_init_params(html)
+
+ self.assertEqual(len(params), 4)
+ self.assertEqual(params[2], 'false') # tag_spaces_allowed
+ self.assertEqual(params[3], 'null') # tag_limit
+
+
+ @override_settings(TAG_LIMIT=5)
+ def test_render_js_init_with_tag_limit(self):
+ """Chekcs that the 'initTagField' includes the correct value based on TAG_LIMIT in settings"""
+ widget = widgets.AdminTagWidget()
+
+ html = widget.render('tags', None, attrs={'id': 'alpha'})
+ params = self.get_js_init_params(html)
+
+ self.assertEqual(len(params), 4)
+ self.assertEqual(params[2], 'true') # tag_spaces_allowed
+ self.assertEqual(params[3], '5') # tag_limit
diff --git a/wagtail/admin/widgets.py b/wagtail/admin/widgets.py
index 04e0b3c95f..8224169e84 100644
--- a/wagtail/admin/widgets.py
+++ b/wagtail/admin/widgets.py
@@ -101,6 +101,7 @@ class AdminTagWidget(TagWidget):
context = super().get_context(name, value, attrs)
context['widget']['autocomplete_url'] = reverse('wagtailadmin_tag_autocomplete')
context['widget']['tag_spaces_allowed'] = getattr(settings, 'TAG_SPACES_ALLOWED', True)
+ context['widget']['tag_limit'] = getattr(settings, 'TAG_LIMIT', None)
return context