mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-23 06:06:11 +00:00
Merge branch 'master' into translation
This commit is contained in:
+12
-8
@@ -22,6 +22,7 @@ import com.sap.sse.security.ui.client.UserService;
|
||||
import com.sap.sse.security.ui.client.UserStatusEventHandler;
|
||||
import com.sap.sse.security.ui.client.WithSecurity;
|
||||
import com.sap.sse.security.ui.client.i18n.StringMessages;
|
||||
import com.sap.sse.security.ui.client.premium.PaywallResolver;
|
||||
import com.sap.sse.security.ui.shared.SuccessInfo;
|
||||
|
||||
/**
|
||||
@@ -39,6 +40,7 @@ public class AuthenticationManagerImpl implements AuthenticationManager {
|
||||
private final EventBus eventBus;
|
||||
private final String emailConfirmationUrl;
|
||||
private final String passwordResetUrl;
|
||||
private final PaywallResolver paywallResolver;
|
||||
|
||||
private final StringMessages i18n = StringMessages.INSTANCE;
|
||||
private final ErrorMessageView view = new ErrorMessageViewImpl();
|
||||
@@ -57,8 +59,9 @@ public class AuthenticationManagerImpl implements AuthenticationManager {
|
||||
*/
|
||||
public AuthenticationManagerImpl(WithSecurity clientFactory, EventBus eventBus,
|
||||
String emailConfirmationUrl, String passwordResetUrl) {
|
||||
this(clientFactory.getUserManagementWriteService(), clientFactory.getUserService(), eventBus, emailConfirmationUrl,
|
||||
passwordResetUrl);
|
||||
this(clientFactory.getUserManagementWriteService(), clientFactory.getUserService(),
|
||||
new PaywallResolver(clientFactory.getUserService(), clientFactory.getSubscriptionServiceFactory()),
|
||||
eventBus, emailConfirmationUrl, passwordResetUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,22 +77,23 @@ public class AuthenticationManagerImpl implements AuthenticationManager {
|
||||
* @param passwordResetUrl
|
||||
* URL which is send to users to reset their password
|
||||
*/
|
||||
public AuthenticationManagerImpl(UserService userService, EventBus eventBus, String emailConfirmationUrl,
|
||||
String passwordResetUrl) {
|
||||
this(userService.getUserManagementWriteService(), userService, eventBus, emailConfirmationUrl, passwordResetUrl);
|
||||
public AuthenticationManagerImpl(UserService userService, PaywallResolver paywallResolver,
|
||||
EventBus eventBus, String emailConfirmationUrl, String passwordResetUrl) {
|
||||
this(userService.getUserManagementWriteService(), userService, paywallResolver, eventBus, emailConfirmationUrl, passwordResetUrl);
|
||||
}
|
||||
|
||||
private AuthenticationManagerImpl(UserManagementWriteServiceAsync userManagementWriteService, UserService userService,
|
||||
final EventBus eventBus, String emailConfirmationUrl, String passwordResetUrl) {
|
||||
PaywallResolver paywallResolver, final EventBus eventBus, String emailConfirmationUrl, String passwordResetUrl) {
|
||||
this.userManagementWriteService = userManagementWriteService;
|
||||
this.userService = userService;
|
||||
this.eventBus = eventBus;
|
||||
this.emailConfirmationUrl = emailConfirmationUrl;
|
||||
this.passwordResetUrl = passwordResetUrl;
|
||||
this.paywallResolver = paywallResolver;
|
||||
userService.addUserStatusEventHandler(new UserStatusEventHandler() {
|
||||
@Override
|
||||
public void onUserStatusChange(UserDTO user, boolean preAuthenticated) {
|
||||
eventBus.fireEvent(new AuthenticationContextEvent(new AuthenticationContextImpl(user, userService)));
|
||||
eventBus.fireEvent(new AuthenticationContextEvent(new AuthenticationContextImpl(user, userService, paywallResolver)));
|
||||
}
|
||||
});
|
||||
eventBus.addHandler(AuthenticationSignOutRequestEvent.TYPE, new AuthenticationSignOutRequestEvent.Handler() {
|
||||
@@ -250,7 +254,7 @@ public class AuthenticationManagerImpl implements AuthenticationManager {
|
||||
|
||||
@Override
|
||||
public AuthenticationContext getAuthenticationContext() {
|
||||
return new AuthenticationContextImpl(userService.getCurrentUser(), userService);
|
||||
return new AuthenticationContextImpl(userService.getCurrentUser(), userService, paywallResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
@@ -4,6 +4,7 @@ import com.sap.sse.gwt.client.ServerInfoDTO;
|
||||
import com.sap.sse.security.shared.HasPermissions.Action;
|
||||
import com.sap.sse.security.shared.dto.SecuredDTO;
|
||||
import com.sap.sse.security.shared.dto.UserDTO;
|
||||
import com.sap.sse.security.ui.client.premium.PaywallResolver;
|
||||
|
||||
/**
|
||||
* Interface for authentication context representations providing access to the current {@link UserDTO user} object and
|
||||
@@ -46,4 +47,6 @@ public interface AuthenticationContext {
|
||||
boolean hasServerPermission(Action action);
|
||||
|
||||
ServerInfoDTO getServerInfo();
|
||||
|
||||
PaywallResolver getPaywallResolver();
|
||||
}
|
||||
|
||||
+9
-1
@@ -10,6 +10,7 @@ import com.sap.sse.security.shared.dto.SecuredDTO;
|
||||
import com.sap.sse.security.shared.dto.UserDTO;
|
||||
import com.sap.sse.security.shared.dto.WildcardPermissionWithSecurityDTO;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
import com.sap.sse.security.ui.client.premium.PaywallResolver;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link AuthenticationContext}.
|
||||
@@ -22,6 +23,7 @@ public class AuthenticationContextImpl implements AuthenticationContext {
|
||||
new ArrayList<WildcardPermissionWithSecurityDTO>(),
|
||||
/* groups */ null);
|
||||
private final UserService userService;
|
||||
private final PaywallResolver paywallResolver;
|
||||
|
||||
/**
|
||||
* Creating an {@link AuthenticationContextImpl} containing the given {@link UserDTO} object.
|
||||
@@ -29,8 +31,9 @@ public class AuthenticationContextImpl implements AuthenticationContext {
|
||||
* @param currentUser
|
||||
* the current {@link UserDTO user} object
|
||||
*/
|
||||
public AuthenticationContextImpl(UserDTO currentUser, UserService userService) {
|
||||
public AuthenticationContextImpl(UserDTO currentUser, UserService userService, PaywallResolver paywallResolver) {
|
||||
this.userService = userService;
|
||||
this.paywallResolver = paywallResolver;
|
||||
if (currentUser == null) {
|
||||
this.currentUser = ANONYMOUS;
|
||||
} else {
|
||||
@@ -84,4 +87,9 @@ public class AuthenticationContextImpl implements AuthenticationContext {
|
||||
public ServerInfoDTO getServerInfo() {
|
||||
return userService.getServerInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaywallResolver getPaywallResolver() {
|
||||
return paywallResolver;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -13,6 +13,7 @@ import com.sap.sse.security.ui.authentication.view.AuthenticationMenuView;
|
||||
import com.sap.sse.security.ui.authentication.view.FlyoutAuthenticationPresenter;
|
||||
import com.sap.sse.security.ui.authentication.view.FlyoutAuthenticationView;
|
||||
import com.sap.sse.security.ui.client.UserService;
|
||||
import com.sap.sse.security.ui.client.premium.PaywallResolver;
|
||||
|
||||
/**
|
||||
* Configures the authentication framework in a common way that is suitable for generic flyout based authentication.
|
||||
@@ -25,9 +26,9 @@ public class GenericAuthentication {
|
||||
private final EventBus eventBus = new SimpleEventBus();
|
||||
private final AuthenticationManager manager;
|
||||
|
||||
public GenericAuthentication(UserService userService, AuthenticationMenuView menuView,
|
||||
public GenericAuthentication(UserService userService, PaywallResolver paywallResolver, AuthenticationMenuView menuView,
|
||||
FlyoutAuthenticationView display, GenericAuthenticationLinkFactory linkFactory, CommonSharedResources res) {
|
||||
manager = new AuthenticationManagerImpl(userService, eventBus,
|
||||
manager = new AuthenticationManagerImpl(userService, paywallResolver, eventBus,
|
||||
linkFactory.createEmailValidationLink(), linkFactory.createPasswordResetLink());
|
||||
final AuthenticationClientFactory clientFactory = new AuthenticationClientFactoryImpl(manager, res);
|
||||
final GenericAuthenticationCallbackImpl callback = new GenericAuthenticationCallbackImpl(linkFactory);
|
||||
|
||||
+5
-3
@@ -37,7 +37,8 @@ public class SAPHeaderWithAuthentication extends SAPHeader {
|
||||
Anchor authenticationMenu = new Anchor();
|
||||
authenticationMenu.addStyleName(res.css().usermanagement_icon());
|
||||
rightWithAuthentication.add(authenticationMenu);
|
||||
authenticationMenuView = new AuthenticationMenuViewImpl(authenticationMenu, res.css().usermanagement_loggedin(), res.css().usermanagement_open());
|
||||
authenticationMenuView = new AuthenticationMenuViewImpl(authenticationMenu, res.css().usermanagement_loggedin(),
|
||||
res.css().usermanagement_open(), res.css().user_menu_premium());
|
||||
rightWrapper = new SimplePanel();
|
||||
rightWrapper.addStyleName(res.css().header_right_extension());
|
||||
rightWithAuthentication.add(rightWrapper);
|
||||
@@ -48,9 +49,10 @@ public class SAPHeaderWithAuthentication extends SAPHeader {
|
||||
public void addWidgetToRightSide(Widget widget) {
|
||||
rightWrapper.add(widget);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the {@link AuthenticationMenuView} associated with the authentication control on the right side of the header.
|
||||
* @return the {@link AuthenticationMenuView} associated with the authentication control on the right side of the
|
||||
* header.
|
||||
*/
|
||||
public AuthenticationMenuView getAuthenticationMenuView() {
|
||||
return authenticationMenuView;
|
||||
|
||||
+5
-6
@@ -4,23 +4,22 @@ import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.resources.client.CssResource;
|
||||
import com.sap.sse.security.ui.authentication.generic.resource.AuthenticationResources;
|
||||
|
||||
|
||||
public interface SAPHeaderWithAuthenticationResources extends AuthenticationResources {
|
||||
public static final SAPHeaderWithAuthenticationResources INSTANCE = GWT.create(SAPHeaderWithAuthenticationResources.class);
|
||||
|
||||
public static final SAPHeaderWithAuthenticationResources INSTANCE = GWT
|
||||
.create(SAPHeaderWithAuthenticationResources.class);
|
||||
|
||||
@Source("header-with-authentication.gss")
|
||||
HeaderWithAuthenticationCss css();
|
||||
|
||||
|
||||
public interface HeaderWithAuthenticationCss extends CssResource {
|
||||
String header_right_wrapper();
|
||||
String header_right_extension();
|
||||
String fixed();
|
||||
|
||||
String usermanagement_icon();
|
||||
String usermanagement_loggedin();
|
||||
String usermanagement_view();
|
||||
String usermanagement_open();
|
||||
|
||||
String user_menu_premium();
|
||||
String languageSelector();
|
||||
}
|
||||
}
|
||||
+14
@@ -53,3 +53,17 @@
|
||||
font-size: 100% !important;
|
||||
font-weight: normal;
|
||||
}
|
||||
.user_menu_premium {
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
margin: 0;
|
||||
padding: 0 3px;
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
background: #efaa00;
|
||||
line-height: 12px;
|
||||
border-radius: 0.33em;
|
||||
}
|
||||
|
||||
+12
-4
@@ -6,7 +6,7 @@ import com.google.gwt.user.client.ui.IsWidget;
|
||||
* Interface for menu items, which interacting with a {@link FlyoutAuthenticationView}.
|
||||
*/
|
||||
public interface AuthenticationMenuView extends IsWidget {
|
||||
|
||||
|
||||
/**
|
||||
* Sets the {@link Presenter}.
|
||||
*
|
||||
@@ -14,7 +14,7 @@ public interface AuthenticationMenuView extends IsWidget {
|
||||
* the {@link Presenter} to set
|
||||
*/
|
||||
void setPresenter(Presenter presenter);
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether or not there is an authenticated user.
|
||||
*
|
||||
@@ -22,7 +22,7 @@ public interface AuthenticationMenuView extends IsWidget {
|
||||
* <code>true</code> if there is an authenticated user, <code>false</code> otherwise
|
||||
*/
|
||||
void setAuthenticated(boolean authenticated);
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether or not the {@link FlyoutAuthenticationView} is open/shown.
|
||||
*
|
||||
@@ -30,7 +30,15 @@ public interface AuthenticationMenuView extends IsWidget {
|
||||
* <code>true</code> the {@link FlyoutAuthenticationView} is open, <code>false</code> otherwise
|
||||
*/
|
||||
void setOpen(boolean open);
|
||||
|
||||
|
||||
/**
|
||||
* Shows the premium indicator below the user icon.
|
||||
*
|
||||
* @param premium
|
||||
* if premium is visible (active premium role) or not.
|
||||
*/
|
||||
void showPremium(boolean premium);
|
||||
|
||||
/**
|
||||
* Presenter interface to toggle {@link FlyoutAuthenticationView}'s visibility.
|
||||
*/
|
||||
|
||||
+16
-4
@@ -4,17 +4,20 @@ import com.google.gwt.debug.client.DebugInfo;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sse.security.ui.client.i18n.StringMessages;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link AuthenticationMenuView} based on an {@link Anchor} widget.
|
||||
*/
|
||||
public class AuthenticationMenuViewImpl implements AuthenticationMenuView {
|
||||
|
||||
|
||||
private Presenter presenter;
|
||||
private final Anchor anchor;
|
||||
private final String loggedInStyle;
|
||||
private final String openStyle;
|
||||
private final Label usermenuPremium;
|
||||
|
||||
/**
|
||||
* Create a new {@link AuthenticationMenuViewImpl} instance with the given parameters.
|
||||
@@ -26,7 +29,7 @@ public class AuthenticationMenuViewImpl implements AuthenticationMenuView {
|
||||
* @param openStyle
|
||||
* the style name to add to the widget, if the {@link FlyoutAuthenticationView} is open
|
||||
*/
|
||||
public AuthenticationMenuViewImpl(Anchor anchor, String loggedInStyle, String openStyle) {
|
||||
public AuthenticationMenuViewImpl(Anchor anchor, String loggedInStyle, String openStyle, String premiumStyle) {
|
||||
this.anchor = anchor;
|
||||
this.loggedInStyle = loggedInStyle;
|
||||
this.openStyle = openStyle;
|
||||
@@ -37,6 +40,10 @@ public class AuthenticationMenuViewImpl implements AuthenticationMenuView {
|
||||
}
|
||||
});
|
||||
this.anchor.ensureDebugId("authenticationMenu");
|
||||
usermenuPremium = new Label(StringMessages.INSTANCE.premium());
|
||||
usermenuPremium.addStyleName(premiumStyle);
|
||||
usermenuPremium.setVisible(false);
|
||||
this.anchor.getElement().appendChild(usermenuPremium.getElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,13 +61,18 @@ public class AuthenticationMenuViewImpl implements AuthenticationMenuView {
|
||||
anchor.setStyleName(loggedInStyle, authenticated);
|
||||
setDebugDataAttribute("data-auth", authenticated);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setOpen(boolean open) {
|
||||
anchor.setStyleName(openStyle, open);
|
||||
setDebugDataAttribute("data-open", open);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void showPremium(boolean premium) {
|
||||
usermenuPremium.setVisible(premium);
|
||||
}
|
||||
|
||||
private void setDebugDataAttribute(String name, boolean value) {
|
||||
if (DebugInfo.isDebugIdEnabled()) {
|
||||
anchor.getElement().setAttribute(name, String.valueOf(value));
|
||||
|
||||
+3
@@ -1,6 +1,7 @@
|
||||
package com.sap.sse.security.ui.authentication.view;
|
||||
|
||||
import com.google.web.bindery.event.shared.EventBus;
|
||||
import com.sap.sse.security.shared.impl.SecuredSecurityTypes.UserActions;
|
||||
import com.sap.sse.security.ui.authentication.AuthenticationContextEvent;
|
||||
import com.sap.sse.security.ui.authentication.AuthenticationPlaceManagementController;
|
||||
import com.sap.sse.security.ui.authentication.AuthenticationRequestEvent;
|
||||
@@ -55,9 +56,11 @@ public class FlyoutAuthenticationPresenter implements AuthenticationMenuView.Pre
|
||||
@Override
|
||||
public void onUserChangeEvent(AuthenticationContextEvent event) {
|
||||
authenticationMenuView.setAuthenticated(event.getCtx().isLoggedIn());
|
||||
authenticationMenuView.showPremium(event.getCtx().hasPermission(event.getCtx().getCurrentUser(), UserActions.BE_PREMIUM));
|
||||
}
|
||||
});
|
||||
authenticationMenuView.setAuthenticated(initialAuthentication.isLoggedIn());
|
||||
authenticationMenuView.showPremium(initialAuthentication.hasPermission(initialAuthentication.getCurrentUser(), UserActions.BE_PREMIUM));
|
||||
}
|
||||
|
||||
public void showRegister() {
|
||||
|
||||
+1
@@ -206,6 +206,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages {
|
||||
String selectSubscriptionPlan();
|
||||
String features();
|
||||
String premiumFeature();
|
||||
String premium();
|
||||
String price();
|
||||
String pleaseSubscribeToUse();
|
||||
String pleaseSubscribeToUseSpecific(String actionName);
|
||||
|
||||
+2
-1
@@ -213,6 +213,7 @@ premiumFeatureListDescription=Compare all the features provided in the FREE or P
|
||||
selectSubscriptionPlan=Please select a subscription plan to subscribe to.
|
||||
features=Features
|
||||
premiumFeature=Premium-Feature
|
||||
premium=Premium
|
||||
price=Price
|
||||
pleaseSubscribeToUse=You do not have permission to use this feature. Consider Subscribing.
|
||||
pleaseSubscribeToUseSpecific=You do not have permission to use the feature "{0}". Consider acquiring a subscription plan.
|
||||
@@ -223,4 +224,4 @@ subscriptionSuggestionTitle=Lacking Permission
|
||||
takeMeToSubscriptions=Go to subscription plans.
|
||||
errorPollingCheckoutResults=Could not retrieve payment status.
|
||||
paymentUnfinished=Waiting for payment to process.
|
||||
paymentFinished=Payment successful. Premium roles have been granted.
|
||||
paymentFinished=Payment successful. Plan roles have been granted.
|
||||
+1
-1
@@ -221,4 +221,4 @@ subscriptionSuggestionTitle=Fehlende Befugnis
|
||||
takeMeToSubscriptions=Zur Abbonementübersicht
|
||||
errorPollingCheckoutResults=Der Zahlungsstatus konnte nicht abgerufen werden.
|
||||
paymentUnfinished=Warte auf Zahlungsbestätigung.
|
||||
paymentFinished=Zahlung erfolgreich. Premium Rolle verliehen.
|
||||
paymentFinished=Zahlung erfolgreich. Planrollen wurden verliehen.
|
||||
+18
-16
@@ -20,22 +20,22 @@ public interface SubscriptionStringConstants extends ConstantsWithLookup {
|
||||
String free_subscription_plan_description();
|
||||
String free_subscription_plan_info();
|
||||
String free_subscription_plan_price_info();
|
||||
String[] free_subscription_plan_features();
|
||||
String yearly_premium_name();
|
||||
String yearly_premium_description();
|
||||
String yearly_premium_price_info();
|
||||
String yearly_premium_info();
|
||||
String[] yearly_premium_features();
|
||||
String weekly_premium_name();
|
||||
String weekly_premium_description();
|
||||
String weekly_premium_price_info();
|
||||
String weekly_premium_info();
|
||||
String[] weekly_premium_features();
|
||||
String trial_premium_name();
|
||||
String trial_premium_description();
|
||||
String trial_premium_price_info();
|
||||
String trial_premium_info();
|
||||
String[] trial_premium_features();
|
||||
String free_subscription_plan_features();
|
||||
String premium_name();
|
||||
String premium_description();
|
||||
String premium_price_info();
|
||||
String premium_info();
|
||||
String premium_features();
|
||||
String data_mining_archive_name();
|
||||
String data_mining_archive_description();
|
||||
String data_mining_archive_price_info();
|
||||
String data_mining_archive_info();
|
||||
String data_mining_archive_features();
|
||||
String data_mining_all_name();
|
||||
String data_mining_all_description();
|
||||
String data_mining_all_price_info();
|
||||
String data_mining_all_info();
|
||||
String data_mining_all_features();
|
||||
String free_subscription_plan_shortname();
|
||||
String premium_subscription_plan_shortname();
|
||||
String datamining_subscription_plan_shortname();
|
||||
@@ -71,4 +71,6 @@ public interface SubscriptionStringConstants extends ConstantsWithLookup {
|
||||
String features_media_tags_description();
|
||||
String features_scoring_title();
|
||||
String features_scoring_description();
|
||||
String features_data_mining_title();
|
||||
String features_data_mining_description();
|
||||
}
|
||||
|
||||
+32
-34
@@ -18,29 +18,31 @@ free_subscription_plan_name=Free
|
||||
free_subscription_plan_description=Try it out
|
||||
free_subscription_plan_info=
|
||||
free_subscription_plan_price_info=
|
||||
free_subscription_plan_features=Basic live wind based leaderboard,\
|
||||
free_subscription_plan_features=Basic live wind based leaderboard|\
|
||||
Create your own account and carry out the sailing analytics for your regatta or training with our \
|
||||
free tracking app or a professional tracking service,\
|
||||
free tracking app or a professional tracking service|\
|
||||
Sync live YouTube videos with your races
|
||||
yearly_premium_name=Yearly Premium
|
||||
yearly_premium_description=Full Access to all Features for one Year (auto renewing)
|
||||
yearly_premium_info=Everything from Weekly Premium for a year, at a discount!
|
||||
yearly_premium_price_info=
|
||||
yearly_premium_features=\
|
||||
Full live analytics with hundreds of features including wind streamlets, comparison charts, \
|
||||
advanced in-map visualizations and much more. See full premium feature-list below, \
|
||||
Tag Personal Timeline with Videos and Notes
|
||||
weekly_premium_name=Weekly Premium
|
||||
weekly_premium_description=Full Access to all Features for one Week (one time payment)
|
||||
weekly_premium_info=Everything from free plan plus:
|
||||
weekly_premium_price_info=One time payment
|
||||
weekly_premium_features=\
|
||||
Full live analytics with hundreds of features including wind streamlets, comparison charts, \
|
||||
advanced in-map visualizations and much more. See full premium feature-list below, \
|
||||
premium_name=Premium
|
||||
premium_description=All the premium features.
|
||||
premium_info=Everything from free plan plus:
|
||||
premium_price_info=One Time Payment
|
||||
premium_features=\
|
||||
Full Live Analytics with hundreds of features including wind streamlets|comparison charts|\
|
||||
advanced in-map visualizations and much more. See full premium feature-list below|\
|
||||
Tag Personal Timeline with Videos and Notes
|
||||
data_mining_archive_name=Data Mining - Archive
|
||||
data_mining_archive_description=Data Mining across archived races and events.
|
||||
data_mining_archive_info=Everything from premium plan plus:
|
||||
data_mining_archive_price_info=One Time Payment
|
||||
data_mining_archive_features=Powerful data mining for archived races and events that can be found under www.sapsailing.com
|
||||
data_mining_all_name=Data Mining - ALL
|
||||
data_mining_all_description=Data Mining across all races and events
|
||||
data_mining_all_info=Everything from data mining archive plan plus:
|
||||
data_mining_all_price_info=One Time Payment
|
||||
data_mining_all_features=Powerful data mining including additional data from all sub domains of sapsailing.com, e.g.: for leagues, live events or club servers
|
||||
free_subscription_plan_shortname=free
|
||||
premium_subscription_plan_shortname=premium
|
||||
datamining_subscription_plan_shortname=data-mining
|
||||
datamining_subscription_plan_shortname=data mining
|
||||
features_limited_live_analytics_title=Live Analytics (limited)
|
||||
features_limited_live_analytics_description=Limited Live Analytics.
|
||||
features_full_live_analytics_title=Live Analytics (full)
|
||||
@@ -48,37 +50,33 @@ features_full_live_analytics_description=Full live analytics with hundreds of fe
|
||||
features_organize_events_title=Organize Events
|
||||
features_organize_events_description=Run the Sailing Analytics at your Regatta with our free tracking app or a professional tracking service*. (below: *Like Trac Trac ) Maneuever detection and Analytics
|
||||
features_events_with_more_regatta_title=Events with more than one regatta
|
||||
features_events_with_more_regatta_description=-
|
||||
features_events_with_more_regatta_description=You can create a multi-class event or a season of events counting in a seasonal leaderboard
|
||||
features_connect_to_tractrac_title=Connect to Trac Trac
|
||||
features_connect_to_tractrac_description=Connect to professional tracking service
|
||||
features_imports_title=Import external data
|
||||
features_imports_description=Import GPX and KML file formats
|
||||
features_imports_description=Import GPX and KML file formats, GRIB files and Expedition logs
|
||||
features_media_management_title=Media Management
|
||||
features_media_management_description=Integrate/upload and replay video and Pictures
|
||||
features_analytic_charts_title=Analytic Charts
|
||||
features_analytic_charts_description=Charts like...
|
||||
features_analytic_charts_description=Charts for comparing competitor performance, wind strip charts and a table for maneuver analysis and comparison
|
||||
features_media_tags_title=Link and upload media (tags)
|
||||
features_media_tags_description= Place and read comments (notes and images on timeline)
|
||||
features_scoring_title=Scoring
|
||||
features_scoring_description=ORC PCS scoring
|
||||
trial_premium_name=Trial
|
||||
trial_premium_description=A short trial experience of the premium features. Lasts a day.
|
||||
trial_premium_info=Everything from free plan plus:
|
||||
trial_premium_price_info=
|
||||
trial_premium_features=\
|
||||
Full live analytics with hundreds of features including wind streamlets, comparison charts, \
|
||||
advanced in-map visualizations and much more. See full premium feature-list below, \
|
||||
Tag Personal Timeline with Videos and Notes
|
||||
features_scoring_description=Low-Point, High-Point, various tie breaking variants, ToT/ToD as well as ORC Performance Curve Scoring handicap support
|
||||
features_map_analytics_title=Map Analytics
|
||||
features_map_analytics_description=Wind streamlets.
|
||||
features_simulator_title=Simulator
|
||||
features_simulator_description=Simulation and analysis.
|
||||
features_advanced_leaderboard_info_title=Detailed leaderboard information
|
||||
features_advanced_leaderboard_info_description=Advanced leaderboard information. E.g. gap to leader.
|
||||
features_advanced_leaderboard_info_description=Advanced leaderboard information, e.g. gap to leader, speed over ground, maneuvers, velocity made good (VMG) and cross-track error (XTE)
|
||||
features_competitor_analytics_title=Competitor analysis
|
||||
features_competitor_analytics_description=Advanced competitor analytics.
|
||||
features_competitor_analytics_description=Advanced competitor analytics charts with values such as speed over ground (SOG), true wind angle (TWA), gap to leader and velocity made good (VMG)
|
||||
features_maneuver_analytics_title=Maneuver Analytics
|
||||
features_maneuver_analytics_description=Advanced analytics regarding maneuver execution of competitors.
|
||||
features_maneuver_analytics_description=Advanced analytics regarding maneuver execution of competitors, including maneuver loss, turn rate and maneuver angle
|
||||
features_wind_analytics_title=Wind analytics
|
||||
features_wind_analytics_description=Advanced analytics regarding collected wind data.
|
||||
features_wind_analytics_description=Advanced analytics regarding collected wind data; colored animated overlay and strip charts for every wind source considered
|
||||
features_data_mining_title=Data Mining
|
||||
features_data_mining_description=Data Mining across races and events. \
|
||||
Depending on plan type for archive only (everything that can be found under www.sapsailing.com) \
|
||||
or all sub domains (e.g. for leagues, live events or club servers)
|
||||
|
||||
|
||||
+34
-36
@@ -20,28 +20,30 @@ free_subscription_plan_name=Free
|
||||
free_subscription_plan_description=Probier es aus
|
||||
free_subscription_plan_info=
|
||||
free_subscription_plan_price_info=
|
||||
free_subscription_plan_features=Standart Live-Wind-basierte Bestenliste,\
|
||||
Erstellen Sie Ihr eigenes Konto und führen Sie die Segelanalyse für Ihre Regatta oder Ihr Training mit unserer kostenlosen Tracking-App oder einem professionellen Tracking-Service durch,\
|
||||
free_subscription_plan_features=Standart Live-Wind-basierte Bestenliste|\
|
||||
Erstellen Sie Ihr eigenes Konto und führen Sie die Segelanalyse für Ihre Regatta oder Ihr Training mit unserer kostenlosen Tracking-App oder einem professionellen Tracking-Service durch|\
|
||||
Synchronisiere YouTube-Videos mit deinen Rennen
|
||||
yearly_premium_name=Jahresabo
|
||||
yearly_premium_description=Ein Jahr Premium. Wiederkehrend.
|
||||
yearly_premium_info=Alles aus dem Wochenabo für ein Jahr und mit einem Rabatt!
|
||||
yearly_premium_price_info=
|
||||
yearly_premium_features=\
|
||||
Vollständige Live-Analyse mit Hunderten von Funktionen, einschließlich Windströmungen, Vergleichstabellen, \
|
||||
erweiterte In-Map-Visualisierungen und vieles mehr. Siehe die vollständige Liste der Premiumfunktionen unten, \
|
||||
Kennzeichnen Sie die persönliche Timeline mit Videos und Notizen
|
||||
weekly_premium_name=Wochenabo
|
||||
weekly_premium_description=Voller Zugriff auf alle Funktionen für eine Woche (einmalige Zahlung)
|
||||
weekly_premium_info=Alles vom kostenlosen Plan plus:
|
||||
weekly_premium_price_info=einmalige Zahlung
|
||||
weekly_premium_features=\
|
||||
Vollständige Live-Analyse mit Hunderten von Funktionen, einschließlich Windströmungen, Vergleichstabellen, \
|
||||
erweiterte In-Map-Visualisierungen und vieles mehr. Siehe die vollständige Liste der Premiumfunktionen unten, \
|
||||
premium_name=Premium
|
||||
premium_description=Eine kurze Kostprobe der Premiummitgliedschaft. Läuft einen Tag.
|
||||
premium_info=Alles vom kostenlosen Plan plus:
|
||||
premium_price_info=einmalige Zahlung
|
||||
premium_features=\
|
||||
Vollständige Live-Analyse mit Hunderten von Funktionen|einschließlich Windströmungen|Vergleichstabellen|\
|
||||
erweiterte In-Map-Visualisierungen und vieles mehr. Siehe die vollständige Liste der Premiumfunktionen unten|\
|
||||
Kennzeichnen Sie die persönliche Timeline mit Videos und Notizen
|
||||
data_mining_archive_name=Data Mining - Archive
|
||||
data_mining_archive_description=Data Mining über archivierte Rennen und Veranstaltungen hinweg.
|
||||
data_mining_archive_info=
|
||||
data_mining_archive_price_info=einmalige Zahlung
|
||||
data_mining_archive_features=Leistungsstarkes Data Mining für archivierte Rennen und Veranstaltungen, die unter www.sapsailing.com zu finden sind
|
||||
data_mining_all_name=Data Mining - All
|
||||
data_mining_all_description=Data Mining über alle Rennen und Events hinweg
|
||||
data_mining_all_info=
|
||||
data_mining_all_price_info=einmalige Zahlung
|
||||
data_mining_all_features=Leistungsstarkes Data Mining inklusive zusätzlicher Daten aus allen Subdomains von sapsailing.com, z. B.: für Ligen, Live-Events oder Clubserver
|
||||
free_subscription_plan_shortname=free
|
||||
premium_subscription_plan_shortname=premium
|
||||
datamining_subscription_plan_shortname=data-mining
|
||||
datamining_subscription_plan_shortname=data mining
|
||||
features_limited_live_analytics_title=Live Analytics (limitiert)
|
||||
features_limited_live_analytics_description=Limitierte Live-Analyse.
|
||||
features_full_live_analytics_title=Live Analytics (vollständig)
|
||||
@@ -49,36 +51,32 @@ features_full_live_analytics_description=Vollständige Live-Analyse mit Hunderte
|
||||
features_organize_events_title=Veranstaltungen organisieren
|
||||
features_organize_events_description=Führen Sie die Sailing Analytics bei Ihrer Regatta mit unserer kostenlosen Tracking-App oder einem professionellen Tracking-Service* durch. (unten: *Wie Trac Trac) Manövererkennung und Analyse
|
||||
features_events_with_more_regatta_title=Veranstaltungen mit mehr als einer Regatta
|
||||
features_events_with_more_regatta_description=-
|
||||
features_events_with_more_regatta_description=Einrichten von Veranstaltungen in mehreren Klassen sowie von Saison-Wertungen wie z.B. bei Segelligen
|
||||
features_connect_to_tractrac_title=Mit Trac Trac verbinden
|
||||
features_connect_to_tractrac_description=Verbinden Sie sich mit einem professionellen Tracking-Service
|
||||
features_imports_title=Externe Daten importieren
|
||||
features_imports_description=GPX- und KML-Dateiformate importieren
|
||||
features_imports_description=GPX- und KML-Dateiformate, GRIB-Dateien und Expedition-Protokolldateien importieren
|
||||
features_media_management_title=Media Management
|
||||
features_media_management_description=Integrieren/hochladen und wiedergeben von Videos und Bildern
|
||||
features_analytic_charts_title=Analysediagramme
|
||||
features_analytic_charts_description=...
|
||||
features_analytic_charts_description=Diagramme zum Vergleich mehrerer Teilnehmer, Windhistorie sowie Manövertabelle
|
||||
features_media_tags_title=Medien verlinken und hochladen (Tags)
|
||||
features_media_tags_description=-
|
||||
features_media_tags_description=Kommentare und Bilder mit der Zeitleiste verknüpfen und teilen
|
||||
features_scoring_title=Scoring
|
||||
features_scoring_description=ORC PCS Wertungen
|
||||
trial_premium_name=Trial
|
||||
trial_premium_description=Eine kurze Kostprobe der Premiummitgliedschaft. Läuft einen Tag.
|
||||
trial_premium_info=Alles vom kostenlosen Plan plus:
|
||||
trial_premium_price_info
|
||||
trial_premium_features=\
|
||||
Vollständige Live-Analyse mit Hunderten von Funktionen, einschließlich Windströmungen, Vergleichstabellen, \
|
||||
erweiterte In-Map-Visualisierungen und vieles mehr. Siehe die vollständige Liste der Premiumfunktionen unten, \
|
||||
Kennzeichnen Sie die persönliche Timeline mit Videos und Notizen
|
||||
features_scoring_description=Low-Point, High-Point, jeweils mit verschiedenen Varianten zum Tie-Break, außerdem verschiedene Handicap-Optionen mit ToT/ToD sowie ORC Performance Curve Scoring
|
||||
features_map_analytics_title=Kartenanalyse
|
||||
features_map_analytics_description=Visualisierung von Windströmungen.
|
||||
features_simulator_title=Simulator
|
||||
features_simulator_description=Simulation und Analyse des Geschehens.
|
||||
features_advanced_leaderboard_info_title=Detaillierte Ranglisteninformationen
|
||||
features_advanced_leaderboard_info_description=Fortgeschrittenen Analyseinformationen zur Rangliste. Wie Beispielsweise Abstand zum Führenden.
|
||||
features_competitor_analytics_title=Competitoranalyse
|
||||
features_competitor_analytics_description=Analyseinformationen zu den Competitoren.
|
||||
features_advanced_leaderboard_info_description=Fortgeschrittenen Analyseinformationen zur Rangliste, z.B. Abstand zum Führenden, Geschwindigkeit über Grund, Abstand zur Kursmittellinie und Manöver
|
||||
features_competitor_analytics_title=Teilnehmerranalyse
|
||||
features_competitor_analytics_description=Analysedigramme zu den Teilnehmern zu verschiedenen Kennzahlen wie Geschwindigkeit, Abstand zum Führenden, Luvgeschwindigkeit und Winkel zum wahren Wind (TWA)
|
||||
features_maneuver_analytics_title=Manöveranalyse
|
||||
features_maneuver_analytics_description=-Fortgeschrittene Analyseinformationen zu den durchgeführten Manövern der Competitoren.
|
||||
features_maneuver_analytics_description=-Fortgeschrittene Analyseinformationen zu den durchgeführten Manövern der Teilnehmer, inklusive Manöververlust, Manöverwinkel und Drehgeschwindigkeit
|
||||
features_wind_analytics_title=Windanalyse
|
||||
features_wind_analytics_description=Analyseinformationen zu den gesammelten Winddaten.
|
||||
features_wind_analytics_description=Analyseinformationen zu den gesammelten Winddaten; farbiges animiertes Karten-Overlay sowie grafischer Verlauf der Historie jeder einzelnen Windquelle
|
||||
features_data_mining_title=Data Mining
|
||||
features_data_mining_description=Data Mining über Rennen und Events hinweg. \
|
||||
Je nach Plantyp nur für Archivserver (alles was unter www.sapsailing.com zu finden ist) \
|
||||
oder alle Subdomains (z. B. für Ligen, Live-Events oder Vereinsserver)
|
||||
|
||||
+3
-1
@@ -23,7 +23,9 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
.premium-permitted .premium-container .premium-check-box input {
|
||||
box-shadow: -1px 1px #ffffff, -3px 3px #d2af25;
|
||||
-moz-box-shadow: -1px 1px 0 0 #ffffff, -3px 3px 0 0 #d2af25;
|
||||
-webkit-box-shadow: -1px 1px 0 0 #ffffff, -3px 3px 0 0 #d2af25;
|
||||
box-shadow: -1px 1px 0 0 #ffffff, -3px 3px 0 0 #d2af25;
|
||||
}
|
||||
.premium-container .premium-icon {
|
||||
width: 17px;
|
||||
|
||||
+2
-1
@@ -25,5 +25,6 @@ public interface SubscriptionService extends RemoteService {
|
||||
SubscriptionPlanDTO getSubscriptionPlanDTOById(String planId);
|
||||
|
||||
boolean isUserInPossessionOfRoles(String planId) throws UserManagementException;
|
||||
|
||||
|
||||
String getSelfServicePortalSession();
|
||||
}
|
||||
|
||||
+2
@@ -18,5 +18,7 @@ public interface SubscriptionServiceAsync<C, P> {
|
||||
public void getSubscriptionPlanDTOById(String planId, AsyncCallback<SubscriptionPlanDTO> callback);
|
||||
|
||||
void isUserInPossessionOfRoles(String planId, AsyncCallback<Boolean> callback);
|
||||
|
||||
void getSelfServicePortalSession(AsyncCallback<String> accessLink);
|
||||
|
||||
}
|
||||
|
||||
+18
-5
@@ -3,6 +3,7 @@ package com.sap.sse.security.ui.server.subscription;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -35,6 +36,7 @@ import com.sap.sse.security.shared.impl.Role;
|
||||
import com.sap.sse.security.shared.impl.User;
|
||||
import com.sap.sse.security.shared.subscription.Subscription;
|
||||
import com.sap.sse.security.shared.subscription.SubscriptionPlan;
|
||||
import com.sap.sse.security.shared.subscription.SubscriptionPrice;
|
||||
import com.sap.sse.security.subscription.SubscriptionApiService;
|
||||
import com.sap.sse.security.ui.client.subscription.SubscriptionService;
|
||||
import com.sap.sse.security.ui.server.Activator;
|
||||
@@ -66,9 +68,14 @@ public abstract class SubscriptionServiceImpl extends RemoteServiceServlet imple
|
||||
public ArrayList<String> getUnlockingSubscriptionplans(WildcardPermission permission)
|
||||
throws UserManagementException {
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
final User currentUser = getCurrentUser();
|
||||
final User currentUser;
|
||||
if (getSecurityService().getCurrentUser() != null) {
|
||||
currentUser = getSecurityService().getCurrentUser();
|
||||
} else {
|
||||
currentUser = getSecurityService().getAllUser();
|
||||
}
|
||||
final SecurityService securityServiceInstance = getSecurityService();
|
||||
User allUser = securityServiceInstance.getUserByName(SecurityService.ALL_USERNAME);
|
||||
User allUser = getSecurityService().getAllUser();
|
||||
getSecurityService().getAllSubscriptionPlans().values().forEach((plan) -> {
|
||||
final Role[] subscriptionPlanUserRolesArray = getSecurityService().getSubscriptionPlanUserRoles(currentUser, plan);
|
||||
final Set<Role> subscriptionPlanUserRoles = Stream.of(subscriptionPlanUserRolesArray).collect(Collectors.toSet());
|
||||
@@ -216,7 +223,7 @@ public abstract class SubscriptionServiceImpl extends RemoteServiceServlet imple
|
||||
if(isUserSubscribedToPlan) {
|
||||
isUserSubscribedToPlanCategory = true;
|
||||
hasHadSubscriptionForOneTimePlan = plan.getIsOneTimePlan();
|
||||
}else {
|
||||
} else {
|
||||
for (SubscriptionPlan subscriptionPlan : getSecurityService().getAllSubscriptionPlans().values()) {
|
||||
if(isUserSubscribedToPlan(subscriptionPlan.getId())
|
||||
&& Util.containsAny(plan.getPlanCategories(), subscriptionPlan.getPlanCategories())) {
|
||||
@@ -231,8 +238,14 @@ public abstract class SubscriptionServiceImpl extends RemoteServiceServlet imple
|
||||
hasHadSubscriptionForOneTimePlan = false;
|
||||
}
|
||||
}
|
||||
return new SubscriptionPlanDTO(plan.getId(), isUserSubscribedToPlan, plan.getPrices(),
|
||||
plan.getPlanCategories(), hasHadSubscriptionForOneTimePlan, isUserSubscribedToPlanCategory, null);
|
||||
final boolean disablePrice = hasHadSubscriptionForOneTimePlan;
|
||||
Set<SubscriptionPrice> prices = new HashSet<>();
|
||||
plan.getPrices().forEach(price -> {
|
||||
price.setDisablePlan(disablePrice);
|
||||
prices.add(price);
|
||||
});
|
||||
return new SubscriptionPlanDTO(plan.getId(), isUserSubscribedToPlan, prices,
|
||||
plan.getPlanCategories(), hasHadSubscriptionForOneTimePlan, isUserSubscribedToPlanCategory, null, plan.getGroup());
|
||||
}
|
||||
|
||||
private boolean isUserSubscribedToPlan(String planId) {
|
||||
|
||||
+20
@@ -3,9 +3,11 @@ package com.sap.sse.security.ui.server.subscription.chargebee;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.chargebee.models.PortalSession;
|
||||
import com.sap.sse.security.shared.impl.User;
|
||||
import com.sap.sse.security.shared.subscription.Subscription;
|
||||
import com.sap.sse.security.shared.subscription.SubscriptionPlan;
|
||||
@@ -85,4 +87,22 @@ public class ChargebeeSubscriptionServiceImpl extends SubscriptionServiceImpl im
|
||||
final SubscriptionPlan subscriptionPlanById = getSecurityService().getSubscriptionPlanById(planId);
|
||||
return subscriptionPlanById == null ? null : convertToDto(subscriptionPlanById);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelfServicePortalSession() {
|
||||
try {
|
||||
User currentUser = getCurrentUser();
|
||||
CompletableFuture<PortalSession> result = new CompletableFuture<>();
|
||||
getApiService().getUserSelfServicePortalSession(currentUser.getId().toString(), (session) -> result.complete(session));
|
||||
final PortalSession portalSession = result.get();
|
||||
if(portalSession != null) {
|
||||
return portalSession.accessUrl();
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error in getting session", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -10,7 +10,8 @@ public interface HasSubscriptionMessageKeys{
|
||||
String getSubscriptionPlanId();
|
||||
|
||||
default public String getSubscriptionPlanNameMessageKey() {
|
||||
return getSubscriptionPlanId() + NAME_MESSAGE_KEY_SUFFX;
|
||||
final String subscriptionPlanId = getSubscriptionPlanId();
|
||||
return subscriptionPlanId.substring(subscriptionPlanId.indexOf("_") + 1) + NAME_MESSAGE_KEY_SUFFX;
|
||||
}
|
||||
|
||||
default public String getSubscriptionPlanDescMessageKey() {
|
||||
|
||||
+8
-1
@@ -6,6 +6,7 @@ import java.util.Set;
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sse.security.shared.subscription.SubscriptionPrice;
|
||||
import com.sap.sse.security.shared.subscription.SubscriptionPlan.PlanCategory;
|
||||
import com.sap.sse.security.shared.subscription.SubscriptionPlan.PlanGroup;
|
||||
import com.sap.sse.security.ui.client.subscription.SubscriptionService;
|
||||
|
||||
/**
|
||||
@@ -20,6 +21,7 @@ public class SubscriptionPlanDTO implements HasSubscriptionMessageKeys, IsSerial
|
||||
private Set<PlanCategory> planCategory;
|
||||
private Boolean userWasAlreadySubscribedToOneTimePlan;
|
||||
private Boolean isUserSubscribedToPlanCategory;
|
||||
private PlanGroup group;
|
||||
|
||||
/**
|
||||
* For GWT Serialization only
|
||||
@@ -30,7 +32,7 @@ public class SubscriptionPlanDTO implements HasSubscriptionMessageKeys, IsSerial
|
||||
|
||||
public SubscriptionPlanDTO(String id, boolean isUserSubscribedToPlan, Set<SubscriptionPrice> prices,
|
||||
Set<PlanCategory> planCategory, boolean userWasAlreadySubscribedToOneTimePlan,
|
||||
boolean isUserSubscribedToPlanCategory, String error) {
|
||||
boolean isUserSubscribedToPlanCategory, String error, PlanGroup group) {
|
||||
this.id = id;
|
||||
this.isUserSubscribedToPlan = isUserSubscribedToPlan;
|
||||
this.planCategory = planCategory;
|
||||
@@ -38,6 +40,11 @@ public class SubscriptionPlanDTO implements HasSubscriptionMessageKeys, IsSerial
|
||||
this.isUserSubscribedToPlanCategory = isUserSubscribedToPlanCategory;
|
||||
this.prices = new HashSet<SubscriptionPrice>(prices);
|
||||
this.error = error;
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public PlanGroup getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public Set<PlanCategory> getPlanCategory() {
|
||||
|
||||
Reference in New Issue
Block a user