View Javadoc

1   package org.apache.turbine.services.pull.util;
2   
3   /*
4    * Copyright 2001-2005 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License")
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.io.InputStream;
20  import java.util.Properties;
21  
22  import org.apache.commons.configuration.Configuration;
23  import org.apache.commons.lang.StringUtils;
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.apache.turbine.Turbine;
27  import org.apache.turbine.om.security.User;
28  import org.apache.turbine.services.pull.ApplicationTool;
29  import org.apache.turbine.services.pull.TurbinePull;
30  import org.apache.turbine.services.servlet.TurbineServlet;
31  import org.apache.turbine.util.RunData;
32  import org.apache.turbine.util.ServerData;
33  import org.apache.turbine.util.uri.DataURI;
34  
35  /***
36   * UIManager.java
37   * <br>
38   * Manages all UI elements for a Turbine Application. Any
39   * UI element can be accessed in any template using the
40   * $ui handle (assuming you use the default PullService
41   * configuration). So, for example, you could access
42   * the background colour for your pages by using
43   * $ui.bgcolor
44   * <p>
45   * <h3>Questions:</h3>
46   * What is the best way to allow an application
47   * to be skinned. And how to allow the flexible
48   * altering of a particular UI element in certain
49   * parts of the template hierarchy. For example
50   * on one section of your site you might like
51   * a certain bgcolor, on another part of your
52   * site you might want another. How can be let
53   * the designer specify these properties and
54   * still use the single $app.ui.bgcolor in
55   * all the templates.
56   * <p>
57   * It would also be very cool to use some form
58   * of inheritence for UI elements. Say a $ui.bgcolor
59   * is used in a template where the bgcolor is not
60   * set for that part of hierarch, it would be cool
61   * if it could find the setting for the bgcolor
62   * in the parent directory. So you could override
63   * a UI element where you wanted and the system
64   * would fall back to the parent when necessary.
65   * <p>
66   * How to specify skins, how to deal with images,
67   * how could this be handled with a web app.
68   * <p>
69   *
70   * This is an application pull tool for the template system. You should <b>not</b>
71   * use it in a normal application!
72   *
73   *
74   * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
75   * @author <a href="mailto:james_coltman@majorband.co.uk">James Coltman</a>
76   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
77   * @author <a href="thomas.vandahl@tewisoft.de">Thomas Vandahl</a>
78   * @version $Id: UIManager.java 290076 2005-09-19 07:13:51Z henning $
79   */
80  public class UIManager implements ApplicationTool
81  {
82      /*** Logging */
83      private static Log log = LogFactory.getLog(UIManager.class);
84  
85      /***
86       * The location of the skins within the application
87       * resources directory.
88       */
89      private static final String SKINS_DIRECTORY = "/ui/skins";
90  
91      /***
92       * The name of the directory where images are
93       * stored for this skin.
94       */
95      private static final String IMAGES_DIRECTORY = "/images";
96  
97      /***
98       * Property tag for the default skin that is to be
99       * used for the web application.
100      */
101     private static final String SKIN_PROPERTY = "tool.ui.skin";
102 
103     /***
104      * Property tag for the image directory inside the skin that is to be
105      * used for the web application.
106      */
107     private static final String IMAGEDIR_PROPERTY = "tool.ui.dir.image";
108 
109     /***
110      * Property tag for the skin directory that is to be
111      * used for the web application.
112      */
113     private static final String SKINDIR_PROPERTY = "tool.ui.dir.skin";
114 
115     /***
116      * Property tag for the css file that is to be
117      * used for the web application.
118      */
119     private static final String CSS_PROPERTY = "tool.ui.css";
120 
121     /***
122      * Property tag for the css file that is to be
123      * used for the web application.
124      */
125     private static final String RELATIVE_PROPERTY = "tool.ui.want.relative";
126 
127     /***
128      * Default skin name. This name actually represents
129      * a directory in the WEBAPP/resources/ui/skins
130      * directory. There is a file called skin.props
131      * which actually contains the name/value pairs.
132      */
133     private static final String SKIN_PROPERTY_DEFAULT = "default";
134 
135     /***
136      * Attribute name of skinName value in User's temp hashmap.
137      */
138     private static final String SKIN_ATTRIBUTE =
139             UIManager.class.getName()+ ".skin";
140 
141     /***
142      * The actual skin being used for the webapp.
143      */
144     private String skinName;
145 
146     /***
147      * The skins directory.
148      */
149     private String skinsDirectory;
150 
151     /***
152      * The file within the skin directory that actually
153      * contains the name/value pairs for the skin.
154      */
155     private static final String SKIN_PROPS_FILE = "skin.props";
156 
157     /***
158      * The file name for the skin style sheet.
159      */
160     private static final String DEFAULT_SKIN_CSS_FILE = "skin.css";
161 
162     /***
163      * This the resources directory relative to the
164      * webapp context. Used for constructing correct
165      * URIs for retrieving images in image().
166      */
167     private String resourcesDirectory;
168     private String imagesDirectory;
169     private String cssFile;
170 
171     private boolean wantRelative = false;
172 
173     /***
174      * Properties to hold the name/value pairs
175      * for the skin.
176      */
177     private Properties skinProperties;
178 
179     /***
180      * Initialize the UIManager object.
181      *
182      * @param data This is null, RunData or User depending upon specified tool scope.
183      */
184     public void init(Object data)
185     {
186         /***
187          * Store the resources directory for use in image().
188          */
189         Configuration cfg = Turbine.getConfiguration();
190 
191         resourcesDirectory = stripSlashes(TurbinePull.getResourcesDirectory());
192 
193         if (data == null)
194         {
195             log.debug("UI Manager scope is global");
196             setSkin();
197         }
198         else if (data instanceof RunData)
199         {
200             log.debug("UI Manager scope is request");
201             setSkin((RunData) data);
202         }
203         else if (data instanceof User)
204         {
205             log.debug("UI Manager scope is session");
206             setSkin((User) data);
207         }
208 
209         skinsDirectory = stripSlashes(cfg.getString(SKINDIR_PROPERTY, SKINS_DIRECTORY));
210 
211         imagesDirectory = stripSlashes(cfg.getString(IMAGEDIR_PROPERTY, IMAGES_DIRECTORY));
212 
213         cssFile = cfg.getString(CSS_PROPERTY, DEFAULT_SKIN_CSS_FILE);
214 
215         wantRelative = cfg.getBoolean(RELATIVE_PROPERTY, false);
216 
217         loadSkin();
218     }
219 
220     private String stripSlashes(final String path)
221     {
222         if (StringUtils.isEmpty(path))
223         {
224             return "";
225         }
226 
227         String ret = path;
228         int len = ret.length() - 1;
229 
230         if (ret.charAt(len) == '/')
231         {
232             ret = ret.substring(0, len);
233         }
234 
235         if (len > 0 && ret.charAt(0) == '/')
236         {
237             ret = ret.substring(1);
238         }
239 
240         return ret;
241     }
242 
243     /***
244      * This lets the tool know that it should be
245      * refreshed. The tool can perform whatever actions
246      * are necessary to refresh itself. This is necessary
247      * for sane development where you probably want the
248      * tools to refresh themselves on every request.
249      */
250     public void refresh()
251     {
252         log.debug("Refreshing UI manager");
253 
254         loadSkin();
255     }
256 
257     /***
258      * Retrieve a property from the properties held
259      * within the properties file for this skin.
260      */
261     public String get(String key)
262     {
263         return skinProperties.getProperty(key);
264     }
265 
266     /***
267      * Retrieve the skin name.
268      */
269     public String getSkin()
270     {
271         return skinName;
272     }
273 
274     /***
275      * Retrieve the URL for an image that is part
276      * of a skin. The images are stored in the
277      * WEBAPP/resources/ui/skins/&lt;SKIN&gt;/images
278      * directory.
279      *
280      * Use this if for some reason your server name,
281      * server scheme, or server port change on a
282      * per request basis. I'm not sure if this
283      * would happend in a load balanced situation.
284      * I think in most cases the image(String image)
285      * method would probably be enough, but I'm not
286      * absolutely positive.
287      */
288     public String image(String imageId, RunData data)
289     {
290         DataURI du = new DataURI(data);
291 
292         StringBuffer sb = new StringBuffer();
293 
294         sb.append(resourcesDirectory).
295                 append('/').
296                 append(skinsDirectory).
297                 append('/').
298                 append(getSkin()).
299                 append('/').
300                 append(imagesDirectory).
301                 append('/').
302                 append(stripSlashes(imageId));
303 
304         du.setScriptName(sb.toString());
305 
306         return wantRelative ? du.getRelativeLink() : du.getAbsoluteLink();
307     }
308 
309     /***
310      * Retrieve the URL for an image that is part
311      * of a skin. The images are stored in the
312      * WEBAPP/resources/ui/skins/&lt;SKIN&gt;/images
313      * directory.
314      */
315     public String image(String imageId)
316     {
317         ServerData sd = Turbine.getDefaultServerData();
318         DataURI du = new DataURI(sd);
319 
320         StringBuffer sb = new StringBuffer();
321 
322         sb.append(resourcesDirectory).
323                 append('/').
324                 append(skinsDirectory).
325                 append('/').
326                 append(getSkin()).
327                 append('/').
328                 append(imagesDirectory).
329                 append('/').
330                 append(stripSlashes(imageId));
331 
332         du.setScriptName(sb.toString());
333         return wantRelative ? du.getRelativeLink() : du.getAbsoluteLink();
334     }
335 
336     /***
337      * Retrieve the URL for the style sheet that is part
338      * of a skin. The style is stored in the
339      * WEBAPP/resources/ui/skins/&lt;SKIN&gt; directory with the
340      * filename skin.css
341      *
342      * Use this if for some reason your server name,
343      * server scheme, or server port change on a
344      * per request basis. I'm not sure if this
345      * would happend in a load balanced situation.
346      * I think in most cases the style()
347      * method would probably be enough, but I'm not
348      * absolutely positive.
349      */
350     public String getStylecss(RunData data)
351     {
352         return getScript(cssFile, data);
353     }
354 
355     /***
356      * Retrieve the URL for the style sheet that is part
357      * of a skin. The style is stored in the
358      * WEBAPP/resources/ui/skins/&lt;SKIN&gt; directory with the
359      * filename skin.css
360      */
361     public String getStylecss()
362     {
363         return getScript(cssFile);
364     }
365 
366     /***
367      * Retrieve the URL for a given script that is part
368      * of a skin. The script is stored in the
369      * WEBAPP/resources/ui/skins/<SKIN> directory
370      */
371     public String getScript(String filename, RunData data)
372     {
373         DataURI du = new DataURI(data);
374 
375         StringBuffer sb = new StringBuffer();
376 
377         sb.append(resourcesDirectory).
378                 append('/').
379                 append(skinsDirectory).
380                 append('/').
381                 append(getSkin()).
382                 append('/').
383                 append(stripSlashes(filename));
384 
385         du.setScriptName(sb.toString());
386         return wantRelative ? du.getRelativeLink() : du.getAbsoluteLink();
387     }
388 
389     /***
390      * Retrieve the URL for a given script that is part
391      * of a skin. The script is stored in the
392      * WEBAPP/resources/ui/skins/<SKIN> directory
393      */
394     public String getScript(String filename)
395     {
396         ServerData sd = Turbine.getDefaultServerData();
397         DataURI du = new DataURI(sd);
398 
399         StringBuffer sb = new StringBuffer();
400 
401         sb.append(resourcesDirectory).
402                 append('/').
403                 append(skinsDirectory).
404                 append('/').
405                 append(getSkin()).
406                 append('/').
407                 append(stripSlashes(filename));
408 
409         du.setScriptName(sb.toString());
410         return wantRelative ? du.getRelativeLink() : du.getAbsoluteLink();
411     }
412 
413     /***
414      * Load the specified skin. In development mode
415      * this may occur frequently as the skin properties
416      * are being changed.
417      */
418     private void loadSkin()
419     {
420         skinProperties = new Properties();
421 
422         try
423         {
424             StringBuffer sb = new StringBuffer();
425 
426             sb.append(resourcesDirectory).
427                     append('/').
428                     append(skinsDirectory).
429                     append('/').
430                     append(getSkin()).
431                     append('/').
432                     append(SKIN_PROPS_FILE);
433 
434             InputStream is = TurbineServlet.getResourceAsStream(sb.toString());
435 
436             skinProperties.load(is);
437         }
438         catch (Exception e)
439         {
440             log.error("Cannot load skin: " + skinName);
441         }
442     }
443 
444     /***
445      * Set the skin name to the skin from the TR.props
446      * file. If the property is not present use the
447      * default skin.
448      */
449     public void setSkin()
450     {
451         this.skinName = Turbine.getConfiguration()
452                 .getString(SKIN_PROPERTY,
453                         SKIN_PROPERTY_DEFAULT);
454     }
455 
456     /***
457      * Set the skin name to the specified skin.
458      *
459      * @param skinName the skin name to use.
460      */
461     public void setSkin(String skinName)
462     {
463         this.skinName = skinName;
464     }
465 
466     /***
467      * Set the skin name when the tool is configured to be
468      * loaded on a per-request basis. By default it calls getSkin
469      * to return the skin specified in TR.properties. Developers can
470      * write a subclass of UIManager that overrides this method to
471      * determine the skin to use based on information held in the request.
472      *
473      * @param data a RunData instance
474      */
475     protected void setSkin(RunData data)
476     {
477         setSkin();
478     }
479 
480     /***
481      * Set the skin name when the tool is configured to be
482      * loaded on a per-session basis. It the user's temp hashmap contains
483      * a value in the attribute specified by the String constant SKIN_ATTRIBUTE
484      * then that is returned. Otherwise it calls getSkin to return the skin
485      * specified in TR.properties.
486      *
487      * @param user a User instance
488      */
489     protected void setSkin(User user)
490     {
491         if (user.getTemp(SKIN_ATTRIBUTE) == null)
492         {
493             setSkin();
494         }
495         else
496         {
497             setSkin((String) user.getTemp(SKIN_ATTRIBUTE));
498         }
499     }
500 
501     /***
502      * Set the skin name user's temp hashmap for the current session.
503      *
504      * @param user a User instance
505      * @param skin the skin name for the session
506      */
507     public static void setSkin(User user, String skin)
508     {
509         user.setTemp(SKIN_ATTRIBUTE, skin);
510     }
511 }