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