From f39dbf3d3d4d5c99910043e8793a96246f0039fe Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Jan 2017 17:32:34 +0200 Subject: [PATCH] Allow creating nested group via UI Signed-off-by: Dmitriy Zaporozhets --- app/controllers/groups_controller.rb | 33 +++++++++----- app/views/groups/_home_panel.html.haml | 17 ++++++++ app/views/groups/_show_nav.html.haml | 7 +++ app/views/groups/show.html.haml | 43 ++----------------- app/views/groups/subgroups.html.haml | 20 +++++++++ app/views/shared/_group_form.html.haml | 7 ++- app/views/shared/groups/_group.html.haml | 6 ++- app/views/shared/projects/_dropdown.html.haml | 12 ++++++ .../dz-create-nested-groups-via-ui.yml | 4 ++ config/routes/group.rb | 1 + spec/features/groups_spec.rb | 19 +++++++- 11 files changed, 115 insertions(+), 54 deletions(-) create mode 100644 app/views/groups/_home_panel.html.haml create mode 100644 app/views/groups/_show_nav.html.haml create mode 100644 app/views/groups/subgroups.html.haml create mode 100644 changelogs/unreleased/dz-create-nested-groups-via-ui.yml diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 264b14713fb..20e1c79fe6b 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -13,9 +13,11 @@ class GroupsController < Groups::ApplicationController before_action :authorize_create_group!, only: [:new, :create] # Load group projects - before_action :group_projects, only: [:show, :projects, :activity, :issues, :merge_requests] + before_action :group_projects, only: [:projects, :activity, :issues, :merge_requests] before_action :event_filter, only: [:activity] + before_action :user_actions, only: [:show, :subgroups] + layout :determine_layout def index @@ -37,13 +39,6 @@ class GroupsController < Groups::ApplicationController end def show - if current_user - @last_push = current_user.recent_push - @notification_setting = current_user.notification_settings_for(group) - end - - @nested_groups = group.children - setup_projects respond_to do |format| @@ -62,6 +57,11 @@ class GroupsController < Groups::ApplicationController end end + def subgroups + @nested_groups = group.children + @nested_groups = @nested_groups.search(params[:filter_groups]) if params[:filter_groups].present? + end + def activity respond_to do |format| format.html @@ -99,13 +99,16 @@ class GroupsController < Groups::ApplicationController protected def setup_projects + options = {} + options[:only_owned] = true if params[:shared] == '0' + options[:only_shared] = true if params[:shared] == '1' + + @projects = GroupProjectsFinder.new(group, options).execute(current_user) @projects = @projects.includes(:namespace) @projects = @projects.sorted_by_activity @projects = filter_projects(@projects) @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.page(params[:page]) if params[:filter_projects].blank? - - @shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user) end def authorize_create_group! @@ -138,7 +141,8 @@ class GroupsController < Groups::ApplicationController :public, :request_access_enabled, :share_with_group_lock, - :visibility_level + :visibility_level, + :parent_id ] end @@ -147,4 +151,11 @@ class GroupsController < Groups::ApplicationController @events = event_filter.apply_filter(@events).with_associations @events = @events.limit(20).offset(params[:offset] || 0) end + + def user_actions + if current_user + @last_push = current_user.recent_push + @notification_setting = current_user.notification_settings_for(group) + end + end end diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml new file mode 100644 index 00000000000..41f54f6bf42 --- /dev/null +++ b/app/views/groups/_home_panel.html.haml @@ -0,0 +1,17 @@ +.group-home-panel.text-center + %div{ class: container_class } + .avatar-container.s70.group-avatar + = image_tag group_icon(@group), class: "avatar s70 avatar-tile" + %h1.group-title + @#{@group.path} + %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) } + = visibility_level_icon(@group.visibility_level, fw: false) + + - if @group.description.present? + .group-home-desc + = markdown_field(@group, :description) + + - if current_user + .group-buttons + = render 'shared/members/access_request_buttons', source: @group + = render 'shared/notifications/button', notification_setting: @notification_setting diff --git a/app/views/groups/_show_nav.html.haml b/app/views/groups/_show_nav.html.haml new file mode 100644 index 00000000000..b2097e88741 --- /dev/null +++ b/app/views/groups/_show_nav.html.haml @@ -0,0 +1,7 @@ +%ul.nav-links + = nav_link(page: group_path(@group)) do + = link_to group_path(@group) do + Projects + = nav_link(page: subgroups_group_path(@group)) do + = link_to subgroups_group_path(@group) do + Subgroups diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index d256d14609e..b040f404ac4 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -4,38 +4,12 @@ - if current_user = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") -.group-home-panel.text-center - %div{ class: container_class } - .avatar-container.s70.group-avatar - = image_tag group_icon(@group), class: "avatar s70 avatar-tile" - %h1.group-title - @#{@group.path} - %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) } - = visibility_level_icon(@group.visibility_level, fw: false) += render 'groups/home_panel' - - if @group.description.present? - .group-home-desc - = markdown_field(@group, :description) - - - if current_user - .group-buttons - = render 'shared/members/access_request_buttons', source: @group - = render 'shared/notifications/button', notification_setting: @notification_setting .groups-header{ class: container_class } .top-area - %ul.nav-links - %li.active - = link_to "#projects", 'data-toggle' => 'tab' do - All Projects - - if @shared_projects.present? - %li - = link_to "#shared", 'data-toggle' => 'tab' do - Shared Projects - - if @nested_groups.present? - %li - = link_to "#groups", 'data-toggle' => 'tab' do - Subgroups + = render 'groups/show_nav' .nav-controls = form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false @@ -44,15 +18,4 @@ = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do New Project - .tab-content - .tab-pane.active#projects - = render "projects", projects: @projects - - - if @shared_projects.present? - .tab-pane#shared - = render "shared_projects", projects: @shared_projects - - - if @nested_groups.present? - .tab-pane#groups - %ul.content-list - = render partial: 'shared/groups/group', collection: @nested_groups + = render "projects", projects: @projects diff --git a/app/views/groups/subgroups.html.haml b/app/views/groups/subgroups.html.haml new file mode 100644 index 00000000000..8610ae7e0ef --- /dev/null +++ b/app/views/groups/subgroups.html.haml @@ -0,0 +1,20 @@ +- @no_container = true + += render 'groups/home_panel' + +.groups-header{ class: container_class } + .top-area + = render 'groups/show_nav' + .nav-controls + = form_tag request.path, method: :get do |f| + = search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name', class: 'form-control', spellcheck: false + - if can? current_user, :admin_group, @group + = link_to new_group_path(parent_id: @group.id), class: 'btn btn-new pull-right' do + New Subgroup + + - if @nested_groups.present? + %ul.content-list + = render partial: 'shared/groups/group', collection: @nested_groups, locals: { full_name: false } + - else + .nothing-here-block + There are no subgroups to show. diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml index 0bc851b4256..efb207b9916 100644 --- a/app/views/shared/_group_form.html.haml +++ b/app/views/shared/_group_form.html.haml @@ -1,3 +1,4 @@ +- parent = Group.find_by(id: params[:parent_id] || @group.parent_id) - if @group.persisted? .form-group = f.label :name, class: 'control-label' do @@ -11,11 +12,15 @@ .col-sm-10 .input-group.gl-field-error-anchor .input-group-addon - = root_url + %span>= root_url + - if parent + %strong= parent.full_path + '/' = f.text_field :path, placeholder: 'open-source', class: 'form-control', autofocus: local_assigns[:autofocus] || false, required: true, pattern: Gitlab::Regex::NAMESPACE_REGEX_STR_SIMPLE, title: 'Please choose a group name with no special characters.' + - if parent + = f.hidden_field :parent_id, value: parent.id - if @group.persisted? .alert.alert-warning.prepend-top-10 diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml index dd9e433491b..60ca23ef680 100644 --- a/app/views/shared/groups/_group.html.haml +++ b/app/views/shared/groups/_group.html.haml @@ -1,4 +1,5 @@ - group_member = local_assigns[:group_member] +- full_name = true unless local_assigns[:full_name] == false - css_class = '' unless local_assigns[:css_class] - css_class += " no-description" if group.description.blank? @@ -28,7 +29,10 @@ = image_tag group_icon(group), class: "avatar s40 hidden-xs" .title = link_to group, class: 'group-name' do - = group.full_name + - if full_name + = group.full_name + - else + = group.name - if group_member as diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml index b7f8551153b..1141afc5977 100644 --- a/app/views/shared/projects/_dropdown.html.haml +++ b/app/views/shared/projects/_dropdown.html.haml @@ -1,6 +1,7 @@ - @sort ||= sort_value_recently_updated - personal = params[:personal] - archived = params[:archived] +- shared = params[:shared] - namespace_id = params[:namespace_id] .dropdown.inline - toggle_text = projects_sort_options_hash[@sort] @@ -28,3 +29,14 @@ %li = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, personal: true), class: ("is-active" if personal.present?) do Owned by me + - if @group && @group.shared_projects.present? + %li.divider + %li + = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: nil), class: ("is-active" unless shared.present?) do + All projects + %li + = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: 0), class: ("is-active" if shared == '0') do + Hide shared projects + %li + = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: 1), class: ("is-active" if shared == '1') do + Hide group projects diff --git a/changelogs/unreleased/dz-create-nested-groups-via-ui.yml b/changelogs/unreleased/dz-create-nested-groups-via-ui.yml new file mode 100644 index 00000000000..f9529a5941a --- /dev/null +++ b/changelogs/unreleased/dz-create-nested-groups-via-ui.yml @@ -0,0 +1,4 @@ +--- +title: Allow creating nested groups via UI +merge_request: 8786 +author: diff --git a/config/routes/group.rb b/config/routes/group.rb index 60a1175fe80..73f69d76995 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -25,5 +25,6 @@ scope(path: 'groups/*id', get :merge_requests, as: :merge_requests_group get :projects, as: :projects_group get :activity, as: :activity_group + get :subgroups, as: :subgroups_group get '/', action: :show, as: :group_canonical end diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index a515c92db37..37b7c20239f 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -45,6 +45,23 @@ feature 'Group', feature: true do end end + describe 'create a nested group' do + let(:group) { create(:group, path: 'foo') } + + before do + visit subgroups_group_path(group) + click_link 'New Subgroup' + end + + it 'creates a nested group' do + fill_in 'Group path', with: 'bar' + click_button 'Create group' + + expect(current_path).to eq(group_path('foo/bar')) + expect(page).to have_content("Group 'bar' was successfully created.") + end + end + describe 'group edit' do let(:group) { create(:group) } let(:path) { edit_group_path(group) } @@ -117,7 +134,7 @@ feature 'Group', feature: true do visit path click_link 'Subgroups' - expect(page).to have_content(nested_group.full_name) + expect(page).to have_content(nested_group.name) end end end