001/*
002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Martin Pernollet
018 */
019
020package org.nuxeo.ecm.platform.groups.audit.service.acl;
021
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.List;
025import java.util.Set;
026
027import org.apache.poi.ss.usermodel.BorderStyle;
028import org.apache.poi.ss.usermodel.Cell;
029import org.apache.poi.ss.usermodel.CellStyle;
030import org.apache.poi.ss.usermodel.FillPatternType;
031import org.nuxeo.ecm.platform.groups.audit.service.acl.ReportLayoutSettings.SpanMode;
032import org.nuxeo.ecm.platform.groups.audit.service.acl.excel.ByteColor;
033import org.nuxeo.ecm.platform.groups.audit.service.acl.excel.ExcelBuilder;
034import org.nuxeo.ecm.platform.groups.audit.service.acl.filter.IContentFilter;
035
036import com.google.common.collect.Multimap;
037
038/**
039 * An excel layout builder that uses one group of columns per user, using one column for each right type (read, write,
040 * etc).
041 *
042 * @author Martin Pernollet <mpernollet@nuxeo.com>
043 */
044public class AclExcelLayoutBuilderMultiColumn extends AclExcelLayoutBuilder {
045    protected static int USERS_ROW = 0;
046
047    protected static int PERMISSIONS_ROW = 1;
048
049    public static ReportLayoutSettings defaultLayout() {
050        ReportLayoutSettings layout = new ReportLayoutSettings();
051        layout.userHeaderHeight = -1;
052        layout.userHeaderRotation = 0;
053        layout.aclHeaderHeight = -1;// fit vertically full ACL name 1800;
054        layout.aclHeaderRotation = 0;
055
056        layout.fileTreeColumnWidth = 2.5; // in number of char
057        layout.aclColumnWidth = 2.5;
058        layout.defaultRowHeight = 100;
059        layout.splitPaneX = 500;
060        layout.splitPaneY = 1500;
061        layout.freezePaneRowSplit = 2;
062        layout.treeLineCursorRowStart = 2;
063
064        layout.aclHeaderCommentColSpan = 10;
065        layout.aclHeaderCommentRowSpan = 2;
066        layout.aclHeaderFontSize = 6; // in font unit
067
068        layout.spanMode = SpanMode.COLUMN_OVERFLOW_ON_NEXT_SHEETS;
069
070        layout.zoomRatioDenominator = 2;
071        layout.zoomRatioNumerator = 1;
072
073        layout.logoImageFile = "src/main/resources/file-delete.png";
074
075        return layout;
076    }
077
078    /* Prebuilt styles */
079
080    protected CellStyle acceptStyle;
081
082    protected CellStyle acceptStyleLeft;
083
084    protected CellStyle acceptStyleRight;
085
086    protected CellStyle denyStyle;
087
088    protected CellStyle denyStyleLeft;
089
090    protected CellStyle denyStyleRight;
091
092    protected CellStyle emptyStyle;
093
094    protected CellStyle emptyStyleLeft;
095
096    protected CellStyle emptyStyleRight;
097
098    protected int logoPictureId = -1;
099
100    public AclExcelLayoutBuilderMultiColumn() {
101        super(defaultLayout());
102    }
103
104    public AclExcelLayoutBuilderMultiColumn(IContentFilter filter) {
105        this(defaultLayout(), filter);
106    }
107
108    public AclExcelLayoutBuilderMultiColumn(ReportLayoutSettings layout, IContentFilter filter) {
109        super(layout, filter);
110    }
111
112    @Override
113    protected void renderInit() {
114        super.renderInit();
115        acceptStyle = excel.newColoredCellStyle(ByteColor.GREEN);
116
117        acceptStyleLeft = excel.newColoredCellStyle(ByteColor.GREEN);
118        acceptStyleLeft.setBorderLeft(BorderStyle.THIN);
119        acceptStyleLeft.setLeftBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
120
121        acceptStyleRight = excel.newColoredCellStyle(ByteColor.GREEN);
122        acceptStyleRight.setBorderRight(BorderStyle.THIN);
123        acceptStyleRight.setRightBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
124
125        denyStyle = excel.newColoredCellStyle(ByteColor.RED);
126        denyStyle.setFillPattern(FillPatternType.THIN_FORWARD_DIAG);  // TODO: Generalize other cells
127        denyStyle.setFillBackgroundColor(excel.getColor(ByteColor.WHITE).getIndex());
128
129        denyStyleLeft = excel.newColoredCellStyle(ByteColor.RED);
130        denyStyleLeft.setBorderLeft(BorderStyle.THIN);
131        denyStyleLeft.setLeftBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
132
133        denyStyleRight = excel.newColoredCellStyle(ByteColor.RED);
134        denyStyleRight.setBorderRight(BorderStyle.THIN);
135        denyStyleRight.setRightBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
136
137        emptyStyle = excel.newColoredCellStyle(ByteColor.WHITE);
138
139        emptyStyleLeft = excel.newColoredCellStyle(ByteColor.WHITE);
140        emptyStyleLeft.setBorderLeft(BorderStyle.THIN);
141        emptyStyleLeft.setLeftBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
142
143        emptyStyleRight = excel.newColoredCellStyle(ByteColor.WHITE);
144        emptyStyleRight.setBorderRight(BorderStyle.THIN);
145        emptyStyleRight.setRightBorderColor(excel.getColor(ByteColor.BLACK).getIndex());
146
147        if (layoutSettings.logoImageFile != null)
148            try {
149                logoPictureId = excel.loadPicture(layoutSettings.logoImageFile);
150            } catch (IOException e) {
151                log.error(e, e);
152            }
153    }
154
155    /* HEADER RENDERING */
156
157    /**
158     * Write users and groups on the first row. Memorize the user (or group) column which can later be retrieved with
159     * getColumn(user)
160     */
161    @Override
162    protected void renderHeader(int tableStartColumn, Set<String> userOrGroups, Set<String> permissions) {
163        renderHeaderPicture();
164        renderHeaderUsers(tableStartColumn, userOrGroups, permissions);
165        renderHeaderAcl(userOrGroups, permissions);
166        formatHeaderRowHeight();
167    }
168
169    protected void renderHeaderPicture() {
170        // excel.mergeRange(USERS_ROW, 0, PERMISSIONS_ROW, tableStartColumn-1);
171        excel.setPicture(logoPictureId, 0, 0, false);
172    }
173
174    protected void renderHeaderUsers(int tableStartColumn, Set<String> userOrGroups, Set<String> permissions) {
175
176        int userColumn = tableStartColumn;
177        for (String user : userOrGroups) {
178            // render the user column header
179            excel.setCell(USERS_ROW, userColumn, user, userHeaderStyle);
180            layout.setUserColumn(userColumn, user);
181
182            // merge cells indicating user name
183            final int from = userColumn;
184            final int to = userColumn + permissions.size() - 1;
185            if (from < ExcelBuilder.LAST_COLUMN && to < ExcelBuilder.LAST_COLUMN)
186                excel.mergeRange(USERS_ROW, from, USERS_ROW, to);
187
188            userColumn += permissions.size();
189            log.debug("user header: " + USERS_ROW + "," + userColumn + " > " + user);
190        }
191    }
192
193    protected void renderHeaderAcl(Set<String> userOrGroups, Set<String> permissions) {
194        for (String user : userOrGroups) {
195            // render ACL column header for this user
196            int userColumn;
197            int aclColumn = 0;
198            int aclHeaderColumn = 0;
199            String aclHeaderText;
200            String aclHeaderShort;
201
202            for (String permission : permissions) {
203                userColumn = layout.getUserColumn(user);
204                aclHeaderColumn = userColumn + aclColumn;
205                aclHeaderText = permission;// formatPermission(permission);
206                aclHeaderShort = formatPermission(permission);
207
208                Cell cell = excel.setCell(PERMISSIONS_ROW, aclHeaderColumn, aclHeaderShort, aclHeaderStyle);
209                excel.setColumnWidth(aclHeaderColumn, (int) (layoutSettings.aclColumnWidth * CELL_WIDTH_UNIT));
210
211                // add a comment with the acl complete name
212                if ((aclHeaderColumn + layoutSettings.aclHeaderCommentColSpan) < ExcelBuilder.LAST_COLUMN)
213                    excel.addComment(cell, aclHeaderText, PERMISSIONS_ROW, aclHeaderColumn,
214                            layoutSettings.aclHeaderCommentColSpan, layoutSettings.aclHeaderCommentRowSpan);
215
216                layout.setUserAclColumn(aclHeaderColumn, Pair.of(user, permission));
217                aclColumn++;
218
219                log.debug("permission header: " + PERMISSIONS_ROW + "," + aclHeaderColumn + " > "
220                        + formatPermission(permission));
221            }
222        }
223    }
224
225    protected void formatHeaderRowHeight() {
226        if (layoutSettings.aclHeaderHeight != -1)
227            excel.setRowHeight(PERMISSIONS_ROW, layoutSettings.aclHeaderHeight);
228        if (layoutSettings.userHeaderHeight != -1)
229            excel.setRowHeight(USERS_ROW, layoutSettings.userHeaderHeight);
230    }
231
232    /* FILE TREE AND MATRIX CONTENT RENDERING */
233
234    @Override
235    protected void renderAcl(Multimap<String, Pair<String, Boolean>> userAcls) {
236        for (String user : userAcls.keySet()) {
237            List<Pair<String, Boolean>> acls = new ArrayList<>(userAcls.get(user));
238            int last = acls.size() - 1;
239
240            // TODO: IF ACLS not contain an ACL that should be first or last,
241            // thus showing border, post draw white cells
242
243            for (int i = 0; i < acls.size(); i++) {
244                boolean isFirst = false;// (i == 0);
245                boolean isLast = false;// (i == last);
246
247                Pair<String, Boolean> ace = acls.get(i);
248                String permission = ace.a;
249                boolean accept = ace.b;
250                int aclColumn = layout.getUserAclColumn(Pair.of(user, permission));
251                String aceText = "";// formatAce(ace)
252
253                if (accept) {
254                    // draws an accept cell
255                    renderAcceptCell(isFirst, isLast, aclColumn, aceText);
256                } else {
257                    // draws a deny cell
258                    renderDenyCell(isFirst, isLast, aclColumn, aceText);
259                }
260            }
261            // String info = formatAcl(userAcls.get(user));
262        }
263    }
264
265    /**
266     * Render a cell with a 'deny' color with left, right or no border according to its position.
267     */
268    protected void renderDenyCell(boolean isFirst, boolean isLast, int aclColumn, String aceText) {
269        if (isFirst) {
270            excel.setCell(treeLineCursor, aclColumn, aceText, denyStyleLeft);
271        } else if (isLast) {
272            excel.setCell(treeLineCursor, aclColumn, aceText, denyStyleRight);
273        } else {
274            excel.setCell(treeLineCursor, aclColumn, aceText, denyStyle);
275        }
276    }
277
278    /**
279     * Render a cell with a 'accept' color with left, right or no border according to its position.
280     */
281    protected void renderAcceptCell(boolean isFirst, boolean isLast, int aclColumn, String aceText) {
282        if (isFirst) {
283            excel.setCell(treeLineCursor, aclColumn, aceText, acceptStyleLeft);
284        } else if (isLast) {
285            excel.setCell(treeLineCursor, aclColumn, aceText, acceptStyleRight);
286        } else {
287            excel.setCell(treeLineCursor, aclColumn, aceText, acceptStyle);
288        }
289    }
290}