diff --git a/configuration/buildAndUpdateProduct.sh b/configuration/buildAndUpdateProduct.sh index 10f6be5e538..54c7e2b1bfb 100755 --- a/configuration/buildAndUpdateProduct.sh +++ b/configuration/buildAndUpdateProduct.sh @@ -612,10 +612,10 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then if [ $p2local -eq 1 ]; then echo "INFO: Building and using local p2 repo" #build local p2 repo - echo "Using following command (pwd: java/com.sap.sailing.targetplatform.base): mvn -fae -s $MAVEN_SETTINGS $clean compile" + echo "Using following command (pwd: java/com.sap.sailing.targetplatform.base): mvn ${extra} -fae -s $MAVEN_SETTINGS $clean compile" echo "Maven version used: `mvn --version`" echo "JAVA_HOME used: $JAVA_HOME" - (cd com.sap.$PROJECT_TYPE.targetplatform.base; mvn -fae -s $MAVEN_SETTINGS $clean compile 2>&1 | tee -a $START_DIR/build.log) + (cd com.sap.$PROJECT_TYPE.targetplatform.base; mvn ${extra} -fae -s $MAVEN_SETTINGS $clean compile 2>&1 | tee -a $START_DIR/build.log) # now get the exit status from mvn, and not that of tee which is what $? contains now MVN_EXIT_CODE=${PIPESTATUS[0]} echo "Maven exit code is $MVN_EXIT_CODE" diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/subscription/PremiumRole.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/subscription/PremiumRole.java index 3122b82e9a7..cec512b4326 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/subscription/PremiumRole.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/subscription/PremiumRole.java @@ -15,7 +15,6 @@ public class PremiumRole extends RolePrototype { private static final UUID ROLE_ID = UUID.fromString("7021e7a2-569a-11ec-bf63-0242ac130002"); private static final long serialVersionUID = 8032532973066767581L; private static final PremiumRole INSTANCE = new PremiumRole(); - private static final String MESSAGE_KEY = "premium_role"; PremiumRole() { super("premium", ROLE_ID.toString(), @@ -40,8 +39,4 @@ public class PremiumRole extends RolePrototype { public static UUID getRoleId() { return ROLE_ID; } - - public static String getMessageKey() { - return MESSAGE_KEY; - } } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/subscription/SailingSubscriptionPlan.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/subscription/SailingSubscriptionPlan.java index c2dd28306df..7ff201dc003 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/subscription/SailingSubscriptionPlan.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/subscription/SailingSubscriptionPlan.java @@ -9,6 +9,8 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import com.sap.sse.security.shared.subscription.AllDataMiningRole; +import com.sap.sse.security.shared.subscription.ArchiveDataMiningRole; import com.sap.sse.security.shared.subscription.SubscriptionPlan; import com.sap.sse.security.shared.subscription.SubscriptionPlanRole; import com.sap.sse.security.shared.subscription.SubscriptionPrice; @@ -18,43 +20,105 @@ import com.sap.sse.security.shared.subscription.SubscriptionPrice.PaymentInterva * Payment subscription plans. A subscription plan has a name, a {@link String}-based ID, and a set of * {@link SubscriptionPlanRole roles} it grants to a subscribing user. These roles can specify how they are to be * qualified when assigned, regarding user and group qualifications. See - * {@link SubscriptionPlanRole.GroupQualificationMode} and {@link SubscriptionPlanRole.UserQualificationMode} for - * more details. + * {@link SubscriptionPlanRole.GroupQualificationMode} and {@link SubscriptionPlanRole.UserQualificationMode} for more + * details. * */ public class SailingSubscriptionPlan extends SubscriptionPlan { private static final String USD_CURRENCY_CODE = "usd"; - private static final BigDecimal YEARLY_PLAN_PRICE = new BigDecimal(49.99); - private static final BigDecimal WEEKLY_PLAN_PRICE = new BigDecimal(8.99); - private static final BigDecimal TRIAL_PLAN_PRICE = new BigDecimal(0); private static final long serialVersionUID = 2563619370274543312L; - private static final String YEARLY_PLAN_ID = "yearly_premium"; - private static final String WEEKLY_PLAN_ID = "weekly_premium"; - private static final String TRIAL_PLAN_ID = "trial_premium"; - private static final String TRIAL_PLAN_ITEMPRICE_ID = "trial_premium_usd_daily"; - private static final String WEEKLY_PLAN_ITEMPRICE_ID = "weekly_premium_usd_weekly"; - private static final String YEARLY_PLAN_ITEMPRICE_ID = "yearly_premium_usd_yearly"; + private static final String TRIAL_PREMIUM_PLAN_ID = "trial_premium"; + private static final String WEEKLY_PREMIUM_PLAN_ID = "weekly_premium"; + private static final String YEARLY_PREMIUM_PLAN_ID = "yearly_premium"; + private static final String TRIAL_DATA_MINING_ARCHIVE_PLAN_ID = "trial_data_mining_archive"; + private static final String WEEKLY_DATA_MINING_ARCHIVE_PLAN_ID = "weekly_data_mining_archive"; + private static final String YEARLY_DATA_MINING_ARCHIVE_PLAN_ID = "yearly_data_mining_archive"; + private static final String TRIAL_DATA_MINING_ALL_PLAN_ID = "trial_data_mining_all"; + private static final String WEEKLY_DATA_MINING_ALL_PLAN_ID = "weekly_data_mining_all"; + private static final String YEARLY_DATA_MINING_ALL_PLAN_ID = "yearly_data_mining_all"; + private static final String TRIAL_PREMIUM_PLAN_ITEMPRICE_ID = "trial_premium_usd_daily"; + private static final String WEEKLY_PREMIUM_PLAN_ITEMPRICE_ID = "weekly_premium_usd_weekly"; + private static final String YEARLY_PREMIUM_PLAN_ITEMPRICE_ID = "yearly_premium_usd_yearly"; + private static final String TRIAL_DATA_MINING_ARCHIVE_PLAN_ITEMPRICE_ID = "trial_data_mining_archive_usd_daily"; + private static final String WEEKLY_DATA_MINING_ARCHIVE_PLAN_ITEMPRICE_ID = "weekly_data_mining_archive_usd_weekly"; + private static final String YEARLY_DATA_MINING_ARCHIVE_PLAN_ITEMPRICE_ID = "yearly_data_mining_archive_usd_yearly"; + private static final String TRIAL_DATA_MINING_ALL_PLAN_ITEMPRICE_ID = "trial_data_mining_all_usd_daily"; + private static final String WEEKLY_DATA_MINING_ALL_PLAN_ITEMPRICE_ID = "weekly_data_mining_all_usd_weekly"; + private static final String YEARLY_DATA_MINING_ALL_PLAN_ITEMPRICE_ID = "yearly_data_mining_all_usd_yearly"; private static final Map plansById = new HashMap<>(); private SailingSubscriptionPlan(String id, Set prices, Set excludesPlanIds, - Boolean isOneTimePlan, SubscriptionPlanRole... roles) { - super(id, prices, excludesPlanIds, isOneTimePlan, roles); + Boolean isOneTimePlan, PlanGroup group, SubscriptionPlanRole... roles) { + super(id, prices, excludesPlanIds, isOneTimePlan, group, roles); plansById.put(id, this); } - public static final SubscriptionPlan YEARLY = new SailingSubscriptionPlan(YEARLY_PLAN_ID, - Stream.of(new SubscriptionPrice(YEARLY_PLAN_ITEMPRICE_ID, YEARLY_PLAN_PRICE, USD_CURRENCY_CODE, - PaymentInterval.YEAR)).collect(Collectors.toSet()), Stream.of(PlanCategory.PREMIUM).collect(Collectors.toSet()), false, + public static final SubscriptionPlan PREMIUM_TRIAL = new SailingSubscriptionPlan(TRIAL_PREMIUM_PLAN_ID, + Stream.of(new SubscriptionPrice(TRIAL_PREMIUM_PLAN_ITEMPRICE_ID, new BigDecimal(0), USD_CURRENCY_CODE, + PaymentInterval.DAY, false)).collect(Collectors.toSet()), + Stream.of(PlanCategory.PREMIUM).collect(Collectors.toSet()), true, + PlanGroup.PREMIUM, new SubscriptionPlanRole(PremiumRole.getRoleId())); - public static final SubscriptionPlan WEEKLY = new SailingSubscriptionPlan(WEEKLY_PLAN_ID, - Stream.of(new SubscriptionPrice(WEEKLY_PLAN_ITEMPRICE_ID, WEEKLY_PLAN_PRICE, USD_CURRENCY_CODE, - PaymentInterval.WEEK)).collect(Collectors.toSet()), Stream.of(PlanCategory.PREMIUM).collect(Collectors.toSet()), false, + public static final SubscriptionPlan PREMIUM_WEEKLY = new SailingSubscriptionPlan(WEEKLY_PREMIUM_PLAN_ID, + Stream.of(new SubscriptionPrice(WEEKLY_PREMIUM_PLAN_ITEMPRICE_ID, new BigDecimal(8.99), USD_CURRENCY_CODE, + PaymentInterval.WEEK, true)).collect(Collectors.toSet()), + Stream.of(PlanCategory.PREMIUM).collect(Collectors.toSet()), false, + PlanGroup.PREMIUM, new SubscriptionPlanRole(PremiumRole.getRoleId())); - public static final SubscriptionPlan TRIAL = new SailingSubscriptionPlan(TRIAL_PLAN_ID, - Stream.of(new SubscriptionPrice(TRIAL_PLAN_ITEMPRICE_ID, TRIAL_PLAN_PRICE, USD_CURRENCY_CODE, - PaymentInterval.DAY)).collect(Collectors.toSet()), Stream.of(PlanCategory.PREMIUM).collect(Collectors.toSet()), true, + public static final SubscriptionPlan PREMIUM_YEARLY = new SailingSubscriptionPlan(YEARLY_PREMIUM_PLAN_ID, + Stream.of(new SubscriptionPrice(YEARLY_PREMIUM_PLAN_ITEMPRICE_ID, new BigDecimal(49.99), USD_CURRENCY_CODE, + PaymentInterval.YEAR, false)).collect(Collectors.toSet()), + Stream.of(PlanCategory.PREMIUM).collect(Collectors.toSet()), false, + PlanGroup.PREMIUM, new SubscriptionPlanRole(PremiumRole.getRoleId())); + public static final SubscriptionPlan DATA_MINING_ARCHIVE_TRIAL = new SailingSubscriptionPlan( + TRIAL_DATA_MINING_ARCHIVE_PLAN_ID, + Stream.of(new SubscriptionPrice(TRIAL_DATA_MINING_ARCHIVE_PLAN_ITEMPRICE_ID, new BigDecimal(0), + USD_CURRENCY_CODE, PaymentInterval.DAY, false)).collect(Collectors.toSet()), + Stream.of(PlanCategory.DATA_MINING, PlanCategory.PREMIUM).collect(Collectors.toSet()), true, + PlanGroup.DATA_MINING_ARCHIVE, + new SubscriptionPlanRole(PremiumRole.getRoleId()), + new SubscriptionPlanRole(ArchiveDataMiningRole.getRoleId())); + public static final SubscriptionPlan DATA_MINING_ARCHIVE_WEEKLY = new SailingSubscriptionPlan( + WEEKLY_DATA_MINING_ARCHIVE_PLAN_ID, + Stream.of(new SubscriptionPrice(WEEKLY_DATA_MINING_ARCHIVE_PLAN_ITEMPRICE_ID, new BigDecimal(89.99), + USD_CURRENCY_CODE, PaymentInterval.WEEK, true)).collect(Collectors.toSet()), + Stream.of(PlanCategory.DATA_MINING, PlanCategory.PREMIUM).collect(Collectors.toSet()), false, + PlanGroup.DATA_MINING_ARCHIVE, + new SubscriptionPlanRole(PremiumRole.getRoleId()), + new SubscriptionPlanRole(ArchiveDataMiningRole.getRoleId())); + public static final SubscriptionPlan DATA_MINING_ARCHIVE_YEARLY = new SailingSubscriptionPlan( + YEARLY_DATA_MINING_ARCHIVE_PLAN_ID, + Stream.of(new SubscriptionPrice(YEARLY_DATA_MINING_ARCHIVE_PLAN_ITEMPRICE_ID, new BigDecimal(499.99), + USD_CURRENCY_CODE, PaymentInterval.YEAR, false)).collect(Collectors.toSet()), + Stream.of(PlanCategory.DATA_MINING, PlanCategory.PREMIUM).collect(Collectors.toSet()), false, + PlanGroup.DATA_MINING_ARCHIVE, + new SubscriptionPlanRole(PremiumRole.getRoleId()), + new SubscriptionPlanRole(ArchiveDataMiningRole.getRoleId())); + + public static final SubscriptionPlan DATA_MINING_ALL_TRIAL = new SailingSubscriptionPlan( + TRIAL_DATA_MINING_ALL_PLAN_ID, + Stream.of(new SubscriptionPrice(TRIAL_DATA_MINING_ALL_PLAN_ITEMPRICE_ID, new BigDecimal(0), + USD_CURRENCY_CODE, PaymentInterval.DAY, false)).collect(Collectors.toSet()), + Stream.of(PlanCategory.DATA_MINING, PlanCategory.PREMIUM).collect(Collectors.toSet()), true, + PlanGroup.DATA_MINING_ALL, + new SubscriptionPlanRole(PremiumRole.getRoleId()), new SubscriptionPlanRole(AllDataMiningRole.getRoleId())); + public static final SubscriptionPlan DATA_MINING_ALL_WEEKLY = new SailingSubscriptionPlan( + WEEKLY_DATA_MINING_ALL_PLAN_ID, + Stream.of(new SubscriptionPrice(WEEKLY_DATA_MINING_ALL_PLAN_ITEMPRICE_ID, new BigDecimal(189.99), + USD_CURRENCY_CODE, PaymentInterval.WEEK, true)).collect(Collectors.toSet()), + Stream.of(PlanCategory.DATA_MINING, PlanCategory.PREMIUM).collect(Collectors.toSet()), false, + PlanGroup.DATA_MINING_ALL, + new SubscriptionPlanRole(PremiumRole.getRoleId()), new SubscriptionPlanRole(AllDataMiningRole.getRoleId())); + public static final SubscriptionPlan DATA_MINING_ALL_YEARLY = new SailingSubscriptionPlan( + YEARLY_DATA_MINING_ALL_PLAN_ID, + Stream.of(new SubscriptionPrice(YEARLY_DATA_MINING_ALL_PLAN_ITEMPRICE_ID, new BigDecimal(999.99), + USD_CURRENCY_CODE, PaymentInterval.YEAR, false)).collect(Collectors.toSet()), + Stream.of(PlanCategory.DATA_MINING, PlanCategory.PREMIUM).collect(Collectors.toSet()), false, + PlanGroup.DATA_MINING_ALL, + new SubscriptionPlanRole(PremiumRole.getRoleId()), new SubscriptionPlanRole(AllDataMiningRole.getRoleId())); + public static Map getAllInstances() { return Collections.unmodifiableMap(plansById); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/header/SAPHeaderComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/header/SAPHeaderComponent.java index 8dd57504ebd..47c38ab3955 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/header/SAPHeaderComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/header/SAPHeaderComponent.java @@ -11,6 +11,7 @@ import com.sap.sse.gwt.client.shared.components.AbstractCompositeComponent; import com.sap.sse.gwt.client.shared.components.Component; import com.sap.sse.gwt.client.shared.settings.ComponentContext; import com.sap.sse.security.ui.client.UserService; +import com.sap.sse.security.ui.client.premium.PaywallResolver; public class SAPHeaderComponent extends AbstractCompositeComponent { private SAPHeaderComponentSettings settings; @@ -20,13 +21,13 @@ public class SAPHeaderComponent extends AbstractCompositeComponent parent, ComponentContext context, SAPHeaderComponentLifecycle componentLifecycle, - UserService userService, SAPHeaderComponentSettings settings, + UserService userService, PaywallResolver paywallResolver, SAPHeaderComponentSettings settings, StringMessages stringMessages, boolean startInAutoScreenMode) { super(parent, context); this.componentLifecycle = componentLifecycle; this.settings = settings; this.sapHeader = new SAPSailingHeaderWithAuthentication(settings.getTitle()); - new FixedSailingAuthentication(userService, sapHeader.getAuthenticationMenuView()); + new FixedSailingAuthentication(userService, paywallResolver, sapHeader.getAuthenticationMenuView()); initWidget(sapHeader); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/authentication/FixedSailingAuthentication.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/authentication/FixedSailingAuthentication.java index 428c807eba6..bbddeb8bb1f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/authentication/FixedSailingAuthentication.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/authentication/FixedSailingAuthentication.java @@ -5,6 +5,7 @@ import com.sap.sse.security.ui.authentication.generic.GenericAuthentication; import com.sap.sse.security.ui.authentication.generic.sapheader.FixedFlyoutAuthenticationView; import com.sap.sse.security.ui.authentication.view.AuthenticationMenuView; import com.sap.sse.security.ui.client.UserService; +import com.sap.sse.security.ui.client.premium.PaywallResolver; /** * Sailing-specific extension of {@link GenericAuthentication} using a {@link FixedFlyoutAuthenticationView}, which is @@ -13,8 +14,9 @@ import com.sap.sse.security.ui.client.UserService; public class FixedSailingAuthentication extends GenericAuthentication { private static final CommonSharedResources res = CommonSharedResources.INSTANCE; - public FixedSailingAuthentication(UserService userService, AuthenticationMenuView userManagementMenuView) { - super(userService, userManagementMenuView, new FixedFlyoutAuthenticationView(res), + public FixedSailingAuthentication(UserService userService, PaywallResolver paywallResolver, + AuthenticationMenuView userManagementMenuView) { + super(userService, paywallResolver, userManagementMenuView, new FixedFlyoutAuthenticationView(res), SailingAuthenticationEntryPointLinkFactory.INSTANCE, res); res.mainCss().ensureInjected(); new FixedSailingLoginHintPopup(getAuthenticationManager()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/authentication/GenericSailingAuthentication.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/authentication/GenericSailingAuthentication.java index 2989d3e9fe6..6858698ebdb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/authentication/GenericSailingAuthentication.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/authentication/GenericSailingAuthentication.java @@ -5,6 +5,7 @@ import com.sap.sse.security.ui.authentication.generic.GenericAuthentication; import com.sap.sse.security.ui.authentication.generic.sapheader.GenericFlyoutAuthenticationView; import com.sap.sse.security.ui.authentication.view.AuthenticationMenuView; import com.sap.sse.security.ui.client.UserService; +import com.sap.sse.security.ui.client.premium.PaywallResolver; /** * Sailing-specific extension of {@link GenericAuthentication} using a {@link GenericFlyoutAuthenticationView}, which is @@ -13,8 +14,9 @@ import com.sap.sse.security.ui.client.UserService; public class GenericSailingAuthentication extends GenericAuthentication { private static final CommonSharedResources res = CommonSharedResources.INSTANCE; - public GenericSailingAuthentication(UserService userService, AuthenticationMenuView userManagementMenuView) { - super(userService, userManagementMenuView, new GenericFlyoutAuthenticationView(res), + public GenericSailingAuthentication(UserService userService, PaywallResolver paywallResolver, + AuthenticationMenuView userManagementMenuView) { + super(userService, paywallResolver, userManagementMenuView, new GenericFlyoutAuthenticationView(res), SailingAuthenticationEntryPointLinkFactory.INSTANCE, res); res.mainCss().ensureInjected(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/TabletAndDesktopApplicationClientFactory.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/TabletAndDesktopApplicationClientFactory.java index 10c517c4a76..d2ecccf64ba 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/TabletAndDesktopApplicationClientFactory.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/app/TabletAndDesktopApplicationClientFactory.java @@ -59,8 +59,10 @@ public class TabletAndDesktopApplicationClientFactory extends AbstractApplicatio this(eventBus, placeController, new DesktopPlacesNavigator(placeController, isStandaloneServer)); } - private TabletAndDesktopApplicationClientFactory(final EventBus eventBus, final PlaceController placeController, final DesktopPlacesNavigator placesNavigator) { + private TabletAndDesktopApplicationClientFactory(final EventBus eventBus, final PlaceController placeController, + final DesktopPlacesNavigator placesNavigator) { super(new TabletAndDesktopApplicationView(placesNavigator, eventBus), eventBus, placeController, placesNavigator); + getUserService(); final AuthenticationViewDesktop userManagementDisplay = new AuthenticationViewDesktop(); WhatsNewDialogFactory.register(getUserService(), placeController); final Runnable signInSuccesfulNavigation = new Runnable() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.gss index af0eb2f3f47..95bbdb7e2af 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.gss +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.gss @@ -162,8 +162,7 @@ .user_menu_button.loggedin.open { background-image: resourceUrl('loggedinOpen'); } -.user_menu_button span.premium { - display: none; +.user_menu_premium { position: absolute; bottom: -2px; left: 50%; @@ -194,7 +193,8 @@ .header_navigation_iconactive { /* background-image: resourceUrl('navigation_hover'); */ } -.sitenavigation .subscriptions:before { +.sitenavigation .subscriptions:before, +.sitenavigation_fixed .subscriptions:before { content: ""; position: absolute; background: resourceUrl('crown'); @@ -212,3 +212,17 @@ } .sitenavigation .subscriptions { } +.sitenavigation .premium_feature, +.sitenavigation_fixed .premium_feature { + color: #e3c861; +} + +.sitenavigation .premium_feature.premium_hint, +.sitenavigation_fixed .premium_feature.premium_hint { + background: resourceUrl('premiumIcon'); + background-size: 15px; + background-repeat: no-repeat; + background-position: right 10px top 19px; + padding-right: 28px; +} + diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.java index 60eb9508e5b..94b89f4a601 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.java @@ -20,6 +20,7 @@ import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.ImageElement; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; @@ -54,6 +55,7 @@ import com.sap.sailing.gwt.home.shared.places.solutions.SolutionsPlace.Solutions import com.sap.sailing.gwt.home.shared.places.start.StartPlace; import com.sap.sailing.gwt.home.shared.places.subscription.SubscriptionPlace; import com.sap.sailing.gwt.home.shared.utils.DropdownHandler; +import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sse.gwt.client.mvp.PlaceChangedEvent; import com.sap.sse.gwt.shared.ClientConfiguration; @@ -92,7 +94,6 @@ public class Header extends Composite implements HeaderConstants { @UiField Element centerMenuPanel; @UiField Anchor usermenu; @UiField ImageElement logoImage; - @UiField Element usermenuPremium; private static final HyperlinkImpl HYPERLINK_IMPL = GWT.create(HyperlinkImpl.class); private final List links; @@ -102,10 +103,10 @@ public class Header extends Composite implements HeaderConstants { private final PlaceNavigation solutionsNavigation; private final PlaceNavigation subscriptionsNavigation; private final AuthenticationMenuView authenticationMenuView; - + interface HeaderUiBinder extends UiBinder { } - + /** * Contains the logic for the dynamic hamburger menu on top of the page. * @author Georg Herdt @@ -233,7 +234,6 @@ public class Header extends Composite implements HeaderConstants { this.navigator = navigator; HeaderResources.INSTANCE.css().ensureInjected(); initWidget(uiBinder.createAndBindUi(this)); - Map menuToDropDownItemMap = new HashMap<>(); menuToDropDownItemMap.put(startPageLink, startPageLinkMenu); menuToDropDownItemMap.put(eventsPageLink, eventsPageLinkMenu); @@ -242,10 +242,18 @@ public class Header extends Composite implements HeaderConstants { menuToDropDownItemMap.put(adminConsolePageLink, adminConsolePageLinkMenu); menuToDropDownItemMap.put(dataMiningPageLink, dataMiningPageLinkMenu); menuToDropDownItemMap.put(strategySimulatorPageLink, strategySimulatorPageLinkMenu); - headerNavigationDropDownMenuContainer.getStyle().setDisplay(Display.NONE); final DropdownHandler dropdownHandler = new DropdownHandler(hamburgerMenuIcon, headerNavigationDropDownMenuContainer); menuItemVisibilityHandler = new MenuItemVisibilityHandler(menuToDropDownItemMap, dropdownHandler, hamburgerMenuIcon, centerMenuPanel); + // close menu if menu item was clicked + for (Anchor anchor: menuToDropDownItemMap.values()) { + anchor.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + dropdownHandler.setVisible(false); + } + }); + } Window.addResizeHandler(menuItemVisibilityHandler); links = Arrays.asList(new Anchor[] { startPageLink, eventsPageLink, solutionsPageLink, subscriptionsPageLink, adminConsolePageLink, dataMiningPageLink }); homeNavigation = navigator.getHomeNavigation(); @@ -256,10 +264,10 @@ public class Header extends Composite implements HeaderConstants { eventsPageLink.setHref(eventsNavigation.getTargetUrl()); solutionsPageLink.setHref(solutionsNavigation.getTargetUrl()); subscriptionsPageLink.setHref(subscriptionsNavigation.getTargetUrl()); - // make the Admin and DataMining links visible only for signed-in users + authenticationMenuView = new AuthenticationMenuViewImpl(usermenu, HeaderResources.INSTANCE.css().loggedin(), + HeaderResources.INSTANCE.css().open(), HeaderResources.INSTANCE.css().user_menu_premium()); + // make the Admin link visible only for signed-in users adminConsolePageLink.getElement().getStyle().setDisplay(Display.NONE); - dataMiningPageLink.getElement().getStyle().setDisplay(Display.NONE); - strategySimulatorPageLink.getElement().getStyle().setDisplay(Display.NONE); // initially hide admin console, data mining and strategy simulator in hamburger menu menuItemVisibilityHandler.addIgnore(adminConsolePageLink); menuItemVisibilityHandler.addIgnore(dataMiningPageLink); @@ -287,36 +295,59 @@ public class Header extends Composite implements HeaderConstants { adminConsolePageLink.getElement().getStyle().setDisplay(Display.NONE); menuItemVisibilityHandler.addIgnore(adminConsolePageLink); } - if (authContext.hasServerPermission(ServerActions.DATA_MINING)) { + menuItemVisibilityHandler.removeIgnore(dataMiningPageLink); + if (authContext.getPaywallResolver().hasPermission(ServerActions.DATA_MINING, authContext.getServerInfo())) { dataMiningPageLinkMenu.setHref(DATA_MINING_PATH); dataMiningPageLinkMenu.setTarget(DATA_MINING_WINDOW); + dataMiningPageLinkMenu.removeStyleName(HeaderResources.INSTANCE.css().premium_hint()); dataMiningPageLink.setHref(DATA_MINING_PATH); dataMiningPageLink.setTarget(DATA_MINING_WINDOW); + dataMiningPageLink.removeStyleName(HeaderResources.INSTANCE.css().premium_hint()); dataMiningPageLink.getElement().getStyle().setDisplay(Display.INLINE_BLOCK); - menuItemVisibilityHandler.removeIgnore(dataMiningPageLink); } else { - dataMiningPageLink.getElement().getStyle().setDisplay(Display.NONE); - menuItemVisibilityHandler.addIgnore(dataMiningPageLink); + dataMiningPageLinkMenu.setHref(subscriptionsNavigation.getTargetUrl()); + dataMiningPageLinkMenu.setTarget(SELF); + dataMiningPageLinkMenu.addStyleName(HeaderResources.INSTANCE.css().premium_hint()); + dataMiningPageLink.setHref(subscriptionsNavigation.getTargetUrl()); + dataMiningPageLink.setTarget(SELF); + dataMiningPageLink.getElement().getStyle().setDisplay(Display.INLINE_BLOCK); + dataMiningPageLink.addStyleName(HeaderResources.INSTANCE.css().premium_hint()); + // add possible subscription plans to URL + authContext.getPaywallResolver().getUnlockingSubscriptionPlans(ServerActions.DATA_MINING, authContext.getServerInfo(), + (unlockingPlans) -> { + dataMiningPageLinkMenu.setHref(EntryPointLinkFactory.createSubscriptionPageLink(unlockingPlans)); + dataMiningPageLink.setHref(EntryPointLinkFactory.createSubscriptionPageLink(unlockingPlans)); + }); } - if (authContext.hasPermission(NamedSecuredObjectDTO.create(authContext.getServerInfo().getName(), - SecuredDomainType.SIMULATOR, new TypeRelativeObjectIdentifier(authContext.getServerInfo().getName())), - DefaultActions.READ)) { + menuItemVisibilityHandler.removeIgnore(strategySimulatorPageLink); + if (authContext.getPaywallResolver().hasPermission(DefaultActions.READ, NamedSecuredObjectDTO.create(authContext.getServerInfo().getName(), + SecuredDomainType.SIMULATOR, new TypeRelativeObjectIdentifier(authContext.getServerInfo().getName())))) { strategySimulatorPageLinkMenu.setHref(STRATEGY_SIMULATOR_PATH); strategySimulatorPageLinkMenu.setTarget(STRATEGY_SIMULATOR_WINDOW); + strategySimulatorPageLinkMenu.removeStyleName(HeaderResources.INSTANCE.css().premium_hint()); strategySimulatorPageLink.setHref(STRATEGY_SIMULATOR_PATH); strategySimulatorPageLink.setTarget(STRATEGY_SIMULATOR_WINDOW); strategySimulatorPageLink.getElement().getStyle().setDisplay(Display.INLINE_BLOCK); - menuItemVisibilityHandler.removeIgnore(strategySimulatorPageLink); + strategySimulatorPageLink.removeStyleName(HeaderResources.INSTANCE.css().premium_hint()); } else { - strategySimulatorPageLink.getElement().getStyle().setDisplay(Display.NONE); - menuItemVisibilityHandler.addIgnore(strategySimulatorPageLink); + strategySimulatorPageLinkMenu.setHref(subscriptionsNavigation.getTargetUrl()); + strategySimulatorPageLinkMenu.setTarget(SELF); + strategySimulatorPageLinkMenu.addStyleName(HeaderResources.INSTANCE.css().premium_hint()); + strategySimulatorPageLink.setHref(subscriptionsNavigation.getTargetUrl()); + strategySimulatorPageLink.setTarget(SELF); + strategySimulatorPageLink.getElement().getStyle().setDisplay(Display.INLINE_BLOCK); + strategySimulatorPageLink.addStyleName(HeaderResources.INSTANCE.css().premium_hint()); + // add possible subscription plans to URL + authContext.getPaywallResolver().getUnlockingSubscriptionPlans(DefaultActions.READ, NamedSecuredObjectDTO.create(authContext.getServerInfo().getName(), + SecuredDomainType.SIMULATOR, new TypeRelativeObjectIdentifier(authContext.getServerInfo().getName())), + (unlockingPlans) -> { + strategySimulatorPageLinkMenu.setHref(EntryPointLinkFactory.createSubscriptionPageLink(unlockingPlans)); + strategySimulatorPageLink.setHref(EntryPointLinkFactory.createSubscriptionPageLink(unlockingPlans)); + }); + } menuItemVisibilityHandler.refreshVisibilityDeferred(); - if (authContext.hasPermission(authContext.getCurrentUser(), UserActions.BE_PREMIUM)) { - usermenuPremium.getStyle().setDisplay(Display.BLOCK); - } else { - usermenuPremium.getStyle().setDisplay(Display.NONE); - } + authenticationMenuView.showPremium(authContext.hasPermission(authContext.getCurrentUser(), UserActions.BE_PREMIUM)); }); searchText.getElement().setAttribute("placeholder", StringMessages.INSTANCE.headerSearchPlaceholder()); searchText.addFocusHandler((focusEvent) -> menuItemVisibilityHandler.refreshVisibility(370)); @@ -337,7 +368,6 @@ public class Header extends Composite implements HeaderConstants { updateActiveLink(event.getNewPlace()); } }); - authenticationMenuView = new AuthenticationMenuViewImpl(usermenu, HeaderResources.INSTANCE.css().loggedin(), HeaderResources.INSTANCE.css().open()); if (!ClientConfiguration.getInstance().isBrandingActive()) { logoImage.getStyle().setDisplay(Display.NONE); logoImage.setTitle(StringMessages.INSTANCE.sapSailingAnalytics()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.ui.xml index e0cdb83d339..336b06404bb 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.ui.xml @@ -28,9 +28,9 @@ + styleName="{local_res.css.sitenavigation_link} {local_res.css.premium_feature}" text="{i18n.dataMining}" target="dataMiningWindow"/> + styleName="{local_res.css.sitenavigation_link} {local_res.css.premium_feature}" text="{i18n.simulator}" target="strategySimulatorWindow"/> diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/HeaderResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/HeaderResources.java index 8fb14de634e..0c3dab4591d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/HeaderResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/HeaderResources.java @@ -23,6 +23,10 @@ public interface HeaderResources extends AuthenticationResources { @Source("crown.png") @MimeType("image/png") DataResource crown(); + + @Source("icon_premium.svg") + @MimeType("image/svg+xml") + DataResource premiumIcon(); public interface LocalCss extends CssResource { String siteheader(); @@ -45,6 +49,8 @@ public interface HeaderResources extends AuthenticationResources { String header_navigation_icon(); String header_navigation_iconactive(); String subscriptions(); - String premium(); + String user_menu_premium(); + String premium_feature(); + String premium_hint(); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/icon_premium.svg b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/icon_premium.svg new file mode 100644 index 00000000000..b763a254b55 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/icon_premium.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCard.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCard.gss index fd0f9949e75..bec8b837b3a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCard.gss +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCard.gss @@ -57,10 +57,6 @@ } } -.subscription.individual { - order: 999; -} - .subscription.highlight { order: 1; } @@ -166,16 +162,18 @@ border-radius: 0.333333333333333em; border-color: red; /* display: inline; */ - padding: 4px; color: red; font-weight: bold; position: relative; - top: -60px; - right: -240px; - transform: rotate(45deg); + top: -45px; + right: -270px; + transform: rotate(30deg); + font-size: 10px; + padding: 4px; + margin: -14px; } -.free .price, .individual .price { +.free .price { border-color: #f0ab00; background-color: #f0ab00; color: #fff; @@ -186,7 +184,7 @@ color: #fff; } -.free .price:hover, .individual .price:hover { +.free .price:hover { border-color: #d79900; } @@ -231,17 +229,17 @@ width: 100%; display: block; position: relative; - padding: 4px; + padding: 0px 4px 12px 4px; } .hint { - position: absolute; + position: relative; + display: inline-block; color: #333; font-size: 12px; width: 100%; top: 3px; text-align: right; - padding: 8px; } .hint button { @@ -293,7 +291,20 @@ width: 27px; } +.featureCheck { + text-align: center; + margin: 0 1em; +} + +.featureNone { + text-align: center; + margin: 0 1em; + color: #999; + font-size: 32px; +} + .featureHeader { + text-align: center; font-weight: bold; font-size: 1.2em; text-transform: capitalize; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCard.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCard.java index 279333f07c9..9ea980f7a87 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCard.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCard.java @@ -23,23 +23,22 @@ import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.common.client.SharedResources; import com.sap.sailing.gwt.home.shared.places.subscription.SailingSubscriptionStringConstants; +import com.sap.sailing.gwt.home.shared.places.subscription.SubscriptionGroupDTO; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sse.security.shared.subscription.SubscriptionPrice; import com.sap.sse.security.ui.authentication.AuthenticationContextEvent; import com.sap.sse.security.ui.authentication.app.AuthenticationContext; import com.sap.sse.security.ui.client.i18n.subscription.SubscriptionStringConstants; -import com.sap.sse.security.ui.shared.subscription.SubscriptionPlanDTO; public class SubscriptionCard extends Composite { public static enum Type { - FREE, OWNER, HIGHLIGHT, INDIVIDUAL, DEFAULT, ONETIMELOCK + FREE, OWNER, HIGHLIGHT, DEFAULT, ONETIMELOCK } private static SubscriptionUiBinder uiBinder = GWT.create(SubscriptionUiBinder.class); private static final String FEATURE_STYLE = SubscriptionCardResources.INSTANCE.css().feature(); private static final String HIGHLIGHT_STYLE = SubscriptionCardResources.INSTANCE.css().highlight(); - private static final String INDIVIDUAL_STYLE = SubscriptionCardResources.INSTANCE.css().individual(); private static final String OWNED_STYLE = SubscriptionCardResources.INSTANCE.css().owned(); private static final String SUBSCRIPTION_STYLE = SubscriptionCardResources.INSTANCE.css().subscription(); private static final String FREE_STYLE = SubscriptionCardResources.INSTANCE.css().free(); @@ -70,16 +69,16 @@ public class SubscriptionCard extends Composite { private final Consumer subscriptionCallback; private SubscriptionPrice currentPrice; - private final SubscriptionPlanDTO subscriptionPlanDTO; + private final SubscriptionGroupDTO subscriptionGroupDTO; - public SubscriptionCard(SubscriptionPlanDTO subscriptionPlanDTO, Type type, Consumer subscriptionCallback, EventBus eventBus, boolean loggedIn) { + public SubscriptionCard(SubscriptionGroupDTO subscriptionGroupDTO, Type type, Consumer subscriptionCallback, EventBus eventBus, boolean loggedIn) { this.subscriptionCallback = subscriptionCallback; - this.subscriptionPlanDTO = subscriptionPlanDTO; + this.subscriptionGroupDTO = subscriptionGroupDTO; SubscriptionCardResources.INSTANCE.css().ensureInjected(); SharedResources.INSTANCE.mediaCss().ensureInjected(); initWidget(uiBinder.createAndBindUi(this)); addStyleName(SUBSCRIPTION_STYLE); - final List priceList = new ArrayList(subscriptionPlanDTO.getPrices()); + final List priceList = new ArrayList(subscriptionGroupDTO.getPrices()); priceList.sort(new Comparator() { @Override public int compare(SubscriptionPrice o1, SubscriptionPrice o2) { @@ -113,20 +112,19 @@ public class SubscriptionCard extends Composite { price.addStyleName(SELECTED_STYLE); } }); - prices.add(price); - Label priceInfo = new Label(subscriptionStringConstants.getString(subscriptionPlanDTO.getSubscriptionPlanPriceInfoMessageKey())); + if (!subscriptionPrice.getDisablePlan()) { + prices.add(price); + } + Label priceInfo = new Label(subscriptionStringConstants.getString(subscriptionGroupDTO.getSubscriptionGroupPriceInfoMessageKey())); priceInfo.addStyleName(PRICE_INFO_STYLE); - if (priceInfo.getText().trim().length() > 0) { + if (Boolean.TRUE.equals(subscriptionPrice.getIsOneTimePayment())) { prices.add(priceInfo); } } - // TODO: Implement new Price handling } else { final String priceText; if (type == Type.FREE) { priceText = i18n.free(); - } else if (type == Type.INDIVIDUAL) { - priceText = i18n.individual(); } else { priceText = i18n.price() + ": - "; } @@ -154,10 +152,6 @@ public class SubscriptionCard extends Composite { highlightHeader.add(new Label(i18n.subscriptionOneTimePlanLockedText())); button.getElement().getStyle().setDisplay(Display.NONE); break; - case INDIVIDUAL: - addStyleName(INDIVIDUAL_STYLE); - button.setText(i18n.send()); - break; case FREE: addStyleName(FREE_STYLE); button.setText(i18n.signInOrUp()); @@ -178,10 +172,10 @@ public class SubscriptionCard extends Composite { default: break; } - title.setInnerText(subscriptionStringConstants.getString(subscriptionPlanDTO.getSubscriptionPlanNameMessageKey())); - description.setInnerText(subscriptionStringConstants.getString(subscriptionPlanDTO.getSubscriptionPlanDescMessageKey())); - info.setInnerText(subscriptionStringConstants.getString(subscriptionPlanDTO.getSubscriptionPlanInfoMessageKey())); - final String[] featureStrings = subscriptionStringConstants.getStringArray(subscriptionPlanDTO.getSubscriptionPlanFeatureMessageKey()); + title.setInnerText(subscriptionStringConstants.getString(subscriptionGroupDTO.getSubscriptionGroupNameMessageKey())); + description.setInnerText(subscriptionStringConstants.getString(subscriptionGroupDTO.getSubscriptionGroupDescMessageKey())); + info.setInnerText(subscriptionStringConstants.getString(subscriptionGroupDTO.getSubscriptionGroupInfoMessageKey())); + final String[] featureStrings = subscriptionStringConstants.getString(subscriptionGroupDTO.getSubscriptionGroupFeatureMessageKey()).split("[|]"); for (String featureString : featureStrings) { if(featureString != "") { FlowPanel feature = new FlowPanel(); @@ -192,8 +186,8 @@ public class SubscriptionCard extends Composite { } } - public SubscriptionPlanDTO getSubscriptionPlanDTO() { - return subscriptionPlanDTO; + public SubscriptionGroupDTO getSubscriptionGroupDTO() { + return subscriptionGroupDTO; } @UiHandler("button") diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardContainer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardContainer.java index 3ba9058fe9f..0c48a807202 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardContainer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardContainer.java @@ -51,32 +51,36 @@ public class SubscriptionCardContainer extends Composite { public SubscriptionCardContainer() { initWidget(uiBinder.createAndBindUi(this)); - featureGrid = new Grid(2, 3); + featureGrid = new Grid(2, 4); final Label freePlanTitle = new Label(subscriptionStringMessages.free_subscription_plan_shortname()); freePlanTitle.addStyleName(SubscriptionCardResources.INSTANCE.css().featureHeader()); featureGrid.setWidget(0, 1, freePlanTitle); final Label premiumPlanTitle = new Label(subscriptionStringMessages.premium_subscription_plan_shortname()); premiumPlanTitle.addStyleName(SubscriptionCardResources.INSTANCE.css().featureHeader()); featureGrid.setWidget(0, 2, premiumPlanTitle); + final Label dataMiningArchivePlanTitle = new Label(subscriptionStringMessages.datamining_subscription_plan_shortname()); + dataMiningArchivePlanTitle.addStyleName(SubscriptionCardResources.INSTANCE.css().featureHeader()); + featureGrid.setWidget(0, 3, dataMiningArchivePlanTitle); addFeatureWithLink(subscriptionStringMessages.features_organize_events_title(), subscriptionStringMessages.features_organize_events_description(), "https://support.sapsailing.com/hc/en-us/articles/360018169799-Create-a-simple-event-on-my-sapsailing-com", - true, true); - addFeature(subscriptionStringMessages.features_events_with_more_regatta_title(), subscriptionStringMessages.features_events_with_more_regatta_description(), true, true); + true, true, true); + addFeature(subscriptionStringMessages.features_events_with_more_regatta_title(), subscriptionStringMessages.features_events_with_more_regatta_description(), true, true, true); addFeatureWithLink(subscriptionStringMessages.features_connect_to_tractrac_title(), subscriptionStringMessages.features_connect_to_tractrac_description(), - "https://tractrac.com/", true, true); - addFeature(subscriptionStringMessages.features_imports_title(), subscriptionStringMessages.features_imports_description(), true, true); - addFeature(subscriptionStringMessages.features_media_management_title(), subscriptionStringMessages.features_media_management_description(), true, true); - addFeature(subscriptionStringMessages.features_limited_live_analytics_title(), subscriptionStringMessages.features_limited_live_analytics_description(), true, true); - addFeature(subscriptionStringMessages.features_media_tags_title(), subscriptionStringMessages.features_media_tags_description(), true, true); - addFeature(subscriptionStringMessages.features_scoring_title(), subscriptionStringMessages.features_scoring_description(), true, true); - addFeature(subscriptionStringMessages.features_wind_analytics_title(), subscriptionStringMessages.features_wind_analytics_description(), false, true); - addFeature(subscriptionStringMessages.features_maneuver_analytics_title(), subscriptionStringMessages.features_maneuver_analytics_description(), false, true); - addFeature(subscriptionStringMessages.features_competitor_analytics_title(), subscriptionStringMessages.features_competitor_analytics_description(), false, true); - addFeature(subscriptionStringMessages.features_advanced_leaderboard_info_title(), subscriptionStringMessages.features_advanced_leaderboard_info_description(), false, true); - addFeature(subscriptionStringMessages.features_simulator_title(), subscriptionStringMessages.features_simulator_description(), false, true); - addFeature(subscriptionStringMessages.features_map_analytics_title(), subscriptionStringMessages.features_map_analytics_description(), false, true); + "https://tractrac.com/", true, true, true); + addFeature(subscriptionStringMessages.features_imports_title(), subscriptionStringMessages.features_imports_description(), true, true, true); + addFeature(subscriptionStringMessages.features_media_management_title(), subscriptionStringMessages.features_media_management_description(), true, true, true); + addFeature(subscriptionStringMessages.features_limited_live_analytics_title(), subscriptionStringMessages.features_limited_live_analytics_description(), true, true, true); + addFeature(subscriptionStringMessages.features_media_tags_title(), subscriptionStringMessages.features_media_tags_description(), true, true, true); + addFeature(subscriptionStringMessages.features_scoring_title(), subscriptionStringMessages.features_scoring_description(), true, true, true); + addFeature(subscriptionStringMessages.features_wind_analytics_title(), subscriptionStringMessages.features_wind_analytics_description(), false, true, true); + addFeature(subscriptionStringMessages.features_maneuver_analytics_title(), subscriptionStringMessages.features_maneuver_analytics_description(), false, true, true); + addFeature(subscriptionStringMessages.features_competitor_analytics_title(), subscriptionStringMessages.features_competitor_analytics_description(), false, true, true); + addFeature(subscriptionStringMessages.features_advanced_leaderboard_info_title(), subscriptionStringMessages.features_advanced_leaderboard_info_description(), false, true, true); + addFeature(subscriptionStringMessages.features_simulator_title(), subscriptionStringMessages.features_simulator_description(), false, true, true); + addFeature(subscriptionStringMessages.features_map_analytics_title(), subscriptionStringMessages.features_map_analytics_description(), false, true, true); + addFeature(subscriptionStringMessages.features_data_mining_title(), subscriptionStringMessages.features_data_mining_description(), false, false, true); features.add(featureGrid); emailContact.addClickHandler(new ClickHandler() { @Override @@ -87,13 +91,13 @@ public class SubscriptionCardContainer extends Composite { }); } - private void addFeature(final String titleString, final String descriptionString, boolean free, boolean premium) { + private void addFeature(final String titleString, final String descriptionString, boolean free, boolean premium, boolean dataMining) { Label description = new Label(descriptionString); description.addStyleName(SubscriptionCardResources.INSTANCE.css().featureDescription()); - addFeatureWithDescription(titleString, free, premium, description); + addFeatureWithDescription(titleString, free, premium, dataMining, description); } - private void addFeatureWithLink(final String titleString, final String descriptionString, final String link, boolean free, boolean premium) { + private void addFeatureWithLink(final String titleString, final String descriptionString, final String link, boolean free, boolean premium, boolean dataMining) { SimplePanel descriptionWithLink = new SimplePanel(); HTML exampleLink = new HTML(descriptionString+ " "); descriptionWithLink.addStyleName(SubscriptionCardResources.INSTANCE.css().featureDescription()); descriptionWithLink.add(exampleLink); - addFeatureWithDescription(titleString, free, premium, descriptionWithLink); + addFeatureWithDescription(titleString, free, premium, dataMining, descriptionWithLink); } - private void addFeatureWithDescription(final String titleString, boolean free, boolean premium, Widget description) { + private void addFeatureWithDescription(final String titleString, boolean free, boolean premium, boolean dataMining, Widget description) { // get size but index starts with 0 therefore row count is current index + 1 int currentRowIndex = featureGrid.getRowCount(); featureGrid.resizeRows(currentRowIndex + 1); @@ -117,23 +121,35 @@ public class SubscriptionCardContainer extends Composite { line.add(title); line.add(description); featureGrid.setWidget(currentRowIndex, 0, line); - if (free) { - createCheckMark(currentRowIndex, 1); - } - if (premium) { - createCheckMark(currentRowIndex, 2); - } + createCheckMark(currentRowIndex, 1, free); + createCheckMark(currentRowIndex, 2, premium); + createCheckMark(currentRowIndex, 3, dataMining); } - private void createCheckMark(int currentRowIndex, final int column) { + /** + * Paint a check icon or if state is false a X icon. + * + * @param currentRowIndex + * the current row + * @param column + * the column + * @param checkState + * the state (true: check, false: X) + */ + private void createCheckMark(int currentRowIndex, final int column, boolean checkState) { FlowPanel check = new FlowPanel(); - check.add(new Image(SharedDesktopResources.INSTANCE.dropdownCheck().getSafeUri())); - check.addStyleName(SubscriptionCardResources.INSTANCE.css().featureCheck()); + if (checkState) { + check.add(new Image(SharedDesktopResources.INSTANCE.dropdownCheck().getSafeUri())); + check.addStyleName(SubscriptionCardResources.INSTANCE.css().featureCheck()); + } else { + check.add(new Label("-")); + check.addStyleName(SubscriptionCardResources.INSTANCE.css().featureNone()); + } featureGrid.setWidget(currentRowIndex, column, check); } public void addSubscription(SubscriptionCard subscription) { - if (!isSubscriptionPlanExisting(subscription.getSubscriptionPlanDTO().getSubscriptionPlanId())) { + if (!isSubscriptionPlanExisting(subscription.getSubscriptionGroupDTO().getSubscriptionGroupId())) { container.add(subscription); } } @@ -143,7 +159,7 @@ public class SubscriptionCardContainer extends Composite { for (int i = 0; i < container.getWidgetCount(); i++) { if (container.getWidget(i) instanceof SubscriptionCard) { SubscriptionCard card = (SubscriptionCard)container.getWidget(i); - if (card.getSubscriptionPlanDTO().getSubscriptionPlanId().equals(planId)) { + if (card.getSubscriptionGroupDTO().getSubscriptionGroupId().equals(planId)) { isExisting = true; break; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardContainer.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardContainer.ui.xml index 358a81c7876..eb0bd42d920 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardContainer.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardContainer.ui.xml @@ -10,6 +10,9 @@ +

