summaryrefslogtreecommitdiff
path: root/app/helpers
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2015-08-25 18:42:46 -0700
committerDouwe Maan <douwe@gitlab.com>2015-08-25 18:42:46 -0700
commit046b28312704f3131e72dcd2dbdacc5264d4aa62 (patch)
treea8c2b14a6e1db3b662fee2c79af70d9fcb643c2e /app/helpers
parente449426a4e7d15cdd582d4f136add52cbfb5e04e (diff)
downloadgitlab-ce-046b28312704f3131e72dcd2dbdacc5264d4aa62.tar.gz
Groundwork for merging CI into CE
Diffstat (limited to 'app/helpers')
-rw-r--r--app/helpers/appearances_helper.rb21
-rw-r--r--app/helpers/application_helper.rb315
-rw-r--r--app/helpers/application_settings_helper.rb59
-rw-r--r--app/helpers/auth_helper.rb50
-rw-r--r--app/helpers/blob_helper.rb74
-rw-r--r--app/helpers/branches_helper.rb17
-rw-r--r--app/helpers/broadcast_messages_helper.rb16
-rw-r--r--app/helpers/ci/application_helper.rb140
-rw-r--r--app/helpers/ci/builds_helper.rb41
-rw-r--r--app/helpers/ci/commits_helper.rb26
-rw-r--r--app/helpers/ci/gitlab_helper.rb36
-rw-r--r--app/helpers/ci/icons_helper.rb11
-rw-r--r--app/helpers/ci/projects_helper.rb36
-rw-r--r--app/helpers/ci/routes_helper.rb29
-rw-r--r--app/helpers/ci/runners_helper.rb22
-rw-r--r--app/helpers/ci/triggers_helper.rb7
-rw-r--r--app/helpers/ci/user_helper.rb15
-rw-r--r--app/helpers/ci/user_sessions_helper.rb32
-rw-r--r--app/helpers/commits_helper.rb183
-rw-r--r--app/helpers/compare_helper.rb21
-rw-r--r--app/helpers/dashboard_helper.rb9
-rw-r--r--app/helpers/diff_helper.rb170
-rw-r--r--app/helpers/emails_helper.rb57
-rw-r--r--app/helpers/events_helper.rb203
-rw-r--r--app/helpers/explore_helper.rb17
-rw-r--r--app/helpers/external_wiki_helper.rb11
-rw-r--r--app/helpers/git_helper.rb5
-rw-r--r--app/helpers/gitlab/appearances_helper.rb23
-rw-r--r--app/helpers/gitlab/application_helper.rb317
-rw-r--r--app/helpers/gitlab/application_settings_helper.rb61
-rw-r--r--app/helpers/gitlab/auth_helper.rb52
-rw-r--r--app/helpers/gitlab/blob_helper.rb76
-rw-r--r--app/helpers/gitlab/branches_helper.rb19
-rw-r--r--app/helpers/gitlab/broadcast_messages_helper.rb18
-rw-r--r--app/helpers/gitlab/commits_helper.rb185
-rw-r--r--app/helpers/gitlab/compare_helper.rb23
-rw-r--r--app/helpers/gitlab/dashboard_helper.rb11
-rw-r--r--app/helpers/gitlab/diff_helper.rb172
-rw-r--r--app/helpers/gitlab/emails_helper.rb59
-rw-r--r--app/helpers/gitlab/events_helper.rb205
-rw-r--r--app/helpers/gitlab/explore_helper.rb19
-rw-r--r--app/helpers/gitlab/external_wiki_helper.rb13
-rw-r--r--app/helpers/gitlab/git_helper.rb7
-rw-r--r--app/helpers/gitlab/gitlab_markdown_helper.rb195
-rw-r--r--app/helpers/gitlab/gitlab_routing_helper.rb69
-rw-r--r--app/helpers/gitlab/graph_helper.rb18
-rw-r--r--app/helpers/gitlab/groups_helper.rb35
-rw-r--r--app/helpers/gitlab/icons_helper.rb87
-rw-r--r--app/helpers/gitlab/issues_helper.rb90
-rw-r--r--app/helpers/gitlab/labels_helper.rb103
-rw-r--r--app/helpers/gitlab/merge_requests_helper.rb76
-rw-r--r--app/helpers/gitlab/milestones_helper.rb38
-rw-r--r--app/helpers/gitlab/namespaces_helper.rb38
-rw-r--r--app/helpers/gitlab/nav_helper.rb23
-rw-r--r--app/helpers/gitlab/notes_helper.rb78
-rw-r--r--app/helpers/gitlab/notifications_helper.rb17
-rw-r--r--app/helpers/gitlab/page_layout_helper.rb28
-rw-r--r--app/helpers/gitlab/preferences_helper.rb67
-rw-r--r--app/helpers/gitlab/projects_helper.rb332
-rw-r--r--app/helpers/gitlab/search_helper.rb114
-rw-r--r--app/helpers/gitlab/selects_helper.rb47
-rw-r--r--app/helpers/gitlab/snippets_helper.rb22
-rw-r--r--app/helpers/gitlab/sorting_helper.rb98
-rw-r--r--app/helpers/gitlab/submodule_helper.rb76
-rw-r--r--app/helpers/gitlab/tab_helper.rb133
-rw-r--r--app/helpers/gitlab/tags_helper.rb16
-rw-r--r--app/helpers/gitlab/tree_helper.rb89
-rw-r--r--app/helpers/gitlab/version_check_helper.rb9
-rw-r--r--app/helpers/gitlab/visibility_level_helper.rb97
-rw-r--r--app/helpers/gitlab/wiki_helper.rb26
-rw-r--r--app/helpers/gitlab_markdown_helper.rb193
-rw-r--r--app/helpers/gitlab_routing_helper.rb67
-rw-r--r--app/helpers/graph_helper.rb16
-rw-r--r--app/helpers/groups_helper.rb33
-rw-r--r--app/helpers/icons_helper.rb85
-rw-r--r--app/helpers/issues_helper.rb88
-rw-r--r--app/helpers/labels_helper.rb101
-rw-r--r--app/helpers/merge_requests_helper.rb74
-rw-r--r--app/helpers/milestones_helper.rb36
-rw-r--r--app/helpers/namespaces_helper.rb36
-rw-r--r--app/helpers/nav_helper.rb21
-rw-r--r--app/helpers/notes_helper.rb76
-rw-r--r--app/helpers/notifications_helper.rb15
-rw-r--r--app/helpers/page_layout_helper.rb26
-rw-r--r--app/helpers/preferences_helper.rb65
-rw-r--r--app/helpers/projects_helper.rb330
-rw-r--r--app/helpers/search_helper.rb112
-rw-r--r--app/helpers/selects_helper.rb45
-rw-r--r--app/helpers/snippets_helper.rb20
-rw-r--r--app/helpers/sorting_helper.rb96
-rw-r--r--app/helpers/submodule_helper.rb74
-rw-r--r--app/helpers/tab_helper.rb131
-rw-r--r--app/helpers/tags_helper.rb14
-rw-r--r--app/helpers/tree_helper.rb88
-rw-r--r--app/helpers/version_check_helper.rb7
-rw-r--r--app/helpers/visibility_level_helper.rb95
-rw-r--r--app/helpers/wiki_helper.rb24
97 files changed, 3676 insertions, 3196 deletions
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
deleted file mode 100644
index 14df8d4cbd7..00000000000
--- a/app/helpers/appearances_helper.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module AppearancesHelper
- def brand_item
- nil
- end
-
- def brand_title
- 'GitLab Community Edition'
- end
-
- def brand_image
- nil
- end
-
- def brand_text
- nil
- end
-
- def brand_header_logo
- image_tag 'logo.svg'
- end
-end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
deleted file mode 100644
index a803b66c502..00000000000
--- a/app/helpers/application_helper.rb
+++ /dev/null
@@ -1,315 +0,0 @@
-require 'digest/md5'
-require 'uri'
-
-module ApplicationHelper
- # Check if a particular controller is the current one
- #
- # args - One or more controller names to check
- #
- # Examples
- #
- # # On TreeController
- # current_controller?(:tree) # => true
- # current_controller?(:commits) # => false
- # current_controller?(:commits, :tree) # => true
- def current_controller?(*args)
- args.any? { |v| v.to_s.downcase == controller.controller_name }
- end
-
- # Check if a particular action is the current one
- #
- # args - One or more action names to check
- #
- # Examples
- #
- # # On Projects#new
- # current_action?(:new) # => true
- # current_action?(:create) # => false
- # current_action?(:new, :create) # => true
- def current_action?(*args)
- args.any? { |v| v.to_s.downcase == action_name }
- end
-
- def project_icon(project_id, options = {})
- project =
- if project_id.is_a?(Project)
- project = project_id
- else
- Project.find_with_namespace(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
-
- def avatar_icon(user_email = '', size = nil)
- user = User.find_by(email: user_email)
-
- if user
- user.avatar_url(size) || default_avatar
- else
- gravatar_icon(user_email, size)
- end
- end
-
- def gravatar_icon(user_email = '', size = nil)
- GravatarService.new.execute(user_email, size) ||
- default_avatar
- end
-
- def default_avatar
- image_path('no_avatar.png')
- end
-
- def last_commit(project)
- if project.repo_exists?
- time_ago_with_tooltip(project.repository.commit.committed_date)
- else
- 'Never'
- end
- rescue
- 'Never'
- end
-
- def grouped_options_refs
- repository = @project.repository
-
- options = [
- ['Branches', repository.branch_names],
- ['Tags', VersionSorter.rsort(repository.tag_names)]
- ]
-
- # If reference is commit id - we should add it to branch/tag selectbox
- if(@ref && !options.flatten.include?(@ref) &&
- @ref =~ /\A[0-9a-zA-Z]{6,52}\z/)
- options << ['Commit', [@ref]]
- end
-
- grouped_options_for_select(options, @ref || @project.default_branch)
- end
-
- def emoji_autocomplete_source
- # should be an array of strings
- # so to_s can be called, because it is sufficient and to_json is too slow
- Emoji.names.to_s
- end
-
- # Define whenever show last push event
- # with suggestion to create MR
- def show_last_push_widget?(event)
- # Skip if event is not about added or modified non-master branch
- return false unless event && event.last_push_to_non_root? && !event.rm_ref?
-
- project = event.project
-
- # Skip if project repo is empty or MR disabled
- return false unless project && !project.empty_repo? && project.merge_requests_enabled
-
- # Skip if user already created appropriate MR
- return false if project.merge_requests.where(source_branch: event.branch_name).opened.any?
-
- # Skip if user removed branch right after that
- return false unless project.repository.branch_names.include?(event.branch_name)
-
- true
- end
-
- def hexdigest(string)
- Digest::SHA1.hexdigest string
- end
-
- def simple_sanitize(str)
- sanitize(str, tags: %w(a span))
- end
-
- def body_data_page
- path = controller.controller_path.split('/')
- namespace = path.first if path.second
-
- [namespace, controller.controller_name, controller.action_name].compact.join(':')
- end
-
- # shortcut for gitlab config
- def gitlab_config
- Gitlab.config.gitlab
- end
-
- # shortcut for gitlab extra config
- def extra_config
- Gitlab.config.extra
- end
-
- def search_placeholder
- if @project && @project.persisted?
- 'Search in this project'
- elsif @snippet || @snippets || @show_snippets
- 'Search snippets'
- elsif @group && @group.persisted?
- 'Search in this group'
- else
- 'Search'
- end
- end
-
- def broadcast_message
- BroadcastMessage.current
- end
-
- # Render a `time` element with Javascript-based relative date and tooltip
- #
- # time - Time object
- # placement - Tooltip placement String (default: "top")
- # html_class - Custom class for `time` element (default: "time_ago")
- # skip_js - When true, exclude the `script` tag (default: false)
- #
- # By default also includes a `script` element with Javascript necessary to
- # initialize the `timeago` jQuery extension. If this method is called many
- # times, for example rendering hundreds of commits, it's advisable to disable
- # this behavior using the `skip_js` argument and re-initializing `timeago`
- # manually once all of the elements have been rendered.
- #
- # A `js-timeago` class is always added to the element, even when a custom
- # `html_class` argument is provided.
- #
- # 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",
- datetime: time.getutc.iso8601,
- title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'),
- data: { toggle: 'tooltip', placement: placement }
-
- element += javascript_tag "$('.js-timeago').timeago()" unless skip_js
-
- element
- end
-
- def render_markup(file_name, file_content)
- if gitlab_markdown?(file_name)
- Haml::Helpers.preserve(markdown(file_content))
- elsif asciidoc?(file_name)
- asciidoc(file_content)
- elsif plain?(file_name)
- content_tag :pre, class: 'plain-readme' do
- file_content
- end
- else
- GitHub::Markup.render(file_name, file_content).
- force_encoding(file_content.encoding).html_safe
- end
- rescue RuntimeError
- simple_format(file_content)
- end
-
- def plain?(filename)
- Gitlab::MarkupHelper.plain?(filename)
- end
-
- def markup?(filename)
- Gitlab::MarkupHelper.markup?(filename)
- end
-
- def gitlab_markdown?(filename)
- Gitlab::MarkupHelper.gitlab_markdown?(filename)
- end
-
- def asciidoc?(filename)
- Gitlab::MarkupHelper.asciidoc?(filename)
- end
-
- def promo_host
- 'about.gitlab.com'
- end
-
- def promo_url
- 'https://' + promo_host
- end
-
- def page_filter_path(options = {})
- without = options.delete(:without)
-
- exist_opts = {
- state: params[:state],
- scope: params[:scope],
- label_name: params[:label_name],
- milestone_id: params[:milestone_id],
- assignee_id: params[:assignee_id],
- author_id: params[:author_id],
- sort: params[:sort],
- }
-
- options = exist_opts.merge(options)
-
- if without.present?
- without.each do |key|
- options.delete(key)
- end
- end
-
- path = request.path
- path << "?#{options.to_param}"
- path
- end
-
- def outdated_browser?
- browser.ie? && browser.version.to_i < 10
- end
-
- def path_to_key(key, admin = false)
- if admin
- admin_user_key_path(@user, key)
- else
- profile_key_path(key)
- end
- end
-
- def state_filters_text_for(entity, project)
- titles = {
- opened: "Open"
- }
-
- entity_title = titles[entity] || entity.to_s.humanize
-
- count =
- if project.nil?
- nil
- elsif current_controller?(:issues)
- project.issues.send(entity).count
- elsif current_controller?(:merge_requests)
- project.merge_requests.send(entity).count
- end
-
- html = content_tag :span, entity_title
-
- if count.present?
- html += " "
- html += content_tag :span, number_with_delimiter(count), class: 'badge'
- end
-
- html.html_safe
- end
-end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
deleted file mode 100644
index 7d6b58ee21a..00000000000
--- a/app/helpers/application_settings_helper.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-module ApplicationSettingsHelper
- def gravatar_enabled?
- current_application_settings.gravatar_enabled?
- end
-
- def twitter_sharing_enabled?
- current_application_settings.twitter_sharing_enabled?
- end
-
- def signup_enabled?
- current_application_settings.signup_enabled?
- end
-
- def signin_enabled?
- current_application_settings.signin_enabled?
- end
-
- def extra_sign_in_text
- current_application_settings.sign_in_text
- end
-
- def user_oauth_applications?
- current_application_settings.user_oauth_applications
- end
-
- # Return a group of checkboxes that use Bootstrap's button plugin for a
- # toggle button effect.
- def restricted_level_checkboxes(help_block_id)
- Gitlab::VisibilityLevel.options.map do |name, level|
- checked = restricted_visibility_levels(true).include?(level)
- css_class = 'btn'
- css_class += ' active' if checked
- checkbox_name = 'application_setting[restricted_visibility_levels][]'
-
- label_tag(checkbox_name, class: css_class) do
- check_box_tag(checkbox_name, level, checked,
- autocomplete: 'off',
- 'aria-describedby' => help_block_id) + name
- end
- end
- end
-
- # Return a group of checkboxes that use Bootstrap's button plugin for a
- # toggle button effect.
- def import_sources_checkboxes(help_block_id)
- Gitlab::ImportSources.options.map do |name, source|
- checked = current_application_settings.import_sources.include?(source)
- css_class = 'btn'
- css_class += ' active' if checked
- checkbox_name = 'application_setting[import_sources][]'
-
- label_tag(checkbox_name, class: css_class) do
- check_box_tag(checkbox_name, source, checked,
- autocomplete: 'off',
- 'aria-describedby' => help_block_id) + name
- end
- end
- end
-end
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
deleted file mode 100644
index 0e7a37b4cc6..00000000000
--- a/app/helpers/auth_helper.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-module AuthHelper
- PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze
- FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos'].freeze
-
- def ldap_enabled?
- Gitlab.config.ldap.enabled
- end
-
- def provider_has_icon?(name)
- PROVIDERS_WITH_ICONS.include?(name.to_s)
- end
-
- def auth_providers
- Gitlab::OAuth::Provider.providers
- end
-
- def label_for_provider(name)
- Gitlab::OAuth::Provider.label_for(name)
- end
-
- def form_based_provider?(name)
- FORM_BASED_PROVIDERS.any? { |pattern| pattern === name.to_s }
- end
-
- def form_based_providers
- auth_providers.select { |provider| form_based_provider?(provider) }
- end
-
- def button_based_providers
- auth_providers.reject { |provider| form_based_provider?(provider) }
- end
-
- def provider_image_tag(provider, size = 64)
- label = label_for_provider(provider)
-
- if provider_has_icon?(provider)
- file_name = "#{provider.to_s.split('_').first}_#{size}.png"
-
- image_tag(image_path("auth_buttons/#{file_name}"), alt: label, title: "Sign in with #{label}")
- else
- label
- end
- end
-
- def auth_active?(provider)
- current_user.identities.exists?(provider: provider.to_s)
- end
-
- extend self
-end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
deleted file mode 100644
index 77d99140c43..00000000000
--- a/app/helpers/blob_helper.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-module BlobHelper
- def highlight(blob_name, blob_content, nowrap: false, continue: false)
- @formatter ||= Rouge::Formatters::HTMLGitlab.new(
- nowrap: nowrap,
- cssclass: 'code highlight',
- lineanchors: true,
- lineanchorsid: 'LC'
- )
-
- begin
- @lexer ||= Rouge::Lexer.guess(filename: blob_name, source: blob_content).new
- result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe
- rescue
- @lexer = Rouge::Lexers::PlainText
- result = @formatter.format(@lexer.lex(blob_content)).html_safe
- end
-
- result
- end
-
- def no_highlight_files
- %w(credits changelog news copying copyright license authors)
- end
-
- def edit_blob_link(project, ref, path, options = {})
- blob =
- begin
- project.repository.blob_at(ref, path)
- rescue
- nil
- end
-
- if blob && blob.text?
- text = 'Edit'
- after = options[:after] || ''
- from_mr = options[:from_merge_request_id]
- link_opts = {}
- link_opts[:from_merge_request_id] = from_mr if from_mr
- cls = 'btn btn-small'
- if allowed_tree_edit?(project, ref)
- link_to(text,
- namespace_project_edit_blob_path(project.namespace, project,
- tree_join(ref, path),
- link_opts),
- class: cls
- )
- else
- content_tag :span, text, class: cls + ' disabled'
- end + after.html_safe
- else
- ''
- end
- end
-
- def leave_edit_message
- "Leave edit mode?\nAll unsaved changes will be lost."
- end
-
- def editing_preview_title(filename)
- if Gitlab::MarkupHelper.previewable?(filename)
- 'Preview'
- else
- 'Preview changes'
- end
- end
-
- # Return an image icon depending on the file mode and extension
- #
- # mode - File unix mode
- # mode - File name
- def blob_icon(mode, name)
- icon("#{file_type_icon_class('file', mode, name)} fw")
- end
-end
diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb
deleted file mode 100644
index d6eaa7d57bc..00000000000
--- a/app/helpers/branches_helper.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module BranchesHelper
- def can_remove_branch?(project, branch_name)
- if project.protected_branch? branch_name
- false
- elsif branch_name == project.repository.root_ref
- false
- else
- can?(current_user, :push_code, project)
- end
- end
-
- def can_push_branch?(project, branch_name)
- return false unless project.repository.branch_names.include?(branch_name)
-
- ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
- end
-end
diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb
deleted file mode 100644
index 6484dca6b55..00000000000
--- a/app/helpers/broadcast_messages_helper.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module BroadcastMessagesHelper
- def broadcast_styling(broadcast_message)
- styling = ''
-
- if broadcast_message.color.present?
- styling << "background-color: #{broadcast_message.color}"
- styling << '; ' if broadcast_message.font.present?
- end
-
- if broadcast_message.font.present?
- styling << "color: #{broadcast_message.font}"
- end
-
- styling
- end
-end
diff --git a/app/helpers/ci/application_helper.rb b/app/helpers/ci/application_helper.rb
new file mode 100644
index 00000000000..3198fe55f91
--- /dev/null
+++ b/app/helpers/ci/application_helper.rb
@@ -0,0 +1,140 @@
+module Ci
+ module ApplicationHelper
+ def loader_html
+ image_tag 'ci/loader.gif', alt: 'Loading'
+ end
+
+ # Navigation link helper
+ #
+ # Returns an `li` element with an 'active' class if the supplied
+ # controller(s) and/or action(s) are currently active. The content of the
+ # element is the value passed to the block.
+ #
+ # options - The options hash used to determine if the element is "active" (default: {})
+ # :controller - One or more controller names to check (optional).
+ # :action - One or more action names to check (optional).
+ # :path - A shorthand path, such as 'dashboard#index', to check (optional).
+ # :html_options - Extra options to be passed to the list element (optional).
+ # block - An optional block that will become the contents of the returned
+ # `li` element.
+ #
+ # When both :controller and :action are specified, BOTH must match in order
+ # to be marked as active. When only one is given, either can match.
+ #
+ # Examples
+ #
+ # # Assuming we're on TreeController#show
+ #
+ # # Controller matches, but action doesn't
+ # nav_link(controller: [:tree, :refs], action: :edit) { "Hello" }
+ # # => '<li>Hello</li>'
+ #
+ # # Controller matches
+ # nav_link(controller: [:tree, :refs]) { "Hello" }
+ # # => '<li class="active">Hello</li>'
+ #
+ # # Shorthand path
+ # nav_link(path: 'tree#show') { "Hello" }
+ # # => '<li class="active">Hello</li>'
+ #
+ # # Supplying custom options for the list element
+ # nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" }
+ # # => '<li class="home active">Hello</li>'
+ #
+ # Returns a list item element String
+ def nav_link(options = {}, &block)
+ if path = options.delete(:path)
+ if path.respond_to?(:each)
+ c = path.map { |p| p.split('#').first }
+ a = path.map { |p| p.split('#').last }
+ else
+ c, a, _ = path.split('#')
+ end
+ else
+ c = options.delete(:controller)
+ a = options.delete(:action)
+ end
+
+ if c && a
+ # When given both options, make sure BOTH are active
+ klass = current_controller?(*c) && current_action?(*a) ? 'active' : ''
+ else
+ # Otherwise check EITHER option
+ klass = current_controller?(*c) || current_action?(*a) ? 'active' : ''
+ end
+
+ # Add our custom class into the html_options, which may or may not exist
+ # and which may or may not already have a :class key
+ o = options.delete(:html_options) || {}
+ o[:class] ||= ''
+ o[:class] += ' ' + klass
+ o[:class].strip!
+
+ if block_given?
+ content_tag(:li, capture(&block), o)
+ else
+ content_tag(:li, nil, o)
+ end
+ end
+
+ # Check if a particular controller is the current one
+ #
+ # args - One or more controller names to check
+ #
+ # Examples
+ #
+ # # On TreeController
+ # current_controller?(:tree) # => true
+ # current_controller?(:commits) # => false
+ # current_controller?(:commits, :tree) # => true
+ def current_controller?(*args)
+ args.any? { |v| v.to_s.downcase == controller.controller_name }
+ end
+
+ # Check if a particular action is the current one
+ #
+ # args - One or more action names to check
+ #
+ # Examples
+ #
+ # # On Projects#new
+ # current_action?(:new) # => true
+ # current_action?(:create) # => false
+ # current_action?(:new, :create) # => true
+ def current_action?(*args)
+ args.any? { |v| v.to_s.downcase == action_name }
+ end
+
+ def date_from_to(from, to)
+ "#{from.to_s(:short)} - #{to.to_s(:short)}"
+ end
+
+ def body_data_page
+ path = controller.controller_path.split('/')
+ namespace = path.first if path.second
+
+ [namespace, controller.controller_name, controller.action_name].compact.join(":")
+ end
+
+ def duration_in_words(finished_at, started_at)
+ if finished_at && started_at
+ interval_in_seconds = finished_at.to_i - started_at.to_i
+ elsif started_at
+ interval_in_seconds = Time.now.to_i - started_at.to_i
+ end
+
+ time_interval_in_words(interval_in_seconds)
+ end
+
+ def time_interval_in_words(interval_in_seconds)
+ minutes = interval_in_seconds / 60
+ seconds = interval_in_seconds - minutes * 60
+
+ if minutes >= 1
+ "#{pluralize(minutes, "minute")} #{pluralize(seconds, "second")}"
+ else
+ "#{pluralize(seconds, "second")}"
+ end
+ end
+ end
+end
diff --git a/app/helpers/ci/builds_helper.rb b/app/helpers/ci/builds_helper.rb
new file mode 100644
index 00000000000..cdabdad17d2
--- /dev/null
+++ b/app/helpers/ci/builds_helper.rb
@@ -0,0 +1,41 @@
+module Ci
+ module BuildsHelper
+ def build_ref_link build
+ gitlab_ref_link build.project, build.ref
+ end
+
+ def build_compare_link build
+ gitlab_compare_link build.project, build.commit.short_before_sha, build.short_sha
+ end
+
+ def build_commit_link build
+ gitlab_commit_link build.project, build.short_sha
+ end
+
+ def build_url(build)
+ ci_project_build_url(build.project, build)
+ end
+
+ def build_status_alert_class(build)
+ if build.success?
+ 'alert-success'
+ elsif build.failed?
+ 'alert-danger'
+ elsif build.canceled?
+ 'alert-disabled'
+ else
+ 'alert-warning'
+ end
+ end
+
+ def build_icon_css_class(build)
+ if build.success?
+ 'fa-circle cgreen'
+ elsif build.failed?
+ 'fa-circle cred'
+ else
+ 'fa-circle light'
+ end
+ end
+ end
+end
diff --git a/app/helpers/ci/commits_helper.rb b/app/helpers/ci/commits_helper.rb
new file mode 100644
index 00000000000..0479bc10594
--- /dev/null
+++ b/app/helpers/ci/commits_helper.rb
@@ -0,0 +1,26 @@
+module Ci
+ module CommitsHelper
+ def commit_status_alert_class(commit)
+ return unless commit
+
+ case commit.status
+ when 'success'
+ 'alert-success'
+ when 'failed', 'canceled'
+ 'alert-danger'
+ when 'skipped'
+ 'alert-disabled'
+ else
+ 'alert-warning'
+ end
+ end
+
+ def commit_link(commit)
+ link_to(commit.short_sha, ci_project_ref_commit_path(commit.project, commit.ref, commit.sha))
+ end
+
+ def truncate_first_line(message, length = 50)
+ truncate(message.each_line.first.chomp, length: length) if message
+ end
+ end
+end
diff --git a/app/helpers/ci/gitlab_helper.rb b/app/helpers/ci/gitlab_helper.rb
new file mode 100644
index 00000000000..2b89a0ce93e
--- /dev/null
+++ b/app/helpers/ci/gitlab_helper.rb
@@ -0,0 +1,36 @@
+module Ci
+ module GitlabHelper
+ def no_turbolink
+ { :"data-no-turbolink" => "data-no-turbolink" }
+ end
+
+ def gitlab_ref_link project, ref
+ gitlab_url = project.gitlab_url.dup
+ gitlab_url << "/commits/#{ref}"
+ link_to ref, gitlab_url, no_turbolink
+ end
+
+ def gitlab_compare_link project, before, after
+ gitlab_url = project.gitlab_url.dup
+ gitlab_url << "/compare/#{before}...#{after}"
+
+ link_to "#{before}...#{after}", gitlab_url, no_turbolink
+ end
+
+ def gitlab_commit_link project, sha
+ gitlab_url = project.gitlab_url.dup
+ gitlab_url << "/commit/#{sha}"
+ link_to Ci::Commit.truncate_sha(sha), gitlab_url, no_turbolink
+ end
+
+ def yaml_web_editor_link(project)
+ commits = project.commits
+
+ if commits.any? && commits.last.push_data[:ci_yaml_file]
+ "#{@project.gitlab_url}/edit/master/.gitlab-ci.yml"
+ else
+ "#{@project.gitlab_url}/new/master"
+ end
+ end
+ end
+end
diff --git a/app/helpers/ci/icons_helper.rb b/app/helpers/ci/icons_helper.rb
new file mode 100644
index 00000000000..ecb6ef7be45
--- /dev/null
+++ b/app/helpers/ci/icons_helper.rb
@@ -0,0 +1,11 @@
+module Ci
+ module IconsHelper
+ def boolean_to_icon(value)
+ if value.to_s == "true"
+ content_tag :i, nil, class: 'fa-circle cgreen'
+ else
+ content_tag :i, nil, class: 'fa-power-off clgray'
+ end
+ end
+ end
+end
diff --git a/app/helpers/ci/projects_helper.rb b/app/helpers/ci/projects_helper.rb
new file mode 100644
index 00000000000..fd991a4165a
--- /dev/null
+++ b/app/helpers/ci/projects_helper.rb
@@ -0,0 +1,36 @@
+module Ci
+ module ProjectsHelper
+ def ref_tab_class ref = nil
+ 'active' if ref == @ref
+ end
+
+ def success_ratio(success_builds, failed_builds)
+ failed_builds = failed_builds.count(:all)
+ success_builds = success_builds.count(:all)
+
+ return 100 if failed_builds.zero?
+
+ ratio = (success_builds.to_f / (success_builds + failed_builds)) * 100
+ ratio.to_i
+ end
+
+ def markdown_badge_code(project, ref)
+ url = status_ci_project_url(project, ref: ref, format: 'png')
+ "[![build status](#{url})](#{ci_project_url(project, ref: ref)})"
+ end
+
+ def html_badge_code(project, ref)
+ url = status_ci_project_url(project, ref: ref, format: 'png')
+ "<a href='#{ci_project_url(project, ref: ref)}'><img src='#{url}' /></a>"
+ end
+
+ def project_uses_specific_runner?(project)
+ project.runners.any?
+ end
+
+ def no_runners_for_project?(project)
+ project.runners.blank? &&
+ Ci::Runner.shared.blank?
+ end
+ end
+end
diff --git a/app/helpers/ci/routes_helper.rb b/app/helpers/ci/routes_helper.rb
new file mode 100644
index 00000000000..f22d5023db5
--- /dev/null
+++ b/app/helpers/ci/routes_helper.rb
@@ -0,0 +1,29 @@
+module Ci
+ module RoutesHelper
+ class Base
+ include Gitlab::Application.routes.url_helpers
+
+ def default_url_options
+ {
+ host: Ci::Settings.gitlab_ci['host'],
+ protocol: Ci::Settings.gitlab_ci['https'] ? "https" : "http",
+ port: Ci::Settings.gitlab_ci['port']
+ }
+ end
+ end
+
+ def url_helpers
+ @url_helpers ||= Ci::Base.new
+ end
+
+ def self.method_missing(method, *args, &block)
+ @url_helpers ||= Ci::Base.new
+
+ if @url_helpers.respond_to?(method)
+ @url_helpers.send(method, *args, &block)
+ else
+ super method, *args, &block
+ end
+ end
+ end
+end
diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb
new file mode 100644
index 00000000000..782208ddfe4
--- /dev/null
+++ b/app/helpers/ci/runners_helper.rb
@@ -0,0 +1,22 @@
+module Ci
+ module RunnersHelper
+ def runner_status_icon(runner)
+ unless runner.contacted_at
+ return content_tag :i, nil,
+ class: "fa-warning-sign",
+ title: "New runner. Has not connected yet"
+ end
+
+ status =
+ if runner.active?
+ runner.contacted_at > 3.hour.ago ? :online : :offline
+ else
+ :paused
+ end
+
+ content_tag :i, nil,
+ class: "fa-circle runner-status-#{status}",
+ title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
+ end
+ end
+end
diff --git a/app/helpers/ci/triggers_helper.rb b/app/helpers/ci/triggers_helper.rb
new file mode 100644
index 00000000000..caff54c3520
--- /dev/null
+++ b/app/helpers/ci/triggers_helper.rb
@@ -0,0 +1,7 @@
+module Ci
+ module TriggersHelper
+ def build_trigger_url(project_id, ref_name)
+ "#{Ci::Settings.gitlab_ci.url}/api/v1/projects/#{project_id}/refs/#{ref_name}/trigger"
+ end
+ end
+end
diff --git a/app/helpers/ci/user_helper.rb b/app/helpers/ci/user_helper.rb
new file mode 100644
index 00000000000..c332d6ed9cf
--- /dev/null
+++ b/app/helpers/ci/user_helper.rb
@@ -0,0 +1,15 @@
+module Ci
+ module UserHelper
+ def user_avatar_url(user = nil, size = nil, default = 'identicon')
+ size = 40 if size.nil? || size <= 0
+
+ if user.blank? || user.avatar_url.blank?
+ 'ci/no_avatar.png'
+ elsif /^(http(s?):\/\/(www|secure)\.gravatar\.com\/avatar\/(\w*))/ =~ user.avatar_url
+ Regexp.last_match[0] + "?s=#{size}&d=#{default}"
+ else
+ user.avatar_url
+ end
+ end
+ end
+end
diff --git a/app/helpers/ci/user_sessions_helper.rb b/app/helpers/ci/user_sessions_helper.rb
new file mode 100644
index 00000000000..0296a74395c
--- /dev/null
+++ b/app/helpers/ci/user_sessions_helper.rb
@@ -0,0 +1,32 @@
+module Ci
+ module UserSessionsHelper
+ def generate_oauth_salt
+ SecureRandom.hex(16)
+ end
+
+ def generate_oauth_hmac(salt, return_to)
+ return unless return_to
+ digest = OpenSSL::Digest.new('sha256')
+ key = Gitlab::Application.secrets.db_key_base + salt
+ OpenSSL::HMAC.hexdigest(digest, key, return_to)
+ end
+
+ def generate_oauth_state(return_to)
+ return unless return_to
+ salt = generate_oauth_salt
+ hmac = generate_oauth_hmac(salt, return_to)
+ "#{salt}:#{hmac}:#{return_to}"
+ end
+
+ def get_ouath_state_return_to(state)
+ state.split(':', 3)[2] if state
+ end
+
+ def is_oauth_state_valid?(state)
+ return true unless state
+ salt, hmac, return_to = state.split(':', 3)
+ return false unless return_to
+ hmac == generate_oauth_hmac(salt, return_to)
+ end
+ end
+end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
deleted file mode 100644
index d13d80be293..00000000000
--- a/app/helpers/commits_helper.rb
+++ /dev/null
@@ -1,183 +0,0 @@
-# encoding: utf-8
-module CommitsHelper
- # Returns a link to the commit author. If the author has a matching user and
- # is a member of the current @project it will link to the team member page.
- # Otherwise it will link to the author email as specified in the commit.
- #
- # options:
- # avatar: true will prepend the avatar image
- # size: size of the avatar image in px
- def commit_author_link(commit, options = {})
- commit_person_link(commit, options.merge(source: :author))
- end
-
- # Just like #author_link but for the committer.
- def commit_committer_link(commit, options = {})
- commit_person_link(commit, options.merge(source: :committer))
- end
-
- def image_diff_class(diff)
- if diff.deleted_file
- "deleted"
- elsif diff.new_file
- "added"
- else
- nil
- end
- end
-
- def commit_to_html(commit, project, inline = true)
- template = inline ? "inline_commit" : "commit"
- escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil?
- end
-
- # Breadcrumb links for a Project and, if applicable, a tree path
- def commits_breadcrumbs
- return unless @project && @ref
-
- # Add the root project link and the arrow icon
- crumbs = content_tag(:li) do
- link_to(
- @project.path,
- namespace_project_commits_path(@project.namespace, @project, @ref)
- )
- end
-
- if @path
- parts = @path.split('/')
-
- parts.each_with_index do |part, i|
- crumbs << content_tag(:li) do
- # The text is just the individual part, but the link needs all the parts before it
- link_to(
- part,
- namespace_project_commits_path(
- @project.namespace,
- @project,
- tree_join(@ref, parts[0..i].join('/'))
- )
- )
- end
- end
- end
-
- crumbs.html_safe
- end
-
- # Return Project default branch, if it present in array
- # Else - first branch in array (mb last actual branch)
- def commit_default_branch(project, branches)
- branches.include?(project.default_branch) ? branches.delete(project.default_branch) : branches.pop
- end
-
- # Returns the sorted alphabetically links to branches, separated by a comma
- def commit_branches_links(project, branches)
- branches.sort.map do |branch|
- link_to(
- namespace_project_tree_path(project.namespace, project, branch)
- ) do
- content_tag :span, class: 'label label-gray' do
- icon('code-fork') + ' ' + branch
- end
- end
- end.join(" ").html_safe
- end
-
- # Returns the sorted links to tags, separated by a comma
- def commit_tags_links(project, tags)
- sorted = VersionSorter.rsort(tags)
- sorted.map do |tag|
- link_to(
- namespace_project_commits_path(project.namespace, project,
- project.repository.find_tag(tag).name)
- ) do
- content_tag :span, class: 'label label-gray' do
- icon('tag') + ' ' + tag
- end
- end
- end.join(" ").html_safe
- end
-
- def link_to_browse_code(project, commit)
- if current_controller?(:projects, :commits)
- if @repo.blob_at(commit.id, @path)
- return link_to(
- "Browse File »",
- namespace_project_blob_path(project.namespace, project,
- tree_join(commit.id, @path)),
- class: "pull-right"
- )
- elsif @path.present?
- return link_to(
- "Browse Dir »",
- namespace_project_tree_path(project.namespace, project,
- tree_join(commit.id, @path)),
- class: "pull-right"
- )
- end
- end
- link_to(
- "Browse Code »",
- namespace_project_tree_path(project.namespace, project, commit),
- class: "pull-right"
- )
- end
-
- protected
-
- # Private: Returns a link to a person. If the person has a matching user and
- # is a member of the current @project it will link to the team member page.
- # Otherwise it will link to the person email as specified in the commit.
- #
- # options:
- # source: one of :author or :committer
- # avatar: true will prepend the avatar image
- # size: size of the avatar image in px
- def commit_person_link(commit, options = {})
- user = commit.send(options[:source])
-
- source_name = clean(commit.send "#{options[:source]}_name".to_sym)
- source_email = clean(commit.send "#{options[:source]}_email".to_sym)
-
- person_name = user.try(:name) || source_name
- person_email = user.try(:email) || source_email
-
- text =
- if options[:avatar]
- avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
- %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{person_name}</span>}
- else
- person_name
- end
-
- options = {
- class: "commit-#{options[:source]}-link has_tooltip",
- data: { :'original-title' => sanitize(source_email) }
- }
-
- if user.nil?
- mail_to(source_email, text.html_safe, options)
- else
- link_to(text.html_safe, user_path(user), options)
- end
- end
-
- def view_file_btn(commit_sha, diff, project)
- link_to(
- namespace_project_blob_path(project.namespace, project,
- tree_join(commit_sha, diff.new_path)),
- class: 'btn btn-small view-file js-view-file'
- ) do
- raw('View file @') + content_tag(:span, commit_sha[0..6],
- class: 'commit-short-id')
- end
- end
-
- def truncate_sha(sha)
- Commit.truncate_sha(sha)
- end
-
- def clean(string)
- Sanitize.clean(string, remove_contents: true)
- end
-end
diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb
deleted file mode 100644
index f1dc906cab4..00000000000
--- a/app/helpers/compare_helper.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module CompareHelper
- def create_mr_button?(from = params[:from], to = params[:to], project = @project)
- from.present? &&
- to.present? &&
- from != to &&
- project.merge_requests_enabled &&
- project.repository.branch_names.include?(from) &&
- project.repository.branch_names.include?(to)
- end
-
- def create_mr_path(from = params[:from], to = params[:to], project = @project)
- new_namespace_project_merge_request_path(
- project.namespace,
- project,
- merge_request: {
- source_branch: to,
- target_branch: from
- }
- )
- end
-end
diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb
deleted file mode 100644
index c25b54eadc6..00000000000
--- a/app/helpers/dashboard_helper.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module DashboardHelper
- def assigned_issues_dashboard_path
- issues_dashboard_path(assignee_id: current_user.id)
- end
-
- def assigned_mrs_dashboard_path
- merge_requests_dashboard_path(assignee_id: current_user.id)
- end
-end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
deleted file mode 100644
index 1bd3ec5e0e0..00000000000
--- a/app/helpers/diff_helper.rb
+++ /dev/null
@@ -1,170 +0,0 @@
-module DiffHelper
- def allowed_diff_size
- if diff_hard_limit_enabled?
- Commit::DIFF_HARD_LIMIT_FILES
- else
- Commit::DIFF_SAFE_FILES
- end
- end
-
- def allowed_diff_lines
- if diff_hard_limit_enabled?
- Commit::DIFF_HARD_LIMIT_LINES
- else
- Commit::DIFF_SAFE_LINES
- end
- end
-
- def safe_diff_files(diffs)
- 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)
- 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
- end
-
- def generate_line_code(file_path, line)
- Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
- end
-
- def parallel_diff(diff_file, index)
- lines = []
- skip_next = false
-
- # Building array of lines
- #
- # [
- # left_type, left_line_number, left_line_content, left_line_code,
- # right_line_type, right_line_number, right_line_content, right_line_code
- # ]
- #
- diff_file.diff_lines.each do |line|
-
- full_line = line.text
- type = line.type
- line_code = generate_line_code(diff_file.file_path, line)
- line_new = line.new_pos
- line_old = line.old_pos
-
- next_line = diff_file.next_line(line.index)
-
- if next_line
- next_line_code = generate_line_code(diff_file.file_path, next_line)
- next_type = next_line.type
- next_line = next_line.text
- end
-
- if type == 'match' || type.nil?
- # line in the right panel is the same as in the left one
- line = [type, line_old, full_line, line_code, type, line_new, full_line, line_code]
- lines.push(line)
- elsif type == 'old'
- if next_type == 'new'
- # Left side has text removed, right side has text added
- line = [type, line_old, full_line, line_code, next_type, line_new, next_line, next_line_code]
- lines.push(line)
- skip_next = true
- elsif next_type == 'old' || next_type.nil?
- # Left side has text removed, right side doesn't have any change
- # No next line code, no new line number, no new line text
- line = [type, line_old, full_line, line_code, next_type, nil, "&nbsp;", nil]
- lines.push(line)
- end
- elsif type == 'new'
- if skip_next
- # Change has been already included in previous line so no need to do it again
- skip_next = false
- next
- else
- # Change is only on the right side, left side has no change
- line = [nil, nil, "&nbsp;", line_code, type, line_new, full_line, line_code]
- lines.push(line)
- end
- end
- end
- lines
- end
-
- def unfold_bottom_class(bottom)
- (bottom) ? 'js-unfold-bottom' : ''
- end
-
- def unfold_class(unfold)
- (unfold) ? 'unfold js-unfold' : ''
- end
-
- def diff_line_content(line)
- if line.blank?
- " &nbsp;"
- else
- line
- end
- end
-
- def line_comments
- @line_comments ||= @line_notes.select(&:active?).group_by(&:line_code)
- end
-
- def organize_comments(type_left, type_right, line_code_left, line_code_right)
- comments_left = comments_right = nil
-
- unless type_left.nil? && type_right == 'new'
- comments_left = line_comments[line_code_left]
- end
-
- unless type_left.nil? && type_right.nil?
- comments_right = line_comments[line_code_right]
- end
-
- [comments_left, comments_right]
- end
-
- def inline_diff_btn
- params_copy = params.dup
- params_copy[:view] = 'inline'
- # Always use HTML to handle case where JSON diff rendered this button
- params_copy.delete(:format)
-
- link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn btn-sm active' : 'btn btn-sm') do
- 'Inline'
- end
- end
-
- def parallel_diff_btn
- params_copy = params.dup
- params_copy[:view] = 'parallel'
- # Always use HTML to handle case where JSON diff rendered this button
- params_copy.delete(:format)
-
- link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active btn-sm' : 'btn btn-sm') do
- 'Side-by-side'
- end
- end
-
- def submodule_link(blob, ref, repository = @repository)
- tree, commit = submodule_links(blob, ref, repository)
- commit_id = if commit.nil?
- blob.id[0..10]
- else
- link_to "#{blob.id[0..10]}", commit
- end
-
- [
- content_tag(:span, link_to(truncate(blob.name, length: 40), tree)),
- '@',
- content_tag(:span, commit_id, class: 'monospace'),
- ].join(' ').html_safe
- end
-end
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
deleted file mode 100644
index 45788ba95ac..00000000000
--- a/app/helpers/emails_helper.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-module EmailsHelper
-
- # Google Actions
- # https://developers.google.com/gmail/markup/reference/go-to-action
- def email_action(url)
- name = action_title(url)
- if name
- data = {
- "@context" => "http://schema.org",
- "@type" => "EmailMessage",
- "action" => {
- "@type" => "ViewAction",
- "name" => name,
- "url" => url,
- }
- }
-
- content_tag :script, type: 'application/ld+json' do
- data.to_json.html_safe
- end
- end
- end
-
- def action_title(url)
- return unless url
- ["merge_requests", "issues", "commit"].each do |action|
- if url.split("/").include?(action)
- return "View #{action.humanize.singularize}"
- end
- end
- end
-
- def color_email_diff(diffcontent)
- formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', inline_theme: 'github')
- lexer = Rouge::Lexers::Diff
- raw formatter.format(lexer.lex(diffcontent))
- end
-
- def password_reset_token_valid_time
- valid_hours = Devise.reset_password_within / 60 / 60
- if valid_hours >= 24
- unit = 'day'
- valid_length = (valid_hours / 24).floor
- else
- unit = 'hour'
- valid_length = valid_hours.floor
- end
-
- pluralize(valid_length, unit)
- end
-
- def reset_token_expire_message
- link_tag = link_to('request a new one', new_user_password_url(user_email: @user.email))
- msg = "This link is valid for #{password_reset_token_valid_time}. "
- msg << "After it expires, you can #{link_tag}."
- end
-end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
deleted file mode 100644
index 8428281f8f6..00000000000
--- a/app/helpers/events_helper.rb
+++ /dev/null
@@ -1,203 +0,0 @@
-module EventsHelper
- def link_to_author(event)
- author = event.author
-
- if author
- link_to author.name, user_path(author.username)
- else
- event.author_name
- end
- end
-
- def event_action_name(event)
- target = if event.target_type
- if event.note?
- event.note_target_type
- else
- event.target_type.titleize.downcase
- end
- else
- 'project'
- end
-
- [event.action_name, target].join(" ")
- end
-
- def event_filter_link(key, tooltip)
- key = key.to_s
- active = 'active' if @event_filter.active?(key)
- link_opts = {
- class: 'event_filter_link',
- id: "#{key}_event_filter",
- title: "Filter by #{tooltip.downcase}",
- data: { toggle: 'tooltip', placement: 'top' }
- }
-
- content_tag :li, class: "filter_icon #{active}" do
- link_to request.path, link_opts do
- icon(icon_for_event[key]) + content_tag(:span, ' ' + tooltip)
- end
- end
- end
-
- def icon_for_event
- {
- EventFilter.push => 'upload',
- EventFilter.merged => 'check-square-o',
- EventFilter.comments => 'comments',
- EventFilter.team => 'user',
- }
- end
-
- def event_feed_title(event)
- words = []
- words << event.author_name
- words << event_action_name(event)
-
- if event.push?
- words << event.ref_type
- words << event.ref_name
- words << "at"
- elsif event.commented?
- if event.note_commit?
- words << event.note_short_commit_id
- else
- words << "##{truncate event.note_target_iid}"
- end
- words << "at"
- elsif event.target
- words << "##{event.target_iid}:"
- words << event.target.title if event.target.respond_to?(:title)
- words << "at"
- end
-
- words << event.project_name
-
- words.join(" ")
- end
-
- def event_feed_url(event)
- if event.issue?
- namespace_project_issue_url(event.project.namespace, event.project,
- event.issue)
- elsif event.merge_request?
- namespace_project_merge_request_url(event.project.namespace,
- event.project, event.merge_request)
- elsif event.note? && event.note_commit?
- namespace_project_commit_url(event.project.namespace, event.project,
- event.note_target)
- elsif event.note?
- if event.note_target
- if event.note_commit?
- namespace_project_commit_path(event.project.namespace, event.project,
- event.note_commit_id,
- anchor: dom_id(event.target))
- elsif event.note_project_snippet?
- namespace_project_snippet_path(event.project.namespace,
- event.project, event.note_target)
- else
- event_note_target_path(event)
- end
- end
- elsif event.push?
- if event.push_with_commits? && event.md_ref?
- if event.commits_count > 1
- namespace_project_compare_url(event.project.namespace, event.project,
- from: event.commit_from, to:
- event.commit_to)
- else
- namespace_project_commit_url(event.project.namespace, event.project,
- id: event.commit_to)
- end
- else
- namespace_project_commits_url(event.project.namespace, event.project,
- event.ref_name)
- end
- end
- end
-
- def event_feed_summary(event)
- if event.issue?
- render "events/event_issue", issue: event.issue
- elsif event.push?
- render "events/event_push", event: event
- elsif event.merge_request?
- render "events/event_merge_request", merge_request: event.merge_request
- elsif event.note?
- render "events/event_note", note: event.note
- end
- end
-
- def event_note_target_path(event)
- if event.note? && event.note_commit?
- namespace_project_commit_path(event.project.namespace, event.project,
- event.note_target)
- else
- polymorphic_path([event.project.namespace.becomes(Namespace),
- event.project, event.note_target],
- anchor: dom_id(event.target))
- end
- end
-
- def event_note_title_html(event)
- if event.note_target
- if event.note_commit?
- link_to(
- namespace_project_commit_path(event.project.namespace, event.project,
- event.note_commit_id,
- anchor: dom_id(event.target)),
- class: "commit_short_id"
- ) do
- "#{event.note_target_type} #{event.note_short_commit_id}"
- end
- 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}"
- end
- else
- link_to event_note_target_path(event) do
- "#{event.note_target_type} ##{truncate event.note_target_iid}"
- end
- end
- else
- content_tag :strong do
- "(deleted)"
- end
- end
- end
-
- def event_note(text, options = {})
- text = first_line_in_markdown(text, 150, options)
- sanitize(text, tags: %w(a img b pre code p span))
- end
-
- def event_commit_title(message)
- escape_once(truncate(message.split("\n").first, length: 70))
- rescue
- "--broken encoding"
- end
-
- def event_to_atom(xml, event)
- if event.proper?
- xml.entry do
- event_link = event_feed_url(event)
- event_title = event_feed_title(event)
- event_summary = event_feed_summary(event)
-
- xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
- xml.link href: event_link
- xml.title truncate(event_title, length: 80)
- xml.updated event.created_at.xmlschema
- xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email)
- xml.author do |author|
- xml.name event.author_name
- xml.email event.author_email
- end
-
- xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? }
- end
- end
- end
-end
diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb
deleted file mode 100644
index 0d291f9a87e..00000000000
--- a/app/helpers/explore_helper.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module ExploreHelper
- def explore_projects_filter_path(options={})
- exist_opts = {
- sort: params[:sort],
- scope: params[:scope],
- group: params[:group],
- tag: params[:tag],
- visibility_level: params[:visibility_level],
- }
-
- options = exist_opts.merge(options)
-
- path = explore_projects_path
- path << "?#{options.to_param}"
- path
- end
-end
diff --git a/app/helpers/external_wiki_helper.rb b/app/helpers/external_wiki_helper.rb
deleted file mode 100644
index 838b85afdfe..00000000000
--- a/app/helpers/external_wiki_helper.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module ExternalWikiHelper
- def get_project_wiki_path(project)
- external_wiki_service = project.services.
- select { |service| service.to_param == 'external_wiki' }.first
- if external_wiki_service.present? && external_wiki_service.active?
- external_wiki_service.properties['external_wiki_url']
- else
- namespace_project_wiki_path(project.namespace, project, :home)
- end
- end
-end
diff --git a/app/helpers/git_helper.rb b/app/helpers/git_helper.rb
deleted file mode 100644
index 09684955233..00000000000
--- a/app/helpers/git_helper.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module GitHelper
- def strip_gpg_signature(text)
- text.gsub(/-----BEGIN PGP SIGNATURE-----(.*)-----END PGP SIGNATURE-----/m, "")
- end
-end
diff --git a/app/helpers/gitlab/appearances_helper.rb b/app/helpers/gitlab/appearances_helper.rb
new file mode 100644
index 00000000000..54cafcd9e40
--- /dev/null
+++ b/app/helpers/gitlab/appearances_helper.rb
@@ -0,0 +1,23 @@
+module Gitlab
+ module AppearancesHelper
+ def brand_item
+ nil
+ end
+
+ def brand_title
+ 'GitLab Community Edition'
+ end
+
+ def brand_image
+ nil
+ end
+
+ def brand_text
+ nil
+ end
+
+ def brand_header_logo
+ image_tag 'logo.svg'
+ end
+ end
+end
diff --git a/app/helpers/gitlab/application_helper.rb b/app/helpers/gitlab/application_helper.rb
new file mode 100644
index 00000000000..b019ffa5fe2
--- /dev/null
+++ b/app/helpers/gitlab/application_helper.rb
@@ -0,0 +1,317 @@
+require 'digest/md5'
+require 'uri'
+
+module Gitlab
+ module ApplicationHelper
+ # Check if a particular controller is the current one
+ #
+ # args - One or more controller names to check
+ #
+ # Examples
+ #
+ # # On TreeController
+ # current_controller?(:tree) # => true
+ # current_controller?(:commits) # => false
+ # current_controller?(:commits, :tree) # => true
+ def current_controller?(*args)
+ args.any? { |v| v.to_s.downcase == controller.controller_name }
+ end
+
+ # Check if a particular action is the current one
+ #
+ # args - One or more action names to check
+ #
+ # Examples
+ #
+ # # On Projects#new
+ # current_action?(:new) # => true
+ # current_action?(:create) # => false
+ # current_action?(:new, :create) # => true
+ def current_action?(*args)
+ args.any? { |v| v.to_s.downcase == action_name }
+ end
+
+ def project_icon(project_id, options = {})
+ project =
+ if project_id.is_a?(Project)
+ project = project_id
+ else
+ Project.find_with_namespace(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
+
+ def avatar_icon(user_email = '', size = nil)
+ user = User.find_by(email: user_email)
+
+ if user
+ user.avatar_url(size) || default_avatar
+ else
+ gravatar_icon(user_email, size)
+ end
+ end
+
+ def gravatar_icon(user_email = '', size = nil)
+ GravatarService.new.execute(user_email, size) ||
+ default_avatar
+ end
+
+ def default_avatar
+ image_path('no_avatar.png')
+ end
+
+ def last_commit(project)
+ if project.repo_exists?
+ time_ago_with_tooltip(project.repository.commit.committed_date)
+ else
+ 'Never'
+ end
+ rescue
+ 'Never'
+ end
+
+ def grouped_options_refs
+ repository = @project.repository
+
+ options = [
+ ['Branches', repository.branch_names],
+ ['Tags', VersionSorter.rsort(repository.tag_names)]
+ ]
+
+ # If reference is commit id - we should add it to branch/tag selectbox
+ if(@ref && !options.flatten.include?(@ref) &&
+ @ref =~ /\A[0-9a-zA-Z]{6,52}\z/)
+ options << ['Commit', [@ref]]
+ end
+
+ grouped_options_for_select(options, @ref || @project.default_branch)
+ end
+
+ def emoji_autocomplete_source
+ # should be an array of strings
+ # so to_s can be called, because it is sufficient and to_json is too slow
+ Emoji.names.to_s
+ end
+
+ # Define whenever show last push event
+ # with suggestion to create MR
+ def show_last_push_widget?(event)
+ # Skip if event is not about added or modified non-master branch
+ return false unless event && event.last_push_to_non_root? && !event.rm_ref?
+
+ project = event.project
+
+ # Skip if project repo is empty or MR disabled
+ return false unless project && !project.empty_repo? && project.merge_requests_enabled
+
+ # Skip if user already created appropriate MR
+ return false if project.merge_requests.where(source_branch: event.branch_name).opened.any?
+
+ # Skip if user removed branch right after that
+ return false unless project.repository.branch_names.include?(event.branch_name)
+
+ true
+ end
+
+ def hexdigest(string)
+ Digest::SHA1.hexdigest string
+ end
+
+ def simple_sanitize(str)
+ sanitize(str, tags: %w(a span))
+ end
+
+ def body_data_page
+ path = controller.controller_path.split('/')
+ namespace = path.first if path.second
+
+ [namespace, controller.controller_name, controller.action_name].compact.join(':')
+ end
+
+ # shortcut for gitlab config
+ def gitlab_config
+ Gitlab.config.gitlab
+ end
+
+ # shortcut for gitlab extra config
+ def extra_config
+ Gitlab.config.extra
+ end
+
+ def search_placeholder
+ if @project && @project.persisted?
+ 'Search in this project'
+ elsif @snippet || @snippets || @show_snippets
+ 'Search snippets'
+ elsif @group && @group.persisted?
+ 'Search in this group'
+ else
+ 'Search'
+ end
+ end
+
+ def broadcast_message
+ BroadcastMessage.current
+ end
+
+ # Render a `time` element with Javascript-based relative date and tooltip
+ #
+ # time - Time object
+ # placement - Tooltip placement String (default: "top")
+ # html_class - Custom class for `time` element (default: "time_ago")
+ # skip_js - When true, exclude the `script` tag (default: false)
+ #
+ # By default also includes a `script` element with Javascript necessary to
+ # initialize the `timeago` jQuery extension. If this method is called many
+ # times, for example rendering hundreds of commits, it's advisable to disable
+ # this behavior using the `skip_js` argument and re-initializing `timeago`
+ # manually once all of the elements have been rendered.
+ #
+ # A `js-timeago` class is always added to the element, even when a custom
+ # `html_class` argument is provided.
+ #
+ # 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",
+ datetime: time.getutc.iso8601,
+ title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'),
+ data: { toggle: 'tooltip', placement: placement }
+
+ element += javascript_tag "$('.js-timeago').timeago()" unless skip_js
+
+ element
+ end
+
+ def render_markup(file_name, file_content)
+ if gitlab_markdown?(file_name)
+ Haml::Helpers.preserve(markdown(file_content))
+ elsif asciidoc?(file_name)
+ asciidoc(file_content)
+ elsif plain?(file_name)
+ content_tag :pre, class: 'plain-readme' do
+ file_content
+ end
+ else
+ GitHub::Markup.render(file_name, file_content).
+ force_encoding(file_content.encoding).html_safe
+ end
+ rescue RuntimeError
+ simple_format(file_content)
+ end
+
+ def plain?(filename)
+ Gitlab::MarkupHelper.plain?(filename)
+ end
+
+ def markup?(filename)
+ Gitlab::MarkupHelper.markup?(filename)
+ end
+
+ def gitlab_markdown?(filename)
+ Gitlab::MarkupHelper.gitlab_markdown?(filename)
+ end
+
+ def asciidoc?(filename)
+ Gitlab::MarkupHelper.asciidoc?(filename)
+ end
+
+ def promo_host
+ 'about.gitlab.com'
+ end
+
+ def promo_url
+ 'https://' + promo_host
+ end
+
+ def page_filter_path(options = {})
+ without = options.delete(:without)
+
+ exist_opts = {
+ state: params[:state],
+ scope: params[:scope],
+ label_name: params[:label_name],
+ milestone_id: params[:milestone_id],
+ assignee_id: params[:assignee_id],
+ author_id: params[:author_id],
+ sort: params[:sort],
+ }
+
+ options = exist_opts.merge(options)
+
+ if without.present?
+ without.each do |key|
+ options.delete(key)
+ end
+ end
+
+ path = request.path
+ path << "?#{options.to_param}"
+ path
+ end
+
+ def outdated_browser?
+ browser.ie? && browser.version.to_i < 10
+ end
+
+ def path_to_key(key, admin = false)
+ if admin
+ admin_user_key_path(@user, key)
+ else
+ profile_key_path(key)
+ end
+ end
+
+ def state_filters_text_for(entity, project)
+ titles = {
+ opened: "Open"
+ }
+
+ entity_title = titles[entity] || entity.to_s.humanize
+
+ count =
+ if project.nil?
+ nil
+ elsif current_controller?(:issues)
+ project.issues.send(entity).count
+ elsif current_controller?(:merge_requests)
+ project.merge_requests.send(entity).count
+ end
+
+ html = content_tag :span, entity_title
+
+ if count.present?
+ html += " "
+ html += content_tag :span, number_with_delimiter(count), class: 'badge'
+ end
+
+ html.html_safe
+ end
+ end
+end
diff --git a/app/helpers/gitlab/application_settings_helper.rb b/app/helpers/gitlab/application_settings_helper.rb
new file mode 100644
index 00000000000..7132d3dcdad
--- /dev/null
+++ b/app/helpers/gitlab/application_settings_helper.rb
@@ -0,0 +1,61 @@
+module Gitlab
+ module ApplicationSettingsHelper
+ def gravatar_enabled?
+ current_application_settings.gravatar_enabled?
+ end
+
+ def twitter_sharing_enabled?
+ current_application_settings.twitter_sharing_enabled?
+ end
+
+ def signup_enabled?
+ current_application_settings.signup_enabled?
+ end
+
+ def signin_enabled?
+ current_application_settings.signin_enabled?
+ end
+
+ def extra_sign_in_text
+ current_application_settings.sign_in_text
+ end
+
+ def user_oauth_applications?
+ current_application_settings.user_oauth_applications
+ end
+
+ # Return a group of checkboxes that use Bootstrap's button plugin for a
+ # toggle button effect.
+ def restricted_level_checkboxes(help_block_id)
+ Gitlab::VisibilityLevel.options.map do |name, level|
+ checked = restricted_visibility_levels(true).include?(level)
+ css_class = 'btn'
+ css_class += ' active' if checked
+ checkbox_name = 'application_setting[restricted_visibility_levels][]'
+
+ label_tag(checkbox_name, class: css_class) do
+ check_box_tag(checkbox_name, level, checked,
+ autocomplete: 'off',
+ 'aria-describedby' => help_block_id) + name
+ end
+ end
+ end
+
+ # Return a group of checkboxes that use Bootstrap's button plugin for a
+ # toggle button effect.
+ def import_sources_checkboxes(help_block_id)
+ Gitlab::ImportSources.options.map do |name, source|
+ checked = current_application_settings.import_sources.include?(source)
+ css_class = 'btn'
+ css_class += ' active' if checked
+ checkbox_name = 'application_setting[import_sources][]'
+
+ label_tag(checkbox_name, class: css_class) do
+ check_box_tag(checkbox_name, source, checked,
+ autocomplete: 'off',
+ 'aria-describedby' => help_block_id) + name
+ end
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/auth_helper.rb b/app/helpers/gitlab/auth_helper.rb
new file mode 100644
index 00000000000..fbd52dbca3d
--- /dev/null
+++ b/app/helpers/gitlab/auth_helper.rb
@@ -0,0 +1,52 @@
+module Gitlab
+ module AuthHelper
+ PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze
+ FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos'].freeze
+
+ def ldap_enabled?
+ Gitlab.config.ldap.enabled
+ end
+
+ def provider_has_icon?(name)
+ PROVIDERS_WITH_ICONS.include?(name.to_s)
+ end
+
+ def auth_providers
+ Gitlab::OAuth::Provider.providers
+ end
+
+ def label_for_provider(name)
+ Gitlab::OAuth::Provider.label_for(name)
+ end
+
+ def form_based_provider?(name)
+ FORM_BASED_PROVIDERS.any? { |pattern| pattern === name.to_s }
+ end
+
+ def form_based_providers
+ auth_providers.select { |provider| form_based_provider?(provider) }
+ end
+
+ def button_based_providers
+ auth_providers.reject { |provider| form_based_provider?(provider) }
+ end
+
+ def provider_image_tag(provider, size = 64)
+ label = label_for_provider(provider)
+
+ if provider_has_icon?(provider)
+ file_name = "#{provider.to_s.split('_').first}_#{size}.png"
+
+ image_tag("auth_buttons/#{file_name}", alt: label, title: "Sign in with #{label}")
+ else
+ label
+ end
+ end
+
+ def auth_active?(provider)
+ current_user.identities.exists?(provider: provider.to_s)
+ end
+
+ extend self
+ end
+end
diff --git a/app/helpers/gitlab/blob_helper.rb b/app/helpers/gitlab/blob_helper.rb
new file mode 100644
index 00000000000..8b53ba8b54f
--- /dev/null
+++ b/app/helpers/gitlab/blob_helper.rb
@@ -0,0 +1,76 @@
+module Gitlab
+ module BlobHelper
+ def highlight(blob_name, blob_content, nowrap: false, continue: false)
+ @formatter ||= Rouge::Formatters::HTMLGitlab.new(
+ nowrap: nowrap,
+ cssclass: 'code highlight',
+ lineanchors: true,
+ lineanchorsid: 'LC'
+ )
+
+ begin
+ @lexer ||= Rouge::Lexer.guess(filename: blob_name, source: blob_content).new
+ result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe
+ rescue
+ @lexer = Rouge::Lexers::PlainText
+ result = @formatter.format(@lexer.lex(blob_content)).html_safe
+ end
+
+ result
+ end
+
+ def no_highlight_files
+ %w(credits changelog news copying copyright license authors)
+ end
+
+ def edit_blob_link(project, ref, path, options = {})
+ blob =
+ begin
+ project.repository.blob_at(ref, path)
+ rescue
+ nil
+ end
+
+ if blob && blob.text?
+ text = 'Edit'
+ after = options[:after] || ''
+ from_mr = options[:from_merge_request_id]
+ link_opts = {}
+ link_opts[:from_merge_request_id] = from_mr if from_mr
+ cls = 'btn btn-small'
+ if allowed_tree_edit?(project, ref)
+ link_to(text,
+ namespace_project_edit_blob_path(project.namespace, project,
+ tree_join(ref, path),
+ link_opts),
+ class: cls
+ )
+ else
+ content_tag :span, text, class: cls + ' disabled'
+ end + after.html_safe
+ else
+ ''
+ end
+ end
+
+ def leave_edit_message
+ "Leave edit mode?\nAll unsaved changes will be lost."
+ end
+
+ def editing_preview_title(filename)
+ if Gitlab::MarkupHelper.previewable?(filename)
+ 'Preview'
+ else
+ 'Preview changes'
+ end
+ end
+
+ # Return an image icon depending on the file mode and extension
+ #
+ # mode - File unix mode
+ # mode - File name
+ def blob_icon(mode, name)
+ icon("#{file_type_icon_class('file', mode, name)} fw")
+ end
+ end
+end
diff --git a/app/helpers/gitlab/branches_helper.rb b/app/helpers/gitlab/branches_helper.rb
new file mode 100644
index 00000000000..ecc56002e84
--- /dev/null
+++ b/app/helpers/gitlab/branches_helper.rb
@@ -0,0 +1,19 @@
+module Gitlab
+ module BranchesHelper
+ def can_remove_branch?(project, branch_name)
+ if project.protected_branch? branch_name
+ false
+ elsif branch_name == project.repository.root_ref
+ false
+ else
+ can?(current_user, :push_code, project)
+ end
+ end
+
+ def can_push_branch?(project, branch_name)
+ return false unless project.repository.branch_names.include?(branch_name)
+
+ ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
+ end
+ end
+end
diff --git a/app/helpers/gitlab/broadcast_messages_helper.rb b/app/helpers/gitlab/broadcast_messages_helper.rb
new file mode 100644
index 00000000000..93f0b0ec5ae
--- /dev/null
+++ b/app/helpers/gitlab/broadcast_messages_helper.rb
@@ -0,0 +1,18 @@
+module Gitlab
+ module BroadcastMessagesHelper
+ def broadcast_styling(broadcast_message)
+ styling = ''
+
+ if broadcast_message.color.present?
+ styling << "background-color: #{broadcast_message.color}"
+ styling << '; ' if broadcast_message.font.present?
+ end
+
+ if broadcast_message.font.present?
+ styling << "color: #{broadcast_message.font}"
+ end
+
+ styling
+ end
+ end
+end
diff --git a/app/helpers/gitlab/commits_helper.rb b/app/helpers/gitlab/commits_helper.rb
new file mode 100644
index 00000000000..8a3de838b39
--- /dev/null
+++ b/app/helpers/gitlab/commits_helper.rb
@@ -0,0 +1,185 @@
+# encoding: utf-8
+module Gitlab
+ module CommitsHelper
+ # Returns a link to the commit author. If the author has a matching user and
+ # is a member of the current @project it will link to the team member page.
+ # Otherwise it will link to the author email as specified in the commit.
+ #
+ # options:
+ # avatar: true will prepend the avatar image
+ # size: size of the avatar image in px
+ def commit_author_link(commit, options = {})
+ commit_person_link(commit, options.merge(source: :author))
+ end
+
+ # Just like #author_link but for the committer.
+ def commit_committer_link(commit, options = {})
+ commit_person_link(commit, options.merge(source: :committer))
+ end
+
+ def image_diff_class(diff)
+ if diff.deleted_file
+ "deleted"
+ elsif diff.new_file
+ "added"
+ else
+ nil
+ end
+ end
+
+ def commit_to_html(commit, project, inline = true)
+ template = inline ? "inline_commit" : "commit"
+ escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil?
+ end
+
+ # Breadcrumb links for a Project and, if applicable, a tree path
+ def commits_breadcrumbs
+ return unless @project && @ref
+
+ # Add the root project link and the arrow icon
+ crumbs = content_tag(:li) do
+ link_to(
+ @project.path,
+ namespace_project_commits_path(@project.namespace, @project, @ref)
+ )
+ end
+
+ if @path
+ parts = @path.split('/')
+
+ parts.each_with_index do |part, i|
+ crumbs << content_tag(:li) do
+ # The text is just the individual part, but the link needs all the parts before it
+ link_to(
+ part,
+ namespace_project_commits_path(
+ @project.namespace,
+ @project,
+ tree_join(@ref, parts[0..i].join('/'))
+ )
+ )
+ end
+ end
+ end
+
+ crumbs.html_safe
+ end
+
+ # Return Project default branch, if it present in array
+ # Else - first branch in array (mb last actual branch)
+ def commit_default_branch(project, branches)
+ branches.include?(project.default_branch) ? branches.delete(project.default_branch) : branches.pop
+ end
+
+ # Returns the sorted alphabetically links to branches, separated by a comma
+ def commit_branches_links(project, branches)
+ branches.sort.map do |branch|
+ link_to(
+ namespace_project_tree_path(project.namespace, project, branch)
+ ) do
+ content_tag :span, class: 'label label-gray' do
+ icon('code-fork') + ' ' + branch
+ end
+ end
+ end.join(" ").html_safe
+ end
+
+ # Returns the sorted links to tags, separated by a comma
+ def commit_tags_links(project, tags)
+ sorted = VersionSorter.rsort(tags)
+ sorted.map do |tag|
+ link_to(
+ namespace_project_commits_path(project.namespace, project,
+ project.repository.find_tag(tag).name)
+ ) do
+ content_tag :span, class: 'label label-gray' do
+ icon('tag') + ' ' + tag
+ end
+ end
+ end.join(" ").html_safe
+ end
+
+ def link_to_browse_code(project, commit)
+ if current_controller?(:projects, :commits)
+ if @repo.blob_at(commit.id, @path)
+ return link_to(
+ "Browse File »",
+ namespace_project_blob_path(project.namespace, project,
+ tree_join(commit.id, @path)),
+ class: "pull-right"
+ )
+ elsif @path.present?
+ return link_to(
+ "Browse Dir »",
+ namespace_project_tree_path(project.namespace, project,
+ tree_join(commit.id, @path)),
+ class: "pull-right"
+ )
+ end
+ end
+ link_to(
+ "Browse Code »",
+ namespace_project_tree_path(project.namespace, project, commit),
+ class: "pull-right"
+ )
+ end
+
+ protected
+
+ # Private: Returns a link to a person. If the person has a matching user and
+ # is a member of the current @project it will link to the team member page.
+ # Otherwise it will link to the person email as specified in the commit.
+ #
+ # options:
+ # source: one of :author or :committer
+ # avatar: true will prepend the avatar image
+ # size: size of the avatar image in px
+ def commit_person_link(commit, options = {})
+ user = commit.send(options[:source])
+
+ source_name = clean(commit.send "#{options[:source]}_name".to_sym)
+ source_email = clean(commit.send "#{options[:source]}_email".to_sym)
+
+ person_name = user.try(:name) || source_name
+ person_email = user.try(:email) || source_email
+
+ text =
+ if options[:avatar]
+ avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
+ %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{person_name}</span>}
+ else
+ person_name
+ end
+
+ options = {
+ class: "commit-#{options[:source]}-link has_tooltip",
+ data: { :'original-title' => sanitize(source_email) }
+ }
+
+ if user.nil?
+ mail_to(source_email, text.html_safe, options)
+ else
+ link_to(text.html_safe, user_path(user), options)
+ end
+ end
+
+ def view_file_btn(commit_sha, diff, project)
+ link_to(
+ namespace_project_blob_path(project.namespace, project,
+ tree_join(commit_sha, diff.new_path)),
+ class: 'btn btn-small view-file js-view-file'
+ ) do
+ raw('View file @') + content_tag(:span, commit_sha[0..6],
+ class: 'commit-short-id')
+ end
+ end
+
+ def truncate_sha(sha)
+ Commit.truncate_sha(sha)
+ end
+
+ def clean(string)
+ Sanitize.clean(string, remove_contents: true)
+ end
+ end
+end
diff --git a/app/helpers/gitlab/compare_helper.rb b/app/helpers/gitlab/compare_helper.rb
new file mode 100644
index 00000000000..407d25d3102
--- /dev/null
+++ b/app/helpers/gitlab/compare_helper.rb
@@ -0,0 +1,23 @@
+module Gitlab
+ module CompareHelper
+ def create_mr_button?(from = params[:from], to = params[:to], project = @project)
+ from.present? &&
+ to.present? &&
+ from != to &&
+ project.merge_requests_enabled &&
+ project.repository.branch_names.include?(from) &&
+ project.repository.branch_names.include?(to)
+ end
+
+ def create_mr_path(from = params[:from], to = params[:to], project = @project)
+ new_namespace_project_merge_request_path(
+ project.namespace,
+ project,
+ merge_request: {
+ source_branch: to,
+ target_branch: from
+ }
+ )
+ end
+ end
+end
diff --git a/app/helpers/gitlab/dashboard_helper.rb b/app/helpers/gitlab/dashboard_helper.rb
new file mode 100644
index 00000000000..2211c93999e
--- /dev/null
+++ b/app/helpers/gitlab/dashboard_helper.rb
@@ -0,0 +1,11 @@
+module Gitlab
+ module DashboardHelper
+ def assigned_issues_dashboard_path
+ issues_dashboard_path(assignee_id: current_user.id)
+ end
+
+ def assigned_mrs_dashboard_path
+ merge_requests_dashboard_path(assignee_id: current_user.id)
+ end
+ end
+end
diff --git a/app/helpers/gitlab/diff_helper.rb b/app/helpers/gitlab/diff_helper.rb
new file mode 100644
index 00000000000..02907eb80f3
--- /dev/null
+++ b/app/helpers/gitlab/diff_helper.rb
@@ -0,0 +1,172 @@
+module Gitlab
+ module DiffHelper
+ def allowed_diff_size
+ if diff_hard_limit_enabled?
+ Commit::DIFF_HARD_LIMIT_FILES
+ else
+ Commit::DIFF_SAFE_FILES
+ end
+ end
+
+ def allowed_diff_lines
+ if diff_hard_limit_enabled?
+ Commit::DIFF_HARD_LIMIT_LINES
+ else
+ Commit::DIFF_SAFE_LINES
+ end
+ end
+
+ def safe_diff_files(diffs)
+ 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)
+ 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
+ end
+
+ def generate_line_code(file_path, line)
+ Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
+ end
+
+ def parallel_diff(diff_file, index)
+ lines = []
+ skip_next = false
+
+ # Building array of lines
+ #
+ # [
+ # left_type, left_line_number, left_line_content, left_line_code,
+ # right_line_type, right_line_number, right_line_content, right_line_code
+ # ]
+ #
+ diff_file.diff_lines.each do |line|
+
+ full_line = line.text
+ type = line.type
+ line_code = generate_line_code(diff_file.file_path, line)
+ line_new = line.new_pos
+ line_old = line.old_pos
+
+ next_line = diff_file.next_line(line.index)
+
+ if next_line
+ next_line_code = generate_line_code(diff_file.file_path, next_line)
+ next_type = next_line.type
+ next_line = next_line.text
+ end
+
+ if type == 'match' || type.nil?
+ # line in the right panel is the same as in the left one
+ line = [type, line_old, full_line, line_code, type, line_new, full_line, line_code]
+ lines.push(line)
+ elsif type == 'old'
+ if next_type == 'new'
+ # Left side has text removed, right side has text added
+ line = [type, line_old, full_line, line_code, next_type, line_new, next_line, next_line_code]
+ lines.push(line)
+ skip_next = true
+ elsif next_type == 'old' || next_type.nil?
+ # Left side has text removed, right side doesn't have any change
+ # No next line code, no new line number, no new line text
+ line = [type, line_old, full_line, line_code, next_type, nil, "&nbsp;", nil]
+ lines.push(line)
+ end
+ elsif type == 'new'
+ if skip_next
+ # Change has been already included in previous line so no need to do it again
+ skip_next = false
+ next
+ else
+ # Change is only on the right side, left side has no change
+ line = [nil, nil, "&nbsp;", line_code, type, line_new, full_line, line_code]
+ lines.push(line)
+ end
+ end
+ end
+ lines
+ end
+
+ def unfold_bottom_class(bottom)
+ (bottom) ? 'js-unfold-bottom' : ''
+ end
+
+ def unfold_class(unfold)
+ (unfold) ? 'unfold js-unfold' : ''
+ end
+
+ def diff_line_content(line)
+ if line.blank?
+ " &nbsp;"
+ else
+ line
+ end
+ end
+
+ def line_comments
+ @line_comments ||= @line_notes.select(&:active?).group_by(&:line_code)
+ end
+
+ def organize_comments(type_left, type_right, line_code_left, line_code_right)
+ comments_left = comments_right = nil
+
+ unless type_left.nil? && type_right == 'new'
+ comments_left = line_comments[line_code_left]
+ end
+
+ unless type_left.nil? && type_right.nil?
+ comments_right = line_comments[line_code_right]
+ end
+
+ [comments_left, comments_right]
+ end
+
+ def inline_diff_btn
+ params_copy = params.dup
+ params_copy[:view] = 'inline'
+ # Always use HTML to handle case where JSON diff rendered this button
+ params_copy.delete(:format)
+
+ link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn btn-sm active' : 'btn btn-sm') do
+ 'Inline'
+ end
+ end
+
+ def parallel_diff_btn
+ params_copy = params.dup
+ params_copy[:view] = 'parallel'
+ # Always use HTML to handle case where JSON diff rendered this button
+ params_copy.delete(:format)
+
+ link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active btn-sm' : 'btn btn-sm') do
+ 'Side-by-side'
+ end
+ end
+
+ def submodule_link(blob, ref, repository = @repository)
+ tree, commit = submodule_links(blob, ref, repository)
+ commit_id = if commit.nil?
+ blob.id[0..10]
+ else
+ link_to "#{blob.id[0..10]}", commit
+ end
+
+ [
+ content_tag(:span, link_to(truncate(blob.name, length: 40), tree)),
+ '@',
+ content_tag(:span, commit_id, class: 'monospace'),
+ ].join(' ').html_safe
+ end
+ end
+end
diff --git a/app/helpers/gitlab/emails_helper.rb b/app/helpers/gitlab/emails_helper.rb
new file mode 100644
index 00000000000..84f106dd536
--- /dev/null
+++ b/app/helpers/gitlab/emails_helper.rb
@@ -0,0 +1,59 @@
+module Gitlab
+ module EmailsHelper
+
+ # Google Actions
+ # https://developers.google.com/gmail/markup/reference/go-to-action
+ def email_action(url)
+ name = action_title(url)
+ if name
+ data = {
+ "@context" => "http://schema.org",
+ "@type" => "EmailMessage",
+ "action" => {
+ "@type" => "ViewAction",
+ "name" => name,
+ "url" => url,
+ }
+ }
+
+ content_tag :script, type: 'application/ld+json' do
+ data.to_json.html_safe
+ end
+ end
+ end
+
+ def action_title(url)
+ return unless url
+ ["merge_requests", "issues", "commit"].each do |action|
+ if url.split("/").include?(action)
+ return "View #{action.humanize.singularize}"
+ end
+ end
+ end
+
+ def color_email_diff(diffcontent)
+ formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', inline_theme: 'github')
+ lexer = Rouge::Lexers::Diff
+ raw formatter.format(lexer.lex(diffcontent))
+ end
+
+ def password_reset_token_valid_time
+ valid_hours = Devise.reset_password_within / 60 / 60
+ if valid_hours >= 24
+ unit = 'day'
+ valid_length = (valid_hours / 24).floor
+ else
+ unit = 'hour'
+ valid_length = valid_hours.floor
+ end
+
+ pluralize(valid_length, unit)
+ end
+
+ def reset_token_expire_message
+ link_tag = link_to('request a new one', new_user_password_url(user_email: @user.email))
+ msg = "This link is valid for #{password_reset_token_valid_time}. "
+ msg << "After it expires, you can #{link_tag}."
+ end
+ end
+end
diff --git a/app/helpers/gitlab/events_helper.rb b/app/helpers/gitlab/events_helper.rb
new file mode 100644
index 00000000000..65522dae533
--- /dev/null
+++ b/app/helpers/gitlab/events_helper.rb
@@ -0,0 +1,205 @@
+module Gitlab
+ module EventsHelper
+ def link_to_author(event)
+ author = event.author
+
+ if author
+ link_to author.name, user_path(author.username)
+ else
+ event.author_name
+ end
+ end
+
+ def event_action_name(event)
+ target = if event.target_type
+ if event.note?
+ event.note_target_type
+ else
+ event.target_type.titleize.downcase
+ end
+ else
+ 'project'
+ end
+
+ [event.action_name, target].join(" ")
+ end
+
+ def event_filter_link(key, tooltip)
+ key = key.to_s
+ active = 'active' if @event_filter.active?(key)
+ link_opts = {
+ class: 'event_filter_link',
+ id: "#{key}_event_filter",
+ title: "Filter by #{tooltip.downcase}",
+ data: { toggle: 'tooltip', placement: 'top' }
+ }
+
+ content_tag :li, class: "filter_icon #{active}" do
+ link_to request.path, link_opts do
+ icon(icon_for_event[key]) + content_tag(:span, ' ' + tooltip)
+ end
+ end
+ end
+
+ def icon_for_event
+ {
+ EventFilter.push => 'upload',
+ EventFilter.merged => 'check-square-o',
+ EventFilter.comments => 'comments',
+ EventFilter.team => 'user',
+ }
+ end
+
+ def event_feed_title(event)
+ words = []
+ words << event.author_name
+ words << event_action_name(event)
+
+ if event.push?
+ words << event.ref_type
+ words << event.ref_name
+ words << "at"
+ elsif event.commented?
+ if event.note_commit?
+ words << event.note_short_commit_id
+ else
+ words << "##{truncate event.note_target_iid}"
+ end
+ words << "at"
+ elsif event.target
+ words << "##{event.target_iid}:"
+ words << event.target.title if event.target.respond_to?(:title)
+ words << "at"
+ end
+
+ words << event.project_name
+
+ words.join(" ")
+ end
+
+ def event_feed_url(event)
+ if event.issue?
+ namespace_project_issue_url(event.project.namespace, event.project,
+ event.issue)
+ elsif event.merge_request?
+ namespace_project_merge_request_url(event.project.namespace,
+ event.project, event.merge_request)
+ elsif event.note? && event.note_commit?
+ namespace_project_commit_url(event.project.namespace, event.project,
+ event.note_target)
+ elsif event.note?
+ if event.note_target
+ if event.note_commit?
+ namespace_project_commit_path(event.project.namespace, event.project,
+ event.note_commit_id,
+ anchor: dom_id(event.target))
+ elsif event.note_project_snippet?
+ namespace_project_snippet_path(event.project.namespace,
+ event.project, event.note_target)
+ else
+ event_note_target_path(event)
+ end
+ end
+ elsif event.push?
+ if event.push_with_commits? && event.md_ref?
+ if event.commits_count > 1
+ namespace_project_compare_url(event.project.namespace, event.project,
+ from: event.commit_from, to:
+ event.commit_to)
+ else
+ namespace_project_commit_url(event.project.namespace, event.project,
+ id: event.commit_to)
+ end
+ else
+ namespace_project_commits_url(event.project.namespace, event.project,
+ event.ref_name)
+ end
+ end
+ end
+
+ def event_feed_summary(event)
+ if event.issue?
+ render "events/event_issue", issue: event.issue
+ elsif event.push?
+ render "events/event_push", event: event
+ elsif event.merge_request?
+ render "events/event_merge_request", merge_request: event.merge_request
+ elsif event.note?
+ render "events/event_note", note: event.note
+ end
+ end
+
+ def event_note_target_path(event)
+ if event.note? && event.note_commit?
+ namespace_project_commit_path(event.project.namespace, event.project,
+ event.note_target)
+ else
+ polymorphic_path([event.project.namespace.becomes(Namespace),
+ event.project, event.note_target],
+ anchor: dom_id(event.target))
+ end
+ end
+
+ def event_note_title_html(event)
+ if event.note_target
+ if event.note_commit?
+ link_to(
+ namespace_project_commit_path(event.project.namespace, event.project,
+ event.note_commit_id,
+ anchor: dom_id(event.target)),
+ class: "commit_short_id"
+ ) do
+ "#{event.note_target_type} #{event.note_short_commit_id}"
+ end
+ 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}"
+ end
+ else
+ link_to event_note_target_path(event) do
+ "#{event.note_target_type} ##{truncate event.note_target_iid}"
+ end
+ end
+ else
+ content_tag :strong do
+ "(deleted)"
+ end
+ end
+ end
+
+ def event_note(text, options = {})
+ text = first_line_in_markdown(text, 150, options)
+ sanitize(text, tags: %w(a img b pre code p span))
+ end
+
+ def event_commit_title(message)
+ escape_once(truncate(message.split("\n").first, length: 70))
+ rescue
+ "--broken encoding"
+ end
+
+ def event_to_atom(xml, event)
+ if event.proper?
+ xml.entry do
+ event_link = event_feed_url(event)
+ event_title = event_feed_title(event)
+ event_summary = event_feed_summary(event)
+
+ xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
+ xml.link href: event_link
+ xml.title truncate(event_title, length: 80)
+ xml.updated event.created_at.xmlschema
+ xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email)
+ xml.author do |author|
+ xml.name event.author_name
+ xml.email event.author_email
+ end
+
+ xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? }
+ end
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/explore_helper.rb b/app/helpers/gitlab/explore_helper.rb
new file mode 100644
index 00000000000..b8e0f482b94
--- /dev/null
+++ b/app/helpers/gitlab/explore_helper.rb
@@ -0,0 +1,19 @@
+module Gitlab
+ module ExploreHelper
+ def explore_projects_filter_path(options={})
+ exist_opts = {
+ sort: params[:sort],
+ scope: params[:scope],
+ group: params[:group],
+ tag: params[:tag],
+ visibility_level: params[:visibility_level],
+ }
+
+ options = exist_opts.merge(options)
+
+ path = explore_projects_path
+ path << "?#{options.to_param}"
+ path
+ end
+ end
+end
diff --git a/app/helpers/gitlab/external_wiki_helper.rb b/app/helpers/gitlab/external_wiki_helper.rb
new file mode 100644
index 00000000000..710cdc727d0
--- /dev/null
+++ b/app/helpers/gitlab/external_wiki_helper.rb
@@ -0,0 +1,13 @@
+module Gitlab
+ module ExternalWikiHelper
+ def get_project_wiki_path(project)
+ external_wiki_service = project.services.
+ select { |service| service.to_param == 'external_wiki' }.first
+ if external_wiki_service.present? && external_wiki_service.active?
+ external_wiki_service.properties['external_wiki_url']
+ else
+ namespace_project_wiki_path(project.namespace, project, :home)
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/git_helper.rb b/app/helpers/gitlab/git_helper.rb
new file mode 100644
index 00000000000..867b30b8c74
--- /dev/null
+++ b/app/helpers/gitlab/git_helper.rb
@@ -0,0 +1,7 @@
+module Gitlab
+ module GitHelper
+ def strip_gpg_signature(text)
+ text.gsub(/-----BEGIN PGP SIGNATURE-----(.*)-----END PGP SIGNATURE-----/m, "")
+ end
+ end
+end
diff --git a/app/helpers/gitlab/gitlab_markdown_helper.rb b/app/helpers/gitlab/gitlab_markdown_helper.rb
new file mode 100644
index 00000000000..265cb4672fe
--- /dev/null
+++ b/app/helpers/gitlab/gitlab_markdown_helper.rb
@@ -0,0 +1,195 @@
+require 'nokogiri'
+
+module Gitlab
+ module GitlabMarkdownHelper
+ include Gitlab::Markdown
+ include PreferencesHelper
+
+ # Use this in places where you would normally use link_to(gfm(...), ...).
+ #
+ # It solves a problem occurring with nested links (i.e.
+ # "<a>outer text <a>gfm ref</a> more outer text</a>"). This will not be
+ # interpreted as intended. Browsers will parse something like
+ # "<a>outer text </a><a>gfm ref</a> more outer text" (notice the last part is
+ # not linked any more). link_to_gfm corrects that. It wraps all parts to
+ # explicitly produce the correct linking behavior (i.e.
+ # "<a>outer text </a><a>gfm ref</a><a> more outer text</a>").
+ def link_to_gfm(body, url, html_options = {})
+ return "" if body.blank?
+
+ escaped_body = if body =~ /\A\<img/
+ body
+ else
+ escape_once(body)
+ end
+
+ gfm_body = gfm(escaped_body, {}, html_options)
+
+ fragment = Nokogiri::XML::DocumentFragment.parse(gfm_body)
+ if fragment.children.size == 1 && fragment.children[0].name == 'a'
+ # Fragment has only one node, and it's a link generated by `gfm`.
+ # Replace it with our requested link.
+ text = fragment.children[0].text
+ fragment.children[0].replace(link_to(text, url, html_options))
+ else
+ # Traverse the fragment's first generation of children looking for pure
+ # text, wrapping anything found in the requested link
+ fragment.children.each do |node|
+ next unless node.text?
+ node.replace(link_to(node.text, url, html_options))
+ end
+ end
+
+ fragment.to_html.html_safe
+ end
+
+ MARKDOWN_OPTIONS = {
+ no_intra_emphasis: true,
+ tables: true,
+ fenced_code_blocks: true,
+ strikethrough: true,
+ lax_spacing: true,
+ space_after_headers: true,
+ superscript: true,
+ footnotes: true
+ }.freeze
+
+ def markdown(text, options={})
+ unless @markdown && options == @options
+ @options = options
+
+ # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch
+ rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, options)
+
+ # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
+ @markdown = Redcarpet::Markdown.new(rend, MARKDOWN_OPTIONS)
+ end
+
+ @markdown.render(text).html_safe
+ end
+
+ def asciidoc(text)
+ Gitlab::Asciidoc.render(text, {
+ commit: @commit,
+ project: @project,
+ project_wiki: @project_wiki,
+ requested_path: @path,
+ ref: @ref
+ })
+ end
+
+ # Return the first line of +text+, up to +max_chars+, after parsing the line
+ # as Markdown. HTML tags in the parsed output are not counted toward the
+ # +max_chars+ limit. If the length limit falls within a tag's contents, then
+ # the tag contents are truncated without removing the closing tag.
+ def first_line_in_markdown(text, max_chars = nil, options = {})
+ md = markdown(text, options).strip
+
+ truncate_visible(md, max_chars || md.length) if md.present?
+ end
+
+ def render_wiki_content(wiki_page)
+ case wiki_page.format
+ when :markdown
+ markdown(wiki_page.content)
+ when :asciidoc
+ asciidoc(wiki_page.content)
+ else
+ wiki_page.formatted_content.html_safe
+ end
+ end
+
+ MARKDOWN_TIPS = [
+ "End a line with two or more spaces for a line-break, or soft-return",
+ "Inline code can be denoted by `surrounding it with backticks`",
+ "Blocks of code can be denoted by three backticks ``` or four leading spaces",
+ "Emoji can be added by :emoji_name:, for example :thumbsup:",
+ "Notify other participants using @user_name",
+ "Notify a specific group using @group_name",
+ "Notify the entire team using @all",
+ "Reference an issue using a hash, for example issue #123",
+ "Reference a merge request using an exclamation point, for example MR !123",
+ "Italicize words or phrases using *asterisks* or _underscores_",
+ "Bold words or phrases using **double asterisks** or __double underscores__",
+ "Strikethrough words or phrases using ~~two tildes~~",
+ "Make a bulleted list using + pluses, - minuses, or * asterisks",
+ "Denote blockquotes using > at the beginning of a line",
+ "Make a horizontal line using three or more hyphens ---, asterisks ***, or underscores ___"
+ ].freeze
+
+ # Returns a random markdown tip for use as a textarea placeholder
+ def random_markdown_tip
+ MARKDOWN_TIPS.sample
+ end
+
+ private
+
+ # Return +text+, truncated to +max_chars+ characters, excluding any HTML
+ # tags.
+ def truncate_visible(text, max_chars)
+ doc = Nokogiri::HTML.fragment(text)
+ content_length = 0
+ truncated = false
+
+ doc.traverse do |node|
+ if node.text? || node.content.empty?
+ if truncated
+ node.remove
+ next
+ end
+
+ # Handle line breaks within a node
+ if node.content.strip.lines.length > 1
+ node.content = "#{node.content.lines.first.chomp}..."
+ truncated = true
+ end
+
+ num_remaining = max_chars - content_length
+ if node.content.length > num_remaining
+ node.content = node.content.truncate(num_remaining)
+ truncated = true
+ end
+ content_length += node.content.length
+ end
+
+ truncated = truncate_if_block(node, truncated)
+ end
+
+ doc.to_html
+ end
+
+ # Used by #truncate_visible. If +node+ is the first block element, and the
+ # text hasn't already been truncated, then append "..." to the node contents
+ # and return true. Otherwise return false.
+ def truncate_if_block(node, truncated)
+ if node.element? && node.description.block? && !truncated
+ node.content = "#{node.content}..." if node.next_sibling
+ true
+ else
+ truncated
+ end
+ end
+
+ # Returns the text necessary to reference `entity` across projects
+ #
+ # project - Project to reference
+ # entity - Object that responds to `to_reference`
+ #
+ # Examples:
+ #
+ # cross_project_reference(project, project.issues.first)
+ # # => 'namespace1/project1#123'
+ #
+ # cross_project_reference(project, project.merge_requests.first)
+ # # => 'namespace1/project1!345'
+ #
+ # Returns a String
+ def cross_project_reference(project, entity)
+ if entity.respond_to?(:to_reference)
+ "#{project.to_reference}#{entity.to_reference}"
+ else
+ ''
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/gitlab_routing_helper.rb b/app/helpers/gitlab/gitlab_routing_helper.rb
new file mode 100644
index 00000000000..7f1e455d5de
--- /dev/null
+++ b/app/helpers/gitlab/gitlab_routing_helper.rb
@@ -0,0 +1,69 @@
+# Shorter routing method for project and project items
+# Since update to rails 4.1.9 we are now allowed to use `/` in project routing
+# so we use nested routing for project resources which include project and
+# project namespace. To avoid writing long methods every time we define shortcuts for
+# some of routing.
+#
+# For example instead of this:
+#
+# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.projects, merge_request)
+#
+# We can simply use shortcut:
+#
+# merge_request_path(merge_request)
+#
+module Gitlab
+ module GitlabRoutingHelper
+ def project_path(project, *args)
+ namespace_project_path(project.namespace, project, *args)
+ end
+
+ def activity_project_path(project, *args)
+ activity_namespace_project_path(project.namespace, project, *args)
+ end
+
+ def edit_project_path(project, *args)
+ edit_namespace_project_path(project.namespace, project, *args)
+ end
+
+ def issue_path(entity, *args)
+ namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args)
+ end
+
+ def merge_request_path(entity, *args)
+ namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args)
+ end
+
+ def milestone_path(entity, *args)
+ namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args)
+ end
+
+ def project_url(project, *args)
+ namespace_project_url(project.namespace, project, *args)
+ end
+
+ def edit_project_url(project, *args)
+ edit_namespace_project_url(project.namespace, project, *args)
+ end
+
+ def issue_url(entity, *args)
+ namespace_project_issue_url(entity.project.namespace, entity.project, entity, *args)
+ end
+
+ def merge_request_url(entity, *args)
+ namespace_project_merge_request_url(entity.project.namespace, entity.project, entity, *args)
+ end
+
+ def project_snippet_url(entity, *args)
+ namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args)
+ end
+
+ def toggle_subscription_path(entity, *args)
+ if entity.is_a?(Issue)
+ toggle_subscription_namespace_project_issue_path(entity.project.namespace, entity.project, entity)
+ else
+ toggle_subscription_namespace_project_merge_request_path(entity.project.namespace, entity.project, entity)
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/graph_helper.rb b/app/helpers/gitlab/graph_helper.rb
new file mode 100644
index 00000000000..047f5c19095
--- /dev/null
+++ b/app/helpers/gitlab/graph_helper.rb
@@ -0,0 +1,18 @@
+module Gitlab
+ module GraphHelper
+ def get_refs(repo, commit)
+ refs = ""
+ refs << commit.ref_names(repo).join(' ')
+
+ # append note count
+ refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0
+
+ refs
+ end
+
+ def parents_zip_spaces(parents, parent_spaces)
+ ids = parents.map { |p| p.id }
+ ids.zip(parent_spaces)
+ end
+ end
+end
diff --git a/app/helpers/gitlab/groups_helper.rb b/app/helpers/gitlab/groups_helper.rb
new file mode 100644
index 00000000000..8172c617249
--- /dev/null
+++ b/app/helpers/gitlab/groups_helper.rb
@@ -0,0 +1,35 @@
+module Gitlab
+ module GroupsHelper
+ def remove_user_from_group_message(group, member)
+ if member.user
+ "Are you sure you want to remove \"#{member.user.name}\" from \"#{group.name}\"?"
+ else
+ "Are you sure you want to revoke the invitation for \"#{member.invite_email}\" to join \"#{group.name}\"?"
+ end
+ end
+
+ def leave_group_message(group)
+ "Are you sure you want to leave \"#{group}\" group?"
+ end
+
+ def should_user_see_group_roles?(user, group)
+ if user
+ user.is_admin? || group.members.exists?(user_id: user.id)
+ else
+ false
+ end
+ end
+
+ def group_icon(group)
+ if group.is_a?(String)
+ group = Group.find_by(path: group)
+ end
+
+ if group && group.avatar.present?
+ group.avatar.url
+ else
+ image_path('no_group_avatar.png')
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/icons_helper.rb b/app/helpers/gitlab/icons_helper.rb
new file mode 100644
index 00000000000..e815d237bb1
--- /dev/null
+++ b/app/helpers/gitlab/icons_helper.rb
@@ -0,0 +1,87 @@
+module Gitlab
+ module IconsHelper
+ include FontAwesome::Rails::IconHelper
+
+ # Creates an icon tag given icon name(s) and possible icon modifiers.
+ #
+ # Right now this method simply delegates directly to `fa_icon` from the
+ # font-awesome-rails gem, but should we ever use a different icon pack in the
+ # future we won't have to change hundreds of method calls.
+ def icon(names, options = {})
+ fa_icon(names, options)
+ end
+
+ def spinner(text = nil, visible = false)
+ css_class = 'loading'
+ css_class << ' hide' unless visible
+
+ content_tag :div, class: css_class do
+ icon('spinner spin') + text
+ end
+ end
+
+ def boolean_to_icon(value)
+ if value
+ icon('circle', class: 'cgreen')
+ else
+ icon('power-off', class: 'clgray')
+ end
+ end
+
+ def public_icon
+ icon('globe fw')
+ end
+
+ def internal_icon
+ icon('shield fw')
+ end
+
+ def private_icon
+ icon('lock fw')
+ end
+
+ def file_type_icon_class(type, mode, name)
+ if type == 'folder'
+ icon_class = 'folder'
+ elsif mode == '120000'
+ icon_class = 'share'
+ else
+ # Guess which icon to choose based on file extension.
+ # If you think a file extension is missing, feel free to add it on PR
+
+ case File.extname(name).downcase
+ when '.pdf'
+ icon_class = 'file-pdf-o'
+ when '.jpg', '.jpeg', '.jif', '.jfif',
+ '.jp2', '.jpx', '.j2k', '.j2c',
+ '.png', '.gif', '.tif', '.tiff',
+ '.svg', '.ico', '.bmp'
+ icon_class = 'file-image-o'
+ when '.zip', '.zipx', '.tar', '.gz', '.bz', '.bzip',
+ '.xz', '.rar', '.7z'
+ icon_class = 'file-archive-o'
+ when '.mp3', '.wma', '.ogg', '.oga', '.wav', '.flac', '.aac'
+ icon_class = 'file-audio-o'
+ when '.mp4', '.m4p', '.m4v',
+ '.mpg', '.mp2', '.mpeg', '.mpe', '.mpv',
+ '.mpg', '.mpeg', '.m2v',
+ '.avi', '.mkv', '.flv', '.ogv', '.mov',
+ '.3gp', '.3g2'
+ icon_class = 'file-video-o'
+ when '.doc', '.dot', '.docx', '.docm', '.dotx', '.dotm', '.docb'
+ icon_class = 'file-word-o'
+ when '.xls', '.xlt', '.xlm', '.xlsx', '.xlsm', '.xltx', '.xltm',
+ '.xlsb', '.xla', '.xlam', '.xll', '.xlw'
+ icon_class = 'file-excel-o'
+ when '.ppt', '.pot', '.pps', '.pptx', '.pptm', '.potx', '.potm',
+ '.ppam', '.ppsx', '.ppsm', '.sldx', '.sldm'
+ icon_class = 'file-powerpoint-o'
+ else
+ icon_class = 'file-text-o'
+ end
+ end
+
+ icon_class
+ end
+ end
+end
diff --git a/app/helpers/gitlab/issues_helper.rb b/app/helpers/gitlab/issues_helper.rb
new file mode 100644
index 00000000000..67238926555
--- /dev/null
+++ b/app/helpers/gitlab/issues_helper.rb
@@ -0,0 +1,90 @@
+module Gitlab
+ module IssuesHelper
+ def issue_css_classes(issue)
+ classes = "issue"
+ classes << " closed" if issue.closed?
+ classes << " today" if issue.today?
+ classes
+ end
+
+ # Returns an OpenStruct object suitable for use by <tt>options_from_collection_for_select</tt>
+ # to allow filtering issues by an unassigned User or Milestone
+ def unassigned_filter
+ # Milestone uses :title, Issue uses :name
+ OpenStruct.new(id: 0, title: 'None (backlog)', name: 'Unassigned')
+ end
+
+ def url_for_project_issues(project = @project, options = {})
+ return '' if project.nil?
+
+ if options[:only_path]
+ project.issues_tracker.project_path
+ else
+ project.issues_tracker.project_url
+ end
+ end
+
+ def url_for_new_issue(project = @project, options = {})
+ return '' if project.nil?
+
+ if options[:only_path]
+ project.issues_tracker.new_issue_path
+ else
+ project.issues_tracker.new_issue_url
+ end
+ end
+
+ def url_for_issue(issue_iid, project = @project, options = {})
+ return '' if project.nil?
+
+ if options[:only_path]
+ project.issues_tracker.issue_path(issue_iid)
+ else
+ project.issues_tracker.issue_url(issue_iid)
+ end
+ end
+
+ def bulk_update_milestone_options
+ options_for_select([['None (backlog)', -1]]) +
+ options_from_collection_for_select(project_active_milestones, 'id',
+ 'title', params[:milestone_id])
+ end
+
+ def milestone_options(object)
+ options_from_collection_for_select(object.project.milestones.active,
+ 'id', 'title', object.milestone_id)
+ end
+
+ def issue_box_class(item)
+ if item.respond_to?(:expired?) && item.expired?
+ 'issue-box-expired'
+ elsif item.respond_to?(:merged?) && item.merged?
+ 'issue-box-merged'
+ elsif item.closed?
+ 'issue-box-closed'
+ else
+ 'issue-box-open'
+ end
+ end
+
+ def issue_to_atom(xml, issue)
+ xml.entry do
+ xml.id namespace_project_issue_url(issue.project.namespace,
+ issue.project, issue)
+ xml.link href: namespace_project_issue_url(issue.project.namespace,
+ issue.project, issue)
+ xml.title truncate(issue.title, length: 80)
+ xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
+ xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email)
+ xml.author do |author|
+ xml.name issue.author_name
+ xml.email issue.author_email
+ end
+ xml.summary issue.title
+ end
+ end
+
+ # Required for Gitlab::Markdown::IssueReferenceFilter
+ module_function :url_for_issue
+ end
+end
diff --git a/app/helpers/gitlab/labels_helper.rb b/app/helpers/gitlab/labels_helper.rb
new file mode 100644
index 00000000000..aa16d71f42c
--- /dev/null
+++ b/app/helpers/gitlab/labels_helper.rb
@@ -0,0 +1,103 @@
+module Gitlab
+ module LabelsHelper
+ include ActionView::Helpers::TagHelper
+
+ # Link to a Label
+ #
+ # label - Label object to link to
+ # project - Project object which will be used as the context for the label's
+ # link. If omitted, defaults to `@project`, or the label's own
+ # project.
+ # block - An optional block that will be passed to `link_to`, forming the
+ # body of the link element. If omitted, defaults to
+ # `render_colored_label`.
+ #
+ # Examples:
+ #
+ # # Allow the generated link to use the label's own project
+ # link_to_label(label)
+ #
+ # # Force the generated link to use @project
+ # @project = Project.first
+ # link_to_label(label)
+ #
+ # # Force the generated link to use a provided project
+ # link_to_label(label, project: Project.last)
+ #
+ # # Customize link body with a block
+ # link_to_label(label) { "My Custom Label Text" }
+ #
+ # Returns a String
+ def link_to_label(label, project: nil, &block)
+ project ||= @project || label.project
+ link = namespace_project_issues_path(project.namespace, project,
+ label_name: label.name)
+
+ if block_given?
+ link_to link, &block
+ else
+ link_to render_colored_label(label), link
+ end
+ end
+
+ def project_label_names
+ @project.labels.pluck(:title)
+ end
+
+ def render_colored_label(label)
+ 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.html_safe
+ end
+
+ def suggested_colors
+ [
+ '#0033CC',
+ '#428BCA',
+ '#44AD8E',
+ '#A8D695',
+ '#5CB85C',
+ '#69D100',
+ '#004E00',
+ '#34495E',
+ '#7F8C8D',
+ '#A295D6',
+ '#5843AD',
+ '#8E44AD',
+ '#FFECDB',
+ '#AD4363',
+ '#D10069',
+ '#CC0033',
+ '#FF0000',
+ '#D9534F',
+ '#D1D100',
+ '#F0AD4E',
+ '#AD8D43'
+ ]
+ end
+
+ def text_color_for_bg(bg_color)
+ r, g, b = bg_color.slice(1,7).scan(/.{2}/).map(&:hex)
+
+ if (r + g + b) > 500
+ '#333333'
+ else
+ '#FFFFFF'
+ end
+ end
+
+ def project_labels_options(project)
+ options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name])
+ end
+
+ # Required for Gitlab::Markdown::LabelReferenceFilter
+ module_function :render_colored_label, :text_color_for_bg, :escape_once
+ end
+end
diff --git a/app/helpers/gitlab/merge_requests_helper.rb b/app/helpers/gitlab/merge_requests_helper.rb
new file mode 100644
index 00000000000..361f6b2fdac
--- /dev/null
+++ b/app/helpers/gitlab/merge_requests_helper.rb
@@ -0,0 +1,76 @@
+module Gitlab
+ module MergeRequestsHelper
+ def new_mr_path_from_push_event(event)
+ target_project = event.project.forked_from_project || event.project
+ new_namespace_project_merge_request_path(
+ event.project.namespace,
+ event.project,
+ new_mr_from_push_event(event, target_project)
+ )
+ end
+
+ def new_mr_path_for_fork_from_push_event(event)
+ new_namespace_project_merge_request_path(
+ event.project.namespace,
+ event.project,
+ new_mr_from_push_event(event, event.project.forked_from_project)
+ )
+ end
+
+ def new_mr_from_push_event(event, target_project)
+ {
+ merge_request: {
+ source_project_id: event.project.id,
+ target_project_id: target_project.id,
+ source_branch: event.branch_name,
+ target_branch: target_project.repository.root_ref
+ }
+ }
+ end
+
+ def mr_css_classes(mr)
+ classes = "merge-request"
+ classes << " closed" if mr.closed?
+ classes << " merged" if mr.merged?
+ classes
+ end
+
+ def ci_build_details_path(merge_request)
+ merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch)
+ end
+
+ def merge_path_description(merge_request, separator)
+ if merge_request.for_fork?
+ "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}"
+ else
+ "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}"
+ end
+ end
+
+ def issues_sentence(issues)
+ issues.map { |i| "##{i.iid}" }.to_sentence
+ end
+
+ def mr_change_branches_path(merge_request)
+ new_namespace_project_merge_request_path(
+ @project.namespace, @project,
+ merge_request: {
+ source_project_id: @merge_request.source_project_id,
+ target_project_id: @merge_request.target_project_id,
+ source_branch: @merge_request.source_branch,
+ target_branch: nil
+ }
+ )
+ end
+
+ def source_branch_with_namespace(merge_request)
+ if merge_request.for_fork?
+ namespace = link_to(merge_request.source_project_namespace,
+ project_path(merge_request.source_project))
+ namespace + ":#{merge_request.source_branch}"
+ else
+ merge_request.source_branch
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/milestones_helper.rb b/app/helpers/gitlab/milestones_helper.rb
new file mode 100644
index 00000000000..116967d4946
--- /dev/null
+++ b/app/helpers/gitlab/milestones_helper.rb
@@ -0,0 +1,38 @@
+module Gitlab
+ module MilestonesHelper
+ def milestones_filter_path(opts = {})
+ if @project
+ namespace_project_milestones_path(@project.namespace, @project, opts)
+ elsif @group
+ group_milestones_path(@group, opts)
+ else
+ dashboard_milestones_path(opts)
+ end
+ end
+
+ def milestone_progress_bar(milestone)
+ options = {
+ class: 'progress-bar progress-bar-success',
+ style: "width: #{milestone.percent_complete}%;"
+ }
+
+ content_tag :div, class: 'progress' do
+ content_tag :div, nil, options
+ end
+ end
+
+ def projects_milestones_options
+ milestones =
+ if @project
+ @project.milestones
+ else
+ Milestone.where(project_id: @projects)
+ end.active
+
+ grouped_milestones = Milestones::GroupService.new(milestones).execute
+ grouped_milestones.unshift(Milestone::None)
+
+ options_from_collection_for_select(grouped_milestones, 'title', 'title', params[:milestone_title])
+ end
+ end
+end
diff --git a/app/helpers/gitlab/namespaces_helper.rb b/app/helpers/gitlab/namespaces_helper.rb
new file mode 100644
index 00000000000..b1caaac3f63
--- /dev/null
+++ b/app/helpers/gitlab/namespaces_helper.rb
@@ -0,0 +1,38 @@
+module Gitlab
+ module NamespacesHelper
+ def namespaces_options(selected = :current_user, scope = :default)
+ groups = current_user.owned_groups + current_user.masters_groups
+ users = [current_user.namespace]
+
+ group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ]
+ users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ]
+
+ options = []
+ options << group_opts
+ options << users_opts
+
+ if selected == :current_user && current_user.namespace
+ selected = current_user.namespace.id
+ end
+
+ grouped_options_for_select(options, selected)
+ end
+
+ def namespace_select_tag(id, opts = {})
+ css_class = "ajax-namespace-select "
+ css_class << "multiselect " if opts[:multiple]
+ css_class << (opts[:class] || '')
+ value = opts[:selected] || ''
+
+ hidden_field_tag(id, value, class: css_class)
+ end
+
+ def namespace_icon(namespace, size = 40)
+ if namespace.kind_of?(Group)
+ group_icon(namespace)
+ else
+ avatar_icon(namespace.owner.email, size)
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/nav_helper.rb b/app/helpers/gitlab/nav_helper.rb
new file mode 100644
index 00000000000..14106d70840
--- /dev/null
+++ b/app/helpers/gitlab/nav_helper.rb
@@ -0,0 +1,23 @@
+module Gitlab
+ module NavHelper
+ def nav_menu_collapsed?
+ cookies[:collapsed_nav] == 'true'
+ end
+
+ def nav_sidebar_class
+ if nav_menu_collapsed?
+ "page-sidebar-collapsed"
+ else
+ "page-sidebar-expanded"
+ end
+ end
+
+ def nav_header_class
+ if nav_menu_collapsed?
+ "header-collapsed"
+ else
+ "header-expanded"
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/notes_helper.rb b/app/helpers/gitlab/notes_helper.rb
new file mode 100644
index 00000000000..15076148b02
--- /dev/null
+++ b/app/helpers/gitlab/notes_helper.rb
@@ -0,0 +1,78 @@
+module Gitlab
+ module NotesHelper
+ # Helps to distinguish e.g. commit notes in mr notes list
+ def note_for_main_target?(note)
+ (@noteable.class.name == note.noteable_type && !note.for_diff_line?)
+ end
+
+ def note_target_fields(note)
+ hidden_field_tag(:target_type, note.noteable.class.name.underscore) +
+ hidden_field_tag(:target_id, note.noteable.id)
+ end
+
+ def note_editable?(note)
+ note.editable? && can?(current_user, :admin_note, note)
+ end
+
+ def link_to_commit_diff_line_note(note)
+ if note.for_commit_diff_line?
+ link_to(
+ "#{note.diff_file_name}:L#{note.diff_new_line}",
+ namespace_project_commit_path(@project.namespace, @project,
+ note.noteable, anchor: note.line_code)
+ )
+ end
+ end
+
+ def noteable_json(noteable)
+ {
+ id: noteable.id,
+ class: noteable.class.name,
+ resources: noteable.class.table_name,
+ project_id: noteable.project.id,
+ }.to_json
+ end
+
+ def link_to_new_diff_note(line_code, line_type = nil)
+ discussion_id = Note.build_discussion_id(
+ @comments_target[:noteable_type],
+ @comments_target[:noteable_id] || @comments_target[:commit_id],
+ line_code
+ )
+
+ data = {
+ noteable_type: @comments_target[:noteable_type],
+ noteable_id: @comments_target[:noteable_id],
+ commit_id: @comments_target[:commit_id],
+ line_code: line_code,
+ discussion_id: discussion_id,
+ line_type: line_type
+ }
+
+ button_tag(class: 'btn add-diff-note js-add-diff-note-button',
+ data: data,
+ title: 'Add a comment to this line') do
+ icon('comment-o')
+ end
+ end
+
+ def link_to_reply_diff(note, line_type = nil)
+ return unless current_user
+
+ data = {
+ noteable_type: note.noteable_type,
+ noteable_id: note.noteable_id,
+ commit_id: note.commit_id,
+ line_code: note.line_code,
+ discussion_id: note.discussion_id,
+ line_type: line_type
+ }
+
+ button_tag class: 'btn reply-btn js-discussion-reply-button',
+ data: data, title: 'Add a reply' do
+ link_text = icon('comment')
+ link_text << ' Reply'
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/notifications_helper.rb b/app/helpers/gitlab/notifications_helper.rb
new file mode 100644
index 00000000000..b6324044ab1
--- /dev/null
+++ b/app/helpers/gitlab/notifications_helper.rb
@@ -0,0 +1,17 @@
+module Gitlab
+ module NotificationsHelper
+ include IconsHelper
+
+ def notification_icon(notification)
+ if notification.disabled?
+ icon('volume-off', class: 'ns-mute')
+ elsif notification.participating?
+ icon('volume-down', class: 'ns-part')
+ elsif notification.watch?
+ icon('volume-up', class: 'ns-watch')
+ else
+ icon('circle-o', class: 'ns-default')
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/page_layout_helper.rb b/app/helpers/gitlab/page_layout_helper.rb
new file mode 100644
index 00000000000..d7a85186155
--- /dev/null
+++ b/app/helpers/gitlab/page_layout_helper.rb
@@ -0,0 +1,28 @@
+module Gitlab
+ module PageLayoutHelper
+ def page_title(*titles)
+ @page_title ||= []
+
+ @page_title.push(*titles.compact) if titles.any?
+
+ @page_title.join(" | ")
+ end
+
+ def header_title(title = nil, title_url = nil)
+ if title
+ @header_title = title
+ @header_title_url = title_url
+ else
+ @header_title_url ? link_to(@header_title, @header_title_url) : @header_title
+ end
+ end
+
+ def sidebar(name = nil)
+ if name
+ @sidebar = name
+ else
+ @sidebar
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/preferences_helper.rb b/app/helpers/gitlab/preferences_helper.rb
new file mode 100644
index 00000000000..3eac5d51acd
--- /dev/null
+++ b/app/helpers/gitlab/preferences_helper.rb
@@ -0,0 +1,67 @@
+module Gitlab
+ # Helper methods for per-User preferences
+ module PreferencesHelper
+ COLOR_SCHEMES = {
+ 1 => 'white',
+ 2 => 'dark',
+ 3 => 'solarized-light',
+ 4 => 'solarized-dark',
+ 5 => 'monokai',
+ }
+ COLOR_SCHEMES.default = 'white'
+
+ # Helper method to access the COLOR_SCHEMES
+ #
+ # The keys are the `color_scheme_ids`
+ # The values are the `name` of the scheme.
+ #
+ # The preview images are `name-scheme-preview.png`
+ # The stylesheets should use the css class `.name`
+ def color_schemes
+ COLOR_SCHEMES.freeze
+ end
+
+ # Maps `dashboard` values to more user-friendly option text
+ DASHBOARD_CHOICES = {
+ projects: 'Your Projects (default)',
+ stars: 'Starred Projects'
+ }.with_indifferent_access.freeze
+
+ # Returns an Array usable by a select field for more user-friendly option text
+ def dashboard_choices
+ defined = User.dashboards
+
+ if defined.size != DASHBOARD_CHOICES.size
+ # Ensure that anyone adding new options updates this method too
+ raise RuntimeError, "`User` defines #{defined.size} dashboard choices," +
+ " but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}."
+ else
+ defined.map do |key, _|
+ # Use `fetch` so `KeyError` gets raised when a key is missing
+ [DASHBOARD_CHOICES.fetch(key), key]
+ end
+ end
+ end
+
+ def project_view_choices
+ [
+ ['Readme (default)', :readme],
+ ['Activity view', :activity]
+ ]
+ end
+
+ def user_application_theme
+ theme = Gitlab::Themes.by_id(current_user.try(:theme_id))
+ theme.css_class
+ end
+
+ def user_color_scheme_class
+ COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
+ end
+
+ def prefer_readme?
+ !current_user ||
+ current_user.project_view == 'readme'
+ end
+ end
+end
diff --git a/app/helpers/gitlab/projects_helper.rb b/app/helpers/gitlab/projects_helper.rb
new file mode 100644
index 00000000000..8a8cd6048df
--- /dev/null
+++ b/app/helpers/gitlab/projects_helper.rb
@@ -0,0 +1,332 @@
+module Gitlab
+ module ProjectsHelper
+ def remove_from_project_team_message(project, member)
+ if member.user
+ "You are going to remove #{member.user.name} from #{project.name} project team. Are you sure?"
+ else
+ "You are going to revoke the invitation for #{member.invite_email} to join #{project.name} project team. Are you sure?"
+ end
+ end
+
+ def link_to_project(project)
+ link_to [project.namespace.becomes(Namespace), project] do
+ title = content_tag(:span, project.name, class: 'project-name')
+
+ if project.namespace
+ namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'namespace-name')
+ title = namespace + title
+ end
+
+ title
+ end
+ end
+
+ def link_to_member(project, author, opts = {})
+ default_opts = { avatar: true, name: true, size: 16, author_class: 'author' }
+ opts = default_opts.merge(opts)
+
+ return "(deleted)" unless author
+
+ author_html = ""
+
+ # Build avatar image tag
+ author_html << image_tag(avatar_icon(author.try(:email), 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]
+
+ author_html = author_html.html_safe
+
+ if opts[:name]
+ link_to(author_html, user_path(author), class: "author_link").html_safe
+ else
+ link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => sanitize(author.name) } ).html_safe
+ end
+ end
+
+ def project_title(project)
+ if project.group
+ content_tag :span do
+ link_to(
+ simple_sanitize(project.group.name), group_path(project.group)
+ ) + ' / ' +
+ link_to(simple_sanitize(project.name),
+ project_path(project))
+ end
+ else
+ owner = project.namespace.owner
+ content_tag :span do
+ link_to(
+ simple_sanitize(owner.name), user_path(owner)
+ ) + ' / ' +
+ link_to(simple_sanitize(project.name),
+ project_path(project))
+ end
+ end
+ end
+
+ def remove_project_message(project)
+ "You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?"
+ end
+
+ def transfer_project_message(project)
+ "You are going to transfer #{project.name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
+ end
+
+ def project_nav_tabs
+ @nav_tabs ||= get_project_nav_tabs(@project, current_user)
+ end
+
+ def project_nav_tab?(name)
+ project_nav_tabs.include? name
+ end
+
+ def project_active_milestones
+ @project.milestones.active.order("due_date, title ASC")
+ end
+
+ def project_for_deploy_key(deploy_key)
+ if deploy_key.projects.include?(@project)
+ @project
+ else
+ deploy_key.projects.find { |project| can?(current_user, :read_project, project) }
+ end
+ end
+
+ def can_change_visibility_level?(project, current_user)
+ return false unless can?(current_user, :change_visibility_level, project)
+
+ if project.forked?
+ project.forked_from_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE
+ else
+ true
+ end
+ end
+
+ private
+
+ def get_project_nav_tabs(project, current_user)
+ nav_tabs = [:home]
+
+ if !project.empty_repo? && can?(current_user, :download_code, project)
+ nav_tabs << [:files, :commits, :network, :graphs]
+ end
+
+ if project.repo_exists? && can?(current_user, :read_merge_request, project)
+ nav_tabs << :merge_requests
+ end
+
+ if can?(current_user, :admin_project, project)
+ nav_tabs << :settings
+ end
+
+ if can?(current_user, :read_issue, project)
+ nav_tabs << :issues
+ end
+
+ if can?(current_user, :read_wiki, project)
+ nav_tabs << :wiki
+ end
+
+ if can?(current_user, :read_project_snippet, project)
+ nav_tabs << :snippets
+ end
+
+ if can?(current_user, :read_label, project)
+ nav_tabs << :labels
+ end
+
+ if can?(current_user, :read_milestone, project)
+ nav_tabs << :milestones
+ end
+
+ nav_tabs.flatten
+ end
+
+ def git_user_name
+ if current_user
+ current_user.name
+ else
+ "Your name"
+ end
+ end
+
+ def git_user_email
+ if current_user
+ current_user.email
+ else
+ "your@email.com"
+ end
+ end
+
+ def repository_size(project = nil)
+ "#{(project || @project).repository_size} MB"
+ rescue
+ # In order to prevent 500 error
+ # when application cannot allocate memory
+ # to calculate repo size - just show 'Unknown'
+ 'unknown'
+ end
+
+ def default_url_to_repo(project = nil)
+ project = project || @project
+ current_user ? project.url_to_repo : project.http_url_to_repo
+ end
+
+ def default_clone_protocol
+ current_user ? "ssh" : "http"
+ end
+
+ def project_last_activity(project)
+ if project.last_activity_at
+ time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago')
+ else
+ "Never"
+ end
+ end
+
+ def add_contribution_guide_path(project)
+ if project && !project.repository.contribution_guide
+ namespace_project_new_blob_path(
+ project.namespace,
+ project,
+ project.default_branch,
+ file_name: "CONTRIBUTING.md",
+ commit_message: "Add contribution guide"
+ )
+ end
+ end
+
+ def add_changelog_path(project)
+ if project && !project.repository.changelog
+ namespace_project_new_blob_path(
+ project.namespace,
+ project,
+ project.default_branch,
+ file_name: "CHANGELOG",
+ commit_message: "Add changelog"
+ )
+ end
+ end
+
+ def add_license_path(project)
+ if project && !project.repository.license
+ namespace_project_new_blob_path(
+ project.namespace,
+ project,
+ project.default_branch,
+ file_name: "LICENSE",
+ commit_message: "Add license"
+ )
+ end
+ end
+
+ def contribution_guide_path(project)
+ if project && contribution_guide = project.repository.contribution_guide
+ namespace_project_blob_path(
+ project.namespace,
+ project,
+ tree_join(project.default_branch,
+ contribution_guide.name)
+ )
+ end
+ end
+
+ def readme_path(project)
+ filename_path(project, :readme)
+ end
+
+ def changelog_path(project)
+ filename_path(project, :changelog)
+ end
+
+ def license_path(project)
+ filename_path(project, :license)
+ end
+
+ def version_path(project)
+ filename_path(project, :version)
+ end
+
+ def hidden_pass_url(original_url)
+ result = URI(original_url)
+ result.password = '*****' unless result.password.nil?
+ result
+ rescue
+ original_url
+ end
+
+ def project_wiki_path_with_version(proj, page, version, is_newest)
+ url_params = is_newest ? {} : { version_id: version }
+ namespace_project_wiki_path(proj.namespace, proj, page, url_params)
+ end
+
+ def project_status_css_class(status)
+ case status
+ when "started"
+ "active"
+ when "failed"
+ "danger"
+ when "finished"
+ "success"
+ end
+ end
+
+ def user_max_access_in_project(user, project)
+ level = project.team.max_member_access(user)
+
+ if level
+ Gitlab::Access.options_with_owner.key(level)
+ end
+ end
+
+ def leave_project_message(project)
+ "Are you sure you want to leave \"#{project.name}\" project?"
+ end
+
+ def new_readme_path
+ ref = @repository.root_ref if @repository
+ ref ||= 'master'
+
+ namespace_project_new_blob_path(@project.namespace, @project, tree_join(ref), file_name: 'README.md')
+ end
+
+ def last_push_event
+ if current_user
+ current_user.recent_push(@project.id)
+ end
+ end
+
+ def readme_cache_key
+ sha = @project.commit.try(:sha) || 'nil'
+ [@project.id, sha, "readme"].join('-')
+ end
+
+ def round_commit_count(project)
+ count = project.commit_count
+
+ if count > 10000
+ '10000+'
+ elsif count > 5000
+ '5000+'
+ elsif count > 1000
+ '1000+'
+ else
+ count
+ end
+ end
+
+ private
+
+ def filename_path(project, filename)
+ if project && blob = project.repository.send(filename)
+ namespace_project_blob_path(
+ project.namespace,
+ project,
+ tree_join(project.default_branch,
+ blob.name)
+ )
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/search_helper.rb b/app/helpers/gitlab/search_helper.rb
new file mode 100644
index 00000000000..f9caf8f2431
--- /dev/null
+++ b/app/helpers/gitlab/search_helper.rb
@@ -0,0 +1,114 @@
+module Gitlab
+ module SearchHelper
+ def search_autocomplete_opts(term)
+ return unless current_user
+
+ resources_results = [
+ groups_autocomplete(term),
+ projects_autocomplete(term)
+ ].flatten
+
+ generic_results = project_autocomplete + default_autocomplete + help_autocomplete
+ generic_results.select! { |result| result[:label] =~ Regexp.new(term, "i") }
+
+ [
+ resources_results,
+ generic_results
+ ].flatten.uniq do |item|
+ item[:label]
+ end
+ end
+
+ private
+
+ # Autocomplete results for various settings pages
+ def default_autocomplete
+ [
+ { label: "Profile settings", url: profile_path },
+ { label: "SSH Keys", url: profile_keys_path },
+ { label: "Dashboard", url: root_path },
+ { label: "Admin Section", url: admin_root_path },
+ ]
+ end
+
+ # Autocomplete results for internal help pages
+ def help_autocomplete
+ [
+ { label: "help: API Help", url: help_page_path("api", "README") },
+ { label: "help: Markdown Help", url: help_page_path("markdown", "markdown") },
+ { label: "help: Permissions Help", url: help_page_path("permissions", "permissions") },
+ { label: "help: Public Access Help", url: help_page_path("public_access", "public_access") },
+ { 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: 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.exists? && @project.repository.root_ref
+ prefix = search_result_sanitize(@project.name_with_namespace)
+ ref = @ref || @project.repository.root_ref
+
+ [
+ { label: "#{prefix} - Files", url: namespace_project_tree_path(@project.namespace, @project, ref) },
+ { label: "#{prefix} - Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) },
+ { label: "#{prefix} - Network", url: namespace_project_network_path(@project.namespace, @project, ref) },
+ { label: "#{prefix} - Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) },
+ { label: "#{prefix} - Issues", url: namespace_project_issues_path(@project.namespace, @project) },
+ { label: "#{prefix} - Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) },
+ { label: "#{prefix} - Milestones", url: namespace_project_milestones_path(@project.namespace, @project) },
+ { label: "#{prefix} - Snippets", url: namespace_project_snippets_path(@project.namespace, @project) },
+ { label: "#{prefix} - Members", url: namespace_project_project_members_path(@project.namespace, @project) },
+ { label: "#{prefix} - Wiki", url: namespace_project_wikis_path(@project.namespace, @project) },
+ ]
+ else
+ []
+ end
+ end
+
+ # Autocomplete results for the current user's groups
+ def groups_autocomplete(term, limit = 5)
+ current_user.authorized_groups.search(term).limit(limit).map do |group|
+ {
+ label: "group: #{search_result_sanitize(group.name)}",
+ url: group_path(group)
+ }
+ end
+ end
+
+ # Autocomplete results for the current user's projects
+ def projects_autocomplete(term, limit = 5)
+ ProjectsFinder.new.execute(current_user).search_by_title(term).
+ sorted_by_stars.non_archived.limit(limit).map do |p|
+ {
+ label: "project: #{search_result_sanitize(p.name_with_namespace)}",
+ url: namespace_project_path(p.namespace, p)
+ }
+ end
+ end
+
+ def search_result_sanitize(str)
+ Sanitize.clean(str)
+ end
+
+ def search_filter_path(options={})
+ exist_opts = {
+ search: params[:search],
+ project_id: params[:project_id],
+ group_id: params[:group_id],
+ scope: params[:scope]
+ }
+
+ options = exist_opts.merge(options)
+ search_path(options)
+ end
+
+ # Sanitize html generated after parsing markdown from issue description or comment
+ def search_md_sanitize(html)
+ sanitize(html, tags: %w(a p ol ul li pre code))
+ end
+ end
+end
diff --git a/app/helpers/gitlab/selects_helper.rb b/app/helpers/gitlab/selects_helper.rb
new file mode 100644
index 00000000000..d52d670a1cf
--- /dev/null
+++ b/app/helpers/gitlab/selects_helper.rb
@@ -0,0 +1,47 @@
+module Gitlab
+ module SelectsHelper
+ def users_select_tag(id, opts = {})
+ css_class = "ajax-users-select "
+ css_class << "multiselect " if opts[:multiple]
+ css_class << (opts[:class] || '')
+ value = opts[:selected] || ''
+ placeholder = opts[:placeholder] || 'Search for a user'
+
+ null_user = opts[:null_user] || false
+ any_user = opts[:any_user] || false
+ email_user = opts[:email_user] || false
+ first_user = opts[:first_user] && current_user ? current_user.username : false
+ current_user = opts[:current_user] || false
+ project = opts[:project] || @project
+
+ html = {
+ class: css_class,
+ 'data-placeholder' => placeholder,
+ 'data-null-user' => null_user,
+ 'data-any-user' => any_user,
+ 'data-email-user' => email_user,
+ 'data-first-user' => first_user,
+ 'data-current-user' => current_user
+ }
+
+ unless opts[:scope] == :all
+ if project
+ html['data-project-id'] = project.id
+ elsif @group
+ html['data-group-id'] = @group.id
+ end
+ end
+
+ hidden_field_tag(id, value, html)
+ end
+
+ def groups_select_tag(id, opts = {})
+ css_class = "ajax-groups-select "
+ css_class << "multiselect " if opts[:multiple]
+ css_class << (opts[:class] || '')
+ value = opts[:selected] || ''
+
+ hidden_field_tag(id, value, class: css_class)
+ end
+ end
+end
diff --git a/app/helpers/gitlab/snippets_helper.rb b/app/helpers/gitlab/snippets_helper.rb
new file mode 100644
index 00000000000..aaf4d43f852
--- /dev/null
+++ b/app/helpers/gitlab/snippets_helper.rb
@@ -0,0 +1,22 @@
+module Gitlab
+ 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,
+ snippet.project, snippet)
+ else
+ snippet_path(snippet)
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/sorting_helper.rb b/app/helpers/gitlab/sorting_helper.rb
new file mode 100644
index 00000000000..29c63a0d129
--- /dev/null
+++ b/app/helpers/gitlab/sorting_helper.rb
@@ -0,0 +1,98 @@
+module Gitlab
+ module SortingHelper
+ def 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,
+ sort_value_milestone_soon => sort_title_milestone_soon,
+ sort_value_milestone_later => sort_title_milestone_later,
+ sort_value_largest_repo => sort_title_largest_repo,
+ sort_value_recently_signin => sort_title_recently_signin,
+ sort_value_oldest_signin => sort_title_oldest_signin,
+ }
+ end
+
+ def sort_title_oldest_updated
+ 'Oldest updated'
+ end
+
+ def sort_title_recently_updated
+ 'Recently updated'
+ end
+
+ def sort_title_oldest_created
+ 'Oldest created'
+ end
+
+ def sort_title_recently_created
+ 'Recently created'
+ end
+
+ def sort_title_milestone_soon
+ 'Milestone due soon'
+ end
+
+ def sort_title_milestone_later
+ 'Milestone due later'
+ end
+
+ def sort_title_name
+ 'Name'
+ end
+
+ def sort_title_largest_repo
+ 'Largest repository'
+ end
+
+ def sort_title_recently_signin
+ 'Recent sign in'
+ end
+
+ def sort_title_oldest_signin
+ 'Oldest sign in'
+ end
+
+ def sort_value_oldest_updated
+ 'updated_asc'
+ end
+
+ def sort_value_recently_updated
+ 'updated_desc'
+ end
+
+ def sort_value_oldest_created
+ 'created_asc'
+ end
+
+ def sort_value_recently_created
+ 'created_desc'
+ end
+
+ def sort_value_milestone_soon
+ 'milestone_due_asc'
+ end
+
+ def sort_value_milestone_later
+ 'milestone_due_desc'
+ end
+
+ def sort_value_name
+ 'name_asc'
+ end
+
+ def sort_value_largest_repo
+ 'repository_size_desc'
+ end
+
+ def sort_value_recently_signin
+ 'recent_sign_in'
+ end
+
+ def sort_value_oldest_signin
+ 'oldest_sign_in'
+ end
+ end
+end
diff --git a/app/helpers/gitlab/submodule_helper.rb b/app/helpers/gitlab/submodule_helper.rb
new file mode 100644
index 00000000000..c0fbebcb1d9
--- /dev/null
+++ b/app/helpers/gitlab/submodule_helper.rb
@@ -0,0 +1,76 @@
+module Gitlab
+ module SubmoduleHelper
+ include Gitlab::ShellAdapter
+
+ # links to files listing for submodule if submodule is a project on this server
+ def submodule_links(submodule_item, ref = nil, repository = @repository)
+ url = repository.submodule_url_for(ref, submodule_item.path)
+
+ return url, nil unless url =~ /([^\/:]+)\/([^\/]+\.git)\Z/
+
+ namespace = $1
+ project = $2
+ project.chomp!('.git')
+
+ if self_url?(url, namespace, project)
+ return namespace_project_path(namespace, project),
+ namespace_project_tree_path(namespace, project,
+ submodule_item.id)
+ elsif relative_self_url?(url)
+ relative_self_links(url, submodule_item.id)
+ elsif github_dot_com_url?(url)
+ standard_links('github.com', namespace, project, submodule_item.id)
+ elsif gitlab_dot_com_url?(url)
+ standard_links('gitlab.com', namespace, project, submodule_item.id)
+ else
+ return url, nil
+ end
+ end
+
+ protected
+
+ def github_dot_com_url?(url)
+ url =~ /github\.com[\/:][^\/]+\/[^\/]+\Z/
+ end
+
+ def gitlab_dot_com_url?(url)
+ url =~ /gitlab\.com[\/:][^\/]+\/[^\/]+\Z/
+ end
+
+ def self_url?(url, namespace, project)
+ return true if url == [ Gitlab.config.gitlab.url, '/', namespace, '/',
+ project, '.git' ].join('')
+ url == gitlab_shell.url_to_repo([namespace, '/', project].join(''))
+ end
+
+ def relative_self_url?(url)
+ # (./)?(../repo.git) || (./)?(../../project/repo.git) )
+ url =~ /\A((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*\.git\z/ || url =~ /\A((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*\.git\z/
+ end
+
+ def standard_links(host, namespace, project, commit)
+ base = [ 'https://', host, '/', namespace, '/', project ].join('')
+ [base, [ base, '/tree/', commit ].join('')]
+ end
+
+ def relative_self_links(url, commit)
+ # Map relative links to a namespace and project
+ # For example:
+ # ../bar.git -> same namespace, repo bar
+ # ../foo/bar.git -> namespace foo, repo bar
+ # ../../foo/bar/baz.git -> namespace bar, repo baz
+ components = url.split('/')
+ base = components.pop.gsub(/.git$/, '')
+ namespace = components.pop.gsub(/^\.\.$/, '')
+
+ if namespace.empty?
+ namespace = @project.namespace.path
+ end
+
+ [
+ namespace_project_path(namespace, base),
+ namespace_project_tree_path(namespace, base, commit)
+ ]
+ end
+ end
+end
diff --git a/app/helpers/gitlab/tab_helper.rb b/app/helpers/gitlab/tab_helper.rb
new file mode 100644
index 00000000000..01d36ff84fc
--- /dev/null
+++ b/app/helpers/gitlab/tab_helper.rb
@@ -0,0 +1,133 @@
+module Gitlab
+ module TabHelper
+ # Navigation link helper
+ #
+ # Returns an `li` element with an 'active' class if the supplied
+ # controller(s) and/or action(s) are currently active. The content of the
+ # element is the value passed to the block.
+ #
+ # options - The options hash used to determine if the element is "active" (default: {})
+ # :controller - One or more controller names to check (optional).
+ # :action - One or more action names to check (optional).
+ # :path - A shorthand path, such as 'dashboard#index', to check (optional).
+ # :html_options - Extra options to be passed to the list element (optional).
+ # block - An optional block that will become the contents of the returned
+ # `li` element.
+ #
+ # When both :controller and :action are specified, BOTH must match in order
+ # to be marked as active. When only one is given, either can match.
+ #
+ # Examples
+ #
+ # # Assuming we're on TreeController#show
+ #
+ # # Controller matches, but action doesn't
+ # nav_link(controller: [:tree, :refs], action: :edit) { "Hello" }
+ # # => '<li>Hello</li>'
+ #
+ # # Controller matches
+ # nav_link(controller: [:tree, :refs]) { "Hello" }
+ # # => '<li class="active">Hello</li>'
+ #
+ # # Several paths
+ # nav_link(path: ['tree#show', 'profile#show']) { "Hello" }
+ # # => '<li class="active">Hello</li>'
+ #
+ # # Shorthand path
+ # nav_link(path: 'tree#show') { "Hello" }
+ # # => '<li class="active">Hello</li>'
+ #
+ # # Supplying custom options for the list element
+ # nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" }
+ # # => '<li class="home active">Hello</li>'
+ #
+ # Returns a list item element String
+ def nav_link(options = {}, &block)
+ klass = active_nav_link?(options) ? 'active' : ''
+
+ # Add our custom class into the html_options, which may or may not exist
+ # and which may or may not already have a :class key
+ o = options.delete(:html_options) || {}
+ o[:class] ||= ''
+ o[:class] += ' ' + klass
+ o[:class].strip!
+
+ if block_given?
+ content_tag(:li, capture(&block), o)
+ else
+ content_tag(:li, nil, o)
+ end
+ end
+
+ def active_nav_link?(options)
+ if path = options.delete(:path)
+ unless path.respond_to?(:each)
+ path = [path]
+ end
+
+ path.any? do |single_path|
+ current_path?(single_path)
+ end
+ elsif page = options.delete(:page)
+ unless page.respond_to?(:each)
+ page = [page]
+ end
+
+ page.any? do |single_page|
+ current_page?(single_page)
+ end
+ else
+ c = options.delete(:controller)
+ a = options.delete(:action)
+
+ if c && a
+ # When given both options, make sure BOTH are true
+ current_controller?(*c) && current_action?(*a)
+ else
+ # Otherwise check EITHER option
+ current_controller?(*c) || current_action?(*a)
+ end
+ end
+ end
+
+ def current_path?(path)
+ c, a, _ = path.split('#')
+ current_controller?(c) && current_action?(a)
+ end
+
+ def project_tab_class
+ return "active" if current_page?(controller: "/projects", action: :edit, id: @project)
+
+ if ['services', 'hooks', 'deploy_keys', 'protected_branches'].include? controller.controller_name
+ "active"
+ end
+ end
+
+ def branches_tab_class
+ if current_controller?(:protected_branches) ||
+ current_controller?(:branches) ||
+ current_page?(namespace_project_repository_path(@project.namespace,
+ @project))
+ 'active'
+ end
+ end
+
+ # Use nav_tab for save controller/action but different params
+ def nav_tab(key, value, &block)
+ o = {}
+ o[:class] = ""
+
+ if value.nil?
+ o[:class] << " active" if params[key].blank?
+ else
+ o[:class] << " active" if params[key] == value
+ end
+
+ if block_given?
+ content_tag(:li, capture(&block), o)
+ else
+ content_tag(:li, nil, o)
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/tags_helper.rb b/app/helpers/gitlab/tags_helper.rb
new file mode 100644
index 00000000000..d694b2c90ce
--- /dev/null
+++ b/app/helpers/gitlab/tags_helper.rb
@@ -0,0 +1,16 @@
+module Gitlab
+ module TagsHelper
+ def tag_path(tag)
+ "/tags/#{tag}"
+ end
+
+ def tag_list(project)
+ html = ''
+ project.tag_list.each do |tag|
+ html << link_to(tag, tag_path(tag))
+ end
+
+ html.html_safe
+ end
+ end
+end
diff --git a/app/helpers/gitlab/tree_helper.rb b/app/helpers/gitlab/tree_helper.rb
new file mode 100644
index 00000000000..dc48ff0e6e2
--- /dev/null
+++ b/app/helpers/gitlab/tree_helper.rb
@@ -0,0 +1,89 @@
+module Gitlab
+ module TreeHelper
+ # Sorts a repository's tree so that folders are before files and renders
+ # their corresponding partials
+ #
+ def render_tree(tree)
+ # Render Folders before Files/Submodules
+ folders, files, submodules = tree.trees, tree.blobs, tree.submodules
+
+ tree = ""
+
+ # Render folders if we have any
+ tree << render(partial: 'projects/tree/tree_item', collection: folders,
+ locals: { type: 'folder' }) if folders.present?
+
+ # Render files if we have any
+ tree << render(partial: 'projects/tree/blob_item', collection: files,
+ locals: { type: 'file' }) if files.present?
+
+ # Render submodules if we have any
+ tree << render(partial: 'projects/tree/submodule_item',
+ collection: submodules) if submodules.present?
+
+ tree.html_safe
+ end
+
+ def render_readme(readme)
+ render_markup(readme.name, readme.data)
+ end
+
+ # Return an image icon depending on the file type and mode
+ #
+ # type - String type of the tree item; either 'folder' or 'file'
+ # mode - File unix mode
+ # name - File name
+ def tree_icon(type, mode, name)
+ icon("#{file_type_icon_class(type, mode, name)} fw")
+ end
+
+ def tree_hex_class(content)
+ "file_#{hexdigest(content.name)}"
+ end
+
+ # Simple shortcut to File.join
+ def tree_join(*args)
+ File.join(*args)
+ end
+
+ def allowed_tree_edit?(project = nil, ref = nil)
+ project ||= @project
+ ref ||= @ref
+ return false unless project.repository.branch_names.include?(ref)
+
+ ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
+ end
+
+ def tree_breadcrumbs(tree, max_links = 2)
+ if @path.present?
+ part_path = ""
+ parts = @path.split('/')
+
+ yield('..', nil) if parts.count > max_links
+
+ parts.each do |part|
+ part_path = File.join(part_path, part) unless part_path.empty?
+ part_path = part if part_path.empty?
+
+ next unless parts.last(2).include?(part) if parts.count > max_links
+ yield(part, tree_join(@ref, part_path))
+ end
+ end
+ end
+
+ def up_dir_path
+ file = File.join(@path, "..")
+ tree_join(@ref, file)
+ end
+
+ # returns the relative path of the first subdir that doesn't have only one directory descendant
+ def flatten_tree(tree)
+ subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path)
+ if subtree.count == 1 && subtree.first.dir?
+ return tree_join(tree.name, flatten_tree(subtree.first))
+ else
+ return tree.name
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/version_check_helper.rb b/app/helpers/gitlab/version_check_helper.rb
new file mode 100644
index 00000000000..46a12cc8c60
--- /dev/null
+++ b/app/helpers/gitlab/version_check_helper.rb
@@ -0,0 +1,9 @@
+module Gitlab
+ module VersionCheckHelper
+ def version_status_badge
+ if Rails.env.production?
+ image_tag VersionCheck.new.url
+ end
+ end
+ end
+end
diff --git a/app/helpers/gitlab/visibility_level_helper.rb b/app/helpers/gitlab/visibility_level_helper.rb
new file mode 100644
index 00000000000..feba901f7d7
--- /dev/null
+++ b/app/helpers/gitlab/visibility_level_helper.rb
@@ -0,0 +1,97 @@
+module Gitlab
+ module VisibilityLevelHelper
+ def visibility_level_color(level)
+ case level
+ when Gitlab::VisibilityLevel::PRIVATE
+ 'vs-private'
+ when Gitlab::VisibilityLevel::INTERNAL
+ 'vs-internal'
+ when Gitlab::VisibilityLevel::PUBLIC
+ 'vs-public'
+ end
+ end
+
+ # Return the description for the +level+ argument.
+ #
+ # +level+ One of the Gitlab::VisibilityLevel constants
+ # +form_model+ Either a model object (Project, Snippet, etc.) or the name of
+ # a Project or Snippet class.
+ def visibility_level_description(level, form_model)
+ case form_model.is_a?(String) ? form_model : form_model.class.name
+ when 'PersonalSnippet', 'ProjectSnippet', 'Snippet'
+ snippet_visibility_level_description(level)
+ when 'Project'
+ project_visibility_level_description(level)
+ end
+ end
+
+ def project_visibility_level_description(level)
+ capture_haml do
+ haml_tag :span do
+ case level
+ when Gitlab::VisibilityLevel::PRIVATE
+ haml_concat "Project access must be granted explicitly for each user."
+ when Gitlab::VisibilityLevel::INTERNAL
+ haml_concat "The project can be cloned by"
+ haml_concat "any logged in user."
+ when Gitlab::VisibilityLevel::PUBLIC
+ haml_concat "The project can be cloned"
+ haml_concat "without any"
+ haml_concat "authentication."
+ end
+ end
+ end
+ end
+
+ def snippet_visibility_level_description(level)
+ capture_haml do
+ haml_tag :span do
+ case level
+ when Gitlab::VisibilityLevel::PRIVATE
+ haml_concat "The snippet is visible only for me."
+ when Gitlab::VisibilityLevel::INTERNAL
+ haml_concat "The snippet is visible for any logged in user."
+ when Gitlab::VisibilityLevel::PUBLIC
+ haml_concat "The snippet can be accessed"
+ haml_concat "without any"
+ haml_concat "authentication."
+ end
+ end
+ end
+ end
+
+ def visibility_level_icon(level)
+ case level
+ when Gitlab::VisibilityLevel::PRIVATE
+ private_icon
+ when Gitlab::VisibilityLevel::INTERNAL
+ internal_icon
+ when Gitlab::VisibilityLevel::PUBLIC
+ public_icon
+ end
+ end
+
+ def visibility_level_label(level)
+ Project.visibility_levels.key(level)
+ end
+
+ def restricted_visibility_levels(show_all = false)
+ return [] if current_user.is_admin? && !show_all
+ current_application_settings.restricted_visibility_levels || []
+ end
+
+ def default_project_visibility
+ current_application_settings.default_project_visibility
+ end
+
+ def default_snippet_visibility
+ current_application_settings.default_snippet_visibility
+ end
+
+ def skip_level?(form_model, level)
+ form_model.is_a?(Project) &&
+ form_model.forked? &&
+ !Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level)
+ end
+ end
+end
diff --git a/app/helpers/gitlab/wiki_helper.rb b/app/helpers/gitlab/wiki_helper.rb
new file mode 100644
index 00000000000..02a1daf0019
--- /dev/null
+++ b/app/helpers/gitlab/wiki_helper.rb
@@ -0,0 +1,26 @@
+module Gitlab
+ module WikiHelper
+ # Rails v4.1.9+ escapes all model IDs, converting slashes into %2F. The
+ # only way around this is to implement our own path generators.
+ def namespace_project_wiki_path(namespace, project, wiki_page, *args)
+ slug =
+ case wiki_page
+ when Symbol
+ wiki_page
+ when String
+ wiki_page
+ else
+ wiki_page.slug
+ end
+ namespace_project_path(namespace, project) + "/wikis/#{slug}"
+ end
+
+ def edit_namespace_project_wiki_path(namespace, project, wiki_page, *args)
+ namespace_project_wiki_path(namespace, project, wiki_page) + '/edit'
+ end
+
+ def history_namespace_project_wiki_path(namespace, project, wiki_page, *args)
+ namespace_project_wiki_path(namespace, project, wiki_page) + '/history'
+ end
+ end
+end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
deleted file mode 100644
index eb3f72a307d..00000000000
--- a/app/helpers/gitlab_markdown_helper.rb
+++ /dev/null
@@ -1,193 +0,0 @@
-require 'nokogiri'
-
-module GitlabMarkdownHelper
- include Gitlab::Markdown
- include PreferencesHelper
-
- # Use this in places where you would normally use link_to(gfm(...), ...).
- #
- # It solves a problem occurring with nested links (i.e.
- # "<a>outer text <a>gfm ref</a> more outer text</a>"). This will not be
- # interpreted as intended. Browsers will parse something like
- # "<a>outer text </a><a>gfm ref</a> more outer text" (notice the last part is
- # not linked any more). link_to_gfm corrects that. It wraps all parts to
- # explicitly produce the correct linking behavior (i.e.
- # "<a>outer text </a><a>gfm ref</a><a> more outer text</a>").
- def link_to_gfm(body, url, html_options = {})
- return "" if body.blank?
-
- escaped_body = if body =~ /\A\<img/
- body
- else
- escape_once(body)
- end
-
- gfm_body = gfm(escaped_body, {}, html_options)
-
- fragment = Nokogiri::XML::DocumentFragment.parse(gfm_body)
- if fragment.children.size == 1 && fragment.children[0].name == 'a'
- # Fragment has only one node, and it's a link generated by `gfm`.
- # Replace it with our requested link.
- text = fragment.children[0].text
- fragment.children[0].replace(link_to(text, url, html_options))
- else
- # Traverse the fragment's first generation of children looking for pure
- # text, wrapping anything found in the requested link
- fragment.children.each do |node|
- next unless node.text?
- node.replace(link_to(node.text, url, html_options))
- end
- end
-
- fragment.to_html.html_safe
- end
-
- MARKDOWN_OPTIONS = {
- no_intra_emphasis: true,
- tables: true,
- fenced_code_blocks: true,
- strikethrough: true,
- lax_spacing: true,
- space_after_headers: true,
- superscript: true,
- footnotes: true
- }.freeze
-
- def markdown(text, options={})
- unless @markdown && options == @options
- @options = options
-
- # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch
- rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, options)
-
- # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
- @markdown = Redcarpet::Markdown.new(rend, MARKDOWN_OPTIONS)
- end
-
- @markdown.render(text).html_safe
- end
-
- def asciidoc(text)
- Gitlab::Asciidoc.render(text, {
- commit: @commit,
- project: @project,
- project_wiki: @project_wiki,
- requested_path: @path,
- ref: @ref
- })
- end
-
- # Return the first line of +text+, up to +max_chars+, after parsing the line
- # as Markdown. HTML tags in the parsed output are not counted toward the
- # +max_chars+ limit. If the length limit falls within a tag's contents, then
- # the tag contents are truncated without removing the closing tag.
- def first_line_in_markdown(text, max_chars = nil, options = {})
- md = markdown(text, options).strip
-
- truncate_visible(md, max_chars || md.length) if md.present?
- end
-
- def render_wiki_content(wiki_page)
- case wiki_page.format
- when :markdown
- markdown(wiki_page.content)
- when :asciidoc
- asciidoc(wiki_page.content)
- else
- wiki_page.formatted_content.html_safe
- end
- end
-
- MARKDOWN_TIPS = [
- "End a line with two or more spaces for a line-break, or soft-return",
- "Inline code can be denoted by `surrounding it with backticks`",
- "Blocks of code can be denoted by three backticks ``` or four leading spaces",
- "Emoji can be added by :emoji_name:, for example :thumbsup:",
- "Notify other participants using @user_name",
- "Notify a specific group using @group_name",
- "Notify the entire team using @all",
- "Reference an issue using a hash, for example issue #123",
- "Reference a merge request using an exclamation point, for example MR !123",
- "Italicize words or phrases using *asterisks* or _underscores_",
- "Bold words or phrases using **double asterisks** or __double underscores__",
- "Strikethrough words or phrases using ~~two tildes~~",
- "Make a bulleted list using + pluses, - minuses, or * asterisks",
- "Denote blockquotes using > at the beginning of a line",
- "Make a horizontal line using three or more hyphens ---, asterisks ***, or underscores ___"
- ].freeze
-
- # Returns a random markdown tip for use as a textarea placeholder
- def random_markdown_tip
- MARKDOWN_TIPS.sample
- end
-
- private
-
- # Return +text+, truncated to +max_chars+ characters, excluding any HTML
- # tags.
- def truncate_visible(text, max_chars)
- doc = Nokogiri::HTML.fragment(text)
- content_length = 0
- truncated = false
-
- doc.traverse do |node|
- if node.text? || node.content.empty?
- if truncated
- node.remove
- next
- end
-
- # Handle line breaks within a node
- if node.content.strip.lines.length > 1
- node.content = "#{node.content.lines.first.chomp}..."
- truncated = true
- end
-
- num_remaining = max_chars - content_length
- if node.content.length > num_remaining
- node.content = node.content.truncate(num_remaining)
- truncated = true
- end
- content_length += node.content.length
- end
-
- truncated = truncate_if_block(node, truncated)
- end
-
- doc.to_html
- end
-
- # Used by #truncate_visible. If +node+ is the first block element, and the
- # text hasn't already been truncated, then append "..." to the node contents
- # and return true. Otherwise return false.
- def truncate_if_block(node, truncated)
- if node.element? && node.description.block? && !truncated
- node.content = "#{node.content}..." if node.next_sibling
- true
- else
- truncated
- end
- end
-
- # Returns the text necessary to reference `entity` across projects
- #
- # project - Project to reference
- # entity - Object that responds to `to_reference`
- #
- # Examples:
- #
- # cross_project_reference(project, project.issues.first)
- # # => 'namespace1/project1#123'
- #
- # cross_project_reference(project, project.merge_requests.first)
- # # => 'namespace1/project1!345'
- #
- # Returns a String
- def cross_project_reference(project, entity)
- if entity.respond_to?(:to_reference)
- "#{project.to_reference}#{entity.to_reference}"
- else
- ''
- end
- end
-end
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
deleted file mode 100644
index d0fae255a04..00000000000
--- a/app/helpers/gitlab_routing_helper.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# Shorter routing method for project and project items
-# Since update to rails 4.1.9 we are now allowed to use `/` in project routing
-# so we use nested routing for project resources which include project and
-# project namespace. To avoid writing long methods every time we define shortcuts for
-# some of routing.
-#
-# For example instead of this:
-#
-# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.projects, merge_request)
-#
-# We can simply use shortcut:
-#
-# merge_request_path(merge_request)
-#
-module GitlabRoutingHelper
- def project_path(project, *args)
- namespace_project_path(project.namespace, project, *args)
- end
-
- def activity_project_path(project, *args)
- activity_namespace_project_path(project.namespace, project, *args)
- end
-
- def edit_project_path(project, *args)
- edit_namespace_project_path(project.namespace, project, *args)
- end
-
- def issue_path(entity, *args)
- namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args)
- end
-
- def merge_request_path(entity, *args)
- namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args)
- end
-
- def milestone_path(entity, *args)
- namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args)
- end
-
- def project_url(project, *args)
- namespace_project_url(project.namespace, project, *args)
- end
-
- def edit_project_url(project, *args)
- edit_namespace_project_url(project.namespace, project, *args)
- end
-
- def issue_url(entity, *args)
- namespace_project_issue_url(entity.project.namespace, entity.project, entity, *args)
- end
-
- def merge_request_url(entity, *args)
- namespace_project_merge_request_url(entity.project.namespace, entity.project, entity, *args)
- end
-
- def project_snippet_url(entity, *args)
- namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args)
- end
-
- def toggle_subscription_path(entity, *args)
- if entity.is_a?(Issue)
- toggle_subscription_namespace_project_issue_path(entity.project.namespace, entity.project, entity)
- else
- toggle_subscription_namespace_project_merge_request_path(entity.project.namespace, entity.project, entity)
- end
- end
-end
diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb
deleted file mode 100644
index e1dda20de85..00000000000
--- a/app/helpers/graph_helper.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module GraphHelper
- def get_refs(repo, commit)
- refs = ""
- refs << commit.ref_names(repo).join(' ')
-
- # append note count
- refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0
-
- refs
- end
-
- def parents_zip_spaces(parents, parent_spaces)
- ids = parents.map { |p| p.id }
- ids.zip(parent_spaces)
- end
-end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
deleted file mode 100644
index b067cb54a43..00000000000
--- a/app/helpers/groups_helper.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-module GroupsHelper
- def remove_user_from_group_message(group, member)
- if member.user
- "Are you sure you want to remove \"#{member.user.name}\" from \"#{group.name}\"?"
- else
- "Are you sure you want to revoke the invitation for \"#{member.invite_email}\" to join \"#{group.name}\"?"
- end
- end
-
- def leave_group_message(group)
- "Are you sure you want to leave \"#{group}\" group?"
- end
-
- def should_user_see_group_roles?(user, group)
- if user
- user.is_admin? || group.members.exists?(user_id: user.id)
- else
- false
- end
- end
-
- def group_icon(group)
- if group.is_a?(String)
- group = Group.find_by(path: group)
- end
-
- if group && group.avatar.present?
- group.avatar.url
- else
- image_path('no_group_avatar.png')
- end
- end
-end
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
deleted file mode 100644
index 1cf5b96481a..00000000000
--- a/app/helpers/icons_helper.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-module IconsHelper
- include FontAwesome::Rails::IconHelper
-
- # Creates an icon tag given icon name(s) and possible icon modifiers.
- #
- # Right now this method simply delegates directly to `fa_icon` from the
- # font-awesome-rails gem, but should we ever use a different icon pack in the
- # future we won't have to change hundreds of method calls.
- def icon(names, options = {})
- fa_icon(names, options)
- end
-
- def spinner(text = nil, visible = false)
- css_class = 'loading'
- css_class << ' hide' unless visible
-
- content_tag :div, class: css_class do
- icon('spinner spin') + text
- end
- end
-
- def boolean_to_icon(value)
- if value
- icon('circle', class: 'cgreen')
- else
- icon('power-off', class: 'clgray')
- end
- end
-
- def public_icon
- icon('globe fw')
- end
-
- def internal_icon
- icon('shield fw')
- end
-
- def private_icon
- icon('lock fw')
- end
-
- def file_type_icon_class(type, mode, name)
- if type == 'folder'
- icon_class = 'folder'
- elsif mode == '120000'
- icon_class = 'share'
- else
- # Guess which icon to choose based on file extension.
- # If you think a file extension is missing, feel free to add it on PR
-
- case File.extname(name).downcase
- when '.pdf'
- icon_class = 'file-pdf-o'
- when '.jpg', '.jpeg', '.jif', '.jfif',
- '.jp2', '.jpx', '.j2k', '.j2c',
- '.png', '.gif', '.tif', '.tiff',
- '.svg', '.ico', '.bmp'
- icon_class = 'file-image-o'
- when '.zip', '.zipx', '.tar', '.gz', '.bz', '.bzip',
- '.xz', '.rar', '.7z'
- icon_class = 'file-archive-o'
- when '.mp3', '.wma', '.ogg', '.oga', '.wav', '.flac', '.aac'
- icon_class = 'file-audio-o'
- when '.mp4', '.m4p', '.m4v',
- '.mpg', '.mp2', '.mpeg', '.mpe', '.mpv',
- '.mpg', '.mpeg', '.m2v',
- '.avi', '.mkv', '.flv', '.ogv', '.mov',
- '.3gp', '.3g2'
- icon_class = 'file-video-o'
- when '.doc', '.dot', '.docx', '.docm', '.dotx', '.dotm', '.docb'
- icon_class = 'file-word-o'
- when '.xls', '.xlt', '.xlm', '.xlsx', '.xlsm', '.xltx', '.xltm',
- '.xlsb', '.xla', '.xlam', '.xll', '.xlw'
- icon_class = 'file-excel-o'
- when '.ppt', '.pot', '.pps', '.pptx', '.pptm', '.potx', '.potm',
- '.ppam', '.ppsx', '.ppsm', '.sldx', '.sldm'
- icon_class = 'file-powerpoint-o'
- else
- icon_class = 'file-text-o'
- end
- end
-
- icon_class
- end
-end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
deleted file mode 100644
index 6ddb37cd0dc..00000000000
--- a/app/helpers/issues_helper.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-module IssuesHelper
- def issue_css_classes(issue)
- classes = "issue"
- classes << " closed" if issue.closed?
- classes << " today" if issue.today?
- classes
- end
-
- # Returns an OpenStruct object suitable for use by <tt>options_from_collection_for_select</tt>
- # to allow filtering issues by an unassigned User or Milestone
- def unassigned_filter
- # Milestone uses :title, Issue uses :name
- OpenStruct.new(id: 0, title: 'None (backlog)', name: 'Unassigned')
- end
-
- def url_for_project_issues(project = @project, options = {})
- return '' if project.nil?
-
- if options[:only_path]
- project.issues_tracker.project_path
- else
- project.issues_tracker.project_url
- end
- end
-
- def url_for_new_issue(project = @project, options = {})
- return '' if project.nil?
-
- if options[:only_path]
- project.issues_tracker.new_issue_path
- else
- project.issues_tracker.new_issue_url
- end
- end
-
- def url_for_issue(issue_iid, project = @project, options = {})
- return '' if project.nil?
-
- if options[:only_path]
- project.issues_tracker.issue_path(issue_iid)
- else
- project.issues_tracker.issue_url(issue_iid)
- end
- end
-
- def bulk_update_milestone_options
- options_for_select([['None (backlog)', -1]]) +
- options_from_collection_for_select(project_active_milestones, 'id',
- 'title', params[:milestone_id])
- end
-
- def milestone_options(object)
- options_from_collection_for_select(object.project.milestones.active,
- 'id', 'title', object.milestone_id)
- end
-
- def issue_box_class(item)
- if item.respond_to?(:expired?) && item.expired?
- 'issue-box-expired'
- elsif item.respond_to?(:merged?) && item.merged?
- 'issue-box-merged'
- elsif item.closed?
- 'issue-box-closed'
- else
- 'issue-box-open'
- end
- end
-
- def issue_to_atom(xml, issue)
- xml.entry do
- xml.id namespace_project_issue_url(issue.project.namespace,
- issue.project, issue)
- xml.link href: namespace_project_issue_url(issue.project.namespace,
- issue.project, issue)
- xml.title truncate(issue.title, length: 80)
- xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email)
- xml.author do |author|
- xml.name issue.author_name
- xml.email issue.author_email
- end
- xml.summary issue.title
- end
- end
-
- # Required for Gitlab::Markdown::IssueReferenceFilter
- module_function :url_for_issue
-end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
deleted file mode 100644
index 8036303851b..00000000000
--- a/app/helpers/labels_helper.rb
+++ /dev/null
@@ -1,101 +0,0 @@
-module LabelsHelper
- include ActionView::Helpers::TagHelper
-
- # Link to a Label
- #
- # label - Label object to link to
- # project - Project object which will be used as the context for the label's
- # link. If omitted, defaults to `@project`, or the label's own
- # project.
- # block - An optional block that will be passed to `link_to`, forming the
- # body of the link element. If omitted, defaults to
- # `render_colored_label`.
- #
- # Examples:
- #
- # # Allow the generated link to use the label's own project
- # link_to_label(label)
- #
- # # Force the generated link to use @project
- # @project = Project.first
- # link_to_label(label)
- #
- # # Force the generated link to use a provided project
- # link_to_label(label, project: Project.last)
- #
- # # Customize link body with a block
- # link_to_label(label) { "My Custom Label Text" }
- #
- # Returns a String
- def link_to_label(label, project: nil, &block)
- project ||= @project || label.project
- link = namespace_project_issues_path(project.namespace, project,
- label_name: label.name)
-
- if block_given?
- link_to link, &block
- else
- link_to render_colored_label(label), link
- end
- end
-
- def project_label_names
- @project.labels.pluck(:title)
- end
-
- def render_colored_label(label)
- 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.html_safe
- end
-
- def suggested_colors
- [
- '#0033CC',
- '#428BCA',
- '#44AD8E',
- '#A8D695',
- '#5CB85C',
- '#69D100',
- '#004E00',
- '#34495E',
- '#7F8C8D',
- '#A295D6',
- '#5843AD',
- '#8E44AD',
- '#FFECDB',
- '#AD4363',
- '#D10069',
- '#CC0033',
- '#FF0000',
- '#D9534F',
- '#D1D100',
- '#F0AD4E',
- '#AD8D43'
- ]
- end
-
- def text_color_for_bg(bg_color)
- r, g, b = bg_color.slice(1,7).scan(/.{2}/).map(&:hex)
-
- if (r + g + b) > 500
- '#333333'
- else
- '#FFFFFF'
- end
- end
-
- def project_labels_options(project)
- options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name])
- end
-
- # Required for Gitlab::Markdown::LabelReferenceFilter
- module_function :render_colored_label, :text_color_for_bg, :escape_once
-end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
deleted file mode 100644
index f8169b4f288..00000000000
--- a/app/helpers/merge_requests_helper.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-module MergeRequestsHelper
- def new_mr_path_from_push_event(event)
- target_project = event.project.forked_from_project || event.project
- new_namespace_project_merge_request_path(
- event.project.namespace,
- event.project,
- new_mr_from_push_event(event, target_project)
- )
- end
-
- def new_mr_path_for_fork_from_push_event(event)
- new_namespace_project_merge_request_path(
- event.project.namespace,
- event.project,
- new_mr_from_push_event(event, event.project.forked_from_project)
- )
- end
-
- def new_mr_from_push_event(event, target_project)
- {
- merge_request: {
- source_project_id: event.project.id,
- target_project_id: target_project.id,
- source_branch: event.branch_name,
- target_branch: target_project.repository.root_ref
- }
- }
- end
-
- def mr_css_classes(mr)
- classes = "merge-request"
- classes << " closed" if mr.closed?
- classes << " merged" if mr.merged?
- classes
- end
-
- def ci_build_details_path(merge_request)
- merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch)
- end
-
- def merge_path_description(merge_request, separator)
- if merge_request.for_fork?
- "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}"
- else
- "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}"
- end
- end
-
- def issues_sentence(issues)
- issues.map { |i| "##{i.iid}" }.to_sentence
- end
-
- def mr_change_branches_path(merge_request)
- new_namespace_project_merge_request_path(
- @project.namespace, @project,
- merge_request: {
- source_project_id: @merge_request.source_project_id,
- target_project_id: @merge_request.target_project_id,
- source_branch: @merge_request.source_branch,
- target_branch: nil
- }
- )
- end
-
- def source_branch_with_namespace(merge_request)
- if merge_request.for_fork?
- namespace = link_to(merge_request.source_project_namespace,
- project_path(merge_request.source_project))
- namespace + ":#{merge_request.source_branch}"
- else
- merge_request.source_branch
- end
- end
-end
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
deleted file mode 100644
index 132a893e532..00000000000
--- a/app/helpers/milestones_helper.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-module MilestonesHelper
- def milestones_filter_path(opts = {})
- if @project
- namespace_project_milestones_path(@project.namespace, @project, opts)
- elsif @group
- group_milestones_path(@group, opts)
- else
- dashboard_milestones_path(opts)
- end
- end
-
- def milestone_progress_bar(milestone)
- options = {
- class: 'progress-bar progress-bar-success',
- style: "width: #{milestone.percent_complete}%;"
- }
-
- content_tag :div, class: 'progress' do
- content_tag :div, nil, options
- end
- end
-
- def projects_milestones_options
- milestones =
- if @project
- @project.milestones
- else
- Milestone.where(project_id: @projects)
- end.active
-
- grouped_milestones = Milestones::GroupService.new(milestones).execute
- grouped_milestones.unshift(Milestone::None)
-
- options_from_collection_for_select(grouped_milestones, 'title', 'title', params[:milestone_title])
- end
-end
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
deleted file mode 100644
index b3132a1f3ba..00000000000
--- a/app/helpers/namespaces_helper.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-module NamespacesHelper
- def namespaces_options(selected = :current_user, scope = :default)
- groups = current_user.owned_groups + current_user.masters_groups
- users = [current_user.namespace]
-
- group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ]
- users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ]
-
- options = []
- options << group_opts
- options << users_opts
-
- if selected == :current_user && current_user.namespace
- selected = current_user.namespace.id
- end
-
- grouped_options_for_select(options, selected)
- end
-
- def namespace_select_tag(id, opts = {})
- css_class = "ajax-namespace-select "
- css_class << "multiselect " if opts[:multiple]
- css_class << (opts[:class] || '')
- value = opts[:selected] || ''
-
- hidden_field_tag(id, value, class: css_class)
- end
-
- def namespace_icon(namespace, size = 40)
- if namespace.kind_of?(Group)
- group_icon(namespace)
- else
- avatar_icon(namespace.owner.email, size)
- end
- end
-end
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
deleted file mode 100644
index 9b1dd8b8e54..00000000000
--- a/app/helpers/nav_helper.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module NavHelper
- def nav_menu_collapsed?
- cookies[:collapsed_nav] == 'true'
- end
-
- def nav_sidebar_class
- if nav_menu_collapsed?
- "page-sidebar-collapsed"
- else
- "page-sidebar-expanded"
- end
- end
-
- def nav_header_class
- if nav_menu_collapsed?
- "header-collapsed"
- else
- "header-expanded"
- end
- end
-end
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
deleted file mode 100644
index 5f0c921413a..00000000000
--- a/app/helpers/notes_helper.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-module NotesHelper
- # Helps to distinguish e.g. commit notes in mr notes list
- def note_for_main_target?(note)
- (@noteable.class.name == note.noteable_type && !note.for_diff_line?)
- end
-
- def note_target_fields(note)
- hidden_field_tag(:target_type, note.noteable.class.name.underscore) +
- hidden_field_tag(:target_id, note.noteable.id)
- end
-
- def note_editable?(note)
- note.editable? && can?(current_user, :admin_note, note)
- end
-
- def link_to_commit_diff_line_note(note)
- if note.for_commit_diff_line?
- link_to(
- "#{note.diff_file_name}:L#{note.diff_new_line}",
- namespace_project_commit_path(@project.namespace, @project,
- note.noteable, anchor: note.line_code)
- )
- end
- end
-
- def noteable_json(noteable)
- {
- id: noteable.id,
- class: noteable.class.name,
- resources: noteable.class.table_name,
- project_id: noteable.project.id,
- }.to_json
- end
-
- def link_to_new_diff_note(line_code, line_type = nil)
- discussion_id = Note.build_discussion_id(
- @comments_target[:noteable_type],
- @comments_target[:noteable_id] || @comments_target[:commit_id],
- line_code
- )
-
- data = {
- noteable_type: @comments_target[:noteable_type],
- noteable_id: @comments_target[:noteable_id],
- commit_id: @comments_target[:commit_id],
- line_code: line_code,
- discussion_id: discussion_id,
- line_type: line_type
- }
-
- button_tag(class: 'btn add-diff-note js-add-diff-note-button',
- data: data,
- title: 'Add a comment to this line') do
- icon('comment-o')
- end
- end
-
- def link_to_reply_diff(note, line_type = nil)
- return unless current_user
-
- data = {
- noteable_type: note.noteable_type,
- noteable_id: note.noteable_id,
- commit_id: note.commit_id,
- line_code: note.line_code,
- discussion_id: note.discussion_id,
- line_type: line_type
- }
-
- button_tag class: 'btn reply-btn js-discussion-reply-button',
- data: data, title: 'Add a reply' do
- link_text = icon('comment')
- link_text << ' Reply'
- end
- end
-end
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
deleted file mode 100644
index 2f8e64c375f..00000000000
--- a/app/helpers/notifications_helper.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module NotificationsHelper
- include IconsHelper
-
- def notification_icon(notification)
- if notification.disabled?
- icon('volume-off', class: 'ns-mute')
- elsif notification.participating?
- icon('volume-down', class: 'ns-part')
- elsif notification.watch?
- icon('volume-up', class: 'ns-watch')
- else
- icon('circle-o', class: 'ns-default')
- end
- end
-end
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
deleted file mode 100644
index 01b6a63552c..00000000000
--- a/app/helpers/page_layout_helper.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-module PageLayoutHelper
- def page_title(*titles)
- @page_title ||= []
-
- @page_title.push(*titles.compact) if titles.any?
-
- @page_title.join(" | ")
- end
-
- def header_title(title = nil, title_url = nil)
- if title
- @header_title = title
- @header_title_url = title_url
- else
- @header_title_url ? link_to(@header_title, @header_title_url) : @header_title
- end
- end
-
- def sidebar(name = nil)
- if name
- @sidebar = name
- else
- @sidebar
- end
- end
-end
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
deleted file mode 100644
index ea774e28ecf..00000000000
--- a/app/helpers/preferences_helper.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# Helper methods for per-User preferences
-module PreferencesHelper
- COLOR_SCHEMES = {
- 1 => 'white',
- 2 => 'dark',
- 3 => 'solarized-light',
- 4 => 'solarized-dark',
- 5 => 'monokai',
- }
- COLOR_SCHEMES.default = 'white'
-
- # Helper method to access the COLOR_SCHEMES
- #
- # The keys are the `color_scheme_ids`
- # The values are the `name` of the scheme.
- #
- # The preview images are `name-scheme-preview.png`
- # The stylesheets should use the css class `.name`
- def color_schemes
- COLOR_SCHEMES.freeze
- end
-
- # Maps `dashboard` values to more user-friendly option text
- DASHBOARD_CHOICES = {
- projects: 'Your Projects (default)',
- stars: 'Starred Projects'
- }.with_indifferent_access.freeze
-
- # Returns an Array usable by a select field for more user-friendly option text
- def dashboard_choices
- defined = User.dashboards
-
- if defined.size != DASHBOARD_CHOICES.size
- # Ensure that anyone adding new options updates this method too
- raise RuntimeError, "`User` defines #{defined.size} dashboard choices," +
- " but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}."
- else
- defined.map do |key, _|
- # Use `fetch` so `KeyError` gets raised when a key is missing
- [DASHBOARD_CHOICES.fetch(key), key]
- end
- end
- end
-
- def project_view_choices
- [
- ['Readme (default)', :readme],
- ['Activity view', :activity]
- ]
- end
-
- def user_application_theme
- theme = Gitlab::Themes.by_id(current_user.try(:theme_id))
- theme.css_class
- end
-
- def user_color_scheme_class
- COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
- end
-
- def prefer_readme?
- !current_user ||
- current_user.project_view == 'readme'
- end
-end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
deleted file mode 100644
index ab9b068de05..00000000000
--- a/app/helpers/projects_helper.rb
+++ /dev/null
@@ -1,330 +0,0 @@
-module ProjectsHelper
- def remove_from_project_team_message(project, member)
- if member.user
- "You are going to remove #{member.user.name} from #{project.name} project team. Are you sure?"
- else
- "You are going to revoke the invitation for #{member.invite_email} to join #{project.name} project team. Are you sure?"
- end
- end
-
- def link_to_project(project)
- link_to [project.namespace.becomes(Namespace), project] do
- title = content_tag(:span, project.name, class: 'project-name')
-
- if project.namespace
- namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'namespace-name')
- title = namespace + title
- end
-
- title
- end
- end
-
- def link_to_member(project, author, opts = {})
- default_opts = { avatar: true, name: true, size: 16, author_class: 'author' }
- opts = default_opts.merge(opts)
-
- return "(deleted)" unless author
-
- author_html = ""
-
- # Build avatar image tag
- author_html << image_tag(avatar_icon(author.try(:email), 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]
-
- author_html = author_html.html_safe
-
- if opts[:name]
- link_to(author_html, user_path(author), class: "author_link").html_safe
- else
- link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => sanitize(author.name) } ).html_safe
- end
- end
-
- def project_title(project)
- if project.group
- content_tag :span do
- link_to(
- simple_sanitize(project.group.name), group_path(project.group)
- ) + ' / ' +
- link_to(simple_sanitize(project.name),
- project_path(project))
- end
- else
- owner = project.namespace.owner
- content_tag :span do
- link_to(
- simple_sanitize(owner.name), user_path(owner)
- ) + ' / ' +
- link_to(simple_sanitize(project.name),
- project_path(project))
- end
- end
- end
-
- def remove_project_message(project)
- "You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?"
- end
-
- def transfer_project_message(project)
- "You are going to transfer #{project.name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
- end
-
- def project_nav_tabs
- @nav_tabs ||= get_project_nav_tabs(@project, current_user)
- end
-
- def project_nav_tab?(name)
- project_nav_tabs.include? name
- end
-
- def project_active_milestones
- @project.milestones.active.order("due_date, title ASC")
- end
-
- def project_for_deploy_key(deploy_key)
- if deploy_key.projects.include?(@project)
- @project
- else
- deploy_key.projects.find { |project| can?(current_user, :read_project, project) }
- end
- end
-
- def can_change_visibility_level?(project, current_user)
- return false unless can?(current_user, :change_visibility_level, project)
-
- if project.forked?
- project.forked_from_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE
- else
- true
- end
- end
-
- private
-
- def get_project_nav_tabs(project, current_user)
- nav_tabs = [:home]
-
- if !project.empty_repo? && can?(current_user, :download_code, project)
- nav_tabs << [:files, :commits, :network, :graphs]
- end
-
- if project.repo_exists? && can?(current_user, :read_merge_request, project)
- nav_tabs << :merge_requests
- end
-
- if can?(current_user, :admin_project, project)
- nav_tabs << :settings
- end
-
- if can?(current_user, :read_issue, project)
- nav_tabs << :issues
- end
-
- if can?(current_user, :read_wiki, project)
- nav_tabs << :wiki
- end
-
- if can?(current_user, :read_project_snippet, project)
- nav_tabs << :snippets
- end
-
- if can?(current_user, :read_label, project)
- nav_tabs << :labels
- end
-
- if can?(current_user, :read_milestone, project)
- nav_tabs << :milestones
- end
-
- nav_tabs.flatten
- end
-
- def git_user_name
- if current_user
- current_user.name
- else
- "Your name"
- end
- end
-
- def git_user_email
- if current_user
- current_user.email
- else
- "your@email.com"
- end
- end
-
- def repository_size(project = nil)
- "#{(project || @project).repository_size} MB"
- rescue
- # In order to prevent 500 error
- # when application cannot allocate memory
- # to calculate repo size - just show 'Unknown'
- 'unknown'
- end
-
- def default_url_to_repo(project = nil)
- project = project || @project
- current_user ? project.url_to_repo : project.http_url_to_repo
- end
-
- def default_clone_protocol
- current_user ? "ssh" : "http"
- end
-
- def project_last_activity(project)
- if project.last_activity_at
- time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago')
- else
- "Never"
- end
- end
-
- def add_contribution_guide_path(project)
- if project && !project.repository.contribution_guide
- namespace_project_new_blob_path(
- project.namespace,
- project,
- project.default_branch,
- file_name: "CONTRIBUTING.md",
- commit_message: "Add contribution guide"
- )
- end
- end
-
- def add_changelog_path(project)
- if project && !project.repository.changelog
- namespace_project_new_blob_path(
- project.namespace,
- project,
- project.default_branch,
- file_name: "CHANGELOG",
- commit_message: "Add changelog"
- )
- end
- end
-
- def add_license_path(project)
- if project && !project.repository.license
- namespace_project_new_blob_path(
- project.namespace,
- project,
- project.default_branch,
- file_name: "LICENSE",
- commit_message: "Add license"
- )
- end
- end
-
- def contribution_guide_path(project)
- if project && contribution_guide = project.repository.contribution_guide
- namespace_project_blob_path(
- project.namespace,
- project,
- tree_join(project.default_branch,
- contribution_guide.name)
- )
- end
- end
-
- def readme_path(project)
- filename_path(project, :readme)
- end
-
- def changelog_path(project)
- filename_path(project, :changelog)
- end
-
- def license_path(project)
- filename_path(project, :license)
- end
-
- def version_path(project)
- filename_path(project, :version)
- end
-
- def hidden_pass_url(original_url)
- result = URI(original_url)
- result.password = '*****' unless result.password.nil?
- result
- rescue
- original_url
- end
-
- def project_wiki_path_with_version(proj, page, version, is_newest)
- url_params = is_newest ? {} : { version_id: version }
- namespace_project_wiki_path(proj.namespace, proj, page, url_params)
- end
-
- def project_status_css_class(status)
- case status
- when "started"
- "active"
- when "failed"
- "danger"
- when "finished"
- "success"
- end
- end
-
- def user_max_access_in_project(user, project)
- level = project.team.max_member_access(user)
-
- if level
- Gitlab::Access.options_with_owner.key(level)
- end
- end
-
- def leave_project_message(project)
- "Are you sure you want to leave \"#{project.name}\" project?"
- end
-
- def new_readme_path
- ref = @repository.root_ref if @repository
- ref ||= 'master'
-
- namespace_project_new_blob_path(@project.namespace, @project, tree_join(ref), file_name: 'README.md')
- end
-
- def last_push_event
- if current_user
- current_user.recent_push(@project.id)
- end
- end
-
- def readme_cache_key
- sha = @project.commit.try(:sha) || 'nil'
- [@project.id, sha, "readme"].join('-')
- end
-
- def round_commit_count(project)
- count = project.commit_count
-
- if count > 10000
- '10000+'
- elsif count > 5000
- '5000+'
- elsif count > 1000
- '1000+'
- else
- count
- end
- end
-
- private
-
- def filename_path(project, filename)
- if project && blob = project.repository.send(filename)
- namespace_project_blob_path(
- project.namespace,
- project,
- tree_join(project.default_branch,
- blob.name)
- )
- end
- end
-end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
deleted file mode 100644
index c31a556ff7b..00000000000
--- a/app/helpers/search_helper.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-module SearchHelper
- def search_autocomplete_opts(term)
- return unless current_user
-
- resources_results = [
- groups_autocomplete(term),
- projects_autocomplete(term)
- ].flatten
-
- generic_results = project_autocomplete + default_autocomplete + help_autocomplete
- generic_results.select! { |result| result[:label] =~ Regexp.new(term, "i") }
-
- [
- resources_results,
- generic_results
- ].flatten.uniq do |item|
- item[:label]
- end
- end
-
- private
-
- # Autocomplete results for various settings pages
- def default_autocomplete
- [
- { label: "Profile settings", url: profile_path },
- { label: "SSH Keys", url: profile_keys_path },
- { label: "Dashboard", url: root_path },
- { label: "Admin Section", url: admin_root_path },
- ]
- end
-
- # Autocomplete results for internal help pages
- def help_autocomplete
- [
- { label: "help: API Help", url: help_page_path("api", "README") },
- { label: "help: Markdown Help", url: help_page_path("markdown", "markdown") },
- { label: "help: Permissions Help", url: help_page_path("permissions", "permissions") },
- { label: "help: Public Access Help", url: help_page_path("public_access", "public_access") },
- { 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: 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.exists? && @project.repository.root_ref
- prefix = search_result_sanitize(@project.name_with_namespace)
- ref = @ref || @project.repository.root_ref
-
- [
- { label: "#{prefix} - Files", url: namespace_project_tree_path(@project.namespace, @project, ref) },
- { label: "#{prefix} - Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) },
- { label: "#{prefix} - Network", url: namespace_project_network_path(@project.namespace, @project, ref) },
- { label: "#{prefix} - Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) },
- { label: "#{prefix} - Issues", url: namespace_project_issues_path(@project.namespace, @project) },
- { label: "#{prefix} - Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) },
- { label: "#{prefix} - Milestones", url: namespace_project_milestones_path(@project.namespace, @project) },
- { label: "#{prefix} - Snippets", url: namespace_project_snippets_path(@project.namespace, @project) },
- { label: "#{prefix} - Members", url: namespace_project_project_members_path(@project.namespace, @project) },
- { label: "#{prefix} - Wiki", url: namespace_project_wikis_path(@project.namespace, @project) },
- ]
- else
- []
- end
- end
-
- # Autocomplete results for the current user's groups
- def groups_autocomplete(term, limit = 5)
- current_user.authorized_groups.search(term).limit(limit).map do |group|
- {
- label: "group: #{search_result_sanitize(group.name)}",
- url: group_path(group)
- }
- end
- end
-
- # Autocomplete results for the current user's projects
- def projects_autocomplete(term, limit = 5)
- ProjectsFinder.new.execute(current_user).search_by_title(term).
- sorted_by_stars.non_archived.limit(limit).map do |p|
- {
- label: "project: #{search_result_sanitize(p.name_with_namespace)}",
- url: namespace_project_path(p.namespace, p)
- }
- end
- end
-
- def search_result_sanitize(str)
- Sanitize.clean(str)
- end
-
- def search_filter_path(options={})
- exist_opts = {
- search: params[:search],
- project_id: params[:project_id],
- group_id: params[:group_id],
- scope: params[:scope]
- }
-
- options = exist_opts.merge(options)
- search_path(options)
- end
-
- # Sanitize html generated after parsing markdown from issue description or comment
- def search_md_sanitize(html)
- sanitize(html, tags: %w(a p ol ul li pre code))
- end
-end
diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb
deleted file mode 100644
index 12fce8db701..00000000000
--- a/app/helpers/selects_helper.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-module SelectsHelper
- def users_select_tag(id, opts = {})
- css_class = "ajax-users-select "
- css_class << "multiselect " if opts[:multiple]
- css_class << (opts[:class] || '')
- value = opts[:selected] || ''
- placeholder = opts[:placeholder] || 'Search for a user'
-
- null_user = opts[:null_user] || false
- any_user = opts[:any_user] || false
- email_user = opts[:email_user] || false
- first_user = opts[:first_user] && current_user ? current_user.username : false
- current_user = opts[:current_user] || false
- project = opts[:project] || @project
-
- html = {
- class: css_class,
- 'data-placeholder' => placeholder,
- 'data-null-user' => null_user,
- 'data-any-user' => any_user,
- 'data-email-user' => email_user,
- 'data-first-user' => first_user,
- 'data-current-user' => current_user
- }
-
- unless opts[:scope] == :all
- if project
- html['data-project-id'] = project.id
- elsif @group
- html['data-group-id'] = @group.id
- end
- end
-
- hidden_field_tag(id, value, html)
- end
-
- def groups_select_tag(id, opts = {})
- css_class = "ajax-groups-select "
- css_class << "multiselect " if opts[:multiple]
- css_class << (opts[:class] || '')
- value = opts[:selected] || ''
-
- hidden_field_tag(id, value, class: css_class)
- end
-end
diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb
deleted file mode 100644
index 906cb12cd48..00000000000
--- a/app/helpers/snippets_helper.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-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,
- snippet.project, snippet)
- else
- snippet_path(snippet)
- end
- end
-end
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
deleted file mode 100644
index bb12d43f397..00000000000
--- a/app/helpers/sorting_helper.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-module SortingHelper
- def 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,
- sort_value_milestone_soon => sort_title_milestone_soon,
- sort_value_milestone_later => sort_title_milestone_later,
- sort_value_largest_repo => sort_title_largest_repo,
- sort_value_recently_signin => sort_title_recently_signin,
- sort_value_oldest_signin => sort_title_oldest_signin,
- }
- end
-
- def sort_title_oldest_updated
- 'Oldest updated'
- end
-
- def sort_title_recently_updated
- 'Recently updated'
- end
-
- def sort_title_oldest_created
- 'Oldest created'
- end
-
- def sort_title_recently_created
- 'Recently created'
- end
-
- def sort_title_milestone_soon
- 'Milestone due soon'
- end
-
- def sort_title_milestone_later
- 'Milestone due later'
- end
-
- def sort_title_name
- 'Name'
- end
-
- def sort_title_largest_repo
- 'Largest repository'
- end
-
- def sort_title_recently_signin
- 'Recent sign in'
- end
-
- def sort_title_oldest_signin
- 'Oldest sign in'
- end
-
- def sort_value_oldest_updated
- 'updated_asc'
- end
-
- def sort_value_recently_updated
- 'updated_desc'
- end
-
- def sort_value_oldest_created
- 'created_asc'
- end
-
- def sort_value_recently_created
- 'created_desc'
- end
-
- def sort_value_milestone_soon
- 'milestone_due_asc'
- end
-
- def sort_value_milestone_later
- 'milestone_due_desc'
- end
-
- def sort_value_name
- 'name_asc'
- end
-
- def sort_value_largest_repo
- 'repository_size_desc'
- end
-
- def sort_value_recently_signin
- 'recent_sign_in'
- end
-
- def sort_value_oldest_signin
- 'oldest_sign_in'
- end
-end
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
deleted file mode 100644
index b3f50ceebe4..00000000000
--- a/app/helpers/submodule_helper.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-module SubmoduleHelper
- include Gitlab::ShellAdapter
-
- # links to files listing for submodule if submodule is a project on this server
- def submodule_links(submodule_item, ref = nil, repository = @repository)
- url = repository.submodule_url_for(ref, submodule_item.path)
-
- return url, nil unless url =~ /([^\/:]+)\/([^\/]+\.git)\Z/
-
- namespace = $1
- project = $2
- project.chomp!('.git')
-
- if self_url?(url, namespace, project)
- return namespace_project_path(namespace, project),
- namespace_project_tree_path(namespace, project,
- submodule_item.id)
- elsif relative_self_url?(url)
- relative_self_links(url, submodule_item.id)
- elsif github_dot_com_url?(url)
- standard_links('github.com', namespace, project, submodule_item.id)
- elsif gitlab_dot_com_url?(url)
- standard_links('gitlab.com', namespace, project, submodule_item.id)
- else
- return url, nil
- end
- end
-
- protected
-
- def github_dot_com_url?(url)
- url =~ /github\.com[\/:][^\/]+\/[^\/]+\Z/
- end
-
- def gitlab_dot_com_url?(url)
- url =~ /gitlab\.com[\/:][^\/]+\/[^\/]+\Z/
- end
-
- def self_url?(url, namespace, project)
- return true if url == [ Gitlab.config.gitlab.url, '/', namespace, '/',
- project, '.git' ].join('')
- url == gitlab_shell.url_to_repo([namespace, '/', project].join(''))
- end
-
- def relative_self_url?(url)
- # (./)?(../repo.git) || (./)?(../../project/repo.git) )
- url =~ /\A((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*\.git\z/ || url =~ /\A((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*\.git\z/
- end
-
- def standard_links(host, namespace, project, commit)
- base = [ 'https://', host, '/', namespace, '/', project ].join('')
- [base, [ base, '/tree/', commit ].join('')]
- end
-
- def relative_self_links(url, commit)
- # Map relative links to a namespace and project
- # For example:
- # ../bar.git -> same namespace, repo bar
- # ../foo/bar.git -> namespace foo, repo bar
- # ../../foo/bar/baz.git -> namespace bar, repo baz
- components = url.split('/')
- base = components.pop.gsub(/.git$/, '')
- namespace = components.pop.gsub(/^\.\.$/, '')
-
- if namespace.empty?
- namespace = @project.namespace.path
- end
-
- [
- namespace_project_path(namespace, base),
- namespace_project_tree_path(namespace, base, commit)
- ]
- end
-end
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
deleted file mode 100644
index 0e7d8065ac7..00000000000
--- a/app/helpers/tab_helper.rb
+++ /dev/null
@@ -1,131 +0,0 @@
-module TabHelper
- # Navigation link helper
- #
- # Returns an `li` element with an 'active' class if the supplied
- # controller(s) and/or action(s) are currently active. The content of the
- # element is the value passed to the block.
- #
- # options - The options hash used to determine if the element is "active" (default: {})
- # :controller - One or more controller names to check (optional).
- # :action - One or more action names to check (optional).
- # :path - A shorthand path, such as 'dashboard#index', to check (optional).
- # :html_options - Extra options to be passed to the list element (optional).
- # block - An optional block that will become the contents of the returned
- # `li` element.
- #
- # When both :controller and :action are specified, BOTH must match in order
- # to be marked as active. When only one is given, either can match.
- #
- # Examples
- #
- # # Assuming we're on TreeController#show
- #
- # # Controller matches, but action doesn't
- # nav_link(controller: [:tree, :refs], action: :edit) { "Hello" }
- # # => '<li>Hello</li>'
- #
- # # Controller matches
- # nav_link(controller: [:tree, :refs]) { "Hello" }
- # # => '<li class="active">Hello</li>'
- #
- # # Several paths
- # nav_link(path: ['tree#show', 'profile#show']) { "Hello" }
- # # => '<li class="active">Hello</li>'
- #
- # # Shorthand path
- # nav_link(path: 'tree#show') { "Hello" }
- # # => '<li class="active">Hello</li>'
- #
- # # Supplying custom options for the list element
- # nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" }
- # # => '<li class="home active">Hello</li>'
- #
- # Returns a list item element String
- def nav_link(options = {}, &block)
- klass = active_nav_link?(options) ? 'active' : ''
-
- # Add our custom class into the html_options, which may or may not exist
- # and which may or may not already have a :class key
- o = options.delete(:html_options) || {}
- o[:class] ||= ''
- o[:class] += ' ' + klass
- o[:class].strip!
-
- if block_given?
- content_tag(:li, capture(&block), o)
- else
- content_tag(:li, nil, o)
- end
- end
-
- def active_nav_link?(options)
- if path = options.delete(:path)
- unless path.respond_to?(:each)
- path = [path]
- end
-
- path.any? do |single_path|
- current_path?(single_path)
- end
- elsif page = options.delete(:page)
- unless page.respond_to?(:each)
- page = [page]
- end
-
- page.any? do |single_page|
- current_page?(single_page)
- end
- else
- c = options.delete(:controller)
- a = options.delete(:action)
-
- if c && a
- # When given both options, make sure BOTH are true
- current_controller?(*c) && current_action?(*a)
- else
- # Otherwise check EITHER option
- current_controller?(*c) || current_action?(*a)
- end
- end
- end
-
- def current_path?(path)
- c, a, _ = path.split('#')
- current_controller?(c) && current_action?(a)
- end
-
- def project_tab_class
- return "active" if current_page?(controller: "/projects", action: :edit, id: @project)
-
- if ['services', 'hooks', 'deploy_keys', 'protected_branches'].include? controller.controller_name
- "active"
- end
- end
-
- def branches_tab_class
- if current_controller?(:protected_branches) ||
- current_controller?(:branches) ||
- current_page?(namespace_project_repository_path(@project.namespace,
- @project))
- 'active'
- end
- end
-
- # Use nav_tab for save controller/action but different params
- def nav_tab(key, value, &block)
- o = {}
- o[:class] = ""
-
- if value.nil?
- o[:class] << " active" if params[key].blank?
- else
- o[:class] << " active" if params[key] == value
- end
-
- if block_given?
- content_tag(:li, capture(&block), o)
- else
- content_tag(:li, nil, o)
- end
- end
-end
diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb
deleted file mode 100644
index fb85544df2d..00000000000
--- a/app/helpers/tags_helper.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-module TagsHelper
- def tag_path(tag)
- "/tags/#{tag}"
- end
-
- def tag_list(project)
- html = ''
- project.tag_list.each do |tag|
- html << link_to(tag, tag_path(tag))
- end
-
- html.html_safe
- end
-end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
deleted file mode 100644
index 03a49e119b8..00000000000
--- a/app/helpers/tree_helper.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-module TreeHelper
- # Sorts a repository's tree so that folders are before files and renders
- # their corresponding partials
- #
- # contents - A Grit::Tree object for the current tree
- def render_tree(tree)
- # Render Folders before Files/Submodules
- folders, files, submodules = tree.trees, tree.blobs, tree.submodules
-
- tree = ""
-
- # Render folders if we have any
- tree << render(partial: 'projects/tree/tree_item', collection: folders,
- locals: { type: 'folder' }) if folders.present?
-
- # Render files if we have any
- tree << render(partial: 'projects/tree/blob_item', collection: files,
- locals: { type: 'file' }) if files.present?
-
- # Render submodules if we have any
- tree << render(partial: 'projects/tree/submodule_item',
- collection: submodules) if submodules.present?
-
- tree.html_safe
- end
-
- def render_readme(readme)
- render_markup(readme.name, readme.data)
- end
-
- # Return an image icon depending on the file type and mode
- #
- # type - String type of the tree item; either 'folder' or 'file'
- # mode - File unix mode
- # name - File name
- def tree_icon(type, mode, name)
- icon("#{file_type_icon_class(type, mode, name)} fw")
- end
-
- def tree_hex_class(content)
- "file_#{hexdigest(content.name)}"
- end
-
- # Simple shortcut to File.join
- def tree_join(*args)
- File.join(*args)
- end
-
- def allowed_tree_edit?(project = nil, ref = nil)
- project ||= @project
- ref ||= @ref
- return false unless project.repository.branch_names.include?(ref)
-
- ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
- end
-
- def tree_breadcrumbs(tree, max_links = 2)
- if @path.present?
- part_path = ""
- parts = @path.split('/')
-
- yield('..', nil) if parts.count > max_links
-
- parts.each do |part|
- part_path = File.join(part_path, part) unless part_path.empty?
- part_path = part if part_path.empty?
-
- next unless parts.last(2).include?(part) if parts.count > max_links
- yield(part, tree_join(@ref, part_path))
- end
- end
- end
-
- def up_dir_path
- file = File.join(@path, "..")
- tree_join(@ref, file)
- end
-
- # returns the relative path of the first subdir that doesn't have only one directory descendant
- def flatten_tree(tree)
- subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path)
- if subtree.count == 1 && subtree.first.dir?
- return tree_join(tree.name, flatten_tree(subtree.first))
- else
- return tree.name
- end
- end
-end
diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb
deleted file mode 100644
index f64d730b448..00000000000
--- a/app/helpers/version_check_helper.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module VersionCheckHelper
- def version_status_badge
- if Rails.env.production?
- image_tag VersionCheck.new.url
- end
- end
-end
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
deleted file mode 100644
index b52cd23aba2..00000000000
--- a/app/helpers/visibility_level_helper.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-module VisibilityLevelHelper
- def visibility_level_color(level)
- case level
- when Gitlab::VisibilityLevel::PRIVATE
- 'vs-private'
- when Gitlab::VisibilityLevel::INTERNAL
- 'vs-internal'
- when Gitlab::VisibilityLevel::PUBLIC
- 'vs-public'
- end
- end
-
- # Return the description for the +level+ argument.
- #
- # +level+ One of the Gitlab::VisibilityLevel constants
- # +form_model+ Either a model object (Project, Snippet, etc.) or the name of
- # a Project or Snippet class.
- def visibility_level_description(level, form_model)
- case form_model.is_a?(String) ? form_model : form_model.class.name
- when 'PersonalSnippet', 'ProjectSnippet', 'Snippet'
- snippet_visibility_level_description(level)
- when 'Project'
- project_visibility_level_description(level)
- end
- end
-
- def project_visibility_level_description(level)
- capture_haml do
- haml_tag :span do
- case level
- when Gitlab::VisibilityLevel::PRIVATE
- haml_concat "Project access must be granted explicitly for each user."
- when Gitlab::VisibilityLevel::INTERNAL
- haml_concat "The project can be cloned by"
- haml_concat "any logged in user."
- when Gitlab::VisibilityLevel::PUBLIC
- haml_concat "The project can be cloned"
- haml_concat "without any"
- haml_concat "authentication."
- end
- end
- end
- end
-
- def snippet_visibility_level_description(level)
- capture_haml do
- haml_tag :span do
- case level
- when Gitlab::VisibilityLevel::PRIVATE
- haml_concat "The snippet is visible only for me."
- when Gitlab::VisibilityLevel::INTERNAL
- haml_concat "The snippet is visible for any logged in user."
- when Gitlab::VisibilityLevel::PUBLIC
- haml_concat "The snippet can be accessed"
- haml_concat "without any"
- haml_concat "authentication."
- end
- end
- end
- end
-
- def visibility_level_icon(level)
- case level
- when Gitlab::VisibilityLevel::PRIVATE
- private_icon
- when Gitlab::VisibilityLevel::INTERNAL
- internal_icon
- when Gitlab::VisibilityLevel::PUBLIC
- public_icon
- end
- end
-
- def visibility_level_label(level)
- Project.visibility_levels.key(level)
- end
-
- def restricted_visibility_levels(show_all = false)
- return [] if current_user.is_admin? && !show_all
- current_application_settings.restricted_visibility_levels || []
- end
-
- def default_project_visibility
- current_application_settings.default_project_visibility
- end
-
- def default_snippet_visibility
- current_application_settings.default_snippet_visibility
- end
-
- def skip_level?(form_model, level)
- form_model.is_a?(Project) &&
- form_model.forked? &&
- !Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level)
- end
-end
diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb
deleted file mode 100644
index f8a96516e61..00000000000
--- a/app/helpers/wiki_helper.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-module WikiHelper
- # Rails v4.1.9+ escapes all model IDs, converting slashes into %2F. The
- # only way around this is to implement our own path generators.
- def namespace_project_wiki_path(namespace, project, wiki_page, *args)
- slug =
- case wiki_page
- when Symbol
- wiki_page
- when String
- wiki_page
- else
- wiki_page.slug
- end
- namespace_project_path(namespace, project) + "/wikis/#{slug}"
- end
-
- def edit_namespace_project_wiki_path(namespace, project, wiki_page, *args)
- namespace_project_wiki_path(namespace, project, wiki_page) + '/edit'
- end
-
- def history_namespace_project_wiki_path(namespace, project, wiki_page, *args)
- namespace_project_wiki_path(namespace, project, wiki_page) + '/history'
- end
-end