Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
ca6cf402
Commit
ca6cf402
authored
Jun 15, 2017
by
Oswaldo Ferreira
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add initial Groups/Billing and Profile/Billing routing and template
parent
c7837b50
Changes
33
Show whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
785 additions
and
2 deletions
+785
-2
app/assets/stylesheets/framework/buttons.scss
app/assets/stylesheets/framework/buttons.scss
+4
-0
app/assets/stylesheets/pages/billings.scss
app/assets/stylesheets/pages/billings.scss
+156
-0
app/controllers/application_controller.rb
app/controllers/application_controller.rb
+4
-0
app/controllers/groups/billings_controller.rb
app/controllers/groups/billings_controller.rb
+12
-0
app/controllers/profiles/billings_controller.rb
app/controllers/profiles/billings_controller.rb
+7
-0
app/helpers/billing_plans_helper.rb
app/helpers/billing_plans_helper.rb
+25
-0
app/services/fetch_subscription_plans_service.rb
app/services/fetch_subscription_plans_service.rb
+39
-0
app/views/admin/application_settings/_form.html.haml
app/views/admin/application_settings/_form.html.haml
+1
-1
app/views/groups/billings/index.html.haml
app/views/groups/billings/index.html.haml
+8
-0
app/views/layouts/nav/_new_profile_sidebar.html.haml
app/views/layouts/nav/_new_profile_sidebar.html.haml
+5
-0
app/views/layouts/nav/_profile.html.haml
app/views/layouts/nav/_profile.html.haml
+5
-0
app/views/profiles/billings/index.html.haml
app/views/profiles/billings/index.html.haml
+4
-0
app/views/shared/billings/_billing_plan.html.haml
app/views/shared/billings/_billing_plan.html.haml
+41
-0
app/views/shared/billings/_billing_plan_header.html.haml
app/views/shared/billings/_billing_plan_header.html.haml
+24
-0
app/views/shared/billings/_billing_plans.html.haml
app/views/shared/billings/_billing_plans.html.haml
+16
-0
app/views/shared/billings/plans/_bronze.svg
app/views/shared/billings/plans/_bronze.svg
+41
-0
app/views/shared/billings/plans/_early_adopter.svg
app/views/shared/billings/plans/_early_adopter.svg
+1
-0
app/views/shared/billings/plans/_free.svg
app/views/shared/billings/plans/_free.svg
+1
-0
app/views/shared/billings/plans/_gold.svg
app/views/shared/billings/plans/_gold.svg
+1
-0
app/views/shared/billings/plans/_silver.svg
app/views/shared/billings/plans/_silver.svg
+1
-0
changelogs/unreleased-ee/2344-display-group-and-user-plans-on-gl-com.yml
...leased-ee/2344-display-group-and-user-plans-on-gl-com.yml
+4
-0
config/routes/group.rb
config/routes/group.rb
+1
-0
config/routes/profile.rb
config/routes/profile.rb
+1
-0
ee/app/models/ee/application_setting.rb
ee/app/models/ee/application_setting.rb
+1
-1
ee/app/models/ee/namespace.rb
ee/app/models/ee/namespace.rb
+12
-0
ee/app/views/groups/ee/_settings_nav.html.haml
ee/app/views/groups/ee/_settings_nav.html.haml
+6
-0
lib/gitlab.rb
lib/gitlab.rb
+4
-0
spec/controllers/groups/billings_controller_spec.rb
spec/controllers/groups/billings_controller_spec.rb
+59
-0
spec/controllers/profiles/billings_controller_spec.rb
spec/controllers/profiles/billings_controller_spec.rb
+32
-0
spec/ee/spec/models/ee/namespace_spec.rb
spec/ee/spec/models/ee/namespace_spec.rb
+13
-0
spec/features/billings/billing_plans_spec.rb
spec/features/billings/billing_plans_spec.rb
+203
-0
spec/helpers/billing_plans_helper_spec.rb
spec/helpers/billing_plans_helper_spec.rb
+17
-0
spec/services/fetch_subscription_plans_service_spec.rb
spec/services/fetch_subscription_plans_service_spec.rb
+36
-0
No files found.
app/assets/stylesheets/framework/buttons.scss
View file @
ca6cf402
...
...
@@ -159,6 +159,10 @@
&
.btn-remove
{
@include
btn-outline
(
$white-light
,
$red-500
,
$red-500
,
$red-500
,
$white-light
,
$red-600
,
$red-600
,
$red-700
);
}
&
.btn-primary
{
@include
btn-outline
(
$white-light
,
$blue-500
,
$blue-500
,
$blue-500
,
$white-light
,
$blue-600
,
$blue-600
,
$blue-700
);
}
}
&
.btn-gray
{
...
...
app/assets/stylesheets/pages/billings.scss
0 → 100644
View file @
ca6cf402
.billing-plan-header
{
border-bottom
:
0
;
.billing-plan-logo
svg
{
height
:
100px
;
}
p
{
margin
:
0
;
&
:first-of-type
{
margin-top
:
16px
;
}
&
:last-of-type
{
margin-bottom
:
16px
;
}
}
}
.billing-plans-alert
{
margin
:
0
;
}
.billing-plans
{
display
:
flex
;
flex-direction
:
row
;
flex-wrap
:
wrap
;
justify-content
:
center
;
margin-top
:
8px
;
.panel
{
display
:
flex
;
flex-direction
:
column
;
margin
:
8px
;
width
:
100%
;
border
:
0
;
&
:first-of-type
{
margin-left
:
0
;
}
&
:last-of-type
{
margin-right
:
0
;
}
@media
(
min-width
:
$screen-sm-min
)
{
width
:
290px
;
}
.panel-heading
,
.panel-body
{
border-left
:
1px
solid
$list-border
;
border-right
:
1px
solid
$list-border
;
}
.panel-heading
{
background-color
:
$blue-500
;
color
:
$white-light
;
border
:
0
;
border-radius
:
4px
4px
0
0
;
font-size
:
20px
;
text-align
:
center
;
}
.panel-body
{
flex-grow
:
1
;
border-radius
:
0
0
4px
4px
;
border-bottom
:
1px
solid
$list-border
;
padding
:
0
;
display
:
flex
;
flex-direction
:
column
;
.price-per-month
{
display
:
flex
;
flex-direction
:
row
;
color
:
$blue-500
;
padding
:
16px
;
padding-bottom
:
0
;
justify-content
:
center
;
font-size
:
50px
;
font-weight
:
700
;
.billing-conditions
{
list-style
:
none
;
font-size
:
20px
;
font-weight
:
600
;
margin
:
auto
0
;
line-height
:
20px
;
padding
:
0
;
}
}
.price-per-year
{
color
:
$blue-500
;
text-align
:
center
;
font-size
:
12px
;
font-weight
:
600
;
padding-bottom
:
16px
;
height
:
32px
;
}
.feature-list
{
display
:
flex
;
flex-direction
:
column
;
flex-grow
:
1
;
text-align
:
center
;
margin
:
0
;
li
{
background-color
:
$gray-light
;
&
:first-child
{
border-top
:
1px
solid
$list-border
;
}
&
:last-child
{
border-bottom
:
1px
solid
$list-border
;
flex-grow
:
1
;
display
:
flex
;
flex-direction
:
column
;
justify-content
:
flex-end
;
&
:hover
{
background-color
:
$gray-light
;
}
}
}
}
.plan-action
{
padding
:
16px
;
.btn
{
width
:
100%
;
}
}
}
&
.current
{
.panel-heading
{
background-color
:
$blue-600
;
}
.panel-body
{
border-color
:
$blue-600
;
border-width
:
2px
;
.price-per-month
,
.price-per-year
{
color
:
$blue-600
;
}
}
}
}
}
app/controllers/application_controller.rb
View file @
ca6cf402
...
...
@@ -104,6 +104,10 @@ class ApplicationController < ActionController::Base
sessionless_sign_in
(
user
)
end
def
verify_namespace_plan_check_enabled
render_404
unless
current_application_settings
.
should_check_namespace_plan?
end
def
log_exception
(
exception
)
Raven
.
capture_exception
(
exception
)
if
sentry_enabled?
...
...
app/controllers/groups/billings_controller.rb
0 → 100644
View file @
ca6cf402
class
Groups::BillingsController
<
Groups
::
ApplicationController
before_action
:authorize_admin_group!
before_action
:verify_namespace_plan_check_enabled
layout
'group_settings'
def
index
@top_most_group
=
@group
.
root_ancestor
if
@group
.
has_parent?
current_plan
=
(
@top_most_group
||
@group
).
actual_plan
@plans_data
=
FetchSubscriptionPlansService
.
new
(
plan:
current_plan
).
execute
end
end
app/controllers/profiles/billings_controller.rb
0 → 100644
View file @
ca6cf402
class
Profiles::BillingsController
<
Profiles
::
ApplicationController
before_action
:verify_namespace_plan_check_enabled
def
index
@plans_data
=
FetchSubscriptionPlansService
.
new
(
plan:
current_user
.
namespace
.
actual_plan
).
execute
end
end
app/helpers/billing_plans_helper.rb
0 → 100644
View file @
ca6cf402
module
BillingPlansHelper
def
subscription_plan_info
(
plans_data
,
current_plan_code
)
plans_data
.
find
{
|
plan
|
plan
.
code
==
current_plan_code
}
end
def
number_to_plan_currency
(
value
)
number_to_currency
(
value
,
unit:
'$'
,
strip_insignificant_zeros:
true
,
format:
"%u%n"
)
end
def
current_plan?
(
plan
)
plan
.
purchase_link
&
.
action
==
'current_plan'
end
def
has_plan_purchase_link?
(
plans_data
)
plans_data
.
any?
{
|
plan
|
plan
.
purchase_link
&
.
href
}
end
def
plan_purchase_link
(
href
,
link_text
)
if
href
link_to
link_text
,
href
,
class:
'btn btn-primary btn-inverted'
else
button_tag
link_text
,
class:
'btn disabled'
end
end
end
app/services/fetch_subscription_plans_service.rb
0 → 100644
View file @
ca6cf402
class
FetchSubscriptionPlansService
URL
=
'https://customers.gitlab.com/gitlab_plans'
.
freeze
def
initialize
(
plan
:)
@plan
=
plan
end
def
execute
cached
{
send_request
}
end
private
def
send_request
response
=
HTTParty
.
get
(
URL
,
query:
{
plan:
@plan
},
headers:
{
'Accept'
=>
'application/json'
})
JSON
.
parse
(
response
.
body
).
map
{
|
plan
|
Hashie
::
Mash
.
new
(
plan
)
}
rescue
=>
e
Rails
.
logger
.
info
"Unable to connect to GitLab Customers App
#{
e
}
"
nil
end
def
cached
if
plans_data
=
cache
.
read
(
cache_key
)
plans_data
else
cache
.
fetch
(
cache_key
,
force:
true
,
expires_in:
1
.
day
)
{
yield
}
end
end
def
cache
Rails
.
cache
end
def
cache_key
"subscription-plans-
#{
@plan
}
"
end
end
app/views/admin/application_settings/_form.html.haml
View file @
ca6cf402
...
...
@@ -99,7 +99,7 @@
=
f
.
check_box
:user_default_external
Newly registered users will by default be external
-
if
Gitlab
.
com?
||
Rails
.
env
.
development
?
-
if
current_application_settings
.
should_check_namespace_plan
?
.form-group
=
f
.
label
:check_namespace_plan
,
'Check feature availability on namespace plan'
,
class:
'control-label col-sm-2'
.col-sm-10
...
...
app/views/groups/billings/index.html.haml
0 → 100644
View file @
ca6cf402
-
page_title
"Billing"
=
render
"groups/settings_head"
-
if
@top_most_group
-
top_most_group_plan
=
subscription_plan_info
(
@plans_data
,
@top_most_group
.
actual_plan
)
=
render
'shared/billings/billing_plan_header'
,
namespace:
@group
,
plan:
top_most_group_plan
,
parent_group:
@top_most_group
-
else
=
render
'shared/billings/billing_plans'
,
plans_data:
@plans_data
,
namespace:
@group
app/views/layouts/nav/_new_profile_sidebar.html.haml
View file @
ca6cf402
...
...
@@ -20,6 +20,11 @@
=
custom_icon
(
'account'
)
%span
.nav-item-name
Account
-
if
current_application_settings
.
should_check_namespace_plan?
=
nav_link
(
controller: :billings
)
do
=
link_to
profile_billings_path
,
title:
'Billing'
do
%span
Billing
-
if
current_application_settings
.
user_oauth_applications?
=
nav_link
(
controller:
'oauth/applications'
)
do
=
link_to
applications_profile_path
,
title:
'Applications'
do
...
...
app/views/layouts/nav/_profile.html.haml
View file @
ca6cf402
...
...
@@ -12,6 +12,11 @@
=
link_to
profile_account_path
,
title:
'Account'
do
%span
Account
-
if
current_application_settings
.
should_check_namespace_plan?
=
nav_link
(
controller: :billings
)
do
=
link_to
profile_billings_path
,
title:
'Billing'
do
%span
Billing
-
if
current_application_settings
.
user_oauth_applications?
=
nav_link
(
controller:
'oauth/applications'
)
do
=
link_to
applications_profile_path
,
title:
'Applications'
do
...
...
app/views/profiles/billings/index.html.haml
0 → 100644
View file @
ca6cf402
-
page_title
'Billing'
=
render
'profiles/head'
=
render
'shared/billings/billing_plans'
,
plans_data:
@plans_data
,
namespace:
current_user
.
namespace
app/views/shared/billings/_billing_plan.html.haml
0 → 100644
View file @
ca6cf402
.panel
{
class:
(
'current'
if
current_plan?
(
plan
))
}
.panel-heading
=
plan
.
name
.panel-body
.price-per-month
.append-right-5
=
number_to_plan_currency
(
plan
.
price_per_month
)
%ul
.billing-conditions
%li
=
s_
(
"BillingPlans|per user"
)
%li
=
s_
(
"BillingPlans|monthly"
)
.price-per-year
-
if
plan
.
price_per_year
>
0
-
price_per_year
=
number_to_plan_currency
(
plan
.
price_per_year
)
=
s_
(
"BillingPlans|paid annually at %{price_per_year}"
)
%
{
price_per_year:
price_per_year
}
%ul
.feature-list.bordered-list
-
plan
.
features
.
each
do
|
feature
|
%li
-
if
feature
.
highlight
%strong
=
feature
.
title
-
else
=
feature
.
title
%li
-
if
plan
.
about_page_href
=
link_to
s_
(
"BillingPlans|See all %{plan_name} features"
)
%
{
plan_name:
plan
.
name
},
plan
.
about_page_href
-
purchase_link
=
plan
.
purchase_link
-
if
purchase_link
.plan-action
-
href
=
purchase_link
.
href
-
case
purchase_link
.
action
-
when
'downgrade'
=
plan_purchase_link
(
href
,
s_
(
"Billinglans|Downgrade"
))
-
when
'current_plan'
=
plan_purchase_link
(
href
,
s_
(
"BillingPlans|Current plan"
))
-
when
'upgrade'
=
plan_purchase_link
(
href
,
s_
(
"BillingPlans|Upgrade"
))
app/views/shared/billings/_billing_plan_header.html.haml
0 → 100644
View file @
ca6cf402
-
parent_group
=
local_assigns
[
:parent_group
]
.billing-plan-header.content-block.center
.billing-plan-logo
-
if
Namespace
::
EE_PLANS
.
keys
.
include?
(
plan
.
code
)
=
render
"shared/billings/plans/
#{
plan
.
code
}
.svg"
-
elsif
plan
.
free?
=
render
"shared/billings/plans/free.svg"
%h4
-
plan_link
=
plan
.
about_page_href
?
link_to
(
plan
.
name
,
plan
.
about_page_href
)
:
plan
.
name
-
if
namespace
==
current_user
.
namespace
=
s_
(
"BillingPlans|You are currently on the %{plan_link} plan."
).
html_safe
%
{
plan_link:
plan_link
}
-
else
=
s_
(
"BillingPlans|%{group_name} is currently on the %{plan_link} plan."
).
html_safe
%
{
group_name:
namespace
.
full_name
,
plan_link:
plan_link
}
-
if
parent_group
%p
=
s_
(
"BillingPlans|This group uses the plan associated with its parent group."
)
-
parent_billing_page_link
=
link_to
parent_group
.
full_name
,
group_billings_path
(
parent_group
)
%p
=
s_
(
"BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
).
html_safe
%
{
parent_billing_page_link:
parent_billing_page_link
}
=
link_to
s_
(
"BillingPlans|Manage plan"
),
group_billings_path
(
parent_group
),
class:
'btn btn-success'
-
else
-
faq_link
=
link_to
s_
(
"BillingPlans|frequently asked questions"
),
"https://about.gitlab.com/gitlab-com/#faq"
=
s_
(
"BillingPlans|Learn more about each plan by reading our %{faq_link}."
).
html_safe
%
{
faq_link:
faq_link
}
app/views/shared/billings/_billing_plans.html.haml
0 → 100644
View file @
ca6cf402
-
current_plan
=
subscription_plan_info
(
plans_data
,
namespace
.
actual_plan
)
-
if
current_plan
=
render
'shared/billings/billing_plan_header'
,
namespace:
namespace
,
plan:
current_plan
-
unless
has_plan_purchase_link?
(
plans_data
)
.billing-plans-alert.panel.panel-warning.prepend-top-10
.panel-heading
=
s_
(
"BillingPlans|Automatic downgrade and upgrade to some plans is currently not available."
)
-
customer_support_url
=
'https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=334447'
;
-
customer_support_link
=
link_to
s_
(
"BillingPlans|Customer Support"
),
customer_support_url
=
s_
(
"BillingPlans|Please contact %{customer_support_link} in that case."
).
html_safe
%
{
customer_support_link:
customer_support_link
}
.billing-plans
-
plans_data
.
each
do
|
plan
|
=
render
'shared/billings/billing_plan'
,
namespace:
namespace
,
plan:
plan
app/views/shared/billings/plans/_bronze.svg
0 → 100644
View file @
ca6cf402
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 106 110"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<defs>
<path
id=
"a"
d=
"M10,0 L14,0 L14,0 C19.5228475,-1.01453063e-15 24,4.4771525 24,10 L24,37 L24,37 C24,39.7614237 21.7614237,42 19,42 L5,42 L5,42 C2.23857625,42 1.2263553e-15,39.7614237 8.8817842e-16,37 L0,10 L0,10 C-6.76353751e-16,4.4771525 4.4771525,1.01453063e-15 10,0 Z"
/>
<path
id=
"b"
d=
"M10,0 L14,0 L14,0 C19.5228475,-1.01453063e-15 24,4.4771525 24,10 L24,37 L24,37 C24,39.7614237 21.7614237,42 19,42 L5,42 L5,42 C2.23857625,42 1.2263553e-15,39.7614237 8.8817842e-16,37 L0,10 L0,10 C-6.76353751e-16,4.4771525 4.4771525,1.01453063e-15 10,0 Z"
/>
<rect
id=
"c"
width=
"46"
height=
"20"
x=
"8"
y=
"9"
rx=
"10"
/>
</defs>
<g
fill=
"none"
fill-rule=
"evenodd"
>
<rect
width=
"106"
height=
"106"
x=
"1"
y=
"4"
fill=
"#F9F9F9"
rx=
"12"
/>
<rect
width=
"106"
height=
"106"
fill=
"#FFFFFF"
rx=
"10"
/>
<path
fill=
"#EEEEEE"
fill-rule=
"nonzero"
d=
"M10,4 C6.6862915,4 4,6.6862915 4,10 L4,96 C4,99.3137085 6.6862915,102 10,102 L96,102 C99.3137085,102 102,99.3137085 102,96 L102,10 C102,6.6862915 99.3137085,4 96,4 L10,4 Z M10,0 L96,0 C101.522847,-1.01453063e-15 106,4.4771525 106,10 L106,96 C106,101.522847 101.522847,106 96,106 L10,106 C4.4771525,106 6.76353751e-16,101.522847 0,96 L0,10 C-6.76353751e-16,4.4771525 4.4771525,1.01453063e-15 10,0 Z"
/>
<g
transform=
"translate(22 23)"
>
<path
fill=
"#FFFFFF"
stroke=
"#EEEEEE"
stroke-width=
"4"
d=
"M13,5 L49,5 L49,5 C54.5228475,5 59,9.4771525 59,15 L59,28 L59,28 C59,30.7614237 56.7614237,33 54,33 L8,33 L8,33 C5.23857625,33 3,30.7614237 3,28 L3,15 L3,15 C3,9.4771525 7.4771525,5 13,5 Z"
/>
<g
transform=
"translate(38)"
>
<path
fill=
"#FFFFFF"
stroke=
"#EFEDF8"
stroke-width=
"4"
d=
"M6,38 L18,38 L18,44 L18,44 C18,45.1045695 17.1045695,46 16,46 L8,46 L8,46 C6.8954305,46 6,45.1045695 6,44 L6,38 Z"
/>
<use
fill=
"#FFFFFF"
xlink:href=
"#a"
/>
<path
stroke=
"#E1DBF1"
stroke-width=
"4"
d=
"M10,2 C5.581722,2 2,5.581722 2,10 L2,37 C2,38.6568542 3.34314575,40 5,40 L19,40 C20.6568542,40 22,38.6568542 22,37 L22,10 C22,5.581722 18.418278,2 14,2 L10,2 Z"
/>
<g
transform=
"translate(3 51)"
>
<rect
width=
"10"
height=
"3"
x=
"4"
fill=
"#FC6D26"
rx=
"1.5"
/>
<rect
width=
"14"
height=
"3"
x=
"2"
y=
"5"
fill=
"#FDC4A8"
rx=
"1.5"
/>
<rect
width=
"18"
height=
"3"
y=
"10"
fill=
"#FEE1D3"
rx=
"1.5"
/>
<rect
width=
"18"
height=
"3"
y=
"15"
fill=
"#FEF0E8"
rx=
"1.5"
/>
</g>
</g>
<path
fill=
"#FFFFFF"
stroke=
"#EFEDF8"
stroke-width=
"4"
d=
"M6,38 L18,38 L18,44 L18,44 C18,45.1045695 17.1045695,46 16,46 L8,46 L8,46 C6.8954305,46 6,45.1045695 6,44 L6,38 Z"
/>
<use
fill=
"#FFFFFF"
xlink:href=
"#b"
/>
<path
stroke=
"#E1DBF1"
stroke-width=
"4"
d=
"M10,2 C5.581722,2 2,5.581722 2,10 L2,37 C2,38.6568542 3.34314575,40 5,40 L19,40 C20.6568542,40 22,38.6568542 22,37 L22,10 C22,5.581722 18.418278,2 14,2 L10,2 Z"
/>
<g
transform=
"translate(3 51)"
>
<rect
width=
"10"
height=
"3"
x=
"4"
fill=
"#FC6D26"
rx=
"1.5"
/>
<rect
width=
"14"
height=
"3"
x=
"2"
y=
"5"
fill=
"#FDC4A8"
rx=
"1.5"
/>
<rect
width=
"18"
height=
"3"
y=
"10"
fill=
"#FEE1D3"
rx=
"1.5"
/>
<rect
width=
"18"
height=
"3"
y=
"15"
fill=
"#FEF0E8"
rx=
"1.5"
/>
</g>
<g>
<use
fill=
"#FFFFFF"
xlink:href=
"#c"
/>
<rect
width=
"42"
height=
"16"
x=
"10"
y=
"11"
stroke=
"#6B4FBB"
stroke-width=
"4"
rx=
"8"
/>
</g>
<circle
cx=
"26"
cy=
"19"
r=
"2"
fill=
"#6B4FBB"
/>
<circle
cx=
"36"
cy=
"19"
r=
"2"
fill=
"#6B4FBB"
/>
</g>
</g>
</svg>
app/views/shared/billings/plans/_early_adopter.svg
0 → 100644
View file @
ca6cf402
<svg
xmlns=
"http://www.w3.org/2000/svg"
width=
"106"
height=
"110"
viewBox=
"0 0 106 110"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
><defs><circle
id=
"a"
cx=
"20"
cy=
"40"
r=
"5"
/><circle
id=
"b"
cx=
"20"
cy=
"20"
r=
"20"
/></defs><g
fill=
"none"
fill-rule=
"evenodd"
><rect
width=
"106"
height=
"106"
y=
"4"
fill=
"#F9F9F9"
rx=
"12"
/><rect
width=
"106"
height=
"106"
fill=
"#FFF"
rx=
"10"
/><path
fill=
"#EEE"
fill-rule=
"nonzero"
d=
"M10 4a6 6 0 0 0-6 6v86a6 6 0 0 0 6 6h86a6 6 0 0 0 6-6V10a6 6 0 0 0-6-6H10zm0-4h86c5.523 0 10 4.477 10 10v86c0 5.523-4.477 10-10 10H10c-5.523 0-10-4.477-10-10V10C0 4.477 4.477 0 10 0z"
/><path
fill=
"#FDC4A8"
d=
"M79.187 33.187l-.974 1.782a.3.3 0 0 1-.527 0l-.974-1.782-1.781-.974a.3.3 0 0 1 0-.526l1.781-.974.974-1.782a.3.3 0 0 1 .527 0l.974 1.782 1.781.974a.3.3 0 0 1 0 .526l-1.78.974z"
/><path
fill=
"#FEE1D3"
d=
"M84.955 70.955l-1.328 2.428a.3.3 0 0 1-.526 0l-1.328-2.428-2.428-1.328a.3.3 0 0 1 0-.526l2.428-1.328 1.328-2.428a.3.3 0 0 1 .526 0l1.328 2.428 2.428 1.328a.3.3 0 0 1 0 .526l-2.428 1.328z"
/><path
fill=
"#FC6D26"
d=
"M19.187 65.187l-.974 1.782a.3.3 0 0 1-.527 0l-.974-1.782-1.781-.974a.3.3 0 0 1 0-.526l1.781-.974.974-1.782a.3.3 0 0 1 .527 0l.974 1.782 1.781.974a.3.3 0 0 1 0 .526l-1.78.974z"
/><ellipse
cx=
"53"
cy=
"90"
fill=
"#F9F9F9"
rx=
"22"
ry=
"2"
/><path
fill=
"#6B4FBB"
fill-rule=
"nonzero"
d=
"M45.215 59.777c0 9.383 2.722 16.768 9.284 19.746 3.385 1.537 5.542 1.513 8.965.543.793-.224.868-.245 1.171-.321 2.864-.715 4.901-.054 8.558 3.637a1 1 0 1 0 1.42-1.407c-4.132-4.172-6.882-5.064-10.462-4.17-.333.083-.412.105-1.232.337-3.01.852-4.705.87-7.594-.44-5.647-2.564-8.11-9.245-8.11-17.925a1 1 0 1 0-2 0z"
/><g
transform=
"rotate(-5 227.182 -255.297)"
><use
fill=
"#FFF"
xlink:href=
"#a"
/><circle
cx=
"20"
cy=
"40"
r=
"3"
stroke=
"#C3B8E3"
stroke-width=
"4"
/><use
fill=
"#FFF"
xlink:href=
"#b"
/><circle
cx=
"20"
cy=
"20"
r=
"18"
stroke=
"#C3B8E3"
stroke-width=
"4"
/></g><path
fill=
"#6B4FBB"
d=
"M44.535 40.745l-1.856.975a1 1 0 0 1-1.451-1.054l.354-2.066a1 1 0 0 0-.287-.885l-1.501-1.464a1 1 0 0 1 .554-1.705l2.074-.302a1 1 0 0 0 .753-.547l.928-1.88a1 1 0 0 1 1.794 0l.928 1.88a1 1 0 0 0 .753.547l2.074.302a1 1 0 0 1 .554 1.705l-1.5 1.464a1 1 0 0 0-.288.885l.354 2.066a1 1 0 0 1-1.451 1.054l-1.856-.975a1 1 0 0 0-.93 0z"
/></g></svg>
\ No newline at end of file
app/views/shared/billings/plans/_free.svg
0 → 100644
View file @
ca6cf402
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 106 110"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
><defs><circle
id=
"0"
cx=
"20"
cy=
"40"
r=
"5"
/><circle
id=
"1"
cx=
"20"
cy=
"20"
r=
"20"
/></defs><g
fill=
"none"
fill-rule=
"evenodd"
><rect
width=
"106"
height=
"106"
y=
"4"
fill=
"#f9f9f9"
rx=
"12"
/><rect
width=
"106"
height=
"106"
fill=
"#fff"
rx=
"10"
/><path
fill=
"#eee"
fill-rule=
"nonzero"
d=
"m10 4c-3.314 0-6 2.686-6 6v86c0 3.314 2.686 6 6 6h86c3.314 0 6-2.686 6-6v-86c0-3.314-2.686-6-6-6h-86m0-4h86c5.523 0 10 4.477 10 10v86c0 5.523-4.477 10-10 10h-86c-5.523 0-10-4.477-10-10v-86c0-5.523 4.477-10 10-10"
/><path
fill=
"#fdc4a8"
d=
"m77.95 33.7l-1.948.571c-.159.047-.326-.045-.372-.204-.016-.055-.016-.114 0-.169l.571-1.948-.571-1.948c-.047-.159.045-.326.204-.372.055-.016.114-.016.169 0l1.948.571 1.948-.571c.159-.047.326.045.372.204.016.055.016.114 0 .169l-.571 1.948.571 1.948c.047.159-.045.326-.204.372-.055.016-.114.016-.169 0l-1.948-.571"
transform=
"matrix(.70711-.70711.70711.70711.239 64.48)"
/><path
fill=
"#fee1d3"
d=
"m83.36 71.61l-2.656.778c-.159.047-.326-.045-.372-.204-.016-.055-.016-.114 0-.169l.778-2.656-.778-2.656c-.047-.159.045-.326.204-.372.055-.016.114-.016.169 0l2.656.778 2.656-.778c.159-.047.326.045.372.204.016.055.016.114 0 .169l-.778 2.656.778 2.656c.047.159-.045.326-.204.372-.055.016-.114.016-.169 0l-2.656-.778"
transform=
"matrix(.70711-.70711.70711.70711-24.631 79.26)"
/><path
fill=
"#fc6d26"
d=
"m17.95 65.7l-1.948.571c-.159.047-.326-.045-.372-.204-.016-.055-.016-.114 0-.169l.571-1.948-.571-1.948c-.047-.159.045-.326.204-.372.055-.016.114-.016.169 0l1.948.571 1.948-.571c.159-.047.326.045.372.204.016.055.016.114 0 .169l-.571 1.948.571 1.948c.047.159-.045.326-.204.372-.055.016-.114.016-.169 0l-1.948-.571"
transform=
"matrix(.70711-.70711.70711.70711-39.962 31.423)"
/><ellipse
cx=
"53"
cy=
"90"
fill=
"#f9f9f9"
rx=
"22"
ry=
"2"
/><g
transform=
"translate(23 15)"
><path
fill=
"#6b4fbb"
fill-rule=
"nonzero"
d=
"m22.21 44.777c0 9.383 2.722 16.767 9.284 19.746 3.385 1.537 5.542 1.513 8.966.543.792-.224.867-.245 1.171-.321 2.863-.715 4.901-.054 8.557 3.638.389.392 1.022.395 1.414.007.392-.389.395-1.022.007-1.414-4.133-4.172-6.882-5.064-10.463-4.17-.333.083-.412.105-1.232.337-3.01.853-4.705.871-7.594-.44-5.648-2.563-8.111-9.244-8.111-17.924 0-.552-.448-1-1-1-.552 0-1 .448-1 1"
/><g
transform=
"matrix(.99619-.08716.08716.99619.115 3.829)"
><use
fill=
"#fff"
xlink:href=
"#0"
/><circle
cx=
"20"
cy=
"40"
r=
"3"
stroke=
"#c3b8e3"
stroke-width=
"4"
/><g><use
fill=
"#fff"
xlink:href=
"#1"
/><circle
cx=
"20"
cy=
"20"
r=
"18"
stroke=
"#c3b8e3"
stroke-width=
"4"
/></g></g></g></g></svg>
app/views/shared/billings/plans/_gold.svg
0 → 100644
View file @
ca6cf402
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 106 110"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
><defs><path
id=
"1"
d=
"m45 25.823c9.187 0 25-2.539 25-10.823 0-8.284-11.193-15-25-15-13.807 0-25 6.716-25 15 0 8.284 15.813 10.823 25 10.823"
fill=
"#fff"
/><ellipse
id=
"0"
cx=
"45"
cy=
"28"
rx=
"45"
ry=
"17"
/></defs><g
fill=
"none"
fill-rule=
"evenodd"
><rect
width=
"106"
height=
"106"
y=
"4"
fill=
"#f9f9f9"
rx=
"12"
/><rect
width=
"106"
height=
"106"
fill=
"#fff"
rx=
"10"
/><path
fill=
"#eee"
fill-rule=
"nonzero"
d=
"m10 4c-3.314 0-6 2.686-6 6v86c0 3.314 2.686 6 6 6h86c3.314 0 6-2.686 6-6v-86c0-3.314-2.686-6-6-6h-86m0-4h86c5.523 0 10 4.477 10 10v86c0 5.523-4.477 10-10 10h-86c-5.523 0-10-4.477-10-10v-86c0-5.523 4.477-10 10-10"
/><g
transform=
"translate(8 11)"
><g
fill-rule=
"nonzero"
transform=
"translate(16 47)"
><path
fill=
"#fef0e8"
d=
"m4.455 1.761c4.504 2.62 13.518 4.239 23.545 4.239 9.962 0 18.929-1.599 23.459-4.19.959-.548 1.292-1.77.743-2.729-.548-.959-1.77-1.292-2.729-.743-3.802 2.174-12.146 3.662-21.474 3.662-9.383 0-17.767-1.506-21.533-3.697-.955-.555-2.179-.232-2.734.723-.555.955-.232 2.179.723 2.734"
/><path
fill=
"#fdc4a8"
d=
"m2.888 11.835c4.995 2.569 14.991 4.165 26.11 4.165 11.04 0 20.978-1.574 26.01-4.111.986-.498 1.382-1.7.884-2.687-.498-.986-1.7-1.382-2.687-.884-4.352 2.197-13.734 3.682-24.2 3.682-10.54 0-19.974-1.506-24.282-3.722-.982-.505-2.188-.118-2.693.864-.505.982-.118 2.188.864 2.693"
/><path
fill=
"#fc6d26"
d=
"m1.289 21.931c5.361 2.508 15.948 4.069 27.711 4.069 11.581 0 22.03-1.513 27.465-3.956 1.01-.453 1.457-1.637 1-2.644-.453-1.01-1.637-1.457-2.644-1-4.813 2.164-14.759 3.604-25.825 3.604-11.229 0-21.298-1.484-26.02-3.692-1-.468-2.191-.037-2.659.964-.468 1-.037 2.191.964 2.659"
/><path
fill=
"#fef0e8"
d=
"m-.617 31.881c5.631 2.534 16.981 4.119 29.617 4.119 12.637 0 23.987-1.586 29.618-4.12 1.01-.453 1.456-1.637 1-2.645-.453-1.01-1.637-1.456-2.645-1-5 2.251-15.857 3.768-27.976 3.768-12.12 0-22.973-1.517-27.975-3.767-1.01-.453-2.191-.004-2.645 1-.453 1.01-.004 2.191 1 2.645"
/></g><use
fill=
"#fff"
xlink:href=
"#0"
/><ellipse
cx=
"45"
cy=
"28"
stroke=
"#e1dbf1"
stroke-width=
"4"
rx=
"43"
ry=
"15"
/><use
xlink:href=
"#1"
/><path
stroke=
"#c3b8e3"
stroke-width=
"4"
d=
"m45 23.823c12.11 0 23-3.567 23-8.823 0-6.908-10.154-13-23-13-12.846 0-23 6.092-23 13 0 5.256 10.894 8.823 23 8.823"
/><path
fill=
"#6b4fbb"
d=
"m14.5 29c-1.381 0-2.5-1.119-2.5-2.5 0-1.381 1.119-2.5 2.5-2.5 1.381 0 2.5 1.119 2.5 2.5 0 1.381-1.119 2.5-2.5 2.5m11 5c-1.381 0-2.5-1.119-2.5-2.5 0-1.381 1.119-2.5 2.5-2.5 1.381 0 2.5 1.119 2.5 2.5 0 1.381-1.119 2.5-2.5 2.5m13 2c-1.381 0-2.5-1.119-2.5-2.5 0-1.381 1.119-2.5 2.5-2.5 1.381 0 2.5 1.119 2.5 2.5 0 1.381-1.119 2.5-2.5 2.5m13 0c-1.381 0-2.5-1.119-2.5-2.5 0-1.381 1.119-2.5 2.5-2.5 1.381 0 2.5 1.119 2.5 2.5 0 1.381-1.119 2.5-2.5 2.5m13-2c-1.381 0-2.5-1.119-2.5-2.5 0-1.381 1.119-2.5 2.5-2.5 1.381 0 2.5 1.119 2.5 2.5 0 1.381-1.119 2.5-2.5 2.5m11-5c-1.381 0-2.5-1.119-2.5-2.5 0-1.381 1.119-2.5 2.5-2.5 1.381 0 2.5 1.119 2.5 2.5 0 1.381-1.119 2.5-2.5 2.5"
/></g></g></svg>
app/views/shared/billings/plans/_silver.svg
0 → 100644
View file @
ca6cf402
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 106 110"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
><g
fill=
"none"
fill-rule=
"evenodd"
><rect
width=
"106"
height=
"106"
y=
"4"
fill=
"#f9f9f9"
rx=
"12"
/><rect
width=
"106"
height=
"106"
fill=
"#fff"
rx=
"10"
/><path
fill=
"#eee"
fill-rule=
"nonzero"
d=
"m10 4c-3.314 0-6 2.686-6 6v86c0 3.314 2.686 6 6 6h86c3.314 0 6-2.686 6-6v-86c0-3.314-2.686-6-6-6h-86m0-4h86c5.523 0 10 4.477 10 10v86c0 5.523-4.477 10-10 10h-86c-5.523 0-10-4.477-10-10v-86c0-5.523 4.477-10 10-10"
/><path
fill=
"#fdc4a8"
d=
"m88.36 49.614l-2.656.778c-.159.047-.326-.045-.372-.204-.016-.055-.016-.114 0-.169l.778-2.656-.778-2.656c-.047-.159.045-.326.204-.372.055-.016.114-.016.169 0l2.656.778 2.656-.778c.159-.047.326.045.372.204.016.055.016.114 0 .169l-.778 2.656.778 2.656c.047.159-.045.326-.204.372-.055.016-.114.016-.169 0l-2.656-.778"
transform=
"matrix(.70711-.70711.70711.70711-7.61 76.36)"
/><g
fill=
"#fc6d26"
><path
d=
"m19.95 39.7l-1.948.571c-.159.047-.326-.045-.372-.204-.016-.055-.016-.114 0-.169l.571-1.948-.571-1.948c-.047-.159.045-.326.204-.372.055-.016.114-.016.169 0l1.948.571 1.948-.571c.159-.047.326.045.372.204.016.055.016.114 0 .169l-.571 1.948.571 1.948c.047.159-.045.326-.204.372-.055.016-.114.016-.169 0l-1.948-.571"
transform=
"matrix(.70711-.70711.70711.70711-20.991 25.22)"
/><path
d=
"m73.95 17.7l-1.948.571c-.159.047-.326-.045-.372-.204-.016-.055-.016-.114 0-.169l.571-1.948-.571-1.948c-.047-.159.045-.326.204-.372.055-.016.114-.016.169 0l1.948.571 1.948-.571c.159-.047.326.045.372.204.016.055.016.114 0 .169l-.571 1.948.571 1.948c.047.159-.045.326-.204.372-.055.016-.114.016-.169 0l-1.948-.571"
transform=
"matrix(.70711-.70711.70711.70711 10.381 56.962)"
/></g><path
fill=
"#fef0e8"
d=
"m27 83h10v17h-10z"
/><path
fill=
"#fee1d3"
d=
"m27 83h5v17h-5z"
/><path
fill=
"#fef0e8"
d=
"m48 83h10v17h-10z"
/><path
fill=
"#fee1d3"
d=
"m48 83h5v17h-5z"
/><path
fill=
"#fef0e8"
d=
"m69 83h10v17h-10z"
/><path
fill=
"#fee1d3"
d=
"m69 83h5v17h-5z"
/><path
fill=
"#c3b8e3"
fill-rule=
"nonzero"
d=
"m29 80h6v-2h-6v2m-2-6h10c1.105 0 2 .895 2 2v6c0 1.105-.895 2-2 2h-10c-1.105 0-2-.895-2-2v-6c0-1.105.895-2 2-2m22 6h8v-2h-8v2m-2-6h12c1.105 0 2 .895 2 2v6c0 1.105-.895 2-2 2h-12c-1.105 0-2-.895-2-2v-6c0-1.105.895-2 2-2m24 6h6v-2h-6v2m-2-6h10c1.105 0 2 .895 2 2v6c0 1.105-.895 2-2 2h-10c-1.105 0-2-.895-2-2v-6c0-1.105.895-2 2-2"
/><path
fill=
"#fff"
d=
"m42.802 43v36h-22.802c-1.657 0-3-1.343-3-3 0-.095.004-.19.013-.284.891-9.374 4.1-14.974 9.626-16.801 6.144-2.031 11.531-7.336 16.16-15.915"
/><path
fill=
"#e1dbf1"
fill-rule=
"nonzero"
d=
"m40.901 79h-20.901c-1.657 0-3-1.343-3-3 0-.095.004-.19.013-.284.891-9.374 4.1-14.974 9.626-16.801 4.421-1.462 8.451-4.619 12.09-9.471-.218 2.132-.375 4.351-.471 6.659-3.159 3.155-6.609 5.369-10.362 6.61-3.57 1.18-5.915 5.04-6.782 12.287h17.551c.098 1.313.21 2.647.336 4h1.901"
/><rect
width=
"6"
height=
"3"
x=
"28"
y=
"69"
fill=
"#efedf8"
rx=
"1.5"
/><rect
width=
"6"
height=
"3"
x=
"72"
y=
"69"
fill=
"#c3b8e3"
rx=
"1.5"
/><g
transform=
"matrix(-1 0 0 1 151.8 0)"
><path
fill=
"#fff"
d=
"m88.8 43v36h-22.802c-1.657 0-3-1.343-3-3 0-.095.004-.19.013-.284.891-9.374 4.1-14.974 9.626-16.801 6.144-2.031 11.531-7.336 16.16-15.915"
/><path
fill=
"#e1dbf1"
fill-rule=
"nonzero"
d=
"m84.8 49.35c1.214-1.63 2.385-3.449 3.512-5.458l.488.123v6.628c-1.277 1.82-2.61 3.457-4 4.908v-6.201m-10.906 13.367c-3.57 1.18-5.915 5.04-6.782 12.287h17.689v-19.453c-3.306 3.452-6.936 5.854-10.906 7.166m14.906-19.713v36h-22.802c-1.657 0-3-1.343-3-3 0-.095.004-.19.013-.284.891-9.374 4.1-14.974 9.626-16.801 6.144-2.031 11.531-7.336 16.16-15.915"
/></g><rect
width=
"6"
height=
"3"
x=
"72"
y=
"69"
fill=
"#efedf8"
rx=
"1.5"
/><path
fill=
"#fff"
d=
"m40 41c1.927-9.373 5.828-17.497 11.703-24.371.718-.84 1.98-.938 2.82-.221.061.052.119.108.173.167 5.46 5.938 9.228 14.08 11.304 24.425 2.266 11.292 2.6 23.958 1 38h-28c-1.414-15.208-1.081-27.875 1-38"
id=
"0"
/><use
xlink:href=
"#0"
/><path
fill=
"#e1dbf1"
fill-rule=
"nonzero"
d=
"m63.39 75c1.119-12.232.677-23.302-1.312-33.21-1.736-8.65-4.688-15.545-8.817-20.738-4.608 5.952-7.719 12.855-9.343 20.756-1.828 8.893-2.251 19.966-1.243 33.19h20.715m-23.39-34c1.927-9.373 5.828-17.497 11.703-24.371.718-.84 1.98-.938 2.82-.221.061.052.119.108.173.167 5.46 5.938 9.228 14.08 11.304 24.425 2.266 11.292 2.6 23.958 1 38h-28c-1.414-15.208-1.081-27.875 1-38"
/><path
fill=
"#c3b8e3"
d=
"m51.5 64h3c.828 0 1.5.672 1.5 1.5 0 .828-.672 1.5-1.5 1.5h-3c-.828 0-1.5-.672-1.5-1.5 0-.828.672-1.5 1.5-1.5m0 5h3c.828 0 1.5.672 1.5 1.5 0 .828-.672 1.5-1.5 1.5h-3c-.828 0-1.5-.672-1.5-1.5 0-.828.672-1.5 1.5-1.5"
/><path
fill=
"#6b4fbb"
fill-rule=
"nonzero"
d=
"m58.987 37.466c-1.01-2.384-3.353-3.966-5.987-3.966-2.666 0-5.03 1.62-6.02 4.047-.313.767.056 1.643.823 1.955.767.313 1.643-.056 1.955-.823.533-1.307 1.807-2.18 3.243-2.18 1.419 0 2.681.852 3.225 2.136.323.763 1.204 1.119 1.966.796.763-.323 1.119-1.204.796-1.966"
/></g></svg>
changelogs/unreleased-ee/2344-display-group-and-user-plans-on-gl-com.yml
0 → 100644
View file @
ca6cf402
---
title
:
Add initial Groups/Billing and Profile/Billing routing and template
merge_request
:
author
:
config/routes/group.rb
View file @
ca6cf402
...
...
@@ -60,6 +60,7 @@ scope(path: 'groups/*group_id',
end
resources
:variables
,
only:
[
:index
,
:show
,
:update
,
:create
,
:destroy
]
resources
:billings
,
only:
[
:index
]
end
end
...
...
config/routes/profile.rb
View file @
ca6cf402
...
...
@@ -55,6 +55,7 @@ resource :profile, only: [:show, :update] do
## EE-specific
resources
:pipeline_quota
,
only:
[
:index
]
resources
:billings
,
only:
[
:index
]
## EE-specific
end
end
ee/app/models/ee/application_setting.rb
View file @
ca6cf402
...
...
@@ -46,7 +46,7 @@ module EE
end
def
should_check_namespace_plan?
check_namespace_plan?
&&
(
::
Gitlab
.
com?
||
Rails
.
env
.
development?
)
check_namespace_plan?
&&
::
Gitlab
.
dev_env_or_com?
end
private
...
...
ee/app/models/ee/namespace.rb
View file @
ca6cf402
...
...
@@ -6,6 +6,8 @@ module EE
module
Namespace
extend
ActiveSupport
::
Concern
FREE_PLAN
=
'free'
.
freeze
BRONZE_PLAN
=
'bronze'
.
freeze
SILVER_PLAN
=
'silver'
.
freeze
GOLD_PLAN
=
'gold'
.
freeze
...
...
@@ -29,6 +31,10 @@ module EE
validates
:plan
,
inclusion:
{
in:
EE_PLANS
.
keys
},
allow_blank:
true
end
def
root_ancestor
ancestors
.
reorder
(
nil
).
find_by
(
parent_id:
nil
)
end
def
move_dir
raise
NotImplementedError
unless
defined?
(
super
)
...
...
@@ -68,6 +74,12 @@ module EE
@features_available_in_plan
[
feature
]
end
# The main difference between the "plan" column and this method is that "plan"
# returns nil / "" when it has no plan. Having no plan means it's a "free" plan.
def
actual_plan
plan
.
presence
||
FREE_PLAN
end
def
actual_shared_runners_minutes_limit
shared_runners_minutes_limit
||
current_application_settings
.
shared_runners_minutes
...
...
ee/app/views/groups/ee/_settings_nav.html.haml
View file @
ca6cf402
...
...
@@ -21,3 +21,9 @@
=
link_to
group_pipeline_quota_path
(
@group
),
title:
'Pipelines quota'
do
%span
Pipelines quota
-
if
current_application_settings
.
should_check_namespace_plan?
=
nav_link
(
path:
'billings#index'
)
do
=
link_to
group_billings_path
(
@group
),
title:
'Billing'
do
%span
Billing
lib/gitlab.rb
View file @
ca6cf402
...
...
@@ -12,4 +12,8 @@ module Gitlab
def
self
.
gl_subdomain?
SUBDOMAIN_REGEX
===
Gitlab
.
config
.
gitlab
.
url
end
def
self
.
dev_env_or_com?
Rails
.
env
.
development?
||
com?
end
end
spec/controllers/groups/billings_controller_spec.rb
0 → 100644
View file @
ca6cf402
require
'spec_helper'
describe
Groups
::
BillingsController
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
,
:private
)
}
describe
'GET index'
do
before
do
stub_application_setting
(
check_namespace_plan:
true
)
allow
(
Gitlab
).
to
receive
(
:com?
)
{
true
}
end
context
'authorized'
do
before
do
group
.
add_owner
(
user
)
sign_in
(
user
)
end
it
'renders index with 200 status code'
do
allow_any_instance_of
(
FetchSubscriptionPlansService
).
to
receive
(
:execute
)
get
:index
,
group_id:
group
expect
(
response
).
to
have_http_status
(
200
)
expect
(
response
).
to
render_template
(
:index
)
end
it
'fetches subscription plans data from customers.gitlab.com'
do
data
=
double
expect_any_instance_of
(
FetchSubscriptionPlansService
).
to
receive
(
:execute
).
and_return
(
data
)
get
:index
,
group_id:
group
expect
(
assigns
(
:plans_data
)).
to
eq
(
data
)
end
end
context
'unauthorized'
do
it
'renders 404 when user is not an owner'
do
group
.
add_developer
(
user
)
sign_in
(
user
)
get
:index
,
group_id:
group
.
id
expect
(
response
).
to
have_http_status
(
404
)
end
it
'renders 404 when it is not gitlab.com'
do
allow
(
Gitlab
).
to
receive
(
:com?
)
{
false
}
group
.
add_owner
(
user
)
sign_in
(
user
)
get
:index
,
group_id:
group
expect
(
response
).
to
have_http_status
(
404
)
end
end
end
end
spec/controllers/profiles/billings_controller_spec.rb
0 → 100644
View file @
ca6cf402
require
'spec_helper'
describe
Profiles
::
BillingsController
do
let
(
:user
)
{
create
(
:user
)
}
describe
'GET #index'
do
before
do
stub_application_setting
(
check_namespace_plan:
true
)
allow
(
Gitlab
).
to
receive
(
:com?
)
{
true
}
end
it
'renders index with 200 status code'
do
allow_any_instance_of
(
FetchSubscriptionPlansService
).
to
receive
(
:execute
)
sign_in
(
user
)
get
:index
expect
(
response
).
to
have_http_status
(
200
)
expect
(
response
).
to
render_template
(
:index
)
end
it
'fetch subscription plans data from customers.gitlab.com'
do
data
=
double
expect_any_instance_of
(
FetchSubscriptionPlansService
).
to
receive
(
:execute
).
and_return
(
data
)
sign_in
(
user
)
get
:index
expect
(
assigns
(
:plans_data
)).
to
eq
(
data
)
end
end
end
spec/ee/spec/models/ee/namespace_spec.rb
View file @
ca6cf402
...
...
@@ -258,4 +258,17 @@ describe Namespace do
it
{
is_expected
.
to
be_falsey
}
end
end
describe
'#root_ancestor'
do
it
'returns the top most ancestor'
,
:nested_groups
do
root_group
=
create
(
:group
)
nested_group
=
create
(
:group
,
parent:
root_group
)
deep_nested_group
=
create
(
:group
,
parent:
nested_group
)
very_deep_nested_group
=
create
(
:group
,
parent:
deep_nested_group
)
expect
(
nested_group
.
root_ancestor
).
to
eq
(
root_group
)
expect
(
deep_nested_group
.
root_ancestor
).
to
eq
(
root_group
)
expect
(
very_deep_nested_group
.
root_ancestor
).
to
eq
(
root_group
)
end
end
end
spec/features/billings/billing_plans_spec.rb
0 → 100644
View file @
ca6cf402
require
'spec_helper'
describe
'Billing plan pages'
,
:feature
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:plans_data
)
do
[
{
name:
"Free"
,
price_per_month:
0
,
free:
true
,
code:
"free"
,
price_per_year:
0
,
purchase_link:
{
action:
"downgrade"
,
href:
nil
},
features:
[]
},
{
name:
"Bronze"
,
price_per_month:
4
,
free:
false
,
code:
"bronze"
,
price_per_year:
48
,
purchase_link:
{
action:
"current_plan"
,
href:
nil
},
features:
[]
},
{
name:
"Silver"
,
price_per_month:
19
,
free:
false
,
code:
"silver"
,
price_per_year:
228
,
purchase_link:
{
action:
"upgrade"
,
href:
nil
},
features:
[]
},
{
name:
"Gold"
,
price_per_month:
99
,
free:
false
,
code:
"gold"
,
price_per_year:
1188
,
purchase_link:
{
action:
"upgrade"
,
href:
nil
},
features:
[]
}
]
end
shared_examples
'displays all plans and correct actions'
do
it
'displays all plans'
do
page
.
within
(
'.billing-plans'
)
do
panels
=
page
.
all
(
'.panel'
)
expect
(
panels
.
length
).
to
eq
(
plans_data
.
length
)
plans_data
.
each
.
with_index
do
|
data
,
index
|
expect
(
panels
[
index
].
find
(
'.panel-heading'
)).
to
have_content
(
data
[
:name
])
end
end
end
it
'displays correct plan actions'
do
expected_actions
=
plans_data
.
map
{
|
data
|
data
.
fetch
(
:purchase_link
).
fetch
(
:action
)
}
plan_actions
=
page
.
all
(
'.billing-plans .panel .plan-action'
)
expect
(
plan_actions
.
length
).
to
eq
(
expected_actions
.
length
)
expected_actions
.
each_with_index
do
|
expected_action
,
index
|
action
=
plan_actions
[
index
]
case
expected_action
when
'downgrade'
expect
(
action
).
to
have_content
(
'Downgrade'
)
expect
(
action
).
to
have_css
(
'.disabled'
)
when
'current_plan'
expect
(
action
).
to
have_content
(
'Current plan'
)
expect
(
action
).
to
have_css
(
'.disabled'
)
when
'upgrade'
expect
(
action
).
to
have_content
(
'Upgrade'
)
expect
(
action
).
to
have_css
(
'.disabled'
)
end
end
end
end
before
do
expect
(
HTTParty
).
to
receive
(
:get
).
and_return
(
double
(
body:
plans_data
.
to_json
))
stub_application_setting
(
check_namespace_plan:
true
)
allow
(
Gitlab
).
to
receive
(
:com?
)
{
true
}
gitlab_sign_in
(
user
)
end
context
'users profile billing page'
do
before
do
allow_any_instance_of
(
EE
::
Namespace
).
to
receive
(
:plan
).
and_return
(
'bronze'
)
visit
profile_billings_path
end
include_examples
'displays all plans and correct actions'
it
'displays plan header'
do
page
.
within
(
'.billing-plan-header'
)
do
expect
(
page
).
to
have_content
(
"You are currently on the Bronze"
)
expect
(
page
).
to
have_css
(
'.billing-plan-logo svg'
)
end
end
end
context
'group billing page'
do
let
(
:group
)
{
create
(
:group
)
}
let!
(
:group_member
)
{
create
(
:group_member
,
:owner
,
group:
group
,
user:
user
)
}
context
'top-most group'
do
before
do
expect_any_instance_of
(
EE
::
Group
).
to
receive
(
:plan
).
at_least
(
:once
).
and_return
(
'bronze'
)
visit
group_billings_path
(
group
)
end
include_examples
'displays all plans and correct actions'
it
'displays plan header'
do
page
.
within
(
'.billing-plan-header'
)
do
expect
(
page
).
to
have_content
(
"
#{
group
.
name
}
is currently on the Bronze plan"
)
expect
(
page
).
to
have_css
(
'.billing-plan-logo svg'
)
end
end
end
end
context
'on sub-group'
,
:nested_groups
do
let
(
:group
)
{
create
(
:group
,
plan:
Namespace
::
BRONZE_PLAN
)
}
let!
(
:group_member
)
{
create
(
:group_member
,
:owner
,
group:
group
,
user:
user
)
}
let
(
:subgroup1
)
{
create
(
:group
,
parent:
group
,
plan:
Namespace
::
SILVER_PLAN
)
}
let!
(
:subgroup1_member
)
{
create
(
:group_member
,
:owner
,
group:
subgroup1
,
user:
user
)
}
let
(
:subgroup2
)
{
create
(
:group
,
parent:
subgroup1
)
}
let!
(
:subgroup2_member
)
{
create
(
:group_member
,
:owner
,
group:
subgroup2
,
user:
user
)
}
before
do
visit
group_billings_path
(
subgroup2
)
end
it
'displays plan header only'
do
page
.
within
(
'.billing-plan-header'
)
do
expect
(
page
).
to
have_content
(
"
#{
subgroup2
.
full_name
}
is currently on the Bronze plan"
)
expect
(
page
).
to
have_css
(
'.billing-plan-logo svg'
)
expect
(
page
.
find
(
'.btn-success'
)).
to
have_content
(
'Manage plan'
)
end
expect
(
page
).
not_to
have_css
(
'.billing-plans'
)
end
end
context
'with unexpected JSON'
do
let
(
:plans_data
)
do
[
{
name:
"Superhero"
,
price_per_month:
999.0
,
free:
true
,
code:
"not-found"
,
price_per_year:
111.0
,
purchase_link:
{
action:
"upgrade"
,
href:
"http://customers.test.host/subscriptions/new?plan_id=super_hero_id"
},
features:
[]
}
]
end
before
do
expect_any_instance_of
(
EE
::
Namespace
).
to
receive
(
:plan
).
at_least
(
:once
).
and_return
(
nil
)
visit
profile_billings_path
end
it
'renders no header for missing plan'
do
expect
(
page
).
not_to
have_css
(
'.billing-plan-header'
)
end
it
'displays all plans'
do
page
.
within
(
'.billing-plans'
)
do
panels
=
page
.
all
(
'.panel'
)
expect
(
panels
.
length
).
to
eq
(
plans_data
.
length
)
plans_data
.
each_with_index
do
|
data
,
index
|
expect
(
panels
[
index
].
find
(
'.panel-heading'
)).
to
have_content
(
data
[
:name
])
end
end
end
end
end
spec/helpers/billing_plans_helper_spec.rb
0 → 100644
View file @
ca6cf402
require
'spec_helper'
describe
BillingPlansHelper
do
describe
'#current_plan?'
do
it
'returns true when current_plan'
do
plan
=
Hashie
::
Mash
.
new
(
purchase_link:
{
action:
'current_plan'
})
expect
(
helper
.
current_plan?
(
plan
)).
to
be_truthy
end
it
'return false when not current_plan'
do
plan
=
Hashie
::
Mash
.
new
(
purchase_link:
{
action:
'upgrade'
})
expect
(
helper
.
current_plan?
(
plan
)).
to
be_falsy
end
end
end
spec/services/fetch_subscription_plans_service_spec.rb
0 → 100644
View file @
ca6cf402
require
'spec_helper'
describe
FetchSubscriptionPlansService
do
describe
'#execute'
do
let
(
:endpoint_url
)
{
'https://customers.gitlab.com/gitlab_plans'
}
subject
{
described_class
.
new
(
plan:
'bronze'
).
execute
}
context
'when successully fetching plans data'
do
it
'returns parsed JSON'
do
json_mock
=
double
(
body:
[{
'foo'
=>
'bar'
}].
to_json
)
expect
(
HTTParty
).
to
receive
(
:get
)
.
with
(
endpoint_url
,
query:
{
plan:
'bronze'
},
headers:
{
'Accept'
=>
'application/json'
})
.
and_return
(
json_mock
)
is_expected
.
to
eq
([
Hashie
::
Mash
.
new
(
'foo'
=>
'bar'
)])
end
end
context
'when failing to fetch plans data'
do
before
do
expect
(
HTTParty
).
to
receive
(
:get
).
and_raise
(
HTTParty
::
Error
.
new
(
'Error message'
))
end
it
'logs failure'
do
expect
(
Rails
).
to
receive_message_chain
(
:logger
,
:info
).
with
(
'Unable to connect to GitLab Customers App Error message'
)
subject
end
it
'returns nil'
do
is_expected
.
to
be_nil
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment