If you have any experience doing this, I'd be very interested in your code as a head-start. I'm interested in this option instead of a plugin because I want to develop extensions to the editor that are available to all Confluence users, including OnDemand users (er, okay: all users with Greasemonkey or similar add-on/extension that enables user scripts).
Specifically, for my first attempt, I'm considering adding a toolbar button that:
What I'm trying to do here is integrate Wikifier RT functionality with the rich text editor in a way that does not exclude OnDemand users.
(I have other ideas to extend the editor, too, such as XSLT-based and regex-based find'n'replace functionality.)
Any thoughts, feedback, working JavaScript code?
Community moderators have prevented the ability to post new answers.
Graham.
You can get access to the editor via AJS.Rte.getEditor()
Hi Craig,
Thanks very much!
The following code in my Greasemonkey user script opens the Wiki Markup dialog:
location.href = 'javascript:void(AJS.Rte.getEditor().execCommand("InsertWikiMarkup", true));';
Next: I want to set the value of the textarea element (id="insertwikitextarea") of the Wiki Markup dialog.
I would like to pass a string to the InsertWikiMarkup command (as the third parameter of the execCommand call), but that doesn't seem to work; possibly because I need to pass the string as, say, an item inside an array, rather than a simple string value.
Can you pass a value to the InsertWikiMarkup command to populate the textarea? If so, how?
Otherwise, I'll have to set the textarea value in that "Wiki Markup" child window after calling the execCommand.
I have tried various things without success, including the following:
alert("An arbitrary delay."); location.href = 'javascript:void(window.open("", "confluence.conf_wikimarkup", "").document.getElementById("insertwikitextarea").value = "some content");';
That "arbitrary delay" alert is a placeholder until I figure out some better (handler-based?) method for waiting to set the textarea value until the child window has loaded. If the InsertWikiMarkup command doesn't support being passed wiki markup to display in its textarea, then I'd appreciate any tips you could offer me in this area.
In a recent comment, I see yet another customer being lost due to the removal of a wiki markup editor view:
Our whole department have rebelled ... No wiki markup? No Confluence.
I understand Atlassian's oft-stated position ("we're sorry to lose you, goodbye"). I wonder why I care. It's not like Wikifier offers 100% bulletproof conversion, but - for what it's worth - I'm inching closer to providing single-click Wikifier functionality inside the editor (rather than the faff of copying'n'pasting to'n'from an external web page), and I'd appreciate whatever technical assistance you can offer me.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I'm inching closer to providing single-click Wikifier functionality inside the editor
I am surprised that someone with more time than just a few spare minutes after work (the reason for my snail-like progress) has not already done this. I continue to hope that someone will. Then I can relax, forget about it, and move on.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi again Craig,
While I'm asking for coding tips... my first thought for this new Wikify toolbar button was to use the XSLT stylesheet from Wikifier RT that converts RTE HTML. However, if possible, I think I would rather convert the Confluence storage format XML (using the XSLT stylesheet from the original Wikifier). Converting RTE HTML is, after all, a completely bonkers thing to even consider, let alone attempt. It occurs to me that I could simply call a Confluence JavaScript function to convert the current rich text editor contents into Confluence storage format XML. Could you please provide me with a snippet of JavaScript that demonstrates calling this function (or otherwise just point me to the relevant Confluence JavaScript API reference documentation)?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Looking at the source (if you have the source distribution of Confluence you can find it in confluence-plugins/confluence-misc-plugins/confluence-tinymce-plugin/src/main/resources/tinymce3/plugins/insertwiki/editor_plugin_src.js), it's not possible to pass in the text for the dialog.
A better approach may be to access the rest resource that does the wikimarkup -> editor format conversion, and handle success / failure yourself. (Basically do a variation of what the insertwiki/editor_plugin_src.js is doing).
If you want to continue down the current path, there should be an event triggered when the dialog is opened (it should be a standard AUI event). Something like:
AJS.bind('show.dialog', function(e, dialog) { AJS.log(e); AJS.log(dialog); });
I don't think there are any REST services for converting from wiki-markup to storage format. There are rest services for editor<->storage format conversion if the Source Editor is installed.
Other tips:
Not sure why you're doing this:
location.href = 'javascript:void(window.open("", "confluence.conf_wikimarkup", "").document.getElementById("insertwikitextarea").value = "some content");';
Just do this if you just want a "delay" something, just use something like setTimeout:
setTimeout(function() { // do something }, 1000);
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Craig,
Thanks very much for the reply.
Regarding that admittedly very odd-looking location.href assignment statement: that's a "Greasemonkey thing" known as a location hack (follow that link only if you want to do your head in ;-).
To me, time-based delays (waiting in the hope that enough time has passed for some event to have occurred; perhaps using a loop that performs some check after a relatively short delay) are inherently inelegant, and to be avoided if possible. I know how to use setTimeout() - thanks; you weren't to know whether I did or not - but I would prefer a cleaner solution, such as setting an event handler. Thanks very much for the AUI tip. My JavaScript skills fall somewhere between complete idiot and newbie (although, hey, I did manage to code the Wikifier web page, which does client-side XSLT in all major browsers).
Regarding "A better approach...": at this stage, I like the idea of copying wiki markup into the Wiki Markup dialog, because I think that saves me writing code: I get the "editor" (textarea), conversion to RTE HTML and replacement of the current editor selection (the Insert button) for "free".
Regarding this:
There are rest services for editor<->storage format conversion if the Source Editor is installed.
Ah. If I interpret that correctly, I think it means that, if the Source Editor plugin is not installed, then there is no function/method provided by Confluence, and available in client-side JavaScript, that will convert the current editor contents (RTE HTML) into Confluence storage format XML. Is that true? If so, then this clarifies my choice of XSLT stylesheet: if I want the "Wikify" toolbar button to work for OnDemand users (or anyone else who does not have the Source Editor plugin), then I'll need to convert the RTE HTML.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Graham.
Interesting note about the location hack to get around some Greasemonkey sandboxing. I agree using setTimeout is generally a bad idea (I wasn't passing judgement ;) ).
Re: conversion rest services - that's correct you don't get this without the source editor (which isn't in OnDemand). You'll need to convert to the Editor format if you want to generate HTML yourself.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Craig,
To recap: I can use the TinyMCE execCommand method to display the Wiki Markup dialog. Now that I've displayed it, could you please show me how to get a window object for the dialog? I'm beating my head against this but making no progress. When I have the window object, I can set the value attribute of its descendant (via the document object) textarea element. When I have that "proof of concept" working, I'll insert the Wikifier JavaScript into the Greasemonkey script. Not much point doing that right now (given the approach I want to take) until I can insert content into the Wiki Markup dialog's textarea. I'd really appreciate some help with this: my Google mojo (Stackoverflow, TinyMCE sites) has failed me.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Graham,
The following function works for me (well in 4.3 rc - I wouldn't expect it to be different for earlier 4.x releases).
function insertWikiMarkup(markup) { AJS.Rte.getEditor().execCommand('InsertWikiMarkup'); AJS.$('#insertwikitextarea').val(markup); AJS.$('#insert-wiki-markup-dialog button.button-panel-button').click(); }
I didn't need to listen for dialog events, which is nice.
Integrate something like that in your greasemonkey location hack and you should be fine.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Craig,
You are very kind, thank you so much for your help.
I now have a "Wiki markup" toolbar button in the Confluence rich text editor that selects all content, and then displays its wiki markup in the Wiki Markup dialog.
Here is the function that gets called when I click "Wiki markup":
function customAction() { var iframe = document.getElementById("wysiwygTextarea_ifr"); var RTEHTML = iframe.contentDocument.body.innerHTML; var wikiMarkup = wikify(RTEHTML); // Get the Atlassian JavaScript abstraction object var AJS = unsafeWindow.AJS; // Get the RTE object var ed = AJS.Rte.getEditor(); // Select entire contents ed.selection.select(ed.getBody(), true); // Display the Wiki Markup dialog ed.execCommand("InsertWikiMarkup"); // Insert the wiki markup into the dialog AJS.$("#insertwikitextarea").val(wikiMarkup); }
The wikify() function uses the same XSLT stylesheet as the Wikifier RT web page. The script uses the Greasemonkey function GM_xmlhttpRequest() to get the .xsl file from the Wikifier RT website. This function allows requests to cross the "same origin policy" boundaries.
I am the first to admit that this XSLT stylesheet is far from comprehensive. I can (and probably should) list combinations of markup that it currently does not correctly convert. It covers the limited formatting that I use (well, most of it); right now, I'm deliberately not spending time trying to make it any more comprehensive without specific requests. I'm open to enhancing it as users meet its limits. I realize that there's a chicken-and-egg issue here: users might not want to even begin using it until it's comprehensive; they might not want to act as testers.
Still, in a small voice, and being fully aware of its current restrictions (some of which are, effectively, insurmountable), and the requirement for Firefox and Greasemonkey (although I believe there are similar add-ons for some other browsers): welcome back, wiki markup editor.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Craig,
I've just taken a look at your wiki-styler.js on Bitbucket, with the idea of replacing my "direct DOM insertion" method of adding the toolbar button with some TinyMCE API calls such as ControlManager.createButton. Turns out you're doing effectively the same thing as me (albeit via jQuery), so I'm going to leave that part of my code as is for now.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Craig,
I think I might have to listen for the Wiki Markup dialog to load, after all. I think execCommand() is asynchronous; or perhaps, more specifically, the function that implements the InsertWikiMarkup command is asynchronous. Occasionally, I get a blank Wiki Markup dialog, even though the wiki markup conversion was successful (I write it to the console log). I can only put this down to the ".val()" statement running before the dialog has actually loaded. I'm going to try using that "bind" code you suggested. Please feel free to spoonfeed me working code (a "bind" for the Insert Wiki dialog). I'm not proud. I've just spent a largely unproductive evening attempting to add some error-checking code to the GM_xmlhttpRequest call. Debugging Greasemonkey scripts is turning out to be tedious. I'm keen to publish this user script, but I'm not going to do that while I can get it to fail. Frustrating!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Well, this is strange... perhaps the problem has nothing to do with asynchronicity, after all. I can reliably reproduce the problem (the blank Wiki Markup dialog, even when I know the wiki markup conversion has worked, because I've written it to the console) by pressing the Esc key to close the Wiki Markup dialog. If I click the Cancel button with the mouse, it seems to keep working forever, but after pressing the Esc key instead, every time I click the "Wiki markup" toolbar button, I get the successfully converted wiki markup logged to the console, but the Wiki Markup dialog is empty.
Can you reproduce this? What is special about pressing the Esc key (as opposed to clicking the Cancel button with a mouse) that might cause this to happen? I hope it's not a Greasemonkey thing.
Last thought for the evening (probably just fuzzy thinking): I'm wondering whether pressing Esc does something to make AJS.$("#insertwikitextarea") not "find" anything.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
To avoid exceeding the "characters left" limit on comments in Atlassian Answers, I have copied the complete draft source of the Greasemonkey user script to a new page, "Wiki markup toolbar button", on the Confluence Advanced Tips wiki, so that you can see it and try it for yourself, if you have the time and inclination (I understand that you might reasonably have neither, no problem).
That page is a temporary home for the draft source: when I think the user script it's ready for general use, I'll upload the source to userscripts.org.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
This is really doing my head in. I have done a bunch of Googling and reading about textarea elements, and the value property (versus attribute), and jQuery attr(), prop(), and val(), and why not to use innerHTML, and using the direct (non-jQuery) .getElementById().value =, but none of the techniques that I have tried work after I press the Esc key to close the Wiki Markup dialog. It's maddening.
Before using one of the above techniques to set the textarea value, I have inserted the following code to test that the textarea exists:
if (AJS.$("#insertwikitextarea").length > 0) { alert("Dialog exists. Length: " + AJS.$("#insertwikitextarea").length); } else { alert("Dialog does not exist"); }
It always exists.
What the heck is it about pressing the Esc key that causes this problem?
I'm wondering whether TinyMCE is involved. Is the textarea in the Wiki Markup dialog a TinyMCE editor? Rather than setting the textarea value directly (either via direct DOM methods or jQuery), perhaps I should be calling the setContent method? The TinyMCE FAQ indicates that this might be so, but I have no idea what to use for the editor name and/or format parameter. If this is the root of the problem, could you please show me how to access the TinyMCE editor object that corresponds to the textarea element of the Wiki Markup dialog (and, if necessary, specify the correct format parameter for setContent), so that I can use setContent on that editor?
The Wiki markup button works... until you press the Esc key to close the Wiki Markup dialog! Grrr :-(. Any advice you could offer me would be greatly appreciated.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Graham,
Sorry for the slow reply I've been away from Answers for a while.
It seems there's a bug, when pressing escape, the dialog is just closed, and not removed from the DOM. On opening the dialog again, instead of reusing the existing dialog a new one is created.
So there are 2 dialogs with textarea with id = insertwikitextarea. This is obviously a bad thing and JQuery on returns the first textarea it finds.
The simplest workaround for you is you use the following instead:
AJS.$("textarea[name='wikitext']").last().val(wikiMarkup);
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Crikey, no need to apologize: here I am, an AJS/TinyMCE/jQuery newbie bleating to be spoonfed code - to achieve (make a half-assed attempt at) something that Atlassian has clearly stated it does not want to do (bring back the wiki markup editor) - and you're taking the time to help me. I have nothing but respect and gratitude.
Yeah, I suspected that the problem might have been a previous instance of that dialog (and its textarea) lying around, which is why I inserted that "length" check (and echoed its value in an alert). But I now realize (although I haven't done any further investigation on this) that my use of an ID-based selector for that check was ill-considered, because length would only ever have returned 1 (because IDs are, as you point out, supposed to be unique).
Anyway, the workaround that you have given me works. Thank you very much!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Er, actually, that workaround only half-worked. Or rather, the workaround works perfectly, but I now have another problem, which also appears to be triggered by pressing the Esc key, and so might also be caused by the same bug that you mentioned: after pressing the Esc key to close the Wiki Markup dialog, any edits that I perform in any subsequently opened Wiki Markup dialog are not honored when I click the Insert button on the dialog.
I can reproduce this bad behavior in the Confluence rich text editor without any of my Greasemonkey shenanigans. So, it could be experienced by any user.
Can you reproduce this behavior? If so, could you please open a JIRA issue for this?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I need to look in the database to double-check, but I think this is the most-commented answer on the site now :-)
Edit: Ah, sadly not even close:
answer comments
53724 24
63513 23
68931 22
49351 22
62137 21
44940 21
12560 20
40256 20
39572 20
52364 20
23047 19
51070 19
60272 18
46797 18
51813 18
13589 18
29629 18
43185 17
29157 17
64628 17
20492 17
44629 17
73295 17
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I was about to suggest that you try out writing a Speakeasy Extension, but I don't think that is an enabled feature in OnDemand either :-(
I think Greasemonkey is dark magic and wish I knew more about it, so can't help you there :-)
However, I can tell you that we have an upcoming Confluence plugin developer tutorial blitz coming up next month - the 2012 Confluence Doc Sprint. One of the tutorials we're hoping to build is 'Adding a new icon to the editor toolbar' - so if you're willing to wait a little bit, we should hopefully have some better documentation on this in the near future.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Joseph,
Thank you, I really appreciate you taking an interest in this question.
I looked at Speakeasy a while back (it seemed like a good option, as I am currently only looking at writing client-side - JavaScript - code), but, as you say, I think that is not currently available in OnDemand.
I'm with you on the "dark magic", but if I want to extend the editor now in a way that does not exclude OnDemand users, I can think of no other option than to cavort at night with a slippery simian under the flickering light of a firebug ;). If you can suggest another method, I would welcome it.
Having said that, I will definitely be interested in reading that tutorial; thanks for the heads-up.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi again Joseph,
I've developed a rudimentary, proof-of-concept user script that adds a custom button to the Confluence rich text editor toolbar. The button doesn't do much - it just displays an alert box showing the HTML of the current selection (if any) - but it demonstrates the basic concept.
I have not tested it extensively, but it seems to work okay so far.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Joseph,
Would you please take pity on me and provide me with a snippet of JavaScript that shows me how to display the Insert Wiki dialog (containing wiki markup that I provide) in the Confluence rich text editor? I believe this means calling the execCommand method of the editor object with "InsertWikiMarkup" command as the first parameter, but I'm struggling to know how to refer to the editor object (the stuff before the period in .execCommand("InsertWikiMarkup" ... ).
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You can download the Greasemonkey script discussed here from the "Wiki markup toolbar button" page on the Confluence Advanced Tips wiki.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I took the liberty of adding the 'confluence-ondemand' tag to this question, I hope you don't mind :-)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
The wizard that pops up when you first ask a question just controls which tags get applied to the question. You could manually add both the 'confluence' and 'confluence-ondemand' tags to the question to achieve what you desire.... however, I assume that the pretty banner at the top of the question will choose one or the other and doesn't have a way to display both.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Is there a way to create a question that applies to the "installed versions" and "OnDemand"? This question applies to both. When I asked this question, I deliberately did not select either "installed" or "OnDemand". However, I notice that the question appears to have defaulted to "applies to the installed versions".
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Community moderators have prevented the ability to post new answers.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.