mirror of
https://github.com/wagtail/wagtail.git
synced 2024-12-01 11:41:20 +01:00
Add ability to edit links in RichText editor
Instead of having the link button to remove an existing link, it will open the chooser on the corresponding tab (page, link, email) and pre-populate the form. Therefore, a second button has been added to remove a link.
This commit is contained in:
parent
54e9e9c918
commit
32c68b5060
@ -55,19 +55,11 @@ class SearchForm(forms.Form):
|
||||
|
||||
class ExternalLinkChooserForm(forms.Form):
|
||||
url = URLOrAbsolutePathField(required=True, label=ugettext_lazy("URL"))
|
||||
|
||||
|
||||
class ExternalLinkChooserWithLinkTextForm(forms.Form):
|
||||
url = URLOrAbsolutePathField(required=True, label=ugettext_lazy("URL"))
|
||||
link_text = forms.CharField(required=True)
|
||||
link_text = forms.CharField(required=False)
|
||||
|
||||
|
||||
class EmailLinkChooserForm(forms.Form):
|
||||
email_address = forms.EmailField(required=True)
|
||||
|
||||
|
||||
class EmailLinkChooserWithLinkTextForm(forms.Form):
|
||||
email_address = forms.EmailField(required=True)
|
||||
link_text = forms.CharField(required=False)
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 68 KiB |
Binary file not shown.
Binary file not shown.
@ -7,7 +7,7 @@
|
||||
editable: null
|
||||
},
|
||||
populateToolbar: function(toolbar) {
|
||||
var button, getEnclosingLink, widget;
|
||||
var buttonSet, addButton, cancelButton, getEnclosingLink, widget;
|
||||
|
||||
widget = this;
|
||||
getEnclosingLink = function() {
|
||||
@ -17,61 +17,135 @@
|
||||
return $(node).parents('a').get(0);
|
||||
};
|
||||
|
||||
button = $('<span class="' + this.widgetName + '"></span>');
|
||||
button.hallobutton({
|
||||
uuid: this.options.uuid,
|
||||
editable: this.options.editable,
|
||||
label: 'Links',
|
||||
buttonSet = $('<span class="' + this.widgetName + '"></span>');
|
||||
|
||||
addButton = $('<span></span>');
|
||||
addButton = addButton.hallobutton({
|
||||
uuid: widget.options.uuid,
|
||||
editable: widget.options.editable,
|
||||
label: 'Add/Edit Link',
|
||||
icon: 'icon-link',
|
||||
command: null,
|
||||
queryState: function(event) {
|
||||
return button.hallobutton('checked', !!getEnclosingLink());
|
||||
return addButton.hallobutton('checked', !!getEnclosingLink());
|
||||
}
|
||||
});
|
||||
addButton.on('click', function() {
|
||||
var enclosingLink, lastSelection, url, urlParams, href, pageId;
|
||||
|
||||
toolbar.append(button);
|
||||
return button.on('click', function(event) {
|
||||
var enclosingLink, lastSelection, url;
|
||||
// Defaults.
|
||||
url = window.chooserUrls.pageChooser;
|
||||
urlParams = {
|
||||
'allow_external_link': true,
|
||||
'allow_email_link': true
|
||||
};
|
||||
|
||||
enclosingLink = getEnclosingLink();
|
||||
lastSelection = widget.options.editable.getSelection();
|
||||
|
||||
if (enclosingLink) {
|
||||
href = enclosingLink.getAttribute('href');
|
||||
pageId = enclosingLink.getAttribute('data-id');
|
||||
|
||||
urlParams['link_text'] = enclosingLink.innerText;
|
||||
|
||||
if (pageId) {
|
||||
// TODO: Actually show the parent not the page itself.
|
||||
url = window.chooserUrls.pageChooser + pageId.toString() + '/';
|
||||
} else if (href.startsWith('mailto:')) {
|
||||
url = window.chooserUrls.emailLinkChooser;
|
||||
href = href.replace('mailto:', '');
|
||||
urlParams['link_url'] = href;
|
||||
} else {
|
||||
url = window.chooserUrls.externalLinkChooser;
|
||||
urlParams['link_url'] = href;
|
||||
}
|
||||
} else if (!lastSelection.collapsed) {
|
||||
urlParams['link_text'] = lastSelection.toString();
|
||||
}
|
||||
|
||||
return ModalWorkflow({
|
||||
url: url,
|
||||
urlParams: urlParams,
|
||||
responses: {
|
||||
pageChosen: function(pageData) {
|
||||
var a, text;
|
||||
|
||||
// Create link
|
||||
a = document.createElement('a');
|
||||
a.setAttribute('href', pageData.url);
|
||||
if (pageData.id) {
|
||||
a.setAttribute('data-id', pageData.id);
|
||||
a.setAttribute('data-linktype', 'page');
|
||||
}
|
||||
|
||||
if (pageData.id) {
|
||||
// If it's a link to an internal page, `pageData.title` will not use the link_text
|
||||
// like external and email responses do, overwriting selection text :(
|
||||
if (!lastSelection.collapsed) {
|
||||
text = lastSelection.toString();
|
||||
} else if (enclosingLink) {
|
||||
text = enclosingLink.innerHTML;
|
||||
}
|
||||
else {
|
||||
text = pageData.title;
|
||||
}
|
||||
} else {
|
||||
text = pageData.title;
|
||||
}
|
||||
a.appendChild(document.createTextNode(text));
|
||||
|
||||
// Remove existing nodes
|
||||
if (enclosingLink && enclosingLink.parentNode) {
|
||||
enclosingLink.parentNode.removeChild(enclosingLink);
|
||||
}
|
||||
lastSelection.deleteContents();
|
||||
|
||||
// Add new node
|
||||
lastSelection.insertNode(a);
|
||||
|
||||
return widget.options.editable.element.trigger('change');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
buttonSet.append(addButton);
|
||||
|
||||
cancelButton = $('<span></span>');
|
||||
cancelButton = cancelButton.hallobutton({
|
||||
uuid: widget.options.uuid,
|
||||
editable: widget.options.editable,
|
||||
label: 'Remove Link',
|
||||
icon: 'icon-chain-broken',
|
||||
command: null,
|
||||
queryState: function(event) {
|
||||
if (!!getEnclosingLink()) {
|
||||
return cancelButton.hallobutton('enable');
|
||||
} else {
|
||||
return cancelButton.hallobutton('disable');
|
||||
}
|
||||
}
|
||||
});
|
||||
cancelButton.on('click', function() {
|
||||
var enclosingLink, sel, range;
|
||||
|
||||
enclosingLink = getEnclosingLink();
|
||||
if (enclosingLink) {
|
||||
$(enclosingLink).replaceWith(enclosingLink.innerHTML);
|
||||
button.hallobutton('checked', false);
|
||||
return widget.options.editable.element.trigger('change');
|
||||
} else {
|
||||
lastSelection = widget.options.editable.getSelection();
|
||||
if (lastSelection.collapsed) {
|
||||
url = window.chooserUrls.pageChooser + '?allow_external_link=true&allow_email_link=true&prompt_for_link_text=true';
|
||||
} else {
|
||||
url = window.chooserUrls.pageChooser + '?allow_external_link=true&allow_email_link=true';
|
||||
}
|
||||
sel = rangy.getSelection();
|
||||
range = sel.getRangeAt(0);
|
||||
|
||||
return ModalWorkflow({
|
||||
url: url,
|
||||
responses: {
|
||||
pageChosen: function(pageData) {
|
||||
var a;
|
||||
range.setStartBefore(sel.anchorNode.parentNode);
|
||||
range.setEndAfter(sel.anchorNode.parentNode);
|
||||
|
||||
a = document.createElement('a');
|
||||
a.setAttribute('href', pageData.url);
|
||||
if (pageData.id) {
|
||||
a.setAttribute('data-id', pageData.id);
|
||||
a.setAttribute('data-linktype', 'page');
|
||||
}
|
||||
sel.setSingleRange(range, false);
|
||||
|
||||
if ((!lastSelection.collapsed) && lastSelection.canSurroundContents()) {
|
||||
lastSelection.surroundContents(a);
|
||||
} else {
|
||||
a.appendChild(document.createTextNode(pageData.title));
|
||||
lastSelection.insertNode(a);
|
||||
}
|
||||
|
||||
return widget.options.editable.element.trigger('change');
|
||||
}
|
||||
}
|
||||
});
|
||||
document.execCommand('unlink');
|
||||
}
|
||||
});
|
||||
buttonSet.append(cancelButton);
|
||||
|
||||
buttonSet.hallobuttonset();
|
||||
toolbar.append(buttonSet);
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
||||
|
@ -72,7 +72,8 @@ $icons: (
|
||||
'title': '\f034',
|
||||
'code': '\e601',
|
||||
'openquote': '“',
|
||||
'horizontalrule': '\2014'
|
||||
'horizontalrule': '\2014',
|
||||
'chain-broken': '\e900'
|
||||
);
|
||||
|
||||
$icons-after: (
|
||||
|
@ -6,7 +6,9 @@
|
||||
|
||||
<script>
|
||||
window.chooserUrls = {
|
||||
'pageChooser': '{% url "wagtailadmin_choose_page" %}'
|
||||
'pageChooser': '{% url "wagtailadmin_choose_page" %}',
|
||||
'externalLinkChooser': '{% url "wagtailadmin_choose_page_external_link" %}',
|
||||
'emailLinkChooser': '{% url "wagtailadmin_choose_page_email_link" %}'
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -313,27 +313,34 @@ class TestChooserExternalLink(TestCase, WagtailTestUtils):
|
||||
self.assertTemplateUsed(response, 'wagtailadmin/chooser/external_link.html')
|
||||
|
||||
def test_get_with_param(self):
|
||||
self.assertEqual(self.get({'prompt_for_link_text': 'foo'}).status_code, 200)
|
||||
self.assertEqual(self.get({'link_text': 'foo'}).status_code, 200)
|
||||
|
||||
def test_create_link(self):
|
||||
response = self.post({'url': 'http://www.example.com/', 'link_text': 'example'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "'onload'") # indicates success / post back to calling page
|
||||
self.assertContains(response, "'url': 'http://www.example.com/'")
|
||||
self.assertContains(response, "'title': 'example'") # When link text is given, it is used
|
||||
|
||||
def test_create_link_without_text(self):
|
||||
response = self.post({'url': 'http://www.example.com/'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "'onload'") # indicates success / post back to calling page
|
||||
self.assertContains(response, "'url': 'http://www.example.com/',")
|
||||
self.assertContains(response, "'title': 'http://www.example.com/'")
|
||||
self.assertContains(response, "'url': 'http://www.example.com/'")
|
||||
self.assertContains(response, "'title': 'http://www.example.com/'") # When no text is given, it uses the url
|
||||
|
||||
def test_invalid_url(self):
|
||||
response = self.post({'url': 'ntp://www.example.com'})
|
||||
response = self.post({'url': 'ntp://www.example.com', 'link_text': 'example'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "'html'") # indicates failure / show error message
|
||||
self.assertContains(response, "Enter a valid URL.")
|
||||
|
||||
def test_allow_local_url(self):
|
||||
response = self.post({'url': '/admin/'})
|
||||
response = self.post({'url': '/admin/', 'link_text': 'admin'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "'onload'") # indicates success / post back to calling page
|
||||
self.assertContains(response, "'url': '/admin/',")
|
||||
self.assertContains(response, "'title': '/admin/'")
|
||||
self.assertContains(response, "'title': 'admin'")
|
||||
|
||||
|
||||
class TestChooserEmailLink(TestCase, WagtailTestUtils):
|
||||
@ -352,9 +359,14 @@ class TestChooserEmailLink(TestCase, WagtailTestUtils):
|
||||
self.assertTemplateUsed(response, 'wagtailadmin/chooser/email_link.html')
|
||||
|
||||
def test_get_with_param(self):
|
||||
self.assertEqual(self.get({'prompt_for_link_text': 'foo'}).status_code, 200)
|
||||
self.assertEqual(self.get({'link_text': 'foo'}).status_code, 200)
|
||||
|
||||
def test_create_link(self):
|
||||
request = self.post({'email_address': 'example@example.com', 'link_text': 'contact'})
|
||||
self.assertContains(request, "'url': 'mailto:example@example.com',")
|
||||
self.assertContains(request, "'title': 'contact'") # When link text is given, it is used
|
||||
|
||||
def test_create_link_without_text(self):
|
||||
request = self.post({'email_address': 'example@example.com'})
|
||||
self.assertContains(request, "'url': 'mailto:example@example.com',")
|
||||
self.assertContains(request, "'title': 'example@example.com'")
|
||||
self.assertContains(request, "'title': 'example@example.com'") # When no link text is given, it uses the email
|
||||
|
@ -4,9 +4,7 @@ from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
|
||||
from wagtail.utils.pagination import paginate
|
||||
from wagtail.wagtailadmin.forms import (
|
||||
EmailLinkChooserForm, EmailLinkChooserWithLinkTextForm, ExternalLinkChooserForm,
|
||||
ExternalLinkChooserWithLinkTextForm, SearchForm)
|
||||
from wagtail.wagtailadmin.forms import EmailLinkChooserForm, ExternalLinkChooserForm, SearchForm
|
||||
from wagtail.wagtailadmin.modal_workflow import render_modal_workflow
|
||||
from wagtail.wagtailcore.models import Page
|
||||
from wagtail.wagtailcore.utils import resolve_model_string
|
||||
@ -144,26 +142,23 @@ def search(request, parent_page_id=None):
|
||||
|
||||
|
||||
def external_link(request):
|
||||
prompt_for_link_text = bool(request.GET.get('prompt_for_link_text'))
|
||||
|
||||
if prompt_for_link_text:
|
||||
form_class = ExternalLinkChooserWithLinkTextForm
|
||||
else:
|
||||
form_class = ExternalLinkChooserForm
|
||||
link_text = request.GET.get('link_text', '')
|
||||
link_url = request.GET.get('link_url', '')
|
||||
|
||||
if request.method == 'POST':
|
||||
form = form_class(request.POST)
|
||||
form = ExternalLinkChooserForm(request.POST)
|
||||
|
||||
if form.is_valid():
|
||||
return render_modal_workflow(
|
||||
request,
|
||||
None, 'wagtailadmin/chooser/external_link_chosen.js',
|
||||
{
|
||||
'url': form.cleaned_data['url'],
|
||||
'link_text': form.cleaned_data['link_text'] if prompt_for_link_text else form.cleaned_data['url']
|
||||
'link_text': form.cleaned_data['link_text'].strip() or form.cleaned_data['url']
|
||||
}
|
||||
)
|
||||
else:
|
||||
form = form_class()
|
||||
form = ExternalLinkChooserForm(initial={'url': link_url, 'link_text': link_text})
|
||||
|
||||
return render_modal_workflow(
|
||||
request,
|
||||
@ -175,28 +170,23 @@ def external_link(request):
|
||||
|
||||
|
||||
def email_link(request):
|
||||
prompt_for_link_text = bool(request.GET.get('prompt_for_link_text'))
|
||||
|
||||
if prompt_for_link_text:
|
||||
form_class = EmailLinkChooserWithLinkTextForm
|
||||
else:
|
||||
form_class = EmailLinkChooserForm
|
||||
link_text = request.GET.get('link_text', '')
|
||||
link_url = request.GET.get('link_url', '')
|
||||
|
||||
if request.method == 'POST':
|
||||
form = form_class(request.POST)
|
||||
form = EmailLinkChooserForm(request.POST)
|
||||
|
||||
if form.is_valid():
|
||||
return render_modal_workflow(
|
||||
request,
|
||||
None, 'wagtailadmin/chooser/external_link_chosen.js',
|
||||
{
|
||||
'url': 'mailto:' + form.cleaned_data['email_address'],
|
||||
'link_text': form.cleaned_data['link_text'] if (
|
||||
prompt_for_link_text and form.cleaned_data['link_text']
|
||||
) else form.cleaned_data['email_address']
|
||||
'link_text': form.cleaned_data['link_text'].strip() or form.cleaned_data['email_address']
|
||||
}
|
||||
)
|
||||
else:
|
||||
form = form_class()
|
||||
form = EmailLinkChooserForm(initial={'email_address': link_url, 'link_text': link_text})
|
||||
|
||||
return render_modal_workflow(
|
||||
request,
|
||||
|
Loading…
Reference in New Issue
Block a user