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