Commit b783387a authored by Douwe Maan's avatar Douwe Maan

Merge branch '2501-trial-and-license-purchases-inside-gitlab-ee' into 'master'

Trial and license purchases inside GitLab EE

Closes #2500

See merge request !2266
parents 1f1a183a 0a42967a
import Cookies from 'js-cookie';
export default class EETrialBanner {
constructor($trialBanner) {
this.COOKIE_KEY = 'show_ee_trial_banner';
this.$trialBanner = $trialBanner;
this.$mainNavbar = this.$trialBanner.siblings('.js-navbar-gitlab');
this.$secondaryNavbar = this.$mainNavbar.siblings('.js-page-with-sidebar');
this.licenseExpiresOn = new Date(this.$trialBanner.data('license-expiry'));
}
init() {
// Wait for navbars to render before querying
this.setCookies();
this.$trialBanner.on('close.bs.alert', e => this.handleTrialBannerDismiss(e));
}
/**
* Trial Expiring/Expired Banner has two stages;
* 1. Show banner when user enters last 7 days of trial
* 2. Show banner again when last 7 days are over and license has expired
*
* Stage 1:
* Banner is showed when `trial_license_message` is sent by backend
* for the first time (in `app/views/layouts/header/_default.html.haml`).
* Here, we perform following steps;
*
* 1. Set cookie `show_ee_trial_banner` with expiry same as license
* 2. Set cookie value to `true`
* 3. Show banner using `toggleBanner(true)`
*
* At this stage, if user dismisses banner, we set cookie value to `false`
* and everytime page is initialized, we check for cookie existence as
* well as its value, and decide show/hide status of banner
*
* Stage 2:
* At this point, Cookie we had set earlier will be expired and
* backend will now send updated message in `trial_license_message`.
* Here, we perform following steps;
*
* 1. Check if cookie is defined (it'll not be defined as it is expired now)
* 2. If cookie is gone, we re-set `show_ee_trial_banner` cookie but with
* expiry of 20 years
* 3. Set cookie value to `true`
* 4. Show banner using `toggleBanner(true)`, which now has updated message
*
* At this stage, if user dismisses banner, we set cookie value to `false`
* and our existing logic of show/hide banner based on cookie value continues
* to work. And since, cookie is set to expire after 20 years, user won't be
* seeing banner again.
*/
setCookies() {
const today = new Date();
// Check if Cookie is defined
if (!Cookies.get(this.COOKIE_KEY)) {
// Cookie was not defined, let's define with default value
// Check if License is yet to expire
if (today < this.licenseExpiresOn) {
// License has not expired yet, we show initial banner of 7 days
// with cookie set to validity same as license expiry
Cookies.set(this.COOKIE_KEY, 'true', { expires: this.licenseExpiresOn });
} else {
// License is already expired so we show final Banner with cookie set to 20 years validity.
Cookies.set(this.COOKIE_KEY, 'true', { expires: 7300 });
}
this.toggleBanner(true);
} else {
// Cookie was defined, let's read value and show/hide banner
this.toggleBanner(Cookies.get(this.COOKIE_KEY) === 'true');
}
}
toggleMainNavbarMargin(state) {
if (this.$mainNavbar.length) {
this.$mainNavbar.toggleClass('has-trial-banner', state);
}
}
toggleSecondaryNavbarMargin(state) {
if (this.$secondaryNavbar.length) {
this.$secondaryNavbar.toggleClass('has-trial-banner', state);
}
}
toggleBanner(state) {
this.$trialBanner.toggleClass('hidden', !state);
this.toggleMainNavbarMargin(state);
this.toggleSecondaryNavbarMargin(state);
}
handleTrialBannerDismiss() {
this.toggleMainNavbarMargin(false);
this.toggleSecondaryNavbarMargin(false);
if (Cookies.get(this.COOKIE_KEY)) {
Cookies.set(this.COOKIE_KEY, 'false');
}
}
}
import EETrialBanner from './ee_trial_banner';
$(() => {
const $trialBanner = $('.js-gitlab-ee-license-banner');
if ($trialBanner.length) {
const eeTrialBanner = new EETrialBanner($trialBanner);
eeTrialBanner.init();
}
});
......@@ -150,6 +150,7 @@ import './syntax_highlight';
import './admin_email_select';
import './application_settings';
import './approvals';
import './ee_trial_banner';
import './ldap_groups_select';
import './path_locks';
import './weight_select';
......@@ -374,4 +375,9 @@ $(function () {
event.preventDefault();
gl.utils.visitUrl(`${action}${$(this).serialize()}`);
});
/**
* EE specific scripts
*/
$('#modal-upload-trial-license').modal('show');
});
......@@ -48,3 +48,23 @@
font-size: 14px;
}
}
/* EE-specific Styles */
@media (min-width: $screen-md-min) {
.blank-state-parent-container.has-start-trial-container {
display: flex;
}
}
.section-ee-trial {
.section-body {
display: flex;
align-items: center;
justify-content: center;
.blank-state {
padding: 20px;
text-align: center;
}
}
}
......@@ -9,6 +9,7 @@
.prepend-top-0 { margin-top: 0; }
.prepend-top-5 { margin-top: 5px; }
.prepend-top-10 { margin-top: 10px; }
.prepend-top-15 { margin-top: 15px; }
.prepend-top-default { margin-top: $gl-padding !important; }
.prepend-top-20 { margin-top: 20px; }
.prepend-left-5 { margin-left: 5px; }
......
/* EE Trial Specific Stylesheet */
/* Main Nav Overrides */
.navbar-gitlab.has-trial-banner {
top: 52px;
}
/* Secondary Nav Overrides - Old Nav */
.page-with-sidebar.has-trial-banner {
margin-top: 102px;
}
/* Sidebar Nav Overrides - New Nav */
.page-with-sidebar.has-trial-banner .nav-sidebar {
top: 102px;
}
/* Trial Banner */
.gitlab-ee-license-banner.alert {
display: flex;
align-content: center;
justify-content: center;
padding: 15px 35px;
position: fixed;
top: 0;
width: 100%;
background: $red-400;
color: $white-light;
text-align: center;
z-index: 400;
@media (max-width: $screen-md-min) {
padding: 10px 35px;
}
.close {
color: $white-dark;
opacity: 0.7;
&:hover {
color: $black;
}
}
p a {
text-decoration: none;
font-weight: bold;
&:hover {
color: $white-dark;
text-decoration: underline;
}
}
.close {
position: absolute;
top: 15%;
right: 10px;
}
}
/* Trial License Install Modal */
.modal-upload-trial-license {
.modal-body-contents {
.trial-activated-graphic {
display: flex;
justify-content: center;
svg {
width: 90px;
height: 90px;
}
}
.confirmation-title,
.confirmation-desc {
text-align: center;
}
.confirmation-desc {
max-width: 80%;
margin: auto;
}
.confirmation-desc {
color: $gl-text-color-secondary;
}
.trial-license-preview {
max-height: 300px;
word-wrap: break-word;
word-break: break-all;
overflow-y: auto;
}
}
}
/* Pure CSS License Key Preview Toggle */
.modal-upload-trial-license {
.trial-license-preview,
.show-license,
.hide-license:target {
display: none;
}
.hide-license,
.hide-license:target + .show-license,
.hide-license:target ~ .trial-license-preview {
display: block;
}
.show-license,
.hide-license {
margin-top: 10px;
text-align: center;
font-size: 18px;
color: $gl-gray-light;
&:hover {
text-decoration: none;
color: $gl-gray-dark;
}
}
}
.blank-state-container {
margin-top: 35px;
.trial-description {
color: $gl-gray-light;
}
}
class Admin::LicensesController < Admin::ApplicationController
before_action :license, only: [:show, :download, :destroy]
before_action :require_license, only: [:show, :download, :destroy]
before_action :require_license, only: [:download, :destroy]
respond_to :html
def show
@previous_licenses = License.previous
if @license.blank?
render :missing
else
@previous_licenses = License.previous
end
end
def download
......@@ -13,7 +17,7 @@ class Admin::LicensesController < Admin::ApplicationController
end
def new
@license = License.new
build_license
end
def create
......@@ -62,6 +66,10 @@ class Admin::LicensesController < Admin::ApplicationController
redirect_to new_admin_license_path
end
def build_license
@license ||= License.new(data: params[:trial_key])
end
def license_params
license_params = params.require(:license).permit(:data_file, :data)
license_params.delete(:data) if license_params[:data_file]
......
......@@ -12,66 +12,30 @@ module LicenseHelper
HistoricalData.max_historical_user_count
end
# in_html is set to false from an initializer, which shouldn't try to render
# HTML links.
#
def license_message(signed_in: signed_in?, is_admin: (current_user && current_user.admin?), in_html: true)
@license_message =
if License.current
yes_license_message(signed_in, is_admin)
else
no_license_message(is_admin, in_html: in_html)
end
end
private
def no_license_message(is_admin, in_html: true)
upload_a_license =
if in_html
link_to('Upload a license', new_admin_license_path)
else
'Upload a license'
end
message = []
message << 'No GitLab Enterprise Edition license has been provided yet.'
message << 'Pushing code and creation of issues and merge requests has been disabled.'
message <<
if is_admin
"#{upload_a_license} in the admin area to activate this functionality."
else
'Ask an admin to upload a license to activate this functionality.'
end
if in_html
content_tag(:p, message.join(' ').html_safe)
else
message.join(' ')
end
end
def yes_license_message(signed_in, is_admin)
license = License.current
def license_message(signed_in: signed_in?, is_admin: current_user&.admin?)
return unless current_license
return unless signed_in
return unless (is_admin && current_license.notify_admins?) || current_license.notify_users?
return unless (is_admin && license.notify_admins?) || license.notify_users?
is_trial = current_license.trial?
message = ["Your Enterprise Edition #{'trial ' if is_trial}license"]
message = []
message << if current_license.expired?
"expired on #{current_license.expires_at}."
else
"will expire in #{pluralize(current_license.remaining_days, 'day')}."
end
message << 'The GitLab Enterprise Edition license'
message << (license.expired? ? 'expired' : 'will expire')
message << "on #{license.expires_at}."
message << link_to('Buy now!', Gitlab::SUBSCRIPTIONS_PLANS_URL, target: '_blank') if is_trial
if license.expired? && license.will_block_changes?
if current_license.expired? && current_license.will_block_changes?
message << 'Pushing code and creation of issues and merge requests'
message <<
if license.block_changes?
if current_license.block_changes?
'has been disabled.'
else
"will be disabled on #{license.block_changes_at}."
"will be disabled on #{current_license.block_changes_at}."
end
message <<
......@@ -82,11 +46,25 @@ module LicenseHelper
end
message << 'to'
message << (license.block_changes? ? 'restore' : 'ensure uninterrupted')
message << (current_license.block_changes? ? 'restore' : 'ensure uninterrupted')
message << 'service.'
end
message.join(' ')
message.join(' ').html_safe
end
def current_license
return @current_license if defined?(@current_license)
@current_license = License.current
end
def new_trial_url
return_to_url = URI.encode(Gitlab.config.gitlab.url)
uri = URI.parse(Gitlab::SUBSCRIPTIONS_URL)
uri.path = '/trials/new'
uri.query = "return_to=#{return_to_url}"
uri.to_s
end
extend self
......
......@@ -185,7 +185,7 @@ class License < ActiveRecord::Base
end
end
delegate :feature_available?, to: :current, allow_nil: true
delegate :block_changes?, :feature_available?, to: :current, allow_nil: true
def reset_current
RequestStore.delete(:current_license)
......@@ -198,10 +198,6 @@ class License < ActiveRecord::Base
features[feature].to_i > 0
end
def block_changes?
!current || current.block_changes?
end
def load_license
license = self.last
......@@ -272,6 +268,8 @@ class License < ActiveRecord::Base
end
def feature_available?(code)
return false if trial? && expired?
feature = FEATURE_CODES.fetch(code)
add_ons[feature].to_i > 0
end
......@@ -302,6 +300,16 @@ class License < ActiveRecord::Base
restricted_attr(:trial)
end
def active?
!expired?
end
def remaining_days
return 0 if expired?
(expires_at - Date.today).to_i
end
private
def restricted_attr(name, default = nil)
......
= link_to 'Buy License', Gitlab::SUBSCRIPTIONS_PLANS_URL, target: '_blank', rel: 'noopener noreferrer nofollow', class: "btn btn-new btn-inverted pull-right btn-buy-license"
= link_to 'Upload New License', new_admin_license_path, class: "btn pull-right btn-upload-license append-right-10"
- license_key = params[:trial_key]
#modal-upload-trial-license.modal-upload-trial-license.modal.fade.in{ tabindex: -1, role: 'dialog' }
.modal-dialog
.modal-content
.modal-body
%button.close{ type: 'button', data: { dismiss: 'modal' }, aria: { label: 'Close' } }
%span &times;
.modal-body-contents
.trial-activated-graphic.prepend-top-15
= custom_icon('ee_trial_license_activated', size: 100)
%h3.confirmation-title
Your trial license was issued!
%p.confirmation-desc.lead
Your trial license was issued and activated. Install it
to enjoy GitLab Enterprise Edition Premium for 30 days.
= form_for License.new, url: admin_license_path, html: { multipart: true, class: 'form-horizontal fieldset-form' } do |f|
= f.hidden_field :data, value: license_key
%a#hide-license.hide-license{ href: '#hide-license' }
Show license key
= icon('chevron-down')
%a#show-license.show-license{ href: '#show-license' }
Hide license key
= icon('chevron-up')
.well.trial-license-preview.prepend-top-15
= license_key
.modal-footer.form-actions
%button.btn.btn-default{ type: 'button', data: { dismiss: 'modal' } } Cancel
= f.submit 'Install license', class: 'btn btn-primary'
- page_title "License"
%h3.page-title
Your License
= render "upload_buy_license"
- if params[:trial_key].present?
= render "upload_trial_license"
%hr
.container.blank-state-container
.text-center
= custom_icon("missing_license")
%h4 You do not have a license.
%p.trial-description You can start a free trial of GitLab Enterprise Edition without any obligation or payment details.
= link_to 'Start free trial', new_trial_url, class: "btn btn-new btn-start-trial prepend-top-10"
- page_title "License"
%h3.page-title
Your License
= link_to 'Upload New License', new_admin_license_path, class: "btn btn-new pull-right"
- if current_license.trial?
= render "upload_buy_license"
- else
= link_to 'Upload New License', new_admin_license_path, class: "btn btn-new pull-right"
%hr
......@@ -32,11 +35,12 @@
Expired:
- else
Expires:
%strong
- if @license.will_expire?
= time_ago_with_tooltip @license.expires_at
- else
Never
- if @license.will_expire? && @license.active?
%strong= time_ago_with_tooltip(@license.expires_at)
- if @license.trial?
%span Free trial will expire in #{pluralize(@license.remaining_days, 'day')}
- else
%strong Never
- if @license.expired?
%span.label.label-danger.pull-right
......
.blank-state
.blank-state-icon
= custom_icon("ee_trial", size: 50)
.blank-state-body
%h3.blank-state-title
Unlock more features with GitLab Enterprise Edition
%p.blank-state-text
GitLab is free to use.
Many features for larger teams are part of
our paid
= succeed "." do
= link_to "Enterprise Edition products", "https://about.gitlab.com/products/", target: "_blank", rel: "noopener noreferrer nofollow"
You can try these out for free without
any obligation or payment details.
= link_to new_trial_url, class: "btn btn-new" do
Start free trial
.row.blank-state-parent-container
.section-container.section-welcome{ class: "#{ 'section-admin-welcome' if current_user.admin? }" }
- admin_without_ee_license = !current_license && current_user.admin?
.row.blank-state-parent-container{ class: ('has-start-trial-container' if admin_without_ee_license) }
.section-container.section-welcome{ class: ('col-md-6' if admin_without_ee_license) }
.container.section-body
.blank-state.blank-state-welcome
%h2.blank-state-welcome-title
......@@ -10,3 +12,7 @@
= render "blank_state_admin_welcome"
- else
= render "blank_state_welcome"
- if admin_without_ee_license
.col-md-6.section-container.section-ee-trial
.container.section-body
= render "blank_state_ee_trial"
- if license_message.present?
.broadcast-message
= icon('bullhorn')
= license_message
- BroadcastMessage.current&.each do |message|
= broadcast_message(message)
.page-with-sidebar{ class: page_with_sidebar_class }
.page-with-sidebar.js-page-with-sidebar{ class: [('page-with-new-sidebar' if @new_sidebar), page_gutter_class] }
- if show_new_nav?
- if defined?(nav) && nav
= render "layouts/nav/#{nav}"
......
%header.navbar.navbar-gitlab{ class: nav_header_class }
= render "layouts/header/ee_license_banner"
%header.navbar.navbar-gitlab.js-navbar-gitlab{ class: nav_header_class }
.navbar-border
%a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content
.container-fluid
......
- if license_message.present?
.alert.alert-dismissible.gitlab-ee-license-banner.hidden.js-gitlab-ee-license-banner{ role: 'alert', data: { license_expiry: current_license.expires_at } }
-# Show dismiss button only when license expiry is about trial license
- if current_license.trial?
%button.close{ type: 'button', 'data-dismiss' => 'alert', 'aria-label' => 'Dismiss banner' }
= icon('times', 'aria-hidden' => 'true')
%p
= license_message
%header.navbar.navbar-gitlab.navbar-gitlab-new{ class: nav_header_class }
= render "layouts/header/ee_license_banner"
%header.navbar.navbar-gitlab.navbar-gitlab-new.js-navbar-gitlab{ class: nav_header_class }
%a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content
.container-fluid
.header-content
......
<svg xmlns="http://www.w3.org/2000/svg" width="330" height="132" viewBox="0 0 330 132">
<g fill="none" fill-rule="evenodd">
<path fill="#000" fill-opacity=".03" d="M174.12 42c-.08 1-.12 2-.12 3 0 20.43 16.57 37 37 37s37-16.57 37-37c0-1-.04-2-.12-3-1.53 19.03-17.46 34-36.88 34s-35.35-14.97-36.88-34z"/>
<path fill="#EEE" fill-rule="nonzero" d="M211 78c-21.54 0-39-17.46-39-39s17.46-39 39-39 39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S230.33 4 211 4s-35 15.67-35 35 15.67 35 35 35z"/>
<g fill-rule="nonzero">
<path fill="#FEE1D3" d="M211.5 51c-6.42 0-12.26-2.84-17.43-8.4-1.32-1.43-1.43-3.58-.27-5.13C199 30.57 204.92 27 211.5 27s12.5 3.56 17.7 10.47c1.16 1.55 1.05 3.7-.27 5.12-5.17 5.53-11 8.4-17.43 8.4zm0-4c5.25 0 10.05-2.34 14.5-7.13-4.5-5.98-9.3-8.87-14.5-8.87-5.2 0-10 2.9-14.5 8.87 4.45 4.8 9.25 7.13 14.5 7.13z"/>
<path fill="#FC6D26" d="M211 47c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm0-4c2.2 0 4-1.8 4-4s-1.8-4-4-4-4 1.8-4 4 1.8 4 4 4zm0-1c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3z"/>
</g>
<path fill="#000" fill-opacity=".03" d="M88.12 83c-.08 1-.12 2-.12 3 0 20.43 16.57 37 37 37s37-16.57 37-37c0-1-.04-2-.12-3-1.53 19.03-17.46 34-36.88 34s-35.35-14.97-36.88-34z"/>
<path fill="#EEE" fill-rule="nonzero" d="M125 119c-21.54 0-39-17.46-39-39s17.46-39 39-39 39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35s-15.67-35-35-35-35 15.67-35 35 15.67 35 35 35z"/>
<path fill="#FEE1D3" fill-rule="nonzero" d="M116 86.34c2.33.83 4 3.05 4 5.66 0 3.3-2.7 6-6 6s-6-2.7-6-6c0-2.6 1.67-4.83 4-5.66V72h4v14.34zM128 66c5.52 0 10 4.48 10 10v12h-4V76c0-3.3-2.7-6-6-6v1.83c0 .55-.45 1-1 1-.24 0-.47-.1-.65-.24l-4.46-3.87c-.46-.36-.5-1-.15-1.4.03-.05.07-.1.1-.12l4.47-3.82c.42-.35 1.05-.3 1.4.1.16.2.25.43.25.66V66zm-14 28c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z"/>
<path fill="#FC6D26" fill-rule="nonzero" d="M114 74c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6zm0-4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm22 28c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6zm0-4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z"/>
<path fill="#000" fill-opacity=".03" d="M2.12 52C2.04 53 2 54 2 55c0 20.43 16.57 37 37 37s37-16.57 37-37c0-1-.04-2-.12-3C74.35 71.03 58.42 86 39 86S3.65 71.03 2.12 52z"/>
<path fill="#EEE" fill-rule="nonzero" d="M39 88C17.46 88 0 70.54 0 49s17.46-39 39-39 39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 14 39 14 4 29.67 4 49s15.67 35 35 35z"/>
<path fill="#6B4FBB" fill-rule="nonzero" d="M48 41h-4c0-2.76-2.24-5-5-5s-5 2.24-5 5h-4c0-4.97 4.03-9 9-9s9 4.03 9 9zm-18 0h4v3h-4v-3zm14 0h4v3h-4v-3z"/>
<path fill="#E1DBF2" fill-rule="nonzero" d="M30 47c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h18c.55 0 1-.45 1-1V48c0-.55-.45-1-1-1H30zm0-4h18c2.76 0 5 2.24 5 5v12c0 2.76-2.24 5-5 5H30c-2.76 0-5-2.24-5-5V48c0-2.76 2.24-5 5-5z"/>
<path fill="#6B4FBB" d="M38 53.73c-.6-.34-1-1-1-1.73 0-1.1.9-2 2-2s2 .9 2 2c0 .74-.4 1.4-1 1.73V55c0 .55-.45 1-1 1s-1-.45-1-1v-1.27z"/>
<path fill="#000" fill-opacity=".03" d="M254.12 92c-.08 1-.12 2-.12 3 0 20.43 16.57 37 37 37s37-16.57 37-37c0-1-.04-2-.12-3-1.53 19.03-17.46 34-36.88 34s-35.35-14.97-36.88-34z"/>
<path fill="#EEE" fill-rule="nonzero" d="M291 128c-21.54 0-39-17.46-39-39s17.46-39 39-39 39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35s-15.67-35-35-35-35 15.67-35 35 15.67 35 35 35z"/>
<path fill="#6B4BBE" fill-rule="nonzero" d="M292 78c5.52 0 10 4.48 10 10 0 2.28-.76 4.43-2.14 6.18-1.03 1.3-.8 3.2.5 4.22 1.3 1.02 3.2.8 4.2-.5 2.22-2.8 3.44-6.26 3.44-9.9 0-8.84-7.16-16-16-16v-3.13c0-.2-.06-.4-.17-.56-.3-.42-.93-.54-1.38-.23l-9.2 6.13c-.1.06-.2.16-.28.27-.3.45-.18 1.08.28 1.38l9.2 6.13c.16.1.35.17.55.17.55 0 1-.45 1-1V78z"/>
<path fill="#E1DBF2" fill-rule="nonzero" d="M290 100c-5.52 0-10-4.48-10-10 0-2.25.74-4.38 2.1-6.12 1-1.3.77-3.2-.54-4.2-1.3-1.02-3.2-.78-4.2.53C275.18 83 274 86.4 274 90c0 8.84 7.16 16 16 16v3.13c0 .55.45 1 1 1 .2 0 .4-.06.55-.17l9.2-6.13c.46-.3.6-.93.28-1.38-.07-.1-.17-.2-.28-.28l-9.2-6.13c-.45-.3-1.08-.2-1.38.27-.1.2-.17.4-.17.6v3.1z"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82"><g fill="none" fill-rule="evenodd"><path fill="#F9F9F9" d="M2.12 42c-.08.99-.12 1.99-.12 3 0 20.435 16.565 37 37 37s37-16.565 37-37c0-1.01-.04-2.01-.12-3C74.353 61.032 58.425 76 39 76 19.575 76 3.647 61.032 2.12 42z"/><path fill="#EEE" fill-rule="nonzero" d="M39 78C17.46 78 0 60.54 0 39S17.46 0 39 0s39 17.46 39 39-17.46 39-39 39zm0-4c19.33 0 35-15.67 35-35S58.33 4 39 4 4 19.67 4 39s15.67 35 35 35z"/><path fill="#6B4FBB" d="M36.6 44.687l-7.314-6.82a3 3 0 1 0-4.092 4.388l9.508 8.866a2.99 2.99 0 0 0 2.15.804 2.99 2.99 0 0 0 2.09-.952l15.686-16.821a3 3 0 1 0-4.388-4.092L36.6 44.687z"/></g></svg>
\ No newline at end of file
This diff is collapsed.
......@@ -9,7 +9,7 @@ end
# Needed to run migration
if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('licenses')
message = LicenseHelper.license_message(signed_in: true, is_admin: true, in_html: false)
if message.present?
if ::License.block_changes? && message.present?
warn "WARNING: #{message}"
end
end
......@@ -8,12 +8,6 @@ Feature: Admin license
And I visit admin license page
Then I should see to whom the license is licensed
Scenario: Viewing license when there is none
Given There is no license
And I visit admin license page
Then I should see a warning telling me there is no license
And I should be redirected to the license upload page
Scenario: Viewing expired license
Given there is a license
And the current license is expired
......
......@@ -14,10 +14,6 @@ class Spinach::Features::AdminLicense < Spinach::FeatureSteps
License.destroy_all
end
step 'I should see a warning telling me there is no license' do
expect(page).to have_content "No GitLab Enterprise Edition license has been provided yet."
end
step 'I should be redirected to the license upload page' do
expect(current_path).to eq(new_admin_license_path)
end
......@@ -27,7 +23,7 @@ class Spinach::Features::AdminLicense < Spinach::FeatureSteps
end
step 'I should see a warning telling me the license has expired' do
expect(page).to have_content "The GitLab Enterprise Edition license expired"
expect(page).to have_content "Your Enterprise Edition license expired"
end
step 'the current license blocks changes' do
......
......@@ -3,6 +3,8 @@ require_dependency 'gitlab/git'
module Gitlab
SUBDOMAIN_REGEX = %r{\Ahttps://[a-z0-9]+\.gitlab\.com\z}
COM_URL = 'https://gitlab.com'.freeze
SUBSCRIPTIONS_URL = 'https://customers.gitlab.com'.freeze
SUBSCRIPTIONS_PLANS_URL = "#{SUBSCRIPTIONS_URL}/plans".freeze
def self.com?
# Check `gl_subdomain?` as well to keep parity with gitlab.com
......
......@@ -15,4 +15,26 @@ describe Admin::LicensesController do
expect(flash[:alert]).to include 'Please enter or upload a license.'
end
end
describe 'GET show' do
context 'with an existent license' do
it 'renders the license details' do
allow(License).to receive(:current).and_return(create(:license))
get :show
expect(response).to render_template(:show)
end
end
context 'without a license' do
it 'renders missing license page' do
allow(License).to receive(:current).and_return(nil)
get :show
expect(response).to render_template(:missing)
end
end
end
end
......@@ -2,10 +2,26 @@ FactoryGirl.define do
factory :gitlab_license, class: "Gitlab::License" do
skip_create
trait :trial do
block_changes_at nil
restrictions do
{ trial: true }
end
end
trait :expired do
expires_at { 3.weeks.ago.to_date }
end
transient do
plan License::STARTER_PLAN
end
starts_at { Date.today - 1.month }
expires_at { Date.today + 11.months }
notify_users_at { |l| l.expires_at }
notify_admins_at { |l| l.expires_at }
block_changes_at { expires_at + 2.weeks }
notify_users_at { expires_at }
notify_admins_at { expires_at }
licensee do
{ "Name" => generate(:name) }
......@@ -20,27 +36,25 @@ FactoryGirl.define do
plan: plan
}
end
transient do
plan License::STARTER_PLAN
end
trait :trial do
restrictions do
{ trial: true }
end
end
end
factory :license do
transient do
plan nil
expired false
trial false
end
data { build(:gitlab_license, plan: plan).export }
end
data do
attrs = [:gitlab_license]
attrs << :trial if trial
attrs << :expired if expired
attrs << { plan: plan }
build(*attrs).export
end
factory :trial_license, class: License do
data { build(:gitlab_license, :trial).export }
# Disable validations when creating an expired license key
to_create {|instance| instance.save(validate: !expired) }
end
end
......@@ -26,5 +26,42 @@ feature "License Admin" do
end
end
end
context 'with an expired trial license' do
let!(:license) { create(:license, trial: true, expired: true) }
it 'does not mention blocking of changes' do
visit admin_license_path
page.within '.gitlab-ee-license-banner' do
expect(page).to have_content('Your Enterprise Edition trial license expired on')
expect(page).not_to have_content('Pushing code and creation of issues and merge requests has been disabled')
end
end
end
context 'when license key is provided in the query string' do
let(:license) { build(:license, data: build(:gitlab_license, restrictions: { active_user_count: 2000 }).export) }
before do
License.destroy_all
end
it 'shows the modal to install the license' do
visit admin_license_path(trial_key: license.data)
page.within '#modal-upload-trial-license' do
expect(page).to have_content('Your trial license was issued')
expect(page).to have_button('Install license')
end
end
it 'can install the license' do
visit admin_license_path(trial_key: license.data)
click_button 'Install license'
expect(page).to have_content('The license was successfully uploaded and is now active')
end
end
end
end
......@@ -11,18 +11,14 @@ describe LicenseHelper do
let(:is_admin) { true }
it 'displays correct error message for admin user' do
admin_msg = '<p>No GitLab Enterprise Edition license has been provided yet. Pushing code and creation of issues and merge requests has been disabled. <a href="/admin/license/new">Upload a license</a> in the admin area to activate this functionality.</p>'
expect(license_message(signed_in: true, is_admin: is_admin)).to eq(admin_msg)
expect(license_message(signed_in: true, is_admin: is_admin)).to be_blank
end
end
context 'normal user' do
let(:is_admin) { false }
it 'displays correct error message for normal user' do
user_msg = '<p>No GitLab Enterprise Edition license has been provided yet. Pushing code and creation of issues and merge requests has been disabled. Ask an admin to upload a license to activate this functionality.</p>'
expect(license_message(signed_in: true, is_admin: is_admin)).to eq(user_msg)
expect(license_message(signed_in: true, is_admin: is_admin)).to be_blank
end
end
end
......
......@@ -302,8 +302,24 @@ describe License do
allow(described_class).to receive(:current).and_return(nil)
end
it "returns true" do
expect(described_class.block_changes?).to be_truthy
it "returns false" do
expect(described_class.block_changes?).to be_falsey
end
end
context 'with an expired trial license' do
let!(:license) { create(:license, trial: true) }
it 'returns false' do
expect(described_class.block_changes?).to be_falsey
end
end
context 'with an expired normal license' do
let!(:license) { create(:license, expired: true) }
it 'returns true' do
expect(described_class.block_changes?).to eq(true)
end
end
......@@ -451,6 +467,19 @@ describe License do
expect { license.feature_available?(:invalid) }.to raise_error(KeyError)
end
context 'with an expired trial license' do
before(:all) do
described_class.destroy_all
create(:license, trial: true, expired: true)
end
::License::FEATURE_CODES.keys do |feature_code|
it "returns false for #{feature_code}" do
expect(license.feature_available?(feature_code)).to eq(false)
end
end
end
end
def build_license_with_add_ons(add_ons, plan: nil)
......
......@@ -781,18 +781,13 @@ describe 'Git HTTP requests' do
let(:env) { { user: user.username, password: user.password } }
before do
project.team << [user, :master]
end
it 'responds with status 403 Forbidden' do
msg = 'No GitLab Enterprise Edition license has been provided yet. Pushing code and creation of issues and merge requests has been disabled. Ask an admin to upload a license to activate this functionality.'
allow(License).to receive(:current).and_return(nil)
upload(path, env) do |response|
expect(response).to have_http_status(:forbidden)
expect(response.body).to eq(msg)
end
project.team << [user, :master]
end
it_behaves_like 'pulls are allowed'
it_behaves_like 'pushes are allowed'
end
end
......
......@@ -6,7 +6,7 @@ describe HistoricalDataWorker do
describe '#perform' do
context 'with a trial license' do
before do
FactoryGirl.create(:trial_license)
FactoryGirl.create(:license, trial: true)
end
it 'does not track historical data' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment