Class: ProjectPresenter

Inherits:
Gitlab::View::Presenter::Delegated show all
Includes:
ActionView::Helpers::NumberHelper, ActionView::Helpers::UrlHelper, BlobHelper, ChecksCollaboration, Gitlab::Utils::StrongMemoize, GitlabRoutingHelper, IconsHelper, StorageHelper, TreeHelper
Defined in:
app/presenters/project_presenter.rb

Defined Under Namespace

Classes: AnchorData

Constant Summary collapse

MAX_TOPICS_TO_SHOW =
3

Constants included from IconsHelper

IconsHelper::DEFAULT_ICON_SIZE

Constants included from TreeHelper

TreeHelper::FILE_LIMIT

Instance Attribute Summary

Attributes included from Gitlab::View::Presenter::Base

#subject

Instance Method Summary collapse

Methods included from Gitlab::Utils::StrongMemoize

#clear_memoization, #strong_memoize, #strong_memoized?

Methods included from ChecksCollaboration

#can_collaborate_with_project?, #user_access

Methods included from BlobHelper

#blob_editor_paths, #blob_icon, #blob_raw_path, #blob_raw_url, #blob_render_error_options, #blob_render_error_reason, #can_modify_blob?, #contribution_options, #copy_blob_source_button, #copy_file_path_button, #delete_blob_link, #dockerfile_names, #download_blob_button, #edit_blob_button, #edit_blob_fork_params, #edit_blob_path, #edit_button_tag, #edit_disabled_button_tag, #edit_fork_button_tag, #edit_link_tag, #edit_modify_file_fork_params, #editing_ci_config?, #editing_preview_title, #encode_ide_path, #gitignore_names, #gitlab_ci_ymls, #highlight, #human_access, #ide_edit_button, #ide_edit_path, #ide_fork_and_edit_path, #leave_edit_message, #licenses_for_select, #metrics_dashboard_ymls, #modify_file_button, #no_highlight_files, #open_raw_blob_button, #readable_blob, #ref_project, #replace_blob_link, #sanitize_svg_data, #show_suggest_pipeline_creation_celebration?, #suggest_pipeline_commit_cookie_name

Methods included from IconsHelper

#audit_icon, #boolean_to_icon, #custom_icon, #external_snippet_icon, #file_type_icon_class, #icon, #loading_icon, #spinner, #sprite_file_icons_path, #sprite_icon, #sprite_icon_path, #visibility_level_icon

Methods included from TreeHelper

#breadcrumb_data_attributes, #can_edit_tree?, #commit_in_fork_help, #commit_in_single_accessible_branch, #directory_download_links, #edit_in_new_fork_notice, #edit_in_new_fork_notice_action, #edit_in_new_fork_notice_now, #fast_project_blob_path, #fast_project_tree_path, #flatten_tree, #on_top_of_branch?, #path_breadcrumbs, #relative_url_root, #render_tree, #selected_branch, #tree_content_data, #tree_edit_branch, #tree_edit_project, #tree_icon, #tree_join, #up_dir_path, #vue_file_list_data

Methods included from StorageHelper

#storage_counter, #storage_counters_details

Methods included from GitlabRoutingHelper

#approve_access_request_group_member_path, #approve_access_request_project_member_path, #artifacts_action_path, #commit_url, #commits_url, #edit_milestone_path, #edit_pipeline_schedule_path, #environment_delete_path, #environment_metrics_path, #environment_path, #expose_fast_artifacts_path, #fast_browse_project_job_artifacts_path, #fast_download_project_job_artifacts_path, #fast_keep_project_job_artifacts_path, #gitlab_dashboard_snippets_path, #gitlab_raw_snippet_blob_path, #gitlab_raw_snippet_blob_url, #gitlab_raw_snippet_path, #gitlab_raw_snippet_url, #gitlab_snippet_note_path, #gitlab_snippet_note_url, #gitlab_snippet_notes_path, #gitlab_snippet_notes_url, #gitlab_snippet_path, #gitlab_snippet_url, #gitlab_toggle_award_emoji_snippet_note_path, #gitlab_toggle_award_emoji_snippet_note_url, #gitlab_toggle_award_emoji_snippet_path, #gitlab_toggle_award_emoji_snippet_url, #group_member_path, #group_members_url, #issue_path, #issue_url, #leave_group_members_path, #leave_project_members_path, #merge_request_path, #merge_request_url, #pipeline_job_url, #pipeline_path, #pipeline_schedule_path, #pipeline_schedules_path, #pipeline_url, #play_pipeline_schedule_path, #preview_markdown_path, #project_commits_path, #project_member_path, #project_members_url, #project_ref_path, #project_tree_path, #request_access_group_members_path, #request_access_project_members_path, #resend_invite_group_member_path, #resend_invite_project_member_path, #take_ownership_pipeline_schedule_path, #toggle_award_emoji_personal_snippet_path, #toggle_award_emoji_project_project_snippet_path, #toggle_award_emoji_project_project_snippet_url, #toggle_subscription_path, #wiki_page_path, #wiki_path

