diff options
Diffstat (limited to 'app/helpers')
29 files changed, 374 insertions, 230 deletions
diff --git a/app/helpers/active_sessions_helper.rb b/app/helpers/active_sessions_helper.rb new file mode 100644 index 00000000000..97b6dac67c5 --- /dev/null +++ b/app/helpers/active_sessions_helper.rb @@ -0,0 +1,23 @@ +module ActiveSessionsHelper + # Maps a device type as defined in `ActiveSession` to an svg icon name and + # outputs the icon html. + # + # see `DeviceDetector::Device::DEVICE_NAMES` about the available device types + def active_session_device_type_icon(active_session) + icon_name = + case active_session.device_type + when 'smartphone', 'feature phone', 'phablet' + 'mobile' + when 'tablet' + 'tablet' + when 'tv', 'smart display', 'camera', 'portable media player', 'console' + 'media' + when 'car browser' + 'car' + else + 'monitor-o' + end + + sprite_icon(icon_name, size: 16, css_class: 'prepend-top-2') + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 86ec500ceb3..6aa307b4db4 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -32,80 +32,6 @@ module ApplicationHelper args.any? { |v| v.to_s.downcase == action_name } end - def project_icon(project_id, options = {}) - project = - if project_id.respond_to?(:avatar_url) - project_id - else - Project.find_by_full_path(project_id) - end - - if project.avatar_url - image_tag project.avatar_url, options - else # generated icon - project_identicon(project, options) - end - end - - def project_identicon(project, options = {}) - allowed_colors = { - red: 'FFEBEE', - purple: 'F3E5F5', - indigo: 'E8EAF6', - blue: 'E3F2FD', - teal: 'E0F2F1', - orange: 'FBE9E7', - gray: 'EEEEEE' - } - - options[:class] ||= '' - options[:class] << ' identicon' - bg_key = project.id % 7 - style = "background-color: ##{allowed_colors.values[bg_key]}; color: #555" - - content_tag(:div, class: options[:class], style: style) do - project.name[0, 1].upcase - end - end - - # Takes both user and email and returns the avatar_icon by - # user (preferred) or email. - def avatar_icon_for(user = nil, email = nil, size = nil, scale = 2, only_path: true) - if user - avatar_icon_for_user(user, size, scale, only_path: only_path) - elsif email - avatar_icon_for_email(email, size, scale, only_path: only_path) - else - default_avatar - end - end - - def avatar_icon_for_email(email = nil, size = nil, scale = 2, only_path: true) - user = User.find_by_any_email(email.try(:downcase)) - if user - avatar_icon_for_user(user, size, scale, only_path: only_path) - else - gravatar_icon(email, size, scale) - end - end - - def avatar_icon_for_user(user = nil, size = nil, scale = 2, only_path: true) - if user - user.avatar_url(size: size, only_path: only_path) || default_avatar - else - gravatar_icon(nil, size, scale) - end - end - - def gravatar_icon(user_email = '', size = nil, scale = 2) - GravatarService.new.execute(user_email, size, scale) || - default_avatar - end - - def default_avatar - asset_path('no_avatar.png') - end - def last_commit(project) if project.repo_exists? time_ago_with_tooltip(project.repository.commit.committed_date) @@ -228,9 +154,7 @@ module ApplicationHelper scope: params[:scope], milestone_title: params[:milestone_title], assignee_id: params[:assignee_id], - assignee_username: params[:assignee_username], author_id: params[:author_id], - author_username: params[:author_username], search: params[:search], label_name: params[:label_name] } diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index b3b080e6dcf..3fbb32c5229 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -74,10 +74,12 @@ module ApplicationSettingsHelper css_class = 'btn' css_class << ' active' unless disabled checkbox_name = 'application_setting[enabled_oauth_sign_in_sources][]' + name = Gitlab::Auth::OAuth::Provider.label_for(source) label_tag(checkbox_name, class: css_class) do check_box_tag(checkbox_name, source, !disabled, - autocomplete: 'off') + Gitlab::Auth::OAuth::Provider.label_for(source) + autocomplete: 'off', + id: name.tr(' ', '_')) + name end end end diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index c109954f3a3..d2daee22aba 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -1,6 +1,6 @@ module AuthHelper PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq).freeze - FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze + LDAP_PROVIDER = /\Aldap/ def ldap_enabled? Gitlab::Auth::LDAP::Config.enabled? @@ -23,7 +23,7 @@ module AuthHelper end def form_based_provider?(name) - FORM_BASED_PROVIDERS.any? { |pattern| pattern === name.to_s } + [LDAP_PROVIDER, 'crowd'].any? { |pattern| pattern === name.to_s } end def form_based_providers @@ -38,6 +38,10 @@ module AuthHelper auth_providers.reject { |provider| form_based_provider?(provider) } end + def providers_for_base_controller + auth_providers.reject { |provider| LDAP_PROVIDER === provider } + end + def enabled_button_based_providers disabled_providers = Gitlab::CurrentSettings.disabled_oauth_sign_in_sources || [] diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index 21b6c0a8ad5..d339c01d492 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -1,4 +1,78 @@ module AvatarsHelper + def project_icon(project_id, options = {}) + project = + if project_id.respond_to?(:avatar_url) + project_id + else + Project.find_by_full_path(project_id) + end + + if project.avatar_url + image_tag project.avatar_url, options + else # generated icon + project_identicon(project, options) + end + end + + def project_identicon(project, options = {}) + allowed_colors = { + red: 'FFEBEE', + purple: 'F3E5F5', + indigo: 'E8EAF6', + blue: 'E3F2FD', + teal: 'E0F2F1', + orange: 'FBE9E7', + gray: 'EEEEEE' + } + + options[:class] ||= '' + options[:class] << ' identicon' + bg_key = project.id % 7 + style = "background-color: ##{allowed_colors.values[bg_key]}; color: #555" + + content_tag(:div, class: options[:class], style: style) do + project.name[0, 1].upcase + end + end + + # Takes both user and email and returns the avatar_icon by + # user (preferred) or email. + def avatar_icon_for(user = nil, email = nil, size = nil, scale = 2, only_path: true) + if user + avatar_icon_for_user(user, size, scale, only_path: only_path) + elsif email + avatar_icon_for_email(email, size, scale, only_path: only_path) + else + default_avatar + end + end + + def avatar_icon_for_email(email = nil, size = nil, scale = 2, only_path: true) + user = User.find_by_any_email(email.try(:downcase)) + if user + avatar_icon_for_user(user, size, scale, only_path: only_path) + else + gravatar_icon(email, size, scale) + end + end + + def avatar_icon_for_user(user = nil, size = nil, scale = 2, only_path: true) + if user + user.avatar_url(size: size, only_path: only_path) || default_avatar + else + gravatar_icon(nil, size, scale) + end + end + + def gravatar_icon(user_email = '', size = nil, scale = 2) + GravatarService.new.execute(user_email, size, scale) || + default_avatar + end + + def default_avatar + ActionController::Base.helpers.image_path('no_avatar.png') + end + def author_avatar(commit_or_event, options = {}) user_avatar(options.merge({ user: commit_or_event.author, diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 2b440e4d584..e7a36e20050 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -17,7 +17,7 @@ module BlobHelper end def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) - "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}" + "#{ide_path}/project#{url_for([project, "edit", "blob", id: [ref, path], script_name: "/"])}" end def edit_blob_button(project = @project, ref = @ref, path = @path, options = {}) @@ -59,7 +59,7 @@ module BlobHelper button_tag label, class: "#{common_classes} disabled has-tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' } elsif can_modify_blob?(blob, project, ref) button_tag label, class: "#{common_classes}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' - elsif can?(current_user, :fork_project, project) + elsif can?(current_user, :fork_project, project) && can?(current_user, :create_merge_request_in, project) edit_fork_button_tag(common_classes, project, label, edit_modify_file_fork_params(action), action) end end @@ -259,7 +259,7 @@ module BlobHelper options = [] if error == :collapsed - options << link_to('load it anyway', url_for(params.merge(viewer: viewer.type, expanded: true, format: nil))) + options << link_to('load it anyway', url_for(safe_params.merge(viewer: viewer.type, expanded: true, format: nil))) end # If the error is `:server_side_but_stored_externally`, the simple viewer will show the same error, @@ -280,7 +280,7 @@ module BlobHelper options << link_to("submit an issue", new_project_issue_path(project)) end - merge_project = can?(current_user, :create_merge_request, project) ? project : (current_user && current_user.fork_of(project)) + merge_project = merge_request_source_project_for_project(@project) if merge_project options << link_to("create a merge request", project_new_merge_request_path(project)) end @@ -334,7 +334,7 @@ module BlobHelper # Web IDE (Beta) requires the user to have this feature enabled elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) edit_link_tag(text, edit_path, common_classes) - elsif current_user && can?(current_user, :fork_project, project) + elsif can?(current_user, :fork_project, project) && can?(current_user, :create_merge_request_in, project) edit_fork_button_tag(common_classes, project, text, edit_blob_fork_params(edit_path)) end end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 636316da80a..f0afcac5986 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -94,7 +94,7 @@ module CiStatusHelper def render_project_pipeline_status(pipeline_status, tooltip_placement: 'auto left') project = pipeline_status.project - path = pipelines_project_commit_path(project, pipeline_status.sha) + path = pipelines_project_commit_path(project, pipeline_status.sha, ref: pipeline_status.ref) render_status_with_link( 'commit', @@ -105,7 +105,7 @@ module CiStatusHelper def render_commit_status(commit, ref: nil, tooltip_placement: 'auto left') project = commit.project - path = pipelines_project_commit_path(project, commit) + path = pipelines_project_commit_path(project, commit, ref: ref) render_status_with_link( 'commit', diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 0333c29e2fd..e594a1d0ba3 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -63,7 +63,7 @@ module CommitsHelper # Returns a link formatted as a commit branch link def commit_branch_link(url, text) link_to(url, class: 'label label-gray ref-name branch-link') do - sprite_icon('fork', size: 16, css_class: 'fork-svg') + "#{text}" + sprite_icon('fork', size: 12, css_class: 'fork-svg') + "#{text}" end end @@ -93,25 +93,18 @@ module CommitsHelper return unless current_controller?(:commits) if @path.blank? - return link_to( - _("Browse Files"), - project_tree_path(project, commit), - class: "btn btn-default" - ) + url = project_tree_path(project, commit) + tooltip = _("Browse Files") elsif @repo.blob_at(commit.id, @path) - return link_to( - _("Browse File"), - project_blob_path(project, - tree_join(commit.id, @path)), - class: "btn btn-default" - ) + url = project_blob_path(project, tree_join(commit.id, @path)) + tooltip = _("Browse File") elsif @path.present? - return link_to( - _("Browse Directory"), - project_tree_path(project, - tree_join(commit.id, @path)), - class: "btn btn-default" - ) + url = project_tree_path(project, tree_join(commit.id, @path)) + tooltip = _("Browse Directory") + end + + link_to url, class: "btn btn-default has-tooltip", title: tooltip, data: { container: "body" } do + sprite_icon('folder-open') end end @@ -170,7 +163,7 @@ module CommitsHelper tooltip = "#{action.capitalize} this #{commit.change_type_title(current_user)} in a new merge request" if has_tooltip btn_class = "btn btn-#{btn_class}" unless btn_class.nil? - if can_collaborate_with_project? + if can_collaborate_with_project?(@project) link_to action.capitalize, "#modal-#{action}-commit", 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" elsif can?(current_user, :fork_project, @project) continue_params = { diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb index 8bf96c0905f..2df5b5d1695 100644 --- a/app/helpers/compare_helper.rb +++ b/app/helpers/compare_helper.rb @@ -3,7 +3,7 @@ module CompareHelper from.present? && to.present? && from != to && - can?(current_user, :create_merge_request, project) && + can?(current_user, :create_merge_request_from, project) && project.repository.branch_exists?(from) && project.repository.branch_exists?(to) end diff --git a/app/helpers/deploy_tokens_helper.rb b/app/helpers/deploy_tokens_helper.rb new file mode 100644 index 00000000000..bd921322476 --- /dev/null +++ b/app/helpers/deploy_tokens_helper.rb @@ -0,0 +1,12 @@ +module DeployTokensHelper + def expand_deploy_tokens_section?(deploy_token) + deploy_token.persisted? || + deploy_token.errors.present? || + Rails.env.test? + end + + def container_registry_enabled?(project) + Gitlab.config.registry.enabled && + can?(current_user, :read_container_image, project) + end +end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index b5ca39711bc..1bb82fd8150 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -180,7 +180,7 @@ module DiffHelper private def diff_btn(title, name, selected) - params_copy = params.dup + params_copy = safe_params.dup params_copy[:view] = name # Always use HTML to handle case where JSON diff rendered this button diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 5089da519df..5a2360b4661 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -41,7 +41,7 @@ module DropdownsHelper def dropdown_toggle(toggle_text, data_attr, options = {}) default_label = data_attr[:default_label] - content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.key?(:toggle_class)}", id: (options[:id] if options.key?(:id)), type: "button", data: data_attr) do + content_tag(:button, disabled: options[:disabled], class: "dropdown-menu-toggle #{options[:toggle_class] if options.key?(:toggle_class)}", id: (options[:id] if options.key?(:id)), type: "button", data: data_attr) do output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}") output << icon('chevron-down') output.html_safe diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 7f3c118c7ab..40073f714ee 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -81,6 +81,14 @@ module GitlabRoutingHelper end end + def edit_milestone_path(entity, *args) + if entity.parent.is_a?(Group) + edit_group_milestone_path(entity.parent, entity, *args) + else + edit_project_milestone_path(entity.parent, entity, *args) + end + end + def toggle_subscription_path(entity, *args) if entity.is_a?(Issue) toggle_subscription_project_issue_path(entity.project, entity) diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 16eceb3f48f..95fea2f18d1 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -1,6 +1,6 @@ module GroupsHelper def group_nav_link_paths - %w[groups#projects groups#edit ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index] + %w[groups#projects groups#edit badges#index ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index] end def group_sidebar_links diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb index c5522ff7a69..2f304b040c7 100644 --- a/app/helpers/icons_helper.rb +++ b/app/helpers/icons_helper.rb @@ -43,6 +43,10 @@ module IconsHelper content_tag(:svg, content_tag(:use, "", { "xlink:href" => "#{sprite_icon_path}##{icon_name}" } ), class: css_classes.empty? ? nil : css_classes) end + def external_snippet_icon(name) + content_tag(:span, "", class: "gl-snippet-icon gl-snippet-icon-#{name}") + end + def audit_icon(names, options = {}) case names when "standard" diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 6d6b840f485..f39a62bccc8 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -9,6 +9,32 @@ module IssuablesHelper "right-sidebar-#{sidebar_gutter_collapsed? ? 'collapsed' : 'expanded'}" end + def sidebar_gutter_tooltip_text + sidebar_gutter_collapsed? ? _('Expand sidebar') : _('Collapse sidebar') + end + + def sidebar_assignee_tooltip_label(issuable) + if issuable.assignee + issuable.assignee.name + else + issuable.allows_multiple_assignees? ? _('Assignee(s)') : _('Assignee') + end + end + + def sidebar_due_date_tooltip_label(issuable) + if issuable.due_date + "#{_('Due date')}<br />#{due_date_remaining_days(issuable)}" + else + _('Due date') + end + end + + def due_date_remaining_days(issuable) + remaining_days_in_words = remaining_days_in_words(issuable) + + "#{issuable.due_date.to_s(:medium)} (#{remaining_days_in_words})" + end + def multi_label_name(current_labels, default_label) if current_labels && current_labels.any? title = current_labels.first.try(:title) @@ -153,22 +179,28 @@ module IssuablesHelper def issuable_labels_tooltip(labels, limit: 5) first, last = labels.partition.with_index { |_, i| i < limit } - label_names = first.collect(&:name) - label_names << "and #{last.size} more" unless last.empty? + if labels && labels.any? + label_names = first.collect(&:name) + label_names << "and #{last.size} more" unless last.empty? - label_names.join(', ') + label_names.join(', ') + else + _("Labels") + end end - def issuables_state_counter_text(issuable_type, state) + def issuables_state_counter_text(issuable_type, state, display_count) titles = { opened: "Open" } state_title = titles[state] || state.to_s.humanize - count = issuables_count_for_state(issuable_type, state) - html = content_tag(:span, state_title) - html << " " << content_tag(:span, number_with_delimiter(count), class: 'badge') + + if display_count + count = issuables_count_for_state(issuable_type, state) + html << " " << content_tag(:span, number_with_delimiter(count), class: 'badge') + end html.html_safe end @@ -191,24 +223,10 @@ module IssuablesHelper end end - def issuable_filter_params - [ - :search, - :author_id, - :assignee_id, - :milestone_title, - :label_name - ] - end - def issuable_reference(issuable) @show_full_reference ? issuable.to_reference(full: true) : issuable.to_reference(@group || @project) end - def issuable_filter_present? - issuable_filter_params.any? { |k| params.key?(k) } - end - def issuable_initial_data(issuable) data = { endpoint: issuable_path(issuable), @@ -333,7 +351,7 @@ module IssuablesHelper def issuable_todo_button_data(issuable, todo, is_collapsed) { todo_text: "Add todo", - mark_text: "Mark done", + mark_text: "Mark todo as done", todo_icon: (is_collapsed ? icon('plus-square') : nil), mark_icon: (is_collapsed ? icon('check-square', class: 'todo-undone') : nil), issuable_id: issuable.id, diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 0f25d401406..96dc7ae1185 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -82,8 +82,8 @@ module IssuesHelper names.to_sentence end - def award_state_class(awards, current_user) - if !current_user + def award_state_class(awardable, awards, current_user) + if !can?(current_user, :award_emoji, awardable) "disabled" elsif current_user && awards.find { |a| a.user_id == current_user.id } "active" @@ -126,6 +126,17 @@ module IssuesHelper link_to link_text, path end + def show_new_issue_link?(project) + return false unless project + return false if project.archived? + + # We want to show the link to users that are not signed in, that way they + # get directed to the sign-in/sign-up flow and afterwards to the new issue page. + return true unless current_user + + can?(current_user, :create_issue, project) + end + # Required for Banzai::Filter::IssueReferenceFilter module_function :url_for_issue module_function :url_for_internal_issue diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb index 2fe1927a189..39e7a7fd396 100644 --- a/app/helpers/markup_helper.rb +++ b/app/helpers/markup_helper.rb @@ -256,7 +256,7 @@ module MarkupHelper return '' unless html.present? context.merge!( - current_user: (current_user if defined?(current_user)), + current_user: (current_user if defined?(current_user)), # RelativeLinkFilter commit: @commit, diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index fb4fe1c40b7..c19c5b9cc82 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -138,6 +138,18 @@ module MergeRequestsHelper end end + def merge_request_source_project_for_project(project = @project) + unless can?(current_user, :create_merge_request_in, project) + return nil + end + + if can?(current_user, :create_merge_request_from, project) + project + else + current_user.fork_of(project) + end + end + def merge_params_ee(merge_request) {} end diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index be8cb358de2..e8caab3e50c 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -1,4 +1,6 @@ module MilestonesHelper + include EntityDateHelper + def milestones_filter_path(opts = {}) if @project project_milestones_path(@project, opts) @@ -72,6 +74,19 @@ module MilestonesHelper end end + def milestone_progress_tooltip_text(milestone) + has_issues = milestone.total_issues_count(current_user) > 0 + + if has_issues + [ + _('Progress'), + _("%{percent}%% complete") % { percent: milestone.percent_complete(current_user) } + ].join('<br />') + else + _('Progress') + end + end + def milestone_progress_bar(milestone) options = { class: 'progress-bar progress-bar-success', @@ -95,27 +110,69 @@ module MilestonesHelper end def milestone_tooltip_title(milestone) - if milestone.due_date - [milestone.due_date.to_s(:medium), "(#{milestone_remaining_days(milestone)})"].join(' ') + if milestone + "#{milestone.title}<br />#{milestone_tooltip_due_date(milestone)}" + else + _('Milestone') end end - def milestone_remaining_days(milestone) - if milestone.expired? - content_tag(:strong, 'Past due') - elsif milestone.upcoming? - content_tag(:strong, 'Upcoming') - elsif milestone.due_date - time_ago = time_ago_in_words(milestone.due_date) - content = time_ago.gsub(/\d+/) { |match| "<strong>#{match}</strong>" } - content.slice!("about ") - content << " remaining" - content.html_safe - elsif milestone.start_date && milestone.start_date.past? - days = milestone.elapsed_days - content = content_tag(:strong, days) - content << " #{'day'.pluralize(days)} elapsed" + def milestone_time_for(date, date_type) + title = date_type == :start ? "Start date" : "End date" + + if date + time_ago = time_ago_in_words(date) + time_ago.slice!("about ") + + time_ago << if date.past? + " ago" + else + " remaining" + end + + content = [ + title, + "<br />", + date.to_s(:medium), + "(#{time_ago})" + ].join(" ") + content.html_safe + else + title + end + end + + def milestone_issues_tooltip_text(milestone) + issues = milestone.count_issues_by_state(current_user) + + return _("Issues") if issues.empty? + + content = [] + + content << n_("1 open issue", "%d open issues", issues["opened"]) % issues["opened"] if issues["opened"] + content << n_("1 closed issue", "%d closed issues", issues["closed"]) % issues["closed"] if issues["closed"] + + content.join('<br />').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", "%d open merge requests", merge_requests.opened.count) % merge_requests.opened.count if merge_requests.opened.any? + content << n_("1 closed merge request", "%d closed merge requests", merge_requests.closed.count) % merge_requests.closed.count if merge_requests.closed.any? + content << n_("1 merged merge request", "%d merged merge requests", merge_requests.merged.count) % merge_requests.merged.count if merge_requests.merged.any? + + content.join('<br />').html_safe + end + + def milestone_tooltip_due_date(milestone) + if milestone.due_date + "#{milestone.due_date.to_s(:medium)} (#{remaining_days_in_words(milestone)})" end end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 56c88e6eab0..7754c34d6f0 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -28,7 +28,7 @@ module NavHelper end elsif current_path?('jobs#show') %w[page-gutter build-sidebar right-sidebar-expanded] - elsif current_controller?('wikis') && current_action?('show', 'create', 'edit', 'update', 'history', 'git_access') + elsif current_controller?('wikis') && current_action?('show', 'create', 'edit', 'update', 'history', 'git_access', 'destroy') %w[page-gutter wiki-sidebar right-sidebar-expanded] else [] diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 27ed48fdbc7..7f67574a428 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -6,10 +6,6 @@ module NotesHelper end end - def note_editable?(note) - Ability.can_edit_note?(current_user, note) - end - def note_supports_quick_actions?(note) Notes::QuickActionsService.supported?(note) end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 15f48e43a28..eb81dc2de43 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -157,40 +157,6 @@ module ProjectsHelper current_user&.recent_push(@project) end - def project_feature_access_select(field) - # Don't show option "everyone with access" if project is private - options = project_feature_options - - level = @project.project_feature.public_send(field) # rubocop:disable GitlabSecurity/PublicSend - - if @project.private? - disabled_option = ProjectFeature::ENABLED - highest_available_option = ProjectFeature::PRIVATE if level == disabled_option - end - - options = options_for_select( - options.invert, - selected: highest_available_option || level, - disabled: disabled_option - ) - - content_tag :div, class: "select-wrapper" do - concat( - content_tag( - :select, - options, - name: "project[project_feature_attributes][#{field}]", - id: "project_project_feature_attributes_#{field}", - class: "pull-right form-control select-control #{repo_children_classes(field)} ", - data: { field: field } - ) - ) - concat( - icon('chevron-down') - ) - end.html_safe - end - def link_to_autodeploy_doc link_to _('About auto deploy'), help_page_path('ci/autodeploy/index'), target: '_blank' end @@ -274,16 +240,6 @@ module ProjectsHelper private - def repo_children_classes(field) - needs_repo_check = [:merge_requests_access_level, :builds_access_level] - return unless needs_repo_check.include?(field) - - classes = "project-repo-select js-repo-select" - classes << " disabled" unless @project.feature_available?(:repository, current_user) - - classes - end - def get_project_nav_tabs(project, current_user) nav_tabs = [:home] @@ -444,15 +400,8 @@ module ProjectsHelper exports_path = File.join(Settings.shared['path'], 'tmp/project_exports') filtered_message = message.strip.gsub(exports_path, "[REPO EXPORT PATH]") - filtered_message.gsub(project.repository_storage_path.chomp('/'), "[REPOS PATH]") - end - - def project_feature_options - { - ProjectFeature::DISABLED => s_('ProjectFeature|Disabled'), - ProjectFeature::PRIVATE => s_('ProjectFeature|Only team members'), - ProjectFeature::ENABLED => s_('ProjectFeature|Everyone with access') - } + disk_path = Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path + filtered_message.gsub(disk_path.chomp('/'), "[REPOS PATH]") end def project_child_container_class(view_path) @@ -463,20 +412,6 @@ module ProjectsHelper IssuesFinder.new(current_user, project_id: project.id).execute end - def visibility_select_options(project, selected_level) - level_options = Gitlab::VisibilityLevel.values.each_with_object([]) do |level, level_options| - next if restricted_levels.include?(level) - - level_options << [ - visibility_level_label(level), - { data: { description: visibility_level_description(level, project) } }, - level - ] - end - - options_for_select(level_options, selected_level) - end - def restricted_levels return [] if current_user.admin? @@ -507,7 +442,7 @@ module ProjectsHelper visibilityHelpPath: help_page_path('public_access/public_access'), registryAvailable: Gitlab.config.registry.enabled, registryHelpPath: help_page_path('user/project/container_registry'), - lfsAvailable: Gitlab.config.lfs.enabled && current_user.admin?, + lfsAvailable: Gitlab.config.lfs.enabled, lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') } diff --git a/app/helpers/safe_params_helper.rb b/app/helpers/safe_params_helper.rb new file mode 100644 index 00000000000..b568e8810cc --- /dev/null +++ b/app/helpers/safe_params_helper.rb @@ -0,0 +1,11 @@ +module SafeParamsHelper + # Rails 5.0 requires to permit `params` if they're used in url helpers. + # Use this helper when generating links with `params.merge(...)` + def safe_params + if params.respond_to?(:permit!) + params.except(:host, :port, :protocol).permit! + else + params + end + end +end diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index f435c80c656..f872990122e 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -1,4 +1,29 @@ module ServicesHelper + def service_event_description(event) + case event + when "push", "push_events" + "Event will be triggered by a push to the repository" + when "tag_push", "tag_push_events" + "Event will be triggered when a new tag is pushed to the repository" + when "note", "note_events" + "Event will be triggered when someone adds a comment" + when "confidential_note", "confidential_note_events" + "Event will be triggered when someone adds a comment on a confidential issue" + when "issue", "issue_events" + "Event will be triggered when an issue is created/updated/closed" + when "confidential_issue", "confidential_issues_events" + "Event will be triggered when a confidential issue is created/updated/closed" + when "merge_request", "merge_request_events" + "Event will be triggered when a merge request is created/updated/merged" + when "pipeline", "pipeline_events" + "Event will be triggered when a pipeline status changes" + when "wiki_page", "wiki_page_events" + "Event will be triggered when a wiki page is created/updated" + when "commit", "commit_events" + "Event will be triggered when a commit is created/updated" + end + end + def service_event_field_name(event) event = event.pluralize if %w[merge_request issue confidential_issue].include?(event) "#{event}_events" diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 00e7e4230b9..733832c1bbb 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -101,4 +101,39 @@ module SnippetsHelper # Return snippet with chunk array { snippet_object: snippet, snippet_chunks: snippet_chunks } end + + def snippet_embed + "<script src=\"#{url_for(only_path: false, overwrite_params: nil)}.js\"></script>" + end + + def embedded_snippet_raw_button + blob = @snippet.blob + return if blob.empty? || blob.raw_binary? || blob.stored_externally? + + snippet_raw_url = if @snippet.is_a?(PersonalSnippet) + raw_snippet_url(@snippet) + else + raw_project_snippet_url(@snippet.project, @snippet) + end + + link_to external_snippet_icon('doc_code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw' + end + + def embedded_snippet_download_button + download_url = if @snippet.is_a?(PersonalSnippet) + raw_snippet_url(@snippet, inline: false) + else + raw_project_snippet_url(@snippet.project, @snippet, inline: false) + end + + link_to external_snippet_icon('download'), download_url, class: 'btn', target: '_blank', title: 'Download', rel: 'noopener noreferrer' + end + + def public_snippet? + if @snippet.project_id? + can?(nil, :read_project_snippet, @snippet) + else + can?(nil, :read_personal_snippet, @snippet) + end + end end diff --git a/app/helpers/system_note_helper.rb b/app/helpers/system_note_helper.rb index 00fe67d6ffb..5b4a141dbcf 100644 --- a/app/helpers/system_note_helper.rb +++ b/app/helpers/system_note_helper.rb @@ -1,14 +1,14 @@ module SystemNoteHelper ICON_NAMES_BY_ACTION = { 'commit' => 'commit', - 'description' => 'pencil', + 'description' => 'pencil-square', 'merge' => 'git-merge', 'merged' => 'git-merge', 'opened' => 'issue-open', 'closed' => 'issue-close', 'time_tracking' => 'timer', 'assignee' => 'user', - 'title' => 'pencil', + 'title' => 'pencil-square', 'task' => 'task-done', 'label' => 'label', 'cross_reference' => 'comment-dots', @@ -18,7 +18,7 @@ module SystemNoteHelper 'milestone' => 'clock', 'discussion' => 'comment', 'moved' => 'arrow-right', - 'outdated' => 'pencil', + 'outdated' => 'pencil-square', 'duplicate' => 'issue-duplicate', 'locked' => 'lock', 'unlocked' => 'lock-open' diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 5e7c20ef51e..dc42caa70e5 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -90,7 +90,7 @@ module TreeHelper end def commit_in_single_accessible_branch - branch_name = html_escape(selected_branch) + branch_name = ERB::Util.html_escape(selected_branch) message = _("Your changes can be committed to %{branch_name} because a merge "\ "request is open.") % { branch_name: "<strong>#{branch_name}</strong>" } diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb index 88f374be1e5..9f78b80c71d 100644 --- a/app/helpers/workhorse_helper.rb +++ b/app/helpers/workhorse_helper.rb @@ -24,8 +24,8 @@ module WorkhorseHelper end # Archive a Git repository and send it through Workhorse - def send_git_archive(repository, ref:, format:) - headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format)) + def send_git_archive(repository, **kwargs) + headers.store(*Gitlab::Workhorse.send_git_archive(repository, **kwargs)) head :ok end |