mirror of
https://github.com/wagtail/wagtail.git
synced 2024-11-25 05:02:57 +01:00
parent
82ca711f16
commit
150e988f4d
@ -1,33 +1,35 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
import { Application } from '@hotwired/stimulus';
|
||||
import { TagController } from './TagController';
|
||||
|
||||
const flushPromises = () => new Promise(setImmediate);
|
||||
|
||||
window.$ = $;
|
||||
|
||||
import { initTagField } from './TagController';
|
||||
|
||||
describe('initTagField', () => {
|
||||
describe('TagController', () => {
|
||||
let application;
|
||||
let element;
|
||||
|
||||
const tagitMock = jest.fn(function tagitMockInner() {
|
||||
const tagitMock = jest.fn(function innerFunction() {
|
||||
element = this;
|
||||
});
|
||||
|
||||
window.$.fn.tagit = tagitMock;
|
||||
|
||||
element = null;
|
||||
|
||||
beforeEach(() => {
|
||||
element = null;
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should not call jQuery tagit if the element is not found', () => {
|
||||
it('should create a global initTagField to call jQuery tagit if the element is found', async () => {
|
||||
expect(window.initTagField).toBeUndefined();
|
||||
expect(tagitMock).not.toHaveBeenCalled();
|
||||
|
||||
initTagField('not-present');
|
||||
|
||||
expect(tagitMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call jQuery tagit if the element is found', () => {
|
||||
expect(tagitMock).not.toHaveBeenCalled();
|
||||
application = Application.start();
|
||||
application.register('w-tag', TagController);
|
||||
|
||||
document.body.innerHTML = `
|
||||
<main>
|
||||
@ -35,10 +37,12 @@ describe('initTagField', () => {
|
||||
</main>
|
||||
`;
|
||||
|
||||
initTagField('tag-input', '/path/to/autocomplete/', {
|
||||
window.initTagField('tag-input', '/path/to/autocomplete/', {
|
||||
someOther: 'option',
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// check the jQuery instance is the correct element
|
||||
expect(element).toContain(document.getElementById('tag-input'));
|
||||
|
||||
@ -59,4 +63,60 @@ describe('initTagField', () => {
|
||||
expect(preprocessTag('"flat white"')).toEqual(`"flat white"`);
|
||||
expect(preprocessTag("'long black'")).toEqual(`"'long black'"`);
|
||||
});
|
||||
|
||||
it('should not call jQuery tagit if the element is not found', async () => {
|
||||
expect(tagitMock).not.toHaveBeenCalled();
|
||||
|
||||
window.initTagField('not-present');
|
||||
|
||||
await flushPromises();
|
||||
|
||||
expect(tagitMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should attach the jQuery tagit to the controlled element', async () => {
|
||||
document.body.innerHTML = `
|
||||
<form id="form">
|
||||
<input
|
||||
id="id_tags"
|
||||
type="text"
|
||||
name="tags"
|
||||
data-controller="w-tag"
|
||||
data-action="example:event->w-tag#clear"
|
||||
data-w-tag-options-value="{"allowSpaces":true,"tagLimit":10}"
|
||||
data-w-tag-url-value="/admin/tag-autocomplete/"
|
||||
>
|
||||
</form>`;
|
||||
|
||||
expect(tagitMock).not.toHaveBeenCalled();
|
||||
|
||||
await flushPromises();
|
||||
|
||||
expect(tagitMock).toHaveBeenCalledWith({
|
||||
allowSpaces: true,
|
||||
autocomplete: { source: '/admin/tag-autocomplete/' },
|
||||
preprocessTag: expect.any(Function),
|
||||
tagLimit: 10,
|
||||
});
|
||||
|
||||
expect(element[0]).toEqual(document.getElementById('id_tags'));
|
||||
|
||||
// check the supplied preprocessTag function
|
||||
const [{ preprocessTag }] = tagitMock.mock.calls[0];
|
||||
|
||||
expect(preprocessTag).toBeInstanceOf(Function);
|
||||
|
||||
expect(preprocessTag()).toEqual();
|
||||
expect(preprocessTag('"flat white"')).toEqual(`"flat white"`);
|
||||
expect(preprocessTag("'long black'")).toEqual(`"'long black'"`);
|
||||
expect(preprocessTag('caffe latte')).toEqual(`"caffe latte"`);
|
||||
|
||||
// check the custom clear behaviour
|
||||
document
|
||||
.getElementById('id_tags')
|
||||
.dispatchEvent(new CustomEvent('example:event'));
|
||||
|
||||
await flushPromises();
|
||||
expect(tagitMock).toHaveBeenCalledWith('removeAll');
|
||||
});
|
||||
});
|
||||
|
@ -1,42 +1,93 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
import { Controller } from '@hotwired/stimulus';
|
||||
import type { Application } from '@hotwired/stimulus';
|
||||
import { domReady } from '../utils/domReady';
|
||||
|
||||
declare global {
|
||||
interface JQuery {
|
||||
tagit(...args): void;
|
||||
tagit: (options: Record<string, any> | string) => void;
|
||||
}
|
||||
|
||||
interface Window {
|
||||
initTagField: (
|
||||
id: string,
|
||||
url: string,
|
||||
options?: Record<string, any>,
|
||||
) => void;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the tag fields using the jQuery tagit widget
|
||||
* Attach the jQuery tagit UI to the controlled element.
|
||||
*
|
||||
* @param id - element id to initialise against
|
||||
* @param source - auto complete URL source
|
||||
* @param options - Other options passed to jQuery tagit
|
||||
* See https://github.com/aehlke/tag-it
|
||||
*
|
||||
* @example
|
||||
* <input id="id_tags" type="text" name="tags" data-controller="w-tag" data-w-tag-url-value="/admin/tag-autocomplete/" />
|
||||
*/
|
||||
const initTagField = (
|
||||
id: string,
|
||||
source: string,
|
||||
options: Record<string, any>,
|
||||
): void => {
|
||||
const tagFieldElement = document.getElementById(id);
|
||||
|
||||
if (!tagFieldElement) return;
|
||||
|
||||
const finalOptions = {
|
||||
autocomplete: { source },
|
||||
preprocessTag(val: any) {
|
||||
// Double quote a tag if it contains a space
|
||||
// and if it isn't already quoted.
|
||||
if (val && val[0] !== '"' && val.indexOf(' ') > -1) {
|
||||
return '"' + val + '"';
|
||||
}
|
||||
|
||||
return val;
|
||||
},
|
||||
...options,
|
||||
export class TagController extends Controller {
|
||||
static values = {
|
||||
options: { default: {}, type: Object },
|
||||
url: String,
|
||||
};
|
||||
|
||||
$('#' + id).tagit(finalOptions);
|
||||
};
|
||||
declare optionsValue: any;
|
||||
declare urlValue: any;
|
||||
tagit?: JQuery<HTMLElement>;
|
||||
|
||||
export { initTagField };
|
||||
/**
|
||||
* Prepare a global function that preserves the previous approach to
|
||||
* registering a tagit field. This will be removed in a future release.
|
||||
*
|
||||
* @deprecated RemovedInWagtail60
|
||||
*/
|
||||
static afterLoad(
|
||||
identifier: string,
|
||||
{ schema: { controllerAttribute } }: Application,
|
||||
) {
|
||||
window.initTagField = (id, url, options = {}): void => {
|
||||
domReady().then(() => {
|
||||
const tagFieldElement = document.getElementById(id);
|
||||
|
||||
if (!tagFieldElement || !url) return;
|
||||
|
||||
Object.entries({ options: JSON.stringify(options), url }).forEach(
|
||||
([name, value]) => {
|
||||
tagFieldElement.setAttribute(
|
||||
`data-${identifier}-${name}-value`,
|
||||
value,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
tagFieldElement.setAttribute(controllerAttribute, identifier);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
connect() {
|
||||
const preprocessTag = this.cleanTag.bind(this);
|
||||
|
||||
$(this.element).tagit({
|
||||
autocomplete: { source: this.urlValue },
|
||||
preprocessTag,
|
||||
...this.optionsValue,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Double quote a tag if it contains a space
|
||||
* and if it isn't already quoted.
|
||||
*/
|
||||
cleanTag(val: string) {
|
||||
return val && val[0] !== '"' && val.indexOf(' ') > -1 ? `"${val}"` : val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to clear all the tags that are set.
|
||||
*/
|
||||
clear() {
|
||||
$(this.element).tagit('removeAll');
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import { SkipLinkController } from './SkipLinkController';
|
||||
import { SlugController } from './SlugController';
|
||||
import { SubmitController } from './SubmitController';
|
||||
import { SyncController } from './SyncController';
|
||||
import { TagController } from './TagController';
|
||||
import { UpgradeController } from './UpgradeController';
|
||||
|
||||
/**
|
||||
@ -32,5 +33,6 @@ export const coreControllerDefinitions: Definition[] = [
|
||||
{ controllerConstructor: SlugController, identifier: 'w-slug' },
|
||||
{ controllerConstructor: SubmitController, identifier: 'w-submit' },
|
||||
{ controllerConstructor: SyncController, identifier: 'w-sync' },
|
||||
{ controllerConstructor: TagController, identifier: 'w-tag' },
|
||||
{ controllerConstructor: UpgradeController, identifier: 'w-upgrade' },
|
||||
];
|
||||
|
@ -3,7 +3,6 @@ import $ from 'jquery';
|
||||
import { coreControllerDefinitions } from '../../controllers';
|
||||
import { escapeHtml } from '../../utils/text';
|
||||
import { initStimulus } from '../../includes/initStimulus';
|
||||
import { initTagField } from '../../includes/initTagField';
|
||||
import { initTooltips } from '../../includes/initTooltips';
|
||||
|
||||
/** initialise Wagtail Stimulus application with core controller definitions */
|
||||
@ -11,8 +10,6 @@ window.Stimulus = initStimulus({ definitions: coreControllerDefinitions });
|
||||
|
||||
window.escapeHtml = escapeHtml;
|
||||
|
||||
window.initTagField = initTagField;
|
||||
|
||||
/*
|
||||
* Enables a "dirty form check", prompting the user if they are navigating away
|
||||
* from a page with unsaved changes, as well as optionally controlling other
|
||||
|
@ -1,16 +1,14 @@
|
||||
/**
|
||||
* Returns a promise that resolves once the DOM is ready for interaction.
|
||||
*/
|
||||
const domReady = async () =>
|
||||
new Promise<void>((resolve) => {
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => resolve(), {
|
||||
once: true,
|
||||
passive: true,
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
const domReady = async () => {
|
||||
if (document.readyState !== 'loading') return;
|
||||
await new Promise<void>((resolve) => {
|
||||
document.addEventListener('DOMContentLoaded', () => resolve(), {
|
||||
once: true,
|
||||
passive: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export { domReady };
|
||||
|
@ -148,3 +148,36 @@ The ordering for "Object permissions" and "Other permissions" now follows a pred
|
||||
This will be different to the previous ordering which never intentionally implemented.
|
||||
|
||||
This default ordering is now `["content_type__app_label", "content_type__model", "codename"]`, which can now be customised [](customising_group_views_permissions_order).
|
||||
|
||||
### Tag (Tagit) field usage now relies on data attributes
|
||||
|
||||
The `AdminTagWidget` widget has now been migrated to a Stimulus controller, if using this widget in Python, no changes are needed to adopt the new approach.
|
||||
|
||||
If the widget is being instantiated in JavaScript or HTML with the global util `window.initTagField`, this undocumented util should be replaced with the new `data-*` attributes approach. Additionally, any direct usage of the jQuery widget in JavaScript (e.g. `$('#my-element).tagit()`) should be removed.
|
||||
|
||||
The global util will be removed in a future release. It is recommended that the documented `AdminTagWidget` be used. However, if you need to use the JavaScript approach you can do this with the following example.
|
||||
|
||||
**Old syntax**
|
||||
|
||||
```html
|
||||
<input id="id_tags" type="text" value="popular, technology" hidden />
|
||||
<script>
|
||||
window.initTagField('id_tags', 'path/to/url', { autocompleteOnly: true });
|
||||
</script>
|
||||
```
|
||||
|
||||
**New syntax**
|
||||
|
||||
```html
|
||||
<input
|
||||
id="id_tags"
|
||||
type="text"
|
||||
value="popular, technology"
|
||||
hidden
|
||||
data-controller="w-tag"
|
||||
data-w-tag-options-value='{"autocompleteOnly": true}'
|
||||
data-w-tag-url-value="/path/to/url"
|
||||
/>
|
||||
```
|
||||
|
||||
Note: The `data-w-tag-options-value` is a JSON object serialised into string. Django's HTML escaping will handle it automatically when you use the `AdminTagWidget`, but if you are manually writing the attributes, be sure to use quotation marks correctly.
|
||||
|
@ -1,9 +1,2 @@
|
||||
{% include 'django/forms/widgets/text.html' %}
|
||||
<p class="help">{{ widget.help_text }}</p>
|
||||
<script>
|
||||
initTagField(
|
||||
"{{ widget.attrs.id|escapejs }}",
|
||||
"{{ widget.autocomplete_url|escapejs }}",
|
||||
{{ widget.options_json|safe }}
|
||||
);
|
||||
</script>
|
@ -12,7 +12,7 @@ from django.core import checks
|
||||
from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
|
||||
from django.test import RequestFactory, TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
from django.utils.html import json_script
|
||||
from django.utils.html import escape, json_script
|
||||
from freezegun import freeze_time
|
||||
|
||||
from wagtail.admin.forms import WagtailAdminModelForm, WagtailAdminPageForm
|
||||
@ -220,11 +220,14 @@ class TestGetFormForModel(TestCase):
|
||||
fields=["title", "slug", "tags"],
|
||||
)
|
||||
form_html = RestaurantPageForm().as_p()
|
||||
self.assertIn("/admin/tag\\u002Dautocomplete/tests/restauranttag/", form_html)
|
||||
self.assertIn(
|
||||
'data-w-tag-url-value="/admin/tag-autocomplete/tests/restauranttag/"',
|
||||
form_html,
|
||||
)
|
||||
|
||||
# widget should pick up the free_tagging=False attribute on the tag model
|
||||
# and set itself to autocomplete only
|
||||
self.assertIn('"autocompleteOnly": true', form_html)
|
||||
self.assertIn(escape('"autocompleteOnly": true'), form_html)
|
||||
|
||||
# Free tagging should also be disabled at the form field validation level
|
||||
RestaurantTag.objects.create(name="Italian", slug="italian")
|
||||
|
@ -1,8 +1,11 @@
|
||||
import json
|
||||
import re
|
||||
from html import unescape
|
||||
|
||||
from django import forms
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.utils.html import escape
|
||||
|
||||
from wagtail.admin import widgets
|
||||
from wagtail.admin.forms.tags import TagField
|
||||
@ -387,30 +390,32 @@ class TestAdminDateTimeInput(TestCase):
|
||||
|
||||
class TestAdminTagWidget(TestCase):
|
||||
def get_js_init_params(self, html):
|
||||
"""Returns a list of the params passed in to initTagField from the supplied HTML"""
|
||||
# example - ["test_id", "/admin/tag-autocomplete/", {'allowSpaces': True}]
|
||||
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:
|
||||
# stuff parameter string into an array so that we can unpack it as JSON
|
||||
return json.loads("[%s]" % params_raw)
|
||||
return []
|
||||
"""
|
||||
Returns a list of the key parts of data needed for the w-tag controlled element
|
||||
An id for the element with the 'w-tag' controller, the autocomplete url & tag options
|
||||
|
||||
def get_help_text_html_element(self, html):
|
||||
"""Return a help text html element with content as string"""
|
||||
start = """<input type="text" name="tags">"""
|
||||
end = "<script>"
|
||||
items_after_input_tag = html.split(start)[1]
|
||||
if items_after_input_tag:
|
||||
help_text_element = items_after_input_tag.split(end)[0].strip()
|
||||
return help_text_element
|
||||
return []
|
||||
example element <input data-controller="w-tag" id="test_id" data-w-tag-url-value="/admin/tag-autocomplete/" data-w-tag-options-value="{...encoded json opts}" />
|
||||
example result - ["test_id", "/admin/tag-autocomplete/", {'allowSpaces': True}]
|
||||
"""
|
||||
|
||||
element_id = re.search(
|
||||
r'data-controller=\"w-tag\" id=\"((?:\\.|[^"\\])*)\"\s+', html
|
||||
).group(1)
|
||||
autocomplete_url = re.search(
|
||||
r'data-w-tag-url-value=\"((?:\\.|[^"\\])*)"', html
|
||||
).group(1)
|
||||
options = re.search(
|
||||
r'data-w-tag-options-value=\"((?:\\.|[^"\\])*)"', html
|
||||
).group(1)
|
||||
|
||||
return [
|
||||
element_id,
|
||||
autocomplete_url,
|
||||
json.loads(unescape(options)),
|
||||
]
|
||||
|
||||
def test_render_js_init_basic(self):
|
||||
"""Checks that the 'initTagField' is correctly added to the inline script for tag widgets"""
|
||||
"""Checks that the 'w-tag' controller attributes are correctly added to the tag widgets"""
|
||||
widget = widgets.AdminTagWidget()
|
||||
|
||||
html = widget.render("tags", None, attrs={"id": "alpha"})
|
||||
@ -427,7 +432,7 @@ class TestAdminTagWidget(TestCase):
|
||||
|
||||
@override_settings(TAG_SPACES_ALLOWED=False)
|
||||
def test_render_js_init_no_spaces_allowed(self):
|
||||
"""Checks that the 'initTagField' includes the correct value based on TAG_SPACES_ALLOWED in settings"""
|
||||
"""Checks that the 'w-tag' controller attributes are correctly added to the tag widgets based on TAG_SPACES_ALLOWED in settings"""
|
||||
widget = widgets.AdminTagWidget()
|
||||
|
||||
html = widget.render("tags", None, attrs={"id": "alpha"})
|
||||
@ -444,7 +449,8 @@ class TestAdminTagWidget(TestCase):
|
||||
|
||||
@override_settings(TAG_LIMIT=5)
|
||||
def test_render_js_init_with_tag_limit(self):
|
||||
"""Checks that the 'initTagField' includes the correct value based on TAG_LIMIT in settings"""
|
||||
"""Checks that the 'w-tag' controller attributes are correctly added to the tag widget using options based on TAG_LIMIT in settings"""
|
||||
|
||||
widget = widgets.AdminTagWidget()
|
||||
|
||||
html = widget.render("tags", None, attrs={"id": "alpha"})
|
||||
@ -461,7 +467,8 @@ class TestAdminTagWidget(TestCase):
|
||||
|
||||
def test_render_js_init_with_tag_model(self):
|
||||
"""
|
||||
Checks that 'initTagField' is passed the correct autocomplete URL for the custom model,
|
||||
Checks that the 'w-tag' controller attributes are correctly added to the tag widget using
|
||||
the correct autocomplete URL for the custom model,
|
||||
and sets autocompleteOnly according to that model's free_tagging attribute
|
||||
"""
|
||||
widget = widgets.AdminTagWidget(tag_model=RestaurantTag)
|
||||
@ -517,16 +524,15 @@ class TestAdminTagWidget(TestCase):
|
||||
help_text = widget.get_context(None, None, {})["widget"]["help_text"]
|
||||
|
||||
html = widget.render("tags", None, {})
|
||||
help_text_html_element = self.get_help_text_html_element(html)
|
||||
|
||||
self.assertEqual(
|
||||
help_text,
|
||||
'Multi-word tags with spaces will automatically be enclosed in double quotes (").',
|
||||
)
|
||||
|
||||
self.assertHTMLEqual(
|
||||
help_text_html_element,
|
||||
"""<p class="help">%s</p>""" % help_text,
|
||||
self.assertIn(
|
||||
"""<p class="help">%s</p>""" % escape(help_text),
|
||||
html,
|
||||
)
|
||||
|
||||
@override_settings(TAG_SPACES_ALLOWED=False)
|
||||
@ -536,15 +542,14 @@ class TestAdminTagWidget(TestCase):
|
||||
help_text = widget.get_context(None, None, {})["widget"]["help_text"]
|
||||
|
||||
html = widget.render("tags", None, {})
|
||||
help_text_html_element = self.get_help_text_html_element(html)
|
||||
|
||||
self.assertEqual(
|
||||
help_text, "Tags can only consist of a single word, no spaces allowed."
|
||||
)
|
||||
|
||||
self.assertHTMLEqual(
|
||||
help_text_html_element,
|
||||
"""<p class="help">%s</p>""" % help_text,
|
||||
self.assertIn(
|
||||
"""<p class="help">%s</p>""" % escape(help_text),
|
||||
html,
|
||||
)
|
||||
|
||||
|
||||
|
@ -14,6 +14,13 @@ class AdminTagWidget(TagWidget):
|
||||
self.tag_model = kwargs.pop("tag_model", Tag)
|
||||
# free_tagging = None means defer to the tag model's setting
|
||||
self.free_tagging = kwargs.pop("free_tagging", None)
|
||||
|
||||
default_attrs = {"data-controller": "w-tag"}
|
||||
attrs = kwargs.get("attrs")
|
||||
if attrs:
|
||||
default_attrs.update(attrs)
|
||||
kwargs["attrs"] = default_attrs
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
@ -41,8 +48,8 @@ class AdminTagWidget(TagWidget):
|
||||
help_text = _("Tags can only consist of a single word, no spaces allowed.")
|
||||
|
||||
context["widget"]["help_text"] = help_text
|
||||
context["widget"]["autocomplete_url"] = autocomplete_url
|
||||
context["widget"]["options_json"] = json.dumps(
|
||||
context["widget"]["attrs"]["data-w-tag-url-value"] = autocomplete_url
|
||||
context["widget"]["attrs"]["data-w-tag-options-value"] = json.dumps(
|
||||
{
|
||||
"allowSpaces": getattr(settings, "TAG_SPACES_ALLOWED", True),
|
||||
"tagLimit": getattr(settings, "TAG_LIMIT", None),
|
||||
|
@ -174,9 +174,6 @@ $(function () {
|
||||
});
|
||||
} else {
|
||||
form.replaceWith(data.form);
|
||||
|
||||
// run tagit enhancement on new form
|
||||
$('.tag_field input', form).tagit(window.tagit_opts);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -3,18 +3,6 @@
|
||||
{% load wagtailimages_tags wagtailadmin_tags %}
|
||||
{% block titletag %}{% blocktrans trimmed count counter=items|length %}Add tags to 1 document {% plural %}Add tags to {{ counter }} documents{% endblocktrans %}{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{{ block.super }}
|
||||
{% url 'wagtailadmin_tag_autocomplete' as autocomplete_url %}
|
||||
<script>
|
||||
$(function() {
|
||||
$('#id_tags').tagit({
|
||||
autocomplete: {source: "{{ autocomplete_url|addslashes }}"}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
{% trans "Add tags to documents" as add_str %}
|
||||
{% include "wagtailadmin/shared/header.html" with title=add_str icon="doc-full-inverse" %}
|
||||
|
@ -36,9 +36,6 @@
|
||||
$titleField.val(data.title);
|
||||
}
|
||||
);
|
||||
$('#id_tags').tagit({
|
||||
autocomplete: {source: "{{ autocomplete_url|addslashes }}"}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -7,15 +7,6 @@
|
||||
{{ block.super }}
|
||||
|
||||
{{ form.media.js }}
|
||||
|
||||
{% url 'wagtailadmin_tag_autocomplete' as autocomplete_url %}
|
||||
<script>
|
||||
$(function() {
|
||||
$('#id_tags').tagit({
|
||||
autocomplete: {source: "{{ autocomplete_url|addslashes }}"}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
|
@ -214,9 +214,6 @@ $(function () {
|
||||
});
|
||||
} else {
|
||||
form.replaceWith(data.form);
|
||||
|
||||
// run tagit enhancement on new form
|
||||
$('.tag_field input', form).tagit(window.tagit_opts);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -3,18 +3,6 @@
|
||||
{% load wagtailimages_tags wagtailadmin_tags %}
|
||||
{% block titletag %}{% blocktrans trimmed count counter=items|length %}Add tags to 1 image {% plural %}Add tags to {{ counter }} images{% endblocktrans %}{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{{ block.super }}
|
||||
{% url 'wagtailadmin_tag_autocomplete' as autocomplete_url %}
|
||||
<script>
|
||||
$(function() {
|
||||
$('#id_tags').tagit({
|
||||
autocomplete: {source: "{{ autocomplete_url|addslashes }}"}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
{% trans "Add tags to images" as add_str %}
|
||||
{% include "wagtailadmin/shared/header.html" with title=add_str icon="doc-full-inverse" %}
|
||||
|
@ -36,9 +36,6 @@
|
||||
$titleField.val(data.title);
|
||||
}
|
||||
);
|
||||
$('#id_tags').tagit({
|
||||
autocomplete: {source: "{{ autocomplete_url|addslashes }}"}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -12,15 +12,6 @@
|
||||
|
||||
{{ form.media.js }}
|
||||
|
||||
{% url 'wagtailadmin_tag_autocomplete' as autocomplete_url %}
|
||||
<script>
|
||||
$(function() {
|
||||
$('#id_tags').tagit({
|
||||
autocomplete: {source: "{{ autocomplete_url|addslashes }}"}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Focal point chooser -->
|
||||
<script src="{% versioned_static 'wagtailadmin/js/vendor/jquery.ba-throttle-debounce.min.js' %}"></script>
|
||||
<script src="{% versioned_static 'wagtailimages/js/vendor/jquery.Jcrop.min.js' %}"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user