1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.onehippo.forge.exdocpicker.impl.field.tree;
17
18 import java.io.Serializable;
19 import java.util.ArrayList;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Set;
23
24 import org.apache.commons.lang3.StringUtils;
25 import org.apache.wicket.AttributeModifier;
26 import org.apache.wicket.Component;
27 import org.apache.wicket.ajax.AjaxRequestTarget;
28 import org.apache.wicket.ajax.markup.html.form.AjaxButton;
29 import org.apache.wicket.behavior.Behavior;
30 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
31 import org.apache.wicket.extensions.markup.html.repeater.data.table.NoRecordsToolbar;
32 import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree;
33 import org.apache.wicket.extensions.markup.html.repeater.tree.TableTree;
34 import org.apache.wicket.extensions.markup.html.repeater.tree.content.CheckedFolder;
35 import org.apache.wicket.extensions.markup.html.repeater.tree.table.TreeColumn;
36 import org.apache.wicket.extensions.markup.html.repeater.tree.theme.HumanTheme;
37 import org.apache.wicket.extensions.markup.html.repeater.tree.theme.WindowsTheme;
38 import org.apache.wicket.markup.ComponentTag;
39 import org.apache.wicket.markup.head.CssHeaderItem;
40 import org.apache.wicket.markup.head.IHeaderResponse;
41 import org.apache.wicket.markup.repeater.Item;
42 import org.apache.wicket.markup.repeater.OddEvenItem;
43 import org.apache.wicket.model.IModel;
44 import org.apache.wicket.model.Model;
45 import org.apache.wicket.request.resource.PackageResourceReference;
46 import org.onehippo.forge.exdocpicker.api.ExternalDocumentCollection;
47 import org.onehippo.forge.exdocpicker.api.ExternalDocumentServiceContext;
48 import org.onehippo.forge.exdocpicker.api.ExternalDocumentServiceFacade;
49 import org.onehippo.forge.exdocpicker.api.PluginConstants;
50 import org.onehippo.forge.exdocpicker.impl.field.AbstractExternalDocumentFieldBrowserDialog;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54
55
56
57
58 public class ExternalTreeItemFieldBrowserDialog extends AbstractExternalDocumentFieldBrowserDialog {
59
60 private static final long serialVersionUID = 1L;
61
62 private static final Logger log = LoggerFactory.getLogger(ExternalTreeItemFieldBrowserDialog.class);
63
64
65
66
67 private ExternalTreeItemDataProvider<Serializable> treeDataProvider;
68
69
70
71
72 private TreeItemExpansionSet treeExpansionSet;
73
74
75
76
77 private final Behavior theme;
78
79
80
81
82
83
84
85
86 public ExternalTreeItemFieldBrowserDialog(IModel<String> titleModel,
87 final ExternalDocumentServiceContext extDocServiceContext,
88 final ExternalDocumentServiceFacade<Serializable> exdocService,
89 IModel<ExternalDocumentCollection<Serializable>> model) {
90 super(titleModel, extDocServiceContext, exdocService, model);
91
92 final String themeName = extDocServiceContext.getPluginConfig().getString(
93 PluginConstants.PARAM_EXTERNAL_TREE_VIEW_THEME, PluginConstants.DEFAULT_EXTERNAL_TREE_VIEW_THEME);
94
95 if (StringUtils.startsWithIgnoreCase(themeName, "window")) {
96 theme = new WindowsTheme();
97 } else {
98 theme = new HumanTheme();
99 }
100
101 AjaxButton expandSelectedButton = new AjaxButton("expand-selected-button") {
102 @Override
103 protected void onSubmit(AjaxRequestTarget target) {
104 expandPickedExternalTreeItemNodes(getExternalDocumentServiceFacade());
105 target.add(ExternalTreeItemFieldBrowserDialog.this);
106 }
107 };
108 add(expandSelectedButton);
109
110 AjaxButton expandAllButton = new AjaxButton("expand-all-button") {
111 @Override
112 protected void onSubmit(AjaxRequestTarget target) {
113 treeExpansionSet.expandAll();
114 target.add(ExternalTreeItemFieldBrowserDialog.this);
115 }
116 };
117 add(expandAllButton);
118
119 AjaxButton collapseAllButton = new AjaxButton("collapse-all-button") {
120 @Override
121 protected void onSubmit(AjaxRequestTarget target) {
122 treeExpansionSet.collapseAll();
123 target.add(ExternalTreeItemFieldBrowserDialog.this);
124 }
125 };
126 add(collapseAllButton);
127 }
128
129
130
131
132 @Override
133 public void renderHead(IHeaderResponse response) {
134 super.renderHead(response);
135 response.render(
136 CssHeaderItem.forReference(new PackageResourceReference(ExternalTreeItemFieldBrowserDialog.class,
137 ExternalTreeItemFieldBrowserDialog.class.getSimpleName() + ".css")));
138 }
139
140
141
142
143 @Override
144 protected void initializeSearchedExternalDocuments() {
145 searchExternalTreeNodeItems();
146 }
147
148
149
150
151 @SuppressWarnings({ "unchecked", "rawtypes" })
152 @Override
153 protected void initializeDataListView() {
154 treeExpansionSet = new TreeItemExpansionSet();
155
156 final int initialExpandDepth = getExternalDocumentServiceContext().getPluginConfig()
157 .getAsInteger(PluginConstants.PARAM_INITIAL_TREE_EXPAND_DEPTH, 0);
158
159
160 if (initialExpandDepth > 0) {
161 for (Iterator<? extends Serializable> itemIt = getSearchedExternalDocuments().iterator(); itemIt
162 .hasNext();) {
163 expandExternalTreeItemNode(getExternalDocumentServiceFacade(), itemIt.next(), 0, initialExpandDepth);
164 }
165 }
166
167
168 expandPickedExternalTreeItemNodes(getExternalDocumentServiceFacade());
169
170 treeDataProvider = new ExternalTreeItemDataProvider<>(getSearchedExternalDocuments(),
171 getExternalDocumentServiceFacade());
172 AbstractTree<Serializable> treeDataView = createTree(new Model(treeExpansionSet));
173 treeDataView.setOutputMarkupId(true);
174
175 treeDataView.add(new Behavior() {
176 private static final long serialVersionUID = 1L;
177
178 @Override
179 public void onComponentTag(Component component, ComponentTag tag) {
180 theme.onComponentTag(component, tag);
181 }
182
183 @Override
184 public void renderHead(Component component, IHeaderResponse response) {
185 theme.renderHead(component, response);
186 }
187 });
188
189 add(treeDataView);
190 }
191
192
193
194
195 protected void searchExternalTreeNodeItems() {
196 try {
197 ExternalDocumentCollection<? extends Serializable> searchedDocs = getExternalDocumentServiceFacade()
198 .searchExternalDocuments(getExternalDocumentServiceContext(), "");
199
200 getSearchedExternalDocuments().clear();
201
202 if (searchedDocs != null && searchedDocs.getSize() > 0) {
203 for (Iterator<? extends Serializable> it = searchedDocs.iterator(); it.hasNext();) {
204 getSearchedExternalDocuments().add(it.next());
205 }
206 }
207 } catch (Exception e) {
208 log.error("Failed to execute search external tree items.", e);
209 }
210 }
211
212
213
214
215
216
217 protected AbstractTree<Serializable> createTree(IModel<Set<Serializable>> state) {
218 List<IColumn<Serializable, String>> columns = createColumns();
219
220 final TableTree<Serializable, String> tree = new TableTree<Serializable, String>("tree", columns,
221 treeDataProvider, Integer.MAX_VALUE, state) {
222 private static final long serialVersionUID = 1L;
223
224 @Override
225 protected Component newContentComponent(String id, IModel<Serializable> model) {
226 CheckableTreeNode treeNode = new CheckableTreeNode(id, this, model);
227 treeNode.add(new AttributeModifier("title", getExternalDocumentServiceFacade().getDocumentDescription(
228 getExternalDocumentServiceContext(), model.getObject(), getRequest().getLocale())));
229 return treeNode;
230 }
231
232 @Override
233 protected Item<Serializable> newRowItem(String id, int index, IModel<Serializable> model) {
234 return new OddEvenItem<>(id, index, model);
235 }
236 };
237
238 tree.getTable().addBottomToolbar(new NoRecordsToolbar(tree.getTable()));
239
240 return tree;
241 }
242
243 private List<IColumn<Serializable, String>> createColumns() {
244 List<IColumn<Serializable, String>> columns = new ArrayList<>();
245 columns.add(new TreeColumn<>(Model.of("Tree")));
246 return columns;
247 }
248
249 private void expandExternalTreeItemNode(ExternalDocumentServiceFacade<Serializable> extDocService,
250 Serializable item, int curDepth, int maxDepth) {
251 if (curDepth < maxDepth) {
252 treeExpansionSet.add(item);
253
254 if (extDocService.hasChildren(item)) {
255 for (Iterator<Serializable> childIt = extDocService.getChildren(item); childIt.hasNext();) {
256 expandExternalTreeItemNode(extDocService, childIt.next(), curDepth + 1, maxDepth);
257 }
258 }
259 }
260 }
261
262 private void expandPickedExternalTreeItemNodes(ExternalDocumentServiceFacade<Serializable> extDocService) {
263 for (Serializable item : getPickedExternalDocuments()) {
264 Serializable parent = extDocService.getParent(item);
265 while (parent != null) {
266 treeExpansionSet.add(parent);
267 parent = getExternalDocumentServiceFacade().getParent(parent);
268 }
269 }
270 }
271
272 private class CheckableTreeNode extends CheckedFolder<Serializable> {
273
274 private static final long serialVersionUID = 1L;
275
276 private final AbstractTree<Serializable> tree;
277
278 public CheckableTreeNode(String id, AbstractTree<Serializable> tree, IModel<Serializable> model) {
279 super(id, tree, model);
280 this.tree = tree;
281 }
282
283 @Override
284 protected IModel<?> newLabelModel(IModel<Serializable> model) {
285 return Model.of(getExternalDocumentServiceFacade().getDocumentTitle(getExternalDocumentServiceContext(),
286 model.getObject(), getRequest().getLocale()));
287 }
288
289 @Override
290 protected Component newCheckBox(String id, IModel<Serializable> model) {
291 Component checkbox = super.newCheckBox(id, model);
292 checkbox.setEnabled(getExternalDocumentServiceFacade()
293 .isDocumentSelectable(getExternalDocumentServiceContext(), model.getObject()));
294 return checkbox;
295 }
296
297 @Override
298 protected IModel<Boolean> newCheckBoxModel(final IModel<Serializable> model) {
299 return new IModel<Boolean>() {
300 private static final long serialVersionUID = 1L;
301
302 @Override
303 public Boolean getObject() {
304 return getPickedExternalDocuments().contains(model.getObject());
305 }
306
307 @Override
308 public void setObject(Boolean object) {
309 if (Boolean.TRUE.equals(object)) {
310 final boolean singleSelectionMode = isSingleSelectionMode();
311 if (singleSelectionMode) {
312 getPickedExternalDocuments().clear();
313 }
314 getPickedExternalDocuments().add(model.getObject());
315 } else {
316 getPickedExternalDocuments().remove(model.getObject());
317 }
318 }
319
320 @Override
321 public void detach() {
322 }
323 };
324 }
325
326 @Override
327 protected void onUpdate(AjaxRequestTarget target) {
328 Serializable foo = getModelObject();
329 Serializable parent = treeDataProvider.getParent(foo);
330
331 while (parent != null) {
332 foo = parent;
333 parent = treeDataProvider.getParent(parent);
334 }
335
336 tree.updateBranch(foo, target);
337 }
338 }
339 }