Revert "Revert "Merge branch 'issue_3946' into 'master'
This reverts commit bf2a86b73c
.
This commit is contained in:
parent
cfb5a76bb2
commit
0de617772d
21 changed files with 396 additions and 293 deletions
|
@ -22,6 +22,7 @@ v 8.10.0 (unreleased)
|
|||
- Fix pagination when sorting by columns with lots of ties (like priority)
|
||||
- Updated project header design
|
||||
- Exclude email check from the standard health check
|
||||
- Updated layout for Projects, Groups, Users on Admin area !4424
|
||||
- Fix changing issue state columns in milestone view
|
||||
- Add notification settings dropdown for groups
|
||||
- Wildcards for protected branches. !4665
|
||||
|
|
|
@ -127,7 +127,7 @@ class Dispatcher
|
|||
when 'groups'
|
||||
new UsersSelect()
|
||||
when 'projects'
|
||||
new NamespaceSelect()
|
||||
new NamespaceSelects()
|
||||
when 'dashboard', 'root'
|
||||
shortcut_handler = new ShortcutsDashboardNavigation()
|
||||
when 'profiles'
|
||||
|
|
|
@ -1,25 +1,56 @@
|
|||
class @NamespaceSelect
|
||||
constructor: ->
|
||||
namespaceFormatResult = (namespace) ->
|
||||
markup = "<div class='namespace-result'>"
|
||||
markup += "<span class='namespace-kind'>" + namespace.kind + "</span>"
|
||||
markup += "<span class='namespace-path'>" + namespace.path + "</span>"
|
||||
markup += "</div>"
|
||||
markup
|
||||
constructor: (opts) ->
|
||||
{
|
||||
@dropdown
|
||||
} = opts
|
||||
|
||||
formatSelection = (namespace) ->
|
||||
namespace.kind + ": " + namespace.path
|
||||
showAny = true
|
||||
fieldName = 'namespace_id'
|
||||
|
||||
$('.ajax-namespace-select').each (i, select) ->
|
||||
$(select).select2
|
||||
placeholder: "Search for namespace"
|
||||
multiple: $(select).hasClass('multiselect')
|
||||
minimumInputLength: 0
|
||||
query: (query) ->
|
||||
Api.namespaces query.term, (namespaces) ->
|
||||
data = { results: namespaces }
|
||||
query.callback(data)
|
||||
if @dropdown.attr 'data-field-name'
|
||||
fieldName = @dropdown.data 'fieldName'
|
||||
|
||||
dropdownCssClass: "ajax-namespace-dropdown"
|
||||
formatResult: namespaceFormatResult
|
||||
formatSelection: formatSelection
|
||||
if @dropdown.attr 'data-show-any'
|
||||
showAny = @dropdown.data 'showAny'
|
||||
|
||||
@dropdown.glDropdown(
|
||||
filterable: true
|
||||
selectable: true
|
||||
filterRemote: true
|
||||
search:
|
||||
fields: ['path']
|
||||
fieldName: fieldName
|
||||
toggleLabel: (selected) ->
|
||||
return if not selected.id? then selected.text else "#{selected.kind}: #{selected.path}"
|
||||
data: (term, dataCallback) ->
|
||||
Api.namespaces term, (namespaces) ->
|
||||
if showAny
|
||||
anyNamespace =
|
||||
text: 'Any namespace'
|
||||
id: null
|
||||
|
||||
namespaces.unshift(anyNamespace)
|
||||
namespaces.splice 1, 0, 'divider'
|
||||
|
||||
dataCallback(namespaces)
|
||||
text: (namespace) ->
|
||||
return if not namespace.id? then namespace.text else "#{namespace.kind}: #{namespace.path}"
|
||||
renderRow: @renderRow
|
||||
clicked: @onSelectItem
|
||||
)
|
||||
|
||||
onSelectItem: (item, el, e) =>
|
||||
e.preventDefault()
|
||||
|
||||
class @NamespaceSelects
|
||||
constructor: (opts = {}) ->
|
||||
{
|
||||
@$dropdowns = $('.js-namespace-select')
|
||||
} = opts
|
||||
|
||||
@$dropdowns.each (i, dropdown) ->
|
||||
$dropdown = $(dropdown)
|
||||
|
||||
new NamespaceSelect(
|
||||
dropdown: $dropdown
|
||||
)
|
||||
|
|
|
@ -68,6 +68,10 @@
|
|||
color: $dropdown-toggle-hover-icon-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.large {
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu,
|
||||
|
|
|
@ -134,6 +134,11 @@
|
|||
margin-bottom: 0;
|
||||
border-bottom: none;
|
||||
|
||||
&.wide {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
li a {
|
||||
padding: 16px 10px 11px;
|
||||
}
|
||||
|
@ -164,6 +169,7 @@
|
|||
> .btn {
|
||||
margin-right: $gl-padding-top;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
|
|
|
@ -71,3 +71,36 @@
|
|||
@extend .broadcast-message;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
// Users List
|
||||
|
||||
.users-list {
|
||||
.user-row {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.controls {
|
||||
> .btn, > .dropdown {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
.btn-block {
|
||||
margin-bottom: 0;
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,39 @@
|
|||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
&.group-admin {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
|
||||
.group-avatar, .group-details, .group-controls {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.group-details {
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.group-controls {
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.ldap-group-links {
|
||||
.form-actions {
|
||||
margin-bottom: $gl-padding;
|
||||
}
|
||||
}
|
||||
|
||||
.groups-cover-block {
|
||||
|
|
|
@ -475,10 +475,6 @@ pre.light-well {
|
|||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
> span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@
|
|||
margin-top: 5px;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
width: 160px;
|
||||
width: 180px;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,12 @@ class Admin::ProjectsController < Admin::ApplicationController
|
|||
def index
|
||||
@projects = Project.all
|
||||
@projects = @projects.in_namespace(params[:namespace_id]) if params[:namespace_id].present?
|
||||
@projects = @projects.where("projects.visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present?
|
||||
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
|
||||
@projects = @projects.with_push if params[:with_push].present?
|
||||
@projects = @projects.abandoned if params[:abandoned].present?
|
||||
@projects = @projects.where(last_repository_check_failed: true) if params[:last_repository_check_failed].present?
|
||||
@projects = @projects.non_archived unless params[:with_archived].present?
|
||||
@projects = @projects.non_archived unless params[:archived].present?
|
||||
@projects = @projects.personal(current_user) if params[:personal].present?
|
||||
@projects = @projects.search(params[:name]) if params[:name].present?
|
||||
@projects = @projects.sort(@sort = params[:sort])
|
||||
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page])
|
||||
|
|
|
@ -39,7 +39,7 @@ module DropdownsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def dropdown_toggle(toggle_text, data_attr, options)
|
||||
def dropdown_toggle(toggle_text, data_attr, options = {})
|
||||
content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.has_key?(:toggle_class)}", id: (options[:id] if options.has_key?(:id)), type: "button", data: data_attr) do
|
||||
output = content_tag(:span, toggle_text, class: "dropdown-toggle-text")
|
||||
output << icon('chevron-down')
|
||||
|
|
|
@ -1,28 +1,20 @@
|
|||
- css_class = '' unless local_assigns[:css_class]
|
||||
- css_class += ' no-description' if group.description.blank?
|
||||
|
||||
%li.group-row{ class: css_class }
|
||||
.controls.hidden-xs
|
||||
= link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: 'btn btn-grouped btn-sm'
|
||||
= link_to 'Destroy', [:admin, group], data: {confirm: "REMOVE #{group.name}? Are you sure?"}, method: :delete, class: 'btn btn-grouped btn-sm btn-remove'
|
||||
%li.group-row.group-admin{ class: css_class }
|
||||
.group-avatar
|
||||
= image_tag group_icon(group), class: 'avatar hidden-xs'
|
||||
.group-details
|
||||
.title
|
||||
= link_to [:admin, group], class: 'group-name' do
|
||||
= group.name
|
||||
.group-stats
|
||||
%span>= pluralize(number_with_delimiter(group.projects.count), 'project')
|
||||
,
|
||||
%span= pluralize(number_with_delimiter(group.users.count), 'member')
|
||||
|
||||
.stats
|
||||
%span
|
||||
= icon('bookmark')
|
||||
= number_with_delimiter(group.projects.count)
|
||||
|
||||
%span
|
||||
= icon('users')
|
||||
= number_with_delimiter(group.users.count)
|
||||
|
||||
%span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
|
||||
= visibility_level_icon(group.visibility_level, fw: false)
|
||||
|
||||
= image_tag group_icon(group), class: 'avatar s40 hidden-xs'
|
||||
.title
|
||||
= link_to [:admin, group], class: 'group-name' do
|
||||
= group.name
|
||||
|
||||
- if group.description.present?
|
||||
.description
|
||||
= markdown(group.description, pipeline: :description)
|
||||
- if group.description.present?
|
||||
.description
|
||||
= markdown(group.description, pipeline: :description)
|
||||
.group-controls.hidden-xs
|
||||
= link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: 'btn'
|
||||
= link_to 'Delete', [:admin, group], data: { confirm: "Are you sure you want to remove #{group.name}?" }, method: :delete, class: 'btn btn-remove'
|
||||
|
|
|
@ -3,41 +3,32 @@
|
|||
= render "admin/dashboard/head"
|
||||
|
||||
%div{ class: container_class }
|
||||
%h3.page-title
|
||||
Groups (#{number_with_delimiter(@groups.total_count)})
|
||||
|
||||
%p.light
|
||||
Group allows you to keep projects organized.
|
||||
Use groups for uniting related projects.
|
||||
|
||||
.top-area
|
||||
.nav-search
|
||||
= form_tag admin_groups_path, method: :get, class: 'form-inline' do
|
||||
.prepend-top-default.append-bottom-default
|
||||
= form_tag admin_groups_path, method: :get, class: 'js-search-form' do |f|
|
||||
= hidden_field_tag :sort, @sort
|
||||
= text_field_tag :name, params[:name], class: "form-control"
|
||||
= button_tag "Search", class: "btn submit btn-primary"
|
||||
|
||||
.nav-controls
|
||||
.dropdown.inline
|
||||
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
|
||||
%span.light
|
||||
- if @sort.present?
|
||||
= sort_options_hash[@sort]
|
||||
- else
|
||||
= sort_title_recently_created
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to admin_groups_path(sort: sort_value_recently_created) do
|
||||
= sort_title_recently_created
|
||||
= link_to admin_groups_path(sort: sort_value_oldest_created) do
|
||||
= sort_title_oldest_created
|
||||
= link_to admin_groups_path(sort: sort_value_recently_updated) do
|
||||
= sort_title_recently_updated
|
||||
= link_to admin_groups_path(sort: sort_value_oldest_updated) do
|
||||
= sort_title_oldest_updated
|
||||
= link_to 'New Group', new_admin_group_path, class: "btn btn-new"
|
||||
|
||||
.search-holder
|
||||
- project_name = params[:name].present? ? params[:name] : nil
|
||||
.search-field-holder
|
||||
= search_field_tag :name, project_name, class: "form-control search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: 'Search by name'
|
||||
= icon("search", class: "search-icon")
|
||||
.dropdown
|
||||
- toggle_text = @sort.present? ? sort_options_hash[@sort] : sort_title_recently_created
|
||||
= dropdown_toggle(toggle_text, { toggle: 'dropdown' })
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li.dropdown-header
|
||||
Sort by
|
||||
%li
|
||||
= link_to admin_groups_path(sort: sort_value_recently_created, name: project_name) do
|
||||
= sort_title_recently_created
|
||||
= link_to admin_groups_path(sort: sort_value_oldest_created, name: project_name) do
|
||||
= sort_title_oldest_created
|
||||
= link_to admin_groups_path(sort: sort_value_recently_updated, name: project_name) do
|
||||
= sort_title_recently_updated
|
||||
= link_to admin_groups_path(sort: sort_value_oldest_updated, name: project_name) do
|
||||
= sort_title_oldest_updated
|
||||
= link_to new_admin_group_path, class: "btn btn-new" do
|
||||
New Group
|
||||
%ul.content-list
|
||||
= render @groups
|
||||
|
||||
|
|
|
@ -1,97 +1,94 @@
|
|||
- @no_container = true
|
||||
- page_title "Projects"
|
||||
= render 'shared/show_aside'
|
||||
- params[:visibility_level] ||= []
|
||||
|
||||
= render "admin/dashboard/head"
|
||||
|
||||
%div{ class: container_class }
|
||||
.row.prepend-top-default
|
||||
%aside.col-md-3
|
||||
.panel.admin-filter
|
||||
= form_tag admin_namespaces_projects_path, method: :get, class: '' do
|
||||
.form-group
|
||||
= label_tag :name, 'Name:'
|
||||
= text_field_tag :name, params[:name], class: "form-control"
|
||||
.top-area
|
||||
.prepend-top-default
|
||||
= form_tag admin_namespaces_projects_path, method: :get do |f|
|
||||
.search-holder
|
||||
.search-field-holder
|
||||
= search_field_tag :name, params[:name], class: "form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false, placeholder: 'Search by name'
|
||||
|
||||
.form-group
|
||||
= label_tag :namespace_id, "Namespace"
|
||||
= namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large'
|
||||
- if params[:visibility_level].present?
|
||||
= hidden_field_tag 'visibility_level', params[:visibility_level]
|
||||
|
||||
.form-group
|
||||
%strong Activity
|
||||
.checkbox
|
||||
= label_tag :with_push do
|
||||
= check_box_tag :with_push, 1, params[:with_push]
|
||||
%span Projects with push events
|
||||
.checkbox
|
||||
= label_tag :abandoned do
|
||||
= check_box_tag :abandoned, 1, params[:abandoned]
|
||||
%span No activity over 6 month
|
||||
.checkbox
|
||||
= label_tag :with_archived do
|
||||
= check_box_tag :with_archived, 1, params[:with_archived]
|
||||
%span Show archived projects
|
||||
- if params[:sort].present?
|
||||
= hidden_field_tag 'sort', params[:sort]
|
||||
|
||||
%fieldset
|
||||
%strong Visibility level:
|
||||
.visibility-levels
|
||||
- Project.visibility_levels.each do |label, level|
|
||||
.checkbox
|
||||
%label
|
||||
= check_box_tag 'visibility_levels[]', level, params[:visibility_levels].present? && params[:visibility_levels].include?(level.to_s)
|
||||
%span.descr
|
||||
= visibility_level_icon(level)
|
||||
= label
|
||||
%fieldset
|
||||
%strong Problems
|
||||
.checkbox
|
||||
= label_tag :last_repository_check_failed do
|
||||
= check_box_tag :last_repository_check_failed, 1, params[:last_repository_check_failed]
|
||||
%span Last repository check failed
|
||||
- if params[:personal].present?
|
||||
= hidden_field_tag 'visibility_level', 'true'
|
||||
|
||||
= hidden_field_tag :sort, params[:sort]
|
||||
= button_tag "Search", class: "btn submit btn-primary"
|
||||
= link_to "Reset", admin_namespaces_projects_path, class: "btn btn-cancel"
|
||||
- if params[:archived].present?
|
||||
= hidden_field_tag 'archived', 'true'
|
||||
|
||||
%section.col-md-9
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
Projects (#{@projects.total_count})
|
||||
.controls
|
||||
.dropdown.inline
|
||||
%button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span.light
|
||||
- if @sort.present?
|
||||
= sort_options_hash[@sort]
|
||||
- else
|
||||
= sort_title_recently_created
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to admin_namespaces_projects_path(sort: sort_value_recently_created) do
|
||||
= sort_title_recently_created
|
||||
= link_to admin_namespaces_projects_path(sort: sort_value_oldest_created) do
|
||||
= sort_title_oldest_created
|
||||
= link_to admin_namespaces_projects_path(sort: sort_value_recently_updated) do
|
||||
= sort_title_recently_updated
|
||||
= link_to admin_namespaces_projects_path(sort: sort_value_oldest_updated) do
|
||||
= sort_title_oldest_updated
|
||||
= link_to admin_namespaces_projects_path(sort: sort_value_largest_repo) do
|
||||
= sort_title_largest_repo
|
||||
= link_to 'New Project', new_project_path, class: "btn btn-sm btn-success"
|
||||
%ul.well-list
|
||||
- @projects.each do |project|
|
||||
%li
|
||||
.list-item-name
|
||||
%span{ class: visibility_level_color(project.visibility_level) }
|
||||
= visibility_level_icon(project.visibility_level)
|
||||
= link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project]
|
||||
.pull-right
|
||||
- if project.archived
|
||||
%span.label.label-warning archived
|
||||
%span.label.label-gray
|
||||
= repository_size(project)
|
||||
= link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
|
||||
= link_to 'Destroy', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-sm btn-remove"
|
||||
- if @projects.blank?
|
||||
.nothing-here-block 0 projects matches
|
||||
= paginate @projects, theme: "gitlab"
|
||||
= icon("search", class: "search-icon")
|
||||
|
||||
.dropdown
|
||||
- toggle_text = 'Search for Namespace'
|
||||
- if params[:namespace_id].present?
|
||||
- namespace = Namespace.find(params[:namespace_id])
|
||||
- toggle_text = "#{namespace.kind}: #{namespace.path}"
|
||||
= dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { toggle_class: 'js-namespace-select large' })
|
||||
.dropdown-menu.dropdown-select.dropdown-menu-align-right
|
||||
= dropdown_title('Namespaces')
|
||||
= dropdown_filter("Search for Namespace")
|
||||
= dropdown_content
|
||||
= dropdown_loading
|
||||
|
||||
= button_tag "Search", class: "btn btn-primary btn-search"
|
||||
|
||||
%ul.nav-links
|
||||
- opts = params[:visibility_level].present? ? {} : { page: admin_namespaces_projects_path }
|
||||
= nav_link(opts) do
|
||||
= link_to admin_namespaces_projects_path do
|
||||
All
|
||||
|
||||
= nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::PRIVATE.to_s ? 'active' : '' }) do
|
||||
= link_to admin_namespaces_projects_path(visibility_level: Gitlab::VisibilityLevel::PRIVATE) do
|
||||
Private
|
||||
= nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::INTERNAL.to_s ? 'active' : '' }) do
|
||||
= link_to admin_namespaces_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL) do
|
||||
Internal
|
||||
= nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::PUBLIC.to_s ? 'active' : '' }) do
|
||||
= link_to admin_namespaces_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC) do
|
||||
Public
|
||||
|
||||
.nav-controls
|
||||
= render 'shared/projects/dropdown'
|
||||
= link_to new_project_path, class: 'btn btn-new' do
|
||||
New Project
|
||||
|
||||
.projects-list-holder
|
||||
- if @projects.any?
|
||||
%ul.projects-list.content-list
|
||||
- @projects.each_with_index do |project|
|
||||
%li.project-row
|
||||
.controls.pull-right
|
||||
- if project.archived
|
||||
%span.label.label-warning archived
|
||||
%span.label.label-gray
|
||||
= repository_size(project)
|
||||
= link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn"
|
||||
= link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove"
|
||||
.title
|
||||
= link_to [:admin, project.namespace.becomes(Namespace), project] do
|
||||
.dash-project-avatar
|
||||
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
|
||||
%span.project-full-name
|
||||
%span.namespace-name
|
||||
- if project.namespace
|
||||
= project.namespace.human_name
|
||||
\/
|
||||
%span.project-name.filter-title
|
||||
= project.name
|
||||
|
||||
- if project.description.present?
|
||||
.description
|
||||
= markdown(project.description, pipeline: :description)
|
||||
|
||||
= paginate @projects, theme: 'gitlab'
|
||||
- else
|
||||
.nothing-here-block No projects found
|
||||
|
|
|
@ -99,7 +99,13 @@
|
|||
.form-group
|
||||
= f.label :new_namespace_id, "Namespace", class: 'control-label'
|
||||
.col-sm-10
|
||||
= namespace_select_tag :new_namespace_id, selected: params[:namespace_id], class: 'input-large'
|
||||
.dropdown
|
||||
= dropdown_toggle('Search for Namespace', { toggle: 'dropdown', field_name: 'new_namespace_id', show_any: 'false' }, { toggle_class: 'js-namespace-select large' })
|
||||
.dropdown-menu.dropdown-select
|
||||
= dropdown_title('Namespaces')
|
||||
= dropdown_filter("Search for Namespace")
|
||||
= dropdown_content
|
||||
= dropdown_loading
|
||||
|
||||
.form-group
|
||||
.col-sm-offset-2.col-sm-10
|
||||
|
|
42
app/views/admin/users/_user.html.haml
Normal file
42
app/views/admin/users/_user.html.haml
Normal file
|
@ -0,0 +1,42 @@
|
|||
%li.user-row
|
||||
.user-avatar
|
||||
= image_tag avatar_icon(user), class: "avatar", alt: ''
|
||||
.user-details
|
||||
.user-name
|
||||
= link_to user.name, [:admin, user]
|
||||
- if user.blocked?
|
||||
%span.label.label-danger blocked
|
||||
- if user.admin?
|
||||
%span.label.label-success Admin
|
||||
- if user.external?
|
||||
%span.label.label-default External
|
||||
- if user == current_user
|
||||
%span It's you!
|
||||
.user-email
|
||||
= mail_to user.email, user.email
|
||||
.controls.pull-right
|
||||
= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn'
|
||||
- unless user == current_user
|
||||
.dropdown.inline
|
||||
%a.dropdown-new.btn.btn-default#project-settings-button{href: '#', data: { toggle: 'dropdown' } }
|
||||
= icon('cog')
|
||||
= icon('caret-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li.dropdown-header
|
||||
Settings
|
||||
%li
|
||||
- if user.ldap_blocked?
|
||||
%span.small Cannot unblock LDAP blocked users
|
||||
- elsif user.blocked?
|
||||
= link_to 'Unblock', unblock_admin_user_path(user), method: :put
|
||||
- else
|
||||
= link_to 'Block', block_admin_user_path(user), data: { confirm: 'USER WILL BE BLOCKED! Are you sure?' }, method: :put
|
||||
- if user.access_locked?
|
||||
%li
|
||||
= link_to 'Unlock', unlock_admin_user_path(user), method: :put, class: 'btn-grouped btn btn-xs btn-success', data: { confirm: 'Are you sure?' }
|
||||
- if user.can_be_removed?
|
||||
%li.divider
|
||||
%li
|
||||
= link_to 'Delete User', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and groups linked to this user will also be removed! Consider cancelling this deletion and blocking the user instead. Are you sure?" },
|
||||
class: 'btn btn-remove btn-block',
|
||||
method: :delete
|
|
@ -1,110 +1,78 @@
|
|||
- @no_container = true
|
||||
- page_title "Users"
|
||||
= render 'shared/show_aside'
|
||||
= render "admin/dashboard/head"
|
||||
|
||||
%div{ class: container_class }
|
||||
.admin-filter
|
||||
%ul.nav-links
|
||||
%li{class: "#{'active' unless params[:filter]}"}
|
||||
= link_to admin_users_path do
|
||||
Active
|
||||
%small.badge= number_with_delimiter(User.active.count)
|
||||
%li{class: "#{'active' if params[:filter] == "admins"}"}
|
||||
= link_to admin_users_path(filter: "admins") do
|
||||
Admins
|
||||
%small.badge= number_with_delimiter(User.admins.count)
|
||||
%li.filter-two-factor-enabled{class: "#{'active' if params[:filter] == 'two_factor_enabled'}"}
|
||||
= link_to admin_users_path(filter: 'two_factor_enabled') do
|
||||
2FA Enabled
|
||||
%small.badge= number_with_delimiter(User.with_two_factor.count)
|
||||
%li.filter-two-factor-disabled{class: "#{'active' if params[:filter] == 'two_factor_disabled'}"}
|
||||
= link_to admin_users_path(filter: 'two_factor_disabled') do
|
||||
2FA Disabled
|
||||
%small.badge= number_with_delimiter(User.without_two_factor.count)
|
||||
%li.filter-external{class: "#{'active' if params[:filter] == 'external'}"}
|
||||
= link_to admin_users_path(filter: 'external') do
|
||||
External
|
||||
%small.badge= number_with_delimiter(User.external.count)
|
||||
%li{class: "#{'active' if params[:filter] == "blocked"}"}
|
||||
= link_to admin_users_path(filter: "blocked") do
|
||||
Blocked
|
||||
%small.badge= number_with_delimiter(User.blocked.count)
|
||||
%li{class: "#{'active' if params[:filter] == "wop"}"}
|
||||
= link_to admin_users_path(filter: "wop") do
|
||||
Without projects
|
||||
%small.badge= number_with_delimiter(User.without_projects.count)
|
||||
.top-area
|
||||
.prepend-top-default
|
||||
= form_tag admin_users_path, method: :get do
|
||||
- if params[:filter].present?
|
||||
= hidden_field_tag "filter", h(params[:filter])
|
||||
.search-holder
|
||||
.search-field-holder
|
||||
= search_field_tag :name, params[:name], placeholder: 'Search by name, email or username', class: 'form-control search-text-input js-search-input', spellcheck: false
|
||||
= icon("search", class: "search-icon")
|
||||
.dropdown
|
||||
- toggle_text = if @sort.present? then sort_options_hash[@sort] else sort_title_name end
|
||||
= dropdown_toggle(toggle_text, { toggle: 'dropdown' })
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%li.dropdown-header
|
||||
Sort by
|
||||
%li
|
||||
= link_to admin_users_path(sort: sort_value_name, filter: params[:filter]) do
|
||||
= sort_title_name
|
||||
= link_to admin_users_path(sort: sort_value_recently_signin, filter: params[:filter]) do
|
||||
= sort_title_recently_signin
|
||||
= link_to admin_users_path(sort: sort_value_oldest_signin, filter: params[:filter]) do
|
||||
= sort_title_oldest_signin
|
||||
= link_to admin_users_path(sort: sort_value_recently_created, filter: params[:filter]) do
|
||||
= sort_title_recently_created
|
||||
= link_to admin_users_path(sort: sort_value_oldest_created, filter: params[:filter]) do
|
||||
= sort_title_oldest_created
|
||||
= link_to admin_users_path(sort: sort_value_recently_updated, filter: params[:filter]) do
|
||||
= sort_title_recently_updated
|
||||
= link_to admin_users_path(sort: sort_value_oldest_updated, filter: params[:filter]) do
|
||||
= sort_title_oldest_updated
|
||||
= link_to 'New User', new_admin_user_path, class: 'btn btn-new btn-search'
|
||||
|
||||
.row-content-block.second-block
|
||||
.pull-right
|
||||
.dropdown.inline
|
||||
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
|
||||
%span.light
|
||||
- if @sort.present?
|
||||
= sort_options_hash[@sort]
|
||||
- else
|
||||
= sort_title_name
|
||||
%b.caret
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
= link_to admin_users_path(sort: sort_value_name, filter: params[:filter]) do
|
||||
= sort_title_name
|
||||
= link_to admin_users_path(sort: sort_value_recently_signin, filter: params[:filter]) do
|
||||
= sort_title_recently_signin
|
||||
= link_to admin_users_path(sort: sort_value_oldest_signin, filter: params[:filter]) do
|
||||
= sort_title_oldest_signin
|
||||
= link_to admin_users_path(sort: sort_value_recently_created, filter: params[:filter]) do
|
||||
= sort_title_recently_created
|
||||
= link_to admin_users_path(sort: sort_value_oldest_created, filter: params[:filter]) do
|
||||
= sort_title_oldest_created
|
||||
= link_to admin_users_path(sort: sort_value_recently_updated, filter: params[:filter]) do
|
||||
= sort_title_recently_updated
|
||||
= link_to admin_users_path(sort: sort_value_oldest_updated, filter: params[:filter]) do
|
||||
= sort_title_oldest_updated
|
||||
.nav-block
|
||||
%ul.nav-links.wide.scrolling-tabs.white.scrolling-tabs
|
||||
.fade-left
|
||||
= nav_link(html_options: { class: ('active' unless params[:filter]) }) do
|
||||
= link_to admin_users_path do
|
||||
Active
|
||||
%small.badge= number_with_delimiter(User.active.count)
|
||||
= nav_link(html_options: { class: ('active' if params[:filter] == 'admins') }) do
|
||||
= link_to admin_users_path(filter: "admins") do
|
||||
Admins
|
||||
%small.badge= number_with_delimiter(User.admins.count)
|
||||
= nav_link(html_options: { class: "#{'active' if params[:filter] == 'two_factor_enabled'} filter-two-factor-enabled" }) do
|
||||
= link_to admin_users_path(filter: 'two_factor_enabled') do
|
||||
2FA Enabled
|
||||
%small.badge= number_with_delimiter(User.with_two_factor.count)
|
||||
= nav_link(html_options: { class: "#{'active' if params[:filter] == 'two_factor_disabled'} filter-two-factor-disabled" }) do
|
||||
= link_to admin_users_path(filter: 'two_factor_disabled') do
|
||||
2FA Disabled
|
||||
%small.badge= number_with_delimiter(User.without_two_factor.count)
|
||||
= nav_link(html_options: { class: ('active' if params[:filter] == 'external') }) do
|
||||
= link_to admin_users_path(filter: 'external') do
|
||||
External
|
||||
%small.badge= number_with_delimiter(User.external.count)
|
||||
= nav_link(html_options: { class: ('active' if params[:filter] == 'blocked') }) do
|
||||
= link_to admin_users_path(filter: "blocked") do
|
||||
Blocked
|
||||
%small.badge= number_with_delimiter(User.blocked.count)
|
||||
= nav_link(html_options: { class: ('active' if params[:filter] == 'wop') }) do
|
||||
= link_to admin_users_path(filter: "wop") do
|
||||
Without projects
|
||||
%small.badge= number_with_delimiter(User.without_projects.count)
|
||||
.fade-right
|
||||
|
||||
= link_to 'New User', new_admin_user_path, class: "btn btn-new"
|
||||
= form_tag admin_users_path, method: :get, class: 'form-inline' do
|
||||
.form-group
|
||||
= search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control', spellcheck: false
|
||||
= hidden_field_tag "filter", params[:filter]
|
||||
= button_tag class: 'btn btn-primary' do
|
||||
%i.fa.fa-search
|
||||
%ul.users-list.content-list
|
||||
- if @users.empty?
|
||||
%li
|
||||
.nothing-here-block No users found.
|
||||
- else
|
||||
= render partial: 'admin/users/user', collection: @users
|
||||
|
||||
|
||||
.panel.panel-default
|
||||
%ul.well-list
|
||||
- @users.each do |user|
|
||||
%li
|
||||
.list-item-name
|
||||
- if user.blocked?
|
||||
= icon("lock", class: "cred")
|
||||
- else
|
||||
= icon("user", class: "cgreen")
|
||||
= link_to user.name, [:admin, user]
|
||||
- if user.admin?
|
||||
%strong.cred (Admin)
|
||||
- if user.external?
|
||||
%strong.cred (External)
|
||||
- if user == current_user
|
||||
%span.cred It's you!
|
||||
.pull-right
|
||||
%span.light
|
||||
%i.fa.fa-envelope
|
||||
= mail_to user.email, user.email, class: 'light'
|
||||
|
||||
.pull-right
|
||||
= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn-grouped btn btn-xs'
|
||||
- unless user == current_user
|
||||
- if user.ldap_blocked?
|
||||
= link_to '#', title: 'Cannot unblock LDAP blocked users', data: {toggle: 'tooltip'}, class: 'btn-grouped btn btn-xs btn-success disabled' do
|
||||
%i.fa.fa-lock
|
||||
Unblock
|
||||
- elsif user.blocked?
|
||||
= link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: 'btn-grouped btn btn-xs btn-success'
|
||||
- else
|
||||
= link_to 'Block', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: 'btn-grouped btn btn-xs btn-warning'
|
||||
- if user.access_locked?
|
||||
= link_to 'Unlock', unlock_admin_user_path(user), method: :put, class: 'btn-grouped btn btn-xs btn-success', data: { confirm: 'Are you sure?' }
|
||||
- if user.can_be_removed?
|
||||
= link_to 'Destroy', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and groups linked to this user will also be removed! Maybe block the user instead? Are you sure?" }, method: :delete, class: 'btn-grouped btn btn-xs btn-remove'
|
||||
= paginate @users, theme: "gitlab"
|
||||
|
|
|
@ -1,31 +1,30 @@
|
|||
- @sort ||= sort_value_recently_updated
|
||||
- personal = params[:personal]
|
||||
- archived = params[:archived]
|
||||
- namespace_id = params[:namespace_id]
|
||||
.dropdown.inline
|
||||
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
%span.light
|
||||
= projects_sort_options_hash[@sort]
|
||||
%b.caret
|
||||
- toggle_text = projects_sort_options_hash[@sort]
|
||||
= dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { id: 'sort-projects-dropdown' })
|
||||
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable
|
||||
%li.dropdown-header
|
||||
Sort by
|
||||
- projects_sort_options_hash.each do |value, title|
|
||||
%li
|
||||
= link_to filter_projects_path(sort: value, archived: archived, personal: personal), class: ("is-active" if @sort == value) do
|
||||
= link_to filter_projects_path(namespace_id: namespace_id, sort: value, archived: archived, personal: personal), class: ("is-active" if @sort == value) do
|
||||
= title
|
||||
|
||||
%li.divider
|
||||
%li
|
||||
= link_to filter_projects_path(sort: @sort, archived: nil), class: ("is-active" unless params[:archived].present?) do
|
||||
= link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, archived: nil), class: ("is-active" unless params[:archived].present?) do
|
||||
Hide archived projects
|
||||
%li
|
||||
= link_to filter_projects_path(sort: @sort, archived: true), class: ("is-active" if params[:archived].present?) do
|
||||
= link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, archived: true), class: ("is-active" if params[:archived].present?) do
|
||||
Show archived projects
|
||||
- if current_user
|
||||
%li.divider
|
||||
%li
|
||||
= link_to filter_projects_path(sort: @sort, personal: nil), class: ("is-active" unless personal) do
|
||||
= link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, personal: nil), class: ("is-active" unless personal.present?) do
|
||||
Owned by anyone
|
||||
%li
|
||||
= link_to filter_projects_path(sort: @sort, personal: true), class: ("is-active" if personal) do
|
||||
= link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, personal: true), class: ("is-active" if personal.present?) do
|
||||
Owned by me
|
||||
|
|
|
@ -10,10 +10,11 @@ Feature: Admin Projects
|
|||
Then I should see all non-archived projects
|
||||
And I should not see project "Archive"
|
||||
|
||||
@javascript
|
||||
Scenario: I should see all projects in the list
|
||||
Given archived project "Archive"
|
||||
When I visit admin projects page
|
||||
And I check "Show archived projects"
|
||||
And I select "Show archived projects"
|
||||
Then I should see all projects
|
||||
And I should see "archived" label
|
||||
|
||||
|
@ -22,6 +23,7 @@ Feature: Admin Projects
|
|||
And I click on first project
|
||||
Then I should see project details
|
||||
|
||||
@javascript
|
||||
Scenario: Transfer project
|
||||
Given group 'Web'
|
||||
And I visit admin project page
|
||||
|
|
|
@ -18,9 +18,9 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps
|
|||
end
|
||||
end
|
||||
|
||||
step 'I check "Show archived projects"' do
|
||||
page.check 'Show archived projects'
|
||||
click_button "Search"
|
||||
step 'I select "Show archived projects"' do
|
||||
find(:css, '#sort-projects-dropdown').click
|
||||
click_link 'Show archived projects'
|
||||
end
|
||||
|
||||
step 'I should see "archived" label' do
|
||||
|
@ -45,7 +45,8 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps
|
|||
step 'I transfer project to group \'Web\'' do
|
||||
allow_any_instance_of(Projects::TransferService).
|
||||
to receive(:move_uploads_to_new_namespace).and_return(true)
|
||||
find(:xpath, "//input[@id='new_namespace_id']").set group.id
|
||||
click_button 'Search for Namespace'
|
||||
click_link 'group: web'
|
||||
click_button 'Transfer'
|
||||
end
|
||||
|
||||
|
|
|
@ -11,12 +11,12 @@ describe Admin::ProjectsController do
|
|||
render_views
|
||||
|
||||
it 'retrieves the project for the given visibility level' do
|
||||
get :index, visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]
|
||||
get :index, visibility_level: [Gitlab::VisibilityLevel::PUBLIC]
|
||||
expect(response.body).to match(project.name)
|
||||
end
|
||||
|
||||
it 'does not retrieve the project' do
|
||||
get :index, visibility_levels: [Gitlab::VisibilityLevel::INTERNAL]
|
||||
get :index, visibility_level: [Gitlab::VisibilityLevel::INTERNAL]
|
||||
expect(response.body).not_to match(project.name)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue