Module: ProjectsHelper

Included in:
Projects::Settings::AccessTokensController
Defined in:
app/helpers/projects_helper.rb

Instance Method Summary collapse

Instance Method Details

#any_projects?(projects) ⇒ Boolean

Returns true if any projects are present.

If the relation has a LIMIT applied we'll cast the relation to an Array since repeated any? checks would otherwise result in multiple COUNT queries being executed.

If no limit is applied we'll just issue a COUNT since the result set could be too large to load into memory.

Returns:

  • (Boolean)

250
251
252
253
254
255
256
257
258
# File 'app/helpers/projects_helper.rb', line 250

def any_projects?(projects)
  return projects.any? if projects.is_a?(Array)

  if projects.limit_value
    projects.to_a.any?
  else
    projects.except(:offset).any?
  end
end

#author_content_tag(author, opts = {}) ⇒ Object


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'app/helpers/projects_helper.rb', line 35

def (author, opts = {})
  default_opts = { author_class: 'author', tooltip: false, by_username: false }
  opts = default_opts.merge(opts)

  has_tooltip = !opts[:by_username] && opts[:tooltip]

  username = opts[:by_username] ? author.to_reference : author.name
  name_tag_options = { class: [opts[:author_class]] }

  if has_tooltip
    name_tag_options[:title] = author.to_reference
    name_tag_options[:data] = { placement: 'top' }
    name_tag_options[:class] << 'has-tooltip'
  end

  # NOTE: ActionView::Helpers::TagHelper#content_tag HTML escapes username
  (:span, username, name_tag_options)
end

#autodeploy_flash_notice(branch_name) ⇒ Object


186
187
188
189
# File 'app/helpers/projects_helper.rb', line 186

def autodeploy_flash_notice(branch_name)
  html_escape(_("Branch %{branch_name} was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}")) %
    { branch_name: tag.strong(truncate(sanitize(branch_name))), link_to_autodeploy_doc: link_to_autodeploy_doc }
end

#can_change_visibility_level?(project, current_user) ⇒ Boolean

Returns:

  • (Boolean)

162
163
164
165
166
167
168
169
170
# File 'app/helpers/projects_helper.rb', line 162

def can_change_visibility_level?(project, current_user)
  return false unless can?(current_user, :change_visibility_level, project)

  if project.fork_source
    project.fork_source.visibility_level > Gitlab::VisibilityLevel::PRIVATE
  else
    true
  end
end

#can_disable_emails?(project, current_user) ⇒ Boolean

Returns:

  • (Boolean)

172
173
174
175
176
# File 'app/helpers/projects_helper.rb', line 172

def can_disable_emails?(project, current_user)
  return false if project.group&.emails_disabled?

  can?(current_user, :set_emails_disabled, project)
end

#can_import_members?Boolean

Returns:

  • (Boolean)

337
338
339
# File 'app/helpers/projects_helper.rb', line 337

def can_import_members?
  Ability.allowed?(current_user, :admin_project_member, @project)
end

#directory?Boolean

Returns:

  • (Boolean)

323
324
325
# File 'app/helpers/projects_helper.rb', line 323

def directory?
  @path.present?
end

#error_tracking_setting_project_jsonObject


309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'app/helpers/projects_helper.rb', line 309

def error_tracking_setting_project_json
  setting = @project.error_tracking_setting

  return if setting.blank? || setting.project_slug.blank? ||
      setting.organization_slug.blank?

  {
    name: setting.project_name,
    organization_name: setting.organization_name,
    organization_slug: setting.organization_slug,
    slug: setting.project_slug
  }.to_json
end

#explore_projects_tab?Boolean

Returns:

  • (Boolean)

290
291
292
293
294
# File 'app/helpers/projects_helper.rb', line 290

def explore_projects_tab?
  current_page?(explore_projects_path) ||
    current_page?(trending_explore_projects_path) ||
    current_page?(starred_explore_projects_path)
end

#external_classification_label_help_messageObject


327
328
329
330
331
332
333
334
335
# File 'app/helpers/projects_helper.rb', line 327

def external_classification_label_help_message
  default_label = ::Gitlab::CurrentSettings.current_application_settings
                    .external_authorization_service_default_label

  s_(
    "ExternalAuthorizationService|When no classification label is set the "\
      "default label `%{default_label}` will be used."
  ) % { default_label: default_label }
end

#grafana_integration_enabled?Boolean

Returns:

  • (Boolean)

381
382
383
# File 'app/helpers/projects_helper.rb', line 381

def grafana_integration_enabled?
  @project.grafana_integration&.enabled?
end

#grafana_integration_masked_tokenObject


