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