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.ui.favorites; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsResource; 032import org.opencms.file.CmsResourceFilter; 033import org.opencms.i18n.CmsEncoder; 034import org.opencms.main.CmsException; 035import org.opencms.main.CmsLog; 036import org.opencms.main.OpenCms; 037import org.opencms.site.CmsSite; 038import org.opencms.ui.A_CmsUI; 039import org.opencms.ui.CmsVaadinUtils; 040import org.opencms.ui.Messages; 041import org.opencms.ui.components.CmsBasicDialog; 042import org.opencms.ui.components.CmsErrorDialog; 043import org.opencms.ui.components.CmsResourceIcon; 044import org.opencms.ui.components.OpenCmsTheme; 045import org.opencms.ui.components.editablegroup.CmsDefaultActionHandler; 046import org.opencms.ui.components.editablegroup.CmsEditableGroup; 047import org.opencms.ui.components.editablegroup.CmsEditableGroupButtons; 048import org.opencms.ui.components.editablegroup.I_CmsEditableGroupRow; 049import org.opencms.util.CmsStringUtil; 050import org.opencms.util.CmsUUID; 051import org.opencms.workplace.explorer.CmsResourceUtil; 052 053import java.util.ArrayList; 054import java.util.HashMap; 055import java.util.List; 056import java.util.Map; 057import java.util.Optional; 058 059import org.apache.commons.logging.Log; 060 061import com.vaadin.shared.ui.ContentMode; 062import com.vaadin.ui.Button; 063import com.vaadin.ui.Component; 064import com.vaadin.ui.Label; 065import com.vaadin.ui.VerticalLayout; 066import com.vaadin.v7.data.Item; 067import com.vaadin.v7.data.util.IndexedContainer; 068 069/** 070 * Dialog which shows the list of favorites for the current user and allows them to jump to individual favorites, 071 * edit the list, or add the current location to the favorite list. 072 */ 073public class CmsFavoriteDialog extends CmsBasicDialog implements CmsEditableGroup.I_RowBuilder { 074 075 /** 076 * Action handler that saves favorites after every change. 077 */ 078 public class SaveAfterChangeActionHandler extends CmsDefaultActionHandler { 079 080 /** 081 * Creates a new instance. 082 * 083 * @param row the row 084 */ 085 @SuppressWarnings("synthetic-access") 086 public SaveAfterChangeActionHandler(I_CmsEditableGroupRow row) { 087 088 super(m_group, row); 089 } 090 091 /** 092 * @see org.opencms.ui.components.editablegroup.CmsDefaultActionHandler#onAdd() 093 */ 094 @SuppressWarnings("synthetic-access") 095 @Override 096 public void onAdd() { 097 098 super.onAdd(); 099 doSave(); 100 101 } 102 103 /** 104 * @see org.opencms.ui.components.editablegroup.CmsDefaultActionHandler#onDelete() 105 */ 106 @Override 107 public void onDelete() { 108 109 super.onDelete(); 110 doSave(); 111 112 } 113 114 /** 115 * @see org.opencms.ui.components.editablegroup.CmsDefaultActionHandler#onDown() 116 */ 117 @SuppressWarnings("synthetic-access") 118 @Override 119 public void onDown() { 120 121 super.onDown(); 122 doSave(); 123 124 } 125 126 /** 127 * @see org.opencms.ui.components.editablegroup.CmsDefaultActionHandler#onUp() 128 */ 129 @Override 130 public void onUp() { 131 132 super.onUp(); 133 doSave(); 134 } 135 136 } 137 138 /** 139 * Handles changes in empty/not empty state by hiding or displaying a message. 140 */ 141 class EmptyHandler implements CmsEditableGroup.I_EmptyHandler { 142 143 /** The group. */ 144 private CmsEditableGroup m_groupForHandler; 145 146 /** The placeholder. */ 147 private Label m_placeholder; 148 149 /** 150 * @see org.opencms.ui.components.editablegroup.CmsEditableGroup.I_EmptyHandler#init(org.opencms.ui.components.editablegroup.CmsEditableGroup) 151 */ 152 public void init(CmsEditableGroup group) { 153 154 String message = CmsVaadinUtils.getMessageText(Messages.GUI_FAVORITES_EMPTY_LIST_PLACEHOLDER_0); 155 m_groupForHandler = group; 156 m_placeholder = new Label(); 157 m_placeholder.setContentMode(ContentMode.HTML); 158 String spacer = "<div></div>"; 159 String content = "<div>" + CmsEncoder.escapeXml(message) + "</div>"; 160 m_placeholder.setValue(spacer + content + spacer); 161 m_placeholder.addStyleName(OpenCmsTheme.BOOKMARKS_PLACEHOLDER); 162 m_placeholder.setHeight("100%"); 163 164 } 165 166 /** 167 * @see org.opencms.ui.components.editablegroup.CmsEditableGroup.I_EmptyHandler#setEmpty(boolean) 168 */ 169 public void setEmpty(boolean empty) { 170 171 if (!m_placeholder.isAttached()) { 172 m_groupForHandler.getContainer().addComponent(m_placeholder); 173 m_groupForHandler.getContainer().setExpandRatio(m_placeholder, 1.0f); 174 } 175 m_groupForHandler.getContainer().setHeight(empty ? "100%" : null); 176 m_placeholder.setVisible(empty); 177 } 178 179 } 180 181 /** Logger instance for this class. */ 182 private static final Log LOG = CmsLog.getLog(CmsFavoriteDialog.class); 183 184 /** Serial version id. */ 185 private static final long serialVersionUID = 1L; 186 187 /** The Add button. */ 188 private Button m_addButton; 189 190 /** The Cancel button. */ 191 private Button m_cancelButton; 192 193 /** The favorite context. */ 194 private I_CmsFavoriteContext m_context; 195 196 /** Current favorite location. */ 197 private Optional<CmsFavoriteEntry> m_currentLocation; 198 199 /** The container layout for the favorite widgets. */ 200 private VerticalLayout m_favContainer; 201 202 /** Load/save handler for favorites. */ 203 private CmsFavoriteDAO m_favDao; 204 205 /** The group for the favorite widgets. */ 206 private CmsEditableGroup m_group; 207 208 /** Map of project labels. */ 209 private Map<CmsUUID, String> m_projectLabels = new HashMap<>(); 210 211 /** Container for the sites, used for their labels. */ 212 private IndexedContainer m_sitesContainer; 213 214 /** 215 * Creates a new dialog instance. 216 * 217 * @param context the favorite context 218 * @param favDao the favorite load/save handler 219 */ 220 public CmsFavoriteDialog(I_CmsFavoriteContext context, CmsFavoriteDAO favDao) 221 throws CmsException { 222 223 super(); 224 m_favDao = favDao; 225 m_context = context; 226 context.setDialog(this); 227 m_sitesContainer = CmsVaadinUtils.getAvailableSitesContainer(A_CmsUI.getCmsObject(), "caption"); 228 CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), null); 229 List<CmsFavoriteEntry> entries = m_favDao.loadFavorites(); 230 m_cancelButton.addClickListener(evt -> m_context.close()); 231 m_favContainer.addLayoutClickListener(evt -> { 232 CmsFavoriteEntry entry = getEntry(evt.getChildComponent()); 233 if (entry != null) { // may be null when user double clicks on delete icon 234 m_context.openFavorite(entry); 235 } 236 }); 237 m_currentLocation = context.getFavoriteForCurrentLocation(); 238 m_addButton.setEnabled(m_currentLocation.isPresent()); 239 m_addButton.setCaption(CmsVaadinUtils.getMessageText(Messages.GUI_FAVORITES_ADD_BUTTON_0)); 240 m_addButton.addClickListener(evt -> onClickAdd()); 241 m_favContainer.removeAllComponents(); 242 m_group = new CmsEditableGroup(m_favContainer, null, new EmptyHandler()); 243 m_group.setAddButtonVisible(false); 244 m_group.setRowBuilder(this); 245 m_group.init(); 246 247 for (CmsFavoriteEntry favEntry : entries) { 248 Component favInfo; 249 try { 250 favInfo = createFavInfo(favEntry); 251 m_group.addRow(favInfo); 252 } catch (CmsException e) { 253 LOG.warn(e.getLocalizedMessage(), e); 254 } 255 256 } 257 } 258 259 /** 260 * @see org.opencms.ui.components.editablegroup.CmsEditableGroup.I_RowBuilder#buildRow(org.opencms.ui.components.editablegroup.CmsEditableGroup, com.vaadin.ui.Component) 261 */ 262 public CmsFavInfo buildRow(CmsEditableGroup group, Component component) { 263 264 CmsFavInfo info = (CmsFavInfo)component; 265 CmsEditableGroupButtons buttons = new CmsEditableGroupButtons(new SaveAfterChangeActionHandler(info)); 266 info.setButtons(buttons); 267 return info; 268 } 269 270 /** 271 * Saves the list of currently displayed favorites. 272 */ 273 protected void doSave() { 274 275 List<CmsFavoriteEntry> entries = getEntries(); 276 try { 277 m_favDao.saveFavorites(entries); 278 } catch (Exception e) { 279 CmsErrorDialog.showErrorDialog(e); 280 } 281 } 282 283 /** 284 * @see org.opencms.ui.components.CmsBasicDialog#enableMaxHeight() 285 */ 286 @Override 287 protected void enableMaxHeight() { 288 // do nothing here, we want to disable the max height mechanism 289 } 290 291 /** 292 * Gets the favorite entries corresponding to the currently displayed favorite widgets. 293 * 294 * @return the list of favorite entries 295 */ 296 List<CmsFavoriteEntry> getEntries() { 297 298 List<CmsFavoriteEntry> result = new ArrayList<>(); 299 for (I_CmsEditableGroupRow row : m_group.getRows()) { 300 CmsFavoriteEntry entry = ((CmsFavInfo)row).getEntry(); 301 result.add(entry); 302 } 303 return result; 304 } 305 306 /** 307 * Creates a favorite widget for a favorite entry. 308 * 309 * @param entry the favorite entry 310 * @return the favorite widget 311 * 312 * @throws CmsException if something goes wrong 313 */ 314 private CmsFavInfo createFavInfo(CmsFavoriteEntry entry) throws CmsException { 315 316 String title = ""; 317 String subtitle = ""; 318 CmsFavInfo result = new CmsFavInfo(entry); 319 CmsObject cms = A_CmsUI.getCmsObject(); 320 String project = getProject(cms, entry); 321 String site = getSite(cms, entry); 322 try { 323 CmsUUID idToLoad = entry.getDetailId() != null ? entry.getDetailId() : entry.getStructureId(); 324 CmsResource resource = cms.readResource(idToLoad, CmsResourceFilter.IGNORE_EXPIRATION.addRequireVisible()); 325 CmsResourceUtil resutil = new CmsResourceUtil(cms, resource); 326 switch (entry.getType()) { 327 case explorerFolder: 328 title = CmsStringUtil.isEmpty(resutil.getTitle()) 329 ? CmsResource.getName(resource.getRootPath()) 330 : resutil.getTitle(); 331 break; 332 case page: 333 title = resutil.getTitle(); 334 break; 335 } 336 subtitle = resource.getRootPath(); 337 CmsResourceIcon icon = result.getResourceIcon(); 338 icon.initContent(resutil, CmsResource.STATE_UNCHANGED, false, false); 339 } catch (CmsException e) { 340 LOG.warn(e.getLocalizedMessage(), e); 341 } 342 result.getTopLine().setValue(title); 343 result.getBottomLine().setValue(subtitle); 344 result.getProjectLabel().setValue(project); 345 result.getSiteLabel().setValue(site); 346 347 return result; 348 349 } 350 351 /** 352 * Gets the favorite entry for a given row. 353 * 354 * @param row the widget used to display the favorite 355 * @return the favorite entry for the widget 356 */ 357 private CmsFavoriteEntry getEntry(Component row) { 358 359 if (row instanceof CmsFavInfo) { 360 361 return ((CmsFavInfo)row).getEntry(); 362 363 } 364 return null; 365 366 } 367 368 /** 369 * Gets the project name for a favorite entry. 370 * 371 * @param cms the CMS context 372 * @param entry the favorite entry 373 * @return the project name for the favorite entry 374 * @throws CmsException if something goes wrong 375 */ 376 private String getProject(CmsObject cms, CmsFavoriteEntry entry) throws CmsException { 377 378 String result = m_projectLabels.get(entry.getProjectId()); 379 if (result == null) { 380 result = cms.readProject(entry.getProjectId()).getName(); 381 m_projectLabels.put(entry.getProjectId(), result); 382 } 383 return result; 384 } 385 386 /** 387 * Gets the site label for the entry. 388 * 389 * @param cms the current CMS context 390 * @param entry the entry 391 * @return the site label for the entry 392 */ 393 private String getSite(CmsObject cms, CmsFavoriteEntry entry) { 394 395 CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(entry.getSiteRoot()); 396 Item item = m_sitesContainer.getItem(entry.getSiteRoot()); 397 if (item != null) { 398 return (String)(item.getItemProperty("caption").getValue()); 399 } 400 String result = entry.getSiteRoot(); 401 if (site != null) { 402 if (!CmsStringUtil.isEmpty(site.getTitle())) { 403 result = site.getTitle(); 404 } 405 } 406 return result; 407 } 408 409 /** 410 * The click handler for the add button. 411 */ 412 private void onClickAdd() { 413 414 if (m_currentLocation.isPresent()) { 415 CmsFavoriteEntry entry = m_currentLocation.get(); 416 List<CmsFavoriteEntry> entries = getEntries(); 417 entries.add(entry); 418 try { 419 m_favDao.saveFavorites(entries); 420 } catch (Exception e) { 421 CmsErrorDialog.showErrorDialog(e); 422 } 423 m_context.close(); 424 } 425 } 426 427}