Mercurial > moin > 1.9
changeset 5312:fd6e62d305a6
Improved attachment dialog for GUI editor.
New features:
- attachments for current page are listed in a listbox
- attachments for a different page can be listed in a listbox
- page and attachment can be entered in separated edit fields
- i18n support
- tested for being robust against XSS issues
author | Josef Meier <jo.meier@gmx.de> |
---|---|
date | Sat, 28 Nov 2009 13:20:44 +0100 |
parents | af42eec12f38 |
children | 37b22f678801 |
files | MoinMoin/action/fckdialog.py MoinMoin/web/static/htdocs/applets/moinFCKplugins/moinattachment/fck_attachment.js MoinMoin/web/static/htdocs/applets/moinFCKplugins/moinattachment/fckplugin.js |
diffstat | 3 files changed, 253 insertions(+), 128 deletions(-) [+] |
line wrap: on
line diff
--- a/MoinMoin/action/fckdialog.py Sat Nov 21 16:31:36 2009 +0100 +++ b/MoinMoin/action/fckdialog.py Sat Nov 28 13:20:44 2009 +0100 @@ -7,6 +7,8 @@ """ from MoinMoin import config, wikiutil +from MoinMoin.action.AttachFile import _get_files +from MoinMoin.Page import Page import re ############################################################################## @@ -357,38 +359,51 @@ </html> ''' % locals()) -############################################################################## -### Attachment dialog -############################################################################## def attachment_dialog(request): - # list of wiki pages - name = request.values.get("pagename", "") - if name: - from MoinMoin import search - # XXX error handling! - searchresult = search.searchPages(request, 't:"%s"' % name) - - pages = [p.page_name for p in searchresult.hits] - pages.sort() - pages[0:0] = [name] - page_list = ''' - <tr> - <td colspan=2> - <select id="sctPagename" size="1" onchange="OnChangePagename(this.value);"> - %s - </select> - <td> - </tr> -''' % "\n".join(['<option value="%s">%s</option>' % (page, page) - for page in pages]) - else: - page_list = "" + """ Attachment dialog for GUI editor. """ + """ Features: This dialog can... """ + """ - list attachments in a drop down list """ + """ - list attachments also for a different page than the current one """ + """ - create new attachment """ + _ = request.getText + url_prefix_static = request.cfg.url_prefix_static # wiki url - url_prefix_static = request.cfg.url_prefix_static - scriptname = request.script_root + '/' - action = scriptname + action = request.script_root + "/" + + # The following code lines implement the feature "list attachments for a different page". + # Meaning of the variables: + # - requestedPagename : Name of the page where attachments shall be listed from. + # - attachmentsPagename : Name of the page where the attachments where retrieved from. + # - destinationPagename : Name of the page where attachment will be placed on. + + requestedPagename = wikiutil.escape(request.values.get("requestedPagename", ""), quote=True) + destinationPagename = wikiutil.escape(request.values.get("destinationPagename", request.page.page_name), quote=True) + + attachmentsPagename = requestedPagename or request.page.page_name + attachments = _get_files(request, attachmentsPagename) + attachments.sort() + attachmentList = ''' + <select id="sctAttachments" size="10" style="width:100%%;visibility:hidden;" onchange="OnAttachmentListChange();"> + %s + </select> +''' % "\n".join(['<option value="%s">%s</option>' % (wikiutil.escape(attachment, quote=True), wikiutil.escape(attachment, quote=True)) + for attachment in attachments]) + + # Translation of dialog texts. + langAttachmentLocation = _("Attachment location") + langPagename = _("Page name") + langAttachmentname = _("Attachment name") + langListAttachmentsButton = _("Refresh attachment list") + langAttachmentList = _("List of attachments") + + if len(attachmentsPagename) > 50: + shortenedPagename = "%s ... %s" % (attachmentsPagename[0:25], attachmentsPagename[-25:]) + else: + shortenedPagename = attachmentsPagename + langAvailableAttachments = "%s: %s" % (_("Available attachments for page"), shortenedPagename) + request.write(''' <!-- * FCKeditor - The text editor for internet @@ -422,32 +437,64 @@ <script src="%(url_prefix_static)s/applets/moinFCKplugins/moinurllib.js" type="text/javascript"></script> </head> <body scroll="no" style="OVERFLOW: hidden"> - <div id="divInfo"> - <div id="divLinkTypeAttachment"> - <table height="100%%" cellSpacing="0" cellPadding="0" width="100%%" border="0"> - <tr> - <td> - <form action=%(action)s method="GET"> - <input type="hidden" name="action" value="fckdialog"> - <input type="hidden" name="dialog" value="attachment"> - <table cellSpacing="0" cellPadding="0" align="center" border="0"> + <form id="DlgAttachmentForm" name="DlgAttachmentForm" action=%(action)s method="GET"> + <input type="hidden" name="action" value="fckdialog"> + <input type="hidden" name="dialog" value="attachment"> + <input type="hidden" id="requestedPagename" name="requestedPagename" value="%(requestedPagename)s"> + <input type="hidden" id="attachmentsPagename" name="attachmentsPagename" value="%(attachmentsPagename)s"> + <input type="hidden" id="destinationPagename" name="destinationPagename" value="%(destinationPagename)s"> + + <div id="divInfo" style="valign=top;"> + <div id="divLinkTypeAttachment"> + <fieldset> + <legend>%(langAttachmentLocation)s</legend> + <table cellSpacing="0" cellPadding="0" width="100%%" border="0"> <tr> - <td> - <span fckLang="AttachmentDlgName">Attachment Name</span><br> - <input id="txtAttachmentname" name="pagename" size="30" value="%(name)s"> - </td> + <td valign="bottom" style="width:90%%" style="padding-bottom:10px"> + <span>%(langPagename)s</span><br> + </td> </tr> - </table> - </form> - </td> - </tr> + <tr> + <td valign="bottom" style="width:100%%" style="padding-bottom:10px;padding-right:10px;"> + <input id="txtPagename" type="text" onkeyup="OnPagenameChange();" onchange="OnPagenameChange();" style="width:98%%"> + </td> + </tr> + <tr> + <td valign="bottom" style="width:90%%" style="padding-bottom:10px;"> + <span>%(langAttachmentname)s</span><br> + </td> + </tr> + <tr valign="bottom"> + <td valign="bottom" style="width:100%%" style="padding-bottom:10px;padding-right:10px;"> + <input id="txtAttachmentname" type="text" onkeyup="OnAttachmentnameChange();" onchange="OnPagenameChange();" style="width:98%%"><br> + </td> + </tr> </table> + </fieldset> + <fieldset> + <legend>%(langAvailableAttachments)s</legend> + <table cellSpacing="0" cellPadding="0" width="100%%" border="0"> + <tr> + <td valign="bottom" style="width:100%%" style="padding-bottom:10px"> + <input id="btnListAttachments" type="submit" value="%(langListAttachmentsButton)s"> + </td> + </tr> + <tr> + <td valign="top" style="padding-top:10px"> + <label for="sctAttachments">%(langAttachmentList)s</label><br> + %(attachmentList)s + </td> + </tr> + </table> + </fieldset> </div> </div> + </form> </body> </html> ''' % locals()) + ############################################################################## ### Image dialog ##############################################################################
--- a/MoinMoin/web/static/htdocs/applets/moinFCKplugins/moinattachment/fck_attachment.js Sat Nov 21 16:31:36 2009 +0100 +++ b/MoinMoin/web/static/htdocs/applets/moinFCKplugins/moinattachment/fck_attachment.js Sat Nov 28 13:20:44 2009 +0100 @@ -18,23 +18,12 @@ * Frederico Caldeira Knabben (fredck@fckeditor.net) */ -var dialog = window.parent ; -var oEditor = dialog.InnerDialogLoaded() ; +var dialog = window.parent; +var oEditor = dialog.InnerDialogLoaded(); var FCK = oEditor.FCK; var FCKLang = oEditor.FCKLang; var FCKConfig = oEditor.FCKConfig; -//#### Dialog Tabs - -// Set the dialog tabs. -window.parent.AddTab('Info', FCKLang.DlgLnkInfoTab); - -// Function called when a dialog tag is selected. -function OnDialogTabChange(tabCode) -{ - ShowE('divInfo' , (tabCode == 'Info')); -} - //#### Regular Expressions library. var oRegex = new Object(); @@ -57,105 +46,194 @@ //#### Initialization Code // oLink: The actual selected link in the editor. -var oLink = dialog.Selection.GetSelection().MoveToAncestorNode( 'A' ) ; -if ( oLink ) - FCK.Selection.SelectNode( oLink ) ; +var oLink = dialog.Selection.GetSelection().MoveToAncestorNode('A'); +if (oLink) + FCK.Selection.SelectNode(oLink); window.onload = function() { - // Translate the dialog box texts. - oEditor.FCKLanguageManager.TranslatePage(document); - - // Load the selected link information (if any). - LoadSelection(); + // Translate the dialog box texts. + oEditor.FCKLanguageManager.TranslatePage(document); - // Show the initial dialog content. - GetE('divInfo').style.display = ''; + // Load the selected link information (if any). + LoadSelection(); - // Activate the "OK" button. - window.parent.SetOkButton(true); + HandleOkButton(); +} - // select first text input element of dialog for usability - SelectField('txtAttachmentname'); -} function LoadSelection() { - if (!oLink) return; + if (!oLink) + { + GetE('requestedPagename').value = GetE('attachmentsPagename').value; + GetE('txtPagename').value = GetE('attachmentsPagename').value; + GetE('sctAttachments').style.visibility = "visible"; + return; + } if (oLink.getAttribute('title') && oLink.getAttribute('title').StartsWith('attachment:')) { - GetE('txtAttachmentname').value = decodeUrl(oLink.getAttribute('title').Remove(0, 'attachment:'.length)); + SetBasePageAttachName(oLink.getAttribute('title').Remove(0, 'attachment:'.length)); } } -//#### Link type selection. -function SetLinkType(linkType) + +// Try to set selected attachment's name and source page name into the dialog. +function SetBasePageAttachName(path) { - ShowE('divLinkTypeAttachment' , (linkType == 'attachment')); -} + path = decodeUrl(path); -//#### Called when user selects Wikipage. -function OnChangePagename(pagename) -{ - GetE("txtPagename").value = pagename; + var idx = path.lastIndexOf('/'); // Attachment points to a different page ? + var requestedPagename = unescapeHTML(GetE('requestedPagename').value); + var attachmentsPagename = unescapeHTML(GetE('attachmentsPagename').value); + var currPagename = path.substring(0, idx); + var currAttachmentname = path.substring(idx+1, path.length); + + // If there is a request for an attachment located on a different page + // then we request a list of attachments for that page. + if ((currPagename != "") && (currPagename != attachmentsPagename)) + { + if (requestedPagename == "") + { + GetE('requestedPagename').value = currPagename; + document.DlgAttachmentForm.submit(); // Transmit the form data and reload attachment dialog. + return; + } + } + else + { + GetE('txtAttachmentname').value = currAttachmentname; + GetE('sctAttachments').value = currAttachmentname; + } + + // Initialize the user interface. + GetE('sctAttachments').style.visibility = "visible"; + GetE('requestedPagename').value = GetE('attachmentsPagename').value; + GetE('txtPagename').value = GetE('attachmentsPagename').value; } -//#### Called while the user types the URL. -function OnUrlChange() -{ - var sUrl = GetE('txtUrl').value; - var sProtocol = oRegex.UrlOnChangeProtocol.exec(sUrl); - if (sProtocol) - { - sUrl = sUrl.substr(sProtocol[0].length); - GetE('txtUrl').value = sUrl; - } - else if (oRegex.UrlOnChangeTestOther.test(sUrl)) - { - GetE('cmbLinkProtocol').value = ''; - } +//#### Called while the user types the remote page name. +function OnPagenameChange() +{ + GetE('requestedPagename').value = StripWhitespace(GetE('txtPagename').value); + + if(GetE('requestedPagename').value != StripWhitespace(GetE('attachmentsPagename').value)) + GetE('sctAttachments').disabled = true; + else + GetE('sctAttachments').disabled = false; + + HandleOkButton(); } + +// If the user types in an attachment name in the attachment name edit field, +// we can check just in time if the name exists on the current page. +function OnAttachmentnameChange() +{ + // Unselect the currently selected listbox item. + var idx = GetE('sctAttachments').selectedIndex; + if(idx!=-1) + GetE('sctAttachments').options[idx].selected = false; + + HandleOkButton(); +} + + +function OnAttachmentListChange() +{ + var idx = GetE('sctAttachments').selectedIndex; + GetE('txtAttachmentname').value = GetE('sctAttachments').options[idx].value; + HandleOkButton(); +} + + +function StripWhitespace(text) +{ + text = text.replace(/^\s*|\s*$/g,''); + return text; +} + + +function HandleOkButton() +{ + var pageName = StripWhitespace(GetE('txtPagename').value); + var attachmentName = StripWhitespace(GetE('txtAttachmentname').value); + + // Activate the "OK" button. + if (pageName.length == 0 || attachmentName.length == 0) + window.parent.SetOkButton(false); + else + window.parent.SetOkButton(true); +} + + +// Escape '<', '>', '&' and '"' to avoid XSS issues. +function escapeHTML(text) +{ + return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """); +} +// Unescape '<', '>', '&' and '"' to avoid XSS issues. +function unescapeHTML(text) +{ + return text.replace(/"/g, "\"").replace(/>/g, ">").replace(/</g, "<").replace(/&/g, "&"); +} + + +function GetAttachmentURI(pageName, attachmentName) +{ + return encodeURI(FCKConfig['WikiBasePath'] + pageName + + "?action=AttachFile&do=view&target=" + attachmentName); +} + + //#### The OK button was hit. function Ok() { - var sUri; - var sText = ''; - - sUri = GetE('txtAttachmentname').value; - if (sUri.length == 0) - { - alert(FCKLang.DlnLnkMsgNoUrl); - return false; - } - sText = sUri; - sUri = encodeUrl(sUri); + var pageName = StripWhitespace(GetE('txtPagename').value); // Attachment's source page/URL. + var attachmentName = StripWhitespace(GetE('txtAttachmentname').value); + var destinationPagename = StripWhitespace(GetE('destinationPagename').value); + var indexOfAttachmentList = GetE('sctAttachments').selectedIndex; + var title = ''; + var fullAttachmentName = ''; - if (oLink) // Modifying an existent link. - { - oLink.href = sUri; - } - else // Creating a new link. + // If attachment is on a different page, than we add a reference to it before + // the attachment name (e.g.: remotepagename/attachment.pdf). + // But: If you rename the destination's page name, this link won't be + // processed by moin and will result in a broken link! + if (destinationPagename != pageName) + fullAttachmentName = pageName + "/" + attachmentName; + else + fullAttachmentName = attachmentName; + + var linkText = fullAttachmentName; + var attachmentURI = GetAttachmentURI(pageName, attachmentName); + + if (oLink) { - oLink = oEditor.FCK.CreateLink(sUri)[0]; - if (! oLink) + // Modify an existent link. + oLink.href = attachmentURI; + SetAttribute( oLink, '_fcksavedurl', attachmentURI ) ; + } + else + { + // Creating a new link. + oLink = oEditor.FCK.CreateLink(fullAttachmentName)[0]; + if (!oLink) { + // If no link text is present... oLink = oEditor.FCK.CreateElement('A'); - oLink.href = sUri; - oLink.appendChild(oEditor.FCK.EditorDocument.createTextNode(sText)); + oLink.href = attachmentURI; + oLink.appendChild(oEditor.FCK.EditorDocument.createTextNode(linkText)); + } + else + { + // If link text is marked... + oLink.href = attachmentURI; } } - - SetAttribute(oLink, 'title', 'attachment:' + sUri); + SetAttribute(oLink, 'title', 'attachment:' + fullAttachmentName); return true; } - -function SetUrl(url) -{ - document.getElementById('txtUrl').value = url; - OnUrlChange(); -} -
--- a/MoinMoin/web/static/htdocs/applets/moinFCKplugins/moinattachment/fckplugin.js Sat Nov 21 16:31:36 2009 +0100 +++ b/MoinMoin/web/static/htdocs/applets/moinFCKplugins/moinattachment/fckplugin.js Sat Nov 28 13:20:44 2009 +0100 @@ -15,13 +15,13 @@ } // Register the related command. -FCKCommands.RegisterCommand('Attachment', new FCKDialogCommand( 'Attachment', FCKLang.DlgLnkWindowTitle, FCKConfig.WikiBasePath + FCKConfig.WikiPage + '?action=fckdialog&dialog=attachment', 400, 330, LinkState, 'CreateAttachment')) ; +FCKCommands.RegisterCommand('Attachment', new FCKDialogCommand( 'Attachment', "Attachment", FCKConfig.WikiBasePath + FCKConfig.WikiPage + '?action=fckdialog&dialog=attachment', 600, 500, LinkState, 'CreateAttachment')) ; oAttachmentItem = new FCKToolbarButton('Attachment', FCKLang.AttachmentBtn, null, null, false, true); } else { -FCKCommands.RegisterCommand('Attachment', new FCKDialogCommand( 'Attachment', FCKLang.DlgLnkWindowTitle, FCKConfig.WikiBasePath + FCKConfig.WikiPage + '?action=fckdialog&dialog=attachment', 400, 330, FCK.GetNamedCommandState, 'CreateAttachment')) ; +FCKCommands.RegisterCommand('Attachment', new FCKDialogCommand( 'Attachment', "Attachment", FCKConfig.WikiBasePath + FCKConfig.WikiPage + '?action=fckdialog&dialog=attachment', 600, 500, FCK.GetNamedCommandState, 'CreateAttachment')) ; oAttachmentItem = new FCKToolbarButton('Attachment', FCKLang.AttachmentBtn, null, null, false, false); }