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 if ((searchObj.getTypes().size() == 1) 539 && !((currentSorting == SortParams.type_asc) || (currentSorting == SortParams.type_desc))) { 540 m_sortSelectBox.setItems(getSortList(false)); 541 } else { 542 m_sortSelectBox.setItems(getSortList(true)); 543 } 544 m_sortSelectBox.selectValue(searchObj.getSortOrder()); 545 displayResultCount(getResultsDisplayed(searchObj), searchObj.getResultCount()); 546 m_searchBar.getSearchInput().setFormValueAsString(searchObj.getQuery()); 547 if (searchObj.getScope() != null) { 548 m_searchBar.getScopeSelection().setFormValue(searchObj.getScope().name()); 549 } 550 paramPanels.addAll(getParamPanels(searchObj)); 551 552 m_hasMoreResults = searchObj.hasMore(); 553 if (searchObj.hasReplacedResults()) { 554 m_preset = null; 555 getList().scrollToTop(); 556 clearList(); 557 showParams(paramPanels); 558 addContent(searchObj); 559 getList().getElement().getStyle().clearDisplay(); 560 } else if (searchObj.getPage() == 1) { 561 m_preset = null; 562 getList().scrollToTop(); 563 clearList(); 564 showParams(paramPanels); 565 m_backwardScrollHandler.updateSearchBean(searchObj); 566 getList().getElement().getStyle().clearDisplay(); 567 scrollToPreset(); 568 } else { 569 showParams(paramPanels); 570 addContent(searchObj); 571 } 572 showUpload(searchObj); 573 } 574 575 /** 576 * Returns the drag and drop handler.<p> 577 * 578 * @return the drag and drop handler 579 */ 580 public CmsDNDHandler getDNDHandler() { 581 582 return m_dndHandler; 583 } 584 585 /** 586 * @see org.opencms.ade.galleries.client.ui.A_CmsTab#getParamPanels(org.opencms.ade.galleries.shared.CmsGallerySearchBean) 587 */ 588 @Override 589 public List<CmsSearchParamPanel> getParamPanels(CmsGallerySearchBean searchObj) { 590 591 List<CmsSearchParamPanel> result = new ArrayList<CmsSearchParamPanel>(); 592 CmsGallerySearchScope scope = CmsGallerySearchScope.valueOf( 593 m_searchBar.getScopeSelection().getFormValueAsString()); 594 if ((scope != m_defaultScope)) { 595 CmsSearchParamPanel panel = new CmsSearchParamPanel( 596 Messages.get().key(Messages.GUI_PARAMS_LABEL_SCOPE_0), 597 this); 598 panel.setContent(Messages.get().key(scope.getKey()), ParamType.scope.name()); 599 result.add(panel); 600 } 601 602 String query = m_searchBar.getSearchInput().getFormValueAsString(); 603 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(query)) { 604 CmsSearchParamPanel panel = new CmsSearchParamPanel( 605 Messages.get().key(Messages.GUI_TAB_SEARCH_LABEL_TEXT_0), 606 this); 607 panel.setContent(query, ParamType.text.name()); 608 result.add(panel); 609 } 610 611 return result; 612 613 } 614 615 /** 616 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#getRequiredHeight() 617 */ 618 @Override 619 public int getRequiredHeight() { 620 621 return super.getRequiredHeight() 622 + (m_searchBar.getOffsetHeight()) 623 + (m_params.isVisible() ? m_params.getOffsetHeight() + 5 : 21); 624 } 625 626 /** 627 * Returns the delete handler.<p> 628 * 629 * @param resourcePath the resource path of the resource 630 * 631 * @return the delete handler 632 */ 633 public DeleteHandler makeDeleteHandler(String resourcePath) { 634 635 return new DeleteHandler(resourcePath); 636 } 637 638 /** 639 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#onResize() 640 */ 641 @Override 642 public void onResize() { 643 644 super.onResize(); 645 // check if more result items should be loaded to fill the available height 646 if (m_hasMoreResults 647 && !getTabHandler().isLoading() 648 && (m_list.getOffsetHeight() > (m_scrollList.getOffsetHeight() - 100))) { 649 getTabHandler().onScrollToBottom(); 650 } 651 } 652 653 /** 654 * Removes the no params message.<p> 655 */ 656 public void removeNoParamMessage() { 657 658 if (m_noParamsMessage != null) { 659 m_tab.remove(m_noParamsMessage); 660 } 661 } 662 663 /** 664 * Removes the query. 665 */ 666 public void removeQuery() { 667 668 m_searchBar.getSearchInput().setFormValueAsString(""); 669 } 670 671 /** 672 * Removes the scope. 673 */ 674 public void removeScope() { 675 676 m_searchBar.getScopeSelection().setFormValueAsString(m_defaultScope.name()); 677 } 678 679 /** 680 * Updates the height (with border) of the result list panel according to the search parameter panels shown.<p> 681 */ 682 public void updateListSize() { 683 684 int paramsHeight = m_params.isVisible() 685 ? m_params.getOffsetHeight() 686 + CmsDomUtil.getCurrentStyleInt(m_params.getElement(), CmsDomUtil.Style.marginBottom) 687 : 21; 688 int optionsHeight = m_options.getOffsetHeight() 689 + CmsDomUtil.getCurrentStyleInt(m_options.getElement(), CmsDomUtil.Style.marginBottom); 690 int addHeight = m_additionalWidgets.getOffsetHeight() 691 + CmsDomUtil.getCurrentStyleInt(m_additionalWidgets.getElement(), CmsDomUtil.Style.marginBottom); 692 693 int listTop = paramsHeight + optionsHeight + addHeight + 5; 694 // another sanity check, don't set any top value below 35 695 if (listTop > 35) { 696 m_list.getElement().getStyle().setTop(listTop, Unit.PX); 697 } 698 } 699 700 /** 701 * Appends the list items for the search results from a search bean.<p> 702 * 703 * @param searchBean a search bean containing results 704 */ 705 protected void addContent(CmsGallerySearchBean searchBean) { 706 707 if (searchBean.getResults() != null) { 708 boolean showPath = SortParams.path_asc.name().equals(searchBean.getSortOrder()) 709 || SortParams.path_desc.name().equals(searchBean.getSortOrder()); 710 addContentItems(searchBean.getResults(), false, showPath); 711 } 712 } 713 714 /** 715 * Adds list items for a list of search results.<p> 716 * 717 * @param list the list of search results 718 * @param front if true, list items will be added to the front of the list, else at the back 719 * @param showPath <code>true</code> to show the resource path in sub title 720 */ 721 protected void addContentItems(List<CmsResultItemBean> list, boolean front, boolean showPath) { 722 723 if (front) { 724 list = Lists.reverse(list); 725 } 726 for (CmsResultItemBean resultItem : list) { 727 addSingleResult(resultItem, front, showPath); 728 } 729 if (isTilingViewAllowed()) { 730 m_selectView.getElement().getStyle().clearDisplay(); 731 selectView(m_selectView.getFormValueAsString()); 732 } else { 733 m_selectView.getElement().getStyle().setDisplay(Display.NONE); 734 selectView(DETAILS); 735 } 736 onContentChange(); 737 } 738 739 /** 740 * Adds a list item for a single search result.<p> 741 * 742 * @param resultItem the search result 743 * @param front if true, adds the list item to the front of the list, else at the back 744 * @param showPath <code>true</code> to show the resource path in sub title 745 */ 746 protected void addSingleResult(CmsResultItemBean resultItem, boolean front, boolean showPath) { 747 748 m_types.add(resultItem.getType()); 749 boolean hasPreview = m_tabHandler.hasPreview(resultItem.getType()); 750 CmsDNDHandler dndHandler = m_dndHandler; 751 if (!m_galleryHandler.filterDnd(resultItem)) { 752 dndHandler = null; 753 } 754 CmsResultListItem listItem = new CmsResultListItem(resultItem, hasPreview, showPath, dndHandler); 755 if (resultItem.isPreset()) { 756 m_preset = listItem; 757 } 758 if (hasPreview) { 759 listItem.addPreviewClickHandler(new PreviewHandler(resultItem.getPath(), resultItem.getType())); 760 } 761 CmsUUID structureId = new CmsUUID(resultItem.getClientId()); 762 listItem.getListItemWidget().addButton( 763 new CmsContextMenuButton(structureId, m_contextMenuHandler, AdeContext.gallery)); 764 listItem.getListItemWidget().addOpenHandler(new OpenHandler<CmsListItemWidget>() { 765 766 public void onOpen(OpenEvent<CmsListItemWidget> event) { 767 768 onContentChange(); 769 } 770 }); 771 if (m_tabHandler.hasSelectResource()) { 772 SelectHandler selectHandler = new SelectHandler( 773 resultItem.getPath(), 774 structureId, 775 resultItem.getRawTitle(), 776 resultItem.getType()); 777 listItem.addSelectClickHandler(selectHandler); 778 779 // this affects both tiled and non-tiled result lists. 780 listItem.addDoubleClickHandler(selectHandler); 781 } 782 m_galleryHandler.processResultItem(listItem); 783 if (front) { 784 addWidgetToFrontOfList(listItem); 785 } else { 786 addWidgetToList(listItem); 787 } 788 } 789 790 /** 791 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#clearList() 792 */ 793 @Override 794 protected void clearList() { 795 796 super.clearList(); 797 m_types.clear(); 798 } 799 800 /** 801 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#getSortList() 802 */ 803 @Override 804 protected LinkedHashMap<String, String> getSortList() { 805 806 return getSortList(true); 807 } 808 809 /** 810 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#getTabHandler() 811 */ 812 @Override 813 protected CmsResultsTabHandler getTabHandler() { 814 815 return m_tabHandler; 816 } 817 818 /** 819 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#hasQuickFilter() 820 */ 821 @Override 822 protected boolean hasQuickFilter() { 823 824 // quick filter not available for this tab 825 return false; 826 } 827 828 /** 829 * Scrolls to the result which corresponds to a preset value in the editor.<p> 830 */ 831 protected void scrollToPreset() { 832 833 final ScrollPanel scrollPanel = getList(); 834 if (m_preset != null) { 835 Widget child = scrollPanel.getWidget(); 836 if (child instanceof CmsList<?>) { 837 @SuppressWarnings("unchecked") 838 CmsList<I_CmsListItem> list = (CmsList<I_CmsListItem>)child; 839 if (list.getWidgetCount() > 0) { 840 final Widget first = (Widget)list.getItem(0); 841 Timer timer = new Timer() { 842 843 @Override 844 public void run() { 845 846 int firstTop = first.getElement().getAbsoluteTop(); 847 int presetTop = m_preset.getElement().getAbsoluteTop(); 848 final int offset = presetTop - firstTop; 849 if (offset >= 0) { 850 scrollPanel.setVerticalScrollPosition(offset); 851 } else { 852 // something is seriously wrong with the positioning if this case occurs 853 scrollPanel.scrollToBottom(); 854 } 855 } 856 }; 857 timer.schedule(10); 858 } 859 } 860 } 861 } 862 863 /** 864 * Helper for setting the scroll position of the scroll panel.<p> 865 * 866 * @param pos the scroll position 867 */ 868 protected void setScrollPosition(final int pos) { 869 870 getList().setVerticalScrollPosition(pos); 871 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 872 873 /** 874 * @see com.google.gwt.core.client.Scheduler.ScheduledCommand#execute() 875 */ 876 public void execute() { 877 878 if (getList().getVerticalScrollPosition() != pos) { 879 getList().setVerticalScrollPosition(pos); 880 } 881 882 } 883 }); 884 885 } 886 887 /** 888 * Selects the view with the given name.<p> 889 * 890 * @param viewName the view name 891 */ 892 void selectView(String viewName) { 893 894 if (DETAILS.equals(viewName)) { 895 getList().removeStyleName(I_CmsLayoutBundle.INSTANCE.galleryResultItemCss().tilingList()); 896 } else if (SMALL.equals(viewName)) { 897 getList().addStyleName(I_CmsLayoutBundle.INSTANCE.galleryResultItemCss().tilingList()); 898 getList().addStyleName(I_CmsLayoutBundle.INSTANCE.galleryResultItemCss().smallThumbnails()); 899 } else if (BIG.equals(viewName)) { 900 getList().addStyleName(I_CmsLayoutBundle.INSTANCE.galleryResultItemCss().tilingList()); 901 getList().removeStyleName(I_CmsLayoutBundle.INSTANCE.galleryResultItemCss().smallThumbnails()); 902 } 903 } 904 905 /** 906 * Creates special upload button (for external link galleries or galleries with custom upload actions). 907 * 908 * @param gallery the gallery bean 909 * 910 * @return the new button 911 */ 912 private CmsPushButton createSpecialUploadButton(CmsGalleryFolderBean gallery) { 913 914 if (CmsEditExternalLinkDialog.LINK_GALLERY_RESOURCE_TYPE_NAME.equals(gallery.getType())) { 915 return createNewExternalLinkButton(gallery.getPath()); 916 } else if (gallery.getUploadAction() != null) { 917 CmsPushButton uploadButton = new CmsPushButton(I_CmsButton.UPLOAD_SMALL); 918 uploadButton.setText(null); 919 uploadButton.setTitle(Messages.get().key(Messages.GUI_GALLERY_UPLOAD_TITLE_1, gallery.getPath())); 920 uploadButton.setButtonStyle(ButtonStyle.FONT_ICON, null); 921 uploadButton.addClickHandler(new ClickHandler() { 922 923 public void onClick(ClickEvent event) { 924 925 CmsRpcAction<CmsUUID> action = new CmsRpcAction<CmsUUID>() { 926 927 @Override 928 public void execute() { 929 930 start(0, true); 931 CmsCoreProvider.getVfsService().getStructureId(gallery.getPath(), this); 932 } 933 934 @Override 935 protected void onResponse(CmsUUID result) { 936 937 stop(false); 938 List<CmsUUID> resultIds = new ArrayList<>(); 939 resultIds.add(result); 940 CmsEmbeddedDialogHandler.openDialog( 941 gallery.getUploadAction(), 942 resultIds, 943 id -> getTabHandler().updateIndex()); 944 } 945 946 }; 947 action.execute(); 948 } 949 }); 950 return uploadButton; 951 } else { 952 return null; 953 } 954 } 955 956 /** 957 * Displays the result count.<p> 958 * 959 * @param displayed the displayed result items 960 * @param total the total of result items 961 */ 962 private void displayResultCount(int displayed, int total) { 963 964 String message = Messages.get().key( 965 Messages.GUI_LABEL_NUM_RESULTS_2, 966 new Integer(displayed), 967 new Integer(total)); 968 m_infoLabel.setText(message); 969 } 970 971 /** 972 * Returns the count of the currently displayed results.<p> 973 * 974 * @param searchObj the search bean 975 * 976 * @return the count of the currently displayed results 977 */ 978 private int getResultsDisplayed(CmsGallerySearchBean searchObj) { 979 980 if (searchObj.hasReplacedResults()) { 981 return searchObj.getResults().size(); 982 } 983 int resultsDisplayed = searchObj.getMatchesPerPage() * searchObj.getLastPage(); 984 return (resultsDisplayed > searchObj.getResultCount()) ? searchObj.getResultCount() : resultsDisplayed; 985 } 986 987 /** 988 * Returns the list of properties to sort the results according to.<p> 989 * 990 * @param includeType <code>true</code> to include sort according to type 991 * 992 * @return the sort list 993 */ 994 private LinkedHashMap<String, String> getSortList(boolean includeType) { 995 996 LinkedHashMap<String, String> list = new LinkedHashMap<String, String>(); 997 list.put(SortParams.title_asc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TITLE_ASC_0)); 998 list.put(SortParams.title_desc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TITLE_DECS_0)); 999 list.put( 1000 SortParams.dateLastModified_asc.name(), 1001 Messages.get().key(Messages.GUI_SORT_LABEL_DATELASTMODIFIED_ASC_0)); 1002 list.put( 1003 SortParams.dateLastModified_desc.name(), 1004 Messages.get().key(Messages.GUI_SORT_LABEL_DATELASTMODIFIED_DESC_0)); 1005 list.put(SortParams.path_asc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_PATH_ASC_0)); 1006 list.put(SortParams.path_desc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_PATH_DESC_0)); 1007 list.put(SortParams.score.name(), Messages.get().key(Messages.GUI_SORT_LABEL_SCORE_0)); 1008 if (includeType) { 1009 list.put(SortParams.type_asc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TYPE_ASC_0)); 1010 list.put(SortParams.type_desc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TYPE_DESC_0)); 1011 } 1012 return list; 1013 } 1014 1015 /** 1016 * Checks if the thumbnail tiling view is allowed for the given result items.<p> 1017 * 1018 * @return <code>true</code> if the thumbnail tiling view is allowed for the given result items 1019 */ 1020 private boolean isTilingViewAllowed() { 1021 1022 if (m_types.size() == 0) { 1023 return false; 1024 } 1025 for (String typeName : m_types) { 1026 if (!isImagelikeType(typeName)) { 1027 return false; 1028 } 1029 } 1030 return true; 1031 1032 } 1033 1034 /** 1035 * Check if we need a special upload button for the gallery. 1036 * 1037 * @param gallery a gallery bean 1038 * @return true if we need a special upload button 1039 */ 1040 private boolean needsSpecialButton(CmsGalleryFolderBean gallery) { 1041 1042 return (gallery.getUploadAction() != null) 1043 || CmsEditExternalLinkDialog.LINK_GALLERY_RESOURCE_TYPE_NAME.equals(gallery.getType()); 1044 1045 } 1046 1047 /** 1048 * Displays the selected search parameters in the result tab.<p> 1049 * 1050 * @param paramPanels the list of search parameter panels to show 1051 * 1052 */ 1053 private void showParams(List<CmsSearchParamPanel> paramPanels) { 1054 1055 m_params.clear(); 1056 if ((paramPanels == null) || (paramPanels.size() == 0)) { 1057 m_params.setVisible(false); 1058 updateListSize(); 1059 return; 1060 } 1061 m_params.setVisible(true); 1062 for (CmsSearchParamPanel panel : paramPanels) { 1063 m_params.add(panel); 1064 } 1065 updateListSize(); 1066 } 1067 1068 /** 1069 * Shows the upload button if appropriate.<p> 1070 * 1071 * @param searchObj the current search object 1072 */ 1073 private void showUpload(CmsGallerySearchBean searchObj) { 1074 1075 boolean uploadDisabled = CmsCoreProvider.get().isUploadDisabled(); 1076 Set<String> targets = new HashSet<String>(); 1077 1078 if (searchObj.getGalleries() != null) { 1079 targets.addAll(searchObj.getGalleries()); 1080 } 1081 if (searchObj.getFolders() != null) { 1082 targets.addAll(searchObj.getFolders()); 1083 } 1084 if (m_specialUploadButton != null) { 1085 m_specialUploadButton.removeFromParent(); 1086 m_specialUploadButton = null; 1087 } 1088 if (m_uploadButton == null) { 1089 m_uploadButton = createUploadButtonForTarget("", false); 1090 m_uploadButton.addStyleName(I_CmsLayoutBundle.INSTANCE.galleryDialogCss().resultTabUpload()); 1091 m_tab.insert(m_uploadButton, 0); 1092 } else { 1093 m_uploadButton.getElement().getStyle().clearDisplay(); 1094 } 1095 if (targets.size() == 1) { 1096 CmsGalleryFolderBean galleryFolder = getTabHandler().getGalleryInfo(targets.iterator().next()); 1097 1098 if ((galleryFolder != null) && needsSpecialButton(galleryFolder)) { 1099 1100 m_specialUploadButton = createSpecialUploadButton(galleryFolder); 1101 if (m_specialUploadButton != null) { 1102 m_specialUploadButton.addStyleName(I_CmsLayoutBundle.INSTANCE.galleryDialogCss().resultTabUpload()); 1103 m_tab.insert(m_specialUploadButton, 0); 1104 if (CmsStringUtil.isEmptyOrWhitespaceOnly(searchObj.getNoUploadReason())) { 1105 m_specialUploadButton.enable(); 1106 } else { 1107 m_specialUploadButton.disable(searchObj.getNoUploadReason()); 1108 } 1109 } 1110 m_uploadButton.getElement().getStyle().setDisplay(Display.NONE); 1111 } else { 1112 String uploadTarget = targets.iterator().next(); 1113 I_CmsUploadButtonHandler handler = m_uploadButton.getButtonHandler(); 1114 if (handler instanceof CmsDialogUploadButtonHandler) { 1115 ((CmsDialogUploadButtonHandler)handler).setTargetFolder(uploadTarget); 1116 // in case the upload target is a folder the root path is used 1117 ((CmsDialogUploadButtonHandler)handler).setIsTargetRootPath(searchObj.getFolders().size() == 1); 1118 m_uploadButton.updateFileInput(); 1119 } 1120 if (CmsStringUtil.isEmptyOrWhitespaceOnly(searchObj.getNoUploadReason())) { 1121 m_uploadButton.enable(); 1122 m_uploadButton.setTitle(Messages.get().key(Messages.GUI_GALLERY_UPLOAD_TITLE_1, uploadTarget)); 1123 } else { 1124 m_uploadButton.disable(searchObj.getNoUploadReason()); 1125 } 1126 } 1127 } else { 1128 m_uploadButton.disable(Messages.get().key(Messages.GUI_GALLERY_UPLOAD_TARGET_UNSPECIFIC_0)); 1129 } 1130 if (uploadDisabled) { 1131 1132 for (Widget button : new Widget[] {m_uploadButton, m_specialUploadButton}) { 1133 if (button != null) { 1134 button.removeFromParent(); 1135 } 1136 } 1137 m_uploadButton = null; 1138 m_specialUploadButton = null; 1139 } 1140 1141 } 1142}