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