An editor button is a button. Please read the documentation relative to the creation of ribbon elements first (Ribbon Control)
Editor button should be enabled/disabled/toggled depending on a selection message with :
a Content target with
a form subtarget (content in edition) with
a rich text field subtarget
with a node subtarget.
EditorButton
You can inherit the EditorButton (org.ametys.cms.ribbon.control.button.EditorButton) that implements a onMessage method and gives you a new entry point "selection-listener".
Declare it like that
Oops !
Copy to clipboard failed. Open the code and copy it manually.
The selection listener allows you to enable/disable/toggle/change icon the button following the current selected node.
Here is the sample of the bold listener : it enables the bold button when the current selection is a inline editor node ; it toggles the button on when the current selected inline editor node is bold
Oops !
Copy to clipboard failed. Open the code and copy it manually.
org.ametys.cms.editor.InsertElementHelper.actionDirect surrounds the currently selected tag with a new tag <tagName>. org.ametys.cms.editor.InsertElementHelper.listener toggles the button when the current selection is in a <tagName>. The 2 descriptions are used depending if the button is enabled or disabled.
You can optionally add a defined attribute on the tagname and also a className.
You can create code like
Oops !
Copy to clipboard failed. Open the code and copy it manually.
EditorButton to create a character tag around a selection with a user valued attribute
If you want to do as before but want to previously ask the user for an input (that will become an attribute value) do the same as the previous section but with the following script.
For example, the following html
Oops !
Copy to clipboard failed. Open the code and copy it manually.
<p>W3C rocks</p>
<p>W3C rocks</p>
<p>W3C rocks</p>
can become when W3C is selected
Oops !
Copy to clipboard failed. Open the code and copy it manually.
<p><acronym title="World Wide Web Consortium">W3C</acronym></p>
<p><acronym title="World Wide Web Consortium">W3C</acronym></p>
<p><acronym title="World Wide Web Consortium">W3C</acronym></p>
displaying the following dialog box to the user :
Oops !
Copy to clipboard failed. Open the code and copy it manually.
Always end by giving the focus back !
This is not a tinyMCE normal behavior, but we have to do so because we use our own toolbar
Always start by looking if the button your are creating already exists in tinyMCE. If so you will be able to copy the main code, but still have to create the hmi specific code
I don't find a command that suits
You have always the solution to insert your own html code.
Replace the execCommand ('Bold') of the preceding sample by:
Oops !
Copy to clipboard failed. Open the code and copy it manually.
A marker is a ufo in the middle of your text to bookmark a place.
For example: "<p>Lorem ipsum<marker/> doler</p>
The marker set in the inline editor will be replaced during rendering by a feature.
For example: "<p>Lorem ipsum<img src="warning.gif"/> doler</p>
To set a marker in your code you can use the image tag, but with the attribute marker="marker" to explain this is not a classic image.
You also sould use class="mceItemNoResize" to make your marker not resizable by the user.
You have to differenciate your marker by specifying a special attribute. For example myfeature="myfeature"
You have to graphically specify an appearance to your button. You can use the src, or specify a transparent file in the src and set a specific class.
Sample :
Here is the tag I want to handle
Oops !
Copy to clipboard failed. Open the code and copy it manually.
This is the default value of the attribute.
If this is null, there is no default value.
If this is a string that means that the attribute will always exists.
This example allows the tag <table> and the attribute "class" (with the default value "myclass").
Oops !
Copy to clipboard failed. Open the code and copy it manually.
In your plugin, create a file "htmleditor2docbook.xsl"
This xsl will have to handle the code you have inserted in the htmleditor and transform it to an acceptable docbook tag.
For sample, the style drop down list generates this kind of htmleditor code
Oops !
Copy to clipboard failed. Open the code and copy it manually.
<p class="introduction">This is a test</p>
<p class="introduction">This is a test</p>
<p class="introduction">This is a test</p>
That can be handled with the following xsl
Oops !
Copy to clipboard failed. Open the code and copy it manually.
Where stylesheets/htmleditor2docbook.xsl is the file you have created above.
You can also use protocols if needed (plugin:cms://stylesheets/htmleditor2docbook.xsl or skin://stylesheets/htmleditor2docbook.xsl)
How to check ?
Use the repository workspace to see your content as a XML. (The repository workspace is available in the administration space of the cms)
You should see something like
Oops !
Copy to clipboard failed. Open the code and copy it manually.
...
<docbook:para class="introduction">This is a test</docbook:para>
...
...
<docbook:para class="introduction">This is a test</docbook:para>
...
...
<docbook:para class="introduction">This is a test</docbook:para>
...
Keep in mind
All the xsls declared using this way are mixed all together, but without a defined order.
If several xslt defines the same matchers, you can not know which one will be used, except if you use the "priority" tag on it.
The sample code above is a good example to break to default save process as the default htmleditor2docbook.xsl is much more complicated than this one (and your code may interfer).
So be sure to use tags that no one also use (in the htmleditor and in the docbook).
Java Transformer
Before your XSL, you can have a java transformer to do more sophisticated stuff.
For example, you can transform copy/paste links to internal links if needed
To do so, first create a javaclass that handles your tag.
Extends org.ametys.cms.transformation.htmledition.HTMLEditionHandler
Then declare it as an extension of org.ametys.cms.transformation.htmledition.HTMLEditionHandlerExtensionPoint under the id of your choice.
Finally add a <handler> tag under <html2docbook> to indicate the role.
Oops !
Copy to clipboard failed. Open the code and copy it manually.
Copy to clipboard failed. Open the code and copy it manually.
package org.ametys.cms.transformation.htmledition;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.cocoon.components.ContextHelper;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.xml.AttributesImpl;
import org.apache.commons.io.IOUtils;
import org.apache.excalibur.source.SourceResolver;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.ametys.runtime.upload.Upload;
import org.ametys.runtime.upload.UploadManager;
import org.ametys.runtime.user.CurrentUserProvider;
/**
* This transformer extracts uploaded files' ids from the incoming HTML for further processing.
*/
public class UploadedDataHTMLEditionHandler extends HTMLEditionHandler
{
// do your code here
}
package org.ametys.cms.transformation.htmledition;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.cocoon.components.ContextHelper;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.xml.AttributesImpl;
import org.apache.commons.io.IOUtils;
import org.apache.excalibur.source.SourceResolver;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.ametys.runtime.upload.Upload;
import org.ametys.runtime.upload.UploadManager;
import org.ametys.runtime.user.CurrentUserProvider;
/**
* This transformer extracts uploaded files' ids from the incoming HTML for further processing.
*/
public class UploadedDataHTMLEditionHandler extends HTMLEditionHandler
{
// do your code here
}
package org.ametys.cms.transformation.htmledition;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.cocoon.components.ContextHelper;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.xml.AttributesImpl;
import org.apache.commons.io.IOUtils;
import org.apache.excalibur.source.SourceResolver;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.ametys.runtime.upload.Upload;
import org.ametys.runtime.upload.UploadManager;
import org.ametys.runtime.user.CurrentUserProvider;
/**
* This transformer extracts uploaded files' ids from the incoming HTML for further processing.
*/
public class UploadedDataHTMLEditionHandler extends HTMLEditionHandler
{
// do your code here
}
docbook2htmleditor
This is exactly the same as above, but in the opposite way : you have to write a XSL that convert your docbook extension to the html editor
In the plugin configuration replace <htmleditor2docbook> by <docbook2htmleditor>
Here is the sample that goes with the preceding one above.
Oops !
Copy to clipboard failed. Open the code and copy it manually.
The new tags your are now handling may need to take place in the consistency checker (that for example, check that links are not broken)
In that case, you have to write a XSL that convert your docbook extension to the consitency format.
In the plugin configuration replace <htmleditor2docbook> by <docbook2consistency>
The output format must be :
Oops !
Copy to clipboard failed. Open the code and copy it manually.
The VALUE depends on the TYPE.
The list of handled types depends on the plugin your are using.
The main type is link and imply that a linkchecker will goes on. The pattern of the value is LINKTYPE:REFERNCE.
The REFERENCE depends on the LINKTYPE.
The LINKTYPE depends on the plugins you are running, but in a web configuration, you have at least :
(empty) (for external links) then the reference is a http url. e.g. http://www.ametys.org
page (for links on pages) then the reference is an ametys object id. e.g. page:page://f69e9744-852c-4d9d-834b-242cbe5d01db
attachment-content (for links on attachments) then the reference is an ametys object. e.g. attachment-content:resource://6df076d3-2437-4b77-95ae-56947f6c1b57
explorer (for links on explorer files) then the reference is an ametys object. e.g. explorer:resource://2973249e-9bd4-4eec-b7d9-823c0d879cde
local (for links on metadata local to the content) then the reference is an ametys object + a metadata path. e.g. local:defaultWebContent://591538d9-6489-4c99-802d-a0cf5459b11e@content;image.jpg
For example, here is the code of the default XSLT that analyses links and images in docbook
Oops !
Copy to clipboard failed. Open the code and copy it manually.
This step is nearly the same as above, but the result will be use for the final rendering (instead of for edition)
XSL
This XSL will mainly have the same look as docbook2htmleditor.xsl but with some differences.
In the plugin configuration use <docbook2html> instead of <htmleditor2docbook> to specify your xsl file.
For example, "title 1" is converted to "h1" for edition, but to "h3" for rendering.
Why "h3", for accessibility purposes, there are generally already h1 and h2, and content takes place under the h2 as a "h3".
Another example : you create a button that insert a youtube video.
In the htmleditor you set <div class="youtube" youtube="youtubeid"/>
In the docbook you save <docbook:video youtube=youtubeid"/>
In docbook2htmleditor you will recreate the <div> tag, but in docbook2html you will create the flash player tag.
See docbook2html in the skin documentation for more information.
Java Transformer
After your XSL, you can have a java transformer to do more sophisticated stuff.
For example, you can request a ldap to replace you brand new tag <emergencyNumber/>
To do so, first create a javaclass that handles your tag.
Implement org.ametys.cms.transformation.docbook.EnhancementHandler (or extends org.ametys.cms.transformation.docbook.ProxyEnhancementHandler)
Then declare it a an avalon component in your plugin configuration under the role of your choice.
Finally add a <handler> tag under <docbook2html> to indicate the role.
Oops !
Copy to clipboard failed. Open the code and copy it manually.
Copy to clipboard failed. Open the code and copy it manually.
/*
* Copyright 2010 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ametys.cms.transformation.docbook;
import org.apache.cocoon.xml.AttributesImpl;
import org.apache.cocoon.xml.XMLUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
public class TestProxyEnhancementHandler extends EnhancementHandler
{
private static final String MYTAG = "emergencyNumber";
@Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException
{
if (MYTAG.equals(localName))
{
String ldapEmergencyNumber = "";
// Do my ldap request that fill ldapEmergencyNumber
AttributesImpl attrs = new AttributesImpl();
attrs.addCDATAAttribute("class", "emergency-number");
XMLUtils.createElement(_contentHandler, "span", attrs, ldapEmergencyNumber);
}
else
{
super.startElement(uri, localName, qName, atts);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException
{
if (!MYTAG.equals(localName))
{
super.endElement(uri, localName, qName);
}
}
}
/*
* Copyright 2010 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ametys.cms.transformation.docbook;
import org.apache.cocoon.xml.AttributesImpl;
import org.apache.cocoon.xml.XMLUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
public class TestProxyEnhancementHandler extends EnhancementHandler
{
private static final String MYTAG = "emergencyNumber";
@Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException
{
if (MYTAG.equals(localName))
{
String ldapEmergencyNumber = "";
// Do my ldap request that fill ldapEmergencyNumber
AttributesImpl attrs = new AttributesImpl();
attrs.addCDATAAttribute("class", "emergency-number");
XMLUtils.createElement(_contentHandler, "span", attrs, ldapEmergencyNumber);
}
else
{
super.startElement(uri, localName, qName, atts);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException
{
if (!MYTAG.equals(localName))
{
super.endElement(uri, localName, qName);
}
}
}
/*
* Copyright 2010 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ametys.cms.transformation.docbook;
import org.apache.cocoon.xml.AttributesImpl;
import org.apache.cocoon.xml.XMLUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
public class TestProxyEnhancementHandler extends EnhancementHandler
{
private static final String MYTAG = "emergencyNumber";
@Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException
{
if (MYTAG.equals(localName))
{
String ldapEmergencyNumber = "";
// Do my ldap request that fill ldapEmergencyNumber
AttributesImpl attrs = new AttributesImpl();
attrs.addCDATAAttribute("class", "emergency-number");
XMLUtils.createElement(_contentHandler, "span", attrs, ldapEmergencyNumber);
}
else
{
super.startElement(uri, localName, qName, atts);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException
{
if (!MYTAG.equals(localName))
{
super.endElement(uri, localName, qName);
}
}
}
Optionally use a different tag in inline editor and in storing for inline editor (very advanced feature)
Some times, you use a tag in the inline editor but you want to substitute it by another one.
For example, you sometimes need a <img marker="marker"/> to represent a marker in the inline editor, but you want to on-the-fly replace it by its logical role. (to avoid playing with several img tags in your xsls)
In that very rare and advanced case, you have to register 2 methods to the online editor. One when the content is set and one when it is read.
The signature of these methods are (editor, object) where object.content is the whole editor content.
Oops !
Copy to clipboard failed. Open the code and copy it manually.
The job is then to search in object.content to replace base object by transformed object.
Example
I want to handle a "htmlexpert" feature: this allows to enter html code that will be kept intact.
I do it by setting a marker in the inline editor that will hold the html code.
The inline editor will use a <img marker="marker" htmlexpert="myhtmlcode"/>
But in my logic I want to represent it by <!-- htmlexper myhtmlcode -->
I want onGetContent to convert from img to comment and the opposite onSetContent
Oops !
Copy to clipboard failed. Open the code and copy it manually.
// Search for <img htmlexpert="..." .../> to replace by <!-- htmlexpert ... -->
org.ametys.cms.editor.Editor.onGetContent.push(
function (editor, object)
{
object.content = object.content.replace(
/<img[^>]+ htmlexpert="-([^"]*)"[^>]+>/g,
function (s)
{
var htmlexpert = RegExp.$1;
return "<!-- htmlexpert " + htmlexpert.replace(/-/g, "%2D") + " -->";
}
);
}
);
// Search for <!-- htmlexpert ... --> to replace by <img htmlexpert="..." .../>
org.ametys.cms.editor.Editor.onSetContent.push(
function (editor, object)
{
object.content = object.content.replace(
/<!-- htmlexpert ([^-]*) -->/g,
function(s)
{
var htmlexpert = RegExp.$1;
return org.ametys.cms.editor.htmlexpert._createHTML(htmlexpert.replace(/%2D/g, "-"));
}
);
}
);
// Search for <img htmlexpert="..." .../> to replace by <!-- htmlexpert ... -->
org.ametys.cms.editor.Editor.onGetContent.push(
function (editor, object)
{
object.content = object.content.replace(
/<img[^>]+ htmlexpert="-([^"]*)"[^>]+>/g,
function (s)
{
var htmlexpert = RegExp.$1;
return "<!-- htmlexpert " + htmlexpert.replace(/-/g, "%2D") + " -->";
}
);
}
);
// Search for <!-- htmlexpert ... --> to replace by <img htmlexpert="..." .../>
org.ametys.cms.editor.Editor.onSetContent.push(
function (editor, object)
{
object.content = object.content.replace(
/<!-- htmlexpert ([^-]*) -->/g,
function(s)
{
var htmlexpert = RegExp.$1;
return org.ametys.cms.editor.htmlexpert._createHTML(htmlexpert.replace(/%2D/g, "-"));
}
);
}
);
// Search for <img htmlexpert="..." .../> to replace by <!-- htmlexpert ... -->
org.ametys.cms.editor.Editor.onGetContent.push(
function (editor, object)
{
object.content = object.content.replace(
/<img[^>]+ htmlexpert="-([^"]*)"[^>]+>/g,
function (s)
{
var htmlexpert = RegExp.$1;
return "<!-- htmlexpert " + htmlexpert.replace(/-/g, "%2D") + " -->";
}
);
}
);
// Search for <!-- htmlexpert ... --> to replace by <img htmlexpert="..." .../>
org.ametys.cms.editor.Editor.onSetContent.push(
function (editor, object)
{
object.content = object.content.replace(
/<!-- htmlexpert ([^-]*) -->/g,
function(s)
{
var htmlexpert = RegExp.$1;
return org.ametys.cms.editor.htmlexpert._createHTML(htmlexpert.replace(/%2D/g, "-"));
}
);
}
);