Methods included from API::Helpers::RelatedResourcesHelpers

#expose_path, #expose_url, #issues_available?, #mrs_available?

Methods inherited from Gitlab::View::Presenter::Delegated

#initialize

Methods included from Gitlab::View::Presenter::Base

#can?, #declarative_policy_delegate, #is_a?, #present, #url_builder, #web_path, #web_url

Methods included from Gitlab::Allowable

#can?

Methods included from Gitlab::Routing

add_helpers, includes_helpers, redirect_legacy_paths, url_helpers

Constructor Details

This class inherits a constructor from Gitlab::View::Presenter::Delegated

Instance Method Details

#add_changelog_pathObject


109
110
111
# File 'app/presenters/project_presenter.rb', line 109

def add_changelog_path
  add_special_file_path(file_name: 'CHANGELOG')
end

#add_ci_yml_ide_pathObject


121
122
123
# File 'app/presenters/project_presenter.rb', line 121

def add_ci_yml_ide_path
  ide_edit_path(project, default_branch_or_master, ci_config_path_or_default)
end

#add_ci_yml_pathObject


117
118
119
# File 'app/presenters/project_presenter.rb', line 117

def add_ci_yml_path
  add_special_file_path(file_name: ci_config_path_or_default)
end

#add_contribution_guide_pathObject


113
114
115
# File 'app/presenters/project_presenter.rb', line 113

def add_contribution_guide_path
  add_special_file_path(file_name: 'CONTRIBUTING.md', commit_message: 'Add CONTRIBUTING')
end

#add_license_pathObject


105
106
107
# File 'app/presenters/project_presenter.rb', line 105

def add_license_path
  add_special_file_path(file_name: 'LICENSE')
end

#add_readme_pathObject


125
126
127
# File 'app/presenters/project_presenter.rb', line 125

def add_readme_path
  add_special_file_path(file_name: 'README.md')
end

#all_clusters_empty?Boolean

Returns:

  • (Boolean)

368
369
370
371
372
# File 'app/presenters/project_presenter.rb', line 368

def all_clusters_empty?
  strong_memoize(:all_clusters_empty) do
    project.all_clusters.empty?
  end
end

#autodevops_anchor_data(show_auto_devops_callout: false) ⇒ Object


293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'app/presenters/project_presenter.rb', line 293

