diff --git a/app/assets/images/logos/shimo.svg b/app/assets/images/logos/shimo.svg new file mode 100644 index 0000000000000000000000000000000000000000..65bd1cc716750c2297a7e39a4ff0154f7b7753a4 --- /dev/null +++ b/app/assets/images/logos/shimo.svg @@ -0,0 +1 @@ + diff --git a/app/controllers/projects/integrations/shimos_controller.rb b/app/controllers/projects/integrations/shimos_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..827dbb8f3f9ea926f98abb68f3179f7d5ad17b44 --- /dev/null +++ b/app/controllers/projects/integrations/shimos_controller.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Projects + module Integrations + class ShimosController < Projects::ApplicationController + feature_category :integrations + + before_action :ensure_renderable + + def show; end + + private + + def ensure_renderable + render_404 unless Feature.enabled?(:shimo_integration, project) && project.has_shimo? && project.shimo_integration&.render? + end + end + end +end diff --git a/app/models/integration.rb b/app/models/integration.rb index d3059fa6d4adf7fb8dcb6b598e1eca9407c4e63b..29d96650a813becdc7c9706e34f42348c5fa3f8a 100644 --- a/app/models/integration.rb +++ b/app/models/integration.rb @@ -14,11 +14,13 @@ class Integration < ApplicationRecord asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat irker jira mattermost mattermost_slash_commands microsoft_teams packagist pipelines_email - pivotaltracker prometheus pushover redmine shimo slack slack_slash_commands teamcity unify_circuit webex_teams youtrack zentao + pivotaltracker prometheus pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack zentao ].freeze + # TODO Shimo is temporary disabled on group and instance-levels. + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/345677 PROJECT_SPECIFIC_INTEGRATION_NAMES = %w[ - jenkins + jenkins shimo ].freeze # Fake integrations to help with local development. diff --git a/app/models/integrations/shimo.rb b/app/models/integrations/shimo.rb index 4f42fda257758e5888c2b6413fb3b3957c83508f..0e1023bb7a7b8030082e381369ee60734ee90843 100644 --- a/app/models/integrations/shimo.rb +++ b/app/models/integrations/shimo.rb @@ -5,7 +5,11 @@ class Shimo < Integration prop_accessor :external_wiki_url validates :external_wiki_url, presence: true, public_url: true, if: :activated? + after_commit :cache_project_has_shimo + def render? + return false unless Feature.enabled?(:shimo_integration, project) + valid? && activated? end @@ -43,5 +47,14 @@ def fields } ] end + + private + + def cache_project_has_shimo + return unless project && !project.destroyed? + + project.project_setting.save! unless project.project_setting.persisted? + project.project_setting.update_column(:has_shimo, activated?) + end end end diff --git a/app/models/project.rb b/app/models/project.rb index 604158d1a6eb22fd841a7c8d962b9119ddf3e78e..01e45737b5fe53bf25022bdbc3901c22d87691f1 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -448,7 +448,7 @@ def self.integration_association_name(name) delegate :restrict_user_defined_variables, :restrict_user_defined_variables=, to: :ci_cd_settings, allow_nil: true delegate :actual_limits, :actual_plan_name, to: :namespace, allow_nil: true delegate :allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline?, - :allow_merge_on_skipped_pipeline=, :has_confluence?, + :allow_merge_on_skipped_pipeline=, :has_confluence?, :has_shimo?, to: :project_setting delegate :active?, to: :prometheus_integration, allow_nil: true, prefix: true delegate :merge_commit_template, :merge_commit_template=, to: :project_setting, allow_nil: true @@ -1467,7 +1467,9 @@ def find_or_initialize_integrations end def disabled_integrations - [:shimo] + disabled_integrations = [] + disabled_integrations << 'shimo' unless Feature.enabled?(:shimo_integration, self) + disabled_integrations end def find_or_initialize_integration(name) diff --git a/app/views/projects/integrations/shimos/show.html.haml b/app/views/projects/integrations/shimos/show.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..92b9e03d5bda216fcdd96a6d72d0b5bf76c6b34c --- /dev/null +++ b/app/views/projects/integrations/shimos/show.html.haml @@ -0,0 +1,10 @@ +- breadcrumb_title s_('Shimo|Shimo Workspace') +- page_title s_('Shimo|Shimo Workspace') +- add_page_specific_style 'page_bundles/wiki' += render layout: 'shared/empty_states/wikis_layout', locals: { image_path: 'illustrations/wiki_login_empty.svg' } do + %h4 + = s_('Shimo|Shimo Workspace integration is enabled') + %p + = s_("Shimo|You've enabled the Shimo Workspace integration. You can view your wiki directly in Shimo.") + = link_to @project.shimo_integration.external_wiki_url, target: '_blank', rel: 'noopener noreferrer', class: 'gl-button btn btn-confirm', title: s_('Shimo|Go to Shimo Workspace') do + = s_('Shimo|Go to Shimo Workspace') diff --git a/config/feature_flags/development/shimo_integration.yml b/config/feature_flags/development/shimo_integration.yml new file mode 100644 index 0000000000000000000000000000000000000000..28c0a7859bc9361eda2bf6fb90116bea3551a8b1 --- /dev/null +++ b/config/feature_flags/development/shimo_integration.yml @@ -0,0 +1,8 @@ +--- +name: shimo_integration +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73129 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345356 +milestone: '14.5' +type: development +group: group::integrations +default_enabled: false diff --git a/config/routes/project.rb b/config/routes/project.rb index 7f9b2cc4fbfe3273f2f5f3e8f79e037923988737..8a8668a23149138d9b6b5d9ce37b72ebf15e0ec3 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -453,6 +453,10 @@ end end end + + namespace :integrations do + resource :shimo, only: [:show] + end end # End of the /-/ scope. diff --git a/lib/sidebars/projects/menus/shimo_menu.rb b/lib/sidebars/projects/menus/shimo_menu.rb new file mode 100644 index 0000000000000000000000000000000000000000..c93c4f6a0a469b0c5b265996b3a74dbc059af270 --- /dev/null +++ b/lib/sidebars/projects/menus/shimo_menu.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Sidebars + module Projects + module Menus + class ShimoMenu < ::Sidebars::Menu + override :link + def link + project_integrations_shimo_path(context.project) + end + + override :title + def title + s_('Shimo|Shimo') + end + + override :image_path + def image_path + 'logos/shimo.svg' + end + + override :image_html_options + def image_html_options + { + size: 16 + } + end + + override :render? + def render? + context.project.has_shimo? + end + + override :active_routes + def active_routes + { controller: :shimo } + end + end + end + end +end diff --git a/lib/sidebars/projects/panel.rb b/lib/sidebars/projects/panel.rb index 8fbd71c15438297d0c620cc68a54450967407034..6bb4fb52e2ad6955278adf255aae0c065c33fbed 100644 --- a/lib/sidebars/projects/panel.rb +++ b/lib/sidebars/projects/panel.rb @@ -32,8 +32,7 @@ def add_menus add_menu(Sidebars::Projects::Menus::InfrastructureMenu.new(context)) add_menu(Sidebars::Projects::Menus::PackagesRegistriesMenu.new(context)) add_menu(Sidebars::Projects::Menus::AnalyticsMenu.new(context)) - add_menu(confluence_or_wiki_menu) - add_menu(Sidebars::Projects::Menus::ExternalWikiMenu.new(context)) + add_wiki_menus add_menu(Sidebars::Projects::Menus::SnippetsMenu.new(context)) add_menu(Sidebars::Projects::Menus::SettingsMenu.new(context)) add_invite_members_menu @@ -46,10 +45,16 @@ def add_invite_members_menu end end - def confluence_or_wiki_menu - confluence_menu = ::Sidebars::Projects::Menus::ConfluenceMenu.new(context) + def add_wiki_menus + add_menu((third_party_wiki_menu || Sidebars::Projects::Menus::WikiMenu).new(context)) + add_menu(Sidebars::Projects::Menus::ExternalWikiMenu.new(context)) + end + + def third_party_wiki_menu + wiki_menu_list = [::Sidebars::Projects::Menus::ConfluenceMenu] + wiki_menu_list << ::Sidebars::Projects::Menus::ShimoMenu if Feature.enabled?(:shimo_integration, context.project) - confluence_menu.render? ? confluence_menu : Sidebars::Projects::Menus::WikiMenu.new(context) + wiki_menu_list.find { |wiki_menu| wiki_menu.new(context).render? } end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 792011abb1ae59f7f337716f58d2c9aa5fb220d1..fd5d55d78cb5905c296bbf5201822c426373ef82 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -31871,15 +31871,27 @@ msgstr "" msgid "Sherlock Transactions" msgstr "" +msgid "Shimo|Go to Shimo Workspace" +msgstr "" + msgid "Shimo|Link to a Shimo Workspace from the sidebar." msgstr "" msgid "Shimo|Shimo" msgstr "" +msgid "Shimo|Shimo Workspace" +msgstr "" + msgid "Shimo|Shimo Workspace URL" msgstr "" +msgid "Shimo|Shimo Workspace integration is enabled" +msgstr "" + +msgid "Shimo|You've enabled the Shimo Workspace integration. You can view your wiki directly in Shimo." +msgstr "" + msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{boldStart}will%{boldEnd} lose access to your account." msgstr "" diff --git a/spec/lib/sidebars/projects/menus/shimo_menu_spec.rb b/spec/lib/sidebars/projects/menus/shimo_menu_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..534267a329e3d53b58c1194910e990a30465ccad --- /dev/null +++ b/spec/lib/sidebars/projects/menus/shimo_menu_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Sidebars::Projects::Menus::ShimoMenu do + let_it_be_with_reload(:project) { create(:project) } + + let(:context) { Sidebars::Projects::Context.new(current_user: project.owner, container: project) } + + subject(:shimo_menu) { described_class.new(context) } + + describe '#render?' do + context 'without a valid Shimo integration' do + it "doesn't render the menu" do + expect(shimo_menu.render?).to be_falsey + end + end + + context 'with a valid Shimo integration' do + let_it_be_with_reload(:shimo_integration) { create(:shimo_integration, project: project) } + + context 'when integration is active' do + it 'renders the menu' do + expect(shimo_menu.render?).to eq true + end + + it 'renders menu link' do + expected_url = Rails.application.routes.url_helpers.project_integrations_shimo_path(project) + expect(shimo_menu.link).to eq expected_url + end + end + + context 'when integration is inactive' do + before do + shimo_integration.update!(active: false) + end + + it "doesn't render the menu" do + expect(shimo_menu.render?).to eq false + end + end + end + end +end diff --git a/spec/models/integrations/shimo_spec.rb b/spec/models/integrations/shimo_spec.rb index 25df8d2b2498ef9314a551bece25486ad660aab1..41f3f3c0c16caf24bbd80bb77057646086448974 100644 --- a/spec/models/integrations/shimo_spec.rb +++ b/spec/models/integrations/shimo_spec.rb @@ -38,4 +38,26 @@ end end end + + describe 'Caching has_shimo on project_settings' do + let(:project) { create(:project) } + + subject { project.project_setting.has_shimo? } + + it 'sets the property to true when integration is active' do + create(:shimo_integration, project: project, active: true) + + is_expected.to be(true) + end + + it 'sets the property to false when integration is not active' do + create(:shimo_integration, project: project, active: false) + + is_expected.to be(false) + end + + it 'creates a project_setting record if one was not already created' do + expect { create(:shimo_integration) }.to change(ProjectSetting, :count).by(1) + end + end end diff --git a/spec/requests/projects/integrations/shimos_controller_spec.rb b/spec/requests/projects/integrations/shimos_controller_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..7322143f87e546bd6dfd2c4df2c38710676e018d --- /dev/null +++ b/spec/requests/projects/integrations/shimos_controller_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Projects::Integrations::ShimosController do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user, developer_projects: [project]) } + let_it_be(:shimo_integration) { create(:shimo_integration, project: project) } + + before do + sign_in(user) + end + + describe 'GET #show' do + context 'when Shimo integration is inactive' do + before do + shimo_integration.update!(active: false) + end + + it 'returns 404 status' do + get project_integrations_shimo_path(project) + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when Shimo integration is active' do + it 'renders the "show" template' do + get project_integrations_shimo_path(project) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:show) + expect(response.body).to include shimo_integration.external_wiki_url + end + end + end +end