From 8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 18 Jun 2020 11:18:50 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-1-stable-ee --- app/helpers/active_sessions_helper.rb | 2 +- app/helpers/application_helper.rb | 3 +- app/helpers/application_settings_helper.rb | 14 ++ app/helpers/auto_devops_helper.rb | 2 +- app/helpers/clusters_helper.rb | 24 +- app/helpers/environments_helper.rb | 15 +- app/helpers/events_helper.rb | 2 +- app/helpers/gitlab_routing_helper.rb | 29 ++- app/helpers/issuables_helper.rb | 5 +- app/helpers/issues_helper.rb | 39 --- app/helpers/markup_helper.rb | 6 +- app/helpers/milestones_helper.rb | 309 ------------------------ app/helpers/milestones_routing_helper.rb | 19 -- app/helpers/namespaces_helper.rb | 41 +++- app/helpers/notes_helper.rb | 23 +- app/helpers/notifications_helper.rb | 2 +- app/helpers/numbers_helper.rb | 5 +- app/helpers/page_layout_helper.rb | 10 + app/helpers/projects/alert_management_helper.rb | 2 +- app/helpers/projects_helper.rb | 51 ++-- app/helpers/search_helper.rb | 107 -------- app/helpers/services_helper.rb | 22 ++ app/helpers/subscribable_banner_helper.rb | 9 + app/helpers/timeboxes_helper.rb | 302 +++++++++++++++++++++++ app/helpers/timeboxes_routing_helper.rb | 21 ++ app/helpers/todos_helper.rb | 14 +- app/helpers/visibility_level_helper.rb | 11 + app/helpers/wiki_helper.rb | 51 +++- 28 files changed, 596 insertions(+), 544 deletions(-) delete mode 100644 app/helpers/milestones_helper.rb delete mode 100644 app/helpers/milestones_routing_helper.rb create mode 100644 app/helpers/subscribable_banner_helper.rb create mode 100644 app/helpers/timeboxes_helper.rb create mode 100644 app/helpers/timeboxes_routing_helper.rb (limited to 'app/helpers') diff --git a/app/helpers/active_sessions_helper.rb b/app/helpers/active_sessions_helper.rb index 84aa1160f12..8fb23f99cb3 100644 --- a/app/helpers/active_sessions_helper.rb +++ b/app/helpers/active_sessions_helper.rb @@ -20,6 +20,6 @@ module ActiveSessionsHelper 'monitor-o' end - sprite_icon(icon_name, size: 16, css_class: 'prepend-top-2') + sprite_icon(icon_name, size: 16, css_class: 'gl-mt-2') end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 2df33073a89..bdfdf5a69b3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -103,7 +103,7 @@ module ApplicationHelper page: body_data_page, page_type_id: controller.params[:id], find_file: find_file_path, - group: "#{@group&.path}" + group: @group&.path }.merge(project_data) end @@ -113,6 +113,7 @@ module ApplicationHelper { project_id: @project.id, project: @project.path, + group: @project.group&.path, namespace_id: @project.namespace&.id } end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index b9f0e3582df..e709d15a946 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -26,6 +26,17 @@ module ApplicationSettingsHelper end end + def storage_weights + ApplicationSetting.repository_storages_weighted_attributes.map do |attribute| + storage = attribute.to_s.delete_prefix('repository_storages_weighted_') + { + name: attribute, + label: storage, + value: @application_setting.repository_storages_weighted[storage] || 0 + } + end + end + def all_protocols_enabled? Gitlab::CurrentSettings.enabled_git_access_protocol.blank? end @@ -228,6 +239,7 @@ module ApplicationSettingsHelper :import_sources, :max_artifacts_size, :max_attachment_size, + :max_import_size, :max_pages_size, :metrics_method_call_threshold, :minimum_password_length, @@ -261,6 +273,8 @@ module ApplicationSettingsHelper :sourcegraph_enabled, :sourcegraph_url, :sourcegraph_public_only, + :spam_check_endpoint_enabled, + :spam_check_endpoint_url, :terminal_max_session_time, :terms, :throttle_authenticated_api_enabled, diff --git a/app/helpers/auto_devops_helper.rb b/app/helpers/auto_devops_helper.rb index 0f0d5350df6..0f14680607e 100644 --- a/app/helpers/auto_devops_helper.rb +++ b/app/helpers/auto_devops_helper.rb @@ -2,7 +2,7 @@ module AutoDevopsHelper def show_auto_devops_callout?(project) - Feature.get(:auto_devops_banner_disabled).off? && + Feature.disabled?(:auto_devops_banner_disabled) && show_callout?('auto_devops_settings_dismissed') && can?(current_user, :admin_pipeline, project) && project.has_auto_devops_implicitly_disabled? && diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index 39aaf242231..1204f882707 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -17,15 +17,23 @@ module ClustersHelper end end + def js_clusters_list_data(path = nil) + { + endpoint: path, + img_tags: { + aws: { path: image_path('illustrations/logos/amazon_eks.svg'), text: s_('ClusterIntegration|Amazon EKS') }, + default: { path: image_path('illustrations/logos/kubernetes.svg'), text: _('Kubernetes Cluster') }, + gcp: { path: image_path('illustrations/logos/google_gke.svg'), text: s_('ClusterIntegration|Google GKE') } + } + } + end + + # This method is depreciated and will be removed when associated HAML files are moved to JavaScript def provider_icon(provider = nil) - case provider - when 'aws' - image_tag 'illustrations/logos/amazon_eks.svg', alt: s_('ClusterIntegration|Amazon EKS'), class: 'gl-h-full' - when 'gcp' - image_tag 'illustrations/logos/google_gke.svg', alt: s_('ClusterIntegration|Google GKE'), class: 'gl-h-full' - else - image_tag 'illustrations/logos/kubernetes.svg', alt: _('Kubernetes Cluster'), class: 'gl-h-full' - end + img_data = js_clusters_list_data.dig(:img_tags, provider&.to_sym) || + js_clusters_list_data.dig(:img_tags, :default) + + image_tag img_data[:path], alt: img_data[:text], class: 'gl-h-full' end def render_gcp_signup_offer diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb index e7b561af3da..41a255434af 100644 --- a/app/helpers/environments_helper.rb +++ b/app/helpers/environments_helper.rb @@ -60,7 +60,8 @@ module EnvironmentsHelper 'custom-metrics-path' => project_prometheus_metrics_path(project), 'validate-query-path' => validate_query_project_prometheus_metrics_path(project), 'custom-metrics-available' => "#{custom_metrics_available?(project)}", - 'prometheus-alerts-available' => "#{can?(current_user, :read_prometheus_alerts, project)}" + 'prometheus-alerts-available' => "#{can?(current_user, :read_prometheus_alerts, project)}", + 'dashboard-timezone' => project.metrics_setting_dashboard_timezone.to_s.upcase } end @@ -68,10 +69,11 @@ module EnvironmentsHelper return {} unless environment { - 'current-environment-name' => environment.name, - 'has-metrics' => "#{environment.has_metrics?}", - 'prometheus-status' => "#{environment.prometheus_status}", - 'environment-state' => "#{environment.state}" + 'metrics-dashboard-base-path' => environment_metrics_path(environment), + 'current-environment-name' => environment.name, + 'has-metrics' => "#{environment.has_metrics?}", + 'prometheus-status' => "#{environment.prometheus_status}", + 'environment-state' => "#{environment.state}" } end @@ -94,7 +96,8 @@ module EnvironmentsHelper 'empty-loading-svg-path' => image_path('illustrations/monitoring/loading.svg'), 'empty-no-data-svg-path' => image_path('illustrations/monitoring/no_data.svg'), 'empty-no-data-small-svg-path' => image_path('illustrations/chart-empty-state-small.svg'), - 'empty-unable-to-connect-svg-path' => image_path('illustrations/monitoring/unable_to_connect.svg') + 'empty-unable-to-connect-svg-path' => image_path('illustrations/monitoring/unable_to_connect.svg'), + 'custom-dashboard-base-path' => Metrics::Dashboard::CustomDashboardService::DASHBOARD_ROOT } end end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index e93aeba6dfd..c1f343edd10 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -188,7 +188,7 @@ module EventsHelper end def event_wiki_page_target_url(event) - project_wiki_url(event.project, event.target.canonical_slug) + project_wiki_url(event.project, event.target&.canonical_slug || Wiki::HOMEPAGE) end def event_note_title_html(event) diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 4474534045b..8a9380f4771 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -162,8 +162,8 @@ module GitlabRoutingHelper # against the arguments. We can speed this up 10x by generating the strings directly. # /*namespace_id/:project_id/-/jobs/:job_id/artifacts/download(.:format) - def fast_download_project_job_artifacts_path(project, job) - expose_fast_artifacts_path(project, job, :download) + def fast_download_project_job_artifacts_path(project, job, params = {}) + expose_fast_artifacts_path(project, job, :download, params) end # /*namespace_id/:project_id/-/jobs/:job_id/artifacts/keep(.:format) @@ -176,8 +176,13 @@ module GitlabRoutingHelper expose_fast_artifacts_path(project, job, :browse) end - def expose_fast_artifacts_path(project, job, action) + def expose_fast_artifacts_path(project, job, action, params = {}) path = "#{project.full_path}/-/jobs/#{job.id}/artifacts/#{action}" + + unless params.empty? + path += "?#{params.to_query}" + end + Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, path) end @@ -240,6 +245,14 @@ module GitlabRoutingHelper end end + def gitlab_dashboard_snippets_path(snippet, *args) + if snippet.is_a?(ProjectSnippet) + project_snippets_path(snippet.project, *args) + else + dashboard_snippets_path + end + end + def gitlab_raw_snippet_path(snippet, *args) if snippet.is_a?(ProjectSnippet) raw_project_snippet_path(snippet.project, snippet, *args) @@ -298,6 +311,16 @@ module GitlabRoutingHelper toggle_award_emoji_snippet_url(snippet, *new_args) end + # Wikis + + def wiki_path(wiki, **options) + Gitlab::UrlBuilder.wiki_url(wiki, only_path: true, **options) + end + + def wiki_page_path(wiki, page, **options) + Gitlab::UrlBuilder.wiki_page_url(wiki, page, only_path: true, **options) + end + private def snippet_query_params(snippet, *args) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 1ce99652463..a848c814742 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -205,9 +205,9 @@ module IssuablesHelper author_output end - output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip prepend-left-4', title: _('1st contribution!')) + output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip gl-ml-2', title: _('1st contribution!')) - output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block prepend-left-8") + output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block gl-ml-3") output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "d-md-none") output.join.html_safe @@ -276,6 +276,7 @@ module IssuablesHelper canUpdate: can?(current_user, :"update_#{issuable.to_ability_name}", issuable), canDestroy: can?(current_user, :"destroy_#{issuable.to_ability_name}", issuable), issuableRef: issuable.to_reference, + issuableStatus: issuable.state, markdownPreviewPath: preview_markdown_path(parent), markdownDocsPath: help_page_path('user/markdown'), lockVersion: issuable.lock_version, diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 39edfeea81e..244b97c7196 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -9,40 +9,6 @@ module IssuesHelper classes.join(' ') end - def url_for_issue(issue_iid, project = @project, options = {}) - return '' if project.nil? - - url = - if options[:internal] - url_for_internal_issue(issue_iid, project, options) - else - url_for_tracker_issue(issue_iid, project, options) - end - - # Ensure we return a valid URL to prevent possible XSS. - URI.parse(url).to_s - rescue URI::InvalidURIError - '' - end - - def url_for_tracker_issue(issue_iid, project, options) - if options[:only_path] - project.issues_tracker.issue_path(issue_iid) - else - project.issues_tracker.issue_url(issue_iid) - end - end - - def url_for_internal_issue(issue_iid, project = @project, options = {}) - helpers = Gitlab::Routing.url_helpers - - if options[:only_path] - helpers.namespace_project_issue_path(namespace_id: project.namespace, project_id: project, id: issue_iid) - else - helpers.namespace_project_issue_url(namespace_id: project.namespace, project_id: project, id: issue_iid) - end - end - def status_box_class(item) if item.try(:expired?) 'status-box-expired' @@ -168,11 +134,6 @@ module IssuesHelper def show_moved_service_desk_issue_warning?(issue) false end - - # Required for Banzai::Filter::IssueReferenceFilter - module_function :url_for_issue - module_function :url_for_internal_issue - module_function :url_for_tracker_issue end IssuesHelper.prepend_if_ee('EE::IssuesHelper') diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb index 4f66356c27e..7ab2b33de8c 100644 --- a/app/helpers/markup_helper.rb +++ b/app/helpers/markup_helper.rb @@ -129,8 +129,8 @@ module MarkupHelper context.merge!( pipeline: :wiki, project: @project, - project_wiki: @project_wiki, - repository: @project_wiki.repository, + wiki: @wiki, + repository: @wiki.repository, page_slug: wiki_page.slug, issuable_state_filter_enabled: true ) @@ -300,7 +300,7 @@ module MarkupHelper # RepositoryLinkFilter and UploadLinkFilter commit: @commit, - project_wiki: @project_wiki, + wiki: @wiki, ref: @ref, requested_path: @path ) diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb deleted file mode 100644 index df1ee54c5ac..00000000000 --- a/app/helpers/milestones_helper.rb +++ /dev/null @@ -1,309 +0,0 @@ -# frozen_string_literal: true - -module MilestonesHelper - include EntityDateHelper - include Gitlab::Utils::StrongMemoize - - def milestone_status_string(milestone) - if milestone.closed? - _('Closed') - elsif milestone.expired? - _('Past due') - elsif milestone.upcoming? - _('Upcoming') - else - _('Open') - end - end - - def milestones_filter_path(opts = {}) - if @project - project_milestones_path(@project, opts) - elsif @group - group_milestones_path(@group, opts) - else - dashboard_milestones_path(opts) - end - end - - def milestones_issues_path(opts = {}) - if @project - project_issues_path(@project, opts) - elsif @group - issues_group_path(@group, opts) - else - issues_dashboard_path(opts) - end - end - - def milestones_browse_issuables_path(milestone, state: nil, type:) - opts = { milestone_title: milestone.title, state: state } - - if @project - polymorphic_path([@project.namespace.becomes(Namespace), @project, type], opts) - elsif @group - polymorphic_url([type, @group], opts) - else - polymorphic_url([type, :dashboard], opts) - end - end - - def milestone_issues_by_label_count(milestone, label, state:) - issues = milestone.issues.with_label(label.title) - issues = - case state - when :opened - issues.opened - when :closed - issues.closed - else - raise ArgumentError, _("invalid milestone state `%{state}`") % { state: state } - end - - issues.size - end - - # Returns count of milestones for different states - # Uses explicit hash keys as the 'opened' state URL params differs from the db value - # and we need to add the total - # rubocop: disable CodeReuse/ActiveRecord - def milestone_counts(milestones) - counts = milestones.reorder(nil).group(:state).count - - { - opened: counts['active'] || 0, - closed: counts['closed'] || 0, - all: counts.values.sum || 0 - } - end - # rubocop: enable CodeReuse/ActiveRecord - - # Show 'active' class if provided GET param matches check - # `or_blank` allows the function to return 'active' when given an empty param - # Could be refactored to be simpler but that may make it harder to read - def milestone_class_for_state(param, check, match_blank_param = false) - if match_blank_param - 'active' if param.blank? || param == check - elsif param == check - 'active' - else - check - end - end - - def milestone_progress_tooltip_text(milestone) - has_issues = milestone.total_issues_count > 0 - - if has_issues - [ - _('Progress'), - _("%{percent}%% complete") % { percent: milestone.percent_complete } - ].join('
') - else - _('Progress') - end - end - - def milestone_progress_bar(milestone) - options = { - class: 'progress-bar bg-success', - style: "width: #{milestone.percent_complete}%;" - } - - content_tag :div, class: 'progress' do - content_tag :div, nil, options - end - end - - def milestones_filter_dropdown_path - project = @target_project || @project - if project - project_milestones_path(project, :json) - elsif @group - group_milestones_path(@group, :json) - else - dashboard_milestones_path(:json) - end - end - - def milestone_time_for(date, date_type) - title = date_type == :start ? "Start date" : "End date" - - if date - time_ago = time_ago_in_words(date).sub("about ", "") - state = if date.past? - "ago" - else - "remaining" - end - - content = [ - title, - "
", - date.to_s(:medium), - "(#{time_ago} #{state})" - ].join(" ") - - content.html_safe - else - title - end - end - - def milestone_issues_tooltip_text(milestone) - total = milestone.total_issues_count - opened = milestone.opened_issues_count - closed = milestone.closed_issues_count - - return _("Issues") if total.zero? - - content = [] - - if opened > 0 - content << n_("1 open issue", "%{issues} open issues", opened) % { issues: opened } - end - - if closed > 0 - content << n_("1 closed issue", "%{issues} closed issues", closed) % { issues: closed } - end - - content.join('
').html_safe - end - - def milestone_merge_requests_tooltip_text(milestone) - merge_requests = milestone.merge_requests - - return _("Merge requests") if merge_requests.empty? - - content = [] - - content << n_("1 open merge request", "%{merge_requests} open merge requests", merge_requests.opened.count) % { merge_requests: merge_requests.opened.count } if merge_requests.opened.any? - content << n_("1 closed merge request", "%{merge_requests} closed merge requests", merge_requests.closed.count) % { merge_requests: merge_requests.closed.count } if merge_requests.closed.any? - content << n_("1 merged merge request", "%{merge_requests} merged merge requests", merge_requests.merged.count) % { merge_requests: merge_requests.merged.count } if merge_requests.merged.any? - - content.join('
').html_safe - end - - def milestone_releases_tooltip_text(milestone) - count = milestone.releases.count - - return _("Releases") if count.zero? - - n_("%{releases} release", "%{releases} releases", count) % { releases: count } - end - - def recent_releases_with_counts(milestone) - total_count = milestone.releases.size - return [[], 0, 0] if total_count == 0 - - recent_releases = milestone.releases.recent.to_a - more_count = total_count - recent_releases.size - [recent_releases, total_count, more_count] - end - - def milestone_tooltip_due_date(milestone) - if milestone.due_date - "#{milestone.due_date.to_s(:medium)} (#{remaining_days_in_words(milestone.due_date, milestone.start_date)})" - else - _('Milestone') - end - end - - def milestone_date_range(milestone) - if milestone.start_date && milestone.due_date - "#{milestone.start_date.to_s(:medium)}–#{milestone.due_date.to_s(:medium)}" - elsif milestone.due_date - if milestone.due_date.past? - _("expired on %{milestone_due_date}") % { milestone_due_date: milestone.due_date.strftime('%b %-d, %Y') } - else - _("expires on %{milestone_due_date}") % { milestone_due_date: milestone.due_date.strftime('%b %-d, %Y') } - end - elsif milestone.start_date - if milestone.start_date.past? - _("started on %{milestone_start_date}") % { milestone_start_date: milestone.start_date.strftime('%b %-d, %Y') } - else - _("starts on %{milestone_start_date}") % { milestone_start_date: milestone.start_date.strftime('%b %-d, %Y') } - end - end - end - - def milestone_tab_path(milestone, tab) - if milestone.global_milestone? - url_for(action: tab, title: milestone.title, format: :json) - else - url_for(action: tab, format: :json) - end - end - - def update_milestone_path(milestone, params = {}) - if milestone.project_milestone? - project_milestone_path(milestone.project, milestone, milestone: params) - else - group_milestone_route(milestone, params) - end - end - - def group_milestone_route(milestone, params = {}) - params = nil if params.empty? - - if milestone.legacy_group_milestone? - group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: params) - else - group_milestone_path(milestone.group, milestone.iid, milestone: params) - end - end - - def group_or_project_milestone_path(milestone) - params = - if milestone.group_milestone? - { milestone: { title: milestone.title } } - else - { title: milestone.title } - end - - milestone_path(milestone.milestone, params) - end - - def edit_milestone_path(milestone) - if milestone.group_milestone? - edit_group_milestone_path(milestone.group, milestone) - elsif milestone.project_milestone? - edit_project_milestone_path(milestone.project, milestone) - end - end - - def can_admin_project_milestones? - strong_memoize(:can_admin_project_milestones) do - can?(current_user, :admin_milestone, @project) - end - end - - def can_admin_group_milestones? - strong_memoize(:can_admin_group_milestones) do - can?(current_user, :admin_milestone, @project.group) - end - end - - def display_issues_count_warning?(milestone) - milestone_visible_issues_count(milestone) > Milestone::DISPLAY_ISSUES_LIMIT - end - - def milestone_issues_count_message(milestone) - total_count = milestone_visible_issues_count(milestone) - limit = Milestone::DISPLAY_ISSUES_LIMIT - link_options = { milestone_title: @milestone.title } - - message = _('Showing %{limit} of %{total_count} issues. ') % { limit: limit, total_count: total_count } - message += link_to(_('View all issues'), milestones_issues_path(link_options)) - - message.html_safe - end - - private - - def milestone_visible_issues_count(milestone) - @milestone_visible_issues_count ||= milestone.issues_visible_to_user(current_user).size - end -end - -MilestonesHelper.prepend_if_ee('EE::MilestonesHelper') diff --git a/app/helpers/milestones_routing_helper.rb b/app/helpers/milestones_routing_helper.rb deleted file mode 100644 index a49b561533a..00000000000 --- a/app/helpers/milestones_routing_helper.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module MilestonesRoutingHelper - def milestone_path(milestone, *args) - if milestone.group_milestone? - group_milestone_path(milestone.group, milestone, *args) - elsif milestone.project_milestone? - project_milestone_path(milestone.project, milestone, *args) - end - end - - def milestone_url(milestone, *args) - if milestone.group_milestone? - group_milestone_url(milestone.group, milestone, *args) - elsif milestone.project_milestone? - project_milestone_url(milestone.project, milestone, *args) - end - end -end diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index 228dc2cc27f..b9f8d81bc4e 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -56,6 +56,45 @@ module NamespacesHelper namespaces_options(selected, options) end + def namespace_storage_alert(namespace) + return {} if current_user.nil? + + payload = Namespaces::CheckStorageSizeService.new(namespace, current_user).execute.payload + + return {} if payload.empty? + + alert_level = payload[:alert_level] + root_namespace = payload[:root_namespace] + + return {} if cookies["hide_storage_limit_alert_#{root_namespace.id}_#{alert_level}"] == 'true' + + payload + end + + def namespace_storage_alert_style(alert_level) + if alert_level == :error || alert_level == :alert + 'danger' + else + alert_level.to_s + end + end + + def namespace_storage_alert_icon(alert_level) + if alert_level == :error || alert_level == :alert + 'error' + elsif alert_level == :info + 'information-o' + else + alert_level.to_s + end + end + + def namespace_storage_usage_link(namespace) + # The usage quota page is only available in EE. This will be changed in + # the future, see https://gitlab.com/gitlab-org/gitlab/-/issues/220042. + nil + end + private # Many importers create a temporary Group, so use the real @@ -89,4 +128,4 @@ module NamespacesHelper end end -NamespacesHelper.include_if_ee('EE::NamespacesHelper') +NamespacesHelper.prepend_if_ee('EE::NamespacesHelper') diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index acf9f8c5b5b..782f1d3e759 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -3,6 +3,13 @@ module NotesHelper MAX_PRERENDERED_NOTES = 10 + def note_target_title(note) + # The design title is already present in `Event#note_target_reference`. + return if note.nil? || note.for_design? + + note.title + end + def note_target_fields(note) if note.noteable hidden_field_tag(:target_type, note.noteable.class.name.underscore) + @@ -54,8 +61,8 @@ module NotesHelper class: 'add-diff-note js-add-diff-note-button', type: 'submit', name: 'button', data: diff_view_line_data(line_code, position, line_type), - title: 'Add a comment to this line' do - icon('comment-o') + title: _('Add a comment to this line') do + sprite_icon('comment', size: 12) end end @@ -162,7 +169,7 @@ module NotesHelper end def notes_data(issuable) - { + data = { discussionsPath: discussions_path(issuable), registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'), newSessionPath: new_session_path(:user, redirect_to_referer: 'yes'), @@ -174,6 +181,16 @@ module NotesHelper prerenderedNotesCount: issuable.capped_notes_count(MAX_PRERENDERED_NOTES), lastFetchedAt: Time.now.to_i } + + if issuable.is_a?(MergeRequest) + data.merge!( + draftsPath: project_merge_request_drafts_path(@project, issuable), + draftsPublishPath: publish_project_merge_request_drafts_path(@project, issuable), + draftsDiscardPath: discard_project_merge_request_drafts_path(@project, issuable) + ) + end + + data end def discussion_resolved_intro(discussion) diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 8fd277564df..68dfd008921 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -122,6 +122,6 @@ module NotificationsHelper end def notification_event_disabled?(event) - event == :fixed_pipeline && Feature.disabled?(:ci_pipeline_fixed_notifications) + event == :fixed_pipeline && !Gitlab::Ci::Features.pipeline_fixed_notifications? end end diff --git a/app/helpers/numbers_helper.rb b/app/helpers/numbers_helper.rb index 3c0b11c4d32..38d3f90dd55 100644 --- a/app/helpers/numbers_helper.rb +++ b/app/helpers/numbers_helper.rb @@ -1,15 +1,14 @@ # frozen_string_literal: true module NumbersHelper - # rubocop: disable CodeReuse/ActiveRecord def limited_counter_with_delimiter(resource, **options) limit = options.fetch(:limit, 1000).to_i - count = resource.limit(limit + 1).count(:all) + count = resource.page.total_count_with_limit(:all, limit: limit) + if count > limit number_with_delimiter(count - 1, options) + '+' else number_with_delimiter(count, options) end end - # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 46e2c9ce56e..a44760e85ca 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -104,6 +104,16 @@ module PageLayoutHelper end end + # This helper ensures there is always a default `Gitlab::SearchContext` available + # to all controller that use the application layout. + def search_context + strong_memoize(:search_context) do + next super if defined?(super) + + Gitlab::SearchContext::Builder.new(controller.view_context).build! + end + end + def fluid_layout current_user && current_user.layout == "fluid" end diff --git a/app/helpers/projects/alert_management_helper.rb b/app/helpers/projects/alert_management_helper.rb index af86ef715c2..bc585899591 100644 --- a/app/helpers/projects/alert_management_helper.rb +++ b/app/helpers/projects/alert_management_helper.rb @@ -15,7 +15,7 @@ module Projects::AlertManagementHelper { 'alert-id' => alert_id, 'project-path' => project.full_path, - 'new-issue-path' => new_project_issue_path(project) + 'project-issues-path' => project_issues_path(project) } end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index d743ea6aeea..bda9a69d71f 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -284,8 +284,8 @@ module ProjectsHelper "xcode://clone?repo=#{CGI.escape(default_url_to_repo(project))}" end - def link_to_bfg - link_to 'BFG', 'https://rtyley.github.io/bfg-repo-cleaner/', target: '_blank', rel: 'noopener noreferrer' + def link_to_filter_repo + link_to 'git filter-repo', 'https://github.com/newren/git-filter-repo', target: '_blank', rel: 'noopener noreferrer' end def explore_projects_tab? @@ -367,6 +367,10 @@ module ProjectsHelper @project.metrics_setting_external_dashboard_url end + def metrics_dashboard_timezone + @project.metrics_setting_dashboard_timezone + end + def grafana_integration_url @project.grafana_integration&.grafana_url end @@ -410,7 +414,7 @@ module ProjectsHelper nav_tabs << :pipelines end - if can?(current_user, :read_environment, project) || can?(current_user, :read_cluster, project) + if can_view_operations_tab?(current_user, project) nav_tabs << :operations end @@ -438,22 +442,29 @@ module ProjectsHelper def tab_ability_map { - environments: :read_environment, - milestones: :read_milestone, - snippets: :read_snippet, - settings: :admin_project, - builds: :read_build, - clusters: :read_cluster, - serverless: :read_cluster, - error_tracking: :read_sentry_issue, - alert_management: :read_alert_management_alert, - labels: :read_label, - issues: :read_issue, - project_members: :read_project_member, - wiki: :read_wiki + environments: :read_environment, + metrics_dashboards: :metrics_dashboard, + milestones: :read_milestone, + snippets: :read_snippet, + settings: :admin_project, + builds: :read_build, + clusters: :read_cluster, + serverless: :read_cluster, + error_tracking: :read_sentry_issue, + alert_management: :read_alert_management_alert, + labels: :read_label, + issues: :read_issue, + project_members: :read_project_member, + wiki: :read_wiki } end + def can_view_operations_tab?(current_user, project) + [:read_environment, :read_cluster, :metrics_dashboard].any? do |ability| + can?(current_user, ability, project) + end + end + def search_tab_ability_map @search_tab_ability_map ||= tab_ability_map.merge( blobs: :download_code, @@ -533,11 +544,6 @@ module ProjectsHelper end end - def project_wiki_path_with_version(proj, page, version, is_newest) - url_params = is_newest ? {} : { version_id: version } - project_wiki_path(proj, page, url_params) - end - def project_status_css_class(status) case status when "started" @@ -670,7 +676,6 @@ module ProjectsHelper def sidebar_settings_paths %w[ projects#edit - project_members#index integrations#show services#edit hooks#index @@ -729,7 +734,7 @@ module ProjectsHelper end def native_code_navigation_enabled?(project) - Feature.enabled?(:code_navigation, project) + Feature.enabled?(:code_navigation, project, default_enabled: true) end def show_visibility_confirm_modal?(project) diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 5ad65c59a2e..4e3b6aad8cc 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -3,28 +3,6 @@ module SearchHelper SEARCH_PERMITTED_PARAMS = [:search, :scope, :project_id, :group_id, :repository_ref, :snippets].freeze - def search_autocomplete_opts(term) - return unless current_user - - resources_results = [ - groups_autocomplete(term), - projects_autocomplete(term) - ].flatten - - search_pattern = Regexp.new(Regexp.escape(term), "i") - - generic_results = project_autocomplete + default_autocomplete + help_autocomplete - generic_results.concat(default_autocomplete_admin) if current_user.admin? - generic_results.select! { |result| result[:label] =~ search_pattern } - - [ - resources_results, - generic_results - ].flatten.uniq do |item| - item[:label] - end - end - def search_entries_info(collection, scope, term) return if collection.to_a.empty? @@ -95,91 +73,6 @@ module SearchHelper private - # Autocomplete results for various settings pages - def default_autocomplete - [ - { category: "Settings", label: _("User settings"), url: profile_path }, - { category: "Settings", label: _("SSH Keys"), url: profile_keys_path }, - { category: "Settings", label: _("Dashboard"), url: root_path } - ] - end - - # Autocomplete results for settings pages, for admins - def default_autocomplete_admin - [ - { category: "Settings", label: _("Admin Section"), url: admin_root_path } - ] - end - - # Autocomplete results for internal help pages - def help_autocomplete - [ - { category: "Help", label: _("API Help"), url: help_page_path("api/README") }, - { category: "Help", label: _("Markdown Help"), url: help_page_path("user/markdown") }, - { category: "Help", label: _("Permissions Help"), url: help_page_path("user/permissions") }, - { category: "Help", label: _("Public Access Help"), url: help_page_path("public_access/public_access") }, - { category: "Help", label: _("Rake Tasks Help"), url: help_page_path("raketasks/README") }, - { category: "Help", label: _("SSH Keys Help"), url: help_page_path("ssh/README") }, - { category: "Help", label: _("System Hooks Help"), url: help_page_path("system_hooks/system_hooks") }, - { category: "Help", label: _("Webhooks Help"), url: help_page_path("user/project/integrations/webhooks") }, - { category: "Help", label: _("Workflow Help"), url: help_page_path("workflow/README") } - ] - end - - # Autocomplete results for the current project, if it's defined - def project_autocomplete - if @project && @project.repository.root_ref - ref = @ref || @project.repository.root_ref - - [ - { category: "In this project", label: _("Files"), url: project_tree_path(@project, ref) }, - { category: "In this project", label: _("Commits"), url: project_commits_path(@project, ref) }, - { category: "In this project", label: _("Network"), url: project_network_path(@project, ref) }, - { category: "In this project", label: _("Graph"), url: project_graph_path(@project, ref) }, - { category: "In this project", label: _("Issues"), url: project_issues_path(@project) }, - { category: "In this project", label: _("Merge Requests"), url: project_merge_requests_path(@project) }, - { category: "In this project", label: _("Milestones"), url: project_milestones_path(@project) }, - { category: "In this project", label: _("Snippets"), url: project_snippets_path(@project) }, - { category: "In this project", label: _("Members"), url: project_project_members_path(@project) }, - { category: "In this project", label: _("Wiki"), url: project_wikis_path(@project) } - ] - else - [] - end - end - - # Autocomplete results for the current user's groups - # rubocop: disable CodeReuse/ActiveRecord - def groups_autocomplete(term, limit = 5) - current_user.authorized_groups.order_id_desc.search(term).limit(limit).map do |group| - { - category: "Groups", - id: group.id, - label: "#{search_result_sanitize(group.full_name)}", - url: group_path(group), - avatar_url: group.avatar_url || '' - } - end - end - # rubocop: enable CodeReuse/ActiveRecord - - # Autocomplete results for the current user's projects - # rubocop: disable CodeReuse/ActiveRecord - def projects_autocomplete(term, limit = 5) - current_user.authorized_projects.order_id_desc.search_by_title(term) - .sorted_by_stars_desc.non_archived.limit(limit).map do |p| - { - category: "Projects", - id: p.id, - value: "#{search_result_sanitize(p.name)}", - label: "#{search_result_sanitize(p.full_name)}", - url: project_path(p), - avatar_url: p.avatar_url || '' - } - end - end - # rubocop: enable CodeReuse/ActiveRecord - def search_result_sanitize(str) Sanitize.clean(str) end diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index b13cc93436f..fe839b92ba6 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -98,6 +98,28 @@ module ServicesHelper end end + def integration_form_refactor? + Feature.enabled?(:integration_form_refactor, @project) + end + + def trigger_events_for_service + return [] unless integration_form_refactor? + + ServiceEventSerializer.new(service: @service).represent(@service.configurable_events).to_json + end + + def fields_for_service + return [] unless integration_form_refactor? + + ServiceFieldSerializer.new(service: @service).represent(@service.global_fields).to_json + end + + def show_service_trigger_events? + return false if @service.is_a?(JiraService) || integration_form_refactor? + + @service.configurable_events.present? + end + extend self end diff --git a/app/helpers/subscribable_banner_helper.rb b/app/helpers/subscribable_banner_helper.rb new file mode 100644 index 00000000000..c9d4370f8ad --- /dev/null +++ b/app/helpers/subscribable_banner_helper.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module SubscribableBannerHelper + # Overridden in EE + def display_subscription_banner! + end +end + +SubscribableBannerHelper.prepend_if_ee('EE::SubscribableBannerHelper') diff --git a/app/helpers/timeboxes_helper.rb b/app/helpers/timeboxes_helper.rb new file mode 100644 index 00000000000..0bffdba7349 --- /dev/null +++ b/app/helpers/timeboxes_helper.rb @@ -0,0 +1,302 @@ +# frozen_string_literal: true + +module TimeboxesHelper + include EntityDateHelper + include Gitlab::Utils::StrongMemoize + + def milestone_status_string(milestone) + if milestone.closed? + _('Closed') + elsif milestone.expired? + _('Past due') + elsif milestone.upcoming? + _('Upcoming') + else + _('Open') + end + end + + def milestones_filter_path(opts = {}) + if @project + project_milestones_path(@project, opts) + elsif @group + group_milestones_path(@group, opts) + else + dashboard_milestones_path(opts) + end + end + + def milestones_issues_path(opts = {}) + if @project + project_issues_path(@project, opts) + elsif @group + issues_group_path(@group, opts) + else + issues_dashboard_path(opts) + end + end + + def milestones_browse_issuables_path(milestone, state: nil, type:) + opts = { milestone_title: milestone.title, state: state } + + if @project + polymorphic_path([@project.namespace.becomes(Namespace), @project, type], opts) + elsif @group + polymorphic_url([type, @group], opts) + else + polymorphic_url([type, :dashboard], opts) + end + end + + def milestone_issues_by_label_count(milestone, label, state:) + issues = milestone.issues.with_label(label.title) + issues = + case state + when :opened + issues.opened + when :closed + issues.closed + else + raise ArgumentError, _("invalid milestone state `%{state}`") % { state: state } + end + + issues.size + end + + # Returns count of milestones for different states + # Uses explicit hash keys as the 'opened' state URL params differs from the db value + # and we need to add the total + # rubocop: disable CodeReuse/ActiveRecord + def milestone_counts(milestones) + counts = milestones.reorder(nil).group(:state).count + + { + opened: counts['active'] || 0, + closed: counts['closed'] || 0, + all: counts.values.sum || 0 + } + end + # rubocop: enable CodeReuse/ActiveRecord + + # Show 'active' class if provided GET param matches check + # `or_blank` allows the function to return 'active' when given an empty param + # Could be refactored to be simpler but that may make it harder to read + def milestone_class_for_state(param, check, match_blank_param = false) + if match_blank_param + 'active' if param.blank? || param == check + elsif param == check + 'active' + else + check + end + end + + def milestone_progress_tooltip_text(milestone) + has_issues = milestone.total_issues_count > 0 + + if has_issues + [ + _('Progress'), + _("%{percent}%% complete") % { percent: milestone.percent_complete } + ].join('
') + else + _('Progress') + end + end + + def milestone_progress_bar(milestone) + options = { + class: 'progress-bar bg-success', + style: "width: #{milestone.percent_complete}%;" + } + + content_tag :div, class: 'progress' do + content_tag :div, nil, options + end + end + + def milestones_filter_dropdown_path + project = @target_project || @project + if project + project_milestones_path(project, :json) + elsif @group + group_milestones_path(@group, :json) + else + dashboard_milestones_path(:json) + end + end + + def milestone_time_for(date, date_type) + title = date_type == :start ? "Start date" : "End date" + + if date + time_ago = time_ago_in_words(date).sub("about ", "") + state = if date.past? + "ago" + else + "remaining" + end + + content = [ + title, + "
", + date.to_s(:medium), + "(#{time_ago} #{state})" + ].join(" ") + + content.html_safe + else + title + end + end + + def milestone_issues_tooltip_text(milestone) + total = milestone.total_issues_count + opened = milestone.opened_issues_count + closed = milestone.closed_issues_count + + return _("Issues") if total.zero? + + content = [] + + if opened > 0 + content << n_("1 open issue", "%{issues} open issues", opened) % { issues: opened } + end + + if closed > 0 + content << n_("1 closed issue", "%{issues} closed issues", closed) % { issues: closed } + end + + content.join('
').html_safe + end + + def milestone_merge_requests_tooltip_text(milestone) + merge_requests = milestone.merge_requests + + return _("Merge requests") if merge_requests.empty? + + content = [] + + content << n_("1 open merge request", "%{merge_requests} open merge requests", merge_requests.opened.count) % { merge_requests: merge_requests.opened.count } if merge_requests.opened.any? + content << n_("1 closed merge request", "%{merge_requests} closed merge requests", merge_requests.closed.count) % { merge_requests: merge_requests.closed.count } if merge_requests.closed.any? + content << n_("1 merged merge request", "%{merge_requests} merged merge requests", merge_requests.merged.count) % { merge_requests: merge_requests.merged.count } if merge_requests.merged.any? + + content.join('
').html_safe + end + + def milestone_releases_tooltip_text(milestone) + count = milestone.releases.count + + return _("Releases") if count.zero? + + n_("%{releases} release", "%{releases} releases", count) % { releases: count } + end + + def recent_releases_with_counts(milestone) + total_count = milestone.releases.size + return [[], 0, 0] if total_count == 0 + + recent_releases = milestone.releases.recent.to_a + more_count = total_count - recent_releases.size + [recent_releases, total_count, more_count] + end + + def milestone_tooltip_due_date(milestone) + if milestone.due_date + "#{milestone.due_date.to_s(:medium)} (#{remaining_days_in_words(milestone.due_date, milestone.start_date)})" + else + _('Milestone') + end + end + + def timebox_date_range(timebox) + if timebox.start_date && timebox.due_date + "#{timebox.start_date.to_s(:medium)}–#{timebox.due_date.to_s(:medium)}" + elsif timebox.due_date + if timebox.due_date.past? + _("expired on %{timebox_due_date}") % { timebox_due_date: timebox.due_date.to_s(:medium) } + else + _("expires on %{timebox_due_date}") % { timebox_due_date: timebox.due_date.to_s(:medium) } + end + elsif timebox.start_date + if timebox.start_date.past? + _("started on %{timebox_start_date}") % { timebox_start_date: timebox.start_date.to_s(:medium) } + else + _("starts on %{timebox_start_date}") % { timebox_start_date: timebox.start_date.to_s(:medium) } + end + end + end + alias_method :milestone_date_range, :timebox_date_range + + def milestone_tab_path(milestone, tab) + url_for(action: tab, format: :json) + end + + def update_milestone_path(milestone, params = {}) + if milestone.project_milestone? + project_milestone_path(milestone.project, milestone, milestone: params) + else + group_milestone_route(milestone, params) + end + end + + def group_milestone_route(milestone, params = {}) + params = nil if params.empty? + + group_milestone_path(milestone.group, milestone.iid, milestone: params) + end + + def group_or_project_milestone_path(milestone) + params = + if milestone.group_milestone? + { milestone: { title: milestone.title } } + else + { title: milestone.title } + end + + milestone_path(milestone.milestone, params) + end + + def edit_milestone_path(milestone) + if milestone.group_milestone? + edit_group_milestone_path(milestone.group, milestone) + elsif milestone.project_milestone? + edit_project_milestone_path(milestone.project, milestone) + end + end + + def can_admin_project_milestones? + strong_memoize(:can_admin_project_milestones) do + can?(current_user, :admin_milestone, @project) + end + end + + def can_admin_group_milestones? + strong_memoize(:can_admin_group_milestones) do + can?(current_user, :admin_milestone, @project.group) + end + end + + def display_issues_count_warning?(milestone) + milestone_visible_issues_count(milestone) > Milestone::DISPLAY_ISSUES_LIMIT + end + + def milestone_issues_count_message(milestone) + total_count = milestone_visible_issues_count(milestone) + limit = Milestone::DISPLAY_ISSUES_LIMIT + link_options = { milestone_title: @milestone.title } + + message = _('Showing %{limit} of %{total_count} issues. ') % { limit: limit, total_count: total_count } + message += link_to(_('View all issues'), milestones_issues_path(link_options)) + + message.html_safe + end + + private + + def milestone_visible_issues_count(milestone) + @milestone_visible_issues_count ||= milestone.issues_visible_to_user(current_user).size + end +end + +TimeboxesHelper.prepend_if_ee('EE::TimeboxesHelper') diff --git a/app/helpers/timeboxes_routing_helper.rb b/app/helpers/timeboxes_routing_helper.rb new file mode 100644 index 00000000000..6fb5a1a3185 --- /dev/null +++ b/app/helpers/timeboxes_routing_helper.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module TimeboxesRoutingHelper + def milestone_path(milestone, *args) + if milestone.group_milestone? + group_milestone_path(milestone.group, milestone, *args) + elsif milestone.project_milestone? + project_milestone_path(milestone.project, milestone, *args) + end + end + + def milestone_url(milestone, *args) + if milestone.group_milestone? + group_milestone_url(milestone.group, milestone, *args) + elsif milestone.project_milestone? + project_milestone_url(milestone.project, milestone, *args) + end + end +end + +TimeboxesRoutingHelper.prepend_if_ee('EE::TimeboxesRoutingHelper') diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 41f39c7e798..2b4f2f11d1e 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -37,11 +37,12 @@ module TodosHelper end def todo_target_title(todo) - if todo.target - "\"#{todo.target.title}\"" - else - "" - end + # Design To Dos' filenames are displayed in `#todo_target_link` (see `Design#to_reference`), + # so to avoid displaying duplicate filenames in the To Do list for designs, + # we return an empty string here. + return "" if todo.target.blank? || todo.for_design? + + "\"#{todo.target.title}\"" end def todo_parent_path(todo) @@ -54,6 +55,7 @@ module TodosHelper def todo_target_type_name(todo) return _('design') if todo.for_design? + return _('alert') if todo.for_alert? todo.target_type.titleize.downcase end @@ -67,6 +69,8 @@ module TodosHelper project_commit_path(todo.project, todo.target, path_options) elsif todo.for_design? todos_design_path(todo, path_options) + elsif todo.for_alert? + details_project_alert_management_path(todo.project, todo.target) else path = [todo.resource_parent, todo.target] diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index d62839cf037..304b58d232a 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -167,6 +167,17 @@ module VisibilityLevelHelper [requested_level, max_allowed_visibility_level(form_model)].min end + def available_visibility_levels(form_model) + Gitlab::VisibilityLevel.values.reject do |level| + disallowed_visibility_level?(form_model, level) || + restricted_visibility_levels.include?(level) + end + end + + def snippets_selected_visibility_level(visibility_levels, selected) + visibility_levels.find { |level| level == selected } || visibility_levels.min + end + def multiple_visibility_levels_restricted? restricted_visibility_levels.many? # rubocop: disable CodeReuse/ActiveRecord end diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index dd8fde2a697..3c983606b73 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -22,7 +22,7 @@ module WikiHelper page_slug_split .map do |dir_or_page| current_slug = "#{current_slug}#{dir_or_page}/" - add_to_breadcrumb_dropdown link_to(WikiPage.unhyphenize(dir_or_page).capitalize, project_wiki_path(@project, current_slug)), location: :after + add_to_breadcrumb_dropdown link_to(WikiPage.unhyphenize(dir_or_page).capitalize, wiki_page_path(@wiki, current_slug)), location: :after end end @@ -32,7 +32,7 @@ module WikiHelper content_tag(:div, class: 'alert alert-danger') do case error when WikiPage::PageChangedError - page_link = link_to s_("WikiPageConflictMessage|the page"), project_wiki_path(@project, @page), target: "_blank" + page_link = link_to s_("WikiPageConflictMessage|the page"), wiki_page_path(@wiki, @page), target: "_blank" concat( (s_("WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{page_link} and make sure your changes will not unintentionally remove theirs.") % { page_link: page_link }).html_safe ) @@ -45,26 +45,63 @@ module WikiHelper end def wiki_attachment_upload_url - expose_url(api_v4_projects_wikis_attachments_path(id: @project.id)) + expose_url(api_v4_projects_wikis_attachments_path(id: @wiki.container.id)) end - def wiki_sort_controls(project, sort, direction) - sort ||= ProjectWiki::TITLE_ORDER + def wiki_sort_controls(wiki, sort, direction) + sort ||= Wiki::TITLE_ORDER link_class = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort' reversed_direction = direction == 'desc' ? 'asc' : 'desc' icon_class = direction == 'desc' ? 'highest' : 'lowest' - link_to(project_wikis_pages_path(project, sort: sort, direction: reversed_direction), + link_to(wiki_path(wiki, action: :pages, sort: sort, direction: reversed_direction), type: 'button', class: link_class, title: _('Sort direction')) do sprite_icon("sort-#{icon_class}", size: 16) end end def wiki_sort_title(key) - if key == ProjectWiki::CREATED_AT_ORDER + if key == Wiki::CREATED_AT_ORDER s_("Wiki|Created date") else s_("Wiki|Title") end end + + def wiki_empty_state_messages(wiki) + case wiki.container + when Project + { + writable: { + title: s_('WikiEmpty|The wiki lets you write documentation for your project'), + body: s_("WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on.") + }, + issuable: { + title: s_('WikiEmpty|This project has no wiki pages'), + body: s_('WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}.') + }, + readonly: { + title: s_('WikiEmpty|This project has no wiki pages'), + body: s_('WikiEmpty|You must be a project member in order to add wiki pages.') + } + } + when Group + { + writable: { + title: s_('WikiEmpty|The wiki lets you write documentation for your group'), + body: s_("WikiEmpty|A wiki is where you can store all the details about your group. This can include why you've created it, its principles, how to use it, and so on.") + }, + issuable: { + title: s_('WikiEmpty|This group has no wiki pages'), + body: s_('WikiEmptyIssueMessage|You must be a group member in order to add wiki pages. If you have suggestions for how to improve the wiki for this group, consider opening an issue in the %{issues_link}.') + }, + readonly: { + title: s_('WikiEmpty|This group has no wiki pages'), + body: s_('WikiEmpty|You must be a group member in order to add wiki pages.') + } + } + else + raise NotImplementedError, "Unknown wiki container type #{wiki.container.class.name}" + end + end end -- cgit v1.2.3