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.portlet.layoutsadmin.lar;
016    
017    import com.liferay.counter.service.CounterLocalServiceUtil;
018    import com.liferay.portal.NoSuchLayoutException;
019    import com.liferay.portal.kernel.bean.PortalBeanLocatorUtil;
020    import com.liferay.portal.kernel.exception.PortalException;
021    import com.liferay.portal.kernel.exception.SystemException;
022    import com.liferay.portal.kernel.json.JSONFactoryUtil;
023    import com.liferay.portal.kernel.json.JSONObject;
024    import com.liferay.portal.kernel.lar.BaseStagedModelDataHandler;
025    import com.liferay.portal.kernel.lar.ExportImportPathUtil;
026    import com.liferay.portal.kernel.lar.PortletDataContext;
027    import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
028    import com.liferay.portal.kernel.lar.StagedModelDataHandlerUtil;
029    import com.liferay.portal.kernel.log.Log;
030    import com.liferay.portal.kernel.log.LogFactoryUtil;
031    import com.liferay.portal.kernel.staging.LayoutStagingUtil;
032    import com.liferay.portal.kernel.staging.StagingUtil;
033    import com.liferay.portal.kernel.util.ArrayUtil;
034    import com.liferay.portal.kernel.util.CharPool;
035    import com.liferay.portal.kernel.util.Constants;
036    import com.liferay.portal.kernel.util.GetterUtil;
037    import com.liferay.portal.kernel.util.ListUtil;
038    import com.liferay.portal.kernel.util.LocaleUtil;
039    import com.liferay.portal.kernel.util.MapUtil;
040    import com.liferay.portal.kernel.util.StringBundler;
041    import com.liferay.portal.kernel.util.StringPool;
042    import com.liferay.portal.kernel.util.StringUtil;
043    import com.liferay.portal.kernel.util.UnicodeProperties;
044    import com.liferay.portal.kernel.util.Validator;
045    import com.liferay.portal.kernel.workflow.WorkflowConstants;
046    import com.liferay.portal.kernel.xml.Element;
047    import com.liferay.portal.lar.LayoutExporter;
048    import com.liferay.portal.lar.LayoutImporter;
049    import com.liferay.portal.model.Group;
050    import com.liferay.portal.model.Image;
051    import com.liferay.portal.model.Layout;
052    import com.liferay.portal.model.LayoutBranch;
053    import com.liferay.portal.model.LayoutConstants;
054    import com.liferay.portal.model.LayoutFriendlyURL;
055    import com.liferay.portal.model.LayoutPrototype;
056    import com.liferay.portal.model.LayoutRevision;
057    import com.liferay.portal.model.LayoutSet;
058    import com.liferay.portal.model.LayoutStagingHandler;
059    import com.liferay.portal.model.LayoutTemplate;
060    import com.liferay.portal.model.LayoutTypePortlet;
061    import com.liferay.portal.model.LayoutTypePortletConstants;
062    import com.liferay.portal.service.ImageLocalServiceUtil;
063    import com.liferay.portal.service.LayoutFriendlyURLLocalServiceUtil;
064    import com.liferay.portal.service.LayoutLocalServiceUtil;
065    import com.liferay.portal.service.LayoutPrototypeLocalServiceUtil;
066    import com.liferay.portal.service.LayoutSetLocalServiceUtil;
067    import com.liferay.portal.service.LayoutTemplateLocalServiceUtil;
068    import com.liferay.portal.service.PortletLocalServiceUtil;
069    import com.liferay.portal.service.ResourceLocalServiceUtil;
070    import com.liferay.portal.service.ServiceContext;
071    import com.liferay.portal.service.ServiceContextThreadLocal;
072    import com.liferay.portal.service.impl.LayoutLocalServiceHelper;
073    import com.liferay.portal.util.PropsValues;
074    import com.liferay.portlet.journal.NoSuchArticleException;
075    import com.liferay.portlet.journal.model.JournalArticle;
076    import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
077    import com.liferay.portlet.journal.service.JournalContentSearchLocalServiceUtil;
078    import com.liferay.portlet.sites.util.SitesUtil;
079    
080    import java.io.IOException;
081    
082    import java.util.ArrayList;
083    import java.util.List;
084    import java.util.Locale;
085    import java.util.Map;
086    
087    /**
088     * @author Mate Thurzo
089     */
090    public class LayoutStagedModelDataHandler
091            extends BaseStagedModelDataHandler<Layout> {
092    
093            public static final String[] CLASS_NAMES = {Layout.class.getName()};
094    
095            @Override
096            public void deleteStagedModel(
097                            String uuid, long groupId, String className, String extraData)
098                    throws PortalException, SystemException {
099    
100                    JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject(
101                            extraData);
102    
103                    boolean privateLayout = extraDataJSONObject.getBoolean("privateLayout");
104    
105                    Layout layout = LayoutLocalServiceUtil.fetchLayoutByUuidAndGroupId(
106                            uuid, groupId, privateLayout);
107    
108                    if (layout != null) {
109                            LayoutLocalServiceUtil.deleteLayout(
110                                    layout, true, new ServiceContext());
111                    }
112            }
113    
114            @Override
115            public String[] getClassNames() {
116                    return CLASS_NAMES;
117            }
118    
119            @Override
120            public String getDisplayName(Layout layout) {
121                    return layout.getNameCurrentValue();
122            }
123    
124            protected String[] appendPortletIds(
125                    String[] portletIds, String[] newPortletIds, String portletsMergeMode) {
126    
127                    for (String portletId : newPortletIds) {
128                            if (ArrayUtil.contains(portletIds, portletId)) {
129                                    continue;
130                            }
131    
132                            if (portletsMergeMode.equals(
133                                            PortletDataHandlerKeys.PORTLETS_MERGE_MODE_ADD_TO_BOTTOM)) {
134    
135                                    portletIds = ArrayUtil.append(portletIds, portletId);
136                            }
137                            else {
138                                    portletIds = ArrayUtil.append(
139                                            new String[] {portletId}, portletIds);
140                            }
141                    }
142    
143                    return portletIds;
144            }
145    
146            @Override
147            protected void doExportStagedModel(
148                            PortletDataContext portletDataContext, Layout layout)
149                    throws Exception {
150    
151                    Element layoutElement = portletDataContext.getExportDataElement(layout);
152    
153                    populateElementLayoutMetadata(layoutElement, layout);
154    
155                    layoutElement.addAttribute("action", Constants.ADD);
156    
157                    portletDataContext.setPlid(layout.getPlid());
158    
159                    long parentLayoutId = layout.getParentLayoutId();
160    
161                    if (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
162                            Layout parentLayout = LayoutLocalServiceUtil.fetchLayout(
163                                    layout.getGroupId(), layout.isPrivateLayout(), parentLayoutId);
164    
165                            if (parentLayout != null) {
166                                    StagedModelDataHandlerUtil.exportReferenceStagedModel(
167                                            portletDataContext, layout, parentLayout,
168                                            PortletDataContext.REFERENCE_TYPE_PARENT);
169    
170                                    layoutElement.addAttribute(
171                                            "parent-layout-uuid", parentLayout.getUuid());
172                            }
173                    }
174    
175                    List<LayoutFriendlyURL> layoutFriendlyURLs =
176                            LayoutFriendlyURLLocalServiceUtil.getLayoutFriendlyURLs(
177                                    layout.getPlid());
178    
179                    for (LayoutFriendlyURL layoutFriendlyURL : layoutFriendlyURLs) {
180                            StagedModelDataHandlerUtil.exportReferenceStagedModel(
181                                    portletDataContext, layout, layoutFriendlyURL,
182                                    PortletDataContext.REFERENCE_TYPE_DEPENDENCY);
183                    }
184    
185                    if (layout.isIconImage()) {
186                            exportLayoutIconImage(portletDataContext, layout, layoutElement);
187                    }
188    
189                    if (layout.isTypeArticle()) {
190                            exportJournalArticle(portletDataContext, layout);
191                    }
192                    else if (layout.isTypeLinkToLayout()) {
193                            exportLinkedLayout(portletDataContext, layout, layoutElement);
194                    }
195    
196                    fixExportTypeSettings(layout);
197    
198                    exportTheme(portletDataContext, layout);
199    
200                    portletDataContext.addClassedModel(
201                            layoutElement, ExportImportPathUtil.getModelPath(layout), layout);
202            }
203    
204            @Override
205            protected void doImportStagedModel(
206                            PortletDataContext portletDataContext, Layout layout)
207                    throws Exception {
208    
209                    long groupId = portletDataContext.getGroupId();
210                    long userId = portletDataContext.getUserId(layout.getUserUuid());
211    
212                    Element layoutElement =
213                            portletDataContext.getImportDataStagedModelElement(layout);
214    
215                    String layoutUuid = GetterUtil.getString(
216                            layoutElement.attributeValue("layout-uuid"));
217    
218                    long layoutId = GetterUtil.getInteger(
219                            layoutElement.attributeValue("layout-id"));
220    
221                    long oldLayoutId = layoutId;
222    
223                    boolean privateLayout = portletDataContext.isPrivateLayout();
224    
225                    Map<Long, Layout> newLayoutsMap =
226                            (Map<Long, Layout>)portletDataContext.getNewPrimaryKeysMap(
227                                    Layout.class + ".layout");
228    
229                    String action = layoutElement.attributeValue("action");
230    
231                    if (action.equals(Constants.DELETE)) {
232                            Layout deletingLayout =
233                                    LayoutLocalServiceUtil.fetchLayoutByUuidAndGroupId(
234                                            layoutUuid, groupId, privateLayout);
235    
236                            if (layout != null) {
237                                    newLayoutsMap.put(oldLayoutId, layout);
238    
239                                    ServiceContext serviceContext =
240                                            ServiceContextThreadLocal.getServiceContext();
241    
242                                    LayoutLocalServiceUtil.deleteLayout(
243                                            deletingLayout, false, serviceContext);
244                            }
245    
246                            return;
247                    }
248    
249                    Layout existingLayout = null;
250                    Layout importedLayout = null;
251    
252                    String friendlyURL = layout.getFriendlyURL();
253    
254                    String layoutsImportMode = MapUtil.getString(
255                            portletDataContext.getParameterMap(),
256                            PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE,
257                            PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_UUID);
258    
259                    if (layoutsImportMode.equals(
260                                    PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_ADD_AS_NEW)) {
261    
262                            layoutId = LayoutLocalServiceUtil.getNextLayoutId(
263                                    groupId, privateLayout);
264                            friendlyURL = StringPool.SLASH + layoutId;
265                    }
266                    else if (layoutsImportMode.equals(
267                                            PortletDataHandlerKeys.
268                                                    LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_NAME)) {
269    
270                            Locale locale = LocaleUtil.getSiteDefault();
271    
272                            String localizedName = layout.getName(locale);
273    
274                            List<Layout> previousLayouts = LayoutLocalServiceUtil.getLayouts(
275                                    groupId, privateLayout);
276    
277                            for (Layout curLayout : previousLayouts) {
278                                    if (localizedName.equals(curLayout.getName(locale)) ||
279                                            friendlyURL.equals(curLayout.getFriendlyURL())) {
280    
281                                            existingLayout = curLayout;
282    
283                                            break;
284                                    }
285                            }
286    
287                            if (existingLayout == null) {
288                                    layoutId = LayoutLocalServiceUtil.getNextLayoutId(
289                                            groupId, privateLayout);
290                            }
291                    }
292                    else if (layoutsImportMode.equals(
293                                            PortletDataHandlerKeys.
294                                                    LAYOUTS_IMPORT_MODE_CREATED_FROM_PROTOTYPE)) {
295    
296                            existingLayout = LayoutLocalServiceUtil.fetchLayoutByUuidAndGroupId(
297                                    layout.getUuid(), groupId, privateLayout);
298    
299                            if (SitesUtil.isLayoutModifiedSinceLastMerge(existingLayout)) {
300                                    newLayoutsMap.put(oldLayoutId, existingLayout);
301    
302                                    return;
303                            }
304    
305                            LayoutFriendlyURL layoutFriendlyURL =
306                                    LayoutFriendlyURLLocalServiceUtil.fetchFirstLayoutFriendlyURL(
307                                            groupId, privateLayout, friendlyURL);
308    
309                            if ((layoutFriendlyURL != null) && (existingLayout == null)) {
310                                    Layout mergeFailFriendlyURLLayout =
311                                            LayoutLocalServiceUtil.getLayout(
312                                                    layoutFriendlyURL.getPlid());
313    
314                                    SitesUtil.addMergeFailFriendlyURLLayout(
315                                            mergeFailFriendlyURLLayout);
316    
317                                    return;
318                            }
319                    }
320                    else {
321    
322                            // The default behaviour of import mode is
323                            // PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_UUID
324    
325                            existingLayout = LayoutLocalServiceUtil.fetchLayoutByUuidAndGroupId(
326                                    layout.getUuid(), groupId, privateLayout);
327    
328                            if (existingLayout == null) {
329                                    existingLayout =
330                                            LayoutLocalServiceUtil.fetchLayoutByFriendlyURL(
331                                                    groupId, privateLayout, friendlyURL);
332                            }
333    
334                            if (existingLayout == null) {
335                                    layoutId = LayoutLocalServiceUtil.getNextLayoutId(
336                                            groupId, privateLayout);
337                            }
338                    }
339    
340                    if (_log.isDebugEnabled()) {
341                            StringBundler sb = new StringBundler(7);
342    
343                            sb.append("Layout with {groupId=");
344                            sb.append(groupId);
345                            sb.append(",privateLayout=");
346                            sb.append(privateLayout);
347                            sb.append(",layoutId=");
348                            sb.append(layoutId);
349    
350                            if (existingLayout == null) {
351                                    sb.append("} does not exist");
352    
353                                    _log.debug(sb.toString());
354                            }
355                            else {
356                                    sb.append("} exists");
357    
358                                    _log.debug(sb.toString());
359                            }
360                    }
361    
362                    if (existingLayout == null) {
363                            long plid = CounterLocalServiceUtil.increment();
364    
365                            importedLayout = LayoutLocalServiceUtil.createLayout(plid);
366    
367                            if (layoutsImportMode.equals(
368                                            PortletDataHandlerKeys.
369                                                    LAYOUTS_IMPORT_MODE_CREATED_FROM_PROTOTYPE)) {
370    
371                                    importedLayout.setSourcePrototypeLayoutUuid(layout.getUuid());
372    
373                                    layoutId = LayoutLocalServiceUtil.getNextLayoutId(
374                                            groupId, privateLayout);
375                            }
376                            else {
377                                    importedLayout.setCreateDate(layout.getCreateDate());
378                                    importedLayout.setModifiedDate(layout.getModifiedDate());
379                                    importedLayout.setLayoutPrototypeUuid(
380                                            layout.getLayoutPrototypeUuid());
381                                    importedLayout.setLayoutPrototypeLinkEnabled(
382                                            layout.isLayoutPrototypeLinkEnabled());
383                                    importedLayout.setSourcePrototypeLayoutUuid(
384                                            layout.getSourcePrototypeLayoutUuid());
385                            }
386    
387                            importedLayout.setUuid(layout.getUuid());
388                            importedLayout.setGroupId(groupId);
389                            importedLayout.setUserId(userId);
390                            importedLayout.setPrivateLayout(privateLayout);
391                            importedLayout.setLayoutId(layoutId);
392    
393                            initNewLayoutPermissions(
394                                    portletDataContext.getCompanyId(), groupId, userId, layout,
395                                    importedLayout, privateLayout);
396    
397                            LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
398                                    groupId, privateLayout);
399    
400                            importedLayout.setLayoutSet(layoutSet);
401                    }
402                    else {
403                            importedLayout = existingLayout;
404                    }
405    
406                    portletDataContext.setPlid(importedLayout.getPlid());
407                    portletDataContext.setOldPlid(layout.getPlid());
408    
409                    newLayoutsMap.put(oldLayoutId, importedLayout);
410    
411                    long parentLayoutId = layout.getParentLayoutId();
412    
413                    String parentLayoutUuid = GetterUtil.getString(
414                            layoutElement.attributeValue("parent-layout-uuid"));
415    
416                    Element parentLayoutElement =
417                            portletDataContext.getReferenceDataElement(
418                                    layout, Layout.class, layout.getGroupId(), parentLayoutUuid);
419    
420                    if ((parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) &&
421                            (parentLayoutElement != null)) {
422    
423                            String parentLayoutPath = parentLayoutElement.attributeValue(
424                                    "path");
425    
426                            Layout parentLayout =
427                                    (Layout)portletDataContext.getZipEntryAsObject(
428                                            parentLayoutPath);
429    
430                            StagedModelDataHandlerUtil.importStagedModel(
431                                    portletDataContext, parentLayout);
432    
433                            Layout importedParentLayout = newLayoutsMap.get(parentLayoutId);
434    
435                            parentLayoutId = importedParentLayout.getLayoutId();
436                    }
437    
438                    if (_log.isDebugEnabled()) {
439                            StringBundler sb = new StringBundler(4);
440    
441                            sb.append("Importing layout with layout id ");
442                            sb.append(layoutId);
443                            sb.append(" and parent layout id ");
444                            sb.append(parentLayoutId);
445    
446                            _log.debug(sb.toString());
447                    }
448    
449                    importedLayout.setCompanyId(portletDataContext.getCompanyId());
450                    importedLayout.setParentLayoutId(parentLayoutId);
451                    importedLayout.setName(layout.getName());
452                    importedLayout.setTitle(layout.getTitle());
453                    importedLayout.setDescription(layout.getDescription());
454                    importedLayout.setKeywords(layout.getKeywords());
455                    importedLayout.setRobots(layout.getRobots());
456                    importedLayout.setType(layout.getType());
457    
458                    String portletsMergeMode = MapUtil.getString(
459                            portletDataContext.getParameterMap(),
460                            PortletDataHandlerKeys.PORTLETS_MERGE_MODE,
461                            PortletDataHandlerKeys.PORTLETS_MERGE_MODE_REPLACE);
462    
463                    if (layout.isTypeArticle()) {
464                            importJournalArticle(portletDataContext, layout);
465    
466                            updateTypeSettings(portletDataContext, importedLayout, layout);
467                    }
468                    else if (layout.isTypePortlet() &&
469                                     Validator.isNotNull(layout.getTypeSettings()) &&
470                                     !portletsMergeMode.equals(
471                                             PortletDataHandlerKeys.PORTLETS_MERGE_MODE_REPLACE)) {
472    
473                            mergePortlets(
474                                    importedLayout, layout.getTypeSettings(), portletsMergeMode);
475                    }
476                    else if (layout.isTypeLinkToLayout()) {
477                            importLinkedLayout(
478                                    portletDataContext, layout, importedLayout, layoutElement,
479                                    newLayoutsMap);
480                    }
481                    else {
482                            updateTypeSettings(portletDataContext, importedLayout, layout);
483                    }
484    
485                    importedLayout.setHidden(layout.isHidden());
486                    importedLayout.setFriendlyURL(
487                            getUniqueFriendlyURL(
488                                    portletDataContext, importedLayout, friendlyURL));
489                    importedLayout.setIconImage(false);
490    
491                    if (layout.isIconImage()) {
492                            importLayoutIconImage(
493                                    portletDataContext, importedLayout, layoutElement);
494                    }
495                    else {
496                            ImageLocalServiceUtil.deleteImage(importedLayout.getIconImageId());
497                    }
498    
499                    if (existingLayout == null) {
500                            int priority = _layoutLocalServiceHelper.getNextPriority(
501                                    groupId, privateLayout, parentLayoutId, null, -1);
502    
503                            importedLayout.setPriority(priority);
504                    }
505    
506                    importedLayout.setLayoutPrototypeUuid(layout.getLayoutPrototypeUuid());
507                    importedLayout.setLayoutPrototypeLinkEnabled(
508                            layout.isLayoutPrototypeLinkEnabled());
509    
510                    ServiceContext serviceContext = portletDataContext.createServiceContext(
511                            layout);
512    
513                    importedLayout.setExpandoBridgeAttributes(serviceContext);
514    
515                    StagingUtil.updateLastImportSettings(
516                            layoutElement, importedLayout, portletDataContext);
517    
518                    fixImportTypeSettings(importedLayout);
519    
520                    importTheme(portletDataContext, layout, importedLayout);
521    
522                    LayoutLocalServiceUtil.updateLayout(importedLayout);
523    
524                    LayoutSetLocalServiceUtil.updatePageCount(groupId, privateLayout);
525    
526                    List<Layout> newLayouts = portletDataContext.getNewLayouts();
527    
528                    newLayouts.add(importedLayout);
529    
530                    Map<Long, Long> layoutPlids =
531                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
532                                    Layout.class);
533    
534                    layoutPlids.put(layout.getPlid(), importedLayout.getPlid());
535    
536                    importLayoutFriendlyURLs(portletDataContext, layout);
537    
538                    portletDataContext.importClassedModel(layout, importedLayout);
539            }
540    
541            protected void exportJournalArticle(
542                            PortletDataContext portletDataContext, Layout layout)
543                    throws Exception {
544    
545                    UnicodeProperties typeSettingsProperties =
546                            layout.getTypeSettingsProperties();
547    
548                    String articleId = typeSettingsProperties.getProperty(
549                            "article-id", StringPool.BLANK);
550    
551                    long articleGroupId = layout.getGroupId();
552    
553                    if (Validator.isNull(articleId)) {
554                            if (_log.isWarnEnabled()) {
555                                    _log.warn(
556                                            "No article id found in typeSettings of layout " +
557                                                    layout.getPlid());
558                            }
559                    }
560    
561                    JournalArticle article = null;
562    
563                    try {
564                            article = JournalArticleLocalServiceUtil.getLatestArticle(
565                                    articleGroupId, articleId, WorkflowConstants.STATUS_APPROVED);
566                    }
567                    catch (NoSuchArticleException nsae) {
568                            if (_log.isWarnEnabled()) {
569                                    StringBundler sb = new StringBundler(4);
570    
571                                    sb.append("No approved article found with group id ");
572                                    sb.append(articleGroupId);
573                                    sb.append(" and layout id ");
574                                    sb.append(articleId);
575    
576                                    _log.warn(sb.toString());
577                            }
578                    }
579    
580                    if (article == null) {
581                            return;
582                    }
583    
584                    StagedModelDataHandlerUtil.exportReferenceStagedModel(
585                            portletDataContext, layout, article,
586                            PortletDataContext.REFERENCE_TYPE_EMBEDDED);
587            }
588    
589            protected void exportLayoutIconImage(
590                            PortletDataContext portletDataContext, Layout layout,
591                            Element layoutElement)
592                    throws Exception {
593    
594                    Image image = ImageLocalServiceUtil.getImage(layout.getIconImageId());
595    
596                    if (image != null) {
597                            String iconPath = ExportImportPathUtil.getModelPath(
598                                    portletDataContext.getScopeGroupId(), Image.class.getName(),
599                                    image.getImageId());
600    
601                            Element iconImagePathElement = layoutElement.addElement(
602                                    "icon-image-path");
603    
604                            iconImagePathElement.addText(iconPath);
605    
606                            portletDataContext.addZipEntry(iconPath, image.getTextObj());
607                    }
608            }
609    
610            protected void exportLinkedLayout(
611                            PortletDataContext portletDataContext, Layout layout,
612                            Element layoutElement)
613                    throws Exception {
614    
615                    UnicodeProperties typeSettingsProperties =
616                            layout.getTypeSettingsProperties();
617    
618                    long linkToLayoutId = GetterUtil.getLong(
619                            typeSettingsProperties.getProperty(
620                                    "linkToLayoutId", StringPool.BLANK));
621    
622                    if (linkToLayoutId > 0) {
623                            try {
624                                    Layout linkedToLayout = LayoutLocalServiceUtil.getLayout(
625                                            portletDataContext.getScopeGroupId(),
626                                            layout.isPrivateLayout(), linkToLayoutId);
627    
628                                    StagedModelDataHandlerUtil.exportReferenceStagedModel(
629                                            portletDataContext, layout, linkedToLayout,
630                                            PortletDataContext.REFERENCE_TYPE_STRONG);
631    
632                                    layoutElement.addAttribute(
633                                            "linked-to-layout-uuid", linkedToLayout.getUuid());
634                            }
635                            catch (NoSuchLayoutException nsle) {
636                            }
637                    }
638            }
639    
640            protected void exportTheme(
641                            PortletDataContext portletDataContext, Layout layout)
642                    throws Exception {
643    
644                    boolean exportThemeSettings = MapUtil.getBoolean(
645                            portletDataContext.getParameterMap(),
646                            PortletDataHandlerKeys.THEME_REFERENCE);
647    
648                    if (_log.isDebugEnabled()) {
649                            _log.debug("Export theme settings " + exportThemeSettings);
650                    }
651    
652                    if (exportThemeSettings &&
653                            !portletDataContext.isPerformDirectBinaryImport() &&
654                            !layout.isInheritLookAndFeel()) {
655    
656                            StagedTheme stagedTheme = new StagedTheme(layout.getTheme());
657    
658                            Element layoutElement = portletDataContext.getExportDataElement(
659                                    layout);
660    
661                            portletDataContext.addReferenceElement(
662                                    layout, layoutElement, stagedTheme,
663                                    PortletDataContext.REFERENCE_TYPE_DEPENDENCY, true);
664                    }
665            }
666    
667            protected Object[] extractFriendlyURLInfo(Layout layout) {
668                    if (!layout.isTypeURL()) {
669                            return null;
670                    }
671    
672                    UnicodeProperties typeSettings = layout.getTypeSettingsProperties();
673    
674                    String url = GetterUtil.getString(typeSettings.getProperty("url"));
675    
676                    String friendlyURLPrivateGroupPath =
677                            PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
678                    String friendlyURLPrivateUserPath =
679                            PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
680                    String friendlyURLPublicPath =
681                            PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
682    
683                    if (!url.startsWith(friendlyURLPrivateGroupPath) &&
684                            !url.startsWith(friendlyURLPrivateUserPath) &&
685                            !url.startsWith(friendlyURLPublicPath)) {
686    
687                            return null;
688                    }
689    
690                    int x = url.indexOf(CharPool.SLASH, 1);
691    
692                    if (x == -1) {
693                            return null;
694                    }
695    
696                    int y = url.indexOf(CharPool.SLASH, x + 1);
697    
698                    if (y == -1) {
699                            return null;
700                    }
701    
702                    return new Object[] {url.substring(x, y), url, x, y};
703            }
704    
705            protected void fixExportTypeSettings(Layout layout) throws Exception {
706                    Object[] friendlyURLInfo = extractFriendlyURLInfo(layout);
707    
708                    if (friendlyURLInfo == null) {
709                            return;
710                    }
711    
712                    String friendlyURL = (String)friendlyURLInfo[0];
713    
714                    Group group = layout.getGroup();
715    
716                    String groupFriendlyURL = group.getFriendlyURL();
717    
718                    if (!friendlyURL.equals(groupFriendlyURL)) {
719                            return;
720                    }
721    
722                    UnicodeProperties typeSettings = layout.getTypeSettingsProperties();
723    
724                    String url = (String)friendlyURLInfo[1];
725    
726                    int x = (Integer)friendlyURLInfo[2];
727                    int y = (Integer)friendlyURLInfo[3];
728    
729                    typeSettings.setProperty(
730                            "url",
731                            url.substring(0, x) + LayoutExporter.SAME_GROUP_FRIENDLY_URL +
732                                    url.substring(y));
733            }
734    
735            protected void fixImportTypeSettings(Layout layout) throws Exception {
736                    Object[] friendlyURLInfo = extractFriendlyURLInfo(layout);
737    
738                    if (friendlyURLInfo == null) {
739                            return;
740                    }
741    
742                    String friendlyURL = (String)friendlyURLInfo[0];
743    
744                    if (!friendlyURL.equals(LayoutExporter.SAME_GROUP_FRIENDLY_URL)) {
745                            return;
746                    }
747    
748                    Group group = layout.getGroup();
749    
750                    UnicodeProperties typeSettings = layout.getTypeSettingsProperties();
751    
752                    String url = (String)friendlyURLInfo[1];
753    
754                    int x = (Integer)friendlyURLInfo[2];
755                    int y = (Integer)friendlyURLInfo[3];
756    
757                    typeSettings.setProperty(
758                            "url",
759                            url.substring(0, x) + group.getFriendlyURL() + url.substring(y));
760            }
761    
762            protected String getUniqueFriendlyURL(
763                            PortletDataContext portletDataContext, Layout existingLayout,
764                            String friendlyURL)
765                    throws SystemException {
766    
767                    for (int i = 1;; i++) {
768                            Layout duplicateFriendlyURLLayout =
769                                    LayoutLocalServiceUtil.fetchLayoutByFriendlyURL(
770                                            portletDataContext.getGroupId(),
771                                            portletDataContext.isPrivateLayout(), friendlyURL);
772    
773                            if ((duplicateFriendlyURLLayout == null) ||
774                                    (duplicateFriendlyURLLayout.getPlid() ==
775                                            existingLayout.getPlid())) {
776    
777                                    break;
778                            }
779    
780                            friendlyURL = friendlyURL + i;
781                    }
782    
783                    return friendlyURL;
784            }
785    
786            protected void importJournalArticle(
787                            PortletDataContext portletDataContext, Layout layout)
788                    throws Exception {
789    
790                    UnicodeProperties typeSettingsProperties =
791                            layout.getTypeSettingsProperties();
792    
793                    String articleId = typeSettingsProperties.getProperty(
794                            "article-id", StringPool.BLANK);
795    
796                    if (Validator.isNull(articleId)) {
797                            return;
798                    }
799    
800                    List<Element> referenceDataElements =
801                            portletDataContext.getReferenceDataElements(
802                                    layout, JournalArticle.class);
803    
804                    if (!referenceDataElements.isEmpty()) {
805                            StagedModelDataHandlerUtil.importStagedModel(
806                                    portletDataContext, referenceDataElements.get(0));
807                    }
808    
809                    Map<String, String> articleIds =
810                            (Map<String, String>)portletDataContext.getNewPrimaryKeysMap(
811                                    JournalArticle.class + ".articleId");
812    
813                    articleId = MapUtil.getString(articleIds, articleId, articleId);
814    
815                    typeSettingsProperties.setProperty("article-id", articleId);
816    
817                    JournalContentSearchLocalServiceUtil.updateContentSearch(
818                            portletDataContext.getScopeGroupId(), layout.isPrivateLayout(),
819                            layout.getLayoutId(), StringPool.BLANK, articleId, true);
820            }
821    
822            protected void importLayoutFriendlyURLs(
823                            PortletDataContext portletDataContext, Layout layout)
824                    throws Exception {
825    
826                    List<Element> layoutFriendlyURLElements =
827                            portletDataContext.getReferenceDataElements(
828                                    layout, LayoutFriendlyURL.class);
829    
830                    for (Element layoutFriendlyURLElement : layoutFriendlyURLElements) {
831                            String layoutFriendlyURLPath =
832                                    layoutFriendlyURLElement.attributeValue("path");
833    
834                            LayoutFriendlyURL layoutFriendlyURL =
835                                    (LayoutFriendlyURL)portletDataContext.getZipEntryAsObject(
836                                            layoutFriendlyURLPath);
837    
838                            StagedModelDataHandlerUtil.importStagedModel(
839                                    portletDataContext, layoutFriendlyURL);
840                    }
841            }
842    
843            protected void importLayoutIconImage(
844                            PortletDataContext portletDataContext, Layout importedLayout,
845                            Element layoutElement)
846                    throws Exception {
847    
848                    String iconImagePath = layoutElement.elementText("icon-image-path");
849    
850                    byte[] iconBytes = portletDataContext.getZipEntryAsByteArray(
851                            iconImagePath);
852    
853                    if (ArrayUtil.isNotEmpty(iconBytes)) {
854                            importedLayout.setIconImage(true);
855    
856                            if (importedLayout.getIconImageId() == 0) {
857                                    long iconImageId = CounterLocalServiceUtil.increment();
858    
859                                    importedLayout.setIconImageId(iconImageId);
860                            }
861    
862                            ImageLocalServiceUtil.updateImage(
863                                    importedLayout.getIconImageId(), iconBytes);
864                    }
865            }
866    
867            protected void importLinkedLayout(
868                            PortletDataContext portletDataContext, Layout layout,
869                            Layout importedLayout, Element layoutElement,
870                            Map<Long, Layout> newLayoutsMap)
871                    throws Exception {
872    
873                    UnicodeProperties typeSettingsProperties =
874                            layout.getTypeSettingsProperties();
875    
876                    long linkToLayoutId = GetterUtil.getLong(
877                            typeSettingsProperties.getProperty(
878                                    "linkToLayoutId", StringPool.BLANK));
879    
880                    String linkedToLayoutUuid = layoutElement.attributeValue(
881                            "linked-to-layout-uuid");
882    
883                    if (Validator.isNull(linkedToLayoutUuid)) {
884                            return;
885                    }
886    
887                    if (linkToLayoutId <= 0) {
888                            updateTypeSettings(portletDataContext, importedLayout, layout);
889    
890                            return;
891                    }
892    
893                    Element linkedToLayoutElement =
894                            portletDataContext.getReferenceDataElement(
895                                    layout, Layout.class, layout.getGroupId(), linkedToLayoutUuid);
896    
897                    if (linkedToLayoutElement != null) {
898                            String linkedToLayoutPath = linkedToLayoutElement.attributeValue(
899                                    "path");
900    
901                            Layout linkedToLayout =
902                                    (Layout)portletDataContext.getZipEntryAsObject(
903                                            linkedToLayoutPath);
904    
905                            StagedModelDataHandlerUtil.importStagedModel(
906                                    portletDataContext, linkedToLayout);
907    
908                            Layout importedLinkedLayout = newLayoutsMap.get(linkToLayoutId);
909    
910                            typeSettingsProperties.setProperty(
911                                    "privateLayout",
912                                    String.valueOf(importedLinkedLayout.isPrivateLayout()));
913                            typeSettingsProperties.setProperty(
914                                    "linkToLayoutId",
915                                    String.valueOf(importedLinkedLayout.getLayoutId()));
916                    }
917                    else {
918                            if (_log.isWarnEnabled()) {
919                                    StringBundler sb = new StringBundler(6);
920    
921                                    sb.append("Unable to link layout with friendly URL ");
922                                    sb.append(layout.getFriendlyURL());
923                                    sb.append(" and layout id ");
924                                    sb.append(layout.getLayoutId());
925                                    sb.append(" to layout with layout id ");
926                                    sb.append(linkToLayoutId);
927    
928                                    _log.warn(sb.toString());
929                            }
930                    }
931    
932                    updateTypeSettings(portletDataContext, importedLayout, layout);
933            }
934    
935            protected void importTheme(
936                            PortletDataContext portletDataContext, Layout layout,
937                            Layout importedLayout)
938                    throws Exception {
939    
940                    boolean importThemeSettings = MapUtil.getBoolean(
941                            portletDataContext.getParameterMap(),
942                            PortletDataHandlerKeys.THEME_REFERENCE);
943    
944                    if (_log.isDebugEnabled()) {
945                            _log.debug("Import theme settings " + importThemeSettings);
946                    }
947    
948                    if (importThemeSettings) {
949                            importedLayout.setColorSchemeId(layout.getColorSchemeId());
950                            importedLayout.setCss(layout.getCss());
951                            importedLayout.setThemeId(layout.getThemeId());
952                            importedLayout.setWapColorSchemeId(layout.getWapColorSchemeId());
953                            importedLayout.setWapThemeId(layout.getWapThemeId());
954                    }
955                    else {
956                            importedLayout.setColorSchemeId(StringPool.BLANK);
957                            importedLayout.setCss(StringPool.BLANK);
958                            importedLayout.setThemeId(StringPool.BLANK);
959                            importedLayout.setWapColorSchemeId(StringPool.BLANK);
960                            importedLayout.setWapThemeId(StringPool.BLANK);
961                    }
962            }
963    
964            protected void initNewLayoutPermissions(
965                            long companyId, long groupId, long userId, Layout layout,
966                            Layout importedLayout, boolean privateLayout)
967                    throws Exception {
968    
969                    boolean addGroupPermissions = true;
970    
971                    Group group = importedLayout.getGroup();
972    
973                    if (privateLayout && group.isUser()) {
974                            addGroupPermissions = false;
975                    }
976    
977                    boolean addGuestPermissions = false;
978    
979                    if (!privateLayout || layout.isTypeControlPanel()) {
980                            addGuestPermissions = true;
981                    }
982    
983                    ResourceLocalServiceUtil.addResources(
984                            companyId, groupId, userId, Layout.class.getName(),
985                            importedLayout.getPlid(), false, addGroupPermissions,
986                            addGuestPermissions);
987            }
988    
989            protected void mergePortlets(
990                    Layout layout, String newTypeSettings, String portletsMergeMode) {
991    
992                    try {
993                            UnicodeProperties previousTypeSettingsProperties =
994                                    layout.getTypeSettingsProperties();
995    
996                            LayoutTypePortlet previousLayoutType =
997                                    (LayoutTypePortlet)layout.getLayoutType();
998    
999                            LayoutTemplate previousLayoutTemplate =
1000                                    previousLayoutType.getLayoutTemplate();
1001    
1002                            List<String> previousColumns = previousLayoutTemplate.getColumns();
1003    
1004                            UnicodeProperties newTypeSettingsProperties = new UnicodeProperties(
1005                                    true);
1006    
1007                            newTypeSettingsProperties.load(newTypeSettings);
1008    
1009                            String layoutTemplateId = newTypeSettingsProperties.getProperty(
1010                                    LayoutTypePortletConstants.LAYOUT_TEMPLATE_ID);
1011    
1012                            previousTypeSettingsProperties.setProperty(
1013                                    LayoutTypePortletConstants.LAYOUT_TEMPLATE_ID,
1014                                    layoutTemplateId);
1015    
1016                            String nestedColumnIds = newTypeSettingsProperties.getProperty(
1017                                    LayoutTypePortletConstants.NESTED_COLUMN_IDS);
1018    
1019                            if (Validator.isNotNull(nestedColumnIds)) {
1020                                    previousTypeSettingsProperties.setProperty(
1021                                            LayoutTypePortletConstants.NESTED_COLUMN_IDS,
1022                                            nestedColumnIds);
1023    
1024                                    String[] nestedColumnIdsArray = StringUtil.split(
1025                                            nestedColumnIds);
1026    
1027                                    for (String nestedColumnId : nestedColumnIdsArray) {
1028                                            String nestedColumnValue =
1029                                                    newTypeSettingsProperties.getProperty(nestedColumnId);
1030    
1031                                            previousTypeSettingsProperties.setProperty(
1032                                                    nestedColumnId, nestedColumnValue);
1033                                    }
1034                            }
1035    
1036                            LayoutTemplate newLayoutTemplate =
1037                                    LayoutTemplateLocalServiceUtil.getLayoutTemplate(
1038                                            layoutTemplateId, false, null);
1039    
1040                            String[] newPortletIds = new String[0];
1041    
1042                            for (String columnId : newLayoutTemplate.getColumns()) {
1043                                    String columnValue = newTypeSettingsProperties.getProperty(
1044                                            columnId);
1045    
1046                                    String[] portletIds = StringUtil.split(columnValue);
1047    
1048                                    if (!previousColumns.contains(columnId)) {
1049                                            newPortletIds = ArrayUtil.append(newPortletIds, portletIds);
1050                                    }
1051                                    else {
1052                                            String[] previousPortletIds = StringUtil.split(
1053                                                    previousTypeSettingsProperties.getProperty(columnId));
1054    
1055                                            portletIds = appendPortletIds(
1056                                                    previousPortletIds, portletIds, portletsMergeMode);
1057    
1058                                            previousTypeSettingsProperties.setProperty(
1059                                                    columnId, StringUtil.merge(portletIds));
1060                                    }
1061                            }
1062    
1063                            // Add portlets in non-existent column to the first column
1064    
1065                            String columnId = previousColumns.get(0);
1066    
1067                            String[] portletIds = StringUtil.split(
1068                                    previousTypeSettingsProperties.getProperty(columnId));
1069    
1070                            appendPortletIds(portletIds, newPortletIds, portletsMergeMode);
1071    
1072                            previousTypeSettingsProperties.setProperty(
1073                                    columnId, StringUtil.merge(portletIds));
1074    
1075                            layout.setTypeSettings(previousTypeSettingsProperties.toString());
1076                    }
1077                    catch (IOException ioe) {
1078                            layout.setTypeSettings(newTypeSettings);
1079                    }
1080            }
1081    
1082            protected void populateElementLayoutMetadata(
1083                            Element layoutElement, Layout layout)
1084                    throws Exception {
1085    
1086                    LayoutStagingHandler layoutStagingHandler =
1087                            LayoutStagingUtil.getLayoutStagingHandler(layout);
1088    
1089                    if (layoutStagingHandler != null) {
1090                            LayoutRevision layoutRevision =
1091                                    layoutStagingHandler.getLayoutRevision();
1092    
1093                            if (layoutRevision != null) {
1094                                    layoutElement.addAttribute(
1095                                            "layout-revision-id",
1096                                            String.valueOf(layoutRevision.getLayoutRevisionId()));
1097                                    layoutElement.addAttribute(
1098                                            "layout-branch-id",
1099                                            String.valueOf(layoutRevision.getLayoutBranchId()));
1100    
1101                                    LayoutBranch layoutBranch = layoutRevision.getLayoutBranch();
1102    
1103                                    layoutElement.addAttribute(
1104                                            "layout-branch-name",
1105                                            String.valueOf(layoutBranch.getName()));
1106                            }
1107                    }
1108    
1109                    layoutElement.addAttribute("layout-uuid", layout.getUuid());
1110                    layoutElement.addAttribute(
1111                            "layout-id", String.valueOf(layout.getLayoutId()));
1112                    layoutElement.addAttribute(
1113                            "layout-priority", String.valueOf(layout.getPriority()));
1114    
1115                    String layoutPrototypeUuid = layout.getLayoutPrototypeUuid();
1116    
1117                    if (Validator.isNotNull(layoutPrototypeUuid)) {
1118                            LayoutPrototype layoutPrototype =
1119                                    LayoutPrototypeLocalServiceUtil.
1120                                            getLayoutPrototypeByUuidAndCompanyId(
1121                                                    layoutPrototypeUuid, layout.getCompanyId());
1122    
1123                            layoutElement.addAttribute(
1124                                    "layout-prototype-uuid", layoutPrototypeUuid);
1125                            layoutElement.addAttribute(
1126                                    "layout-prototype-name",
1127                                    layoutPrototype.getName(LocaleUtil.getDefault()));
1128                    }
1129            }
1130    
1131            protected void removePortletFromLayoutTypePortlet(
1132                    String portletId, LayoutTypePortlet layoutTypePortlet) {
1133    
1134                    List<String> columnIds = new ArrayList<String>();
1135    
1136                    LayoutTemplate layoutTemplate = layoutTypePortlet.getLayoutTemplate();
1137    
1138                    columnIds.addAll(layoutTemplate.getColumns());
1139    
1140                    String nestedColumnIds = layoutTypePortlet.getTypeSettingsProperty(
1141                            LayoutTypePortletConstants.NESTED_COLUMN_IDS);
1142    
1143                    columnIds.addAll(ListUtil.fromArray(StringUtil.split(nestedColumnIds)));
1144    
1145                    for (String columnId : columnIds) {
1146                            String columnValue = layoutTypePortlet.getTypeSettingsProperty(
1147                                    columnId);
1148    
1149                            columnValue = StringUtil.remove(columnValue, portletId);
1150    
1151                            layoutTypePortlet.setTypeSettingsProperty(columnId, columnValue);
1152                    }
1153            }
1154    
1155            protected void updateTypeSettings(
1156                            PortletDataContext portletDataContext, Layout importedLayout,
1157                            Layout layout)
1158                    throws PortalException, SystemException {
1159    
1160                    long groupId = layout.getGroupId();
1161    
1162                    try {
1163                            LayoutTypePortlet importedLayoutType =
1164                                    (LayoutTypePortlet)importedLayout.getLayoutType();
1165    
1166                            List<String> importedPortletIds =
1167                                    importedLayoutType.getPortletIds();
1168    
1169                            layout.setGroupId(importedLayout.getGroupId());
1170    
1171                            LayoutTypePortlet layoutTypePortlet =
1172                                    (LayoutTypePortlet)layout.getLayoutType();
1173    
1174                            // Remove portlets with unchecked setup from the target layout
1175    
1176                            List<String> sourcePortletIds = layoutTypePortlet.getPortletIds();
1177    
1178                            for (String portletId : sourcePortletIds) {
1179                                    boolean importPortletConfiguration = false;
1180    
1181                                    try {
1182                                            Map<String, Boolean> importPortletControlsMap =
1183                                                    LayoutImporter.getImportPortletControlsMap(
1184                                                            portletDataContext.getCompanyId(), portletId,
1185                                                            portletDataContext.getParameterMap(), null,
1186                                                            portletDataContext.getManifestSummary());
1187    
1188                                            importPortletConfiguration = importPortletControlsMap.get(
1189                                                    PortletDataHandlerKeys.PORTLET_CONFIGURATION);
1190                                    }
1191                                    catch (Exception e) {
1192                                    }
1193    
1194                                    if (!importPortletConfiguration &&
1195                                            !importedPortletIds.contains(portletId)) {
1196    
1197                                            removePortletFromLayoutTypePortlet(
1198                                                    portletId, layoutTypePortlet);
1199                                    }
1200                            }
1201    
1202                            importedPortletIds.removeAll(layoutTypePortlet.getPortletIds());
1203    
1204                            // Delete already removed portlet instances
1205    
1206                            if (!importedPortletIds.isEmpty()) {
1207                                    PortletLocalServiceUtil.deletePortlets(
1208                                            importedLayout.getCompanyId(),
1209                                            importedPortletIds.toArray(
1210                                                    new String[importedPortletIds.size()]),
1211                                            importedLayout.getPlid());
1212                            }
1213    
1214                            importedLayout.setTypeSettingsProperties(
1215                                    layoutTypePortlet.getTypeSettingsProperties());
1216                    }
1217                    finally {
1218                            layout.setGroupId(groupId);
1219                    }
1220            }
1221    
1222            private static Log _log = LogFactoryUtil.getLog(
1223                    LayoutStagedModelDataHandler.class);
1224    
1225            private LayoutLocalServiceHelper _layoutLocalServiceHelper =
1226                    (LayoutLocalServiceHelper)PortalBeanLocatorUtil.locate(
1227                            LayoutLocalServiceHelper.class.getName());
1228    
1229    }