001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.service.impl;
016    
017    import com.liferay.portal.LayoutFriendlyURLException;
018    import com.liferay.portal.LayoutFriendlyURLsException;
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.kernel.bean.BeanReference;
024    import com.liferay.portal.kernel.bean.IdentifiableBean;
025    import com.liferay.portal.kernel.exception.PortalException;
026    import com.liferay.portal.kernel.exception.SystemException;
027    import com.liferay.portal.kernel.language.LanguageUtil;
028    import com.liferay.portal.kernel.portlet.FriendlyURLMapper;
029    import com.liferay.portal.kernel.util.FriendlyURLNormalizerUtil;
030    import com.liferay.portal.kernel.util.LocaleUtil;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.util.StringUtil;
033    import com.liferay.portal.kernel.util.Validator;
034    import com.liferay.portal.model.Group;
035    import com.liferay.portal.model.Layout;
036    import com.liferay.portal.model.LayoutConstants;
037    import com.liferay.portal.model.LayoutFriendlyURL;
038    import com.liferay.portal.model.LayoutSet;
039    import com.liferay.portal.model.LayoutSetPrototype;
040    import com.liferay.portal.model.ResourceConstants;
041    import com.liferay.portal.model.Role;
042    import com.liferay.portal.model.RoleConstants;
043    import com.liferay.portal.model.impl.LayoutImpl;
044    import com.liferay.portal.security.permission.ActionKeys;
045    import com.liferay.portal.service.PortletLocalServiceUtil;
046    import com.liferay.portal.service.ResourcePermissionLocalService;
047    import com.liferay.portal.service.RoleLocalServiceUtil;
048    import com.liferay.portal.service.persistence.LayoutFriendlyURLPersistence;
049    import com.liferay.portal.service.persistence.LayoutPersistence;
050    import com.liferay.portal.service.persistence.LayoutSetPersistence;
051    import com.liferay.portal.util.Portal;
052    import com.liferay.portal.util.PortalUtil;
053    import com.liferay.portal.util.comparator.LayoutPriorityComparator;
054    import com.liferay.portlet.sites.util.SitesUtil;
055    
056    import java.util.HashMap;
057    import java.util.List;
058    import java.util.Locale;
059    import java.util.Map;
060    
061    /**
062     * @author Raymond Aug??
063     */
064    public class LayoutLocalServiceHelper implements IdentifiableBean {
065    
066            @Override
067            public String getBeanIdentifier() {
068                    return _beanIdentifier;
069            }
070    
071            public String getFriendlyURL(
072                            long groupId, boolean privateLayout, long layoutId, String name,
073                            String friendlyURL)
074                    throws PortalException, SystemException {
075    
076                    friendlyURL = getFriendlyURL(friendlyURL);
077    
078                    if (Validator.isNotNull(friendlyURL)) {
079                            return friendlyURL;
080                    }
081    
082                    friendlyURL = StringPool.SLASH + getFriendlyURL(name);
083    
084                    String originalFriendlyURL = friendlyURL;
085    
086                    for (int i = 1;; i++) {
087                            try {
088                                    validateFriendlyURL(
089                                            groupId, privateLayout, layoutId, friendlyURL);
090    
091                                    break;
092                            }
093                            catch (LayoutFriendlyURLException lfurle) {
094                                    int type = lfurle.getType();
095    
096                                    if (type == LayoutFriendlyURLException.DUPLICATE) {
097                                            friendlyURL = originalFriendlyURL + i;
098                                    }
099                                    else {
100                                            friendlyURL = StringPool.SLASH + layoutId;
101    
102                                            break;
103                                    }
104                            }
105                    }
106    
107                    return friendlyURL;
108            }
109    
110            public String getFriendlyURL(String friendlyURL) {
111                    return FriendlyURLNormalizerUtil.normalize(friendlyURL);
112            }
113    
114            public Map<Locale, String> getFriendlyURLMap(
115                            long groupId, boolean privateLayout, long layoutId, String name,
116                            Map<Locale, String> friendlyURLMap)
117                    throws PortalException, SystemException {
118    
119                    Map<Locale, String> newFriendlyURLMap = new HashMap<Locale, String>();
120    
121                    Locale[] locales = LanguageUtil.getAvailableLocales(groupId);
122    
123                    for (Locale locale : locales) {
124                            String friendlyURL = friendlyURLMap.get(locale);
125    
126                            if (Validator.isNotNull(friendlyURL)) {
127                                    friendlyURL = getFriendlyURL(
128                                            groupId, privateLayout, layoutId, name, friendlyURL);
129    
130                                    newFriendlyURLMap.put(locale, friendlyURL);
131                            }
132                    }
133    
134                    Locale siteDefaultLocale = LocaleUtil.getSiteDefault();
135    
136                    if (newFriendlyURLMap.isEmpty() ||
137                            Validator.isNull(newFriendlyURLMap.get(siteDefaultLocale))) {
138    
139                            String friendlyURL = getFriendlyURL(
140                                    groupId, privateLayout, layoutId, name, StringPool.BLANK);
141    
142                            newFriendlyURLMap.put(siteDefaultLocale, friendlyURL);
143                    }
144    
145                    return newFriendlyURLMap;
146            }
147    
148            public int getNextPriority(
149                            long groupId, boolean privateLayout, long parentLayoutId,
150                            String sourcePrototypeLayoutUuid, int defaultPriority)
151                    throws SystemException {
152    
153                    try {
154                            int priority = defaultPriority;
155    
156                            if (priority < 0) {
157                                    Layout layout = layoutPersistence.findByG_P_P_First(
158                                            groupId, privateLayout, parentLayoutId,
159                                            new LayoutPriorityComparator(false));
160    
161                                    priority = layout.getPriority() + 1;
162                            }
163    
164                            if ((priority < _PRIORITY_BUFFER) &&
165                                    Validator.isNull(sourcePrototypeLayoutUuid)) {
166    
167                                    LayoutSet layoutSet = layoutSetPersistence.fetchByG_P(
168                                            groupId, privateLayout);
169    
170                                    if (Validator.isNotNull(
171                                                    layoutSet.getLayoutSetPrototypeUuid()) &&
172                                            layoutSet.isLayoutSetPrototypeLinkEnabled()) {
173    
174                                            priority = priority + _PRIORITY_BUFFER;
175                                    }
176                            }
177    
178                            return priority;
179                    }
180                    catch (NoSuchLayoutException nsle) {
181                            return 0;
182                    }
183            }
184    
185            public long getParentLayoutId(
186                            long groupId, boolean privateLayout, long parentLayoutId)
187                    throws SystemException {
188    
189                    if (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
190    
191                            // Ensure parent layout exists
192    
193                            Layout parentLayout = layoutPersistence.fetchByG_P_L(
194                                    groupId, privateLayout, parentLayoutId);
195    
196                            if (parentLayout == null) {
197                                    parentLayoutId = LayoutConstants.DEFAULT_PARENT_LAYOUT_ID;
198                            }
199                    }
200    
201                    return parentLayoutId;
202            }
203    
204            public boolean hasLayoutSetPrototypeLayout(
205                            LayoutSetPrototype layoutSetPrototype, String layoutUuid)
206                    throws PortalException, SystemException {
207    
208                    Layout layout = layoutPersistence.fetchByUUID_G_P(
209                            layoutUuid, layoutSetPrototype.getGroupId(), true);
210    
211                    if (layout != null) {
212                            return true;
213                    }
214    
215                    return false;
216            }
217    
218            @Override
219            public void setBeanIdentifier(String beanIdentifier) {
220                    _beanIdentifier = beanIdentifier;
221            }
222    
223            public void validate(
224                            long groupId, boolean privateLayout, long layoutId,
225                            long parentLayoutId, String name, String type, boolean hidden,
226                            Map<Locale, String> friendlyURLMap)
227                    throws PortalException, SystemException {
228    
229                    validateName(name);
230    
231                    boolean firstLayout = false;
232    
233                    if (parentLayoutId == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
234                            List<Layout> layouts = layoutPersistence.findByG_P_P(
235                                    groupId, privateLayout, parentLayoutId, 0, 1);
236    
237                            if (layouts.size() == 0) {
238                                    firstLayout = true;
239                            }
240                            else {
241                                    long firstLayoutId = layouts.get(0).getLayoutId();
242    
243                                    if (firstLayoutId == layoutId) {
244                                            firstLayout = true;
245                                    }
246                            }
247                    }
248                    else {
249    
250                            // Layout cannot become a child of a layout that is not sortable
251                            // because it is linked to a layout set prototype
252    
253                            Layout parentLayout = layoutPersistence.findByG_P_L(
254                                    groupId, privateLayout, parentLayoutId);
255    
256                            if (!SitesUtil.isLayoutSortable(parentLayout)) {
257                                    throw new LayoutParentLayoutIdException(
258                                            LayoutParentLayoutIdException.NOT_SORTABLE);
259                            }
260                    }
261    
262                    if (firstLayout) {
263                            validateFirstLayout(type);
264                    }
265    
266                    if (!PortalUtil.isLayoutParentable(type)) {
267                            if (layoutPersistence.countByG_P_P(
268                                            groupId, privateLayout, layoutId) > 0) {
269    
270                                    throw new LayoutTypeException(
271                                            LayoutTypeException.NOT_PARENTABLE);
272                            }
273                    }
274    
275                    validateFriendlyURLs(groupId, privateLayout, layoutId, friendlyURLMap);
276            }
277    
278            public void validateFirstLayout(Layout layout)
279                    throws PortalException, SystemException {
280    
281                    Group group = layout.getGroup();
282    
283                    if (group.isGuest() && layout.isPublicLayout() &&
284                            !hasGuestViewPermission(layout)) {
285    
286                            LayoutTypeException lte = new LayoutTypeException(
287                                    LayoutTypeException.FIRST_LAYOUT_PERMISSION);
288    
289                            throw lte;
290                    }
291    
292                    validateFirstLayout(layout.getType());
293            }
294    
295            public void validateFirstLayout(String type) throws PortalException {
296                    if (Validator.isNull(type) || !PortalUtil.isLayoutFirstPageable(type)) {
297                            LayoutTypeException lte = new LayoutTypeException(
298                                    LayoutTypeException.FIRST_LAYOUT);
299    
300                            lte.setLayoutType(type);
301    
302                            throw lte;
303                    }
304            }
305    
306            public void validateFriendlyURL(
307                            long groupId, boolean privateLayout, long layoutId,
308                            String friendlyURL)
309                    throws PortalException, SystemException {
310    
311                    if (Validator.isNull(friendlyURL)) {
312                            return;
313                    }
314    
315                    int exceptionType = LayoutImpl.validateFriendlyURL(friendlyURL);
316    
317                    if (exceptionType != -1) {
318                            throw new LayoutFriendlyURLException(exceptionType);
319                    }
320    
321                    List<LayoutFriendlyURL> layoutFriendlyURLs =
322                            layoutFriendlyURLPersistence.findByG_P_F(
323                                    groupId, privateLayout, friendlyURL);
324    
325                    for (LayoutFriendlyURL layoutFriendlyURL : layoutFriendlyURLs) {
326                            Layout layout = layoutPersistence.findByPrimaryKey(
327                                    layoutFriendlyURL.getPlid());
328    
329                            if (layout.getLayoutId() != layoutId) {
330                                    throw new LayoutFriendlyURLException(
331                                            LayoutFriendlyURLException.DUPLICATE);
332                            }
333                    }
334    
335                    LayoutImpl.validateFriendlyURLKeyword(friendlyURL);
336    
337                    if (friendlyURL.contains(Portal.FRIENDLY_URL_SEPARATOR)) {
338                            LayoutFriendlyURLException lfurle =
339                                    new LayoutFriendlyURLException(
340                                            LayoutFriendlyURLException.KEYWORD_CONFLICT);
341    
342                            lfurle.setKeywordConflict(Portal.FRIENDLY_URL_SEPARATOR);
343    
344                            throw lfurle;
345                    }
346    
347                    List<FriendlyURLMapper> friendlyURLMappers =
348                            PortletLocalServiceUtil.getFriendlyURLMappers();
349    
350                    for (FriendlyURLMapper friendlyURLMapper : friendlyURLMappers) {
351                            if (friendlyURLMapper.isCheckMappingWithPrefix()) {
352                                    continue;
353                            }
354    
355                            String mapping = StringPool.SLASH + friendlyURLMapper.getMapping();
356    
357                            if (friendlyURL.contains(mapping + StringPool.SLASH) ||
358                                    friendlyURL.endsWith(mapping)) {
359    
360                                    LayoutFriendlyURLException lfurle =
361                                            new LayoutFriendlyURLException(
362                                                    LayoutFriendlyURLException.KEYWORD_CONFLICT);
363    
364                                    lfurle.setKeywordConflict(friendlyURLMapper.getMapping());
365    
366                                    throw lfurle;
367                            }
368                    }
369    
370                    Locale[] availableLocales = LanguageUtil.getAvailableLocales();
371    
372                    for (Locale locale : availableLocales) {
373                            String languageId = StringUtil.toLowerCase(
374                                    LocaleUtil.toLanguageId(locale));
375    
376                            String i18nPathLanguageId =
377                                    StringPool.SLASH +
378                                            PortalUtil.getI18nPathLanguageId(locale, languageId);
379    
380                            if (friendlyURL.startsWith(i18nPathLanguageId + StringPool.SLASH) ||
381                                    friendlyURL.startsWith(
382                                            StringPool.SLASH + languageId + StringPool.SLASH) ||
383                                    friendlyURL.endsWith(i18nPathLanguageId) ||
384                                    friendlyURL.endsWith(StringPool.SLASH + languageId)) {
385    
386                                    LayoutFriendlyURLException lfurle =
387                                            new LayoutFriendlyURLException(
388                                                    LayoutFriendlyURLException.KEYWORD_CONFLICT);
389    
390                                    lfurle.setKeywordConflict(i18nPathLanguageId);
391    
392                                    throw lfurle;
393                            }
394                    }
395    
396                    String layoutIdFriendlyURL = friendlyURL.substring(1);
397    
398                    if (Validator.isNumber(layoutIdFriendlyURL) &&
399                            !layoutIdFriendlyURL.equals(String.valueOf(layoutId))) {
400    
401                            LayoutFriendlyURLException lfurle = new LayoutFriendlyURLException(
402                                    LayoutFriendlyURLException.POSSIBLE_DUPLICATE);
403    
404                            lfurle.setKeywordConflict(layoutIdFriendlyURL);
405    
406                            throw lfurle;
407                    }
408            }
409    
410            public void validateFriendlyURLs(
411                            long groupId, boolean privateLayout, long layoutId,
412                            Map<Locale, String> friendlyURLMap)
413                    throws PortalException, SystemException {
414    
415                    LayoutFriendlyURLsException layoutFriendlyURLsException = null;
416    
417                    for (Map.Entry<Locale, String> entry : friendlyURLMap.entrySet()) {
418                            try {
419                                    String friendlyURL = entry.getValue();
420    
421                                    validateFriendlyURL(
422                                            groupId, privateLayout, layoutId, friendlyURL);
423                            }
424                            catch (LayoutFriendlyURLException lfurle) {
425                                    Locale locale = entry.getKey();
426    
427                                    if (layoutFriendlyURLsException == null) {
428                                            layoutFriendlyURLsException =
429                                                    new LayoutFriendlyURLsException();
430                                    }
431    
432                                    layoutFriendlyURLsException.addLocalizedException(
433                                            locale, lfurle);
434                            }
435                    }
436    
437                    if (layoutFriendlyURLsException != null) {
438                            throw layoutFriendlyURLsException;
439                    }
440            }
441    
442            public void validateName(String name) throws PortalException {
443                    if (Validator.isNull(name)) {
444                            throw new LayoutNameException();
445                    }
446            }
447    
448            public void validateName(String name, String languageId)
449                    throws PortalException {
450    
451                    String defaultLanguageId = LocaleUtil.toLanguageId(
452                            LocaleUtil.getSiteDefault());
453    
454                    if (defaultLanguageId.equals(languageId)) {
455                            validateName(name);
456                    }
457            }
458    
459            public void validateParentLayoutId(
460                            long groupId, boolean privateLayout, long layoutId,
461                            long parentLayoutId)
462                    throws PortalException, SystemException {
463    
464                    Layout layout = layoutPersistence.findByG_P_L(
465                            groupId, privateLayout, layoutId);
466    
467                    if (parentLayoutId == layout.getParentLayoutId()) {
468                            return;
469                    }
470    
471                    // Layouts can always be moved to the root level
472    
473                    if (parentLayoutId == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
474                            return;
475                    }
476    
477                    // Layout cannot become a child of a layout that is not parentable
478    
479                    Layout parentLayout = layoutPersistence.findByG_P_L(
480                            groupId, privateLayout, parentLayoutId);
481    
482                    if (!PortalUtil.isLayoutParentable(parentLayout)) {
483                            throw new LayoutParentLayoutIdException(
484                                    LayoutParentLayoutIdException.NOT_PARENTABLE);
485                    }
486    
487                    // Layout cannot become a child of a layout that is not sortable because
488                    // it is linked to a layout set prototype
489    
490                    if (!SitesUtil.isLayoutSortable(parentLayout)) {
491                            throw new LayoutParentLayoutIdException(
492                                    LayoutParentLayoutIdException.NOT_SORTABLE);
493                    }
494    
495                    // Layout cannot become descendant of itself
496    
497                    if (PortalUtil.isLayoutDescendant(layout, parentLayoutId)) {
498                            throw new LayoutParentLayoutIdException(
499                                    LayoutParentLayoutIdException.SELF_DESCENDANT);
500                    }
501    
502                    // If layout is moved, the new first layout must be valid
503    
504                    if (layout.getParentLayoutId() ==
505                                    LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
506    
507                            List<Layout> layouts = layoutPersistence.findByG_P_P(
508                                    groupId, privateLayout,
509                                    LayoutConstants.DEFAULT_PARENT_LAYOUT_ID, 0, 2);
510    
511                            // You can only reach this point if there are more than two layouts
512                            // at the root level because of the descendant check
513    
514                            long firstLayoutId = layouts.get(0).getLayoutId();
515    
516                            if (firstLayoutId == layoutId) {
517                                    Layout secondLayout = layouts.get(1);
518    
519                                    if (Validator.isNull(secondLayout.getType()) ||
520                                            !PortalUtil.isLayoutFirstPageable(secondLayout.getType())) {
521    
522                                            throw new LayoutParentLayoutIdException(
523                                                    LayoutParentLayoutIdException.FIRST_LAYOUT_TYPE);
524                                    }
525                            }
526                    }
527            }
528    
529            protected boolean hasGuestViewPermission(Layout layout)
530                    throws PortalException, SystemException {
531    
532                    Role role = RoleLocalServiceUtil.getRole(
533                            layout.getCompanyId(), RoleConstants.GUEST);
534    
535                    return resourcePermissionLocalService.hasResourcePermission(
536                            layout.getCompanyId(), Layout.class.getName(),
537                            ResourceConstants.SCOPE_INDIVIDUAL,
538                            String.valueOf(layout.getPlid()), role.getRoleId(),
539                            ActionKeys.VIEW);
540            }
541    
542            @BeanReference(type = LayoutFriendlyURLPersistence.class)
543            protected LayoutFriendlyURLPersistence layoutFriendlyURLPersistence;
544    
545            @BeanReference(type = LayoutPersistence.class)
546            protected LayoutPersistence layoutPersistence;
547    
548            @BeanReference(type = LayoutSetPersistence.class)
549            protected LayoutSetPersistence layoutSetPersistence;
550    
551            @BeanReference(type = ResourcePermissionLocalService.class)
552            protected ResourcePermissionLocalService resourcePermissionLocalService;
553    
554            private static final int _PRIORITY_BUFFER = 1000000;
555    
556            private String _beanIdentifier;
557    
558    }