377
378
379
# File 'app/helpers/projects_helper.rb', line 377

def grafana_integration_masked_token
  @project.grafana_integration&.masked_token
end

#grafana_integration_urlObject


373
374
375
# File 'app/helpers/projects_helper.rb', line 373

def grafana_integration_url
  @project.grafana_integration&.grafana_url
end

#last_push_eventObject


178
179
180
# File 'app/helpers/projects_helper.rb', line 178

def last_push_event
  current_user&.recent_push(@project)
end

182
183
184
# File 'app/helpers/projects_helper.rb', line 182

def link_to_autodeploy_doc
  link_to _('About auto deploy'), help_page_path('topics/autodevops/stages.md', anchor: 'auto-deploy'), target: '_blank'
end

286
287
288
# File 'app/helpers/projects_helper.rb', line 286

def link_to_filter_repo
  link_to 'git filter-repo', 'https://github.com/newren/git-filter-repo', target: '_blank', rel: 'noopener noreferrer'
end

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'app/helpers/projects_helper.rb', line 54

def link_to_member(project, author, opts = {}, &block)
  default_opts = { avatar: true, name: true, title: ":name" }
  opts = default_opts.merge(opts)

  data_attrs = {
    user_id: author.id,
    username: author.username,
    name: author.name
  }

  return "(deleted)" unless author

  author_html = []

  # Build avatar image tag
  author_html << link_to_member_avatar(author, opts) if opts[:avatar]

  # Build name span tag
  author_html << (author, opts) if opts[:name]

  author_html << capture(&block) if block

  author_html = author_html.join.html_safe

  if opts[:name]
    link_to(author_html, user_path(author), class: "author-link js-user-link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}", data: data_attrs).html_safe
  else
    title = opts[:title].sub(":name", sanitize(author.name))
    link_to(author_html, user_path(author), class: "author-link has-tooltip", title: title, data: { container: 'body', qa_selector: 'assignee_link' }).html_safe
  end
end

22
23
24
25
26
27
28
29
30
31
32
33
# File 'app/helpers/projects_helper.rb', line 22

