Commit e0e3f1c2 authored by Oswaldo Ferreira's avatar Oswaldo Ferreira

Move button list logic to project presenter

parent ccc858a5
...@@ -34,7 +34,7 @@ module ApplicationHelper ...@@ -34,7 +34,7 @@ module ApplicationHelper
def project_icon(project_id, options = {}) def project_icon(project_id, options = {})
project = project =
if project_id.is_a?(Project) if project_id.is_a?(Project) || project_id.is_a?(ProjectPresenter)
project_id project_id
else else
Project.find_by_full_path(project_id) Project.find_by_full_path(project_id)
......
...@@ -10,12 +10,6 @@ module BranchesHelper ...@@ -10,12 +10,6 @@ module BranchesHelper
project_branches_path(@project, @id, options) project_branches_path(@project, @id, options)
end end
def can_push_branch?(project, branch_name)
return false unless project.repository.branch_exists?(branch_name)
::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch_name)
end
def project_branches def project_branches
options_for_select(@project.repository.branch_names, @project.default_branch) options_for_select(@project.repository.branch_names, @project.default_branch)
end end
......
...@@ -48,30 +48,4 @@ module PreferencesHelper ...@@ -48,30 +48,4 @@ module PreferencesHelper
def user_color_scheme def user_color_scheme
Gitlab::ColorSchemes.for_user(current_user).css_class Gitlab::ColorSchemes.for_user(current_user).css_class
end end
def default_project_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 @project.feature_available?(:issues, current_user)
"projects/issues/issues"
else
"customize_workflow"
end
end
def anonymous_project_view
if !@project.empty_repo? && can?(current_user, :download_code, @project)
'files'
else
'activity'
end
end
end end
This diff is collapsed.
...@@ -55,7 +55,7 @@ module TreeHelper ...@@ -55,7 +55,7 @@ module TreeHelper
def tree_edit_branch(project = @project, ref = @ref) def tree_edit_branch(project = @project, ref = @ref)
return unless can_edit_tree?(project, ref) return unless can_edit_tree?(project, ref)
if can_push_branch?(project, ref) if project.user_can_push_to_branch?(current_user, ref)
ref ref
else else
project = tree_edit_project(project) project = tree_edit_project(project)
......
...@@ -15,6 +15,7 @@ class Project < ActiveRecord::Base ...@@ -15,6 +15,7 @@ class Project < ActiveRecord::Base
include ValidAttribute include ValidAttribute
include ProjectFeaturesCompatibility include ProjectFeaturesCompatibility
include SelectForProjectAuthorization include SelectForProjectAuthorization
include Presentable
include Routable include Routable
include GroupDescendant include GroupDescendant
include Gitlab::SQL::Pattern include Gitlab::SQL::Pattern
...@@ -1015,6 +1016,12 @@ class Project < ActiveRecord::Base ...@@ -1015,6 +1016,12 @@ class Project < ActiveRecord::Base
!ProtectedBranch.default_branch_protected? || team.max_member_access(user.id) > Gitlab::Access::DEVELOPER !ProtectedBranch.default_branch_protected? || team.max_member_access(user.id) > Gitlab::Access::DEVELOPER
end end
def user_can_push_to_branch?(user, branch_name)
return false unless repository.branch_exists?(branch_name)
::Gitlab::UserAccess.new(user, project: self).can_push_to_branch?(branch_name)
end
def forked? def forked?
return true if fork_network && fork_network.root_project != self return true if fork_network && fork_network.root_project != self
......
class ProjectPresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::NumberHelper
include ActionView::Helpers::UrlHelper
include GitlabRoutingHelper
include StorageHelper
include TreeHelper
presents :project
def project_stat_anchor_items(show_auto_devops_callout:)
[
files_anchor_data,
commits_anchor_data,
branches_anchor_data,
tags_anchor_data,
readme_anchor_data,
changelog_anchor_data,
license_anchor_data,
contribution_guide_anchor_data,
gitlab_ci_anchor_data,
autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout),
kubernetes_cluster_anchor_data
].compact.reject { |i| !i[:enabled] }
end
def project_stat_button_items(show_auto_devops_callout:)
[
changelog_anchor_data,
license_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,
koding_anchor_data
].compact.reject { |i| i[:enabled] }
end
def empty_project_stat_anchor_items
[
autodevops_anchor_data,
kubernetes_cluster_anchor_data
].compact.reject { |i| !i[:enabled] }
end
def empty_project_stat_button_items
[
new_file_anchor_data,
readme_anchor_data,
license_anchor_data,
autodevops_anchor_data,
kubernetes_cluster_anchor_data
].compact.reject { |i| i[:enabled] }
end
def default_project_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
def readme_path
filename_path(:readme)
end
def changelog_path
filename_path(:changelog)
end
def license_path
filename_path(:license_blob)
end
def ci_configuration_path
filename_path(:gitlab_ci_yml)
end
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
def add_license_path
add_special_file_path(file_name: 'LICENSE')
end
def add_ci_yml_path
add_special_file_path(file_name: '.gitlab-ci.yml')
end
def add_readme_path
add_special_file_path(file_name: 'README.md')
end
def add_koding_stack_path
project_new_blob_path(
project,
default_branch || 'master',
file_name: '.koding.yml',
commit_message: "Add Koding stack script",
content: <<-CONTENT.strip_heredoc
provider:
aws:
access_key: '${var.aws_access_key}'
secret_key: '${var.aws_secret_key}'
resource:
aws_instance:
#{project.path}-vm:
instance_type: t2.nano
user_data: |-
# Created by GitLab UI for :>
echo _KD_NOTIFY_@Installing Base packages...@
apt-get update -y
apt-get install git -y
echo _KD_NOTIFY_@Cloning #{project.name}...@
export KODING_USER=${var.koding_user_username}
export REPO_URL=#{root_url}${var.koding_queryString_repo}.git
export BRANCH=${var.koding_queryString_branch}
sudo -i -u $KODING_USER git clone $REPO_URL -b $BRANCH
echo _KD_NOTIFY_@#{project.name} cloned.@
CONTENT
)
end
def license_short_name
license = repository.license
license&.nickname || license&.name || 'LICENSE'
end
private
def filename_path(filename)
if blob = repository.public_send(filename) # rubocop:disable GitlabSecurity/PublicSend
project_blob_path(
project,
tree_join(default_branch, blob.name)
)
end
end
def anonymous_project_view
if !project.empty_repo? && can?(current_user, :download_code, project)
'files'
else
'activity'
end
end
def add_special_file_path(file_name:, commit_message: nil, branch_name: nil)
commit_message ||= s_("CommitMessage|Add %{file_name}") % { file_name: file_name }
project_new_blob_path(
project,
project.default_branch || 'master',
file_name: file_name,
commit_message: commit_message,
branch_name: branch_name
)
end
def can_current_user_push_code?
if empty_repo?
can?(current_user, :push_code, project)
else
user_can_push_to_branch?(current_user, default_branch)
end
end
def files_anchor_data
{
enabled: true,
label: _('Files (%{human_size})') % { human_size: storage_counter(statistics.total_repository_size) },
link: project_tree_path(project)
}
end
def commits_anchor_data
{
enabled: true,
label: n_('Commit (%{commit_count})', 'Commits (%{commit_count})', statistics.commit_count) % { commit_count: number_with_delimiter(statistics.commit_count) },
link: project_commits_path(project, repository.root_ref)
}
end
def branches_anchor_data
{
enabled: true,
label: n_('Branch (%{branch_count})', 'Branches (%{branch_count})', repository.branch_count) % { branch_count: number_with_delimiter(repository.branch_count) },
link: project_branches_path(project)
}
end
def tags_anchor_data
{
enabled: true,
label: n_('Tag (%{tag_count})', 'Tags (%{tag_count})', repository.tag_count) % { tag_count: number_with_delimiter(repository.tag_count) },
link: project_tags_path(project)
}
end
def new_file_anchor_data
if current_user && can_current_user_push_code?
{
enabled: false,
label: _('New file'),
link: project_new_blob_path(project, default_branch || 'master'),
class_modifier: 'new'
}
end
end
def readme_anchor_data
if current_user && can_current_user_push_code? && repository.readme.blank?
{
enabled: false,
label: _('Add Readme'),
link: add_readme_path
}
elsif repository.readme.present?
{
enabled: true,
label: _('Readme'),
link: default_project_view != 'readme' ? readme_path : '#readme'
}
end
end
def changelog_anchor_data
if current_user && can_current_user_push_code? && repository.changelog.blank?
{
enabled: false,
label: _('Add Changelog'),
link: add_special_file_path(file_name: 'CHANGELOG')
}
elsif repository.changelog.present?
{
enabled: true,
label: _('Changelog'),
link: changelog_path
}
end
end
def license_anchor_data
if current_user && can_current_user_push_code? && repository.license_blob.blank?
{
enabled: false,
label: _('Add License'),
link: add_license_path
}
elsif repository.license_blob.present?
{
enabled: true,
label: license_short_name,
link: license_path
}
end
end
def contribution_guide_anchor_data
if current_user && can_current_user_push_code? && repository.contribution_guide.blank?
{
enabled: false,
label: _('Add Contribution guide'),
link: add_special_file_path(file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide')
}
elsif repository.contribution_guide.present?
{
enabled: true,
label: _('Contribution guide'),
link: contribution_guide_path
}
end
end
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
{
enabled: auto_devops_enabled?,
label: auto_devops_enabled? ? _('Auto DevOps enabled') : _('Enable Auto DevOps'),
link: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings')
}
elsif auto_devops_enabled?
{
enabled: true,
label: _('Auto DevOps enabled'),
link: nil
}
end
end
def kubernetes_cluster_anchor_data
if current_user && can?(current_user, :create_cluster, project)
cluster_link = clusters.size == 1 ? project_cluster_path(project, clusters.first) : project_clusters_path(project)
if clusters.empty?
cluster_link = new_project_cluster_path(project)
end
{
enabled: !clusters.empty?,
label: clusters.empty? ? _('Add Kubernetes cluster') : n_('Kubernetes cluster', 'Kubernetes clusters', clusters.size),
link: cluster_link
}
end
end
def gitlab_ci_anchor_data
if current_user && can_current_user_push_code? && repository.gitlab_ci_yml.blank? && !auto_devops_enabled?
{
enabled: false,
label: _('Set up CI/CD'),
link: add_ci_yml_path
}
elsif repository.gitlab_ci_yml.present?
{
enabled: true,
label: _('CI/CD configuration'),
link: ci_configuration_path
}
end
end
def koding_anchor_data
if current_user && can_current_user_push_code? && koding_enabled? && repository.koding_yml.blank?
{
enabled: false,
label: _('Set up Koding'),
link: add_koding_stack_path
}
end
end
def koding_enabled?
Gitlab::CurrentSettings.koding_enabled?
end
end
...@@ -20,4 +20,4 @@ ...@@ -20,4 +20,4 @@
distributed with computer software, forming part of its documentation. distributed with computer software, forming part of its documentation.
GitLab will render it here instead of this message. GitLab will render it here instead of this message.
%p %p
= link_to "Add Readme", add_special_file_path(@project, file_name: 'README.md'), class: 'btn btn-new' = link_to "Add Readme", @project.add_readme_path, class: 'btn btn-new'
- if koding_enabled? && current_user && @repository.koding_yml && can_push_branch?(@project, @project.default_branch) - if koding_enabled? && current_user && @repository.koding_yml && @project.user_can_push_to_branch?(current_user, @project.default_branch)
= link_to koding_project_url(@project), class: 'btn project-action-button inline', target: '_blank', rel: 'noopener noreferrer' do = link_to koding_project_url(@project), class: 'btn project-action-button inline', target: '_blank', rel: 'noopener noreferrer' do
_('Run in IDE (Koding)') _('Run in IDE (Koding)')
- @no_container = true - @no_container = true
- breadcrumb_title "Details" - breadcrumb_title "Details"
- @project = @project.present(current_user: current_user)
= render partial: 'flash_messages', locals: { project: @project } = render partial: 'flash_messages', locals: { project: @project }
...@@ -32,8 +33,8 @@ ...@@ -32,8 +33,8 @@
.prepend-top-20 .prepend-top-20
%nav.project-stats{ class: container_class } %nav.project-stats{ class: container_class }
= render 'stat_anchor_list', anchors: empty_project_stat_anchor_items(@project) = render 'stat_anchor_list', anchors: @project.empty_project_stat_anchor_items
= render 'stat_anchor_list', anchors: empty_project_stat_button_items(@project) = render 'stat_anchor_list', anchors: @project.empty_project_stat_button_items
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
%div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] } %div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] }
......
- project_stat_items_args = { show_auto_devops_callout: show_auto_devops_callout?(@project) }
- @no_container = true - @no_container = true
- breadcrumb_title "Details" - breadcrumb_title "Details"
- @content_class = "limit-container-width" unless fluid_layout - @content_class = "limit-container-width" unless fluid_layout
- @project = @project.present(current_user: current_user)
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity") = auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity")
...@@ -14,8 +16,8 @@ ...@@ -14,8 +16,8 @@
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
%nav.project-stats{ class: container_class } %nav.project-stats{ class: container_class }
= render 'stat_anchor_list', anchors: project_stat_anchor_items(@project) = render 'stat_anchor_list', anchors: @project.project_stat_anchor_items(project_stat_items_args)
= render 'stat_anchor_list', anchors: project_stat_button_items(@project) = render 'stat_anchor_list', anchors: @project.project_stat_button_items(project_stat_items_args)
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
...@@ -25,7 +27,7 @@ ...@@ -25,7 +27,7 @@
= icon("exclamation-triangle fw") = icon("exclamation-triangle fw")
#{ _('Archived project! Repository is read-only') } #{ _('Archived project! Repository is read-only') }
- view_path = default_project_view - view_path = @project.default_project_view
- if show_auto_devops_callout?(@project) - if show_auto_devops_callout?(@project)
= render 'shared/auto_devops_callout' = render 'shared/auto_devops_callout'
......
...@@ -69,7 +69,7 @@ ...@@ -69,7 +69,7 @@
- else - else
= form.submit 'Save changes', class: 'btn btn-save' = form.submit 'Save changes', class: 'btn btn-save'
- if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) - if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = issuable.project.present.contribution_guide_path)
.inline.prepend-top-10 .inline.prepend-top-10
Please review the Please review the
%strong= link_to('contribution guidelines', guide_url) %strong= link_to('contribution guidelines', guide_url)
......
require 'spec_helper' require 'spec_helper'
describe 'Project show page', :feature do describe 'Project show page', :feature do
include ProjectsHelper
context 'when project pending delete' do context 'when project pending delete' do
let(:project) { create(:project, :empty_repo, pending_delete: true) } let(:project) { create(:project, :empty_repo, pending_delete: true) }
...@@ -29,6 +27,7 @@ describe 'Project show page', :feature do ...@@ -29,6 +27,7 @@ describe 'Project show page', :feature do
describe 'empty project' do describe 'empty project' do
let(:project) { create(:project, :public, :empty_repo) } let(:project) { create(:project, :public, :empty_repo) }
let(:presenter) { project.present(current_user: user) }
describe 'as a normal user' do describe 'as a normal user' do
before do before do
...@@ -71,13 +70,13 @@ describe 'Project show page', :feature do ...@@ -71,13 +70,13 @@ describe 'Project show page', :feature do
it '"Add Readme" button linked to new file populated for a readme' do it '"Add Readme" button linked to new file populated for a readme' do
page.within('.project-stats') do page.within('.project-stats') do
expect(page).to have_link('Add Readme', href: add_special_file_path(project, file_name: 'README.md')) expect(page).to have_link('Add Readme', href: presenter.add_readme_path)
end end
end end
it '"Add License" button linked to new file populated for a license' do it '"Add License" button linked to new file populated for a license' do
page.within('.project-stats') do page.within('.project-stats') do
expect(page).to have_link('Add License', href: add_special_file_path(project, file_name: 'LICENSE')) expect(page).to have_link('Add License', href: presenter.add_license_path)
end end
end end
...@@ -121,6 +120,7 @@ describe 'Project show page', :feature do ...@@ -121,6 +120,7 @@ describe 'Project show page', :feature do
describe 'populated project' do describe 'populated project' do
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
let(:presenter) { project.present(current_user: user) }
describe 'as a normal user' do describe 'as a normal user' do
before do before do
...@@ -192,7 +192,7 @@ describe 'Project show page', :feature do ...@@ -192,7 +192,7 @@ describe 'Project show page', :feature do
expect(project.repository.gitlab_ci_yml).to be_nil expect(project.repository.gitlab_ci_yml).to be_nil
page.within('.project-stats') do page.within('.project-stats') do
expect(page).to have_link('Set up CI/CD', href: add_special_file_path(project, file_name: '.gitlab-ci.yml')) expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path)
end end
end end
...@@ -327,7 +327,7 @@ describe 'Project show page', :feature do ...@@ -327,7 +327,7 @@ describe 'Project show page', :feature do
visit project_path(project) visit project_path(project)
page.within('.project-stats') do page.within('.project-stats') do
expect(page).to have_link('Set up Koding', href: add_koding_stack_path(project)) expect(page).to have_link('Set up Koding', href: presenter.add_koding_stack_path)
end end
end end
end end
......
...@@ -264,32 +264,6 @@ describe ProjectsHelper do ...@@ -264,32 +264,6 @@ describe ProjectsHelper do
end end
end end
describe '#license_short_name' do
let(:project) { create(:project) }
context 'when project.repository has a license_key' do
it 'returns the nickname of the license if present' do
allow(project.repository).to receive(:license_key).and_return('agpl-3.0')
expect(helper.license_short_name(project)).to eq('GNU AGPLv3')
end
it 'returns the name of the license if nickname is not present' do
allow(project.repository).to receive(:license_key).and_return('mit')
expect(helper.license_short_name(project)).to eq('MIT License')
end
end
context 'when project.repository has no license_key but a license_blob' do
it 'returns LICENSE' do
allow(project.repository).to receive(:license_key).and_return(nil)
expect(helper.license_short_name(project)).to eq('LICENSE')
end
end
end
describe '#sanitized_import_error' do describe '#sanitized_import_error' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
......
require 'spec_helper'
describe ProjectPresenter do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:presenter) { described_class.new(project, current_user: user) }
describe '#license_short_name' do
context 'when project.repository has a license_key' do
it 'returns the nickname of the license if present' do
allow(project.repository).to receive(:license_key).and_return('agpl-3.0')
expect(presenter.license_short_name).to eq('GNU AGPLv3')
end
it 'returns the name of the license if nickname is not present' do
allow(project.repository).to receive(:license_key).and_return('mit')
expect(presenter.license_short_name).to eq('MIT License')
end
end
context 'when project.repository has no license_key but a license_blob' do
it 'returns LICENSE' do
allow(project.repository).to receive(:license_key).and_return(nil)
expect(presenter.license_short_name).to eq('LICENSE')
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment