From 85dcf48b51b931163d225857d4cdc5b67b98fe71 Mon Sep 17 00:00:00 2001 From: "massimiliano.assante" Date: Mon, 15 Sep 2014 14:45:05 +0000 Subject: [PATCH] added leave group option git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/portlets/user/gcube-loggedin@99899 82a268e6-3cf1-43bd-a215-b396298e98cf --- .classpath | 15 ++-- .settings/com.google.gdt.eclipse.core.prefs | 2 +- .settings/org.eclipse.jdt.core.prefs | 7 +- .settings/org.eclipse.wst.common.component | 6 +- ....eclipse.wst.common.project.facet.core.xml | 2 +- pom.xml | 37 ++++++--- .../gcubeloggedin/client/LoggedinService.java | 2 + .../client/LoggedinServiceAsync.java | 3 + .../gcubeloggedin/client/UIConstants.java | 2 + .../gcubeloggedin/client/ui/AboutView.java | 75 +++++++++++++++--- .../gcubeloggedin/client/ui/AboutView.ui.xml | 6 +- .../gcubeloggedin/client/ui/WarningAlert.java | 47 +++++++++++ .../client/ui/WarningAlert.ui.xml | 33 ++++++++ .../server/LoggedinServiceImpl.java | 64 +++++++++++++++ .../user/gcubeloggedin/GCubeLoggedin.gwt.xml | 4 +- src/main/webapp/GCubeLoggedin.css | 53 ++++--------- src/main/webapp/images/loading.gif | Bin 0 -> 7358 bytes 17 files changed, 274 insertions(+), 84 deletions(-) create mode 100644 src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/WarningAlert.java create mode 100644 src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/WarningAlert.ui.xml create mode 100644 src/main/webapp/images/loading.gif diff --git a/.classpath b/.classpath index 9e79e11..1100482 100644 --- a/.classpath +++ b/.classpath @@ -1,6 +1,6 @@ - + @@ -19,16 +19,17 @@ - - - - - - + + + + + + + diff --git a/.settings/com.google.gdt.eclipse.core.prefs b/.settings/com.google.gdt.eclipse.core.prefs index 9df02cd..9b49b11 100644 --- a/.settings/com.google.gdt.eclipse.core.prefs +++ b/.settings/com.google.gdt.eclipse.core.prefs @@ -1,5 +1,5 @@ eclipse.preferences.version=1 jarsExcludedFromWebInfLib= -lastWarOutDir=/Users/massi/Documents/workspace/gcube-loggedin/target/gcube-loggedin-2.3.0-SNAPSHOT +lastWarOutDir=/Users/massi/Documents/workspace/gcube-loggedin/target/gcube-loggedin-2.4.0-SNAPSHOT warSrcDir=src/main/webapp warSrcDirIsOutput=false diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 36c3062..443e085 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,9 +1,8 @@ -#Wed Apr 10 17:44:47 CEST 2013 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component index 9d20a86..8b60e94 100644 --- a/.settings/org.eclipse.wst.common.component +++ b/.settings/org.eclipse.wst.common.component @@ -1,9 +1,11 @@ - - + + + uses + diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml index 7895606..4045d87 100644 --- a/.settings/org.eclipse.wst.common.project.facet.core.xml +++ b/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -3,5 +3,5 @@ - + diff --git a/pom.xml b/pom.xml index e89239f..9b4d23b 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ org.gcube.portlets.user gcube-loggedin war - 2.3.0-SNAPSHOT + 2.4.0-SNAPSHOT gCube Loggedin Portlet @@ -48,19 +48,10 @@ - - - - xerces - xercesImpl - 2.9.1 - provided - com.google.gwt gwt-user - provided + provided com.google.gwt @@ -77,11 +68,31 @@ gcube-widgets provided + + org.gcube.portlets.widgets + session-checker + [0.2.0-SNAPSHOT, 2.0.0-SNAPSHOT) + org.gcube.applicationsupportlayer aslcore provided + + org.gcube.dvos + usermanagement-core + provided + + + org.gcube.common + home-library-jcr + provided + + + org.gcube.common + home-library + provided + com.liferay.portal portal-service @@ -151,8 +162,8 @@ maven-compiler-plugin 2.3.2 - 1.6 - 1.6 + 1.7 + 1.7 diff --git a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/LoggedinService.java b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/LoggedinService.java index 3f33b03..796a8d9 100644 --- a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/LoggedinService.java +++ b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/LoggedinService.java @@ -14,5 +14,7 @@ public interface LoggedinService extends RemoteService { VObject getSelectedRE(); String getDefaultCommunityURL(); + + String removeUserFromVRE(); } diff --git a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/LoggedinServiceAsync.java b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/LoggedinServiceAsync.java index 8e9daff..3d3006f 100644 --- a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/LoggedinServiceAsync.java +++ b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/LoggedinServiceAsync.java @@ -14,4 +14,7 @@ public interface LoggedinServiceAsync { void getDefaultCommunityURL(AsyncCallback callback); + + + void removeUserFromVRE(AsyncCallback callback); } diff --git a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/UIConstants.java b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/UIConstants.java index 710c586..438707e 100644 --- a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/UIConstants.java +++ b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/UIConstants.java @@ -5,5 +5,7 @@ import com.google.gwt.core.client.GWT; public class UIConstants { public static final String LOADING_IMAGE = GWT.getModuleBaseURL() + "../images/loading-bar.gif"; + + public static final String LOADINGE = GWT.getModuleBaseURL() + "../images/loading.gif"; } diff --git a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/AboutView.java b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/AboutView.java index a575fd6..13b377a 100644 --- a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/AboutView.java +++ b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/AboutView.java @@ -1,7 +1,9 @@ package org.gcube.portlets.user.gcubeloggedin.client.ui; import org.gcube.portlets.user.gcubeloggedin.client.LoggedinServiceAsync; +import org.gcube.portlets.user.gcubeloggedin.client.UIConstants; import org.gcube.portlets.user.gcubeloggedin.shared.VObject; +import org.gcube.portlets.widgets.sessionchecker.client.CheckSession; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Document; @@ -10,6 +12,7 @@ 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.Window; +import com.google.gwt.user.client.Window.Location; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Composite; @@ -28,33 +31,79 @@ public class AboutView extends Composite { public AboutView() { initWidget(uiBinder.createAndBindUi(this)); } + private WarningAlert wa; + + @UiField HTMLPanel htmlPanel; + @UiField HTML description; + @UiField Button backButton; + @UiField Button leaveButton; - @UiField - Button button; - @UiField - HTMLPanel htmlPanel; - @UiField - HTML description; - private LoggedinServiceAsync loggedinService; - + public AboutView(VObject vobj, LoggedinServiceAsync loggedinService) { initWidget(uiBinder.createAndBindUi(this)); this.loggedinService = loggedinService; description.setHTML("" + vobj.getDescription()); + leaveButton.getElement().getStyle().setBackgroundColor("#F2DEDE"); + leaveButton.getElement().getStyle().setBorderColor("#EBCCD1"); + leaveButton.getElement().getStyle().setColor("#A94440"); + + wa = new WarningAlert("Are you sure you want to leave this group? " + + "By leaving this group you will no longer receive updates and loose the workspace folder related to the group.", this); } - @UiHandler("button") + @UiHandler("backButton") void onClick(ClickEvent e) { loggedinService.getDefaultCommunityURL(new AsyncCallback() { - public void onFailure(Throwable arg0) { - Window.alert("We're sorry we couldn't reach the server, try again later ... " + arg0.getMessage()); - } public void onSuccess(String url) { Window.open(url, "_self", ""); - } + } + public void onFailure(Throwable arg0) { + Window.alert("We're sorry we couldn't reach the server, try again later ... " + arg0.getMessage()); + } }); } + @UiHandler("leaveButton") + void onUnsubscribe(ClickEvent e) { + + htmlPanel.add(wa); + } + + + protected void abandonGroup() { + htmlPanel.remove(wa); + final HTML loading = getLoadingHTML(); + htmlPanel.add(loading); + loggedinService.removeUserFromVRE(new AsyncCallback() { + @Override + public void onSuccess(String result) { + if (result != null) + Location.assign(result); + else + CheckSession.showLogoutDialog(); + + } + @Override + public void onFailure(Throwable caught) { + htmlPanel.remove(loading); + Window.alert("We're sorry we couldn't reach the server, try again later ... " + caught.getMessage()); + } + }); + } + + /** + * + * @return + */ + private HTML getLoadingHTML() { + return new HTML( + "
"+ + ""+ + ""+ + "
"+ + ""+ + "
") ; + } } diff --git a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/AboutView.ui.xml b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/AboutView.ui.xml index 8ade0bd..2b55a08 100644 --- a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/AboutView.ui.xml +++ b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/AboutView.ui.xml @@ -1,8 +1,10 @@ + - - Change Environment + + Back + Leave Group \ No newline at end of file diff --git a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/WarningAlert.java b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/WarningAlert.java new file mode 100644 index 0000000..4b9702f --- /dev/null +++ b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/WarningAlert.java @@ -0,0 +1,47 @@ +package org.gcube.portlets.user.gcubeloggedin.client.ui; + +import org.gcube.portlets.user.gcubewidgets.client.elements.*; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Element; +import com.google.gwt.event.dom.client.ClickEvent; +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.ui.Composite; +import com.google.gwt.user.client.ui.Widget; + +public class WarningAlert extends Composite { + + private static WarningAlertUiBinder uiBinder = GWT + .create(WarningAlertUiBinder.class); + + interface WarningAlertUiBinder extends UiBinder { + } + + @UiField Element errorMessage; + @UiField Span cancelHandler; + @UiField Span confirmHandler; + + private AboutView owner; + + public WarningAlert(String message, AboutView owner) { + initWidget(uiBinder.createAndBindUi(this)); + errorMessage.setInnerText(message); + this.owner = owner; + cancelHandler.setHTML(" Cancel"); + + confirmHandler.setHTML(" Confirm Leave"); + } + + + @UiHandler("cancelHandler") + void onCloseClick(ClickEvent e) { + this.removeFromParent(); + } + + @UiHandler("confirmHandler") + void onConfirmClick(ClickEvent e) { + owner.abandonGroup(); + } +} diff --git a/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/WarningAlert.ui.xml b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/WarningAlert.ui.xml new file mode 100644 index 0000000..d0905f2 --- /dev/null +++ b/src/main/java/org/gcube/portlets/user/gcubeloggedin/client/ui/WarningAlert.ui.xml @@ -0,0 +1,33 @@ + + + + .alert { + border: 1px solid #FAEBCC; + border-radius: 4px; + margin: 20px; + padding: 15px; + background-color: #FCF8D4; + color: #8A6D3F; + font-family: 'Helvetica Neue', Arial, sans-serif; + font-size: 14px; + } + + .profile-link { + font-weight: bold; + } + + .profile-link:hover { + cursor: pointer; + cursor: hand; + text-decoration: underline; + } + + +
+ + or + +
+
\ No newline at end of file diff --git a/src/main/java/org/gcube/portlets/user/gcubeloggedin/server/LoggedinServiceImpl.java b/src/main/java/org/gcube/portlets/user/gcubeloggedin/server/LoggedinServiceImpl.java index 4aefeb7..f13f3a0 100644 --- a/src/main/java/org/gcube/portlets/user/gcubeloggedin/server/LoggedinServiceImpl.java +++ b/src/main/java/org/gcube/portlets/user/gcubeloggedin/server/LoggedinServiceImpl.java @@ -10,12 +10,22 @@ import javax.servlet.http.HttpSession; import org.gcube.application.framework.core.session.ASLSession; import org.gcube.application.framework.core.session.SessionManager; +import org.gcube.common.homelibrary.home.HomeLibrary; +import org.gcube.common.homelibrary.home.exceptions.InternalErrorException; +import org.gcube.common.homelibrary.home.workspace.exceptions.ItemNotFoundException; import org.gcube.portal.custom.communitymanager.OrganizationsUtil; import org.gcube.portal.custom.scopemanager.scopehelper.ScopeHelper; import org.gcube.portlets.user.gcubeloggedin.client.LoggedinService; import org.gcube.portlets.user.gcubeloggedin.shared.VObject; import org.gcube.portlets.user.gcubeloggedin.shared.VObject.UserBelongingClient; import org.gcube.portlets.user.gcubeloggedin.shared.VREClient; +import org.gcube.vomanagement.usermanagement.GroupManager; +import org.gcube.vomanagement.usermanagement.UserManager; +import org.gcube.vomanagement.usermanagement.exception.GroupRetrievalFault; +import org.gcube.vomanagement.usermanagement.exception.UserManagementSystemException; +import org.gcube.vomanagement.usermanagement.exception.UserRetrievalFault; +import org.gcube.vomanagement.usermanagement.impl.liferay.LiferayGroupManager; +import org.gcube.vomanagement.usermanagement.impl.liferay.LiferayUserManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -156,4 +166,58 @@ public class LoggedinServiceImpl extends RemoteServiceServlet implements Loggedi _log.trace("\n getPortalBasicUrl: " +toReturn + "queryString: " + request.getQueryString()); return toReturn; } + + /** + *@return the redirect url if everything goes ok, null otherwise + */ + @Override + public String removeUserFromVRE() { + String username = getASLSession().getUsername(); + if (username.compareTo("test.user") == 0) + return null; + _log.debug("Going to remove user from the current Group: " + getCurrentGroupID() + ". Username is: " + username); + UserManager userM = new LiferayUserManager(); + try { + userM.dismissUserFromGroup(getCurrentGroupID(), userM.getUserId(username)); + removeUserFromHLGroup(username, getASLSession().getScope()); + return getPortalBasicUrl(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * Get the current group ID + * + * @return the current group ID or null if an exception is thrown + * @throws Exception + */ + private String getCurrentGroupID(){ + ASLSession session = getASLSession(); + _log.debug("The current group NAME is --> " + session.getGroupName()); + try { + try { + GroupManager groupM = new LiferayGroupManager(); + return groupM.getGroupId(session.getGroupName()); + } catch (UserManagementSystemException e) { + throw new Exception(e.getMessage(), e.getCause()); + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + private void removeUserFromHLGroup(String username, String group) { + try { + org.gcube.common.homelibrary.home.workspace.usermanager.UserManager um = HomeLibrary.getHomeManagerFactory().getUserManager(); + um.removeUserFromGroup(group, username, getASLSession().getUsername()); + } catch (InternalErrorException e) { + _log.error("Failed to get the usermanager from HL. Could not add remove user from the HL group"); + } catch (ItemNotFoundException e1) { + + } + } + } diff --git a/src/main/resources/org/gcube/portlets/user/gcubeloggedin/GCubeLoggedin.gwt.xml b/src/main/resources/org/gcube/portlets/user/gcubeloggedin/GCubeLoggedin.gwt.xml index b76ab5a..733b601 100644 --- a/src/main/resources/org/gcube/portlets/user/gcubeloggedin/GCubeLoggedin.gwt.xml +++ b/src/main/resources/org/gcube/portlets/user/gcubeloggedin/GCubeLoggedin.gwt.xml @@ -7,9 +7,9 @@ - + - + diff --git a/src/main/webapp/GCubeLoggedin.css b/src/main/webapp/GCubeLoggedin.css index c9b7b05..9c790d6 100644 --- a/src/main/webapp/GCubeLoggedin.css +++ b/src/main/webapp/GCubeLoggedin.css @@ -4,21 +4,18 @@ } table { - border-collapse: separate !important; - border-spacing: 0; + border-collapse: separate !important; + border-spacing: 0; } .framed { margin: 0 0 10px; padding: 10px; margin: 0px 5px; - - background: #FFF url(images/vre_bg_gray.png) repeat-x left bottom; - + background: #FFF; border-radius: 6px !important; - -moz-border-radius: 6px !important; - -webkit-border-radius: 6px !important; - + -moz-border-radius: 6px !important; + -webkit-border-radius: 6px !important; border: 1px solid #DBDBDB; } @@ -28,35 +25,13 @@ table { margin-right: 5px; } -/** Example rules used by the template application (remove for your app) */ -h1 { - font-size: 2em; - font-weight: bold; - color: #777777; - margin: 40px 0px 70px; - text-align: center; +.leaveButton { + border: 1px solid #EBCCD1; + border-radius: 4px; + margin: 20px; + padding: 15px; + background-color: #F2DEDE; + color: #A94440; + font-family: 'Helvetica Neue', Arial, sans-serif; + font-size: 14px; } - -.description { - line-height: 18px; - font-family: Tahoma,Helvetica,Arial,sans-serif; - font-size: 10pt; - color: rgb(51, 51, 51); - text-align: justify; -} - -/** Most GWT widgets already have a style name defined */ -.gwt-DialogBox { - width: 400px; -} - -.dialogVPanel { - margin: 5px; -} - -.serverResponseLabelError { - color: red; -} - - - diff --git a/src/main/webapp/images/loading.gif b/src/main/webapp/images/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..8ed30cb6362966f7f8210cf5317a178707578ff1 GIT binary patch literal 7358 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 Pbju^cui5_rK{oeaxY@}K literal 0 HcmV?d00001