001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.layoutsadmin.util;
016    
017    import com.liferay.exportimport.kernel.staging.LayoutStagingUtil;
018    import com.liferay.exportimport.kernel.staging.StagingUtil;
019    import com.liferay.portal.kernel.dao.orm.QueryUtil;
020    import com.liferay.portal.kernel.exception.PortalException;
021    import com.liferay.portal.kernel.json.JSONArray;
022    import com.liferay.portal.kernel.json.JSONFactoryUtil;
023    import com.liferay.portal.kernel.json.JSONObject;
024    import com.liferay.portal.kernel.log.Log;
025    import com.liferay.portal.kernel.log.LogFactoryUtil;
026    import com.liferay.portal.kernel.model.Group;
027    import com.liferay.portal.kernel.model.Layout;
028    import com.liferay.portal.kernel.model.LayoutBranch;
029    import com.liferay.portal.kernel.model.LayoutConstants;
030    import com.liferay.portal.kernel.model.LayoutRevision;
031    import com.liferay.portal.kernel.model.LayoutSetBranch;
032    import com.liferay.portal.kernel.model.impl.VirtualLayout;
033    import com.liferay.portal.kernel.security.permission.ActionKeys;
034    import com.liferay.portal.kernel.service.LayoutLocalServiceUtil;
035    import com.liferay.portal.kernel.service.LayoutServiceUtil;
036    import com.liferay.portal.kernel.service.LayoutSetBranchLocalServiceUtil;
037    import com.liferay.portal.kernel.service.permission.GroupPermissionUtil;
038    import com.liferay.portal.kernel.service.permission.LayoutPermissionUtil;
039    import com.liferay.portal.kernel.theme.ThemeDisplay;
040    import com.liferay.portal.kernel.util.ArrayUtil;
041    import com.liferay.portal.kernel.util.ParamUtil;
042    import com.liferay.portal.kernel.util.SessionClicks;
043    import com.liferay.portal.kernel.util.StringBundler;
044    import com.liferay.portal.kernel.util.StringPool;
045    import com.liferay.portal.kernel.util.WebKeys;
046    import com.liferay.portal.util.PropsValues;
047    import com.liferay.sites.kernel.util.SitesUtil;
048    
049    import java.util.ArrayList;
050    import java.util.Collections;
051    import java.util.Iterator;
052    import java.util.List;
053    
054    import javax.servlet.http.HttpServletRequest;
055    import javax.servlet.http.HttpSession;
056    
057    /**
058     * @author Brian Wing Shun Chan
059     * @author Eduardo Lundgren
060     * @author Bruno Basto
061     * @author Marcellus Tavares
062     * @author Zsolt Szab??
063     * @author Tibor Lipusz
064     */
065    public class LayoutsTreeUtil {
066    
067            public static String getLayoutsJSON(
068                            HttpServletRequest request, long groupId, boolean privateLayout,
069                            long parentLayoutId, boolean incomplete, String treeId)
070                    throws Exception {
071    
072                    return getLayoutsJSON(
073                            request, groupId, privateLayout, parentLayoutId, null, incomplete,
074                            treeId);
075            }
076    
077            public static String getLayoutsJSON(
078                            HttpServletRequest request, long groupId, boolean privateLayout,
079                            long parentLayoutId, long[] expandedLayoutIds, boolean incomplete,
080                            String treeId)
081                    throws Exception {
082    
083                    if (_log.isDebugEnabled()) {
084                            StringBundler sb = new StringBundler(13);
085    
086                            sb.append("getLayoutsJSON(groupId=");
087                            sb.append(groupId);
088                            sb.append(", privateLayout=");
089                            sb.append(privateLayout);
090                            sb.append(", parentLayoutId=");
091                            sb.append(parentLayoutId);
092                            sb.append(", expandedLayoutIds=");
093                            sb.append(expandedLayoutIds);
094                            sb.append(", incomplete=");
095                            sb.append(incomplete);
096                            sb.append(", treeId=");
097                            sb.append(treeId);
098                            sb.append(StringPool.CLOSE_PARENTHESIS);
099    
100                            _log.debug(sb.toString());
101                    }
102    
103                    LayoutTreeNodes layoutTreeNodes = _getLayoutTreeNodes(
104                            request, groupId, privateLayout, parentLayoutId, incomplete,
105                            expandedLayoutIds, treeId);
106    
107                    return _toJSON(request, groupId, layoutTreeNodes);
108            }
109    
110            public static String getLayoutsJSON(
111                            HttpServletRequest request, long groupId, String treeId)
112                    throws Exception {
113    
114                    if (_log.isDebugEnabled()) {
115                            StringBundler sb = new StringBundler(5);
116    
117                            sb.append("getLayoutsJSON(groupId=");
118                            sb.append(groupId);
119                            sb.append(", treeId=");
120                            sb.append(treeId);
121                            sb.append(StringPool.CLOSE_PARENTHESIS);
122    
123                            _log.debug(sb.toString());
124                    }
125    
126                    LayoutTreeNodes layoutTreeNodes = new LayoutTreeNodes();
127    
128                    layoutTreeNodes.addAll(
129                            _getLayoutTreeNodes(
130                                    request, groupId, true,
131                                    LayoutConstants.DEFAULT_PARENT_LAYOUT_ID, false, null, treeId));
132                    layoutTreeNodes.addAll(
133                            _getLayoutTreeNodes(
134                                    request, groupId, false,
135                                    LayoutConstants.DEFAULT_PARENT_LAYOUT_ID, false, null, treeId));
136    
137                    return _toJSON(request, groupId, layoutTreeNodes);
138            }
139    
140            private static Layout _fetchCurrentLayout(HttpServletRequest request) {
141                    long selPlid = ParamUtil.getLong(request, "selPlid");
142    
143                    if (selPlid > 0) {
144                            return LayoutLocalServiceUtil.fetchLayout(selPlid);
145                    }
146    
147                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
148                            WebKeys.THEME_DISPLAY);
149    
150                    Layout layout = themeDisplay.getLayout();
151    
152                    if (!layout.isTypeControlPanel()) {
153                            return layout;
154                    }
155    
156                    return null;
157            }
158    
159            private static List<Layout> _getAncestorLayouts(HttpServletRequest request)
160                    throws Exception {
161    
162                    Layout layout = _fetchCurrentLayout(request);
163    
164                    if (layout == null) {
165                            return Collections.emptyList();
166                    }
167    
168                    List<Layout> ancestorLayouts = LayoutServiceUtil.getAncestorLayouts(
169                            layout.getPlid());
170    
171                    if (_log.isDebugEnabled()) {
172                            StringBundler sb = new StringBundler(7);
173    
174                            sb.append("_getAncestorLayouts(plid=");
175                            sb.append(layout.getPlid());
176                            sb.append(", ancestorLayouts=");
177                            sb.append(ancestorLayouts);
178                            sb.append(", layout=");
179                            sb.append(layout);
180                            sb.append(StringPool.CLOSE_PARENTHESIS);
181    
182                            _log.debug(sb.toString());
183                    }
184    
185                    ancestorLayouts.add(layout);
186    
187                    return ancestorLayouts;
188            }
189    
190            private static LayoutTreeNodes _getLayoutTreeNodes(
191                            HttpServletRequest request, long groupId, boolean privateLayout,
192                            long parentLayoutId, boolean incomplete, long[] expandedLayoutIds,
193                            String treeId)
194                    throws Exception {
195    
196                    if (_log.isDebugEnabled()) {
197                            StringBundler sb = new StringBundler(13);
198    
199                            sb.append("_getLayoutTreeNodes(groupId=");
200                            sb.append(groupId);
201                            sb.append(", privateLayout=");
202                            sb.append(privateLayout);
203                            sb.append(", parentLayoutId=");
204                            sb.append(parentLayoutId);
205                            sb.append(", expandedLayoutIds=");
206                            sb.append(expandedLayoutIds);
207                            sb.append(", incomplete=");
208                            sb.append(incomplete);
209                            sb.append(", treeId=");
210                            sb.append(treeId);
211                            sb.append(StringPool.CLOSE_PARENTHESIS);
212    
213                            _log.debug(sb.toString());
214                    }
215    
216                    List<LayoutTreeNode> layoutTreeNodes = new ArrayList<>();
217    
218                    List<Layout> ancestorLayouts = _getAncestorLayouts(request);
219    
220                    List<Layout> layouts = LayoutServiceUtil.getLayouts(
221                            groupId, privateLayout, parentLayoutId, incomplete,
222                            QueryUtil.ALL_POS, QueryUtil.ALL_POS);
223    
224                    for (Layout layout :
225                                    _paginateLayouts(
226                                            request, groupId, privateLayout, parentLayoutId, layouts,
227                                            treeId)) {
228    
229                            LayoutTreeNode layoutTreeNode = new LayoutTreeNode(layout);
230    
231                            LayoutTreeNodes childLayoutTreeNodes = null;
232    
233                            if (_isExpandableLayout(
234                                            request, ancestorLayouts, expandedLayoutIds, layout)) {
235    
236                                    if (layout instanceof VirtualLayout) {
237                                            VirtualLayout virtualLayout = (VirtualLayout)layout;
238    
239                                            childLayoutTreeNodes = _getLayoutTreeNodes(
240                                                    request, virtualLayout.getSourceGroupId(),
241                                                    virtualLayout.isPrivateLayout(),
242                                                    virtualLayout.getLayoutId(), incomplete,
243                                                    expandedLayoutIds, treeId);
244                                    }
245                                    else {
246                                            childLayoutTreeNodes = _getLayoutTreeNodes(
247                                                    request, groupId, layout.isPrivateLayout(),
248                                                    layout.getLayoutId(), incomplete, expandedLayoutIds,
249                                                    treeId);
250                                    }
251                            }
252                            else {
253                                    int childLayoutsCount = LayoutServiceUtil.getLayoutsCount(
254                                            groupId, privateLayout, layout.getLayoutId());
255    
256                                    childLayoutTreeNodes = new LayoutTreeNodes(
257                                            new ArrayList<LayoutTreeNode>(), childLayoutsCount);
258                            }
259    
260                            layoutTreeNode.setChildLayoutTreeNodes(childLayoutTreeNodes);
261    
262                            layoutTreeNodes.add(layoutTreeNode);
263                    }
264    
265                    return new LayoutTreeNodes(layoutTreeNodes, layouts.size());
266            }
267    
268            private static int _getLoadedLayoutsCount(
269                            HttpSession session, long groupId, boolean privateLayout,
270                            long layoutId, String treeId)
271                    throws Exception {
272    
273                    StringBundler sb = new StringBundler(7);
274    
275                    sb.append(treeId);
276                    sb.append(StringPool.COLON);
277                    sb.append(groupId);
278                    sb.append(StringPool.COLON);
279                    sb.append(privateLayout);
280                    sb.append(StringPool.COLON);
281                    sb.append("Pagination");
282    
283                    String key = sb.toString();
284    
285                    String paginationJSON = SessionClicks.get(
286                            session, key, JSONFactoryUtil.getNullJSON());
287    
288                    JSONObject paginationJSONObject = JSONFactoryUtil.createJSONObject(
289                            paginationJSON);
290    
291                    if (_log.isDebugEnabled()) {
292                            sb = new StringBundler(9);
293    
294                            sb.append("_getLoadedLayoutsCount(key=");
295                            sb.append(key);
296                            sb.append(", layoutId=");
297                            sb.append(layoutId);
298                            sb.append(", paginationJSON=");
299                            sb.append(paginationJSON);
300                            sb.append(", paginationJSONObject");
301                            sb.append(paginationJSONObject);
302                            sb.append(StringPool.CLOSE_PARENTHESIS);
303    
304                            _log.debug(sb.toString());
305                    }
306    
307                    return paginationJSONObject.getInt(String.valueOf(layoutId), 0);
308            }
309    
310            private static boolean _isDeleteable(
311                            Layout layout, ThemeDisplay themeDisplay)
312                    throws PortalException {
313    
314                    if (!LayoutPermissionUtil.contains(
315                                    themeDisplay.getPermissionChecker(), layout,
316                                    ActionKeys.DELETE)) {
317    
318                            return false;
319                    }
320    
321                    Group group = layout.getGroup();
322    
323                    if (group.isGuest() && !layout.isPrivateLayout() &&
324                            layout.isRootLayout() &&
325                            (LayoutLocalServiceUtil.getLayoutsCount(
326                                    group, false, LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) == 1)) {
327    
328                            return false;
329                    }
330    
331                    return true;
332            }
333    
334            private static boolean _isExpandableLayout(
335                    HttpServletRequest request, List<Layout> ancestorLayouts,
336                    long[] expandedLayoutIds, Layout layout) {
337    
338                    boolean expandParentLayouts = ParamUtil.getBoolean(
339                            request, "expandParentLayouts");
340    
341                    if (expandParentLayouts || ancestorLayouts.contains(layout) ||
342                            ArrayUtil.contains(expandedLayoutIds, layout.getLayoutId())) {
343    
344                            return true;
345                    }
346    
347                    return false;
348            }
349    
350            private static boolean _isPaginationEnabled(HttpServletRequest request) {
351                    boolean paginate = ParamUtil.getBoolean(request, "paginate", true);
352    
353                    if (paginate &&
354                            (PropsValues.LAYOUT_MANAGE_PAGES_INITIAL_CHILDREN > -1)) {
355    
356                            return true;
357                    }
358    
359                    return false;
360            }
361    
362            private static List<Layout> _paginateLayouts(
363                            HttpServletRequest request, long groupId, boolean privateLayout,
364                            long parentLayoutId, List<Layout> layouts, String treeId)
365                    throws Exception {
366    
367                    if (!_isPaginationEnabled(request)) {
368                            return layouts;
369                    }
370    
371                    HttpSession session = request.getSession();
372    
373                    int loadedLayoutsCount = _getLoadedLayoutsCount(
374                            session, groupId, privateLayout, parentLayoutId, treeId);
375    
376                    int start = ParamUtil.getInteger(request, "start");
377    
378                    start = Math.max(0, Math.min(start, layouts.size()));
379    
380                    int end = ParamUtil.getInteger(
381                            request, "end",
382                            start + PropsValues.LAYOUT_MANAGE_PAGES_INITIAL_CHILDREN);
383    
384                    if (loadedLayoutsCount > end) {
385                            end = loadedLayoutsCount;
386                    }
387    
388                    end = Math.max(start, Math.min(end, layouts.size()));
389    
390                    if (_log.isDebugEnabled()) {
391                            StringBundler sb = new StringBundler(7);
392    
393                            sb.append("_paginateLayouts(loadedLayoutsCount=");
394                            sb.append(loadedLayoutsCount);
395                            sb.append(", start=");
396                            sb.append(start);
397                            sb.append(", end=");
398                            sb.append(end);
399                            sb.append(StringPool.CLOSE_PARENTHESIS);
400    
401                            _log.debug(sb.toString());
402                    }
403    
404                    return layouts.subList(start, end);
405            }
406    
407            private static String _toJSON(
408                            HttpServletRequest request, long groupId,
409                            LayoutTreeNodes layoutTreeNodes)
410                    throws Exception {
411    
412                    if (_log.isDebugEnabled()) {
413                            StringBundler sb = new StringBundler(5);
414    
415                            sb.append("_toJSON(groupId=");
416                            sb.append(groupId);
417                            sb.append(", layoutTreeNodes=");
418                            sb.append(layoutTreeNodes);
419                            sb.append(StringPool.CLOSE_PARENTHESIS);
420    
421                            _log.debug(sb.toString());
422                    }
423    
424                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
425                            WebKeys.THEME_DISPLAY);
426    
427                    JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
428    
429                    boolean hasManageLayoutsPermission = GroupPermissionUtil.contains(
430                            themeDisplay.getPermissionChecker(), groupId,
431                            ActionKeys.MANAGE_LAYOUTS);
432    
433                    for (LayoutTreeNode layoutTreeNode : layoutTreeNodes) {
434                            JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
435    
436                            String childrenJSON = _toJSON(
437                                    request, groupId, layoutTreeNode.getChildLayoutTreeNodes());
438    
439                            jsonObject.put(
440                                    "children", JSONFactoryUtil.createJSONObject(childrenJSON));
441    
442                            Layout layout = layoutTreeNode.getLayout();
443    
444                            jsonObject.put("contentDisplayPage", layout.isContentDisplayPage());
445                            jsonObject.put("deleteable", _isDeleteable(layout, themeDisplay));
446                            jsonObject.put("friendlyURL", layout.getFriendlyURL());
447    
448                            if (layout instanceof VirtualLayout) {
449                                    VirtualLayout virtualLayout = (VirtualLayout)layout;
450    
451                                    jsonObject.put("groupId", virtualLayout.getSourceGroupId());
452                            }
453                            else {
454                                    jsonObject.put("groupId", layout.getGroupId());
455                            }
456    
457                            jsonObject.put("hasChildren", layout.hasChildren());
458                            jsonObject.put("layoutId", layout.getLayoutId());
459                            jsonObject.put("name", layout.getName(themeDisplay.getLocale()));
460                            jsonObject.put(
461                                    "parentable",
462                                    LayoutPermissionUtil.contains(
463                                            themeDisplay.getPermissionChecker(), layout,
464                                            ActionKeys.ADD_LAYOUT));
465                            jsonObject.put("parentLayoutId", layout.getParentLayoutId());
466                            jsonObject.put("plid", layout.getPlid());
467                            jsonObject.put("priority", layout.getPriority());
468                            jsonObject.put("privateLayout", layout.isPrivateLayout());
469                            jsonObject.put("regularURL", layout.getRegularURL(request));
470                            jsonObject.put(
471                                    "sortable",
472                                    hasManageLayoutsPermission &&
473                                            SitesUtil.isLayoutSortable(layout));
474                            jsonObject.put("type", layout.getType());
475                            jsonObject.put(
476                                    "updateable",
477                                    LayoutPermissionUtil.contains(
478                                            themeDisplay.getPermissionChecker(), layout,
479                                            ActionKeys.UPDATE));
480                            jsonObject.put("uuid", layout.getUuid());
481    
482                            LayoutRevision layoutRevision = LayoutStagingUtil.getLayoutRevision(
483                                    layout);
484    
485                            if (layoutRevision != null) {
486                                    long layoutSetBranchId = layoutRevision.getLayoutSetBranchId();
487    
488                                    if (StagingUtil.isIncomplete(layout, layoutSetBranchId)) {
489                                            jsonObject.put("incomplete", true);
490                                    }
491    
492                                    LayoutSetBranch layoutSetBranch =
493                                            LayoutSetBranchLocalServiceUtil.getLayoutSetBranch(
494                                                    layoutSetBranchId);
495    
496                                    LayoutBranch layoutBranch = layoutRevision.getLayoutBranch();
497    
498                                    if (!layoutBranch.isMaster()) {
499                                            jsonObject.put(
500                                                    "layoutBranchId", layoutBranch.getLayoutBranchId());
501                                            jsonObject.put("layoutBranchName", layoutBranch.getName());
502                                    }
503    
504                                    if (layoutRevision.isHead()) {
505                                            jsonObject.put("layoutRevisionHead", true);
506                                    }
507    
508                                    jsonObject.put(
509                                            "layoutRevisionId", layoutRevision.getLayoutRevisionId());
510                                    jsonObject.put("layoutSetBranchId", layoutSetBranchId);
511                                    jsonObject.put(
512                                            "layoutSetBranchName", layoutSetBranch.getName());
513                            }
514    
515                            jsonArray.put(jsonObject);
516                    }
517    
518                    JSONObject responseJSONObject = JSONFactoryUtil.createJSONObject();
519    
520                    responseJSONObject.put("layouts", jsonArray);
521                    responseJSONObject.put("total", layoutTreeNodes.getTotal());
522    
523                    return responseJSONObject.toString();
524            }
525    
526            private static final Log _log = LogFactoryUtil.getLog(
527                    LayoutsTreeUtil.class);
528    
529            private static class LayoutTreeNode {
530    
531                    public LayoutTreeNode(Layout layout) {
532                            _layout = layout;
533                    }
534    
535                    public LayoutTreeNodes getChildLayoutTreeNodes() {
536                            return _childLayoutTreeNodes;
537                    }
538    
539                    public Layout getLayout() {
540                            return _layout;
541                    }
542    
543                    public void setChildLayoutTreeNodes(
544                            LayoutTreeNodes childLayoutTreeNodes) {
545    
546                            _childLayoutTreeNodes = childLayoutTreeNodes;
547                    }
548    
549                    @Override
550                    public String toString() {
551                            StringBundler sb = new StringBundler(5);
552    
553                            sb.append("{childLayoutTreeNodes=");
554                            sb.append(_childLayoutTreeNodes);
555                            sb.append(", layout=");
556                            sb.append(_layout);
557                            sb.append(StringPool.CLOSE_CURLY_BRACE);
558    
559                            return sb.toString();
560                    }
561    
562                    private LayoutTreeNodes _childLayoutTreeNodes = new LayoutTreeNodes();
563                    private final Layout _layout;
564    
565            }
566    
567            private static class LayoutTreeNodes implements Iterable<LayoutTreeNode> {
568    
569                    public LayoutTreeNodes() {
570                            _layoutTreeNodesList = new ArrayList<>();
571                    }
572    
573                    public LayoutTreeNodes(
574                            List<LayoutTreeNode> layoutTreeNodesList, int total) {
575    
576                            _layoutTreeNodesList = layoutTreeNodesList;
577                            _total = total;
578                    }
579    
580                    public void addAll(LayoutTreeNodes layoutTreeNodes) {
581                            _layoutTreeNodesList.addAll(
582                                    layoutTreeNodes.getLayoutTreeNodesList());
583    
584                            _total += layoutTreeNodes.getTotal();
585                    }
586    
587                    public List<LayoutTreeNode> getLayoutTreeNodesList() {
588                            return _layoutTreeNodesList;
589                    }
590    
591                    public int getTotal() {
592                            return _total;
593                    }
594    
595                    @Override
596                    public Iterator<LayoutTreeNode> iterator() {
597                            return _layoutTreeNodesList.iterator();
598                    }
599    
600                    @Override
601                    public String toString() {
602                            StringBundler sb = new StringBundler(5);
603    
604                            sb.append("{layoutTreeNodesList=");
605                            sb.append(_layoutTreeNodesList);
606                            sb.append(", total=");
607                            sb.append(_total);
608                            sb.append(StringPool.CLOSE_CURLY_BRACE);
609    
610                            return sb.toString();
611                    }
612    
613                    private final List<LayoutTreeNode> _layoutTreeNodesList;
614                    private int _total;
615    
616            }
617    
618    }