def link_to_member_avatar(author, opts = {})
  default_opts = { size: 16, lazy_load: false }
  opts = default_opts.merge(opts)

  classes = %W[avatar avatar-inline s#{opts[:size]}]
  classes << opts[:avatar_class] if opts[:avatar_class]

  avatar = avatar_icon_for_user(author, opts[:size])
  src = opts[:lazy_load] ? nil : avatar

  image_tag(src, width: opts[:size], class: classes, alt: '', "data-src" => avatar)
end

9
10
11
12
13
14
15
16
17
18
19
20
# File 'app/helpers/projects_helper.rb', line 9

def link_to_project(project)
  link_to namespace_project_path(namespace_id: project.namespace, id: project), title: h(project.name) do
    title = (:span, project.name, class: 'project-name')

    if project.namespace
      namespace = (:span, "#{project.namespace.human_name} / ", class: 'namespace-name')
      title = namespace + title
    end

    title
  end
end

234
235
236
237
238
239
240
# File 'app/helpers/projects_helper.rb', line 234

def link_to_set_password
  if current_user.require_password_creation_for_git?
    link_to s_('SetPasswordToCloneLink|set a password'), edit_profile_password_path
  else
    link_to s_('CreateTokenToCloneLink|create a personal access token'), profile_personal_access_tokens_path
  end
end

#load_pipeline_status(projects) ⇒ Object


211
212
213
214
# File 'app/helpers/projects_helper.rb', line 211

def load_pipeline_status(projects)
  Gitlab::Cache::Ci::ProjectPipelineStatus
    .load_in_batch_for_projects(projects)
end

#membership_locked?Boolean

Returns:

  • (Boolean)

345
346
347
# File 'app/helpers/projects_helper.rb', line 345

def membership_locked?
  false
end

#metrics_dashboard_timezoneObject


369
370
371
# File 'app/helpers/projects_helper.rb', line 369

def metrics_dashboard_timezone
  @project.metrics_setting_dashboard_timezone
end

#metrics_external_dashboard_urlObject


365
366
367
# File 'app/helpers/projects_helper.rb', line 365

def metrics_external_dashboard_url
  @project.metrics_setting_external_dashboard_url
end

#project_can_be_shared?Boolean

Returns:

  • (Boolean)

341
342
343
# File 'app/helpers/projects_helper.rb', line 341

def project_can_be_shared?
  !membership_locked? || @project.allowed_to_share_with_group?
end

#project_for_deploy_key(deploy_key) ⇒ Object


152
153
154
155
156
157
158
159
160
# File 'app/helpers/projects_helper.rb', line 152

def project_for_deploy_key(deploy_key)
  if deploy_key.has_access_to?(@project)
    @project
  else
    deploy_key.projects.find do |project|
      can?(current_user, :read_project, project)
    end
  end
end

#project_incident_management_settingObject


4
5
6
7
# File 'app/helpers/projects_helper.rb', line 4

def project_incident_management_setting
  @project_incident_management_setting ||= @project.incident_management_setting ||
    @project.build_incident_management_setting
end

#project_license_name(project) ⇒ Object


385
386
387
388
389
390
391
392
393
394
# File 'app/helpers/projects_helper.rb', line 385

def project_license_name(project)
  key = "project:#{project.id}:license_name"

  Gitlab::SafeRequestStore.fetch(key) { project.repository.license&.name }
rescue GRPC::Unavailable, GRPC::DeadlineExceeded, Gitlab::Git::CommandError => e
  Gitlab::ErrorTracking.track_exception(e)
  Gitlab::SafeRequestStore[key] = nil

  nil
end

#project_list_cache_key(project, pipeline_status: true) ⇒ Object


191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'app/helpers/projects_helper.rb', line 191

def project_list_cache_key(project, pipeline_status: true)
  key = [
    project.route.cache_key,
    project.cache_key,
    project.last_activity_date,
    controller.controller_name,
    controller.action_name,
    Gitlab::CurrentSettings.cache_key,
    "cross-project:#{can?(current_user, :read_cross_project)}",
    max_project_member_access_cache_key(project),
    pipeline_status,
    Gitlab::I18n.locale,
    'v2.6'
  ]

  key << pipeline_status_cache_key(project.pipeline_status) if pipeline_status && project.pipeline_status.has_status?

  key
end

#project_nav_tab?(name) ⇒ Boolean

Returns:

  • (Boolean)

148
149
150
# File 'app/helpers/projects_helper.rb', line 148

def project_nav_tab?(name)
  project_nav_tabs.include? name
end

#project_nav_tabsObject


138
139
140
# File 'app/helpers/projects_helper.rb', line 138

def project_nav_tabs
  @nav_tabs ||= get_project_nav_tabs(@project, current_user)
end

#project_search_tabs?(tab) ⇒ Boolean

Returns:

  • (Boolean)

142
143
144
145
146
# File 'app/helpers/projects_helper.rb', line 142

def project_search_tabs?(tab)
  abilities = Array(search_tab_ability_map[tab])

  abilities.any? { |ability| can?(current_user, ability, @project) }
end

#project_title(project) ⇒ Object


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'app/helpers/projects_helper.rb', line 86

def project_title(project)
  namespace_link =
    if project.group
      group_title(project.group, nil, nil)
    else
      owner = project.namespace.owner
      link_to(simple_sanitize(owner.name), user_path(owner))
    end

  project_link = link_to project_path(project) do
    icon = project_icon(project, alt: project.name, class: 'avatar-tile', width: 15, height: 15) if project.avatar_url && !Rails.env.test?
    [icon, ("span", simple_sanitize(project.name), class: "breadcrumb-item-text js-breadcrumb-item-text")].join.html_safe
  end

  namespace_link = breadcrumb_list_item(namespace_link) unless project.group
  project_link = breadcrumb_list_item project_link

  "#{namespace_link} #{project_link}".html_safe
end

#push_to_create_project_command(user = current_user) ⇒ Object


267
268
269
270
271
272
273
274
275
276
# File 'app/helpers/projects_helper.rb', line 267

def push_to_create_project_command(user = current_user)
  repository_url =
    if Gitlab::CurrentSettings.current_application_settings.enabled_git_access_protocol == 'http'
      user_url(user)
    else
      Gitlab.config.gitlab_shell.ssh_path_prefix + user.username
    end

  "git push --set-upstream #{repository_url}/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)"
end

#remove_fork_project_description_message(project) ⇒ Object


116
117
118
119
120
121
122
123
124
125
126
127
# File 'app/helpers/projects_helper.rb', line 116

def remove_fork_project_description_message(project)
  source = visible_fork_source(project)

  if source
    msg = _('This will remove the fork relationship between this project and %{fork_source}.') %
      { fork_source: link_to(source.full_name, project_path(source)) }

    msg.html_safe
  else
    _('This will remove the fork relationship between this project and other projects in the fork network.')
  end
end

#remove_fork_project_warning_message(project) ⇒ Object


129
130
131
132
# File 'app/helpers/projects_helper.rb', line 129

def remove_fork_project_warning_message(project)
  _("You are going to remove the fork relationship from %{project_full_name}. Are you ABSOLUTELY sure?") %
    { project_full_name: project.full_name }
end

#remove_project_message(project) ⇒ Object


106
107
108
109
# File 'app/helpers/projects_helper.rb', line 106

def remove_project_message(project)
  _("You are going to delete %{project_full_name}. Deleted projects CANNOT be restored! Are you ABSOLUTELY sure?") %
    { project_full_name: project.full_name }
end

#settings_operations_available?Boolean

overridden in EE

Returns:

  • (Boolean)

305
306
307
# File 'app/helpers/projects_helper.rb', line 305

def settings_operations_available?
  can?(current_user, :read_environment, @project)
end

#share_project_description(project) ⇒ Object


349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'app/helpers/projects_helper.rb', line 349

def share_project_description(project)
  share_with_group   = project.allowed_to_share_with_group?
  share_with_members = !membership_locked?

  description =
    if share_with_group && share_with_members
      _("You can invite a new member to %{project_name} or invite another group.")
    elsif share_with_group
      _("You can invite another group to %{project_name}.")
    elsif share_with_members
      _("You can invite a new member to %{project_name}.")
    end

  html_escape(description) % { project_name: tag.strong(project.name) }
end

#show_auto_devops_implicitly_enabled_banner?(project, user) ⇒ Boolean

Returns:

  • (Boolean)

228
229
230
231
232
# File 'app/helpers/projects_helper.rb', line 228

def show_auto_devops_implicitly_enabled_banner?(project, user)
  return false unless user_can_see_auto_devops_implicitly_enabled_banner?(project, user)

  cookies["hide_auto_devops_implicitly_enabled_banner_#{project.id}".to_sym].blank?
end

#show_issue_count?(disabled: false, compact_mode: false) ⇒ Boolean

Returns:

  • (Boolean)

300
301
302
# File 'app/helpers/projects_helper.rb', line 300

def show_issue_count?(disabled: false, compact_mode: false)
  !disabled && !compact_mode
end

#show_merge_request_count?(disabled: false, compact_mode: false) ⇒ Boolean

Returns:

  • (Boolean)

296
297
298
# File 'app/helpers/projects_helper.rb', line 296

def show_merge_request_count?(disabled: false, compact_mode: false)
  !disabled && !compact_mode
end

#show_no_password_message?Boolean

Returns:

  • (Boolean)

223
224
225
226
# File 'app/helpers/projects_helper.rb', line 223

def show_no_password_message?
  cookies[:hide_no_password_message].blank? && !current_user.hide_no_password &&
    current_user.require_extra_setup_for_git_auth?
end

#show_no_ssh_key_message?Boolean

Returns:

  • (Boolean)

216
217
218
219
220
221
# File 'app/helpers/projects_helper.rb', line 216

def show_no_ssh_key_message?
  Gitlab::CurrentSettings.user_show_add_ssh_key_message? &&
    cookies[:hide_no_ssh_message].blank? &&
    !current_user.hide_no_ssh_key &&
    current_user.require_ssh_key?
end

#show_projects?(projects, params) ⇒ Boolean

TODO: Remove this method when removing the feature flag gitlab.com/gitlab-org/gitlab/merge_requests/11209#note_162234863 make sure to remove from the EE specific controller as well: ee/app/controllers/ee/dashboard/projects_controller.rb

Returns:

  • (Boolean)

263
264
265
# File 'app/helpers/projects_helper.rb', line 263

def show_projects?(projects, params)
  Feature.enabled?(:project_list_filter_bar) || !!(params[:personal] || params[:name] || any_projects?(projects))
end

#show_xcode_link?(project = @project) ⇒ Boolean

Returns:

  • (Boolean)

278
279
280
# File 'app/helpers/projects_helper.rb', line 278

def show_xcode_link?(project = @project)
  browser.platform.mac? && project.repository.xcode_project?
end

#transfer_project_message(project) ⇒ Object


111
112
113
114
# File 'app/helpers/projects_helper.rb', line 111

def transfer_project_message(project)
  _("You are going to transfer %{project_full_name} to another namespace. Are you ABSOLUTELY sure?") %
    { project_full_name: project.full_name }
end

#visible_fork_source(project) ⇒ Object


134
135
136
# File 'app/helpers/projects_helper.rb', line 134

def visible_fork_source(project)
  project.fork_source if project.fork_source && can?(current_user, :read_project, project.fork_source)
end

#xcode_uri_to_repo(project = @project) ⇒ Object


282
283
284
# File 'app/helpers/projects_helper.rb', line 282

def xcode_uri_to_repo(project = @project)
  "xcode://clone?repo=#{CGI.escape(default_url_to_repo(project))}"
end