+ +

@@ -19,9 +22,6 @@
-

- -

diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardResources.java index ff963bff3b9..33ef70b9a0d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/subscription/SubscriptionCardResources.java @@ -17,7 +17,6 @@ public interface SubscriptionCardResources extends SharedHomeResources { String feature(); String highlight(); String owned(); - String individual(); String subscription(); String title(); String description(); @@ -41,6 +40,7 @@ public interface SubscriptionCardResources extends SharedHomeResources { String featureDescription(); String featureLink(); String featureCheck(); + String featureNone(); String featureHeader(); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/user/profile/subscriptionstab/UserSubscriptions.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/user/profile/subscriptionstab/UserSubscriptions.java index 2fdfb12877e..03540a28911 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/user/profile/subscriptionstab/UserSubscriptions.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/user/profile/subscriptionstab/UserSubscriptions.java @@ -60,6 +60,8 @@ public class UserSubscriptions extends Composite implements UserSubscriptionsVie @UiField Button subscribeButtonUi; + @UiField + Button selfServiceControlUi; @UiField(provided = true) SortedCellTable subscriptionsUi = new SortedCellTable<>(0, DesignedCellTableResources.INSTANCE); @@ -80,13 +82,20 @@ public class UserSubscriptions extends Composite implements UserSubscriptionsVie presenter.navigateToSubscribe(); } + @UiHandler("selfServiceControlUi") + void onSelfServiceControlClicked(final ClickEvent event) { + presenter.openSelfServicePortal(); + } + @Override public void updateView(final SubscriptionListDTO subscriptions) { subscribeButtonUi.setEnabled(true); if (subscriptions == null) { + selfServiceControlUi.setVisible(false); subscriptionsUi.setPageSize(0); subscriptionsUi.setList(new ArrayList()); } else { + selfServiceControlUi.setVisible(true); subscriptionsUi.setPageSize(subscriptions.getSubscriptionItems().length); subscriptionsUi.setList(Arrays.asList(subscriptions.getSubscriptionItems())); } @@ -129,7 +138,6 @@ public class UserSubscriptions extends Composite implements UserSubscriptionsVie @Override public void update(final int index, final SubscriptionDTO object, final String value) { if (!object.isCancelled()) { - // FIXME: Maybe integrate a confirmation dialog to avoid unintended canceling presenter.cancelSubscription(object.getSubscriptionPlanId(), object.getProvider()); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/user/profile/subscriptionstab/UserSubscriptions.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/user/profile/subscriptionstab/UserSubscriptions.ui.xml index 50a8905f243..d23bc9b7f17 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/user/profile/subscriptionstab/UserSubscriptions.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/user/profile/subscriptionstab/UserSubscriptions.ui.xml @@ -13,11 +13,16 @@ - - + + + + + + +
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html index 1a161a3d284..fae1317e80b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html @@ -26,6 +26,11 @@
  • Added a low-point scoring scheme variant that requires three wins in the medal series, including any wins from an optional carry-forward column in the medal series. This is a variant of the low-point scoring scheme that requires two wins in the medal series.
  • +
  • Data mining has become a subscribable option now. You can find it + in the top-level menu bar. Its two plans ("all" including live events, or "archive" which excludes + current live events) can also be tried for a short time for free.
  • +
  • Simulator and Data Mining are now always part of the top-level menu bar and can easily be subscribed + to from there.
  • "Sailing Loves Photography" images and videos for the Home.html landing page are now collected across ten years of events, not only one year.
  • diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/resources/SharedDesktopResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/resources/SharedDesktopResources.java index 183e4f0e3cb..b6c495a52b7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/resources/SharedDesktopResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/resources/SharedDesktopResources.java @@ -7,16 +7,16 @@ import com.sap.sailing.gwt.home.shared.SharedHomeResources; import com.google.gwt.resources.client.ImageResource; public interface SharedDesktopResources extends SharedHomeResources { - + public static final SharedDesktopResources INSTANCE = GWT.create(SharedDesktopResources.class); - + @Source("dropdown__check@2x.png") ImageResource dropdownCheck(); - + @Source("liveraces.svg") @MimeType("image/svg+xml") DataResource liveraces(); - + @Source("arrow-down-filled-black.png") ImageResource arrowDownFilledBlack(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/user/profile/subscriptions/UserSubscriptions.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/user/profile/subscriptions/UserSubscriptions.java index 1b53ce2399f..54be4b9859e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/user/profile/subscriptions/UserSubscriptions.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/user/profile/subscriptions/UserSubscriptions.java @@ -65,5 +65,10 @@ public class UserSubscriptions extends Composite implements UserSubscriptionsVie public void onSubscribeClicked(final ClickEvent event) { presenter.navigateToSubscribe(); } + + @UiHandler("selfServiceControlUi") + void onSelfServiceControlClicked(final ClickEvent event) { + presenter.openSelfServicePortal(); + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/user/profile/subscriptions/UserSubscriptions.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/user/profile/subscriptions/UserSubscriptions.ui.xml index fadabd67163..28a1957dfc0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/user/profile/subscriptions/UserSubscriptions.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/user/profile/subscriptions/UserSubscriptions.ui.xml @@ -16,5 +16,10 @@ + + + + + \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/header/HeaderConstants.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/header/HeaderConstants.java index d6de96efe3b..7e58b1c53a2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/header/HeaderConstants.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/header/HeaderConstants.java @@ -33,4 +33,9 @@ public interface HeaderConstants { * The name of the tab/window to open the strategy simulator UI in */ String STRATEGY_SIMULATOR_WINDOW = "strategyDimulatorWindow"; + + /** + * Default self target if not specified different. + */ + String SELF = "_self"; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/AbstractSubscriptionActivity.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/AbstractSubscriptionActivity.java index 45c67ff2cc5..845812472e2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/AbstractSubscriptionActivity.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/AbstractSubscriptionActivity.java @@ -2,6 +2,10 @@ package com.sap.sailing.gwt.home.shared.places.subscription; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import com.google.gwt.activity.shared.AbstractActivity; import com.google.gwt.event.shared.EventBus; @@ -11,6 +15,7 @@ import com.google.gwt.user.client.ui.AcceptsOneWidget; import com.sap.sailing.gwt.home.desktop.partials.subscription.SubscriptionCard.Type; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sse.security.shared.subscription.InvalidSubscriptionProviderException; +import com.sap.sse.security.shared.subscription.SubscriptionPlan.PlanGroup; import com.sap.sse.security.ui.authentication.AuthenticationContextEvent; import com.sap.sse.security.ui.authentication.app.AuthenticationContext; import com.sap.sse.security.ui.shared.subscription.SubscriptionPlanDTO; @@ -65,17 +70,40 @@ public abstract class AbstractSubscriptionActivity extends AbstractActivity impl public void onSuccess(final ArrayList result) { view.resetSubscriptions(); addFreePlan(view); + Map groupMap = new HashMap<>(); result.forEach(plan -> { + final Type type; if (checkIfUserIsOwnerOfThePlan(plan) || checkIfUserIsSubscribedToPlanCategory(plan)) { - view.addSubscriptionPlan(plan, Type.OWNER, eventBus); - } else if (checkIfUserWasAlreadySubscribedToOneTimePlan(plan)) { - view.addSubscriptionPlan(plan, Type.ONETIMELOCK, eventBus); - } else if (subscriptionsPlace.getPlansToHighlight() - .contains(plan.getSubscriptionPlanId())) { - view.addSubscriptionPlan(plan, Type.HIGHLIGHT, eventBus); + type = Type.OWNER; + } else if (subscriptionsPlace.getPlansToHighlight().contains(plan.getSubscriptionPlanId())) { + type = Type.HIGHLIGHT; } else { - view.addSubscriptionPlan(plan, Type.DEFAULT, eventBus); + type = Type.DEFAULT; } + final SubscriptionGroupDTO groupDTO; + if (groupMap.containsKey(plan.getGroup())) { + groupDTO = groupMap.get(plan.getGroup()); + plan.getPrices().forEach(price -> { + groupDTO.getPrices().add(price); + }); + } else { + groupDTO = new SubscriptionGroupDTO(plan.getGroup().getId(), + plan.isUserSubscribedToPlan(), plan.getPrices(), plan.getGroup(), + plan.isUserSubscribedToPlanCategory(), plan.getError(), type); + groupMap.put(plan.getGroup(), groupDTO); + } + }); + List groups = new ArrayList(groupMap.values()); + groups.sort(new Comparator() { + + @Override + public int compare(SubscriptionGroupDTO o1, SubscriptionGroupDTO o2) { + // comparing by ordinal + return o1.getGroup().ordinal() - o2.getGroup().ordinal(); + } + }); + groups.forEach(group -> { + view.addSubscriptionGroup(group, group.getType(), eventBus); }); } @@ -85,11 +113,11 @@ public abstract class AbstractSubscriptionActivity extends AbstractActivity impl } private void addFreePlan(final SubscriptionView view) { - final SubscriptionPlanDTO freePlan = new SubscriptionPlanDTO( + final SubscriptionGroupDTO freePlan = new SubscriptionGroupDTO( "free_subscription_plan" /* id */, /* isUserSubscribedToPlan */ false, - Collections.emptySet() /* prices */, /* planCategories */ Collections.emptySet(), - /* userWasAlreadySubscribedToOneTimePlan */ false, false, null /* error */); - view.addSubscriptionPlan(freePlan, Type.FREE, eventBus); + Collections.emptySet() /* prices */, /* group */ null, + /*isUserSubscribedToPlanCategory*/ false, null /* error */, Type.DEFAULT); + view.addSubscriptionGroup(freePlan, Type.FREE, eventBus); } }); } catch (final InvalidSubscriptionProviderException exc) { @@ -116,10 +144,6 @@ public abstract class AbstractSubscriptionActivity extends AbstractActivity impl return plan.isUserSubscribedToPlan(); } - private boolean checkIfUserWasAlreadySubscribedToOneTimePlan(final SubscriptionPlanDTO plan) { - return plan.isUserWasAlreadySubscribedToOneTimePlan(); - } - private boolean checkIfUserIsSubscribedToPlanCategory(final SubscriptionPlanDTO plan) { return plan.isUserSubscribedToPlanCategory(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/SubscriptionGroupDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/SubscriptionGroupDTO.java new file mode 100644 index 00000000000..e9faec56808 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/SubscriptionGroupDTO.java @@ -0,0 +1,87 @@ +package com.sap.sailing.gwt.home.shared.places.subscription; + +import java.util.HashSet; +import java.util.Set; + +import com.google.gwt.user.client.rpc.IsSerializable; +import com.sap.sailing.gwt.home.desktop.partials.subscription.SubscriptionCard; +import com.sap.sse.security.shared.subscription.SubscriptionPlan.PlanGroup; +import com.sap.sse.security.shared.subscription.SubscriptionPrice; + +/** + * A class to collect info from a bunch of SubscriptionPlanDTO which can be represented on one card. + */ +public class SubscriptionGroupDTO implements IsSerializable { + private static final long serialVersionUID = -3820239604039244557L; + + public static final String FEATURES_MESSAGE_KEY_SUFFX = "_features"; + public static final String NAME_MESSAGE_KEY_SUFFX = "_name"; + public static final String DESC_MESSAGE_KEY_SUFFX = "_description"; + public static final String INFO_MESSAGE_KEY_SUFFIX = "_info"; + public static final String PRICE_INFO_MESSAGE_KEY_SUFFIX = "_price_info"; + + private final String id; + private HashSet prices = new HashSet(); + private final Boolean isUserSubscribedToPlan; + private final PlanGroup group; + private final Boolean isUserSubscribedToGroup; + private final SubscriptionCard.Type type; + + public SubscriptionGroupDTO(String id, boolean isUserSubscribedToPlan, Set prices, + PlanGroup group, boolean isUserSubscribedToGroup, String error, SubscriptionCard.Type type) { + this.id = id; + this.isUserSubscribedToPlan = isUserSubscribedToPlan; + this.group = group; + this.isUserSubscribedToGroup = isUserSubscribedToGroup; + this.prices = new HashSet(prices); + this.type = type; + } + + public PlanGroup getGroup() { + return group; + } + + public static long getSerialversionuid() { + return serialVersionUID; + } + + public String getSubscriptionGroupId() { + return id; + } + + public boolean isUserSubscribedToPlan() { + return isUserSubscribedToPlan; + } + + public HashSet getPrices() { + return prices; + } + + public boolean isUserSubscribedToGroup() { + return isUserSubscribedToGroup; + } + + public SubscriptionCard.Type getType() { + return type; + } + + public String getSubscriptionGroupNameMessageKey() { + return id + NAME_MESSAGE_KEY_SUFFX; + } + + public String getSubscriptionGroupDescMessageKey() { + return id + DESC_MESSAGE_KEY_SUFFX; + } + + public String getSubscriptionGroupInfoMessageKey() { + return id + INFO_MESSAGE_KEY_SUFFIX; + } + + public String getSubscriptionGroupFeatureMessageKey() { + return id + FEATURES_MESSAGE_KEY_SUFFX; + } + + public String getSubscriptionGroupPriceInfoMessageKey() { + return id + PRICE_INFO_MESSAGE_KEY_SUFFIX; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/SubscriptionView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/SubscriptionView.java index e3cde542556..5e4d75de2a7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/SubscriptionView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/SubscriptionView.java @@ -5,11 +5,10 @@ import com.google.gwt.user.client.ui.IsWidget; import com.sap.sailing.gwt.home.desktop.partials.subscription.SubscriptionCard; import com.sap.sse.security.ui.authentication.app.AuthenticationContext; import com.sap.sse.security.ui.client.subscription.SubscribeView; -import com.sap.sse.security.ui.shared.subscription.SubscriptionPlanDTO; public interface SubscriptionView extends SubscribeView, IsWidget { - void addSubscriptionPlan(SubscriptionPlanDTO plan, final SubscriptionCard.Type type, final EventBus eventBus); + void addSubscriptionGroup(SubscriptionGroupDTO plan, final SubscriptionCard.Type type, final EventBus eventBus); void resetSubscriptions(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/SubscriptionViewImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/SubscriptionViewImpl.java index 78e2d300996..bd36b43be41 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/SubscriptionViewImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/subscription/SubscriptionViewImpl.java @@ -1,7 +1,6 @@ package com.sap.sailing.gwt.home.shared.places.subscription; import com.google.gwt.event.shared.EventBus; -import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Composite; import com.sap.sailing.gwt.home.desktop.partials.subscription.SubscriptionCard; import com.sap.sailing.gwt.home.desktop.partials.subscription.SubscriptionCardContainer; @@ -9,7 +8,6 @@ import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sse.gwt.client.Notification; import com.sap.sse.gwt.client.Notification.NotificationType; import com.sap.sse.security.ui.authentication.app.AuthenticationContext; -import com.sap.sse.security.ui.shared.subscription.SubscriptionPlanDTO; public class SubscriptionViewImpl extends Composite implements SubscriptionView { @@ -26,12 +24,12 @@ public class SubscriptionViewImpl extends Composite implements SubscriptionView } @Override - public void addSubscriptionPlan(final SubscriptionPlanDTO plan, final SubscriptionCard.Type type, + public void addSubscriptionGroup(final SubscriptionGroupDTO group, final SubscriptionCard.Type type, final EventBus eventBus) { switch (type) { case HIGHLIGHT: case DEFAULT: - container.addSubscription(new SubscriptionCard(plan, type, (price) -> { + container.addSubscription(new SubscriptionCard(group, type, (price) -> { final AuthenticationContext authenticationContext = presenter.getAuthenticationContext(); if (!authenticationContext.isLoggedIn()) { onOpenCheckoutError(StringMessages.INSTANCE.notLoggedIn()); @@ -47,20 +45,15 @@ public class SubscriptionViewImpl extends Composite implements SubscriptionView }, eventBus, presenter.getAuthenticationContext().isLoggedIn())); break; case OWNER: - container.addSubscription(new SubscriptionCard(plan, type, price -> presenter.manageSubscriptions(), + container.addSubscription(new SubscriptionCard(group, type, price -> presenter.manageSubscriptions(), eventBus, presenter.getAuthenticationContext().isLoggedIn())); break; case ONETIMELOCK: - container.addSubscription(new SubscriptionCard(plan, type, price -> {}, + container.addSubscription(new SubscriptionCard(group, type, price -> {}, eventBus, presenter.getAuthenticationContext().isLoggedIn())); break; - case INDIVIDUAL: - container.addSubscription( - new SubscriptionCard(plan, type, price -> Window.Location.assign("mailto:info@sapsailing.com"), - eventBus, presenter.getAuthenticationContext().isLoggedIn())); - break; case FREE: - container.addSubscription(new SubscriptionCard(plan, type, price -> presenter.toggleAuthenticationFlyout(), + container.addSubscription(new SubscriptionCard(group, type, price -> presenter.toggleAuthenticationFlyout(), eventBus, presenter.getAuthenticationContext().isLoggedIn())); break; default: diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/user/profile/subscriptions/UserSubscriptionsPresenter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/user/profile/subscriptions/UserSubscriptionsPresenter.java index ee73d509660..9b329f6fe6d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/user/profile/subscriptions/UserSubscriptionsPresenter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/user/profile/subscriptions/UserSubscriptionsPresenter.java @@ -1,5 +1,6 @@ package com.sap.sailing.gwt.home.shared.places.user.profile.subscriptions; +import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.sap.sailing.gwt.home.shared.app.PlaceNavigation; import com.sap.sailing.gwt.home.shared.places.subscription.SubscriptionPlace; @@ -81,6 +82,35 @@ public class UserSubscriptionsPresenter { + if (confirmed) { + try { + factory.getDefaultAsyncService() + .getSelfServicePortalSession(new AsyncCallback() { + @Override + public void onSuccess(final String result) { + if (result == null) { + showError(StringMessages.INSTANCE.failedFetchingSelfServicePortalSession()); + } else { + Window.open(result, "_blank", ""); + } + } + @Override + public void onFailure(final Throwable caught) { + showError(StringMessages.INSTANCE.failedFetchingSelfServicePortalSession()); + } + }); + } catch (final InvalidSubscriptionProviderException e) { + onInvalidSubscriptionProviderError(e); + } + } + }).center(); + } private void fetchSubscription() { try { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/user/profile/subscriptions/UserSubscriptionsView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/user/profile/subscriptions/UserSubscriptionsView.java index 0b6a6c41285..866214454d3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/user/profile/subscriptions/UserSubscriptionsView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/places/user/profile/subscriptions/UserSubscriptionsView.java @@ -25,5 +25,7 @@ public interface UserSubscriptionsView extends SubscriptionsView, IsWidget { void cancelSubscription(String planId, String providerName); void navigateToSubscribe(); + + void openSelfServicePortal(); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java index bef51ac8286..fce00ab7a1e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java @@ -23,6 +23,7 @@ import com.sap.sailing.gwt.ui.shared.RegattaOverviewEntryDTO; import com.sap.sse.gwt.client.Notification; import com.sap.sse.gwt.client.Notification.NotificationType; import com.sap.sse.gwt.settings.SettingsToUrlSerializer; +import com.sap.sse.security.ui.client.premium.PaywallResolver; public class RegattaOverviewEntryPoint extends AbstractSailingReadEntryPoint { @@ -52,7 +53,8 @@ public class RegattaOverviewEntryPoint extends AbstractSailingReadEntryPoint { clockLabel.addStyleName(style.clockLabel()); siteHeader = new SAPSailingHeaderWithAuthentication(); - new FixedSailingAuthentication(getUserService(), siteHeader.getAuthenticationMenuView()); + PaywallResolver paywallResolver = new PaywallResolver(getUserService(), getSubscriptionServiceFactory()); + new FixedSailingAuthentication(getUserService(), paywallResolver, siteHeader.getAuthenticationMenuView()); siteHeader.addWidgetToRightSide(clockLabel); containerPanel.addNorth(siteHeader, 75); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/places/AdminConsoleViewImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/places/AdminConsoleViewImpl.java index 612ca760540..fd150f61da2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/places/AdminConsoleViewImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/places/AdminConsoleViewImpl.java @@ -127,6 +127,7 @@ import com.sap.sse.security.ui.authentication.generic.GenericAuthorizedContentDe import com.sap.sse.security.ui.client.UserService; import com.sap.sse.security.ui.client.component.RoleDefinitionsPanel; import com.sap.sse.security.ui.client.component.UserGroupManagementPanel; +import com.sap.sse.security.ui.client.premium.PaywallResolver; import com.sap.sse.security.ui.client.usermanagement.UserManagementPanel; public class AdminConsoleViewImpl extends Composite implements AdminConsoleView { @@ -150,6 +151,7 @@ public class AdminConsoleViewImpl extends Composite implements AdminConsoleView private final StringMessages stringMessages = StringMessages.INSTANCE; private UserService userService; + private PaywallResolver paywallResolver; private ErrorReporter errorReporter; @@ -168,12 +170,13 @@ public class AdminConsoleViewImpl extends Composite implements AdminConsoleView this.userService = presenter.getUserService(); this.errorReporter = presenter.getErrorReporter(); this.placeController = presenter.getPlaceController(); + this.paywallResolver = new PaywallResolver(this.userService, presenter.getSubscriptionServiceFactory()); } @Override public HeaderPanel createUI(final ServerInfoDTO serverInfo) { SAPSailingHeaderWithAuthentication header = new SAPSailingHeaderWithAuthentication(stringMessages.administration()); - GenericAuthentication genericSailingAuthentication = new FixedSailingAuthentication(userService, header.getAuthenticationMenuView()); + GenericAuthentication genericSailingAuthentication = new FixedSailingAuthentication(userService, paywallResolver, header.getAuthenticationMenuView()); AuthorizedContentDecorator authorizedContentDecorator = new GenericAuthorizedContentDecorator(genericSailingAuthentication); authorizedContentDecorator.setContentWidgetFactory(() -> createAdminConsolePanel(serverInfo)); headerPanel.setHeaderWidget(header); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 2253de5801f..a943100c317 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -2407,4 +2407,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String errorSettingRaceToDefineItsOwnCompetitors(String leaderboardName, String raceColumnName, String fleetName, String message); String twdInDegrees(); String legDirectionInDegrees(); + String goToSelfServicePortalDialogTitle(); + String goToSelfServicePortalDialogText(); + String failedFetchingSelfServicePortalSession(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 68d246dc85f..62db373f9e1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -2387,7 +2387,7 @@ paymentStatus=Payment status paymentStatusSuccess=Success paymentStatusNoSuccess=No Success refunded=Refunded -cancelledAt=Cancelled at +cancelledAt=Active until nextBillingAt=Next billing at currencyValue={0,number,#0.00} {1} trialText=Your trial expires in {0} (ends on {1}) @@ -2443,4 +2443,7 @@ confirmCancelSubscriptionText=Do you really want to cancel your subscription?\n subscriptionOneTimePlanLockedText=This plan may only be aquired once. errorSettingRaceToDefineItsOwnCompetitors=Error setting race for fleet {2} in column {1} of leaderboard {0} to define its own competitors: {3} twdInDegrees=TWD (from) in degrees -legDirectionInDegrees=Leg direction in degrees \ No newline at end of file +legDirectionInDegrees=Leg direction in degrees +goToSelfServicePortalDialogTitle=Go to Self Service Portal. +goToSelfServicePortalDialogText=This will take you to the subscription providers Self-Service portal. +failedFetchingSelfServicePortalSession=Could not open the self service portal. \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index cfc97fb5a61..b3fa305b481 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -2396,7 +2396,7 @@ paused=Unterbrochen paymentStatusSuccess=Erfolg paymentStatusNoSuccess=Kein Erfolg refunded=Zurückerstattet -cancelledAt=Gekündigt am +cancelledAt=Aktiv bis nextBillingAt=Nächste Abrechnung am currencyValue={0,number,#0.00} {1} trialText=Die Versuchs-Periode endet in {0} (endet am {1}) @@ -2453,4 +2453,7 @@ confirmCancelSubscriptionText=Möchten Sie ihr Abonemment wirklich kündigen?\n subscriptionOneTimePlanLockedText=Dieser Plan ist nur einmalig buchbar. errorSettingRaceToDefineItsOwnCompetitors=Fehler beim Treffen der Festlegung, dass die Wettfahrt für Gruppe {2} in Spalte {1} der Rangliste {0} ihre eigenen Teilnehmer definiert: {3} twdInDegrees=TWD (aus) in Grad -legDirectionInDegrees=Bahnschenkel-Peilung in Grad \ No newline at end of file +legDirectionInDegrees=Bahnschenkel-Peilung in Grad +goToSelfServicePortalDialogTitle=Zum Self Service Portal. +goToSelfServicePortalDialogText=Self-Service Portal des Abonementanbieters in einem neuen Tab öffnen? +failedFetchingSelfServicePortalSession=Self-Service Portal konnte nicht geöffnet werden. \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java index a78347c1f0a..fdce0cfd49e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java @@ -55,6 +55,7 @@ import com.sap.sse.security.ui.authentication.decorator.WidgetFactory; import com.sap.sse.security.ui.authentication.generic.GenericAuthentication; import com.sap.sse.security.ui.authentication.generic.GenericAuthorizedContentDecorator; import com.sap.sse.security.ui.authentication.generic.sapheader.SAPHeaderWithAuthentication; +import com.sap.sse.security.ui.client.premium.PaywallResolver; public class DataMiningEntryPoint extends AbstractSailingReadEntryPoint { @@ -90,7 +91,8 @@ public class DataMiningEntryPoint extends AbstractSailingReadEntryPoint { private void createDataminingPanel(ServerInfoDTO serverInfo, final String queryIdentifier) { removeUrlParameter(); SAPHeaderWithAuthentication header = new SAPSailingHeaderWithAuthentication(getStringMessages().dataMining()); - GenericAuthentication genericSailingAuthentication = new FixedSailingAuthentication(getUserService(), + PaywallResolver paywallResolver = new PaywallResolver(getUserService(), getSubscriptionServiceFactory()); + GenericAuthentication genericSailingAuthentication = new FixedSailingAuthentication(getUserService(), paywallResolver, header.getAuthenticationMenuView()); AuthorizedContentDecorator authorizedContentDecorator = new GenericAuthorizedContentDecorator( genericSailingAuthentication); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java index bf1bc36c0c2..705baec6b8b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java @@ -251,7 +251,8 @@ public class LeaderboardEntryPoint extends AbstractSailingReadEntryPoint impleme leaderboardDisplayName = leaderboardName; } SAPSailingHeaderWithAuthentication header = new SAPSailingHeaderWithAuthentication(leaderboardDisplayName); - new FixedSailingAuthentication(getUserService(), header.getAuthenticationMenuView()); + PaywallResolver paywallResolver = new PaywallResolver(getUserService(), getSubscriptionServiceFactory()); + new FixedSailingAuthentication(getUserService(), paywallResolver, header.getAuthenticationMenuView()); mainPanel.addNorth(header, 75); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java index 69af5db70d2..f6048f4e880 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java @@ -85,8 +85,9 @@ public class LeaderboardEditPage extends AbstractSailingWriteEntryPoint implemen @Override public void onSuccess(Iterable result) { SAPHeaderWithAuthentication header = initHeader(); + PaywallResolver paywallResolver = new PaywallResolver(getUserService(), getSubscriptionServiceFactory()); GenericAuthentication genericSailingAuthentication = new FixedSailingAuthentication(getUserService(), - header.getAuthenticationMenuView()); + paywallResolver, header.getAuthenticationMenuView()); AuthorizedContentDecorator authorizedContentDecorator = new GenericAuthorizedContentDecorator( genericSailingAuthentication); getSailingService().getLeaderboardWithSecurity(leaderboardName, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/pairinglist/PairingListEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/pairinglist/PairingListEntryPoint.java index 618935b6361..7586c2e5353 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/pairinglist/PairingListEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/pairinglist/PairingListEntryPoint.java @@ -25,6 +25,7 @@ import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; import com.sap.sse.common.Color; import com.sap.sse.gwt.settings.SettingsToUrlSerializer; +import com.sap.sse.security.ui.client.premium.PaywallResolver; public class PairingListEntryPoint extends AbstractSailingReadEntryPoint implements ProvidesLeaderboardRouting { @@ -63,7 +64,8 @@ public class PairingListEntryPoint extends AbstractSailingReadEntryPoint impleme mainPanel.setHeight("100%"); SAPSailingHeaderWithAuthentication header = new SAPSailingHeaderWithAuthentication( pairingListContextDefinition.getLeaderboardName()); - new FixedSailingAuthentication(getUserService(), header.getAuthenticationMenuView()); + PaywallResolver paywallResolver = new PaywallResolver(getUserService(), getSubscriptionServiceFactory()); + new FixedSailingAuthentication(getUserService(), paywallResolver, header.getAuthenticationMenuView()); mainPanel.addNorth(header, 75); VerticalPanel contentPanel = new VerticalPanel(); contentPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java index 963f1138109..8f2d90af9e5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java @@ -155,7 +155,8 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingReadEntryP private void createErrorPage(String message) { final DockLayoutPanel vp = new DockLayoutPanel(Unit.PX); final SAPHeaderWithAuthentication header = new SAPSailingHeaderWithAuthentication(); - new FixedSailingAuthentication(getUserService(), header.getAuthenticationMenuView()); + PaywallResolver paywallResolver = new PaywallResolver(getUserService(), getSubscriptionServiceFactory()); + new FixedSailingAuthentication(getUserService(), paywallResolver, header.getAuthenticationMenuView()); RootLayoutPanel.get().add(vp); vp.addNorth(header, 100); final Label infoText = new Label(message); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index 1d18ec01773..1ce3780086d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -142,7 +142,8 @@ public class RaceBoardEntryPoint extends AbstractSailingReadEntryPoint implement private void createErrorPage(String message) { final DockLayoutPanel vp = new DockLayoutPanel(Unit.PX); final SAPHeaderWithAuthentication header = new SAPSailingHeaderWithAuthentication(); - new FixedSailingAuthentication(getUserService(), header.getAuthenticationMenuView()); + PaywallResolver paywallResolver = new PaywallResolver(getUserService(), getSubscriptionServiceFactory()); + new FixedSailingAuthentication(getUserService(), paywallResolver, header.getAuthenticationMenuView()); RootLayoutPanel.get().add(vp); vp.addNorth(header, 100); final Label infoText = new Label(message); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index f10ec42772a..92b46c886af 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -288,7 +288,9 @@ public class RaceBoardPanel } } } - this.userManagementMenuView = new AuthenticationMenuViewImpl(new Anchor(), mainCss.usermanagement_loggedin(), mainCss.usermanagement_open()); + + this.userManagementMenuView = new AuthenticationMenuViewImpl(new Anchor(), mainCss.usermanagement_loggedin(), + mainCss.usermanagement_open(), mainCss.user_menu_premium()); this.userManagementMenuView.asWidget().setStyleName(mainCss.usermanagement_icon()); timeRangeWithZoomModel = new TimeRangeWithZoomModel(); final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(selectedRaceIdentifier, competitorsAndTheirBoats); @@ -557,7 +559,7 @@ public class RaceBoardPanel for (Pair, Action> componentAndAction : componentsForSideBySideViewer) { addChildComponent(componentAndAction.getA()); } - this.setupUserManagementControlPanel(userService); + this.setupUserManagementControlPanel(userService, paywallResolver); mainPanel.add(mapViewer.getViewerWidget()); boolean showLeaderboard = initialPerspectiveOwnSettings.isShowLeaderboard() && isScreenLargeEnoughToInitiallyDisplayLeaderboard; @@ -594,10 +596,10 @@ public class RaceBoardPanel racetimePanel.setBarOverlays(overlays); } - private void setupUserManagementControlPanel(UserService userService) { + private void setupUserManagementControlPanel(UserService userService, PaywallResolver paywallResolver) { mainCss.ensureInjected(); final FlyoutAuthenticationView display = new RaceBoardAuthenticationView(); - final GenericAuthentication genericAuthentication = new GenericAuthentication(userService, userManagementMenuView, display, + final GenericAuthentication genericAuthentication = new GenericAuthentication(userService, paywallResolver, userManagementMenuView, display, SailingAuthenticationEntryPointLinkFactory.INSTANCE, raceBoardResources); new RaceBoardLoginHintPopup(genericAuthentication.getAuthenticationManager()); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardResources.java index d59a7e30f18..62a93f97952 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardResources.java @@ -7,25 +7,21 @@ import com.sap.sse.security.ui.authentication.generic.resource.AuthenticationRes public interface RaceBoardResources extends CommonSharedResources, AuthenticationResources { public static final RaceBoardResources INSTANCE = GWT.create(RaceBoardResources.class); - - @Source({CommonSharedResources.RESET, CommonSharedResources.MAIN, "raceboard-main.gss"}) - RaceBoardMainCss mainCss(); - - @Source({CommonSharedResources.MEDIA, "raceboard-media.gss"}) - RaceBoardMediaCss mediaCss(); - - + @Source({ CommonSharedResources.RESET, CommonSharedResources.MAIN, "raceboard-main.gss" }) + RaceBoardMainCss mainCss(); + + @Source({ CommonSharedResources.MEDIA, "raceboard-media.gss" }) + RaceBoardMediaCss mediaCss(); + public interface RaceBoardMainCss extends CommonMainCss { String button(); String buttonprimary(); String buttonprimaryoutlined(); String buttonarrowrightwhite(); - String mainsection(); String mainsection_header(); String mainsection_header_title(); - String usermanagement_icon(); String usermanagement_loggedin(); String usermanagement_view(); @@ -33,9 +29,9 @@ public interface RaceBoardResources extends CommonSharedResources, Authenticatio String usermanagement_view_content(); String usermanagement_mobile(); String usermanagement_open(); + String user_menu_premium(); } public interface RaceBoardMediaCss extends CommonMediaCss { } - } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/raceboard-main.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/raceboard-main.gss index b7fc684a8a4..6e0ba8e848a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/raceboard-main.gss +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/raceboard-main.gss @@ -46,3 +46,17 @@ .usermanagement_mobile .usermanagement_view_content { width: 100%; } +.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; +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java index e129c1f27fe..d98cb201d83 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java @@ -29,6 +29,7 @@ import com.sap.sse.security.ui.authentication.decorator.AuthorizedContentDecorat import com.sap.sse.security.ui.authentication.decorator.WidgetFactory; import com.sap.sse.security.ui.authentication.generic.GenericAuthentication; import com.sap.sse.security.ui.authentication.generic.GenericAuthorizedContentDecorator; +import com.sap.sse.security.ui.client.premium.PaywallResolver; public class SimulatorEntryPoint extends AbstractSailingReadEntryPoint { @@ -192,7 +193,8 @@ public class SimulatorEntryPoint extends AbstractSailingReadEntryPoint { private void createSimulatorPanel(ServerInfoDTO serverInfo) { SAPSailingHeaderWithAuthentication header = new SAPSailingHeaderWithAuthentication(getStringMessages().strategySimulatorTitle()); - GenericAuthentication genericSailingAuthentication = new FixedSailingAuthentication(getUserService(), header.getAuthenticationMenuView()); + PaywallResolver paywallResolver = new PaywallResolver(getUserService(), getSubscriptionServiceFactory()); + GenericAuthentication genericSailingAuthentication = new FixedSailingAuthentication(getUserService(), paywallResolver, header.getAuthenticationMenuView()); AuthorizedContentDecorator authorizedContentDecorator = new GenericAuthorizedContentDecorator(genericSailingAuthentication); final String serverName = serverInfo.getName(); authorizedContentDecorator.setPermissionToCheck(NamedSecuredObjectDTO.create(serverName, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java index f839538f8b4..5452eaed54f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java @@ -16,6 +16,7 @@ import com.sap.sailing.gwt.ui.client.AbstractSailingReadEntryPoint; import com.sap.sailing.gwt.ui.client.shared.panels.SimpleWelcomeWidget; import com.sap.sse.gwt.settings.SettingsToUrlSerializer; import com.sap.sse.gwt.shared.ClientConfiguration; +import com.sap.sse.security.ui.client.premium.PaywallResolver; /** * @@ -80,7 +81,8 @@ public class SpectatorEntryPoint extends AbstractSailingReadEntryPoint { private SAPSailingHeaderWithAuthentication getHeader(String title) { SAPSailingHeaderWithAuthentication header = new SAPSailingHeaderWithAuthentication(title); - new FixedSailingAuthentication(getUserService(), header.getAuthenticationMenuView()); + PaywallResolver paywallResolver = new PaywallResolver(getUserService(), getSubscriptionServiceFactory()); + new FixedSailingAuthentication(getUserService(), paywallResolver, header.getAuthenticationMenuView()); header.getElement().getStyle().setPosition(Position.FIXED); header.getElement().getStyle().setTop(0, Unit.PX); header.getElement().getStyle().setWidth(100, Unit.PCT); diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java index 3a24e2bc330..a3eba6ed3e0 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java @@ -78,6 +78,8 @@ import com.sap.sse.security.shared.HasPermissions.DefaultActions; import com.sap.sse.security.shared.HasPermissionsProvider; import com.sap.sse.security.shared.RoleDefinition; import com.sap.sse.security.shared.SubscriptionPlanProvider; +import com.sap.sse.security.shared.subscription.AllDataMiningRole; +import com.sap.sse.security.shared.subscription.ArchiveDataMiningRole; import com.sap.sse.security.util.GenericJSONPreferenceConverter; import com.sap.sse.util.ClearStateTestSupport; import com.sap.sse.util.ServiceTrackerFactory; @@ -266,6 +268,8 @@ public class Activator implements BundleActivator { // TODO: Registering SubscriptionPlan specific RoleDefinitions here requires additional maintenance. Consider // implementing another Construct like OSGIHasPermissionsProvider securityService.getOrCreateRoleDefinitionFromPrototype(PremiumRole.getInstance()); + securityService.getOrCreateRoleDefinitionFromPrototype(ArchiveDataMiningRole.getInstance()); + securityService.getOrCreateRoleDefinitionFromPrototype(AllDataMiningRole.getInstance()); }, null)); final TrackedRaceStatisticsCache trackedRaceStatisticsCache = new TrackedRaceStatisticsCacheImpl(); registrations.add(context.registerService(TrackedRaceStatisticsCache.class.getName(), diff --git a/java/com.sap.sse.gwt.test/.options b/java/com.sap.sse.gwt.test/.options new file mode 100644 index 00000000000..2cd960c7a59 --- /dev/null +++ b/java/com.sap.sse.gwt.test/.options @@ -0,0 +1,80 @@ +#### Debugging options for org.eclipse.osgi + +# Turn on general debugging for org.eclipse.osgi +org.eclipse.osgi/debug=false +# Prints out location debug information +org.eclipse.osgi/debug/location = false +# Prints out class loading debug information +org.eclipse.osgi/debug/loader=false +# Prints out event (FrameworkEvent/BundleEvent/ServiceEvent) and listener debug information +org.eclipse.osgi/debug/events=false +# Prints out OSGi service debug information (registration/getting/ungetting etc.) +org.eclipse.osgi/debug/services=false +# Prints out bundle manifest parsing debug information +org.eclipse.osgi/debug/manifest=false +# Prints out LDAP filter debug information +org.eclipse.osgi/debug/filter=false +# Prints out security (PermissionAdmin service) debug information +org.eclipse.osgi/debug/security=false +# Prints out start level service debug information +org.eclipse.osgi/debug/startlevel=false +# Prints out package admin service debug information +org.eclipse.osgi/debug/packageadmin=false +# Prints out timing information for loading and calling BundleActivator.start +org.eclipse.osgi/debug/bundleTime=false +# Prints out total time for starting a bundle which includes: +# - loading and calling BundleActivator.start +# - other time spent resolving bundles and firing bundle events to listeners +org.eclipse.osgi/debug/bundleStartTime=false +# Debug the loading of message bundles +org.eclipse.osgi/debug/messageBundles=false +# Debug the object pool additions +org.eclipse.osgi/debug/objectPool/adds=false +# Debug the object pool duplications +org.eclipse.osgi/debug/objectPool/dups=false +# Debug the caching of bundle headers +org.eclipse.osgi/debug/cachedmanifest = false +# Debug the lifecycle operations of the framework/systemBundle +org.eclipse.osgi/debug/systemBundle=false +# Debug the framework persistent storage +org.eclipse.osgi/debug/storage=false +# Debug the bundle file reading +org.eclipse.osgi/debug/bundleFile=false +# Debug the bundle file open calls +org.eclipse.osgi/debug/bundleFile/open=true +# Debug the bundle file close calls +org.eclipse.osgi/debug/bundleFile/close=true + +# Eclipse adaptor options +org.eclipse.osgi/eclipseadaptor/debug = false +org.eclipse.osgi/eclipseadaptor/debug/platformadmin=false +org.eclipse.osgi/eclipseadaptor/debug/platformadmin/resolver=false +org.eclipse.osgi/eclipseadaptor/converter/debug = false + +### OSGi resolver options +# General debug option for the resolver. Enabling this option is equivalent to +# enabling all other resolver debug options. +org.eclipse.osgi/resolver = false +# Displays the current root bundle which is being resolved +org.eclipse.osgi/resolver/roots=false +# Displays information about matching capabilities returned to the resolver and +# why any were filtered out. +org.eclipse.osgi/resolver/providers = false +# Displays information about capabilities or resources that were filtered by +# resolver hooks. +org.eclipse.osgi/resolver/hooks = false +# Displays information about uses constraint violations. +org.eclipse.osgi/resolver/uses = false +# Displays wiring information after the resolve process completes. +org.eclipse.osgi/resolver/wiring = false +# Displays the resolution report information. +org.eclipse.osgi/resolver/report = false + + +#### Monitoring settings + +# monitor eager bundle activation +org.eclipse.osgi/monitor/activation=false +# monitor lazy bundle activation +org.eclipse.osgi/monitor/lazy=false + diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/AllDataMiningRole.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/AllDataMiningRole.java new file mode 100644 index 00000000000..33ca887b06d --- /dev/null +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/AllDataMiningRole.java @@ -0,0 +1,30 @@ +package com.sap.sse.security.shared.subscription; + +import java.util.UUID; + +import com.sap.sse.security.shared.RolePrototype; +import com.sap.sse.security.shared.WildcardPermission; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; + +/** + * Specifies a role that when associated to a user gives access to the streamlet Feature on the RaceMap + */ +public class AllDataMiningRole extends RolePrototype { + private static final long serialVersionUID = 6671501683896115261L; + private static final UUID ROLE_ID = UUID.fromString("de4205b5-ccf9-49b2-91e1-9a41b4db166b"); + private static final AllDataMiningRole INSTANCE = new AllDataMiningRole(); + + AllDataMiningRole() { + super("all_data_mining", ROLE_ID.toString(), + WildcardPermission.builder().withTypes(SecuredSecurityTypes.SERVER) + .withActions(SecuredSecurityTypes.ServerActions.DATA_MINING).build()); + } + + public static AllDataMiningRole getInstance() { + return INSTANCE; + } + + public static UUID getRoleId() { + return ROLE_ID; + } +} diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/ArchiveDataMiningRole.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/ArchiveDataMiningRole.java new file mode 100644 index 00000000000..b0f888129b2 --- /dev/null +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/ArchiveDataMiningRole.java @@ -0,0 +1,30 @@ +package com.sap.sse.security.shared.subscription; + +import java.util.UUID; + +import com.sap.sse.security.shared.RolePrototype; +import com.sap.sse.security.shared.WildcardPermission; +import com.sap.sse.security.shared.impl.SecuredSecurityTypes; + +/** + * Specifies a role that when associated to a user gives access to the streamlet Feature on the RaceMap + */ +public class ArchiveDataMiningRole extends RolePrototype { + private static final long serialVersionUID = 6671501683896115261L; + private static final UUID ROLE_ID = UUID.fromString("f2993a7a-c08d-11ec-9d64-0242ac120002"); + private static final ArchiveDataMiningRole INSTANCE = new ArchiveDataMiningRole(); + + ArchiveDataMiningRole() { + super("archive_data_mining", ROLE_ID.toString(), + WildcardPermission.builder().withTypes(SecuredSecurityTypes.SERVER) + .withActions(SecuredSecurityTypes.ServerActions.DATA_MINING).withIds("ARCHIVE").build()); + } + + public static ArchiveDataMiningRole getInstance() { + return INSTANCE; + } + + public static UUID getRoleId() { + return ROLE_ID; + } +} diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SSESubscriptionPlan.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SSESubscriptionPlan.java index ecc72284d52..c258ead6222 100644 --- a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SSESubscriptionPlan.java +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SSESubscriptionPlan.java @@ -21,8 +21,8 @@ public class SSESubscriptionPlan extends SubscriptionPlan { private static final Map plansById = new HashMap(); private SSESubscriptionPlan(String id, HashSet prices, Set planCategories, - Boolean isOneTimePlan, SubscriptionPlanRole... roles) { - super(id, prices, planCategories, isOneTimePlan, roles); + Boolean isOneTimePlan, PlanGroup group, SubscriptionPlanRole... roles) { + super(id, prices, planCategories, isOneTimePlan, group, roles); plansById.put(id, this); } diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SubscriptionPlan.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SubscriptionPlan.java index 03e62cd4417..9562a3f92a0 100644 --- a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SubscriptionPlan.java +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SubscriptionPlan.java @@ -22,6 +22,7 @@ public abstract class SubscriptionPlan implements Serializable { private final Set prices; private final Set planCategory; private final Boolean isOneTimePlan; + private final PlanGroup group; /** * Roles assigned for this plan, if user subscribe to the plan then the user will be assigned these roles */ @@ -31,16 +32,39 @@ public abstract class SubscriptionPlan implements Serializable { * Used to make Plans of the same category mutually exclusive. */ public enum PlanCategory { - PREMIUM; + PREMIUM("premium"), DATA_MINING("data_mining"); + final String id; + PlanCategory(String id) { + this.id = id; + } + public String getId() { + return id; + } + } + + /** + * Used to organize plans in groups to show on one subscription card. + * The order of this enumeration is usually also the main order on the UI. + */ + public enum PlanGroup { + PREMIUM("premium"), DATA_MINING_ARCHIVE("data_mining_archive"), DATA_MINING_ALL("data_mining_all"); + final String id; + PlanGroup(String id) { + this.id = id; + } + public String getId() { + return id; + } } protected SubscriptionPlan(String id, Set prices, Set planCategory, - Boolean isOneTimePlan, SubscriptionPlanRole[] roles) { + Boolean isOneTimePlan, PlanGroup group, SubscriptionPlanRole[] roles) { this.id = id; this.roles = roles; this.prices = prices; this.planCategory = planCategory; this.isOneTimePlan = isOneTimePlan; + this.group = group; } public String getId() { @@ -62,6 +86,10 @@ public abstract class SubscriptionPlan implements Serializable { public Boolean getIsOneTimePlan() { return isOneTimePlan; } + + public PlanGroup getGroup() { + return group; + } public boolean isUserInPossessionOfRoles(User user) { boolean foundAll = true; diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SubscriptionPrice.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SubscriptionPrice.java index 0b7d2929cfa..ad064561cae 100644 --- a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SubscriptionPrice.java +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/subscription/SubscriptionPrice.java @@ -3,31 +3,36 @@ package com.sap.sse.security.shared.subscription; import java.io.Serializable; import java.math.BigDecimal; -public class SubscriptionPrice implements Serializable{ +public class SubscriptionPrice implements Serializable { private static final long serialVersionUID = -1448071130685381256L; private String priceId; private BigDecimal price; private PaymentInterval paymentInterval; private String currencyCode; + private Boolean disablePlan; + private Boolean isOneTimePayment; /* * For GWT Serialization */ @Deprecated - public SubscriptionPrice() {} - - public SubscriptionPrice(String priceId, BigDecimal price, String currencyCode, PaymentInterval paymentInterval) { + public SubscriptionPrice() { + } + + public SubscriptionPrice(String priceId, BigDecimal price, String currencyCode, PaymentInterval paymentInterval, + boolean isOneTimePayment) { super(); this.priceId = priceId; this.price = price; this.currencyCode = currencyCode; this.paymentInterval = paymentInterval; + this.isOneTimePayment = isOneTimePayment; } - + public String getCurrencyCode() { return currencyCode; } - + public void setCurrencyCode(String currencyCode) { this.currencyCode = currencyCode; } @@ -47,7 +52,7 @@ public class SubscriptionPrice implements Serializable{ public void setPaymentInterval(PaymentInterval paymentInterval) { this.paymentInterval = paymentInterval; } - + public String getPriceId() { return priceId; } @@ -56,10 +61,21 @@ public class SubscriptionPrice implements Serializable{ this.priceId = priceId; } + public Boolean getDisablePlan() { + return disablePlan; + } + + public void setDisablePlan(Boolean disablePlan) { + this.disablePlan = disablePlan; + } + + public Boolean getIsOneTimePayment() { + return isOneTimePayment; + } + public enum PaymentInterval { YEAR, MONTH, WEEK, DAY } - @Override public String toString() { diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/AuthenticationManagerImpl.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/AuthenticationManagerImpl.java index 142198ffe56..6456cc81835 100755 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/AuthenticationManagerImpl.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/AuthenticationManagerImpl.java @@ -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 diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/app/AuthenticationContext.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/app/AuthenticationContext.java index c29e2f0abe8..44865216800 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/app/AuthenticationContext.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/app/AuthenticationContext.java @@ -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(); } diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/app/AuthenticationContextImpl.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/app/AuthenticationContextImpl.java index 3a3622b373b..2ebfa7d1bc9 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/app/AuthenticationContextImpl.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/app/AuthenticationContextImpl.java @@ -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(), /* 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; + } } diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/GenericAuthentication.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/GenericAuthentication.java index 692863c1824..7daa2184118 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/GenericAuthentication.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/GenericAuthentication.java @@ -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); diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/SAPHeaderWithAuthentication.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/SAPHeaderWithAuthentication.java index 1e8ac052b8a..e68d29ab9c6 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/SAPHeaderWithAuthentication.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/SAPHeaderWithAuthentication.java @@ -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; diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/SAPHeaderWithAuthenticationResources.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/SAPHeaderWithAuthenticationResources.java index 0a2f96993e1..75982dae67d 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/SAPHeaderWithAuthenticationResources.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/SAPHeaderWithAuthenticationResources.java @@ -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(); } } \ No newline at end of file diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/header-with-authentication.gss b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/header-with-authentication.gss index 5c5b58c42ae..d68357a6c08 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/header-with-authentication.gss +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/generic/sapheader/header-with-authentication.gss @@ -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; +} diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/AuthenticationMenuView.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/AuthenticationMenuView.java index 3948e57f42d..977d61792bd 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/AuthenticationMenuView.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/AuthenticationMenuView.java @@ -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 { * true if there is an authenticated user, false otherwise */ void setAuthenticated(boolean authenticated); - + /** * Sets whether or not the {@link FlyoutAuthenticationView} is open/shown. * @@ -30,7 +30,15 @@ public interface AuthenticationMenuView extends IsWidget { * true the {@link FlyoutAuthenticationView} is open, false 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. */ diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/AuthenticationMenuViewImpl.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/AuthenticationMenuViewImpl.java index 403019c175c..8383bbc124a 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/AuthenticationMenuViewImpl.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/AuthenticationMenuViewImpl.java @@ -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)); diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/FlyoutAuthenticationPresenter.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/FlyoutAuthenticationPresenter.java index 48f8b9298da..24d9ffe06cd 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/FlyoutAuthenticationPresenter.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/authentication/view/FlyoutAuthenticationPresenter.java @@ -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() { diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.java index 8c828fc31fd..933d6fa6ce1 100755 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.java @@ -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); diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.properties b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.properties index 0e2e6a00ce5..930d867fa47 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.properties +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages.properties @@ -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. \ No newline at end of file +paymentFinished=Payment successful. Plan roles have been granted. \ No newline at end of file diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_de.properties b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_de.properties index 65925b69175..595ae8181b9 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_de.properties +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/StringMessages_de.properties @@ -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. \ No newline at end of file +paymentFinished=Zahlung erfolgreich. Planrollen wurden verliehen. \ No newline at end of file diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants.java index 8c6e4bcb556..97b23c65b02 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants.java @@ -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(); } diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants.properties b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants.properties index 358fc7f9d1f..25fbe64b575 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants.properties +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants.properties @@ -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) diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants_de.properties b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants_de.properties index 4b80dac3db9..5c103a1701f 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants_de.properties +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/subscription/SubscriptionStringConstants_de.properties @@ -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) diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/premium/PremiumCheckBox.ui.xml b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/premium/PremiumCheckBox.ui.xml index ca60de41177..e366a013c5a 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/premium/PremiumCheckBox.ui.xml +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/premium/PremiumCheckBox.ui.xml @@ -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; diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/subscription/SubscriptionService.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/subscription/SubscriptionService.java index 0079f60e618..f972375edc2 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/subscription/SubscriptionService.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/subscription/SubscriptionService.java @@ -25,5 +25,6 @@ public interface SubscriptionService extends RemoteService { SubscriptionPlanDTO getSubscriptionPlanDTOById(String planId); boolean isUserInPossessionOfRoles(String planId) throws UserManagementException; - + + String getSelfServicePortalSession(); } diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/subscription/SubscriptionServiceAsync.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/subscription/SubscriptionServiceAsync.java index 253e8b0101e..bb5e2f69353 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/subscription/SubscriptionServiceAsync.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/subscription/SubscriptionServiceAsync.java @@ -18,5 +18,7 @@ public interface SubscriptionServiceAsync { public void getSubscriptionPlanDTOById(String planId, AsyncCallback callback); void isUserInPossessionOfRoles(String planId, AsyncCallback callback); + + void getSelfServicePortalSession(AsyncCallback accessLink); } diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/subscription/SubscriptionServiceImpl.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/subscription/SubscriptionServiceImpl.java index 4b8b285890a..ca51a9407ae 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/subscription/SubscriptionServiceImpl.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/subscription/SubscriptionServiceImpl.java @@ -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 getUnlockingSubscriptionplans(WildcardPermission permission) throws UserManagementException { final ArrayList 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 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 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) { diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/subscription/chargebee/ChargebeeSubscriptionServiceImpl.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/subscription/chargebee/ChargebeeSubscriptionServiceImpl.java index 1eced09cfdf..cec79b90c94 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/subscription/chargebee/ChargebeeSubscriptionServiceImpl.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/subscription/chargebee/ChargebeeSubscriptionServiceImpl.java @@ -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 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; + } + } } diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/shared/subscription/HasSubscriptionMessageKeys.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/shared/subscription/HasSubscriptionMessageKeys.java index 79f5d185ab6..2b42d224f84 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/shared/subscription/HasSubscriptionMessageKeys.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/shared/subscription/HasSubscriptionMessageKeys.java @@ -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() { diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/shared/subscription/SubscriptionPlanDTO.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/shared/subscription/SubscriptionPlanDTO.java index f96a7c354dd..e4c06410f0f 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/shared/subscription/SubscriptionPlanDTO.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/shared/subscription/SubscriptionPlanDTO.java @@ -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; 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 prices, Set 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(prices); this.error = error; + this.group = group; + } + + public PlanGroup getGroup() { + return group; } public Set getPlanCategory() { diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/subscription/SubscriptionApiService.java b/java/com.sap.sse.security/src/com/sap/sse/security/subscription/SubscriptionApiService.java index 09edb5b8f4a..37d9476dc76 100644 --- a/java/com.sap.sse.security/src/com/sap/sse/security/subscription/SubscriptionApiService.java +++ b/java/com.sap.sse.security/src/com/sap/sse/security/subscription/SubscriptionApiService.java @@ -3,6 +3,7 @@ package com.sap.sse.security.subscription; import java.util.List; import java.util.Map; +import com.chargebee.models.PortalSession; import com.sap.sse.security.shared.subscription.Subscription; /** @@ -18,6 +19,10 @@ public interface SubscriptionApiService extends SubscriptionApiBaseService { void onCancelResult(SubscriptionCancelResult cancelResult); } + public static interface OnSelfServicePortalSessionResultListener{ + void onSessionResult(PortalSession sessionResult); + } + /** * Fetch user subscriptions from payment service provider. The logic of fetching subscriptions should be done in * background. @@ -41,4 +46,6 @@ public interface SubscriptionApiService extends SubscriptionApiBaseService { } SubscriptionDataHandler getDataHandler(); + + void getUserSelfServicePortalSession(String userId, OnSelfServicePortalSessionResultListener listener); } diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/subscription/chargebee/ChargeBeeGetSelfServicePortalSessionTask.java b/java/com.sap.sse.security/src/com/sap/sse/security/subscription/chargebee/ChargeBeeGetSelfServicePortalSessionTask.java new file mode 100644 index 00000000000..0c7bf6f85c4 --- /dev/null +++ b/java/com.sap.sse.security/src/com/sap/sse/security/subscription/chargebee/ChargeBeeGetSelfServicePortalSessionTask.java @@ -0,0 +1,45 @@ +package com.sap.sse.security.subscription.chargebee; + +import java.util.logging.Logger; + +import com.chargebee.models.PortalSession; +import com.sap.sse.security.subscription.SubscriptionApiBaseService; +import com.sap.sse.security.subscription.SubscriptionApiRequestProcessor; + +/** + * Task to perform canceling a subscription by subscription id + */ +public class ChargeBeeGetSelfServicePortalSessionTask implements ChargebeeSelfServicePortalSessionRequest.OnResultListener { + private static final Logger logger = Logger.getLogger(ChargeBeeGetSelfServicePortalSessionTask.class.getName()); + + /** + * Cancel result listener + */ + @FunctionalInterface + public static interface OnResultListener { + void onSessionResult(PortalSession result); + } + + private final String userId; + private final SubscriptionApiRequestProcessor requestProcessor; + private final OnResultListener listener; + private final SubscriptionApiBaseService chargebeeApiServiceParams; + + public ChargeBeeGetSelfServicePortalSessionTask(String userId, SubscriptionApiRequestProcessor requestProcessor, + OnResultListener listener, SubscriptionApiBaseService chargebeeApiServiceParams) { + this.userId = userId; + this.requestProcessor = requestProcessor; + this.listener = listener; + this.chargebeeApiServiceParams = chargebeeApiServiceParams; + } + + public void run() { + logger.info(() -> "Schedule fetch of self service portal session, user id: " + userId); + requestProcessor.addRequest(new ChargebeeSelfServicePortalSessionRequest(userId, this, requestProcessor, chargebeeApiServiceParams)); + } + + @Override + public void onSessionResult(PortalSession session) { + listener.onSessionResult(session); + } +} diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/subscription/chargebee/ChargebeeApiService.java b/java/com.sap.sse.security/src/com/sap/sse/security/subscription/chargebee/ChargebeeApiService.java index b44be877bc1..e3e922970cd 100644 --- a/java/com.sap.sse.security/src/com/sap/sse/security/subscription/chargebee/ChargebeeApiService.java +++ b/java/com.sap.sse.security/src/com/sap/sse/security/subscription/chargebee/ChargebeeApiService.java @@ -70,6 +70,12 @@ public class ChargebeeApiService implements SubscriptionApiService { new ChargebeeCancelSubscriptionTask(subscriptionId, requestProcessor, result -> listener.onCancelResult(result), this) .run(); } + + @Override + public void getUserSelfServicePortalSession(String userId, OnSelfServicePortalSessionResultListener listener) { + new ChargeBeeGetSelfServicePortalSessionTask(userId, requestProcessor, result -> listener.onSessionResult(result), this) + .run(); + } @Override public SubscriptionDataHandler getDataHandler() { diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/subscription/chargebee/ChargebeeSelfServicePortalSessionRequest.java b/java/com.sap.sse.security/src/com/sap/sse/security/subscription/chargebee/ChargebeeSelfServicePortalSessionRequest.java new file mode 100644 index 00000000000..8d0274d5880 --- /dev/null +++ b/java/com.sap.sse.security/src/com/sap/sse/security/subscription/chargebee/ChargebeeSelfServicePortalSessionRequest.java @@ -0,0 +1,63 @@ +package com.sap.sse.security.subscription.chargebee; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.chargebee.Result; +import com.chargebee.models.PortalSession; +import com.chargebee.models.PortalSession.CreateRequest; +import com.sap.sse.security.subscription.SubscriptionApiBaseService; +import com.sap.sse.security.subscription.SubscriptionApiRequestProcessor; + +/** + * Request to fetch Chargebee subscription by subscription id + */ +public class ChargebeeSelfServicePortalSessionRequest extends ChargebeeApiRequest { + private static final Logger logger = Logger.getLogger(ChargebeeSelfServicePortalSessionRequest.class.getName()); + + public static interface OnResultListener { + void onSessionResult(PortalSession session); + } + + private final String userId; + private final OnResultListener listener; + + public ChargebeeSelfServicePortalSessionRequest(String userId, OnResultListener listener, + SubscriptionApiRequestProcessor requestProcessor, SubscriptionApiBaseService chargebeeApiServiceParams) { + super(requestProcessor, chargebeeApiServiceParams); + this.userId = userId; + this.listener = listener; + } + + @Override + protected ChargebeeInternalApiRequestWrapper createRequest() { + logger.info(() -> "Fetch Chargebee Self-Service Portal session, user id: " + userId); + CreateRequest request = PortalSession.create() + .customerId(userId); + return new ChargebeeInternalApiRequestWrapper(request); + } + + @Override + protected void processResult(ChargebeeInternalApiRequestWrapper request) { + Result result = request.getResult(); + final PortalSession session; + if (result != null) { + session = result.portalSession(); + } else { + session = null; + } + onDone(session); + } + + @Override + protected void handleError(Exception e) { + logger.log(Level.SEVERE, "Fetch Chargebee Self-Service Portal session failed, user id: " + userId, e); + onDone(null); + } + + private void onDone(PortalSession session) { + if (listener != null) { + listener.onSessionResult(session); + } + } +} diff --git a/java/pom.xml b/java/pom.xml index 926de3efddf..2eb53234b79 100755 --- a/java/pom.xml +++ b/java/pom.xml @@ -520,6 +520,7 @@ **/*$* ${reportsdirectory} + true diff --git a/wiki/howto/development/ci.md b/wiki/howto/development/ci.md index 0b084a2e725..b6f39085ef6 100644 --- a/wiki/howto/development/ci.md +++ b/wiki/howto/development/ci.md @@ -62,7 +62,7 @@ The basic idea of setting up a build job is to create a so-called "free-style so In order to elastically scale our build / CI infrastructure, we use AWS to provide Hudson build slaves on demand. The Hudson Master (https://hudson.sapsailing.com) has a script obtained from our git at ``./configuration/launchhudsonslave`` which takes an Amazon Machine Image (AMI), launches it in our default region (eu-west-1) and connects to it. The AWS credentials are stored in the ``root`` account on ``hudson.sapsailing.com``, and the ``hudson`` user is granted access to the script by means of an ``/etc/sudoers.d`` entry. -The image has been crafted specifically to contain the tools required for the build. In order to set up such an image based on Ubuntu, consider running the following commands as root (see also https://tecadmin.net/setup-selenium-chromedriver-on-ubuntu/), on a fresh Ubuntu 20.04 instance with a 100GB root partition, starting as the "ubuntu" user: +The image has been crafted specifically to contain the tools required for the build. In order to set up such an image based on Ubuntu, consider running the following commands as root on a fresh Ubuntu 20.04 instance with a 100GB root partition, starting as the "ubuntu" user: ``` scp -o StrictHostKeyChecking=false trac@sapsailing.com:/home/wiki/gitwiki/configuration/imageupgrade_functions.sh /tmp @@ -79,16 +79,12 @@ The image has been crafted specifically to contain the tools required for the bu apt-get install -y unzip xvfb libxi6 libgconf-2-4 nfs-common curl -sS -o - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add - - echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list echo "deb https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" >/etc/apt/sources.list.d/mongodb-org-4.4.list apt-get -y update apt-get -y upgrade - apt-get -y install google-chrome-stable maven rabbitmq-server mongodb-org fwupd linux-aws linux-headers-aws linux-image-aws docker.io + apt-get -y install maven rabbitmq-server mongodb-org firefox firefox-geckodriver fwupd linux-aws linux-headers-aws linux-image-aws docker.io apt-get -y autoremove cd /tmp - wget https://chromedriver.storage.googleapis.com/2.41/chromedriver_linux64.zip - cd /usr/bin - unzip /tmp/chromedriver_linux64.zip mv /tmp/imageupgrade /usr/local/bin mv /tmp/imageupgrade_functions.sh /usr/local/bin mv /tmp/mounthudsonworkspace /usr/local/bin diff --git a/wiki/howto/onboarding.md b/wiki/howto/onboarding.md index ffaa904d253..91ec4b8039d 100644 --- a/wiki/howto/onboarding.md +++ b/wiki/howto/onboarding.md @@ -85,7 +85,8 @@ The primary Git repository for the project is hosted on sapsailing.com. It is mi 1. Get the content of the git repository * Generate SSH Keys with "ssh-keygen -t rsa -C "" " command in Cygwin Terminal (Not with Putty!!!) - * Clone the repository to your local file system from `ssh://[SAP-User]@git.wdf.sap.corp:29418/SAPSail/sapsailingcapture.git` or `ssh://[user]@sapsailing.com/home/trac/git` User "trac" has all public ssh keys. + * Clone the repository to your local file system from `ssh://[SAP-User]@git.wdf.sap.corp:29418/SAPSail/sapsailingcapture.git` or `ssh://trac@sapsailing.com/home/trac/git` User "trac" has all public ssh keys. + To Clone the repository use the command: `git clone` after directing into a prior initialized repository. * Please note that when using one of the newer versions of Cygwin, your Cygwin home folder setting might differ from your Windows home folder. This will likely lead to problems when issuing certain commands. For troubleshooting, take a look at the following thread: [https://stackoverflow.com/questions/1494658/how-can-i-change-my-cygwin-home-folder-after-installation](https://stackoverflow.com/questions/1494658/how-can-i-change-my-cygwin-home-folder-after-installation) 2. Check out the 'master' branch from the git repository. The 'master' branch is the main development branch. Please check that you start your work on this branch. diff --git a/wiki/info/misc/subscriptions.md b/wiki/info/misc/subscriptions.md index 43bedddc85e..bf6bf5b7963 100644 --- a/wiki/info/misc/subscriptions.md +++ b/wiki/info/misc/subscriptions.md @@ -14,21 +14,23 @@ Additionally there is a detailed list of all features which are included in a pa com.sap.sailing.domain.common.subscription.SailingSubscriptionPlan A plan definition includes the plan and price IDs and a list of connected roles. + +3. Group the plan by defining a (SubscriptionPlan.)PlanGroup and set the group to init of SailingSubscriptionPlan -3. Add following texts to class (and related property files) +4. Add following texts to class (and related property files) com.sap.sse.security.ui.client.i18n.subscription.SubscriptionStringConstants - - `_name` + - `_name` - Name or title of the plan (`

    `) - - `_description` + - `_description` - A short description of the plan content (`

    `) - - `_info` + - `_info` - Can be uses for additional info (`
    `) - - `_price_info` - - It's a red rubber stamp over the price. It will only be rendered if not empty text is existing. - - `_features` - - Comma separated list of feature labels + - `_price_info` + - It's a red rubber stamp over the price. It will only be rendered if price is a one time payment. + - `_features` + - Pipe (|) separated list of feature labels ### Business Model Info