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.apps.sessions; 029 030import org.opencms.file.CmsUser; 031import org.opencms.main.CmsException; 032import org.opencms.main.CmsLog; 033import org.opencms.main.CmsSessionInfo; 034import org.opencms.main.OpenCms; 035import org.opencms.security.CmsOrganizationalUnit; 036import org.opencms.site.CmsSite; 037import org.opencms.ui.A_CmsUI; 038import org.opencms.ui.CmsCssIcon; 039import org.opencms.ui.CmsVaadinUtils; 040import org.opencms.ui.apps.Messages; 041import org.opencms.ui.components.CmsBasicDialog; 042import org.opencms.ui.components.CmsResourceIcon; 043import org.opencms.ui.components.OpenCmsTheme; 044import org.opencms.ui.contextmenu.CmsContextMenu; 045import org.opencms.ui.contextmenu.CmsMenuItemVisibilityMode; 046import org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry; 047import org.opencms.util.CmsStringUtil; 048 049import java.util.ArrayList; 050import java.util.List; 051import java.util.Locale; 052import java.util.Set; 053 054import org.apache.commons.logging.Log; 055 056import com.vaadin.v7.data.Item; 057import com.vaadin.v7.data.util.IndexedContainer; 058import com.vaadin.v7.data.util.filter.Or; 059import com.vaadin.v7.data.util.filter.SimpleStringFilter; 060import com.vaadin.v7.event.ItemClickEvent; 061import com.vaadin.v7.event.ItemClickEvent.ItemClickListener; 062import com.vaadin.event.MouseEvents; 063import com.vaadin.shared.MouseEventDetails.MouseButton; 064import com.vaadin.v7.shared.ui.label.ContentMode; 065import com.vaadin.ui.Component; 066import com.vaadin.v7.ui.Label; 067import com.vaadin.v7.ui.Table; 068import com.vaadin.ui.Window; 069import com.vaadin.ui.themes.ValoTheme; 070 071/** 072 * Class for the table to show all current sessions.<p> 073 */ 074public class CmsSessionsTable extends Table { 075 076 /** 077 * The menu entry to switch to the explorer of concerning site.<p> 078 */ 079 class KillEntry implements I_CmsSimpleContextMenuEntry<Set<String>> { 080 081 /** 082 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 083 */ 084 public void executeAction(Set<String> data) { 085 086 showKillDialog( 087 data, 088 CmsVaadinUtils.getMessageText( 089 Messages.GUI_MESSAGES_DESTROY_SESSIONS_1, 090 CmsSessionsApp.getUserNames(data, CmsVaadinUtils.getMessageText(Messages.GUI_MESSAGES_AND_0))), 091 CmsSessionsTable.this); 092 093 } 094 095 /** 096 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 097 */ 098 public String getTitle(Locale locale) { 099 100 return CmsVaadinUtils.getMessageText(Messages.GUI_MESSAGES_DESTROY_SESSION_0); 101 } 102 103 /** 104 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 105 */ 106 public CmsMenuItemVisibilityMode getVisibility(Set<String> data) { 107 108 if ((data.size() == 1) & data.iterator().next().equals(m_mySessionId)) { 109 return CmsMenuItemVisibilityMode.VISIBILITY_INACTIVE; 110 } 111 112 return CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE; 113 } 114 115 } 116 117 /** 118 * The menu entry to switch to the explorer of concerning site.<p> 119 */ 120 class SendBroadcastEntry implements I_CmsSimpleContextMenuEntry<Set<String>> { 121 122 /** 123 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 124 */ 125 public void executeAction(Set<String> data) { 126 127 CmsSessionsApp.showSendBroadcastDialog( 128 data, 129 CmsVaadinUtils.getMessageText( 130 Messages.GUI_MESSAGES_BROADCAST_SESSIONS_1, 131 CmsSessionsApp.getUserNames(data, CmsVaadinUtils.getMessageText(Messages.GUI_MESSAGES_AND_0))), 132 CmsSessionsTable.this); 133 134 } 135 136 /** 137 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 138 */ 139 public String getTitle(Locale locale) { 140 141 return CmsVaadinUtils.getMessageText(Messages.GUI_MESSAGES_BROADCAST_SEND_0); 142 } 143 144 /** 145 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 146 */ 147 public CmsMenuItemVisibilityMode getVisibility(Set<String> data) { 148 149 return CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE; 150 } 151 152 } 153 154 /** 155 * All table properties.<p> 156 */ 157 enum TableProperty { 158 159 /**Date of release column.*/ 160 DateCreated(Messages.GUI_MESSAGES_BROADCAST_COLS_CREATION_0, String.class, "", false), 161 /**Icon.*/ 162 Icon(null, Label.class, null, false), 163 164 /**Icon column.*/ 165 IS_ACTIVE(Messages.GUI_MESSAGES_BROADCAST_COLS_STATUS_0, Long.class, new Long(0L), false), 166 167 /**Is Broadcast send but not displayed.*/ 168 IS_WAITING(null, Boolean.class, new Boolean(false), false), 169 170 /**Date of expiration column. */ 171 OrgUnit(Messages.GUI_MESSAGES_BROADCAST_COLS_ORGUNIT_0, String.class, "", false), 172 173 /**Last modified column. */ 174 Project(Messages.GUI_MESSAGES_BROADCAST_COLS_PROJECT_0, String.class, "", false), 175 176 /**Path column.*/ 177 Site(Messages.GUI_MESSAGES_BROADCAST_COLS_SITE_0, String.class, "", false), 178 179 /**Broken links column. */ 180 UserName(Messages.GUI_MESSAGES_BROADCAST_COLS_USER_0, String.class, "", false); 181 182 /**Indicates if column is collapsable.*/ 183 private boolean m_collapsable; 184 185 /**Default value for column.*/ 186 private Object m_defaultValue; 187 188 /**Header Message key.*/ 189 private String m_headerMessage; 190 191 /**Type of column property.*/ 192 private Class<?> m_type; 193 194 /** 195 * constructor. 196 * 197 * @param headerMessage key 198 * @param type to property 199 * @param defaultValue of column 200 * @param collapsable should this column be collapsable? 201 */ 202 TableProperty(String headerMessage, Class<?> type, Object defaultValue, boolean collapsable) { 203 204 m_headerMessage = headerMessage; 205 m_type = type; 206 m_defaultValue = defaultValue; 207 m_collapsable = collapsable; 208 } 209 210 /** 211 * Returns list of all properties with non-empty header.<p> 212 * 213 * @return list of properties 214 */ 215 static List<TableProperty> withHeader() { 216 217 List<TableProperty> props = new ArrayList<TableProperty>(); 218 219 for (TableProperty prop : TableProperty.values()) { 220 if (prop.m_headerMessage != null) { 221 props.add(prop); 222 } 223 } 224 return props; 225 } 226 227 /** 228 * Returns the default value of property.<p> 229 * 230 * @return object 231 */ 232 Object getDefaultValue() { 233 234 return m_defaultValue; 235 } 236 237 /** 238 * Returns localized header.<p> 239 * 240 * @return string for header 241 */ 242 String getLocalizedMessage() { 243 244 if (m_headerMessage == null) { 245 return ""; 246 } 247 return CmsVaadinUtils.getMessageText(m_headerMessage); 248 } 249 250 /** 251 * Returns tye of value for given property.<p> 252 * 253 * @return type 254 */ 255 Class<?> getType() { 256 257 return m_type; 258 } 259 260 /** 261 * Indicates if column is collapsable.<p> 262 * 263 * @return boolean, true = is collapsable 264 */ 265 boolean isCollapsable() { 266 267 return m_collapsable; 268 } 269 270 } 271 272 /** 273 * User entry.<p> 274 */ 275 class UserEntry implements I_CmsSimpleContextMenuEntry<Set<String>>, I_CmsSimpleContextMenuEntry.I_HasCssStyles { 276 277 /** 278 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#executeAction(java.lang.Object) 279 */ 280 public void executeAction(Set<String> context) { 281 282 showUserInfoWindow(context.iterator().next()); 283 284 } 285 286 /** 287 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry.I_HasCssStyles#getStyles() 288 */ 289 public String getStyles() { 290 291 return ValoTheme.LABEL_BOLD; 292 } 293 294 /** 295 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getTitle(java.util.Locale) 296 */ 297 public String getTitle(Locale locale) { 298 299 return CmsVaadinUtils.getMessageText(Messages.GUI_MESSAGES_SHOW_USER_0); 300 } 301 302 /** 303 * @see org.opencms.ui.contextmenu.I_CmsSimpleContextMenuEntry#getVisibility(java.lang.Object) 304 */ 305 public CmsMenuItemVisibilityMode getVisibility(Set<String> data) { 306 307 return (data != null) && (data.size() == 1) 308 ? CmsMenuItemVisibilityMode.VISIBILITY_ACTIVE 309 : CmsMenuItemVisibilityMode.VISIBILITY_INVISIBLE; 310 } 311 312 } 313 314 /** The logger for this class. */ 315 static Log LOG = CmsLog.getLog(CmsSessionsTable.class.getName()); 316 317 /**Time limit (in milliseconds) since when a user is inactive.*/ 318 public static final long INACTIVE_LIMIT = 3 * 60 * 1000; //3 minute 319 320 /**vaadin serial id.*/ 321 private static final long serialVersionUID = 4136423899776482696L; 322 323 /**Session id of user who uses the tool.*/ 324 protected String m_mySessionId; 325 326 /**Container holding table data.*/ 327 private IndexedContainer m_container; 328 329 /** The context menu. */ 330 private CmsContextMenu m_menu; 331 332 /** The available menu entries. */ 333 private List<I_CmsSimpleContextMenuEntry<Set<String>>> m_menuEntries; 334 335 /** 336 * public constructor.<p> 337 */ 338 public CmsSessionsTable() { 339 340 try { 341 342 m_mySessionId = OpenCms.getSessionManager().getSessionInfo( 343 CmsVaadinUtils.getRequest()).getSessionId().getStringValue(); 344 345 ini(); 346 347 setColumnWidth(TableProperty.IS_ACTIVE, 80); 348 349 addGeneratedColumn(TableProperty.Icon, new ColumnGenerator() { 350 351 private static final long serialVersionUID = 1431421875590401227L; 352 353 public Object generateCell(Table source, Object itemId, Object columnId) { 354 355 CmsCssIcon icon = new CmsCssIcon(OpenCmsTheme.ICON_SESSION); 356 if (((Boolean)source.getItem(itemId).getItemProperty( 357 TableProperty.IS_WAITING).getValue()).booleanValue()) { 358 icon.setOverlay(OpenCmsTheme.STATE_CHANGED + " " + CmsResourceIcon.ICON_CLASS_CHANGED); 359 } 360 return new Label(icon.getHtmlWithOverlay(), ContentMode.HTML); 361 } 362 363 }); 364 365 setCellStyleGenerator(new CellStyleGenerator() { 366 367 private static final long serialVersionUID = 1L; 368 369 public String getStyle(Table source, Object itemId, Object propertyId) { 370 371 String furtherClass = ""; 372 if (m_mySessionId.equals(itemId)) { 373 furtherClass = " " + OpenCmsTheme.IN_NAVIGATION; 374 } 375 376 if (TableProperty.UserName.equals(propertyId)) { 377 return " " + OpenCmsTheme.HOVER_COLUMN + furtherClass; 378 } 379 380 if (((Boolean)source.getItem(itemId).getItemProperty( 381 TableProperty.IS_WAITING).getValue()).booleanValue() & (propertyId == null)) { 382 return " " + OpenCmsTheme.STATE_CHANGED; 383 } 384 385 if (TableProperty.IS_ACTIVE.equals(propertyId)) { 386 return CmsUserInfoDialog.getStatusStyleForItem( 387 (Long)source.getItem(itemId).getItemProperty(TableProperty.IS_ACTIVE).getValue()); 388 389 } 390 391 return null; 392 } 393 }); 394 addItemClickListener(new ItemClickListener() { 395 396 private static final long serialVersionUID = 7957778390938304845L; 397 398 public void itemClick(ItemClickEvent event) { 399 400 onItemClick(event, event.getItemId(), event.getPropertyId()); 401 } 402 403 }); 404 405 addGeneratedColumn(TableProperty.IS_ACTIVE, new ColumnGenerator() { 406 407 private static final long serialVersionUID = -6781906011584975559L; 408 409 public Object generateCell(Table source, Object itemId, Object columnId) { 410 411 return CmsUserInfoDialog.getStatusForItem( 412 (Long)source.getItem(itemId).getItemProperty(TableProperty.IS_ACTIVE).getValue()); 413 414 } 415 416 }); 417 418 setItemDescriptionGenerator(new ItemDescriptionGenerator() { 419 420 private static final long serialVersionUID = 7367011213487089661L; 421 422 public String generateDescription(Component source, Object itemId, Object propertyId) { 423 424 if (TableProperty.IS_ACTIVE.equals(propertyId)) { 425 426 String[] ret = CmsSessionInfo.getHourMinuteSecondTimeString( 427 ((Long)((Table)source).getItem(itemId).getItemProperty(propertyId).getValue()).longValue()); 428 429 if (Integer.parseInt(ret[1]) == 1) { 430 return CmsVaadinUtils.getMessageText(Messages.GUI_MESSAGES_LAST_ACTIVITY_ONE_MINUTE_0); 431 } 432 if (Integer.parseInt(ret[1]) == 0) { 433 return CmsVaadinUtils.getMessageText(Messages.GUI_MESSAGES_LAST_ACTIVITY_LESS_ONE_MINUTE_0); 434 } 435 return CmsVaadinUtils.getMessageText( 436 Messages.GUI_MESSAGES_LAST_ACTIVITY_MINUTES_1, 437 new Integer(ret[1])); 438 } 439 return null; 440 } 441 }); 442 443 } catch (CmsException e) { 444 LOG.error("Unable to read sessions", e); 445 } 446 447 } 448 449 /** 450 * Runnable called when a window should be closed.<p> 451 * Reinitializes the table.<p> 452 * 453 * @param window to be closed 454 * @param table to be updated 455 * @return a runnable 456 */ 457 protected static Runnable getCloseRunnable(final Window window, final CmsSessionsTable table) { 458 459 return new Runnable() { 460 461 public void run() { 462 463 window.close(); 464 try { 465 table.ini(); 466 } catch (CmsException e) { 467 LOG.error("Error on reading session information", e); 468 } 469 470 } 471 472 }; 473 } 474 475 /** 476 *Shows the dialog to destroy given sessions.<p> 477 * 478 * @param ids to kill session 479 * @param caption of the window 480 * @param table to be updated 481 */ 482 protected static void showKillDialog(Set<String> ids, String caption, final CmsSessionsTable table) { 483 484 final Window window = CmsBasicDialog.prepareWindow(); 485 window.setCaption(caption); 486 window.setContent(new CmsKillSessionDialog(ids, getCloseRunnable(window, table))); 487 A_CmsUI.get().addWindow(window); 488 } 489 490 /** 491 * Filters the table according to given search string.<p> 492 * 493 * @param search string to be looked for. 494 */ 495 public void filterTable(String search) { 496 497 m_container.removeAllContainerFilters(); 498 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(search)) { 499 m_container.addContainerFilter( 500 new Or( 501 new SimpleStringFilter(TableProperty.UserName, search, true, false), 502 new SimpleStringFilter(TableProperty.Site, search, true, false), 503 new SimpleStringFilter(TableProperty.Project, search, true, false))); 504 } 505 if ((getValue() != null) & !((Set<String>)getValue()).isEmpty()) { 506 setCurrentPageFirstItemId(((Set<String>)getValue()).iterator().next()); 507 } 508 } 509 510 /** 511 * Initializes the table.<p> 512 * 513 * @throws CmsException when something goes wrong 514 */ 515 protected void ini() throws CmsException { 516 517 if (m_container == null) { 518 m_container = new IndexedContainer(); 519 setContainerDataSource(m_container); 520 } else { 521 m_container.removeAllItems(); 522 } 523 for (TableProperty prop : TableProperty.values()) { 524 m_container.addContainerProperty(prop, prop.getType(), prop.getDefaultValue()); 525 setColumnHeader(prop, prop.getLocalizedMessage()); 526 } 527 528 setColumnWidth(TableProperty.Icon, 40); 529 setSelectable(true); 530 setMultiSelect(true); 531 532 m_menu = new CmsContextMenu(); 533 m_menu.setAsTableContextMenu(this); 534 535 List<CmsSessionInfo> sessionInfos = OpenCms.getSessionManager().getSessionInfos(); 536 List<CmsOrganizationalUnit> manageableOus = OpenCms.getRoleManager().getManageableOrgUnits( 537 A_CmsUI.getCmsObject(), 538 "", 539 true, 540 false); 541 for (CmsSessionInfo session : sessionInfos) { 542 CmsUser user = A_CmsUI.getCmsObject().readUser(session.getUserId()); 543 CmsOrganizationalUnit userOu = OpenCms.getOrgUnitManager().readOrganizationalUnit( 544 A_CmsUI.getCmsObject(), 545 user.getOuFqn()); 546 if (!(manageableOus.contains(userOu) && !user.isWebuser())) { 547 continue; 548 } 549 // CmsListItem item = getList().newItem(sessionInfo.getSessionId().toString()); 550 Item item = m_container.addItem(session.getSessionId().getStringValue()); 551 item.getItemProperty(TableProperty.UserName).setValue(user.getName()); 552 item.getItemProperty(TableProperty.DateCreated).setValue( 553 session.getAgeOfSession() + " " + CmsVaadinUtils.getMessageText(Messages.GUI_MESSAGES_HOUR_0)); 554 item.getItemProperty(TableProperty.IS_ACTIVE).setValue( 555 new Long(System.currentTimeMillis() - session.getTimeUpdated())); 556 item.getItemProperty(TableProperty.OrgUnit).setValue(userOu.getName()); 557 item.getItemProperty(TableProperty.Project).setValue( 558 A_CmsUI.getCmsObject().readProject(session.getProject()).getName()); 559 CmsSite site = OpenCms.getSiteManager().getSiteForSiteRoot(session.getSiteRoot()); 560 String siteTitle = site == null 561 ? CmsVaadinUtils.getMessageText(org.opencms.ade.galleries.Messages.GUI_ROOT_SITE_0) 562 : site.getTitle(); 563 item.getItemProperty(TableProperty.Site).setValue(siteTitle); 564 565 item.getItemProperty(TableProperty.IS_WAITING).setValue( 566 new Boolean(!session.getBroadcastQueue().isEmpty())); 567 568 } 569 570 setVisibleColumns( 571 TableProperty.Icon, 572 TableProperty.IS_ACTIVE, 573 TableProperty.UserName, 574 TableProperty.DateCreated, 575 TableProperty.Site, 576 TableProperty.Project); 577 578 } 579 580 /** 581 * Shows window with user information.<p> 582 * 583 * @param data sessionid to be shown user off 584 */ 585 protected void showUserInfoWindow(String data) { 586 587 CmsUserInfoDialog.showUserInfo(OpenCms.getSessionManager().getSessionInfo(data)); 588 } 589 590 /** 591 * Returns the available menu entries.<p> 592 * 593 * @return the menu entries 594 */ 595 List<I_CmsSimpleContextMenuEntry<Set<String>>> getMenuEntries() { 596 597 if (m_menuEntries == null) { 598 m_menuEntries = new ArrayList<I_CmsSimpleContextMenuEntry<Set<String>>>(); 599 m_menuEntries.add(new UserEntry()); 600 m_menuEntries.add(new SendBroadcastEntry()); 601 m_menuEntries.add(new KillEntry()); 602 } 603 return m_menuEntries; 604 } 605 606 /** 607 * Handles the table item clicks, including clicks on images inside of a table item.<p> 608 * 609 * @param event the click event 610 * @param itemId of the clicked row 611 * @param propertyId column id 612 */ 613 void onItemClick(MouseEvents.ClickEvent event, Object itemId, Object propertyId) { 614 615 if (!event.isCtrlKey() && !event.isShiftKey()) { 616 617 changeValueIfNotMultiSelect(itemId); 618 619 // don't interfere with multi-selection using control key 620 if (event.getButton().equals(MouseButton.RIGHT) || (TableProperty.Icon.equals(propertyId))) { 621 622 m_menu.setEntries(getMenuEntries(), (Set<String>)getValue()); 623 m_menu.openForTable(event, itemId, propertyId, this); 624 } else if (event.getButton().equals(MouseButton.LEFT) && TableProperty.UserName.equals(propertyId)) { 625 showUserInfoWindow(((Set<String>)getValue()).iterator().next()); 626 } 627 } 628 } 629 630 /** 631 * Checks value of table and sets it new if needed:<p> 632 * if multiselect: new itemId is in current Value? -> no change of value<p> 633 * no multiselect and multiselect, but new item not selected before: set value to new item<p> 634 * 635 * @param itemId if of clicked item 636 */ 637 private void changeValueIfNotMultiSelect(Object itemId) { 638 639 @SuppressWarnings("unchecked") 640 Set<String> value = (Set<String>)getValue(); 641 if (value == null) { 642 select(itemId); 643 } else if (!value.contains(itemId)) { 644 setValue(null); 645 select(itemId); 646 } 647 } 648 649}