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