summaryrefslogtreecommitdiff
path: root/app/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'app/helpers')
-rw-r--r--app/helpers/appearances_helper.rb28
-rw-r--r--app/helpers/application_helper.rb22
-rw-r--r--app/helpers/auth_helper.rb4
-rw-r--r--app/helpers/blob_helper.rb45
-rw-r--r--app/helpers/button_helper.rb30
-rw-r--r--app/helpers/ci_status_helper.rb14
-rw-r--r--app/helpers/commits_helper.rb17
-rw-r--r--app/helpers/diff_helper.rb36
-rw-r--r--app/helpers/dropdowns_helper.rb101
-rw-r--r--app/helpers/events_helper.rb12
-rw-r--r--app/helpers/explore_helper.rb12
-rw-r--r--app/helpers/gitlab_markdown_helper.rb2
-rw-r--r--app/helpers/groups_helper.rb4
-rw-r--r--app/helpers/icons_helper.rb11
-rw-r--r--app/helpers/issuables_helper.rb33
-rw-r--r--app/helpers/issues_helper.rb17
-rw-r--r--app/helpers/labels_helper.rb45
-rw-r--r--app/helpers/milestones_helper.rb59
-rw-r--r--app/helpers/nav_helper.rb1
-rw-r--r--app/helpers/preferences_helper.rb4
-rw-r--r--app/helpers/projects_helper.rb18
-rw-r--r--app/helpers/search_helper.rb2
-rw-r--r--app/helpers/snippets_helper.rb10
-rw-r--r--app/helpers/sorting_helper.rb10
-rw-r--r--app/helpers/todos_helper.rb11
-rw-r--r--app/helpers/visibility_level_helper.rb37
26 files changed, 444 insertions, 141 deletions
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index c5820bf4c50..e0abc3a2869 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -1,21 +1,33 @@
module AppearancesHelper
- def brand_item
- nil
- end
-
def brand_title
- 'GitLab Community Edition'
+ if brand_item && brand_item.title
+ brand_item.title
+ else
+ 'GitLab Community Edition'
+ end
end
def brand_image
- nil
+ if brand_item.logo?
+ image_tag brand_item.logo
+ else
+ nil
+ end
end
def brand_text
- nil
+ markdown(brand_item.description)
+ end
+
+ def brand_item
+ @appearance ||= Appearance.first
end
def brand_header_logo
- render 'shared/logo.svg'
+ if brand_item && brand_item.header_logo?
+ image_tag brand_item.header_logo
+ else
+ render 'shared/logo.svg'
+ end
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index f0aa2b57121..e6ceb213532 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -72,7 +72,7 @@ module ApplicationHelper
if user_or_email.is_a?(User)
user = user_or_email
else
- user = User.find_by(email: user_or_email.downcase)
+ user = User.find_by_any_email(user_or_email.try(:downcase))
end
if user
@@ -182,7 +182,7 @@ module ApplicationHelper
# Returns an HTML-safe String
def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false)
element = content_tag :time, time.to_s,
- class: "#{html_class} js-timeago js-timeago-pending",
+ class: "#{html_class} js-timeago #{"js-timeago-pending" unless skip_js}",
datetime: time.to_time.getutc.iso8601,
title: time.in_time_zone.to_s(:medium),
data: { toggle: 'tooltip', placement: placement, container: 'body' }
@@ -196,6 +196,22 @@ module ApplicationHelper
element
end
+ def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago', include_author: false)
+ return if object.updated_at == object.created_at
+
+ content_tag :small, class: "edited-text" do
+ output = content_tag(:span, "Edited ")
+ output << time_ago_with_tooltip(object.updated_at, placement: placement, html_class: html_class)
+
+ if include_author && object.updated_by && object.updated_by != object.author
+ output << content_tag(:span, " by ")
+ output << link_to_member(object.project, object.updated_by, avatar: false, author_class: nil)
+ end
+
+ output
+ end
+ end
+
def render_markup(file_name, file_content)
if gitlab_markdown?(file_name)
Haml::Helpers.preserve(markdown(file_content))
@@ -285,7 +301,7 @@ module ApplicationHelper
if project.nil?
nil
elsif current_controller?(:issues)
- project.issues.send(entity).count
+ project.issues.visible_to_user(current_user).send(entity).count
elsif current_controller?(:merge_requests)
project.merge_requests.send(entity).count
end
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index de669e529a7..b4f80fd9b3e 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -6,6 +6,10 @@ module AuthHelper
Gitlab.config.ldap.enabled
end
+ def omniauth_enabled?
+ Gitlab.config.omniauth.enabled
+ end
+
def provider_has_icon?(name)
PROVIDERS_WITH_ICONS.include?(name.to_s)
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 7143a744869..820d69c230b 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -27,7 +27,7 @@ module BlobHelper
link_opts)
if !on_top_of_branch?(project, ref)
- button_tag "Edit", class: "btn btn-default disabled has_tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
+ button_tag "Edit", class: "btn btn-default disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
elsif can_edit_blob?(blob, project, ref)
link_to "Edit", edit_path, class: 'btn'
elsif can?(current_user, :fork_project, project)
@@ -50,9 +50,9 @@ module BlobHelper
return unless blob
if !on_top_of_branch?(project, ref)
- button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
+ button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
elsif blob.lfs_pointer?
- button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' }
+ button_tag label, class: "btn btn-#{btn_class} 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_edit_blob?(blob, project, ref)
button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal'
elsif can?(current_user, :fork_project, project)
@@ -134,4 +134,43 @@ module BlobHelper
blob.data = Loofah.scrub_fragment(blob.data, :strip).to_xml
blob
end
+
+ # If we blindly set the 'real' content type when serving a Git blob we
+ # are enabling XSS attacks. An attacker could upload e.g. a Javascript
+ # file to a Git repository, trick the browser of a victim into
+ # downloading the blob, and then the 'application/javascript' content
+ # type would tell the browser to execute the attacker's Javascript. By
+ # overriding the content type and setting it to 'text/plain' (in the
+ # example of Javascript) we tell the browser of the victim not to
+ # execute untrusted data.
+ def safe_content_type(blob)
+ if blob.text?
+ 'text/plain; charset=utf-8'
+ elsif blob.image?
+ blob.content_type
+ else
+ 'application/octet-stream'
+ end
+ end
+
+ def cached_blob?
+ stale = stale?(etag: @blob.id) # The #stale? method sets cache headers.
+
+ # Because we are opionated we set the cache headers ourselves.
+ response.cache_control[:public] = @project.public?
+
+ if @ref && @commit && @ref == @commit.id
+ # This is a link to a commit by its commit SHA. That means that the blob
+ # is immutable. The only reason to invalidate the cache is if the commit
+ # was deleted or if the user lost access to the repository.
+ response.cache_control[:max_age] = Blob::CACHE_TIME_IMMUTABLE
+ else
+ # A branch or tag points at this blob. That means that the expected blob
+ # value may change over time.
+ response.cache_control[:max_age] = Blob::CACHE_TIME
+ end
+
+ response.etag = @blob.id
+ !stale
+ end
end
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index d6c05843743..a9047ede8c5 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -23,36 +23,34 @@ module ButtonHelper
end
def http_clone_button(project)
- klass = 'btn js-protocol-switch'
- klass << ' active' if default_clone_protocol == 'http'
- klass << ' has_tooltip' if current_user.try(:require_password?)
+ klass = 'http-selector'
+ klass << ' has-tooltip' if current_user.try(:require_password?)
protocol = gitlab_config.protocol.upcase
- content_tag :button, protocol,
+ content_tag :a, protocol,
class: klass,
+ href: @project.http_url_to_repo,
data: {
- clone: project.http_url_to_repo,
+ html: true,
+ placement: 'right',
container: 'body',
- html: 'true',
title: "Set a password on your account<br>to pull or push via #{protocol}"
- },
- type: :button
+ }
end
def ssh_clone_button(project)
- klass = 'btn js-protocol-switch'
- klass << ' active' if default_clone_protocol == 'ssh'
- klass << ' has_tooltip' if current_user.try(:require_ssh_key?)
+ klass = 'ssh-selector'
+ klass << ' has-tooltip' if current_user.try(:require_ssh_key?)
- content_tag :button, 'SSH',
+ content_tag :a, 'SSH',
class: klass,
+ href: project.ssh_url_to_repo,
data: {
- clone: project.ssh_url_to_repo,
+ html: true,
+ placement: 'right',
container: 'body',
- html: 'true',
title: 'Add an SSH key to your profile<br>to pull or push via SSH.'
- },
- type: :button
+ }
end
end
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index d8bee21c82e..8b1575d5e0c 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -12,9 +12,13 @@ module CiStatusHelper
ci_label_for_status(ci_commit.status)
end
- def ci_status_with_icon(status)
- content_tag :span, class: "ci-status ci-#{status}" do
- ci_icon_for_status(status) + '&nbsp;'.html_safe + ci_label_for_status(status)
+ def ci_status_with_icon(status, target = nil)
+ content = ci_icon_for_status(status) + '&nbsp;'.html_safe + ci_label_for_status(status)
+ klass = "ci-status ci-#{status}"
+ if target
+ link_to content, target, class: klass
+ else
+ content_tag :span, content, class: klass
end
end
@@ -42,12 +46,12 @@ module CiStatusHelper
icon(icon_name + ' fw')
end
- def render_ci_status(ci_commit)
+ def render_ci_status(ci_commit, tooltip_placement: 'auto left')
link_to ci_status_icon(ci_commit),
ci_status_path(ci_commit),
class: "ci-status-link ci-status-icon-#{ci_commit.status.dasherize}",
title: "Build #{ci_status_label(ci_commit)}",
- data: { toggle: 'tooltip', placement: 'left' }
+ data: { toggle: 'tooltip', placement: tooltip_placement }
end
def no_runners_for_project?(project)
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 7ff539118d3..bde0799f3de 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -130,7 +130,7 @@ module CommitsHelper
if can_collaborate_with_project?
content_tag :span, 'data-toggle' => 'modal', 'data-target' => '#modal-revert-commit' do
- link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'tooltip', title: tooltip, class: "btn btn-default btn-grouped btn-#{btn_class}"
+ link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'tooltip', 'data-container' => 'body', title: tooltip, class: "btn btn-default btn-grouped btn-#{btn_class}"
end
elsif can?(current_user, :fork_project, @project)
continue_params = {
@@ -142,7 +142,7 @@ module CommitsHelper
namespace_key: current_user.namespace.id,
continue: continue_params)
- link_to 'Revert', fork_path, class: 'btn btn-grouped btn-close', method: :post, 'data-toggle' => 'tooltip', title: tooltip
+ link_to 'Revert', fork_path, class: 'btn btn-grouped btn-close', method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: tooltip
end
end
@@ -182,7 +182,7 @@ module CommitsHelper
end
options = {
- class: "commit-#{options[:source]}-link has_tooltip",
+ class: "commit-#{options[:source]}-link has-tooltip",
data: { 'original-title'.to_sym => sanitize(source_email) }
}
@@ -211,4 +211,15 @@ module CommitsHelper
def clean(string)
Sanitize.clean(string, remove_contents: true)
end
+
+ def limited_commits(commits)
+ if commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
+ [
+ commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE),
+ commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE
+ ]
+ else
+ [commits, 0]
+ end
+ end
end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 6a3ab3ea40a..ff32e834499 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -12,40 +12,20 @@ module DiffHelper
params[:view] == 'parallel' ? 'parallel' : 'inline'
end
- def allowed_diff_size
- if diff_hard_limit_enabled?
- Commit::DIFF_HARD_LIMIT_FILES
- else
- Commit::DIFF_SAFE_FILES
- end
+ def diff_hard_limit_enabled?
+ params[:force_show_diff].present?
end
- def allowed_diff_lines
+ def diff_options
+ options = { ignore_whitespace_change: params[:w] == '1' }
if diff_hard_limit_enabled?
- Commit::DIFF_HARD_LIMIT_LINES
- else
- Commit::DIFF_SAFE_LINES
+ options.merge!(Commit.max_diff_options)
end
+ options
end
def safe_diff_files(diffs, diff_refs)
- lines = 0
- safe_files = []
- diffs.first(allowed_diff_size).each do |diff|
- lines += diff.diff.lines.count
- break if lines > allowed_diff_lines
- safe_files << Gitlab::Diff::File.new(diff, diff_refs)
- end
- safe_files
- end
-
- def diff_hard_limit_enabled?
- # Enabling hard limit allows user to see more diff information
- if params[:force_show_diff].present?
- true
- else
- false
- end
+ diffs.decorate! { |diff| Gitlab::Diff::File.new(diff, diff_refs) }
end
def generate_line_code(file_path, line)
@@ -137,7 +117,7 @@ module DiffHelper
# Always use HTML to handle case where JSON diff rendered this button
params_copy.delete(:format)
- link_to url_for(params_copy), id: "#{name}-diff-btn", class: (selected ? 'btn active' : 'btn') do
+ link_to url_for(params_copy), id: "#{name}-diff-btn", class: (selected ? 'btn active' : 'btn'), data: { view_type: name } do
title
end
end
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
new file mode 100644
index 00000000000..14697f774cc
--- /dev/null
+++ b/app/helpers/dropdowns_helper.rb
@@ -0,0 +1,101 @@
+module DropdownsHelper
+ def dropdown_tag(toggle_text, options: {}, &block)
+ content_tag :div, class: "dropdown" do
+ data_attr = { toggle: "dropdown" }
+
+ if options.has_key?(:data)
+ data_attr = options[:data].merge(data_attr)
+ end
+
+ dropdown_output = dropdown_toggle(toggle_text, data_attr, options)
+
+ dropdown_output << content_tag(:div, class: "dropdown-menu dropdown-select #{options[:dropdown_class] if options.has_key?(:dropdown_class)}") do
+ output = ""
+
+ if options.has_key?(:title)
+ output << dropdown_title(options[:title])
+ end
+
+ if options.has_key?(:filter)
+ output << dropdown_filter(options[:placeholder])
+ end
+
+ output << content_tag(:div, class: "dropdown-content") do
+ capture(&block) if block && !options.has_key?(:footer_content)
+ end
+
+ if block && options[:footer_content]
+ output << content_tag(:div, class: "dropdown-footer") do
+ capture(&block)
+ end
+ end
+
+ output << dropdown_loading
+
+ output.html_safe
+ end
+
+ dropdown_output.html_safe
+ end
+ end
+
+ def dropdown_toggle(toggle_text, data_attr, options)
+ content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.has_key?(:toggle_class)}", id: (options[:id] if options.has_key?(:id)), type: "button", data: data_attr) do
+ output = content_tag(:span, toggle_text, class: "dropdown-toggle-text")
+ output << icon('chevron-down')
+ output.html_safe
+ end
+ end
+
+ def dropdown_title(title, back: false)
+ content_tag :div, class: "dropdown-title" do
+ title_output = ""
+
+ if back
+ title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-back", aria: { label: "Go back" }, type: "button") do
+ icon('arrow-left')
+ end
+ end
+
+ title_output << content_tag(:span, title)
+
+ title_output << content_tag(:button, class: "dropdown-title-button dropdown-menu-close", aria: { label: "Close" }, type: "button") do
+ icon('times', class: 'dropdown-menu-close-icon')
+ end
+
+ title_output.html_safe
+ end
+ end
+
+ def dropdown_filter(placeholder)
+ content_tag :div, class: "dropdown-input" do
+ filter_output = search_field_tag nil, nil, class: "dropdown-input-field", placeholder: placeholder
+ filter_output << icon('search', class: "dropdown-input-search")
+ filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button")
+
+ filter_output.html_safe
+ end
+ end
+
+ def dropdown_content(&block)
+ content_tag(:div, class: "dropdown-content") do
+ if block
+ capture(&block)
+ end
+ end
+ end
+
+ def dropdown_footer(&block)
+ content_tag(:div, class: "dropdown-footer") do
+ if block
+ capture(&block)
+ end
+ end
+ end
+
+ def dropdown_loading
+ content_tag :div, class: "dropdown-loading" do
+ icon('spinner spin')
+ end
+ end
+end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index 31bf45baeb7..d3e5e3aa8b9 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -3,7 +3,7 @@ module EventsHelper
author = event.author
if author
- link_to author.name, user_path(author.username)
+ link_to author.name, user_path(author.username), title: h(author.name)
else
event.author_name
end
@@ -159,7 +159,7 @@ module EventsHelper
link_to(
namespace_project_commit_path(event.project.namespace, event.project,
event.note_commit_id,
- anchor: dom_id(event.target)),
+ anchor: dom_id(event.target), title: h(event.target_title)),
class: "commit_short_id"
) do
"#{event.note_target_type} #{event.note_short_commit_id}"
@@ -167,12 +167,12 @@ module EventsHelper
elsif event.note_project_snippet?
link_to(namespace_project_snippet_path(event.project.namespace,
event.project,
- event.note_target)) do
- "#{event.note_target_type} ##{truncate event.note_target_id}"
+ event.note_target), title: h(event.project.name)) do
+ "#{event.note_target_type} #{truncate event.note_target.to_reference}"
end
else
link_to event_note_target_path(event) do
- "#{event.note_target_type} ##{truncate event.note_target_iid}"
+ "#{event.note_target_type} #{truncate event.note_target.to_reference}"
end
end
else
@@ -194,7 +194,7 @@ module EventsHelper
end
def event_to_atom(xml, event)
- if event.proper?
+ if event.visible_to_user?(current_user)
xml.entry do
event_link = event_feed_url(event)
event_title = event_feed_title(event)
diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb
index 3648757428b..337b0aacbb5 100644
--- a/app/helpers/explore_helper.rb
+++ b/app/helpers/explore_helper.rb
@@ -1,5 +1,5 @@
module ExploreHelper
- def explore_projects_filter_path(options={})
+ def filter_projects_path(options={})
exist_opts = {
sort: params[:sort],
scope: params[:scope],
@@ -9,15 +9,7 @@ module ExploreHelper
}
options = exist_opts.merge(options)
-
- path = if explore_controller?
- explore_projects_path
- elsif current_action?(:starred)
- starred_dashboard_projects_path
- else
- dashboard_projects_path
- end
-
+ path = request.path
path << "?#{options.to_param}"
path
end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 89d2a648494..2f760af02fd 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -50,6 +50,8 @@ module GitlabMarkdownHelper
context[:project] ||= @project
+ text = Banzai.pre_process(text, context)
+
html = Banzai.render(text, context)
context.merge!(
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 1d36969cd62..b1f0a765bb9 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -19,6 +19,10 @@ module GroupsHelper
end
end
+ def can_change_group_visibility_level?(group)
+ can?(current_user, :change_visibility_level, group)
+ end
+
def group_icon(group)
if group.is_a?(String)
group = Group.find_by(path: group)
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 84c6d0883b0..ab3ef454e1c 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -10,6 +10,15 @@ module IconsHelper
options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
end
+ def audit_icon(names, options = {})
+ case names
+ when "standard"
+ names = "key"
+ end
+
+ options.include?(:base) ? fa_stacked_icon(names, options) : fa_icon(names, options)
+ end
+
def spinner(text = nil, visible = false)
css_class = 'loading'
css_class << ' hide' unless visible
@@ -37,7 +46,7 @@ module IconsHelper
else # Gitlab::VisibilityLevel::PUBLIC
'globe'
end
-
+
name << " fw" if fw
icon(name)
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 91a3aa371ef..62050691a39 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -16,10 +16,37 @@ module IssuablesHelper
base_issuable_scope(issuable).where('iid > ?', issuable.iid).last
end
+ def issuable_json_path(issuable)
+ project = issuable.project
+
+ if issuable.kind_of?(MergeRequest)
+ namespace_project_merge_request_path(project.namespace, project, issuable.iid, :json)
+ else
+ namespace_project_issue_path(project.namespace, project, issuable.iid, :json)
+ end
+ end
+
def prev_issuable_for(issuable)
base_issuable_scope(issuable).where('iid < ?', issuable.iid).first
end
+ def user_dropdown_label(user_id, default_label)
+ return "Unassigned" if user_id == "0"
+
+ if @project
+ member = @project.team.find_member(user_id)
+ user = member.user if member
+ else
+ user = User.find_by(id: user_id)
+ end
+
+ if user
+ user.name
+ else
+ default_label
+ end
+ end
+
private
def sidebar_gutter_collapsed?
@@ -31,7 +58,11 @@ module IssuablesHelper
end
def issuable_state_scope(issuable)
- issuable.open? ? :opened : :closed
+ if issuable.respond_to?(:merged?) && issuable.merged?
+ :merged
+ else
+ issuable.open? ? :opened : :closed
+ end
end
end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index ae4ebc0854a..24b90fef4fe 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -57,6 +57,19 @@ module IssuesHelper
options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
end
+ def project_options(issuable, current_user, ability: :read_project)
+ projects = current_user.authorized_projects
+ projects = projects.select do |project|
+ current_user.can?(ability, project)
+ end
+
+ no_project = OpenStruct.new(id: 0, name_with_namespace: 'No project')
+ projects.unshift(no_project)
+ projects.delete(issuable.project)
+
+ options_from_collection_for_select(projects, :id, :name_with_namespace)
+ end
+
def status_box_class(item)
if item.respond_to?(:expired?) && item.expired?
'status-box-expired'
@@ -98,6 +111,10 @@ module IssuesHelper
end.sort.to_sentence(last_word_connector: ', or ')
end
+ def confidential_icon(issue)
+ icon('eye-slash') if issue.confidential?
+ end
+
def emoji_icon(name, unicode = nil, aliases = [])
unicode ||= Emoji.emoji_filename(name) rescue ""
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 1c7fcc13b42..3dded7c2f23 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -32,7 +32,7 @@ module LabelsHelper
# link_to_label(label) { "My Custom Label Text" }
#
# Returns a String
- def link_to_label(label, project: nil, type: :issue, &block)
+ def link_to_label(label, project: nil, type: :issue, tooltip: true, &block)
project ||= @project || label.project
link = send("namespace_project_#{type.to_s.pluralize}_path",
project.namespace,
@@ -42,7 +42,7 @@ module LabelsHelper
if block_given?
link_to link, &block
else
- link_to render_colored_label(label), link
+ link_to render_colored_label(label, tooltip: tooltip), link
end
end
@@ -50,19 +50,26 @@ module LabelsHelper
@project.labels.pluck(:title)
end
- def render_colored_label(label)
+ def render_colored_label(label, label_suffix = '', tooltip: true)
label_color = label.color || Label::DEFAULT_COLOR
text_color = text_color_for_bg(label_color)
# Intentionally not using content_tag here so that this method can be called
# by LabelReferenceFilter
- span = %(<span class="label color-label") +
- %( style="background-color: #{label_color}; color: #{text_color}">) +
- escape_once(label.name) + '</span>'
+ span = %(<span class="label color-label #{"has-tooltip" if tooltip}" ) +
+ %(style="background-color: #{label_color}; color: #{text_color}" ) +
+ %(title="#{escape_once(label.description)}" data-container="body">) +
+ %(#{escape_once(label.name)}#{label_suffix}</span>)
span.html_safe
end
+ def render_colored_cross_project_label(label, tooltip: true)
+ label_suffix = label.project.name_with_namespace
+ label_suffix = " <i>in #{escape_once(label_suffix)}</i>"
+ render_colored_label(label, label_suffix, tooltip: tooltip)
+ end
+
def suggested_colors
[
'#0033CC',
@@ -103,21 +110,23 @@ module LabelsHelper
end
end
- def projects_labels_options
- labels =
- if @project
- @project.labels
- else
- Label.where(project_id: @projects)
- end
+ def labels_filter_path
+ if @project
+ namespace_project_labels_path(@project.namespace, @project, :json)
+ else
+ dashboard_labels_path(:json)
+ end
+ end
- grouped_labels = GlobalLabel.build_collection(labels)
- grouped_labels.unshift(Label::None)
- grouped_labels.unshift(Label::Any)
+ def label_subscription_status(label)
+ label.subscribed?(current_user) ? 'subscribed' : 'unsubscribed'
+ end
- options_from_collection_for_select(grouped_labels, 'name', 'title', params[:label_name])
+ def label_subscription_toggle_button_text(label)
+ label.subscribed?(current_user) ? 'Unsubscribe' : 'Subscribe'
end
# Required for Banzai::Filter::LabelReferenceFilter
- module_function :render_colored_label, :text_color_for_bg, :escape_once
+ module_function :render_colored_label, :render_colored_cross_project_label,
+ :text_color_for_bg, :escape_once
end
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index a42cbcff182..87fc2db6901 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -9,10 +9,36 @@ module MilestonesHelper
end
end
+ def milestones_label_path(opts = {})
+ if @project
+ namespace_project_issues_path(@project.namespace, @project, opts)
+ elsif @group
+ issues_group_path(@group, opts)
+ else
+ issues_dashboard_path(opts)
+ end
+ end
+
+ def milestones_browse_issuables_path(milestone, type:)
+ opts = { milestone_title: milestone.title }
+
+ 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:)
+ milestone.issues.with_label(label.title).send(state).size
+ end
+
def milestone_progress_bar(milestone)
options = {
class: 'progress-bar progress-bar-success',
- style: "width: #{milestone.percent_complete}%;"
+ style: "width: #{milestone.percent_complete(current_user)}%;"
}
content_tag :div, class: 'progress' do
@@ -20,20 +46,21 @@ module MilestonesHelper
end
end
- def projects_milestones_options
- milestones =
- if @project
- @project.milestones
- else
- Milestone.where(project_id: @projects)
- end.active
-
- epoch = DateTime.parse('1970-01-01')
- grouped_milestones = GlobalMilestone.build_collection(milestones)
- grouped_milestones = grouped_milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date }
- grouped_milestones.unshift(Milestone::None)
- grouped_milestones.unshift(Milestone::Any)
-
- options_from_collection_for_select(grouped_milestones, 'name', 'title', params[:milestone_title])
+ def milestones_filter_dropdown_path
+ if @project
+ namespace_project_milestones_path(@project.namespace, @project, :json)
+ else
+ dashboard_milestones_path(:json)
+ end
+ end
+
+ def milestone_remaining_days(milestone)
+ if milestone.expired?
+ content_tag(:strong, 'expired')
+ elsif milestone.due_date
+ days = milestone.remaining_days
+ content = content_tag(:strong, days)
+ content << " #{'day'.pluralize(days)} remaining"
+ end
end
end
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index 29cb753e62c..5d86bd490a8 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -23,6 +23,7 @@ module NavHelper
if current_path?('merge_requests#show') ||
current_path?('merge_requests#diffs') ||
current_path?('merge_requests#commits') ||
+ current_path?('merge_requests#builds') ||
current_path?('issues#show')
if cookies[:collapsed_gutter] == 'true'
"page-gutter right-sidebar-collapsed"
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index c73cb3028ee..c3832cf5d65 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -12,7 +12,9 @@ module PreferencesHelper
projects: 'Your Projects (default)',
stars: 'Starred Projects',
project_activity: "Your Projects' Activity",
- starred_project_activity: "Starred Projects' Activity"
+ starred_project_activity: "Starred Projects' Activity",
+ groups: "Your Groups",
+ todos: "Your Todos"
}.with_indifferent_access.freeze
# Returns an Array usable by a select field for more user-friendly option text
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index d6fb629b0c2..4e4c6e301d5 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -8,7 +8,7 @@ module ProjectsHelper
end
def link_to_project(project)
- link_to [project.namespace.becomes(Namespace), project] do
+ link_to [project.namespace.becomes(Namespace), project], title: h(project.name) do
title = content_tag(:span, project.name, class: 'project-name')
if project.namespace
@@ -26,7 +26,7 @@ module ProjectsHelper
image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
end
- def link_to_member(project, author, opts = {})
+ def link_to_member(project, author, opts = {}, &block)
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
opts = default_opts.merge(opts)
@@ -38,15 +38,21 @@ module ProjectsHelper
author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
# Build name span tag
- author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name]
+ if opts[:by_username]
+ author_html << content_tag(:span, sanitize("@#{author.username}"), class: opts[:author_class]) if opts[:name]
+ else
+ author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name]
+ end
+
+ author_html << capture(&block) if block
author_html = author_html.html_safe
if opts[:name]
- link_to(author_html, user_path(author), class: "author_link").html_safe
+ link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
else
title = opts[:title].sub(":name", sanitize(author.name))
- link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe
+ link_to(author_html, user_path(author), class: "author_link has-tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe
end
end
@@ -203,7 +209,7 @@ module ProjectsHelper
def default_clone_protocol
if !current_user || current_user.require_ssh_key?
- "http"
+ gitlab_config.protocol
else
"ssh"
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 1eb790b1796..494dad0b41e 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -40,7 +40,7 @@ module SearchHelper
{ label: "help: Rake Tasks Help", url: help_page_path("raketasks", "README") },
{ label: "help: SSH Keys Help", url: help_page_path("ssh", "README") },
{ label: "help: System Hooks Help", url: help_page_path("system_hooks", "system_hooks") },
- { label: "help: Web Hooks Help", url: help_page_path("web_hooks", "web_hooks") },
+ { label: "help: Webhooks Help", url: help_page_path("web_hooks", "web_hooks") },
{ label: "help: Workflow Help", url: help_page_path("workflow", "README") },
]
end
diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb
index 41ae4048992..0a5a8eb5aee 100644
--- a/app/helpers/snippets_helper.rb
+++ b/app/helpers/snippets_helper.rb
@@ -1,14 +1,4 @@
module SnippetsHelper
- def lifetime_select_options
- options = [
- ['forever', nil],
- ['1 day', "#{Date.current + 1.day}"],
- ['1 week', "#{Date.current + 1.week}"],
- ['1 month', "#{Date.current + 1.month}"]
- ]
- options_for_select(options)
- end
-
def reliable_snippet_path(snippet)
if snippet.project_id?
namespace_project_snippet_path(snippet.project.namespace,
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index f9026b887da..2f2d2721d6d 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -16,6 +16,16 @@ module SortingHelper
}
end
+ def projects_sort_options_hash
+ {
+ sort_value_name => sort_title_name,
+ sort_value_recently_updated => sort_title_recently_updated,
+ sort_value_oldest_updated => sort_title_oldest_updated,
+ sort_value_recently_created => sort_title_recently_created,
+ sort_value_oldest_created => sort_title_oldest_created,
+ }
+ end
+
def sort_title_oldest_updated
'Oldest updated'
end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index 4b745a5b969..edc5686cf08 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -16,14 +16,19 @@ module TodosHelper
def todo_target_link(todo)
target = todo.target_type.titleize.downcase
- link_to "#{target} #{todo.target.to_reference}", todo_target_path(todo)
+ link_to "#{target} #{todo.target_reference}", todo_target_path(todo), { title: todo.target.title }
end
def todo_target_path(todo)
anchor = dom_id(todo.note) if todo.note.present?
- polymorphic_path([todo.project.namespace.becomes(Namespace),
- todo.project, todo.target], anchor: anchor)
+ if todo.for_commit?
+ namespace_project_commit_path(todo.project.namespace.becomes(Namespace), todo.project,
+ todo.target, anchor: anchor)
+ else
+ polymorphic_path([todo.project.namespace.becomes(Namespace),
+ todo.project, todo.target], anchor: anchor)
+ end
end
def todos_filter_params
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index 71d33b445c2..3a83ae15dd8 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -19,6 +19,8 @@ module VisibilityLevelHelper
case form_model
when Project
project_visibility_level_description(level)
+ when Group
+ group_visibility_level_description(level)
when Snippet
snippet_visibility_level_description(level, form_model)
end
@@ -35,6 +37,17 @@ module VisibilityLevelHelper
end
end
+ def group_visibility_level_description(level)
+ case level
+ when Gitlab::VisibilityLevel::PRIVATE
+ "The group and its projects can only be viewed by members."
+ when Gitlab::VisibilityLevel::INTERNAL
+ "The group and any internal projects can be viewed by any logged in user."
+ when Gitlab::VisibilityLevel::PUBLIC
+ "The group and any public projects can be viewed without any authentication."
+ end
+ end
+
def snippet_visibility_level_description(level, snippet = nil)
case level
when Gitlab::VisibilityLevel::PRIVATE
@@ -50,6 +63,23 @@ module VisibilityLevelHelper
end
end
+ def visibility_icon_description(form_model)
+ case form_model
+ when Project
+ project_visibility_icon_description(form_model.visibility_level)
+ when Group
+ group_visibility_icon_description(form_model.visibility_level)
+ end
+ end
+
+ def group_visibility_icon_description(level)
+ "#{visibility_level_label(level)} - #{group_visibility_level_description(level)}"
+ end
+
+ def project_visibility_icon_description(level)
+ "#{visibility_level_label(level)} - #{project_visibility_level_description(level)}"
+ end
+
def visibility_level_label(level)
Project.visibility_levels.key(level)
end
@@ -67,8 +97,11 @@ module VisibilityLevelHelper
current_application_settings.default_snippet_visibility
end
+ def default_group_visibility
+ current_application_settings.default_group_visibility
+ end
+
def skip_level?(form_model, level)
- form_model.is_a?(Project) &&
- !form_model.visibility_level_allowed?(level)
+ form_model.is_a?(Project) && !form_model.visibility_level_allowed?(level)
end
end