Commit 83408f08 authored by Douwe Maan's avatar Douwe Maan

Merge branch '2887-promote-service-desk-feature' into 'master'

Promote EE features

Closes #2887, #2888, #2918, #2966, #2987, #2988, and #2967

See merge request !2540
parents 35ba4e76 3036a29e
......@@ -204,6 +204,7 @@ import initGroupAnalytics from './init_group_analytics';
new ProjectSelect();
break;
case 'projects:milestones:show':
new UserCallout();
case 'groups:milestones:show':
case 'dashboard:milestones:show':
new Milestone();
......@@ -290,6 +291,7 @@ import initGroupAnalytics from './init_group_analytics';
action: mrNewSubmitNode.dataset.mrSubmitAction,
});
}
new UserCallout();
case 'projects:merge_requests:creations:diffs':
case 'projects:merge_requests:edit':
new gl.Diff();
......@@ -391,7 +393,8 @@ import initGroupAnalytics from './init_group_analytics';
setupProjectEdit();
// Initialize expandable settings panels
initSettingsPanels();
new UsersSelect();
new UserCallout('js-service-desk-callout');
new UserCallout('js-mr-approval-callout');
break;
case 'projects:imports:show':
new ProjectImport();
......@@ -541,6 +544,7 @@ import initGroupAnalytics from './init_group_analytics';
break;
case 'projects:settings:repository:show':
new UsersSelect();
new UserCallout();
// Initialize expandable settings panels
initSettingsPanels();
break;
......
/* global Chart */
export default () => {
const dataEl = document.getElementById('js-analytics-data');
if (dataEl) {
const data = JSON.parse(dataEl.innerHTML);
const labels = data.labels;
const outputElIds = ['push', 'issues_closed', 'merge_requests_created'];
......@@ -10,8 +11,7 @@ export default () => {
const ctx = el.getContext('2d');
const chart = new Chart(ctx);
chart.Bar(
{
chart.Bar({
labels,
datasets: [{
fillColor: 'rgba(220,220,220,0.5)',
......@@ -31,4 +31,5 @@ export default () => {
});
$('#event-stats').tablesorter();
}
};
......@@ -10,7 +10,7 @@ export default class UserCallout {
init() {
if (!this.isCalloutDismissed || this.isCalloutDismissed === 'false') {
$('.js-close-callout').on('click', e => this.dismissCallout(e));
this.userCalloutBody.find('.js-close-callout').on('click', e => this.dismissCallout(e));
}
}
......
......@@ -17,6 +17,7 @@ $darken-border-factor: 5%;
$white-light: #fff;
$white-normal: #f0f0f0;
$white-dark: #eaeaea;
$white-transparent: rgba(255, 255, 255, 0.8);
$gray-lightest: #fdfdfd;
$gray-light: #fafafa;
......
......@@ -289,6 +289,7 @@ table.u2f-registrations {
margin: 20px -5px 0;
.bordered-box {
padding: 32px;
border: 1px solid $blue-300;
border-radius: $border-radius-default;
background-color: $blue-25;
......@@ -296,10 +297,6 @@ table.u2f-registrations {
display: flex;
justify-content: center;
align-items: center;
}
.landing {
padding: 32px;
.close {
position: absolute;
......
.user-callout.promotion-callout {
margin: 20px 0 0;
&.prepend-top-10 {
margin-top: 10px;
}
&.append-bottom-20 {
margin-bottom: 20px;
}
.bordered-box {
padding: 20px;
border-color: $border-color;
background-color: $white-light;
align-items: flex-start;
.user-callout-copy {
max-width: 700px;
}
.close {
.dismiss-icon {
color: $gray-darkest;
}
&:hover {
.dismiss-icon {
color: $text-color;
}
}
}
.svg-container {
margin-right: 15px;
}
}
}
.promotion-modal {
.modal-dialog {
width: 540px;
}
.modal-header {
border-bottom: none;
}
.modal-body {
margin-top: -20px;
padding: 16px 16px 32px;
}
.modal-footer {
border-top: none;
}
}
.promotion-backdrop {
background-color: $white-transparent;
position: absolute;
padding-top: 72px;
.user-callout-copy {
max-width: 700px;
}
}
......@@ -30,4 +30,8 @@ class Groups::AnalyticsController < Groups::ApplicationController
def user_ids
@user_ids ||= @users.map(&:id)
end
def check_contribution_analytics_available!
render_404 unless @group.feature_available?(:contribution_analytics) || LicenseHelper.show_promotions?(current_user)
end
end
class Projects::AuditEventsController < Projects::ApplicationController
include LicenseHelper
before_action :authorize_admin_project!
before_action :check_audit_events_available!
......@@ -7,4 +9,8 @@ class Projects::AuditEventsController < Projects::ApplicationController
def index
@events = project.audit_events.page(params[:page])
end
def check_audit_events_available!
render_404 unless @project.feature_available?(:audit_events) || LicenseHelper.show_promotions?(current_user)
end
end
......@@ -67,5 +67,30 @@ module LicenseHelper
uri.to_s
end
def upgrade_plan_url
if @project.group
group_billings_path(@project.group)
else
profile_billings_path
end
end
def show_promotions?(selected_user = current_user)
return false unless selected_user
return @show_promotions if defined?(@show_promotions)
@show_promotions =
if current_application_settings.should_check_namespace_plan?
true
else
license = License.current
license.nil? || license.expired?
end
end
def show_project_feature_promotion?(project_feature, callout_id = nil)
!@project.feature_available?(project_feature) && show_promotions? && (callout_id.nil? || show_callout?(callout_id))
end
extend self
end
- page_title "Contribution Analytics"
- header_title group_title(@group, "Contribution Analytics", group_analytics_path(@group))
- content_for :page_specific_javascripts do
- if @group.feature_available?(:contribution_analytics)
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('graphs')
.sub-header-block
.sub-header-block
.pull-right
.dropdown.inline
%button.dropdown-toggle.btn{ type: 'button', 'data-toggle' => 'dropdown' }
......@@ -23,9 +25,9 @@
.oneline
Contribution analytics for issues, merge requests and push events since #{@start_date}
%h3 Push
%h3 Push
.row
.row
.col-md-4
%ul
%li
......@@ -44,9 +46,9 @@
%p.light Push events per group member
%canvas#push{ height: 250 }
%h3 Merge Requests
%h3 Merge Requests
.row
.row
.col-md-4
%ul
%li
......@@ -61,9 +63,9 @@
%p.light Merge requests created per group member
%canvas#merge_requests_created{ height: 250 }
%h3 Issues
%h3 Issues
.row
.row
.col-md-4
%ul
%li
......@@ -78,11 +80,11 @@
%p.light Issues closed per group member
%canvas#issues_closed{ height: 250 }
.gray-content-block
.gray-content-block
.oneline
Contributions per group member
.table-holder
.table-holder
%table.table.sortable-table#event-stats
%thead
%tr
......@@ -121,10 +123,12 @@
%td= @stats[:total_events][index]
%script#js-analytics-data{ type: "application/json" }
%script#js-analytics-data{ type: "application/json" }
- data = {}
- data[:labels] = @users.map(&:name)
- [:push, :issues_closed, :merge_requests_created].each do |scope|
- data[scope] = {}
- data[scope][:data] = @stats[scope]
= data.to_json.html_safe
- elsif show_promotions?
= render 'shared/promotions/promote_contribution_analytics'
......@@ -24,7 +24,7 @@
= link_to group_group_members_path(@group), title: 'Members' do
%span
Members
- if @group.feature_available?(:contribution_analytics)
- if @group.feature_available?(:contribution_analytics) || show_promotions?
= nav_link(path: 'analytics#show') do
= link_to group_analytics_path(@group), title: 'Contribution Analytics', data: {placement: 'right'} do
%span
......
......@@ -25,7 +25,7 @@
%span
Activity
- if @group.feature_available?(:contribution_analytics)
- if @group.feature_available?(:contribution_analytics) || show_promotions?
= nav_link(path: 'analytics#show') do
= link_to group_analytics_path(@group), title: 'Contribution Analytics', data: {placement: 'right'} do
%span
......
......@@ -214,6 +214,8 @@
%span
Pages
= render 'projects/settings/ee/nav'
- else
= nav_link(path: %w[members#show]) do
= link_to project_settings_members_path(@project), title: 'Members', class: 'shortcuts-tree' do
......
......@@ -2,7 +2,11 @@
= render "projects/settings/head"
- if show_project_feature_promotion?(:audit_events)
= render 'shared/promotions/promote_audit_events'
%h3.page-title Project Audit Events
%p.light Events in #{@project.full_path}
= render 'shared/audit_events/event_table', events: @events
- if @project.feature_available?(:audit_events)
= render 'shared/audit_events/event_table', events: @events
......@@ -163,24 +163,13 @@
%p
Customize your merge request restrictions.
.settings-content.no-animate{ class: ('expanded' if expanded) }
= render 'shared/promotions/promote_mr_features'
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "merge-request-settings-form" }, authenticity_token: true do |f|
= render 'merge_request_settings', form: f
= f.submit 'Save changes', class: "btn btn-save"
- if EE::Gitlab::ServiceDesk.enabled?(project: @project)
%section.settings.js-service-desk-setting-wrapper
.settings-header
%h4
Service Desk
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
Customize your service desk settings.
= link_to "Learn more about service desk.", help_page_path('user/project/service_desk')
.settings-content.no-animate{ class: ('expanded' if expanded) }
.js-service-desk-setting-root{ data: { endpoint: project_service_desk_path(@project),
enabled: "#{@project.service_desk_enabled}",
incoming_email: (@project.service_desk_address if @project.service_desk_enabled) } }
= render 'projects/ee/service_desk_settings'
= render 'export', project: @project
......
- if current_user && @project.feature_available?(:export_issues)
- if (current_user && @project.feature_available?(:export_issues)) || show_promotions?
%button.csv_download_link.btn.append-right-10.has-tooltip{ title: 'Export as CSV' }
= icon('download')
- return unless current_user && @project.feature_available?(:export_issues)
.issues-export-modal.modal
- if current_user && @project.feature_available?(:export_issues)
.issues-export-modal.modal
.modal-dialog
.modal-content
.modal-header
......@@ -20,3 +19,5 @@
in an attachment.
.modal-footer
= link_to 'Export issues', export_csv_project_issues_path(@project, request.query_parameters), method: :post, class: 'btn btn-success pull-left', title: 'Export issues'
- elsif show_promotions?
= render 'shared/promotions/promote_csv_export'
......@@ -25,3 +25,5 @@
= render "projects/protected_tags/index"
= render @deploy_keys
= render 'shared/promotions/promote_repository_features'
<svg xmlns="http://www.w3.org/2000/svg" width="74" height="78" viewBox="0 0 74 78"><g fill="none" fill-rule="evenodd"><path fill="#F9F9F9" d="M.053 39A37.599 37.599 0 0 0 0 41c0 20.435 16.565 37 37 37s37-16.565 37-37c0-.671-.018-1.338-.053-2C72.907 58.505 56.764 74 37 74 17.236 74 1.092 58.505.053 39z"/><path fill="#EEE" fill-rule="nonzero" d="M37 70c18.225 0 33-14.775 33-33S55.225 4 37 4 4 18.775 4 37s14.775 33 33 33zm0 4C16.565 74 0 57.435 0 37S16.565 0 37 0s37 16.565 37 37-16.565 37-37 37z"/><g fill-rule="nonzero"><path fill="#E1DBF2" d="M37 49c-6.406 0-12.228-2.843-17.38-8.412a4 4 0 0 1-.267-5.113C24.53 28.559 30.434 25 37 25c6.566 0 12.47 3.56 17.647 10.475a4 4 0 0 1-.266 5.113C49.228 46.158 43.406 49 37 49zm0-4c5.225 0 10.012-2.337 14.445-7.128C46.966 31.89 42.173 29 37 29s-9.966 2.89-14.445 8.872C26.988 42.662 31.775 45 37 45z"/><path fill="#6B4FBB" d="M37 45a8 8 0 1 1 0-16 8 8 0 0 1 0 16zm0-4a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm0-1a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"/></g></g></svg>
\ No newline at end of file
<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="#E1DBF2" fill-rule="nonzero" d="M59.542 39.692l-12.93 15.484a2 2 0 0 1-3.36-.466L31.911 29.34l-9.073 10.532a4.007 4.007 0 0 0-2.9-2.762l10.998-12.767a2 2 0 0 1 3.341.49l11.318 25.318 9.511-11.39A3.989 3.989 0 0 0 58 40c.547 0 1.068-.11 1.542-.308z"/><circle cx="32" cy="26" r="4" fill="#6B4FBB"/><circle cx="45" cy="54" r="4" fill="#6B4FBB"/><path fill="#6B4FBB" fill-rule="nonzero" d="M19 47a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-4a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm39-1a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-4a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></g></svg>
\ No newline at end of file
<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.11986346,42 C2.04046544,42.9895269 2,43.9900378 2,45 C2,65.4345357 18.5654643,82 39,82 C59.4345357,82 76,65.4345357 76,45 C76,43.9900378 75.9595346,42.9895269 75.8801365,42 C74.3530766,61.0315425 58.4245736,76 39,76 C19.5754264,76 3.64692341,61.0315425 2.11986346,42 Z"/>
<path fill="#EEEEEE" fill-rule="nonzero" d="M39,78 C17.4608948,78 0,60.5391052 0,39 C0,17.4608948 17.4608948,0 39,0 C60.5391052,0 78,17.4608948 78,39 C78,60.5391052 60.5391052,78 39,78 Z M39,74 C58.3299662,74 74,58.3299662 74,39 C74,19.6700338 58.3299662,4 39,4 C19.6700338,4 4,19.6700338 4,39 C4,58.3299662 19.6700338,74 39,74 Z"/>
<path fill="#E1DBF2" fill-rule="nonzero" d="M39.670936,22.3541589 C38.0983891,22.716355 36.7037657,23.1741464 35.4859588,23.7326005 C34.4819249,24.193024 34.0412416,25.3802012 34.501665,26.3842351 C34.9620885,27.388269 36.1492657,27.8289523 37.1532996,27.3685289 C38.1094554,26.9300611 39.2483006,26.55623 40.5687283,26.252103 C41.6451158,26.0041845 42.3167227,24.9306224 42.0688042,23.8542348 C41.8208857,22.7778472 40.7473235,22.1062404 39.670936,22.3541589 Z M29.6758858,30.0170788 C29.4780579,30.7816803 29.3836179,31.5837814 29.3901446,32.4195311 C29.4045873,33.4859725 29.5976087,34.4314808 29.9757846,35.3060759 C30.4141738,36.3199243 31.5914451,36.7864262 32.6052935,36.3480369 C33.6191419,35.9096477 34.0856438,34.7323765 33.6472546,33.718528 C33.4838878,33.3407149 33.3972215,32.9161831 33.3898994,32.3767671 C33.3861622,31.8939624 33.4397509,31.438821 33.5483677,31.019019 C33.8250458,29.9496626 33.1824532,28.8584861 32.1130968,28.5818079 C31.0437405,28.3051298 29.952564,28.9477224 29.6758858,30.0170788 Z M36.6272376,40.4932998 C37.7133168,40.8851469 38.7297008,41.1918271 40.5894013,41.7131118 C41.6529776,42.0112383 42.7568557,41.3907187 43.0549822,40.3271425 C43.3531087,39.2635663 42.7325891,38.1596881 41.6690129,37.8615616 C39.899684,37.3656086 38.9517195,37.0795731 37.9847486,36.7306991 C36.9457351,36.3558328 35.7995591,36.8942304 35.4246928,37.9332439 C35.0498265,38.9722574 35.5882242,40.1184335 36.6272376,40.4932998 Z M46.7875829,43.8511503 C47.8524563,44.422726 48.5019906,45.0252727 48.8111047,45.6925961 C49.2753683,46.6948601 50.4642238,47.1309957 51.4664878,46.6667321 C52.4687518,46.2024686 52.9048873,45.0136131 52.4406238,44.0113491 C51.7210658,42.457949 50.4457724,41.2749108 48.6793167,40.3267577 C47.7060826,39.8043698 46.4936414,40.169853 45.9712535,41.1430871 C45.4488657,42.1163213 45.8143488,43.3287625 46.7875829,43.8511503 Z M48.230047,50.5614784 C47.7707449,51.2398807 47.0579604,51.7552716 45.9583537,52.1623784 C44.9224975,52.5458828 44.3936619,53.696502 44.7771664,54.7323582 C45.1606709,55.7682144 46.3112901,56.29705 47.3471463,55.9135455 C49.185476,55.2329417 50.5810001,54.2238844 51.5423128,52.8039983 C52.1615676,51.8893414 51.9220968,50.6458602 51.0074399,50.0266055 C50.092783,49.4073507 48.8493018,49.6468214 48.230047,50.5614784 Z M39.6938681,53.1746657 C38.5642199,53.2416635 37.289592,53.2865495 35.7764848,53.3153389 C34.6721152,53.3363513 33.7938807,54.2486538 33.8148931,55.3530234 C33.8359056,56.457393 34.748208,57.3356275 35.8525776,57.3146151 C37.4153285,57.2848811 38.7414211,57.2381828 39.9306863,57.1676492 C41.0333182,57.1022536 41.8741644,56.1553803 41.8087689,55.0527483 C41.7433734,53.9501164 40.7965,53.1092701 39.6938681,53.1746657 Z"/>
<path fill="#6B4FBB" fill-rule="nonzero" d="M26,62 C22.1340068,62 19,58.8659932 19,55 C19,51.1340068 22.1340068,48 26,48 C29.8659932,48 33,51.1340068 33,55 C33,58.8659932 29.8659932,62 26,62 Z M26,58 C27.6568542,58 29,56.6568542 29,55 C29,53.3431458 27.6568542,52 26,52 C24.3431458,52 23,53.3431458 23,55 C23,56.6568542 24.3431458,58 26,58 Z M52,30 C48.1340068,30 45,26.8659932 45,23 C45,19.1340068 48.1340068,16 52,16 C55.8659932,16 59,19.1340068 59,23 C59,26.8659932 55.8659932,30 52,30 Z M52,26 C53.6568542,26 55,24.6568542 55,23 C55,21.3431458 53.6568542,20 52,20 C50.3431458,20 49,21.3431458 49,23 C49,24.6568542 50.3431458,26 52,26 Z"/>
</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="#E1DBF2" fill-rule="nonzero" d="M45 23c-1.312 0-2.593.23-3.795.673A10.982 10.982 0 0 0 33 20c-5.8 0-10.553 4.49-10.97 10.184A11.003 11.003 0 0 0 16 40c0 6.075 4.925 11 11 11h1.042a2 2 0 1 0 0-4H27a7 7 0 0 1-2.323-13.606 2 2 0 0 0 1.334-1.998 7 7 0 0 1 12.89-4.163 2 2 0 0 0 2.667.664A7.004 7.004 0 0 1 51.7 31.966a2 2 0 0 0 1.334 1.334A7 7 0 0 1 51 47h-.94a2 2 0 1 0 0 4H51c6.075 0 11-4.925 11-11 0-4.524-2.754-8.51-6.822-10.178A11.005 11.005 0 0 0 45 23z"/><path fill="#6B4FBB" d="M41 53V43a2 2 0 1 0-4 0v10h-2.796a1 1 0 0 0-.753 1.659l4.796 5.48a1 1 0 0 0 1.506 0l4.796-5.48A1 1 0 0 0 43.796 53H41z"/></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="82" viewBox="0 0 78 82">
<g fill="none" fill-rule="evenodd" transform="translate(2 2)">
<path fill="#F9F9F9" d="M0.119863464,40 C0.0404654401,40.9895269 0,41.9900378 0,43 C0,63.4345357 16.5654643,80 37,80 C57.4345357,80 74,63.4345357 74,43 C74,41.9900378 73.9595346,40.9895269 73.8801365,40 C72.3530766,59.0315425 56.4245736,74 37,74 C17.5754264,74 1.64692341,59.0315425 0.119863464,40 Z"/>
<path fill="#EEEEEE" fill-rule="nonzero" d="M37,76 C15.4608948,76 -2,58.5391052 -2,37 C-2,15.4608948 15.4608948,-2 37,-2 C58.5391052,-2 76,15.4608948 76,37 C76,58.5391052 58.5391052,76 37,76 Z M37,72 C56.3299662,72 72,56.3299662 72,37 C72,17.6700338 56.3299662,2 37,2 C17.6700338,2 2,17.6700338 2,37 C2,56.3299662 17.6700338,72 37,72 Z"/>
<rect width="16" height="4" x="41" y="35" fill="#E1DBF1" rx="2"/>
<rect width="16" height="4" x="17" y="35" fill="#E1DBF1" rx="2"/>
<path fill="#6B4FBB" fill-rule="nonzero" d="M37,45 C32.581722,45 29,41.418278 29,37 C29,32.581722 32.581722,29 37,29 C41.418278,29 45,32.581722 45,37 C45,41.418278 41.418278,45 37,45 Z M37,41 C39.209139,41 41,39.209139 41,37 C41,34.790861 39.209139,33 37,33 C34.790861,33 33,34.790861 33,37 C33,39.209139 34.790861,41 37,41 Z"/>
</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"/><rect width="7" height="1" x="59" y="38" fill="#E1DBF2" rx=".5"/><path fill="#6B4FBB" d="M60.5 42a3.5 3.5 0 0 0 0-7v7z"/><rect width="7" height="1" x="12" y="38" fill="#E1DBF2" transform="matrix(-1 0 0 1 31 0)" rx=".5"/><path fill="#6B4FBB" d="M17.5 42a3.5 3.5 0 0 1 0-7v7z"/><path fill="#E1DBF1" fill-rule="nonzero" d="M39 58c10.493 0 19-8.507 19-19s-8.507-19-19-19-19 8.507-19 19 8.507 19 19 19zm0 4c-12.703 0-23-10.297-23-23s10.297-23 23-23 23 10.297 23 23-10.297 23-23 23z"/><path fill="#6B4FBB" d="M35 56a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/><path fill="#E1DBF1" fill-rule="nonzero" d="M26.5 40c0 4.143 3.355 7.5 7.494 7.5h10.012A7.497 7.497 0 0 0 51.5 40c0-4.143-3.355-7.5-7.494-7.5H33.994A7.497 7.497 0 0 0 26.5 40zm-3 0c0-5.799 4.698-10.5 10.494-10.5h10.012C49.802 29.5 54.5 34.2 54.5 40c0 5.799-4.698 10.5-10.494 10.5H33.994C28.198 50.5 23.5 45.8 23.5 40z"/><path fill="#6B4FBB" fill-rule="nonzero" d="M35.255 42.406a1 1 0 1 1 1.872-.703 2.001 2.001 0 0 0 3.76-.038 1 1 0 1 1 1.886.665 4 4 0 0 1-7.518.076zM31.5 40a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm15 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z"/><path fill="#6B4FBB" d="M38 22h2a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2zm0 3h2a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2z" style="mix-blend-mode:multiply"/></g></svg>
\ No newline at end of file
......@@ -34,3 +34,4 @@
this milestone and the chart will appear here, always up-to-date.
= link_to "Add start and due date", edit_project_milestone_path(project, milestone), class: 'btn'
= render 'shared/promotions/promote_burndown_charts'
- if EE::Gitlab::ServiceDesk.enabled?(project: @project) || (show_promotions? && show_callout?('promote_service_desk_dismissed'))
- expanded = Rails.env.test?
%section.settings.js-service-desk-setting-wrapper
.settings-header
%h4
Service Desk
%button.btn.js-settings-toggle
= expanded ? 'Collapse' : 'Expand'
%p
Customize your service desk settings.
= link_to "Learn more about service desk.", help_page_path('user/project/service_desk')
.settings-content.no-animate{ class: ('expanded' if expanded) }
- if EE::Gitlab::ServiceDesk.enabled?(project: @project)
.js-service-desk-setting-root{ data: { endpoint: project_service_desk_path(@project),
enabled: "#{@project.service_desk_enabled}",
incoming_email: (@project.service_desk_address if @project.service_desk_enabled) } }
- elsif show_promotions? && show_callout?('promote_service_desk_dismissed')
= render 'shared/promotions/promote_servicedesk'
- if @project.feature_available?(:audit_events)
- if @project.feature_available?(:audit_events) || show_promotions?
= nav_link(controller: :audit_events) do
= link_to project_audit_events_path(@project), title: "Audit Events" do
%span
......
- issuable = local_assigns.fetch(:issuable)
- return unless issuable.project.feature_available?(:merge_request_squash)
.form-group
- if issuable.project.feature_available?(:merge_request_squash)
.form-group
.col-sm-10.col-sm-offset-2
.checkbox
= label_tag 'merge_request[squash]' do
......@@ -10,3 +9,7 @@
= check_box_tag 'merge_request[squash]', '1', issuable.squash
Squash commits when merge request is accepted.
= link_to 'About this feature', help_page_path('user/project/merge_requests/squash_and_merge')
- elsif show_promotions? && show_callout?('promote_squash_commits_dismissed')
.form-group
.col-sm-10.col-sm-offset-2
= render 'shared/promotions/promote_squash_commits'
.center.promotion-backdrop.append-top-72
.svg-container
= custom_icon('icon_audit_events_purple')
.user-callout-copy
%h4
- if current_application_settings.should_check_namespace_plan?
Upgrade your plan to activate Audit Events.
- else
Track your project with Audit Events.
%p
Audit Events is a way to keep track of important events that happened in GitLab.
= link_to 'Read more', help_page_path('administration/audit_events.html'), target: '_blank'
= render 'shared/promotions/promotion_link_project'
- if show_promotions? && show_callout?('promote_burndown_charts_dismissed')
.user-callout.promotion-callout#promote_burndown_charts{ data: { uid: 'promote_burndown_charts_dismissed' } }
.bordered-box.content-block
%button.btn.btn-default.close.js-close-callout{ type: 'button', 'aria-label' => 'Dismiss burndown charts promotion' }
= icon('times', class: 'dismiss-icon', 'aria-hidden' => 'true')
.svg-container
= custom_icon('icon_burndown_charts')
.user-callout-copy
%h4
- if current_application_settings.should_check_namespace_plan?
Upgrade your plan to improve milestones with Burndown Charts.
- else
Improve milestones with Burndown Charts.
%p
Burndown Charts are visual representations of the progress of completing a milestone. At a glance, you see the current state for the completion a given milestone. Without them, you would have to organize the data from the milestone and plot it yourself to have the same sense of progress.
= link_to 'Read more', help_page_path('user/project/milestones/burndown_charts.html'), target: '_blank'
= render 'shared/promotions/promotion_link_project'
.center.promotion-backdrop
.svg-container
= custom_icon('icon_contribution_analytics')
.user-callout-copy
%h4
- if current_application_settings.should_check_namespace_plan?
Upgrade your plan to activate Contribution Analytics.
- else
Track activity with Contribution Analytics.
%p
With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members.
= link_to 'Read more', help_page_path('user/analytics/contribution_analytics.html'), target: '_blank'
= render 'shared/promotions/promotion_link_project'
.issues-export-modal.modal.promotion-modal
.modal-dialog
.modal-content
.modal-header
%a.close{ href: '#', 'data-dismiss' => 'modal' } ×
.modal-body.center
%div
.svg-container
= custom_icon('icon_export_issues')
.user-callout-copy
%h4
- if current_application_settings.should_check_namespace_plan?
Upgrade your plan to export issues.
- else
Export issues with GitLab Enterprise Edition.
%p
Export Issues to CSV enables you and your team to export all the data collected from issues into a comma-separated values (CSV) file, which stores tabular data in plain text.
= link_to 'Read more', help_page_path('user/project/issues/csv_export.html'), target: '_blank'
= render 'shared/promotions/promotion_link_project'
- if show_promotions? && show_callout?('promote_mr_features_dismissed') && (!@project.feature_available?(:merge_request_approvers) || !@project.feature_available?(:fast_forward_merge))
.user-callout.promotion-callout.append-bottom-20.js-mr-approval-callout#promote_mr_features{ data: { uid: 'promote_mr_features_dismissed' } }
.bordered-box.content-block
%button.btn.btn-default.close.js-close-callout{ type: 'button', 'aria-label' => 'Dismiss Merge Request promotion' }
= icon('times', class: 'dismiss-icon', 'aria-hidden' => 'true')
.user-callout-copy
%h4
- if current_application_settings.should_check_namespace_plan?
Upgrade your plan to improve Merge Requests.
- else
Improve Merge Requests and customer support with GitLab Enterprise Edition.
%ul
- unless @project.feature_available?(:merge_request_approvers)
%li
= link_to 'Merge Request Approvals', help_page_path('user/project/merge_requests/merge_request_approvals.html'), target: '_blank'
%p
Merge request approvals allow you to set the number of necessary approvals and predefine a list of approvers that will need to approve every merge request in a project.
- unless @project.feature_available?(:fast_forward_merge)
%li
= link_to 'Fast-forward Merge', help_page_path('user/project/merge_requests/fast_forward_merge.html'), target: '_blank'
%p
If you prefer a linear Git history and a way to accept merge requests without creating merge commits, you can configure this on a per-project basis.
= render 'shared/promotions/promotion_link_project'
- if show_promotions? && show_callout?('promote_repository_features_dismissed') && (!@project.feature_available?(:push_rules) || !@project.feature_available?(:repository_mirrors) || !@project.feature_available?(:protected_refs_for_users))
.user-callout.promotion-callout{ id: 'promote_repository_features', data: { uid: 'promote_repository_features_dismissed' } }
.bordered-box.content-block
%button.btn.btn-default.close.js-close-callout{ type: 'button', 'aria-label' => 'Dismiss repository features promotion' }
= icon('times', class: 'dismiss-icon', 'aria-hidden' => 'true')
.svg-container
= custom_icon('icon_push_rules')
.user-callout-copy
%h4
- if current_application_settings.should_check_namespace_plan?
Upgrade your plan to improve repositories.
- else
Improve repositories with GitLab Enterprise Edition.
%ul
- unless @project.feature_available?(:push_rules)
%li
= link_to 'Push Rules', help_page_path('push_rules/push_rules.md#push-rules'), target: '_blank'
%p
Push Rules are defined per project so you can have different rules applied to different projects depends on your needs.
- unless @project.feature_available?(:repository_mirrors)
%li
= link_to 'Repository Mirroring', help_page_path('workflow/repository_mirroring.html'), target: '_blank'
%p
Repository Mirroring is a way to mirror repositories from external sources. It can be used to mirror all branches, tags, and commits that you have in your repository.
- unless @project.feature_available?(:protected_refs_for_users)
%li
= link_to 'Better Protected Branches', help_page_path('user/project/protected_branches.html'), target: '_blank'
%p
You can restrict access to protected branches by choosing a role (Masters, Developers) as well as certain users.
= render 'shared/promotions/promotion_link_project'
.user-callout.promotion-callout.js-service-desk-callout#promote_service_desk{ data: { uid: 'promote_service_desk_dismissed' } }
.bordered-box.content-block
%button.btn.btn-default.close.js-close-callout{ type: 'button', 'aria-label' => 'Dismiss Service Desk promotion' }
= icon('times', class: 'dismiss-icon', 'aria-hidden' => 'true')
.svg-container
= custom_icon('icon_service_desk')
.user-callout-copy
%h4
- if current_application_settings.should_check_namespace_plan?
Upgrade your plan to activate Service Desk.
- else
Improve customer support with GitLab Service Desk.
%p
GitLab Service Desk is a simple way to allow people to create issues in your GitLab instance without needing their own user account. It provides a unique email address for end users to create issues in a project, and replies can be sent either through the GitLab interface or by email. End users will only see the thread through email.
= link_to 'Read more', help_page_path('user/project/service_desk.html'), target: '_blank'
= render 'shared/promotions/promotion_link_project'
.user-callout.promotion-callout.prepend-top-10#promote_squash_commits{ data: { uid: 'promote_squash_commits_dismissed' } }
.bordered-box.content-block
%button.btn.btn-default.close.js-close-callout{ type: 'button', 'aria-label' => 'Dismiss merge request promotion' }
= icon('times', class: 'dismiss-icon', 'aria-hidden' => 'true')
.user-callout-copy
%h4
- if current_application_settings.should_check_namespace_plan?
Upgrade your plan to improve Merge Requests with Squash Commit.
- else
Improve Merge Requests with Squash Commit and GitLab Enterprise Edition.
%p
Squashing lets you tidy up the commit history of a branch when accepting a merge request. It applies all of the changes in the merge request as a single commit, and then merges that commit using the merge method set for the project.
= link_to 'Read more', help_page_path('user/project/merge_requests/squash_and_merge.html'), target: '_blank'
= render 'shared/promotions/promotion_link_project'
- if current_application_settings.should_check_namespace_plan?
- if can?(current_user, :admin_namespace, @project.namespace)
= link_to 'Upgrade your plan', upgrade_plan_url, class: 'btn btn-primary'
- elsif @project.group
%p Contact an owner of group #{ @project.group.name } to upgrade the plan.
- else
- owner = @project.namespace.owner
%p Contact owner #{ link_to(owner.name, user_path(owner)) } to upgrade the plan.
- elsif current_user&.admin?
- if License.current&.expired?
= link_to 'Buy GitLab Enterprise Edition', Gitlab::SUBSCRIPTIONS_PLANS_URL, class: 'btn btn-primary'
- else
= link_to 'Start GitLab Enterprise Edition trial', new_trial_url, class: 'btn btn-primary'
- else
%p
Contact your Administrator to upgrade your license.
......@@ -38,7 +38,7 @@ describe Groups::AnalyticsController do
create_push_event(user3, project)
end
it 'returns 404 when feature is not available' do
it 'returns 404 when feature is not available and we dont show promotions' do
stub_licensed_features(contribution_analytics: false)
get :show, group_id: group.path
......@@ -46,6 +46,22 @@ describe Groups::AnalyticsController do
expect(response).to have_http_status(404)
end
context 'unlicensed but we show promotions' do
before do
allow(License).to receive(:current).and_return(nil)
allow(LicenseHelper).to receive(:show_promotions?).and_return(true)
stub_application_setting(check_namespace_plan: false)
end
it 'returns page when feature is not available and we show promotions' do
stub_licensed_features(contribution_analytics: false)
get :show, group_id: group.path
expect(response).to have_http_status(200)
end
end
it 'sets instance variables properly' do
get :show, group_id: group.path
......
......@@ -6,7 +6,7 @@ describe 'layouts/nav/_group' do
end
describe 'contribution analytics tab' do
it 'is not visible when there is no valid license' do
it 'is not visible when there is no valid license and we dont show promotions' do
stub_licensed_features(contribution_analytics: false)
render
......@@ -14,7 +14,27 @@ describe 'layouts/nav/_group' do
expect(rendered).not_to have_text 'Contribution Analytics'
end
it 'is not visible when there is no valid license' do
context 'no license installed' do
let!(:cuser) { create(:admin) }
before do
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
allow(view).to receive(:can?).and_return(true)
allow(view).to receive(:current_user).and_return(cuser)
end
it 'is visible when there is no valid license but we show promotions' do
stub_licensed_features(contribution_analytics: false)
render
expect(rendered).to have_text 'Contribution Analytics'
end
end
it 'is visible' do
stub_licensed_features(contribution_analytics: true)
render
......
......@@ -28,6 +28,27 @@ feature 'Projects > Audit Events', :js do
end
end
context 'unlicensed but we show promotions' do
before do
stub_licensed_features(audit_events: false)
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
allow(LicenseHelper).to receive(:show_promotions?).and_return(true)
end
it 'returns 200' do
visit project_audit_events_path(project)
expect(page.status_code).to eq(200)
end
it 'does not have Audit Events button in head nav bar' do
visit edit_project_path(project)
expect(page).to have_link('Audit Events')
end
end
it 'has Audit Events button in head nav bar' do
visit edit_project_path(project)
......
require 'spec_helper'
describe 'Promotions', js: true do
let(:admin) { create(:admin) }
let(:user) { create(:user) }
let(:otherdeveloper) { create(:user, name: 'TheOtherDeveloper') }
let(:group) { create(:group) }
let(:project) { create(:project, :repository, namespace: group) }
let(:milestone) { create(:milestone, project: project, start_date: Date.today, due_date: 7.days.from_now) }
let!(:issue) { create(:issue, project: project, author: user) }
let(:otherproject) { create(:project, :repository, namespace: otherdeveloper.namespace) }
describe 'if you have a license' do
before do
project.team << [user, :master]
end
it 'should show no promotion at all' do
sign_in(user)
visit edit_project_path(project)
expect(page).not_to have_selector('#promote_service_desk')
end
end
describe 'for project features in general on premise' do
context 'no license installed' do
before do
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
project.team << [user, :master]
end
it 'should have the contact admin line' do
sign_in(user)
visit edit_project_path(project)
expect(find('#promote_service_desk')).to have_content 'Contact your Administrator to upgrade your license.'
end
it 'should have the start trial button' do
sign_in(admin)
visit edit_project_path(project)
expect(find('#promote_service_desk')).to have_content 'Start GitLab Enterprise Edition trial'
end
end
end
describe 'for project features in general', js: true do
context 'for .com' do
before do
project.team << [user, :master]
otherproject.team << [user, :master]
stub_application_setting(check_namespace_plan: true)
allow(Gitlab).to receive(:com?) { true }
sign_in(user)
end
it 'should have the Upgrade your plan button' do
visit edit_project_path(project)
expect(find('#promote_service_desk')).to have_content 'Upgrade your plan'
end
it 'should have the contact owner line' do
visit edit_project_path(otherproject)
expect(find('#promote_service_desk')).to have_content 'Contact owner'
end
end
end
describe 'for service desk', js: true do
before do
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
project.team << [user, :master]
sign_in(user)
end
it 'should appear in project edit page' do
visit edit_project_path(project)
expect(find('#promote_service_desk')).to have_content 'Improve customer support with GitLab Service Desk.'
end
it 'does not show when cookie is set' do
visit edit_project_path(project)
within('#promote_service_desk') do
find('.close').trigger('click')
end
visit edit_project_path(project)
expect(page).not_to have_selector('#promote_service_desk')
end
end
describe 'for merge request improve', js: true do
before do
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
project.team << [user, :master]
sign_in(user)
end
it 'should appear in project edit page' do
visit edit_project_path(project)
expect(find('#promote_mr_features')).to have_content 'Improve Merge Requests'
end
it 'does not show when cookie is set' do
visit edit_project_path(project)
within('#promote_mr_features') do
find('.close').trigger('click')
end
visit edit_project_path(project)
expect(page).not_to have_selector('#promote_mr_features')
end
end
describe 'for repository features', js: true do
before do
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
project.team << [user, :master]
sign_in(user)
end
it 'should appear in repository settings page' do
visit project_settings_repository_path(project)
expect(find('#promote_repository_features')).to have_content 'Improve repositories with GitLab Enterprise Edition'
end
it 'does not show when cookie is set' do
visit project_settings_repository_path(project)
within('#promote_repository_features') do
find('.close').trigger('click')
end
visit project_settings_repository_path(project)
expect(page).not_to have_selector('#promote_repository_features')
end
end
describe 'for squash commits', js: true do
before do
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
project.team << [user, :master]
sign_in(user)
end
it 'should appear in new MR page' do
visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: 'feature' })
expect(find('#promote_squash_commits')).to have_content 'Improve Merge Requests with Squash Commit and GitLab Enterprise Edition.'
end
it 'does not show when cookie is set' do
visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: 'feature' })
within('#promote_squash_commits') do
find('.close').trigger('click')
end
visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: 'feature' })
expect(page).not_to have_selector('#promote_squash_commits')
end
end
describe 'for burndown charts', js: true do
before do
stub_application_setting(check_namespace_plan: true)
allow(Gitlab).to receive(:com?) { true }
project.team << [user, :master]
sign_in(user)
end
it 'should appear in milestone page' do
visit project_milestone_path(project, milestone)
expect(find('#promote_burndown_charts')).to have_content "Upgrade your plan to improve milestones with Burndown Charts."
end
it 'does not show when cookie is set' do
visit project_milestone_path(project, milestone)
within('#promote_burndown_charts') do
find('.close').trigger('click')
end
visit project_milestone_path(project, milestone)
expect(page).not_to have_selector('#promote_burndown_charts')
end
end
describe 'for issue export', js: true do
before do
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
project.team << [user, :master]
sign_in(user)
end
it 'should appear on export modal' do
visit project_issues_path(project)
click_on 'Export as CSV'
expect(find('.issues-export-modal')).to have_content 'Export issues with GitLab Enterprise Edition.'
end
end
describe 'for project audit events', js: true do
before do
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
project.team << [user, :master]
sign_in(user)
end
it 'should appear on the page' do
visit project_audit_events_path(project)
expect(find('.user-callout-copy')).to have_content 'Track your project with Audit Events'
end
end
describe 'for group contribution analytics', js: true do
before do
allow(License).to receive(:current).and_return(nil)
stub_application_setting(check_namespace_plan: false)
group.add_owner(user)
sign_in(user)
end
it 'should appear on the page' do
visit group_analytics_path(group)
expect(find('.user-callout-copy')).to have_content 'Track activity with Contribution Analytics.'
end
end
end
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