diff options
Diffstat (limited to 'app')
51 files changed, 283 insertions, 198 deletions
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue index 648fa6ff804..396a675b4ac 100644 --- a/app/assets/javascripts/notes/components/comment_form.vue +++ b/app/assets/javascripts/notes/components/comment_form.vue @@ -317,10 +317,10 @@ Please check your network connection and try again.`; <note-signed-out-widget v-if="!isLoggedIn" /> <discussion-locked-widget issuable-type="issue" - v-else-if="!canCreateNote" + v-else-if="isLocked(getNoteableData) && !canCreateNote" /> <ul - v-else + v-else-if="canCreateNote" class="notes notes-form timeline"> <li class="timeline-entry"> <div class="timeline-entry-inner"> diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue index a7e2d857013..626b0799581 100644 --- a/app/assets/javascripts/notes/components/note_actions.vue +++ b/app/assets/javascripts/notes/components/note_actions.vue @@ -40,6 +40,10 @@ export default { type: Boolean, required: true, }, + canAwardEmoji: { + type: Boolean, + required: true, + }, canDelete: { type: Boolean, required: true, @@ -74,9 +78,6 @@ export default { shouldShowActionsDropdown() { return this.currentUserId && (this.canEdit || this.canReportAsAbuse); }, - canAddAwardEmoji() { - return this.currentUserId; - }, isAuthoredByCurrentUser() { return this.authorId === this.currentUserId; }, @@ -149,7 +150,7 @@ export default { </button> </div> <div - v-if="canAddAwardEmoji" + v-if="canAwardEmoji" class="note-actions-item"> <a v-tooltip diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue index 6cb8229e268..e8fd155a1ee 100644 --- a/app/assets/javascripts/notes/components/note_awards_list.vue +++ b/app/assets/javascripts/notes/components/note_awards_list.vue @@ -28,6 +28,10 @@ export default { type: Number, required: true, }, + canAwardEmoji: { + type: Boolean, + required: true, + }, }, computed: { ...mapGetters(['getUserData']), @@ -67,9 +71,6 @@ export default { isAuthoredByMe() { return this.noteAuthorId === this.getUserData.id; }, - isLoggedIn() { - return this.getUserData.id; - }, }, created() { this.emojiSmiling = emojiSmiling; @@ -156,7 +157,7 @@ export default { return title; }, handleAward(awardName) { - if (!this.isLoggedIn) { + if (!this.canAwardEmoji) { return; } @@ -208,7 +209,7 @@ export default { </span> </button> <div - v-if="isLoggedIn" + v-if="canAwardEmoji" class="award-menu-holder"> <button v-tooltip diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue index 069f94c5845..0cb626c14f4 100644 --- a/app/assets/javascripts/notes/components/note_body.vue +++ b/app/assets/javascripts/notes/components/note_body.vue @@ -112,6 +112,7 @@ export default { :note-author-id="note.author.id" :awards="note.award_emoji" :toggle-award-path="note.toggle_award_path" + :can-award-emoji="note.current_user.can_award_emoji" /> <note-attachment v-if="note.attachment" diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index 3554027d2b4..566f5c68e66 100644 --- a/app/assets/javascripts/notes/components/noteable_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -177,6 +177,7 @@ export default { :note-id="note.id" :access-level="note.human_access" :can-edit="note.current_user.can_edit" + :can-award-emoji="note.current_user.can_award_emoji" :can-delete="note.current_user.can_edit" :can-report-as-abuse="canReportAsAbuse" :report-abuse-path="note.report_abuse_path" diff --git a/app/controllers/concerns/checks_collaboration.rb b/app/controllers/concerns/checks_collaboration.rb new file mode 100644 index 00000000000..81367663a06 --- /dev/null +++ b/app/controllers/concerns/checks_collaboration.rb @@ -0,0 +1,21 @@ +module ChecksCollaboration + def can_collaborate_with_project?(project, ref: nil) + return true if can?(current_user, :push_code, project) + + can_create_merge_request = + can?(current_user, :create_merge_request_in, project) && + current_user.already_forked?(project) + + can_create_merge_request || + user_access(project).can_push_to_branch?(ref) + end + + # rubocop:disable Gitlab/ModuleWithInstanceVariables + # enabling this so we can easily cache the user access value as it might be + # used across multiple calls in the view + def user_access(project) + @user_access ||= {} + @user_access[project] ||= Gitlab::UserAccess.new(current_user, project: project) + end + # rubocop:enable Gitlab/ModuleWithInstanceVariables +end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 6d9b42a2c04..032bb2267e7 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -1,5 +1,6 @@ class Projects::ApplicationController < ApplicationController include RoutableActions + include ChecksCollaboration skip_before_action :authenticate_user! before_action :project @@ -31,14 +32,6 @@ class Projects::ApplicationController < ApplicationController @repository ||= project.repository end - def can_collaborate_with_project?(project = nil, ref: nil) - project ||= @project - - can?(current_user, :push_code, project) || - (current_user && current_user.already_forked?(project)) || - user_access(project).can_push_to_branch?(ref) - end - def authorize_action!(action) unless can?(current_user, action, project) return access_denied! @@ -91,9 +84,4 @@ class Projects::ApplicationController < ApplicationController def check_issues_available! return render_404 unless @project.feature_available?(:issues, current_user) end - - def user_access(project) - @user_access ||= {} - @user_access[project] ||= Gitlab::UserAccess.new(current_user, project: project) - end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index b14939c4216..767e492f566 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -20,7 +20,7 @@ class Projects::IssuesController < Projects::ApplicationController before_action :authorize_update_issuable!, only: [:edit, :update, :move] # Allow create a new branch and empty WIP merge request from current issue - before_action :authorize_create_merge_request!, only: [:create_merge_request] + before_action :authorize_create_merge_request_from!, only: [:create_merge_request] respond_to :html diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index a90030a8312..4a377fefc62 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -5,7 +5,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap skip_before_action :merge_request before_action :whitelist_query_limiting, only: [:create] - before_action :authorize_create_merge_request! + before_action :authorize_create_merge_request_from! before_action :apply_diff_view_cookie!, only: [:diffs, :diff_for_path] before_action :build_merge_request, except: [:create] diff --git a/app/finders/merge_request_target_project_finder.rb b/app/finders/merge_request_target_project_finder.rb index f358938344e..188ec447a94 100644 --- a/app/finders/merge_request_target_project_finder.rb +++ b/app/finders/merge_request_target_project_finder.rb @@ -12,6 +12,7 @@ class MergeRequestTargetProjectFinder if @source_project.fork_network @source_project.fork_network.projects .public_or_visible_to_user(current_user) + .non_archived .with_feature_available_for_user(:merge_requests, current_user) else Project.where(id: source_project) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 2b440e4d584..866b8773db6 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -59,7 +59,7 @@ module BlobHelper button_tag label, class: "#{common_classes} disabled has-tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' } elsif can_modify_blob?(blob, project, ref) button_tag label, class: "#{common_classes}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' - elsif can?(current_user, :fork_project, project) + elsif can?(current_user, :fork_project, project) && can?(current_user, :create_merge_request_in, project) edit_fork_button_tag(common_classes, project, label, edit_modify_file_fork_params(action), action) end end @@ -280,7 +280,7 @@ module BlobHelper options << link_to("submit an issue", new_project_issue_path(project)) end - merge_project = can?(current_user, :create_merge_request, project) ? project : (current_user && current_user.fork_of(project)) + merge_project = merge_request_source_project_for_project(@project) if merge_project options << link_to("create a merge request", project_new_merge_request_path(project)) end @@ -334,7 +334,7 @@ module BlobHelper # Web IDE (Beta) requires the user to have this feature enabled elsif !current_user || (current_user && can_modify_blob?(blob, project, ref)) edit_link_tag(text, edit_path, common_classes) - elsif current_user && can?(current_user, :fork_project, project) + elsif can?(current_user, :fork_project, project) && can?(current_user, :create_merge_request_in, project) edit_fork_button_tag(common_classes, project, text, edit_blob_fork_params(edit_path)) end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 7cc56de24e4..98894b86551 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -163,7 +163,7 @@ module CommitsHelper tooltip = "#{action.capitalize} this #{commit.change_type_title(current_user)} in a new merge request" if has_tooltip btn_class = "btn btn-#{btn_class}" unless btn_class.nil? - if can_collaborate_with_project? + if can_collaborate_with_project?(@project) link_to action.capitalize, "#modal-#{action}-commit", 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" elsif can?(current_user, :fork_project, @project) continue_params = { diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb index 8bf96c0905f..2df5b5d1695 100644 --- a/app/helpers/compare_helper.rb +++ b/app/helpers/compare_helper.rb @@ -3,7 +3,7 @@ module CompareHelper from.present? && to.present? && from != to && - can?(current_user, :create_merge_request, project) && + can?(current_user, :create_merge_request_from, project) && project.repository.branch_exists?(from) && project.repository.branch_exists?(to) end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 0f25d401406..96dc7ae1185 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -82,8 +82,8 @@ module IssuesHelper names.to_sentence end - def award_state_class(awards, current_user) - if !current_user + def award_state_class(awardable, awards, current_user) + if !can?(current_user, :award_emoji, awardable) "disabled" elsif current_user && awards.find { |a| a.user_id == current_user.id } "active" @@ -126,6 +126,17 @@ module IssuesHelper link_to link_text, path end + def show_new_issue_link?(project) + return false unless project + return false if project.archived? + + # We want to show the link to users that are not signed in, that way they + # get directed to the sign-in/sign-up flow and afterwards to the new issue page. + return true unless current_user + + can?(current_user, :create_issue, project) + end + # Required for Banzai::Filter::IssueReferenceFilter module_function :url_for_issue module_function :url_for_internal_issue diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index fb4fe1c40b7..c19c5b9cc82 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -138,6 +138,18 @@ module MergeRequestsHelper end end + def merge_request_source_project_for_project(project = @project) + unless can?(current_user, :create_merge_request_in, project) + return nil + end + + if can?(current_user, :create_merge_request_from, project) + project + else + current_user.fork_of(project) + end + end + def merge_params_ee(merge_request) {} end diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 27ed48fdbc7..7f67574a428 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -6,10 +6,6 @@ module NotesHelper end end - def note_editable?(note) - Ability.can_edit_note?(current_user, note) - end - def note_supports_quick_actions?(note) Notes::QuickActionsService.supported?(note) end diff --git a/app/models/ability.rb b/app/models/ability.rb index 6dae49f38dc..618d4af4272 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -46,10 +46,6 @@ class Ability end end - def can_edit_note?(user, note) - allowed?(user, :edit_note, note) - end - def allowed?(user, action, subject = :global, opts = {}) if subject.is_a?(Hash) opts, subject = subject, :global diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index d8394415362..fce37e7f78e 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -79,11 +79,7 @@ module Awardable end def user_can_award?(current_user, name) - if user_authored?(current_user) - !awardable_votes?(normalize_name(name)) - else - true - end + awardable_by_user?(current_user, name) && Ability.allowed?(current_user, :award_emoji, self) end def user_authored?(current_user) @@ -119,4 +115,12 @@ module Awardable def normalize_name(name) Gitlab::Emoji.normalize_emoji_name(name) end + + def awardable_by_user?(current_user, name) + if user_authored?(current_user) + !awardable_votes?(normalize_name(name)) + else + true + end + end end diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb index 1ab391a5a9d..808a81cbbf9 100644 --- a/app/policies/ci/build_policy.rb +++ b/app/policies/ci/build_policy.rb @@ -11,7 +11,7 @@ module Ci end condition(:owner_of_job) do - can?(:developer_access) && @subject.triggered_by?(@user) + @subject.triggered_by?(@user) end rule { protected_ref }.policy do @@ -19,6 +19,6 @@ module Ci prevent :erase_build end - rule { can?(:master_access) | owner_of_job }.enable :erase_build + rule { can?(:admin_build) | (can?(:update_build) & owner_of_job) }.enable :erase_build end end diff --git a/app/policies/ci/pipeline_schedule_policy.rb b/app/policies/ci/pipeline_schedule_policy.rb index dc7a4aed577..ecba0488d3c 100644 --- a/app/policies/ci/pipeline_schedule_policy.rb +++ b/app/policies/ci/pipeline_schedule_policy.rb @@ -7,23 +7,17 @@ module Ci end condition(:owner_of_schedule) do - can?(:developer_access) && pipeline_schedule.owned_by?(@user) + pipeline_schedule.owned_by?(@user) end - condition(:non_owner_of_schedule) do - !pipeline_schedule.owned_by?(@user) - end - - rule { can?(:developer_access) }.policy do - enable :play_pipeline_schedule - end + rule { can?(:create_pipeline) }.enable :play_pipeline_schedule - rule { can?(:master_access) | owner_of_schedule }.policy do + rule { can?(:admin_pipeline) | (can?(:update_build) & owner_of_schedule) }.policy do enable :update_pipeline_schedule enable :admin_pipeline_schedule end - rule { can?(:master_access) & non_owner_of_schedule }.policy do + rule { can?(:admin_pipeline_schedule) & ~owner_of_schedule }.policy do enable :take_ownership_pipeline_schedule end diff --git a/app/policies/issuable_policy.rb b/app/policies/issuable_policy.rb index e86d1c8f98e..b431d376e3d 100644 --- a/app/policies/issuable_policy.rb +++ b/app/policies/issuable_policy.rb @@ -18,9 +18,7 @@ class IssuablePolicy < BasePolicy rule { locked & ~is_project_member }.policy do prevent :create_note - prevent :update_note prevent :admin_note prevent :resolve_note - prevent :edit_note end end diff --git a/app/policies/note_policy.rb b/app/policies/note_policy.rb index d4cb5a77e63..077a6761ee6 100644 --- a/app/policies/note_policy.rb +++ b/app/policies/note_policy.rb @@ -1,26 +1,21 @@ class NotePolicy < BasePolicy delegate { @subject.project } - delegate { @subject.noteable if @subject.noteable.lockable? } + delegate { @subject.noteable if DeclarativePolicy.has_policy?(@subject.noteable) } condition(:is_author) { @user && @subject.author == @user } - condition(:for_merge_request, scope: :subject) { @subject.for_merge_request? } condition(:is_noteable_author) { @user && @subject.noteable.author_id == @user.id } condition(:editable, scope: :subject) { @subject.editable? } - rule { ~editable | anonymous }.prevent :edit_note - - rule { is_author | admin }.enable :edit_note - rule { can?(:master_access) }.enable :edit_note + rule { ~editable }.prevent :admin_note rule { is_author }.policy do enable :read_note - enable :update_note enable :admin_note enable :resolve_note end - rule { for_merge_request & is_noteable_author }.policy do + rule { is_noteable_author }.policy do enable :resolve_note end end diff --git a/app/policies/personal_snippet_policy.rb b/app/policies/personal_snippet_policy.rb index cac0530b9f7..c1a84727cfa 100644 --- a/app/policies/personal_snippet_policy.rb +++ b/app/policies/personal_snippet_policy.rb @@ -25,4 +25,6 @@ class PersonalSnippetPolicy < BasePolicy end rule { anonymous }.prevent :comment_personal_snippet + + rule { can?(:comment_personal_snippet) }.enable :award_emoji end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 21bb0934dee..3529d0aa60c 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -1,12 +1,26 @@ class ProjectPolicy < BasePolicy - def self.create_read_update_admin(name) - [ - :"create_#{name}", - :"read_#{name}", - :"update_#{name}", - :"admin_#{name}" - ] - end + extend ClassMethods + + READONLY_FEATURES_WHEN_ARCHIVED = %i[ + issue + list + merge_request + label + milestone + project_snippet + wiki + note + pipeline + pipeline_schedule + build + trigger + environment + deployment + commit_status + container_image + pages + cluster + ].freeze desc "User is a project owner" condition :owner do @@ -15,7 +29,7 @@ class ProjectPolicy < BasePolicy end desc "Project has public builds enabled" - condition(:public_builds, scope: :subject) { project.public_builds? } + condition(:public_builds, scope: :subject, score: 0) { project.public_builds? } # For guest access we use #team_member? so we can use # project.members, which gets cached in subject scope. @@ -35,7 +49,7 @@ class ProjectPolicy < BasePolicy condition(:master) { team_access_level >= Gitlab::Access::MASTER } desc "Project is public" - condition(:public_project, scope: :subject) { project.public? } + condition(:public_project, scope: :subject, score: 0) { project.public? } desc "Project is visible to internal users" condition(:internal_access) do @@ -46,7 +60,7 @@ class ProjectPolicy < BasePolicy condition(:group_member, scope: :subject) { project_group_member? } desc "Project is archived" - condition(:archived, scope: :subject) { project.archived? } + condition(:archived, scope: :subject, score: 0) { project.archived? } condition(:default_issues_tracker, scope: :subject) { project.default_issues_tracker? } @@ -56,10 +70,10 @@ class ProjectPolicy < BasePolicy end desc "Project has an external wiki" - condition(:has_external_wiki, scope: :subject) { project.has_external_wiki? } + condition(:has_external_wiki, scope: :subject, score: 0) { project.has_external_wiki? } desc "Project has request access enabled" - condition(:request_access_enabled, scope: :subject) { project.request_access_enabled } + condition(:request_access_enabled, scope: :subject, score: 0) { project.request_access_enabled } desc "Has merge requests allowing pushes to user" condition(:has_merge_requests_allowing_pushes, scope: :subject) do @@ -126,6 +140,7 @@ class ProjectPolicy < BasePolicy rule { can?(:guest_access) }.policy do enable :read_project + enable :create_merge_request_in enable :read_board enable :read_list enable :read_wiki @@ -140,6 +155,7 @@ class ProjectPolicy < BasePolicy enable :create_note enable :upload_file enable :read_cycle_analytics + enable :award_emoji end # These abilities are not allowed to admins that are not members of the project, @@ -197,7 +213,7 @@ class ProjectPolicy < BasePolicy enable :create_pipeline enable :update_pipeline enable :create_pipeline_schedule - enable :create_merge_request + enable :create_merge_request_from enable :create_wiki enable :push_code enable :resolve_note @@ -208,7 +224,7 @@ class ProjectPolicy < BasePolicy end rule { can?(:master_access) }.policy do - enable :delete_protected_branch + enable :push_to_delete_protected_branch enable :update_project_snippet enable :update_environment enable :update_deployment @@ -231,37 +247,50 @@ class ProjectPolicy < BasePolicy end rule { archived }.policy do - prevent :create_merge_request prevent :push_code - prevent :delete_protected_branch - prevent :update_merge_request - prevent :admin_merge_request + prevent :push_to_delete_protected_branch + prevent :request_access + prevent :upload_file + prevent :resolve_note + prevent :create_merge_request_from + prevent :create_merge_request_in + prevent :award_emoji + + READONLY_FEATURES_WHEN_ARCHIVED.each do |feature| + prevent(*create_update_admin_destroy(feature)) + end + end + + rule { issues_disabled }.policy do + prevent(*create_read_update_admin_destroy(:issue)) end rule { merge_requests_disabled | repository_disabled }.policy do - prevent(*create_read_update_admin(:merge_request)) + prevent :create_merge_request_in + prevent :create_merge_request_from + prevent(*create_read_update_admin_destroy(:merge_request)) end rule { issues_disabled & merge_requests_disabled }.policy do - prevent(*create_read_update_admin(:label)) - prevent(*create_read_update_admin(:milestone)) + prevent(*create_read_update_admin_destroy(:label)) + prevent(*create_read_update_admin_destroy(:milestone)) end rule { snippets_disabled }.policy do - prevent(*create_read_update_admin(:project_snippet)) + prevent(*create_read_update_admin_destroy(:project_snippet)) end rule { wiki_disabled & ~has_external_wiki }.policy do - prevent(*create_read_update_admin(:wiki)) + prevent(*create_read_update_admin_destroy(:wiki)) prevent(:download_wiki_code) end rule { builds_disabled | repository_disabled }.policy do - prevent(*create_read_update_admin(:build)) - prevent(*(create_read_update_admin(:pipeline) - [:read_pipeline])) - prevent(*create_read_update_admin(:pipeline_schedule)) - prevent(*create_read_update_admin(:environment)) - prevent(*create_read_update_admin(:deployment)) + prevent(*create_update_admin_destroy(:pipeline)) + prevent(*create_read_update_admin_destroy(:build)) + prevent(*create_read_update_admin_destroy(:pipeline_schedule)) + prevent(*create_read_update_admin_destroy(:environment)) + prevent(*create_read_update_admin_destroy(:deployment)) end rule { repository_disabled }.policy do @@ -272,7 +301,7 @@ class ProjectPolicy < BasePolicy end rule { container_registry_disabled }.policy do - prevent(*create_read_update_admin(:container_image)) + prevent(*create_read_update_admin_destroy(:container_image)) end rule { anonymous & ~public_project }.prevent_all @@ -314,13 +343,6 @@ class ProjectPolicy < BasePolicy enable :read_pipeline_schedule end - rule { issues_disabled }.policy do - prevent :create_issue - prevent :update_issue - prevent :admin_issue - prevent :read_issue - end - # These rules are included to allow maintainers of projects to push to certain # to run pipelines for the branches they have access to. rule { can?(:public_access) & has_merge_requests_allowing_pushes }.policy do diff --git a/app/policies/project_policy/class_methods.rb b/app/policies/project_policy/class_methods.rb new file mode 100644 index 00000000000..60e5aba00ba --- /dev/null +++ b/app/policies/project_policy/class_methods.rb @@ -0,0 +1,19 @@ +class ProjectPolicy + module ClassMethods + def create_read_update_admin_destroy(name) + [ + :"read_#{name}", + *create_update_admin_destroy(name) + ] + end + + def create_update_admin_destroy(name) + [ + :"create_#{name}", + :"update_#{name}", + :"admin_#{name}", + :"destroy_#{name}" + ] + end + end +end diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb index 9f3f2637183..4b4132af2d0 100644 --- a/app/presenters/merge_request_presenter.rb +++ b/app/presenters/merge_request_presenter.rb @@ -3,6 +3,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated include GitlabRoutingHelper include MarkupHelper include TreeHelper + include ChecksCollaboration include Gitlab::Utils::StrongMemoize presents :merge_request @@ -152,11 +153,11 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated end def can_revert_on_current_merge_request? - user_can_collaborate_with_project? && cached_can_be_reverted? + can_collaborate_with_project?(project) && cached_can_be_reverted? end def can_cherry_pick_on_current_merge_request? - user_can_collaborate_with_project? && can_be_cherry_picked? + can_collaborate_with_project?(project) && can_be_cherry_picked? end def can_push_to_source_branch? @@ -195,12 +196,6 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated end.sort.to_sentence end - def user_can_collaborate_with_project? - can?(current_user, :push_code, project) || - (current_user && current_user.already_forked?(project)) || - can_push_to_source_branch? - end - def user_can_fork_project? can?(current_user, :fork_project, project) end diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index b5e2334b6e3..840fdbcbf14 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -29,6 +29,10 @@ class IssueEntity < IssuableEntity expose :can_update do |issue| can?(request.current_user, :update_issue, issue) end + + expose :can_award_emoji do |issue| + can?(request.current_user, :award_emoji, issue) + end end expose :create_note_path do |issue| diff --git a/app/serializers/note_entity.rb b/app/serializers/note_entity.rb index c964aa9c99b..06d603b277e 100644 --- a/app/serializers/note_entity.rb +++ b/app/serializers/note_entity.rb @@ -15,7 +15,11 @@ class NoteEntity < API::Entities::Note expose :current_user do expose :can_edit do |note| - Ability.can_edit_note?(request.current_user, note) + Ability.allowed?(request.current_user, :admin_note, note) + end + + expose :can_award_emoji do |note| + Ability.allowed?(request.current_user, :award_emoji, note) end end diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb index c57a2445341..fe1ac70781e 100644 --- a/app/services/merge_requests/create_service.rb +++ b/app/services/merge_requests/create_service.rb @@ -71,8 +71,8 @@ module MergeRequests params.delete(:source_project_id) params.delete(:target_project_id) - unless can?(current_user, :read_project, @source_project) && - can?(current_user, :read_project, @project) + unless can?(current_user, :create_merge_request_from, @source_project) && + can?(current_user, :create_merge_request_in, @project) raise Gitlab::Access::AccessDeniedError end diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index c47b8a88f56..aeba9788fda 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -101,7 +101,7 @@ - if @project.archived? %li %span.light archived: - %strong repository is read-only + %strong project is read-only %li %span.light access: diff --git a/app/views/award_emoji/_awards_block.html.haml b/app/views/award_emoji/_awards_block.html.haml index 5f07d2720c2..4b3c52af16a 100644 --- a/app/views/award_emoji/_awards_block.html.haml +++ b/app/views/award_emoji/_awards_block.html.haml @@ -3,13 +3,13 @@ .awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: toggle_award_url(awardable) } } - awards_sort(grouped_emojis).each do |emoji, awards| %button.btn.award-control.js-emoji-btn.has-tooltip{ type: "button", - class: [(award_state_class(awards, current_user)), (award_user_authored_class(emoji) if user_authored)], + class: [(award_state_class(awardable, awards, current_user)), (award_user_authored_class(emoji) if user_authored)], data: { placement: "bottom", title: award_user_list(awards, current_user) } } = emoji_icon(emoji) %span.award-control-text.js-counter = awards.count - - if current_user + - if can?(current_user, :award_emoji, awardable) .award-menu-holder.js-award-holder %button.btn.award-control.has-tooltip.js-add-award{ type: 'button', 'aria-label': 'Add reaction', diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml index eb32f393310..6f53f5ac1ae 100644 --- a/app/views/layouts/header/_new_dropdown.haml +++ b/app/views/layouts/header/_new_dropdown.haml @@ -19,8 +19,8 @@ %li.dropdown-bold-header GitLab - if @project&.persisted? - - create_project_issue = can?(current_user, :create_issue, @project) - - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) + - create_project_issue = show_new_issue_link?(@project) + - merge_project = merge_request_source_project_for_project(@project) - create_project_snippet = can?(current_user, :create_project_snippet, @project) - if create_project_issue || merge_project || create_project_snippet %li.dropdown-bold-header This project diff --git a/app/views/projects/_last_push.html.haml b/app/views/projects/_last_push.html.haml index 6a1035d2dc7..f6d396c8127 100644 --- a/app/views/projects/_last_push.html.haml +++ b/app/views/projects/_last_push.html.haml @@ -13,6 +13,7 @@ #{time_ago_with_tooltip(event.created_at)} - .flex-right - = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm qa-create-merge-request" do - #{ _('Create merge request') } + - if can?(current_user, :create_merge_request_in, event.project.default_merge_request_target) + .flex-right + = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm qa-create-merge-request" do + #{ _('Create merge request') } diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 883dfb3e6c8..71176acd12d 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -4,7 +4,7 @@ - diverging_commit_counts = @repository.diverging_commit_counts(branch) - number_commits_behind = diverging_commit_counts[:behind] - number_commits_ahead = diverging_commit_counts[:ahead] -- merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) +- merge_project = merge_request_source_project_for_project(@project) %li{ class: "branch-item js-branch-#{branch.name}" } .branch-info .branch-title @@ -61,7 +61,7 @@ title: s_('Branches|The default branch cannot be deleted') } = icon("trash-o") - elsif protected_branch?(@project, branch) - - if can?(current_user, :delete_protected_branch, @project) + - if can?(current_user, :push_to_delete_protected_branch, @project) %button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip", title: s_('Branches|Delete protected branch'), data: { toggle: "modal", diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 18e948ce35a..2e86a7d36d7 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -1,13 +1,17 @@ -- if current_user +- can_create_issue = show_new_issue_link?(@project) +- can_create_project_snippet = can?(current_user, :create_project_snippet, @project) +- can_push_code = can?(current_user, :push_code, @project) +- create_mr_from_new_fork = can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project) +- merge_project = merge_request_source_project_for_project(@project) + +- show_menu = can_create_issue || can_create_project_snippet || can_push_code || create_mr_from_new_fork || merge_project + +- if show_menu .project-action-button.dropdown.inline %a.btn.dropdown-toggle.has-tooltip{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...') } = icon('plus') = icon("caret-down") %ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown - - can_create_issue = can?(current_user, :create_issue, @project) - - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) - - can_create_project_snippet = can?(current_user, :create_project_snippet, @project) - - if can_create_issue || merge_project || can_create_project_snippet %li.dropdown-header= _('This project') @@ -20,17 +24,17 @@ - if can_create_project_snippet %li= link_to _('New snippet'), new_project_snippet_path(@project) - - if can?(current_user, :push_code, @project) + - if can_push_code %li.dropdown-header= _('This repository') - - if can?(current_user, :push_code, @project) + - if can_push_code %li= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master') - unless @project.empty_repo? %li= link_to _('New branch'), new_project_branch_path(@project) %li= link_to _('New tag'), new_project_tag_path(@project) - - elsif current_user && current_user.already_forked?(@project) + - elsif can_collaborate_with_project?(@project) %li= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master') - - elsif can?(current_user, :fork_project, @project) + - elsif create_mr_from_new_fork - continue_params = { to: project_new_blob_path(@project, @project.default_branch || 'master'), notice: edit_in_new_fork_notice, notice_now: edit_in_new_fork_notice_now } diff --git a/app/views/projects/clusters/_empty_state.html.haml b/app/views/projects/clusters/_empty_state.html.haml index 112dde66ff7..5f49d03b1bb 100644 --- a/app/views/projects/clusters/_empty_state.html.haml +++ b/app/views/projects/clusters/_empty_state.html.haml @@ -7,5 +7,6 @@ - link_to_help_page = link_to(_('Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer') %p= s_('ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page} - .text-center - = link_to s_('ClusterIntegration|Add Kubernetes cluster'), new_project_cluster_path(@project), class: 'btn btn-success' + - if can?(current_user, :create_cluster, @project) + .text-center + = link_to s_('ClusterIntegration|Add Kubernetes cluster'), new_project_cluster_path(@project), class: 'btn btn-success' diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 74c5317428c..213c4c90a0e 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -1,3 +1,5 @@ +- can_collaborate = can_collaborate_with_project?(@project) + .page-content-header.js-commit-box{ 'data-commit-path' => branches_project_commit_path(@project, @commit.id) } .header-main-content = render partial: 'signature', object: @commit.signature @@ -32,12 +34,13 @@ %li.visible-xs-block.visible-sm-block = link_to project_tree_path(@project, @commit) do #{ _('Browse Files') } - - unless @commit.has_been_reverted?(current_user) + - if can_collaborate && !@commit.has_been_reverted?(current_user) %li.clearfix = revert_commit_link(@commit, project_commit_path(@project, @commit.id), has_tooltip: false) - %li.clearfix - = cherry_pick_commit_link(@commit, project_commit_path(@project, @commit.id), has_tooltip: false) - - if can_collaborate_with_project? + - if can_collaborate + %li.clearfix + = cherry_pick_commit_link(@commit, project_commit_path(@project, @commit.id), has_tooltip: false) + - if can?(current_user, :push_code, @project) %li.clearfix = link_to s_("CreateTag|Tag"), new_project_tag_path(@project, ref: @commit) %li.divider diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index abb292f8f27..541ae905246 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -17,6 +17,6 @@ .limited-width-notes = render "shared/notes/notes_with_form", :autocomplete => true - - if can_collaborate_with_project? + - if can_collaborate_with_project?(@project) - %w(revert cherry-pick).each do |type| = render "projects/commit/change", type: type, commit: @commit, title: @commit.title diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 99eeb9551e3..0994498c6be 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -114,17 +114,18 @@ Archive project - if @project.archived? %p - Unarchiving the project will mark its repository as active. The project can be committed to. + Unarchiving the project will restore people's ability to make changes to it. + The repository can be committed to, and issues, comments and other entities can be created. %strong Once active this project shows up in the search and on the dashboard. = link_to 'Unarchive project', unarchive_project_path(@project), - data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." }, + data: { confirm: "Are you sure that you want to unarchive this project?" }, method: :post, class: "btn btn-success" - else %p - Archiving the project will mark its repository as read-only. It is hidden from the dashboard and doesn't show up in searches. - %strong Archived projects cannot be committed to! + Archiving the project will make it entirely read-only. It is hidden from the dashboard and doesn't show up in searches. + %strong The repository cannot be committed to, and no issues, comments or other entities can be created. = link_to 'Archive project', archive_project_path(@project), - data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, + data: { confirm: "Are you sure that you want to archive this project?" }, method: :post, class: "btn btn-warning" .sub-section.rename-respository %h4.warning-title diff --git a/app/views/projects/issues/_nav_btns.html.haml b/app/views/projects/issues/_nav_btns.html.haml index 0d39edb7bfd..dd1a836fa20 100644 --- a/app/views/projects/issues/_nav_btns.html.haml +++ b/app/views/projects/issues/_nav_btns.html.haml @@ -2,9 +2,10 @@ = icon('rss') - if @can_bulk_update = button_tag "Edit issues", class: "btn btn-default append-right-10 js-bulk-update-toggle" -= link_to "New issue", new_project_issue_path(@project, - issue: { assignee_id: finder.assignee.try(:id), - milestone_id: finder.milestones.first.try(:id) }), - class: "btn btn-new", - title: "New issue", - id: "new_issue_link" +- if show_new_issue_link?(@project) + = link_to "New issue", new_project_issue_path(@project, + issue: { assignee_id: finder.assignee.try(:id), + milestone_id: finder.milestones.first.try(:id) }), + class: "btn btn-new", + title: "New issue", + id: "new_issue_link" diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml index 36e24037214..4b8bf578b28 100644 --- a/app/views/projects/issues/_new_branch.html.haml +++ b/app/views/projects/issues/_new_branch.html.haml @@ -1,8 +1,8 @@ -- can_create_merge_request = can?(current_user, :create_merge_request, @project) -- data_action = can_create_merge_request ? 'create-mr' : 'create-branch' -- value = can_create_merge_request ? 'Create merge request' : 'Create branch' - - if can?(current_user, :push_code, @project) + - can_create_merge_request = can?(current_user, :create_merge_request_in, @project) + - data_action = can_create_merge_request ? 'create-mr' : 'create-branch' + - value = can_create_merge_request ? 'Create merge request' : 'Create branch' + - can_create_path = can_create_branch_project_issue_path(@project, @issue) - create_mr_path = create_merge_request_project_issue_path(@project, @issue, branch_name: @issue.to_branch_name, ref: @project.default_branch) - create_branch_path = project_branches_path(@project, branch_name: @issue.to_branch_name, ref: @project.default_branch, issue_iid: @issue.iid) diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index ec7e87219f5..f1fc1c2316d 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -7,6 +7,7 @@ - can_update_issue = can?(current_user, :update_issue, @issue) - can_report_spam = @issue.submittable_as_spam_by?(current_user) +- can_create_issue = show_new_issue_link?(@project) .detail-page-header .detail-page-header-body @@ -42,16 +43,18 @@ %li= link_to 'Reopen issue', issue_path(@issue, issue: { state_event: :reopen }, format: 'json'), class: "btn-reopen js-btn-issue-action #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' - if can_report_spam %li= link_to 'Submit as spam', mark_as_spam_project_issue_path(@project, @issue), method: :post, class: 'btn-spam', title: 'Submit as spam' - - if can_update_issue || can_report_spam - %li.divider - %li= link_to 'New issue', new_project_issue_path(@project), title: 'New issue', id: 'new_issue_link' + - if can_create_issue + - if can_update_issue || can_report_spam + %li.divider + %li= link_to 'New issue', new_project_issue_path(@project), title: 'New issue', id: 'new_issue_link' = render 'shared/issuable/close_reopen_button', issuable: @issue, can_update: can_update_issue - if can_report_spam = link_to 'Submit as spam', mark_as_spam_project_issue_path(@project, @issue), method: :post, class: 'hidden-xs hidden-sm btn btn-grouped btn-spam', title: 'Submit as spam' - = link_to new_project_issue_path(@project), class: 'hidden-xs hidden-sm btn btn-grouped new-issue-link btn-new btn-inverted', title: 'New issue', id: 'new_issue_link' do - New issue + - if can_create_issue + = link_to new_project_issue_path(@project), class: 'hidden-xs hidden-sm btn btn-grouped new-issue-link btn-new btn-inverted', title: 'New issue', id: 'new_issue_link' do + New issue .issue-details.issuable-details .detail-page-description.content-block diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index b2c0d9e1cfa..623380c9c61 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -1,6 +1,6 @@ - @no_container = true - @can_bulk_update = can?(current_user, :admin_merge_request, @project) -- merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) +- merge_project = merge_request_source_project_for_project(@project) - new_merge_request_path = project_new_merge_request_path(merge_project) if merge_project - page_title "Merge Requests" diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml index 5ea653ccad5..b4fe1cabdfd 100644 --- a/app/views/projects/notes/_actions.html.haml +++ b/app/views/projects/notes/_actions.html.haml @@ -36,7 +36,7 @@ %template{ 'v-else' => '' } = render 'shared/icons/icon_resolve_discussion.svg' -- if current_user +- if can?(current_user, :award_emoji, note) - if note.emoji_awardable? - user_authored = note.user_authored?(current_user) .note-actions-item diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 94331a16abd..e28accd5b43 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -24,7 +24,7 @@ .text-warning.center.prepend-top-20 %p = icon("exclamation-triangle fw") - #{ _('Archived project! Repository is read-only') } + #{ _('Archived project! Repository and other project resources are read-only') } - view_path = @project.default_view diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 3d5f92f9aaa..98b4d6339da 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -31,6 +31,6 @@ = link_to edit_project_tag_release_path(@project, tag.name), class: 'btn has-tooltip', title: s_('TagsPage|Edit release notes'), data: { container: "body" } do = icon("pencil") - - if can?(current_user, :admin_project, @project) - = link_to project_tag_path(@project, tag.name), class: "btn btn-remove remove-row has-tooltip prepend-left-10 #{protected_tag?(@project, tag) ? 'disabled' : ''}", title: s_('TagsPage|Delete tag'), method: :delete, data: { confirm: s_('TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?') % { tag_name: tag.name }, container: 'body' }, remote: true do - = icon("trash-o") + - if can?(current_user, :admin_project, @project) + = link_to project_tag_path(@project, tag.name), class: "btn btn-remove remove-row has-tooltip prepend-left-10 #{protected_tag?(@project, tag) ? 'disabled' : ''}", title: s_('TagsPage|Delete tag'), method: :delete, data: { confirm: s_('TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?') % { tag_name: tag.name }, container: 'body' }, remote: true do + = icon("trash-o") diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index dfe2c37ed8e..7a3469cdd26 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -28,7 +28,7 @@ = icon('history') .btn-container.controls-item = render 'projects/buttons/download', project: @project, ref: @tag.name - - if can?(current_user, :admin_project, @project) + - if can?(current_user, :push_code, @project) && can?(current_user, :admin_project, @project) .btn-container.controls-item-full = link_to project_tag_path(@project, @tag.name), class: "btn btn-remove remove-row has-tooltip #{protected_tag?(@project, @tag) ? 'disabled' : ''}", title: s_('TagsPage|Delete tag'), method: :delete, data: { confirm: s_('TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?') % { tag_name: @tag.name } } do %i.fa.fa-trash-o diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 5ef5e9c09a2..8587d3b0c0d 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -1,3 +1,6 @@ +- can_collaborate = can_collaborate_with_project?(@project) +- can_create_mr_from_fork = can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project) + .tree-ref-container .tree-ref-holder = render 'shared/ref_switcher', destination: 'tree', path: @path, show_create: true @@ -15,7 +18,7 @@ %li = link_to truncate(title, length: 40), project_tree_path(@project, tree_join(@ref, path)) - - if current_user + - if can_collaborate || can_create_mr_from_fork %li %a.btn.add-to-tree{ addtotree_toggle_attributes } = sprite_icon('plus', size: 16, css_class: 'pull-left') @@ -35,7 +38,7 @@ %li = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do #{ _('New directory') } - - elsif can?(current_user, :fork_project, @project) + - elsif can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project) %li - continue_params = { to: project_new_blob_path(@project, @id), notice: edit_in_new_fork_notice, @@ -61,23 +64,25 @@ = link_to fork_path, method: :post do #{ _('New directory') } - %li.divider - %li.dropdown-header - #{ _('This repository') } - %li - = link_to new_project_branch_path(@project) do - #{ _('New branch') } - %li - = link_to new_project_tag_path(@project) do - #{ _('New tag') } + - if can?(current_user, :push_code, @project) + %li.divider + %li.dropdown-header + #{ _('This repository') } + %li + = link_to new_project_branch_path(@project) do + #{ _('New branch') } + %li + = link_to new_project_tag_path(@project) do + #{ _('New tag') } .tree-controls = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' = render 'projects/find_file_link' - = succeed " " do - = link_to ide_edit_path(@project, @id, ""), class: 'btn btn-default' do - = _('Web IDE') + - if can_collaborate + = succeed " " do + = link_to ide_edit_path(@project, @id, ""), class: 'btn btn-default' do + = _('Web IDE') = render 'projects/buttons/download', project: @project, ref: @ref diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 56403907844..836df57a3a2 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -47,20 +47,20 @@ class: 'text-danger' .pull-right.hidden-xs.hidden-sm - - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) - %button.js-promote-project-label-button.btn.btn-transparent.btn-action.has-tooltip{ title: _('Promote to Group Label'), - disabled: true, - type: 'button', - data: { url: promote_project_label_path(label.project, label), - label_title: label.title, - label_color: label.color, - label_text_color: label.text_color, - group_name: label.project.group.name, - target: '#promote-label-modal', - container: 'body', - toggle: 'modal' } } - = sprite_icon('level-up') - if can?(current_user, :admin_label, label) + - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) + %button.js-promote-project-label-button.btn.btn-transparent.btn-action.has-tooltip{ title: _('Promote to Group Label'), + disabled: true, + type: 'button', + data: { url: promote_project_label_path(label.project, label), + label_title: label.title, + label_color: label.color, + label_text_color: label.text_color, + group_name: label.project.group.name, + target: '#promote-label-modal', + container: 'body', + toggle: 'modal' } } + = sprite_icon('level-up') = link_to edit_label_path(label), title: "Edit", class: 'btn btn-transparent btn-action', data: {toggle: "tooltip"} do %span.sr-only Edit = sprite_icon('pencil') diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml index a942ebc328b..ee134480705 100644 --- a/app/views/shared/milestones/_sidebar.html.haml +++ b/app/views/shared/milestones/_sidebar.html.haml @@ -72,7 +72,7 @@ .title.hide-collapsed Issues %span.badge= milestone.issues_visible_to_user(current_user).count - - if project && can?(current_user, :create_issue, project) + - if show_new_issue_link?(project) = link_to new_project_issue_path(project, issue: { milestone_id: milestone.id }), class: "pull-right", title: "New Issue" do New issue .value.hide-collapsed.bold diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index bf359774ead..893a7f26ebd 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -2,7 +2,7 @@ - return if note.cross_reference_not_visible_for?(current_user) - show_image_comment_badge = local_assigns.fetch(:show_image_comment_badge, false) -- note_editable = note_editable?(note) +- note_editable = can?(current_user, :admin_note, note) - note_counter = local_assigns.fetch(:note_counter, 0) %li.timeline-entry{ id: dom_id(note), |