content: /*
   * This library is part of OpenCms -
   * the Open Source Content Management System
   *
   * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   * Lesser General Public License for more details.
   *
   * For further information about Alkacon Software, please see the
   * company website: https://www.alkacon.com
   *
   * For further information about OpenCms, please see the
   * project website: https://www.opencms.org
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  package org.opencms.ui.dialogs.history;
  
  import org.opencms.file.CmsObject;
  import org.opencms.file.CmsResource;
  import org.opencms.gwt.CmsRpcException;
  import org.opencms.gwt.CmsVfsService;
  import org.opencms.gwt.shared.CmsHistoryResourceBean;
  import org.opencms.gwt.shared.CmsHistoryResourceCollection;
  import org.opencms.gwt.shared.CmsHistoryVersion.OfflineOnline;
  import org.opencms.main.CmsException;
  import org.opencms.main.CmsLog;
  import org.opencms.main.OpenCms;
  import org.opencms.ui.A_CmsUI;
  import org.opencms.ui.CmsVaadinUtils;
  import org.opencms.ui.I_CmsDialogContext;
  import org.opencms.ui.I_CmsUpdateListener;
  import org.opencms.ui.Messages;
  import org.opencms.ui.components.CmsBasicDialog;
  import org.opencms.ui.components.CmsConfirmationDialog;
  import org.opencms.ui.components.extensions.CmsGwtDialogExtension;
  import org.opencms.ui.dialogs.history.diff.CmsAttributeDiff;
  import org.opencms.ui.dialogs.history.diff.CmsImageDiff;
  import org.opencms.ui.dialogs.history.diff.CmsPropertyDiff;
  import org.opencms.ui.dialogs.history.diff.CmsShowVersionButtons;
  import org.opencms.ui.dialogs.history.diff.CmsTextDiff;
  import org.opencms.ui.dialogs.history.diff.CmsValueDiff;
  import org.opencms.ui.dialogs.history.diff.I_CmsDiffProvider;
  import org.opencms.ui.util.CmsComponentField;
  import org.opencms.ui.util.CmsLogicalCheckboxGroup;
  import org.opencms.ui.util.table.CmsBeanTableBuilder;
  import org.opencms.util.CmsUUID;
  
  import java.util.Arrays;
  import java.util.List;
  
  import org.apache.commons.logging.Log;
  
  import com.google.common.base.Optional;
  import com.google.common.collect.Lists;
  import com.vaadin.ui.Alignment;
  import com.vaadin.ui.Button;
  import com.vaadin.ui.Button.ClickEvent;
  import com.vaadin.ui.Button.ClickListener;
  import com.vaadin.v7.ui.CheckBox;
  import com.vaadin.ui.Component;
  import com.vaadin.v7.ui.HorizontalLayout;
  import com.vaadin.ui.Notification;
  import com.vaadin.ui.Panel;
  import com.vaadin.v7.ui.Table;
  import com.vaadin.v7.ui.VerticalLayout;
  import com.vaadin.ui.Window;
  import com.vaadin.ui.themes.ValoTheme;
  
  /**
   * Dialog used to change resource modification times.<p>
   */
  public class CmsHistoryDialog extends CmsBasicDialog {
  
      /** Logger for this class. */
      private static final Log LOG = CmsLog.getLog(CmsHistoryDialog.class);
  
      /** Serial version id. */
      private static final long serialVersionUID = 1L;
  
      /** The dialog context. */
      protected I_CmsDialogContext m_context;
  
      /** Unbound field for the compare button. */
      private CmsComponentField<Button> m_compareButton = CmsComponentField.newInstance();
  
      /** Objects used to display differences between two versions. */
      private List<I_CmsDiffProvider> m_diffs = Arrays.<I_CmsDiffProvider> asList(
          new CmsShowVersionButtons(),
          new CmsPropertyDiff(),
          new CmsAttributeDiff(),
          new CmsImageDiff(),
          new CmsTextDiff(),
          new CmsValueDiff());
  
      /** Check box group for column V1.<p> */
      private CmsLogicalCheckboxGroup m_group1 = new CmsLogicalCheckboxGroup();
  
      /** Check box group for column V2.<p> */
      private CmsLogicalCheckboxGroup m_group2 = new CmsLogicalCheckboxGroup();
  
      /** Container for the list. */
      private VerticalLayout m_listContainer;
  
      /** The resource for which the history dialog was opened. */
      private CmsResource m_resource;
  
      /** Unbound field for the first selected check box. */
      private CmsComponentField<CheckBox> m_selected1 = CmsComponentField.newInstance();
  
      /** Unbound field for the second selected check box. */
      private CmsComponentField<CheckBox> m_selected2 = CmsComponentField.newInstance();
  
      /**
       * Creates a new instance.<p>
       *
       * @param context the dialog context
       */
      public CmsHistoryDialog(I_CmsDialogContext context) {
          m_context = context;
          m_resource = context.getResources().get(0);
          setWidth("100%");
          CmsVaadinUtils.readAndLocalizeDesign(
              this,
              OpenCms.getWorkplaceManager().getMessages(A_CmsUI.get().getLocale()),
              null);
          CmsResource resource = context.getResources().get(0);
  
          CmsVfsService vfsService = new CmsVfsService();
          vfsService.setCms(context.getCms());
          try {
              CmsHistoryResourceCollection historyList = vfsService.getResourceHistoryInternal(resource.getStructureId());
  
              Table historyTable = buildHistoryTable(historyList);
  
              historyTable.setWidth("100%");
              m_listContainer.setWidth("100%");
              Button compareButton = new Button(CmsVaadinUtils.getMessageText(Messages.GUI_HISTORY_DIALOG_COMPARE_0));
              m_listContainer.addComponent(compareButton);
              m_listContainer.setComponentAlignment(compareButton, Alignment.MIDDLE_RIGHT);
  
              compareButton.addClickListener(new ClickListener() {
  
                  private static final long serialVersionUID = 1L;
  
                  @SuppressWarnings("synthetic-access")
                  public void buttonClick(ClickEvent event) {
  
                      try {
                          tryCompare();
                      } catch (Exception e) {
                          LOG.error(e.getLocalizedMessage(), e);
                          m_context.error(e);
                      }
                  }
              });
              m_compareButton.set(compareButton);
              m_group1.setChangeListener(new CmsLogicalCheckboxGroup.I_ChangeListener() {
  
                  @SuppressWarnings("synthetic-access")
                  public void onSelect(CheckBox box) {
  
                      m_selected1.set(box);
                      m_compareButton.get().setEnabled(canCompare(m_selected1.get(), m_selected2.get()));
  
                  }
              });
  
              m_group2.setChangeListener(new CmsLogicalCheckboxGroup.I_ChangeListener() {
  
                  @SuppressWarnings("synthetic-access")
                  public void onSelect(CheckBox box) {
  
                      m_selected2.set(box);
                      m_compareButton.get().setEnabled(canCompare(m_selected1.get(), m_selected2.get()));
                  }
              });
              m_compareButton.get().setEnabled(false);
              m_listContainer.addComponent(historyTable);
          } catch (CmsException e) {
              LOG.error(e.getLocalizedMessage(), e);
          }
          addButton(createCloseButton());
          displayResourceInfo(m_context.getResources());
      }
  
      /**
       * Replaces the contents of the window containing a given component with a basic dialog
       * consisting of a back button to restore the previous window state and another user provided widget.<p>
       *
       * @param currentComponent the component whose parent window's content should be replaced
       * @param newView the user supplied part of the new window content
       * @param newCaption the caption for the child dialog
       */
      public static void openChildDialog(Component currentComponent, Component newView, String newCaption) {
  
          final Window window = CmsVaadinUtils.getWindow(currentComponent);
          final String oldCaption = window.getCaption();
          CmsBasicDialog dialog = new CmsBasicDialog();
  
          VerticalLayout vl = new VerticalLayout();
          dialog.setContent(vl);
          Button backButton = new Button(CmsVaadinUtils.getMessageText(Messages.GUI_CHILD_DIALOG_GO_BACK_0));
          HorizontalLayout buttonBar = new HorizontalLayout();
          buttonBar.addComponent(backButton);
          buttonBar.setMargin(true);
          vl.addComponent(buttonBar);
          vl.addComponent(newView);
          final Component oldContent = window.getContent();
          if (oldContent instanceof CmsBasicDialog) {
              List<CmsResource> infoResources = ((CmsBasicDialog)oldContent).getInfoResources();
              dialog.displayResourceInfo(infoResources);
              if (oldContent instanceof CmsHistoryDialog) {
                  dialog.addButton(((CmsHistoryDialog)oldContent).createCloseButton());
              }
          }
          backButton.addClickListener(new ClickListener() {
  
              private static final long serialVersionUID = 1L;
  
              public void buttonClick(ClickEvent event) {
  
                  window.setContent(oldContent);
                  window.setCaption(oldCaption);
                  window.center();
  
              }
  
          });
          window.setContent(dialog);
          window.setCaption(newCaption);
          window.center();
  
      }
  
      /**
       * Restores a resource's state to the given version, but asks the user for confirmation beforehand.<p>
       *
       * @param cms the CMS context
       * @param structureId the structure id of the resource to restore
       * @param version the version to which the resource should be restored
       */
      public void actionRestore(final CmsObject cms, final CmsUUID structureId, final Integer version) {
  
          String title = CmsVaadinUtils.getMessageText(Messages.GUI_HISTORY_DIALOG_CONFIRM_RESTORE_TITLE_0);
          String message = CmsVaadinUtils.getMessageText(Messages.GUI_HISTORY_DIALOG_CONFIRM_RESTORE_0);
  
          CmsConfirmationDialog.show(title, message, new Runnable() {
  
              @SuppressWarnings("synthetic-access")
              public void run() {
  
                  CmsVfsService svc = new CmsVfsService();
                  svc.setCms(cms);
                  try {
                      svc.restoreResource(structureId, version.intValue());
                      m_context.finish(Arrays.asList(m_resource.getStructureId()));
                  } catch (CmsRpcException e) {
                      LOG.error(e.getLocalizedMessage(), e);
                      m_context.error(e);
                  }
              }
          });
      }
  
      /**
       * Creates a close button for child dialogs.<p>
       *
       * @return the close button
       */
      public Button createCloseButton() {
  
          Button button = new Button(CmsVaadinUtils.getMessageText(org.opencms.ui.Messages.GUI_BUTTON_CLOSE_DIALOG_0));
          button.setWidth("150px");
          button.addClickListener(new ClickListener() {
  
              private static final long serialVersionUID = 1L;
  
              public void buttonClick(ClickEvent event) {
  
                  m_context.finish(Lists.newArrayList(m_context.getResources().get(0).getStructureId()));
              }
          });
          return button;
      }
  
      /**
       * Opens the 'compare' view for the two selected versions of the resource.<p>
       *
       * @throws CmsException if something goes wrong
       */
      public void tryCompare() throws CmsException {
  
          CmsObject cms = A_CmsUI.getCmsObject();
          CheckBox check1 = m_group1.getSelected();
          CheckBox check2 = m_group2.getSelected();
          if (!canCompare(check1, check2)) {
              Notification.show(
                  CmsVaadinUtils.getMessageText(Messages.GUI_HISTORY_DIALOG_SELECT_TWO_DIFFERENT_VERSIONS_0));
          } else {
              CmsHistoryResourceBean bean1 = (CmsHistoryResourceBean)(check1.getData());
              CmsHistoryResourceBean bean2 = (CmsHistoryResourceBean)(check2.getData());
              VerticalLayout diffContainer = new VerticalLayout();
              diffContainer.setSpacing(true);
              for (I_CmsDiffProvider diff : m_diffs) {
                  Optional<Component> optionalDiff = diff.diff(cms, bean1, bean2);
                  if (optionalDiff.isPresent()) {
                      diffContainer.addComponent(optionalDiff.get());
                  }
              }
              Panel panel = new Panel();
              panel.setSizeFull();
              diffContainer.setWidth("100%");
              diffContainer.setMargin(true);
              panel.addStyleName(ValoTheme.PANEL_BORDERLESS);
              panel.setContent(diffContainer);
              openChildDialog(
                  CmsHistoryDialog.this,
                  panel,
                  CmsVaadinUtils.getMessageText(Messages.GUI_HISTORY_DIALOG_COMPARE_0));
          }
  
      }
  
      /**
       * Displays the preview for a given resource version.<p>
       *
       * @param bean the resource version to display the preview for
       */
      private void actionPreview(CmsHistoryResourceBean bean) {
  
          CmsGwtDialogExtension ext = new CmsGwtDialogExtension(A_CmsUI.get(), new I_CmsUpdateListener<String>() {
  
              public void onUpdate(List<String> updatedItems) {
  
                  // nothing to do
              }
          });
          OfflineOnline offOnline = null;
          if (bean.getVersion().isOffline()) {
              offOnline = OfflineOnline.offline;
          }
          if (bean.getVersion().isOnline()) {
              offOnline = OfflineOnline.online;
          }
          ext.showPreview(bean.getStructureId(), bean.getVersion().getVersionNumber(), offOnline);
      }
  
      /**
       * Builds a table containing the different versions of a resource.<p>
       *
       * @param historyList the list of history resource beans from which to construct the table
       *
       * @return the table
       */
      private Table buildHistoryTable(CmsHistoryResourceCollection historyList) {
  
          final CmsObject cms = A_CmsUI.getCmsObject();
          try {
              CmsBeanTableBuilder<CmsHistoryRow> builder = CmsBeanTableBuilder.newInstance(
                  CmsHistoryRow.class,
                  A_CmsUI.get().getDisplayType().toString());
              List<CmsHistoryRow> rows = Lists.newArrayList();
  
              for (CmsHistoryResourceBean bean : historyList.getResources()) {
                  final CmsHistoryResourceBean beanFinal = bean;
                  final CmsUUID structureId = bean.getStructureId();
                  CmsHistoryRow row = new CmsHistoryRow(bean);
                  rows.add(row);
                  m_group1.add(row.getCheckBoxV1());
                  m_group2.add(row.getCheckBoxV2());
                  final Integer version = bean.getVersion().getVersionNumber();
                  if (version != null) {
                      row.getRestoreButton().addClickListener(new ClickListener() {
  
                          private static final long serialVersionUID = 1L;
  
                          public void buttonClick(ClickEvent event) {
  
                              actionRestore(cms, structureId, version);
  
                          }
  
                      });
                  }
                  row.getPreviewButton().addClickListener(new ClickListener() {
  
                      private static final long serialVersionUID = 1L;
  
                      @SuppressWarnings("synthetic-access")
                      public void buttonClick(ClickEvent event) {
  
                          actionPreview(beanFinal);
  
                      }
  
                  });
                  for (CheckBox checkBox : Arrays.asList(row.getCheckBoxV1(), row.getCheckBoxV2())) {
                      checkBox.setData(bean);
                  }
              }
  
              Table result = builder.buildTable(rows);
              result.setPageLength(Math.min(rows.size(), 12));
              result.setSortEnabled(false);
              return result;
          } catch (Exception e) {
              return null;
  
          }
      }
  
      /**
       * Checks if two different versions are selected, and so can be compared.<p>
       *
       * @param check1 the first selected check box
       * @param check2 the second selected check box
       *
       * @return true if the check boxes correspond to two different versions
       */
      private boolean canCompare(CheckBox check1, CheckBox check2) {
  
          return !((check1 == null) || (check2 == null) || (check1.getData() == check2.getData()));
      }
  
  }
  