Merge pull request #7995 from opf/feature/31935/ee-activation
[31935] WIP: Enterprise activation frontend [ci skip]pull/8264/head
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 143 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 24 KiB |
@ -0,0 +1,73 @@ |
||||
# Info boxes |
||||
|
||||
## Simple info boxes |
||||
``` |
||||
<div class="info-boxes"> |
||||
<h2 class="info-boxes--title">Heading</h2> |
||||
<div class="info-boxes--container"> |
||||
<div class="info-boxes--item"> |
||||
<h3 class="info-boxes--item-title">Box 1</h3> |
||||
<div class="info-boxes--item-content"> |
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> |
||||
</div> |
||||
</div> |
||||
<div class="info-boxes--item"> |
||||
<h3 class="info-boxes--item-title">Box 2</h3> |
||||
<div class="info-boxes--item-content"> |
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> |
||||
</div> |
||||
</div> |
||||
<div class="info-boxes--item"> |
||||
<h3 class="info-boxes--item-title">Box 3</h3> |
||||
<div class="info-boxes--item-content"> |
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
``` |
||||
|
||||
## Centered with image and links |
||||
``` |
||||
<div class="info-boxes -centered"> |
||||
<h2 class="info-boxes--title">Heading</h2> |
||||
<div class="info-boxes--container"> |
||||
<div class="info-boxes--item"> |
||||
<img src="https://via.placeholder.com/250x200" class="info-boxes--teaser-image"> |
||||
<h3 class="info-boxes--item-title">Box 1</h3> |
||||
<div class="info-boxes--item-content"> |
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> |
||||
<ul class="widget-box--arrow-links"> |
||||
<li> |
||||
<a href="" target="_blank">Learn more</a> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
<div class="info-boxes--item"> |
||||
<img src="https://via.placeholder.com/250x200" class="info-boxes--teaser-image"> |
||||
<h3 class="info-boxes--item-title">Box 2</h3> |
||||
<div class="info-boxes--item-content"> |
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> |
||||
<ul class="widget-box--arrow-links"> |
||||
<li> |
||||
<a href="" target="_blank">Learn more</a> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
<div class="info-boxes--item"> |
||||
<img src="https://via.placeholder.com/250x200" class="info-boxes--teaser-image"> |
||||
<h3 class="info-boxes--item-title">Box 3</h3> |
||||
<div class="info-boxes--item-content"> |
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> |
||||
<ul class="widget-box--arrow-links"> |
||||
<li> |
||||
<a href="" target="_blank">Learn more</a> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
``` |
@ -0,0 +1,62 @@ |
||||
//-- copyright |
||||
// OpenProject is an open source project management software. |
||||
// Copyright (C) 2012-2020 the OpenProject GmbH |
||||
// |
||||
// This program is free software; you can redistribute it and/or |
||||
// modify it under the terms of the GNU General Public License version 3. |
||||
// |
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
// Copyright (C) 2006-2017 Jean-Philippe Lang |
||||
// Copyright (C) 2010-2013 the ChiliProject Team |
||||
// |
||||
// This program is free software; you can redistribute it and/or |
||||
// modify it under the terms of the GNU General Public License |
||||
// as published by the Free Software Foundation; either version 2 |
||||
// of the License, or (at your option) any later version. |
||||
// |
||||
// This program is distributed in the hope that it will be useful, |
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
// GNU General Public License for more details. |
||||
// |
||||
// You should have received a copy of the GNU General Public License |
||||
// along with this program; if not, write to the Free Software |
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
// |
||||
// See docs/COPYRIGHT.rdoc for more details. |
||||
//++ |
||||
|
||||
.info-boxes |
||||
width: 100% |
||||
max-width: 1140px |
||||
|
||||
.info-boxes--title, |
||||
.info-boxes--item-title |
||||
margin: 20px auto |
||||
font-weight: bold |
||||
|
||||
&.-centered |
||||
margin: auto |
||||
padding: 0 15px |
||||
.info-boxes--title |
||||
text-align: center |
||||
|
||||
.info-boxes--container |
||||
display: grid |
||||
grid-gap: 20px |
||||
grid-template-columns: repeat(auto-fit,minmax(200px,1fr)) |
||||
|
||||
.info-boxes--item |
||||
.info-boxes--teaser-image |
||||
display: block |
||||
margin: auto |
||||
max-width: 150px |
||||
|
||||
.info-boxes--item-title |
||||
white-space: nowrap |
||||
border-bottom: none |
||||
|
||||
.info-boxes--item-content |
||||
.widget-box--arrow-links |
||||
text-transform: uppercase |
||||
font-size: .875rem |
@ -0,0 +1,21 @@ |
||||
module EnterpriseTrialHelper |
||||
def augur_content_security_policy |
||||
append_content_security_policy_directives( |
||||
connect_src: [OpenProject::Configuration.enterprise_trial_creation_host] |
||||
) |
||||
end |
||||
|
||||
def chargebee_content_security_policy |
||||
append_content_security_policy_directives( |
||||
script_src: %w(js.chargebee.com), |
||||
style_src: %w(js.chargebee.com openproject-enterprise-test.chargebee.com), |
||||
frame_src: %w(js.chargebee.com openproject-enterprise-test.chargebee.com) |
||||
) |
||||
end |
||||
|
||||
def youtube_content_security_policy |
||||
append_content_security_policy_directives( |
||||
frame_src: %w(https://www.youtube.com) |
||||
) |
||||
end |
||||
end |
@ -0,0 +1,41 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
require_dependency 'token/base' |
||||
|
||||
module Token |
||||
class EnterpriseTrialKey < Base |
||||
include ExpirableToken |
||||
|
||||
def self.validity_time |
||||
1.days |
||||
end |
||||
end |
||||
end |
@ -1,28 +1,122 @@ |
||||
<div class="notification-box upsale-notification"> |
||||
<div class="notification-box--content"> |
||||
<h3><%= t('admin.enterprise.upgrade_to_ee') %></h3> |
||||
<%= image_tag "enterprise_edition.png", class: "widget-box--teaser-image" %> |
||||
|
||||
<p><%= t('homescreen.blocks.upsale.description') %></p> |
||||
|
||||
<ul class=""> |
||||
<li> |
||||
<%= t('homescreen.blocks.upsale.additional_features') %> |
||||
</li> |
||||
<li> |
||||
<%= t('homescreen.blocks.upsale.professional_support') %> |
||||
</li> |
||||
</ul> |
||||
<p> |
||||
<b><%= t('homescreen.blocks.upsale.become_hero') %></b> <%= t('homescreen.blocks.upsale.you_contribute') %> |
||||
</p> |
||||
<%= link_to( "#{OpenProject::Static::Links.links[:upsale][:href]}/?utm_source=unknown&utm_medium=community-edition&utm_campaign=enterprise-admin", |
||||
{ class: 'button -alt-highlight', |
||||
target: '_blank', |
||||
aria: {label: t('admin.enterprise.order')}, |
||||
title: t('admin.enterprise.order')}) do %> |
||||
<%= op_icon('button--icon icon-add') %> |
||||
<span class="button--text"><%= t('admin.enterprise.order') %></span> |
||||
<% end %> |
||||
<%#-- copyright |
||||
OpenProject is an open source project management software. |
||||
Copyright (C) 2012-2020 the OpenProject GmbH |
||||
|
||||
This program is free software; you can redistribute it and/or |
||||
modify it under the terms of the GNU General Public License version 3. |
||||
|
||||
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
Copyright (C) 2006-2017 Jean-Philippe Lang |
||||
Copyright (C) 2010-2013 the ChiliProject Team |
||||
|
||||
This program is free software; you can redistribute it and/or |
||||
modify it under the terms of the GNU General Public License |
||||
as published by the Free Software Foundation; either version 2 |
||||
of the License, or (at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program; if not, write to the Free Software |
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
|
||||
See docs/COPYRIGHT.rdoc for more details. |
||||
|
||||
++#%> |
||||
<% content_for :header_tags do %> |
||||
<script src="<%= OpenProject::Static::Links.links[:chargebee][:href] %>" |
||||
data-cb-site="openproject-enterprise-test"> |
||||
</script> |
||||
<% end %> |
||||
|
||||
<enterprise-base></enterprise-base> |
||||
|
||||
<div class='upsale--actions'> |
||||
<a href="#" |
||||
class="button -highlight" |
||||
data-cb-type="checkout" |
||||
data-cb-plan-id="enterprise-edition---annual-user-license"> |
||||
<%= t('admin.enterprise.book_now') %> |
||||
</a> |
||||
|
||||
<% quote_link = OpenProject::Static::Links.links.fetch :upsale_get_quote %> |
||||
<%= link_to t(quote_link[:label]), |
||||
quote_link[:href], |
||||
target: '_blank', |
||||
class: 'button -highlight'%> |
||||
|
||||
<%= static_link_to :contact %> |
||||
</div> |
||||
|
||||
<p> |
||||
<b><%= t('js.admin.enterprise.upsale.confidence') %></b> |
||||
</p> |
||||
|
||||
<div class="info-boxes upsale-benefits"> |
||||
<h3 class="info-boxes--title -no-border"><%= t('js.admin.enterprise.upsale.benefits.description') %></h3> |
||||
<div class="info-boxes--container"> |
||||
<div class="info-boxes--item"> |
||||
<%= image_tag "installation_alerts.svg", |
||||
class: "info-boxes--teaser-image", |
||||
title: t('js.admin.enterprise.upsale.benefits.installation'), |
||||
alt: t('js.admin.enterprise.upsale.benefits.installation') %> |
||||
<h4 class="info-boxes--item-title"><%= t('js.admin.enterprise.upsale.benefits.installation') %></h4> |
||||
<div class="info-boxes--item-content"> |
||||
<p><%= t('js.admin.enterprise.upsale.benefits.installation_text') %></p> |
||||
<ul class="widget-box--arrow-links"> |
||||
<li> |
||||
<%= static_link_to :upsale_benefits_installation %> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
<div class="info-boxes--item"> |
||||
<%= image_tag "system_maintenance.jpg", |
||||
class: "info-boxes--teaser-image", |
||||
title: t('js.admin.enterprise.upsale.benefits.professional_support'), |
||||
alt: t('js.admin.enterprise.upsale.benefits.professional_support') %> |
||||
<h4 class="info-boxes--item-title"><%= t('js.admin.enterprise.upsale.benefits.professional_support') %></h4> |
||||
<div class="info-boxes--item-content"> |
||||
<p><%= t('js.admin.enterprise.upsale.benefits.professional_support_text') %></p> |
||||
<ul class="widget-box--arrow-links"> |
||||
<li> |
||||
<%= static_link_to :upsale_benefits_support %> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
<div class="info-boxes--item"> |
||||
<%= image_tag "premium_features.svg", |
||||
class: "info-boxes--teaser-image", |
||||
title: t('js.admin.enterprise.upsale.benefits.premium_features'), |
||||
alt: t('js.admin.enterprise.upsale.benefits.premium_features') %> |
||||
<h4 class="info-boxes--item-title"><%= t('js.admin.enterprise.upsale.benefits.premium_features') %></h4> |
||||
<div class="info-boxes--item-content"> |
||||
<p><%= t('js.admin.enterprise.upsale.benefits.premium_features_text') %></p> |
||||
<ul class="widget-box--arrow-links"> |
||||
<li> |
||||
<%= static_link_to :upsale_benefits_features %> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
<div class="info-boxes--item"> |
||||
<%= image_tag "security_alerts.jpg", |
||||
class: "info-boxes--teaser-image", |
||||
title: t('js.admin.enterprise.upsale.benefits.high_security'), |
||||
alt: t('js.admin.enterprise.upsale.benefits.high_security') %> |
||||
<h4 class="info-boxes--item-title"><%= t('js.admin.enterprise.upsale.benefits.high_security') %></h4> |
||||
<div class="info-boxes--item-content"> |
||||
<p><%= t('js.admin.enterprise.upsale.benefits.high_security_text') %></p> |
||||
<ul class="widget-box--arrow-links"> |
||||
<li> |
||||
<%= static_link_to :upsale_benefits_security %> |
||||
</li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
@ -1,28 +1,31 @@ |
||||
<%= render 'homescreen/blocks/header', title: t('homescreen.blocks.upsale.title') %> |
||||
|
||||
<%= image_tag "enterprise_edition.png", class: "widget-box--teaser-image" %> |
||||
|
||||
<p><%= t('homescreen.blocks.upsale.description') %></p> |
||||
|
||||
<ul class="widget-box--feature-list"> |
||||
<li> |
||||
<%= t('homescreen.blocks.upsale.additional_features') %> |
||||
</li> |
||||
<li> |
||||
<%= t('homescreen.blocks.upsale.professional_support') %> |
||||
</li> |
||||
</ul> |
||||
<p> |
||||
<b><%= t('homescreen.blocks.upsale.become_hero') %></b> <%= t('homescreen.blocks.upsale.you_contribute') %> |
||||
</p> |
||||
<div class="widget-box--description"> |
||||
<%= image_tag "enterprise_edition.png", class: "widget-box--teaser-image" %> |
||||
<p> |
||||
<%= t('js.admin.enterprise.upsale.text') %> |
||||
</p> |
||||
</div> |
||||
|
||||
<div class="widget-box--blocks--buttons"> |
||||
<% if current_user.admin? %> |
||||
<%= link_to t('js.admin.enterprise.upsale.button_start_trial'), enterprise_path, class: 'button -alt-highlight' %> |
||||
<% end %> |
||||
|
||||
<%= link_to "#{OpenProject::Static::Links.links[:upsale][:href]}/?utm_source=unknown&utm_medium=community-edition&utm_campaign=home-screen", |
||||
{ class: 'button -alt-highlight', |
||||
{ class: 'button -highlight', |
||||
aria: {label: t('homescreen.blocks.upsale.more_info')}, |
||||
target: '_blank', |
||||
title: t('homescreen.blocks.upsale.more_info')} do %> |
||||
<%= op_icon('button--icon icon-info2') %> |
||||
<span class="button--text"><%= t('homescreen.blocks.upsale.more_info') %></span> |
||||
<%= op_icon('button--icon icon-external-link') %> |
||||
<% end %> |
||||
</div> |
||||
|
||||
<span><b><%= t('js.admin.enterprise.upsale.become_hero') %></b></span> |
||||
|
||||
<p><%= t('js.admin.enterprise.upsale.you_contribute') %></p> |
||||
|
||||
<p> |
||||
<b><%= t('js.admin.enterprise.upsale.confidence') %></b> |
||||
</p> |
||||
|
@ -0,0 +1,53 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2020 the OpenProject GmbH
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See docs/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import {Component, ElementRef} from "@angular/core"; |
||||
import {I18nService} from "app/modules/common/i18n/i18n.service"; |
||||
import {EEActiveTrialBase} from "core-components/enterprise/enterprise-active-trial/ee-active-trial.base"; |
||||
|
||||
export const enterpriseActiveSavedTrialSelector = 'enterprise-active-saved-trial'; |
||||
|
||||
@Component({ |
||||
selector: enterpriseActiveSavedTrialSelector, |
||||
templateUrl: './ee-active-trial.component.html', |
||||
styleUrls: ['./ee-active-trial.component.sass'] |
||||
}) |
||||
export class EEActiveSavedTrialComponent extends EEActiveTrialBase { |
||||
public subscriber = this.elementRef.nativeElement.dataset['subscriber']; |
||||
public email = this.elementRef.nativeElement.dataset['email']; |
||||
public userCount = this.elementRef.nativeElement.dataset['userCount']; |
||||
public startsAt = this.elementRef.nativeElement.dataset['startsAt']; |
||||
public expiresAt = this.elementRef.nativeElement.dataset['expiresAt']; |
||||
public company:string; |
||||
public domain:string; |
||||
|
||||
constructor(readonly elementRef:ElementRef, |
||||
readonly I18n:I18nService) { |
||||
super(I18n); |
||||
} |
||||
} |
@ -0,0 +1,46 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2020 the OpenProject GmbH
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See docs/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import {UntilDestroyedMixin} from "core-app/helpers/angular/until-destroyed.mixin"; |
||||
import {I18nService} from "app/modules/common/i18n/i18n.service"; |
||||
|
||||
export class EEActiveTrialBase extends UntilDestroyedMixin { |
||||
public text = { |
||||
label_email: this.I18n.t('js.admin.enterprise.trial.form.label_email'), |
||||
label_expires_at: this.I18n.t('js.admin.enterprise.trial.form.label_expires_at'), |
||||
label_maximum_users: this.I18n.t('js.admin.enterprise.trial.form.label_maximum_users'), |
||||
label_company: this.I18n.t('js.admin.enterprise.trial.form.label_company'), |
||||
label_domain: this.I18n.t('js.admin.enterprise.trial.form.label_domain'), |
||||
label_starts_at: this.I18n.t('js.admin.enterprise.trial.form.label_starts_at'), |
||||
label_subscriber: this.I18n.t('js.admin.enterprise.trial.form.label_subscriber') |
||||
}; |
||||
|
||||
constructor(readonly I18n:I18nService) { |
||||
super(); |
||||
} |
||||
} |
@ -0,0 +1,49 @@ |
||||
<div class="enterprise--active-token"> |
||||
<div class="attributes-group"> |
||||
<div class="attributes-key-value"> |
||||
<div class="attributes-key-value--key">{{ text.label_subscriber }}</div> |
||||
<div class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value -text"> |
||||
<span>{{ subscriber }}</span> |
||||
</div> |
||||
</div> |
||||
<div class="attributes-key-value--key">{{ text.label_email }}</div> |
||||
<div class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value -text"> |
||||
<span>{{ email }}</span> |
||||
</div> |
||||
</div> |
||||
<div *ngIf="company" class="attributes-key-value--key">{{ text.label_company }}</div> |
||||
<div *ngIf="company" class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value -text"> |
||||
<span>{{ company }}</span> |
||||
</div> |
||||
</div> |
||||
<div *ngIf="domain" class="attributes-key-value--key">{{ text.label_domain }}</div> |
||||
<div *ngIf="domain" class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value -text"> |
||||
<span>{{ domain }}</span> |
||||
</div> |
||||
</div> |
||||
<div *ngIf="userCount" class="attributes-key-value--key">{{ text.label_maximum_users }}</div> |
||||
<div *ngIf="userCount" class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value -text"> |
||||
<span>{{ userCount }}</span> |
||||
</div> |
||||
</div> |
||||
<div *ngIf="startsAt" class="attributes-key-value--key">{{ text.label_starts_at }}</div> |
||||
<div *ngIf="startsAt" class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value -text"> |
||||
<span>{{ startsAt }}</span> |
||||
</div> |
||||
</div> |
||||
<!-- only show if token expires --> |
||||
<div *ngIf="expiresAt" class="attributes-key-value--key">{{ text.label_expires_at }}</div> |
||||
<div *ngIf="expiresAt" class="attributes-key-value--value-container"> |
||||
<div class="attributes-key-value--value -text"> |
||||
<span>{{ expiresAt }}</span> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
@ -0,0 +1,2 @@ |
||||
.attributes-group |
||||
margin-bottom: 1.25rem |
@ -0,0 +1,109 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2020 the OpenProject GmbH
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See docs/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import {ChangeDetectorRef, Component, ElementRef, OnInit} from "@angular/core"; |
||||
import {distinctUntilChanged} from "rxjs/operators"; |
||||
import {I18nService} from "app/modules/common/i18n/i18n.service"; |
||||
import {EnterpriseTrialService} from "app/components/enterprise/enterprise-trial.service"; |
||||
import {HttpClient, HttpErrorResponse} from "@angular/common/http"; |
||||
import {EEActiveTrialBase} from "core-components/enterprise/enterprise-active-trial/ee-active-trial.base"; |
||||
import {GonService} from "core-app/modules/common/gon/gon.service"; |
||||
|
||||
@Component({ |
||||
selector: 'enterprise-active-trial', |
||||
templateUrl: './ee-active-trial.component.html', |
||||
styleUrls: ['./ee-active-trial.component.sass'] |
||||
}) |
||||
export class EEActiveTrialComponent extends EEActiveTrialBase implements OnInit { |
||||
public subscriber:string; |
||||
public email:string; |
||||
public userCount:string; |
||||
public startsAt:string; |
||||
public expiresAt:string; |
||||
public company:string; |
||||
public domain:string; |
||||
|
||||
constructor(readonly elementRef:ElementRef, |
||||
readonly cdRef:ChangeDetectorRef, |
||||
readonly I18n:I18nService, |
||||
readonly http:HttpClient, |
||||
readonly Gon:GonService, |
||||
public eeTrialService:EnterpriseTrialService) { |
||||
super(I18n); |
||||
} |
||||
|
||||
ngOnInit() { |
||||
if (!this.subscriber) { |
||||
this.eeTrialService.userData$ |
||||
.pipe( |
||||
distinctUntilChanged(), |
||||
this.untilDestroyed() |
||||
) |
||||
.subscribe(userForm => { |
||||
this.formatUserData(userForm); |
||||
this.cdRef.detectChanges(); |
||||
}); |
||||
|
||||
this.initialize(); |
||||
} |
||||
} |
||||
|
||||
private initialize():void { |
||||
let eeTrialKey = this.Gon.get('ee_trial_key') as any; |
||||
|
||||
if (eeTrialKey && !this.eeTrialService.userData) { |
||||
// after reload: get data from Augur using the trial key saved in gon
|
||||
this.eeTrialService.trialLink = this.eeTrialService.baseUrlAugur + '/public/v1/trials/' + eeTrialKey.value; |
||||
this.getUserDataFromAugur(); |
||||
} |
||||
} |
||||
|
||||
// use the trial key saved in the db
|
||||
// to get the user data from Augur
|
||||
private getUserDataFromAugur() { |
||||
this.http |
||||
.get<any>(this.eeTrialService.trialLink + '/details') |
||||
.toPromise() |
||||
.then((userForm:any) => { |
||||
this.formatUserData(userForm); |
||||
this.eeTrialService.retryConfirmation(); |
||||
}) |
||||
.catch((error:HttpErrorResponse) => { |
||||
// Check whether the mail has been confirmed by now
|
||||
this.eeTrialService.getToken(); |
||||
}); |
||||
} |
||||
|
||||
private formatUserData(userForm:any) { |
||||
this.subscriber = userForm.first_name + ' ' + userForm.last_name; |
||||
this.email = userForm.email; |
||||
this.company = userForm.company; |
||||
this.domain = userForm.domain; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,23 @@ |
||||
<div *ngIf="noTrialRequested; else alreadyRequested"> |
||||
<p> |
||||
<b>{{ text.text }}</b> |
||||
</p> |
||||
<p> |
||||
<b>{{ text.become_hero }}</b><br> |
||||
{{ text.you_contribute }} |
||||
</p> |
||||
<button class="button -alt-highlight" (click)="openTrialModal()"> |
||||
{{ text.button_trial }} |
||||
</button> |
||||
</div> |
||||
|
||||
<ng-template #alreadyRequested> |
||||
<enterprise-trial-waiting></enterprise-trial-waiting> |
||||
<p class="confirmation-hint">{{ text.email_not_received }} |
||||
<a (click)="openTrialModal()">{{ text.try_another_email }}</a><br> |
||||
{{ text.try_another_email_hint }} |
||||
</p> |
||||
</ng-template> |
||||
|
||||
|
||||
|
@ -0,0 +1,7 @@ |
||||
.button |
||||
float: left |
||||
|
||||
.confirmation-hint |
||||
font-size: 0.9em |
||||
font-style: italic |
||||
margin-bottom: 2rem |
@ -0,0 +1,72 @@ |
||||
// -- copyright
|
||||
// OpenProject is a project management system.
|
||||
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See doc/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import {Component, Injector} from "@angular/core"; |
||||
import {I18nService} from "core-app/modules/common/i18n/i18n.service"; |
||||
import {DynamicBootstrapper} from "core-app/globals/dynamic-bootstrapper"; |
||||
import {EnterpriseTrialModal} from "core-components/enterprise/enterprise-modal/enterprise-trial.modal"; |
||||
import {OpModalService} from "core-components/op-modals/op-modal.service"; |
||||
import {EnterpriseTrialService} from "core-components/enterprise/enterprise-trial.service"; |
||||
|
||||
export const enterpriseBaseSelector = 'enterprise-base'; |
||||
|
||||
@Component({ |
||||
selector: enterpriseBaseSelector, |
||||
templateUrl: './enterprise-base.component.html', |
||||
styleUrls: ['./enterprise-base.component.sass'] |
||||
}) |
||||
export class EnterpriseBaseComponent { |
||||
public text = { |
||||
button_trial: this.I18n.t('js.admin.enterprise.upsale.button_start_trial'), |
||||
button_book: this.I18n.t('js.admin.enterprise.upsale.button_book_now'), |
||||
link_quote: this.I18n.t('js.admin.enterprise.upsale.link_quote'), |
||||
become_hero: this.I18n.t('js.admin.enterprise.upsale.become_hero'), |
||||
you_contribute: this.I18n.t('js.admin.enterprise.upsale.you_contribute'), |
||||
email_not_received: this.I18n.t('js.admin.enterprise.trial.email_not_received'), |
||||
text: this.I18n.t('js.admin.enterprise.upsale.text'), |
||||
try_another_email: this.I18n.t('js.admin.enterprise.trial.try_another_email'), |
||||
try_another_email_hint: this.I18n.t('js.admin.enterprise.trial.try_another_email_hint') |
||||
}; |
||||
|
||||
constructor(protected I18n:I18nService, |
||||
protected opModalService:OpModalService, |
||||
readonly injector:Injector, |
||||
public eeTrialService:EnterpriseTrialService) { |
||||
} |
||||
|
||||
public openTrialModal() { |
||||
// cancel request and open first modal window
|
||||
this.eeTrialService.cancelled = true; |
||||
this.eeTrialService.modalOpen = true; |
||||
this.opModalService.show(EnterpriseTrialModal, this.injector); |
||||
} |
||||
|
||||
public get noTrialRequested() { |
||||
return this.eeTrialService.status === undefined; |
||||
} |
||||
} |
@ -0,0 +1,86 @@ |
||||
<form id="enterprise-trial-form" class="form" [formGroup]="trialForm"> |
||||
<div class="form--field -wide-label -required"> |
||||
<label class="form--label" for="trial-company-name">{{ text.label_company }}</label> |
||||
<div class="form--field-container"> |
||||
<div class="form--text-field-container"> |
||||
<input type="text" |
||||
id="trial-company-name" |
||||
class="form--text-field" |
||||
formControlName="company"> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="form--field -wide-label -required"> |
||||
<label class="form--label" for="trial-first-name">{{ text.label_first_name }}</label> |
||||
<div class="form--field-container"> |
||||
<div class="form--text-field-container"> |
||||
<input type="text" |
||||
id="trial-first-name" |
||||
class="form--text-field" |
||||
formControlName="first_name"> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="form--field -wide-label -required"> |
||||
<label class="form--label" for="trial-last-name">{{ text.label_last_name }}</label> |
||||
<div class="form--field-container"> |
||||
<div class="form--text-field-container"> |
||||
<input type="text" |
||||
id="trial-last-name" |
||||
class="form--text-field" |
||||
formControlName="last_name"> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="form--field -wide-label -required" [ngClass]="{ '-error': eeTrialService.errorMsg }"> |
||||
<label class="form--label" for="trial-email">{{ text.label_email }}</label> |
||||
<div class="form--field-container"> |
||||
<div class="form--text-field-container" |
||||
[ngClass]="{ '-required-highlighting' : eeTrialService.errorMsg }"> |
||||
<input type="email" |
||||
class="form--text-field" |
||||
id="trial-email" |
||||
formControlName="email" (blur)="checkMailField()"> |
||||
</div> |
||||
</div> |
||||
<div *ngIf="eeTrialService.errorMsg" class="form--field-instructions">{{ eeTrialService.errorMsg }}</div> |
||||
</div> |
||||
<div class="form--field -wide-label -required"> |
||||
<label class="form--label" for="trial-domain-name">{{ text.label_domain }}</label> |
||||
<div class="form--field-container"> |
||||
<div class="form--text-field-container"> |
||||
<input type="text" |
||||
id="trial-domain-name" |
||||
class="form--text-field" |
||||
formControlName="domain"> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="form--field -required"> |
||||
<div class="form--field-container"> |
||||
<label class="form--label-with-check-box -no-ellipsis" for="trial-general-consent"> |
||||
<div class="form--check-box-container"> |
||||
<input type="checkbox" |
||||
id="trial-general-consent" |
||||
class="form--check-box" |
||||
formControlName="general_consent" |
||||
required> |
||||
</div> |
||||
<span [innerHTML]="text.general_consent"></span> |
||||
</label> |
||||
</div> |
||||
</div> |
||||
<div class="form--field"> |
||||
<div class="form--field-container"> |
||||
<label class="form--label-with-check-box -no-ellipsis" for="trial-newsletter-consent"> |
||||
<div class="form--check-box-container"> |
||||
<input type="checkbox" |
||||
id="trial-newsletter-consent" |
||||
class="form--check-box" |
||||
formControlName="newsletter_consent"> |
||||
</div> |
||||
<span [innerHtml]="text.receive_newsletter"></span> |
||||
</label> |
||||
</div> |
||||
</div> |
||||
</form> |
@ -0,0 +1,88 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2020 the OpenProject GmbH
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See docs/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import {Component, ElementRef} from "@angular/core"; |
||||
import {FormBuilder, Validators} from "@angular/forms"; |
||||
import {I18nService} from "app/modules/common/i18n/i18n.service"; |
||||
import {EnterpriseTrialService} from "core-components/enterprise/enterprise-trial.service"; |
||||
|
||||
const termsOfServiceURL = 'https://www.openproject.com/terms-of-service/'; |
||||
const legalNoticeURL = 'https://www.openproject.com/legal-notice/'; |
||||
const newsletterURL = 'https://www.openproject.com/newsletter/'; |
||||
|
||||
@Component({ |
||||
selector: 'enterprise-trial-form', |
||||
templateUrl: './ee-trial-form.component.html' |
||||
}) |
||||
export class EETrialFormComponent { |
||||
// enterprise trial form
|
||||
trialForm = this.formBuilder.group({ |
||||
company: ['', Validators.required], |
||||
first_name: ['', Validators.required], |
||||
last_name: ['', Validators.required], |
||||
email: ['', [Validators.required, Validators.email]], |
||||
domain: ['', Validators.required], |
||||
general_consent: [null, Validators.required], |
||||
newsletter_consent: null, |
||||
}); |
||||
|
||||
public text = { |
||||
general_consent: this.I18n.t('js.admin.enterprise.trial.form.general_consent', { |
||||
link_terms: termsOfServiceURL, |
||||
link_privacy: legalNoticeURL |
||||
}), |
||||
invalid_email: this.I18n.t('js.admin.enterprise.trial.form.invalid_email'), |
||||
label_test_ee: this.I18n.t('js.admin.enterprise.trial.form.test_ee'), |
||||
label_company: this.I18n.t('js.admin.enterprise.trial.form.label_company'), |
||||
label_first_name: this.I18n.t('js.admin.enterprise.trial.form.label_first_name'), |
||||
label_last_name: this.I18n.t('js.admin.enterprise.trial.form.label_last_name'), |
||||
label_email: this.I18n.t('js.admin.enterprise.trial.form.label_email'), |
||||
label_domain: this.I18n.t('js.admin.enterprise.trial.form.label_domain'), |
||||
privacy_policy: this.I18n.t('js.admin.enterprise.trial.form.privacy_policy'), |
||||
receive_newsletter: this.I18n.t('js.admin.enterprise.trial.form.receive_newsletter',{ link: newsletterURL }), |
||||
terms_of_service: this.I18n.t('js.admin.enterprise.trial.form.terms_of_service') |
||||
}; |
||||
|
||||
constructor(readonly elementRef:ElementRef, |
||||
readonly I18n:I18nService, |
||||
private formBuilder:FormBuilder, |
||||
public eeTrialService:EnterpriseTrialService) { |
||||
|
||||
} |
||||
|
||||
// checks if mail is valid after input field was edited by the user
|
||||
// displays message for user
|
||||
public checkMailField() { |
||||
if (this.trialForm.value.email !== '' && this.trialForm.controls.email.errors) { |
||||
this.eeTrialService.errorMsg = this.text.invalid_email; |
||||
} else { |
||||
this.eeTrialService.errorMsg = undefined; |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,73 @@ |
||||
<div class="op-modal--portal"> |
||||
<div class="op-modal--modal-container" |
||||
data-indicator-name="modal" |
||||
tabindex="0"> |
||||
<div class="op-modal--modal-header"> |
||||
<h3 [textContent]="headerText()"></h3> |
||||
<a *ngIf="showClose" class="op-modal--modal-close-button"> |
||||
<i class="icon-close" (click)="closeModal($event)" [attr.title]="text.close_popup"> |
||||
</i> |
||||
</a> |
||||
</div> |
||||
|
||||
<div [ngSwitch]="openWindow()" class="op-modal--modal-body"> |
||||
<!-- first modal window --> |
||||
<div *ngSwitchCase="1"> |
||||
<enterprise-trial-form></enterprise-trial-form> |
||||
</div> |
||||
<!-- second modal window --> |
||||
<div *ngSwitchCase="2"> |
||||
<enterprise-trial-waiting></enterprise-trial-waiting> |
||||
</div> |
||||
<!-- third modal window --> |
||||
<div *ngSwitchCase="3"> |
||||
<div class="onboarding--video-block"> |
||||
<div class="onboarding--video-text"> |
||||
<span>{{ text.quick_overview }}</span> |
||||
</div> |
||||
<div class="onboarding--video iframe-target-wrapper"> |
||||
<iframe frameborder="0" |
||||
height="400" |
||||
width="100%" |
||||
[src]="trustedEEVideoURL" |
||||
allowfullscreen> |
||||
</iframe> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="op-modal--modal-footer"> |
||||
<div *ngIf="!eeTrialService.status || eeTrialService.cancelled; else mailSubmitted"> |
||||
<button class="confirm-form-submit--continue button -highlight" |
||||
(click)="onSubmit()" |
||||
[disabled]="!trialForm || trialForm.invalid" |
||||
[textContent]="text.button_submit" |
||||
[attr.title]="text.button_submit" |
||||
[hidden]="eeTrialService.mailSubmitted && !eeTrialService.cancelled"> |
||||
</button> |
||||
<button class="confirm-form-submit--cancel button" |
||||
(click)="closeModal($event)" |
||||
[textContent]="text.button_cancel" |
||||
[attr.title]="text.button_cancel"> |
||||
</button> |
||||
</div> |
||||
<ng-template #mailSubmitted> |
||||
<button class="confirm-form-submit--continue button -highlight" |
||||
(click)="startEnterpriseTrial()" |
||||
[textContent]="text.button_continue" |
||||
[attr.title]="text.button_continue" |
||||
[disabled]="!eeTrialService.confirmed" |
||||
[hidden]="eeTrialService.trialStarted"> |
||||
</button> |
||||
<button *ngIf="eeTrialService.trialStarted" |
||||
class="confirm-form-submit--continue button -highlight" |
||||
(click)="closeModal($event)" |
||||
[textContent]="text.button_continue" |
||||
[attr.title]="text.button_continue"> |
||||
</button> |
||||
</ng-template> |
||||
</div> |
||||
|
||||
</div> |
||||
</div> |
@ -0,0 +1,9 @@ |
||||
.op-modal--modal-body |
||||
padding: 0 |
||||
|
||||
.onboarding--video-text |
||||
margin-bottom: 1.25rem |
||||
|
||||
.op-modal--modal-footer |
||||
margin: 0 |
||||
padding: 0 |
@ -0,0 +1,130 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2020 the OpenProject GmbH
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See docs/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import {AfterViewInit, ChangeDetectorRef, Component, ElementRef, Inject, Input, ViewChild} from "@angular/core"; |
||||
import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser"; |
||||
import {FormControl, FormGroup} from "@angular/forms"; |
||||
import {OpModalComponent} from "app/components/op-modals/op-modal.component"; |
||||
import {OpModalLocalsToken} from "app/components/op-modals/op-modal.service"; |
||||
import {OpModalLocalsMap} from "app/components/op-modals/op-modal.types"; |
||||
import {I18nService} from "app/modules/common/i18n/i18n.service"; |
||||
import {EETrialFormComponent} from "core-components/enterprise/enterprise-modal/enterprise-trial-form/ee-trial-form.component"; |
||||
import {EnterpriseTrialService} from "core-components/enterprise/enterprise-trial.service"; |
||||
|
||||
export const eeOnboardingVideoURL = 'https://www.youtube.com/embed/zLMSydhFSkw?autoplay=1'; |
||||
|
||||
@Component({ |
||||
selector: 'enterprise-trial-modal', |
||||
templateUrl: './enterprise-trial.modal.html', |
||||
styleUrls: ['./enterprise-trial.modal.sass'] |
||||
}) |
||||
export class EnterpriseTrialModal extends OpModalComponent implements AfterViewInit { |
||||
@ViewChild(EETrialFormComponent, { static: false }) formComponent:EETrialFormComponent; |
||||
@Input() public opReferrer:string; |
||||
|
||||
public trialForm:FormGroup; |
||||
public errorMsg:string|undefined; |
||||
|
||||
// modal configuration
|
||||
public showClose = true; |
||||
public closeOnEscape = false; |
||||
public closeOnOutsideClick = false; |
||||
|
||||
public trustedEEVideoURL:SafeResourceUrl; |
||||
public text = { |
||||
button_submit: this.I18n.t('js.modals.button_submit'), |
||||
button_cancel: this.I18n.t('js.modals.button_cancel'), |
||||
button_continue: this.I18n.t('js.button_continue'), |
||||
close_popup: this.I18n.t('js.close_popup_title'), |
||||
heading_confirmation: this.I18n.t('js.admin.enterprise.trial.confirmation'), |
||||
heading_next_steps: this.I18n.t('js.admin.enterprise.trial.next_steps'), |
||||
heading_test_ee: this.I18n.t('js.admin.enterprise.trial.test_ee'), |
||||
quick_overview: this.I18n.t('js.admin.enterprise.trial.quick_overview') |
||||
}; |
||||
|
||||
constructor(readonly elementRef:ElementRef, |
||||
@Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, |
||||
readonly cdRef:ChangeDetectorRef, |
||||
readonly I18n:I18nService, |
||||
readonly domSanitizer:DomSanitizer, |
||||
public eeTrialService:EnterpriseTrialService) { |
||||
super(locals, cdRef, elementRef); |
||||
this.trustedEEVideoURL = this.trustedURL(eeOnboardingVideoURL); |
||||
} |
||||
|
||||
ngAfterViewInit() { |
||||
this.trialForm = this.formComponent.trialForm; |
||||
} |
||||
|
||||
// checks if form is valid and submits it
|
||||
public onSubmit() { |
||||
if (this.trialForm.valid) { |
||||
this.trialForm.addControl('_type', new FormControl('enterprise-trial')); |
||||
this.eeTrialService.sendForm(this.trialForm); |
||||
} |
||||
} |
||||
|
||||
public startEnterpriseTrial() { |
||||
// open onboarding modal screen
|
||||
this.eeTrialService.setStartTrialStatus(); |
||||
} |
||||
|
||||
public headerText() { |
||||
if (this.eeTrialService.mailSubmitted) { |
||||
return this.text.heading_confirmation; |
||||
} else if (this.eeTrialService.trialStarted) { |
||||
return this.text.heading_next_steps; |
||||
} else { |
||||
return this.text.heading_test_ee; |
||||
} |
||||
} |
||||
|
||||
public closeModal(event:any) { |
||||
this.closeMe(event); |
||||
// refresh page to show enterprise trial
|
||||
if (this.eeTrialService.trialStarted || this.eeTrialService.confirmed) { |
||||
window.location.reload(); |
||||
} |
||||
this.eeTrialService.modalOpen = false; |
||||
} |
||||
|
||||
public trustedURL(url:string) { |
||||
return this.domSanitizer.bypassSecurityTrustResourceUrl(url); |
||||
} |
||||
|
||||
public openWindow():number { |
||||
if (!this.eeTrialService.status || this.eeTrialService.cancelled) { |
||||
return 1; |
||||
} else if (this.eeTrialService.mailSubmitted && !this.eeTrialService.cancelled) { |
||||
return 2; |
||||
} else { |
||||
return 3; |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,16 @@ |
||||
<enterprise-active-trial></enterprise-active-trial> |
||||
|
||||
<p>{{ text.confirmation_info }}</p> |
||||
<p> |
||||
<span>{{ text.status_label }} </span> |
||||
<span *ngIf="!eeTrialService.confirmed; else confirmedStatus" class="status--waiting"> |
||||
{{ text.status_waiting }} |
||||
|
||||
<a id="resend-link" (click)="resendMail()">{{ text.resend }}</a> |
||||
<p *ngIf="eeTrialService.cancelled">{{ text.session_timeout }}</p> |
||||
</span> |
||||
|
||||
<ng-template #confirmedStatus> |
||||
<span class="status--confirmed icon-yes">{{ text.status_confirmed }}</span> |
||||
</ng-template> |
||||
</p> |
@ -0,0 +1,7 @@ |
||||
#resend-link |
||||
float: right |
||||
|
||||
.status--confirmed |
||||
color: var(--button--alt-highlight-background-color) |
||||
.status--waiting |
||||
color: orange |
@ -0,0 +1,79 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2020 the OpenProject GmbH
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See docs/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import {Component, ElementRef} from "@angular/core"; |
||||
import {I18nService} from "app/modules/common/i18n/i18n.service"; |
||||
import {EnterpriseTrialService} from "app/components/enterprise/enterprise-trial.service"; |
||||
import {HttpClient, HttpErrorResponse} from "@angular/common/http"; |
||||
import {NotificationsService} from "core-app/modules/common/notifications/notifications.service"; |
||||
|
||||
|
||||
@Component({ |
||||
selector: 'enterprise-trial-waiting', |
||||
templateUrl: './ee-trial-waiting.component.html', |
||||
styleUrls: ['./ee-trial-waiting.component.sass'] |
||||
}) |
||||
export class EETrialWaitingComponent { |
||||
public text = { |
||||
confirmation_info: this.I18n.t('js.admin.enterprise.trial.confirmation_info'), |
||||
resend: this.I18n.t('js.admin.enterprise.trial.resend_link'), |
||||
resend_success: this.I18n.t('js.admin.enterprise.trial.resend_success'), |
||||
resend_warning: this.I18n.t('js.admin.enterprise.trial.resend_warning'), |
||||
session_timeout: this.I18n.t('js.admin.enterprise.trial.session_timeout'), |
||||
status_confirmed: this.I18n.t('js.admin.enterprise.trial.status_confirmed'), |
||||
status_label: this.I18n.t('js.admin.enterprise.trial.status_label'), |
||||
status_waiting: this.I18n.t('js.admin.enterprise.trial.status_waiting') |
||||
}; |
||||
|
||||
constructor(readonly elementRef:ElementRef, |
||||
readonly I18n:I18nService, |
||||
protected http:HttpClient, |
||||
protected notificationsService:NotificationsService, |
||||
public eeTrialService:EnterpriseTrialService) { |
||||
} |
||||
|
||||
// resend mail if resend link has been clicked
|
||||
public resendMail() { |
||||
this.eeTrialService.cancelled = false; |
||||
this.http.post(this.eeTrialService.resendLink, {}) |
||||
.toPromise() |
||||
.then(() => { |
||||
this.notificationsService.addSuccess(this.text.resend_success); |
||||
this.eeTrialService.retryConfirmation(); |
||||
}) |
||||
.catch(() => { |
||||
if (this.eeTrialService.trialLink) { |
||||
// Check whether the mail has been confirmed by now
|
||||
this.eeTrialService.getToken(); |
||||
} else { |
||||
this.notificationsService.addError(this.text.resend_warning); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,166 @@ |
||||
import {Injectable} from "@angular/core"; |
||||
import {I18nService} from "core-app/modules/common/i18n/i18n.service"; |
||||
import {HttpClient, HttpErrorResponse} from "@angular/common/http"; |
||||
import {PathHelperService} from "core-app/modules/common/path-helper/path-helper.service"; |
||||
import {NotificationsService} from "core-app/modules/common/notifications/notifications.service"; |
||||
import {FormGroup} from "@angular/forms"; |
||||
import {BehaviorSubject} from 'rxjs'; |
||||
|
||||
@Injectable() |
||||
export class EnterpriseTrialService { |
||||
// user data needs to be sync in ee-active-trial.component.ts
|
||||
private userDataSubject = new BehaviorSubject<any>({}); |
||||
public userData$ = this.userDataSubject.asObservable(); |
||||
public userData:any; |
||||
public baseUrlAugur:string; |
||||
|
||||
|
||||
public trialLink:string; |
||||
public resendLink:string; |
||||
|
||||
public modalOpen = false; |
||||
public confirmed:boolean; |
||||
public cancelled = false; |
||||
public status:'mailSubmitted'|'startTrial'|undefined; |
||||
public errorMsg:string|undefined; |
||||
|
||||
constructor(readonly I18n:I18nService, |
||||
protected http:HttpClient, |
||||
readonly pathHelper:PathHelperService, |
||||
protected notificationsService:NotificationsService) { |
||||
let gon = (window as any).gon; |
||||
this.baseUrlAugur = gon.augur_url; |
||||
|
||||
if ((window as any).gon.ee_trial_key) { |
||||
this.setMailSubmittedStatus(); |
||||
} |
||||
} |
||||
|
||||
// send POST request with form object
|
||||
// receive an enterprise trial link to access a token
|
||||
public sendForm(form:FormGroup) { |
||||
this.userData = form.value; |
||||
this.userDataSubject.next(this.userData); |
||||
|
||||
this.cancelled = false; |
||||
this.http.post(this.baseUrlAugur + '/public/v1/trials', form.value) |
||||
.toPromise() |
||||
.then((enterpriseTrial:any) => { |
||||
this.trialLink = enterpriseTrial._links.self.href; |
||||
this.saveTrialKey(this.trialLink); |
||||
|
||||
this.retryConfirmation(); |
||||
}) |
||||
.catch((error:HttpErrorResponse) => { |
||||
// mail is invalid or user already created a trial
|
||||
if (error.status === 422 || error.status === 400) { |
||||
this.errorMsg = error.error.description; |
||||
} else { |
||||
this.notificationsService.addWarning(error.error.description || I18n.t('js.error.internal')); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
// get a token from the trial link if user confirmed mail
|
||||
public getToken() { |
||||
// 2) GET /public/v1/trials/:id
|
||||
this.http |
||||
.get<any>(this.trialLink) |
||||
.toPromise() |
||||
.then((res:any) => { |
||||
// show confirmed status and enable continue btn
|
||||
this.confirmed = true; |
||||
|
||||
// returns token if mail was confirmed
|
||||
// -> if token is new (token_retrieved: false) save token in backend
|
||||
if (!res.token_retrieved) { |
||||
this.saveToken(res.token); |
||||
} |
||||
|
||||
// load page if mail was confirmed and modal window is not open
|
||||
if (!this.modalOpen) { |
||||
setTimeout(() => { // display confirmed status before reloading
|
||||
window.location.reload(); |
||||
}, 500); |
||||
} |
||||
}) |
||||
.catch((error:HttpErrorResponse) => { |
||||
// returns error 422 while waiting of confirmation
|
||||
if (error.status === 422 && error.error.identifier === 'waiting_for_email_verification') { |
||||
// get resend button link
|
||||
this.resendLink = error.error._links.resend.href; |
||||
// save a key for the requested trial
|
||||
if (!this.status || this.cancelled) { // only do it once
|
||||
this.saveTrialKey(this.resendLink); |
||||
} |
||||
// open next modal window -> status waiting
|
||||
this.setMailSubmittedStatus(); |
||||
this.confirmed = false; |
||||
} else if (_.get(error, 'error._type') === 'Error') { |
||||
this.notificationsService.addError(error.error.message); |
||||
} else { |
||||
this.notificationsService.addError(error.error || I18n.t('js.error.internal')); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
// save a part of the resend link in db
|
||||
// which allows to remember if a user has already requested a trial token
|
||||
// and to ask for the corresponding user data saved in Augur
|
||||
private saveTrialKey(resendlink:string) { |
||||
// extract token from resend link
|
||||
let trialKey = resendlink.split('/')[6]; |
||||
return this.http.post( |
||||
this.pathHelper.api.v3.appBasePath + '/admin/enterprise/save_trial_key', |
||||
{ trial_key: trialKey }, |
||||
{ withCredentials: true } |
||||
) |
||||
.toPromise() |
||||
.catch((e:any) => { |
||||
this.notificationsService.addError(e.error.message || e.message || e); |
||||
}); |
||||
} |
||||
|
||||
// save received token in controller
|
||||
private saveToken(token:string) { |
||||
this.http.post( |
||||
this.pathHelper.api.v3.appBasePath + '/admin/enterprise', |
||||
{ enterprise_token: { encoded_token: token } }, |
||||
{ withCredentials: true } |
||||
) |
||||
.toPromise() |
||||
.catch((error:HttpErrorResponse) => { |
||||
this.notificationsService.addError(error.error.description || I18n.t('js.error.internal')); |
||||
}); |
||||
} |
||||
|
||||
// retry request while waiting for mail confirmation
|
||||
public retryConfirmation(delay:number = 5000, retries:number = 60) { |
||||
if (this.cancelled || this.confirmed) { |
||||
return; |
||||
} else if (retries === 0) { |
||||
this.cancelled = true; |
||||
} else { |
||||
this.getToken(); |
||||
setTimeout( () => { |
||||
this.retryConfirmation(delay, retries - 1); |
||||
}, delay); |
||||
} |
||||
} |
||||
|
||||
public setStartTrialStatus() { |
||||
this.status = 'startTrial'; |
||||
} |
||||
|
||||
public setMailSubmittedStatus() { |
||||
this.status = 'mailSubmitted'; |
||||
} |
||||
|
||||
public get trialStarted():boolean { |
||||
return this.status === 'startTrial'; |
||||
} |
||||
|
||||
public get mailSubmitted():boolean { |
||||
return this.status === 'mailSubmitted'; |
||||
} |
||||
} |
@ -0,0 +1,59 @@ |
||||
// -- copyright
|
||||
// OpenProject is an open source project management software.
|
||||
// Copyright (C) 2012-2020 the OpenProject GmbH
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License version 3.
|
||||
//
|
||||
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
||||
// Copyright (C) 2006-2013 Jean-Philippe Lang
|
||||
// Copyright (C) 2010-2013 the ChiliProject Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// See docs/COPYRIGHT.rdoc for more details.
|
||||
// ++
|
||||
|
||||
import {NgModule} from '@angular/core'; |
||||
import {OpenprojectCommonModule} from "core-app/modules/common/openproject-common.module"; |
||||
import {EnterpriseTrialService} from "core-components/enterprise/enterprise-trial.service"; |
||||
import {EnterpriseBaseComponent} from "core-components/enterprise/enterprise-base.component"; |
||||
import {EnterpriseTrialModal} from "core-components/enterprise/enterprise-modal/enterprise-trial.modal"; |
||||
import {EETrialFormComponent} from "core-components/enterprise/enterprise-modal/enterprise-trial-form/ee-trial-form.component"; |
||||
import {EETrialWaitingComponent} from "core-components/enterprise/enterprise-trial-waiting/ee-trial-waiting.component"; |
||||
import {EEActiveTrialComponent} from "core-components/enterprise/enterprise-active-trial/ee-active-trial.component"; |
||||
import {EEActiveSavedTrialComponent} from "core-components/enterprise/enterprise-active-trial/ee-active-saved-trial.component"; |
||||
import {FormsModule, ReactiveFormsModule} from "@angular/forms"; |
||||
|
||||
@NgModule({ |
||||
imports: [ |
||||
OpenprojectCommonModule, |
||||
FormsModule, |
||||
ReactiveFormsModule |
||||
], |
||||
providers: [ |
||||
EnterpriseTrialService |
||||
], |
||||
declarations: [ |
||||
EnterpriseBaseComponent, |
||||
EnterpriseTrialModal, |
||||
EETrialFormComponent, |
||||
EETrialWaitingComponent, |
||||
EEActiveTrialComponent, |
||||
EEActiveSavedTrialComponent, |
||||
] |
||||
}) |
||||
export class OpenprojectEnterpriseModule { |
||||
} |
@ -0,0 +1,246 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe 'Enterprise trial management', |
||||
type: :feature, |
||||
skip: true, |
||||
driver: :headless_firefox_billy do |
||||
|
||||
let(:admin) { FactoryBot.create(:admin) } |
||||
|
||||
let(:trial_id) { '1b6486b4-5a30-4042-8714-99d7c8e6b637' } |
||||
let(:created_body) do |
||||
{ |
||||
_type: "enterprise-trial", |
||||
id: trial_id, |
||||
_links: |
||||
{ |
||||
self: |
||||
{ |
||||
href: "https://augur.openproject-edge.com/public/v1/trials/#{trial_id}" |
||||
}, |
||||
details: |
||||
{ |
||||
href: "https://augur.openproject-edge.com/public/v1/trials/#{trial_id}/details" |
||||
} |
||||
} |
||||
} |
||||
end |
||||
|
||||
let(:waiting_body) do |
||||
{ |
||||
_type: "error", |
||||
code: 422, |
||||
identifier: "waiting_for_email_verification", |
||||
description: "User has to confirm their email address", |
||||
_links: { |
||||
resend: { |
||||
href: "https://augur.openproject-edge.com/public/v1/trials/#{trial_id}/resend", |
||||
method: "POST" |
||||
}, |
||||
details: { |
||||
href: "https://augur.openproject-edge.com/public/v1/trials/#{trial_id}/details" |
||||
} |
||||
} |
||||
} |
||||
end |
||||
|
||||
let(:expired_token) do |
||||
<<~EOS |
||||
-----BEGIN OPENPROJECT-EE TOKEN----- |
||||
eyJkYXRhIjoiTE02OG5UWjJ1cTY4dnlKNWo4NEk0ZnZGdHlFcUtEU1ZxVGd5 |
||||
WnBicTUzTlA5VFFOa3NSc3haOGl1KzZpXG5VTEhuQmhnWjc5c3pYRzhTV2lt |
||||
Tlg3QnpLdkh2MlFLeXFqOCtkQ2dzNHNhQUEvV21aRWZ3YmtPVExTSTBcblVY |
||||
eTYxMmFnKzY0OXVOT2dOdTZmTm5mQndoTnNZdnFGRmxDZjJZd1VQU0ROZUhQ |
||||
dWF2bDJEa3hlTTlLdlxuaWRNbC8wU3BxdWpzMVk4VjlLazhEejRJNUViQU1E |
||||
K1NOMzE1eHplOWc2MDduN2p4c3FKS3k3RVVrUTI5XG5RRG5DSTVZSTJ6bTJv |
||||
dkpaXG4iLCJrZXkiOiJSaXlnRTE0RWswdi9qVFZkOW9HRWJOcldudStQQlN2 |
||||
K0xDTEVpUWZadEczY2g2djN1TERWdWVZeG8xV2NcbjRXdUFGUkdKOFEvejhn |
||||
OG01NWpyMkRKdGh6UUdoVjRYa2t4ZlN2ZUdaaUVzRWJFMmh5NzQ2cDRHNjl1 |
||||
b1xuaFFOQmtqZ1FqWUZwTW9yUVBSRmhXRTNjbkp1dGFKOGU1dUVTbkZPYUFD |
||||
RDdsdkNvMUhMY2J4NWduMm96XG5NcXllbC96NytBdSt5QUNtT2poSlRaUW9L |
||||
M25ZenVuZ1FXbXJiZm93ZGUzVVN6c1lraEdyRHlBNXJSWmlcbk9TaXpqSnNE |
||||
MXBIRmZ4aVhEMnYzVlNuMWJMNXpJWFZNMDBUSFJGUHZLODVYY3IzTEVFNTZy |
||||
TVBCMytnRlxuYUptcVFUYVJOWFowamJKZ3cwNFdqUEtTbGxxaDVIVWZ5Umk2 |
||||
ZjErRGxxYWlsMmcvOUZpTUpPc29QOFhhXG5IYm9oUURBY1drOHBGVE9Fci8z |
||||
NTNSdU4rejE3SklJdVdsM0Z2ZmhiQVFGZVdHQ0paR0JzTnJ4WUV3QzBcbmN1 |
||||
WC80NFZKem9kcVkrRHlSUWFYNFlkcytCOUJVRnB2MHRaSnUrZmNza1MyVnc5 |
||||
MGtCM3hQZTBmYTFjV1xuZTVSdHFucklFNXRQMzlEQ25YZWxwSmxGRXh5YzhX |
||||
ekZiL3BlVEVqUWtCWnM0ZUNKMzhQT1djQXh1R2s2XG5iRFppYWlEQitJSFV4 |
||||
QTBuYXhSa2R5OWJvbHBNa1RBNjk1a3Q3TnhSTDJ2WU1PZFFEY3pIekFNQmpV |
||||
TE5cbnFQd3FuQWlreDgrazJqTStHWDFmTXgrUzcyb3FQTjd6M0ZqaU85S1BV |
||||
VVAveTlNMG1RV1hCZEY0bVJUR1xuM2RuUDNoOXErSHNnTkFHTUNxSHBTRzNv |
||||
L05ONTRQSCs1NCsyWk5MVDFzZ1ZubjBsQ1hVdlh0Vkt6b1hhXG5TdXlZNzBV |
||||
R3NJTUdDYmZhdnlYREVpQzU2SWtJVzNTSU1CVHVQdisxQ1J4TCtIcEEzRE5x |
||||
R09BVjVMbFFcbkUrdGNqZlNpUlVzRXcxeWkxWUZPODVEM2ZVTXdLZzFKclZB |
||||
WEV1YVdvbjUxMzNVRnZNZjBNbFhkSUQ1L1xuNEtXczNGeEdmWVJJRUQ5VlhR |
||||
eFNYdEQ3cWYzSlFFbHdHSGVMdUtVYkRmMWEzTEVKMUFKb2FOV0phcG9xXG4v |
||||
QlU3ZHJoM28zSDFXYVBpeUhpUlQrVExTa2cxUXhxY2p0eUVuK1JiNnBKVmwr |
||||
eVJXWHMrR1pkcGFDY09cbnh2ZWRIam12NzNsUjc5WFpVNVh6UlhwY3E1d1pm |
||||
T2FVaHZ1NHllQysyR0FpYlIrcGowbTA0UzRXbjI0elxucjUvZGtnSG5Xek9B |
||||
V2lZb0MxOEZpckhTSnVGM1FHWHJUK1JyT2c4QVdwTDlHMGZQQlpveTJvNFZj |
||||
V004XG51UXJvSDUwT3Rtcm00cW53QUU3TEFyc3g3bWxOblBGMmpyejZMeWkz |
||||
UlhDN1ZrSE9FVXhiUHNjZHJiRlhcbkd3cTlvNU5LNi9sb2RVTTAzeklyaTBs |
||||
TVdKSlpUU3BNMnVzU0VxWUpoS05uSGI1a3lYcy9MRkhOWW05c1xuN2hBOVdS |
||||
RUxWQi9Tc2x5RjJQczNzSHJQaGtZM1BGZElSeU9Kb2JxdnZoaUpPTVA5dDVu |
||||
MUxUeTFjbkhGXG5DbTBDM0U1bWFjTi9hOE5OSXk2dGhia3JJVE5XK2I4K2Jw |
||||
VDN3OGkxSDVYNCtodlJ5T1g0Y0JEVWhNN2pcbnN3Wkw0citmWlRmaGlNQkZi |
||||
K2NmSUZ0U2lyMVBpdz09XG4iLCJpdiI6IjFxbEZqRWM4QzcrMjg4QWR6cXdL |
||||
OEE9PVxuIn0= |
||||
-----END OPENPROJECT-EE TOKEN----- |
||||
EOS |
||||
end |
||||
let (:confirmed_body) do |
||||
{ |
||||
_type: "enterprise-trial", |
||||
id: trial_id, |
||||
token: expired_token, |
||||
token_retrieved: false, |
||||
_links: { |
||||
self: { |
||||
href: "https://augur.openproject-edge.com/public/v1/trials/#{trial_id}" |
||||
} |
||||
} |
||||
} |
||||
end |
||||
|
||||
let(:mail_in_use_body) do |
||||
{ |
||||
_type: "error", |
||||
code: 422, |
||||
identifier: "user_already_created_trial", |
||||
description: "Each user can only create one trial." |
||||
} |
||||
end |
||||
|
||||
before do |
||||
login_as(admin) |
||||
visit enterprise_path |
||||
end |
||||
|
||||
def fill_out_modal(mail: 'foo@foocorp.example') |
||||
fill_in 'Company', with: 'Foo Corp.' |
||||
fill_in 'First name', with: 'Foo' |
||||
fill_in 'Last name', with: 'Bar' |
||||
fill_in 'Email', with: mail |
||||
fill_in 'Domain', with: 'foo.example.com' |
||||
|
||||
find('#trial-general-consent').check |
||||
end |
||||
|
||||
it 'blocks the request assuming the mail was used' do |
||||
proxy.stub('https://augur.openproject-edge.com:443/public/v1/trials', method: 'post') |
||||
.and_return(headers: {'Access-Control-Allow-Origin' => '*'}, code: 422, body: mail_in_use_body.to_json) |
||||
|
||||
find('.button', text: 'Start free trial').click |
||||
fill_out_modal |
||||
find('.button:not(:disabled)', text: 'Submit').click |
||||
|
||||
expect(page).to have_selector('.form--field.-error #trial-email') |
||||
expect(page).to have_text 'Each user can only create one trial.' |
||||
expect(page).to have_no_text 'email sent - waiting for confirmation' |
||||
end |
||||
|
||||
context 'with a waiting request pending' do |
||||
before do |
||||
proxy.stub('https://augur.openproject-edge.com:443/public/v1/trials', method: 'post') |
||||
.and_return(headers: {'Access-Control-Allow-Origin' => '*'}, code: 200, body: created_body.to_json) |
||||
|
||||
proxy.stub("https://augur.openproject-edge.com:443/public/v1/trials/#{trial_id}") |
||||
.and_return(headers: {'Access-Control-Allow-Origin' => '*'}, code: 422, body: waiting_body.to_json) |
||||
|
||||
find('.button', text: 'Start free trial').click |
||||
fill_out_modal |
||||
find('.button:not(:disabled)', text: 'Submit').click |
||||
|
||||
expect(page).to have_text 'foo@foocorp.example' |
||||
expect(page).to have_text 'email sent - waiting for confirmation' |
||||
end |
||||
|
||||
it 'can get the trial if reloading the page' do |
||||
# We need to go to another page to stop the request cycle |
||||
visit info_admin_index_path |
||||
|
||||
# Stub with successful body |
||||
# Stub the proxy to a successful return |
||||
# which marks the user has confirmed the mail link |
||||
proxy.stub("https://augur.openproject-edge.com:443/public/v1/trials/#{trial_id}") |
||||
.and_return(headers: {'Access-Control-Allow-Origin' => '*'}, code: 200, body: confirmed_body.to_json) |
||||
|
||||
# Stub the details URL to still return 403 |
||||
proxy.stub("https://augur.openproject-edge.com:443/public/v1/trials/#{trial_id}/details") |
||||
.and_return(headers: {'Access-Control-Allow-Origin' => '*'}, code: 403) |
||||
|
||||
visit enterprise_path |
||||
|
||||
expect(page).to have_selector('.attributes-key-value--value-container', text: 'OpenProject Test', wait: 20) |
||||
expect(page).to have_selector('.attributes-key-value--value-container', text: '01/01/2020') |
||||
expect(page).to have_selector('.attributes-key-value--value-container', text: '01/02/2020') |
||||
expect(page).to have_selector('.attributes-key-value--value-container', text: '5') |
||||
# Generated expired token has different mail |
||||
expect(page).to have_selector('.attributes-key-value--value-container', text: 'info@openproject.com') |
||||
end |
||||
|
||||
it 'can confirm that trial regularly' do |
||||
# Stub resend method |
||||
proxy.stub("https://augur.openproject-edge.com:443/public/v1/trials/#{trial_id}/resend") |
||||
.and_return(headers: {'Access-Control-Allow-Origin' => '*'}, code: 200, body: waiting_body.to_json) |
||||
|
||||
find('.op-modal--modal-body #resend-link', text: 'Resend').click |
||||
|
||||
expect(page).to have_text 'Email has been resent.' |
||||
|
||||
expect(page).to have_text 'foo@foocorp.example' |
||||
expect(page).to have_text 'email sent - waiting for confirmation' |
||||
|
||||
# Stub the proxy to a successful return |
||||
# which marks the user has confirmed the mail link |
||||
proxy.stub("https://augur.openproject-edge.com:443/public/v1/trials/#{trial_id}") |
||||
.and_return(headers: {'Access-Control-Allow-Origin' => '*'}, code: 200, body: confirmed_body.to_json) |
||||
|
||||
# Wait until the next request |
||||
expect(page).to have_selector '.status--confirmed', text: 'confirmed', wait: 20 |
||||
|
||||
# advance to video |
||||
click_on 'Continue' |
||||
|
||||
# advance to close |
||||
click_on 'Continue' |
||||
|
||||
expect(page).to have_selector('.flash.notice', text: 'Successful update.', wait: 10) |
||||
expect(page).to have_selector('.attributes-key-value--value-container', text: 'OpenProject Test') |
||||
expect(page).to have_selector('.attributes-key-value--value-container', text: '01/01/2020') |
||||
expect(page).to have_selector('.attributes-key-value--value-container', text: '01/02/2020') |
||||
expect(page).to have_selector('.attributes-key-value--value-container', text: '5') |
||||
# Generated expired token has different mail |
||||
expect(page).to have_selector('.attributes-key-value--value-container', text: 'info@openproject.com') |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,39 @@ |
||||
#-- encoding: UTF-8 |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2020 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2017 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
# puffing-billy is a gem that creates a middleman proxy between the browser controlled |
||||
# by capybara/selenium and the spec execution. |
||||
# |
||||
# This allows us to stub requests to external APIs to guarantee responses regardless of |
||||
# their availability. |
||||
# |
||||
# In order to use the proxied server, you need to use `driver: headless_firefox_billy` in your examples |
||||
# |
||||
# See https://github.com/oesmith/puffing-billy for more information |
||||
require 'billy/capybara/rspec' |
After Width: | Height: | Size: 1.5 KiB |