/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE file at the root of the source * tree and available online at * * https://github.com/keeps/roda */ /** * */ package org.roda.wui.client.browse; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.common.Pair; import org.roda.core.data.v2.index.facet.Facets; import org.roda.core.data.v2.index.filter.EmptyKeyFilterParameter; import org.roda.core.data.v2.index.filter.Filter; import org.roda.core.data.v2.index.filter.SimpleFilterParameter; import org.roda.core.data.v2.ip.AIPState; import org.roda.core.data.v2.ip.IndexedFile; import org.roda.core.data.v2.ip.IndexedRepresentation; import org.roda.wui.client.browse.bundle.BrowseRepresentationBundle; import org.roda.wui.client.browse.bundle.DescriptiveMetadataViewBundle; import org.roda.wui.client.common.LastSelectedItemsSingleton; import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.actions.Actionable; import org.roda.wui.client.common.actions.DisseminationActions; import org.roda.wui.client.common.actions.FileActions; import org.roda.wui.client.common.actions.RepresentationActions; import org.roda.wui.client.common.lists.DIPList; import org.roda.wui.client.common.lists.SearchFileList; import org.roda.wui.client.common.lists.pagination.ListSelectionUtils; import org.roda.wui.client.common.search.SearchFilters; import org.roda.wui.client.common.search.SearchPanel; import org.roda.wui.client.common.utils.AsyncCallbackUtils; import org.roda.wui.client.common.utils.HtmlSnippetUtils; import org.roda.wui.client.common.utils.JavascriptUtils; import org.roda.wui.client.main.BreadcrumbPanel; import org.roda.wui.client.main.BreadcrumbUtils; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.DescriptionLevelUtils; import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.tools.ListUtils; import org.roda.wui.common.client.tools.RestErrorOverlayType; import org.roda.wui.common.client.tools.RestUtils; import org.roda.wui.common.client.widgets.Toast; import org.roda.wui.common.client.widgets.wcag.WCAGUtilities; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JsonUtils; import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.logical.shared.SelectionEvent; import com.google.gwt.event.logical.shared.SelectionHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.http.client.Request; import com.google.gwt.http.client.RequestBuilder; import com.google.gwt.http.client.RequestCallback; import com.google.gwt.http.client.RequestException; import com.google.gwt.http.client.Response; import com.google.gwt.i18n.client.LocaleInfo; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.safehtml.shared.SafeUri; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.TabPanel; import com.google.gwt.user.client.ui.Widget; import config.i18n.client.ClientMessages; /** * @author Luis Faria * */ public class BrowseRepresentation extends Composite { private static final MyUiBinder uiBinder = GWT.create(MyUiBinder.class); private static final ClientMessages messages = (ClientMessages) GWT.create(ClientMessages.class); public static final HistoryResolver RESOLVER = new HistoryResolver() { @Override public void resolve(List<String> historyTokens, final AsyncCallback<Widget> callback) { if (historyTokens.size() == 2) { final String historyAipId = historyTokens.get(0); final String histortyRepresentationId = historyTokens.get(1); BrowserService.Util.getInstance().retrieveBrowseRepresentationBundle(historyAipId, histortyRepresentationId, LocaleInfo.getCurrentLocale().getLocaleName(), representationFields, new AsyncCallback<BrowseRepresentationBundle>() { @Override public void onFailure(Throwable caught) { Toast.showError(caught.getClass().getSimpleName(), caught.getMessage()); errorRedirect(callback); } @Override public void onSuccess(final BrowseRepresentationBundle representationBundle) { callback.onSuccess(new BrowseRepresentation(representationBundle)); } }); } else { errorRedirect(callback); } } @Override public void isCurrentUserPermitted(AsyncCallback<Boolean> callback) { UserLogin.getInstance().checkRole(BrowseAIP.RESOLVER, callback); } @Override public List<String> getHistoryPath() { return ListUtils.concat(BrowseAIP.RESOLVER.getHistoryPath(), getHistoryToken()); } @Override public String getHistoryToken() { return "representation"; } private void errorRedirect(AsyncCallback<Widget> callback) { HistoryUtils.newHistory(BrowseAIP.RESOLVER); callback.onSuccess(null); } }; interface MyUiBinder extends UiBinder<Widget, BrowseRepresentation> { } private List<HandlerRegistration> handlers; private IndexedRepresentation representation; private String aipId; private String repId; private String repUUID; private static final List<String> representationFields = new ArrayList<>(Arrays.asList(RodaConstants.INDEX_UUID, RodaConstants.REPRESENTATION_AIP_ID, RodaConstants.REPRESENTATION_ID, RodaConstants.REPRESENTATION_TYPE)); private static final String ALL_FILTER = SearchFilters.allFilter(IndexedFile.class.getName()); // Focus @UiField FocusPanel keyboardFocus; // STATUS @UiField HTML aipState; // IDENTIFICATION @UiField SimplePanel representationIcon; @UiField HTML representationType; @UiField Label representationId; @UiField BreadcrumbPanel breadcrumb; // DESCRIPTIVE METADATA @UiField TabPanel itemMetadata; @UiField Button newDescriptiveMetadata; // FILES @UiField(provided = true) SearchPanel filesSearch; @UiField(provided = true) SearchFileList filesList; // DISSEMINATIONS @UiField Label disseminationsTitle; @UiField(provided = true) SearchPanel disseminationsSearch; @UiField(provided = true) DIPList disseminationsList; // SIDEBAR @UiField SimplePanel actionsSidebar; @UiField FlowPanel searchSection; @UiField Button searchPrevious, searchNext; public BrowseRepresentation(BrowseRepresentationBundle bundle) { this.representation = bundle.getRepresentation(); this.aipId = representation.getAipId(); this.repId = representation.getId(); this.repUUID = representation.getUUID(); handlers = new ArrayList<>(); String summary = messages.representationListOfFiles(); AIPState state = bundle.getAip().getState(); boolean justActive = AIPState.ACTIVE.equals(state); boolean selectable = true; boolean showFilesPath = false; // FILES Filter filter = new Filter(new SimpleFilterParameter(RodaConstants.FILE_REPRESENTATION_UUID, repUUID), new EmptyKeyFilterParameter(RodaConstants.FILE_PARENT_UUID)); filesList = new SearchFileList(filter, justActive, Facets.NONE, summary, selectable, showFilesPath); LastSelectedItemsSingleton.getInstance().setSelectedJustActive(justActive); filesList.setActionable(FileActions.get(aipId, repId)); ListSelectionUtils.bindBrowseOpener(filesList); filesSearch = new SearchPanel(filter, ALL_FILTER, true, messages.searchPlaceHolder(), false, false, true); filesSearch.setList(filesList); // DISSEMINATIONS disseminationsList = new DIPList(Filter.NULL, Facets.NONE, messages.listOfDisseminations(), true); disseminationsList.setActionable(DisseminationActions.get()); ListSelectionUtils.bindBrowseOpener(disseminationsList); disseminationsSearch = new SearchPanel(Filter.NULL, RodaConstants.DIP_SEARCH, true, messages.searchPlaceHolder(), false, false, true); disseminationsSearch.setList(disseminationsList); // INIT initWidget(uiBinder.createAndBindUi(this)); // STATUS aipState.setHTML(HtmlSnippetUtils.getAIPStateHTML(state)); aipState.setVisible(!justActive); // IDENTIFICATION HTMLPanel representationIconHtmlPanel = new HTMLPanel( DescriptionLevelUtils.getRepresentationTypeIcon(representation.getType(), false)); representationIconHtmlPanel.addStyleName("browseItemIcon-other"); representationIcon.setWidget(representationIconHtmlPanel); String type = representation.getType() != null ? representation.getType() : representation.getId(); representationType.setHTML(HtmlSnippetUtils.getRepresentationTypeHTML(type, representation.isOriginal())); representationId.setText(representation.getId()); breadcrumb.updatePath(BreadcrumbUtils.getRepresentationBreadcrumbs(bundle)); breadcrumb.setVisible(true); // DESCRIPTIVE METADATA final List<Pair<String, HTML>> descriptiveMetadataContainers = new ArrayList<>(); final Map<String, DescriptiveMetadataViewBundle> bundles = new HashMap<>(); for (DescriptiveMetadataViewBundle descMetadataBundle : bundle.getRepresentationDescriptiveMetadata()) { String title = descMetadataBundle.getLabel() != null ? descMetadataBundle.getLabel() : descMetadataBundle.getId(); HTML container = new HTML(); container.addStyleName("metadataContent"); itemMetadata.add(container, title); descriptiveMetadataContainers.add(Pair.of(descMetadataBundle.getId(), container)); bundles.put(descMetadataBundle.getId(), descMetadataBundle); } HandlerRegistration tabHandler = itemMetadata.addSelectionHandler(new SelectionHandler<Integer>() { @Override public void onSelection(SelectionEvent<Integer> event) { if (event.getSelectedItem() < descriptiveMetadataContainers.size()) { Pair<String, HTML> pair = descriptiveMetadataContainers.get(event.getSelectedItem()); String descId = pair.getFirst(); final HTML html = pair.getSecond(); final DescriptiveMetadataViewBundle bundle = bundles.get(descId); if (html.getText().length() == 0) { getDescriptiveMetadataHTML(descId, bundle, new AsyncCallback<SafeHtml>() { @Override public void onFailure(Throwable caught) { if (!AsyncCallbackUtils.treatCommonFailures(caught)) { Toast.showError(messages.errorLoadingDescriptiveMetadata(caught.getMessage())); } } @Override public void onSuccess(SafeHtml result) { html.setHTML(result); } }); } } } }); final int addTabIndex = itemMetadata.getWidgetCount(); FlowPanel addTab = new FlowPanel(); addTab.add(new HTML(SafeHtmlUtils.fromSafeConstant("<i class=\"fa fa-plus-circle\"></i>"))); itemMetadata.add(new Label(), addTab); HandlerRegistration addTabHandler = itemMetadata.addSelectionHandler(new SelectionHandler<Integer>() { @Override public void onSelection(SelectionEvent<Integer> event) { if (event.getSelectedItem() == addTabIndex) { newRepresentationDescriptiveMetadata(); } } }); addTab.addStyleName("addTab"); addTab.getParent().addStyleName("addTabWrapper"); handlers.add(tabHandler); handlers.add(addTabHandler); if (!bundle.getRepresentationDescriptiveMetadata().isEmpty()) { newDescriptiveMetadata.setVisible(false); itemMetadata.setVisible(true); itemMetadata.selectTab(0); } else { newDescriptiveMetadata.setVisible(true); itemMetadata.setVisible(false); } // DISSEMINATIONS (POST-INIT) if (bundle.getDipCount() > 0) { Filter disseminationsFilter = new Filter( new SimpleFilterParameter(RodaConstants.DIP_REPRESENTATION_UUIDS, repUUID)); disseminationsList.set(disseminationsFilter, state.equals(AIPState.ACTIVE), Facets.NONE); disseminationsSearch.setDefaultFilter(disseminationsFilter, true); disseminationsSearch.clearSearchInputBox(); } disseminationsList.getParent().setVisible(bundle.getDipCount() > 0); // SIDEBAR actionsSidebar.setWidget(RepresentationActions.get(aipId).createActionsLayout(representation, new AsyncCallback<Actionable.ActionImpact>() { @Override public void onFailure(Throwable caught) { AsyncCallbackUtils.defaultFailureTreatment(caught); } @Override public void onSuccess(Actionable.ActionImpact impact) { if (Actionable.ActionImpact.UPDATED.equals(impact)) { BrowserService.Util.getInstance().retrieve(IndexedRepresentation.class.getName(), representation.getUUID(), representationFields, new AsyncCallback<IndexedRepresentation>() { @Override public void onFailure(Throwable caught) { AsyncCallbackUtils.defaultFailureTreatment(caught); } @Override public void onSuccess(IndexedRepresentation rep) { representation = rep; representationType.setText(rep.getType()); } }); } } })); ListSelectionUtils.bindLayout(representation, searchPrevious, searchNext, keyboardFocus, true, false, false, searchSection); // CSS this.addStyleName("browse"); this.addStyleName("browse-representation"); this.addStyleName(state.toString().toLowerCase()); Element firstElement = this.getElement().getFirstChildElement(); if ("input".equalsIgnoreCase(firstElement.getTagName())) { firstElement.setAttribute("title", "browse input"); } WCAGUtilities.getInstance().makeAccessible(itemMetadata.getElement()); } @Override protected void onLoad() { super.onLoad(); JavascriptUtils.stickSidebar(); } private void getDescriptiveMetadataHTML(final String descId, final DescriptiveMetadataViewBundle bundle, final AsyncCallback<SafeHtml> callback) { SafeUri uri = RestUtils.createRepresentationDescriptiveMetadataHTMLUri(aipId, repId, descId); RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, uri.asString()); requestBuilder.setHeader("Authorization", "Custom"); try { requestBuilder.sendRequest(null, new RequestCallback() { @Override public void onResponseReceived(Request request, Response response) { if (200 == response.getStatusCode()) { String html = response.getText(); SafeHtmlBuilder b = new SafeHtmlBuilder(); b.append(SafeHtmlUtils.fromSafeConstant("<div class='descriptiveMetadataLinks'>")); if (bundle.hasHistory()) { // History link String historyLink = HistoryUtils.createHistoryHashLink(DescriptiveMetadataHistory.RESOLVER, aipId, repId, descId); String historyLinkHtml = "<a href='" + historyLink + "' class='toolbarLink'><i class='fa fa-history'></i></a>"; b.append(SafeHtmlUtils.fromSafeConstant(historyLinkHtml)); } // Edit link String editLink = HistoryUtils.createHistoryHashLink(EditDescriptiveMetadata.RESOLVER, aipId, repId, descId); String editLinkHtml = "<a href='" + editLink + "' class='toolbarLink'><i class='fa fa-edit'></i></a>"; b.append(SafeHtmlUtils.fromSafeConstant(editLinkHtml)); // Download link SafeUri downloadUri = RestUtils.createRepresentationDescriptiveMetadataDownloadUri(aipId, repId, descId); String downloadLinkHtml = "<a href='" + downloadUri.asString() + "' class='toolbarLink'><i class='fa fa-download'></i></a>"; b.append(SafeHtmlUtils.fromSafeConstant(downloadLinkHtml)); b.append(SafeHtmlUtils.fromSafeConstant("</div>")); b.append(SafeHtmlUtils.fromSafeConstant("<div class='descriptiveMetadataHTML'>")); b.append(SafeHtmlUtils.fromTrustedString(html)); b.append(SafeHtmlUtils.fromSafeConstant("</div>")); SafeHtml safeHtml = b.toSafeHtml(); callback.onSuccess(safeHtml); } else { String text = response.getText(); String message; try { RestErrorOverlayType error = (RestErrorOverlayType) JsonUtils.safeEval(text); message = error.getMessage(); } catch (IllegalArgumentException e) { message = text; } SafeHtmlBuilder b = new SafeHtmlBuilder(); b.append(SafeHtmlUtils.fromSafeConstant("<div class='descriptiveMetadataLinks'>")); if (bundle.hasHistory()) { // History link String historyLink = HistoryUtils.createHistoryHashLink(DescriptiveMetadataHistory.RESOLVER, aipId, repId, descId); String historyLinkHtml = "<a href='" + historyLink + "' class='toolbarLink'><i class='fa fa-history'></i></a>"; b.append(SafeHtmlUtils.fromSafeConstant(historyLinkHtml)); } // Edit link String editLink = HistoryUtils.createHistoryHashLink(EditDescriptiveMetadata.RESOLVER, aipId, repId, descId); String editLinkHtml = "<a href='" + editLink + "' class='toolbarLink'><i class='fa fa-edit'></i></a>"; b.append(SafeHtmlUtils.fromSafeConstant(editLinkHtml)); b.append(SafeHtmlUtils.fromSafeConstant("</div>")); // error message b.append(SafeHtmlUtils.fromSafeConstant("<div class='error'>")); b.append(messages.descriptiveMetadataTransformToHTMLError()); b.append(SafeHtmlUtils.fromSafeConstant("<pre><code>")); b.append(SafeHtmlUtils.fromString(message)); b.append(SafeHtmlUtils.fromSafeConstant("</core></pre>")); b.append(SafeHtmlUtils.fromSafeConstant("</div>")); callback.onSuccess(b.toSafeHtml()); } } @Override public void onError(Request request, Throwable exception) { callback.onFailure(exception); } }); } catch (RequestException e) { callback.onFailure(e); } } private void newRepresentationDescriptiveMetadata() { HistoryUtils.newHistory(CreateDescriptiveMetadata.RESOLVER, RodaConstants.RODA_OBJECT_REPRESENTATION, aipId, repId); } @UiHandler("newDescriptiveMetadata") void buttonNewDescriptiveMetadataEventsHandler(ClickEvent e) { newRepresentationDescriptiveMetadata(); } }