View Javadoc
1   /*
2    * Copyright (C) 2003-2008 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.services.wcm.skin;
18  
19  import java.net.URL;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Map.Entry;
27  
28  import javax.jcr.Node;
29  import javax.jcr.RepositoryException;
30  import javax.servlet.ServletContext;
31  
32  import org.apache.commons.httpclient.methods.GetMethod;
33  import org.apache.commons.lang.StringUtils;
34  
35  
36  import org.exoplatform.container.PortalContainer;
37  import org.exoplatform.container.RootContainer.PortalContainerPostCreateTask;
38  import org.exoplatform.container.RootContainer.PortalContainerInitTask;
39  import org.exoplatform.portal.resource.SkinConfig;
40  import org.exoplatform.portal.resource.SkinKey;
41  import org.exoplatform.portal.resource.SkinService;
42  import org.exoplatform.portal.resource.SkinVisitor;
43  
44  import org.exoplatform.services.jcr.RepositoryService;
45  import org.exoplatform.services.jcr.ext.common.SessionProvider;
46  import org.exoplatform.services.log.ExoLogger;
47  import org.exoplatform.services.log.Log;
48  import org.exoplatform.services.wcm.core.WCMConfigurationService;
49  import org.exoplatform.services.wcm.portal.LivePortalManagerService;
50  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
51  import org.gatein.wci.WebApp;
52  import org.picocontainer.Startable;
53  
54  /**
55   * Created by The eXo Platform SAS Author : Hoa.Pham hoa.pham@exoplatform.com
56   * Apr 9, 2008
57   */
58  public class XSkinService implements Startable {
59    /* Don't use "-", "_", ":" "#" "%" */
60    final static String     SEPARATOR             = "/"; 
61    final static String     MODULE_NAME_PATTERN   = "{repositoryName}"+SEPARATOR+"{portalName}";
62    final static String     MODULE_NAME_REGEXP    = "(.*)"+SEPARATOR+"(.*)";
63    final static String     MODULE_PARAM          = "moduleName";
64    final static String     SKIN_PARAM            = "skinName";
65    final static String     CONTEXT_PARAM         = "context";
66    final static String     SITENAME_PARAM        = "siteName";
67    
68    /** The Constant SKIN_PATH_REGEXP. */
69    public final static String      SKIN_PATH_REGEXP     = "/(.*)/css/jcr/"+MODULE_NAME_REGEXP+"/(.*)/(.*).css";
70  
71    /** The Constant SKIN_PATH_PATTERN. */
72    private final static String     SKIN_PATH_PATTERN    = "/{docBase}/css/jcr/{moduleName}/(.*)/Stylesheet.css";  
73  
74    /** The log. */
75    private static final Log LOG = ExoLogger.getLogger(XSkinService.class.getName());
76  
77    /** The configuration service. */
78    private WCMConfigurationService configurationService;
79  
80    /** The skin service. */
81    private SkinService skinService ;
82  
83    /** The servlet context. */
84    private ServletContext servletContext;
85  
86    /**
87     * Instantiates a new extended skin service to manage skin for web content.
88     *
89     * @param livePortalService the LivePortalManagerService service
90     * @throws Exception the exception
91     */
92    public XSkinService(LivePortalManagerService livePortalService) throws Exception {
93      this.skinService = WCMCoreUtils.getService(SkinService.class);
94      this.skinService.addResourceResolver(new WCMSkinResourceResolver(this.skinService, livePortalService));
95      this.configurationService = WCMCoreUtils.getService(WCMConfigurationService.class);
96      this.servletContext = WCMCoreUtils.getService(ServletContext.class);
97    }
98  
99    /**
100    * Gets the active style sheet of the specified web content node
101    * 
102    * @param webcontent the web content node to get style sheet
103    * @return the active style sheet
104    * @throws Exception the exception
105    */
106   public String getActiveStylesheet(Node webcontent) throws Exception {
107     return WCMCoreUtils.getActiveStylesheet(webcontent);
108   }
109 
110   /**
111    * Update portal skin on modify.
112    *
113    * @param portal the portal
114    * @param cssFile the css file
115    *
116    * @throws Exception the exception
117    */
118   public void updatePortalSkinOnModify(Node portal, Node cssFile) throws Exception {
119     String sharedPortalName = configurationService.getSharedPortalName();
120     if (sharedPortalName.equals(portal.getName())) {
121       addSharedPortalSkin(portal);
122     } else {
123       addPortalSkin(portal);
124     }
125   }
126 
127   /**
128    * Update portal skin on remove.
129    *
130    * @param portal the portal
131    * @param cssFile the css file
132    *
133    * @throws Exception the exception
134    */
135   public void updatePortalSkinOnRemove(Node portal, Node cssFile) throws Exception {
136     updatePortalSkinOnModify(portal, cssFile);
137   }
138 
139   /**
140    * Adds the portal skin.
141    *
142    * @param portalNode the portal
143    *
144    * @throws Exception the exception
145    */
146   private void addPortalSkin(Node portalNode) throws Exception {
147     String moduleName = createModuleName(portalNode.getName());
148     String skinPath = StringUtils.replaceOnce(SKIN_PATH_PATTERN, "{moduleName}",moduleName)
149                                  .replaceFirst("\\{docBase\\}",
150                                                servletContext.getServletContextName());
151     Iterator<String> iterator = skinService.getAvailableSkinNames().iterator();
152     if (iterator.hasNext() == false) {
153       skinPath = StringUtils.replaceOnce(skinPath,"(.*)", "Default");
154       skinService.invalidateCachedSkin(skinPath);
155       skinService.addSkin(moduleName, "Default", skinPath);
156     } else {
157       while (iterator.hasNext()) {
158         String skinName = iterator.next();
159         skinPath = StringUtils.replaceOnce(skinPath,"(.*)",skinName);
160         skinService.invalidateCachedSkin(skinPath);        
161         skinService.addSkin(moduleName, skinName, skinPath);
162       }
163     }
164   }
165 
166   /**
167    * Adds the shared portal skin.
168    *
169    * @param portalNode the portal
170    *
171    * @throws Exception the exception
172    */
173   private void addSharedPortalSkin(Node portalNode) throws Exception {
174     String moduleName = createModuleName(portalNode.getName());
175     String skinPath = StringUtils.replaceOnce(SKIN_PATH_PATTERN, "{moduleName}", moduleName)
176                                  .replaceFirst("\\{docBase\\}",
177                                                servletContext.getServletContextName());
178     for (Iterator<String> iterator = skinService.getAvailableSkinNames().iterator(); iterator.hasNext();) {
179       String skinName = iterator.next();
180       skinPath = StringUtils.replaceOnce(skinPath, "(.*)", skinName);
181       skinService.invalidateCachedSkin(skinPath);
182       skinService.addPortalSkin(moduleName, skinName, skinPath);
183     }
184   }
185 
186   /* (non-Javadoc)
187    * @see org.picocontainer.Startable#start()
188    */
189   public void start() {
190     /**
191      * Because all skins are added via {@link PortalContainerInitTask} (which run after services start) in {@link org.exoplatform.portal.resource.GateInSkinConfigDeployer#add(WebApp, URL)}
192      * And we need to add custom styleSheet after all skins are configured and ready.
193      * So, we must use {@link PortalContainerPostCreateTask} (it's run after all init tasks executed) for doing that.
194      */
195     final PortalContainerPostCreateTask task = new PortalContainerPostCreateTask() {
196       public void execute(ServletContext context, PortalContainer portalContainer) {
197         SessionProvider sessionProvider = SessionProvider.createSystemProvider();
198         try {
199           LivePortalManagerService livePortalManagerService = portalContainer.getComponentInstanceOfType(LivePortalManagerService.class);
200           List<Node> livePortals = livePortalManagerService.getLivePortals(sessionProvider);
201           for (Node portal : livePortals) {
202             addPortalSkin(portal);
203           }
204           Node sharedPortal = livePortalManagerService.getLiveSharedPortal(sessionProvider);
205           addSharedPortalSkin(sharedPortal);
206         } catch (Exception e) {
207           if (LOG.isErrorEnabled()) {
208             LOG.error("Exception when start XSkinService", e);
209           }
210         } finally {
211           sessionProvider.close();
212         }
213       }
214     };
215     PortalContainer.addInitTask(this.servletContext, task, null);
216   }
217 
218   /* (non-Javadoc)
219    * @see org.picocontainer.Startable#stop()
220    */
221   public void stop() {
222   }
223 
224   static String createModuleName(String siteName){
225     String repoName;
226     try{
227       repoName = WCMCoreUtils.getRepository().getConfiguration().getName();
228     }catch(NullPointerException e){
229       repoName = WCMCoreUtils.getService(RepositoryService.class).getConfig().getDefaultRepositoryName();
230     }
231     String moduleName = StringUtils.replaceOnce(MODULE_NAME_PATTERN, "{repositoryName}", repoName);
232     moduleName = StringUtils.replaceOnce(moduleName,"{portalName}",siteName);
233     return moduleName;
234   }
235   
236   static Map<String,String> getSkinParams(String path){
237     if (!path.matches(SKIN_PATH_REGEXP)) return null;
238     String moduleName;
239     String skinName;
240     String siteName;
241     String context;
242     String[] elements = path.split("/");
243     if (XSkinService.SEPARATOR.equals("/")){
244       context = elements[4];
245       siteName = elements[5];
246       skinName = elements[6];      
247     }else{
248       context = elements[4].split(XSkinService.SEPARATOR)[0];
249       siteName = elements[4].split(XSkinService.SEPARATOR)[1];
250       skinName = elements[5];
251     }
252     
253     moduleName = StringUtils.replaceOnce(MODULE_NAME_PATTERN, "{repositoryName}", context);
254     moduleName = StringUtils.replaceOnce(moduleName,"{portalName}",siteName);
255     Map<String,String> params = new HashMap<String,String>();
256     params.put(MODULE_PARAM, moduleName);
257     params.put(SKIN_PARAM, skinName);
258     params.put(SITENAME_PARAM, siteName);
259     params.put(CONTEXT_PARAM, context);    
260     return params;  
261   }
262   
263   
264 }