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.editors.messagebundle; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsResource; 032import org.opencms.file.types.I_CmsResourceType; 033import org.opencms.i18n.CmsMessages; 034import org.opencms.main.CmsException; 035import org.opencms.main.CmsLog; 036import org.opencms.main.CmsUIServlet; 037import org.opencms.main.OpenCms; 038import org.opencms.ui.CmsVaadinUtils; 039import org.opencms.ui.FontOpenCms; 040import org.opencms.ui.apps.CmsEditor; 041import org.opencms.ui.apps.I_CmsAppUIContext; 042import org.opencms.ui.apps.I_CmsHasShortcutActions; 043import org.opencms.ui.components.CmsConfirmationDialog; 044import org.opencms.ui.components.CmsErrorDialog; 045import org.opencms.ui.components.CmsToolBar; 046import org.opencms.ui.components.I_CmsWindowCloseListener; 047import org.opencms.ui.contextmenu.CmsContextMenu; 048import org.opencms.ui.editors.I_CmsEditor; 049import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorModel.ConfigurableMessages; 050import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorModel.KeyChangeResult; 051import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.BundleType; 052import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.EditMode; 053import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.EntryChangeEvent; 054import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.I_EntryChangeListener; 055import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.I_ItemDeletionListener; 056import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.I_OptionListener; 057import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.ItemDeletionEvent; 058import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.TableProperty; 059import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.TranslateTableFieldFactory; 060 061import java.io.IOException; 062import java.util.Collection; 063import java.util.HashMap; 064import java.util.Locale; 065import java.util.Map; 066 067import org.apache.commons.logging.Log; 068 069import org.tepi.filtertable.FilterTable; 070 071import com.vaadin.annotations.Theme; 072import com.vaadin.event.Action; 073import com.vaadin.event.ContextClickEvent; 074import com.vaadin.event.ContextClickEvent.ContextClickListener; 075import com.vaadin.event.ShortcutAction; 076import com.vaadin.navigator.ViewChangeListener; 077import com.vaadin.server.VaadinServlet; 078import com.vaadin.ui.Button; 079import com.vaadin.ui.Button.ClickEvent; 080import com.vaadin.ui.Button.ClickListener; 081import com.vaadin.ui.Component; 082import com.vaadin.ui.Component.Focusable; 083import com.vaadin.ui.Notification; 084import com.vaadin.ui.Notification.Type; 085import com.vaadin.ui.Panel; 086import com.vaadin.ui.UI; 087import com.vaadin.v7.data.Item; 088import com.vaadin.v7.data.Property; 089import com.vaadin.v7.data.util.IndexedContainer; 090import com.vaadin.v7.ui.VerticalLayout; 091 092/** 093 * Controller for the VAADIN UI of the Message Bundle Editor. 094 */ 095@Theme("opencms") 096public class CmsMessageBundleEditor 097implements I_CmsEditor, I_CmsWindowCloseListener, ViewChangeListener, I_EntryChangeListener, I_ItemDeletionListener, 098I_OptionListener, I_CmsHasShortcutActions { 099 100 /** Used to implement {@link java.io.Serializable}. */ 101 private static final long serialVersionUID = 5366955716462191580L; 102 103 /** The log object for this class. */ 104 private static final Log LOG = CmsLog.getLog(CmsMessageBundleEditor.class); 105 106 /** Exit shortcut. */ 107 private static final Action ACTION_EXIT = new ShortcutAction( 108 "Ctrl+Shift+X", 109 ShortcutAction.KeyCode.X, 110 new int[] {ShortcutAction.ModifierKey.CTRL, ShortcutAction.ModifierKey.SHIFT}); 111 112 /** Exit shortcut, (using Apple CMD as modifier). */ 113 private static final Action ACTION_EXIT_CMD = new ShortcutAction( 114 "CMD+Shift+X", 115 ShortcutAction.KeyCode.X, 116 new int[] {ShortcutAction.ModifierKey.META, ShortcutAction.ModifierKey.SHIFT}); 117 118 /** Save shortcut. */ 119 private static final Action ACTION_SAVE = new ShortcutAction( 120 "Ctrl+S", 121 ShortcutAction.KeyCode.S, 122 new int[] {ShortcutAction.ModifierKey.CTRL}); 123 124 /** Save shortcut, (using Apple CMD as modifier). */ 125 private static final Action ACTION_SAVE_CMD = new ShortcutAction( 126 "CMD+S", 127 ShortcutAction.KeyCode.S, 128 new int[] {ShortcutAction.ModifierKey.META}); 129 130 /** Save & Exit shortcut. */ 131 private static final Action ACTION_SAVE_AND_EXIT = new ShortcutAction( 132 "Ctrl+Shift+S", 133 ShortcutAction.KeyCode.S, 134 new int[] {ShortcutAction.ModifierKey.CTRL, ShortcutAction.ModifierKey.SHIFT}); 135 136 /** Save & Exit shortcut, (using Apple CMD as modifier). */ 137 private static final Action ACTION_SAVE_AND_EXIT_CMD = new ShortcutAction( 138 "CMD+Shift+S", 139 ShortcutAction.KeyCode.S, 140 new int[] {ShortcutAction.ModifierKey.META, ShortcutAction.ModifierKey.SHIFT}); 141 142 /** The bundle editor shortcuts. */ 143 Map<Action, Runnable> m_shortcutActions; 144 145 /** Messages used by the GUI. */ 146 CmsMessages m_messages; 147 148 /** Configurable Messages. */ 149 ConfigurableMessages m_configurableMessages; 150 151 /** The field factories for the different modes. */ 152 private final Map<CmsMessageBundleEditorTypes.EditMode, CmsMessageBundleEditorTypes.TranslateTableFieldFactory> m_fieldFactories = new HashMap<CmsMessageBundleEditorTypes.EditMode, CmsMessageBundleEditorTypes.TranslateTableFieldFactory>( 153 2); 154 /** The cell style generators for the different modes. */ 155 private final Map<CmsMessageBundleEditorTypes.EditMode, CmsMessageBundleEditorTypes.TranslateTableCellStyleGenerator> m_styleGenerators = new HashMap<CmsMessageBundleEditorTypes.EditMode, CmsMessageBundleEditorTypes.TranslateTableCellStyleGenerator>( 156 2); 157 158 /** Flag, indicating if leaving the editor is confirmed. */ 159 boolean m_leaving; 160 161 /** The context of the UI. */ 162 I_CmsAppUIContext m_context; 163 164 /** The model behind the UI. */ 165 CmsMessageBundleEditorModel m_model; 166 167 /** CmsObject for read / write actions. */ 168 CmsObject m_cms; 169 170 /** The resource that was opened with the editor. */ 171 CmsResource m_resource; 172 173 /** The table component that is shown. */ 174 FilterTable m_table; 175 176 /** The save button. */ 177 Button m_saveBtn; 178 /** The save and exit button. */ 179 Button m_saveExitBtn; 180 181 /** The options column, optionally shown in the table. */ 182 CmsMessageBundleEditorTypes.OptionColumnGenerator m_optionsColumn; 183 184 /** The place where to go when the editor is closed. */ 185 private String m_backLink; 186 187 /** The options view of the editor. */ 188 private CmsMessageBundleEditorOptions m_options; 189 190 /** 191 * Default constructor. 192 */ 193 public CmsMessageBundleEditor() { 194 195 m_shortcutActions = new HashMap<Action, Runnable>(); 196 m_shortcutActions = new HashMap<Action, Runnable>(); 197 Runnable save = new Runnable() { 198 199 public void run() { 200 201 saveAction(); 202 } 203 }; 204 m_shortcutActions.put(ACTION_SAVE, save); 205 m_shortcutActions.put(ACTION_SAVE_CMD, save); 206 Runnable saveExit = new Runnable() { 207 208 public void run() { 209 210 saveAction(); 211 closeAction(); 212 } 213 }; 214 m_shortcutActions.put(ACTION_SAVE_AND_EXIT, saveExit); 215 m_shortcutActions.put(ACTION_SAVE_AND_EXIT_CMD, saveExit); 216 Runnable exit = new Runnable() { 217 218 public void run() { 219 220 closeAction(); 221 } 222 }; 223 m_shortcutActions.put(ACTION_EXIT, exit); 224 m_shortcutActions.put(ACTION_EXIT_CMD, exit); 225 } 226 227 /** 228 * @see com.vaadin.navigator.ViewChangeListener#afterViewChange(com.vaadin.navigator.ViewChangeListener.ViewChangeEvent) 229 */ 230 public void afterViewChange(ViewChangeEvent event) { 231 232 // do nothing 233 234 } 235 236 /** 237 * @see com.vaadin.navigator.ViewChangeListener#beforeViewChange(com.vaadin.navigator.ViewChangeListener.ViewChangeEvent) 238 */ 239 public boolean beforeViewChange(final ViewChangeEvent event) { 240 241 if (!m_leaving && m_model.hasChanges()) { 242 CmsConfirmationDialog.show( 243 CmsVaadinUtils.getMessageText(org.opencms.ui.apps.Messages.GUI_EDITOR_CLOSE_CAPTION_0), 244 CmsVaadinUtils.getMessageText(org.opencms.ui.apps.Messages.GUI_EDITOR_CLOSE_TEXT_0), 245 new Runnable() { 246 247 public void run() { 248 249 m_leaving = true; 250 event.getNavigator().navigateTo(event.getViewName()); 251 } 252 }); 253 return false; 254 } 255 256 cleanUpAction(); 257 return true; 258 } 259 260 /** 261 * @see org.opencms.ui.editors.I_CmsEditor#getPriority() 262 */ 263 public int getPriority() { 264 265 return 200; 266 } 267 268 /** 269 * @see org.opencms.ui.apps.I_CmsHasShortcutActions#getShortcutActions() 270 */ 271 public Map<Action, Runnable> getShortcutActions() { 272 273 return m_shortcutActions; 274 } 275 276 /** 277 * @see org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.I_OptionListener#handleAddKey(java.lang.String) 278 */ 279 public boolean handleAddKey(final String newKey) { 280 281 Map<Object, Object> filters = getFilters(); 282 m_table.clearFilters(); 283 boolean canAdd = !keyAlreadyExists(newKey); 284 if (canAdd) { 285 Object copyEntryId = m_table.addItem(); 286 Item copyEntry = m_table.getItem(copyEntryId); 287 copyEntry.getItemProperty(TableProperty.KEY).setValue(newKey); 288 } 289 setFilters(filters); 290 291 if (m_model.hasDescriptor() 292 | m_model.getBundleType().equals(CmsMessageBundleEditorTypes.BundleType.DESCRIPTOR)) { 293 handleChange(TableProperty.KEY); 294 handleChange(TableProperty.DESCRIPTION); 295 } 296 297 return canAdd; 298 } 299 300 /** 301 * @see org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.I_EntryChangeListener#handleEntryChange(org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.EntryChangeEvent) 302 */ 303 public void handleEntryChange(EntryChangeEvent event) { 304 305 if (event.getPropertyId().equals(TableProperty.KEY)) { 306 KeyChangeResult result = m_model.handleKeyChange(event, true); 307 String captionKey = null; 308 String descriptionKey = null; 309 switch (result) { 310 case SUCCESS: 311 handleChange(event.getPropertyId()); 312 return; 313 case FAILED_DUPLICATED_KEY: 314 captionKey = Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_KEY_ALREADY_EXISTS_CAPTION_0; 315 descriptionKey = Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_KEY_ALREADY_EXISTS_DESCRIPTION_0; 316 break; 317 case FAILED_FOR_OTHER_LANGUAGE: 318 captionKey = Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_KEY_RENAMING_FAILED_CAPTION_0; 319 descriptionKey = Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_KEY_RENAMING_FAILED_DESCRIPTION_0; 320 break; 321 default: 322 throw new IllegalArgumentException(); 323 } 324 CmsMessageBundleEditorTypes.showWarning(m_messages.key(captionKey), m_messages.key(descriptionKey)); 325 event.getSource().focus(); 326 } 327 handleChange(event.getPropertyId()); 328 329 } 330 331 /** 332 * @see org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.I_ItemDeletionListener#handleItemDeletion(org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.ItemDeletionEvent) 333 */ 334 public boolean handleItemDeletion(ItemDeletionEvent e) { 335 336 Item it = m_table.getItem(e.getItemId()); 337 Property<?> keyProp = it.getItemProperty(TableProperty.KEY); 338 String key = (String)keyProp.getValue(); 339 if (m_model.handleKeyDeletion(key)) { 340 if (m_model.hasDescriptor() 341 | m_model.getBundleType().equals(CmsMessageBundleEditorTypes.BundleType.DESCRIPTOR)) { 342 handleChange(TableProperty.DESCRIPTION); 343 } 344 handleChange(TableProperty.KEY); 345 return true; 346 } 347 CmsMessageBundleEditorTypes.showWarning( 348 m_messages.key(Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_REMOVE_ENTRY_FAILED_CAPTION_0), 349 m_messages.key(Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_REMOVE_ENTRY_FAILED_DESCRIPTION_0)); 350 return false; 351 352 } 353 354 /** 355 * @see org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.I_OptionListener#handleLanguageChange(java.util.Locale) 356 */ 357 public void handleLanguageChange(final Locale locale) { 358 359 if (!locale.equals(m_model.getLocale())) { 360 Object sortProperty = m_table.getSortContainerPropertyId(); 361 boolean isAcending = m_table.isSortAscending(); 362 Map<Object, Object> filters = getFilters(); 363 m_table.clearFilters(); 364 if (m_model.setLocale(locale)) { 365 m_options.setEditedFilePath(m_model.getEditedFilePath()); 366 m_table.sort(new Object[] {sortProperty}, new boolean[] {isAcending}); 367 } else { 368 String caption = m_messages.key( 369 Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_LOCALE_SWITCHING_FAILED_CAPTION_0); 370 String description = m_messages.key( 371 Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_LOCALE_SWITCHING_FAILED_DESCRIPTION_0); 372 Notification warning = new Notification(caption, description, Type.WARNING_MESSAGE, true); 373 warning.setDelayMsec(-1); 374 warning.show(UI.getCurrent().getPage()); 375 m_options.setLanguage(m_model.getLocale()); 376 } 377 setFilters(filters); 378 m_table.select(m_table.getCurrentPageFirstItemId()); 379 } 380 } 381 382 /** 383 * @see org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.I_OptionListener#handleModeChange(org.opencms.ui.editors.messagebundle.CmsMessageBundleEditorTypes.EditMode) 384 */ 385 public void handleModeChange(final EditMode mode) { 386 387 setEditMode(mode); 388 389 } 390 391 /** 392 * @see org.opencms.ui.editors.I_CmsEditor#initUI(org.opencms.ui.apps.I_CmsAppUIContext, org.opencms.file.CmsResource, java.lang.String, java.util.Map) 393 */ 394 public void initUI(I_CmsAppUIContext context, CmsResource resource, String backLink, Map<String, String> params) { 395 396 m_cms = ((CmsUIServlet)VaadinServlet.getCurrent()).getCmsObject(); 397 m_messages = Messages.get().getBundle(UI.getCurrent().getLocale()); 398 m_resource = resource; 399 m_backLink = backLink; 400 m_context = context; 401 402 try { 403 m_model = new CmsMessageBundleEditorModel(m_cms, m_resource); 404 m_options = new CmsMessageBundleEditorOptions( 405 m_model.getLocales(), 406 m_model.getLocale(), 407 m_model.getEditMode(), 408 this); 409 m_options.setEditedFilePath(m_model.getEditedFilePath()); 410 m_configurableMessages = m_model.getConfigurableMessages(m_messages, UI.getCurrent().getLocale()); 411 412 fillToolBar(context); 413 context.showInfoArea(false); 414 415 Component main = createMainComponent(); 416 417 initFieldFactories(); 418 initStyleGenerators(); 419 420 m_table.setTableFieldFactory(m_fieldFactories.get(m_model.getEditMode())); 421 m_table.setCellStyleGenerator(m_styleGenerators.get(m_model.getEditMode())); 422 423 m_optionsColumn.registerItemDeletionListener(this); 424 425 adjustVisibleColumns(); 426 427 context.setAppContent(main); 428 429 adjustFocus(); 430 431 if (m_model.getSwitchedLocaleOnOpening()) { 432 CmsMessageBundleEditorTypes.showWarning( 433 m_messages.key(Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_SWITCHED_LOCALE_CAPTION_0), 434 m_messages.key(Messages.GUI_NOTIFICATION_MESSAGEBUNDLEEDITOR_SWITCHED_LOCALE_DESCRIPTION_0)); 435 } 436 437 } catch (IOException | CmsException e) { 438 LOG.error(m_messages.key(Messages.ERR_LOADING_RESOURCES_0), e); 439 Notification.show(m_messages.key(Messages.ERR_LOADING_RESOURCES_0), Type.ERROR_MESSAGE); 440 closeAction(); 441 } 442 443 } 444 445 /** 446 * @see org.opencms.ui.editors.I_CmsEditor#matchesResource(org.opencms.file.CmsResource, boolean) 447 */ 448 public boolean matchesResource(CmsResource resource, boolean plainText) { 449 450 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(resource); 451 return matchesType(type, plainText); 452 } 453 454 /** 455 * @see org.opencms.ui.editors.I_CmsEditor#matchesResource(org.opencms.file.CmsResource, boolean) 456 */ 457 public boolean matchesType(I_CmsResourceType type, boolean plainText) { 458 459 return !plainText && (CmsMessageBundleEditorTypes.BundleType.toBundleType(type.getTypeName()) != null); 460 } 461 462 /** 463 * @see org.opencms.ui.editors.I_CmsEditor#newInstance() 464 */ 465 public I_CmsEditor newInstance() { 466 467 return new CmsMessageBundleEditor(); 468 } 469 470 /** 471 * @see org.opencms.ui.components.I_CmsWindowCloseListener#onWindowClose() 472 */ 473 public void onWindowClose() { 474 475 cleanUpAction(); 476 477 } 478 479 /** 480 * Unlocks all resources. Call when closing the editor. 481 */ 482 void closeAction() { 483 484 CmsEditor.openBackLink(m_backLink); 485 } 486 487 /** 488 * Returns the currently set filters in a map column -> filter. 489 * 490 * @return the currently set filters in a map column -> filter. 491 */ 492 Map<Object, Object> getFilters() { 493 494 Map<Object, Object> result = new HashMap<Object, Object>(4); 495 result.put(TableProperty.KEY, m_table.getFilterFieldValue(TableProperty.KEY)); 496 result.put(TableProperty.DEFAULT, m_table.getFilterFieldValue(TableProperty.DEFAULT)); 497 result.put(TableProperty.DESCRIPTION, m_table.getFilterFieldValue(TableProperty.DESCRIPTION)); 498 result.put(TableProperty.TRANSLATION, m_table.getFilterFieldValue(TableProperty.TRANSLATION)); 499 return result; 500 } 501 502 /** 503 * Publish the changes. 504 */ 505 void publishAction() { 506 507 //save first 508 saveAction(); 509 510 //publish 511 m_model.publish(); 512 513 } 514 515 /** 516 * Save the changes. 517 */ 518 void saveAction() { 519 520 Map<Object, Object> filters = getFilters(); 521 m_table.clearFilters(); 522 523 try { 524 525 m_model.save(); 526 disableSaveButtons(); 527 528 } catch (CmsException e) { 529 LOG.error(m_messages.key(Messages.ERR_SAVING_CHANGES_0), e); 530 CmsErrorDialog.showErrorDialog(m_messages.key(Messages.ERR_SAVING_CHANGES_0), e); 531 } 532 533 setFilters(filters); 534 535 } 536 537 /** 538 * Set the edit mode. 539 * @param newMode the edit mode to set. 540 * @return Flag, indicating if mode switching was successful. 541 */ 542 boolean setEditMode(CmsMessageBundleEditorTypes.EditMode newMode) { 543 544 CmsMessageBundleEditorTypes.EditMode oldMode = m_model.getEditMode(); 545 boolean success = false; 546 if (!newMode.equals(oldMode)) { 547 Map<Object, Object> filters = getFilters(); 548 m_table.clearFilters(); 549 if (m_model.setEditMode(newMode)) { 550 m_table.setTableFieldFactory(m_fieldFactories.get(newMode)); 551 m_table.setCellStyleGenerator(m_styleGenerators.get(newMode)); 552 adjustOptionsColumn(oldMode, newMode); 553 m_options.updateShownOptions(m_model.hasMasterMode(), m_model.canAddKeys()); 554 m_options.setEditMode(newMode); 555 success = true; 556 } else { 557 Notification.show(m_messages.key(Messages.ERR_MODE_CHANGE_NOT_POSSIBLE_0), Type.ERROR_MESSAGE); 558 559 } 560 setFilters(filters); 561 adjustFocus(); 562 } 563 return success; 564 565 } 566 567 /** 568 * Sets the provided filters. 569 * @param filters a map "column id -> filter". 570 */ 571 void setFilters(Map<Object, Object> filters) { 572 573 for (Object column : filters.keySet()) { 574 Object filterValue = filters.get(column); 575 if ((filterValue != null) && !filterValue.toString().isEmpty() && !m_table.isColumnCollapsed(column)) { 576 m_table.setFilterFieldValue(column, filterValue); 577 } 578 } 579 } 580 581 /** 582 * Sets the focus to the first editable field of the table. 583 * If entries can be added, it is set to the first field of the "Add entries" row. 584 */ 585 private void adjustFocus() { 586 587 // Put the focus on the "Filter key" field first 588 ((Focusable)m_table.getFilterField(TableProperty.KEY)).focus(); 589 } 590 591 /** 592 * Show or hide the options column dependent on the provided edit mode. 593 * @param oldMode the old edit mode 594 * @param newMode the edit mode for which the options column's visibility should be adjusted. 595 */ 596 private void adjustOptionsColumn( 597 CmsMessageBundleEditorTypes.EditMode oldMode, 598 CmsMessageBundleEditorTypes.EditMode newMode) { 599 600 if (m_model.isShowOptionsColumn(oldMode) != m_model.isShowOptionsColumn(newMode)) { 601 m_table.removeGeneratedColumn(TableProperty.OPTIONS); 602 if (m_model.isShowOptionsColumn(newMode)) { 603 // Don't know why exactly setting the filter field invisible is necessary here, 604 // it should be already set invisible - but apparently not setting it invisible again 605 // will result in the field being visible. 606 m_table.setFilterFieldVisible(TableProperty.OPTIONS, false); 607 m_table.addGeneratedColumn(TableProperty.OPTIONS, m_optionsColumn); 608 } 609 } 610 } 611 612 /** 613 * Adjust the visible columns. 614 */ 615 private void adjustVisibleColumns() { 616 617 if (m_table.isColumnCollapsingAllowed()) { 618 if ((m_model.hasDefaultValues()) || m_model.getBundleType().equals(BundleType.DESCRIPTOR)) { 619 m_table.setColumnCollapsed(TableProperty.DEFAULT, false); 620 } else { 621 m_table.setColumnCollapsed(TableProperty.DEFAULT, true); 622 } 623 624 if (((m_model.getEditMode().equals(EditMode.MASTER) || m_model.hasDescriptionValues())) 625 || m_model.getBundleType().equals(BundleType.DESCRIPTOR)) { 626 m_table.setColumnCollapsed(TableProperty.DESCRIPTION, false); 627 } else { 628 m_table.setColumnCollapsed(TableProperty.DESCRIPTION, true); 629 } 630 } 631 } 632 633 /** 634 * Unlock all edited resources. 635 */ 636 private void cleanUpAction() { 637 638 try { 639 m_model.deleteDescriptorIfNecessary(); 640 } catch (CmsException e) { 641 LOG.error(m_messages.key(Messages.ERR_DELETING_DESCRIPTOR_0), e); 642 } 643 // unlock resource 644 m_model.unlock(); 645 } 646 647 /** 648 * Returns a button component. On click, it triggers adding a bundle descriptor. 649 * @return a button for adding a descriptor to a bundle. 650 */ 651 @SuppressWarnings("serial") 652 private Component createAddDescriptorButton() { 653 654 Button addDescriptorButton = CmsToolBar.createButton( 655 FontOpenCms.COPY_LOCALE, 656 m_messages.key(Messages.GUI_ADD_DESCRIPTOR_0)); 657 658 addDescriptorButton.setDisableOnClick(true); 659 660 addDescriptorButton.addClickListener(new ClickListener() { 661 662 @SuppressWarnings("synthetic-access") 663 public void buttonClick(ClickEvent event) { 664 665 Map<Object, Object> filters = getFilters(); 666 m_table.clearFilters(); 667 if (!m_model.addDescriptor()) { 668 CmsVaadinUtils.showAlert( 669 m_messages.key(Messages.ERR_BUNDLE_DESCRIPTOR_CREATION_FAILED_0), 670 m_messages.key(Messages.ERR_BUNDLE_DESCRIPTOR_CREATION_FAILED_DESCRIPTION_0), 671 null); 672 } else { 673 IndexedContainer newContainer = null; 674 try { 675 newContainer = m_model.getContainerForCurrentLocale(); 676 m_table.setContainerDataSource(newContainer); 677 initFieldFactories(); 678 initStyleGenerators(); 679 setEditMode(EditMode.MASTER); 680 m_table.setColumnCollapsingAllowed(true); 681 adjustVisibleColumns(); 682 m_options.updateShownOptions(m_model.hasMasterMode(), m_model.canAddKeys()); 683 m_options.setEditMode(m_model.getEditMode()); 684 } catch (IOException | CmsException e) { 685 // Can never appear here, since container is created by addDescriptor already. 686 LOG.error(e.getLocalizedMessage(), e); 687 } 688 } 689 setFilters(filters); 690 } 691 }); 692 return addDescriptorButton; 693 } 694 695 /** 696 * Create the close button UI Component. 697 * @return the close button. 698 */ 699 @SuppressWarnings("serial") 700 private Component createCloseButton() { 701 702 Button closeBtn = CmsToolBar.createButton( 703 FontOpenCms.CIRCLE_INV_CANCEL, 704 m_messages.key(Messages.GUI_BUTTON_CANCEL_0)); 705 closeBtn.addClickListener(new ClickListener() { 706 707 public void buttonClick(ClickEvent event) { 708 709 closeAction(); 710 } 711 712 }); 713 return closeBtn; 714 } 715 716 /** 717 * Creates the button for converting an XML bundle in a property bundle. 718 * @return the created button. 719 */ 720 private Component createConvertToPropertyBundleButton() { 721 722 Button addDescriptorButton = CmsToolBar.createButton( 723 FontOpenCms.SETTINGS, 724 m_messages.key(Messages.GUI_CONVERT_TO_PROPERTY_BUNDLE_0)); 725 726 addDescriptorButton.setDisableOnClick(true); 727 728 addDescriptorButton.addClickListener(new ClickListener() { 729 730 private static final long serialVersionUID = 1L; 731 732 public void buttonClick(ClickEvent event) { 733 734 try { 735 m_model.saveAsPropertyBundle(); 736 Notification.show("Conversion successful."); 737 } catch (CmsException | IOException e) { 738 CmsVaadinUtils.showAlert("Conversion failed", e.getLocalizedMessage(), null); 739 } 740 } 741 }); 742 addDescriptorButton.setDisableOnClick(true); 743 return addDescriptorButton; 744 } 745 746 /** 747 * Creates the main component of the editor with all sub-components. 748 * @return the completely filled main component of the editor. 749 * @throws IOException thrown if setting the table's content data source fails. 750 * @throws CmsException thrown if setting the table's content data source fails. 751 */ 752 private Component createMainComponent() throws IOException, CmsException { 753 754 VerticalLayout mainComponent = new VerticalLayout(); 755 mainComponent.setSizeFull(); 756 mainComponent.addStyleName("o-message-bundle-editor"); 757 m_table = createTable(); 758 Panel navigator = new Panel(); 759 navigator.setSizeFull(); 760 navigator.setContent(m_table); 761 navigator.addActionHandler(new CmsMessageBundleEditorTypes.TableKeyboardHandler(m_table)); 762 navigator.addStyleName("v-panel-borderless"); 763 764 mainComponent.addComponent(m_options.getOptionsComponent()); 765 mainComponent.addComponent(navigator); 766 mainComponent.setExpandRatio(navigator, 1f); 767 m_options.updateShownOptions(m_model.hasMasterMode(), m_model.canAddKeys()); 768 return mainComponent; 769 } 770 771 /** Creates the save button UI Component. 772 * @return the save button. 773 */ 774 @SuppressWarnings("serial") 775 private Component createPublishButton() { 776 777 Button publishBtn = CmsToolBar.createButton(FontOpenCms.PUBLISH, m_messages.key(Messages.GUI_BUTTON_PUBLISH_0)); 778 publishBtn.addClickListener(new ClickListener() { 779 780 public void buttonClick(ClickEvent event) { 781 782 publishAction(); 783 } 784 785 }); 786 return publishBtn; 787 } 788 789 /** Creates the save button UI Component. 790 * @return the save button. 791 */ 792 @SuppressWarnings("serial") 793 private Button createSaveButton() { 794 795 Button saveBtn = CmsToolBar.createButton(FontOpenCms.SAVE, m_messages.key(Messages.GUI_BUTTON_SAVE_0)); 796 saveBtn.addClickListener(new ClickListener() { 797 798 public void buttonClick(ClickEvent event) { 799 800 saveAction(); 801 } 802 803 }); 804 saveBtn.setEnabled(false); 805 return saveBtn; 806 } 807 808 /** Creates the save and exit button UI Component. 809 * @return the save and exit button. 810 */ 811 @SuppressWarnings("serial") 812 private Button createSaveExitButton() { 813 814 Button saveExitBtn = CmsToolBar.createButton( 815 FontOpenCms.SAVE_EXIT, 816 m_messages.key(Messages.GUI_BUTTON_SAVE_AND_EXIT_0)); 817 saveExitBtn.addClickListener(new ClickListener() { 818 819 public void buttonClick(ClickEvent event) { 820 821 saveAction(); 822 closeAction(); 823 824 } 825 }); 826 saveExitBtn.setEnabled(false); 827 return saveExitBtn; 828 } 829 830 /** Creates the (filled) table UI component. 831 * @return the (filled) table 832 * @throws IOException thrown if reading the properties file fails. 833 * @throws CmsException thrown if some read action for getting the table contentFilter fails. 834 */ 835 private FilterTable createTable() throws IOException, CmsException { 836 837 final FilterTable table = new FilterTable(); 838 table.setSizeFull(); 839 840 table.setContainerDataSource(m_model.getContainerForCurrentLocale()); 841 842 table.setColumnHeader(TableProperty.KEY, m_configurableMessages.getColumnHeader(TableProperty.KEY)); 843 table.setColumnCollapsible(TableProperty.KEY, false); 844 845 table.setColumnHeader(TableProperty.DEFAULT, m_configurableMessages.getColumnHeader(TableProperty.DEFAULT)); 846 table.setColumnCollapsible(TableProperty.DEFAULT, true); 847 848 table.setColumnHeader( 849 TableProperty.DESCRIPTION, 850 m_configurableMessages.getColumnHeader(TableProperty.DESCRIPTION)); 851 table.setColumnCollapsible(TableProperty.DESCRIPTION, true); 852 853 table.setColumnHeader( 854 TableProperty.TRANSLATION, 855 m_configurableMessages.getColumnHeader(TableProperty.TRANSLATION)); 856 table.setColumnCollapsible(TableProperty.TRANSLATION, false); 857 858 table.setColumnHeader(TableProperty.OPTIONS, m_configurableMessages.getColumnHeader(TableProperty.OPTIONS)); 859 table.setFilterDecorator(new CmsMessageBundleEditorFilterDecorator()); 860 861 table.setFilterBarVisible(true); 862 table.setColumnCollapsible(TableProperty.OPTIONS, false); 863 864 table.setSortEnabled(true); 865 table.setEditable(true); 866 867 table.setSelectable(true); 868 table.setImmediate(true); 869 table.setMultiSelect(false); 870 871 table.setColumnCollapsingAllowed(m_model.hasDescriptor()); 872 873 table.setColumnReorderingAllowed(false); 874 875 m_optionsColumn = generateOptionsColumn(table); 876 877 if (m_model.isShowOptionsColumn(m_model.getEditMode())) { 878 table.setFilterFieldVisible(TableProperty.OPTIONS, false); 879 table.addGeneratedColumn(TableProperty.OPTIONS, m_optionsColumn); 880 } 881 table.setColumnWidth(TableProperty.OPTIONS, CmsMessageBundleEditorTypes.OPTION_COLUMN_WIDTH); 882 table.setColumnExpandRatio(TableProperty.KEY, 1f); 883 table.setColumnExpandRatio(TableProperty.DESCRIPTION, 1f); 884 table.setColumnExpandRatio(TableProperty.DEFAULT, 1f); 885 table.setColumnExpandRatio(TableProperty.TRANSLATION, 1f); 886 887 table.setPageLength(30); 888 table.setCacheRate(1); 889 table.sort(new Object[] {TableProperty.KEY}, new boolean[] {true}); 890 table.addContextClickListener(new ContextClickListener() { 891 892 private static final long serialVersionUID = 1L; 893 894 public void contextClick(ContextClickEvent event) { 895 896 Object itemId = m_table.getValue(); 897 CmsContextMenu contextMenu = m_model.getContextMenuForItem(itemId); 898 if (null != contextMenu) { 899 contextMenu.setAsContextMenuOf(m_table); 900 contextMenu.setOpenAutomatically(false); 901 contextMenu.open(event.getClientX(), event.getClientY()); 902 } 903 } 904 }); 905 table.setNullSelectionAllowed(false); 906 table.select(table.getCurrentPageFirstItemId()); 907 return table; 908 } 909 910 /** 911 * Disable the save buttons, e.g., after saving. 912 */ 913 private void disableSaveButtons() { 914 915 if (m_saveBtn.isEnabled()) { 916 m_saveBtn.setEnabled(false); 917 m_saveExitBtn.setEnabled(false); 918 } 919 920 } 921 922 /** Adds Editor specific UI components to the toolbar. 923 * @param context The context that provides access to the toolbar. 924 */ 925 private void fillToolBar(final I_CmsAppUIContext context) { 926 927 context.setAppTitle(m_messages.key(Messages.GUI_APP_TITLE_0)); 928 929 // create components 930 Component publishBtn = createPublishButton(); 931 m_saveBtn = createSaveButton(); 932 m_saveExitBtn = createSaveExitButton(); 933 Component closeBtn = createCloseButton(); 934 935 context.enableDefaultToolbarButtons(false); 936 context.addToolbarButtonRight(closeBtn); 937 context.addToolbarButton(publishBtn); 938 context.addToolbarButton(m_saveExitBtn); 939 context.addToolbarButton(m_saveBtn); 940 941 Component addDescriptorBtn = createAddDescriptorButton(); 942 if (m_model.hasDescriptor() || m_model.getBundleType().equals(BundleType.DESCRIPTOR)) { 943 addDescriptorBtn.setEnabled(false); 944 } 945 context.addToolbarButton(addDescriptorBtn); 946 if (m_model.getBundleType().equals(BundleType.XML)) { 947 Component convertToPropertyBundleBtn = createConvertToPropertyBundleButton(); 948 context.addToolbarButton(convertToPropertyBundleBtn); 949 } 950 } 951 952 /** Generates the options column for the table. 953 * @param table table instance passed to the option column generator 954 * @return the options column 955 */ 956 private CmsMessageBundleEditorTypes.OptionColumnGenerator generateOptionsColumn(FilterTable table) { 957 958 return new CmsMessageBundleEditorTypes.OptionColumnGenerator(table); 959 } 960 961 /** 962 * Handle a value change. 963 * @param propertyId the column in which the value has changed. 964 */ 965 private void handleChange(Object propertyId) { 966 967 if (!m_saveBtn.isEnabled()) { 968 m_saveBtn.setEnabled(true); 969 m_saveExitBtn.setEnabled(true); 970 } 971 m_model.handleChange(propertyId); 972 973 } 974 975 /** 976 * Initialize the field factories for the messages table. 977 */ 978 private void initFieldFactories() { 979 980 if (m_model.hasMasterMode()) { 981 TranslateTableFieldFactory masterFieldFactory = new CmsMessageBundleEditorTypes.TranslateTableFieldFactory( 982 m_table, 983 m_model.getEditableColumns(CmsMessageBundleEditorTypes.EditMode.MASTER)); 984 masterFieldFactory.registerKeyChangeListener(this); 985 m_fieldFactories.put(CmsMessageBundleEditorTypes.EditMode.MASTER, masterFieldFactory); 986 } 987 TranslateTableFieldFactory defaultFieldFactory = new CmsMessageBundleEditorTypes.TranslateTableFieldFactory( 988 m_table, 989 m_model.getEditableColumns(CmsMessageBundleEditorTypes.EditMode.DEFAULT)); 990 defaultFieldFactory.registerKeyChangeListener(this); 991 m_fieldFactories.put(CmsMessageBundleEditorTypes.EditMode.DEFAULT, defaultFieldFactory); 992 993 } 994 995 /** 996 * Initialize the style generators for the messages table. 997 */ 998 private void initStyleGenerators() { 999 1000 if (m_model.hasMasterMode()) { 1001 m_styleGenerators.put( 1002 CmsMessageBundleEditorTypes.EditMode.MASTER, 1003 new CmsMessageBundleEditorTypes.TranslateTableCellStyleGenerator( 1004 m_model.getEditableColumns(CmsMessageBundleEditorTypes.EditMode.MASTER))); 1005 } 1006 m_styleGenerators.put( 1007 CmsMessageBundleEditorTypes.EditMode.DEFAULT, 1008 new CmsMessageBundleEditorTypes.TranslateTableCellStyleGenerator( 1009 m_model.getEditableColumns(CmsMessageBundleEditorTypes.EditMode.DEFAULT))); 1010 1011 } 1012 1013 /** 1014 * Checks if a key already exists. 1015 * @param newKey the key to check for. 1016 * @return <code>true</code> if the key already exists, <code>false</code> otherwise. 1017 */ 1018 private boolean keyAlreadyExists(String newKey) { 1019 1020 Collection<?> itemIds = m_table.getItemIds(); 1021 for (Object itemId : itemIds) { 1022 if (m_table.getItem(itemId).getItemProperty(TableProperty.KEY).getValue().equals(newKey)) { 1023 return true; 1024 } 1025 } 1026 return false; 1027 } 1028 1029}