summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2014-02-14 13:13:42 +0000
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2014-02-14 13:13:42 +0000
commitf745c6e5b5aa84a882a73ed00e8c3c6d28c70657 (patch)
tree140432dbe4dead3f44205cdbca5d60b71662dae5
parent361a8781c939e96c9718403db5330f389f13e64b (diff)
parentfd12e5c604f63516bc09c53f7fde52bc3a5cd494 (diff)
downloadgitlab-ce-f745c6e5b5aa84a882a73ed00e8c3c6d28c70657.tar.gz
Merge branch 'improve_large_groups' into 'master'
Improve for large groupsImprove for large groups This merge request is a set of patched to improve application for large groups with more then 100 people. Pages to be improved: :white_check_mark: Group#members page :white_check_mark: Project#issues page :white_check_mark: Project#merge_requests page :white_check_mark: Project#new_issue page :white_check_mark: Project#new_mr page :white_check_mark: Project#members page
-rw-r--r--app/assets/javascripts/api.js.coffee18
-rw-r--r--app/assets/javascripts/project_users_select.js.coffee44
-rw-r--r--app/assets/javascripts/users_select.js.coffee4
-rw-r--r--app/assets/stylesheets/application.scss1
-rw-r--r--app/assets/stylesheets/generic/common.scss1
-rw-r--r--app/assets/stylesheets/generic/forms.scss24
-rw-r--r--app/assets/stylesheets/generic/selects.scss13
-rw-r--r--app/assets/stylesheets/sections/groups.scss9
-rw-r--r--app/assets/stylesheets/sections/issues.scss29
-rw-r--r--app/controllers/groups_controller.rb9
-rw-r--r--app/controllers/projects/issues_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/helpers/application_helper.rb9
-rw-r--r--app/helpers/issues_helper.rb4
-rw-r--r--app/helpers/selects_helper.rb20
-rw-r--r--app/views/groups/_new_group_member.html.haml7
-rw-r--r--app/views/groups/members.html.haml28
-rw-r--r--app/views/projects/issues/_form.html.haml2
-rw-r--r--app/views/projects/issues/_head.html.haml4
-rw-r--r--app/views/projects/issues/_issues.html.haml157
-rw-r--r--app/views/projects/merge_requests/_form.html.haml2
-rw-r--r--app/views/projects/merge_requests/index.html.haml98
-rw-r--r--app/views/projects/team_members/_group_members.html.haml8
-rw-r--r--app/views/shared/_sort_dropdown.html.haml2
-rw-r--r--features/steps/group/group.rb1
-rw-r--r--lib/api/entities.rb6
-rw-r--r--lib/api/projects.rb14
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')
&nbsp;
= 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 &nbsp;
- = 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')
&nbsp;
= 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