001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.lar;
016    
017    import com.liferay.portal.kernel.cluster.ClusterExecutorUtil;
018    import com.liferay.portal.kernel.cluster.ClusterRequest;
019    import com.liferay.portal.kernel.lar.PortletDataContext;
020    import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.util.ColorSchemeFactoryUtil;
024    import com.liferay.portal.kernel.util.FileUtil;
025    import com.liferay.portal.kernel.util.GetterUtil;
026    import com.liferay.portal.kernel.util.MapUtil;
027    import com.liferay.portal.kernel.util.MethodHandler;
028    import com.liferay.portal.kernel.util.MethodKey;
029    import com.liferay.portal.kernel.util.StringBundler;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.StringUtil;
032    import com.liferay.portal.kernel.util.Time;
033    import com.liferay.portal.kernel.util.Validator;
034    import com.liferay.portal.kernel.xml.Attribute;
035    import com.liferay.portal.kernel.xml.Element;
036    import com.liferay.portal.kernel.zip.ZipReader;
037    import com.liferay.portal.kernel.zip.ZipReaderFactoryUtil;
038    import com.liferay.portal.model.Layout;
039    import com.liferay.portal.model.LayoutSet;
040    import com.liferay.portal.model.PortletConstants;
041    import com.liferay.portal.service.LayoutLocalServiceUtil;
042    import com.liferay.portal.service.LayoutSetLocalServiceUtil;
043    import com.liferay.portal.theme.ThemeLoader;
044    import com.liferay.portal.theme.ThemeLoaderFactory;
045    import com.liferay.portal.util.PortalUtil;
046    import com.liferay.portal.util.PropsValues;
047    
048    import java.io.InputStream;
049    
050    import java.util.List;
051    import java.util.Map;
052    
053    import org.apache.commons.lang.time.StopWatch;
054    
055    /**
056     * @author Mate Thurzo
057     */
058    public class ThemeImporter {
059    
060            public void importTheme(
061                            PortletDataContext portletDataContext, Layout layout)
062                    throws Exception {
063    
064                    boolean importTheme = MapUtil.getBoolean(
065                            portletDataContext.getParameterMap(), PortletDataHandlerKeys.THEME);
066    
067                    if (_log.isDebugEnabled()) {
068                            _log.debug("Import theme " + importTheme);
069                    }
070    
071                    if (!importTheme || layout.isInheritLookAndFeel()) {
072                            return;
073                    }
074    
075                    StringBundler sb = new StringBundler(4);
076    
077                    sb.append("theme");
078                    sb.append(StringPool.DASH);
079                    sb.append(layout.getLayoutId());
080                    sb.append(".zip");
081    
082                    InputStream themeZipInputStream =
083                            portletDataContext.getZipEntryAsInputStream(sb.toString());
084    
085                    if (themeZipInputStream != null) {
086                            String themeId = layout.getThemeId();
087                            String colorSchemeId = layout.getColorSchemeId();
088    
089                            long groupId = portletDataContext.getGroupId();
090                            boolean privateLayout = portletDataContext.isPrivateLayout();
091    
092                            Map<Long, Layout> newLayoutsMap =
093                                    (Map<Long, Layout>)portletDataContext.getNewPrimaryKeysMap(
094                                            Layout.class + ".layout");
095    
096                            Layout importedLayout = newLayoutsMap.get(layout.getLayoutId());
097    
098                            long layoutId = importedLayout.getLayoutId();
099    
100                            String importThemeId = importTheme(
101                                    groupId, privateLayout, layoutId, themeZipInputStream);
102    
103                            if (importThemeId != null) {
104                                    themeId = importThemeId;
105    
106                                    colorSchemeId =
107                                            ColorSchemeFactoryUtil.getDefaultRegularColorSchemeId();
108                            }
109    
110                            LayoutLocalServiceUtil.updateLookAndFeel(
111                                    groupId, privateLayout, layoutId, themeId, colorSchemeId,
112                                    importedLayout.getCss(), false);
113                    }
114            }
115    
116            public void importTheme(
117                            PortletDataContext portletDataContext, LayoutSet layoutSet)
118                    throws Exception {
119    
120                    boolean importTheme = MapUtil.getBoolean(
121                            portletDataContext.getParameterMap(), PortletDataHandlerKeys.THEME);
122                    boolean importThemeSettings = MapUtil.getBoolean(
123                            portletDataContext.getParameterMap(),
124                            PortletDataHandlerKeys.THEME_REFERENCE);
125    
126                    if (_log.isDebugEnabled()) {
127                            _log.debug("Import theme " + importTheme);
128                            _log.debug("Import theme settings " + importThemeSettings);
129                    }
130    
131                    Element importDataRootElement =
132                            portletDataContext.getImportDataRootElement();
133                    Element headerElement = importDataRootElement.element("header");
134    
135                    String themeId = layoutSet.getThemeId();
136                    String colorSchemeId = layoutSet.getColorSchemeId();
137    
138                    if (importThemeSettings) {
139                            Attribute themeIdAttribute = headerElement.attribute("theme-id");
140    
141                            if (themeIdAttribute != null) {
142                                    themeId = themeIdAttribute.getValue();
143                            }
144    
145                            Attribute colorSchemeIdAttribute = headerElement.attribute(
146                                    "color-scheme-id");
147    
148                            if (colorSchemeIdAttribute != null) {
149                                    colorSchemeId = colorSchemeIdAttribute.getValue();
150                            }
151                    }
152    
153                    InputStream themeZipInputStream = null;
154    
155                    if (importTheme) {
156                            themeZipInputStream = portletDataContext.getZipEntryAsInputStream(
157                                    "theme.zip");
158                    }
159    
160                    if (themeZipInputStream != null) {
161                            StopWatch stopWatch = null;
162    
163                            if (_log.isDebugEnabled()) {
164                                    stopWatch = new StopWatch();
165    
166                                    stopWatch.start();
167                            }
168    
169                            String importThemeId = importTheme(
170                                    layoutSet.getGroupId(), layoutSet.isPrivateLayout(), 0,
171                                    themeZipInputStream);
172    
173                            if (importThemeId != null) {
174                                    themeId = importThemeId;
175                                    colorSchemeId =
176                                            ColorSchemeFactoryUtil.getDefaultRegularColorSchemeId();
177                            }
178    
179                            if (_log.isDebugEnabled()) {
180                                    _log.debug(
181                                            "Importing theme takes " + stopWatch.getTime() + " ms");
182                            }
183                    }
184    
185                    String css = GetterUtil.getString(headerElement.elementText("css"));
186    
187                    LayoutSetLocalServiceUtil.updateLookAndFeel(
188                            layoutSet.getGroupId(), layoutSet.isPrivateLayout(), themeId,
189                            colorSchemeId, css, false);
190            }
191    
192            protected String importTheme(
193                            long groupId, boolean privateLayout, long layoutId,
194                            InputStream themeZipInputStream)
195                    throws Exception {
196    
197                    ThemeLoader themeLoader = ThemeLoaderFactory.getDefaultThemeLoader();
198    
199                    if (themeLoader == null) {
200                            _log.error("No theme loaders are deployed");
201    
202                            return null;
203                    }
204    
205                    ZipReader zipReader = ZipReaderFactoryUtil.getZipReader(
206                            themeZipInputStream);
207    
208                    String lookAndFeelXML = zipReader.getEntryAsString(
209                            "liferay-look-and-feel.xml");
210    
211                    StringBundler sb = new StringBundler();
212    
213                    sb.append(String.valueOf(groupId));
214    
215                    if (privateLayout) {
216                            sb.append("-private");
217                    }
218                    else {
219                            sb.append("-public");
220                    }
221    
222                    if (Validator.isNotNull(layoutId)) {
223                            sb.append(StringPool.DASH);
224                            sb.append(String.valueOf(layoutId));
225                    }
226    
227                    if (PropsValues.THEME_LOADER_NEW_THEME_ID_ON_IMPORT) {
228                            sb.append(StringPool.DASH);
229                            sb.append(Time.getShortTimestamp());
230                    }
231    
232                    String themeId = sb.toString();
233    
234                    lookAndFeelXML = StringUtil.replace(
235                            lookAndFeelXML,
236                            new String[] {
237                                    "[$GROUP_ID$]", "[$THEME_ID$]", "[$THEME_NAME$]"
238                            },
239                            new String[] {
240                                    String.valueOf(groupId), themeId, themeId
241                            }
242                    );
243    
244                    FileUtil.deltree(
245                            themeLoader.getFileStorage() + StringPool.SLASH + themeId);
246    
247                    List<String> zipEntries = zipReader.getEntries();
248    
249                    for (String zipEntry : zipEntries) {
250                            String key = zipEntry;
251    
252                            sb = new StringBundler(5);
253    
254                            sb.append(themeLoader.getFileStorage());
255                            sb.append(StringPool.SLASH);
256                            sb.append(themeId);
257                            sb.append(StringPool.SLASH);
258                            sb.append(key);
259    
260                            String fileName = sb.toString();
261    
262                            if (key.equals("liferay-look-and-feel.xml")) {
263                                    FileUtil.write(fileName, lookAndFeelXML.getBytes());
264                            }
265                            else {
266                                    InputStream is = zipReader.getEntryAsInputStream(zipEntry);
267    
268                                    FileUtil.write(fileName, is);
269                            }
270                    }
271    
272                    themeLoader.loadThemes();
273    
274                    ClusterRequest clusterRequest = ClusterRequest.createMulticastRequest(
275                            _loadThemesMethodHandler, true);
276    
277                    clusterRequest.setFireAndForget(true);
278    
279                    ClusterExecutorUtil.execute(clusterRequest);
280    
281                    themeId +=
282                            PortletConstants.WAR_SEPARATOR +
283                                    themeLoader.getServletContextName();
284    
285                    return PortalUtil.getJsSafePortletId(themeId);
286            }
287    
288            private static Log _log = LogFactoryUtil.getLog(ThemeImporter.class);
289    
290            private static MethodHandler _loadThemesMethodHandler = new MethodHandler(
291                    new MethodKey(ThemeLoaderFactory.class, "loadThemes"));
292    
293    }