001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.service.impl;
016    
017    import com.liferay.portal.LayoutFriendlyURLException;
018    import com.liferay.portal.LayoutHiddenException;
019    import com.liferay.portal.LayoutNameException;
020    import com.liferay.portal.LayoutParentLayoutIdException;
021    import com.liferay.portal.LayoutTypeException;
022    import com.liferay.portal.NoSuchLayoutException;
023    import com.liferay.portal.RequiredLayoutException;
024    import com.liferay.portal.kernel.exception.PortalException;
025    import com.liferay.portal.kernel.exception.SystemException;
026    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
027    import com.liferay.portal.kernel.language.LanguageUtil;
028    import com.liferay.portal.kernel.util.DateFormatFactoryUtil;
029    import com.liferay.portal.kernel.util.FileUtil;
030    import com.liferay.portal.kernel.util.FriendlyURLNormalizerUtil;
031    import com.liferay.portal.kernel.util.GetterUtil;
032    import com.liferay.portal.kernel.util.ListUtil;
033    import com.liferay.portal.kernel.util.LocaleUtil;
034    import com.liferay.portal.kernel.util.StringBundler;
035    import com.liferay.portal.kernel.util.StringPool;
036    import com.liferay.portal.kernel.util.UnicodeProperties;
037    import com.liferay.portal.kernel.util.Validator;
038    import com.liferay.portal.kernel.workflow.WorkflowConstants;
039    import com.liferay.portal.lar.LayoutExporter;
040    import com.liferay.portal.lar.LayoutImporter;
041    import com.liferay.portal.lar.PortletExporter;
042    import com.liferay.portal.lar.PortletImporter;
043    import com.liferay.portal.model.Group;
044    import com.liferay.portal.model.Layout;
045    import com.liferay.portal.model.LayoutConstants;
046    import com.liferay.portal.model.LayoutReference;
047    import com.liferay.portal.model.LayoutSet;
048    import com.liferay.portal.model.LayoutTypePortlet;
049    import com.liferay.portal.model.Portlet;
050    import com.liferay.portal.model.PortletConstants;
051    import com.liferay.portal.model.PortletPreferences;
052    import com.liferay.portal.model.Resource;
053    import com.liferay.portal.model.ResourceConstants;
054    import com.liferay.portal.model.User;
055    import com.liferay.portal.model.impl.LayoutImpl;
056    import com.liferay.portal.service.ServiceContext;
057    import com.liferay.portal.service.base.LayoutLocalServiceBaseImpl;
058    import com.liferay.portal.util.PortalUtil;
059    import com.liferay.portal.util.PortletKeys;
060    import com.liferay.portal.util.PropsValues;
061    import com.liferay.portal.util.comparator.LayoutComparator;
062    import com.liferay.portal.util.comparator.LayoutPriorityComparator;
063    import com.liferay.portlet.PortletPreferencesFactoryUtil;
064    import com.liferay.portlet.expando.model.ExpandoBridge;
065    
066    import java.io.File;
067    import java.io.IOException;
068    import java.io.InputStream;
069    
070    import java.text.DateFormat;
071    
072    import java.util.ArrayList;
073    import java.util.Date;
074    import java.util.HashMap;
075    import java.util.HashSet;
076    import java.util.LinkedHashSet;
077    import java.util.List;
078    import java.util.Locale;
079    import java.util.Map;
080    import java.util.Set;
081    
082    import javax.portlet.PortletException;
083    
084    /**
085     * The implementation of the layout local service.
086     *
087     * @author Brian Wing Shun Chan
088     * @author Joel Kozikowski
089     * @author Charles May
090     * @author Raymond Augé
091     * @author Jorge Ferrer
092     * @author Bruno Farache
093     */
094    public class LayoutLocalServiceImpl extends LayoutLocalServiceBaseImpl {
095    
096            /**
097             * Returns the object counter's name.
098             *
099             * @param  groupId the primary key of the group
100             * @param  privateLayout whether layout is private to the group
101             * @return the object counter's name
102             */
103            public static String getCounterName(long groupId, boolean privateLayout) {
104                    StringBundler sb = new StringBundler();
105    
106                    sb.append(Layout.class.getName());
107                    sb.append(StringPool.POUND);
108                    sb.append(groupId);
109                    sb.append(StringPool.POUND);
110                    sb.append(privateLayout);
111    
112                    return sb.toString();
113            }
114    
115            /**
116             * Adds a layout with additional parameters.
117             *
118             * <p>
119             * This method handles the creation of the layout including its resources,
120             * metadata, and internal data structures. It is not necessary to make
121             * subsequent calls to any methods to setup default groups, resources, ...
122             * etc.
123             * </p>
124             *
125             * @param  userId the primary key of the user
126             * @param  groupId the primary key of the group
127             * @param  privateLayout whether the layout is private to the group
128             * @param  parentLayoutId the primary key of the parent layout (optionally
129             *         {@link
130             *         com.liferay.portal.model.LayoutConstants#DEFAULT_PARENT_LAYOUT_ID})
131             * @param  nameMap the layout's locales and localized names
132             * @param  titleMap the layout's locales and localized titles
133             * @param  descriptionMap the layout's locales and localized descriptions
134             * @param  keywordsMap the layout's locales and localized keywords
135             * @param  robotsMap the layout's locales and localized robots
136             * @param  type the layout's type (optionally {@link
137             *         com.liferay.portal.model.LayoutConstants#TYPE_PORTLET}). The
138             *         possible types can be found in {@link
139             *         com.liferay.portal.model.LayoutConstants}.
140             * @param  hidden whether the layout is hidden
141             * @param  friendlyURL the layout's friendly URL (optionally {@link
142             *         com.liferay.portal.util.PropsValues#DEFAULT_USER_PRIVATE_LAYOUT_FRIENDLY_URL}
143             *         or {@link
144             *         com.liferay.portal.util.PropsValues#DEFAULT_USER_PUBLIC_LAYOUT_FRIENDLY_URL}).
145             *         The default values can be overridden in
146             *         <code>portal-ext.properties</code> by specifying new values for
147             *         the corresponding properties defined in {@link
148             *         com.liferay.portal.util.PropsValues}. To see how the URL is
149             *         normalized when accessed see {@link
150             *         com.liferay.portal.kernel.util.FriendlyURLNormalizerUtil#normalize(
151             *         String)}.
152             * @param  locked whether the layout is locked
153             * @param  serviceContext the service context. Must specify the replacement
154             *         universally unique identifier and can specify the replacement
155             *         create date, replacement modified date and the new expando bridge
156             *         attributes.
157             * @return the layout
158             * @throws PortalException if a group or user with the primary key could not
159             *         be found, or if layout values were invalid
160             * @throws SystemException if a system exception occurred
161             */
162            public Layout addLayout(
163                            long userId, long groupId, boolean privateLayout,
164                            long parentLayoutId, Map<Locale, String> nameMap,
165                            Map<Locale, String> titleMap, Map<Locale, String> descriptionMap,
166                            Map<Locale, String> keywordsMap, Map<Locale, String> robotsMap,
167                            String type, boolean hidden, String friendlyURL, boolean locked,
168                            ServiceContext serviceContext)
169                    throws PortalException, SystemException {
170    
171                    // Layout
172    
173                    User user = userPersistence.findByPrimaryKey(userId);
174                    long layoutId = getNextLayoutId(groupId, privateLayout);
175                    parentLayoutId = getParentLayoutId(
176                            groupId, privateLayout, parentLayoutId);
177                    String name = nameMap.get(LocaleUtil.getDefault());
178                    friendlyURL = getFriendlyURL(
179                            groupId, privateLayout, layoutId, name, friendlyURL);
180                    int priority = getNextPriority(groupId, privateLayout, parentLayoutId);
181    
182                    validate(
183                            groupId, privateLayout, layoutId, parentLayoutId, name, type,
184                            hidden, friendlyURL);
185    
186                    Date now = new Date();
187    
188                    long plid = counterLocalService.increment();
189    
190                    Layout layout = layoutPersistence.create(plid);
191    
192                    layout.setUuid(serviceContext.getUuid());
193                    layout.setGroupId(groupId);
194                    layout.setCompanyId(user.getCompanyId());
195                    layout.setCreateDate(serviceContext.getCreateDate(now));
196                    layout.setModifiedDate(serviceContext.getModifiedDate(now));
197                    layout.setPrivateLayout(privateLayout);
198                    layout.setLayoutId(layoutId);
199                    layout.setParentLayoutId(parentLayoutId);
200                    layout.setNameMap(nameMap);
201                    layout.setTitleMap(titleMap);
202                    layout.setDescriptionMap(descriptionMap);
203                    layout.setKeywordsMap(keywordsMap);
204                    layout.setRobotsMap(robotsMap);
205                    layout.setType(type);
206                    layout.setHidden(hidden);
207                    layout.setFriendlyURL(friendlyURL);
208                    layout.setPriority(priority);
209    
210                    if (locked) {
211                            UnicodeProperties typeSettingsProperties =
212                                    layout.getTypeSettingsProperties();
213    
214                            typeSettingsProperties.put("locked", String.valueOf(locked));
215    
216                            layout.setTypeSettingsProperties(typeSettingsProperties);
217                    }
218    
219                    if (type.equals(LayoutConstants.TYPE_PORTLET)) {
220                            LayoutTypePortlet layoutTypePortlet =
221                                    (LayoutTypePortlet)layout.getLayoutType();
222    
223                            layoutTypePortlet.setLayoutTemplateId(
224                                    0, PropsValues.LAYOUT_DEFAULT_TEMPLATE_ID, false);
225                    }
226    
227                    layoutPersistence.update(layout, false);
228    
229                    // Resources
230    
231                    boolean addGroupPermissions = true;
232    
233                    Group group = groupLocalService.getGroup(groupId);
234    
235                    if (privateLayout && group.isUser()) {
236                            addGroupPermissions = false;
237                    }
238    
239                    boolean addGuestPermissions = false;
240    
241                    if (!privateLayout || type.equals(LayoutConstants.TYPE_CONTROL_PANEL)) {
242                            addGuestPermissions = true;
243                    }
244    
245                    resourceLocalService.addResources(
246                            user.getCompanyId(), groupId, user.getUserId(),
247                            Layout.class.getName(), layout.getPlid(), false,
248                            addGroupPermissions, addGuestPermissions);
249    
250                    // Group
251    
252                    groupLocalService.updateSite(groupId, true);
253    
254                    // Layout set
255    
256                    layoutSetLocalService.updatePageCount(groupId, privateLayout);
257    
258                    LayoutSet layoutSet = layoutSetLocalService.getLayoutSet(
259                            groupId, privateLayout);
260    
261                    layout.setLayoutSet(layoutSet);
262    
263                    // Expando
264    
265                    ExpandoBridge expandoBridge = layout.getExpandoBridge();
266    
267                    expandoBridge.setAttributes(serviceContext);
268    
269                    // Message boards
270    
271                    if (PropsValues.LAYOUT_COMMENTS_ENABLED) {
272                            mbMessageLocalService.addDiscussionMessage(
273                                    userId, user.getFullName(), groupId, Layout.class.getName(),
274                                    plid, WorkflowConstants.ACTION_PUBLISH);
275                    }
276    
277                    return layout;
278            }
279    
280            /**
281             * Adds a layout.
282             *
283             * <p>
284             * This method handles the creation of the layout including its resources,
285             * metadata, and internal data structures. It is not necessary to make
286             * subsequent calls to any methods to setup default groups, resources, ...
287             * etc.
288             * </p>
289             *
290             * @param  userId the primary key of the user
291             * @param  groupId the primary key of the group
292             * @param  privateLayout whether the layout is private to the group
293             * @param  parentLayoutId the primary key of the parent layout (optionally
294             *         {@link
295             *         com.liferay.portal.model.LayoutConstants#DEFAULT_PARENT_LAYOUT_ID}).
296             *         The possible values can be found in {@link
297             *         com.liferay.portal.model.LayoutConstants}.
298             * @param  name the layout's name (optionally {@link
299             *         com.liferay.portal.util.PropsValues#DEFAULT_USER_PRIVATE_LAYOUT_NAME}
300             *         or {@link
301             *         com.liferay.portal.util.PropsValues#DEFAULT_USER_PUBLIC_LAYOUT_NAME}).
302             *         The default values can be overridden in
303             *         <code>portal-ext.properties</code> by specifying new values for
304             *         the corresponding properties defined in {@link
305             *         com.liferay.portal.util.PropsValues}
306             * @param  title the layout's title
307             * @param  description the layout's description
308             * @param  type the layout's type (optionally {@link
309             *         com.liferay.portal.model.LayoutConstants#TYPE_PORTLET}). The
310             *         possible types can be found in {@link
311             *         com.liferay.portal.model.LayoutConstants}.
312             * @param  hidden whether the layout is hidden
313             * @param  friendlyURL the friendly URL of the layout (optionally {@link
314             *         com.liferay.portal.util.PropsValues#DEFAULT_USER_PRIVATE_LAYOUT_FRIENDLY_URL}
315             *         or {@link
316             *         com.liferay.portal.util.PropsValues#DEFAULT_USER_PUBLIC_LAYOUT_FRIENDLY_URL}).
317             *         The default values can be overridden in
318             *         <code>portal-ext.properties</code> by specifying new values for
319             *         the corresponding properties defined in {@link
320             *         com.liferay.portal.util.PropsValues}. To see how the URL is
321             *         normalized when accessed see {@link
322             *         com.liferay.portal.kernel.util.FriendlyURLNormalizerUtil#normalize(
323             *         String)}.
324             * @param  locked whether the layout is locked
325             * @param  serviceContext the service context. Must specify the universally
326             *         unique identifier and can specify the create date and modified
327             *         date.
328             * @return the layout
329             * @throws PortalException if a group or user with the primary key could not
330             *         be found
331             * @throws SystemException if a system exception occurred
332             */
333            public Layout addLayout(
334                            long userId, long groupId, boolean privateLayout,
335                            long parentLayoutId, String name, String title, String description,
336                            String type, boolean hidden, String friendlyURL, boolean locked,
337                            ServiceContext serviceContext)
338                    throws PortalException, SystemException {
339    
340                    Map<Locale, String> localeNamesMap = new HashMap<Locale, String>();
341    
342                    Locale defaultLocale = LocaleUtil.getDefault();
343    
344                    localeNamesMap.put(defaultLocale, name);
345    
346                    return addLayout(
347                            userId, groupId, privateLayout, parentLayoutId, localeNamesMap,
348                            new HashMap<Locale, String>(), new HashMap<Locale, String>(),
349                            new HashMap<Locale, String>(), new HashMap<Locale, String>(),
350                            type, hidden, friendlyURL, locked, serviceContext);
351            }
352    
353            /**
354             * Deletes the layout, its child layouts, and its associated resources.
355             *
356             * @param  layout the layout
357             * @param  updateLayoutSet whether the layout set's page counter needs to be
358             *         updated
359             * @param  serviceContext the service context
360             * @throws PortalException if a portal exception occurred
361             * @throws SystemException if a system exception occurred
362             */
363            public void deleteLayout(
364                            Layout layout, boolean updateLayoutSet,
365                            ServiceContext serviceContext)
366                    throws PortalException, SystemException {
367    
368                    // Child layouts
369    
370                    List<Layout> childLayouts = layoutPersistence.findByG_P_P(
371                            layout.getGroupId(), layout.isPrivateLayout(),
372                            layout.getLayoutId());
373    
374                    for (Layout childLayout : childLayouts) {
375                            deleteLayout(childLayout, updateLayoutSet, serviceContext);
376                    }
377    
378                    // Portlet preferences
379    
380                    portletPreferencesLocalService.deletePortletPreferences(
381                            PortletKeys.PREFS_OWNER_ID_DEFAULT,
382                            PortletKeys.PREFS_OWNER_TYPE_LAYOUT, layout.getPlid());
383    
384                    // Ratings
385    
386                    ratingsStatsLocalService.deleteStats(
387                            Layout.class.getName(), layout.getPlid());
388    
389                    // Message boards
390    
391                    mbMessageLocalService.deleteDiscussionMessages(
392                            Layout.class.getName(), layout.getPlid());
393    
394                    // Journal articles
395    
396                    journalArticleLocalService.deleteLayoutArticleReferences(
397                            layout.getGroupId(), layout.getUuid());
398    
399                    // Journal content searches
400    
401                    journalContentSearchLocalService.deleteLayoutContentSearches(
402                            layout.getGroupId(), layout.isPrivateLayout(),
403                            layout.getLayoutId());
404    
405                    // Expando
406    
407                    expandoValueLocalService.deleteValues(
408                            Layout.class.getName(), layout.getPlid());
409    
410                    // Icon
411    
412                    imageLocalService.deleteImage(layout.getIconImageId());
413    
414                    // Scope group
415    
416                    Group scopeGroup = layout.getScopeGroup();
417    
418                    if (scopeGroup != null) {
419                            groupLocalService.deleteGroup(scopeGroup.getGroupId());
420                    }
421    
422                    // Resources
423    
424                    String primKey =
425                            layout.getPlid() + PortletConstants.LAYOUT_SEPARATOR + "%";
426    
427                    List<Resource> resources = resourceFinder.findByC_P(
428                            layout.getCompanyId(), primKey);
429    
430                    for (Resource resource : resources) {
431                            resourceLocalService.deleteResource(resource);
432                    }
433    
434                    resourceLocalService.deleteResource(
435                            layout.getCompanyId(), Layout.class.getName(),
436                            ResourceConstants.SCOPE_INDIVIDUAL, layout.getPlid());
437    
438                    // Layout
439    
440                    layoutPersistence.remove(layout);
441    
442                    // Layout set
443    
444                    if (updateLayoutSet) {
445                            layoutSetLocalService.updatePageCount(
446                                    layout.getGroupId(), layout.isPrivateLayout());
447                    }
448            }
449    
450            /**
451             * Deletes the layout with the plid, also deleting the layout's child
452             * layouts, and associated resources.
453             *
454             * @param  plid the primary key of the layout
455             * @param  serviceContext the service context
456             * @throws PortalException if a layout with the primary key could not be
457             *         found , or if some other portal exception occurred
458             * @throws SystemException if a system exception occurred
459             */
460            public void deleteLayout(long plid, ServiceContext serviceContext)
461                    throws PortalException, SystemException {
462    
463                    Layout layout = layoutPersistence.findByPrimaryKey(plid);
464    
465                    deleteLayout(layout, true, serviceContext);
466            }
467    
468            /**
469             * Deletes the layout with the primary key, also deleting the layout's child
470             * layouts, and associated resources.
471             *
472             * @param  groupId the primary key of the group
473             * @param  privateLayout whether the layout is private to the group
474             * @param  layoutId the primary key of the layout
475             * @param  serviceContext the service context
476             * @throws PortalException if a matching layout could not be found , or if
477             *         some other portal exception occurred
478             * @throws SystemException if a system exception occurred
479             */
480            public void deleteLayout(
481                            long groupId, boolean privateLayout, long layoutId,
482                            ServiceContext serviceContext)
483                    throws PortalException, SystemException {
484    
485                    Layout layout = layoutPersistence.findByG_P_L(
486                            groupId, privateLayout, layoutId);
487    
488                    deleteLayout(layout, true, serviceContext);
489            }
490    
491            /**
492             * Deletes the group's private or non-private layouts, also deleting the
493             * layouts' child layouts, and associated resources.
494             *
495             * @param  groupId the primary key of the group
496             * @param  privateLayout whether the layout is private to the group
497             * @param  serviceContext the service context
498             * @throws PortalException if a group with the primary key could not be
499             *         found or if a layout set for the group and privacy could not be
500             *         found
501             * @throws SystemException if a system exception occurred
502             */
503            public void deleteLayouts(
504                            long groupId, boolean privateLayout, ServiceContext serviceContext)
505                    throws PortalException, SystemException {
506    
507                    // Layouts
508    
509                    List<Layout> layouts = layoutPersistence.findByG_P_P(
510                            groupId, privateLayout, LayoutConstants.DEFAULT_PARENT_LAYOUT_ID);
511    
512                    for (Layout layout : layouts) {
513                            try {
514                                    deleteLayout(layout, false, serviceContext);
515                            }
516                            catch (NoSuchLayoutException nsle) {
517                            }
518                    }
519    
520                    // Layout set
521    
522                    layoutSetLocalService.updatePageCount(groupId, privateLayout);
523    
524                    // Counter
525    
526                    counterLocalService.reset(getCounterName(groupId, privateLayout));
527            }
528    
529            /**
530             * Exports layouts with the primary keys and criteria as a byte array.
531             *
532             * @param  groupId the primary key of the group
533             * @param  privateLayout whether the layout is private to the group
534             * @param  layoutIds the primary keys of the layouts to be exported
535             * @param  parameterMap the mapping of parameters indicating which
536             *         information to export. For information on the keys used in the
537             *         map see {@link
538             *         com.liferay.portal.kernel.lar.PortletDataHandlerKeys}.
539             * @param  startDate the export's start date
540             * @param  endDate the export's end date
541             * @return the layouts as a byte array
542             * @throws PortalException if a group or any layout with the primary key
543             *         could not be found, or if some other portal exception occurred
544             * @throws SystemException if a system exception occurred
545             */
546            public byte[] exportLayouts(
547                            long groupId, boolean privateLayout, long[] layoutIds,
548                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
549                    throws PortalException, SystemException {
550    
551                    File file = exportLayoutsAsFile(
552                            groupId, privateLayout, layoutIds, parameterMap, startDate,
553                            endDate);
554    
555                    try {
556                            return FileUtil.getBytes(file);
557                    }
558                    catch (IOException ioe) {
559                            throw new SystemException(ioe);
560                    }
561                    finally {
562                            file.delete();
563                    }
564            }
565    
566            /**
567             * Exports all layouts that match the criteria as a byte array.
568             *
569             * @param  groupId the primary key of the group
570             * @param  privateLayout whether the layout is private to the group
571             * @param  parameterMap the mapping of parameters indicating which
572             *         information to export. For information on the keys used in the
573             *         map see {@link
574             *         com.liferay.portal.kernel.lar.PortletDataHandlerKeys}.
575             * @param  startDate the export's start date
576             * @param  endDate the export's end date
577             * @return the layout as a byte array
578             * @throws PortalException if a group with the primary key could not be
579             *         found or if some other portal exception occurred
580             * @throws SystemException if a system exception occurred
581             */
582            public byte[] exportLayouts(
583                            long groupId, boolean privateLayout,
584                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
585                    throws PortalException, SystemException {
586    
587                    return exportLayouts(
588                            groupId, privateLayout, null, parameterMap, startDate, endDate);
589            }
590    
591            /**
592             * Exports the layouts that match the primary keys and criteria as a file.
593             *
594             * @param  groupId the primary key of the group
595             * @param  privateLayout whether the layout is private to the group
596             * @param  layoutIds the primary keys of the layouts to be exported
597             *         (optionally <code>null</code>)
598             * @param  parameterMap the mapping of parameters indicating which
599             *         information to export. For information on the keys used in the
600             *         map see {@link
601             *         com.liferay.portal.kernel.lar.PortletDataHandlerKeys}.
602             * @param  startDate the export's start date
603             * @param  endDate the export's end date
604             * @return the layouts as a File
605             * @throws PortalException if a group or any layout with the primary key
606             *         could not be found, or if some other portal exception occurred
607             * @throws SystemException if a system exception occurred
608             */
609            public File exportLayoutsAsFile(
610                            long groupId, boolean privateLayout, long[] layoutIds,
611                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
612                    throws PortalException, SystemException {
613    
614                    try {
615                            LayoutExporter layoutExporter = new LayoutExporter();
616    
617                            return layoutExporter.exportLayoutsAsFile(
618                                    groupId, privateLayout, layoutIds, parameterMap, startDate,
619                                    endDate);
620                    }
621                    catch (PortalException pe) {
622                            throw pe;
623                    }
624                    catch (SystemException se) {
625                            throw se;
626                    }
627                    catch (Exception e) {
628                            throw new SystemException(e);
629                    }
630            }
631    
632            /**
633             * Exports the portlet information (categories, permissions, ... etc.) as a
634             * byte array.
635             *
636             * @param  plid the primary key of the layout
637             * @param  groupId the primary key of the group
638             * @param  portletId the primary key of the portlet
639             * @param  parameterMap the mapping of parameters indicating which
640             *         information to export. For information on the keys used in the
641             *         map see {@link
642             *         com.liferay.portal.kernel.lar.PortletDataHandlerKeys}.
643             * @param  startDate the export's start date
644             * @param  endDate the export's end date
645             * @return the portlet information as a byte array
646             * @throws PortalException if a group or portlet with the primary key could
647             *         not be found, or if some other portal exception occurred
648             * @throws SystemException if a system exception occurred
649             */
650            public byte[] exportPortletInfo(
651                            long plid, long groupId, String portletId,
652                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
653                    throws PortalException, SystemException {
654    
655                    File file = exportPortletInfoAsFile(
656                            plid, groupId, portletId, parameterMap, startDate, endDate);
657    
658                    try {
659                            return FileUtil.getBytes(file);
660                    }
661                    catch (IOException ioe) {
662                            throw new SystemException(ioe);
663                    }
664                    finally {
665                            file.delete();
666                    }
667            }
668    
669            /**
670             * Exports the portlet information (categories, permissions, ... etc.) as a
671             * file.
672             *
673             * @param  plid the primary key of the layout
674             * @param  groupId the primary key of the group
675             * @param  portletId the primary key of the portlet
676             * @param  parameterMap the mapping of parameters indicating which
677             *         information to export. For information on the keys used in the
678             *         map see {@link
679             *         com.liferay.portal.kernel.lar.PortletDataHandlerKeys}.
680             * @param  startDate the export's start date
681             * @param  endDate the export's end date
682             * @return the portlet information as a file
683             * @throws PortalException if a group or portlet with the primary key could
684             *         not be found, or if some other portal exception occurred
685             * @throws SystemException if a system exception occurred
686             */
687            public File exportPortletInfoAsFile(
688                            long plid, long groupId, String portletId,
689                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
690                    throws PortalException, SystemException {
691    
692                    try {
693                            PortletExporter portletExporter = new PortletExporter();
694    
695                            return portletExporter.exportPortletInfoAsFile(
696                                    plid, groupId, portletId, parameterMap, startDate, endDate);
697                    }
698                    catch (PortalException pe) {
699                            throw pe;
700                    }
701                    catch (SystemException se) {
702                            throw se;
703                    }
704                    catch (Exception e) {
705                            throw new SystemException(e);
706                    }
707            }
708    
709            /**
710             * Returns the layout matching the universally unique identifier and group
711             * ID
712             *
713             * @param  uuid the universally unique identifier of the scope layout
714             * @param  groupId the primary key of the group
715             * @return the layout, or <code>null</code> if a matching layout could not
716             *         be found
717             * @throws SystemException if a system exception occurred
718             */
719            public Layout fetchLayoutByUuidAndGroupId(String uuid, long groupId)
720                    throws SystemException {
721    
722                    return layoutPersistence.fetchByUUID_G(uuid, groupId);
723            }
724    
725            /**
726             * Returns the primary key of the default layout for the group
727             *
728             * @param  groupId the primary key of the group
729             * @return the primary key of the default layout for the group (optionally
730             *         {@link com.liferay.portal.model.LayoutConstants#DEFAULT_PLID})
731             * @throws SystemException if a system exception occurred
732             */
733            public long getDefaultPlid(long groupId) throws SystemException {
734                    if (groupId > 0) {
735                            List<Layout> layouts = layoutPersistence.findByGroupId(
736                                    groupId, 0, 1);
737    
738                            if (layouts.size() > 0) {
739                                    Layout layout = layouts.get(0);
740    
741                                    return layout.getPlid();
742                            }
743                    }
744    
745                    return LayoutConstants.DEFAULT_PLID;
746            }
747    
748            /**
749             * Returns primary key of the matching default layout for the group
750             *
751             * @param  groupId the primary key of the group
752             * @param  privateLayout whether the layout is private to the group
753             * @return the primary key of the default layout for the group; {@link
754             *         com.liferay.portal.model.LayoutConstants#DEFAULT_PLID}) otherwise
755             * @throws SystemException if a system exception occurred
756             */
757            public long getDefaultPlid(long groupId, boolean privateLayout)
758                    throws SystemException {
759    
760                    if (groupId > 0) {
761                            List<Layout> layouts = layoutPersistence.findByG_P(
762                                    groupId, privateLayout, 0, 1);
763    
764                            if (layouts.size() > 0) {
765                                    Layout layout = layouts.get(0);
766    
767                                    return layout.getPlid();
768                            }
769                    }
770    
771                    return LayoutConstants.DEFAULT_PLID;
772            }
773    
774            /**
775             * Returns primary key of the default portlet layout for the group
776             *
777             * @param  groupId the primary key of the group
778             * @param  privateLayout whether the layout is private to the group
779             * @param  portletId the primary key of the portlet
780             * @return the primary key of the default portlet layout for the group;
781             *         {@link com.liferay.portal.model.LayoutConstants#DEFAULT_PLID}
782             *         otherwise
783             * @throws PortalException if a portlet with the primary key could not be
784             *         found
785             * @throws SystemException if a system exception occurred
786             */
787            public long getDefaultPlid(
788                            long groupId, boolean privateLayout, String portletId)
789                    throws PortalException, SystemException {
790    
791                    if (groupId > 0) {
792                            List<Layout> layouts = layoutPersistence.findByG_P(
793                                    groupId, privateLayout);
794    
795                            for (Layout layout : layouts) {
796                                    if (layout.isTypePortlet()) {
797                                            LayoutTypePortlet layoutTypePortlet =
798                                                    (LayoutTypePortlet)layout.getLayoutType();
799    
800                                            if (layoutTypePortlet.hasPortletId(portletId)) {
801                                                    return layout.getPlid();
802                                            }
803                                    }
804                            }
805                    }
806    
807                    return LayoutConstants.DEFAULT_PLID;
808            }
809    
810            /**
811             * Returns the layout for the friendly URL
812             *
813             * @param  groupId the primary key of the group
814             * @param  privateLayout whether the layout is private to the group
815             * @param  friendlyURL the friendly URL of the layout
816             * @return the layout for the friendly URL
817             * @throws PortalException if the friendly URL is <code>null</code> or a
818             *         matching layout could not be found
819             * @throws SystemException if a system exception occurred
820             */
821            public Layout getFriendlyURLLayout(
822                            long groupId, boolean privateLayout, String friendlyURL)
823                    throws PortalException, SystemException {
824    
825                    if (Validator.isNull(friendlyURL)) {
826                            throw new NoSuchLayoutException();
827                    }
828    
829                    friendlyURL = getFriendlyURL(friendlyURL);
830    
831                    Layout layout = layoutPersistence.fetchByG_P_F(
832                            groupId, privateLayout, friendlyURL);
833    
834                    if ((layout == null) &&
835                            (friendlyURL.startsWith(StringPool.SLASH)) &&
836                            (Validator.isNumber(friendlyURL.substring(1)))) {
837    
838                            long layoutId = GetterUtil.getLong(friendlyURL.substring(1));
839    
840                            layout = layoutPersistence.fetchByG_P_L(
841                                    groupId, privateLayout, layoutId);
842                    }
843    
844                    if (layout == null) {
845                            throw new NoSuchLayoutException();
846                    }
847    
848                    return layout;
849            }
850    
851            /**
852             * Returns the layout matching the primary key; throws a {@link
853             * com.liferay.portal.NoSuchLayoutException} otherwise.
854             *
855             * @param  plid the primary key of the layout
856             * @return the matching layout
857             * @throws PortalException if a layout with the primary key could not be
858             *         found
859             * @throws SystemException if a system exception occurred
860             */
861            @Override
862            public Layout getLayout(long plid)
863                    throws PortalException, SystemException {
864    
865                    return layoutPersistence.findByPrimaryKey(plid);
866            }
867    
868            /**
869             * Returns the layout matching the primary key, group, and privacy; throws a
870             * {@link com.liferay.portal.NoSuchLayoutException} otherwise.
871             *
872             * @param  groupId the primary key of the group
873             * @param  privateLayout whether the layout is private to the group
874             * @param  layoutId the primary key of the layout
875             * @return the matching layout
876             * @throws PortalException if a matching layout could not be found
877             * @throws SystemException if a system exception occurred
878             */
879            public Layout getLayout(long groupId, boolean privateLayout, long layoutId)
880                    throws PortalException, SystemException {
881    
882                    return layoutPersistence.findByG_P_L(groupId, privateLayout, layoutId);
883            }
884    
885            /**
886             * Returns the layout for the icon image; throws a {@link
887             * com.liferay.portal.NoSuchLayoutException} otherwise.
888             *
889             * @param  iconImageId the primary key of the icon image
890             * @return Returns the layout for the icon image
891             * @throws PortalException if an icon image with the primary key could not
892             *         be found
893             * @throws SystemException if a system exception occurred
894             */
895            public Layout getLayoutByIconImageId(long iconImageId)
896                    throws PortalException, SystemException {
897    
898                    return layoutPersistence.findByIconImageId(iconImageId);
899            }
900    
901            /**
902             * Returns the layout with the universally unique identifier and the group.
903             *
904             * @param  uuid the universally unique identifier of the layout
905             * @param  groupId the primary key of the group
906             * @return the layout with the universally unique identifier and the group
907             * @throws PortalException if a group with the primary key could not be
908             *         found
909             * @throws SystemException if a system exception occurred
910             */
911            @Override
912            public Layout getLayoutByUuidAndGroupId(String uuid, long groupId)
913                    throws PortalException, SystemException {
914    
915                    return layoutPersistence.findByUUID_G(uuid, groupId);
916            }
917    
918            /**
919             * Returns all the layouts belonging to the group.
920             *
921             * @param  groupId the primary key of the group
922             * @param  privateLayout whether the layout is private to the group
923             * @return the matching layouts, or <code>null</code> if no matches were
924             *         found
925             * @throws SystemException if a system exception occurred
926             */
927            public List<Layout> getLayouts(long groupId, boolean privateLayout)
928                    throws SystemException {
929    
930                    return layoutPersistence.findByG_P(groupId, privateLayout);
931            }
932    
933            /**
934             * Returns all the layouts belonging to the group that are children of the
935             * parent layout.
936             *
937             * @param  groupId the primary key of the group
938             * @param  privateLayout whether the layout is private to the group
939             * @param  parentLayoutId the primary key of the parent layout
940             * @return the matching layouts, or <code>null</code> if no matches were
941             *         found
942             * @throws SystemException if a system exception occurred
943             */
944            public List<Layout> getLayouts(
945                            long groupId, boolean privateLayout, long parentLayoutId)
946                    throws SystemException {
947    
948                    return layoutPersistence.findByG_P_P(
949                            groupId, privateLayout, parentLayoutId);
950            }
951    
952            /**
953             * Returns a range of all the layouts belonging to the group that are
954             * children of the parent layout.
955             *
956             * <p>
957             * Useful when paginating results. Returns a maximum of <code>end -
958             * start</code> instances. <code>start</code> and <code>end</code> are not
959             * primary keys, they are indexes in the result set. Thus, <code>0</code>
960             * refers to the first result in the set. Setting both <code>start</code>
961             * and <code>end</code> to {@link
962             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
963             * result set.
964             * </p>
965             *
966             * @param  groupId the primary key of the group
967             * @param  privateLayout whether the layout is private to the group
968             * @param  parentLayoutId the primary key of the parent layout
969             * @param  incomplete whether the layout is incomplete
970             * @param  start the lower bound of the range of layouts
971             * @param  end the upper bound of the range of layouts (not inclusive)
972             * @return the matching layouts, or <code>null</code> if no matches were
973             *         found
974             * @throws SystemException if a system exception occurred
975             */
976            public List<Layout> getLayouts(
977                            long groupId, boolean privateLayout, long parentLayoutId,
978                            boolean incomplete, int start, int end)
979                    throws SystemException {
980    
981                    return layoutPersistence.findByG_P_P(
982                            groupId, privateLayout, parentLayoutId, start, end);
983            }
984    
985            /**
986             * Returns all the layouts that match the layout IDs and belong to the
987             * group.
988             *
989             * @param  groupId the primary key of the group
990             * @param  privateLayout whether the layout is private to the group
991             * @param  layoutIds the primary keys of the layouts
992             * @return the matching layouts, or <code>null</code> if no matches were
993             *         found
994             * @throws PortalException if a group or layout with the primary key could
995             *         not be found
996             * @throws SystemException if a system exception occurred
997             */
998            public List<Layout> getLayouts(
999                            long groupId, boolean privateLayout, long[] layoutIds)
1000                    throws PortalException, SystemException {
1001    
1002                    List<Layout> layouts = new ArrayList<Layout>();
1003    
1004                    for (long layoutId : layoutIds) {
1005                            Layout layout = getLayout(groupId, privateLayout, layoutId);
1006    
1007                            layouts.add(layout);
1008                    }
1009    
1010                    return layouts;
1011            }
1012    
1013            /**
1014             * Returns all the layouts that match the type and belong to the group.
1015             *
1016             * @param  groupId the primary key of the group
1017             * @param  privateLayout whether the layout is private to the group
1018             * @param  type the type of the layouts (optionally {@link
1019             *         com.liferay.portal.model.LayoutConstants#TYPE_PORTLET})
1020             * @return the matching layouts, or <code>null</code> if no matches were
1021             *         found
1022             * @throws SystemException if a system exception occurred
1023             */
1024            public List<Layout> getLayouts(
1025                            long groupId, boolean privateLayout, String type)
1026                    throws SystemException {
1027    
1028                    return layoutPersistence.findByG_P_T(groupId, privateLayout, type);
1029            }
1030    
1031            /**
1032             * Returns the layout references for all the layouts that belong to the
1033             * company and belong to the portlet that matches the preferences.
1034             *
1035             * @param  companyId the primary key of the company
1036             * @param  portletId the primary key of the portlet
1037             * @param  preferencesKey the portlet's preference key
1038             * @param  preferencesValue the portlet's preference value
1039             * @return the layout references of the matching layouts
1040             * @throws SystemException if a system exception occurred
1041             */
1042            public LayoutReference[] getLayouts(
1043                            long companyId, String portletId, String preferencesKey,
1044                            String preferencesValue)
1045                    throws SystemException {
1046    
1047                    List<LayoutReference> layoutReferences = layoutFinder.findByC_P_P(
1048                            companyId, portletId, preferencesKey, preferencesValue);
1049    
1050                    return layoutReferences.toArray(
1051                            new LayoutReference[layoutReferences.size()]);
1052            }
1053    
1054            /**
1055             * Returns the primary key to use for the next layout.
1056             *
1057             * @param  groupId the primary key of the group
1058             * @param  privateLayout whether the layout is private to the group
1059             * @return the primary key to use for the next layout
1060             * @throws SystemException if a system exception occurred
1061             */
1062            public long getNextLayoutId(long groupId, boolean privateLayout)
1063                    throws SystemException {
1064    
1065                    long nextLayoutId = counterLocalService.increment(
1066                            getCounterName(groupId, privateLayout));
1067    
1068                    if (nextLayoutId == 1) {
1069                            List<Layout> layouts = layoutPersistence.findByG_P(
1070                                    groupId, privateLayout, 0, 1, new LayoutComparator());
1071    
1072                            if (!layouts.isEmpty()) {
1073                                    Layout layout = layouts.get(0);
1074    
1075                                    nextLayoutId = layout.getLayoutId() + 1;
1076    
1077                                    counterLocalService.reset(
1078                                            getCounterName(groupId, privateLayout), nextLayoutId);
1079                            }
1080                    }
1081    
1082                    return nextLayoutId;
1083            }
1084    
1085            /**
1086             * Returns all the layouts whose friendly URLs are <code>null</code>
1087             *
1088             * @return all the layouts whose friendly URLs are <code>null</code>
1089             * @throws SystemException if a system exception occurred
1090             */
1091            public List<Layout> getNullFriendlyURLLayouts() throws SystemException {
1092                    return layoutFinder.findByNullFriendlyURL();
1093            }
1094    
1095            /**
1096             * Returns all the layouts within scope of the group
1097             *
1098             * @param  groupId the primary key of the group
1099             * @param  privateLayout whether the layout is private to the group
1100             * @return the layouts within scope of the group
1101             * @throws SystemException if a system exception occurred
1102             */
1103            public List<Layout> getScopeGroupLayouts(
1104                            long groupId, boolean privateLayout)
1105                    throws SystemException {
1106    
1107                    return layoutFinder.findByScopeGroup(groupId, privateLayout);
1108            }
1109    
1110            /**
1111             * Returns <code>true</code> if the group has any layouts;
1112             * <code>false</code> otherwise.
1113             *
1114             * @param  groupId the primary key of the group
1115             * @param  privateLayout whether the layout is private to the group
1116             * @param  parentLayoutId the primary key of the parent layout
1117             * @return <code>true</code> if the group has any layouts;
1118             *         <code>false</code> otherwise
1119             * @throws SystemException if a system exception occurred
1120             */
1121            public boolean hasLayouts(
1122                            long groupId, boolean privateLayout, long parentLayoutId)
1123                    throws SystemException {
1124    
1125                    return layoutPersistence.countByG_P_P(
1126                            groupId, privateLayout, parentLayoutId) > 0;
1127            }
1128    
1129            /**
1130             * Imports the layouts from the byte array.
1131             *
1132             * @param  userId the primary key of the user
1133             * @param  groupId the primary key of the group
1134             * @param  privateLayout whether the layout is private to the group
1135             * @param  parameterMap the mapping of parameters indicating which
1136             *         information will be imported. For information on the keys used in
1137             *         the map see {@link
1138             *         com.liferay.portal.kernel.lar.PortletDataHandlerKeys}.
1139             * @param  bytes the byte array with the data
1140             * @throws PortalException if a group or user with the primary key could not
1141             *         be found, or if some other portal exception occurred
1142             * @throws SystemException if a system exception occurred
1143             * @see    com.liferay.portal.lar.LayoutImporter
1144             */
1145            public void importLayouts(
1146                            long userId, long groupId, boolean privateLayout,
1147                            Map<String, String[]> parameterMap, byte[] bytes)
1148                    throws PortalException, SystemException {
1149    
1150                    importLayouts(
1151                            userId, groupId, privateLayout, parameterMap,
1152                            new UnsyncByteArrayInputStream(bytes));
1153            }
1154    
1155            /**
1156             * Imports the layouts from the file.
1157             *
1158             * @param  userId the primary key of the user
1159             * @param  groupId the primary key of the group
1160             * @param  privateLayout whether the layout is private to the group
1161             * @param  parameterMap the mapping of parameters indicating which
1162             *         information will be imported. For information on the keys used in
1163             *         the map see {@link
1164             *         com.liferay.portal.kernel.lar.PortletDataHandlerKeys}.
1165             * @param  file the LAR file with the data
1166             * @throws PortalException if a group or user with the primary key could not
1167             *         be found, or if some other portal exception occurred
1168             * @throws SystemException if a system exception occurred
1169             * @see    com.liferay.portal.lar.LayoutImporter
1170             */
1171            public void importLayouts(
1172                            long userId, long groupId, boolean privateLayout,
1173                            Map<String, String[]> parameterMap, File file)
1174                    throws PortalException, SystemException {
1175    
1176                    try {
1177                            LayoutImporter layoutImporter = new LayoutImporter();
1178    
1179                            layoutImporter.importLayouts(
1180                                    userId, groupId, privateLayout, parameterMap, file);
1181                    }
1182                    catch (PortalException pe) {
1183                            throw pe;
1184                    }
1185                    catch (SystemException se) {
1186                            throw se;
1187                    }
1188                    catch (Exception e) {
1189                            throw new SystemException(e);
1190                    }
1191            }
1192    
1193            /**
1194             * Imports the layouts from the input stream.
1195             *
1196             * @param  userId the primary key of the user
1197             * @param  groupId the primary key of the group
1198             * @param  privateLayout whether the layout is private to the group
1199             * @param  parameterMap the mapping of parameters indicating which
1200             *         information will be imported. For information on the keys used in
1201             *         the map see {@link
1202             *         com.liferay.portal.kernel.lar.PortletDataHandlerKeys}.
1203             * @param  is the input stream
1204             * @throws PortalException if a group or user with the primary key could not
1205             *         be found, or if some other portal exception occurred
1206             * @throws SystemException if a system exception occurred
1207             * @see    com.liferay.portal.lar.LayoutImporter
1208             */
1209            public void importLayouts(
1210                            long userId, long groupId, boolean privateLayout,
1211                            Map<String, String[]> parameterMap, InputStream is)
1212                    throws PortalException, SystemException {
1213    
1214                    try {
1215                            File file = FileUtil.createTempFile("lar");
1216    
1217                            FileUtil.write(file, is);
1218    
1219                            importLayouts(userId, groupId, privateLayout, parameterMap, file);
1220                    }
1221                    catch (IOException e) {
1222                            throw new SystemException(e);
1223                    }
1224            }
1225    
1226            /**
1227             * Imports the portlet information (categories, permissions, ... etc.) from
1228             * the file.
1229             *
1230             * @param  userId the primary key of the user
1231             * @param  plid the primary key of the target layout
1232             * @param  groupId the primary key of the target group
1233             * @param  portletId the primary key of the portlet
1234             * @param  parameterMap the mapping of parameters indicating which
1235             *         information will be imported. For information on the keys used in
1236             *         the map see {@link
1237             *         com.liferay.portal.kernel.lar.PortletDataHandlerKeys}.
1238             * @param  file the LAR file with the data
1239             * @throws PortalException if a group, layout, portlet or user with the
1240             *         primary key could not be found
1241             * @throws SystemException if a system exception occurred
1242             */
1243            public void importPortletInfo(
1244                            long userId, long plid, long groupId, String portletId,
1245                            Map<String, String[]> parameterMap, File file)
1246                    throws PortalException, SystemException {
1247    
1248                    try {
1249                            PortletImporter portletImporter = new PortletImporter();
1250    
1251                            portletImporter.importPortletInfo(
1252                                    userId, plid, groupId, portletId, parameterMap, file);
1253                    }
1254                    catch (PortalException pe) {
1255                            throw pe;
1256                    }
1257                    catch (SystemException se) {
1258                            throw se;
1259                    }
1260                    catch (Exception e) {
1261                            throw new SystemException(e);
1262                    }
1263            }
1264    
1265            /**
1266             * Imports the portlet information (categories, permissions, ... etc.) from
1267             * the input stream.
1268             *
1269             * @param  userId the primary key of the user
1270             * @param  plid the primary key of the layout
1271             * @param  groupId the primary key of the group
1272             * @param  portletId the primary key of the portlet
1273             * @param  parameterMap the mapping of parameters indicating which
1274             *         information will be imported. For information on the keys used in
1275             *         the map see {@link
1276             *         com.liferay.portal.kernel.lar.PortletDataHandlerKeys}.
1277             * @param  is the input stream
1278             * @throws PortalException if a group, portlet, layout or user with the
1279             *         primary key could not be found
1280             * @throws SystemException if a system exception occurred
1281             */
1282            public void importPortletInfo(
1283                            long userId, long plid, long groupId, String portletId,
1284                            Map<String, String[]> parameterMap, InputStream is)
1285                    throws PortalException, SystemException {
1286    
1287                    try {
1288                            File file = FileUtil.createTempFile("lar");
1289    
1290                            FileUtil.write(file, is);
1291    
1292                            importPortletInfo(
1293                                    userId, plid, groupId, portletId, parameterMap, file);
1294                    }
1295                    catch (IOException e) {
1296                            throw new SystemException(e);
1297                    }
1298            }
1299    
1300            /**
1301             * Sets the layouts for the group, replacing and prioritizing all layouts of
1302             * the parent layout.
1303             *
1304             * @param  groupId the primary key of the group
1305             * @param  privateLayout whether the layout is private to the group
1306             * @param  parentLayoutId the primary key of the parent layout
1307             * @param  layoutIds the primary keys of the layouts
1308             * @param  serviceContext the service context
1309             * @throws PortalException if a group or layout with the primary key could
1310             *         not be found, if no layouts were specified, if the first layout
1311             *         was not page-able, if the first layout was hidden, or if some
1312             *         other portal exception occurred
1313             * @throws SystemException if a system exception occurred
1314             */
1315            public void setLayouts(
1316                            long groupId, boolean privateLayout, long parentLayoutId,
1317                            long[] layoutIds, ServiceContext serviceContext)
1318                    throws PortalException, SystemException {
1319    
1320                    if (layoutIds == null) {
1321                            return;
1322                    }
1323    
1324                    if (parentLayoutId == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
1325                            if (layoutIds.length < 1) {
1326                                    throw new RequiredLayoutException(
1327                                            RequiredLayoutException.AT_LEAST_ONE);
1328                            }
1329    
1330                            Layout layout = layoutPersistence.findByG_P_L(
1331                                    groupId, privateLayout, layoutIds[0]);
1332    
1333                            if (!PortalUtil.isLayoutFirstPageable(layout.getType())) {
1334                                    throw new RequiredLayoutException(
1335                                            RequiredLayoutException.FIRST_LAYOUT_TYPE);
1336                            }
1337    
1338                            if (layout.isHidden()) {
1339                                    throw new RequiredLayoutException(
1340                                            RequiredLayoutException.FIRST_LAYOUT_HIDDEN);
1341                            }
1342                    }
1343    
1344                    Set<Long> layoutIdsSet = new LinkedHashSet<Long>();
1345    
1346                    for (long layoutId : layoutIds) {
1347                            layoutIdsSet.add(layoutId);
1348                    }
1349    
1350                    Set<Long> newLayoutIdsSet = new HashSet<Long>();
1351    
1352                    List<Layout> layouts = layoutPersistence.findByG_P_P(
1353                            groupId, privateLayout, parentLayoutId);
1354    
1355                    for (Layout layout : layouts) {
1356                            if (!layoutIdsSet.contains(layout.getLayoutId())) {
1357                                    deleteLayout(layout, true, serviceContext);
1358                            }
1359                            else {
1360                                    newLayoutIdsSet.add(layout.getLayoutId());
1361                            }
1362                    }
1363    
1364                    int priority = 0;
1365    
1366                    for (long layoutId : layoutIdsSet) {
1367                            Layout layout = layoutPersistence.findByG_P_L(
1368                                    groupId, privateLayout, layoutId);
1369    
1370                            layout.setPriority(priority++);
1371    
1372                            layoutPersistence.update(layout, false);
1373                    }
1374    
1375                    layoutSetLocalService.updatePageCount(groupId, privateLayout);
1376            }
1377    
1378            /**
1379             * Updates the friendly URL of the layout.
1380             *
1381             * @param  plid the primary key of the layout
1382             * @param  friendlyURL the friendly URL to be assigned
1383             * @return the updated layout
1384             * @throws PortalException if a group or layout with the primary key could
1385             *         not be found
1386             * @throws SystemException if a system exception occurred
1387             */
1388            public Layout updateFriendlyURL(long plid, String friendlyURL)
1389                    throws PortalException, SystemException {
1390    
1391                    Date now = new Date();
1392    
1393                    Layout layout = layoutPersistence.findByPrimaryKey(plid);
1394    
1395                    friendlyURL = getFriendlyURL(
1396                            layout.getGroupId(), layout.isPrivateLayout(),
1397                            layout.getLayoutId(), StringPool.BLANK, friendlyURL);
1398    
1399                    validateFriendlyURL(
1400                            layout.getGroupId(), layout.isPrivateLayout(), layout.getLayoutId(),
1401                            friendlyURL);
1402    
1403                    layout.setModifiedDate(now);
1404                    layout.setFriendlyURL(friendlyURL);
1405    
1406                    layoutPersistence.update(layout, false);
1407    
1408                    return layout;
1409            }
1410    
1411            /**
1412             * Updates the layout.
1413             *
1414             * @param  groupId the primary key of the group
1415             * @param  privateLayout whether the layout is private to the group
1416             * @param  layoutId the primary key of the layout
1417             * @param  parentLayoutId the primary key of the layout's new parent layout
1418             * @param  nameMap the locales and localized names to merge (optionally
1419             *         <code>null</code>)
1420             * @param  titleMap the locales and localized titles to merge (optionally
1421             *         <code>null</code>)
1422             * @param  descriptionMap the locales and localized descriptions to merge
1423             *         (optionally <code>null</code>)
1424             * @param  keywordsMap the locales and localized keywords to merge
1425             *         (optionally <code>null</code>)
1426             * @param  robotsMap the locales and localized robots to merge (optionally
1427             *         <code>null</code>)
1428             * @param  type the layout's new type (optionally {@link
1429             *         com.liferay.portal.model.LayoutConstants#TYPE_PORTLET})
1430             * @param  hidden whether the layout is hidden
1431             * @param  friendlyURL the layout's new friendly URL (optionally {@link
1432             *         com.liferay.portal.util.PropsValues#DEFAULT_USER_PRIVATE_LAYOUT_FRIENDLY_URL}
1433             *         or {@link
1434             *         com.liferay.portal.util.PropsValues#DEFAULT_USER_PRIVATE_LAYOUT_FRIENDLY_URL}).
1435             *         The default values can be overridden in
1436             *         <code>portal-ext.properties</code> by specifying new values for
1437             *         the corresponding properties defined in {@link
1438             *         com.liferay.portal.util.PropsValues}. To see how the URL is
1439             *         normalized when accessed see {@link
1440             *         com.liferay.portal.kernel.util.FriendlyURLNormalizerUtil#normalize(
1441             *         String)}.
1442             * @param  iconImage whether the icon image will be updated
1443             * @param  iconBytes the byte array of the layout's new icon image
1444             * @param  locked whether the layout is locked
1445             * @param  serviceContext the service context. Can specify the replacement
1446             *         modified date and new expando bridge attributes.
1447             * @return the updated layout
1448             * @throws PortalException if a group or layout with the primary key could
1449             *         not be found, if a unique friendly URL could not be generated, if
1450             *         a valid parent layout ID to use could not be found, or if the
1451             *         layout parameters were invalid
1452             * @throws SystemException if a system exception occurred
1453             */
1454            public Layout updateLayout(
1455                            long groupId, boolean privateLayout, long layoutId,
1456                            long parentLayoutId, Map<Locale, String> nameMap,
1457                            Map<Locale, String> titleMap, Map<Locale, String> descriptionMap,
1458                            Map<Locale, String> keywordsMap, Map<Locale, String> robotsMap,
1459                            String type, boolean hidden, String friendlyURL, Boolean iconImage,
1460                            byte[] iconBytes, boolean locked, ServiceContext serviceContext)
1461                    throws PortalException, SystemException {
1462    
1463                    // Layout
1464    
1465                    parentLayoutId = getParentLayoutId(
1466                            groupId, privateLayout, parentLayoutId);
1467                    String name = nameMap.get(LocaleUtil.getDefault());
1468                    friendlyURL = getFriendlyURL(
1469                            groupId, privateLayout, layoutId, StringPool.BLANK, friendlyURL);
1470    
1471                    validate(
1472                            groupId, privateLayout, layoutId, parentLayoutId, name, type,
1473                            hidden, friendlyURL);
1474    
1475                    validateParentLayoutId(
1476                            groupId, privateLayout, layoutId, parentLayoutId);
1477    
1478                    Date now = new Date();
1479    
1480                    Layout layout = layoutPersistence.findByG_P_L(
1481                            groupId, privateLayout, layoutId);
1482    
1483                    if (parentLayoutId != layout.getParentLayoutId()) {
1484                            layout.setPriority(
1485                                    getNextPriority(groupId, privateLayout, parentLayoutId));
1486                    }
1487    
1488                    layout.setModifiedDate(serviceContext.getModifiedDate(now));
1489                    layout.setParentLayoutId(parentLayoutId);
1490                    layout.setNameMap(nameMap);
1491                    layout.setTitleMap(titleMap);
1492                    layout.setDescriptionMap(descriptionMap);
1493                    layout.setKeywordsMap(keywordsMap);
1494                    layout.setRobotsMap(robotsMap);
1495                    layout.setType(type);
1496                    layout.setHidden(hidden);
1497                    layout.setFriendlyURL(friendlyURL);
1498    
1499                    if (iconImage != null) {
1500                            layout.setIconImage(iconImage.booleanValue());
1501    
1502                            if (iconImage.booleanValue()) {
1503                                    long iconImageId = layout.getIconImageId();
1504    
1505                                    if (iconImageId <= 0) {
1506                                            iconImageId = counterLocalService.increment();
1507    
1508                                            layout.setIconImageId(iconImageId);
1509                                    }
1510                            }
1511                    }
1512    
1513                    UnicodeProperties typeSettingsProperties =
1514                            layout.getTypeSettingsProperties();
1515    
1516                    typeSettingsProperties.put("locked", String.valueOf(locked));
1517    
1518                    layout.setTypeSettingsProperties(typeSettingsProperties);
1519    
1520                    layoutPersistence.update(layout, false);
1521    
1522                    // Icon
1523    
1524                    if (iconImage != null) {
1525                            if (!iconImage.booleanValue()) {
1526                                    imageLocalService.deleteImage(layout.getIconImageId());
1527                            }
1528                            else if ((iconBytes != null) && (iconBytes.length > 0)) {
1529                                    imageLocalService.updateImage(
1530                                            layout.getIconImageId(), iconBytes);
1531                            }
1532                    }
1533    
1534                    // Portlet preferences
1535    
1536                    Locale[] locales = LanguageUtil.getAvailableLocales();
1537    
1538                    for (Locale locale : locales) {
1539                            updateScopedPortletNames(
1540                                    groupId, privateLayout, layoutId, nameMap.get(locale),
1541                                    LanguageUtil.getLanguageId(locale));
1542                    }
1543    
1544                    // Expando
1545    
1546                    ExpandoBridge expandoBridge = layout.getExpandoBridge();
1547    
1548                    expandoBridge.setAttributes(serviceContext);
1549    
1550                    return layout;
1551            }
1552    
1553            /**
1554             * Updates the layout replacing its type settings.
1555             *
1556             * @param  groupId the primary key of the group
1557             * @param  privateLayout whether the layout is private to the group
1558             * @param  layoutId the primary key of the layout
1559             * @param  typeSettings the settings to load the unicode properties object.
1560             *         See {@link com.liferay.portal.kernel.util.UnicodeProperties
1561             *         #fastLoad(String)}.
1562             * @return the updated layout
1563             * @throws PortalException if a matching layout could not be found
1564             * @throws SystemException if a system exception occurred
1565             */
1566            public Layout updateLayout(
1567                            long groupId, boolean privateLayout, long layoutId,
1568                            String typeSettings)
1569                    throws PortalException, SystemException {
1570    
1571                    Date now = new Date();
1572    
1573                    UnicodeProperties typeSettingsProperties = new UnicodeProperties();
1574    
1575                    DateFormat dateFormat = DateFormatFactoryUtil.getSimpleDateFormat(
1576                            PropsValues.INDEX_DATE_FORMAT_PATTERN);
1577    
1578                    typeSettingsProperties.fastLoad(typeSettings);
1579    
1580                    typeSettingsProperties.setProperty(
1581                            "modifiedDate", dateFormat.format(now));
1582    
1583                    Layout layout = layoutPersistence.findByG_P_L(
1584                            groupId, privateLayout, layoutId);
1585    
1586                    layout.setModifiedDate(now);
1587                    layout.setTypeSettings(typeSettingsProperties.toString());
1588    
1589                    layoutPersistence.update(layout, false);
1590    
1591                    return layout;
1592            }
1593    
1594            /**
1595             * Updates the look and feel of the layout.
1596             *
1597             * @param  groupId the primary key of the group
1598             * @param  privateLayout whether the layout is private to the group
1599             * @param  layoutId the primary key of the layout
1600             * @param  themeId the primary key of the layout's new theme
1601             * @param  colorSchemeId the primary key of the layout's new color scheme
1602             * @param  css the layout's new CSS
1603             * @param  wapTheme whether the theme is for WAP browsers
1604             * @return the updated layout
1605             * @throws PortalException if a matching layout could not be found
1606             * @throws SystemException if a system exception occurred
1607             */
1608            public Layout updateLookAndFeel(
1609                            long groupId, boolean privateLayout, long layoutId, String themeId,
1610                            String colorSchemeId, String css, boolean wapTheme)
1611                    throws PortalException, SystemException {
1612    
1613                    Date now = new Date();
1614    
1615                    Layout layout = layoutPersistence.findByG_P_L(
1616                            groupId, privateLayout, layoutId);
1617    
1618                    layout.setModifiedDate(now);
1619    
1620                    if (wapTheme) {
1621                            layout.setWapThemeId(themeId);
1622                            layout.setWapColorSchemeId(colorSchemeId);
1623                    }
1624                    else {
1625                            layout.setThemeId(themeId);
1626                            layout.setColorSchemeId(colorSchemeId);
1627                            layout.setCss(css);
1628                    }
1629    
1630                    layoutPersistence.update(layout, false);
1631    
1632                    return layout;
1633            }
1634    
1635            /**
1636             * Updates the name of the layout.
1637             *
1638             * @param  layout the layout to be updated
1639             * @param  name the layout's new name
1640             * @param  languageId the primary key of the language. For more information
1641             *         see {@link java.util.Locale}.
1642             * @return the updated layout
1643             * @throws PortalException if the new name was <code>null</code>
1644             * @throws SystemException if a system exception occurred
1645             */
1646            public Layout updateName(Layout layout, String name, String languageId)
1647                    throws PortalException, SystemException {
1648    
1649                    Date now = new Date();
1650    
1651                    validateName(name, languageId);
1652    
1653                    layout.setModifiedDate(now);
1654                    layout.setName(name, LocaleUtil.fromLanguageId(languageId));
1655    
1656                    layoutPersistence.update(layout, false);
1657    
1658                    return layout;
1659            }
1660    
1661            /**
1662             * Updates the name of the layout matching the group, layout ID, and
1663             * privacy.
1664             *
1665             * @param  groupId the primary key of the group
1666             * @param  privateLayout whether the layout is private to the group
1667             * @param  layoutId the primary key of the layout
1668             * @param  name the layout's new name
1669             * @param  languageId the primary key of the language. For more information
1670             *         see {@link java.util.Locale}.
1671             * @return the updated layout
1672             * @throws PortalException if a matching layout could not be found or if the
1673             *         new name was <code>null</code>
1674             * @throws SystemException if a system exception occurred
1675             */
1676            public Layout updateName(
1677                            long groupId, boolean privateLayout, long layoutId, String name,
1678                            String languageId)
1679                    throws PortalException, SystemException {
1680    
1681                    Layout layout = layoutPersistence.findByG_P_L(
1682                            groupId, privateLayout, layoutId);
1683    
1684                    return layoutLocalService.updateName(layout, name, languageId);
1685            }
1686    
1687            /**
1688             * Updates the name of the layout matching the primary key.
1689             *
1690             * @param  plid the primary key of the layout
1691             * @param  name the name to be assigned
1692             * @param  languageId the primary key of the language. For more information
1693             *         see {@link java.util.Locale}.
1694             * @return the updated layout
1695             * @throws PortalException if a layout with the primary key could not be
1696             *         found or if the name was <code>null</code>
1697             * @throws SystemException if a system exception occurred
1698             */
1699            public Layout updateName(long plid, String name, String languageId)
1700                    throws PortalException, SystemException {
1701    
1702                    Layout layout = layoutPersistence.findByPrimaryKey(plid);
1703    
1704                    return layoutLocalService.updateName(layout, name, languageId);
1705            }
1706    
1707            /**
1708             * Updates the parent layout ID of the layout matching the group, layout ID,
1709             * and privacy.
1710             *
1711             * @param  groupId the primary key of the group
1712             * @param  privateLayout whether the layout is private to the group
1713             * @param  layoutId the primary key of the layout
1714             * @param  parentLayoutId the primary key to be assigned to the parent
1715             *         layout
1716             * @return the matching layout
1717             * @throws PortalException if a valid parent layout ID to use could not be
1718             *         found or if a matching layout could not be found
1719             * @throws SystemException if a system exception occurred
1720             */
1721            public Layout updateParentLayoutId(
1722                            long groupId, boolean privateLayout, long layoutId,
1723                            long parentLayoutId)
1724                    throws PortalException, SystemException {
1725    
1726                    parentLayoutId = getParentLayoutId(
1727                            groupId, privateLayout, parentLayoutId);
1728    
1729                    validateParentLayoutId(
1730                            groupId, privateLayout, layoutId, parentLayoutId);
1731    
1732                    Date now = new Date();
1733    
1734                    Layout layout = layoutPersistence.findByG_P_L(
1735                            groupId, privateLayout, layoutId);
1736    
1737                    if (parentLayoutId != layout.getParentLayoutId()) {
1738                            layout.setPriority(
1739                                    getNextPriority(groupId, privateLayout, parentLayoutId));
1740                    }
1741    
1742                    layout.setModifiedDate(now);
1743                    layout.setParentLayoutId(parentLayoutId);
1744    
1745                    layoutPersistence.update(layout, false);
1746    
1747                    return layout;
1748            }
1749    
1750            /**
1751             * Updates the parent layout ID of the layout matching the primary key. If a
1752             * layout matching the parent primary key is found, the layout ID of that
1753             * layout is assigned, otherwise {@link
1754             * com.liferay.portal.model.LayoutConstants#DEFAULT_PARENT_LAYOUT_ID} is
1755             * assigned.
1756             *
1757             * @param  plid the primary key of the layout
1758             * @param  parentPlid the primary key of the parent layout
1759             * @return the layout matching the primary key
1760             * @throws PortalException if a layout with the primary key could not be
1761             *         found or if a valid parent layout ID to use could not be found
1762             * @throws SystemException if a system exception occurred
1763             */
1764            public Layout updateParentLayoutId(long plid, long parentPlid)
1765                    throws PortalException, SystemException {
1766    
1767                    Date now = new Date();
1768    
1769                    Layout layout = layoutPersistence.findByPrimaryKey(plid);
1770    
1771                    long parentLayoutId = LayoutConstants.DEFAULT_PARENT_LAYOUT_ID;
1772    
1773                    if (parentPlid > 0) {
1774                            Layout parentLayout = layoutPersistence.fetchByPrimaryKey(
1775                                    parentPlid);
1776    
1777                            if (parentLayout != null) {
1778                                    parentLayoutId = parentLayout.getLayoutId();
1779                            }
1780                    }
1781    
1782                    parentLayoutId = getParentLayoutId(
1783                            layout.getGroupId(), layout.isPrivateLayout(), parentLayoutId);
1784    
1785                    validateParentLayoutId(
1786                            layout.getGroupId(), layout.isPrivateLayout(), layout.getLayoutId(),
1787                            parentLayoutId);
1788    
1789                    if (parentLayoutId != layout.getParentLayoutId()) {
1790                            int priority = getNextPriority(
1791                                    layout.getGroupId(), layout.isPrivateLayout(), parentLayoutId);
1792    
1793                            layout.setPriority(priority);
1794                    }
1795    
1796                    layout.setModifiedDate(now);
1797                    layout.setParentLayoutId(parentLayoutId);
1798    
1799                    layoutPersistence.update(layout, false);
1800    
1801                    return layout;
1802            }
1803    
1804            /**
1805             * Updates the priority of the layout.
1806             *
1807             * @param  layout the layout to be updated
1808             * @param  priority the layout's new priority
1809             * @return the updated layout
1810             * @throws SystemException if a system exception occurred
1811             */
1812            public Layout updatePriority(Layout layout, int priority)
1813                    throws SystemException {
1814    
1815                    if (layout.getPriority() == priority) {
1816                            return layout;
1817                    }
1818    
1819                    Date now = new Date();
1820    
1821                    boolean lessThan = false;
1822    
1823                    if (layout.getPriority() < priority) {
1824                            lessThan = true;
1825                    }
1826    
1827                    layout.setModifiedDate(now);
1828                    layout.setPriority(priority);
1829    
1830                    layoutPersistence.update(layout, false);
1831    
1832                    priority = 0;
1833    
1834                    List<Layout> layouts = layoutPersistence.findByG_P_P(
1835                            layout.getGroupId(), layout.isPrivateLayout(),
1836                            layout.getParentLayoutId());
1837    
1838                    layouts = ListUtil.sort(
1839                            layouts, new LayoutPriorityComparator(layout, lessThan));
1840    
1841                    for (Layout curLayout : layouts) {
1842                            curLayout.setModifiedDate(now);
1843                            curLayout.setPriority(priority++);
1844    
1845                            layoutPersistence.update(curLayout, false);
1846    
1847                            if (curLayout.equals(layout)) {
1848                                    layout = curLayout;
1849                            }
1850                    }
1851    
1852                    return layout;
1853            }
1854    
1855            /**
1856             * Updates the priority of the layout matching the group, layout ID, and
1857             * privacy.
1858             *
1859             * @param  groupId the primary key of the group
1860             * @param  privateLayout whether the layout is private to the group
1861             * @param  layoutId the primary key of the layout
1862             * @param  priority the layout's new priority
1863             * @return the updated layout
1864             * @throws PortalException if a matching layout could not be found
1865             * @throws SystemException if a system exception occurred
1866             */
1867            public Layout updatePriority(
1868                            long groupId, boolean privateLayout, long layoutId, int priority)
1869                    throws PortalException, SystemException {
1870    
1871                    Layout layout = layoutPersistence.findByG_P_L(
1872                            groupId, privateLayout, layoutId);
1873    
1874                    return updatePriority(layout, priority);
1875            }
1876    
1877            /**
1878             * Updates the priority of the layout matching the primary key.
1879             *
1880             * @param  plid the primary key of the layout
1881             * @param  priority the layout's new priority
1882             * @return the updated layout
1883             * @throws PortalException if a layout with the primary key could not be
1884             *         found
1885             * @throws SystemException if a system exception occurred
1886             */
1887            public Layout updatePriority(long plid, int priority)
1888                    throws PortalException, SystemException {
1889    
1890                    Layout layout = layoutPersistence.findByPrimaryKey(plid);
1891    
1892                    return updatePriority(layout, priority);
1893            }
1894    
1895            /**
1896             * Updates the names of the portlets within scope of the group, the scope of
1897             * the layout's universally unique identifier, and the privacy.
1898             *
1899             * @param  groupId the primary key of the group
1900             * @param  privateLayout whether the layout is private to the group
1901             * @param  layoutId the primary key of the layout whose universally unique
1902             *         identifier to match
1903             * @param  name the new name for the portlets
1904             * @param  languageId the primary key of the language
1905             * @throws PortalException if a matching layout could not be found
1906             * @throws SystemException if a system exception occurred
1907             * @see    com.liferay.portlet.portletconfiguration.action.EditScopeAction
1908             */
1909            public void updateScopedPortletNames(
1910                            long groupId, boolean privateLayout, long layoutId, String name,
1911                            String languageId)
1912                    throws PortalException, SystemException {
1913    
1914                    Layout layout = getLayout(groupId, privateLayout, layoutId);
1915    
1916                    List<Layout> layouts = getLayouts(groupId, privateLayout);
1917    
1918                    for (Layout curLayout : layouts) {
1919                            List<PortletPreferences> portletPreferencesList =
1920                                    portletPreferencesLocalService.getPortletPreferencesByPlid(
1921                                            curLayout.getPlid());
1922    
1923                            for (PortletPreferences portletPreferences :
1924                                            portletPreferencesList) {
1925    
1926                                    String portletId = portletPreferences.getPortletId();
1927    
1928                                    Portlet portlet = portletLocalService.getPortletById(portletId);
1929    
1930                                    if ((portlet == null) || !portlet.isScopeable()) {
1931                                            continue;
1932                                    }
1933    
1934                                    javax.portlet.PortletPreferences preferences =
1935                                            PortletPreferencesFactoryUtil.getLayoutPortletSetup(
1936                                                    curLayout, portletId);
1937    
1938                                    String scopeLayoutUuid = GetterUtil.getString(
1939                                            preferences.getValue("lfrScopeLayoutUuid", null));
1940    
1941                                    if (!scopeLayoutUuid.equals(layout.getUuid())) {
1942                                            continue;
1943                                    }
1944    
1945                                    String portletTitle = PortalUtil.getPortletTitle(
1946                                            portletId, languageId);
1947    
1948                                    String newPortletTitle = PortalUtil.getNewPortletTitle(
1949                                            portletTitle, curLayout.getName(languageId), name);
1950    
1951                                    if (newPortletTitle.equals(portletTitle)) {
1952                                            continue;
1953                                    }
1954    
1955                                    try {
1956                                            preferences.setValue(
1957                                                    "portletSetupTitle_" + languageId, newPortletTitle);
1958                                            preferences.setValue(
1959                                                    "portletSetupUseCustomTitle", Boolean.TRUE.toString());
1960    
1961                                            preferences.store();
1962                                    }
1963                                    catch (IOException ioe) {
1964                                            throw new SystemException(ioe);
1965                                    }
1966                                    catch (PortletException pe) {
1967                                            throw new SystemException(pe);
1968                                    }
1969                            }
1970                    }
1971            }
1972    
1973            protected String getFriendlyURL(
1974                            long groupId, boolean privateLayout, long layoutId,
1975                            String name, String friendlyURL)
1976                    throws PortalException, SystemException {
1977    
1978                    friendlyURL = getFriendlyURL(friendlyURL);
1979    
1980                    if (Validator.isNull(friendlyURL)) {
1981                            friendlyURL = StringPool.SLASH + getFriendlyURL(name);
1982    
1983                            String originalFriendlyURL = friendlyURL;
1984    
1985                            for (int i = 1;; i++) {
1986                                    try {
1987                                            validateFriendlyURL(
1988                                                    groupId, privateLayout, layoutId, friendlyURL);
1989    
1990                                            break;
1991                                    }
1992                                    catch (LayoutFriendlyURLException lfurle) {
1993                                            int type = lfurle.getType();
1994    
1995                                            if (type == LayoutFriendlyURLException.DUPLICATE) {
1996                                                    friendlyURL = originalFriendlyURL + i;
1997                                            }
1998                                            else {
1999                                                    friendlyURL = StringPool.SLASH + layoutId;
2000    
2001                                                    break;
2002                                            }
2003                                    }
2004                            }
2005                    }
2006    
2007                    return friendlyURL;
2008            }
2009    
2010            protected String getFriendlyURL(String friendlyURL) {
2011                    return FriendlyURLNormalizerUtil.normalize(friendlyURL);
2012            }
2013    
2014            protected int getNextPriority(
2015                            long groupId, boolean privateLayout, long parentLayoutId)
2016                    throws SystemException {
2017    
2018                    List<Layout> layouts = layoutPersistence.findByG_P_P(
2019                            groupId, privateLayout, parentLayoutId);
2020    
2021                    if (layouts.size() == 0) {
2022                            return 0;
2023                    }
2024    
2025                    Layout layout = layouts.get(layouts.size() - 1);
2026    
2027                    return layout.getPriority() + 1;
2028            }
2029    
2030            protected long getParentLayoutId(
2031                            long groupId, boolean privateLayout, long parentLayoutId)
2032                    throws SystemException {
2033    
2034                    if (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
2035    
2036                            // Ensure parent layout exists
2037    
2038                            Layout parentLayout = layoutPersistence.fetchByG_P_L(
2039                                    groupId, privateLayout, parentLayoutId);
2040    
2041                            if (parentLayout == null) {
2042                                    parentLayoutId = LayoutConstants.DEFAULT_PARENT_LAYOUT_ID;
2043                            }
2044                    }
2045    
2046                    return parentLayoutId;
2047            }
2048    
2049            protected void validate(
2050                            long groupId, boolean privateLayout, long layoutId,
2051                            long parentLayoutId, String name, String type, boolean hidden,
2052                            String friendlyURL)
2053                    throws PortalException, SystemException {
2054    
2055                    validateName(name);
2056    
2057                    boolean firstLayout = false;
2058    
2059                    if (parentLayoutId == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
2060                            List<Layout> layouts = layoutPersistence.findByG_P_P(
2061                                    groupId, privateLayout, parentLayoutId, 0, 1);
2062    
2063                            if (layouts.size() == 0) {
2064                                    firstLayout = true;
2065                            }
2066                            else {
2067                                    long firstLayoutId = layouts.get(0).getLayoutId();
2068    
2069                                    if (firstLayoutId == layoutId) {
2070                                            firstLayout = true;
2071                                    }
2072                            }
2073                    }
2074    
2075                    if (firstLayout) {
2076                            validateFirstLayout(type);
2077                    }
2078    
2079                    if (!PortalUtil.isLayoutParentable(type)) {
2080                            if (layoutPersistence.countByG_P_P(
2081                                            groupId, privateLayout, layoutId) > 0) {
2082    
2083                                    throw new LayoutTypeException(
2084                                            LayoutTypeException.NOT_PARENTABLE);
2085                            }
2086                    }
2087    
2088                    validateFriendlyURL(groupId, privateLayout, layoutId, friendlyURL);
2089            }
2090    
2091            protected void validateFirstLayout(String type)
2092                    throws PortalException {
2093    
2094                    if (Validator.isNull(type) || !PortalUtil.isLayoutFirstPageable(type)) {
2095                            throw new LayoutTypeException(LayoutTypeException.FIRST_LAYOUT);
2096                    }
2097            }
2098    
2099            protected void validateFriendlyURL(
2100                            long groupId, boolean privateLayout, long layoutId,
2101                            String friendlyURL)
2102                    throws PortalException, SystemException {
2103    
2104                    if (Validator.isNull(friendlyURL)) {
2105                            return;
2106                    }
2107    
2108                    int exceptionType = LayoutImpl.validateFriendlyURL(friendlyURL);
2109    
2110                    if (exceptionType != -1) {
2111                            throw new LayoutFriendlyURLException(exceptionType);
2112                    }
2113    
2114                    Layout layout = layoutPersistence.fetchByG_P_F(
2115                            groupId, privateLayout, friendlyURL);
2116    
2117                    if ((layout != null) && (layout.getLayoutId() != layoutId)) {
2118                            throw new LayoutFriendlyURLException(
2119                                    LayoutFriendlyURLException.DUPLICATE);
2120                    }
2121    
2122                    LayoutImpl.validateFriendlyURLKeyword(friendlyURL);
2123    
2124                    /*List<FriendlyURLMapper> friendlyURLMappers =
2125                            portletLocalService.getFriendlyURLMappers();
2126    
2127                    for (FriendlyURLMapper friendlyURLMapper : friendlyURLMappers) {
2128                            if (friendlyURL.indexOf(friendlyURLMapper.getMapping()) != -1) {
2129                                    LayoutFriendlyURLException lfurle =
2130                                            new LayoutFriendlyURLException(
2131                                                    LayoutFriendlyURLException.KEYWORD_CONFLICT);
2132    
2133                                    lfurle.setKeywordConflict(friendlyURLMapper.getMapping());
2134    
2135                                    throw lfurle;
2136                            }
2137                    }*/
2138    
2139                    String layoutIdFriendlyURL = friendlyURL.substring(1);
2140    
2141                    if (Validator.isNumber(layoutIdFriendlyURL) &&
2142                            !layoutIdFriendlyURL.equals(String.valueOf(layoutId))) {
2143    
2144                            LayoutFriendlyURLException lfurle = new LayoutFriendlyURLException(
2145                                    LayoutFriendlyURLException.POSSIBLE_DUPLICATE);
2146    
2147                            lfurle.setKeywordConflict(layoutIdFriendlyURL);
2148    
2149                            throw lfurle;
2150                    }
2151            }
2152    
2153            protected void validateName(String name) throws PortalException {
2154                    if (Validator.isNull(name)) {
2155                            throw new LayoutNameException();
2156                    }
2157            }
2158    
2159            protected void validateName(String name, String languageId)
2160                    throws PortalException {
2161    
2162                    String defaultLanguageId = LocaleUtil.toLanguageId(
2163                            LocaleUtil.getDefault());
2164    
2165                    if (defaultLanguageId.equals(languageId)) {
2166                            validateName(name);
2167                    }
2168            }
2169    
2170            protected void validateParentLayoutId(
2171                            long groupId, boolean privateLayout, long layoutId,
2172                            long parentLayoutId)
2173                    throws PortalException, SystemException {
2174    
2175                    Layout layout = layoutPersistence.findByG_P_L(
2176                            groupId, privateLayout, layoutId);
2177    
2178                    if (parentLayoutId != layout.getParentLayoutId()) {
2179    
2180                            // Layouts can always be moved to the root level
2181    
2182                            if (parentLayoutId == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
2183                                    return;
2184                            }
2185    
2186                            // Layout cannot become a child of a layout that is not parentable
2187    
2188                            Layout parentLayout = layoutPersistence.findByG_P_L(
2189                                    groupId, privateLayout, parentLayoutId);
2190    
2191                            if (!PortalUtil.isLayoutParentable(parentLayout)) {
2192                                    throw new LayoutParentLayoutIdException(
2193                                            LayoutParentLayoutIdException.NOT_PARENTABLE);
2194                            }
2195    
2196                            // Layout cannot become descendant of itself
2197    
2198                            if (PortalUtil.isLayoutDescendant(layout, parentLayoutId)) {
2199                                    throw new LayoutParentLayoutIdException(
2200                                            LayoutParentLayoutIdException.SELF_DESCENDANT);
2201                            }
2202    
2203                            // If layout is moved, the new first layout must be valid
2204    
2205                            if (layout.getParentLayoutId() ==
2206                                            LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
2207    
2208                                    List<Layout> layouts = layoutPersistence.findByG_P_P(
2209                                            groupId, privateLayout,
2210                                            LayoutConstants.DEFAULT_PARENT_LAYOUT_ID, 0, 2);
2211    
2212                                    // You can only reach this point if there are more than two
2213                                    // layouts at the root level because of the descendant check
2214    
2215                                    long firstLayoutId = layouts.get(0).getLayoutId();
2216    
2217                                    if (firstLayoutId == layoutId) {
2218                                            Layout secondLayout = layouts.get(1);
2219    
2220                                            try {
2221                                                    validateFirstLayout(secondLayout.getType());
2222                                            }
2223                                            catch (LayoutHiddenException lhe) {
2224                                                    throw new LayoutParentLayoutIdException(
2225                                                            LayoutParentLayoutIdException.FIRST_LAYOUT_HIDDEN);
2226                                            }
2227                                            catch (LayoutTypeException lte) {
2228                                                    throw new LayoutParentLayoutIdException(
2229                                                            LayoutParentLayoutIdException.FIRST_LAYOUT_TYPE);
2230                                            }
2231                                    }
2232                            }
2233                    }
2234            }
2235    
2236    }