1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.wcm.wcm.ui.granite.components.pathfield;
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27
28 import javax.annotation.PostConstruct;
29 import javax.servlet.ServletException;
30
31 import org.apache.commons.lang3.Strings;
32 import org.apache.sling.api.SlingHttpServletRequest;
33 import org.apache.sling.api.SlingHttpServletResponse;
34 import org.apache.sling.api.resource.Resource;
35 import org.apache.sling.api.resource.ResourceResolver;
36 import org.apache.sling.api.resource.ValueMap;
37 import org.apache.sling.api.wrappers.ValueMapDecorator;
38 import org.apache.sling.api.wrappers.ValueMapUtil;
39 import org.apache.sling.models.annotations.Model;
40 import org.apache.sling.models.annotations.injectorspecific.SlingObject;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43 import org.osgi.annotation.versioning.ProviderType;
44
45 import com.adobe.granite.ui.components.ComponentHelper;
46 import com.adobe.granite.ui.components.Config;
47 import com.adobe.granite.ui.components.ExpressionHelper;
48 import com.adobe.granite.ui.components.ds.DataSource;
49
50 import io.wcm.wcm.ui.granite.pathfield.impl.util.DummyPageContext;
51 import io.wcm.wcm.ui.granite.resource.GraniteUiSyntheticResource;
52
53
54
55
56
57 @Model(adaptables = SlingHttpServletRequest.class)
58 @ProviderType
59 public final class ColumnView {
60
61 private static final String FALLBACK_ROOT_RESOURCE = "/";
62 private static final long DEFAULT_PAGINATION_LIMIT = 1000;
63
64 private static final String NN_DATASOURCE = "datasource";
65 private static final String PN_SIZE = "size";
66 private static final String PN_LIMIT = "limit";
67 private static final String PN_ITEM_RESOURCE_TYPE = "itemResourceType";
68 private static final String PN_SHOW_ROOT = "showRoot";
69 private static final String PN_LOAD_ANCESTORS = "loadAncestors";
70 private static final String PN_PATH = "path";
71
72 @SlingObject
73 private Resource componentResource;
74 @SlingObject
75 private SlingHttpServletRequest request;
76 @SlingObject
77 private SlingHttpServletResponse response;
78 @SlingObject
79 private ResourceResolver resourceResolver;
80
81 private Resource currentResource;
82 private final List<Column> columns = new ArrayList<>();
83
84 @PostConstruct
85 @SuppressWarnings("null")
86 private void activate() {
87 ComponentHelper cmp = new ComponentHelper(new DummyPageContext(request, response));
88 Config cfg = cmp.getConfig();
89 ExpressionHelper ex = cmp.getExpressionHelper();
90
91 Integer size = ex.get(cfg.get(PN_SIZE, String.class), Integer.class);
92 Long limit = ex.get(cfg.get(PN_LIMIT, String.class), Long.class);
93 String itemResourceType = cfg.get(PN_ITEM_RESOURCE_TYPE, String.class);
94 boolean showRoot = cfg.get(PN_SHOW_ROOT, false);
95 boolean loadAncestors = cfg.get(PN_LOAD_ANCESTORS, false);
96
97
98 Resource rootResource = resourceResolver.getResource(ex.getString(cfg.get("rootPath", FALLBACK_ROOT_RESOURCE)));
99 if (rootResource == null) {
100 rootResource = resourceResolver.getResource(FALLBACK_ROOT_RESOURCE);
101 }
102
103
104 String path = ex.getString(cfg.get(PN_PATH, rootResource.getPath()));
105 currentResource = resourceResolver.getResource(path);
106 if (currentResource == null || !isSameResourceOrChild(rootResource, currentResource)) {
107 currentResource = rootResource;
108 }
109
110
111 if (showRoot && (Strings.CS.equals(currentResource.getPath(), rootResource.getPath()) || loadAncestors)) {
112 columns.add(getRootColumn(rootResource, itemResourceType));
113 }
114
115
116 if (loadAncestors) {
117 columns.addAll(getAncestorColumns(currentResource, rootResource));
118 }
119
120
121
122
123 long totalSize = DEFAULT_PAGINATION_LIMIT;
124 if (limit != null) {
125 totalSize = Math.max(totalSize, limit);
126 }
127 if (size != null) {
128 totalSize = Math.max(totalSize, size);
129 }
130
131
132 Long limitFromDataSource = null;
133 Resource datasourceResource = componentResource.getChild(NN_DATASOURCE);
134 if (datasourceResource != null) {
135 limitFromDataSource = ex.get(datasourceResource.getValueMap().get(PN_LIMIT, String.class), Long.class);
136 }
137
138 DataSource dataSource = null;
139 if (size != null && size >= 20 && datasourceResource != null && limitFromDataSource != null) {
140
141
142 long newLimit = limitFromDataSource + totalSize - size + 1;
143 dataSource = getDataSource(cmp, currentResource, datasourceResource, newLimit);
144 }
145 else {
146 dataSource = getDataSource(cmp, currentResource);
147 if (size != null) {
148 totalSize = size;
149 }
150 }
151
152
153 columns.add(getCurrentResourceColumn(dataSource, totalSize, currentResource, itemResourceType));
154 }
155
156 private boolean isSameResourceOrChild(Resource rootResource, Resource resource) {
157 if (Strings.CS.equals(rootResource.getPath(), resource.getPath())) {
158 return true;
159 }
160 else {
161 return Strings.CS.startsWith(resource.getPath(), rootResource.getPath() + "/");
162 }
163 }
164
165
166
167
168
169 public Resource getCurrentResource() {
170 return this.currentResource;
171 }
172
173
174
175
176
177 public List<Column> getColumns() {
178 return this.columns;
179 }
180
181
182
183
184
185
186
187 private DataSource getDataSource(ComponentHelper cmp, Resource resource) {
188 return getDataSource(cmp, resource, null, null);
189 }
190
191
192
193
194
195
196
197
198
199 @SuppressWarnings("java:S112")
200 private DataSource getDataSource(@NotNull ComponentHelper cmp, @NotNull Resource resource,
201 @Nullable Resource dataSourceResource, @Nullable Long newLimit) {
202 try {
203
204
205
206
207
208 ValueMap overwriteProperties = new ValueMapDecorator(Map.of(PN_PATH, resource.getPath()));
209 Resource resourceWrapper = GraniteUiSyntheticResource.wrapMerge(componentResource, overwriteProperties);
210
211 if (dataSourceResource != null && newLimit != null) {
212
213 ValueMap overwriteDataSourceProperties = new ValueMapDecorator(Map.of(PN_LIMIT, newLimit));
214 Resource dataSourceResourceWrapper = GraniteUiSyntheticResource.child(resourceWrapper, NN_DATASOURCE,
215 dataSourceResource.getResourceType(),
216 ValueMapUtil.merge(overwriteDataSourceProperties, dataSourceResource.getValueMap()));
217 return cmp.asDataSource(dataSourceResourceWrapper, resourceWrapper);
218 }
219 else {
220 return cmp.getItemDataSource(resourceWrapper);
221 }
222 }
223 catch (ServletException | IOException ex) {
224 throw new RuntimeException("Unable to get data source.", ex);
225 }
226 }
227
228
229
230
231
232
233
234
235
236 private static Column getCurrentResourceColumn(DataSource dataSource, long totalSize,
237 Resource currentResource, String itemResourceType) {
238
239 Iterator<Resource> items = dataSource.iterator();
240
241 boolean hasMore = false;
242 List<Resource> list = new ArrayList<>();
243 while (items.hasNext() && list.size() < totalSize) {
244 list.add(items.next());
245 }
246 hasMore = items.hasNext();
247 items = list.iterator();
248
249 Column column = new Column()
250 .isCurrentResource(true)
251 .columnId(currentResource.getPath())
252 .hasMore(hasMore)
253 .metaElement(true);
254 while (items.hasNext()) {
255 Resource item = items.next();
256 column.addItem(new ColumnItem(item)
257 .resourceType(itemResourceType));
258 }
259
260 return column;
261 }
262
263
264
265
266
267
268
269 private static Column getRootColumn(Resource rootResource, String itemResourceType) {
270
271
272
273
274
275
276
277
278 String columnId = "parentof:" + rootResource.getPath();
279
280 Column column = new Column()
281 .columnId(columnId)
282 .hasMore(false);
283 column.addItem(new ColumnItem(rootResource)
284 .resourceType(itemResourceType)
285 .active(true));
286 return column;
287 }
288
289
290
291
292
293
294
295 private static List<Column> getAncestorColumns(Resource currentResource, Resource rootResource) {
296 List<Column> columns = new ArrayList<>();
297 List<Resource> ancestors = getAncestors(currentResource, rootResource);
298 for (int i = 0; i < ancestors.size(); i++) {
299 Resource r = ancestors.get(i);
300
301 String activeId;
302 if (i < ancestors.size() - 1) {
303 activeId = ancestors.get(i + 1).getPath();
304 }
305 else {
306 activeId = currentResource.getPath();
307 }
308
309 Column column = new Column()
310 .columnId(r.getPath())
311 .lazy(true)
312 .activeId(activeId);
313 columns.add(column);
314 }
315 return columns;
316 }
317
318
319
320
321
322 private static List<Resource> getAncestors(Resource currentResource, Resource rootResource) {
323 List<Resource> results = new ArrayList<>();
324
325 if (currentResource == null || rootResource == null || Strings.CS.equals(currentResource.getPath(), rootResource.getPath())) {
326 return results;
327 }
328
329 Resource parent = currentResource.getParent();
330 while (parent != null) {
331 results.add(0, parent);
332 if (parent.getPath().equals(rootResource.getPath())) {
333 break;
334 }
335 parent = parent.getParent();
336 }
337 return results;
338 }
339
340 }