kopia lustrzana https://github.com/wagtail/wagtail
Add ability to create anchor link tab within rich text link
- add tests - Update changelog & release notespull/5518/head
rodzic
2de92f045c
commit
794d40b86b
|
@ -7,6 +7,7 @@ Changelog
|
|||
* Added `construct_page_listing_buttons` hook (Michael van Tellingen)
|
||||
* Added more detailed documentation and troubleshooting for installing OpenCV for feature detection (Daniele Procida)
|
||||
* Move and refactor upgrade notification JS (Jonny Scholes)
|
||||
* Add ability to insert internal anchor links/links with fragment identifiers in Draftail (rich text) fields (Iman Syed)
|
||||
* Remove need for Elasticsearch `update_all_types` workaround, upgrade minimum release to 6.4.0 or above (Jonathan Liuti)
|
||||
* Fix: Added line breaks to long filenames on multiple image / document uploader (Kevin Howbrook)
|
||||
* Fix: Added https support for Scribd oEmbed provider (Rodrigo)
|
||||
|
|
|
@ -14,7 +14,7 @@ const MAIL_ICON = <Icon name="mail" />;
|
|||
const getEmailAddress = mailto => mailto.replace('mailto:', '').split('?')[0];
|
||||
const getDomainName = url => url.replace(/(^\w+:|^)\/\//, '').split('/')[0];
|
||||
|
||||
// Determines how to display the link based on its type: page, mail, or external.
|
||||
// Determines how to display the link based on its type: page, mail, anchor or external.
|
||||
export const getLinkAttributes = (data) => {
|
||||
const url = data.url || null;
|
||||
let icon;
|
||||
|
@ -29,6 +29,9 @@ export const getLinkAttributes = (data) => {
|
|||
} else if (url.startsWith('mailto:')) {
|
||||
icon = MAIL_ICON;
|
||||
label = getEmailAddress(url);
|
||||
} else if (url.startsWith('#')) {
|
||||
icon = LINK_ICON;
|
||||
label = url;
|
||||
} else {
|
||||
icon = LINK_ICON;
|
||||
label = getDomainName(url);
|
||||
|
|
|
@ -56,6 +56,13 @@ describe('Link', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('anchor', () => {
|
||||
expect(getLinkAttributes({ url: '#testanchor' })).toMatchObject({
|
||||
url: '#testanchor',
|
||||
label: '#testanchor',
|
||||
});
|
||||
});
|
||||
|
||||
it('external', () => {
|
||||
expect(getLinkAttributes({ url: 'http://www.ex.com/' })).toMatchObject({
|
||||
url: 'http://www.ex.com/',
|
||||
|
|
|
@ -42,6 +42,7 @@ export const getChooserConfig = (entityType, entity, selectedText) => {
|
|||
page_type: 'wagtailcore.page',
|
||||
allow_external_link: true,
|
||||
allow_email_link: true,
|
||||
allow_anchor_link: true,
|
||||
link_text: selectedText,
|
||||
};
|
||||
|
||||
|
@ -57,6 +58,9 @@ export const getChooserConfig = (entityType, entity, selectedText) => {
|
|||
} else if (data.url.startsWith('mailto:')) {
|
||||
url = global.chooserUrls.emailLinkChooser;
|
||||
urlParams.link_url = data.url.replace('mailto:', '');
|
||||
} else if (data.url.startsWith('#')) {
|
||||
url = global.chooserUrls.anchorLinkChooser;
|
||||
urlParams.link_url = data.url.replace('#', '');
|
||||
} else {
|
||||
url = global.chooserUrls.externalLinkChooser;
|
||||
urlParams.link_url = data.url;
|
||||
|
|
|
@ -144,6 +144,14 @@ describe('ModalWorkflowSource', () => {
|
|||
})).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('anchor', () => {
|
||||
expect(filterEntityData({ type: 'LINK' }, {
|
||||
prefer_this_title_as_link_text: false,
|
||||
title: 'testanchor',
|
||||
url: '#testanchor',
|
||||
})).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('external', () => {
|
||||
expect(filterEntityData({ type: 'LINK' }, {
|
||||
prefer_this_title_as_link_text: false,
|
||||
|
|
|
@ -28,6 +28,12 @@ Object {
|
|||
}
|
||||
`;
|
||||
|
||||
exports[`ModalWorkflowSource #filterEntityData LINK anchor 1`] = `
|
||||
Object {
|
||||
"url": "#testanchor",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`ModalWorkflowSource #filterEntityData LINK external 1`] = `
|
||||
Object {
|
||||
"url": "https://www.example.com/",
|
||||
|
@ -55,6 +61,7 @@ Object {
|
|||
},
|
||||
"url": "/admin/choose-external-link/",
|
||||
"urlParams": Object {
|
||||
"allow_anchor_link": true,
|
||||
"allow_email_link": true,
|
||||
"allow_external_link": true,
|
||||
"link_text": "",
|
||||
|
@ -71,6 +78,7 @@ Object {
|
|||
},
|
||||
"url": "/admin/choose-email-link/",
|
||||
"urlParams": Object {
|
||||
"allow_anchor_link": true,
|
||||
"allow_email_link": true,
|
||||
"allow_external_link": true,
|
||||
"link_text": "",
|
||||
|
@ -87,6 +95,7 @@ Object {
|
|||
},
|
||||
"url": "/admin/choose-page/",
|
||||
"urlParams": Object {
|
||||
"allow_anchor_link": true,
|
||||
"allow_email_link": true,
|
||||
"allow_external_link": true,
|
||||
"link_text": "",
|
||||
|
@ -102,6 +111,7 @@ Object {
|
|||
},
|
||||
"url": "/admin/choose-page/1/",
|
||||
"urlParams": Object {
|
||||
"allow_anchor_link": true,
|
||||
"allow_email_link": true,
|
||||
"allow_external_link": true,
|
||||
"link_text": "",
|
||||
|
@ -117,6 +127,7 @@ Object {
|
|||
},
|
||||
"url": "/admin/choose-page/",
|
||||
"urlParams": Object {
|
||||
"allow_anchor_link": true,
|
||||
"allow_email_link": true,
|
||||
"allow_external_link": true,
|
||||
"link_text": "",
|
||||
|
|
|
@ -55,6 +55,7 @@ global.wagtail = {};
|
|||
global.chooserUrls = {
|
||||
documentChooser: '/admin/documents/chooser/',
|
||||
emailLinkChooser: '/admin/choose-email-link/',
|
||||
anchorLinkChooser: '/admin/choose-anchor-link',
|
||||
embedsChooser: '/admin/embeds/chooser/',
|
||||
externalLinkChooser: '/admin/choose-external-link/',
|
||||
imageChooser: '/admin/images/chooser/',
|
||||
|
|
|
@ -22,6 +22,7 @@ Other features
|
|||
* Added more detailed documentation and troubleshooting for installing OpenCV for feature detection (Daniele Procida)
|
||||
* Move and refactor upgrade notification JS (Jonny Scholes)
|
||||
* Remove need for Elasticsearch ``update_all_types`` workaround, upgrade minimum release to 6.4.0 or above (Jonathan Liuti)
|
||||
* Add ability to insert internal anchor links/links with fragment identifiers in Draftail (rich text) fields (Iman Syed)
|
||||
|
||||
|
||||
Bug fixes
|
||||
|
|
|
@ -27,7 +27,12 @@ class URLOrAbsolutePathField(forms.URLField):
|
|||
|
||||
|
||||
class ExternalLinkChooserForm(forms.Form):
|
||||
url = URLOrAbsolutePathField(required=True, label=ugettext_lazy("URL"))
|
||||
url = URLOrAbsolutePathField(required=True, label=ugettext_lazy(""))
|
||||
link_text = forms.CharField(required=False)
|
||||
|
||||
|
||||
class AnchorLinkChooserForm(forms.Form):
|
||||
url = forms.CharField(required=True, label=ugettext_lazy("#"))
|
||||
link_text = forms.CharField(required=False)
|
||||
|
||||
|
||||
|
|
|
@ -315,7 +315,8 @@ class HtmlToContentStateHandler(HTMLParser):
|
|||
element_handler.handle_endtag(name, self.state, self.contentstate)
|
||||
|
||||
def handle_data(self, content):
|
||||
# normalise whitespace sequences to a single space
|
||||
# normalise whitespace sequences to a single space unless whitespace is contained in <pre> tag,
|
||||
# in which case, leave it alone
|
||||
# This is in line with https://www.w3.org/TR/html4/struct/text.html#h-9.1
|
||||
content = re.sub(WHITESPACE_RE, ' ', content)
|
||||
|
||||
|
@ -341,7 +342,6 @@ class HtmlToContentStateHandler(HTMLParser):
|
|||
content = content.lstrip()
|
||||
elif self.state.leading_whitespace == FORCE_WHITESPACE and not content.startswith(' '):
|
||||
content = ' ' + content
|
||||
|
||||
if content.endswith(' '):
|
||||
# don't output trailing whitespace yet, because we want to discard it if the end
|
||||
# of the block follows. Instead, we'll set leading_whitespace = force so that
|
||||
|
|
|
@ -37,7 +37,8 @@
|
|||
url = window.chooserUrls.pageChooser;
|
||||
urlParams = {
|
||||
'allow_external_link': true,
|
||||
'allow_email_link': true
|
||||
'allow_email_link': true,
|
||||
'allow_anchor_link': true,
|
||||
};
|
||||
|
||||
enclosingLink = getEnclosingLink();
|
||||
|
@ -56,6 +57,10 @@
|
|||
url = window.chooserUrls.emailLinkChooser;
|
||||
href = href.replace('mailto:', '');
|
||||
urlParams['link_url'] = href;
|
||||
} else if (href.startsWith('#')) {
|
||||
url = window.chooserUrls.anchorLinkChooser;
|
||||
href = href.replace('#', '');
|
||||
urlParams['link_url'] = href;
|
||||
} else if (!linkType) { /* external link */
|
||||
url = window.chooserUrls.externalLinkChooser;
|
||||
urlParams['link_url'] = href;
|
||||
|
|
|
@ -110,6 +110,18 @@ PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS = {
|
|||
*/
|
||||
$('#id_q', modal.body).trigger('focus');
|
||||
},
|
||||
|
||||
'anchor_link': function(modal, jsonData) {
|
||||
$('p.link-types a', modal.body).on('click', function() {
|
||||
modal.loadUrl(this.href);
|
||||
return false;
|
||||
});
|
||||
|
||||
$('form', modal.body).on('submit', function() {
|
||||
modal.postForm(this.action, $(this).serialize());
|
||||
return false;
|
||||
});
|
||||
},
|
||||
'email_link': function(modal, jsonData) {
|
||||
$('p.link-types a', modal.body).on('click', function() {
|
||||
modal.loadUrl(this.href);
|
||||
|
@ -135,5 +147,5 @@ PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS = {
|
|||
'external_link_chosen': function(modal, jsonData) {
|
||||
modal.respond('pageChosen', jsonData['result']);
|
||||
modal.close();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% load i18n wagtailadmin_tags %}
|
||||
{% if allow_external_link or allow_email_link or current == 'external' or current == 'email' %}
|
||||
{% if allow_external_link or allow_email_link or allow_anchor_link or current == 'external' or current == 'email' or current == 'anchor' %}
|
||||
<p class="link-types">
|
||||
{% if current == 'internal' %}
|
||||
<b>{% trans "Internal link" %}</b>
|
||||
|
@ -22,5 +22,11 @@
|
|||
{% elif allow_email_link %}
|
||||
| <a href="{% url 'wagtailadmin_choose_page_email_link' %}{% querystring p=None parent_page_id=parent_page_id %}">{% trans "Email link" %}</a>
|
||||
{% endif %}
|
||||
|
||||
{% if current == 'anchor' %}
|
||||
| <b>{% trans "Anchor link" %}</b>
|
||||
{% elif allow_anchor_link %}
|
||||
| <a href="{% url 'wagtailadmin_choose_page_anchor_link' %}{% querystring p=None parent_page_id=parent_page_id %}">{% trans "Anchor link" %}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{% load i18n wagtailadmin_tags %}
|
||||
{% trans "Add an anchor link" as anchor_str %}
|
||||
{% include "wagtailadmin/shared/header.html" with title=anchor_str %}
|
||||
|
||||
<div class="nice-padding">
|
||||
{% include 'wagtailadmin/chooser/_link_types.html' with current='anchor' %}
|
||||
|
||||
<form action="{% url 'wagtailadmin_choose_page_anchor_link' %}{% querystring %}" method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
<ul class="fields">
|
||||
{% for field in form %}
|
||||
{% include "wagtailadmin/shared/field_as_li.html" %}
|
||||
{% endfor %}
|
||||
<li><input type="submit" value="{% trans 'Insert anchor' %}" class="button" /></li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
|
@ -9,7 +9,8 @@
|
|||
window.chooserUrls = {
|
||||
'pageChooser': '{% url "wagtailadmin_choose_page" %}',
|
||||
'externalLinkChooser': '{% url "wagtailadmin_choose_page_external_link" %}',
|
||||
'emailLinkChooser': '{% url "wagtailadmin_choose_page_email_link" %}'
|
||||
'emailLinkChooser': '{% url "wagtailadmin_choose_page_email_link" %}',
|
||||
'anchorLinkChooser': '{% url "wagtailadmin_choose_page_anchor_link" %}',
|
||||
};
|
||||
window.unicodeSlugsEnabled = {% if unicode_slugs_enabled %}true{% else %}false{% endif %};
|
||||
</script>
|
||||
|
|
|
@ -572,6 +572,66 @@ class TestChooserExternalLink(TestCase, WagtailTestUtils):
|
|||
self.assertEqual(response_json['result']['title'], "admin")
|
||||
|
||||
|
||||
class TestChooserAnchorLink(TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
self.login()
|
||||
|
||||
def get(self, params={}):
|
||||
return self.client.get(reverse('wagtailadmin_choose_page_anchor_link'), params)
|
||||
|
||||
def post(self, post_data={}, url_params={}):
|
||||
url = reverse('wagtailadmin_choose_page_anchor_link')
|
||||
if url_params:
|
||||
url += '?' + urlencode(url_params)
|
||||
return self.client.post(url, post_data)
|
||||
|
||||
def test_simple(self):
|
||||
response = self.get()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, 'wagtailadmin/chooser/anchor_link.html')
|
||||
|
||||
def test_prepopulated_form(self):
|
||||
response = self.get({'link_text': 'Example Anchor Text', 'link_url': 'exampleanchor'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'Example Anchor Text')
|
||||
self.assertContains(response, 'exampleanchor')
|
||||
|
||||
def test_create_link(self):
|
||||
response = self.post({'anchor-link-chooser-url': 'exampleanchor', 'anchor-link-chooser-link_text': 'Example Anchor Text'})
|
||||
result = json.loads(response.content.decode())['result']
|
||||
self.assertEqual(result['url'], "#exampleanchor")
|
||||
self.assertEqual(result['title'], "Example Anchor Text") # When link text is given, it is used
|
||||
self.assertEqual(result['prefer_this_title_as_link_text'], True)
|
||||
|
||||
def test_create_link_without_text(self):
|
||||
response = self.post({'anchor-link-chooser-url': 'exampleanchor'})
|
||||
result = json.loads(response.content.decode())['result']
|
||||
self.assertEqual(result['url'], "#exampleanchor")
|
||||
self.assertEqual(result['title'], "exampleanchor") # When no link text is given, it uses anchor
|
||||
self.assertEqual(result['prefer_this_title_as_link_text'], False)
|
||||
|
||||
def test_notice_changes_to_link_text(self):
|
||||
response = self.post(
|
||||
{'anchor-link-chooser-url': 'exampleanchor2', 'email-link-chooser-link_text': 'Example Text'}, # POST data
|
||||
{'link_url': 'exampleanchor2', 'link_text': 'Example Text'} # GET params - initial data
|
||||
)
|
||||
result = json.loads(response.content.decode())['result']
|
||||
self.assertEqual(result['url'], "#exampleanchor2")
|
||||
self.assertEqual(result['title'], "exampleanchor2")
|
||||
# no change to link text, so prefer the existing link/selection content where available
|
||||
self.assertEqual(result['prefer_this_title_as_link_text'], True)
|
||||
|
||||
response = self.post(
|
||||
{'anchor-link-chooser-url': 'exampleanchor2', 'anchor-link-chooser-link_text': 'Example Anchor Test 2.1'}, # POST data
|
||||
{'link_url': 'exampleanchor', 'link_text': 'Example Anchor Text'} # GET params - initial data
|
||||
)
|
||||
result = json.loads(response.content.decode())['result']
|
||||
self.assertEqual(result['url'], "#exampleanchor2")
|
||||
self.assertEqual(result['title'], "Example Anchor Test 2.1")
|
||||
# link text has changed, so tell the caller to use it
|
||||
self.assertEqual(result['prefer_this_title_as_link_text'], True)
|
||||
|
||||
|
||||
class TestChooserEmailLink(TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
self.login()
|
||||
|
|
|
@ -37,6 +37,7 @@ urlpatterns = [
|
|||
url(r'^choose-page/search/$', chooser.search, name='wagtailadmin_choose_page_search'),
|
||||
url(r'^choose-external-link/$', chooser.external_link, name='wagtailadmin_choose_page_external_link'),
|
||||
url(r'^choose-email-link/$', chooser.email_link, name='wagtailadmin_choose_page_email_link'),
|
||||
url(r'^choose-anchor-link/$', chooser.anchor_link, name='wagtailadmin_choose_page_anchor_link'),
|
||||
|
||||
url(r'^tag-autocomplete/$', tags.autocomplete, name='wagtailadmin_tag_autocomplete'),
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ from django.core.paginator import Paginator
|
|||
from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
|
||||
from wagtail.admin.forms.choosers import EmailLinkChooserForm, ExternalLinkChooserForm
|
||||
from wagtail.admin.forms.choosers import (
|
||||
AnchorLinkChooserForm, EmailLinkChooserForm, ExternalLinkChooserForm)
|
||||
from wagtail.admin.forms.search import SearchForm
|
||||
from wagtail.admin.modal_workflow import render_modal_workflow
|
||||
from wagtail.core import hooks
|
||||
|
@ -12,13 +13,14 @@ from wagtail.core.utils import resolve_model_string
|
|||
|
||||
def shared_context(request, extra_context=None):
|
||||
context = {
|
||||
# parent_page ID is passed as a GET parameter on the external_link and email_link views
|
||||
# parent_page ID is passed as a GET parameter on the external_link, anchor_link and mail_link views
|
||||
# so that it's remembered when browsing from 'Internal link' to another link type
|
||||
# and back again. On the 'browse' / 'internal link' view this will be overridden to be
|
||||
# sourced from the standard URL path parameter instead.
|
||||
'parent_page_id': request.GET.get('parent_page_id'),
|
||||
'allow_external_link': request.GET.get('allow_external_link'),
|
||||
'allow_email_link': request.GET.get('allow_email_link'),
|
||||
'allow_anchor_link': request.GET.get('allow_anchor_link'),
|
||||
}
|
||||
if extra_context:
|
||||
context.update(extra_context)
|
||||
|
@ -222,6 +224,37 @@ def external_link(request):
|
|||
)
|
||||
|
||||
|
||||
def anchor_link(request):
|
||||
initial_data = {
|
||||
'link_text': request.GET.get('link_text', ''),
|
||||
'url': request.GET.get('link_url', ''),
|
||||
}
|
||||
|
||||
if request.method == 'POST':
|
||||
form = AnchorLinkChooserForm(request.POST, initial=initial_data, prefix='anchor-link-chooser')
|
||||
|
||||
if form.is_valid():
|
||||
result = {
|
||||
'url': '#' + form.cleaned_data['url'],
|
||||
'title': form.cleaned_data['link_text'].strip() or form.cleaned_data['url'],
|
||||
'prefer_this_title_as_link_text': ('link_text' in form.changed_data),
|
||||
}
|
||||
return render_modal_workflow(
|
||||
request, None, None,
|
||||
None, json_data={'step': 'external_link_chosen', 'result': result}
|
||||
)
|
||||
else:
|
||||
form = AnchorLinkChooserForm(initial=initial_data, prefix='anchor-link-chooser')
|
||||
|
||||
return render_modal_workflow(
|
||||
request,
|
||||
'wagtailadmin/chooser/anchor_link.html', None,
|
||||
shared_context(request, {
|
||||
'form': form,
|
||||
}), json_data={'step': 'anchor_link'}
|
||||
)
|
||||
|
||||
|
||||
def email_link(request):
|
||||
initial_data = {
|
||||
'link_text': request.GET.get('link_text', ''),
|
||||
|
|
|
@ -66,6 +66,8 @@ class LinkRewriter:
|
|||
link_type = 'external'
|
||||
elif href.startswith('mailto:'):
|
||||
link_type = 'email'
|
||||
elif href.startswith('#'):
|
||||
link_type = 'anchor'
|
||||
|
||||
if not link_type:
|
||||
# otherwise return ordinary links without a linktype unchanged
|
||||
|
@ -74,7 +76,7 @@ class LinkRewriter:
|
|||
try:
|
||||
rule = self.link_rules[link_type]
|
||||
except KeyError:
|
||||
if link_type in ['email', 'external']:
|
||||
if link_type in ['email', 'external', 'anchor']:
|
||||
# If no rule is registered for supported types
|
||||
# return ordinary links without a linktype unchanged
|
||||
return match.group(0)
|
||||
|
|
|
@ -108,11 +108,13 @@ class TestLinkRewriterTagReplacing(TestCase):
|
|||
self.assertEqual(page_type_link, '<a href="/article/3">')
|
||||
|
||||
# but it should also be able to handle other supported
|
||||
# link types (email, external) even if no rules is provided
|
||||
# link types (email, external, anchor) even if no rules is provided
|
||||
external_type_link = rewriter('<a href="https://wagtail.io/">')
|
||||
self.assertEqual(external_type_link, '<a href="https://wagtail.io/">')
|
||||
email_type_link = rewriter('<a href="mailto:test@wagtail.io">')
|
||||
self.assertEqual(email_type_link, '<a href="mailto:test@wagtail.io">')
|
||||
anchor_type_link = rewriter('<a href="#test">')
|
||||
self.assertEqual(anchor_type_link, '<a href="#test">')
|
||||
|
||||
# As well as link which don't have any linktypes
|
||||
link_without_linktype = rewriter('<a data-link="https://wagtail.io">')
|
||||
|
@ -131,6 +133,7 @@ class TestLinkRewriterTagReplacing(TestCase):
|
|||
'page': lambda attrs: '<a href="/article/{}">'.format(attrs['id']),
|
||||
'external': lambda attrs: '<a rel="nofollow" href="{}">'.format(attrs['href']),
|
||||
'email': lambda attrs: '<a data-email="true" href="{}">'.format(attrs['href']),
|
||||
'anchor': lambda attrs: '<a data-anchor="true" href="{}">'.format(attrs['href']),
|
||||
'custom': lambda attrs: '<a data-phone="true" href="{}">'.format(attrs['href']),
|
||||
}
|
||||
rewriter = LinkRewriter(rules)
|
||||
|
@ -146,6 +149,8 @@ class TestLinkRewriterTagReplacing(TestCase):
|
|||
self.assertEqual(external_type_link_http, '<a rel="nofollow" href="http://wagtail.io/">')
|
||||
email_type_link = rewriter('<a href="mailto:test@wagtail.io">')
|
||||
self.assertEqual(email_type_link, '<a data-email="true" href="mailto:test@wagtail.io">')
|
||||
anchor_type_link = rewriter('<a href="#test">')
|
||||
self.assertEqual(anchor_type_link, '<a data-anchor="true" href="#test">')
|
||||
|
||||
# But not the unsupported ones.
|
||||
link_with_no_linktype = rewriter('<a href="tel:+4917640206387">')
|
||||
|
|
Ładowanie…
Reference in New Issue