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