package org.sigmah.shared.dto.element; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import java.util.ArrayList; import org.sigmah.client.dispatch.CommandResultHandler; import org.sigmah.client.dispatch.DispatchAsync; import org.sigmah.client.dispatch.monitor.LoadingMask; import org.sigmah.client.event.UpdateEvent; import org.sigmah.client.i18n.I18N; import org.sigmah.client.ui.notif.ConfirmCallback; import org.sigmah.client.ui.notif.N10N; import org.sigmah.client.ui.presenter.reminder.ReminderType; import org.sigmah.client.ui.res.icon.IconImageBundle; import org.sigmah.client.ui.widget.FlexibleGrid; import org.sigmah.client.ui.widget.form.ButtonFileUploadField; import org.sigmah.client.util.DateUtils; import org.sigmah.shared.command.Delete; import org.sigmah.shared.command.GetValue; import org.sigmah.shared.command.result.ValueResult; import org.sigmah.shared.command.result.VoidResult; import org.sigmah.shared.dto.ProjectDTO; import org.sigmah.shared.dto.element.event.RequiredValueEvent; import org.sigmah.shared.dto.referential.GlobalPermissionEnum; import org.sigmah.shared.dto.reminder.MonitoredPointDTO; import org.sigmah.shared.dto.value.FileDTO; import org.sigmah.shared.dto.value.FileUploadUtils; import org.sigmah.shared.dto.value.FileVersionDTO; import org.sigmah.shared.dto.value.ListableValue; import org.sigmah.shared.file.Cause; import org.sigmah.shared.file.ProgressListener; import org.sigmah.shared.file.TransfertManager; import org.sigmah.shared.servlet.FileUploadResponse; import org.sigmah.shared.util.ProfileUtils; import com.allen_sauer.gwt.log.client.Log; import com.extjs.gxt.ui.client.Style.HorizontalAlignment; import com.extjs.gxt.ui.client.Style.Scroll; import com.extjs.gxt.ui.client.Style.SortDir; import com.extjs.gxt.ui.client.event.DomEvent; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.store.ListStore; import com.extjs.gxt.ui.client.store.Store; import com.extjs.gxt.ui.client.store.StoreSorter; import com.extjs.gxt.ui.client.widget.Component; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Window; import com.extjs.gxt.ui.client.widget.form.FormPanel; import com.extjs.gxt.ui.client.widget.form.FormPanel.Encoding; import com.extjs.gxt.ui.client.widget.form.FormPanel.Method; import com.extjs.gxt.ui.client.widget.form.HiddenField; import com.extjs.gxt.ui.client.widget.grid.ColumnConfig; import com.extjs.gxt.ui.client.widget.grid.ColumnData; import com.extjs.gxt.ui.client.widget.grid.Grid; import com.extjs.gxt.ui.client.widget.grid.GridCellRenderer; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.extjs.gxt.ui.client.widget.toolbar.ToolBar; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.Label; import org.sigmah.shared.dto.orgunit.OrgUnitDTO; import org.sigmah.shared.dto.referential.ValueEventChangeType; /** * FilesListElementDTO. * * @author Denis Colliot (dcolliot@ideia.fr) * @author Raphaƫl Calabro (rcalabro@ideia.fr) * @author Renato Almeida (renatoaf.ufcg@gmail.com) */ public class FilesListElementDTO extends FlexibleElementDTO { /** * Serial version UID. */ private static final long serialVersionUID = 8520711106031085130L; /** * Entity name mapped by the current DTO starting from the "server.domain" package name. */ public static final String ENTITY_NAME = "element.FilesListElement"; private static StoreSorter<FileDTO> storeSorter; /** * Current value result updated after each upload to keep the consistency of the widget. */ private ValueResult currentValueResult; /** * The component main panel. */ private transient ContentPanel mainPanel; /** * Files list model data. */ private transient ListStore<FileDTO> store; /** * If a monitored point has been created during the upload. */ private transient boolean monitoredPointGenerated = false; /** * The upload button. */ private transient ButtonFileUploadField uploadField; public Integer getLimit() { return get("limit"); } public void setLimit(Integer limit) { set("limit", limit); } private int getAdjustedLimit() { final Integer limit = getLimit(); if (limit == null) { return -1; } if (limit <= 0) { return -1; } return limit; } /** * {@inheritDoc} */ @Override public String getEntityName() { return ENTITY_NAME; } /** * {@inheritDoc} */ @Override public boolean isCorrectRequiredValue(ValueResult result) { if (result == null || !result.isValueDefined()) { return false; } return !result.getValuesObject().isEmpty(); } /** * Updates the grid store for the current value. */ private void updateStore() { if (currentValueResult != null && currentValueResult.isValueDefined()) { store.removeAll(); int max = getAdjustedLimit(); int count = 0; for (ListableValue s : currentValueResult.getValuesObject()) { final FileDTO fileDTO = (FileDTO) s; fileDTO.set("containerId", currentContainerDTO.getId()); fileDTO.set("elementId", getId()); store.add(fileDTO); count++; if (count == max) { if (uploadField != null) { uploadField.setEnabled(false); if (uploadField.isRendered()) { uploadField.reset(); } } break; } } store.sort("date", SortDir.DESC); } } /** * {@inheritDoc} */ @Override public void init() { super.init(); assert transfertManager != null; store = new ListStore<FileDTO>(); } /** * {@inheritDoc} */ @Override protected Component getComponent(ValueResult valueResult, boolean enabled) { final boolean canAdd = enabled && userCanPerformChangeType(ValueEventChangeType.ADD); currentValueResult = valueResult; // Creates the upload button (with a hidden form panel). uploadField = new ButtonFileUploadField(); uploadField.setButtonCaption(I18N.CONSTANTS.flexibleElementFilesListAddDocument()); uploadField.setName(FileUploadUtils.DOCUMENT_CONTENT); uploadField.setButtonIcon(IconImageBundle.ICONS.attach()); uploadField.setEnabled(canAdd); final FormPanel uploadFormPanel = new FormPanel(); uploadFormPanel.setLayout(new FitLayout()); uploadFormPanel.setBodyBorder(false); uploadFormPanel.setHeaderVisible(false); uploadFormPanel.setPadding(0); uploadFormPanel.setEncoding(Encoding.MULTIPART); uploadFormPanel.setMethod(Method.POST); uploadFormPanel.setAction(GWT.getModuleBaseURL() + "upload"); final HiddenField<String> elementIdHidden = new HiddenField<String>(); elementIdHidden.setName(FileUploadUtils.DOCUMENT_FLEXIBLE_ELEMENT); final HiddenField<String> projectIdHidden = new HiddenField<String>(); projectIdHidden.setName(FileUploadUtils.DOCUMENT_PROJECT); final HiddenField<String> nameHidden = new HiddenField<String>(); nameHidden.setName(FileUploadUtils.DOCUMENT_NAME); final HiddenField<String> authorHidden = new HiddenField<String>(); authorHidden.setName(FileUploadUtils.DOCUMENT_AUTHOR); final HiddenField<String> pointDateHidden = new HiddenField<String>(); pointDateHidden.setName(FileUploadUtils.MONITORED_POINT_DATE); final HiddenField<String> pointLabelHidden = new HiddenField<String>(); pointLabelHidden.setName(FileUploadUtils.MONITORED_POINT_LABEL); uploadFormPanel.add(uploadField); uploadFormPanel.add(nameHidden); uploadFormPanel.add(authorHidden); uploadFormPanel.add(elementIdHidden); uploadFormPanel.add(projectIdHidden); uploadFormPanel.add(pointDateHidden); uploadFormPanel.add(pointLabelHidden); // Creates actions tool bar. final ToolBar actionsToolBar = new ToolBar(); actionsToolBar.setAlignment(HorizontalAlignment.LEFT); actionsToolBar.add(uploadFormPanel); // Upload the selected file immediately after it's selected. uploadField.addListener(Events.OnChange, new Listener<DomEvent>() { @Override public void handleEvent(DomEvent be) { be.getEvent().stopPropagation(); if (transfertManager.canUpload()) { // Set hidden fields values. elementIdHidden.setValue(String.valueOf(getId())); projectIdHidden.setValue(String.valueOf(currentContainerDTO.getId())); nameHidden.setValue(uploadField.getValue()); authorHidden.setValue(String.valueOf(auth().getUserId())); // Debug form hidden values. if (Log.isDebugEnabled()) { final StringBuilder sb = new StringBuilder(); sb.append("Upload a new file with parameters: "); sb.append("name="); sb.append(nameHidden.getValue()); sb.append(" ; author id="); sb.append(authorHidden.getValue()); sb.append(" ; project id="); sb.append(projectIdHidden.getValue()); sb.append(" ; element id="); sb.append(elementIdHidden.getValue()); Log.debug(sb.toString()); } transfertManager.upload(uploadFormPanel, createUploadProgressListener()); } else { N10N.warn(I18N.CONSTANTS.flexibleElementFilesListUploadUnable()); } } }); updateStore(); // Creates the grid which contains the files list. final FlexibleGrid<FileDTO> filesGrid = new FlexibleGrid<FileDTO>(store, null, getColumnModel(enabled)); filesGrid.setAutoExpandColumn("name"); filesGrid.setVisibleElementsCount(5); store.setStoreSorter(getStoreSorter()); // Creates the main panel. mainPanel = new ContentPanel(); mainPanel.setHeaderVisible(true); mainPanel.setBorders(true); int max = getAdjustedLimit(); if (max != -1) { mainPanel.setHeadingHtml(getLabel() + " (" + I18N.MESSAGES.flexibleElementFilesListLimitReached(String.valueOf(getAdjustedLimit())) + ")"); } else { mainPanel.setHeadingHtml(getLabel()); } if (canAdd) { mainPanel.setTopComponent(actionsToolBar); } mainPanel.add(filesGrid); return mainPanel; } /** * Refreshes files list */ public void updateComponent() { // Server call to obtain elements value dispatch.execute(new GetValue(currentContainerDTO.getId(), getId(), getEntityName()), new CommandResultHandler<ValueResult>() { @Override public void onCommandFailure(final Throwable throwable) { // The widget cannot be refreshed for the new value state. } @Override public void onCommandSuccess(final ValueResult valueResult) { currentValueResult = valueResult; updateStore(); } }); } /** * {@inheritDoc} */ @Override protected boolean userCanPerformChangeTypeOnProject(ValueEventChangeType changeType, ProjectDTO project) { return super.userCanPerformChangeTypeOnProject(changeType, project) && ( // Add and edit changes are allowed if the user has basic rights to edit this field. changeType != ValueEventChangeType.REMOVE || // The user needs the "remove project file" to remove file from this field. ProfileUtils.isGranted(auth(), GlobalPermissionEnum.REMOVE_PROJECT_FILE) ); } /** * {@inheritDoc} */ @Override protected boolean userCanPerformChangeTypeOnOrgUnit(ValueEventChangeType changeType, OrgUnitDTO orgUnit) { return super.userCanPerformChangeTypeOnOrgUnit(changeType, orgUnit) && ( // Add and edit changes are allowed if the user has basic rights to edit this field. changeType != ValueEventChangeType.REMOVE || // The user needs the "remove orgunit file" to remove file from this field. ProfileUtils.isGranted(auth(), GlobalPermissionEnum.REMOVE_ORG_UNIT_FILE) ); } private ProgressListener createUploadProgressListener() { return new ProgressListener() { @Override public void onProgress(double progress, double speed) { } @Override public void onFailure(Cause cause) { mainPanel.unmask(); // Displaying an error message. final StringBuilder errorMessageBuilder = new StringBuilder(); errorMessageBuilder.append(I18N.CONSTANTS.flexibleElementFilesListUploadErrorDetails()).append("\n"); switch (cause) { case FILE_TOO_LARGE: errorMessageBuilder.append(I18N.CONSTANTS.flexibleElementFilesListUploadErrorTooBig()); break; default: errorMessageBuilder.append(I18N.CONSTANTS.flexibleElementFilesListUploadErrorEmpty()); break; } N10N.warn(I18N.CONSTANTS.flexibleElementFilesListUploadError(), errorMessageBuilder.toString()); } @Override public void onLoad(String result) { // Reset upload fields. uploadField.reset(); handlerManager.fireEvent(new RequiredValueEvent(true, true)); mainPanel.unmask(); // Displaying a notification of success N10N.validNotif(I18N.CONSTANTS.infoConfirmation(), I18N.CONSTANTS.flexibleElementFilesListUploadFileConfirm()); // Adds the monitored point. if (monitoredPointGenerated && currentContainerDTO != null && currentContainerDTO instanceof ProjectDTO) { createMonitoredPoint(result); } updateComponent(); } }; } /** * Creates a new monitored point after a successful upload (if needed). * * @param value * Result from an upload. */ private void createMonitoredPoint(String value) { Log.debug("[updateComponentAfterUpload] Adds a monitored point with response '" + value + "'."); final FileUploadResponse response = FileUploadResponse.parse(value); final MonitoredPointDTO point = response.getMonitoredPoint(); if (point != null) { if (Log.isDebugEnabled()) { Log.debug("[updateComponentAfterUpload] Adds a monitored point '" + point.getLabel() + "'"); } N10N.validNotif(I18N.CONSTANTS.infoConfirmation(), I18N.CONSTANTS.monitoredPointAddConfirm()); // Forces the default completion state. point.setCompletionDate(null); if (eventBus != null) { // Sends an event to notify monitored point update. eventBus.fireEvent(new UpdateEvent(UpdateEvent.REMINDER_UPDATED, ReminderType.MONITORED_POINT)); } } monitoredPointGenerated = false; } /** * Defines the column model for the files list grid. * * @param enabled * If the component is enabled. * @return The column model. */ private ColumnConfig[] getColumnModel(final boolean enabled) { final boolean canAdd = enabled && userCanPerformChangeType(ValueEventChangeType.ADD); final boolean canRemove = enabled && userCanPerformChangeType(ValueEventChangeType.REMOVE); // File's add date. final ColumnConfig dateColumn = new ColumnConfig(); dateColumn.setId("date"); dateColumn.setHeaderText(I18N.CONSTANTS.flexibleElementFilesListDate()); dateColumn.setWidth(60); dateColumn.setRenderer(new GridCellRenderer<FileDTO>() { final DateTimeFormat format = DateUtils.DATE_SHORT; @Override public Object render(FileDTO model, String property, ColumnData config, int rowIndex, int colIndex, ListStore<FileDTO> store, Grid<FileDTO> grid) { final FileVersionDTO last = model.getLastVersion(); return format.format(last.getAddedDate()); } }); // File's name. final ColumnConfig nameColumn = new ColumnConfig(); nameColumn.setId("name"); nameColumn.setHeaderText(I18N.CONSTANTS.flexibleElementFilesListName()); nameColumn.setWidth(100); nameColumn.setRenderer(new GridCellRenderer<FileDTO>() { @Override public Object render(final FileDTO model, String property, ColumnData config, int rowIndex, int colIndex, ListStore<FileDTO> store, Grid<FileDTO> grid) { return createDownloadLink(model, null, transfertManager); } }); // File's author. final ColumnConfig authorColumn = new ColumnConfig(); authorColumn.setId("author"); authorColumn.setHeaderText(I18N.CONSTANTS.flexibleElementFilesListAuthor()); authorColumn.setWidth(100); authorColumn.setRenderer(new GridCellRenderer<FileDTO>() { @Override public Object render(FileDTO model, String property, ColumnData config, int rowIndex, int colIndex, ListStore<FileDTO> store, Grid<FileDTO> grid) { final FileVersionDTO last = model.getLastVersion(); return last.getAuthorFirstName() != null ? last.getAuthorFirstName() + " " + last.getAuthorName() : last.getAuthorName(); } }); // File's last version number. final ColumnConfig versionColumn = new ColumnConfig(); versionColumn.setId("version"); versionColumn.setHeaderText(I18N.CONSTANTS.flexibleElementFilesListVersion()); versionColumn.setWidth(20); versionColumn.setRenderer(new GridCellRenderer<FileDTO>() { @Override public Object render(final FileDTO model, String property, ColumnData config, int rowIndex, int colIndex, ListStore<FileDTO> store, Grid<FileDTO> grid) { final FileVersionDTO last = model.getLastVersion(); return last.getVersionNumber(); } }); // Upload new version. final ColumnConfig addVersionColumn = new ColumnConfig(); addVersionColumn.setId("addVersion"); addVersionColumn.setHeaderText(null); addVersionColumn.setWidth(60); addVersionColumn.setSortable(false); addVersionColumn.setRenderer(new GridCellRenderer<FileDTO>() { @Override public Object render(final FileDTO model, String property, ColumnData config, int rowIndex, int colIndex, ListStore<FileDTO> store, Grid<FileDTO> grid) { final ButtonFileUploadField uploadField = new ButtonFileUploadField(); uploadField.setButtonCaption(I18N.CONSTANTS.flexibleElementFilesListUploadVersion()); uploadField.setName(FileUploadUtils.DOCUMENT_CONTENT); uploadField.setButtonIcon(IconImageBundle.ICONS.attach()); uploadField.setEnabled(canAdd); final FormPanel uploadFormPanel = new FormPanel(); uploadFormPanel.setLayout(new FitLayout()); uploadFormPanel.setBodyBorder(false); uploadFormPanel.setHeaderVisible(false); uploadFormPanel.setPadding(0); uploadFormPanel.setEncoding(Encoding.MULTIPART); uploadFormPanel.setMethod(Method.POST); uploadFormPanel.setAction(GWT.getModuleBaseURL() + "upload"); final HiddenField<String> elementIdHidden = new HiddenField<String>(); elementIdHidden.setName(FileUploadUtils.DOCUMENT_FLEXIBLE_ELEMENT); final HiddenField<String> projectIdHidden = new HiddenField<String>(); projectIdHidden.setName(FileUploadUtils.DOCUMENT_PROJECT); final HiddenField<String> idHidden = new HiddenField<String>(); idHidden.setName(FileUploadUtils.DOCUMENT_ID); final HiddenField<String> nameHidden = new HiddenField<String>(); nameHidden.setName(FileUploadUtils.DOCUMENT_NAME); final HiddenField<String> authorHidden = new HiddenField<String>(); authorHidden.setName(FileUploadUtils.DOCUMENT_AUTHOR); final HiddenField<String> versionHidden = new HiddenField<String>(); versionHidden.setName(FileUploadUtils.DOCUMENT_VERSION); uploadFormPanel.add(uploadField); uploadFormPanel.add(elementIdHidden); uploadFormPanel.add(projectIdHidden); uploadFormPanel.add(authorHidden); uploadFormPanel.add(idHidden); uploadFormPanel.add(nameHidden); uploadFormPanel.add(versionHidden); uploadField.addListener(Events.OnChange, new Listener<DomEvent>() { @Override public void handleEvent(DomEvent be) { be.getEvent().stopPropagation(); if (transfertManager.canUpload()) { // Set hidden fields values. elementIdHidden.setValue(String.valueOf(getId())); projectIdHidden.setValue(String.valueOf(currentContainerDTO.getId())); idHidden.setValue(String.valueOf(model.getId())); nameHidden.setValue(uploadField.getValue()); authorHidden.setValue(String.valueOf(auth().getUserId())); versionHidden.setValue(String.valueOf(model.getLastVersion().getVersionNumber() + 1)); // Debug form hidden values. if (Log.isDebugEnabled()) { final StringBuilder sb = new StringBuilder(); sb.append("Upload a new version with parameters: "); sb.append("version number="); sb.append(versionHidden.getValue()); sb.append(" ; file id="); sb.append(idHidden.getValue()); sb.append(" ; file name="); sb.append(nameHidden.getValue()); sb.append(" ; author id="); sb.append(authorHidden.getValue()); Log.debug(sb.toString()); } // Submits the form. transfertManager.upload(uploadFormPanel, createUploadProgressListener()); } else { N10N.warn(I18N.CONSTANTS.flexibleElementFilesListUploadUnable()); } } }); return uploadFormPanel; } }); // Versions list. final ColumnConfig historyColumn = new ColumnConfig(); historyColumn.setId("history"); historyColumn.setHeaderText(null); historyColumn.setWidth(20); historyColumn.setSortable(false); historyColumn.setRenderer(new GridCellRenderer<FileDTO>() { @Override public Object render(final FileDTO model, String property, ColumnData config, int rowIndex, int colIndex, ListStore<FileDTO> store, Grid<FileDTO> grid) { final com.google.gwt.user.client.ui.Label historyButton = new com.google.gwt.user.client.ui.Label(I18N.CONSTANTS.flexibleElementFilesListHistory()); historyButton.addStyleName("flexibility-action"); historyButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent e) { final FileDetailsWindow versionsWindow = new FileDetailsWindow(dispatch, transfertManager, canRemove); versionsWindow.addListener(new FileDetailsWindow.FileDetailsWindowListener() { @Override public void versionDeleted(FileVersionDTO version) { updateComponent(); } }); versionsWindow.show(model); } }); return historyButton; } }); // Delete. final ColumnConfig deleteColumn = new ColumnConfig(); deleteColumn.setId("delete"); deleteColumn.setHeaderText(null); deleteColumn.setWidth(10); deleteColumn.setSortable(false); deleteColumn.setRenderer(new GridCellRenderer<FileDTO>() { @Override public Object render(final FileDTO model, String property, ColumnData config, int rowIndex, int colIndex, final ListStore<FileDTO> store, Grid<FileDTO> grid) { if (!enabled) { return new Label("-"); } final Image image = IconImageBundle.ICONS.remove().createImage(); image.setTitle(I18N.CONSTANTS.remove()); image.addStyleName("flexibility-action"); image.addClickHandler(new ClickHandler() { @Override public void onClick(final ClickEvent event) { // Asks the client to confirm the file removal. N10N.confirmation(I18N.CONSTANTS.flexibleElementFilesListDelete(), I18N.MESSAGES.flexibleElementFilesListConfirmDelete(model.getName()), new ConfirmCallback() { /** * OK action. */ @Override public void onAction() { // Deletes it. dispatch.execute(new Delete(model, currentContainerDTO.getId(), getId()), new CommandResultHandler<VoidResult>() { @Override public void onCommandFailure(final Throwable caught) { N10N.error(I18N.CONSTANTS.flexibleElementFilesListDeleteError(), I18N.CONSTANTS.flexibleElementFilesListDeleteErrorDetails()); } @Override public void onCommandSuccess(final VoidResult result) { store.remove(model); uploadField.setEnabled(true); uploadField.reset(); if (store.getCount() == 0) { handlerManager.fireEvent(new RequiredValueEvent(false, true)); } } }, new LoadingMask(mainPanel)); } }); } }); return image; } }); if (canRemove) { return new ColumnConfig[] { dateColumn, nameColumn, authorColumn, versionColumn, addVersionColumn, historyColumn, deleteColumn }; } else { return new ColumnConfig[] { dateColumn, nameColumn, authorColumn, versionColumn, addVersionColumn, historyColumn }; } } private static StoreSorter<FileDTO> getStoreSorter() { if(storeSorter == null) { storeSorter = new StoreSorter<FileDTO>() { @Override public int compare(Store<FileDTO> store, FileDTO m1, FileDTO m2, String property) { final FileVersionDTO last1 = m1.getLastVersion(); final FileVersionDTO last2 = m2.getLastVersion(); if (FileDTO.DATE.equals(property)) { return last1.getAddedDate().compareTo(last2.getAddedDate()); } else if (FileDTO.AUTHOR.equals(property)) { final String authorM1 = last1.getAuthorFirstName() != null ? last1.getAuthorFirstName() + " " + last1.getAuthorName() : last1.getAuthorName(); final String authorM2 = last2.getAuthorFirstName() != null ? last2.getAuthorFirstName() + " " + last2.getAuthorName() : last2.getAuthorName(); return authorM1.compareTo(authorM2); } else if (FileDTO.VERSION.equals(property)) { return Integer.valueOf(last1.getVersionNumber()).compareTo(last2.getVersionNumber()); } else if (FileDTO.NAME.equals(property)) { final String title1 = last1.getName() + '.' + last1.getExtension(); final String title2 = last2.getName() + '.' + last2.getExtension(); return title1.compareTo(title2); } else { return super.compare(store, m1, m2, property); } } }; } return storeSorter; } /** * Builds and shows a window with the file's details. * * @author tmi */ private static final class FileDetailsWindow { /** * Listener. * * @author tmi */ private interface FileDetailsWindowListener { /** * Method called when a version is deleted. * * @param version * The deleted version. */ public void versionDeleted(FileVersionDTO version); } private final DispatchAsync dispatch; /** * GXT window. */ private final Window window; /** * Window main panel. */ private final ContentPanel mainPanel; /** * The versiions grid. */ private final FlexibleGrid<FileVersionDTO> grid; /** * The current displayed file. */ private FileDTO file; /** * Versions store. */ private final ListStore<FileVersionDTO> store; /** * Listeners */ private final ArrayList<FileDetailsWindowListener> listeners = new ArrayList<FileDetailsWindowListener>(); private final TransfertManager transfertManager; /** * Builds the window. * * @param dispatch * @param enabled * If the component is enabled. */ public FileDetailsWindow(final DispatchAsync dispatch, final TransfertManager transfertManager, boolean canRemove) { this.dispatch = dispatch; this.transfertManager = transfertManager; store = new ListStore<FileVersionDTO>(); grid = new FlexibleGrid<FileVersionDTO>(store, null, 10, getColumnModel(canRemove)); grid.setAutoExpandColumn("name"); grid.setAutoHeight(true); store.setStoreSorter(new StoreSorter<FileVersionDTO>() { @Override public int compare(Store<FileVersionDTO> store, FileVersionDTO m1, FileVersionDTO m2, String property) { if ("author".equals(property)) { final String authorM1 = m1.getAuthorFirstName() != null ? m1.getAuthorFirstName() + " " + m1.getAuthorName() : m1.getAuthorName(); final String authorM2 = m2.getAuthorFirstName() != null ? m2.getAuthorFirstName() + " " + m2.getAuthorName() : m2.getAuthorName(); return authorM1.compareTo(authorM2); } else if ("name".equals(property)) { final String title1 = m1.getName() + '.' + m1.getExtension(); final String title2 = m2.getName() + '.' + m2.getExtension(); return title1.compareTo(title2); } else if ("size".equals(property)) { return new Long(m1.getSize()).compareTo(m2.getSize()); } else { return super.compare(store, m1, m2, property); } } }); mainPanel = new ContentPanel(); mainPanel.setHeaderVisible(false); mainPanel.setBodyBorder(false); mainPanel.add(grid); mainPanel.setScrollMode(Scroll.AUTOY); // Builds window. window = new Window(); window.setSize(550, 250); window.setPlain(true); window.setModal(true); window.setBlinkModal(true); window.setResizable(false); window.setLayout(new FitLayout()); window.add(mainPanel); } /** * Defines the column model for the versions list grid. * * @param enabled * If the component is enabled. * @return The column model. */ private ColumnConfig[] getColumnModel(final boolean canRemove) { // Version's number. final ColumnConfig versionColumn = new ColumnConfig(); versionColumn.setId("versionNumber"); versionColumn.setHeaderText(I18N.CONSTANTS.flexibleElementFilesListVersionNumber()); versionColumn.setWidth(55); // Version's add date. final ColumnConfig dateColumn = new ColumnConfig(); dateColumn.setId("addedDate"); dateColumn.setHeaderText(I18N.CONSTANTS.flexibleElementFilesListDate()); dateColumn.setWidth(110); dateColumn.setDateTimeFormat(DateUtils.DATE_TIME_SHORT); // Version's author. final ColumnConfig authorColumn = new ColumnConfig(); authorColumn.setId("author"); authorColumn.setHeaderText(I18N.CONSTANTS.flexibleElementFilesListAuthor()); authorColumn.setWidth(100); authorColumn.setRenderer(new GridCellRenderer<FileVersionDTO>() { @Override public Object render(FileVersionDTO model, String property, ColumnData config, int rowIndex, int colIndex, ListStore<FileVersionDTO> store, Grid<FileVersionDTO> grid) { return model.getAuthorFirstName() != null ? model.getAuthorFirstName() + " " + model.getAuthorName() : model.getAuthorName(); } }); // Version's name. final ColumnConfig nameColumn = new ColumnConfig(); nameColumn.setId("name"); nameColumn.setHeaderText(I18N.CONSTANTS.flexibleElementFilesListName()); nameColumn.setWidth(100); nameColumn.setRenderer(new GridCellRenderer<FileVersionDTO>() { @Override public Object render(FileVersionDTO model, String property, ColumnData config, int rowIndex, int colIndex, ListStore<FileVersionDTO> store, Grid<FileVersionDTO> grid) { return createDownloadLink(file, model.getVersionNumber(), transfertManager); } }); // Version's size. final ColumnConfig sizeColumn = new ColumnConfig(); sizeColumn.setId("size"); sizeColumn.setHeaderText(I18N.CONSTANTS.flexibleElementFilesListSize()); sizeColumn.setWidth(60); sizeColumn.setRenderer(new GridCellRenderer<FileVersionDTO>() { @Override public Object render(FileVersionDTO model, String property, ColumnData config, int rowIndex, int colIndex, ListStore<FileVersionDTO> store, Grid<FileVersionDTO> grid) { final Size size = Size.convertToBestUnit(new Size(model.getSize(), Size.SizeUnit.BYTE)); return Math.round(size.getSize()) + " " + Size.SizeUnit.getTranslation(size.getUnit()); } }); // Delete. final ColumnConfig deleteColumn = new ColumnConfig(); deleteColumn.setId("delete"); deleteColumn.setHeaderText(null); deleteColumn.setWidth(25); deleteColumn.setSortable(false); deleteColumn.setRenderer(new GridCellRenderer<FileVersionDTO>() { @Override public Object render(final FileVersionDTO model, String property, ColumnData config, int rowIndex, int colIndex, final ListStore<FileVersionDTO> store, Grid<FileVersionDTO> grid) { final Image image = IconImageBundle.ICONS.remove().createImage(); image.setTitle(I18N.CONSTANTS.remove()); image.addStyleName("flexibility-action"); image.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { // Do not delete a single version. if (store.getCount() <= 1) { N10N.warn(I18N.CONSTANTS.flexibleElementFilesListVersionDeleteForbidden(), I18N.CONSTANTS.flexibleElementFilesListVersionDeleteForbiddenDetails()); return; } // Asks the client to confirm the version deletion. N10N.confirmation(I18N.CONSTANTS.flexibleElementFilesListVersionDelete(), I18N.MESSAGES.flexibleElementFilesListConfirmVersionDelete(String.valueOf(model.getVersionNumber())), new ConfirmCallback() { @Override public void onAction() { // Deletes it. dispatch.execute(new Delete(model, model.<Integer>get("containerId"), model.<Integer>get("elementId")), new CommandResultHandler<VoidResult>() { @Override public void onCommandFailure(final Throwable caught) { N10N.warn(I18N.CONSTANTS.flexibleElementFilesListDeleteError(), I18N.CONSTANTS.flexibleElementFilesListDeleteErrorDetails()); } @Override public void onCommandSuccess(final VoidResult result) { store.remove(model); fireVersionDeleted(model); } }, new LoadingMask(window)); } }); } }); return image; } }); if (canRemove) { return new ColumnConfig[] { versionColumn, dateColumn, authorColumn, nameColumn, sizeColumn, deleteColumn }; } else { return new ColumnConfig[] { versionColumn, dateColumn, authorColumn, nameColumn, sizeColumn }; } } /** * Shows the windows for the given file. * * @param file * The file. */ public void show(FileDTO file) { if (file == null) { return; } this.file = file; final FileVersionDTO lastVersion = file.getLastVersion(); // Clears the existing versions. store.removeAll(); // Adds each version to the store to be displayed in the grid. for (final FileVersionDTO version : this.file.getVersions()) { version.set("containerId", file.get("containerId")); version.set("elementId", file.get("elementId")); version.set("fileId", file.getId()); store.add(version); } store.sort("versionNumber", SortDir.DESC); // Configures the window parameters to be consistent with the new displayed file. window.setHeadingText(I18N.CONSTANTS.flexibleElementFilesListHistory() + " - " + lastVersion.getName() + '.' + lastVersion.getExtension()); window.show(); } /** * Adds a listener to the window. * * @param listener * The new listener. */ public void addListener(FileDetailsWindowListener listener) { listeners.add(listener); } /** * Method called when a version is deleted. * * @param version * The deleted version. */ protected void fireVersionDeleted(FileVersionDTO version) { for (final FileDetailsWindowListener listener : listeners) { listener.versionDeleted(version); } } } /** * Create a download link for the given file. * * @param file * The file to download. * @return The download link. */ private static com.google.gwt.user.client.ui.Label createDownloadLink(final FileDTO file, final Integer version, final TransfertManager transfertManager) { final FileVersionDTO versionDTO; if (version != null) { versionDTO = file.getVersion(version); } else { versionDTO = file.getLastVersion(); } if (versionDTO == null) { return null; } if(!versionDTO.isAvailable()) { return new com.google.gwt.user.client.ui.Label( versionDTO.getName() + '.' + versionDTO.getExtension() + ' ' + I18N.CONSTANTS.flexibleElementFilesListNotUploadedYet()); } final com.google.gwt.user.client.ui.Label downloadButton = new com.google.gwt.user.client.ui.Label(versionDTO.getName() + '.' + versionDTO.getExtension()); downloadButton.addStyleName("flexibility-action"); // Buttons listeners. downloadButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent e) { transfertManager.canDownload(versionDTO, new AsyncCallback<Boolean>() { @Override public void onFailure(Throwable caught) { N10N.error(I18N.CONSTANTS.flexibleElementFilesListDownloadError(), I18N.CONSTANTS.flexibleElementFilesListDownloadErrorDetails()); } @Override public void onSuccess(Boolean result) { if (result) { transfertManager.download(versionDTO, new ProgressListener() { @Override public void onProgress(double progress, double speed) { } @Override public void onFailure(Cause cause) { N10N.error(I18N.CONSTANTS.flexibleElementFilesListDownloadError(), I18N.CONSTANTS.flexibleElementFilesListDownloadErrorDetails()); } @Override public void onLoad(String result) { } }); } else { N10N.error(I18N.CONSTANTS.flexibleElementFilesListDownloadError(), I18N.CONSTANTS.flexibleElementFilesListDownloadUnable()); } } }); } }); return downloadButton; } /** * Utility class used to manipulate file's sizes. * * @author tmi */ public static final class Size { private final double size; private final SizeUnit unit; public Size(double size, SizeUnit unit) { this.size = size; this.unit = unit; } public double getSize() { return size; } public SizeUnit getUnit() { return unit; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(size); sb.append(unit.name()); return sb.toString(); } /** * Converts a size from one unit to another. * * @param size * The size (must be <code>positive</code> and not <code>null</code>). * @param targetUnit * The desired size unit. * @return The size converted. */ public static Size convert(Size size, SizeUnit targetUnit) { if (targetUnit == null) { throw new IllegalArgumentException("Units cannot be null."); } if (size == null) { throw new IllegalArgumentException("Size cannot be null."); } if (size.size < 0) { throw new IllegalArgumentException("Size cannot be negative."); } if (size.size == 0 || size.unit == targetUnit) { return size; } double computedSize; if (size.unit.weight < targetUnit.weight) { computedSize = size.size / ((targetUnit.weight - size.unit.weight) * SizeUnit.STEP); } else { computedSize = size.size * Math.pow(SizeUnit.STEP, size.unit.weight - targetUnit.weight); } return new Size(computedSize, targetUnit); } /** * Converts a size to the best appropriate unit (greater than <code>0</code>). * * @param size * The size (must be <code>positive</code> and not <code>null</code>). * @return The given {@code size} converted to the best appropriate unit (greater than <code>0</code>). */ public static Size convertToBestUnit(Size size) { if (size == null) { throw new IllegalArgumentException("Size cannot be null."); } if (size.size < 0) { throw new IllegalArgumentException("Size cannot be negative."); } if (size.size == 0) { return size; } double computedSize = 0; SizeUnit computedUnit = null; double currentSize = size.size; SizeUnit currentUnit = size.unit; if (size.size > 1) { while (currentSize >= 1 && currentUnit != null) { computedSize = currentSize; computedUnit = currentUnit; currentSize = currentSize / SizeUnit.STEP; currentUnit = SizeUnit.getPrevUnit(currentUnit); } } else { computedSize = size.size; computedUnit = size.unit; while (computedSize <= 1 && currentUnit != null) { computedSize = currentSize; computedUnit = currentUnit; currentSize = currentSize * SizeUnit.STEP; currentUnit = SizeUnit.getNextUnit(currentUnit); } } return new Size(computedSize, computedUnit); } /** * Represents size units. * * @author tmi */ public static enum SizeUnit { BYTE(1), KILOBYTE(2), MEGABYTE(3), GIGABYTE(4), TERABYTE(5); private static final int STEP = 1024; private final int weight; private SizeUnit(int weight) { this.weight = weight; } /** * Gets the next unit (the first greater one). * * @param unit * The base unit. * @return The next unit. */ private static SizeUnit getNextUnit(SizeUnit unit) { if (unit == null) { return null; } for (final SizeUnit u : SizeUnit.values()) { if (u.weight + 1 == unit.weight) { return u; } } return null; } /** * Gets the previous unit (the first lower one). * * @param unit * The base unit. * @return The previous unit. */ private static SizeUnit getPrevUnit(SizeUnit unit) { if (unit == null) { return null; } for (final SizeUnit u : SizeUnit.values()) { if (u.weight - 1 == unit.weight) { return u; } } return null; } /** * Gets the translation key of this unit specific to the current application. * * @param unit * The unit. * @return The translation key. */ public static String getTranslation(SizeUnit unit) { switch (unit) { case BYTE: return I18N.CONSTANTS.flexibleElementFilesListSizeByteUnit(); case KILOBYTE: return I18N.CONSTANTS.flexibleElementFilesListSizeKByteUnit(); case MEGABYTE: return I18N.CONSTANTS.flexibleElementFilesListSizeMByteUnit(); case GIGABYTE: return I18N.CONSTANTS.flexibleElementFilesListSizeGByteUnit(); case TERABYTE: return I18N.CONSTANTS.flexibleElementFilesListSizeTByteUnit(); default: return ""; } } } } }