def autodevops_anchor_data(show_auto_devops_callout: false)
  if current_user && can?(current_user, :admin_pipeline, project) && repository.gitlab_ci_yml.blank? && !show_auto_devops_callout
    if auto_devops_enabled?
      AnchorData.new(false,
                     statistic_icon('settings') + _('Auto DevOps enabled'),
                     project_settings_ci_cd_path(project, anchor: 'autodevops-settings'),
                     'default')
    else
      AnchorData.new(false,
                     statistic_icon + _('Enable Auto DevOps'),
                     project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
    end
  elsif auto_devops_enabled?
    AnchorData.new(false,
                   _('Auto DevOps enabled'),
                   nil)
  end
end

#branches_anchor_dataObject


201
202
203
204
205
206
207
208
209
210
# File 'app/presenters/project_presenter.rb', line 201

def branches_anchor_data
  AnchorData.new(true,
                 statistic_icon('branch') +
                 n_('%{strong_start}%{branch_count}%{strong_end} Branch', '%{strong_start}%{branch_count}%{strong_end} Branches', repository.branch_count).html_safe % {
                   branch_count: number_with_delimiter(repository.branch_count),
                   strong_start: '<strong class="project-stat-value">'.html_safe,
                   strong_end: '</strong>'.html_safe
                 },
                 empty_repo? ? nil : project_branches_path(project))
end

#can_current_user_push_code?Boolean

Returns:

  • (Boolean)

134
135
136
137
138
139
140
141
142
# File 'app/presenters/project_presenter.rb', line 134

def can_current_user_push_code?
  strong_memoize(:can_current_user_push_code) do
    if empty_repo?
      can?(current_user, :push_code, project)
    else
      can_current_user_push_to_branch?(default_branch)
    end
  end
end

#can_current_user_push_to_branch?(branch) ⇒ Boolean

Returns:

  • (Boolean)

144
145
146
# File 'app/presenters/project_presenter.rb', line 144

def can_current_user_push_to_branch?(branch)
  user_access(project).can_push_to_branch?(branch)
end

#can_current_user_push_to_default_branch?Boolean

Returns:

  • (Boolean)

148
149
150
# File 'app/presenters/project_presenter.rb', line 148

def can_current_user_push_to_default_branch?
  can_current_user_push_to_branch?(default_branch)
end

#can_setup_review_app?Boolean

Returns:

  • (Boolean)

362
363
364
365
366
# File 'app/presenters/project_presenter.rb', line 362

def can_setup_review_app?
  strong_memoize(:can_setup_review_app) do
    (can_instantiate_cluster? && all_clusters_empty?) || cicd_missing?
  end
end

#changelog_anchor_dataObject


246
247
248
249
250
251
252
253
254
255
256
257
# File 'app/presenters/project_presenter.rb', line 246

def changelog_anchor_data
  if current_user && can_current_user_push_to_default_branch? && repository.changelog.blank?
    AnchorData.new(false,
                   statistic_icon + _('Add CHANGELOG'),
                   add_changelog_path)
  elsif repository.changelog.present?
    AnchorData.new(false,
                   statistic_icon('doc-text') + _('CHANGELOG'),
                   changelog_path,
                  'default')
  end
end

#changelog_pathObject


83
84
85
# File 'app/presenters/project_presenter.rb', line 83

def changelog_path
  filename_path(:changelog)
end

#ci_configuration_pathObject


91
92
93
# File 'app/presenters/project_presenter.rb', line 91

def ci_configuration_path
  filename_path(:gitlab_ci_yml)
end

#commits_anchor_dataObject


190
191
192
193
194
195
196
197
198
199
# File 'app/presenters/project_presenter.rb', line 190

def commits_anchor_data
  AnchorData.new(true,
                 statistic_icon('commit') +
                 n_('%{strong_start}%{commit_count}%{strong_end} Commit', '%{strong_start}%{commit_count}%{strong_end} Commits', statistics.commit_count).html_safe % {
                   commit_count: number_with_delimiter(statistics.commit_count),
                   strong_start: '<strong class="project-stat-value">'.html_safe,
                   strong_end: '</strong>'.html_safe
                 },
                 empty_repo? ? nil : project_commits_path(project, repository.root_ref))
end

#contribution_guide_anchor_dataObject


280
281
282
283
284
285
286
287
288
289
290
291
# File 'app/presenters/project_presenter.rb', line 280

def contribution_guide_anchor_data
  if current_user && can_current_user_push_to_default_branch? && repository.contribution_guide.blank?
    AnchorData.new(false,
                   statistic_icon + _('Add CONTRIBUTING'),
                   add_contribution_guide_path)
  elsif repository.contribution_guide.present?
    AnchorData.new(false,
                   statistic_icon('doc-text') + _('CONTRIBUTING'),
                   contribution_guide_path,
                   'default')
  end
end

#contribution_guide_pathObject


95
96
97
98
99
100
101
102
103
# File 'app/presenters/project_presenter.rb', line 95

def contribution_guide_path
  if project && contribution_guide = repository.contribution_guide
    project_blob_path(
      project,
      tree_join(project.default_branch,
                contribution_guide.name)
    )
  end
end

#count_of_extra_topics_not_shownObject


350
351
352
353
354
355
356
# File 'app/presenters/project_presenter.rb', line 350

def count_of_extra_topics_not_shown
  if project.tag_list.count > MAX_TOPICS_TO_SHOW
    project.tag_list.count - MAX_TOPICS_TO_SHOW
  else
    0
  end
end

#default_viewObject


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'app/presenters/project_presenter.rb', line 61

def default_view
  return anonymous_project_view unless current_user

  user_view = current_user.project_view

  if can?(current_user, :download_code, project)
    user_view
  elsif user_view == "activity"
    "activity"
  elsif can?(current_user, :read_wiki, project)
    "wiki"
  elsif feature_available?(:issues, current_user)
    "projects/issues/issues"
  else
    "customize_workflow"
  end
end

#empty_repo_statistics_anchorsObject


46
47
48
# File 'app/presenters/project_presenter.rb', line 46

def empty_repo_statistics_anchors
  []
end

#empty_repo_statistics_buttonsObject


50
51
52
53
54
55
56
57
58
59
# File 'app/presenters/project_presenter.rb', line 50

def empty_repo_statistics_buttons
  [
    new_file_anchor_data,
    readme_anchor_data,
    license_anchor_data,
    changelog_anchor_data,
    contribution_guide_anchor_data,
    gitlab_ci_anchor_data
  ].compact.reject { |item| item.is_link }
end

#files_anchor_dataObject


152
153
154
155
156
157
158
159
160
161
# File 'app/presenters/project_presenter.rb', line 152

def files_anchor_data
  AnchorData.new(true,
                 statistic_icon('doc-code') +
                 _('%{strong_start}%{human_size}%{strong_end} Files').html_safe % {
                   human_size: storage_counter(statistics.total_repository_size),
                   strong_start: '<strong class="project-stat-value">'.html_safe,
                   strong_end: '</strong>'.html_safe
                 },
                 empty_repo? ? nil : project_tree_path(project))
end

#gitlab_ci_anchor_dataObject


329
330
331
332
333
334
335
336
337
338
339
340
# File 'app/presenters/project_presenter.rb', line 329

def gitlab_ci_anchor_data
  if cicd_missing?
    AnchorData.new(false,
                   statistic_icon + _('Set up CI/CD'),
                   add_ci_yml_ide_path)
  elsif repository.gitlab_ci_yml.present?
    AnchorData.new(false,
                   statistic_icon('doc-text') + _('CI/CD configuration'),
                   ci_configuration_path,
                  'default')
  end
end

#has_extra_topics?Boolean

Returns:

  • (Boolean)

358
359
360
# File 'app/presenters/project_presenter.rb', line 358

def has_extra_topics?
  count_of_extra_topics_not_shown > 0
end

#kubernetes_cluster_anchor_dataObject


312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'app/presenters/project_presenter.rb', line 312

def kubernetes_cluster_anchor_data
  if can_instantiate_cluster?
    if clusters.empty?
      AnchorData.new(false,
                     statistic_icon + _('Add Kubernetes cluster'),
                     new_project_cluster_path(project))
    else
      cluster_link = clusters.count == 1 ? project_cluster_path(project, clusters.first) : project_clusters_path(project)

      AnchorData.new(false,
                     _('Kubernetes'),
                     cluster_link,
                    'default')
    end
  end
end

#license_anchor_dataObject


259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'app/presenters/project_presenter.rb', line 259

def license_anchor_data
  icon = statistic_icon('scale')

  if repository.license_blob.present?
    AnchorData.new(false,
                   icon + (:span, license_short_name, class: 'project-stat-value'),
                   license_path,
                   'default')
  else
    if current_user && can_current_user_push_to_default_branch?
      AnchorData.new(false,
                     (:span, statistic_icon + _('Add LICENSE'), class: 'add-license-link d-flex'),
                     add_license_path)
    else
      AnchorData.new(false,
                     icon + (:span, _('No license. All rights reserved'), class: 'project-stat-value'),
                     nil)
    end
  end
end

#license_pathObject


87
88
89
# File 'app/presenters/project_presenter.rb', line 87

def license_path
  filename_path(:license_blob)
end

#license_short_nameObject


129
130
131
132
# File 'app/presenters/project_presenter.rb', line 129

def license_short_name
  license = repository.license
  license&.nickname || license&.name || 'LICENSE'
end

#new_file_anchor_dataObject


223
224
225
226
227
228
229
230
# File 'app/presenters/project_presenter.rb', line 223

def new_file_anchor_data
  if current_user && can_current_user_push_to_default_branch?
    AnchorData.new(false,
                   statistic_icon + _('New file'),
                   project_new_blob_path(project, default_branch_or_master),
                   'missing')
  end
end

#readme_anchor_dataObject


232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'app/presenters/project_presenter.rb', line 232

def readme_anchor_data
  if current_user && can_current_user_push_to_default_branch? && repository.readme.nil?
    AnchorData.new(false,
                   statistic_icon + _('Add README'),
                   add_readme_path)
  elsif repository.readme
    AnchorData.new(false,
                   statistic_icon('doc-text') + _('README'),
                   default_view != 'readme' ? readme_path : '#readme',
                  'default',
                  'doc-text')
  end
end

#readme_pathObject


79
80
81
# File 'app/presenters/project_presenter.rb', line 79

def readme_path
  filename_path(:readme)
end

#releases_anchor_dataObject


174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'app/presenters/project_presenter.rb', line 174

def releases_anchor_data
  return unless can?(current_user, :read_release, project)

  releases_count = project.releases.count
  return if releases_count < 1

  AnchorData.new(true,
                 statistic_icon('rocket') +
                 n_('%{strong_start}%{release_count}%{strong_end} Release', '%{strong_start}%{release_count}%{strong_end} Releases', releases_count).html_safe % {
                   release_count: number_with_delimiter(releases_count),
                   strong_start: '<strong class="project-stat-value">'.html_safe,
                   strong_end: '</strong>'.html_safe
                 },
                project_releases_path(project))
end

#statistic_icon(icon_name = 'plus-square-o') ⇒ Object


19
20
21
# File 'app/presenters/project_presenter.rb', line 19

def statistic_icon(icon_name = 'plus-square-o')
  sprite_icon(icon_name, css_class: 'icon gl-mr-2')
end

#statistics_anchors(show_auto_devops_callout:) ⇒ Object


23
24
25
26
27
28
29
30
31
32
# File 'app/presenters/project_presenter.rb', line 23

def statistics_anchors(show_auto_devops_callout:)
  [
    commits_anchor_data,
    branches_anchor_data,
    tags_anchor_data,
    files_anchor_data,
    storage_anchor_data,
    releases_anchor_data
  ].compact.select(&:is_link)
end

#statistics_buttons(show_auto_devops_callout:) ⇒ Object


34
35
36
37
38
39
40
41
42
43
44
# File 'app/presenters/project_presenter.rb', line 34

def statistics_buttons(show_auto_devops_callout:)
  [
    readme_anchor_data,
    license_anchor_data,
    changelog_anchor_data,
    contribution_guide_anchor_data,
    autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout),
    kubernetes_cluster_anchor_data,
    gitlab_ci_anchor_data
  ].compact.reject(&:is_link).sort_by.with_index { |item, idx| [item.class_modifier ? 0 : 1, idx] }
