kopia lustrzana https://github.com/wagtail/wagtail
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.pull/2564/merge
rodzic
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)
|
||||
|
||||
|
||||
|
|
Plik diff jest za duży
Load Diff
Plik binarny nie jest wyświetlany.
File diff suppressed because one or more lines are too long
Przed Szerokość: | Wysokość: | Rozmiar: 65 KiB Po Szerokość: | Wysokość: | Rozmiar: 68 KiB |
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -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,
|
||||
|
|
Ładowanie…
Reference in New Issue