001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ade.galleries.client.ui; 029 030import org.opencms.ade.galleries.client.CmsResultContextMenuHandler; 031import org.opencms.ade.galleries.client.CmsResultsTabHandler; 032import org.opencms.ade.galleries.client.I_CmsGalleryHandler; 033import org.opencms.ade.galleries.client.Messages; 034import org.opencms.ade.galleries.client.ui.css.I_CmsLayoutBundle; 035import org.opencms.ade.galleries.shared.CmsGalleryFolderBean; 036import org.opencms.ade.galleries.shared.CmsGallerySearchBean; 037import org.opencms.ade.galleries.shared.CmsGallerySearchScope; 038import org.opencms.ade.galleries.shared.CmsResultItemBean; 039import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.GalleryTabId; 040import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.SortParams; 041import org.opencms.ade.upload.client.ui.CmsDialogUploadButtonHandler; 042import org.opencms.gwt.client.CmsCoreProvider; 043import org.opencms.gwt.client.dnd.CmsDNDHandler; 044import org.opencms.gwt.client.rpc.CmsRpcAction; 045import org.opencms.gwt.client.ui.CmsList; 046import org.opencms.gwt.client.ui.CmsListItemWidget; 047import org.opencms.gwt.client.ui.CmsPushButton; 048import org.opencms.gwt.client.ui.I_CmsButton; 049import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle; 050import org.opencms.gwt.client.ui.I_CmsListItem; 051import org.opencms.gwt.client.ui.contextmenu.CmsContextMenuButton; 052import org.opencms.gwt.client.ui.contextmenu.CmsContextMenuHandler; 053import org.opencms.gwt.client.ui.externallink.CmsEditExternalLinkDialog; 054import org.opencms.gwt.client.ui.input.CmsSelectBox; 055import org.opencms.gwt.client.ui.input.CmsTextBox; 056import org.opencms.gwt.client.ui.input.upload.CmsUploadButton; 057import org.opencms.gwt.client.ui.input.upload.I_CmsUploadButtonHandler; 058import org.opencms.gwt.client.util.CmsDebugLog; 059import org.opencms.gwt.client.util.CmsDomUtil; 060import org.opencms.gwt.client.util.CmsEmbeddedDialogHandler; 061import org.opencms.gwt.shared.CmsCoreData.AdeContext; 062import org.opencms.gwt.shared.CmsGwtConstants; 063import org.opencms.util.CmsStringUtil; 064import org.opencms.util.CmsUUID; 065 066import java.util.ArrayList; 067import java.util.HashSet; 068import java.util.LinkedHashMap; 069import java.util.List; 070import java.util.Map; 071import java.util.Set; 072 073import com.google.common.collect.Lists; 074import com.google.gwt.core.client.GWT; 075import com.google.gwt.core.client.Scheduler; 076import com.google.gwt.core.client.Scheduler.ScheduledCommand; 077import com.google.gwt.dom.client.Style.Display; 078import com.google.gwt.dom.client.Style.Unit; 079import com.google.gwt.event.dom.client.ClickEvent; 080import com.google.gwt.event.dom.client.ClickHandler; 081import com.google.gwt.event.dom.client.DoubleClickEvent; 082import com.google.gwt.event.dom.client.DoubleClickHandler; 083import com.google.gwt.event.dom.client.KeyCodes; 084import com.google.gwt.event.dom.client.ScrollEvent; 085import com.google.gwt.event.dom.client.ScrollHandler; 086import com.google.gwt.event.logical.shared.OpenEvent; 087import com.google.gwt.event.logical.shared.OpenHandler; 088import com.google.gwt.event.logical.shared.ValueChangeEvent; 089import com.google.gwt.event.logical.shared.ValueChangeHandler; 090import com.google.gwt.uibinder.client.UiBinder; 091import com.google.gwt.uibinder.client.UiField; 092import com.google.gwt.uibinder.client.UiHandler; 093import com.google.gwt.uibinder.client.UiTemplate; 094import com.google.gwt.user.client.Timer; 095import com.google.gwt.user.client.ui.Composite; 096import com.google.gwt.user.client.ui.FlowPanel; 097import com.google.gwt.user.client.ui.HTML; 098import com.google.gwt.user.client.ui.ScrollPanel; 099import com.google.gwt.user.client.ui.Widget; 100 101/** 102 * Provides the widget for the results tab.<p> 103 * 104 * It displays the selected search parameter, the sort order and 105 * the search results for the current search. 106 * 107 * @since 8.0. 108 */ 109public class CmsResultsTab extends A_CmsListTab { 110 111 /** 112 * Click-handler for the delete button.<p> 113 */ 114 public class DeleteHandler implements ClickHandler { 115 116 /** The resource path of the selected item. */ 117 protected String m_resourcePath; 118 119 /** 120 * Constructor.<p> 121 * 122 * @param resourcePath the item resource path 123 */ 124 protected DeleteHandler(String resourcePath) { 125 126 m_resourcePath = resourcePath; 127 } 128 129 /** 130 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 131 */ 132 public void onClick(ClickEvent event) { 133 134 getTabHandler().deleteResource(m_resourcePath); 135 } 136 137 } 138 139 /** 140 * Enum representing different options for the results tab. 141 */ 142 public enum ParamType { 143 /** Search scope. */ 144 scope, 145 /** Query text. */ 146 text; 147 } 148 149 /** 150 * Bar containing the search scope selection and a text search field. 151 */ 152 public class SearchBar extends Composite { 153 154 /** The field for the text search. */ 155 @UiField 156 protected CmsTextBox m_searchInput; 157 158 /** The search button. */ 159 @UiField 160 protected CmsPushButton m_textSearchButton; 161 162 /** The select box for the search scope selection. */ 163 @UiField 164 protected CmsSelectBox m_scopeSelection; 165 166 /** 167 * Creates a new instance. 168 */ 169 public SearchBar() { 170 171 I_CmsSearchBarUiBinder uiBinder = GWT.create(I_CmsSearchBarUiBinder.class); 172 FlowPanel content = uiBinder.createAndBindUi(this); 173 initWidget(content); 174 m_searchInput.setGhostValue(Messages.get().key(Messages.GUI_QUICK_FINDER_SEARCH_0), true); 175 m_searchInput.setGhostModeClear(true); 176 m_textSearchButton.setButtonStyle(ButtonStyle.FONT_ICON, null); 177 m_textSearchButton.setImageClass(I_CmsButton.SEARCH_SMALL); 178 m_textSearchButton.setTitle(Messages.get().key(Messages.GUI_TAB_SEARCH_SEARCH_EXISTING_0)); 179 } 180 181 /** 182 * Gets the scope selection widget. 183 * 184 * @return the scope selection widget 185 */ 186 public CmsSelectBox getScopeSelection() { 187 188 return m_scopeSelection; 189 } 190 191 /** 192 * Gets the search button. 193 * 194 * @return the search button 195 */ 196 public CmsPushButton getSearchButton() { 197 198 return m_textSearchButton; 199 } 200 201 /** 202 * Gets the search input field. 203 * 204 * @return the search input field 205 */ 206 public CmsTextBox getSearchInput() { 207 208 return m_searchInput; 209 } 210 211 /** 212 * Handles the change event on the search scope select box.<p> 213 * 214 * @param event the change event 215 */ 216 @UiHandler("m_scopeSelection") 217 protected void onScopeChange(ValueChangeEvent<String> event) { 218 219 String value = event.getValue(); 220 m_tabHandler.setScope(CmsGallerySearchScope.valueOf(value)); 221 222 } 223 } 224 225 /** 226 * Scroll handler which executes an action when the user has scrolled to the bottom.<p> 227 * 228 * @since 8.0.0 229 */ 230 protected class CmsAsynchronousScrollToBottomHandler implements ScrollHandler { 231 232 /** 233 * If the lower edge of the content being scrolled is at most this many pixels below the lower 234 * edge of the scrolling viewport, the action is triggered. 235 */ 236 public static final int DEFAULT_SCROLL_THRESHOLD = 200; 237 238 /** 239 * Constructs a new scroll handler with a custom scroll threshold. 240 * 241 * The scroll threshold is the distance from the bottom edge of the scrolled content 242 * such that when the distance from the bottom edge of the scroll viewport to the bottom 243 * edge of the scrolled content becomes lower than the distance, the scroll action is triggered. 244 * 245 */ 246 public CmsAsynchronousScrollToBottomHandler() { 247 248 // noop 249 } 250 251 /** 252 * @see com.google.gwt.event.dom.client.ScrollHandler#onScroll(com.google.gwt.event.dom.client.ScrollEvent) 253 */ 254 public void onScroll(ScrollEvent event) { 255 256 if (!m_hasMoreResults || getTabHandler().isLoading()) { 257 return; 258 } 259 final ScrollPanel scrollPanel = (ScrollPanel)event.getSource(); 260 final int scrollPos = scrollPanel.getVerticalScrollPosition(); 261 Widget child = scrollPanel.getWidget(); 262 int childHeight = child.getOffsetHeight(); 263 int ownHeight = scrollPanel.getOffsetHeight(); 264 boolean isBottom = (scrollPos + ownHeight) >= (childHeight - DEFAULT_SCROLL_THRESHOLD); 265 if (isBottom) { 266 getTabHandler().onScrollToBottom(); 267 setScrollPosition(scrollPos); 268 } 269 } 270 } 271 272 /** 273 * Special click handler to use with preview button.<p> 274 */ 275 protected class PreviewHandler implements ClickHandler { 276 277 /** The resource path of the selected item. */ 278 private String m_resourcePath; 279 280 /** The resource type of the selected item. */ 281 private String m_resourceType; 282 283 /** 284 * Constructor.<p> 285 * 286 * @param resourcePath the item resource path 287 * @param resourceType the item resource type 288 */ 289 public PreviewHandler(String resourcePath, String resourceType) { 290 291 m_resourcePath = resourcePath; 292 m_resourceType = resourceType; 293 } 294 295 /** 296 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 297 */ 298 public void onClick(ClickEvent event) { 299 300 getTabHandler().openPreview(m_resourcePath, m_resourceType); 301 302 } 303 } 304 305 /** 306 * Special click handler to use with select button.<p> 307 */ 308 protected class SelectHandler implements ClickHandler, DoubleClickHandler { 309 310 /** The id of the selected item. */ 311 private String m_resourcePath; 312 313 /** The resource type of the selected item. */ 314 private String m_resourceType; 315 316 /** The structure id. */ 317 private CmsUUID m_structureId; 318 319 /** The resource title. */ 320 private String m_title; 321 322 /** 323 * Constructor.<p> 324 * 325 * @param resourcePath the item resource path 326 * @param structureId the structure id 327 * @param title the resource title 328 * @param resourceType the item resource type 329 */ 330 public SelectHandler(String resourcePath, CmsUUID structureId, String title, String resourceType) { 331 332 m_resourcePath = resourcePath; 333 m_structureId = structureId; 334 m_resourceType = resourceType; 335 m_title = title; 336 } 337 338 /** 339 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 340 */ 341 public void onClick(ClickEvent event) { 342 343 getTabHandler().selectResource(m_resourcePath, m_structureId, m_title, m_resourceType); 344 } 345 346 /** 347 * @see com.google.gwt.event.dom.client.DoubleClickHandler#onDoubleClick(com.google.gwt.event.dom.client.DoubleClickEvent) 348 */ 349 public void onDoubleClick(DoubleClickEvent event) { 350 351 getTabHandler().selectResource(m_resourcePath, m_structureId, m_title, m_resourceType); 352 } 353 } 354 355 /** 356 * UiBinder interface for the search bar. 357 */ 358 @UiTemplate("CmsResultsTabSearchBar.ui.xml") 359 interface I_CmsSearchBarUiBinder extends UiBinder<FlowPanel, SearchBar> { 360 // UiBinder 361 } 362 363 /** The big thumbnails view name. */ 364 static final String BIG = "big"; 365 366 /** The details view name. */ 367 static final String DETAILS = "details"; 368 369 /** The small thumbnails view name. */ 370 static final String SMALL = "small"; 371 372 /** The handler for scrolling to the top of the scroll panel. */ 373 protected CmsResultsBackwardsScrollHandler m_backwardScrollHandler = new CmsResultsBackwardsScrollHandler(this); 374 375 /** Stores the information if more results in the search object are available. */ 376 protected boolean m_hasMoreResults; 377 378 /** The result list item which corresponds to a preset value in the editor. */ 379 protected CmsResultListItem m_preset; 380 381 /** The gallery handler. */ 382 I_CmsGalleryHandler m_galleryHandler; 383 384 /** The context menu handler. */ 385 private CmsContextMenuHandler m_contextMenuHandler; 386 387 /** The optional dnd manager. */ 388 private CmsDNDHandler m_dndHandler; 389 390 /** A HTML widget for the message if nor search params were selected. */ 391 private HTML m_noParamsMessage; 392 393 /** The panel showing the search parameters. */ 394 private FlowPanel m_params; 395 396 /** The view select box. */ 397 private CmsSelectBox m_selectView; 398 399 /** The button to create new external link resources. */ 400 private CmsPushButton m_specialUploadButton; 401 402 /** The reference to the handler of this tab. */ 403 CmsResultsTabHandler m_tabHandler; 404 405 /** Set of resource types currently displayed in the result list. */ 406 private Set<String> m_types; 407 408 /** The upload button. */ 409 private CmsUploadButton m_uploadButton; 410 411 /** The search bar. */ 412 private SearchBar m_searchBar = new SearchBar(); 413 414 /** The default scope. */ 415 private CmsGallerySearchScope m_defaultScope; 416 417 /** 418 * The constructor.<p> 419 * 420 * @param tabHandler the tab handler 421 * @param dndHandler the dnd manager 422 * @param galleryHandler the gallery handler 423 * @param scope the initial scope 424 * @param defaultScope the default scope 425 **/ 426 public CmsResultsTab( 427 CmsResultsTabHandler tabHandler, 428 CmsDNDHandler dndHandler, 429 I_CmsGalleryHandler galleryHandler, 430 CmsGallerySearchScope scope, 431 CmsGallerySearchScope defaultScope) { 432 433 super(GalleryTabId.cms_tab_results); 434 m_defaultScope = defaultScope; 435 m_galleryHandler = galleryHandler; 436 m_additionalWidgets.add(m_searchBar); 437 for (CmsGallerySearchScope choice : CmsGallerySearchScope.values()) { 438 String name = Messages.get().key(choice.getKey()); 439 m_searchBar.getScopeSelection().addOption(choice.name(), name); 440 } 441 m_searchBar.getScopeSelection().selectValue(scope.name()); 442 m_searchBar.getSearchButton().addClickHandler(event -> { 443 m_tabHandler.updateResult(); 444 }); 445 m_searchBar.getSearchInput().addValueChangeHandler(event -> { 446 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(event.getValue()) && (event.getValue().length() >= 3)) { 447 m_tabHandler.setSearchQuery(event.getValue()); 448 } else { 449 m_tabHandler.setSearchQuery(null); 450 } 451 }); 452 m_searchBar.getSearchInput().addKeyPressHandler(event -> { 453 if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) { 454 m_tabHandler.updateResult(); 455 } 456 }); 457 tabHandler.addSearchChangeHandler(new ValueChangeHandler<CmsGallerySearchBean>() { 458 459 @SuppressWarnings("synthetic-access") 460 public void onValueChange(ValueChangeEvent<CmsGallerySearchBean> event) { 461 462 // only set the query if the tab is not currently selected 463 if (!isSelected()) { 464 m_searchBar.getSearchInput().setFormValueAsString(event.getValue().getQuery()); 465 } 466 } 467 }); 468 469 m_contextMenuHandler = new CmsResultContextMenuHandler(tabHandler); 470 m_types = new HashSet<String>(); 471 m_hasMoreResults = false; 472 m_dndHandler = dndHandler; 473 m_tabHandler = tabHandler; 474 m_params = new FlowPanel(); 475 m_params.setStyleName(I_CmsLayoutBundle.INSTANCE.galleryDialogCss().tabParamsPanel()); 476 m_tab.insert(m_params, 0); 477 getList().addScrollHandler(new CmsAsynchronousScrollToBottomHandler()); 478 getList().addScrollHandler(m_backwardScrollHandler); 479 init(); 480 Map<String, String> views = new LinkedHashMap<String, String>(); 481 views.put(DETAILS, Messages.get().key(Messages.GUI_VIEW_LABEL_DETAILS_0)); 482 views.put(SMALL, Messages.get().key(Messages.GUI_VIEW_LABEL_SMALL_ICONS_0)); 483 views.put(BIG, Messages.get().key(Messages.GUI_VIEW_LABEL_BIG_ICONS_0)); 484 String resultViewType = m_tabHandler.getResultViewType(); 485 if (!views.containsKey(resultViewType)) { 486 resultViewType = SMALL; 487 } 488 m_selectView = new CmsSelectBox(views); 489 m_selectView.addStyleName(DIALOG_CSS.selectboxWidth()); 490 m_selectView.selectValue(resultViewType); 491 selectView(resultViewType); 492 addWidgetToOptions(m_selectView); 493 m_selectView.addValueChangeHandler(new ValueChangeHandler<String>() { 494 495 public void onValueChange(ValueChangeEvent<String> event) { 496 497 selectView(event.getValue()); 498 setScrollPosition(0); 499 onContentChange(); 500 getTabHandler().setResultViewType(event.getValue()); 501 } 502 }); 503 } 504 505 /** 506 * Checks if the type is viewable as an image in the gallery result tab.<p> 507 * 508 * @param typeName the type to check 509 * @return true if the type can be viewed as an image in the result tab 510 */ 511 public static boolean isImagelikeType(String typeName) { 512 513 return CmsGwtConstants.TYPE_IMAGE.equals(typeName); 514 } 515 516 /** 517 * Clears all search parameters.<p> 518 */ 519 @Override 520 public void clearParams() { 521 522 CmsDebugLog.getInstance().printLine("Unallowed call to clear params in result tab."); 523 } 524 525 /** 526 * Fill the content of the results tab.<p> 527 * 528 * @param searchObj the current search object containing search results 529 * @param paramPanels list of search parameter panels to show 530 */ 531 public void fillContent(final CmsGallerySearchBean searchObj, List<CmsSearchParamPanel> paramPanels) { 532 533 removeNoParamMessage(); 534 535 // in case there is a single type selected and the current sort order is not by type, 536 // hide the type ascending and type descending sort order options 537 SortParams currentSorting = SortParams.valueOf(searchObj.getSortOrder()); 538 539 // this should always be true in the results tab, but put it in an 'if' in case of future changes 540 if (m_sortSelectBox instanceof CmsSelectBox) { 541 if ((searchObj.getTypes().size() == 1) 542 && !((currentSorting == SortParams.type_asc) || (currentSorting == SortParams.type_desc))) { 543 ((CmsSelectBox)m_sortSelectBox).setItems(getSortList(false)); 544 } else { 545 ((CmsSelectBox)m_sortSelectBox).setItems(getSortList(true)); 546 } 547 } else { 548 CmsDebugLog.consoleLog("gallery result tab sort box is not a CmsSelectBox!"); 549 } 550 m_sortSelectBox.selectValue(searchObj.getSortOrder()); 551 displayResultCount(getResultsDisplayed(searchObj), searchObj.getResultCount()); 552 m_searchBar.getSearchInput().setFormValueAsString(searchObj.getQuery()); 553 if (searchObj.getScope() != null) { 554 m_searchBar.getScopeSelection().setFormValue(searchObj.getScope().name()); 555 } 556 paramPanels.addAll(getParamPanels(searchObj)); 557 558 m_hasMoreResults = searchObj.hasMore(); 559 if (searchObj.hasReplacedResults()) { 560 m_preset = null; 561 getList().scrollToTop(); 562 clearList(); 563 showParams(paramPanels); 564 addContent(searchObj); 565 getList().getElement().getStyle().clearDisplay(); 566 } else if (searchObj.getPage() == 1) { 567 m_preset = null; 568 getList().scrollToTop(); 569 clearList(); 570 showParams(paramPanels); 571 m_backwardScrollHandler.updateSearchBean(searchObj); 572 getList().getElement().getStyle().clearDisplay(); 573 scrollToPreset(); 574 } else { 575 showParams(paramPanels); 576 addContent(searchObj); 577 } 578 showUpload(searchObj); 579 } 580 581 /** 582 * Returns the drag and drop handler.<p> 583 * 584 * @return the drag and drop handler 585 */ 586 public CmsDNDHandler getDNDHandler() { 587 588 return m_dndHandler; 589 } 590 591 /** 592 * @see org.opencms.ade.galleries.client.ui.A_CmsTab#getParamPanels(org.opencms.ade.galleries.shared.CmsGallerySearchBean) 593 */ 594 @Override 595 public List<CmsSearchParamPanel> getParamPanels(CmsGallerySearchBean searchObj) { 596 597 List<CmsSearchParamPanel> result = new ArrayList<CmsSearchParamPanel>(); 598 CmsGallerySearchScope scope = CmsGallerySearchScope.valueOf( 599 m_searchBar.getScopeSelection().getFormValueAsString()); 600 if ((scope != m_defaultScope)) { 601 CmsSearchParamPanel panel = new CmsSearchParamPanel( 602 Messages.get().key(Messages.GUI_PARAMS_LABEL_SCOPE_0), 603 this); 604 panel.setContent(Messages.get().key(scope.getKey()), ParamType.scope.name()); 605 result.add(panel); 606 } 607 608 String query = m_searchBar.getSearchInput().getFormValueAsString(); 609 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(query)) { 610 CmsSearchParamPanel panel = new CmsSearchParamPanel( 611 Messages.get().key(Messages.GUI_TAB_SEARCH_LABEL_TEXT_0), 612 this); 613 panel.setContent(query, ParamType.text.name()); 614 result.add(panel); 615 } 616 617 return result; 618 619 } 620 621 /** 622 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#getRequiredHeight() 623 */ 624 @Override 625 public int getRequiredHeight() { 626 627 return super.getRequiredHeight() 628 + (m_searchBar.getOffsetHeight()) 629 + (m_params.isVisible() ? m_params.getOffsetHeight() + 5 : 21); 630 } 631 632 /** 633 * Returns the delete handler.<p> 634 * 635 * @param resourcePath the resource path of the resource 636 * 637 * @return the delete handler 638 */ 639 public DeleteHandler makeDeleteHandler(String resourcePath) { 640 641 return new DeleteHandler(resourcePath); 642 } 643 644 /** 645 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#onResize() 646 */ 647 @Override 648 public void onResize() { 649 650 super.onResize(); 651 // check if more result items should be loaded to fill the available height 652 if (m_hasMoreResults 653 && !getTabHandler().isLoading() 654 && (m_list.getOffsetHeight() > (m_scrollList.getOffsetHeight() - 100))) { 655 getTabHandler().onScrollToBottom(); 656 } 657 } 658 659 /** 660 * Removes the no params message.<p> 661 */ 662 public void removeNoParamMessage() { 663 664 if (m_noParamsMessage != null) { 665 m_tab.remove(m_noParamsMessage); 666 } 667 } 668 669 /** 670 * Removes the query. 671 */ 672 public void removeQuery() { 673 674 m_searchBar.getSearchInput().setFormValueAsString(""); 675 } 676 677 /** 678 * Removes the scope. 679 */ 680 public void removeScope() { 681 682 m_searchBar.getScopeSelection().setFormValueAsString(m_defaultScope.name()); 683 } 684 685 /** 686 * Updates the height (with border) of the result list panel according to the search parameter panels shown.<p> 687 */ 688 public void updateListSize() { 689 690 int paramsHeight = m_params.isVisible() 691 ? m_params.getOffsetHeight() 692 + CmsDomUtil.getCurrentStyleInt(m_params.getElement(), CmsDomUtil.Style.marginBottom) 693 : 21; 694 int optionsHeight = m_options.getOffsetHeight() 695 + CmsDomUtil.getCurrentStyleInt(m_options.getElement(), CmsDomUtil.Style.marginBottom); 696 int addHeight = m_additionalWidgets.getOffsetHeight() 697 + CmsDomUtil.getCurrentStyleInt(m_additionalWidgets.getElement(), CmsDomUtil.Style.marginBottom); 698 699 int listTop = paramsHeight + optionsHeight + addHeight + 5; 700 // another sanity check, don't set any top value below 35 701 if (listTop > 35) { 702 m_list.getElement().getStyle().setTop(listTop, Unit.PX); 703 } 704 } 705 706 /** 707 * Appends the list items for the search results from a search bean.<p> 708 * 709 * @param searchBean a search bean containing results 710 */ 711 protected void addContent(CmsGallerySearchBean searchBean) { 712 713 if (searchBean.getResults() != null) { 714 boolean showPath = SortParams.path_asc.name().equals(searchBean.getSortOrder()) 715 || SortParams.path_desc.name().equals(searchBean.getSortOrder()); 716 addContentItems(searchBean.getResults(), false, showPath); 717 } 718 } 719 720 /** 721 * Adds list items for a list of search results.<p> 722 * 723 * @param list the list of search results 724 * @param front if true, list items will be added to the front of the list, else at the back 725 * @param showPath <code>true</code> to show the resource path in sub title 726 */ 727 protected void addContentItems(List<CmsResultItemBean> list, boolean front, boolean showPath) { 728 729 if (front) { 730 list = Lists.reverse(list); 731 } 732 for (CmsResultItemBean resultItem : list) { 733 addSingleResult(resultItem, front, showPath); 734 } 735 if (isTilingViewAllowed()) { 736 m_selectView.getElement().getStyle().clearDisplay(); 737 selectView(m_selectView.getFormValueAsString()); 738 } else { 739 m_selectView.getElement().getStyle().setDisplay(Display.NONE); 740 selectView(DETAILS); 741 } 742 onContentChange(); 743 } 744 745 /** 746 * Adds a list item for a single search result.<p> 747 * 748 * @param resultItem the search result 749 * @param front if true, adds the list item to the front of the list, else at the back 750 * @param showPath <code>true</code> to show the resource path in sub title 751 */ 752 protected void addSingleResult(CmsResultItemBean resultItem, boolean front, boolean showPath) { 753 754 m_types.add(resultItem.getType()); 755 boolean hasPreview = m_tabHandler.hasPreview(resultItem.getType()); 756 CmsDNDHandler dndHandler = m_dndHandler; 757 if (!m_galleryHandler.filterDnd(resultItem)) { 758 dndHandler = null; 759 } 760 CmsResultListItem listItem = new CmsResultListItem(resultItem, hasPreview, showPath, dndHandler); 761 if (resultItem.isPreset()) { 762 m_preset = listItem; 763 } 764 if (hasPreview) { 765 listItem.addPreviewClickHandler(new PreviewHandler(resultItem.getPath(), resultItem.getType())); 766 } 767 CmsUUID structureId = new CmsUUID(resultItem.getClientId()); 768 listItem.getListItemWidget().addButton( 769 new CmsContextMenuButton(structureId, m_contextMenuHandler, AdeContext.gallery)); 770 listItem.getListItemWidget().addOpenHandler(new OpenHandler<CmsListItemWidget>() { 771 772 public void onOpen(OpenEvent<CmsListItemWidget> event) { 773 774 onContentChange(); 775 } 776 }); 777 if (m_tabHandler.hasSelectResource()) { 778 SelectHandler selectHandler = new SelectHandler( 779 resultItem.getPath(), 780 structureId, 781 resultItem.getRawTitle(), 782 resultItem.getType()); 783 listItem.addSelectClickHandler(selectHandler); 784 785 // this affects both tiled and non-tiled result lists. 786 listItem.addDoubleClickHandler(selectHandler); 787 } 788 m_galleryHandler.processResultItem(listItem); 789 if (front) { 790 addWidgetToFrontOfList(listItem); 791 } else { 792 addWidgetToList(listItem); 793 } 794 } 795 796 /** 797 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#clearList() 798 */ 799 @Override 800 protected void clearList() { 801 802 super.clearList(); 803 m_types.clear(); 804 } 805 806 /** 807 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#getSortList() 808 */ 809 @Override 810 protected LinkedHashMap<String, String> getSortList() { 811 812 return getSortList(true); 813 } 814 815 /** 816 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#getTabHandler() 817 */ 818 @Override 819 protected CmsResultsTabHandler getTabHandler() { 820 821 return m_tabHandler; 822 } 823 824 /** 825 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#hasQuickFilter() 826 */ 827 @Override 828 protected boolean hasQuickFilter() { 829 830 // quick filter not available for this tab 831 return false; 832 } 833 834 /** 835 * Scrolls to the result which corresponds to a preset value in the editor.<p> 836 */ 837 protected void scrollToPreset() { 838 839 final ScrollPanel scrollPanel = getList(); 840 if (m_preset != null) { 841 Widget child = scrollPanel.getWidget(); 842 if (child instanceof CmsList<?>) { 843 @SuppressWarnings("unchecked") 844 CmsList<I_CmsListItem> list = (CmsList<I_CmsListItem>)child; 845 if (list.getWidgetCount() > 0) { 846 final Widget first = (Widget)list.getItem(0); 847 Timer timer = new Timer() { 848 849 @Override 850 public void run() { 851 852 int firstTop = first.getElement().getAbsoluteTop(); 853 int presetTop = m_preset.getElement().getAbsoluteTop(); 854 final int offset = presetTop - firstTop; 855 if (offset >= 0) { 856 scrollPanel.setVerticalScrollPosition(offset); 857 } else { 858 // something is seriously wrong with the positioning if this case occurs 859 scrollPanel.scrollToBottom(); 860 } 861 } 862 }; 863 timer.schedule(10); 864 } 865 } 866 } 867 } 868 869 /** 870 * Helper for setting the scroll position of the scroll panel.<p> 871 * 872 * @param pos the scroll position 873 */ 874 protected void setScrollPosition(final int pos) { 875 876 getList().setVerticalScrollPosition(pos); 877 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 878 879 /** 880 * @see com.google.gwt.core.client.Scheduler.ScheduledCommand#execute() 881 */ 882 public void execute() { 883 884 if (getList().getVerticalScrollPosition() != pos) { 885 getList().setVerticalScrollPosition(pos); 886 } 887 888 } 889 }); 890 891 } 892 893 /** 894 * Selects the view with the given name.<p> 895 * 896 * @param viewName the view name 897 */ 898 void selectView(String viewName) { 899 900 if (DETAILS.equals(viewName)) { 901 getList().removeStyleName(I_CmsLayoutBundle.INSTANCE.galleryResultItemCss().tilingList()); 902 } else if (SMALL.equals(viewName)) { 903 getList().addStyleName(I_CmsLayoutBundle.INSTANCE.galleryResultItemCss().tilingList()); 904 getList().addStyleName(I_CmsLayoutBundle.INSTANCE.galleryResultItemCss().smallThumbnails()); 905 } else if (BIG.equals(viewName)) { 906 getList().addStyleName(I_CmsLayoutBundle.INSTANCE.galleryResultItemCss().tilingList()); 907 getList().removeStyleName(I_CmsLayoutBundle.INSTANCE.galleryResultItemCss().smallThumbnails()); 908 } 909 } 910 911 /** 912 * Creates special upload button (for external link galleries or galleries with custom upload actions). 913 * 914 * @param gallery the gallery bean 915 * 916 * @return the new button 917 */ 918 private CmsPushButton createSpecialUploadButton(CmsGalleryFolderBean gallery) { 919 920 if (CmsEditExternalLinkDialog.LINK_GALLERY_RESOURCE_TYPE_NAME.equals(gallery.getType())) { 921 return createNewExternalLinkButton(gallery.getPath()); 922 } else if (gallery.getUploadAction() != null) { 923 CmsPushButton uploadButton = new CmsPushButton(I_CmsButton.UPLOAD_SMALL); 924 uploadButton.setText(null); 925 uploadButton.setTitle(Messages.get().key(Messages.GUI_GALLERY_UPLOAD_TITLE_1, gallery.getPath())); 926 uploadButton.setButtonStyle(ButtonStyle.FONT_ICON, null); 927 uploadButton.addClickHandler(new ClickHandler() { 928 929 public void onClick(ClickEvent event) { 930 931 CmsRpcAction<CmsUUID> action = new CmsRpcAction<CmsUUID>() { 932 933 @Override 934 public void execute() { 935 936 start(0, true); 937 CmsCoreProvider.getVfsService().getStructureId(gallery.getPath(), this); 938 } 939 940 @Override 941 protected void onResponse(CmsUUID result) { 942 943 stop(false); 944 List<CmsUUID> resultIds = new ArrayList<>(); 945 resultIds.add(result); 946 CmsEmbeddedDialogHandler.openDialog( 947 gallery.getUploadAction(), 948 resultIds, 949 id -> getTabHandler().updateIndex()); 950 } 951 952 }; 953 action.execute(); 954 } 955 }); 956 return uploadButton; 957 } else { 958 return null; 959 } 960 } 961 962 /** 963 * Displays the result count.<p> 964 * 965 * @param displayed the displayed result items 966 * @param total the total of result items 967 */ 968 private void displayResultCount(int displayed, int total) { 969 970 String message = Messages.get().key( 971 Messages.GUI_LABEL_NUM_RESULTS_2, 972 Integer.valueOf(displayed), 973 Integer.valueOf(total)); 974 m_infoLabel.setText(message); 975 } 976 977 /** 978 * Returns the count of the currently displayed results.<p> 979 * 980 * @param searchObj the search bean 981 * 982 * @return the count of the currently displayed results 983 */ 984 private int getResultsDisplayed(CmsGallerySearchBean searchObj) { 985 986 if (searchObj.hasReplacedResults()) { 987 return searchObj.getResults().size(); 988 } 989 int resultsDisplayed = searchObj.getMatchesPerPage() * searchObj.getLastPage(); 990 return (resultsDisplayed > searchObj.getResultCount()) ? searchObj.getResultCount() : resultsDisplayed; 991 } 992 993 /** 994 * Returns the list of properties to sort the results according to.<p> 995 * 996 * @param includeType <code>true</code> to include sort according to type 997 * 998 * @return the sort list 999 */ 1000 private LinkedHashMap<String, String> getSortList(boolean includeType) { 1001 1002 LinkedHashMap<String, String> list = new LinkedHashMap<String, String>(); 1003 list.put(SortParams.title_asc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TITLE_ASC_0)); 1004 list.put(SortParams.title_desc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TITLE_DECS_0)); 1005 list.put( 1006 SortParams.dateLastModified_asc.name(), 1007 Messages.get().key(Messages.GUI_SORT_LABEL_DATELASTMODIFIED_ASC_0)); 1008 list.put( 1009 SortParams.dateLastModified_desc.name(), 1010 Messages.get().key(Messages.GUI_SORT_LABEL_DATELASTMODIFIED_DESC_0)); 1011 list.put(SortParams.path_asc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_PATH_ASC_0)); 1012 list.put(SortParams.path_desc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_PATH_DESC_0)); 1013 list.put(SortParams.score.name(), Messages.get().key(Messages.GUI_SORT_LABEL_SCORE_0)); 1014 if (includeType) { 1015 list.put(SortParams.type_asc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TYPE_ASC_0)); 1016 list.put(SortParams.type_desc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TYPE_DESC_0)); 1017 } 1018 return list; 1019 } 1020 1021 /** 1022 * Checks if the thumbnail tiling view is allowed for the given result items.<p> 1023 * 1024 * @return <code>true</code> if the thumbnail tiling view is allowed for the given result items 1025 */ 1026 private boolean isTilingViewAllowed() { 1027 1028 if (m_types.size() == 0) { 1029 return false; 1030 } 1031 for (String typeName : m_types) { 1032 if (!isImagelikeType(typeName)) { 1033 return false; 1034 } 1035 } 1036 return true; 1037 1038 } 1039 1040 /** 1041 * Check if we need a special upload button for the gallery. 1042 * 1043 * @param gallery a gallery bean 1044 * @return true if we need a special upload button 1045 */ 1046 private boolean needsSpecialButton(CmsGalleryFolderBean gallery) { 1047 1048 return (gallery.getUploadAction() != null) 1049 || CmsEditExternalLinkDialog.LINK_GALLERY_RESOURCE_TYPE_NAME.equals(gallery.getType()); 1050 1051 } 1052 1053 /** 1054 * Displays the selected search parameters in the result tab.<p> 1055 * 1056 * @param paramPanels the list of search parameter panels to show 1057 * 1058 */ 1059 private void showParams(List<CmsSearchParamPanel> paramPanels) { 1060 1061 m_params.clear(); 1062 if ((paramPanels == null) || (paramPanels.size() == 0)) { 1063 m_params.setVisible(false); 1064 updateListSize(); 1065 return; 1066 } 1067 m_params.setVisible(true); 1068 for (CmsSearchParamPanel panel : paramPanels) { 1069 m_params.add(panel); 1070 } 1071 updateListSize(); 1072 } 1073 1074 /** 1075 * Shows the upload button if appropriate.<p> 1076 * 1077 * @param searchObj the current search object 1078 */ 1079 private void showUpload(CmsGallerySearchBean searchObj) { 1080 1081 boolean uploadDisabled = CmsCoreProvider.get().isUploadDisabled(); 1082 Set<String> targets = new HashSet<String>(); 1083 1084 if (searchObj.getGalleries() != null) { 1085 targets.addAll(searchObj.getGalleries()); 1086 } 1087 if (searchObj.getFolders() != null) { 1088 targets.addAll(searchObj.getFolders()); 1089 } 1090 if (m_specialUploadButton != null) { 1091 m_specialUploadButton.removeFromParent(); 1092 m_specialUploadButton = null; 1093 } 1094 if (m_uploadButton == null) { 1095 m_uploadButton = createUploadButtonForTarget("", false); 1096 m_uploadButton.addStyleName(I_CmsLayoutBundle.INSTANCE.galleryDialogCss().resultTabUpload()); 1097 m_tab.insert(m_uploadButton, 0); 1098 } else { 1099 m_uploadButton.getElement().getStyle().clearDisplay(); 1100 } 1101 if (targets.size() == 1) { 1102 CmsGalleryFolderBean galleryFolder = getTabHandler().getGalleryInfo(targets.iterator().next()); 1103 1104 if ((galleryFolder != null) && needsSpecialButton(galleryFolder)) { 1105 1106 m_specialUploadButton = createSpecialUploadButton(galleryFolder); 1107 if (m_specialUploadButton != null) { 1108 m_specialUploadButton.addStyleName(I_CmsLayoutBundle.INSTANCE.galleryDialogCss().resultTabUpload()); 1109 m_tab.insert(m_specialUploadButton, 0); 1110 if (CmsStringUtil.isEmptyOrWhitespaceOnly(searchObj.getNoUploadReason())) { 1111 m_specialUploadButton.enable(); 1112 } else { 1113 m_specialUploadButton.disable(searchObj.getNoUploadReason()); 1114 } 1115 } 1116 m_uploadButton.getElement().getStyle().setDisplay(Display.NONE); 1117 } else { 1118 String uploadTarget = targets.iterator().next(); 1119 I_CmsUploadButtonHandler handler = m_uploadButton.getButtonHandler(); 1120 if (handler instanceof CmsDialogUploadButtonHandler) { 1121 ((CmsDialogUploadButtonHandler)handler).setTargetFolder(uploadTarget); 1122 // in case the upload target is a folder the root path is used 1123 ((CmsDialogUploadButtonHandler)handler).setIsTargetRootPath(searchObj.getFolders().size() == 1); 1124 m_uploadButton.updateFileInput(); 1125 } 1126 if (CmsStringUtil.isEmptyOrWhitespaceOnly(searchObj.getNoUploadReason())) { 1127 m_uploadButton.enable(); 1128 m_uploadButton.setTitle(Messages.get().key(Messages.GUI_GALLERY_UPLOAD_TITLE_1, uploadTarget)); 1129 } else { 1130 m_uploadButton.disable(searchObj.getNoUploadReason()); 1131 } 1132 } 1133 } else { 1134 m_uploadButton.disable(Messages.get().key(Messages.GUI_GALLERY_UPLOAD_TARGET_UNSPECIFIC_0)); 1135 } 1136 if (uploadDisabled) { 1137 1138 for (Widget button : new Widget[] {m_uploadButton, m_specialUploadButton}) { 1139 if (button != null) { 1140 button.removeFromParent(); 1141 } 1142 } 1143 m_uploadButton = null; 1144 m_specialUploadButton = null; 1145 } 1146 1147 } 1148}