diff options
author | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2015-03-27 13:43:46 -0700 |
---|---|---|
committer | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2015-03-27 13:43:46 -0700 |
commit | 4d9b4d358228e4fdd58866366e0184cd3f80388e (patch) | |
tree | c31dc49dafc161d98d47793b2ee88010356238cd | |
parent | 3d4c2d0b510f15ac3f0abe6ba4468dd019b06278 (diff) | |
parent | 9007af2c7e37e0147a835af978238b7a53496bf4 (diff) | |
download | gitlab-ce-4d9b4d358228e4fdd58866366e0184cd3f80388e.tar.gz |
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce
37 files changed, 304 insertions, 319 deletions
diff --git a/CHANGELOG b/CHANGELOG index 242d2c773c6..04d889456dd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -43,6 +43,10 @@ v 7.10.0 (unreleased) - Link note avatar to user. - Make Git-over-SSH errors more descriptive. - Fix EmailsOnPush. + - Refactor issue filtering + - AJAX selectbox for issue assignee and author filters + - Fix issue with missing options in issue filtering dropdown if selected one + - Prevent holding Control-Enter or Command-Enter from posting comment multiple times. v 7.9.0 - Send EmailsOnPush email when branch or tag is created or deleted. diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee index 27d04e7cac6..9e5d594c861 100644 --- a/app/assets/javascripts/api.js.coffee +++ b/app/assets/javascripts/api.js.coffee @@ -1,57 +1,7 @@ @Api = groups_path: "/api/:version/groups.json" group_path: "/api/:version/groups/:id.json" - users_path: "/api/:version/users.json" - user_path: "/api/:version/users/:id.json" - notes_path: "/api/:version/projects/:id/notes.json" namespaces_path: "/api/:version/namespaces.json" - project_users_path: "/api/:version/projects/:id/users.json" - - # Get 20 (depends on api) recent notes - # and sort the ascending from oldest to newest - notes: (project_id, callback) -> - url = Api.buildUrl(Api.notes_path) - url = url.replace(':id', project_id) - - $.ajax( - url: url, - data: - private_token: gon.api_token - gfm: true - recent: true - dataType: "json" - ).done (notes) -> - notes.sort (a, b) -> - return a.id - b.id - callback(notes) - - user: (user_id, callback) -> - url = Api.buildUrl(Api.user_path) - url = url.replace(':id', user_id) - - $.ajax( - url: url - data: - private_token: gon.api_token - dataType: "json" - ).done (user) -> - callback(user) - - # Return users list. Filtered by query - # Only active users retrieved - users: (query, callback) -> - url = Api.buildUrl(Api.users_path) - - $.ajax( - url: url - data: - private_token: gon.api_token - search: query - per_page: 20 - active: true - dataType: "json" - ).done (users) -> - callback(users) group: (group_id, callback) -> url = Api.buildUrl(Api.group_path) @@ -80,23 +30,6 @@ ).done (groups) -> callback(groups) - # Return project users list. Filtered by query - # Only active users retrieved - projectUsers: (project_id, query, callback) -> - url = Api.buildUrl(Api.project_users_path) - url = url.replace(':id', project_id) - - $.ajax( - url: url - data: - private_token: gon.api_token - search: query - per_page: 20 - active: true - dataType: "json" - ).done (users) -> - callback(users) - # Return namespaces list. Filtered by query namespaces: (query, callback) -> url = Api.buildUrl(Api.namespaces_path) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 3535d8c2cfc..821712f7512 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -127,7 +127,7 @@ class Dispatcher when 'show' new ProjectShow() when 'issues', 'merge_requests' - new ProjectUsersSelect() + new UsersSelect() when 'wikis' new Wikis() shortcut_handler = new ShortcutsNavigation() diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index c366c98cf54..b61c4dd6544 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -57,6 +57,7 @@ class @Notes @notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea' # Chrome doesn't fire keypress or keyup for Command+Enter, so we need keydown. $(document).on('keydown', @notes_forms, (e) -> + return if e.originalEvent.repeat if e.keyCode == 10 || ((e.metaKey || e.ctrlKey) && e.keyCode == 13) $(@).parents('form').submit() ) diff --git a/app/assets/javascripts/project_users_select.js.coffee b/app/assets/javascripts/project_users_select.js.coffee deleted file mode 100644 index 80ab1a61ab9..00000000000 --- a/app/assets/javascripts/project_users_select.js.coffee +++ /dev/null @@ -1,56 +0,0 @@ -class @ProjectUsersSelect - constructor: -> - $('.ajax-project-users-select').each (i, select) => - project_id = $(select).data('project-id') || $('body').data('project-id') - - $(select).select2 - placeholder: $(select).data('placeholder') || "Search for a user" - multiple: $(select).hasClass('multiselect') - minimumInputLength: 0 - query: (query) -> - Api.projectUsers project_id, query.term, (users) -> - data = { results: users } - - if query.term.length == 0 - nullUser = { - name: 'Unassigned', - avatar: null, - username: 'none', - id: -1 - } - - data.results.unshift(nullUser) - - query.callback(data) - - initSelection: (element, callback) -> - id = $(element).val() - if id != "" && id != "-1" - Api.user(id, callback) - - - formatResult: (args...) => - @formatResult(args...) - formatSelection: (args...) => - @formatSelection(args...) - dropdownCssClass: "ajax-project-users-dropdown" - dropdownAutoWidth: true - escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results - m - - formatResult: (user) -> - if user.avatar_url - avatar = user.avatar_url - else - avatar = gon.default_avatar_url - - avatarMarkup = "<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>" - - "<div class='user-result'> - #{avatarMarkup} - <div class='user-name'>#{user.name}</div> - <div class='user-username'>#{user.username}</div> - </div>" - - formatSelection: (user) -> - user.name diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 9eee7406511..f464067686e 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -1,20 +1,48 @@ class @UsersSelect constructor: -> + @usersPath = "/autocomplete/users.json" + @userPath = "/autocomplete/users/:id.json" + $('.ajax-users-select').each (i, select) => + @projectId = $(select).data('project-id') + @groupId = $(select).data('group-id') + showNullUser = $(select).data('null-user') + showAnyUser = $(select).data('any-user') + $(select).select2 placeholder: "Search for a user" multiple: $(select).hasClass('multiselect') minimumInputLength: 0 - query: (query) -> - Api.users query.term, (users) -> + query: (query) => + @users query.term, (users) => data = { results: users } + + if query.term.length == 0 + anyUser = { + name: 'Any', + avatar: null, + username: 'none', + id: null + } + + nullUser = { + name: 'Unassigned', + avatar: null, + username: 'none', + id: 0 + } + + if showNullUser + data.results.unshift(nullUser) + if showAnyUser + data.results.unshift(anyUser) + query.callback(data) - initSelection: (element, callback) -> + initSelection: (element, callback) => id = $(element).val() - if id isnt "" - Api.user(id, callback) - + if id != "" && id != "0" + @user(id, callback) formatResult: (args...) => @formatResult(args...) @@ -38,3 +66,34 @@ class @UsersSelect formatSelection: (user) -> user.name + + user: (user_id, callback) => + url = @buildUrl(@userPath) + url = url.replace(':id', user_id) + + $.ajax( + url: url + dataType: "json" + ).done (user) -> + callback(user) + + # Return users list. Filtered by query + # Only active users retrieved + users: (query, callback) => + url = @buildUrl(@usersPath) + + $.ajax( + url: url + data: + search: query + per_page: 20 + active: true + project_id: @projectId + group_id: @groupId + dataType: "json" + ).done (users) -> + callback(users) + + buildUrl: (url) -> + url = gon.relative_url_root + url if gon.relative_url_root? + return url diff --git a/app/assets/stylesheets/generic/filters.scss b/app/assets/stylesheets/generic/filters.scss new file mode 100644 index 00000000000..20c6a85de98 --- /dev/null +++ b/app/assets/stylesheets/generic/filters.scss @@ -0,0 +1,25 @@ +.filter-item { + margin-right: 15px; +} + +.issues-state-filters { + li.active a, + li.active a:hover { + background: #f5f5f5; + border-bottom: 1px solid #f5f5f5 !important; + } +} + +.issues-details-filters { + font-size: 13px; + background: #f5f5f5; + margin: -10px 0; + padding: 10px 15px; + margin-top: -15px; + border-left: 1px solid #DDD; + border-right: 1px solid #DDD; + + .btn { + font-size: 13px; + } +} diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/generic/selects.scss index 7557f411111..69613608c82 100644 --- a/app/assets/stylesheets/generic/selects.scss +++ b/app/assets/stylesheets/generic/selects.scss @@ -28,6 +28,7 @@ .select2-drop-active { border: 1px solid #BBB !important; margin-top: 4px; + font-size: 13px; &.select2-drop-above { margin-bottom: 8px; @@ -106,3 +107,7 @@ font-weight: bolder; } } + +.ajax-users-dropdown { + min-width: 225px !important; +} diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 6c1dd4f7e9f..b8ad26d69cd 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -41,12 +41,9 @@ } .check-all-holder { - height: 36px; + line-height: 36px; float: left; - margin-right: 12px; - padding: 6px 15px; - border: 1px solid #ccc; - @include border-radius(4px); + margin-right: 15px; } .issues_content { @@ -60,6 +57,7 @@ } @media (min-width: 800px) { + .issues-filters, .issues_bulk_update { select, .select2-container { width: 120px !important; @@ -69,14 +67,16 @@ } @media (min-width: 1200px) { + .issues-filters, .issues_bulk_update { select, .select2-container { - width: 160px !important; + width: 150px !important; display: inline-block; } } } +.issues-filters, .issues_bulk_update { .select2-container .select2-choice { color: #444 !important; diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb new file mode 100644 index 00000000000..11af9895261 --- /dev/null +++ b/app/controllers/autocomplete_controller.rb @@ -0,0 +1,30 @@ +class AutocompleteController < ApplicationController + def users + @users = + if params[:project_id].present? + project = Project.find(params[:project_id]) + + if can?(current_user, :read_project, project) + project.team.users + end + elsif params[:group_id] + group = Group.find(params[:group_id]) + + if can?(current_user, :read_group, group) + group.users + end + else + User.all + end + + @users = @users.search(params[:search]) if params[:search].present? + @users = @users.active + @users = @users.page(params[:page]).per(PER_PAGE) + render json: @users, only: [:name, :username, :id], methods: [:avatar_url] + end + + def user + @user = User.find(params[:id]) + render json: @user, only: [:name, :username, :id], methods: [:avatar_url] + end +end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 088a766ed3a..2c0702073d4 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -19,6 +19,8 @@ require_relative 'projects_finder' class IssuableFinder + NONE = '0' + attr_accessor :current_user, :params def execute(current_user, params) @@ -112,7 +114,7 @@ class IssuableFinder def by_milestone(items) if params[:milestone_id].present? - items = items.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id])) + items = items.where(milestone_id: (params[:milestone_id] == NONE ? nil : params[:milestone_id])) end items @@ -120,7 +122,7 @@ class IssuableFinder def by_assignee(items) if params[:assignee_id].present? - items = items.where(assignee_id: (params[:assignee_id] == '0' ? nil : params[:assignee_id])) + items = items.where(assignee_id: (params[:assignee_id] == NONE ? nil : params[:assignee_id])) end items @@ -128,7 +130,7 @@ class IssuableFinder def by_author(items) if params[:author_id].present? - items = items.where(author_id: (params[:author_id] == '0' ? nil : params[:author_id])) + items = items.where(author_id: (params[:author_id] == NONE ? nil : params[:author_id])) end items diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 38b5fc4a011..3f3509bb18a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -275,7 +275,9 @@ module ApplicationHelper 'https://' + promo_host end - def page_filter_path(options={}) + def page_filter_path(options = {}) + without = options.delete(:without) + exist_opts = { state: params[:state], scope: params[:scope], @@ -288,6 +290,12 @@ module ApplicationHelper options = exist_opts.merge(options) + if without.present? + without.each do |key| + options.delete(key) + end + end + path = request.path path << "?#{options.to_param}" path diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 49063491abf..32ef2e7ca84 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -47,4 +47,8 @@ module LabelsHelper "#FFF" end end + + def project_labels_options(project) + options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name]) + end end diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index 59fdc0d49cc..282bdf744d2 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -19,4 +19,15 @@ module MilestonesHelper content_tag :div, nil, options end end + + def projects_milestones_options + milestones = + if @project + @project.milestones + else + Milestone.where(project_id: @projects) + end.active + + options_from_collection_for_select(milestones, 'id', 'title', params[:milestone_id]) + end end diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb index 796d805f219..457cd3fa46b 100644 --- a/app/helpers/selects_helper.rb +++ b/app/helpers/selects_helper.rb @@ -4,18 +4,27 @@ module SelectsHelper css_class << "multiselect " if opts[:multiple] css_class << (opts[:class] || '') value = opts[:selected] || '' + placeholder = opts[:placeholder] || 'Search for a user' - hidden_field_tag(id, value, class: css_class) - end + null_user = opts[:null_user] || false + any_user = opts[:any_user] || false - def project_users_select_tag(id, opts = {}) - css_class = "ajax-project-users-select " - css_class << "multiselect " if opts[:multiple] - css_class << (opts[:class] || '') - value = opts[:selected] || '' - placeholder = opts[:placeholder] || 'Select user' - project_id = opts[:project_id] || @project.id - hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id) + html = { + class: css_class, + 'data-placeholder' => placeholder, + 'data-null-user' => null_user, + 'data-any-user' => any_user, + } + + unless opts[:scope] == :all + if @project + html['data-project-id'] = @project.id + elsif @group + html['data-group-id'] = @group.id + end + end + + hidden_field_tag(id, value, html) end def groups_select_tag(id, opts = {}) diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index 3371fe7d5ef..8f04a69287a 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -14,8 +14,8 @@ module Issues issue.update_nth_task(params[:task_num].to_i, false) end - params[:assignee_id] = "" if params[:assignee_id] == "-1" - params[:milestone_id] = "" if params[:milestone_id] == "-1" + params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE + params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE old_labels = issue.labels.to_a diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index 0ac6dfea6fd..23af2656c37 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -23,8 +23,8 @@ module MergeRequests merge_request.update_nth_task(params[:task_num].to_i, false) end - params[:assignee_id] = "" if params[:assignee_id] == "-1" - params[:milestone_id] = "" if params[:milestone_id] == "-1" + params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE + params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE old_labels = merge_request.labels.to_a diff --git a/app/views/projects/_issuable_form.html.haml b/app/views/projects/_issuable_form.html.haml index 7fd5fe8a6e1..e321a84974e 100644 --- a/app/views/projects/_issuable_form.html.haml +++ b/app/views/projects/_issuable_form.html.haml @@ -35,8 +35,8 @@ %i.fa.fa-user Assign to .col-sm-10 - = project_users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", - placeholder: 'Select a user', class: 'custom-form-control', + = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", + placeholder: 'Select a user', class: 'custom-form-control', null_user: true, selected: issuable.assignee_id) = link_to 'Assign to me', '#', class: 'btn assign-to-me-link' diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml index c3d6dc2e50b..52e38050419 100644 --- a/app/views/projects/issues/_issue_context.html.haml +++ b/app/views/projects/issues/_issue_context.html.haml @@ -8,7 +8,7 @@ - else none - if can?(current_user, :modify_issue, @issue) - = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id) + = users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id, null_user: true) %div.prepend-top-20.clearfix .issuable-context-title @@ -44,5 +44,3 @@ :coffeescript new Subscription("#{toggle_subscription_namespace_project_issue_path(@issue.project.namespace, @project, @issue)}") - - diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 54e3009cca2..210b77a6b15 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -19,7 +19,7 @@ .issues_bulk_update.hide = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control') - = project_users_select_tag('update[assignee_id]', placeholder: 'Assignee') + = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true) = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone") = hidden_field_tag 'update[issues_ids]', [] = hidden_field_tag :state_event, params[:state_event] diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml index 82c0e653759..1d38662bff8 100644 --- a/app/views/projects/issues/update.js.haml +++ b/app/views/projects/issues/update.js.haml @@ -13,5 +13,5 @@ $('select.select2').select2({width: 'resolve', dropdownAutoWidth: true}) $('.edit-issue.inline-update input[type="submit"]').hide(); -new ProjectUsersSelect(); +new UsersSelect() new Issue(); diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 1d8eef4e8ce..d986ce67c0c 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -39,7 +39,7 @@ %i.fa.fa-user Assign to .col-sm-10 - = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id) + = users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id) = link_to 'Assign to me', '#', class: 'btn assign-to-me-link' .form-group diff --git a/app/views/projects/merge_requests/show/_context.html.haml b/app/views/projects/merge_requests/show/_context.html.haml index 80e5c223d60..105562fb05e 100644 --- a/app/views/projects/merge_requests/show/_context.html.haml +++ b/app/views/projects/merge_requests/show/_context.html.haml @@ -9,7 +9,7 @@ none .issuable-context-selectbox - if can?(current_user, :modify_merge_request, @merge_request) - = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id) + = users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id, null_user: true) %div.prepend-top-20.clearfix .issuable-context-title diff --git a/app/views/projects/merge_requests/update.js.haml b/app/views/projects/merge_requests/update.js.haml index f5cc98c7fa4..b4df1d20737 100644 --- a/app/views/projects/merge_requests/update.js.haml +++ b/app/views/projects/merge_requests/update.js.haml @@ -2,7 +2,7 @@ $('.context').html("#{escape_javascript(render partial: 'projects/merge_requests/show/context', locals: { issue: @issue })}"); $('.context').effect('highlight'); - new ProjectUsersSelect(); + new UsersSelect() $('select.select2').select2({width: 'resolve', dropdownAutoWidth: true}); merge_request = new MergeRequest(); diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml index 0f824bdabf8..5daae2708e6 100644 --- a/app/views/projects/project_members/_new_project_member.html.haml +++ b/app/views/projects/project_members/_new_project_member.html.haml @@ -1,7 +1,7 @@ = form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'form-horizontal users-project-form' } do |f| .form-group = f.label :user_ids, "People", class: 'control-label' - .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large') + .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all) .form-group = f.label :access_level, "Project Access", class: 'control-label' diff --git a/app/views/shared/_issuable_filter.html.haml b/app/views/shared/_issuable_filter.html.haml index 5412b9ef0f4..77085ccb56b 100644 --- a/app/views/shared/_issuable_filter.html.haml +++ b/app/views/shared/_issuable_filter.html.haml @@ -14,106 +14,35 @@ %i.fa.fa-compass All - %div - - if controller.controller_name == 'issues' - .check-all-holder - = check_box_tag "check_all_issues", nil, false, - class: "check_all_issues left", - disabled: !can?(current_user, :modify_issue, @project) - .issues-other-filters - .dropdown.inline.assignee-filter - %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} - %i.fa.fa-user - %span.light assignee: - - if @assignee.present? - %strong= @assignee.name - - elsif params[:assignee_id] == "0" - Unassigned - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to page_filter_path(assignee_id: nil) do - Any - = link_to page_filter_path(assignee_id: 0) do - Unassigned - - @assignees.sort_by(&:name).each do |user| - %li - = link_to page_filter_path(assignee_id: user.id) do - = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' - = user.name + .issues-details-filters + = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_id, :label_name]), method: :get, class: 'filter-form' do + - if controller.controller_name == 'issues' + .check-all-holder + = check_box_tag "check_all_issues", nil, false, + class: "check_all_issues left", + disabled: !can?(current_user, :modify_issue, @project) + .issues-other-filters + .filter-item.inline + = users_select_tag(:assignee_id, selected: params[:assignee_id], + placeholder: 'Assignee', class: 'trigger-submit', any_user: true, null_user: true) - .dropdown.inline.prepend-left-10.author-filter - %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} - %i.fa.fa-user - %span.light author: - - if @author.present? - %strong= @author.name - - elsif params[:author_id] == "0" - Unassigned - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to page_filter_path(author_id: nil) do - Any - = link_to page_filter_path(author_id: 0) do - Unassigned - - @authors.sort_by(&:name).each do |user| - %li - = link_to page_filter_path(author_id: user.id) do - = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' - = user.name + .filter-item.inline + = users_select_tag(:author_id, selected: params[:author_id], + placeholder: 'Author', class: 'trigger-submit', any_user: true) - .dropdown.inline.prepend-left-10.milestone-filter - %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} - %i.fa.fa-clock-o - %span.light milestone: - - if @milestone.present? - %strong= @milestone.title - - elsif params[:milestone_id] == "0" - None (backlog) - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to page_filter_path(milestone_id: nil) do - Any - = link_to page_filter_path(milestone_id: 0) do - None (backlog) - - @milestones.each do |milestone| - %li - = link_to page_filter_path(milestone_id: milestone.id) do - %strong= milestone.title - %small.light= milestone.expires_at + .filter-item.inline.milestone-filter + = select_tag('milestone_id', projects_milestones_options, class: "select2 trigger-submit", prompt: 'Milestone') - - if @project - .dropdown.inline.prepend-left-10.labels-filter - %button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} - %i.fa.fa-tags - %span.light label: - - if params[:label_name].present? - %strong= params[:label_name] - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to page_filter_path(label_name: nil) do - Any - - if @project.labels.any? - - @project.labels.each do |label| - %li - = link_to page_filter_path(label_name: label.name) do - = render_colored_label(label) - - else - %li - = link_to generate_namespace_project_labels_path(@project.namespace, @project, redirect: request.original_url), method: :post do - %i.fa.fa-plus-circle - Create default labels + - if @project + .filter-item.inline.labels-filter + = select_tag('label_name', project_labels_options(@project), class: "select2 trigger-submit", prompt: 'Label') - .pull-right - = render 'shared/sort_dropdown' + .pull-right + = render 'shared/sort_dropdown' + +:coffeescript + new UsersSelect() + + $('form.filter-form').on 'submit', (event) -> + event.preventDefault() + Turbolinks.visit @.action + '&' + $(@).serialize() diff --git a/config/routes.rb b/config/routes.rb index c30cd768572..388858d2670 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,6 +8,11 @@ Gitlab::Application.routes.draw do authorizations: 'oauth/authorizations' end + # Autocomplete + get '/autocomplete/users' => 'autocomplete#users' + get '/autocomplete/users/:id' => 'autocomplete#user' + + # Search get 'search' => 'search#show' get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete diff --git a/doc/ssh/README.md b/doc/ssh/README.md index 66941521c2e..0acb15896d3 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -68,6 +68,6 @@ You can't add the same deploy key twice with the 'New Deploy Key' option. If you want to add the same key to another project, please enable it in the list that says 'Deploy keys from projects available to you'. All the deploy keys of all the projects you have access to are available. This project -access can happen through being a direct member of the projecti, or through +access can happen through being a direct member of the project, or through a group. See `def accessible_deploy_keys` in `app/models/user.rb` for more information. diff --git a/features/dashboard/issues.feature b/features/dashboard/issues.feature index 72627e43e05..99dad88a402 100644 --- a/features/dashboard/issues.feature +++ b/features/dashboard/issues.feature @@ -10,10 +10,12 @@ Feature: Dashboard Issues Scenario: I should see assigned issues Then I should see issues assigned to me + @javascript Scenario: I should see authored issues When I click "Authored by me" link Then I should see issues authored by me + @javascript Scenario: I should see all issues When I click "All" link Then I should see all issues diff --git a/features/dashboard/merge_requests.feature b/features/dashboard/merge_requests.feature index dcef1290e7e..4a2c997d707 100644 --- a/features/dashboard/merge_requests.feature +++ b/features/dashboard/merge_requests.feature @@ -10,10 +10,12 @@ Feature: Dashboard Merge Requests Scenario: I should see assigned merge_requests Then I should see merge requests assigned to me + @javascript Scenario: I should see authored merge_requests When I click "Authored by me" link Then I should see merge requests authored by me + @javascript Scenario: I should see all merge_requests When I click "All" link Then I should see all merge requests diff --git a/features/project/issues/filter_labels.feature b/features/project/issues/filter_labels.feature index 2c69a78a749..e316f519861 100644 --- a/features/project/issues/filter_labels.feature +++ b/features/project/issues/filter_labels.feature @@ -8,11 +8,7 @@ Feature: Project Issues Filter Labels And project "Shop" has issue "Feature1" with labels: "feature" Given I visit project "Shop" issues page - Scenario: I should see project issues - Then I should see "bug" in labels filter - And I should see "feature" in labels filter - And I should see "enhancement" in labels filter - + @javascript Scenario: I filter by one label Given I click link "bug" Then I should see "Bugfix1" in issues list diff --git a/features/steps/dashboard/issues.rb b/features/steps/dashboard/issues.rb index b77113e3974..60da36e86de 100644 --- a/features/steps/dashboard/issues.rb +++ b/features/steps/dashboard/issues.rb @@ -1,6 +1,7 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps include SharedAuthentication include SharedPaths + include Select2Helper step 'I should see issues assigned to me' do should_see(assigned_issue) @@ -35,21 +36,13 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps end step 'I click "Authored by me" link' do - within ".assignee-filter" do - click_link "Any" - end - within ".author-filter" do - click_link current_user.name - end + select2(current_user.id, from: "#author_id") + select2(nil, from: "#assignee_id") end step 'I click "All" link' do - within ".author-filter" do - click_link "Any" - end - within ".assignee-filter" do - click_link "Any" - end + select2(nil, from: "#author_id") + select2(nil, from: "#assignee_id") end def should_see(issue) diff --git a/features/steps/dashboard/merge_requests.rb b/features/steps/dashboard/merge_requests.rb index 6261c89924c..9d92082bb83 100644 --- a/features/steps/dashboard/merge_requests.rb +++ b/features/steps/dashboard/merge_requests.rb @@ -1,6 +1,7 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps include SharedAuthentication include SharedPaths + include Select2Helper step 'I should see merge requests assigned to me' do should_see(assigned_merge_request) @@ -39,21 +40,13 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps end step 'I click "Authored by me" link' do - within ".assignee-filter" do - click_link "Any" - end - within ".author-filter" do - click_link current_user.name - end + select2(current_user.id, from: "#author_id") + select2(nil, from: "#assignee_id") end step 'I click "All" link' do - within ".author-filter" do - click_link "Any" - end - within ".assignee-filter" do - click_link "Any" - end + select2(nil, from: "#author_id") + select2(nil, from: "#assignee_id") end def should_see(merge_request) diff --git a/features/steps/project/issues/filter_labels.rb b/features/steps/project/issues/filter_labels.rb index e62fa9c84c8..5740bd12837 100644 --- a/features/steps/project/issues/filter_labels.rb +++ b/features/steps/project/issues/filter_labels.rb @@ -2,24 +2,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps include SharedAuthentication include SharedProject include SharedPaths - - step 'I should see "bug" in labels filter' do - within ".labels-filter" do - page.should have_content "bug" - end - end - - step 'I should see "feature" in labels filter' do - within ".labels-filter" do - page.should have_content "feature" - end - end - - step 'I should see "enhancement" in labels filter' do - within ".labels-filter" do - page.should have_content "enhancement" - end - end + include Select2Helper step 'I should see "Bugfix1" in issues list' do within ".issues-list" do @@ -46,9 +29,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps end step 'I click link "bug"' do - within ".labels-filter" do - click_link "bug" - end + select2('bug', from: "#label_name") end step 'I click link "feature"' do diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb new file mode 100644 index 00000000000..a0909cec3bd --- /dev/null +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe AutocompleteController do + let!(:project) { create(:project) } + let!(:user) { create(:user) } + let!(:user2) { create(:user) } + + context 'project members' do + before do + sign_in(user) + project.team << [user, :master] + + get(:users, project_id: project.id) + end + + let(:body) { JSON.parse(response.body) } + + it { body.should be_kind_of(Array) } + it { body.size.should eq(1) } + it { body.first["username"].should == user.username } + end + + context 'group members' do + let(:group) { create(:group) } + + before do + sign_in(user) + group.add_owner(user) + + get(:users, group_id: group.id) + end + + let(:body) { JSON.parse(response.body) } + + it { body.should be_kind_of(Array) } + it { body.size.should eq(1) } + it { body.first["username"].should == user.username } + end + + context 'all users' do + before do + sign_in(user) + get(:users) + end + + let(:body) { JSON.parse(response.body) } + + it { body.should be_kind_of(Array) } + it { body.size.should eq(User.count) } + end +end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index a2db57ad908..e5f33d5a25a 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -95,7 +95,7 @@ describe 'Issues', feature: true do let(:issue) { @issue } it 'should allow filtering by issues with no specified milestone' do - visit namespace_project_issues_path(project.namespace, project, milestone_id: '0') + visit namespace_project_issues_path(project.namespace, project, milestone_id: IssuableFinder::NONE) expect(page).not_to have_content 'foobar' expect(page).to have_content 'barbaz' @@ -111,7 +111,7 @@ describe 'Issues', feature: true do end it 'should allow filtering by issues with no specified assignee' do - visit namespace_project_issues_path(project.namespace, project, assignee_id: '0') + visit namespace_project_issues_path(project.namespace, project, assignee_id: IssuableFinder::NONE) expect(page).to have_content 'foobar' expect(page).not_to have_content 'barbaz' diff --git a/spec/support/select2_helper.rb b/spec/support/select2_helper.rb index c7cf109a7bb..691f84f39d4 100644 --- a/spec/support/select2_helper.rb +++ b/spec/support/select2_helper.rb @@ -17,9 +17,9 @@ module Select2Helper selector = options[:from] if options[:multiple] - execute_script("$('#{selector}').select2('val', ['#{value}']);") + execute_script("$('#{selector}').select2('val', ['#{value}'], true);") else - execute_script("$('#{selector}').select2('val', '#{value}');") + execute_script("$('#{selector}').select2('val', '#{value}', true);") end end end |