Commit 42970cc4 authored by Luke Bennett's avatar Luke Bennett Committed by Michael Kozono

Fix error from gold trial callout on explore page when not logged in

Check that there is a current user before checking if the current user
has a namespace with gold or a trial
parent c0922480
import Visibility from 'visibilityjs';
import Vue from 'vue';
import initDismissableCallout from '~/dismissable_callout';
import PersistentUserCallout from '../persistent_user_callout';
import { s__, sprintf } from '../locale';
import Flash from '../flash';
import Poll from '../lib/utils/poll';
......@@ -67,7 +67,7 @@ export default class Clusters {
this.showTokenButton = document.querySelector('.js-show-cluster-token');
this.tokenField = document.querySelector('.js-cluster-token');
initDismissableCallout('.js-cluster-security-warning');
Clusters.initDismissableCallout();
initSettingsPanels();
setupToggleButtons(document.querySelector('.js-cluster-enable-toggle-area'));
this.initApplications(clusterType);
......@@ -108,6 +108,12 @@ export default class Clusters {
});
}
static initDismissableCallout() {
const callout = document.querySelector('.js-cluster-security-warning');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
}
addListeners() {
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
eventHub.$on('installApplication', this.installApplication);
......
import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import Flash from '~/flash';
export default function initDismissableCallout(alertSelector) {
const alertEl = document.querySelector(alertSelector);
if (!alertEl) {
return;
}
const closeButtonEl = alertEl.getElementsByClassName('close')[0];
const { dismissEndpoint, featureId } = closeButtonEl.dataset;
closeButtonEl.addEventListener('click', () => {
axios
.post(dismissEndpoint, {
feature_name: featureId,
})
.then(() => {
$(alertEl).alert('close');
})
.catch(() => {
Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
});
});
}
import initDismissableCallout from '~/dismissable_callout';
import PersistentUserCallout from '~/persistent_user_callout';
document.addEventListener('DOMContentLoaded', () => {
initDismissableCallout('.gcp-signup-offer');
const callout = document.querySelector('.gcp-signup-offer');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
});
import initDismissableCallout from '~/dismissable_callout';
import PersistentUserCallout from '~/persistent_user_callout';
import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
function initGcpSignupCallout() {
const callout = document.querySelector('.gcp-signup-offer');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
}
document.addEventListener('DOMContentLoaded', () => {
const { page } = document.body.dataset;
const newClusterViews = [
......@@ -10,7 +16,7 @@ document.addEventListener('DOMContentLoaded', () => {
];
if (newClusterViews.indexOf(page) > -1) {
initDismissableCallout('.gcp-signup-offer');
initGcpSignupCallout();
initGkeDropdowns();
}
});
import initDismissableCallout from '~/dismissable_callout';
import PersistentUserCallout from '~/persistent_user_callout';
document.addEventListener('DOMContentLoaded', () => {
initDismissableCallout('.gcp-signup-offer');
const callout = document.querySelector('.gcp-signup-offer');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
});
import initDismissableCallout from '~/dismissable_callout';
import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
import PersistentUserCallout from '../../persistent_user_callout';
import Project from './project';
import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation';
......@@ -12,7 +12,9 @@ document.addEventListener('DOMContentLoaded', () => {
];
if (newClusterViews.indexOf(page) > -1) {
initDismissableCallout('.gcp-signup-offer');
const callout = document.querySelector('.gcp-signup-offer');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
initGkeDropdowns();
}
......
import axios from './lib/utils/axios_utils';
import { __ } from './locale';
import Flash from './flash';
export default class PersistentUserCallout {
constructor(container) {
const { dismissEndpoint, featureId } = container.dataset;
this.container = container;
this.dismissEndpoint = dismissEndpoint;
this.featureId = featureId;
this.init();
}
init() {
const closeButton = this.container.querySelector('.js-close');
closeButton.addEventListener('click', event => this.dismiss(event));
}
dismiss(event) {
event.preventDefault();
axios
.post(this.dismissEndpoint, {
feature_name: this.featureId,
})
.then(() => {
this.container.remove();
})
.catch(() => {
Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
});
}
}
......@@ -33,3 +33,5 @@ module DashboardHelper
links
end
end
DashboardHelper.prepend(EE::DashboardHelper)
......@@ -19,3 +19,5 @@ module UserCalloutsHelper
current_user&.callouts&.find_by(feature_name: UserCallout.feature_names[feature_name])
end
end
UserCalloutsHelper.prepend(EE::UserCalloutsHelper)
......@@ -100,3 +100,5 @@ module UsersHelper
items
end
end
UsersHelper.prepend(EE::UsersHelper)
- link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert' }
%button.close{ type: "button", data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } } ×
.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert', data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } }
%button.close.js-close{ type: "button" } ×
.gcp-signup-offer--content
.gcp-signup-offer--icon.append-right-8
= sprite_icon("information", size: 16)
......
......@@ -4,6 +4,9 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
= render_if_exists "shared/gold_trial_callout"
- page_title "Activity"
- header_title "Activity", activity_dashboard_path
......
- @hide_top_links = true
- page_title "Groups"
- header_title "Groups", dashboard_groups_path
= render_if_exists "shared/gold_trial_callout"
= render 'dashboard/groups_head'
- if params[:filter].blank? && @groups.empty?
......
......@@ -4,6 +4,8 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues")
= render_if_exists "shared/gold_trial_callout"
.page-title-holder
%h1.page-title= _('Issues')
......
......@@ -2,6 +2,8 @@
- page_title _("Merge Requests")
- @breadcrumb_link = merge_requests_dashboard_path(assignee_username: current_user.username)
= render_if_exists "shared/gold_trial_callout"
.page-title-holder
%h1.page-title= _('Merge Requests')
......
......@@ -4,6 +4,8 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
= render_if_exists "shared/gold_trial_callout"
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
......
......@@ -4,6 +4,8 @@
- page_title "Starred Projects"
- header_title "Projects", dashboard_projects_path
= render_if_exists "shared/gold_trial_callout"
%div{ class: container_class }
= render "projects/last_push"
= render 'dashboard/projects_head'
......
......@@ -2,6 +2,8 @@
- page_title "Todos"
- header_title "Todos", dashboard_todos_path
= render_if_exists "shared/gold_trial_callout"
.page-title-holder
%h1.page-title= _('Todos')
......
......@@ -2,6 +2,8 @@
- page_title _("Groups")
- header_title _("Groups"), dashboard_groups_path
= render_if_exists "shared/gold_trial_callout"
- if current_user
= render 'dashboard/groups_head'
- else
......
......@@ -2,6 +2,8 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
= render_if_exists "shared/gold_trial_callout"
- if current_user
= render 'dashboard/projects_head'
- else
......
......@@ -2,6 +2,8 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
= render_if_exists "shared/gold_trial_callout"
- if current_user
= render 'dashboard/projects_head'
- else
......
......@@ -2,6 +2,8 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
= render_if_exists "shared/gold_trial_callout"
- if current_user
= render 'dashboard/projects_head'
- else
......
import PersistentUserCallout from '~/persistent_user_callout';
document.addEventListener('DOMContentLoaded', () => {
const callout = document.querySelector('.js-gold-trial-callout');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
});
// if the "projects dashboard" is a user's default dashboard, when they visit the
// instance root index, the dashboard will be served by the root controller instead
// of a dashboard controller. The root index redirects for all other default dashboards.
import '~/pages/root/index';
import '../dashboard/index';
......@@ -34,7 +34,7 @@
}
.svg-container {
margin-right: 15px;
margin-right: $gl-padding;
}
}
......@@ -77,6 +77,24 @@
margin: 0;
}
}
&.thin-callout {
.bordered-box {
padding: $gl-padding;
padding-left: 40px;
.close {
position: relative;
top: 0;
right: 0;
}
}
.svg-container .svg {
max-width: 39px;
max-height: 39px;
}
}
}
.promotion-modal {
......
# frozen_string_literal: true
module EE
module DashboardHelper
def controller_action_to_child_dashboards(controller = controller_name, action = action_name)
case "#{controller}##{action}"
when 'projects#index', 'root#index', 'projects#starred', 'projects#trending'
%w(projects)
when 'dashboard#activity'
%w(starred_project_activity project_activity)
when 'groups#index'
%w(groups)
when 'todos#index'
%w(todos)
when 'dashboard#issues'
%w(issues)
when 'dashboard#merge_requests'
%w(merge_requests)
else
[]
end
end
def user_default_dashboard?(user = current_user)
controller_action_to_child_dashboards.any? {|dashboard| dashboard == user.dashboard }
end
end
end
# frozen_string_literal: true
module EE
module UserCalloutsHelper
GOLD_TRIAL = 'gold_trial'.freeze
def show_gold_trial?(user = current_user)
return false unless user
return false if user_dismissed?(GOLD_TRIAL)
return false unless show_gold_trial_suitable_env?
users_namespaces_clean?(user)
end
def show_gold_trial_suitable_env?
(::Gitlab.com? || Rails.env.development?) &&
!::Gitlab::Database.read_only?
end
def users_namespaces_clean?(user)
return false if user.any_namespace_with_gold?
!user.any_namespace_with_trial?
end
end
end
......@@ -191,11 +191,36 @@ module EE
project_creation_level: project_creation_levels)
end
def any_namespace_with_trial?
::Namespace
.from("(#{namespace_union(:trial_ends_on)}) #{::Namespace.table_name}")
.where('trial_ends_on > ?', Time.now.utc)
.any?
end
def any_namespace_with_gold?
::Namespace
.includes(:plan)
.where("namespaces.id IN (#{namespace_union})") # rubocop:disable GitlabSecurity/SqlInjection
.where.not(plans: { id: nil })
.any?
end
override :has_current_license?
def has_current_license?
License.current.present?
end
def group_sso?(group)
return false unless group
if group_saml_identities.loaded?
group_saml_identities.any? { |identity| identity.saml_provider.group_id == group.id }
else
group_saml_identities.where(saml_provider: group.saml_provider).any?
end
end
override :ldap_sync_time
def ldap_sync_time
::Gitlab.config.ldap['sync_time']
......@@ -205,14 +230,13 @@ module EE
update_column :admin_email_unsubscribed_at, Time.now
end
def group_sso?(group)
return false unless group
private
if group_saml_identities.loaded?
group_saml_identities.any? { |identity| identity.saml_provider.group_id == group.id }
else
group_saml_identities.where(saml_provider: group.saml_provider).any?
end
def namespace_union(select = :id)
::Gitlab::SQL::Union.new([
::Namespace.select(select).where(type: nil, owner: self),
owned_groups.select(select).where(parent_id: nil)
]).to_sql
end
end
end
- if show_gold_trial? && user_default_dashboard?
.pt-1.d-none.d-md-block{ class: container_class }
.user-callout.promotion-callout.thin-callout.js-gold-trial-callout{ data: { uid: 'trial_callout_dismissed', feature_id: UserCalloutsHelper::GOLD_TRIAL, dismiss_endpoint: user_callouts_path } }
.bordered-box.justify-content-left.align-items-center
.svg-container
= image_tag 'illustrations/golden_tanuki.svg', class: 'svg'
.d-flex.flex-grow.align-items-center
.user-callout-copy.ml-0
%h5.mb-0.mt-0= _('Free Trial of GitLab.com Gold')
%p.mb-0
%span= _('Try all GitLab has to offer for 30 days.')
%span.d-none.d-sm-inline= _('No credit card required.')
= link_to _('Start your trial'), 'https://customers.gitlab.com/trials/new?gl_com=true', class: 'btn btn-primary mr-3 mt-2 mt-sm-0', target: '_blank'
%button.btn.btn-default.close.js-close{ type: 'button',
'aria-label' => _('Dismiss trial promotion') }
= sprite_icon('close', css_class: 'dismiss-icon')
---
title: Promote starting a GitLab.com Gold trial on the dashboard
merge_request: 6947
author:
type: other
---
title: Fix error on explore page when logged out due to gold trial callout
merge_request: 8674
author:
type: fixed
# frozen_string_literal: true
require 'spec_helper'
describe 'Dashboard activity' do
let(:user) { create(:user) }
let(:page_path) { activity_dashboard_path }
it_behaves_like 'gold trial callout'
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Dashboard groups' do
let(:user) { create(:user) }
let(:page_path) { dashboard_groups_path }
it_behaves_like 'gold trial callout'
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Dashboard issues' do
let(:user) { create(:user) }
let(:page_path) { issues_dashboard_path }
it_behaves_like 'gold trial callout'
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Dashboard merge requests' do
let(:user) { create(:user) }
let(:page_path) { merge_requests_dashboard_path }
it_behaves_like 'gold trial callout'
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Dashboard projects' do
let(:user) { create(:user) }
let(:page_path) { dashboard_projects_path }
it_behaves_like 'gold trial callout'
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Dashboard todos' do
let(:user) { create(:user) }
let(:page_path) { dashboard_todos_path }
it_behaves_like 'gold trial callout'
end
# frozen_string_literal: true
require "spec_helper"
describe EE::UserCalloutsHelper do
describe '.show_gold_trial?' do
let(:user) { create(:user) }
before do
allow(helper).to receive(:user_dismissed?).with(EE::UserCalloutsHelper::GOLD_TRIAL).and_return(false)
allow(Gitlab).to receive(:com?).and_return(true)
allow(Gitlab::Database).to receive(:read_only?).and_return(false)
allow(user).to receive(:any_namespace_with_gold?).and_return(false)
allow(user).to receive(:any_namespace_with_trial?).and_return(false)
end
it 'returns true when all conditions are met' do
expect(helper.show_gold_trial?(user)).to be(true)
end
it 'returns false when there is no user record' do
allow(helper).to receive(:current_user).and_return(nil)
expect(helper.show_gold_trial?).to be(false)
end
end
end
# frozen_string_literal: true
shared_examples 'gold trial callout' do
before do
sign_in(user)
end
it 'hides promotion callout if not .com' do
allow(Gitlab).to receive(:com?).and_return(false)
visit page_path
expect(page).not_to have_selector '.promotion-callout'
end
context '.com' do
before do
allow(Gitlab).to receive(:com?).and_return(true)
end
it 'shows dismissable promotion callout if default dashboard', :js do
allow_any_instance_of(EE::DashboardHelper).to receive(:user_default_dashboard?).and_return(true)
visit page_path
expect(page).to have_selector '.promotion-callout'
find('.js-close').click
expect(page).not_to have_selector '.promotion-callout'
end
it 'hides dismissable promotion callout if not default dashboard', :js do
allow_any_instance_of(EE::DashboardHelper).to receive(:user_default_dashboard?).and_return(false)
visit page_path
expect(page).not_to have_selector '.promotion-callout'
end
it 'hides promotion callout if a trial is active' do
group = create(:group, name: 'trial group', trial_ends_on: 1.year.from_now)
group.add_owner(user)
visit page_path
expect(page).not_to have_selector '.promotion-callout'
end
it 'hides promotion callout if a gold plan is active', :js do
group = create(:group, name: 'gold group', plan: :gold_plan)
group.add_owner(user)
visit page_path
expect(page).not_to have_selector '.promotion-callout'
end
end
end
......@@ -3066,6 +3066,9 @@ msgstr ""
msgid "Dismiss Merge Request promotion"
msgstr ""
msgid "Dismiss trial promotion"
msgstr ""
msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
msgstr ""
......@@ -3828,6 +3831,9 @@ msgstr ""
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
msgid "From %{provider_title}"
msgstr ""
......@@ -5854,6 +5860,9 @@ msgstr ""
msgid "No contributions were found"
msgstr ""
msgid "No credit card required."
msgstr ""
msgid "No due date"
msgstr ""
......@@ -8313,6 +8322,9 @@ msgstr ""
msgid "Start the Runner!"
msgstr ""
msgid "Start your trial"
msgstr ""
msgid "Started"
msgstr ""
......@@ -9337,6 +9349,9 @@ msgstr ""
msgid "Try again?"
msgstr ""
msgid "Try all GitLab has to offer for 30 days."
msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
describe 'Dashboard Merge Requests' do
......@@ -6,6 +8,7 @@ describe 'Dashboard Merge Requests' do
include ProjectForksHelper
let(:current_user) { create :user }
let(:user) { current_user }
let(:project) { create(:project) }
let(:public_project) { create(:project, :public, :repository) }
......
# frozen_string_literal: true
require 'spec_helper'
describe 'Dashboard Projects' do
......
# frozen_string_literal: true
require 'spec_helper'
describe 'Root explore' do
set(:public_project) { create(:project, :public) }
set(:archived_project) { create(:project, :archived) }
set(:internal_project) { create(:project, :internal) }
set(:private_project) { create(:project, :private) }
before do
allow(Gitlab).to receive(:com?).and_return(true)
end
context 'when logged in' do
set(:user) { create(:user) }
before do
sign_in(user)
visit explore_projects_path
end
include_examples 'shows public and internal projects'
end
context 'when not logged in' do
before do
visit explore_projects_path
end
include_examples 'shows public projects'
end
end
......@@ -6,24 +6,6 @@ describe 'User explores projects' do
set(:private_project) { create(:project, :private) }
set(:public_project) { create(:project, :public) }
shared_examples_for 'shows public projects' do
it 'shows projects' do
expect(page).to have_content(public_project.title)
expect(page).not_to have_content(internal_project.title)
expect(page).not_to have_content(private_project.title)
expect(page).not_to have_content(archived_project.title)
end
end
shared_examples_for 'shows public and internal projects' do
it 'shows projects' do
expect(page).to have_content(public_project.title)
expect(page).to have_content(internal_project.title)
expect(page).not_to have_content(private_project.title)
expect(page).not_to have_content(archived_project.title)
end
end
context 'when not signed in' do
context 'when viewing public projects' do
before do
......
# frozen_string_literal: true
shared_examples 'shows public projects' do
it 'shows projects' do
expect(page).to have_content(public_project.title)
expect(page).not_to have_content(internal_project.title)
expect(page).not_to have_content(private_project.title)
expect(page).not_to have_content(archived_project.title)
end
end
shared_examples 'shows public and internal projects' do
it 'shows projects' do
expect(page).to have_content(public_project.title)
expect(page).to have_content(internal_project.title)
expect(page).not_to have_content(private_project.title)
expect(page).not_to have_content(archived_project.title)
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