end

#storage_anchor_dataObject


163
164
165
166
167
168
169
170
171
172
# File 'app/presenters/project_presenter.rb', line 163

def storage_anchor_data
  AnchorData.new(true,
                 statistic_icon('disk') +
                 _('%{strong_start}%{human_size}%{strong_end} Storage').html_safe % {
                   human_size: storage_counter(statistics.storage_size),
                   strong_start: '<strong class="project-stat-value">'.html_safe,
                   strong_end: '</strong>'.html_safe
                 },
                 empty_repo? ? nil : project_tree_path(project))
end

#tags_anchor_dataObject


212
213
214
215
216
217
218
219
220
221
# File 'app/presenters/project_presenter.rb', line 212

def tags_anchor_data
  AnchorData.new(true,
                 statistic_icon('label') +
                 n_('%{strong_start}%{tag_count}%{strong_end} Tag', '%{strong_start}%{tag_count}%{strong_end} Tags', repository.tag_count).html_safe % {
                   tag_count: number_with_delimiter(repository.tag_count),
                   strong_start: '<strong class="project-stat-value">'.html_safe,
                   strong_end: '</strong>'.html_safe
                 },
                 empty_repo? ? nil : project_tags_path(project))
end

#topics_not_shownObject


346
347
348
# File 'app/presenters/project_presenter.rb', line 346

def topics_not_shown
  project.tag_list - topics_to_show
end

#topics_to_showObject


342
343
344
# File 'app/presenters/project_presenter.rb', line 342

def topics_to_show
  project.tag_list.take(MAX_TOPICS_TO_SHOW) # rubocop: disable CodeReuse/ActiveRecord
end