001 /**
002 * Copyright 2010 The Kuali Foundation Licensed under the
003 * Educational Community License, Version 2.0 (the "License"); you may
004 * not use this file except in compliance with the License. You may
005 * obtain a copy of the License at
006 *
007 * http://www.osedu.org/licenses/ECL-2.0
008 *
009 * Unless required by applicable law or agreed to in writing,
010 * software distributed under the License is distributed on an "AS IS"
011 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
012 * or implied. See the License for the specific language governing
013 * permissions and limitations under the License.
014 */
015
016 package org.kuali.student.common.ui.client.widgets.search;
017
018 import java.util.ArrayList;
019 import java.util.HashMap;
020 import java.util.LinkedHashMap;
021 import java.util.List;
022 import java.util.Map;
023
024 import org.kuali.student.common.assembly.data.LookupMetadata;
025 import org.kuali.student.common.assembly.data.LookupMetadata.Usage;
026 import org.kuali.student.common.assembly.data.LookupParamMetadata;
027 import org.kuali.student.common.assembly.data.Metadata;
028 import org.kuali.student.common.assembly.data.Metadata.WriteAccess;
029 import org.kuali.student.common.search.dto.SearchParam;
030 import org.kuali.student.common.search.dto.SearchRequest;
031 import org.kuali.student.common.ui.client.application.Application;
032 import org.kuali.student.common.ui.client.configurable.mvc.DefaultWidgetFactory;
033 import org.kuali.student.common.ui.client.mvc.Callback;
034 import org.kuali.student.common.ui.client.util.UtilConstants;
035 import org.kuali.student.common.ui.client.widgets.KSButton;
036 import org.kuali.student.common.ui.client.widgets.KSButtonAbstract.ButtonStyle;
037 import org.kuali.student.common.ui.client.widgets.KSDropDown;
038 import org.kuali.student.common.ui.client.widgets.KSLabel;
039 import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations;
040 import org.kuali.student.common.ui.client.widgets.buttongroups.ButtonEnumerations.ButtonEnum;
041 import org.kuali.student.common.ui.client.widgets.field.layout.button.ActionCancelGroup;
042 import org.kuali.student.common.ui.client.widgets.field.layout.button.ButtonGroup;
043 import org.kuali.student.common.ui.client.widgets.field.layout.element.FieldElement;
044 import org.kuali.student.common.ui.client.widgets.field.layout.layouts.FieldLayoutComponent;
045 import org.kuali.student.common.ui.client.widgets.layout.HorizontalBlockFlowPanel;
046 import org.kuali.student.common.ui.client.widgets.layout.VerticalFlowPanel;
047 import org.kuali.student.common.ui.client.widgets.list.KSSelectItemWidgetAbstract;
048 import org.kuali.student.common.ui.client.widgets.list.ListItems;
049 import org.kuali.student.common.ui.client.widgets.list.SelectionChangeEvent;
050 import org.kuali.student.common.ui.client.widgets.list.SelectionChangeHandler;
051 import org.kuali.student.common.ui.client.widgets.searchtable.ResultRow;
052
053 import com.google.gwt.core.client.GWT;
054 import com.google.gwt.event.dom.client.ClickEvent;
055 import com.google.gwt.event.dom.client.ClickHandler;
056 import com.google.gwt.event.dom.client.KeyCodes;
057 import com.google.gwt.event.dom.client.KeyDownEvent;
058 import com.google.gwt.event.dom.client.KeyDownHandler;
059 import com.google.gwt.event.shared.HandlerRegistration;
060 import com.google.gwt.user.client.Window;
061 import com.google.gwt.user.client.ui.Composite;
062 import com.google.gwt.user.client.ui.HTMLPanel;
063 import com.google.gwt.user.client.ui.HasText;
064 import com.google.gwt.user.client.ui.HasValue;
065 import com.google.gwt.user.client.ui.SimplePanel;
066 import com.google.gwt.user.client.ui.SuggestBox;
067 import com.google.gwt.user.client.ui.VerticalPanel;
068 import com.google.gwt.user.client.ui.Widget;
069
070 public class SearchPanel extends Composite{
071
072 //Layout configuration
073 private VerticalFlowPanel layout = new VerticalFlowPanel();
074 private SimplePanel searchSelectorPanel = new SimplePanel();
075 private VerticalFlowPanel resultsTablePanel = new VerticalFlowPanel();
076 private HorizontalBlockFlowPanel enteredCriteriaString = new HorizontalBlockFlowPanel();
077 private CollapsablePanel modifySearchPanel;
078 private String criteriaInstructions = getMessage("searchPanelEnterFields");
079 private KSLabel enteredCriteriaHeading = new KSLabel(getMessage("searchPanelCriteria"));
080 protected SearchResultsTable table;
081 private boolean isMultiSelect = true;
082
083 public static enum SearchStyle{ADVANCED, CUSTOM};
084 private ActionCancelGroup actionCancelButtons;
085
086 private String actionLabel = getMessage("search"); //set default action label
087 private boolean resultsSelected = false;
088 private boolean hasSearchParams = false; //indicates if there are any user supplied search parameters
089 //if false will auto default search and not display modify search link.
090
091 //Search data
092 private List<LookupMetadata> lookups = new ArrayList<LookupMetadata>();
093 private boolean multiSelect = false;
094 private boolean resultsShown = false;
095 private SearchParametersWidget activeSearchParametersWidget = null;
096 // uses "name" of the lookup metadata to lookup the widget that layouts the search UI
097 private Map<String, SearchParametersWidget> searchParameterWidgetMap = new HashMap<String, SearchParametersWidget>();
098 private List<SearchField> searchFields = new ArrayList<SearchField>();
099 private List<Callback<LookupMetadata>> lookupChangedCallbacks = new ArrayList<Callback<LookupMetadata>>();
100 private String selectedLookupName;
101 private List<Callback<List<SelectedResults>>> selectedCompleteCallbacks = new ArrayList<Callback<List<SelectedResults>>>();
102 private List<Callback<Boolean>> actionCompletedCallbacks = new ArrayList<Callback<Boolean>>();
103
104 private Callback<ButtonEnumerations.ButtonEnum> actionCancelCallback = new Callback<ButtonEnumerations.ButtonEnum>(){
105
106 @Override
107 public void exec(ButtonEnum result) {
108 if (result == ButtonEnumerations.SearchCancelEnum.SEARCH) {
109 table.removeContent();
110 getActionCompleteCallback().exec(true);
111 }
112 }
113 };
114
115 interface SearchParametersWidget {
116 public SearchRequest getSearchRequest();
117 public LookupMetadata getLookupMetadata();
118 public List<HasSearchParam> getSearchParams();
119 }
120
121 public SearchPanel(LookupMetadata meta){
122 lookups.add(meta);
123 this.initWidget(layout);
124 }
125
126 public SearchPanel(List<LookupMetadata> metas){
127 lookups = metas;
128 this.initWidget(layout);
129 }
130
131 @SuppressWarnings("unchecked")
132 public ButtonGroup getButtons(){
133 return actionCancelButtons;
134 }
135
136 public void setMutipleSelect(boolean isMultiSelect){
137 this.isMultiSelect = isMultiSelect;
138 }
139
140 public void setupButtons() {
141 if (actionCancelButtons != null) {
142 actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
143 actionCancelButtons.addCallback(actionCancelCallback);
144 }
145 }
146
147 public void setupSearch() {
148 resultsTablePanel.clear();
149 layout.clear();
150 resultsShown = false;
151 hasSearchParams = false;
152
153 //create search panel
154 Widget searchParamPanel;
155 if (lookups.size() == 1) {
156 searchParamPanel = createSearchParamPanel(lookups.get(0));
157 selectedLookupName = lookups.get(0).getName();
158 activeSearchParametersWidget = searchParameterWidgetMap.get(selectedLookupName);
159 } else {
160 LinkedHashMap<String, Widget> searches = new LinkedHashMap<String, Widget>();
161 LinkedHashMap<String, LookupMetadata> searchLookups = new LinkedHashMap<String, LookupMetadata>();
162 for(LookupMetadata lookup: lookups){
163 searches.put(lookup.getName(), createSearchParamPanel(lookup));
164 searchLookups.put(lookup.getName(), lookup);
165 }
166 selectedLookupName = lookups.get(0).getName();
167 // Sets the activeSearchParametersWidget to be the first search
168 activeSearchParametersWidget = searchParameterWidgetMap.get(selectedLookupName);
169 String actionLabel = (lookups.get(0) == null)? null : lookups.get(0)
170 .getWidgetOptionValue(LookupMetadata.WidgetOption.ADVANCED_LIGHTBOX_ACTION_LABEL);
171 setActionLabel(actionLabel);
172 searchParamPanel = new SwappablePanel(searches);
173 ((SwappablePanel)searchParamPanel).setSearchLookups(searchLookups);
174 ((SwappablePanel)searchParamPanel).addLookupChangedCallback(new Callback<LookupMetadata>() {
175 @Override
176 public void exec(LookupMetadata selectedLookup) {
177 activeSearchParametersWidget = searchParameterWidgetMap.get(selectedLookup.getName());
178 selectedLookupName = selectedLookup.getName();
179 if (lookupChangedCallbacks != null) {
180 for (Callback<LookupMetadata> callback : lookupChangedCallbacks) {
181 callback.exec(selectedLookup);
182 }
183 }
184 }
185 });
186 }
187 searchSelectorPanel.setWidget(searchParamPanel);
188 layout.add(searchSelectorPanel);
189
190 //Create layout for results screen
191
192 //Entered criteria section
193 if (hasSearchParams){
194 enteredCriteriaHeading.addStyleName("ks-form-module-single-line-margin");
195 enteredCriteriaHeading.addStyleName("KS-Advanced-Search-Search-Criteria-Label");
196 resultsTablePanel.add(enteredCriteriaHeading);
197 resultsTablePanel.add(enteredCriteriaString);
198 resultsTablePanel.setVisible(false);
199 }
200
201 //Search Results table
202 table = GWT.create(SearchResultsTable.class);
203 table.setMutipleSelect(isMultiSelect);
204 table.addStyleName("KS-Advanced-Search-Results-Table");
205 for (Callback<List<SelectedResults>> selectionCompleteCallback : selectedCompleteCallbacks) {
206 table.addSelectionCompleteCallback(selectionCompleteCallback);
207 }
208 resultsTablePanel.add(table);
209 layout.add(resultsTablePanel);
210
211 table.getMslabel().addClickHandler(new ClickHandler(){
212
213 @Override
214 public void onClick(ClickEvent event) {
215 if(modifySearchPanel.isOpen()){
216 modifySearchPanel.close();
217 }
218 else{
219 modifySearchPanel.open();
220 }
221
222 resultsTablePanel.setVisible(false);
223 resultsSelected = false;
224 actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
225 }
226 });
227
228 resultsSelected = false;
229 actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
230
231 //Execute the search and display the results of there are no user supplied search parameters defined
232 if (!hasSearchParams){
233 getActionCompleteCallback().exec(true);
234 }
235 }
236
237 private Widget createSearchParamPanel(LookupMetadata meta){
238 ParamListItems listItems = new ParamListItems(meta);
239 final AdvancedSearch advancedSearch = new AdvancedSearch(meta);
240 LinkPanel panel = new LinkPanel(SearchStyle.ADVANCED, advancedSearch);
241 searchParameterWidgetMap.put(meta.getName(), advancedSearch);
242
243 //check whether we need custom tab i.e. whether we have at least one parameter that should appear on custom tab
244 for(LookupParamMetadata metaParam: meta.getParams()){
245 if (metaParam.getUsage() != null && metaParam.getUsage() != Usage.DEFAULT){
246 hasSearchParams = true; //Only set to true if this only has user supplied params (i.e. params without default values)
247 }
248 if ((metaParam.getUsage() == Usage.CUSTOM) || (metaParam.getUsage() == Usage.ADVANCED_CUSTOM)) {
249
250 final CustomizedSearch customizedSearch = new CustomizedSearch(meta, listItems);
251 KSButton button = panel.addLinkToPanel(SearchStyle.ADVANCED, getMessage("searchPanelCustomizeSearch"), SearchStyle.CUSTOM);
252 button.addClickHandler(new ClickHandler(){
253
254 @Override
255 public void onClick(ClickEvent event) {
256 resultsTablePanel.setVisible(false);
257 activeSearchParametersWidget = customizedSearch;
258 }});
259 button.addStyleName("KS-Advanced-Search-Link");
260 button.getParent().addStyleName("clearfix");
261 panel.addPanel(SearchStyle.CUSTOM, customizedSearch);
262 button = panel.addLinkToPanel(SearchStyle.CUSTOM, getMessage("searchPanelReturnToAdvancedSearch"), SearchStyle.ADVANCED);
263 button.addClickHandler(new ClickHandler(){
264
265 @Override
266 public void onClick(ClickEvent event) {
267 resultsTablePanel.setVisible(false);
268 activeSearchParametersWidget = advancedSearch;
269 }});
270 button.addStyleName("KS-Advanced-Search-Link");
271 button.getParent().addStyleName("clearfix");
272 break;
273 }
274 }
275
276 return panel;
277 }
278
279 private class CustomizedSearch extends Composite implements SearchParametersWidget {
280
281 private List<CustomLine> lines = new ArrayList<CustomLine>();
282 private List<HasSearchParam> searchParams = new ArrayList<HasSearchParam>();
283 private VerticalPanel layout = new VerticalPanel();
284 private VerticalPanel linePanel = new VerticalPanel();
285 private LookupMetadata meta;
286
287 public CustomizedSearch(final LookupMetadata meta, final ParamListItems listItems){
288
289 KSLabel instrLabel = new KSLabel(criteriaInstructions);
290 layout.add(instrLabel);
291
292 layout.add(linePanel);
293 CustomLine line = new CustomLine(meta, listItems);
294 line.addStyleName("ks-form-module-single-line-margin");
295 linePanel.add(line);
296 lines.add(line);
297 searchParams.add(line);
298 this.meta = meta;
299
300 KSButton addCriteria = new KSButton(getMessage("searchPanelAddCriteria"), ButtonStyle.SECONDARY);
301 addCriteria.addClickHandler(new ClickHandler(){
302
303 @Override
304 public void onClick(ClickEvent event) {
305 CustomLine line = new CustomLine(meta, listItems);
306 line.addStyleName("ks-form-module-single-line-margin");
307 linePanel.add(line);
308 lines.add(line);
309 searchParams.add(line);
310 }
311 });
312
313 addCriteria.addStyleName("ks-form-module-single-line-margin");
314 layout.add(addCriteria);
315
316 this.initWidget(layout);
317 }
318
319 public LookupMetadata getLookupMetadata() {
320 return meta;
321 }
322
323 @Override
324 public SearchRequest getSearchRequest() {
325 //Create search request and then pass it to the table
326 //TODO pass search to the table
327 SearchRequest sr = new SearchRequest();
328 List<SearchParam> params = new ArrayList<SearchParam>();
329 for(CustomLine field: lines){
330 SearchParam param = field.getSearchParam();
331 //TODO is this check needed here? probably. assuming string here
332 if((param.getValue() != null)){
333 params.add(param);
334 }
335 }
336
337 //add search criteria widgets to the custom tab
338 for(LookupParamMetadata metaParam: meta.getParams()){
339
340 //select only parameters shown on custom search tab
341 if ((metaParam.getUsage() != Usage.CUSTOM) && (metaParam.getUsage() != Usage.ADVANCED_CUSTOM)) {
342 continue;
343 }
344
345 if(metaParam.getWriteAccess() == WriteAccess.NEVER){
346 SearchParam param = new SearchParam();
347 param.setKey(metaParam.getKey());
348 if(metaParam.getDefaultValueList()==null){
349 param.setValue(metaParam.getDefaultValueString());
350 }else{
351 param.setValue(metaParam.getDefaultValueList());
352 }
353 params.add(param);
354 }
355 else if(metaParam.getWriteAccess() == WriteAccess.WHEN_NULL){
356 if((metaParam.getDefaultValueString() != null && !metaParam.getDefaultValueString().isEmpty())||
357 (metaParam.getDefaultValueList() != null && !metaParam.getDefaultValueList().isEmpty())){
358 SearchParam param = new SearchParam();
359 param.setKey(metaParam.getKey());
360 if(metaParam.getDefaultValueList()==null){
361 param.setValue(metaParam.getDefaultValueString());
362 }else{
363 param.setValue(metaParam.getDefaultValueList());
364 }
365 params.add(param);
366 }
367 }
368 }
369
370 sr.setParams(params);
371 sr.setSearchKey(meta.getSearchTypeId());
372 return sr;
373 }
374
375 @Override
376 public List<HasSearchParam> getSearchParams() {
377 return searchParams;
378 }
379
380 }
381
382 private interface HasSearchParam{
383 public SearchParam getSearchParam();
384 public String getFieldName();
385 public String getSearchText();
386 }
387
388 private static class CustomLine extends Composite implements HasSearchParam{
389 private KSDropDown paramSelector = new KSDropDown();
390 private SimplePanel widgetPanel = new SimplePanel();
391 private Widget widget = null;
392 private String key;
393 private HorizontalBlockFlowPanel layout = new HorizontalBlockFlowPanel();
394 private ParamListItems listItems;
395
396 public CustomLine(LookupMetadata meta, final ParamListItems listItems){
397
398 List<LookupParamMetadata> customParams = new ArrayList<LookupParamMetadata>();
399
400 for (LookupParamMetadata lookupParamMetadata : listItems.getParams()) {
401 if (lookupParamMetadata.getWriteAccess() != WriteAccess.NEVER){
402 if (lookupParamMetadata.getUsage() == Usage.CUSTOM || lookupParamMetadata.getUsage() == Usage.ADVANCED_CUSTOM ) {
403 customParams.add(lookupParamMetadata);
404 }
405 }
406 }
407
408 for(LookupParamMetadata param:customParams){
409 String id = param.getKey()+"-name";
410 if(Application.getApplicationContext().getMessage(id)!=null)
411 {
412 param.setName(Application.getApplicationContext().getMessage(id));
413 }
414 }
415
416 ParamListItems customParamList = new ParamListItems(customParams);
417
418 this.listItems = customParamList;
419 paramSelector.setBlankFirstItem(false);
420 paramSelector.setListItems(customParamList);
421
422 String id = meta.getParams().get(0).getKey();
423 paramSelector.selectItem(id);
424 widget = listItems.getWidget(id);
425 key = id;
426 widgetPanel.setWidget(widget);
427 paramSelector.addSelectionChangeHandler(new SelectionChangeHandler(){
428
429 @Override
430 public void onSelectionChange(SelectionChangeEvent event) {
431 String id = ((KSSelectItemWidgetAbstract)event.getWidget()).getSelectedItem();
432 widget = listItems.getWidget(id);
433 widgetPanel.setWidget(widget);
434 key = id;
435
436 }
437 });
438 layout.add(paramSelector);
439 layout.add(widgetPanel);
440 this.initWidget(layout);
441 }
442
443 public SearchParam getSearchParam(){
444 return SearchPanel.getSearchParam(widget, key);
445 }
446
447 public String getKey(){
448 return key;
449 }
450
451 public String getFieldName(){
452 String id = paramSelector.getSelectedItem();
453 return listItems.getItemText(id);
454 }
455
456 @Override
457 public String getSearchText() {
458 return SearchPanel.getSearchText(widget);
459 }
460 }
461
462 private class AdvancedSearch extends Composite implements SearchParametersWidget {
463 private LookupMetadata meta;
464 private List<HasSearchParam> searchParams = new ArrayList<HasSearchParam>();
465
466 public AdvancedSearch(final LookupMetadata meta){
467 VerticalPanel panel = new VerticalPanel();
468
469 KSLabel instrLabel = new KSLabel();
470 panel.add(instrLabel);
471 this.meta = meta;
472
473 //add widget for each search criteria to the advanced tab
474 boolean allFieldsRequired = true;
475 for(LookupParamMetadata param: meta.getParams()){
476
477 //select only parameters shown on advanced search tab
478 if ((param.getUsage() != Usage.ADVANCED) && (param.getUsage() != Usage.ADVANCED_CUSTOM)) {
479 continue;
480 }
481
482 if ((param.getWriteAccess() == WriteAccess.ALWAYS) || (param.getWriteAccess() == WriteAccess.REQUIRED)){
483 SearchField paramField = new SearchField(param);
484 searchFields.add(paramField);
485 panel.add(paramField);
486 searchParams.add(paramField);
487 }
488 else if (param.getWriteAccess() == WriteAccess.WHEN_NULL){
489 if(param.getDefaultValueString() == null && param.getDefaultValueList() == null){
490 SearchField paramField = new SearchField(param);
491 searchFields.add(paramField);
492 panel.add(paramField);
493 searchParams.add(paramField);
494 }
495 }
496
497 if (param.getWriteAccess() != Metadata.WriteAccess.REQUIRED) {
498 allFieldsRequired = false;
499 }
500 }
501
502 //do not show criteria instructions if we have only one criteria field or in case all fields are required
503 if ((searchFields.size() > 1) || (allFieldsRequired == false)) {
504 instrLabel.setText(criteriaInstructions);
505 }
506 this.addKeyDownHandler(downHandler);
507 this.initWidget(panel);
508 }
509
510 public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
511 return addDomHandler(handler, KeyDownEvent.getType());
512 }
513
514 public LookupMetadata getLookupMetadata() {
515 return meta;
516 }
517
518 private KeyDownHandler downHandler = new KeyDownHandler(){
519
520 @Override
521 public void onKeyDown(KeyDownEvent event) {
522 if(event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) // Enter button
523 actionCancelCallback.exec(ButtonEnumerations.SearchCancelEnum.SEARCH);
524 }
525
526 };
527
528 @Override
529 public SearchRequest getSearchRequest() {
530 SearchRequest sr = new SearchRequest();
531 List<SearchParam> params = new ArrayList<SearchParam>();
532 List<HasSearchParam> searchParams = getSearchParams();
533
534 //initialize search parameters if user entered values into search criteria fields in UI
535
536 for(HasSearchParam field: searchParams){
537 SearchParam param = field.getSearchParam();
538 //TODO is this null check needed here? probably. assuming string here
539 //TODO make check more robust here/inserting params more robust
540 //do not pass to the search parameters that are empty
541 //FIXME hack - comparison to 'optional' - replace with check against 'optional' field and update related lookup metadata
542 if ((param.getValue() != null) && ((param.getValue().toString().trim().isEmpty() == false) || (param.getKey().toLowerCase().indexOf("optional") == -1))) {
543 params.add(param);
544 }
545 }
546
547 //initialize search parameters that are hidden from the UI because they are set to default context specific values
548 for(LookupParamMetadata metaParam: meta.getParams()){
549 if(metaParam.getWriteAccess() == WriteAccess.NEVER){
550 if ((metaParam.getDefaultValueString() == null || metaParam.getDefaultValueString().isEmpty())&&
551 (metaParam.getDefaultValueList() == null || metaParam.getDefaultValueList().isEmpty())) {
552 //FIXME throw an exception?
553 GWT.log("Key = " + metaParam.getKey() + " has write access NEVER but has no default value!", null);
554 continue;
555 }
556 SearchParam param = new SearchParam();
557 param.setKey(metaParam.getKey());
558 if(metaParam.getDefaultValueList()==null){
559 param.setValue(metaParam.getDefaultValueString());
560 }else{
561 param.setValue(metaParam.getDefaultValueList());
562 }
563 params.add(param);
564 }
565 else if(metaParam.getWriteAccess() == WriteAccess.WHEN_NULL){
566 if((metaParam.getDefaultValueString() != null && !metaParam.getDefaultValueString().isEmpty())||
567 (metaParam.getDefaultValueList() != null && !metaParam.getDefaultValueList().isEmpty())){
568 SearchParam param = new SearchParam();
569 param.setKey(metaParam.getKey());
570 if(metaParam.getDefaultValueList()==null){
571 param.setValue(metaParam.getDefaultValueString());
572 }else{
573 param.setValue(metaParam.getDefaultValueList());
574 }
575 params.add(param);
576 }
577 }
578 }
579 sr.setParams(params);
580 if (meta.getResultSortKey() != null) {
581 sr.setSortColumn(meta.getResultSortKey());
582 }
583 sr.setSearchKey(meta.getSearchTypeId());
584 return sr;
585 }
586
587 @Override
588 public List<HasSearchParam> getSearchParams() {
589 return searchParams;
590 }
591 }
592
593 private class SearchField extends Composite implements HasSearchParam{
594
595 private Widget widget = null;
596 private LookupParamMetadata meta = null;
597 private VerticalFlowPanel panel = new VerticalFlowPanel();
598 private String fieldName;
599
600 public SearchParam getSearchParam(){
601 return SearchPanel.getSearchParam(widget, meta.getKey());
602 }
603
604 public SearchField(LookupParamMetadata param){
605 meta = param;
606 //TODO use message call here
607 if(getMessage(param.getKey()+FieldLayoutComponent.NAME_MESSAGE_KEY)!=null)
608 fieldName = getMessage(param.getKey()+FieldLayoutComponent.NAME_MESSAGE_KEY);
609 else
610 fieldName = param.getName();
611
612 widget = DefaultWidgetFactory.getInstance().getWidget(param);
613 if(param.getDefaultValueString() != null){
614 //TODO Add handling of default value lists here
615 if(widget instanceof HasText){
616 ((HasText) widget).setText(param.getDefaultValueString().toString());
617 }
618 else if(widget instanceof HasValue){
619 ((HasValue) widget).setValue(param.getDefaultValueString());
620 }
621 }
622
623 //FIXME: remove because required field '*' indication will be part of FieldElement class
624 if (param.getWriteAccess() == Metadata.WriteAccess.REQUIRED) {
625 fieldName += " *";
626 }
627
628 FieldElement fieldElement = new FieldElement(fieldName, widget);
629 fieldElement.getTitleWidget().addStyleName("KS-Picker-Criteria-Text");
630 panel.add(fieldElement);
631 panel.addStyleName("clearfix");
632
633 this.initWidget(panel);
634 }
635
636 public Widget getFieldPanel(){
637 return panel;
638 }
639
640 public String getFieldName() {
641 return fieldName;
642 }
643
644 @Override
645 public String getSearchText() {
646 return SearchPanel.getSearchText(widget);
647 }
648 }
649
650 private static SearchParam getSearchParam(final Widget widget, String key){
651 SearchParam param = new SearchParam();
652 param.setKey(key);
653 if(widget instanceof HasText){
654 param.setValue(((HasText) widget).getText());
655 }
656 else if(widget instanceof HasValue){
657 Object value = ((HasValue) widget).getValue();
658 if(value != null){
659 //TODO need to handle date and other types here, how they are converted for search, etc
660 if(value instanceof String){
661 param.setValue((String)value);
662 }
663 else{
664 param.setValue(value.toString());
665 GWT.log("Fields in search probably(?) shouldnt have values other than string", null);
666 }
667 }
668 }
669 else if (widget instanceof KSPicker){
670 KSPicker pickerWidget = (KSPicker)widget;
671 String pickerValue = pickerWidget.getValue().toString();
672 if (UtilConstants.IMPOSSIBLE_CHARACTERS.equals(pickerValue)){
673 SuggestBox suggestBox = (SuggestBox)pickerWidget.getInputWidget();
674 pickerValue = suggestBox.getText();
675 }
676
677 param.setValue(pickerValue);
678 }
679 else {
680 param.setValue("");
681 }
682
683 return param;
684 }
685
686 private static String getSearchText(final Widget widget){
687 if(widget instanceof HasText){
688 return ((HasText) widget).getText();
689 }
690 else if(widget instanceof HasValue){
691 Object value = ((HasValue) widget).getValue();
692 if(value != null){
693 //TODO need to handle date and other types here, how they are converted for search, etc
694 if(value instanceof String){
695 return (String)value;
696 }
697 else{
698 GWT.log("Fields in search probably(?) shouldnt have values other than string", null);
699 return value.toString();
700 }
701 }
702 }
703 else if (widget instanceof KSPicker){
704 return ((KSPicker)widget).getDisplayValue();
705 }
706 return "";
707 }
708
709 private void showCriteriaChosen(List<HasSearchParam> fields){
710 enteredCriteriaString.clear();
711 boolean first = true;;
712 for(HasSearchParam field: fields){
713 String name = field.getFieldName();
714 String value = field.getSearchText();
715 if(!value.isEmpty()&&value.equals("$$##@@"))
716 value = field.getSearchParam().getValue().toString().toUpperCase();
717 if(!value.isEmpty()){
718 HTMLPanel label = new HTMLPanel(name + ": <b>" + value + "</b> ");
719 if (!first) {
720 label.addStyleName("KS-Advanced-Search-Search-Criteria-Text");
721 }
722 enteredCriteriaString.add(label);
723 first = false;
724 }
725 }
726 }
727
728 public List<String> getSelectedIds(){
729 List<String> ids = new ArrayList<String>();
730 if(table != null){
731 ids = table.getSelectedIds();
732 }
733 return ids;
734 }
735
736 public List<SelectedResults> getSelectedValues() {
737
738 List<SelectedResults> selectedValues = new ArrayList<SelectedResults>();
739 if (table != null) {
740 List<ResultRow> selectedRows = table.getSelectedRows();
741 for (ResultRow row : selectedRows) {
742 String displayKey = row.getValue(activeSearchParametersWidget.getLookupMetadata().getResultDisplayKey());
743 String returnKey = row.getValue(activeSearchParametersWidget.getLookupMetadata().getResultReturnKey());
744 selectedValues.add(new SelectedResults(displayKey, returnKey, row));
745 if (multiSelect == false) {
746 break;
747 }
748 }
749 }
750
751 return selectedValues;
752 }
753
754 public boolean isMultiSelect() {
755 return multiSelect;
756 }
757
758 public void setMultiSelect(boolean multiSelect) {
759 this.multiSelect = multiSelect;
760 }
761
762 private static class ParamListItems implements ListItems{
763
764 private List<LookupParamMetadata> params = new ArrayList<LookupParamMetadata>();
765
766 public ParamListItems(LookupMetadata meta){
767 params = meta.getParams();
768 }
769
770 public ParamListItems(List<LookupParamMetadata> params){
771 this.params = params;
772 }
773
774 @Override
775 public List<String> getAttrKeys() {
776 return new ArrayList<String>();
777 }
778
779 @Override
780 public String getItemAttribute(String id, String attrkey) {
781 return "";
782 }
783
784 @Override
785 public int getItemCount() {
786 return params.size();
787 }
788
789 @Override
790 public List<String> getItemIds() {
791 List<String> ids = new ArrayList<String>();
792 for(LookupParamMetadata param: params){
793 ids.add(param.getKey());
794 }
795 return ids;
796 }
797
798 @Override
799 public String getItemText(String id) {
800 String itemText = id;
801 for(LookupParamMetadata param: params){
802 if(param.getKey().equals(id)){
803 //TODO this should be a message key
804 itemText = param.getName();
805 break;
806 }
807 }
808 return itemText;
809 }
810
811 public Widget getWidget(String id){
812 Widget w = null;
813 for(LookupParamMetadata param: params){
814 if(param.getKey().equals(id)){
815 w = DefaultWidgetFactory.getInstance().getWidget(param);
816 break;
817 }
818 }
819 return w;
820 }
821
822 public List<LookupParamMetadata> getParams() {
823 return params;
824 }
825 }
826
827 private String getMessage(final String msgKey) {
828 return Application.getApplicationContext().getMessage(msgKey);
829 }
830
831 public void addLookupChangedCallback(Callback<LookupMetadata> callback) {
832 lookupChangedCallbacks.add(callback);
833 }
834
835 //FIXME: Is an action complete callback really necessary here, couldn't this simply be a method to perform the seach action.
836 public Callback<Boolean> getActionCompleteCallback() {
837 return new Callback<Boolean>() {
838
839 @Override
840 public void exec(Boolean result) {
841
842 if (resultsSelected == true) {
843 List<SelectedResults> selectedItems = getSelectedValues();
844 if (selectedItems.isEmpty())
845 Window.alert("Please, select a value");
846 else
847 {
848 for(Callback<List<SelectedResults>> callback: selectedCompleteCallbacks){
849 callback.exec(selectedItems);
850 }
851 return;
852 }
853 }
854
855 actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getMessage("select"));
856 resultsSelected = true;
857
858 SearchRequest sr = getSearchRequest();
859 table.performSearch(sr, activeSearchParametersWidget.getLookupMetadata().getResults(), activeSearchParametersWidget.getLookupMetadata().getResultReturnKey(), activeSearchParametersWidget.getLookupMetadata().getResultDisplayKey(), true);
860 resultsTablePanel.setVisible(true);
861 List<HasSearchParam> userCriteria = new ArrayList<HasSearchParam>();
862 List<HasSearchParam> searchParams = activeSearchParametersWidget.getSearchParams();
863
864 //initialize search parameters if user entered values into search criteria fields in UI
865 for(HasSearchParam field: searchParams){
866 SearchParam param = field.getSearchParam();
867 //TODO is this null check needed here? probably. assuming string here
868 //TODO make check more robust here/inserting params more robust
869 //do not pass to the search parameters that are empty
870 //FIXME hack - comparison to 'optional' - replace with check against 'optional' field and update related lookup metadata
871 if ((param.getValue() != null) && ((param.getValue().toString().trim().isEmpty() == false) || (param.getKey().toLowerCase().indexOf("optional") == -1))) {
872 userCriteria.add(field);
873 }
874 }
875 showCriteriaChosen(userCriteria);
876
877 if (hasSearchParams){
878 //Only display modify link if there are search parametes available.
879 if(!resultsShown){
880 searchSelectorPanel.removeFromParent();
881 modifySearchPanel = GWT.create(CollapsablePanel.class);
882 modifySearchPanel.initialise(getMessage("searchPanelModifySearch"), searchSelectorPanel, false);
883 modifySearchPanel.getLabel().addClickHandler(new ClickHandler(){
884 @Override
885 public void onClick(ClickEvent event) {
886 resultsTablePanel.setVisible(false);
887 actionCancelButtons.setButtonText(ButtonEnumerations.SearchCancelEnum.SEARCH, getActionLabel());
888 resultsSelected = false;
889 }});
890 SearchPanel.this.layout.insert(modifySearchPanel, 0);
891
892 }
893 else{
894 modifySearchPanel.close();
895 }
896 }
897 resultsShown = true;
898
899 for(Callback<Boolean> callback: actionCompletedCallbacks){
900 callback.exec( Boolean.valueOf(true));
901 }
902 }
903 };
904 }
905
906 public SearchRequest getSearchRequest() {
907 if (activeSearchParametersWidget != null) {
908 return activeSearchParametersWidget.getSearchRequest();
909 }
910 return null;
911 }
912
913 public void setActionCancelButtonGroup(ActionCancelGroup actionCancelButtons) {
914 this.actionCancelButtons = actionCancelButtons;
915 }
916
917 public String getSelectedLookupName() {
918 return selectedLookupName;
919 }
920
921 public void setSelectedLookupName(String selectedLookupName) {
922 this.selectedLookupName = selectedLookupName;
923 }
924
925 public void addSelectionCompleteCallback(Callback<List<SelectedResults>> callback){
926 selectedCompleteCallbacks.add(callback);
927 }
928
929 public void addActionCompleteCallback(Callback<Boolean> callback){
930 actionCompletedCallbacks.add(callback);
931 }
932
933 public String getActionLabel() {
934 return actionLabel;
935 }
936
937 public void setActionLabel(String actionLabel) {
938 if ((actionLabel != null) && (actionLabel.trim().length() > 0)) {
939 this.actionLabel = actionLabel;
940 }
941 }
942
943 }