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