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