001/*
002    Licensed to the Apache Software Foundation (ASF) under one
003    or more contributor license agreements.  See the NOTICE file
004    distributed with this work for additional information
005    regarding copyright ownership.  The ASF licenses this file
006    to you under the Apache License, Version 2.0 (the
007    "License"); you may not use this file except in compliance
008    with the License.  You may obtain a copy of the License at
009
010       http://www.apache.org/licenses/LICENSE-2.0
011
012    Unless required by applicable law or agreed to in writing,
013    software distributed under the License is distributed on an
014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015    KIND, either express or implied.  See the License for the
016    specific language governing permissions and limitations
017    under the License.
018 */
019package org.apache.wiki.ui;
020
021import java.util.Collection;
022import java.util.HashMap;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Map;
026import java.util.Properties;
027import java.util.Set;
028
029import javax.servlet.jsp.PageContext;
030
031import org.apache.log4j.Logger;
032import org.apache.wiki.WikiContext;
033import org.apache.wiki.WikiEngine;
034import org.apache.wiki.api.exceptions.NoSuchVariableException;
035import org.apache.wiki.modules.ModuleManager;
036import org.apache.wiki.modules.WikiModuleInfo;
037import org.apache.wiki.preferences.Preferences;
038import org.apache.wiki.util.XmlUtil;
039import org.jdom2.Element;
040
041
042/**
043 *  Defines an editor manager.  An editor can be added by adding a
044 *  suitable JSP file under templates/default/editors
045 *  If you want your editor to include any scripts or something, you
046 *  can simply request it by adding the following in your
047 *  ini/jspwiki_module.xml:
048 *
049 *  <pre>
050 *  <modules>
051 *   <editor name="myeditor">
052 *       <author>Janne Jalkanen</author>
053 *       <script>foo.js</script>
054 *       <stylesheet>foo.css</stylesheet>
055 *       <path>editors/myeditor.jsp</path>
056 *   </editor>
057 *  </modules>
058 *  </pre>
059 *
060 *  @since 2.4
061 */
062public class EditorManager extends ModuleManager {
063
064    /** The property name for setting the editor.  Current value is "jspwiki.editor" */
065    /* not used anymore -- replaced by defaultpref.template.editor */
066    public static final String       PROP_EDITORTYPE = "jspwiki.editor";
067
068    /** Parameter for changing editors at run-time */
069    public static final String       PARA_EDITOR     = "editor";
070
071    /** Known name for the plain wikimarkup editor. */
072    public static final String       EDITOR_PLAIN    = "plain";
073
074    /** Known name for the preview editor component. */
075    public static final String       EDITOR_PREVIEW  = "preview";
076
077    /** Known attribute name for storing the user edited text inside a HTTP parameter. */
078    public static final String       REQ_EDITEDTEXT  = "_editedtext";
079
080    /** Known attribute name for storing the user edited text inside a session or a page context */
081    public static final String       ATTR_EDITEDTEXT = REQ_EDITEDTEXT;
082
083    private             Map<String, WikiEditorInfo>  m_editors;
084
085    private static      Logger       log             = Logger.getLogger( EditorManager.class );
086
087    public EditorManager( WikiEngine engine )
088    {
089        super(engine);
090    }
091
092    /**
093     *  Initializes the EditorManager.  It also registers any editors it can find.
094     *
095     *  @param props  Properties for setup.
096     */
097    public void initialize( Properties props )
098    {
099        registerEditors();
100    }
101
102    /**
103     *  This method goes through the jspwiki_module.xml files and hunts for editors.
104     *  Any editors found are put in the registry.
105     *
106     */
107    private void registerEditors() {
108        log.info( "Registering editor modules" );
109        m_editors = new HashMap<String, WikiEditorInfo>();
110
111        //
112        // Register all editors which have created a resource containing its properties.
113        //
114        // Get all resources of all modules
115        //
116        List< Element > editors = XmlUtil.parse( PLUGIN_RESOURCE_LOCATION, "/modules/editor" );
117
118        for( Iterator< Element > i = editors.iterator(); i.hasNext(); ) {
119            Element pluginEl = i.next();
120            String name = pluginEl.getAttributeValue( "name" );
121            WikiEditorInfo info = WikiEditorInfo.newInstance( name, pluginEl );
122
123            if( checkCompatibility(info) ) {
124                m_editors.put( name, info );
125                log.debug( "Registered editor " + name );
126            } else {
127                log.info( "Editor '" + name + "' not compatible with this version of JSPWiki." );
128            }
129        }
130    }
131
132    /**
133     *  Returns an editor for the current context.  The editor names are matched in
134     *  a case insensitive manner.  At the moment, the only place that this method
135     *  looks in is the property file, but in the future this will also look at
136     *  user preferences.
137     *  <p>
138     *  Determines the editor to use by the following order of conditions:
139     *  1. Editor set in User Preferences
140     *  2. Default Editor set in jspwiki.properties
141     *  <p>
142     *  For the PREVIEW context, this method returns the "preview" editor.
143     *
144     * @param context The context that is chosen.
145     * @return The name of the chosen editor.  If no match could be found, will
146     *         revert to the default "plain" editor.
147     */
148    public String getEditorName( WikiContext context )
149    {
150        if( context.getRequestContext().equals(WikiContext.PREVIEW) )
151            return EDITOR_PREVIEW;
152
153        String editor = null;
154
155        // User has set an editor in preferences
156        editor = Preferences.getPreference( context, PARA_EDITOR );
157
158        /* FIXME: actual default 'editor' property is read by the Preferences class */
159        if (editor == null)
160        {
161            // or use the default editor in jspwiki.properties
162            try
163            {
164                editor = m_engine.getVariableManager().getValue( context, PROP_EDITORTYPE );
165            }
166            catch( NoSuchVariableException e ) {} // This is fine
167        }
168
169        if (editor != null)
170        {
171            String[] editorlist = getEditorList();
172
173            editor = editor.trim();
174
175            for( int i = 0; i < editorlist.length; i++ )
176            {
177                if( editorlist[i].equalsIgnoreCase(editor))
178                {
179                    return editorlist[i];
180                }
181            }
182        }
183
184        return EDITOR_PLAIN;
185    }
186
187    /**
188     *  Returns a list of editors as Strings of editor names.
189     *
190     *  @return the list of available editors
191     */
192    public String[] getEditorList()
193    {
194        String[] editors = new String[m_editors.size()];
195
196        Set<String> keys = m_editors.keySet();
197
198        return keys.toArray( editors );
199    }
200
201    /**
202     *  Convenience method for getting the path to the editor JSP file.
203     *
204     *  @param context
205     *  @return e.g. "editors/plain.jsp"
206     */
207    public String getEditorPath( WikiContext context )
208    {
209        String path = null;
210
211        String editor = getEditorName( context );
212
213        WikiEditorInfo ed = m_editors.get( editor );
214
215        if( ed != null )
216        {
217            path = ed.getPath();
218        }
219        else
220        {
221            path = "editors/"+editor+".jsp";
222        }
223
224        return path;
225    }
226
227    /**
228     *  Convenience function which examines the current context and attempts to figure
229     *  out whether the edited text is in the HTTP request parameters or somewhere in
230     *  the session.
231     *
232     *  @param ctx the JSP page context
233     *  @return the edited text, if present in the session page context or as a parameter
234     */
235    public static String getEditedText( PageContext ctx )
236    {
237        String usertext = ctx.getRequest().getParameter( REQ_EDITEDTEXT );
238
239        if( usertext == null )
240        {
241            usertext = (String)ctx.findAttribute( ATTR_EDITEDTEXT );
242        }
243
244        return usertext;
245    }
246
247    /**
248     *  Contains info about an editor.
249     *
250     */
251    private static final class WikiEditorInfo
252        extends WikiModuleInfo
253    {
254        private String m_path;
255
256        protected static WikiEditorInfo newInstance( String name, Element el )
257        {
258            if( name == null || name.length() == 0 ) return null;
259            WikiEditorInfo info = new WikiEditorInfo( name );
260
261            info.initializeFromXML( el );
262            return info;
263        }
264
265        protected void initializeFromXML( Element el )
266        {
267            super.initializeFromXML( el );
268            m_path = el.getChildText("path");
269        }
270
271        private WikiEditorInfo( String name )
272        {
273            super(name);
274        }
275
276        public String getPath()
277        {
278            return m_path;
279        }
280    }
281
282    /**
283     *  {@inheritDoc}
284     */
285    @Override
286    public Collection< WikiModuleInfo > modules() {
287        return modules( m_editors.values().iterator() );
288    }
289
290    /**
291     *  {@inheritDoc}
292     */
293    @Override
294    public WikiEditorInfo getModuleInfo(String moduleName) {
295        return m_editors.get(moduleName);
296    }
297
298}