2018-08-18 07:19:57 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-05-01 04:39:11 -04:00
|
|
|
module PageLayoutHelper
|
2020-11-05 07:09:05 -05:00
|
|
|
include Gitlab::Utils::StrongMemoize
|
|
|
|
|
2015-05-01 04:39:11 -04:00
|
|
|
def page_title(*titles)
|
|
|
|
@page_title ||= []
|
|
|
|
|
|
|
|
@page_title.push(*titles.compact) if titles.any?
|
|
|
|
|
2017-09-06 06:58:24 -04:00
|
|
|
if titles.any? && !defined?(@breadcrumb_title)
|
2017-07-14 03:56:06 -04:00
|
|
|
@breadcrumb_title = @page_title.last
|
2017-07-12 04:04:22 -04:00
|
|
|
end
|
|
|
|
|
2018-10-30 06:53:01 -04:00
|
|
|
# Segments are separated by middot
|
2017-08-23 07:23:41 -04:00
|
|
|
@page_title.join(" · ")
|
2015-05-01 04:39:11 -04:00
|
|
|
end
|
|
|
|
|
2015-12-23 16:56:27 -05:00
|
|
|
# Define or get a description for the current page
|
|
|
|
#
|
|
|
|
# description - String (default: nil)
|
|
|
|
#
|
|
|
|
# If this helper is called multiple times with an argument, only the last
|
|
|
|
# description will be returned when called without an argument. Descriptions
|
|
|
|
# have newlines replaced with spaces and all HTML tags are sanitized.
|
|
|
|
#
|
|
|
|
# Examples:
|
|
|
|
#
|
|
|
|
# page_description # => "GitLab Community Edition"
|
|
|
|
# page_description("Foo")
|
|
|
|
# page_description # => "Foo"
|
|
|
|
#
|
|
|
|
# page_description("<b>Bar</b>\nBaz")
|
|
|
|
# page_description # => "Bar Baz"
|
|
|
|
#
|
|
|
|
# Returns an HTML-safe String.
|
|
|
|
def page_description(description = nil)
|
|
|
|
if description.present?
|
2015-12-24 16:26:52 -05:00
|
|
|
@page_description = description.squish
|
2016-01-06 07:02:51 -05:00
|
|
|
elsif @page_description.present?
|
2019-05-22 10:30:10 -04:00
|
|
|
sanitize(@page_description.truncate_words(30), tags: [])
|
2015-12-23 16:56:27 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-09-25 11:09:36 -04:00
|
|
|
def page_canonical_link(link = nil)
|
|
|
|
if link
|
|
|
|
@page_canonical_link = link
|
|
|
|
else
|
2020-11-05 07:09:05 -05:00
|
|
|
@page_canonical_link ||= generic_canonical_url
|
2020-09-25 11:09:36 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-02-15 09:07:29 -05:00
|
|
|
def favicon
|
2017-09-28 07:57:08 -04:00
|
|
|
Gitlab::Favicon.main
|
2017-02-15 09:07:29 -05:00
|
|
|
end
|
|
|
|
|
2015-12-23 16:56:27 -05:00
|
|
|
def page_image
|
2022-04-27 11:10:01 -04:00
|
|
|
default = image_url('twitter_card.jpg')
|
2015-12-23 16:56:27 -05:00
|
|
|
|
2016-01-05 14:50:52 -05:00
|
|
|
subject = @project || @user || @group
|
|
|
|
|
2020-10-27 11:08:39 -04:00
|
|
|
image = subject.avatar_url(only_path: false) if subject.present?
|
2016-01-06 07:02:51 -05:00
|
|
|
image || default
|
2015-12-23 16:56:27 -05:00
|
|
|
end
|
|
|
|
|
2015-12-24 17:03:54 -05:00
|
|
|
# Define or get attributes to be used as Twitter card metadata
|
|
|
|
#
|
|
|
|
# map - Hash of label => data pairs. Keys become labels, values become data
|
|
|
|
#
|
|
|
|
# Raises ArgumentError if given more than two attributes
|
|
|
|
def page_card_attributes(map = {})
|
|
|
|
raise ArgumentError, 'cannot provide more than two attributes' if map.length > 2
|
|
|
|
|
|
|
|
@page_card_attributes ||= {}
|
2016-06-29 09:23:44 -04:00
|
|
|
@page_card_attributes = map.reject { |_, v| v.blank? } if map.present?
|
2015-12-24 17:03:54 -05:00
|
|
|
@page_card_attributes
|
|
|
|
end
|
|
|
|
|
|
|
|
def page_card_meta_tags
|
2018-08-18 07:19:57 -04:00
|
|
|
tags = []
|
2015-12-24 17:03:54 -05:00
|
|
|
|
|
|
|
page_card_attributes.each_with_index do |pair, i|
|
|
|
|
tags << tag(:meta, property: "twitter:label#{i + 1}", content: pair[0])
|
|
|
|
tags << tag(:meta, property: "twitter:data#{i + 1}", content: pair[1])
|
|
|
|
end
|
|
|
|
|
2018-08-18 07:19:57 -04:00
|
|
|
tags.join.html_safe
|
2015-12-24 17:03:54 -05:00
|
|
|
end
|
|
|
|
|
2015-05-01 04:39:11 -04:00
|
|
|
def header_title(title = nil, title_url = nil)
|
|
|
|
if title
|
|
|
|
@header_title = title
|
|
|
|
@header_title_url = title_url
|
|
|
|
else
|
2017-09-06 06:55:23 -04:00
|
|
|
return @header_title unless @header_title_url
|
|
|
|
|
2017-09-06 07:19:03 -04:00
|
|
|
breadcrumb_list_item(link_to(@header_title, @header_title_url))
|
2015-05-01 04:39:11 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def sidebar(name = nil)
|
|
|
|
if name
|
|
|
|
@sidebar = name
|
|
|
|
else
|
|
|
|
@sidebar
|
|
|
|
end
|
|
|
|
end
|
2015-08-26 05:51:28 -04:00
|
|
|
|
2016-04-19 14:05:30 -04:00
|
|
|
def nav(name = nil)
|
|
|
|
if name
|
|
|
|
@nav = name
|
|
|
|
else
|
|
|
|
@nav
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-06-01 20:08:07 -04:00
|
|
|
# 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
|
|
|
|
|
2016-09-19 19:02:52 -04:00
|
|
|
def fluid_layout
|
2022-01-14 10:16:52 -05:00
|
|
|
@force_fluid_layout == true || (current_user && current_user.layout == "fluid")
|
2015-08-26 05:51:28 -04:00
|
|
|
end
|
2015-09-09 17:29:19 -04:00
|
|
|
|
|
|
|
def blank_container(enabled = false)
|
|
|
|
if @blank_container.nil?
|
|
|
|
@blank_container = enabled
|
|
|
|
else
|
|
|
|
@blank_container
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def container_class
|
2018-08-18 07:19:57 -04:00
|
|
|
css_class = ["container-fluid"]
|
2015-09-09 17:29:19 -04:00
|
|
|
|
2015-09-10 03:51:37 -04:00
|
|
|
unless fluid_layout
|
2018-08-18 07:19:57 -04:00
|
|
|
css_class << "container-limited"
|
2015-09-09 17:29:19 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
if blank_container
|
2018-08-18 07:19:57 -04:00
|
|
|
css_class << "container-blank"
|
2015-09-09 17:29:19 -04:00
|
|
|
end
|
|
|
|
|
2018-08-18 07:19:57 -04:00
|
|
|
css_class.join(' ')
|
2015-09-09 17:29:19 -04:00
|
|
|
end
|
2020-11-05 07:09:05 -05:00
|
|
|
|
2020-11-05 13:08:48 -05:00
|
|
|
def page_itemtype(itemtype = nil)
|
|
|
|
if itemtype
|
|
|
|
@page_itemtype = { itemscope: true, itemtype: itemtype }
|
|
|
|
else
|
|
|
|
@page_itemtype || {}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-17 10:09:28 -05:00
|
|
|
def user_status_properties(user)
|
2021-03-18 02:11:52 -04:00
|
|
|
default_properties = {
|
|
|
|
current_emoji: '',
|
|
|
|
current_message: '',
|
|
|
|
default_emoji: UserStatus::DEFAULT_EMOJI
|
|
|
|
}
|
|
|
|
|
2020-11-17 10:09:28 -05:00
|
|
|
return default_properties unless user&.status
|
|
|
|
|
|
|
|
default_properties.merge({
|
|
|
|
current_emoji: user.status.emoji.to_s,
|
|
|
|
current_message: user.status.message.to_s,
|
2021-03-18 02:11:52 -04:00
|
|
|
current_availability: user.status.availability.to_s,
|
|
|
|
current_clear_status_after: user.status.clear_status_at.to_s
|
2020-11-17 10:09:28 -05:00
|
|
|
})
|
|
|
|
end
|
|
|
|
|
2020-11-05 07:09:05 -05:00
|
|
|
private
|
|
|
|
|
|
|
|
def generic_canonical_url
|
|
|
|
strong_memoize(:generic_canonical_url) do
|
|
|
|
next unless request.get? || request.head?
|
|
|
|
next unless generate_generic_canonical_url?
|
|
|
|
|
|
|
|
# Request#url builds the url without the trailing slash
|
|
|
|
request.url
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def generate_generic_canonical_url?
|
|
|
|
# For the main domain it doesn't matter whether there is
|
|
|
|
# a trailing slash or not, they're not considered different
|
|
|
|
# pages
|
|
|
|
return false if request.path == '/'
|
|
|
|
|
|
|
|
# We only need to generate the canonical url when the request has a trailing
|
|
|
|
# slash. In the request object, only the `original_fullpath` and
|
|
|
|
# `original_url` keep the slash if it's present. Both `path` and
|
|
|
|
# `fullpath` would return the path without the slash.
|
|
|
|
# Therefore, we need to process `original_fullpath`
|
|
|
|
request.original_fullpath.sub(request.path, '')[0] == '/'
|
|
|
|
end
|
2015-05-01 04:39:11 -04:00
|
|
|
end
|