View Javadoc
1   /**
2    * Copyright 2014-2024 Bloomreach B.V. (<a href="http://www.bloomreach.com">http://www.bloomreach.com</a>)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.onehippo.forge.exdocpicker.impl.field;
17  
18  import java.io.Serializable;
19  import java.util.Iterator;
20  
21  import org.apache.commons.lang3.StringUtils;
22  import org.apache.wicket.AttributeModifier;
23  import org.apache.wicket.ajax.AjaxRequestTarget;
24  import org.apache.wicket.ajax.markup.html.form.AjaxButton;
25  import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
26  import org.apache.wicket.behavior.AttributeAppender;
27  import org.apache.wicket.markup.ComponentTag;
28  import org.apache.wicket.markup.MarkupStream;
29  import org.apache.wicket.markup.head.CssHeaderItem;
30  import org.apache.wicket.markup.head.IHeaderResponse;
31  import org.apache.wicket.markup.html.WebMarkupContainer;
32  import org.apache.wicket.markup.html.basic.Label;
33  import org.apache.wicket.markup.html.image.Image;
34  import org.apache.wicket.markup.repeater.Item;
35  import org.apache.wicket.markup.repeater.data.DataView;
36  import org.apache.wicket.markup.repeater.data.IDataProvider;
37  import org.apache.wicket.model.IModel;
38  import org.apache.wicket.model.Model;
39  import org.apache.wicket.request.resource.PackageResourceReference;
40  import org.apache.wicket.request.resource.ResourceReference;
41  import org.onehippo.forge.exdocpicker.api.ExternalDocumentCollection;
42  import org.onehippo.forge.exdocpicker.api.ExternalDocumentServiceContext;
43  import org.onehippo.forge.exdocpicker.api.ExternalDocumentServiceFacade;
44  import org.onehippo.forge.exdocpicker.api.PluginConstants;
45  import org.onehippo.forge.exdocpicker.impl.SimpleExternalDocumentCollectionDataProvider;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  /**
50   * External document field picker dialog implementation with the default flat list view UI, providing a basic search
51   * capability and select-all / clear-all buttons.
52   */
53  public class ExternalDocumentFieldBrowserDialog extends AbstractExternalDocumentFieldBrowserDialog {
54  
55      private static final long serialVersionUID = 1L;
56      private static final Logger log = LoggerFactory.getLogger(ExternalDocumentFieldBrowserDialog.class);
57  
58      private String searchQuery;
59  
60      /**
61       * Constructs external document(s) picker dialog.
62       * @param titleModel title model
63       * @param extDocServiceContext {@link ExternalDocumentServiceContext} instance
64       * @param exdocService {@link ExternalDocumentServiceFacade} instance
65       * @param model the model containing the currently selected external documents in the document node data
66       */
67      public ExternalDocumentFieldBrowserDialog(IModel<String> titleModel,
68              final ExternalDocumentServiceContext extDocServiceContext,
69              final ExternalDocumentServiceFacade<Serializable> exdocService,
70              IModel<ExternalDocumentCollection<Serializable>> model) {
71          super(titleModel, extDocServiceContext, exdocService, model);
72  
73          AjaxButton selectAllButton = new AjaxButton("select-all-button") {
74  
75              @Override
76              protected void onSubmit(AjaxRequestTarget target) {
77                  getPickedExternalDocuments().clear();
78                  for (Iterator<? extends Serializable> it = getSearchedExternalDocuments().iterator(); it.hasNext(); ) {
79                      getPickedExternalDocuments().add(it.next());
80                  }
81                  target.add(ExternalDocumentFieldBrowserDialog.this);
82              }
83          };
84          selectAllButton.setEnabled(false);
85          add(selectAllButton);
86  
87          AjaxButton clearAllButton = new AjaxButton("clear-all-button") {
88              @Override
89              protected void onSubmit(AjaxRequestTarget target) {
90                  getPickedExternalDocuments().clear();
91                  target.add(ExternalDocumentFieldBrowserDialog.this);
92              }
93          };
94          clearAllButton.setEnabled(false);
95          add(clearAllButton);
96  
97          if (isSingleSelectionMode()) {
98              selectAllButton.setVisible(false);
99              clearAllButton.setVisible(false);
100         }
101 
102         if (getSearchedExternalDocuments().getSize() > 0) {
103             selectAllButton.setEnabled(true);
104             clearAllButton.setEnabled(true);
105         } else {
106             selectAllButton.setEnabled(false);
107             clearAllButton.setEnabled(false);
108         }
109     }
110 
111     /**
112      * {@inheritDoc}
113      */
114     @Override
115     public void renderHead(IHeaderResponse response) {
116         super.renderHead(response);
117         response.render(
118                 CssHeaderItem.forReference(new PackageResourceReference(ExternalDocumentFieldBrowserDialog.class,
119                         ExternalDocumentFieldBrowserDialog.class.getSimpleName() + ".css")));
120     }
121 
122     /**
123      * Returns the search term.
124      * @return the search term
125      */
126     public String getSearchQuery() {
127         return searchQuery;
128     }
129 
130     /**
131      * Sets the search term.
132      * @param searchQuery the search term
133      */
134     public void setSearchQuery(String searchQuery) {
135         this.searchQuery = searchQuery;
136     }
137 
138     /**
139      * {@inheritDoc}
140      */
141     @Override
142     protected void initializeSearchedExternalDocuments() {
143         boolean initialSearchEnabled = getPluginConfig().getAsBoolean(PluginConstants.PARAM_INITIAL_SEARCH_ENABLED,
144                 PluginConstants.DEFAULT_INITIAL_SEARCH_ENABLED);
145         searchQuery = getPluginConfig().getString(PluginConstants.PARAM_INITIAL_SEARCH_QUERY, "");
146 
147         // initially search all
148         if (initialSearchEnabled) {
149             searchExternalDocumentsBySearchQuery();
150         }
151     }
152 
153     /**
154      * {@inheritDoc}
155      */
156     @Override
157     protected void initializeDataListView() {
158         final IDataProvider<Serializable> provider = new SimpleExternalDocumentCollectionDataProvider<>(
159                 getSearchedExternalDocuments());
160 
161         final DataView<Serializable> resultsDataView = new DataView<Serializable>("item", provider, getPageSize()) {
162             private static final long serialVersionUID = 1L;
163 
164             @Override
165             protected void populateItem(final Item<Serializable> listItem) {
166                 final ExternalDocumentServiceFacade<Serializable> exdocService = getExternalDocumentServiceFacade();
167                 final ExternalDocumentServiceContext exdocContext = getExternalDocumentServiceContext();
168 
169                 final Serializable doc = listItem.getModelObject();
170                 listItem.setOutputMarkupId(true);
171 
172                 AjaxCheckBox selectCheckbox = new AjaxCheckBox("select-button", new Model<>()) {
173                     private static final long serialVersionUID = 1L;
174 
175                     @Override
176                     protected void onUpdate(AjaxRequestTarget target) {
177                         if (getModelObject()) {
178                             final boolean singleSelectionMode = isSingleSelectionMode();
179                             if (singleSelectionMode) {
180                                 getPickedExternalDocuments().clear();
181                             }
182                             getPickedExternalDocuments().add(doc);
183                             if (singleSelectionMode) {
184                                 target.add(ExternalDocumentFieldBrowserDialog.this);
185                             }
186                         } else {
187                             getPickedExternalDocuments().remove(doc);
188                         }
189                     }
190                 };
191 
192                 selectCheckbox.getModel().setObject(getPickedExternalDocuments().contains(doc));
193                 selectCheckbox.setEnabled(exdocService.isDocumentSelectable(exdocContext, doc));
194 
195                 final String iconLink = exdocService.getDocumentIconLink(exdocContext, doc, getRequest().getLocale());
196                 final String iconStyle = exdocContext.getPluginConfig().getString(PluginConstants.PARAM_ICON_STYLE, null);
197 
198                 final ExternalDocumentIconImage iconImage = new ExternalDocumentIconImage("image", iconLink);
199 
200                 if (StringUtils.isNotBlank(iconStyle)) {
201                     iconImage.add(new AttributeModifier("style", iconStyle));
202                 }
203 
204                 listItem.add(iconImage);
205                 listItem.add(selectCheckbox);
206                 listItem.add(new Label("title-label",
207                         exdocService.getDocumentTitle(exdocContext, doc, getRequest().getLocale())));
208                 final String description = exdocService.getDocumentDescription(exdocContext, doc,
209                         getRequest().getLocale());
210 
211                 WebMarkupContainer frame = new WebMarkupContainer("paragraph-label") {
212                     private static final long serialVersionUID = 1L;
213 
214                     @Override
215                     public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) {
216                         replaceComponentTagBody(markupStream, openTag, StringUtils.defaultString(description));
217                     }
218                 };
219 
220                 listItem.add(frame);
221                 listItem.add(new AttributeAppender(
222                         "class",
223                         (IModel<String>) () -> ((listItem.getIndex() & 1) == 1) ? "even" : "odd", " ")
224                 );
225             }
226         };
227 
228         resultsDataView.setOutputMarkupId(true);
229 
230         add(resultsDataView);
231         add(new ExternalDocumentFieldBrowserPageNavigator("navigator", resultsDataView));
232     }
233 
234     /**
235      * Search external documents by the search term.
236      */
237     protected void searchExternalDocumentsBySearchQuery() {
238         try {
239             ExternalDocumentCollection<? extends Serializable> searchedDocs = getExternalDocumentServiceFacade()
240                     .searchExternalDocuments(getExternalDocumentServiceContext(), getSearchQuery());
241 
242             getSearchedExternalDocuments().clear();
243 
244             if (searchedDocs != null && searchedDocs.getSize() > 0) {
245                 for (Iterator<? extends Serializable> it = searchedDocs.iterator(); it.hasNext();) {
246                     getSearchedExternalDocuments().add(it.next());
247                 }
248             }
249         } catch (Exception e) {
250             log.error("Failed to execute search external documents by the search query, '" + getSearchQuery() + "'.",
251                     e);
252         }
253     }
254 
255     /**
256      * Icon image for an external document.
257      */
258     public static class ExternalDocumentIconImage extends Image {
259 
260         private static final ResourceReference NO_ICON = new PackageResourceReference(
261                 ExternalDocumentFieldBrowserDialog.class, "no-icon.jpg");
262 
263         private static final long serialVersionUID = 1L;
264 
265         public ExternalDocumentIconImage(String id, String imageUrl) {
266             super(id);
267 
268             if (StringUtils.isBlank(imageUrl)) {
269                 this.setImageResourceReference(NO_ICON, null);
270             } else {
271                 add(new AttributeModifier("src", new Model<>(imageUrl)));
272             }
273         }
274     }
275 }