/** * Copyright (c) 2008-2011 Sonatype, Inc. * All rights reserved. Includes the third-party code listed at http://www.sonatype.com/products/nexus/attributions. * * This program is free software: you can redistribute it and/or modify it only under the terms of the GNU Affero General * Public License Version 3 as published by the Free Software Foundation. * * 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 Affero General Public License Version 3 * for more details. * * You should have received a copy of the GNU Affero General Public License Version 3 along with this program. If not, see * http://www.gnu.org/licenses. * * Sonatype Nexus (TM) Open Source Version is available from Sonatype, Inc. Sonatype and Sonatype Nexus are trademarks of * Sonatype, Inc. Apache Maven is a trademark of the Apache Foundation. M2Eclipse is a trademark of the Eclipse Foundation. * All other trademarks are the property of their respective owners. */ package org.sonatype.nexus.ext.gwt.ui.client.reposerver; import org.sonatype.nexus.ext.gwt.ui.client.Action; import org.sonatype.nexus.ext.gwt.ui.client.ApplicationContext; import org.sonatype.nexus.ext.gwt.ui.client.Constants; import org.sonatype.nexus.ext.gwt.ui.client.ServerFunctionPanel; import org.sonatype.nexus.ext.gwt.ui.client.ServerInstance; import org.sonatype.nexus.ext.gwt.ui.client.data.ResponseHandler; import org.sonatype.nexus.ext.gwt.ui.client.reposerver.model.ContentListResource; import org.sonatype.nexus.ext.gwt.ui.client.reposerver.model.Repository; import org.sonatype.nexus.ext.gwt.ui.client.reposerver.model.RepositoryStatusResource; import org.sonatype.nexus.ext.gwt.ui.client.reposerver.model.AuthenticationClientPermissions.Permissions; import com.extjs.gxt.ui.client.Events; import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.event.BaseEvent; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.event.ContainerEvent; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.event.SelectionChangedEvent; import com.extjs.gxt.ui.client.event.SelectionChangedListener; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.event.WindowEvent; import com.extjs.gxt.ui.client.widget.Component; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.LayoutContainer; import com.extjs.gxt.ui.client.widget.MessageBox; import com.extjs.gxt.ui.client.widget.button.ToolButton; import com.extjs.gxt.ui.client.widget.layout.BorderLayout; import com.extjs.gxt.ui.client.widget.layout.BorderLayoutData; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.extjs.gxt.ui.client.widget.table.CellRenderer; import com.extjs.gxt.ui.client.widget.table.Table; import com.extjs.gxt.ui.client.widget.table.TableColumn; import com.extjs.gxt.ui.client.widget.table.TableColumnModel; import com.extjs.gxt.ui.client.widget.table.TableSelectionModel; import com.extjs.gxt.ui.client.widget.toolbar.TextToolItem; import com.extjs.gxt.ui.client.widget.toolbar.ToolBar; import com.extjs.gxt.ui.client.widget.tree.Tree; import com.google.gwt.http.client.Response; import com.google.gwt.user.client.Window; public class RepoMaintenancePage extends LayoutContainer implements ServerFunctionPanel { private RepoServerInstance server; private ContentPanel repoPanel; private ContentPanel repoTree; private ContentPanel repoTreePanel; private RepoTreeBinding repoTreeBinding; public void init(ServerInstance server) { this.server = (RepoServerInstance) server; setLayout(new BorderLayout()); setWidth("100%"); setHeight("100%"); addRepoList(); addRepoPanel(); createRepoTree(); showRepoHelp(); } private void addRepoList() { ContentPanel panel = new ContentPanel() { { setHeaderVisible(false); setLayout(new FitLayout()); } }; Table table = new Table() { { setColumnModel(new TableColumnModel( new TableColumn("name", "Repository", 175f), new TableColumn("repoType", "Type", 50f), new TableColumn("statusText", "Status", 200f) { { setRenderer(new CellRenderer() { public String render(Component item, String property, Object value) { if (value == null) { return "<i>retrieving</i>"; } return (String) value; } }); } }, new TableColumn("resourceURI", "Repository Path", 1f) { { setRenderer(new CellRenderer() { public String render(Component item, String property, Object value) { String path = (String) value; path = Constants.HOST + path.replace(Constants.SERVICE_REPOSITORIES, Constants.CONTENT_REPOSITORIES); return "<a href=\"" + path + "\" target=\"_blank\">" + path + "</a>"; } }); } } )); // This disables showing the repository when right clicking setSelectionModel(new TableSelectionModel() { protected void onContextMenu(ContainerEvent event) { } }); } }; final RepoTableBinding tableBinding = new RepoTableBinding(table, server); tableBinding.getBinder().addSelectionChangedListener(new SelectionChangedListener<Repository>() { public void selectionChanged(SelectionChangedEvent<Repository> event) { showRepoTree(event.getSelectedItem()); } }); ToolBar toolBar = new ToolBar(); TextToolItem refreshButton = new TextToolItem("Refresh", "st-icon-refresh") { { addSelectionListener(new SelectionListener<ComponentEvent>() { public void componentSelected(ComponentEvent ce) { tableBinding.reload(); showRepoHelp(); } }); } }; toolBar.add(refreshButton); panel.setTopComponent(toolBar); panel.add(table); BorderLayoutData panelLayoutData = new BorderLayoutData(Style.LayoutRegion.NORTH) { { setSplit(true); setSize(200); setMinSize(150); setMaxSize(400); } }; add(panel, panelLayoutData); final boolean editEnabled = ApplicationContext.instance().checkPermission("maintRepos", Permissions.EDIT); ContextMenuProvider tableMenu = new ContextMenuProvider(table); tableMenu.addAction(new Action<Repository>("View") { public void execute(Repository repo) { showRepoTree(repo); } }); tableMenu.addAction(new Action<Repository>("Clear Cache") { public boolean supports(Repository repo) { return editEnabled && !repo.getRepoType().equals("virtual"); } public void execute(Repository repo) { server.clearRepositoryCache(repo.getId(), null, new ResponseHandler() { public void onError(Response response, Throwable error) { MessageBox.alert("Error", "The server did not clear the repository's cache", null); } public void onSuccess(Response response, Object entity) { //do nothing } }); } }); tableMenu.addAction(new Action<Repository>("Re-Index") { public boolean supports(Repository repo) { return editEnabled; } public void execute(Repository repo) { server.reindexRepository(repo.getId(), null, new ResponseHandler() { public void onError(Response response, Throwable error) { MessageBox.alert("Error", "The server did not re-index the repository", null); } public void onSuccess(Response response, Object entity) { //do nothing } }); } }); tableMenu.addAction(new Action<Repository>("Rebuild Attributes") { public boolean supports(Repository repo) { return editEnabled; } public void execute(Repository repo) { server.rebuildRepositoryAttributes(repo.getId(), null, new ResponseHandler() { public void onError(Response response, Throwable error) { MessageBox.alert("Error", "The server did not rebuild attributes in the repository", null); } public void onSuccess(Response response, Object entity) { //do nothing } }); } }); tableMenu.addAction(new Action<Repository>("Block Proxy") { public boolean supports(Repository repo) { return editEnabled && "proxy".equals(repo.getRepoType()) && "allow".equals(repo.getStatus().getProxyMode()); } public void execute(Repository repo) { RepositoryStatusResource status = repo.getStatus().copy(); status.setId(RepoServerUtil.toRepositoryId(repo.getResourceURI())); status.setProxyMode("blockedManual"); server.updateRepositoryStatus(status, new ResponseHandler<RepositoryStatusResource>() { public void onError(Response response, Throwable error) { MessageBox.alert("Error", "The server did not update the proxy repository status to blocked", null); } public void onSuccess(Response response, RepositoryStatusResource status) { tableBinding.updateRepoStatus(status); } }); } }); tableMenu.addAction(new Action<Repository>("Allow Proxy") { public boolean supports(Repository repo) { return editEnabled && "proxy".equals(repo.getRepoType()) && !"allow".equals(repo.getStatus().getProxyMode()); } public void execute(Repository repo) { RepositoryStatusResource status = repo.getStatus().copy(); status.setId(RepoServerUtil.toRepositoryId(repo.getResourceURI())); status.setProxyMode("allow"); server.updateRepositoryStatus(status, new ResponseHandler<RepositoryStatusResource>() { public void onError(Response response, Throwable error) { MessageBox.alert("Error", "The server did not update the proxy repository status to allow", null); } public void onSuccess(Response response, RepositoryStatusResource status) { tableBinding.updateRepoStatus(status); } }); } }); tableMenu.addAction(new Action<Repository>("Put Out of Service") { public boolean supports(Repository repo) { return editEnabled && "inService".equals(repo.getStatus().getLocalStatus()); } public void execute(Repository repo) { RepositoryStatusResource status = repo.getStatus().copy(); status.setId(RepoServerUtil.toRepositoryId(repo.getResourceURI())); status.setLocalStatus("outOfService"); server.updateRepositoryStatus(status, new ResponseHandler<RepositoryStatusResource>() { public void onError(Response response, Throwable error) { MessageBox.alert("Error", "The server did not put the repository out of service", null); } public void onSuccess(Response response, RepositoryStatusResource status) { tableBinding.updateRepoStatus(status); } }); } }); tableMenu.addAction(new Action<Repository>("Put in Service") { public boolean supports(Repository repo) { return editEnabled && "outOfService".equals(repo.getStatus().getLocalStatus()); } public void execute(Repository repo) { RepositoryStatusResource status = repo.getStatus().copy(); status.setId(RepoServerUtil.toRepositoryId(repo.getResourceURI())); status.setLocalStatus("inService"); server.updateRepositoryStatus(status, new ResponseHandler<RepositoryStatusResource>() { public void onError(Response response, Throwable error) { MessageBox.alert("Error", "The server did not put the repository into service", null); } public void onSuccess(Response response, RepositoryStatusResource status) { tableBinding.updateRepoStatus(status); } }); } }); addListener(Events.Render, new Listener<BaseEvent>() { public void handleEvent(BaseEvent be) { tableBinding.reload(); } }); } private void addRepoPanel() { repoPanel = new ContentPanel() { { setHeading("Repository Information"); setLayout(new FitLayout()); setLayoutOnChange(true); } }; add(repoPanel, new BorderLayoutData(Style.LayoutRegion.CENTER)); } private void createRepoTree() { repoTree = new ContentPanel() { { setFrame(true); setHeaderVisible(false); setLayout(new FitLayout()); } }; Tree tree = new Tree() { { setItemIconStyle("tree-leaf"); } }; repoTreeBinding = new RepoTreeBinding(tree, server); repoTreePanel = new ContentPanel() { { setId("st-repo-browser"); setBodyBorder(true); setBorders(true); setScrollMode(Style.Scroll.AUTO); getHeader().addTool(new ToolButton("x-tool-refresh", new SelectionListener<ComponentEvent>() { public void componentSelected(ComponentEvent ce) { repoTreeBinding.reload(); } })); } }; final boolean editEnabled = ApplicationContext.instance().checkPermission("maintRepos", Permissions.EDIT); ContextMenuProvider treeMenu = new ContextMenuProvider(tree); treeMenu.addAction(new Action<ContentListResource>("Clear Cache") { public boolean supports(ContentListResource data) { return editEnabled && !"virtual".equals(repoTreeBinding.getSelectedRepository().getRepoType()); } public void execute(ContentListResource data) { String repositoryId = repoTreeBinding.getSelectedRepository().getId(); server.clearRepositoryCache(repositoryId, data.getRelativePath(), new ResponseHandler() { public void onError(Response response, Throwable error) { MessageBox.alert("Error", "The server did not clear the repository's cache", null); } public void onSuccess(Response response, Object entity) { //do nothing } }); } }); treeMenu.addAction(new Action<ContentListResource>("Re-Index") { public boolean supports(ContentListResource data) { return editEnabled; } public void execute(ContentListResource data) { String repositoryId = repoTreeBinding.getSelectedRepository().getId(); server.reindexRepository(repositoryId, data.getRelativePath(), new ResponseHandler() { public void onError(Response response, Throwable error) { MessageBox.alert("Error", "The server did not re-index the repository", null); } public void onSuccess(Response response, Object entity) { //do nothing } }); } }); treeMenu.addAction(new Action<ContentListResource>("Rebuild Attributes") { public boolean supports(ContentListResource data) { return editEnabled; } public void execute(ContentListResource data) { String repositoryId = repoTreeBinding.getSelectedRepository().getId(); server.rebuildRepositoryAttributes(repositoryId, data.getRelativePath(), new ResponseHandler() { public void onError(Response response, Throwable error) { MessageBox.alert("Error", "The server did not rebuild attributes in the repository", null); } public void onSuccess(Response response, Object entity) { //do nothing } }); } }); treeMenu.addAction(new Action<ContentListResource>("Download From Remote") { public boolean supports(ContentListResource data) { return editEnabled && data.isLeaf() && "proxy".equals(repoTreeBinding.getSelectedRepository().getRepoType()); } public void execute(ContentListResource data) { String url = repoTreeBinding. getSelectedRepository().getRemoteUri() + data.getRelativePath(); Window.open(url, "_blank", ""); } }); treeMenu.addAction(new Action<ContentListResource>("Download") { public boolean supports(ContentListResource data) { return editEnabled && data.isLeaf(); } public void execute(ContentListResource data) { String url = Constants.HOST + "/nexus/content/repositories/apache-snapshots" + data.getRelativePath(); Window.open(url, "_blank", ""); } }); treeMenu.addAction(new Action<ContentListResource>("Delete") { public boolean supports(ContentListResource data) { return editEnabled && data.getParent() != null; } public void execute(final ContentListResource data) { MessageBox.confirm("Delete Repository Item?", "Delete the selected file/folder?", new Listener<WindowEvent>() { public void handleEvent(WindowEvent event) { if (!event.buttonClicked.getItemId().equals("yes")) { return; } String repositoryId = repoTreeBinding.getSelectedRepository().getId(); server.deleteRepositoryItem(repositoryId, data.getRelativePath(), new ResponseHandler() { public void onError(Response response, Throwable error) { MessageBox.alert("Error", "The server did not delete the file/folder from the repository", null); } public void onSuccess(Response response, Object entity) { repoTreeBinding.reload(); } }); } }); } }); treeMenu.addAction(new Action<ContentListResource>("View Remote") { public boolean supports(ContentListResource data) { return editEnabled && "proxy".equals(repoTreeBinding.getSelectedRepository().getRepoType()) && !data.isLeaf() && data.getParent() != null; } public void execute(ContentListResource data) { String url = repoTreeBinding. getSelectedRepository().getRemoteUri() + data.getRelativePath(); Window.open(url, "_blank", ""); } }); repoTreePanel.add(tree); repoTree.add(repoTreePanel); } private void showRepoTree(Repository repo) { if (repo == null) { return; } repoTreePanel.setHeading((String) repo.get("name") + " Repository Content"); repoTreeBinding.selectRepository(repo); repoPanel.removeAll(); repoPanel.add(repoTree); } private void showRepoHelp() { repoPanel.removeAll(); repoPanel.addText("Select a repository to view it") .setStyleName("st-little-padding"); } }