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.LARFileException;
018    import com.liferay.portal.LARTypeException;
019    import com.liferay.portal.LayoutImportException;
020    import com.liferay.portal.LayoutPrototypeException;
021    import com.liferay.portal.LocaleException;
022    import com.liferay.portal.MissingReferenceException;
023    import com.liferay.portal.NoSuchLayoutException;
024    import com.liferay.portal.NoSuchLayoutPrototypeException;
025    import com.liferay.portal.NoSuchLayoutSetPrototypeException;
026    import com.liferay.portal.kernel.backgroundtask.BackgroundTaskThreadLocal;
027    import com.liferay.portal.kernel.cluster.ClusterExecutorUtil;
028    import com.liferay.portal.kernel.cluster.ClusterRequest;
029    import com.liferay.portal.kernel.language.LanguageUtil;
030    import com.liferay.portal.kernel.lar.ExportImportHelperUtil;
031    import com.liferay.portal.kernel.lar.ExportImportThreadLocal;
032    import com.liferay.portal.kernel.lar.ManifestSummary;
033    import com.liferay.portal.kernel.lar.MissingReference;
034    import com.liferay.portal.kernel.lar.MissingReferences;
035    import com.liferay.portal.kernel.lar.PortletDataContext;
036    import com.liferay.portal.kernel.lar.PortletDataContextFactoryUtil;
037    import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
038    import com.liferay.portal.kernel.lar.PortletDataHandlerStatusMessageSenderUtil;
039    import com.liferay.portal.kernel.lar.StagedModelDataHandlerUtil;
040    import com.liferay.portal.kernel.lar.UserIdStrategy;
041    import com.liferay.portal.kernel.log.Log;
042    import com.liferay.portal.kernel.log.LogFactoryUtil;
043    import com.liferay.portal.kernel.search.Indexer;
044    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
045    import com.liferay.portal.kernel.util.ArrayUtil;
046    import com.liferay.portal.kernel.util.ColorSchemeFactoryUtil;
047    import com.liferay.portal.kernel.util.Constants;
048    import com.liferay.portal.kernel.util.FileUtil;
049    import com.liferay.portal.kernel.util.GetterUtil;
050    import com.liferay.portal.kernel.util.LocaleUtil;
051    import com.liferay.portal.kernel.util.MapUtil;
052    import com.liferay.portal.kernel.util.MethodHandler;
053    import com.liferay.portal.kernel.util.MethodKey;
054    import com.liferay.portal.kernel.util.ReleaseInfo;
055    import com.liferay.portal.kernel.util.StringPool;
056    import com.liferay.portal.kernel.util.StringUtil;
057    import com.liferay.portal.kernel.util.Time;
058    import com.liferay.portal.kernel.util.Tuple;
059    import com.liferay.portal.kernel.util.UnicodeProperties;
060    import com.liferay.portal.kernel.util.Validator;
061    import com.liferay.portal.kernel.xml.Attribute;
062    import com.liferay.portal.kernel.xml.Document;
063    import com.liferay.portal.kernel.xml.Element;
064    import com.liferay.portal.kernel.xml.SAXReaderUtil;
065    import com.liferay.portal.kernel.zip.ZipReader;
066    import com.liferay.portal.kernel.zip.ZipReaderFactoryUtil;
067    import com.liferay.portal.model.Group;
068    import com.liferay.portal.model.Layout;
069    import com.liferay.portal.model.LayoutConstants;
070    import com.liferay.portal.model.LayoutPrototype;
071    import com.liferay.portal.model.LayoutSet;
072    import com.liferay.portal.model.LayoutSetPrototype;
073    import com.liferay.portal.model.Portlet;
074    import com.liferay.portal.model.PortletConstants;
075    import com.liferay.portal.model.User;
076    import com.liferay.portal.security.permission.PermissionCacheUtil;
077    import com.liferay.portal.service.GroupLocalServiceUtil;
078    import com.liferay.portal.service.LayoutLocalServiceUtil;
079    import com.liferay.portal.service.LayoutPrototypeLocalServiceUtil;
080    import com.liferay.portal.service.LayoutSetLocalServiceUtil;
081    import com.liferay.portal.service.LayoutSetPrototypeLocalServiceUtil;
082    import com.liferay.portal.service.PortletLocalServiceUtil;
083    import com.liferay.portal.service.ServiceContext;
084    import com.liferay.portal.service.ServiceContextThreadLocal;
085    import com.liferay.portal.service.persistence.LayoutUtil;
086    import com.liferay.portal.service.persistence.UserUtil;
087    import com.liferay.portal.servlet.filters.cache.CacheUtil;
088    import com.liferay.portal.theme.ThemeLoader;
089    import com.liferay.portal.theme.ThemeLoaderFactory;
090    import com.liferay.portal.util.PortalUtil;
091    import com.liferay.portal.util.PropsValues;
092    import com.liferay.portlet.journal.model.JournalArticle;
093    import com.liferay.portlet.journalcontent.util.JournalContentUtil;
094    import com.liferay.portlet.sites.util.Sites;
095    
096    import java.io.File;
097    import java.io.InputStream;
098    
099    import java.util.ArrayList;
100    import java.util.Date;
101    import java.util.List;
102    import java.util.Locale;
103    import java.util.Map;
104    
105    import org.apache.commons.lang.time.StopWatch;
106    
107    /**
108     * @author Brian Wing Shun Chan
109     * @author Joel Kozikowski
110     * @author Charles May
111     * @author Raymond Aug??
112     * @author Jorge Ferrer
113     * @author Bruno Farache
114     * @author Wesley Gong
115     * @author Zsigmond Rab
116     * @author Douglas Wong
117     * @author Julio Camarero
118     * @author Zsolt Berentey
119     */
120    public class LayoutImporter {
121    
122            public void importLayouts(
123                            long userId, long groupId, boolean privateLayout,
124                            Map<String, String[]> parameterMap, File file)
125                    throws Exception {
126    
127                    try {
128                            ExportImportThreadLocal.setLayoutImportInProcess(true);
129    
130                            doImportLayouts(userId, groupId, privateLayout, parameterMap, file);
131                    }
132                    finally {
133                            ExportImportThreadLocal.setLayoutImportInProcess(false);
134    
135                            CacheUtil.clearCache();
136                            JournalContentUtil.clearCache();
137                            PermissionCacheUtil.clearCache();
138                    }
139            }
140    
141            public MissingReferences validateFile(
142                            long userId, long groupId, boolean privateLayout,
143                            Map<String, String[]> parameterMap, File file)
144                    throws Exception {
145    
146                    try {
147                            ExportImportThreadLocal.setLayoutValidationInProcess(true);
148    
149                            LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
150                                    groupId, privateLayout);
151    
152                            ZipReader zipReader = ZipReaderFactoryUtil.getZipReader(file);
153    
154                            PortletDataContext portletDataContext =
155                                    PortletDataContextFactoryUtil.createImportPortletDataContext(
156                                            layoutSet.getCompanyId(), groupId, parameterMap, null,
157                                            zipReader);
158    
159                            validateFile(portletDataContext);
160    
161                            MissingReferences missingReferences =
162                                    ExportImportHelperUtil.validateMissingReferences(
163                                            userId, groupId, parameterMap, file);
164    
165                            Map<String, MissingReference> dependencyMissingReferences =
166                                    missingReferences.getDependencyMissingReferences();
167    
168                            if (!dependencyMissingReferences.isEmpty()) {
169                                    throw new MissingReferenceException(missingReferences);
170                            }
171    
172                            return missingReferences;
173                    }
174                    finally {
175                            ExportImportThreadLocal.setLayoutValidationInProcess(false);
176                    }
177            }
178    
179            protected void deleteMissingLayouts(
180                            List<String> sourceLayoutUuids, List<Layout> previousLayouts,
181                            ServiceContext serviceContext)
182                    throws Exception {
183    
184                    if (_log.isDebugEnabled() && !sourceLayoutUuids.isEmpty()) {
185                            _log.debug("Delete missing layouts");
186                    }
187    
188                    for (Layout layout : previousLayouts) {
189                            if (!sourceLayoutUuids.contains(layout.getUuid())) {
190                                    try {
191                                            LayoutLocalServiceUtil.deleteLayout(
192                                                    layout, false, serviceContext);
193                                    }
194                                    catch (NoSuchLayoutException nsle) {
195                                    }
196                            }
197                    }
198            }
199    
200            protected void doImportLayouts(
201                            long userId, long groupId, boolean privateLayout,
202                            Map<String, String[]> parameterMap, File file)
203                    throws Exception {
204    
205                    boolean deleteMissingLayouts = MapUtil.getBoolean(
206                            parameterMap, PortletDataHandlerKeys.DELETE_MISSING_LAYOUTS,
207                            Boolean.TRUE.booleanValue());
208                    boolean deletePortletData = MapUtil.getBoolean(
209                            parameterMap, PortletDataHandlerKeys.DELETE_PORTLET_DATA);
210                    boolean importCategories = MapUtil.getBoolean(
211                            parameterMap, PortletDataHandlerKeys.CATEGORIES);
212                    boolean importPermissions = MapUtil.getBoolean(
213                            parameterMap, PortletDataHandlerKeys.PERMISSIONS);
214                    boolean importTheme = MapUtil.getBoolean(
215                            parameterMap, PortletDataHandlerKeys.THEME);
216                    boolean importThemeSettings = MapUtil.getBoolean(
217                            parameterMap, PortletDataHandlerKeys.THEME_REFERENCE);
218                    boolean importLogo = MapUtil.getBoolean(
219                            parameterMap, PortletDataHandlerKeys.LOGO);
220                    boolean importLayoutSetSettings = MapUtil.getBoolean(
221                            parameterMap, PortletDataHandlerKeys.LAYOUT_SET_SETTINGS);
222    
223                    boolean layoutSetPrototypeLinkEnabled = MapUtil.getBoolean(
224                            parameterMap,
225                            PortletDataHandlerKeys.LAYOUT_SET_PROTOTYPE_LINK_ENABLED, true);
226    
227                    Group group = GroupLocalServiceUtil.getGroup(groupId);
228    
229                    if (group.isLayoutSetPrototype()) {
230                            layoutSetPrototypeLinkEnabled = false;
231                    }
232    
233                    String layoutsImportMode = MapUtil.getString(
234                            parameterMap, PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE,
235                            PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_UUID);
236                    String userIdStrategy = MapUtil.getString(
237                            parameterMap, PortletDataHandlerKeys.USER_ID_STRATEGY);
238    
239                    if (_log.isDebugEnabled()) {
240                            _log.debug("Delete portlet data " + deletePortletData);
241                            _log.debug("Import categories " + importCategories);
242                            _log.debug("Import permissions " + importPermissions);
243                            _log.debug("Import theme " + importTheme);
244                    }
245    
246                    StopWatch stopWatch = null;
247    
248                    if (_log.isInfoEnabled()) {
249                            stopWatch = new StopWatch();
250    
251                            stopWatch.start();
252                    }
253    
254                    LayoutCache layoutCache = new LayoutCache();
255    
256                    LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
257                            groupId, privateLayout);
258    
259                    long companyId = layoutSet.getCompanyId();
260    
261                    User user = UserUtil.findByPrimaryKey(userId);
262    
263                    UserIdStrategy strategy = _portletImporter.getUserIdStrategy(
264                            user, userIdStrategy);
265    
266                    if (BackgroundTaskThreadLocal.hasBackgroundTask()) {
267                            ManifestSummary manifestSummary =
268                                    ExportImportHelperUtil.getManifestSummary(
269                                            userId, groupId, parameterMap, file);
270    
271                            PortletDataHandlerStatusMessageSenderUtil.sendStatusMessage(
272                                    "layout", manifestSummary);
273                    }
274    
275                    ZipReader zipReader = ZipReaderFactoryUtil.getZipReader(file);
276    
277                    PortletDataContext portletDataContext =
278                            PortletDataContextFactoryUtil.createImportPortletDataContext(
279                                    companyId, groupId, parameterMap, strategy, zipReader);
280    
281                    portletDataContext.setPortetDataContextListener(
282                            new PortletDataContextListenerImpl(portletDataContext));
283    
284                    portletDataContext.setPrivateLayout(privateLayout);
285    
286                    // Zip
287    
288                    InputStream themeZip = null;
289    
290                    validateFile(portletDataContext);
291    
292                    // Company id
293    
294                    long sourceCompanyId = GetterUtil.getLong(
295                            _headerElement.attributeValue("company-id"));
296    
297                    portletDataContext.setSourceCompanyId(sourceCompanyId);
298    
299                    // Company group id
300    
301                    long sourceCompanyGroupId = GetterUtil.getLong(
302                            _headerElement.attributeValue("company-group-id"));
303    
304                    portletDataContext.setSourceCompanyGroupId(sourceCompanyGroupId);
305    
306                    // Group id
307    
308                    long sourceGroupId = GetterUtil.getLong(
309                            _headerElement.attributeValue("group-id"));
310    
311                    portletDataContext.setSourceGroupId(sourceGroupId);
312    
313                    // User personal site group id
314    
315                    long sourceUserPersonalSiteGroupId = GetterUtil.getLong(
316                            _headerElement.attributeValue("user-personal-site-group-id"));
317    
318                    portletDataContext.setSourceUserPersonalSiteGroupId(
319                            sourceUserPersonalSiteGroupId);
320    
321                    // Layout and layout set prototype
322    
323                    String layoutSetPrototypeUuid = _layoutsElement.attributeValue(
324                            "layout-set-prototype-uuid");
325    
326                    String larType = _headerElement.attributeValue("type");
327    
328                    if (group.isLayoutPrototype() && larType.equals("layout-prototype")) {
329                            deleteMissingLayouts = false;
330    
331                            LayoutPrototype layoutPrototype =
332                                    LayoutPrototypeLocalServiceUtil.getLayoutPrototype(
333                                            group.getClassPK());
334    
335                            String layoutPrototypeUuid = GetterUtil.getString(
336                                    _headerElement.attributeValue("type-uuid"));
337    
338                            LayoutPrototype existingLayoutPrototype = null;
339    
340                            if (Validator.isNotNull(layoutPrototypeUuid)) {
341                                    try {
342                                            existingLayoutPrototype =
343                                                    LayoutPrototypeLocalServiceUtil.
344                                                            getLayoutPrototypeByUuidAndCompanyId(
345                                                                    layoutPrototypeUuid, companyId);
346                                    }
347                                    catch (NoSuchLayoutPrototypeException nslpe) {
348                                    }
349                            }
350    
351                            if (existingLayoutPrototype == null) {
352                                    List<Layout> layouts =
353                                            LayoutLocalServiceUtil.getLayoutsByLayoutPrototypeUuid(
354                                                    layoutPrototype.getUuid());
355    
356                                    layoutPrototype.setUuid(layoutPrototypeUuid);
357    
358                                    LayoutPrototypeLocalServiceUtil.updateLayoutPrototype(
359                                            layoutPrototype);
360    
361                                    for (Layout layout : layouts) {
362                                            layout.setLayoutPrototypeUuid(layoutPrototypeUuid);
363    
364                                            LayoutLocalServiceUtil.updateLayout(layout);
365                                    }
366                            }
367                    }
368                    else if (group.isLayoutSetPrototype() &&
369                                     larType.equals("layout-set-prototype")) {
370    
371                            LayoutSetPrototype layoutSetPrototype =
372                                    LayoutSetPrototypeLocalServiceUtil.getLayoutSetPrototype(
373                                            group.getClassPK());
374    
375                            String importedLayoutSetPrototypeUuid = GetterUtil.getString(
376                                    _headerElement.attributeValue("type-uuid"));
377    
378                            LayoutSetPrototype existingLayoutSetPrototype = null;
379    
380                            if (Validator.isNotNull(importedLayoutSetPrototypeUuid)) {
381                                    try {
382                                            existingLayoutSetPrototype =
383                                                    LayoutSetPrototypeLocalServiceUtil.
384                                                            getLayoutSetPrototypeByUuidAndCompanyId(
385                                                                    importedLayoutSetPrototypeUuid, companyId);
386                                    }
387                                    catch (NoSuchLayoutSetPrototypeException nslspe) {
388                                    }
389                            }
390    
391                            if (existingLayoutSetPrototype == null) {
392                                    layoutSetPrototype.setUuid(importedLayoutSetPrototypeUuid);
393    
394                                    LayoutSetPrototypeLocalServiceUtil.updateLayoutSetPrototype(
395                                            layoutSetPrototype);
396                            }
397                    }
398                    else if (larType.equals("layout-set-prototype")) {
399                            layoutSetPrototypeUuid = GetterUtil.getString(
400                                    _headerElement.attributeValue("type-uuid"));
401                    }
402    
403                    ServiceContext serviceContext =
404                            ServiceContextThreadLocal.getServiceContext();
405    
406                    if (Validator.isNotNull(layoutSetPrototypeUuid)) {
407                            layoutSet.setLayoutSetPrototypeUuid(layoutSetPrototypeUuid);
408                            layoutSet.setLayoutSetPrototypeLinkEnabled(
409                                    layoutSetPrototypeLinkEnabled);
410    
411                            LayoutSetLocalServiceUtil.updateLayoutSet(layoutSet);
412                    }
413    
414                    // Look and feel
415    
416                    if (importTheme) {
417                            themeZip = portletDataContext.getZipEntryAsInputStream("theme.zip");
418                    }
419    
420                    // Look and feel
421    
422                    String themeId = layoutSet.getThemeId();
423                    String colorSchemeId = layoutSet.getColorSchemeId();
424    
425                    if (importThemeSettings) {
426                            Attribute themeIdAttribute = _headerElement.attribute("theme-id");
427    
428                            if (themeIdAttribute != null) {
429                                    themeId = themeIdAttribute.getValue();
430                            }
431    
432                            Attribute colorSchemeIdAttribute = _headerElement.attribute(
433                                    "color-scheme-id");
434    
435                            if (colorSchemeIdAttribute != null) {
436                                    colorSchemeId = colorSchemeIdAttribute.getValue();
437                            }
438                    }
439    
440                    if (importLogo) {
441                            String logoPath = _headerElement.attributeValue("logo-path");
442    
443                            byte[] iconBytes = portletDataContext.getZipEntryAsByteArray(
444                                    logoPath);
445    
446                            if (ArrayUtil.isNotEmpty(iconBytes)) {
447                                    File logo = null;
448    
449                                    try {
450                                            logo = FileUtil.createTempFile(iconBytes);
451    
452                                            LayoutSetLocalServiceUtil.updateLogo(
453                                                    groupId, privateLayout, true, logo);
454                                    }
455                                    finally {
456                                            FileUtil.delete(logo);
457                                    }
458                            }
459                            else {
460                                    LayoutSetLocalServiceUtil.updateLogo(
461                                            groupId, privateLayout, false, (File)null);
462                            }
463                    }
464    
465                    if (importLayoutSetSettings) {
466                            String settings = GetterUtil.getString(
467                                    _headerElement.elementText("settings"));
468    
469                            LayoutSetLocalServiceUtil.updateSettings(
470                                    groupId, privateLayout, settings);
471                    }
472    
473                    String css = GetterUtil.getString(_headerElement.elementText("css"));
474    
475                    if (themeZip != null) {
476                            String importThemeId = importTheme(layoutSet, themeZip);
477    
478                            if (importThemeId != null) {
479                                    themeId = importThemeId;
480                                    colorSchemeId =
481                                            ColorSchemeFactoryUtil.getDefaultRegularColorSchemeId();
482                            }
483    
484                            if (_log.isDebugEnabled()) {
485                                    _log.debug(
486                                            "Importing theme takes " + stopWatch.getTime() + " ms");
487                            }
488                    }
489    
490                    boolean wapTheme = false;
491    
492                    LayoutSetLocalServiceUtil.updateLookAndFeel(
493                            groupId, privateLayout, themeId, colorSchemeId, css, wapTheme);
494    
495                    // Read asset categories, asset tags, comments, locks, permissions, and
496                    // ratings entries to make them available to the data handlers through
497                    // the context
498    
499                    if (importPermissions) {
500                            _permissionImporter.readPortletDataPermissions(portletDataContext);
501                    }
502    
503                    _portletImporter.readAssetCategories(portletDataContext);
504                    _portletImporter.readAssetTags(portletDataContext);
505                    _portletImporter.readComments(portletDataContext);
506                    _portletImporter.readExpandoTables(portletDataContext);
507                    _portletImporter.readLocks(portletDataContext);
508                    _portletImporter.readRatingsEntries(portletDataContext);
509    
510                    // Layouts
511    
512                    List<Layout> previousLayouts = LayoutUtil.findByG_P(
513                            groupId, privateLayout);
514    
515                    // Remove layouts that were deleted from the layout set prototype
516    
517                    if (Validator.isNotNull(layoutSetPrototypeUuid) &&
518                            layoutSetPrototypeLinkEnabled) {
519    
520                            LayoutSetPrototype layoutSetPrototype =
521                                    LayoutSetPrototypeLocalServiceUtil.
522                                            getLayoutSetPrototypeByUuidAndCompanyId(
523                                                    layoutSetPrototypeUuid, companyId);
524    
525                            for (Layout layout : previousLayouts) {
526                                    String sourcePrototypeLayoutUuid =
527                                            layout.getSourcePrototypeLayoutUuid();
528    
529                                    if (Validator.isNull(layout.getSourcePrototypeLayoutUuid())) {
530                                            continue;
531                                    }
532    
533                                    Layout sourcePrototypeLayout = LayoutUtil.fetchByUUID_G_P(
534                                            sourcePrototypeLayoutUuid, layoutSetPrototype.getGroupId(),
535                                            true);
536    
537                                    if (sourcePrototypeLayout == null) {
538                                            LayoutLocalServiceUtil.deleteLayout(
539                                                    layout, false, serviceContext);
540                                    }
541                            }
542                    }
543    
544                    List<String> sourceLayoutsUuids = new ArrayList<String>();
545                    List<Layout> newLayouts = new ArrayList<Layout>();
546    
547                    if (_log.isDebugEnabled()) {
548                            if (_layoutElements.size() > 0) {
549                                    _log.debug("Importing layouts");
550                            }
551                    }
552    
553                    for (Element layoutElement : _layoutElements) {
554                            importLayout(
555                                    portletDataContext, sourceLayoutsUuids, newLayouts,
556                                    layoutElement);
557                    }
558    
559                    Element portletsElement = _rootElement.element("portlets");
560    
561                    List<Element> portletElements = portletsElement.elements("portlet");
562    
563                    // Delete portlet data
564    
565                    Map<Long, Layout> newLayoutsMap =
566                            (Map<Long, Layout>)portletDataContext.getNewPrimaryKeysMap(
567                                    Layout.class + ".layout");
568    
569                    if (deletePortletData) {
570                            if (_log.isDebugEnabled()) {
571                                    if (portletElements.size() > 0) {
572                                            _log.debug("Deleting portlet data");
573                                    }
574                            }
575    
576                            for (Element portletElement : portletElements) {
577                                    String portletId = portletElement.attributeValue("portlet-id");
578                                    long layoutId = GetterUtil.getLong(
579                                            portletElement.attributeValue("layout-id"));
580    
581                                    Layout layout = newLayoutsMap.get(layoutId);
582    
583                                    long plid = layout.getPlid();
584    
585                                    portletDataContext.setPlid(plid);
586    
587                                    _portletImporter.deletePortletData(
588                                            portletDataContext, portletId, plid);
589                            }
590                    }
591    
592                    // Import portlets
593    
594                    if (_log.isDebugEnabled()) {
595                            if (portletElements.size() > 0) {
596                                    _log.debug("Importing portlets");
597                            }
598                    }
599    
600                    for (Element portletElement : portletElements) {
601                            String portletPath = portletElement.attributeValue("path");
602                            String portletId = portletElement.attributeValue("portlet-id");
603                            long layoutId = GetterUtil.getLong(
604                                    portletElement.attributeValue("layout-id"));
605                            long oldPlid = GetterUtil.getLong(
606                                    portletElement.attributeValue("old-plid"));
607    
608                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
609                                    portletDataContext.getCompanyId(), portletId);
610    
611                            if (!portlet.isActive() || portlet.isUndeployedPortlet()) {
612                                    continue;
613                            }
614    
615                            Layout layout = newLayoutsMap.get(layoutId);
616    
617                            long plid = LayoutConstants.DEFAULT_PLID;
618    
619                            if (layout != null) {
620                                    plid = layout.getPlid();
621                            }
622    
623                            layout = LayoutUtil.fetchByPrimaryKey(plid);
624    
625                            if ((layout == null) && !group.isCompany()) {
626                                    continue;
627                            }
628    
629                            portletDataContext.setPlid(plid);
630                            portletDataContext.setOldPlid(oldPlid);
631    
632                            Document portletDocument = SAXReaderUtil.read(
633                                    portletDataContext.getZipEntryAsString(portletPath));
634    
635                            portletElement = portletDocument.getRootElement();
636    
637                            // The order of the import is important. You must always import the
638                            // portlet preferences first, then the portlet data, then the
639                            // portlet permissions. The import of the portlet data assumes that
640                            // portlet preferences already exist.
641    
642                            _portletImporter.setPortletScope(
643                                    portletDataContext, portletElement);
644    
645                            long portletPreferencesGroupId = groupId;
646    
647                            Element portletDataElement = portletElement.element("portlet-data");
648    
649                            boolean[] importPortletControls = getImportPortletControls(
650                                    companyId, portletId, parameterMap, portletDataElement);
651    
652                            try {
653                                    if ((layout != null) && !group.isCompany()) {
654                                            portletPreferencesGroupId = layout.getGroupId();
655                                    }
656    
657                                    // Portlet preferences
658    
659                                    _portletImporter.importPortletPreferences(
660                                            portletDataContext, layoutSet.getCompanyId(),
661                                            portletPreferencesGroupId, layout, null, portletElement,
662                                            importPortletControls[2], importPortletControls[0],
663                                            importPortletControls[3], false, importPortletControls[1]);
664    
665                                    // Portlet data
666    
667                                    if (importPortletControls[1]) {
668                                            _portletImporter.importPortletData(
669                                                    portletDataContext, portletId, plid,
670                                                    portletDataElement);
671                                    }
672                            }
673                            finally {
674                                    _portletImporter.resetPortletScope(
675                                            portletDataContext, portletPreferencesGroupId);
676                            }
677    
678                            // Portlet permissions
679    
680                            if (importPermissions) {
681                                    _permissionImporter.importPortletPermissions(
682                                            layoutCache, companyId, groupId, userId, layout,
683                                            portletElement, portletId);
684                            }
685    
686                            // Archived setups
687    
688                            _portletImporter.importPortletPreferences(
689                                    portletDataContext, layoutSet.getCompanyId(), groupId, null,
690                                    null, portletElement, importPortletControls[2],
691                                    importPortletControls[0], importPortletControls[3], false,
692                                    importPortletControls[1]);
693                    }
694    
695                    if (importPermissions) {
696                            if (userId > 0) {
697                                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
698                                            User.class);
699    
700                                    indexer.reindex(userId);
701                            }
702                    }
703    
704                    // Asset links
705    
706                    _portletImporter.readAssetLinks(portletDataContext);
707    
708                    // Delete missing layouts
709    
710                    if (deleteMissingLayouts) {
711                            deleteMissingLayouts(
712                                    sourceLayoutsUuids, previousLayouts, serviceContext);
713                    }
714    
715                    // Page count
716    
717                    layoutSet = LayoutSetLocalServiceUtil.updatePageCount(
718                            groupId, privateLayout);
719    
720                    // Site
721    
722                    GroupLocalServiceUtil.updateSite(groupId, true);
723    
724                    // Last merge time must be the same for merged layouts and the layout
725                    // set
726    
727                    long lastMergeTime = System.currentTimeMillis();
728    
729                    for (Layout layout : newLayouts) {
730                            boolean modifiedTypeSettingsProperties = false;
731    
732                            UnicodeProperties typeSettingsProperties =
733                                    layout.getTypeSettingsProperties();
734    
735                            // Journal article layout type
736    
737                            String articleId = typeSettingsProperties.getProperty("article-id");
738    
739                            if (Validator.isNotNull(articleId)) {
740                                    Map<String, String> articleIds =
741                                            (Map<String, String>)portletDataContext.
742                                                    getNewPrimaryKeysMap(
743                                                            JournalArticle.class + ".articleId");
744    
745                                    typeSettingsProperties.setProperty(
746                                            "article-id",
747                                            MapUtil.getString(articleIds, articleId, articleId));
748    
749                                    modifiedTypeSettingsProperties = true;
750                            }
751    
752                            // Last merge time for layout
753    
754                            if (layoutsImportMode.equals(
755                                            PortletDataHandlerKeys.
756                                                    LAYOUTS_IMPORT_MODE_CREATED_FROM_PROTOTYPE)) {
757    
758                                    typeSettingsProperties.setProperty(
759                                            Sites.LAST_MERGE_TIME, String.valueOf(lastMergeTime));
760    
761                                    modifiedTypeSettingsProperties = true;
762                            }
763    
764                            if (modifiedTypeSettingsProperties) {
765                                    LayoutUtil.update(layout);
766                            }
767                    }
768    
769                    // Last merge time for layout set
770    
771                    if (layoutsImportMode.equals(
772                                    PortletDataHandlerKeys.
773                                            LAYOUTS_IMPORT_MODE_CREATED_FROM_PROTOTYPE)) {
774    
775                            UnicodeProperties settingsProperties =
776                                    layoutSet.getSettingsProperties();
777    
778                            String mergeFailFriendlyURLLayouts =
779                                    settingsProperties.getProperty(
780                                            Sites.MERGE_FAIL_FRIENDLY_URL_LAYOUTS);
781    
782                            if (Validator.isNull(mergeFailFriendlyURLLayouts)) {
783                                    settingsProperties.setProperty(
784                                            Sites.LAST_MERGE_TIME, String.valueOf(lastMergeTime));
785    
786                                    LayoutSetLocalServiceUtil.updateLayoutSet(layoutSet);
787                            }
788                    }
789    
790                    // Deletion system events
791    
792                    _deletionSystemEventImporter.importDeletionSystemEvents(
793                            portletDataContext);
794    
795                    if (_log.isInfoEnabled()) {
796                            _log.info("Importing layouts takes " + stopWatch.getTime() + " ms");
797                    }
798    
799                    zipReader.close();
800            }
801    
802            protected boolean[] getImportPortletControls(
803                            long companyId, String portletId,
804                            Map<String, String[]> parameterMap, Element portletDataElement)
805                    throws Exception {
806    
807                    boolean importPortletConfiguration = MapUtil.getBoolean(
808                            parameterMap, PortletDataHandlerKeys.PORTLET_CONFIGURATION);
809                    boolean importPortletConfigurationAll = MapUtil.getBoolean(
810                            parameterMap, PortletDataHandlerKeys.PORTLET_CONFIGURATION_ALL);
811                    boolean importPortletData = MapUtil.getBoolean(
812                            parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
813                    boolean importPortletDataAll = MapUtil.getBoolean(
814                            parameterMap, PortletDataHandlerKeys.PORTLET_DATA_ALL);
815    
816                    if (_log.isDebugEnabled()) {
817                            _log.debug("Import portlet data " + importPortletData);
818                            _log.debug("Import all portlet data " + importPortletDataAll);
819                            _log.debug(
820                                    "Import portlet configuration " + importPortletConfiguration);
821                    }
822    
823                    boolean importCurPortletData = importPortletData;
824    
825                    String rootPortletId =
826                            ExportImportHelperUtil.getExportableRootPortletId(
827                                    companyId, portletId);
828    
829                    if (portletDataElement == null) {
830                            importCurPortletData = false;
831                    }
832                    else if (importPortletDataAll) {
833                            importCurPortletData = true;
834                    }
835                    else if (rootPortletId != null) {
836                            importCurPortletData =
837                                    importPortletData &&
838                                    MapUtil.getBoolean(
839                                            parameterMap,
840                                            PortletDataHandlerKeys.PORTLET_DATA +
841                                                    StringPool.UNDERLINE + rootPortletId);
842                    }
843    
844                    boolean importCurPortletArchivedSetups = importPortletConfiguration;
845                    boolean importCurPortletSetup = importPortletConfiguration;
846                    boolean importCurPortletUserPreferences = importPortletConfiguration;
847    
848                    if (importPortletConfigurationAll) {
849                            importCurPortletArchivedSetups =
850                                    MapUtil.getBoolean(
851                                            parameterMap,
852                                            PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS_ALL);
853                            importCurPortletSetup =
854                                    MapUtil.getBoolean(
855                                            parameterMap, PortletDataHandlerKeys.PORTLET_SETUP_ALL);
856                            importCurPortletUserPreferences =
857                                    MapUtil.getBoolean(
858                                            parameterMap,
859                                            PortletDataHandlerKeys.PORTLET_USER_PREFERENCES_ALL);
860                    }
861                    else if (rootPortletId != null) {
862                            boolean importCurPortletConfiguration =
863                                    importPortletConfiguration &&
864                                    MapUtil.getBoolean(
865                                            parameterMap,
866                                            PortletDataHandlerKeys.PORTLET_CONFIGURATION +
867                                                    StringPool.UNDERLINE + rootPortletId);
868    
869                            importCurPortletArchivedSetups =
870                                    importCurPortletConfiguration &&
871                                    MapUtil.getBoolean(
872                                            parameterMap,
873                                            PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS +
874                                                    StringPool.UNDERLINE + rootPortletId);
875                            importCurPortletSetup =
876                                    importCurPortletConfiguration &&
877                                    MapUtil.getBoolean(
878                                            parameterMap,
879                                            PortletDataHandlerKeys.PORTLET_SETUP +
880                                                    StringPool.UNDERLINE + rootPortletId);
881                            importCurPortletUserPreferences =
882                                    importCurPortletConfiguration &&
883                                    MapUtil.getBoolean(
884                                            parameterMap,
885                                            PortletDataHandlerKeys.PORTLET_USER_PREFERENCES +
886                                                    StringPool.UNDERLINE + rootPortletId);
887                    }
888    
889                    return new boolean[] {
890                            importCurPortletArchivedSetups, importCurPortletData,
891                            importCurPortletSetup, importCurPortletUserPreferences};
892            }
893    
894            protected void importLayout(
895                            PortletDataContext portletDataContext,
896                            List<String> sourceLayoutsUuids, List<Layout> newLayouts,
897                            Element layoutElement)
898                    throws Exception {
899    
900                    String action = layoutElement.attributeValue("action");
901    
902                    if (!action.equals(Constants.SKIP)) {
903                            StagedModelDataHandlerUtil.importStagedModel(
904                                    portletDataContext, layoutElement);
905    
906                            List<Layout> portletDataContextNewLayouts =
907                                    portletDataContext.getNewLayouts();
908    
909                            newLayouts.addAll(portletDataContextNewLayouts);
910    
911                            portletDataContextNewLayouts.clear();
912                    }
913    
914                    if (!action.equals(Constants.DELETE)) {
915                            sourceLayoutsUuids.add(layoutElement.attributeValue("uuid"));
916                    }
917            }
918    
919            protected String importTheme(LayoutSet layoutSet, InputStream themeZip)
920                    throws Exception {
921    
922                    ThemeLoader themeLoader = ThemeLoaderFactory.getDefaultThemeLoader();
923    
924                    if (themeLoader == null) {
925                            _log.error("No theme loaders are deployed");
926    
927                            return null;
928                    }
929    
930                    ZipReader zipReader = ZipReaderFactoryUtil.getZipReader(themeZip);
931    
932                    String lookAndFeelXML = zipReader.getEntryAsString(
933                            "liferay-look-and-feel.xml");
934    
935                    String themeId = String.valueOf(layoutSet.getGroupId());
936    
937                    if (layoutSet.isPrivateLayout()) {
938                            themeId += "-private";
939                    }
940                    else {
941                            themeId += "-public";
942                    }
943    
944                    if (PropsValues.THEME_LOADER_NEW_THEME_ID_ON_IMPORT) {
945                            Date now = new Date();
946    
947                            themeId += "-" + Time.getShortTimestamp(now);
948                    }
949    
950                    String themeName = themeId;
951    
952                    lookAndFeelXML = StringUtil.replace(
953                            lookAndFeelXML,
954                            new String[] {
955                                    "[$GROUP_ID$]", "[$THEME_ID$]", "[$THEME_NAME$]"
956                            },
957                            new String[] {
958                                    String.valueOf(layoutSet.getGroupId()), themeId, themeName
959                            }
960                    );
961    
962                    FileUtil.deltree(
963                            themeLoader.getFileStorage() + StringPool.SLASH + themeId);
964    
965                    List<String> zipEntries = zipReader.getEntries();
966    
967                    for (String zipEntry : zipEntries) {
968                            String key = zipEntry;
969    
970                            if (key.equals("liferay-look-and-feel.xml")) {
971                                    FileUtil.write(
972                                            themeLoader.getFileStorage() + StringPool.SLASH + themeId +
973                                                    StringPool.SLASH + key,
974                                            lookAndFeelXML.getBytes());
975                            }
976                            else {
977                                    InputStream is = zipReader.getEntryAsInputStream(zipEntry);
978    
979                                    FileUtil.write(
980                                            themeLoader.getFileStorage() + StringPool.SLASH + themeId +
981                                                    StringPool.SLASH + key,
982                                            is);
983                            }
984                    }
985    
986                    themeLoader.loadThemes();
987    
988                    ClusterRequest clusterRequest = ClusterRequest.createMulticastRequest(
989                            _loadThemesMethodHandler, true);
990    
991                    clusterRequest.setFireAndForget(true);
992    
993                    ClusterExecutorUtil.execute(clusterRequest);
994    
995                    themeId +=
996                            PortletConstants.WAR_SEPARATOR +
997                                    themeLoader.getServletContextName();
998    
999                    return PortalUtil.getJsSafePortletId(themeId);
1000            }
1001    
1002            protected void readXML(PortletDataContext portletDataContext)
1003                    throws Exception {
1004    
1005                    if ((_rootElement != null) && (_headerElement != null) &&
1006                            (_layoutsElement != null) && (_layoutElements != null)) {
1007    
1008                            return;
1009                    }
1010    
1011                    String xml = portletDataContext.getZipEntryAsString("/manifest.xml");
1012    
1013                    if (xml == null) {
1014                            throw new LARFileException("manifest.xml not found in the LAR");
1015                    }
1016    
1017                    try {
1018                            Document document = SAXReaderUtil.read(xml);
1019    
1020                            _rootElement = document.getRootElement();
1021    
1022                            portletDataContext.setImportDataRootElement(_rootElement);
1023                    }
1024                    catch (Exception e) {
1025                            throw new LARFileException(e);
1026                    }
1027    
1028                    _headerElement = _rootElement.element("header");
1029    
1030                    _layoutsElement = portletDataContext.getImportDataGroupElement(
1031                            Layout.class);
1032    
1033                    _layoutElements = _layoutsElement.elements();
1034            }
1035    
1036            protected void validateFile(PortletDataContext portletDataContext)
1037                    throws Exception {
1038    
1039                    // Build compatibility
1040    
1041                    readXML(portletDataContext);
1042    
1043                    int buildNumber = ReleaseInfo.getBuildNumber();
1044    
1045                    int importBuildNumber = GetterUtil.getInteger(
1046                            _headerElement.attributeValue("build-number"));
1047    
1048                    if (buildNumber != importBuildNumber) {
1049                            throw new LayoutImportException(
1050                                    "LAR build number " + importBuildNumber + " does not match " +
1051                                            "portal build number " + buildNumber);
1052                    }
1053    
1054                    // Type
1055    
1056                    String larType = _headerElement.attributeValue("type");
1057    
1058                    if (!larType.equals("layout-prototype") &&
1059                            !larType.equals("layout-set") &&
1060                            !larType.equals("layout-set-prototype")) {
1061    
1062                            throw new LARTypeException(larType);
1063                    }
1064    
1065                    // Available locales
1066    
1067                    Locale[] sourceAvailableLocales = LocaleUtil.fromLanguageIds(
1068                            StringUtil.split(
1069                                    _headerElement.attributeValue("available-locales")));
1070    
1071                    Locale[] targetAvailableLocales = LanguageUtil.getAvailableLocales(
1072                            portletDataContext.getScopeGroupId());
1073    
1074                    for (Locale sourceAvailableLocale : sourceAvailableLocales) {
1075                            if (!ArrayUtil.contains(
1076                                            targetAvailableLocales, sourceAvailableLocale)) {
1077    
1078                                    LocaleException le = new LocaleException(
1079                                            LocaleException.TYPE_EXPORT_IMPORT);
1080    
1081                                    le.setSourceAvailableLocales(sourceAvailableLocales);
1082                                    le.setTargetAvailableLocales(targetAvailableLocales);
1083    
1084                                    throw le;
1085                            }
1086                    }
1087    
1088                    // Layout prototypes validity
1089    
1090                    validateLayoutPrototypes(
1091                            portletDataContext.getCompanyId(), _layoutsElement,
1092                            _layoutElements);
1093            }
1094    
1095            protected void validateLayoutPrototypes(
1096                            long companyId, Element layoutsElement,
1097                            List<Element> layoutElements)
1098                    throws Exception {
1099    
1100                    List<Tuple> missingLayoutPrototypes = new ArrayList<Tuple>();
1101    
1102                    String layoutSetPrototypeUuid = layoutsElement.attributeValue(
1103                            "layout-set-prototype-uuid");
1104    
1105                    if (Validator.isNotNull(layoutSetPrototypeUuid)) {
1106                            try {
1107                                    LayoutSetPrototypeLocalServiceUtil.
1108                                            getLayoutSetPrototypeByUuidAndCompanyId(
1109                                                    layoutSetPrototypeUuid, companyId);
1110                            }
1111                            catch (NoSuchLayoutSetPrototypeException nlspe) {
1112                                    String layoutSetPrototypeName = layoutsElement.attributeValue(
1113                                            "layout-set-prototype-name");
1114    
1115                                    missingLayoutPrototypes.add(
1116                                            new Tuple(
1117                                                    LayoutSetPrototype.class.getName(),
1118                                                    layoutSetPrototypeUuid, layoutSetPrototypeName));
1119                            }
1120                    }
1121    
1122                    for (Element layoutElement : layoutElements) {
1123                            String action = layoutElement.attributeValue("action");
1124    
1125                            if (action.equals(Constants.SKIP)) {
1126                                    continue;
1127                            }
1128    
1129                            String layoutPrototypeUuid = GetterUtil.getString(
1130                                    layoutElement.attributeValue("layout-prototype-uuid"));
1131    
1132                            if (Validator.isNotNull(layoutPrototypeUuid)) {
1133                                    try {
1134                                            LayoutPrototypeLocalServiceUtil.
1135                                                    getLayoutPrototypeByUuidAndCompanyId(
1136                                                            layoutPrototypeUuid, companyId);
1137                                    }
1138                                    catch (NoSuchLayoutPrototypeException nslpe) {
1139                                            String layoutPrototypeName = GetterUtil.getString(
1140                                                    layoutElement.attributeValue("layout-prototype-name"));
1141    
1142                                            missingLayoutPrototypes.add(
1143                                                    new Tuple(
1144                                                            LayoutPrototype.class.getName(),
1145                                                            layoutPrototypeUuid, layoutPrototypeName));
1146                                    }
1147                            }
1148                    }
1149    
1150                    if (!missingLayoutPrototypes.isEmpty()) {
1151                            throw new LayoutPrototypeException(missingLayoutPrototypes);
1152                    }
1153            }
1154    
1155            private static Log _log = LogFactoryUtil.getLog(LayoutImporter.class);
1156    
1157            private static MethodHandler _loadThemesMethodHandler = new MethodHandler(
1158                    new MethodKey(ThemeLoaderFactory.class, "loadThemes"));
1159    
1160            private DeletionSystemEventImporter _deletionSystemEventImporter =
1161                    new DeletionSystemEventImporter();
1162            private Element _headerElement;
1163            private List<Element> _layoutElements;
1164            private Element _layoutsElement;
1165            private PermissionImporter _permissionImporter = new PermissionImporter();
1166            private PortletImporter _portletImporter = new PortletImporter();
1167            private Element _rootElement;
1168    
1169    }