Commit 05ef7ba1 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'permission-improvements' into 'master'

Update permissions for issue tracker management

Don't allow guest or reporter to set assignee, milestone and label when create or update new issue and merge request.
After this change `Guest` and `Reporter` rule is used to report issues but only `Developer` and higher roles can manage issues (schedule milestone, assign to user or close any issue)

Also I removed some duplication code between issues and merge requests and put all issuable partials in one directory

See merge request !890
parents c342a9ab 58ceb8e9
...@@ -21,6 +21,7 @@ v 7.13.0 (unreleased) ...@@ -21,6 +21,7 @@ v 7.13.0 (unreleased)
- Show a user's Two-factor Authentication status in the administration area. - Show a user's Two-factor Authentication status in the administration area.
- Explicit error when commit not found in the CI - Explicit error when commit not found in the CI
- Improve performance for issue and merge request pages - Improve performance for issue and merge request pages
- Users with guest access level can not set assignee, labels or milestones for issue and merge request
v 7.12.0 (unreleased) v 7.12.0 (unreleased)
- Fix Error 500 when one user attempts to access a personal, internal snippet (Stan Hu) - Fix Error 500 when one user attempts to access a personal, internal snippet (Stan Hu)
......
...@@ -31,20 +31,14 @@ class Dispatcher ...@@ -31,20 +31,14 @@ class Dispatcher
when 'projects:compare:show' when 'projects:compare:show'
new Diff() new Diff()
when 'projects:issues:new','projects:issues:edit' when 'projects:issues:new','projects:issues:edit'
GitLab.GfmAutoComplete.setup()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ZenMode()
new DropzoneInput($('.issue-form')) new DropzoneInput($('.issue-form'))
if page == 'projects:issues:new' new IssuableForm($('.issue-form'))
new IssuableForm($('.issue-form'))
when 'projects:merge_requests:new', 'projects:merge_requests:edit' when 'projects:merge_requests:new', 'projects:merge_requests:edit'
GitLab.GfmAutoComplete.setup()
new Diff() new Diff()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ZenMode()
new DropzoneInput($('.merge-request-form')) new DropzoneInput($('.merge-request-form'))
if page == 'projects:merge_requests:new' new IssuableForm($('.merge-request-form'))
new IssuableForm($('.merge-request-form'))
when 'projects:merge_requests:show' when 'projects:merge_requests:show'
new Diff() new Diff()
shortcut_handler = new ShortcutsIssuable() shortcut_handler = new ShortcutsIssuable()
...@@ -113,13 +107,6 @@ class Dispatcher ...@@ -113,13 +107,6 @@ class Dispatcher
new NamespaceSelect() new NamespaceSelect()
when 'dashboard' when 'dashboard'
shortcut_handler = new ShortcutsDashboardNavigation() shortcut_handler = new ShortcutsDashboardNavigation()
switch path[1]
when 'issues', 'merge_requests'
new UsersSelect()
when 'groups'
switch path[1]
when 'issues', 'merge_requests'
new UsersSelect()
when 'profiles' when 'profiles'
new Profile() new Profile()
when 'projects' when 'projects'
...@@ -135,8 +122,6 @@ class Dispatcher ...@@ -135,8 +122,6 @@ class Dispatcher
new ProjectNew() new ProjectNew()
when 'show' when 'show'
new ProjectShow() new ProjectShow()
when 'issues', 'merge_requests'
new UsersSelect()
when 'wikis' when 'wikis'
new Wikis() new Wikis()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
......
#= require jquery.waitforimages
class @IssuableContext
constructor: ->
new UsersSelect()
$('select.select2').select2({width: 'resolve', dropdownAutoWidth: true})
$(".context .inline-update").on "change", "select", ->
$(this).submit()
$(".context .inline-update").on "change", ".js-assignee", ->
$(this).submit()
$('.issuable-details').waitForImages ->
$('.issuable-affix').affix offset:
top: ->
@top = ($('.issuable-affix').offset().top - 70)
bottom: ->
@bottom = $('.footer').outerHeight(true)
$('.issuable-affix').on 'affix.bs.affix', ->
$(@).width($(@).outerWidth())
.on 'affixed-top.bs.affix affixed-bottom.bs.affix', ->
$(@).width('')
class @IssuableForm class @IssuableForm
constructor: (@form) -> constructor: (@form) ->
GitLab.GfmAutoComplete.setup()
new UsersSelect()
new ZenMode()
@titleField = @form.find("input[name*='[title]']") @titleField = @form.find("input[name*='[title]']")
@descriptionField = @form.find("textarea[name*='[description]']") @descriptionField = @form.find("textarea[name*='[description]']")
......
...@@ -3,29 +3,12 @@ ...@@ -3,29 +3,12 @@
class @Issue class @Issue
constructor: -> constructor: ->
$('.edit-issue.inline-update input[type="submit"]').hide()
$(".context .inline-update").on "change", "select", ->
$(this).submit()
$(".context .inline-update").on "change", "#issue_assignee_id", ->
$(this).submit()
# Prevent duplicate event bindings # Prevent duplicate event bindings
@disableTaskList() @disableTaskList()
if $("a.btn-close").length if $("a.btn-close").length
@initTaskList() @initTaskList()
$('.issue-details').waitForImages ->
$('.issuable-affix').affix offset:
top: ->
@top = ($('.issuable-affix').offset().top - 70)
bottom: ->
@bottom = $('.footer').outerHeight(true)
$('.issuable-affix').on 'affix.bs.affix', ->
$(@).width($(@).outerWidth())
.on 'affixed-top.bs.affix affixed-bottom.bs.affix', ->
$(@).width('')
initTaskList: -> initTaskList: ->
$('.issue-details .js-task-list-container').taskList('enable') $('.issue-details .js-task-list-container').taskList('enable')
$(document).on 'tasklist:changed', '.issue-details .js-task-list-container', @updateTaskList $(document).on 'tasklist:changed', '.issue-details .js-task-list-container', @updateTaskList
...@@ -42,5 +25,5 @@ class @Issue ...@@ -42,5 +25,5 @@ class @Issue
$.ajax $.ajax
type: 'PATCH' type: 'PATCH'
url: $('form.js-issue-update').attr('action') url: $('form.js-issuable-update').attr('action')
data: patchData data: patchData
...@@ -10,7 +10,6 @@ class @MergeRequest ...@@ -10,7 +10,6 @@ class @MergeRequest
# action - String, current controller action # action - String, current controller action
# #
constructor: (@opts) -> constructor: (@opts) ->
@initContextWidget()
this.$el = $('.merge-request') this.$el = $('.merge-request')
this.$('.show-all-commits').on 'click', => this.$('.show-all-commits').on 'click', =>
...@@ -26,28 +25,10 @@ class @MergeRequest ...@@ -26,28 +25,10 @@ class @MergeRequest
if $("a.btn-close").length if $("a.btn-close").length
@initTaskList() @initTaskList()
$('.merge-request-details').waitForImages ->
$('.issuable-affix').affix offset:
top: ->
@top = ($('.issuable-affix').offset().top - 70)
bottom: ->
@bottom = $('.footer').outerHeight(true)
$('.issuable-affix').on 'affix.bs.affix', ->
$(@).width($(@).outerWidth())
.on 'affixed-top.bs.affix affixed-bottom.bs.affix', ->
$(@).width('')
# Local jQuery finder # Local jQuery finder
$: (selector) -> $: (selector) ->
this.$el.find(selector) this.$el.find(selector)
initContextWidget: ->
$('.edit-merge_request.inline-update input[type="submit"]').hide()
$(".context .inline-update").on "change", "select", ->
$(this).submit()
$(".context .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit()
showAllCommits: -> showAllCommits: ->
this.$('.first-commits').remove() this.$('.first-commits').remove()
this.$('.all-commits').removeClass 'hide' this.$('.all-commits').removeClass 'hide'
...@@ -68,5 +49,5 @@ class @MergeRequest ...@@ -68,5 +49,5 @@ class @MergeRequest
$.ajax $.ajax
type: 'PATCH' type: 'PATCH'
url: $('form.js-merge-request-update').attr('action') url: $('form.js-issuable-update').attr('action')
data: patchData data: patchData
...@@ -145,9 +145,3 @@ h2.issue-title { ...@@ -145,9 +145,3 @@ h2.issue-title {
.issue-form .select2-container { .issue-form .select2-container {
width: 250px !important; width: 250px !important;
} }
.issues-holder {
.issue-info {
margin-left: 20px;
}
}
...@@ -52,4 +52,12 @@ module GitlabRoutingHelper ...@@ -52,4 +52,12 @@ module GitlabRoutingHelper
def project_snippet_url(entity, *args) def project_snippet_url(entity, *args)
namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args) namespace_project_snippet_url(entity.project.namespace, entity.project, entity, *args)
end end
def toggle_subscription_path(entity, *args)
if entity.is_a?(Issue)
toggle_subscription_namespace_project_issue_path(entity.project.namespace, entity.project, entity)
else
toggle_subscription_namespace_project_merge_request_path(entity.project.namespace, entity.project, entity)
end
end
end end
...@@ -185,7 +185,6 @@ class Ability ...@@ -185,7 +185,6 @@ class Ability
:modify_issue, :modify_issue,
:modify_project_snippet, :modify_project_snippet,
:modify_merge_request, :modify_merge_request,
:admin_issue,
:admin_milestone, :admin_milestone,
:admin_project_snippet, :admin_project_snippet,
:admin_project_member, :admin_project_member,
......
...@@ -26,4 +26,12 @@ class IssuableBaseService < BaseService ...@@ -26,4 +26,12 @@ class IssuableBaseService < BaseService
issuable, issuable.project, current_user, branch_type, issuable, issuable.project, current_user, branch_type,
old_branch, new_branch) old_branch, new_branch)
end end
def filter_params
unless can?(current_user, :admin_issue, project)
params.delete(:milestone_id)
params.delete(:label_ids)
params.delete(:assignee_id)
end
end
end end
module Issues module Issues
class CreateService < Issues::BaseService class CreateService < Issues::BaseService
def execute def execute
filter_params
label_params = params[:label_ids] label_params = params[:label_ids]
issue = project.issues.new(params.except(:label_ids)) issue = project.issues.new(params.except(:label_ids))
issue.author = current_user issue.author = current_user
......
...@@ -17,6 +17,7 @@ module Issues ...@@ -17,6 +17,7 @@ module Issues
params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE
params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
filter_params
old_labels = issue.labels.to_a old_labels = issue.labels.to_a
if params.present? && issue.update_attributes(params.except(:state_event, if params.present? && issue.update_attributes(params.except(:state_event,
......
module MergeRequests module MergeRequests
class CreateService < MergeRequests::BaseService class CreateService < MergeRequests::BaseService
def execute def execute
filter_params
label_params = params[:label_ids] label_params = params[:label_ids]
merge_request = MergeRequest.new(params.except(:label_ids)) merge_request = MergeRequest.new(params.except(:label_ids))
merge_request.source_project = project merge_request.source_project = project
......
...@@ -27,6 +27,7 @@ module MergeRequests ...@@ -27,6 +27,7 @@ module MergeRequests
params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE
params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
filter_params
old_labels = merge_request.labels.to_a old_labels = merge_request.labels.to_a
if params.present? && merge_request.update_attributes( if params.present? && merge_request.update_attributes(
......
...@@ -17,5 +17,5 @@ ...@@ -17,5 +17,5 @@
= link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do = link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do
%i.fa.fa-rss %i.fa.fa-rss
= render 'shared/issuable_filter', type: :issues = render 'shared/issuable/filter', type: :issues
= render 'shared/issues' = render 'shared/issues'
...@@ -7,5 +7,5 @@ ...@@ -7,5 +7,5 @@
List all merge requests from all projects you have access to. List all merge requests from all projects you have access to.
%hr %hr
.append-bottom-20 .append-bottom-20
= render 'shared/issuable_filter', type: :merge_requests = render 'shared/issuable/filter', type: :merge_requests
= render 'shared/merge_requests' = render 'shared/merge_requests'
...@@ -21,5 +21,5 @@ ...@@ -21,5 +21,5 @@
= link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do = link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do
%i.fa.fa-rss %i.fa.fa-rss
= render 'shared/issuable_filter', type: :issues = render 'shared/issuable/filter', type: :issues
= render 'shared/issues' = render 'shared/issues'
...@@ -10,5 +10,5 @@ ...@@ -10,5 +10,5 @@
To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page. To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
%hr %hr
.append-bottom-20 .append-bottom-20
= render 'shared/issuable_filter', type: :merge_requests = render 'shared/issuable/filter', type: :merge_requests
= render 'shared/merge_requests' = render 'shared/merge_requests'
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
= cross_project_reference(@project, @issue) = cross_project_reference(@project, @issue)
%hr %hr
.context .context
= render partial: 'issue_context', locals: { issue: @issue } = render 'shared/issuable/context', issuable: @issue
- if @issue.labels.any? - if @issue.labels.any?
.issuable-context-title .issuable-context-title
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
%hr %hr
= form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form' } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form' } do |f|
= render 'projects/issuable_form', f: f, issuable: @issue = render 'shared/issuable/form', f: f, issuable: @issue
:javascript :javascript
$('.assign-to-me-link').on('click', function(e){ $('.assign-to-me-link').on('click', function(e){
......
%li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue) } %li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue) }
- if controller.controller_name == 'issues' - if controller.controller_name == 'issues' && can?(current_user, :admin_issue, @project)
.issue-check .issue-check
= check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue) = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue"
.issue-title .issue-title
%span.issue-title-text %span.issue-title-text
......
= form_for [@project.namespace.becomes(Namespace), @project, @issue], remote: true, html: {class: 'edit-issue inline-update js-issue-update'} do |f|
%div.prepend-top-20
.issuable-context-title
%label
Assignee:
- if issue.assignee
%strong= link_to_member(@project, @issue.assignee, size: 24)
- else
none
- if can?(current_user, :modify_issue, @issue)
= users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id, null_user: true, first_user: true)
%div.prepend-top-20.clearfix
.issuable-context-title
%label
Milestone:
- if issue.milestone
%span.back-to-milestone
= link_to namespace_project_milestone_path(@project.namespace, @project, @issue.milestone) do
%strong
%i.fa.fa-clock-o
= @issue.milestone.title
- else
none
- if can?(current_user, :modify_issue, @issue)
= f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'})
= hidden_field_tag :issue_context
= f.submit class: 'btn'
- if current_user
- subscribed = @issue.subscribed?(current_user)
%div.prepend-top-20.clearfix
.issuable-context-title
%label
Subscription:
%button.btn.btn-block.subscribe-button{:type => 'button'}
%i.fa.fa-eye
%span= subscribed ? "Unsubscribe" : "Subscribe"
- subscribtion_status = subscribed ? "subscribed" : "unsubscribed"
.subscription-status{"data-status" => subscribtion_status}
.description-block.unsubscribed{class: ( "hidden" if subscribed )}
You're not receiving notifications from this thread.
.description-block.subscribed{class: ( "hidden" unless subscribed )}
You're receiving notifications because you're subscribed to this thread.
:coffeescript
new Subscription("#{toggle_subscription_namespace_project_issue_path(@issue.project.namespace, @project, @issue)}")
...@@ -11,14 +11,14 @@ ...@@ -11,14 +11,14 @@
= link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do = link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
%i.fa.fa-rss %i.fa.fa-rss
= render 'shared/issuable_search_form', path: namespace_project_issues_path(@project.namespace, @project) = render 'shared/issuable/search_form', path: namespace_project_issues_path(@project.namespace, @project)
- if can? current_user, :write_issue, @project - if can? current_user, :write_issue, @project
= link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
%i.fa.fa-plus %i.fa.fa-plus
New Issue New Issue
= render 'shared/issuable_filter', type: :issues = render 'shared/issuable/filter', type: :issues
.issues-holder .issues-holder
= render "issues" = render "issues"
- page_title "#{@issue.title} (##{@issue.iid})", "Issues" - page_title "#{@issue.title} (##{@issue.iid})", "Issues"
.issue .issue
.issue-details .issue-details.issuable-details
%h4.page-title %h4.page-title
.issue-box{ class: issue_box_class(@issue) } .issue-box{ class: issue_box_class(@issue) }
- if @issue.closed? - if @issue.closed?
......
- if params[:status_only] $('.context').html("#{escape_javascript(render 'shared/issuable/context', issuable: @issue)}");
- if @issue.valid? $('.context').effect('highlight')
:plain
$("##{dom_id(@issue)}").fadeOut();
- elsif params[:issue_context]
$('.context').html("#{escape_javascript(render partial: 'issue_context', locals: { issue: @issue })}");
$('.context').effect('highlight');
- if @issue.milestone
$('.milestone-nav-link').replaceWith("<span class='milestone-nav-link'>| <span class='light'>Milestone</span> #{escape_javascript(link_to @issue.milestone.title, namespace_project_milestone_path(@issue.project.namespace, @issue.project, @issue.milestone))}</span>")
- else
$('.milestone-nav-link').html('')
$('select.select2').select2({width: 'resolve', dropdownAutoWidth: true})
$('.edit-issue.inline-update input[type="submit"]').hide();
new UsersSelect()
new Issue(); new Issue();
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
= cross_project_reference(@project, @merge_request) = cross_project_reference(@project, @merge_request)
%hr %hr
.context .context
= render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request } = render 'shared/issuable/context', issuable: @merge_request
- if @merge_request.labels.any? - if @merge_request.labels.any?
.issuable-context-title .issuable-context-title
......
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form' } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form' } do |f|
.merge-request-form-info .merge-request-form-info
= render 'projects/issuable_form', f: f, issuable: @merge_request = render 'shared/issuable/form', f: f, issuable: @merge_request
:javascript :javascript
disableButtonIfEmptyField("#merge_request_title", ".btn-save"); disableButtonIfEmptyField("#merge_request_title", ".btn-save");
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
%hr %hr
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form' } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form' } do |f|
.merge-request-form-info .merge-request-form-info
= render 'projects/issuable_form', f: f, issuable: @merge_request = render 'shared/issuable/form', f: f, issuable: @merge_request
= f.hidden_field :source_project_id = f.hidden_field :source_project_id
= f.hidden_field :source_branch = f.hidden_field :source_branch
= f.hidden_field :target_project_id = f.hidden_field :target_project_id
......
- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests" - page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
.merge-request{'data-url' => merge_request_path(@merge_request)} .merge-request{'data-url' => merge_request_path(@merge_request)}
.merge-request-details .merge-request-details.issuable-details
= render "projects/merge_requests/show/mr_title" = render "projects/merge_requests/show/mr_title"
%hr %hr
= render "projects/merge_requests/show/mr_box" = render "projects/merge_requests/show/mr_box"
......
- page_title "Merge Requests" - page_title "Merge Requests"
.append-bottom-10 .append-bottom-10
.pull-right .pull-right
= render 'shared/issuable_search_form', path: namespace_project_merge_requests_path(@project.namespace, @project) = render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
- if can? current_user, :write_merge_request, @project - if can? current_user, :write_merge_request, @project
.pull-left.hidden-xs .pull-left.hidden-xs
= link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-new", title: "New Merge Request" do = link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-new", title: "New Merge Request" do
%i.fa.fa-plus %i.fa.fa-plus
New Merge Request New Merge Request
= render 'shared/issuable_filter', type: :merge_requests = render 'shared/issuable/filter', type: :merge_requests
.merge-requests-holder .merge-requests-holder
= render 'merge_requests' = render 'merge_requests'
- if params[:merge_request_context] $('.context').html("#{escape_javascript(render 'shared/issuable/context', issuable: @merge_request)}");
$('.context').html("#{escape_javascript(render partial: 'projects/merge_requests/show/context', locals: { issue: @issue })}"); $('.context').effect('highlight')
$('.context').effect('highlight'); merge_request = new MergeRequest();
new UsersSelect()
$('select.select2').select2({width: 'resolve', dropdownAutoWidth: true});
merge_request = new MergeRequest();
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update js-merge-request-update'} do |f| = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
%div.prepend-top-20 %div.prepend-top-20
.issuable-context-title .issuable-context-title
%label %label
Assignee: Assignee:
- if @merge_request.assignee - if issuable.assignee
%strong= link_to_member(@project, @merge_request.assignee, size: 24) %strong= link_to_member(@project, issuable.assignee, size: 24)
- else - else
none none
.issuable-context-selectbox .issuable-context-selectbox
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :admin_issue, @project)
= users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id, project: @target_project, null_user: true) = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true)
%div.prepend-top-20.clearfix %div.prepend-top-20.clearfix
.issuable-context-title .issuable-context-title
%label %label
Milestone: Milestone:
- if @merge_request.milestone - if issuable.milestone
%span.back-to-milestone %span.back-to-milestone
= link_to namespace_project_milestone_path(@project.namespace, @project, @merge_request.milestone) do = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do
%strong %strong
= icon('clock-o') = icon('clock-o')
= @merge_request.milestone.title = issuable.milestone.title
- else - else
none none
.issuable-context-selectbox .issuable-context-selectbox
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :admin_issue, @project)
= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: 'Select milestone' }, {class: 'select2 select2-compact js-select2 js-milestone'}) = f.select(:milestone_id, milestone_options(issuable), { include_blank: 'Select milestone' }, {class: 'select2 select2-compact js-select2 js-milestone'})
= hidden_field_tag :merge_request_context = hidden_field_tag :issuable_context
= f.submit class: 'btn' = f.submit class: 'btn hide'
- if current_user - if current_user
- subscribed = @merge_request.subscribed?(current_user) - subscribed = issuable.subscribed?(current_user)
%div.prepend-top-20.clearfix %div.prepend-top-20.clearfix
.issuable-context-title .issuable-context-title
%label %label
...@@ -46,4 +46,5 @@ ...@@ -46,4 +46,5 @@
You're receiving notifications because you're subscribed to this thread. You're receiving notifications because you're subscribed to this thread.
:coffeescript :coffeescript
new Subscription("#{toggle_subscription_namespace_project_merge_request_path(@merge_request.project.namespace, @project, @merge_request)}") new Subscription("#{toggle_subscription_path(issuable)}")
new IssuableContext()
...@@ -29,11 +29,10 @@ ...@@ -29,11 +29,10 @@
.issues-details-filters .issues-details-filters
= form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name]), method: :get, class: 'filter-form' do = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name]), method: :get, class: 'filter-form' do
- if controller.controller_name == 'issues' - if controller.controller_name == 'issues' && can?(current_user, :admin_issue, @project)
.check-all-holder .check-all-holder
= check_box_tag "check_all_issues", nil, false, = check_box_tag "check_all_issues", nil, false,
class: "check_all_issues left", class: "check_all_issues left"
disabled: !can?(current_user, :modify_issue, @project)
.issues-other-filters .issues-other-filters
.filter-item.inline .filter-item.inline
= users_select_tag(:assignee_id, selected: params[:assignee_id], = users_select_tag(:assignee_id, selected: params[:assignee_id],
...@@ -64,6 +63,8 @@ ...@@ -64,6 +63,8 @@
= button_tag "Update issues", class: "btn update_selected_issues btn-save" = button_tag "Update issues", class: "btn update_selected_issues btn-save"
:coffeescript :coffeescript
new UsersSelect()
$('form.filter-form').on 'submit', (event) -> $('form.filter-form').on 'submit', (event) ->
event.preventDefault() event.preventDefault()
Turbolinks.visit @.action + '&' + $(@).serialize() Turbolinks.visit @.action + '&' + $(@).serialize()
...@@ -37,47 +37,48 @@ ...@@ -37,47 +37,48 @@
.clearfix .clearfix
.error-alert .error-alert
%hr %hr
.form-group - if can?(current_user, :admin_issue, @project)
.issue-assignee .form-group
= f.label :assignee_id, class: 'control-label' do .issue-assignee
%i.fa.fa-user = f.label :assignee_id, class: 'control-label' do
Assign to %i.fa.fa-user
.col-sm-10 Assign to
= users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", .col-sm-10
placeholder: 'Select a user', class: 'custom-form-control', null_user: true, = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
selected: issuable.assignee_id, project: @target_project || @project) placeholder: 'Select a user', class: 'custom-form-control', null_user: true,
&nbsp; selected: issuable.assignee_id, project: @target_project || @project)
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link' &nbsp;
.form-group = link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
.issue-milestone .form-group
= f.label :milestone_id, class: 'control-label' do .issue-milestone
%i.fa.fa-clock-o = f.label :milestone_id, class: 'control-label' do
Milestone %i.fa.fa-clock-o
Milestone
.col-sm-10
- if milestone_options(issuable).present?
= f.select(:milestone_id, milestone_options(issuable),
{ include_blank: 'Select milestone' }, { class: 'select2' })
- else
.prepend-top-10
%span.light No open milestones available.
&nbsp;
- if can? current_user, :admin_milestone, issuable.project
= link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank
.form-group
= f.label :label_ids, class: 'control-label' do
%i.fa.fa-tag
Labels
.col-sm-10 .col-sm-10
- if milestone_options(issuable).present? - if issuable.project.labels.any?
= f.select(:milestone_id, milestone_options(issuable), = f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
{ include_blank: 'Select milestone' }, { class: 'select2' }) { selected: issuable.label_ids }, multiple: true, class: 'select2'
- else - else
.prepend-top-10 .prepend-top-10
%span.light No open milestones available. %span.light No labels yet.
&nbsp; &nbsp;
- if can? current_user, :admin_milestone, issuable.project - if can? current_user, :admin_label, issuable.project
= link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank = link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank
.form-group
= f.label :label_ids, class: 'control-label' do
%i.fa.fa-tag
Labels
.col-sm-10
- if issuable.project.labels.any?
= f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
{ selected: issuable.label_ids }, multiple: true, class: 'select2'
- else
.prepend-top-10
%span.light No labels yet.
&nbsp;
- if can? current_user, :admin_label, issuable.project
= link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank
- if issuable.is_a?(MergeRequest) - if issuable.is_a?(MergeRequest)
%hr %hr
......
...@@ -184,3 +184,15 @@ Feature: Project Issues ...@@ -184,3 +184,15 @@ Feature: Project Issues
Then I should see that I am subscribed Then I should see that I am subscribed
When I click button "Unsubscribe" When I click button "Unsubscribe"
Then I should see that I am unsubscribed Then I should see that I am unsubscribed
Scenario: I submit new unassigned issue as guest
Given I logout
Given public project "Community"
When I visit project "Community" page
And I click link "New Issue"
And I should not see assignee field
And I should not see milestone field
And I should not see labels field
And I submit new issue "500 error on profile"
Then I should see issue "500 error on profile"
...@@ -262,6 +262,24 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps ...@@ -262,6 +262,24 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end end
end end
step 'I should not see labels field' do
page.within '.issue-form' do
expect(page).not_to have_content("Labels")
end
end
step 'I should not see milestone field' do
page.within '.issue-form' do
expect(page).not_to have_content("Milestone")
end
end
step 'I should not see assignee field' do
page.within '.issue-form' do
expect(page).not_to have_content("Assign to")
end
end
def filter_issue(text) def filter_issue(text)
fill_in 'issue_search', with: text fill_in 'issue_search', with: text
end end
......
...@@ -157,7 +157,7 @@ module API ...@@ -157,7 +157,7 @@ module API
if issue.valid? if issue.valid?
# Find or create labels and attach to issue. Labels are valid because # Find or create labels and attach to issue. Labels are valid because
# we already checked its name, so there can't be an error here # we already checked its name, so there can't be an error here
unless params[:labels].nil? if params[:labels] && can?(current_user, :admin_issue, user_project)
issue.remove_labels issue.remove_labels
# Create and add labels to the new created issue # Create and add labels to the new created issue
issue.add_labels_by_names(params[:labels].split(',')) issue.add_labels_by_names(params[:labels].split(','))
......
...@@ -218,7 +218,7 @@ describe 'Issues', feature: true do ...@@ -218,7 +218,7 @@ describe 'Issues', feature: true do
it 'with dropdown menu' do it 'with dropdown menu' do
visit namespace_project_issue_path(project.namespace, project, issue) visit namespace_project_issue_path(project.namespace, project, issue)
find('.edit-issue.inline-update #issue_assignee_id'). find('.context #issue_assignee_id').
set project.team.members.first.id set project.team.members.first.id
click_button 'Update Issue' click_button 'Update Issue'
...@@ -257,7 +257,7 @@ describe 'Issues', feature: true do ...@@ -257,7 +257,7 @@ describe 'Issues', feature: true do
it 'with dropdown menu' do it 'with dropdown menu' do
visit namespace_project_issue_path(project.namespace, project, issue) visit namespace_project_issue_path(project.namespace, project, issue)
find('.edit-issue.inline-update'). find('.context').
select(milestone.title, from: 'issue_milestone_id') select(milestone.title, from: 'issue_milestone_id')
click_button 'Update Issue' click_button 'Update Issue'
......
require 'spec_helper' require 'spec_helper'
feature 'Task Lists' do feature 'Task Lists', feature: true do
include Warden::Test::Helpers include Warden::Test::Helpers
let(:project) { create(:project) } let(:project) { create(:project) }
...@@ -52,7 +52,7 @@ feature 'Task Lists' do ...@@ -52,7 +52,7 @@ feature 'Task Lists' do
expect(page).to have_selector(container) expect(page).to have_selector(container)
expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox") expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox")
expect(page).to have_selector("#{container} .js-task-list-field") expect(page).to have_selector("#{container} .js-task-list-field")
expect(page).to have_selector('form.js-issue-update') expect(page).to have_selector('form.js-issuable-update')
expect(page).to have_selector('a.btn-close') expect(page).to have_selector('a.btn-close')
end end
...@@ -128,7 +128,7 @@ feature 'Task Lists' do ...@@ -128,7 +128,7 @@ feature 'Task Lists' do
expect(page).to have_selector(container) expect(page).to have_selector(container)
expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox") expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox")
expect(page).to have_selector("#{container} .js-task-list-field") expect(page).to have_selector("#{container} .js-task-list-field")
expect(page).to have_selector('form.js-merge-request-update') expect(page).to have_selector('form.js-issuable-update')
expect(page).to have_selector('a.btn-close') expect(page).to have_selector('a.btn-close')
end end
......
...@@ -10,4 +10,4 @@ ...@@ -10,4 +10,4 @@
%textarea.js-task-list-field %textarea.js-task-list-field
\- [ ] Task List Item \- [ ] Task List Item
%form.js-issue-update{action: '/foo'} %form.js-issuable-update{action: '/foo'}
...@@ -10,4 +10,4 @@ ...@@ -10,4 +10,4 @@
%textarea.js-task-list-field %textarea.js-task-list-field
\- [ ] Task List Item \- [ ] Task List Item
%form.js-merge-request-update{action: '/foo'} %form.js-issuable-update{action: '/foo'}
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