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.CmsGalleriesTabHandler; 031import org.opencms.ade.galleries.client.Messages; 032import org.opencms.ade.galleries.client.ui.css.I_CmsLayoutBundle; 033import org.opencms.ade.galleries.shared.CmsGalleryFolderBean; 034import org.opencms.ade.galleries.shared.CmsGalleryGroup; 035import org.opencms.ade.galleries.shared.CmsGallerySearchBean; 036import org.opencms.ade.galleries.shared.CmsGalleryTreeEntry; 037import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.GalleryTabId; 038import org.opencms.ade.galleries.shared.I_CmsGalleryProviderConstants.SortParams; 039import org.opencms.gwt.client.CmsCoreProvider; 040import org.opencms.gwt.client.rpc.CmsRpcAction; 041import org.opencms.gwt.client.ui.CmsListItem; 042import org.opencms.gwt.client.ui.CmsListItemWidget; 043import org.opencms.gwt.client.ui.CmsPushButton; 044import org.opencms.gwt.client.ui.CmsSimpleListItem; 045import org.opencms.gwt.client.ui.I_CmsButton; 046import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle; 047import org.opencms.gwt.client.ui.externallink.CmsEditExternalLinkDialog; 048import org.opencms.gwt.client.ui.input.CmsCheckBox; 049import org.opencms.gwt.client.ui.tree.CmsTreeItem; 050import org.opencms.gwt.client.util.CmsEmbeddedDialogHandler; 051import org.opencms.gwt.client.util.CmsScrollToBottomHandler; 052import org.opencms.util.CmsStringUtil; 053import org.opencms.util.CmsUUID; 054 055import java.util.ArrayList; 056import java.util.HashMap; 057import java.util.Iterator; 058import java.util.LinkedHashMap; 059import java.util.List; 060import java.util.Map; 061 062import com.google.common.collect.Lists; 063import com.google.gwt.core.client.Scheduler; 064import com.google.gwt.core.client.Scheduler.RepeatingCommand; 065import com.google.gwt.event.dom.client.ClickEvent; 066import com.google.gwt.event.dom.client.ClickHandler; 067import com.google.gwt.user.client.Timer; 068import com.google.gwt.user.client.ui.Label; 069 070/** 071 * Provides the widget for the galleries(folder) tab.<p> 072 * 073 * It displays the available gallery folders in the given order. 074 * 075 * @since 8.0. 076 */ 077public class CmsGalleriesTab extends A_CmsListTab { 078 079 /** 080 * A class which generates list items incrementally to fill the galleries tab.<p> 081 */ 082 protected class ListItemGenerator implements Iterator<CmsTreeItem> { 083 084 /** The internal iterator over the gallery beans. */ 085 protected Iterator<CmsGalleryFolderBean> m_beanIterator; 086 087 /** True if output should be grouped. */ 088 protected boolean m_useGroups; 089 090 /** 091 * Creates a new instance.<p> 092 * @param folders the list of folders for which to generate list items 093 * @param grouped true if the list items should be displayed in groups (this assumes the items have already been sorted correctly) 094 */ 095 public ListItemGenerator(List<CmsGalleryFolderBean> folders, boolean grouped) { 096 097 if (folders == null) { 098 folders = new ArrayList<CmsGalleryFolderBean>(); 099 } 100 101 m_beanIterator = folders.iterator(); 102 m_useGroups = grouped; 103 } 104 105 /** 106 * @see java.util.Iterator#hasNext() 107 */ 108 public boolean hasNext() { 109 110 return m_beanIterator.hasNext(); 111 } 112 113 /** 114 * @see java.util.Iterator#next() 115 */ 116 public CmsTreeItem next() { 117 118 CmsGalleryFolderBean gallery = m_beanIterator.next(); 119 CmsTreeItem treeItem = createTreeItem(gallery, m_selectedGalleries, false, m_useGroups); 120 return treeItem; 121 } 122 123 /** 124 * @see java.util.Iterator#remove() 125 */ 126 public void remove() { 127 128 throw new UnsupportedOperationException(); 129 } 130 } 131 132 /** 133 * Command for adding more list items to the list of publish items.<p> 134 */ 135 protected class MoreItemsCommand implements RepeatingCommand { 136 137 /** The number of items left to add. */ 138 private int m_numItems; 139 140 /** 141 * Creates a new instance.<p> 142 * 143 * @param numItems the maximal number of items to add 144 */ 145 public MoreItemsCommand(int numItems) { 146 147 m_numItems = numItems; 148 } 149 150 /** 151 * @see com.google.gwt.core.client.Scheduler.RepeatingCommand#execute() 152 */ 153 public boolean execute() { 154 155 if (m_numItems == 0) { 156 setLoading(false); 157 onContentChange(); 158 return false; 159 } 160 boolean hasMore = m_itemIterator.hasNext(); 161 if (!hasMore) { 162 setLoading(false); 163 onContentChange(); 164 return false; 165 } else { 166 CmsTreeItem treeItem = m_itemIterator.next(); 167 CmsGalleryGroup group = (CmsGalleryGroup)treeItem.getData(); 168 if ((group != null) && (group != m_lastGroup)) { 169 m_lastGroup = group; 170 CmsSimpleListItem header = new CmsSimpleListItem(); 171 header.getElement().setInnerText(getGroupName(group)); 172 String groupHeaderClass = I_CmsLayoutBundle.INSTANCE.galleryDialogCss().groupHeader(); 173 header.addStyleName(groupHeaderClass); 174 addWidgetToList(header); 175 } 176 177 addWidgetToList(treeItem); 178 } 179 m_numItems -= 1; 180 return true; 181 } 182 } 183 184 /** 185 * A class which generates tree items incrementally to fill the galleries tab.<p> 186 */ 187 protected class TreeItemGenerator implements Iterator<CmsTreeItem> { 188 189 /** The internal iterator over the gallery beans. <p> */ 190 protected Iterator<CmsGalleryTreeEntry> m_beanIterator; 191 192 /** 193 * Creates a new instance.<p> 194 * 195 * @param folders the folders from which to generate list items 196 */ 197 public TreeItemGenerator(List<CmsGalleryTreeEntry> folders) { 198 199 if (folders == null) { 200 folders = new ArrayList<CmsGalleryTreeEntry>(); 201 } 202 203 m_beanIterator = folders.iterator(); 204 } 205 206 /** 207 * @see java.util.Iterator#hasNext() 208 */ 209 public boolean hasNext() { 210 211 return m_beanIterator.hasNext(); 212 } 213 214 /** 215 * @see java.util.Iterator#next() 216 */ 217 public CmsTreeItem next() { 218 219 CmsGalleryTreeEntry gallery = m_beanIterator.next(); 220 CmsTreeItem treeItem = createTreeItem(gallery, m_selectedGalleries, true, false); 221 addChildren(treeItem, gallery.getChildren(), m_selectedGalleries); 222 treeItem.setOpen(true); 223 return treeItem; 224 } 225 226 /** 227 * @see java.util.Iterator#remove() 228 */ 229 public void remove() { 230 231 throw new UnsupportedOperationException(); 232 } 233 } 234 235 /** 236 * Handles the change of the item selection.<p> 237 */ 238 private class SelectionHandler extends A_SelectionHandler { 239 240 /** The gallery path as id for the selected gallery. */ 241 private String m_galleryPath; 242 243 /** 244 * Constructor.<p> 245 * 246 * @param gallerPath as id for the selected category 247 * @param checkBox the reference to the checkbox 248 */ 249 public SelectionHandler(String gallerPath, CmsCheckBox checkBox) { 250 251 super(checkBox); 252 m_galleryPath = gallerPath; 253 m_selectionHandlers.add(this); 254 } 255 256 /** 257 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab.A_SelectionHandler#onSelectionChange() 258 */ 259 @Override 260 protected void onSelectionChange() { 261 262 if (getCheckBox().isChecked()) { 263 getTabHandler().onSelectGallery(m_galleryPath); 264 } else { 265 getTabHandler().onDeselectGallery(m_galleryPath); 266 } 267 268 } 269 270 /** 271 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab.A_SelectionHandler#selectBeforeGoingToResultTab() 272 */ 273 @Override 274 protected void selectBeforeGoingToResultTab() { 275 276 for (SelectionHandler otherHandler : m_selectionHandlers) { 277 if ((otherHandler != this) 278 && (otherHandler.getCheckBox() != null) 279 && otherHandler.getCheckBox().isChecked()) { 280 otherHandler.getCheckBox().setChecked(false); 281 otherHandler.onSelectionChange(); 282 } 283 } 284 getCheckBox().setChecked(true); 285 onSelectionChange(); 286 } 287 } 288 289 /** The batch size for adding new elements to the tab.<p> */ 290 protected static final int LOAD_BATCH_SIZE = 50; 291 292 /** The labels to display for groups. */ 293 protected Map<CmsGalleryGroup, String> m_groupLabels = new HashMap<>(); 294 295 /** An iterator which produces new list items which should be added to the tab.<p> */ 296 protected Iterator<CmsTreeItem> m_itemIterator; 297 298 /** The group of the gallery folder list item that was last rendered. */ 299 protected CmsGalleryGroup m_lastGroup; 300 301 /** List of selected galleries. */ 302 protected List<String> m_selectedGalleries; 303 304 /** The selection handlers. */ 305 List<SelectionHandler> m_selectionHandlers = Lists.newArrayList(); 306 307 /** Map of gallery folders by path. */ 308 private Map<String, CmsGalleryFolderBean> m_galleries; 309 310 /** Flag which indicates whether new elements are currently being inserted into the galleries tab.<p> */ 311 private boolean m_loading; 312 313 /** The tab handler. */ 314 private CmsGalleriesTabHandler m_tabHandler; 315 316 /** 317 * Constructor.<p> 318 * 319 * @param tabHandler the tab handler 320 */ 321 public CmsGalleriesTab(CmsGalleriesTabHandler tabHandler) { 322 323 super(GalleryTabId.cms_tab_galleries); 324 getList().addScrollHandler(new CmsScrollToBottomHandler(new Runnable() { 325 326 public void run() { 327 328 if (!isLoading()) { 329 loadMoreItems(); 330 } 331 } 332 })); 333 m_tabHandler = tabHandler; 334 m_galleries = new HashMap<String, CmsGalleryFolderBean>(); 335 init(); 336 } 337 338 /** 339 * Fill the content of the galleries tab panel.<p> 340 * 341 * @param galleryInfos the gallery info beans 342 * @param selectedGalleries the list of galleries to select 343 * @param grouped true if the items should be broken into groups 344 */ 345 public void fillContent(List<CmsGalleryFolderBean> galleryInfos, List<String> selectedGalleries, boolean grouped) { 346 347 clearList(); 348 m_lastGroup = null; 349 m_selectedGalleries = selectedGalleries; 350 if (!galleryInfos.isEmpty()) { 351 for (CmsGalleryFolderBean galleryInfo : galleryInfos) { 352 m_galleries.put(galleryInfo.getPath(), galleryInfo); 353 } 354 m_itemIterator = new ListItemGenerator(galleryInfos, grouped); 355 loadMoreItems(); 356 } else { 357 showIsEmptyLabel(); 358 } 359 onContentChange(); 360 } 361 362 /** 363 * @see org.opencms.ade.galleries.client.ui.A_CmsTab#getParamPanels(org.opencms.ade.galleries.shared.CmsGallerySearchBean) 364 */ 365 @Override 366 public List<CmsSearchParamPanel> getParamPanels(CmsGallerySearchBean searchObj) { 367 368 List<CmsSearchParamPanel> result = new ArrayList<CmsSearchParamPanel>(); 369 for (String galleryPath : searchObj.getGalleries()) { 370 CmsGalleryFolderBean galleryBean = m_galleries.get(galleryPath); 371 if (galleryBean != null) { 372 String title = galleryBean.getTitle(); 373 if (CmsStringUtil.isEmptyOrWhitespaceOnly(title)) { 374 title = galleryBean.getPath(); 375 } 376 CmsSearchParamPanel panel = new CmsSearchParamPanel( 377 Messages.get().key(Messages.GUI_PARAMS_LABEL_GALLERIES_0), 378 this); 379 panel.setContent(title, galleryPath); 380 result.add(panel); 381 } 382 } 383 return result; 384 } 385 386 /** 387 * Returns the value of the "loading" flag, which indicates whether new elements are currently being added into the galleries tab.<p> 388 * 389 * @return the "loading" flag 390 */ 391 public boolean isLoading() { 392 393 return m_loading; 394 395 } 396 397 /** 398 * @see org.opencms.ade.galleries.client.ui.A_CmsTab#onSelection() 399 */ 400 @Override 401 public void onSelection() { 402 403 super.onSelection(); 404 Timer timer = new Timer() { 405 406 @Override 407 public void run() { 408 409 m_quickSearch.setFocus(true); 410 } 411 }; 412 timer.schedule(20); 413 } 414 415 /** 416 * Sets the "loading" flag.<p> 417 * 418 * @param loading the new value of the loading flag 419 */ 420 public void setLoading(boolean loading) { 421 422 m_loading = loading; 423 } 424 425 /** 426 * De-selects the galleries in the galleries list.<p> 427 * 428 * @param galleries the galleries to deselect 429 */ 430 public void uncheckGalleries(List<String> galleries) { 431 432 for (String gallery : galleries) { 433 CmsListItem item = searchTreeItem(m_scrollList, gallery); 434 if (item != null) { 435 item.getCheckBox().setChecked(false); 436 } 437 } 438 } 439 440 /** 441 * Update the galleries list.<p> 442 * 443 * @param galleries the new gallery list 444 * @param selectedGalleries the list of galleries to select 445 * @param useGroups true if the galleries should be broken into groups (this assumes the galleries have already been sorted correctly) 446 */ 447 public void updateListContent( 448 List<CmsGalleryFolderBean> galleries, 449 List<String> selectedGalleries, 450 boolean useGroups) { 451 452 clearList(); 453 fillContent(galleries, selectedGalleries, useGroups); 454 } 455 456 /** 457 * Update the galleries tree.<p> 458 * 459 * @param galleryTreeEntries the new gallery tree list 460 * @param selectedGalleries the list of galleries to select 461 */ 462 public void updateTreeContent(List<CmsGalleryTreeEntry> galleryTreeEntries, List<String> selectedGalleries) { 463 464 clearList(); 465 m_selectedGalleries = selectedGalleries; 466 if (!galleryTreeEntries.isEmpty()) { 467 m_itemIterator = new TreeItemGenerator(galleryTreeEntries); 468 loadMoreItems(); 469 } else { 470 showIsEmptyLabel(); 471 } 472 onContentChange(); 473 } 474 475 /** 476 * Adds children to the gallery tree and select the galleries.<p> 477 * 478 * @param parent the parent item 479 * @param children the list of children 480 * @param selectedGalleries the list of galleries to select 481 */ 482 protected void addChildren(CmsTreeItem parent, List<CmsGalleryTreeEntry> children, List<String> selectedGalleries) { 483 484 if (children != null) { 485 for (CmsGalleryTreeEntry child : children) { 486 // set the category tree item and add to parent tree item 487 CmsTreeItem treeItem = createTreeItem(child, selectedGalleries, true, false); 488 if ((selectedGalleries != null) && selectedGalleries.contains(child.getPath())) { 489 parent.setOpen(true); 490 openParents(parent); 491 } 492 parent.addChild(treeItem); 493 addChildren(treeItem, child.getChildren(), selectedGalleries); 494 } 495 } 496 } 497 498 /** 499 * Creates a tree item widget used in list and tree view of this tab.<p> 500 * 501 * @param galleryInfo the gallery folder bean 502 * @param selectedGalleries the selected galleries 503 * @param forTree <code>true</code> if the item is used within tree view 504 * @param useGroups true if the gallery tree items should be broken into groups 505 * 506 * @return the tree item 507 */ 508 protected CmsTreeItem createTreeItem( 509 CmsGalleryFolderBean galleryInfo, 510 List<String> selectedGalleries, 511 boolean forTree, 512 boolean useGroups) { 513 514 CmsListItemWidget listItemWidget = new CmsListItemWidget(galleryInfo); 515 listItemWidget.setUnselectable(); 516 CmsCheckBox checkBox = new CmsCheckBox(); 517 SelectionHandler selectionHandler = new SelectionHandler(galleryInfo.getPath(), checkBox); 518 checkBox.addClickHandler(selectionHandler); 519 listItemWidget.addClickHandler(selectionHandler); 520 if ((selectedGalleries != null) && selectedGalleries.contains(galleryInfo.getPath())) { 521 checkBox.setChecked(true); 522 } 523 524 if (galleryInfo.isEditable()) { 525 String uploadAction = galleryInfo.getUploadAction(); 526 527 if (null != uploadAction) { 528 CmsPushButton uploadButton = new CmsPushButton(I_CmsButton.UPLOAD_SMALL); 529 uploadButton.setText(null); 530 uploadButton.setTitle(Messages.get().key(Messages.GUI_GALLERY_UPLOAD_TITLE_1, galleryInfo.getPath())); 531 uploadButton.setButtonStyle(ButtonStyle.FONT_ICON, null); 532 uploadButton.addClickHandler(new ClickHandler() { 533 534 public void onClick(ClickEvent event) { 535 536 // prevent event from bubbling up to surrounding widget 537 event.stopPropagation(); 538 539 CmsRpcAction<CmsUUID> action = new CmsRpcAction<CmsUUID>() { 540 541 @Override 542 public void execute() { 543 544 start(0, true); 545 CmsCoreProvider.getVfsService().getStructureId(galleryInfo.getPath(), this); 546 } 547 548 @Override 549 protected void onResponse(CmsUUID result) { 550 551 stop(false); 552 List<CmsUUID> resultIds = new ArrayList<>(); 553 resultIds.add(result); 554 CmsEmbeddedDialogHandler.openDialog( 555 uploadAction, 556 resultIds, 557 id -> getTabHandler().updateIndex()); 558 } 559 560 }; 561 action.execute(); 562 563 } 564 }); 565 listItemWidget.addButton(uploadButton); 566 567 } else { 568 if (CmsEditExternalLinkDialog.LINK_GALLERY_RESOURCE_TYPE_NAME.equals(galleryInfo.getType())) { 569 CmsPushButton createExternalLink = createNewExternalLinkButton(galleryInfo.getPath()); 570 if (createExternalLink != null) { 571 listItemWidget.addButton(createExternalLink); 572 } 573 } else { 574 if (!CmsCoreProvider.get().isUploadDisabled()) { 575 String uploadPath = CmsStringUtil.joinPaths( 576 CmsCoreProvider.get().getSiteRoot(), 577 galleryInfo.getPath()); 578 if (CmsCoreProvider.get().getUploadRestriction().isUploadEnabled(uploadPath)) { 579 listItemWidget.addButton(createUploadButtonForTarget(galleryInfo.getPath(), false)); 580 } 581 } 582 } 583 } 584 } 585 listItemWidget.addButton(createSelectButton(selectionHandler)); 586 if (m_tabHandler.hasGalleriesSelectable()) { 587 CmsPushButton selectButton = createSelectResourceButton( 588 galleryInfo.getPath(), 589 CmsUUID.getNullUUID(), 590 "", 591 ""); 592 listItemWidget.addButton(selectButton); 593 } 594 595 CmsTreeItem treeItem = new CmsTreeItem(forTree, checkBox, listItemWidget); 596 if (useGroups) { 597 treeItem.setData(galleryInfo.getGroup()); 598 } 599 if (galleryInfo.getGroup() != null) { 600 m_groupLabels.putIfAbsent(galleryInfo.getGroup(), galleryInfo.getGroupLabel()); 601 } 602 treeItem.setId(galleryInfo.getPath()); 603 return treeItem; 604 } 605 606 /** 607 * Gets the label to display for a group. 608 * 609 * @param group the gallery group 610 * @return the label to display for the gallery group 611 */ 612 protected String getGroupName(CmsGalleryGroup group) { 613 614 if (m_groupLabels.containsKey(group)) { 615 return m_groupLabels.get(group); 616 } else { 617 return "" + group; 618 } 619 } 620 621 /** 622 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#getSortList() 623 */ 624 @Override 625 protected LinkedHashMap<String, String> getSortList() { 626 627 LinkedHashMap<String, String> list = new LinkedHashMap<String, String>(); 628 list.put(SortParams.grouped.name(), Messages.get().key(Messages.GUI_SORT_LABEL_GROUPED_0)); 629 list.put(SortParams.grouped_title.name(), Messages.get().key(Messages.GUI_SORT_LABEL_GROUPED_TITLE_0)); 630 list.put(SortParams.title_asc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TITLE_ASC_0)); 631 list.put(SortParams.title_desc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TITLE_DECS_0)); 632 list.put(SortParams.type_asc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TYPE_ASC_0)); 633 list.put(SortParams.type_desc.name(), Messages.get().key(Messages.GUI_SORT_LABEL_TYPE_DESC_0)); 634 list.put(SortParams.tree.name(), Messages.get().key(Messages.GUI_SORT_LABEL_HIERARCHIC_0)); 635 636 return list; 637 } 638 639 /** 640 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#getTabHandler() 641 */ 642 @Override 643 protected CmsGalleriesTabHandler getTabHandler() { 644 645 return m_tabHandler; 646 } 647 648 /** 649 * @see org.opencms.ade.galleries.client.ui.A_CmsListTab#hasQuickFilter() 650 */ 651 @Override 652 protected boolean hasQuickFilter() { 653 654 // allow filter if not in tree mode 655 return SortParams.tree != SortParams.valueOf(m_sortSelectBox.getFormValueAsString()); 656 } 657 658 /** 659 * Adds more gallery list items to display in the tab, if available.<p> 660 */ 661 protected void loadMoreItems() { 662 663 setLoading(true); 664 MoreItemsCommand cmd = new MoreItemsCommand(30); 665 Scheduler.get().scheduleFixedDelay(cmd, 1); 666 } 667 668 /** 669 * Goes up the tree and opens the parents of the item.<p> 670 * 671 * @param item the child item to start from 672 */ 673 private void openParents(CmsTreeItem item) { 674 675 if (item != null) { 676 item.setOpen(true); 677 openParents(item.getParentItem()); 678 } 679 } 680 681 /** 682 * Shows the tab list is empty label.<p> 683 */ 684 private void showIsEmptyLabel() { 685 686 CmsSimpleListItem item = new CmsSimpleListItem(); 687 Label isEmptyLabel = new Label(Messages.get().key(Messages.GUI_TAB_GALLERIES_IS_EMPTY_0)); 688 item.add(isEmptyLabel); 689 m_scrollList.add(item); 690 } 691}