diff options
27 files changed, 345 insertions, 172 deletions
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee index 5f4a38ebbd0..fafa5cdfaa4 100644 --- a/app/assets/javascripts/api.js.coffee +++ b/app/assets/javascripts/api.js.coffee @@ -3,6 +3,7 @@ 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 @@ -50,6 +51,23 @@ ).done (users) -> callback(users) + # 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/project_users_select.js.coffee b/app/assets/javascripts/project_users_select.js.coffee new file mode 100644 index 00000000000..59a53cb52bc --- /dev/null +++ b/app/assets/javascripts/project_users_select.js.coffee @@ -0,0 +1,44 @@ +$ -> + projectUserFormatResult = (user) -> + if user.avatar_url + avatar = user.avatar_url + else if gon.gravatar_enabled + avatar = gon.gravatar_url + avatar = avatar.replace('%{hash}', md5(user.email)) + avatar = avatar.replace('%{size}', '24') + else + avatar = gon.relative_url_root + "/assets/no_avatar.png" + + "<div class='user-result'> + <div class='user-image'><img class='avatar s24' src='#{avatar}'></div> + <div class='user-name'>#{user.name}</div> + <div class='user-username'>#{user.username}</div> + </div>" + + projectUserFormatSelection = (user) -> + user.name + + $('.ajax-project-users-select').each (i, select) -> + 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 } + query.callback(data) + + initSelection: (element, callback) -> + id = $(element).val() + if id isnt "" + Api.user(id, callback) + + + formatResult: projectUserFormatResult + formatSelection: projectUserFormatSelection + dropdownCssClass: "ajax-project-users-dropdown" + dropdownAutoWidth: true + escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results + m diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index c1fa16ca89c..ce9a505b1e3 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -1,7 +1,7 @@ $ -> userFormatResult = (user) -> - if user.avatar - avatar = user.avatar.url + if user.avatar_url + avatar = user.avatar_url else if gon.gravatar_enabled avatar = gon.gravatar_url avatar = avatar.replace('%{hash}', md5(user.email)) diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index cc5fdf61405..d5522f00f50 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -65,6 +65,7 @@ @import "sections/wall.scss"; @import "sections/dashboard.scss"; @import "sections/stat_graph.scss"; +@import "sections/groups.scss"; /** * Code ighlight diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 4824194ec42..91618688081 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -112,6 +112,7 @@ pre.well-pre { .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { background: #29b; + color: #FFF } .breadcrumb > li + li:before { diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss index 931b75a3234..19e3e7a9536 100644 --- a/app/assets/stylesheets/generic/forms.scss +++ b/app/assets/stylesheets/generic/forms.scss @@ -51,3 +51,27 @@ label { .input-mn-300 { min-width: 300px; } + +.custom-form-control { + width: 150px; +} + +@media (min-width: $screen-sm-min) { + .custom-form-control { + width: 150px; + } +} + +/* Medium devices (desktops, 992px and up) */ +@media (min-width: $screen-md-min) { + .custom-form-control { + width: 170px; + } +} + +/* Large devices (large desktops, 1200px and up) */ +@media (min-width: $screen-lg-min) { + .custom-form-control { + width: 200px; + } +} diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/generic/selects.scss index c506bff8a74..7ee1fec4e03 100644 --- a/app/assets/stylesheets/generic/selects.scss +++ b/app/assets/stylesheets/generic/selects.scss @@ -1,5 +1,4 @@ /** Select2 selectbox style override **/ - .select2-container, .select2-container.select2-drop-above { .select2-choice { background: #FFF; @@ -12,9 +11,13 @@ } .select2-drop-active { - border: 1px solid #BBB; + border: 1px solid #BBB !important; margin-top: 4px; + &.select2-drop-above { + margin-bottom: 8px; + } + .select2-search input { background: #fafafa; border-color: #DDD; @@ -78,3 +81,9 @@ select { .project-refs-form .select2-container { margin-right: 10px; } + +.ajax-users-dropdown, .ajax-project-users-dropdown { + .select2-search { + padding-top: 4px; + } +} diff --git a/app/assets/stylesheets/sections/groups.scss b/app/assets/stylesheets/sections/groups.scss new file mode 100644 index 00000000000..60ec79acadb --- /dev/null +++ b/app/assets/stylesheets/sections/groups.scss @@ -0,0 +1,9 @@ +.new-group-member-holder { + margin-top: 50px; + background: #f9f9f9; + padding-top: 20px; +} + +.member-search-form { + float: left; +} diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index 5afb13a86ba..a7fa900fafa 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -14,8 +14,8 @@ .issue-check { float: left; - padding: 8px 0; padding-right: 8px; + margin-bottom: 10px; min-width: 15px; } @@ -38,13 +38,21 @@ } } -input.check_all_issues { +.check-all-holder { + height: 32px; float: left; - padding: 0; - margin: 0; - margin-right: 10px; - position: relative; - top: 13px; + margin-right: 12px; + padding: 6px 10px; + border: 1px solid #ccc; + @include border-radius(4px); + + + input.check_all_issues { + padding: 0; + margin: 0; + position: relative; + top: 3px; + } } .issues_content { @@ -91,6 +99,13 @@ input.check_all_issues { .update_selected_issues { margin-left: 4px; } + + .select2-container .select2-choice { + height: 32px; + line-height: 28px; + color: #444 !important; + font-weight: 500; + } } } diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 7b418ec98f5..b927dd2f20a 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -63,7 +63,14 @@ class GroupsController < ApplicationController def members @project = group.projects.find(params[:project_id]) if params[:project_id] - @members = group.users_groups.order('group_access DESC') + @members = group.users_groups + + if params[:search].present? + users = group.users.search(params[:search]) + @members = @members.where(user_id: users) + end + + @members = @members.order('group_access DESC').page(params[:page]).per(50) @users_group = UsersGroup.new end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index ba5c52d510f..dd11948edd1 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -28,7 +28,7 @@ class Projects::IssuesController < Projects::ApplicationController @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? sort_param = params[:sort] || 'newest' @sort = sort_param.humanize unless sort_param.empty? - + @assignees = User.where(id: @project.issues.pluck(:assignee_id)) respond_to do |format| format.html diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index de31ee12b6a..d36b5b27e86 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -28,6 +28,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController assignee_id, milestone_id = params[:assignee_id], params[:milestone_id] @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero? @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? + @assignees = User.where(id: @project.merge_requests.pluck(:assignee_id)) end def show diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1550e8b7e05..4e7d01acd2a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -162,15 +162,6 @@ module ApplicationHelper alias_method :url_to_image, :image_url - def users_select_tag(id, opts = {}) - css_class = "ajax-users-select " - css_class << "multiselect " if opts[:multiple] - css_class << (opts[:class] || '') - value = opts[:selected] || '' - - hidden_field_tag(id, value, class: css_class) - end - def body_data_page path = controller.controller_path.split('/') namespace = path.first if path.second diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index cdba6ce84dc..16981edd980 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -70,11 +70,11 @@ module IssuesHelper end def bulk_update_milestone_options - options_for_select(["None (backlog)", nil]) + options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id]) + options_for_select(["None (backlog)"]) + options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id]) end def bulk_update_assignee_options - options_for_select(["None (unassigned)", nil]) + options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id]) + options_for_select(["None (unassigned)"]) + options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id]) end def assignee_options object diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb new file mode 100644 index 00000000000..a1fe4488ae9 --- /dev/null +++ b/app/helpers/selects_helper.rb @@ -0,0 +1,20 @@ +module SelectsHelper + def users_select_tag(id, opts = {}) + css_class = "ajax-users-select " + css_class << "multiselect " if opts[:multiple] + css_class << (opts[:class] || '') + value = opts[:selected] || '' + + hidden_field_tag(id, value, class: css_class) + end + + 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' + + hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder) + end +end diff --git a/app/views/groups/_new_group_member.html.haml b/app/views/groups/_new_group_member.html.haml index 5801139a9f2..3ab9276c541 100644 --- a/app/views/groups/_new_group_member.html.haml +++ b/app/views/groups/_new_group_member.html.haml @@ -1,15 +1,8 @@ = form_for @users_group, url: group_users_groups_path(@group), html: { class: 'form-horizontal users-group-form' } do |f| - %h4.append-bottom-20 - New member(s) for - %strong #{@group.name} - group - - %p 1. Choose users you want in the group .form-group = f.label :user_ids, "People", class: 'control-label' .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large') - %p 2. Set access level for them .form-group = f.label :group_access, "Group Access", class: 'control-label' .col-sm-10= select_tag :group_access, options_for_select(UsersGroup.group_access_roles, @users_group.group_access), class: "project-access-select select2" diff --git a/app/views/groups/members.html.haml b/app/views/groups/members.html.haml index 3095a2c7b74..38069feb37b 100644 --- a/app/views/groups/members.html.haml +++ b/app/views/groups/members.html.haml @@ -6,14 +6,34 @@ %strong= link_to "here", help_permissions_path, class: "vlink" %hr -.ui-box + +.clearfix + = form_tag members_group_path(@group), method: :get, class: 'form-inline member-search-form' do + .form-group + = search_field_tag :search, params[:search], { placeholder: 'Find member by name', class: 'form-control search-text-input input-mn-300' } + = submit_tag 'Search', class: 'btn' + + - if current_user.can? :manage_group, @group + .pull-right + = link_to '#', class: 'btn btn-new js-toggle-visibility-link' do + Add members + %i.icon-chevron-down + + .js-toggle-visibility-container.hide.new-group-member-holder + = render "new_group_member" + +.ui-box.prepend-top-20 .title %strong #{@group.name} group members %small - (#{@members.count}) + (#{@members.total_count}) %ul.well-list - @members.each do |member| = render 'users_groups/users_group', member: member, show_controls: true -- if current_user.can? :manage_group, @group - = render "new_group_member" += paginate @members, theme: 'gitlab' + +:coffeescript + $('form.member-search-form').on 'submit', (event) -> + event.preventDefault() + Turbolinks.visit @.action + '?' + $(@).serialize() diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index c95e8178594..2bf66eef6cb 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -24,7 +24,7 @@ %i.icon-user Assign to .col-sm-10 - = f.select(:assignee_id, assignee_options(@issue), { include_blank: "Select a user" }, {class: 'select2'}) + = project_users_select_tag('issue[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control') = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link' .form-group diff --git a/app/views/projects/issues/_head.html.haml b/app/views/projects/issues/_head.html.haml index 61213e752f8..2f0aa33fe14 100644 --- a/app/views/projects/issues/_head.html.haml +++ b/app/views/projects/issues/_head.html.haml @@ -17,10 +17,10 @@ %li.pull-right .pull-right - = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'inline issue-search-form' do + = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do .append-right-10.hidden-xs.hidden-sm = search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' } - if can? current_user, :write_issue, @project - = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do + = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do %i.icon-plus New Issue diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml index 87d30a4a163..020bfa36973 100644 --- a/app/views/projects/issues/_issues.html.haml +++ b/app/views/projects/issues/_issues.html.haml @@ -1,87 +1,86 @@ -.ui-box - .title +.append-bottom-10 + .check-all-holder = check_box_tag "check_all_issues", nil, false, class: "check_all_issues left" - .clearfix - .issues_bulk_update.hide - = form_tag bulk_update_project_issues_path(@project), method: :post do - %span Update selected issues with - = select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status") - = select_tag('update[assignee_id]', bulk_update_assignee_options, prompt: "Assignee") - = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone") - = hidden_field_tag 'update[issues_ids]', [] - = hidden_field_tag :status, params[:status] - = button_tag "Save", class: "btn update_selected_issues btn-small btn-save" - .issues-filters - %span Filter by - .dropdown.inline.prepend-left-10 - %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} - %i.icon-tags - %span.light labels: - - if params[:label_name].present? - %strong= params[:label_name] - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to project_filter_path(label_name: nil) do - Any - - issue_label_names.each do |label_name| - %li - = link_to project_filter_path(label_name: label_name) do - %span{class: "label #{label_css_class(label_name)}"} - %i.icon-tag - = label_name - .dropdown.inline.prepend-left-10 - %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} - %i.icon-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 project_filter_path(assignee_id: nil) do - Any - = link_to project_filter_path(assignee_id: 0) do - Unassigned - - @project.team.members.sort_by(&:name).each do |user| - %li - = link_to project_filter_path(assignee_id: user.id) do - = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' - = user.name + .issues-filters + .dropdown.inline + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %i.icon-tags + %span.light labels: + - if params[:label_name].present? + %strong= params[:label_name] + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to project_filter_path(label_name: nil) do + Any + - issue_label_names.each do |label_name| + %li + = link_to project_filter_path(label_name: label_name) do + %span{class: "label #{label_css_class(label_name)}"} + %i.icon-tag + = label_name + .dropdown.inline.prepend-left-10 + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %i.icon-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 project_filter_path(assignee_id: nil) do + Any + = link_to project_filter_path(assignee_id: 0) do + Unassigned + - @assignees.sort_by(&:name).each do |user| + %li + = link_to project_filter_path(assignee_id: user.id) do + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' + = user.name - .dropdown.inline.prepend-left-10 - %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} - %i.icon-time - %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 project_filter_path(milestone_id: nil) do - Any - = link_to project_filter_path(milestone_id: 0) do - None (backlog) - - project_active_milestones.each do |milestone| - %li - = link_to project_filter_path(milestone_id: milestone.id) do - %strong= milestone.title - %small.light= milestone.expires_at + .dropdown.inline.prepend-left-10 + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %i.icon-time + %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 project_filter_path(milestone_id: nil) do + Any + = link_to project_filter_path(milestone_id: 0) do + None (backlog) + - project_active_milestones.each do |milestone| + %li + = link_to project_filter_path(milestone_id: milestone.id) do + %strong= milestone.title + %small.light= milestone.expires_at - .pull-right - = render 'shared/sort_dropdown' + .pull-right + = render 'shared/sort_dropdown' + .clearfix + .issues_bulk_update.hide + = form_tag bulk_update_project_issues_path(@project), method: :post do + = select_tag('update[status]', options_for_select(['Open', 'Closed']), prompt: "Status") + = project_users_select_tag('update[assignee_id]', placeholder: 'Assignee') + = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone") + = hidden_field_tag 'update[issues_ids]', [] + = hidden_field_tag :status, params[:status] + = button_tag "Update issues", class: "btn update_selected_issues btn-save" +.ui-box %ul.well-list.issues-list = render @issues - if @issues.blank? diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index b4ba127da25..b9f80c35665 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -47,7 +47,7 @@ %i.icon-user Assign to .col-sm-10 - = f.select(:assignee_id, assignee_options(@merge_request), { include_blank: "Select a user" }, {class: 'select2'}) + = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control') = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link' .form-group diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index a525a49015f..d45bec3a239 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -10,59 +10,57 @@ .col-md-3 = render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project) .col-md-9 - .ui-box - .title - .mr-filters - %span Filter by - .dropdown.inline.prepend-left-10 - %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} - %i.icon-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 project_filter_path(assignee_id: nil) do - Any - = link_to project_filter_path(assignee_id: 0) do - Unassigned - - @project.team.members.sort_by(&:name).each do |user| - %li - = link_to project_filter_path(assignee_id: user.id) do - = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' - = user.name + .mr-filters.append-bottom-10 + .dropdown.inline + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %i.icon-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 project_filter_path(assignee_id: nil) do + Any + = link_to project_filter_path(assignee_id: 0) do + Unassigned + - @assignees.sort_by(&:name).each do |user| + %li + = link_to project_filter_path(assignee_id: user.id) do + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' + = user.name - .dropdown.inline.prepend-left-10 - %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} - %i.icon-time - %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 project_filter_path(milestone_id: nil) do - Any - = link_to project_filter_path(milestone_id: 0) do - None (backlog) - - project_active_milestones.each do |milestone| - %li - = link_to project_filter_path(milestone_id: milestone.id) do - %strong= milestone.title - %small.light= milestone.expires_at + .dropdown.inline.prepend-left-10 + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %i.icon-time + %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 project_filter_path(milestone_id: nil) do + Any + = link_to project_filter_path(milestone_id: 0) do + None (backlog) + - project_active_milestones.each do |milestone| + %li + = link_to project_filter_path(milestone_id: milestone.id) do + %strong= milestone.title + %small.light= milestone.expires_at - .pull-right - = render 'shared/sort_dropdown' + .pull-right + = render 'shared/sort_dropdown' + .ui-box %ul.well-list.mr-list = render @merge_requests - if @merge_requests.blank? diff --git a/app/views/projects/team_members/_group_members.html.haml b/app/views/projects/team_members/_group_members.html.haml index 68f08006854..eceec6627b9 100644 --- a/app/views/projects/team_members/_group_members.html.haml +++ b/app/views/projects/team_members/_group_members.html.haml @@ -1,10 +1,14 @@ +- group_users_count = @group.users_groups.count .ui-box .title %strong #{@group.name} - group members (#{@group.users_groups.count}) + group members (#{group_users_count}) .pull-right = link_to members_group_path(@group), class: 'btn btn-small' do %i.icon-edit %ul.well-list - - @group.users_groups.order('group_access DESC').each do |member| + - @group.users_groups.order('group_access DESC').limit(20).each do |member| = render 'users_groups/users_group', member: member, show_controls: false + - if group_users_count > 20 + %li + and #{group_users_count - 20} more. For full list visit #{link_to 'group members page', members_group_path(@group)} diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml index 2e875669967..7b37b39780e 100644 --- a/app/views/shared/_sort_dropdown.html.haml +++ b/app/views/shared/_sort_dropdown.html.haml @@ -1,5 +1,5 @@ .dropdown.inline.prepend-left-10 - %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %span.light sort: - if @sort.present? = @sort diff --git a/features/steps/group/group.rb b/features/steps/group/group.rb index bd59b7a12f6..81472d1ca35 100644 --- a/features/steps/group/group.rb +++ b/features/steps/group/group.rb @@ -29,6 +29,7 @@ class Groups < Spinach::FeatureSteps And 'I select user "Mary Jane" from list with role "Reporter"' do user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane") + click_link 'Add members' within ".users-group-form" do select2(user.id, from: "#user_ids", multiple: true) select "Reporter", from: "group_access" diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 8f54d0d4d84..8557fa074d4 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -6,6 +6,12 @@ module API expose :is_admin?, as: :is_admin expose :can_create_group?, as: :can_create_group expose :can_create_project?, as: :can_create_project + + expose :avatar_url do |user, options| + if user.avatar.present? + user.avatar.url + end + end end class UserSafe < Grape::Entity diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 888aa7e77d2..bcca69ff49a 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -11,7 +11,7 @@ module API end not_found! end - + def map_public_to_visibility_level(attrs) publik = attrs.delete(:public) publik = [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(publik) @@ -308,6 +308,18 @@ module API projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%") present paginate(projects), with: Entities::Project end + + + # Get a users list + # + # Example Request: + # GET /users + get ':id/users' do + @users = User.where(id: user_project.team.users.map(&:id)) + @users = @users.search(params[:search]) if params[:search].present? + @users = paginate @users + present @users, with: Entities::User + end end end end |