From c09fd6dcba74aa59ffdbcd1599d49629cac3603f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 22 Jan 2016 19:28:37 +0100 Subject: [PATCH 01/14] Add a dropdown to filter and sort projects in the dashboard & the group page The group page has been splitted in two real page (instead of loading the activity feed and the project lists) since sorting reload the page. --- CHANGELOG | 1 + app/assets/stylesheets/pages/projects.scss | 44 ++-- app/controllers/concerns/projects_listing.rb | 46 ++++ .../dashboard/projects_controller.rb | 18 +- .../explore/projects_controller.rb | 21 +- app/controllers/groups/projects_controller.rb | 11 + app/controllers/groups_controller.rb | 52 +++- app/helpers/sorting_helper.rb | 68 ++++- app/helpers/tab_helper.rb | 8 +- app/models/concerns/sortable.rb | 26 +- app/models/project.rb | 4 + app/views/admin/users/index.html.haml | 2 +- app/views/dashboard/_projects_head.html.haml | 18 +- .../dashboard/projects/_projects.html.haml | 3 +- app/views/dashboard/projects/index.html.haml | 2 +- .../dashboard/projects/starred.html.haml | 2 +- .../explore/projects/_dropdown.html.haml | 3 +- app/views/explore/projects/starred.html.haml | 6 +- app/views/explore/projects/trending.html.haml | 4 +- app/views/groups/_header.html.haml | 34 +++ app/views/groups/_projects.html.haml | 11 +- app/views/groups/_projects_head.html.haml | 15 ++ app/views/groups/edit.html.haml | 1 + app/views/groups/projects.html.haml | 42 +--- app/views/groups/projects/edit.html.haml | 31 +++ app/views/groups/show.html.haml | 57 +---- app/views/layouts/group_settings.html.haml | 4 +- .../layouts/nav/_group_settings.html.haml | 4 +- app/views/shared/projects/_controls.html.haml | 12 + app/views/shared/projects/_dropdown.html.haml | 18 ++ config/routes.rb | 1 + features/dashboard/projects.feature | 61 +++++ features/dashboard/starred_projects.feature | 57 ++++- features/groups.feature | 38 ++- features/steps/dashboard/dashboard.rb | 12 - features/steps/dashboard/projects.rb | 5 + features/steps/dashboard/starred_projects.rb | 10 - features/steps/groups.rb | 42 +++- features/steps/shared/paths.rb | 6 +- features/steps/shared/project.rb | 236 +++++++++++++++--- spec/helpers/sorting_helper_spec.rb | 45 ++++ spec/models/concerns/sortable_spec.rb | 54 ++++ 42 files changed, 899 insertions(+), 236 deletions(-) create mode 100644 app/controllers/concerns/projects_listing.rb create mode 100644 app/controllers/groups/projects_controller.rb create mode 100644 app/views/groups/_header.html.haml create mode 100644 app/views/groups/_projects_head.html.haml create mode 100644 app/views/groups/projects/edit.html.haml create mode 100644 app/views/shared/projects/_controls.html.haml create mode 100644 app/views/shared/projects/_dropdown.html.haml create mode 100644 features/dashboard/projects.feature create mode 100644 features/steps/dashboard/projects.rb create mode 100644 spec/helpers/sorting_helper_spec.rb create mode 100644 spec/models/concerns/sortable_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 0c4d90855a52..b2bbeaba6562 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,7 @@ v 8.5.0 (unreleased) - Upgrade gitlab_git to 7.2.23 to fix commit message mentions in first branch push - New UI for pagination - Fix diff comments loaded by AJAX to load comment with diff in discussion tab + - Add a dropdown to filter and sort projects in the dashboard & the group page v 8.4.0 - Allow LDAP users to change their email if it was not set by the LDAP server diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 003a4c22f206..1ea4a4a41be5 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -191,9 +191,10 @@ .dropdown-menu { @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); - @include border-radius (0px); + @include border-radius (1px); border: none; + margin-top: 5px; padding: 16px 0; font-size: 14px; font-weight: 100; @@ -207,6 +208,10 @@ } } + hr { + margin: 10px; + } + i { margin-right: 8px; } @@ -281,7 +286,7 @@ margin-top: -1px; } -.top-area { +.nav-block { border-bottom: 1px solid #EEE; ul.nav-links { @@ -291,22 +296,29 @@ border-bottom: none; } - .projects-search-form { + .projects-controls { width: 50%; - display: inline-block; - float: right; - padding-top: 11px; - text-align: right; - .btn-green { - margin-left: 10px; - float: right; - } - } + .dropdown-menu { + font-size: $gl-font-size; - @media (max-width: $screen-xs-max) { - .projects-search-form { - padding-top: 15px; + li { + + &.dropdown-label { + padding-left: $gl-padding; + padding-bottom: $gl-vert-padding; + font-size: 14px; + } + + a { + line-height: 18px; + padding-left: 39px; + + &.active { + padding-left: $gl-padding; + } + } + } } } } @@ -559,7 +571,7 @@ pre.light-well { } } -.cannot-be-merged, +.cannot-be-merged, .cannot-be-merged:hover { color: #E62958; margin-top: 2px; diff --git a/app/controllers/concerns/projects_listing.rb b/app/controllers/concerns/projects_listing.rb new file mode 100644 index 000000000000..339ef09c4950 --- /dev/null +++ b/app/controllers/concerns/projects_listing.rb @@ -0,0 +1,46 @@ +# == AuthenticatesWithTwoFactor +# +# Controller concern to handle projects list filtering & sorting +# +# Upon inclusion, skips `require_no_authentication` on `:create`. +module ProjectsListing + extend ActiveSupport::Concern + + included do + before_action :load_filter_and_sort + end + + private + + def load_filter_and_sort + @filter = params.fetch(:filter) { 'all' } + @sort = params.fetch(:sort) { 'recently_active' } + end + + def load_user_projects + @user_projects = refine_projects(ProjectsFinder.new.execute(current_user)) + end + + def load_starred_projects + @starred_projects = refine_projects( + current_user.starred_projects.includes(:forked_from_project) + ) + end + + def refine_projects(relation) + apply_filter(apply_base_scopes(relation)).sort(@sort) + end + + def apply_base_scopes(relation) + relation.non_archived.includes(:namespace) + end + + def apply_filter(relation) + if current_user && @filter == 'personal' + relation.personal(current_user) + else + relation + end + end + +end diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 58e9049f158e..00a50f7c852f 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -1,15 +1,14 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController - before_action :event_filter + include ProjectsListing + + before_action :load_user_projects, :load_starred_projects, :load_last_push, :event_filter def index - @projects = current_user.authorized_projects.sorted_by_activity.non_archived - @projects = @projects.includes(:namespace) - @last_push = current_user.recent_push + @projects = @user_projects respond_to do |format| format.html format.atom do - event_filter load_events render layout: false end @@ -17,10 +16,7 @@ def index end def starred - @projects = current_user.starred_projects - @projects = @projects.includes(:namespace, :forked_from_project, :tags) - @projects = @projects.sort(@sort = params[:sort]) - @last_push = current_user.recent_push + @projects = @starred_projects @groups = [] respond_to do |format| @@ -35,6 +31,10 @@ def starred private + def load_last_push + @last_push = current_user.recent_push + end + def load_events @events = Event.in_projects(@projects.pluck(:id)) @events = @event_filter.apply_filter(@events).with_associations diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index a5aeaed66c56..e35ac5dc6190 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -1,24 +1,25 @@ class Explore::ProjectsController < Explore::ApplicationController + include ProjectsListing + + skip_before_action :load_filter_and_sort + before_action :load_user_projects, :load_starred_projects, if: :current_user + def index - @projects = ProjectsFinder.new.execute(current_user) + @projects = ProjectsFinder.new.execute(current_user).non_archived.includes(:namespace) @tags = @projects.tags_on(:tags) @projects = @projects.tagged_with(params[:tag]) if params[:tag].present? @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? - @projects = @projects.non_archived @projects = @projects.search(params[:search]) if params[:search].present? - @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) + @projects = @projects.sort(@sort).page(params[:page]).per(PER_PAGE) end def trending - @trending_projects = TrendingProjectsFinder.new.execute(current_user) - @trending_projects = @trending_projects.non_archived - @trending_projects = @trending_projects.page(params[:page]).per(PER_PAGE) + @projects = TrendingProjectsFinder.new.execute(current_user).non_archived. + page(params[:page]).per(PER_PAGE) end def starred - @starred_projects = ProjectsFinder.new.execute(current_user) - @starred_projects = @starred_projects.reorder('star_count DESC') - @starred_projects = @starred_projects.page(params[:page]).per(PER_PAGE) + @projects = ProjectsFinder.new.execute(current_user).non_archived. + sort('stars').page(params[:page]).per(PER_PAGE) end end diff --git a/app/controllers/groups/projects_controller.rb b/app/controllers/groups/projects_controller.rb new file mode 100644 index 000000000000..67eaabe382cc --- /dev/null +++ b/app/controllers/groups/projects_controller.rb @@ -0,0 +1,11 @@ +class Groups::ProjectsController < Groups::ApplicationController + # Authorize + before_action :authorize_admin_group! + + layout 'group_settings' + + def edit + @projects = @group.projects.page(params[:page]) + end + +end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index fb26a4e6fc33..8ba17dd498c8 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,18 +1,20 @@ class GroupsController < Groups::ApplicationController include IssuesAction include MergeRequestsAction + include ProjectsListing - skip_before_action :authenticate_user!, only: [:show, :issues, :merge_requests] + skip_before_action :authenticate_user!, only: [:show, :projects, :issues, :merge_requests] respond_to :html before_action :group, except: [:new, :create] # Authorize - before_action :authorize_read_group!, except: [:show, :new, :create, :autocomplete] - before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects] + before_action :authorize_read_group!, except: [:show, :projects, :new, :create, :autocomplete] + before_action :authorize_admin_group!, only: [:edit, :update, :destroy] before_action :authorize_create_group!, only: [:new, :create] # Load group projects - before_action :load_projects, except: [:new, :create, :projects, :edit, :update, :autocomplete] + before_action :load_projects, only: [:show, :projects, :issues, :merge_requests] + before_action :load_contributed_projects, :load_starred_projects, only: :projects before_action :event_filter, only: :show layout :determine_layout @@ -39,7 +41,6 @@ def create def show @last_push = current_user.recent_push if current_user - @projects = @projects.includes(:namespace) respond_to do |format| format.html @@ -60,7 +61,23 @@ def edit end def projects - @projects = @group.projects.page(params[:page]) + no_projects = ProjectsFinder.new.execute(current_user, group: group).empty? + redirect_to(group_path(@group)) if no_projects + + @all_projects = @projects + + if current_user + # @projects are the scoped project, it can reference @all_projects is the + # current scope is 'all' or no scope matches. + @projects = case params[:scope] + when 'contributed' + @contributed_projects + when 'starred' + @starred_projects + else + @all_projects + end + end end def update @@ -84,7 +101,26 @@ def group end def load_projects - @projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity.non_archived + @projects ||= refine_projects( + ProjectsFinder.new.execute(current_user, group: group) + ) + end + + def load_contributed_projects + return unless current_user + + @contributed_projects ||= refine_projects( + ContributedProjectsFinder.new(current_user).execute(current_user). + in_group_namespace.in_namespace(@group.id) + ).reject(&:forked?) + end + + def load_starred_projects + return unless current_user + + @starred_projects ||= refine_projects( + current_user.starred_projects.in_group_namespace.in_namespace(@group.id) + ) end def project_ids @@ -111,7 +147,7 @@ def authorize_create_group! def determine_layout if [:new, :create].include?(action_name.to_sym) 'application' - elsif [:edit, :update, :projects].include?(action_name.to_sym) + elsif [:edit, :update].include?(action_name.to_sym) 'group_settings' else 'group' diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 241179b0212b..3c560db78253 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -1,7 +1,8 @@ module SortingHelper def sort_options_hash { - sort_value_name => sort_title_name, + sort_value_name_asc => sort_title_name_asc, + sort_value_name_desc => sort_title_name_desc, sort_value_recently_updated => sort_title_recently_updated, sort_value_oldest_updated => sort_title_oldest_updated, sort_value_recently_created => sort_title_recently_created, @@ -11,6 +12,8 @@ def sort_options_hash sort_value_largest_repo => sort_title_largest_repo, sort_value_recently_signin => sort_title_recently_signin, sort_value_oldest_signin => sort_title_oldest_signin, + sort_value_recently_active => sort_title_recently_active, + sort_value_stars => sort_title_stars } end @@ -42,6 +45,14 @@ def sort_title_name 'Name' end + def sort_title_name_asc + 'Name from A to Z' + end + + def sort_title_name_desc + 'Name from Z to A' + end + def sort_title_largest_repo 'Largest repository' end @@ -54,6 +65,14 @@ def sort_title_oldest_signin 'Oldest sign in' end + def sort_title_recently_active + 'Recently active' + end + + def sort_title_stars + 'Most stars' + end + def sort_value_oldest_updated 'updated_asc' end @@ -78,10 +97,14 @@ def sort_value_milestone_later 'milestone_due_desc' end - def sort_value_name + def sort_value_name_asc 'name_asc' end + def sort_value_name_desc + 'name_desc' + end + def sort_value_largest_repo 'repository_size_desc' end @@ -93,4 +116,45 @@ def sort_value_recently_signin def sort_value_oldest_signin 'oldest_sign_in' end + + def sort_value_recently_active + 'recently_active' + end + + def sort_value_stars + 'stars' + end + + def link_to_sort(label, sort_method, current_sort) + link_to_sort_or_filter(label, keyword: 'sort', value: sort_method, current_value: current_sort) + end + + def link_to_filter(label, filter_method, current_filter) + link_to_sort_or_filter(label, keyword: 'filter', value: filter_method, current_value: current_filter) + end + + private + + def link_to_sort_or_filter(label, keyword:, value:, current_value: nil) + label ||= value.to_s.humanize + active = currently_active_sort_or_filter?(value, current_value) + + active_class = active ? 'active' : nil + + url_params = params.reject { |k, v| k == keyword && v == value } + + link_to(url_for(url_params.merge(keyword => value)), class: active_class) do + if active + content_tag(:span) do + content_tag(:i, nil, class: %w[fa fa-check]).concat(content_tag(:strong, label, class: 'item-title')) + end + else + label + end + end + end + + def currently_active_sort_or_filter?(value, current_value) + current_value == value + end end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 04e53fe7c610..59a6089e246b 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -59,7 +59,7 @@ def nav_link(options = {}, &block) end def active_nav_link?(options) - if path = options.delete(:path) + active = if path = options.delete(:path) unless path.respond_to?(:each) path = [path] end @@ -87,6 +87,12 @@ def active_nav_link?(options) current_controller?(*c) || current_action?(*a) end end + + if query = options.delete(:query) + active &&= query.all? { |k,v| params[k] == v } + end + + active end def current_path?(path) diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index 8b47b9e0abd2..6be07e0c4352 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -16,13 +16,27 @@ module Sortable scope :order_created_asc, -> { reorder(created_at: :asc) } scope :order_updated_desc, -> { reorder(updated_at: :desc) } scope :order_updated_asc, -> { reorder(updated_at: :asc) } - scope :order_name_asc, -> { reorder(name: :asc) } - scope :order_name_desc, -> { reorder(name: :desc) } + scope :order_name_asc, -> { reorder("LOWER(#{quoted_table_name}.#{connection.quote_column_name('name')}) ASC") } + scope :order_name_desc, -> { reorder("LOWER(#{quoted_table_name}.#{connection.quote_column_name('name')}) DESC") } end module ClassMethods + # Adds a new sort method. + def sortable_by(sort_name, scope_name) + sortables[sort_name.to_s] = scope_name + end + + # Accessor for sort methods marked mentionable. + def sortables + @sortables ||= {} + end + def order_by(method) - case method.to_s + return all if method.blank? + + method = method.to_s + + case method when 'name_asc' then order_name_asc when 'name_desc' then order_name_desc when 'updated_asc' then order_updated_asc @@ -32,7 +46,11 @@ def order_by(method) when 'id_desc' then order_id_desc when 'id_asc' then order_id_asc else - all + if sortables[method].present? && respond_to?(sortables[method]) + send(sortables[method]) + else + all + end end end end diff --git a/app/models/project.rb b/app/models/project.rb index 4bd51449c250..b356edd5b1aa 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -204,6 +204,10 @@ def update_forks_visibility_level scope :sorted_by_stars, -> { reorder('projects.star_count DESC') } scope :sorted_by_names, -> { joins(:namespace).reorder('namespaces.name ASC, projects.name ASC') } + # Custom sorting methods + sortable_by 'recently_active', :sorted_by_activity + sortable_by 'stars', :sorted_by_stars + scope :without_user, ->(user) { where('projects.id NOT IN (:ids)', ids: user.authorized_projects.map(&:id) ) } scope :without_team, ->(team) { team.projects.present? ? where('projects.id NOT IN (:ids)', ids: team.projects.map(&:id)) : scoped } scope :not_in_group, ->(group) { where('projects.id NOT IN (:ids)', ids: group.project_ids ) } diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index b050a4d01c33..6507ad6f8b71 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -40,7 +40,7 @@ %b.caret %ul.dropdown-menu %li - = link_to admin_users_path(sort: sort_value_name, filter: params[:filter]) do + = link_to admin_users_path(sort: sort_value_name_asc, 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 diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index 5c4b58cd6887..4dd740a0e890 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -1,20 +1,16 @@ = content_for :flash_message do = render 'shared/project_limit' -.top-area + +.nav-block %ul.nav-links = nav_link(page: [dashboard_projects_path, root_path]) do - = link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do - Your Projects + = link_to dashboard_projects_path(filter: @filter, sort: @sort), title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do + = "Your Projects (#{@user_projects.size})" = nav_link(page: starred_dashboard_projects_path) do - = link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do - Starred Projects + = link_to starred_dashboard_projects_path(filter: @filter, sort: @sort), title: 'Starred Projects', data: {placement: 'right'} do + = "Starred Projects (#{@starred_projects.size})" = nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path], html_options: { class: 'hidden-xs' }) do = link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do Explore Projects - .projects-search-form - = search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control hidden-xs', spellcheck: false - - if current_user.can_create_project? - = link_to new_project_path, class: 'btn btn-green' do - %i.fa.fa-plus - New Project + = render 'shared/projects/controls' diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml index cea9ffcc748a..4baf43bd7c56 100644 --- a/app/views/dashboard/projects/_projects.html.haml +++ b/app/views/dashboard/projects/_projects.html.haml @@ -1,3 +1,2 @@ .projects-list-holder - - = render 'shared/projects/list', projects: @projects, ci: true + = render 'shared/projects/list', projects: projects, ci: true diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml index 53abf274bdbf..befe5aa0ca49 100644 --- a/app/views/dashboard/projects/index.html.haml +++ b/app/views/dashboard/projects/index.html.haml @@ -11,6 +11,6 @@ = render "events/event_last_push", event: @last_push - if @projects.any? - = render 'projects' + = render 'projects', projects: @projects - else = render "zero_authorized_projects" diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml index 70705923d42b..a43ee497efb9 100644 --- a/app/views/dashboard/projects/starred.html.haml +++ b/app/views/dashboard/projects/starred.html.haml @@ -7,7 +7,7 @@ = render "events/event_last_push", event: @last_push - if @projects.any? - = render 'projects' + = render 'projects', projects: @projects - else %h3 You don't have starred projects yet %p.slead Visit project page and press on star icon and it will appear on this page. diff --git a/app/views/explore/projects/_dropdown.html.haml b/app/views/explore/projects/_dropdown.html.haml index b23a3c1e5c17..6796d37532b9 100644 --- a/app/views/explore/projects/_dropdown.html.haml +++ b/app/views/explore/projects/_dropdown.html.haml @@ -1,7 +1,7 @@ .dropdown.inline %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %span.light sort: - - if @sort.present? + - if sort_options_hash[@sort].present? = sort_options_hash[@sort] - elsif current_page?(trending_explore_projects_path) || current_page?(explore_root_path) Trending projects @@ -24,4 +24,3 @@ = sort_title_recently_updated = link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do = sort_title_oldest_updated - diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml index 95d46e331f8a..3ad7f0e2653e 100644 --- a/app/views/explore/projects/starred.html.haml +++ b/app/views/explore/projects/starred.html.haml @@ -9,9 +9,9 @@ .explore-trending-block .gray-content-block.second-block .pull-right - = render 'explore/projects/dropdown' + = render 'dropdown' .oneline %i.fa.fa-star See most starred projects - = render 'projects', projects: @starred_projects - = paginate @starred_projects, theme: 'gitlab' + = render 'projects', projects: @projects + = paginate @projects, theme: 'gitlab' diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml index fa0b718e48b7..cf3860f5a0a1 100644 --- a/app/views/explore/projects/trending.html.haml +++ b/app/views/explore/projects/trending.html.haml @@ -9,8 +9,8 @@ .explore-trending-block .gray-content-block.second-block .pull-right - = render 'explore/projects/dropdown' + = render 'dropdown' .oneline %i.fa.fa-comments-o See most discussed projects for last month - = render 'projects', projects: @trending_projects + = render 'projects', projects: @projects diff --git a/app/views/groups/_header.html.haml b/app/views/groups/_header.html.haml new file mode 100644 index 000000000000..f36cb780d03d --- /dev/null +++ b/app/views/groups/_header.html.haml @@ -0,0 +1,34 @@ +- @no_container = true + +- unless can?(current_user, :read_group, @group) + - @disable_search_panel = true + += content_for :meta_tags do + - if current_user + = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") + +.cover-block + .cover-controls + - if @group && can?(current_user, :admin_group, @group) + = link_to icon('pencil'), edit_group_path(@group), class: 'btn' + - if current_user + = link_to icon('rss'), group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn rss-btn' + + .avatar-holder + = link_to group_icon(@group), target: '_blank' do + = image_tag group_icon(@group), class: "avatar group-avatar s90" + .cover-title + = @group.name + + .cover-desc.username + @#{@group.path} + + - if @group.description.present? + .cover-desc.description + = markdown(@group.description, pipeline: :description) + + %ul.nav-links + = nav_link(page: group_path(@group)) do + = link_to 'Activity', group_path(@group) + = nav_link(page: projects_group_path(@group)) do + = link_to 'Projects', projects_group_path(@group, scope: params[:scope], filter: @filter, sort: @sort) diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index bbafc08435af..537c98508df7 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -1,11 +1,2 @@ .projects-list-holder - .projects-search-form - .input-group - = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false - - if can? current_user, :create_projects, @group - %span.input-group-btn - = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-green' do - %i.fa.fa-plus - New Project - - = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false, skip_namespace: true + = render 'shared/projects/list', projects: projects, projects_limit: 20, skip_namespace: true diff --git a/app/views/groups/_projects_head.html.haml b/app/views/groups/_projects_head.html.haml new file mode 100644 index 000000000000..c22cde483293 --- /dev/null +++ b/app/views/groups/_projects_head.html.haml @@ -0,0 +1,15 @@ +.nav-block + %ul.nav-links + = nav_link(page: projects_group_path, query: { scope: nil }) do + = link_to projects_group_path(@group, filter: @filter, sort: @sort), title: "All projects from the '#{@group.name}' group", class: 'shortcuts-activity', data: {placement: 'right'} do + = "All (#{@all_projects.size})" + + - if current_user + = nav_link(page: projects_group_path, query: { scope: 'contributed' }) do + = link_to projects_group_path(@group, scope: 'contributed', filter: @filter, sort: @sort), title: "Projects from the '#{@group.name}' group you contributed to", class: 'shortcuts-activity', data: {placement: 'right'} do + = "Contributed (#{@contributed_projects.size})" + = nav_link(page: projects_group_path, query: { scope: 'starred' }) do + = link_to projects_group_path(@group, scope: 'starred', filter: @filter, sort: @sort), title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do + = "Starred (#{@starred_projects.size})" + + = render 'shared/projects/controls' diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index e2f97fd93375..4ad8e7e87463 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -1,3 +1,4 @@ +- page_title "Settings" - header_title group_title(@group, "Settings", edit_group_path(@group)) - @blank_container = true diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 9ca11ed11773..1136c3c479b8 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -1,31 +1,11 @@ -- page_title "Projects" -- header_title group_title(@group, "Projects", projects_group_path(@group)) - -.panel.panel-default.prepend-top-default - .panel-heading - %strong= @group.name - projects: - - if can? current_user, :admin_group, @group - .panel-head-actions - = link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do - %i.fa.fa-plus - New Project - %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) - %strong= link_to project.name_with_namespace, project - .pull-right - - if project.archived - %span.label.label-warning archived - %span.label.label-gray - = repository_size(project) - = link_to 'Members', namespace_project_project_members_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" - = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" - = link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-sm btn-remove" - - if @projects.blank? - .nothing-here-block This group has no projects yet - -= paginate @projects, theme: "gitlab" += render 'header' + +%div{class: container_class} + .tab-content + .tab-pane.active + - if can?(current_user, :read_group, @group) + = render 'projects_head' + = render 'projects', projects: @projects + - else + %p.nav-links.no-top + No projects to show diff --git a/app/views/groups/projects/edit.html.haml b/app/views/groups/projects/edit.html.haml new file mode 100644 index 000000000000..ca5568dde206 --- /dev/null +++ b/app/views/groups/projects/edit.html.haml @@ -0,0 +1,31 @@ +- page_title "Projects" +- header_title group_title(@group, "Projects", edit_group_projects_path(@group)) + +.panel.panel-default.prepend-top-default + .panel-heading + %strong= @group.name + projects: + - if can? current_user, :admin_group, @group + .panel-head-actions + = link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do + %i.fa.fa-plus + New Project + %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) + %strong= link_to project.name_with_namespace, project + .pull-right + - if project.archived + %span.label.label-warning archived + %span.label.label-gray + = repository_size(project) + = link_to 'Members', namespace_project_project_members_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" + = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" + = link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-sm btn-remove" + - if @projects.blank? + .nothing-here-block This group has no projects yet + += paginate @projects, theme: "gitlab" diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index ebb3df7dca38..503c4a9e1f37 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,58 +1,15 @@ -- @no_container = true += render 'header' -- unless can?(current_user, :read_group, @group) - - @disable_search_panel = true - -= content_for :meta_tags do - - if current_user - = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") - -.cover-block - .cover-controls - - if @group && can?(current_user, :admin_group, @group) - = link_to icon('pencil'), edit_group_path(@group), class: 'btn' - - if current_user - = link_to icon('rss'), group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn rss-btn' - - .avatar-holder - = link_to group_icon(@group), target: '_blank' do - = image_tag group_icon(@group), class: "avatar group-avatar s90" - .cover-title - = @group.name - - .cover-desc.username - @#{@group.path} - - - if @group.description.present? - .cover-desc.description - = markdown(@group.description, pipeline: :description) - - - %ul.nav-links - %li.active - = link_to "#activity", 'data-toggle' => 'tab' do - Activity - - if @projects.present? - %li - = link_to "#projects", 'data-toggle' => 'tab' do - Projects - -- if can?(current_user, :read_group, @group) - %div{ class: container_class } - .tab-content - .tab-pane.active#activity +%div{class: container_class} + .tab-content + .tab-pane.active + - if can?(current_user, :read_group, @group) .activity-filter-block - if current_user = render "events/event_last_push", event: @last_push - = render 'shared/event_filter' .content_list = spinner - - .tab-pane#projects - = render "projects", projects: @projects - -- else - %p.nav-links.no-top - No projects to show + - else + %p.nav-links.no-top No activity to show diff --git a/app/views/layouts/group_settings.html.haml b/app/views/layouts/group_settings.html.haml index a1a1fc2f8584..d3032af47ef8 100644 --- a/app/views/layouts/group_settings.html.haml +++ b/app/views/layouts/group_settings.html.haml @@ -1,5 +1,3 @@ -- page_title "Settings" -- header_title group_title(@group, "Settings", edit_group_path(@group)) -- sidebar "group_settings" +- sidebar "group_settings" = render template: "layouts/group" diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml index 56a92fe9103b..9ba47ed81100 100644 --- a/app/views/layouts/nav/_group_settings.html.haml +++ b/app/views/layouts/nav/_group_settings.html.haml @@ -13,8 +13,8 @@ = icon ('pencil-square-o fw') %span Group Settings - = nav_link(path: 'groups#projects') do - = link_to projects_group_path(@group), title: 'Projects' do + = nav_link(path: 'groups/projects#edit') do + = link_to edit_group_projects_path(@group), title: 'Projects' do = icon('folder fw') %span Projects diff --git a/app/views/shared/projects/_controls.html.haml b/app/views/shared/projects/_controls.html.haml new file mode 100644 index 000000000000..f196ca92909a --- /dev/null +++ b/app/views/shared/projects/_controls.html.haml @@ -0,0 +1,12 @@ +.controls.projects-controls + .form-inline.pull-right + .form-group= search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control hidden-xs', spellcheck: false + + - unless current_controller?('explore/projects') + .form-group.prepend-left-10= render 'shared/projects/dropdown' + + - if current_user && current_user.can_create_project? + .form-group.prepend-left-10 + = link_to new_project_path, class: 'btn btn-green' do + %i.fa.fa-plus + New Project diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml new file mode 100644 index 000000000000..aff727b37d39 --- /dev/null +++ b/app/views/shared/projects/_dropdown.html.haml @@ -0,0 +1,18 @@ +.dropdown.inline + %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} + %span.light + = sort_options_hash[@sort] + %b.caret + %ul.dropdown-menu.dropdown-menu-align-right + %li.dropdown-label Sort by + %li + = link_to_sort sort_title_recently_active, sort_value_recently_active, @sort + = link_to_sort sort_title_stars, sort_value_stars, @sort + = link_to_sort sort_title_name_asc, sort_value_name_asc, @sort + = link_to_sort sort_title_name_desc, sort_value_name_desc, @sort + + - if current_user && !current_controller?(:groups) + %hr + %li + = link_to_filter 'Owned by anyone', 'all', @filter + = link_to_filter 'Owned by me', 'personal', @filter diff --git a/config/routes.rb b/config/routes.rb index 75418db8d258..66aec7e3c36b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -350,6 +350,7 @@ end scope module: :groups do + resource :projects, only: [:edit] resources :group_members, only: [:index, :create, :update, :destroy] do post :resend_invite, on: :member delete :leave, on: :collection diff --git a/features/dashboard/projects.feature b/features/dashboard/projects.feature new file mode 100644 index 000000000000..e00c495338d2 --- /dev/null +++ b/features/dashboard/projects.feature @@ -0,0 +1,61 @@ +@dashboard +Feature: Dashboard Projects + Background: + Given I sign in as a user + And public project "Community" + And I am member of a project "Community" with a guest role + And I starred project "Community" + And I own project "Forum" + And I starred project "Forum" + And I own project "Shop" + And project "Shop" has push event + And project "Community" has push event + And "John Doe" someone starred project "Community" + And I visit dashboard projects page + + Scenario: I should see projects list + Then I should see "Community" project link + Then I should see "Forum" project link + Then I should see "Shop" project link + + Scenario: I sort projects by recent activity + And I sort projects list by "Recently active" + Then I should see "Community" at the top + + Scenario: I sort projects by most stars + And I sort projects list by "Most stars" + Then I should see "Community" at the top + + Scenario: I sort projects by name from A to Z + And I sort projects list by "Name from A to Z" + Then I should see "Community" at the top + + Scenario: I sort projects by name from Z to A + And I sort projects list by "Name from Z to A" + Then I should see "Shop" at the top + + Scenario: I filter to see only my own projects, I should see projects list + And I filter to see only my own projects + Then I should not see "Community" project link + Then I should see "Forum" project link + Then I should see "Shop" project link + + Scenario: I filter to see only my own projects, I sort projects by recent activity + And I filter to see only my own projects + And I sort projects list by "Recently active" + Then I should see "Shop" at the top + + Scenario: I filter to see only my own projects, I sort projects by most stars + And I filter to see only my own projects + And I sort projects list by "Most stars" + Then I should see "Forum" at the top + + Scenario: I filter to see only my own projects, I sort projects by name from A to Z + And I filter to see only my own projects + And I sort projects list by "Name from A to Z" + Then I should see "Forum" at the top + + Scenario: I filter to see only my own projects, I sort projects by name from Z to A + And I filter to see only my own projects + And I sort projects list by "Name from Z to A" + Then I should see "Shop" at the top diff --git a/features/dashboard/starred_projects.feature b/features/dashboard/starred_projects.feature index 9dfd2fbab9cb..99c94c166e7a 100644 --- a/features/dashboard/starred_projects.feature +++ b/features/dashboard/starred_projects.feature @@ -3,10 +3,63 @@ Feature: Dashboard Starred Projects Background: Given I sign in as a user And public project "Community" + And I am member of a project "Community" with a guest role And I starred project "Community" + And I own project "Forum" + And I starred project "Forum" And I own project "Shop" + And I own project "Grocery" + And I starred project "Grocery" + And project "Grocery" has push event + And project "Community" has push event + And "John Doe" someone starred project "Community" + And "John Doe" someone starred project "Forum" And I visit dashboard starred projects page Scenario: I should see projects list - Then I should see project "Community" - And I should not see project "Shop" + Then I should see "Community" project link + Then I should see "Forum" project link + Then I should see "Grocery" project link + Then I should not see "Shop" project link + + Scenario: I sort projects by recent activity + And I sort projects list by "Recently active" + Then I should see "Community" at the top + + Scenario: I sort projects by most stars + And I sort projects list by "Most stars" + Then I should see "Community" at the top + + Scenario: I sort projects by name from A to Z + And I sort projects list by "Name from A to Z" + Then I should see "Community" at the top + + Scenario: I sort projects by name from Z to A + And I sort projects list by "Name from Z to A" + Then I should see "Grocery" at the top + + Scenario: I filter to see only my own projects, I should see projects list + And I filter to see only my own projects + Then I should not see "Community" project link + Then I should see "Forum" project link + Then I should not see "Shop" project link + + Scenario: I filter to see only my own projects, I sort projects by recent activity + And I filter to see only my own projects + And I sort projects list by "Recently active" + Then I should see "Grocery" at the top + + Scenario: I filter to see only my own projects, I sort projects by most stars + And I filter to see only my own projects + And I sort projects list by "Most stars" + Then I should see "Forum" at the top + + Scenario: I filter to see only my own projects, I sort projects by name from A to Z + And I filter to see only my own projects + And I sort projects list by "Name from A to Z" + Then I should see "Forum" at the top + + Scenario: I filter to see only my own projects, I sort projects by name from Z to A + And I filter to see only my own projects + And I sort projects list by "Name from Z to A" + Then I should see "Grocery" at the top diff --git a/features/groups.feature b/features/groups.feature index c803e9529803..4efff242a956 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -2,17 +2,45 @@ Feature: Groups Background: Given I sign in as "John Doe" And "John Doe" is owner of group "Owned" + And Group "Owned" has a public project "Public-project" + And Group "Owned" has a public project "Star-project" with 2 stars including 1 from "John Doe" + And Group "Owned" has an internal project "Moon-project" with 1 star from "John Doe" + And project "Moon-project" has push event Scenario: I should have back to group button When I visit group "Owned" page Then I should see back to dashboard button - @javascript + @javascript @wip Scenario: I should see group "Owned" dashboard list When I visit group "Owned" page - Then I should see group "Owned" projects list And I should see projects activity feed + # Projects + Scenario: I sort projects by recent activity + When I visit group "Owned" projects page + Then I should see group "Owned" projects list + + Scenario: I sort projects by recent activity + When I visit group "Owned" projects page + And I sort projects list by "Recently active" + Then I should see "Moon-project" at the top + + Scenario: I sort projects by most stars + When I visit group "Owned" projects page + And I sort projects list by "Most stars" + Then I should see "Star-project" at the top + + Scenario: I sort projects by name from A to Z + When I visit group "Owned" projects page + And I sort projects list by "Name from A to Z" + Then I should see "Moon-project" at the top + + Scenario: I sort projects by name from Z to A + When I visit group "Owned" projects page + And I sort projects list by "Name from Z to A" + Then I should see "Star-project" at the top + Scenario: I should see group "Owned" issues list Given project from group "Owned" has issues assigned to me When I visit group "Owned" issues page @@ -46,7 +74,7 @@ Feature: Groups # Group projects in settings Scenario: I should see all projects in the project list in settings Given Group "Owned" has archived project - When I visit group "Owned" projects page + When I visit group "Owned" projects edit page Then I should see group "Owned" projects list And I should see "archived" label @@ -55,8 +83,6 @@ Feature: Groups Scenario: Signed out user should see group Given "Mary Jane" is owner of group "Owned" And I am a signed out user - And Group "Owned" has a public project "Public-project" - When I visit group "Owned" page + When I visit group "Owned" projects page Then I should see group "Owned" Then I should see project "Public-project" - diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb index 63f0ec2b6e86..dea367a67a90 100644 --- a/features/steps/dashboard/dashboard.rb +++ b/features/steps/dashboard/dashboard.rb @@ -3,18 +3,6 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps include SharedPaths include SharedProject - step 'I should see "New Project" link' do - expect(page).to have_link "New project" - end - - step 'I should see "Shop" project link' do - expect(page).to have_link "Shop" - end - - step 'I should see "Shop" project CI status' do - expect(page).to have_link "Build skipped" - end - step 'I should see last push widget' do expect(page).to have_content "You pushed to fix" expect(page).to have_link "Create Merge Request" diff --git a/features/steps/dashboard/projects.rb b/features/steps/dashboard/projects.rb new file mode 100644 index 000000000000..4ad1afb070f0 --- /dev/null +++ b/features/steps/dashboard/projects.rb @@ -0,0 +1,5 @@ +class Spinach::Features::DashboardProjects < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedProject +end diff --git a/features/steps/dashboard/starred_projects.rb b/features/steps/dashboard/starred_projects.rb index c33813e550b8..dd9e371173c6 100644 --- a/features/steps/dashboard/starred_projects.rb +++ b/features/steps/dashboard/starred_projects.rb @@ -2,14 +2,4 @@ class Spinach::Features::DashboardStarredProjects < Spinach::FeatureSteps include SharedAuthentication include SharedPaths include SharedProject - - step 'I starred project "Community"' do - current_user.toggle_star(Project.find_by(name: 'Community')) - end - - step 'I should not see project "Shop"' do - page.within '.projects-list' do - expect(page).not_to have_content('Shop') - end - end end diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 4c5122d1b7d5..0bee5eca7878 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -2,6 +2,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps include SharedAuthentication include SharedPaths include SharedGroup + include SharedProject include SharedUser step 'I should see back to dashboard button' do @@ -19,9 +20,34 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'Group "Owned" has a public project "Public-project"' do group = owned_group - @project = create :empty_project, :public, + @project = Project.find_by(name: "Public-project") || create(:empty_project, :public, group: group, - name: "Public-project" + name: "Public-project") + end + + step 'Group "Owned" has a public project "Star-project" with 2 stars including 1 from "John Doe"' do + group = owned_group + + @project = Project.find_by(name: 'Star-project') || create(:project, :public, + group: group, + name: 'Star-project', + path: 'star_project') + user_exists('John Doe').toggle_star(@project) + user_exists('Mary Jane').toggle_star(@project) + end + + step 'Group "Owned" has an internal project "Moon-project" with 1 star from "John Doe"' do + group = owned_group + + @project = Project.find_by(name: 'Moon-project') || create(:project, :internal, + group: group, + name: 'Moon-project', + path: 'moon_project') + user_exists('John Doe').toggle_star(@project) + end + + step 'project "Moon-project" has push event' do + event_for_project(Project.find_by(name: "Moon-project"), user_exists('John Doe')) end step 'I should see project "Public-project"' do @@ -120,6 +146,18 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived') end + # ---------------------------------------- + # Sorting + # ---------------------------------------- + + step 'I should see "Star-project" at the top' do + expect_top_project_in_list("Star-project") + end + + step 'I should see "Moon-project" at the top' do + expect_top_project_in_list("Moon-project") + end + private def assigned_to_me(key) diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 4264c9c6f1a0..da54707bdce3 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -47,6 +47,10 @@ module SharedPaths visit projects_group_path(Group.find_by(name: "Owned")) end + step 'I visit group "Owned" projects edit page' do + visit edit_group_projects_path(Group.find_by(name: "Owned")) + end + step 'I visit group "Guest" page' do visit group_path(Group.find_by(name: "Guest")) end @@ -80,7 +84,7 @@ module SharedPaths end step 'I visit dashboard projects page' do - visit projects_dashboard_path + visit dashboard_projects_path end step 'I visit dashboard issues page' do diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index d9c75d122382..64a59373b900 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -1,10 +1,17 @@ +require_relative 'user' + module SharedProject include Spinach::DSL + include SharedUser # Create a project without caring about what it's called step "I own a project" do - @project = create(:project, namespace: @user.namespace) - @project.team << [@user, :master] + @project = user_owns_project( + user: @user, + project_name: 'Internal', + project_type: :project, + visibility: :internal + ) end step "project exists in some group namespace" do @@ -14,9 +21,13 @@ module SharedProject # Create a specific project called "Shop" step 'I own project "Shop"' do - @project = Project.find_by(name: "Shop") - @project ||= create(:project, name: "Shop", namespace: @user.namespace, snippets_enabled: true) - @project.team << [@user, :master] + @project = user_owns_project( + user: @user, + project_name: 'Shop', + project_type: :project, + visibility: :internal, + snippets_enabled: true + ) end step 'I disable snippets in project' do @@ -39,16 +50,28 @@ module SharedProject # Create another specific project called "Forum" step 'I own project "Forum"' do - @project = Project.find_by(name: "Forum") - @project ||= create(:project, name: "Forum", namespace: @user.namespace, path: 'forum_project') - @project.team << [@user, :master] + @project = user_owns_project( + user: @user, + project_name: 'Forum', + project_type: :project, + path: 'forum_project' + ) + end + + # Create another specific project called "Forum" + step 'I own project "Grocery"' do + @project = user_owns_project( + user: @user, + project_name: 'Grocery' + ) end # Create an empty project without caring about the name step 'I own an empty project' do - @project = create(:empty_project, - name: 'Empty Project', namespace: @user.namespace) - @project.team << [@user, :master] + @project = user_owns_project( + user: @user, + project_name: 'Empty Project' + ) end step 'I visit my empty project page' do @@ -63,28 +86,17 @@ module SharedProject step 'project "Shop" has push event' do @project = Project.find_by(name: "Shop") + event_for_project(@project) + end - data = { - before: Gitlab::Git::BLANK_SHA, - after: "6d394385cf567f80a8fd85055db1ab4c5295806f", - ref: "refs/heads/fix", - user_id: @user.id, - user_name: @user.name, - repository: { - name: @project.name, - url: "localhost/rubinius", - description: "", - homepage: "localhost/rubinius", - private: true - } - } + step 'project "Grocery" has push event' do + @project = Project.find_by(name: "Grocery") + event_for_project(@project) + end - @event = Event.create( - project: @project, - action: Event::PUSHED, - data: data, - author_id: @user.id - ) + step 'project "Community" has push event' do + @project = Project.find_by(name: "Community") + event_for_project(@project) end step 'I should see project "Shop" activity feed' do @@ -110,6 +122,10 @@ def current_project @project.team << [@user, Gitlab::Access::GUEST] end + step 'I am member of a project "Community" with a guest role' do + Project.find_by(name: "Community").team << [@user, Gitlab::Access::GUEST] + end + step 'I am member of a project with a reporter role' do @project.team << [@user, Gitlab::Access::REPORTER] end @@ -240,10 +256,162 @@ def current_project end end - def user_owns_project(user_name:, project_name:, visibility: :private) - user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore) + # ---------------------------------------- + # Starring + # ---------------------------------------- + + step 'I starred project "Community"' do + current_user.toggle_star(Project.find_by(name: 'Community')) + end + + step 'I starred project "Forum"' do + current_user.toggle_star(Project.find_by(name: 'Forum')) + end + + step 'I starred project "Grocery"' do + current_user.toggle_star(Project.find_by(name: 'Grocery')) + end + + step '"John Doe" someone starred project "Community"' do + user_exists("John Doe").toggle_star(Project.find_by(name: 'Community')) + end + + step '"John Doe" someone starred project "Forum"' do + user_exists("John Doe").toggle_star(Project.find_by(name: 'Community')) + end + + # ---------------------------------------- + # Sorting + # ---------------------------------------- + + step 'I sort projects list by "Recently active"' do + sort_by('Recently active') + end + + step 'I sort projects list by "Most stars"' do + sort_by('Most stars') + end + + step 'I sort projects list by "Name from A to Z"' do + sort_by('Name from A to Z') + end + + step 'I sort projects list by "Name from Z to A"' do + sort_by('Name from Z to A') + end + + step 'I should see "Community" at the top' do + expect_top_project_in_list("Community") + end + + step 'I should see "Shop" at the top' do + expect_top_project_in_list("Shop") + end + + step 'I should see "Forum" at the top' do + expect_top_project_in_list("Forum") + end + + step 'I should see "Grocery" at the top' do + expect_top_project_in_list("Grocery") + end + + # ---------------------------------------- + # Filtering + # ---------------------------------------- + + step 'I filter to see only my own projects' do + filter_by('Owned by me') + end + + # ---------------------------------------- + # Links + # ---------------------------------------- + + step 'I should see "New Project" link' do + expect(page).to have_link "New project" + end + + step 'I should see "Community" project link' do + expect_link_in_list "Community" + end + + step 'I should not see "Community" project link' do + expect_link_in_list "Community", false + end + + step 'I should see "Shop" project link' do + expect_link_in_list "Shop" + end + + step 'I should not see "Shop" project link' do + expect_link_in_list "Shop", false + end + + step 'I should see "Forum" project link' do + expect_link_in_list "Forum" + end + + step 'I should see "Grocery" project link' do + expect_link_in_list "Grocery" + end + + step 'I should see "Shop" project CI status' do + expect_link_in_list "Build skipped" + end + + private + + def event_for_project(project, user) + data = { + before: Gitlab::Git::BLANK_SHA, + after: "6d394385cf567f80a8fd85055db1ab4c5295806f", + ref: "refs/heads/fix", + user_id: (user || @user).id, + user_name: (user || @user).name, + repository: { + name: project.name, + url: "localhost/rubinius", + description: "", + homepage: "localhost/rubinius", + private: true + } + } + + @event = Event.create( + project: project, + action: Event::PUSHED, + data: data, + author_id: (user || @user).id + ) + end + + def sort_by(sort) + find('button.dropdown-toggle.btn').click + page.within('ul.dropdown-menu') do + click_link sort + end + end + + def filter_by(filter) + find('button.dropdown-toggle.btn').click + page.within('ul.dropdown-menu') do + click_link filter + end + end + + def expect_top_project_in_list(project_name) + expect(page.find('ul.projects-list li.project-row:first-child')).to have_content(project_name) + end + + def expect_link_in_list(project_name, truthy = true) + expect(page.find('ul.projects-list')).send (truthy ? 'to' : 'not_to'), have_content(project_name) + end + + def user_owns_project(user:, project_name: nil, project_type: :empty_project, visibility: :private, **args) + user = user.is_a?(User) ? user : user_exists(user, username: user.gsub(/\s/, '').underscore) project = Project.find_by(name: project_name) - project ||= create(:empty_project, visibility, name: project_name, namespace: user.namespace) + project ||= create(project_type, visibility, name: project_name, namespace: user.namespace, **args) project.team << [user, :master] end end diff --git a/spec/helpers/sorting_helper_spec.rb b/spec/helpers/sorting_helper_spec.rb new file mode 100644 index 000000000000..f74c8025fa17 --- /dev/null +++ b/spec/helpers/sorting_helper_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe SortingHelper do + + describe 'link_to_sort' do + it 'it returns a simple link tag when sort is not the current one' do + stub_url_for('sort', 'bar') + expect(helper.link_to_sort('Foo', 'bar', 'baz')).to eq <<-LINK.strip_heredoc.strip + Foo + LINK + end + + it 'it returns an active link tag when sort is the current one' do + stub_url_for('sort', 'bar') + expect(helper.link_to_sort('Foo', 'bar', 'bar')).to eq <<-LINK.strip_heredoc.strip + Foo + LINK + end + end + + describe 'link_to_filter' do + it 'it returns a simple link tag when sort is not the current one' do + stub_url_for('sort', 'bar') + expect(helper.link_to_filter('Foo', 'bar', 'baz')).to eq <<-LINK.strip_heredoc.strip + Foo + LINK + end + + it 'it returns an active link tag when sort is the current one' do + stub_url_for('sort', 'bar') + expect(helper.link_to_filter('Foo', 'bar', 'bar')).to eq <<-LINK.strip_heredoc.strip + Foo + LINK + end + end + + private + + def stub_url_for(key, value) + url = "/dashboard/projects" + url << "?#{key}=#{value}" + allow(helper).to receive(:url_for).and_return(url) + end + +end diff --git a/spec/models/concerns/sortable_spec.rb b/spec/models/concerns/sortable_spec.rb new file mode 100644 index 000000000000..f7c238c2364e --- /dev/null +++ b/spec/models/concerns/sortable_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe Sortable do + + class SortableModel + def self.default_scope; end + def self.scope(name, block) + define_singleton_method(name, &block) + end + def self.all + RECORDS + end + + include Sortable + + sortable_by 'stars', :sort_by_stars + + RECORDS = [ + OpenStruct.new(title: 'Awesome', id: 1, stars: 1), + OpenStruct.new(title: 'Amazing', id: 2, stars: 2), + OpenStruct.new(title: 'Incredible', id: 3, stars: 0), + ] + + scope :sort_by_stars, ->{ RECORDS.sort_by(&:stars).reverse } + end + + describe :order_by do + %w[id created_at updated_at].each do |attr| + %i[asc desc].each do |way| + it "can be sorted #{attr.sub('_at', '')}_#{way}" do + expect(SortableModel).to receive(:reorder).with(attr.to_sym => way) + + SortableModel.order_by("#{attr.sub('_at', '')}_#{way}") + end + end + end + + it 'returns .all when sort method is unknown' do + expect(SortableModel.order_by('foo')).to eq SortableModel::RECORDS + end + + it 'does not crash on nil' do + expect(SortableModel.order_by(nil)).to eq SortableModel::RECORDS + end + end + + describe :sortable_by do + it 'allows to define sorting methods' do + expected = SortableModel.sort_by_stars + + expect(SortableModel.order_by('stars')).to eq expected + end + end +end -- GitLab From 750aa82ae50cefcf0eb5d2fe166cac24db8016bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 25 Jan 2016 16:15:56 +0100 Subject: [PATCH 02/14] Fix specs, add specs, fix Rubocop offences, fix some queries & views --- app/controllers/concerns/projects_listing.rb | 16 ++++--- .../dashboard/milestones_controller.rb | 2 +- .../dashboard/projects_controller.rb | 2 +- app/controllers/dashboard_controller.rb | 2 +- .../explore/projects_controller.rb | 2 + app/controllers/groups_controller.rb | 38 ++++++--------- app/helpers/search_helper.rb | 2 +- app/helpers/sorting_helper.rb | 4 -- app/helpers/tab_helper.rb | 46 +++++++------------ app/models/concerns/sortable.rb | 27 ++--------- app/models/project.rb | 10 ++-- app/models/user.rb | 2 +- app/views/admin/users/index.html.haml | 4 +- app/views/dashboard/_projects_head.html.haml | 2 +- .../explore/projects/_projects.html.haml | 5 +- app/views/groups/_projects_head.html.haml | 6 +-- app/views/groups/projects.html.haml | 5 +- app/views/groups/show.html.haml | 2 +- app/views/shared/projects/_dropdown.html.haml | 5 +- features/dashboard/projects.feature | 4 +- features/dashboard/starred_projects.feature | 6 ++- features/groups.feature | 15 +++++- features/steps/dashboard/projects.rb | 8 ++++ features/steps/dashboard/starred_projects.rb | 8 ++++ features/steps/groups.rb | 24 ++++++++++ features/steps/shared/group.rb | 21 +++++++-- features/steps/shared/paths.rb | 4 ++ features/steps/shared/project.rb | 12 +++-- spec/models/concerns/sortable_spec.rb | 19 ++++---- 29 files changed, 164 insertions(+), 139 deletions(-) diff --git a/app/controllers/concerns/projects_listing.rb b/app/controllers/concerns/projects_listing.rb index 339ef09c4950..efc83ce636fe 100644 --- a/app/controllers/concerns/projects_listing.rb +++ b/app/controllers/concerns/projects_listing.rb @@ -1,8 +1,8 @@ -# == AuthenticatesWithTwoFactor +# == ProjectsListing # # Controller concern to handle projects list filtering & sorting # -# Upon inclusion, skips `require_no_authentication` on `:create`. +# Upon inclusion, calls `load_filter_and_sort` on all actions. module ProjectsListing extend ActiveSupport::Concern @@ -18,16 +18,18 @@ def load_filter_and_sort end def load_user_projects - @user_projects = refine_projects(ProjectsFinder.new.execute(current_user)) + return unless current_user + + @user_projects = prepare_for_listing(current_user.authorized_projects) end def load_starred_projects - @starred_projects = refine_projects( - current_user.starred_projects.includes(:forked_from_project) - ) + return unless current_user + + @starred_projects = prepare_for_listing(current_user.starred_projects) end - def refine_projects(relation) + def prepare_for_listing(relation) apply_filter(apply_base_scopes(relation)).sort(@sort) end diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb index 2bdce0f8a002..8e7887febd66 100644 --- a/app/controllers/dashboard/milestones_controller.rb +++ b/app/controllers/dashboard/milestones_controller.rb @@ -14,6 +14,6 @@ def show private def projects - @projects ||= current_user.authorized_projects.sorted_by_activity.non_archived + @projects ||= current_user.authorized_projects.sort('recently_active').non_archived end end diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 00a50f7c852f..7403d5445913 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -36,7 +36,7 @@ def load_last_push end def load_events - @events = Event.in_projects(@projects.pluck(:id)) + @events = Event.in_projects(@projects).includes(:target) @events = @event_filter.apply_filter(@events).with_associations @events = @events.limit(20).offset(params[:offset] || 0) end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 087da9350871..1f41f387664c 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -36,6 +36,6 @@ def load_events end def projects - @projects ||= current_user.authorized_projects.sorted_by_activity.non_archived + @projects ||= current_user.authorized_projects.sort('recently_active').non_archived end end diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index e35ac5dc6190..f4e659219c27 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -1,7 +1,9 @@ class Explore::ProjectsController < Explore::ApplicationController include ProjectsListing + # The explore page doesn't use the new filters & sorts. skip_before_action :load_filter_and_sort + # But it needs to show the user's own projects & her starred projects before_action :load_user_projects, :load_starred_projects, if: :current_user def index diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 8ba17dd498c8..d05c419bd5f9 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -5,7 +5,7 @@ class GroupsController < Groups::ApplicationController skip_before_action :authenticate_user!, only: [:show, :projects, :issues, :merge_requests] respond_to :html - before_action :group, except: [:new, :create] + before_action :load_group, except: [:new, :create] # Authorize before_action :authorize_read_group!, except: [:show, :projects, :new, :create, :autocomplete] @@ -61,14 +61,16 @@ def edit end def projects - no_projects = ProjectsFinder.new.execute(current_user, group: group).empty? - redirect_to(group_path(@group)) if no_projects - + # No matter if the user is logged-in or not, @all_projects is always needed. @all_projects = @projects + # When the user is logged-in, we need @all_projects, @contributed_projects + # and, @starred_projects to display their respective projects count in the + # UI. Depending on the current scope (none or 'all', 'contributed' or + # 'starred'), the @projects variable will reference one of these 3 variables + # and is used for the projects list in the page. if current_user - # @projects are the scoped project, it can reference @all_projects is the - # current scope is 'all' or no scope matches. + @starred_projects = @starred_projects.in_namespace(@group.id) @projects = case params[:scope] when 'contributed' @contributed_projects @@ -96,37 +98,25 @@ def destroy protected - def group + def load_group @group ||= Group.find_by(path: params[:id]) end def load_projects - @projects ||= refine_projects( - ProjectsFinder.new.execute(current_user, group: group) + @projects ||= prepare_for_listing( + ProjectsFinder.new.execute(current_user, group: @group) ) end def load_contributed_projects return unless current_user - @contributed_projects ||= refine_projects( + @contributed_projects ||= prepare_for_listing( ContributedProjectsFinder.new(current_user).execute(current_user). - in_group_namespace.in_namespace(@group.id) - ).reject(&:forked?) - end - - def load_starred_projects - return unless current_user - - @starred_projects ||= refine_projects( - current_user.starred_projects.in_group_namespace.in_namespace(@group.id) + in_namespace(@group.id) ) end - def project_ids - @projects.pluck(:id) - end - # Dont allow unauthorized access to group def authorize_read_group! unless @group and (@projects.present? or can?(current_user, :read_group, @group)) @@ -159,7 +149,7 @@ def group_params end def load_events - @events = Event.in_projects(project_ids) + @events = Event.in_projects(@projects).includes(:target) @events = event_filter.apply_filter(@events).with_associations @events = @events.limit(20).offset(params[:offset] || 0) end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index d4f782586264..5674f9a0ec00 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -81,7 +81,7 @@ def groups_autocomplete(term, limit = 5) # Autocomplete results for the current user's projects def projects_autocomplete(term, limit = 5) ProjectsFinder.new.execute(current_user).search_by_title(term). - sorted_by_stars.non_archived.limit(limit).map do |p| + sort('stars').non_archived.limit(limit).map do |p| { label: "project: #{search_result_sanitize(p.name_with_namespace)}", url: namespace_project_path(p.namespace, p) diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 3c560db78253..05348b7dcfcf 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -41,10 +41,6 @@ def sort_title_milestone_later 'Milestone due later' end - def sort_title_name - 'Name' - end - def sort_title_name_asc 'Name from A to Z' end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 59a6089e246b..4831b961940e 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -59,40 +59,28 @@ def nav_link(options = {}, &block) end def active_nav_link?(options) - active = if path = options.delete(:path) - unless path.respond_to?(:each) - path = [path] - end + active_link = if path = options.delete(:path) + Array(path).any? { |single_path| current_path?(single_path) } + elsif page = options.delete(:page) + Array(page).any? { |single_page| current_page?(single_page) } + else + c = options.delete(:controller) + a = options.delete(:action) - path.any? do |single_path| - current_path?(single_path) - end - elsif page = options.delete(:page) - unless page.respond_to?(:each) - page = [page] - end - - page.any? do |single_page| - current_page?(single_page) - end - else - c = options.delete(:controller) - a = options.delete(:action) - - if c && a - # When given both options, make sure BOTH are true - current_controller?(*c) && current_action?(*a) - else - # Otherwise check EITHER option - current_controller?(*c) || current_action?(*a) - end - end + if c && a + # When given both options, make sure BOTH are true + current_controller?(*c) && current_action?(*a) + else + # Otherwise check EITHER option + current_controller?(*c) || current_action?(*a) + end + end if query = options.delete(:query) - active &&= query.all? { |k,v| params[k] == v } + active_link &&= query.all? { |k,v| params[k] == v } end - active + active_link end def current_path?(path) diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index 6be07e0c4352..c037749d8f06 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -21,36 +21,15 @@ module Sortable end module ClassMethods - # Adds a new sort method. - def sortable_by(sort_name, scope_name) - sortables[sort_name.to_s] = scope_name - end - - # Accessor for sort methods marked mentionable. - def sortables - @sortables ||= {} - end - def order_by(method) return all if method.blank? method = method.to_s - case method - when 'name_asc' then order_name_asc - when 'name_desc' then order_name_desc - when 'updated_asc' then order_updated_asc - when 'updated_desc' then order_updated_desc - when 'created_asc' then order_created_asc - when 'created_desc' then order_created_desc - when 'id_desc' then order_id_desc - when 'id_asc' then order_id_asc + if respond_to?("order_#{method}") + send "order_#{method}" else - if sortables[method].present? && respond_to?(sortables[method]) - send(sortables[method]) - else - all - end + all end end end diff --git a/app/models/project.rb b/app/models/project.rb index b356edd5b1aa..9e6bd72c5272 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -200,13 +200,9 @@ def update_forks_visibility_level mount_uploader :avatar, AvatarUploader # Scopes - scope :sorted_by_activity, -> { reorder(last_activity_at: :desc) } - scope :sorted_by_stars, -> { reorder('projects.star_count DESC') } - scope :sorted_by_names, -> { joins(:namespace).reorder('namespaces.name ASC, projects.name ASC') } - - # Custom sorting methods - sortable_by 'recently_active', :sorted_by_activity - sortable_by 'stars', :sorted_by_stars + ## Sortable + scope :order_stars, -> { reorder('projects.star_count DESC') } + scope :order_recently_active, -> { reorder(last_activity_at: :desc) } scope :without_user, ->(user) { where('projects.id NOT IN (:ids)', ids: user.authorized_projects.map(&:id) ) } scope :without_team, ->(team) { team.projects.present? ? where('projects.id NOT IN (:ids)', ids: team.projects.map(&:id)) : scoped } diff --git a/app/models/user.rb b/app/models/user.rb index 4214f01f6a46..2aa76731158a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -505,7 +505,7 @@ def recent_push(project_id = nil) end def projects_sorted_by_activity - authorized_projects.sorted_by_activity + authorized_projects.sort('recently_active') end def several_namespaces? diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 6507ad6f8b71..6076e26f35b9 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -36,12 +36,12 @@ - if @sort.present? = sort_options_hash[@sort] - else - = sort_title_name + = sort_title_name_asc %b.caret %ul.dropdown-menu %li = link_to admin_users_path(sort: sort_value_name_asc, filter: params[:filter]) do - = sort_title_name + = sort_title_name_asc = 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 diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index 4dd740a0e890..cad7ee2792d1 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -4,7 +4,7 @@ .nav-block %ul.nav-links = nav_link(page: [dashboard_projects_path, root_path]) do - = link_to dashboard_projects_path(filter: @filter, sort: @sort), title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do + = link_to dashboard_projects_path(filter: @filter, sort: @sort), title: 'Your Projects', data: {placement: 'right'} do = "Your Projects (#{@user_projects.size})" = nav_link(page: starred_dashboard_projects_path) do = link_to starred_dashboard_projects_path(filter: @filter, sort: @sort), title: 'Starred Projects', data: {placement: 'right'} do diff --git a/app/views/explore/projects/_projects.html.haml b/app/views/explore/projects/_projects.html.haml index 669079e95217..565eb401f79b 100644 --- a/app/views/explore/projects/_projects.html.haml +++ b/app/views/explore/projects/_projects.html.haml @@ -1,6 +1,5 @@ - if projects.any? - .public-projects + .projects-list-holder.public-projects = render 'shared/projects/list', projects: projects - else - .nothing-here-block - No such projects + .nothing-here-block No such projects diff --git a/app/views/groups/_projects_head.html.haml b/app/views/groups/_projects_head.html.haml index c22cde483293..1da14197cd3c 100644 --- a/app/views/groups/_projects_head.html.haml +++ b/app/views/groups/_projects_head.html.haml @@ -1,15 +1,15 @@ .nav-block %ul.nav-links = nav_link(page: projects_group_path, query: { scope: nil }) do - = link_to projects_group_path(@group, filter: @filter, sort: @sort), title: "All projects from the '#{@group.name}' group", class: 'shortcuts-activity', data: {placement: 'right'} do + = link_to projects_group_path(@group, filter: @filter, sort: @sort), title: "All projects from the '#{@group.name}' group", data: {placement: 'right'} do = "All (#{@all_projects.size})" - if current_user = nav_link(page: projects_group_path, query: { scope: 'contributed' }) do - = link_to projects_group_path(@group, scope: 'contributed', filter: @filter, sort: @sort), title: "Projects from the '#{@group.name}' group you contributed to", class: 'shortcuts-activity', data: {placement: 'right'} do + = link_to projects_group_path(@group, scope: 'contributed', filter: @filter, sort: @sort), title: "Projects from the '#{@group.name}' group you contributed to", data: {placement: 'right'} do = "Contributed (#{@contributed_projects.size})" = nav_link(page: projects_group_path, query: { scope: 'starred' }) do - = link_to projects_group_path(@group, scope: 'starred', filter: @filter, sort: @sort), title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do + = link_to projects_group_path(@group, scope: 'starred', filter: @filter, sort: @sort), title: "Starred projects from the '#{@group.name}' group", data: {placement: 'right'} do = "Starred (#{@starred_projects.size})" = render 'shared/projects/controls' diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 1136c3c479b8..e2b0d6cbeef2 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -3,9 +3,8 @@ %div{class: container_class} .tab-content .tab-pane.active - - if can?(current_user, :read_group, @group) + - if can?(current_user, :read_group, @group) && @projects.any? = render 'projects_head' = render 'projects', projects: @projects - else - %p.nav-links.no-top - No projects to show + .nothing-here-block No projects to see diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 503c4a9e1f37..5f087151c5fa 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -12,4 +12,4 @@ .content_list = spinner - else - %p.nav-links.no-top No activity to show + .nothing-here-block No activity to show diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml index aff727b37d39..26df9a8d7fd8 100644 --- a/app/views/shared/projects/_dropdown.html.haml +++ b/app/views/shared/projects/_dropdown.html.haml @@ -1,7 +1,10 @@ .dropdown.inline %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %span.light - = sort_options_hash[@sort] + - if sort_options_hash[@sort].present? + = sort_options_hash[@sort] + - else + = sort_title_recently_active %b.caret %ul.dropdown-menu.dropdown-menu-align-right %li.dropdown-label Sort by diff --git a/features/dashboard/projects.feature b/features/dashboard/projects.feature index e00c495338d2..f6a8dfbd5ad6 100644 --- a/features/dashboard/projects.feature +++ b/features/dashboard/projects.feature @@ -10,10 +10,12 @@ Feature: Dashboard Projects And I own project "Shop" And project "Shop" has push event And project "Community" has push event - And "John Doe" someone starred project "Community" + And "John Doe" starred project "Community" And I visit dashboard projects page Scenario: I should see projects list + Then I should see "Your projects (3)" + Then I should see "Starred projects (2)" Then I should see "Community" project link Then I should see "Forum" project link Then I should see "Shop" project link diff --git a/features/dashboard/starred_projects.feature b/features/dashboard/starred_projects.feature index 99c94c166e7a..ac884cb4ff53 100644 --- a/features/dashboard/starred_projects.feature +++ b/features/dashboard/starred_projects.feature @@ -12,11 +12,13 @@ Feature: Dashboard Starred Projects And I starred project "Grocery" And project "Grocery" has push event And project "Community" has push event - And "John Doe" someone starred project "Community" - And "John Doe" someone starred project "Forum" + And "John Doe" starred project "Community" + And "John Doe" starred project "Forum" And I visit dashboard starred projects page Scenario: I should see projects list + Then I should see "Your projects (4)" + Then I should see "Starred projects (3)" Then I should see "Community" project link Then I should see "Forum" project link Then I should see "Grocery" project link diff --git a/features/groups.feature b/features/groups.feature index 4efff242a956..a9e833b2e910 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -11,14 +11,18 @@ Feature: Groups When I visit group "Owned" page Then I should see back to dashboard button - @javascript @wip + @javascript Scenario: I should see group "Owned" dashboard list When I visit group "Owned" page And I should see projects activity feed # Projects - Scenario: I sort projects by recent activity + Scenario: I should see group "Owned" projects list When I visit group "Owned" projects page + Then I should see a nav block + Then I should see "All (4)" + Then I should see "Contributed (1)" + Then I should see "Starred (2)" Then I should see group "Owned" projects list Scenario: I sort projects by recent activity @@ -71,6 +75,12 @@ Feature: Groups Then I should not see group "Owned" avatar And I should not see the "Remove avatar" button + @javascript + Scenario: I should not see a nav block if group is empty + And "John Doe" is owner of group "Empty" + When I visit group "Empty" page + Then I should not see a nav block + # Group projects in settings Scenario: I should see all projects in the project list in settings Given Group "Owned" has archived project @@ -85,4 +95,5 @@ Feature: Groups And I am a signed out user When I visit group "Owned" projects page Then I should see group "Owned" + Then I should see "All (2)" Then I should see project "Public-project" diff --git a/features/steps/dashboard/projects.rb b/features/steps/dashboard/projects.rb index 4ad1afb070f0..e6d141e6ee7f 100644 --- a/features/steps/dashboard/projects.rb +++ b/features/steps/dashboard/projects.rb @@ -2,4 +2,12 @@ class Spinach::Features::DashboardProjects < Spinach::FeatureSteps include SharedAuthentication include SharedPaths include SharedProject + + step 'I should see "Your Projects (3)"' do + expect(page).to have_link 'Your Projects (3)' + end + + step 'I should see "Starred Projects (2)"' do + expect(page).to have_link 'Starred Projects (2)' + end end diff --git a/features/steps/dashboard/starred_projects.rb b/features/steps/dashboard/starred_projects.rb index dd9e371173c6..8cb1c860896f 100644 --- a/features/steps/dashboard/starred_projects.rb +++ b/features/steps/dashboard/starred_projects.rb @@ -2,4 +2,12 @@ class Spinach::Features::DashboardStarredProjects < Spinach::FeatureSteps include SharedAuthentication include SharedPaths include SharedProject + + step 'I should see "Your Projects (4)"' do + expect(page).to have_link 'Your Projects (4)' + end + + step 'I should see "Starred Projects (3)"' do + expect(page).to have_link 'Starred Projects (3)' + end end diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 0bee5eca7878..09245e64a997 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -13,6 +13,22 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect(page).to have_content '@owned' end + step 'I should see "All (4)"' do + expect(page).to have_link "All (4)" + end + + step 'I should see "All (2)"' do + expect(page).to have_link "All (2)" + end + + step 'I should see "Contributed (1)"' do + expect(page).to have_link "Contributed (1)" + end + + step 'I should see "Starred (2)"' do + expect(page).to have_link "Starred (2)" + end + step 'I am a signed out user' do logout end @@ -158,6 +174,14 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect_top_project_in_list("Moon-project") end + step 'I should see a nav block' do + expect(page).to have_selector(:css, '.nav-block') + end + + step 'I should not see a nav block' do + expect(page).not_to have_selector(:css, '.nav-block') + end + private def assigned_to_me(key) diff --git a/features/steps/shared/group.rb b/features/steps/shared/group.rb index fe6736dacd42..c496c416a8d7 100644 --- a/features/steps/shared/group.rb +++ b/features/steps/shared/group.rb @@ -9,6 +9,10 @@ module SharedGroup is_member_of("John Doe", "Owned", Gitlab::Access::OWNER) end + step '"John Doe" is owner of group "Empty"' do + is_member_of("John Doe", "Empty", Gitlab::Access::OWNER, with_project: false) + end + step '"John Doe" is guest of group "Guest"' do is_member_of("John Doe", "Guest", Gitlab::Access::GUEST) end @@ -35,18 +39,25 @@ module SharedGroup protected - def is_member_of(username, groupname, role) + def is_member_of(username, groupname, role, with_project: true) @project_count ||= 0 user = User.find_by(name: username) || create(:user, name: username) group = Group.find_by(name: groupname) || create(:group, name: groupname) group.add_user(user, role) - project ||= create(:project, namespace: group, path: "project#{@project_count}") - create(:closed_issue_event, project: project) - project.team << [user, :master] - @project_count += 1 + + if with_project + project ||= create(:project, namespace: group, path: "project#{@project_count}") + create(:closed_issue_event, project: project) + project.team << [user, :master] + @project_count += 1 + end end def owned_group @owned_group ||= Group.find_by(name: "Owned") end + + def empty_group + @empty ||= Group.find_by(name: "Empty") + end end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index da54707bdce3..73fa606b7d99 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -23,6 +23,10 @@ module SharedPaths visit group_path(Group.find_by(name: "Owned")) end + step 'I visit group "Empty" page' do + visit group_path(Group.find_by(name: "Empty")) + end + step 'I visit group "Owned" issues page' do visit issues_group_path(Group.find_by(name: "Owned")) end diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 64a59373b900..47ad488475cf 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -272,12 +272,12 @@ def current_project current_user.toggle_star(Project.find_by(name: 'Grocery')) end - step '"John Doe" someone starred project "Community"' do + step '"John Doe" starred project "Community"' do user_exists("John Doe").toggle_star(Project.find_by(name: 'Community')) end - step '"John Doe" someone starred project "Forum"' do - user_exists("John Doe").toggle_star(Project.find_by(name: 'Community')) + step '"John Doe" starred project "Forum"' do + user_exists("John Doe").toggle_star(Project.find_by(name: 'Forum')) end # ---------------------------------------- @@ -362,7 +362,7 @@ def current_project private - def event_for_project(project, user) + def event_for_project(project, user = nil) data = { before: Gitlab::Git::BLANK_SHA, after: "6d394385cf567f80a8fd85055db1ab4c5295806f", @@ -405,7 +405,7 @@ def expect_top_project_in_list(project_name) end def expect_link_in_list(project_name, truthy = true) - expect(page.find('ul.projects-list')).send (truthy ? 'to' : 'not_to'), have_content(project_name) + expect(page.find('ul.projects-list')).send((truthy ? 'to' : 'not_to'), have_content(project_name)) end def user_owns_project(user:, project_name: nil, project_type: :empty_project, visibility: :private, **args) @@ -413,5 +413,7 @@ def user_owns_project(user:, project_name: nil, project_type: :empty_project, vi project = Project.find_by(name: project_name) project ||= create(project_type, visibility, name: project_name, namespace: user.namespace, **args) project.team << [user, :master] + + project end end diff --git a/spec/models/concerns/sortable_spec.rb b/spec/models/concerns/sortable_spec.rb index f7c238c2364e..b67d4f1195f7 100644 --- a/spec/models/concerns/sortable_spec.rb +++ b/spec/models/concerns/sortable_spec.rb @@ -13,24 +13,23 @@ def self.all include Sortable - sortable_by 'stars', :sort_by_stars - RECORDS = [ OpenStruct.new(title: 'Awesome', id: 1, stars: 1), OpenStruct.new(title: 'Amazing', id: 2, stars: 2), OpenStruct.new(title: 'Incredible', id: 3, stars: 0), ] - scope :sort_by_stars, ->{ RECORDS.sort_by(&:stars).reverse } + scope :order_stars, ->{ RECORDS.sort_by(&:stars).reverse } end describe :order_by do %w[id created_at updated_at].each do |attr| %i[asc desc].each do |way| it "can be sorted #{attr.sub('_at', '')}_#{way}" do - expect(SortableModel).to receive(:reorder).with(attr.to_sym => way) + clean_attribute = attr.sub('_at', '') + expect(SortableModel).to receive("order_#{clean_attribute}_#{way}") - SortableModel.order_by("#{attr.sub('_at', '')}_#{way}") + SortableModel.order_by("#{clean_attribute}_#{way}") end end end @@ -42,13 +41,13 @@ def self.all it 'does not crash on nil' do expect(SortableModel.order_by(nil)).to eq SortableModel::RECORDS end - end - describe :sortable_by do - it 'allows to define sorting methods' do - expected = SortableModel.sort_by_stars + describe 'custom sort methods' do + it 'allows to define custom sorting methods' do + expected = SortableModel.order_stars - expect(SortableModel.order_by('stars')).to eq expected + expect(SortableModel.order_by('stars')).to eq expected + end end end end -- GitLab From 9fc53e6bf249dce02698c04fb713afdc093f9e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 25 Jan 2016 17:07:46 +0100 Subject: [PATCH 03/14] Fix specs and clean links in group page --- app/controllers/dashboard/projects_controller.rb | 2 +- app/controllers/groups_controller.rb | 2 +- app/views/groups/_header.html.haml | 2 +- app/views/groups/_projects_head.html.haml | 6 +++--- features/steps/explore/groups.rb | 2 +- features/steps/shared/project.rb | 10 +++++----- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 7403d5445913..05a60bd0bdc0 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -36,7 +36,7 @@ def load_last_push end def load_events - @events = Event.in_projects(@projects).includes(:target) + @events = Event.in_projects(@projects) @events = @event_filter.apply_filter(@events).with_associations @events = @events.limit(20).offset(params[:offset] || 0) end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index d05c419bd5f9..3cb12cb753bc 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -149,7 +149,7 @@ def group_params end def load_events - @events = Event.in_projects(@projects).includes(:target) + @events = Event.in_projects(@projects) @events = event_filter.apply_filter(@events).with_associations @events = @events.limit(20).offset(params[:offset] || 0) end diff --git a/app/views/groups/_header.html.haml b/app/views/groups/_header.html.haml index f36cb780d03d..7fa803c39dfa 100644 --- a/app/views/groups/_header.html.haml +++ b/app/views/groups/_header.html.haml @@ -31,4 +31,4 @@ = nav_link(page: group_path(@group)) do = link_to 'Activity', group_path(@group) = nav_link(page: projects_group_path(@group)) do - = link_to 'Projects', projects_group_path(@group, scope: params[:scope], filter: @filter, sort: @sort) + = link_to 'Projects', projects_group_path(@group) diff --git a/app/views/groups/_projects_head.html.haml b/app/views/groups/_projects_head.html.haml index 1da14197cd3c..c1384421c521 100644 --- a/app/views/groups/_projects_head.html.haml +++ b/app/views/groups/_projects_head.html.haml @@ -1,15 +1,15 @@ .nav-block %ul.nav-links = nav_link(page: projects_group_path, query: { scope: nil }) do - = link_to projects_group_path(@group, filter: @filter, sort: @sort), title: "All projects from the '#{@group.name}' group", data: {placement: 'right'} do + = link_to projects_group_path(@group, sort: @sort), title: "All projects from the '#{@group.name}' group", data: {placement: 'right'} do = "All (#{@all_projects.size})" - if current_user = nav_link(page: projects_group_path, query: { scope: 'contributed' }) do - = link_to projects_group_path(@group, scope: 'contributed', filter: @filter, sort: @sort), title: "Projects from the '#{@group.name}' group you contributed to", data: {placement: 'right'} do + = link_to projects_group_path(@group, scope: 'contributed', sort: @sort), title: "Projects from the '#{@group.name}' group you contributed to", data: {placement: 'right'} do = "Contributed (#{@contributed_projects.size})" = nav_link(page: projects_group_path, query: { scope: 'starred' }) do - = link_to projects_group_path(@group, scope: 'starred', filter: @filter, sort: @sort), title: "Starred projects from the '#{@group.name}' group", data: {placement: 'right'} do + = link_to projects_group_path(@group, scope: 'starred', sort: @sort), title: "Starred projects from the '#{@group.name}' group", data: {placement: 'right'} do = "Starred (#{@starred_projects.size})" = render 'shared/projects/controls' diff --git a/features/steps/explore/groups.rb b/features/steps/explore/groups.rb index 87f32e70d592..d216d226604c 100644 --- a/features/steps/explore/groups.rb +++ b/features/steps/explore/groups.rb @@ -23,7 +23,7 @@ class Spinach::Features::ExploreGroups < Spinach::FeatureSteps end step 'I visit group "TestGroup" page' do - visit group_path(Group.find_by(name: "TestGroup")) + visit projects_group_path(Group.find_by(name: "TestGroup")) end step 'I visit group "TestGroup" issues page' do diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 47ad488475cf..9b808e77e458 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -195,21 +195,21 @@ def current_project step '"John Doe" owns private project "Enterprise"' do user_owns_project( - user_name: 'John Doe', + user: 'John Doe', project_name: 'Enterprise' ) end step '"Mary Jane" owns private project "Enterprise"' do user_owns_project( - user_name: 'Mary Jane', + user: 'Mary Jane', project_name: 'Enterprise' ) end step '"John Doe" owns internal project "Internal"' do user_owns_project( - user_name: 'John Doe', + user: 'John Doe', project_name: 'Internal', visibility: :internal ) @@ -217,7 +217,7 @@ def current_project step '"John Doe" owns public project "Community"' do user_owns_project( - user_name: 'John Doe', + user: 'John Doe', project_name: 'Community', visibility: :public ) @@ -357,7 +357,7 @@ def current_project end step 'I should see "Shop" project CI status' do - expect_link_in_list "Build skipped" + expect(page).to have_link "Build skipped" end private -- GitLab From 01acf2824fca59f068c50d9f89adb2fe15b9d27d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 25 Jan 2016 18:57:06 +0100 Subject: [PATCH 04/14] Fix spec --- app/controllers/explore/projects_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index f4e659219c27..838a934b36f2 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -21,7 +21,7 @@ def trending end def starred - @projects = ProjectsFinder.new.execute(current_user).non_archived. - sort('stars').page(params[:page]).per(PER_PAGE) + @projects = ProjectsFinder.new.execute(current_user).sort('stars'). + page(params[:page]).per(PER_PAGE) end end -- GitLab From b4dba39d61806a137539409a69453a73a0271d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 26 Jan 2016 11:00:47 +0100 Subject: [PATCH 05/14] Try to fix specs --- app/models/concerns/sortable.rb | 2 -- features/dashboard/projects.feature | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index c037749d8f06..4def791e9c6c 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -24,8 +24,6 @@ module ClassMethods def order_by(method) return all if method.blank? - method = method.to_s - if respond_to?("order_#{method}") send "order_#{method}" else diff --git a/features/dashboard/projects.feature b/features/dashboard/projects.feature index f6a8dfbd5ad6..8b752058b82c 100644 --- a/features/dashboard/projects.feature +++ b/features/dashboard/projects.feature @@ -2,11 +2,11 @@ Feature: Dashboard Projects Background: Given I sign in as a user + And I own project "Forum" + And I starred project "Forum" And public project "Community" And I am member of a project "Community" with a guest role And I starred project "Community" - And I own project "Forum" - And I starred project "Forum" And I own project "Shop" And project "Shop" has push event And project "Community" has push event -- GitLab From 575a9d53b0cf36971cd8890086906147b41bd368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 27 Jan 2016 12:58:33 +0100 Subject: [PATCH 06/14] Address MR feedback --- app/assets/stylesheets/framework.scss | 1 + app/assets/stylesheets/framework/common.scss | 15 ----- .../stylesheets/framework/dropdown.scss | 60 +++++++++++++++++++ .../stylesheets/framework/variables.scss | 3 + app/assets/stylesheets/pages/projects.scss | 50 ---------------- app/controllers/concerns/projects_listing.rb | 25 +++----- .../dashboard/projects_controller.rb | 6 +- .../explore/projects_controller.rb | 6 -- .../groups/application_controller.rb | 14 +++-- app/controllers/groups/projects_controller.rb | 41 ++++++++++++- app/controllers/groups_controller.rb | 48 ++------------- app/helpers/sorting_helper.rb | 25 ++------ app/helpers/tab_helper.rb | 31 +++++----- app/models/concerns/sortable.rb | 4 +- app/models/group.rb | 4 +- app/views/admin/users/index.html.haml | 6 +- app/views/dashboard/_projects_head.html.haml | 6 +- app/views/explore/projects/index.html.haml | 1 + app/views/explore/projects/starred.html.haml | 1 + app/views/explore/projects/trending.html.haml | 1 + app/views/groups/_header.html.haml | 14 ++--- app/views/groups/_projects_head.html.haml | 18 +++--- app/views/groups/edit.html.haml | 2 - app/views/groups/projects/edit.html.haml | 1 - .../index.html.haml} | 7 ++- app/views/groups/show.html.haml | 4 ++ app/views/layouts/group_settings.html.haml | 2 + app/views/shared/_sort_dropdown.html.haml | 2 +- app/views/shared/projects/_controls.html.haml | 6 +- app/views/shared/projects/_dropdown.html.haml | 24 +++----- config/routes.rb | 5 +- features/dashboard/projects.feature | 21 ++----- features/dashboard/starred_projects.feature | 21 ++----- features/groups.feature | 17 ++---- features/steps/dashboard/projects.rb | 8 +-- features/steps/dashboard/starred_projects.rb | 8 +-- features/steps/explore/groups.rb | 2 +- features/steps/groups.rb | 16 ++--- features/steps/shared/note.rb | 2 +- features/steps/shared/paths.rb | 2 +- features/steps/shared/project.rb | 8 +-- spec/models/group_spec.rb | 2 +- 42 files changed, 240 insertions(+), 300 deletions(-) create mode 100644 app/assets/stylesheets/framework/dropdown.scss rename app/views/groups/{projects.html.haml => projects/index.html.haml} (53%) diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index fa7641b1676d..ef0a2b41f9a0 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -11,6 +11,7 @@ @import "framework/calendar.scss"; @import "framework/callout.scss"; @import "framework/common.scss"; +@import "framework/dropdown.scss"; @import "framework/files.scss"; @import "framework/filters.scss"; @import "framework/flash.scss"; diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 9bc814cfd2d6..a23f0cc0ab28 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -56,21 +56,6 @@ hr { margin: $gl-padding 0; } -.dropdown-menu > li > a { - text-shadow: none; -} - -.dropdown-menu-align-right { - left: auto; - right: 0px; -} - -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - background: $gl-primary; - color: #FFF; -} - .str-truncated { @include str-truncated; } diff --git a/app/assets/stylesheets/framework/dropdown.scss b/app/assets/stylesheets/framework/dropdown.scss new file mode 100644 index 000000000000..d1e615ab35d8 --- /dev/null +++ b/app/assets/stylesheets/framework/dropdown.scss @@ -0,0 +1,60 @@ +.dropdown-menu { + @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); + @include border-radius (1px); + + border: none; + margin-top: 5px; + padding: $gl-padding-top 0; + font-size: $gl-font-size; + font-weight: 100; + + li { + a { + line-height: $gl-dropdown-line-height; + color: #5f697a; + text-shadow: none; + + &:hover, + &:focus { + background: $gl-primary; + color: #FFF; + } + } + } + + // Specific class to ensure we don't break all current dropdowns, allowing for + // a smooth transition to new dropdowns design + &.with-label { + li { + // First li must be the label + &:first-child { + padding-left: $gl-padding; + padding-bottom: $gl-vert-padding; + font-size: $gl-dropdown-label-font-size; + } + + a { + line-height: $gl-dropdown-line-height; + padding-left: $gl-padding + $gl-font-size + $gl-dropdown-fa-icon-margin-right; + + // The active li has an icon to its left so it needs an extra padding + &.active { + padding-left: $gl-padding; + } + } + } + + hr { + margin: 5px 10px; + } + + i { + margin-right: $gl-dropdown-fa-icon-margin-right; + } + } + + &.align-right { + left: auto; + right: 0px; + } +} diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 3ec48da9a41e..6a42c85446d2 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -25,6 +25,9 @@ $gl-padding: 16px; $gl-vert-padding: 6px; $gl-padding-top:10px; $gl-avatar-size: 46px; +$gl-dropdown-line-height: 22px; +$gl-dropdown-fa-icon-margin-right: 8px; +$gl-dropdown-label-font-size: 14px; $secondary-text: #7f8fa4; $error-exclamation-point: #E62958; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 1ea4a4a41be5..c21ae093a9ce 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -189,34 +189,6 @@ } } -.dropdown-menu { - @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); - @include border-radius (1px); - - border: none; - margin-top: 5px; - padding: 16px 0; - font-size: 14px; - font-weight: 100; - - li a { - color: #5f697a; - line-height: 30px; - - &:hover { - background-color: #3084bb !important; - } - } - - hr { - margin: 10px; - } - - i { - margin-right: 8px; - } -} - .project-visibility-level-holder { .radio { margin-bottom: 10px; @@ -298,28 +270,6 @@ .projects-controls { width: 50%; - - .dropdown-menu { - font-size: $gl-font-size; - - li { - - &.dropdown-label { - padding-left: $gl-padding; - padding-bottom: $gl-vert-padding; - font-size: 14px; - } - - a { - line-height: 18px; - padding-left: 39px; - - &.active { - padding-left: $gl-padding; - } - } - } - } } } diff --git a/app/controllers/concerns/projects_listing.rb b/app/controllers/concerns/projects_listing.rb index efc83ce636fe..eee3e6768c4a 100644 --- a/app/controllers/concerns/projects_listing.rb +++ b/app/controllers/concerns/projects_listing.rb @@ -1,32 +1,25 @@ # == ProjectsListing # # Controller concern to handle projects list filtering & sorting -# -# Upon inclusion, calls `load_filter_and_sort` on all actions. module ProjectsListing extend ActiveSupport::Concern + include SortingHelper - included do - before_action :load_filter_and_sort - end + FILTERS_WHITELIST = %w[all personal] private - def load_filter_and_sort - @filter = params.fetch(:filter) { 'all' } - @sort = params.fetch(:sort) { 'recently_active' } + def init_filter_and_sort + @filter = whitelist_filter(params[:filter], 'all') + @sort = whitelist_sort(params[:sort], 'recently_active') end - def load_user_projects - return unless current_user - - @user_projects = prepare_for_listing(current_user.authorized_projects) + def whitelist_filter(filter, default) + FILTERS_WHITELIST.include?(filter) ? filter : default end - def load_starred_projects - return unless current_user - - @starred_projects = prepare_for_listing(current_user.starred_projects) + def whitelist_sort(sort, default) + sort_options_hash.keys.include?(sort) ? sort : default end def prepare_for_listing(relation) diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 05a60bd0bdc0..312e0342ba70 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -1,10 +1,10 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController include ProjectsListing - before_action :load_user_projects, :load_starred_projects, :load_last_push, :event_filter + before_action :init_filter_and_sort, :load_last_push, :event_filter def index - @projects = @user_projects + @projects = prepare_for_listing(current_user.authorized_projects) respond_to do |format| format.html @@ -16,7 +16,7 @@ def index end def starred - @projects = @starred_projects + @projects = prepare_for_listing(current_user.starred_projects) @groups = [] respond_to do |format| diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index 838a934b36f2..c614c3a6c414 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -1,10 +1,4 @@ class Explore::ProjectsController < Explore::ApplicationController - include ProjectsListing - - # The explore page doesn't use the new filters & sorts. - skip_before_action :load_filter_and_sort - # But it needs to show the user's own projects & her starred projects - before_action :load_user_projects, :load_starred_projects, if: :current_user def index @projects = ProjectsFinder.new.execute(current_user).non_archived.includes(:namespace) diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index be801858eafe..8806d412f4b0 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -1,15 +1,19 @@ class Groups::ApplicationController < ApplicationController layout 'group' - before_action :group + before_action :find_group private - def group + def find_group @group ||= Group.find_by(path: params[:group_id]) end + def find_projects + @projects ||= ProjectsFinder.new.execute(current_user, group: @group) + end + def authorize_read_group! - unless @group and can?(current_user, :read_group, @group) + unless can?(current_user, :read_group, @group) if current_user.nil? return authenticate_user! else @@ -19,13 +23,13 @@ def authorize_read_group! end def authorize_admin_group! - unless can?(current_user, :admin_group, group) + unless can?(current_user, :admin_group, @group) return render_404 end end def authorize_admin_group_member! - unless can?(current_user, :admin_group_member, group) + unless can?(current_user, :admin_group_member, @group) return render_403 end end diff --git a/app/controllers/groups/projects_controller.rb b/app/controllers/groups/projects_controller.rb index 67eaabe382cc..57f41283772f 100644 --- a/app/controllers/groups/projects_controller.rb +++ b/app/controllers/groups/projects_controller.rb @@ -1,11 +1,48 @@ class Groups::ProjectsController < Groups::ApplicationController + include ProjectsListing + + skip_before_action :authenticate_user!, only: [:index] + # Authorize - before_action :authorize_admin_group! + before_action :authorize_admin_group!, only: [:edit] + + before_action :init_filter_and_sort, only: [:index] - layout 'group_settings' + layout :determine_layout + + def index + @projects = + if current_user + prepare_for_listing(find_scoped_project(params[:scope])) + else + find_projects + end + end def edit @projects = @group.projects.page(params[:page]) end + private + + def find_scoped_project(scope) + case scope + when 'contributed' + ContributedProjectsFinder.new(current_user). + execute(current_user).in_namespace(@group.id) + when 'starred' + current_user.starred_projects.in_namespace(@group.id) + else + find_projects + end + end + + def determine_layout + if action_name.to_sym == :edit + 'group_settings' + else + 'group' + end + end + end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 3cb12cb753bc..9448a47d1322 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,20 +1,17 @@ class GroupsController < Groups::ApplicationController include IssuesAction include MergeRequestsAction - include ProjectsListing skip_before_action :authenticate_user!, only: [:show, :projects, :issues, :merge_requests] respond_to :html - before_action :load_group, except: [:new, :create] + before_action :find_group, except: [:new, :create] # Authorize - before_action :authorize_read_group!, except: [:show, :projects, :new, :create, :autocomplete] + before_action :authorize_read_group!, except: [:show, :new, :create, :autocomplete] before_action :authorize_admin_group!, only: [:edit, :update, :destroy] before_action :authorize_create_group!, only: [:new, :create] - # Load group projects - before_action :load_projects, only: [:show, :projects, :issues, :merge_requests] - before_action :load_contributed_projects, :load_starred_projects, only: :projects + before_action :find_projects, only: [:show, :issues, :merge_requests] before_action :event_filter, only: :show layout :determine_layout @@ -60,28 +57,6 @@ def show def edit end - def projects - # No matter if the user is logged-in or not, @all_projects is always needed. - @all_projects = @projects - - # When the user is logged-in, we need @all_projects, @contributed_projects - # and, @starred_projects to display their respective projects count in the - # UI. Depending on the current scope (none or 'all', 'contributed' or - # 'starred'), the @projects variable will reference one of these 3 variables - # and is used for the projects list in the page. - if current_user - @starred_projects = @starred_projects.in_namespace(@group.id) - @projects = case params[:scope] - when 'contributed' - @contributed_projects - when 'starred' - @starred_projects - else - @all_projects - end - end - end - def update if @group.update_attributes(group_params) redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated." @@ -98,25 +73,10 @@ def destroy protected - def load_group + def find_group @group ||= Group.find_by(path: params[:id]) end - def load_projects - @projects ||= prepare_for_listing( - ProjectsFinder.new.execute(current_user, group: @group) - ) - end - - def load_contributed_projects - return unless current_user - - @contributed_projects ||= prepare_for_listing( - ContributedProjectsFinder.new(current_user).execute(current_user). - in_namespace(@group.id) - ) - end - # Dont allow unauthorized access to group def authorize_read_group! unless @group and (@projects.present? or can?(current_user, :read_group, @group)) diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 05348b7dcfcf..2cac7bf1b185 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -1,8 +1,7 @@ module SortingHelper def sort_options_hash { - sort_value_name_asc => sort_title_name_asc, - sort_value_name_desc => sort_title_name_desc, + sort_value_name => sort_title_name, sort_value_recently_updated => sort_title_recently_updated, sort_value_oldest_updated => sort_title_oldest_updated, sort_value_recently_created => sort_title_recently_created, @@ -41,12 +40,8 @@ def sort_title_milestone_later 'Milestone due later' end - def sort_title_name_asc - 'Name from A to Z' - end - - def sort_title_name_desc - 'Name from Z to A' + def sort_title_name + 'Name' end def sort_title_largest_repo @@ -93,14 +88,10 @@ def sort_value_milestone_later 'milestone_due_desc' end - def sort_value_name_asc + def sort_value_name 'name_asc' end - def sort_value_name_desc - 'name_desc' - end - def sort_value_largest_repo 'repository_size_desc' end @@ -135,14 +126,10 @@ def link_to_sort_or_filter(label, keyword:, value:, current_value: nil) label ||= value.to_s.humanize active = currently_active_sort_or_filter?(value, current_value) - active_class = active ? 'active' : nil - - url_params = params.reject { |k, v| k == keyword && v == value } - - link_to(url_for(url_params.merge(keyword => value)), class: active_class) do + link_to(url_for(params.merge(keyword => value)), class: (active ? 'active' : nil)) do if active content_tag(:span) do - content_tag(:i, nil, class: %w[fa fa-check]).concat(content_tag(:strong, label, class: 'item-title')) + content_tag(:i, nil, class: 'fa fa-check').concat(content_tag(:strong, label, class: 'item-title')) end else label diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 4831b961940e..3ee402168970 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -59,22 +59,23 @@ def nav_link(options = {}, &block) end def active_nav_link?(options) - active_link = if path = options.delete(:path) - Array(path).any? { |single_path| current_path?(single_path) } - elsif page = options.delete(:page) - Array(page).any? { |single_page| current_page?(single_page) } - else - c = options.delete(:controller) - a = options.delete(:action) + active_link = + if path = options.delete(:path) + Array(path).any? { |single_path| current_path?(single_path) } + elsif page = options.delete(:page) + Array(page).any? { |single_page| current_page?(single_page) } + else + c = options.delete(:controller) + a = options.delete(:action) - if c && a - # When given both options, make sure BOTH are true - current_controller?(*c) && current_action?(*a) - else - # Otherwise check EITHER option - current_controller?(*c) || current_action?(*a) - end - end + if c && a + # When given both options, make sure BOTH are true + current_controller?(*c) && current_action?(*a) + else + # Otherwise check EITHER option + current_controller?(*c) || current_action?(*a) + end + end if query = options.delete(:query) active_link &&= query.all? { |k,v| params[k] == v } diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index 4def791e9c6c..f0d6be7d18b1 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -16,8 +16,8 @@ module Sortable scope :order_created_asc, -> { reorder(created_at: :asc) } scope :order_updated_desc, -> { reorder(updated_at: :desc) } scope :order_updated_asc, -> { reorder(updated_at: :asc) } - scope :order_name_asc, -> { reorder("LOWER(#{quoted_table_name}.#{connection.quote_column_name('name')}) ASC") } - scope :order_name_desc, -> { reorder("LOWER(#{quoted_table_name}.#{connection.quote_column_name('name')}) DESC") } + scope :order_name_asc, -> { reorder(name: :asc) } + scope :order_name_desc, -> { reorder(name: :desc) } end module ClassMethods diff --git a/app/models/group.rb b/app/models/group.rb index 5a31b46920ca..5752a4d0fa80 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -19,7 +19,7 @@ class Group < Namespace include Gitlab::ConfigHelper include Referable - + has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember' alias_method :members, :group_members has_many :users, through: :group_members @@ -55,7 +55,7 @@ def visible_to_user(user) end def to_reference(_from_project = nil) - "#{self.class.reference_prefix}#{name}" + "#{self.class.reference_prefix}#{path}" end def human_name diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 6076e26f35b9..b050a4d01c33 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -36,12 +36,12 @@ - if @sort.present? = sort_options_hash[@sort] - else - = sort_title_name_asc + = sort_title_name %b.caret %ul.dropdown-menu %li - = link_to admin_users_path(sort: sort_value_name_asc, filter: params[:filter]) do - = sort_title_name_asc + = 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 diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index cad7ee2792d1..7a29b278bc77 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -5,12 +5,12 @@ %ul.nav-links = nav_link(page: [dashboard_projects_path, root_path]) do = link_to dashboard_projects_path(filter: @filter, sort: @sort), title: 'Your Projects', data: {placement: 'right'} do - = "Your Projects (#{@user_projects.size})" + Your Projects = nav_link(page: starred_dashboard_projects_path) do = link_to starred_dashboard_projects_path(filter: @filter, sort: @sort), title: 'Starred Projects', data: {placement: 'right'} do - = "Starred Projects (#{@starred_projects.size})" + Starred Projects = nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path], html_options: { class: 'hidden-xs' }) do - = link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do + = link_to explore_root_path, title: 'Explore Projects', data: {placement: 'right'} do Explore Projects = render 'shared/projects/controls' diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml index b9a958fbe7be..69066631c595 100644 --- a/app/views/explore/projects/index.html.haml +++ b/app/views/explore/projects/index.html.haml @@ -1,5 +1,6 @@ - page_title "Projects" - header_title "Projects", dashboard_projects_path +- @disable_projects_dropdown = true - if current_user = render 'dashboard/projects_head' diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml index 3ad7f0e2653e..919aaa3bb181 100644 --- a/app/views/explore/projects/starred.html.haml +++ b/app/views/explore/projects/starred.html.haml @@ -1,5 +1,6 @@ - page_title "Projects" - header_title "Projects", dashboard_projects_path +- @disable_projects_dropdown = true - if current_user = render 'dashboard/projects_head' diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml index cf3860f5a0a1..8e38cad21738 100644 --- a/app/views/explore/projects/trending.html.haml +++ b/app/views/explore/projects/trending.html.haml @@ -1,5 +1,6 @@ - page_title "Projects" - header_title "Projects", dashboard_projects_path +- @disable_projects_dropdown = true - if current_user = render 'dashboard/projects_head' diff --git a/app/views/groups/_header.html.haml b/app/views/groups/_header.html.haml index 7fa803c39dfa..9df5840e1559 100644 --- a/app/views/groups/_header.html.haml +++ b/app/views/groups/_header.html.haml @@ -3,10 +3,6 @@ - unless can?(current_user, :read_group, @group) - @disable_search_panel = true -= content_for :meta_tags do - - if current_user - = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") - .cover-block .cover-controls - if @group && can?(current_user, :admin_group, @group) @@ -17,11 +13,9 @@ .avatar-holder = link_to group_icon(@group), target: '_blank' do = image_tag group_icon(@group), class: "avatar group-avatar s90" - .cover-title - = @group.name + .cover-title= @group.name - .cover-desc.username - @#{@group.path} + .cover-desc.username= @group.to_reference - if @group.description.present? .cover-desc.description @@ -30,5 +24,5 @@ %ul.nav-links = nav_link(page: group_path(@group)) do = link_to 'Activity', group_path(@group) - = nav_link(page: projects_group_path(@group)) do - = link_to 'Projects', projects_group_path(@group) + = nav_link(page: group_projects_path(@group)) do + = link_to 'Projects', group_projects_path(@group) diff --git a/app/views/groups/_projects_head.html.haml b/app/views/groups/_projects_head.html.haml index c1384421c521..2b5f548563c4 100644 --- a/app/views/groups/_projects_head.html.haml +++ b/app/views/groups/_projects_head.html.haml @@ -1,15 +1,15 @@ .nav-block %ul.nav-links - = nav_link(page: projects_group_path, query: { scope: nil }) do - = link_to projects_group_path(@group, sort: @sort), title: "All projects from the '#{@group.name}' group", data: {placement: 'right'} do - = "All (#{@all_projects.size})" + = nav_link(page: group_projects_path, query: { scope: nil }) do + = link_to group_projects_path(@group, sort: @sort), title: "All projects from the '#{@group.name}' group", data: {placement: 'right'} do + All - if current_user - = nav_link(page: projects_group_path, query: { scope: 'contributed' }) do - = link_to projects_group_path(@group, scope: 'contributed', sort: @sort), title: "Projects from the '#{@group.name}' group you contributed to", data: {placement: 'right'} do - = "Contributed (#{@contributed_projects.size})" - = nav_link(page: projects_group_path, query: { scope: 'starred' }) do - = link_to projects_group_path(@group, scope: 'starred', sort: @sort), title: "Starred projects from the '#{@group.name}' group", data: {placement: 'right'} do - = "Starred (#{@starred_projects.size})" + = nav_link(page: group_projects_path, query: { scope: 'contributed' }) do + = link_to group_projects_path(@group, scope: 'contributed', sort: @sort), title: "Projects from the '#{@group.name}' group you contributed to", data: {placement: 'right'} do + Contributed + = nav_link(page: group_projects_path, query: { scope: 'starred' }) do + = link_to group_projects_path(@group, scope: 'starred', sort: @sort), title: "Starred projects from the '#{@group.name}' group", data: {placement: 'right'} do + Starred = render 'shared/projects/controls' diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 4ad8e7e87463..bea6f3573103 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -1,5 +1,3 @@ -- page_title "Settings" -- header_title group_title(@group, "Settings", edit_group_path(@group)) - @blank_container = true .panel.panel-default.prepend-top-default diff --git a/app/views/groups/projects/edit.html.haml b/app/views/groups/projects/edit.html.haml index ca5568dde206..52b64e9aa256 100644 --- a/app/views/groups/projects/edit.html.haml +++ b/app/views/groups/projects/edit.html.haml @@ -1,5 +1,4 @@ - page_title "Projects" -- header_title group_title(@group, "Projects", edit_group_projects_path(@group)) .panel.panel-default.prepend-top-default .panel-heading diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects/index.html.haml similarity index 53% rename from app/views/groups/projects.html.haml rename to app/views/groups/projects/index.html.haml index e2b0d6cbeef2..d2ee1f6d004b 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects/index.html.haml @@ -1,10 +1,11 @@ -= render 'header' +- @disable_dropdown_filters = true += render 'groups/header' %div{class: container_class} .tab-content .tab-pane.active - if can?(current_user, :read_group, @group) && @projects.any? - = render 'projects_head' - = render 'projects', projects: @projects + = render 'groups/projects_head' + = render 'groups/projects', projects: @projects - else .nothing-here-block No projects to see diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 5f087151c5fa..419250f59481 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,3 +1,7 @@ += content_for :meta_tags do + - if current_user + = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") + = render 'header' %div{class: container_class} diff --git a/app/views/layouts/group_settings.html.haml b/app/views/layouts/group_settings.html.haml index d3032af47ef8..c236d48420c8 100644 --- a/app/views/layouts/group_settings.html.haml +++ b/app/views/layouts/group_settings.html.haml @@ -1,3 +1,5 @@ +- page_title "Settings" +- header_title group_title(@group, "Settings", edit_group_path(@group)) - sidebar "group_settings" = render template: "layouts/group" diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml index af3d35de325d..37a768b5fe80 100644 --- a/app/views/shared/_sort_dropdown.html.haml +++ b/app/views/shared/_sort_dropdown.html.haml @@ -6,7 +6,7 @@ - else = sort_title_recently_created %b.caret - %ul.dropdown-menu.dropdown-menu-align-right + %ul.dropdown-menu.align-right %li = link_to page_filter_path(sort: sort_value_recently_created) do = sort_title_recently_created diff --git a/app/views/shared/projects/_controls.html.haml b/app/views/shared/projects/_controls.html.haml index f196ca92909a..54a48a417a04 100644 --- a/app/views/shared/projects/_controls.html.haml +++ b/app/views/shared/projects/_controls.html.haml @@ -1,9 +1,9 @@ .controls.projects-controls .form-inline.pull-right - .form-group= search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control hidden-xs', spellcheck: false + .form-group.hidden-xs.hidden-sm= search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control', spellcheck: false - - unless current_controller?('explore/projects') - .form-group.prepend-left-10= render 'shared/projects/dropdown' + - unless @disable_projects_dropdown + .form-group.prepend-left-10.hidden-xs= render 'shared/projects/dropdown' - if current_user && current_user.can_create_project? .form-group.prepend-left-10 diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml index 26df9a8d7fd8..2b6fcb831581 100644 --- a/app/views/shared/projects/_dropdown.html.haml +++ b/app/views/shared/projects/_dropdown.html.haml @@ -1,21 +1,15 @@ .dropdown.inline %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %span.light - - if sort_options_hash[@sort].present? - = sort_options_hash[@sort] - - else - = sort_title_recently_active + = sort_options_hash[@sort] || sort_title_recently_active %b.caret - %ul.dropdown-menu.dropdown-menu-align-right - %li.dropdown-label Sort by - %li - = link_to_sort sort_title_recently_active, sort_value_recently_active, @sort - = link_to_sort sort_title_stars, sort_value_stars, @sort - = link_to_sort sort_title_name_asc, sort_value_name_asc, @sort - = link_to_sort sort_title_name_desc, sort_value_name_desc, @sort + %ul.dropdown-menu.align-right.with-label + %li Sort by + %li= link_to_sort sort_title_recently_active, sort_value_recently_active, @sort + %li= link_to_sort sort_title_stars, sort_value_stars, @sort + %li= link_to_sort sort_title_name, sort_value_name, @sort - - if current_user && !current_controller?(:groups) + - if current_user && !@disable_dropdown_filters %hr - %li - = link_to_filter 'Owned by anyone', 'all', @filter - = link_to_filter 'Owned by me', 'personal', @filter + %li= link_to_filter 'Owned by anyone', 'all', @filter + %li= link_to_filter 'Owned by me', 'personal', @filter diff --git a/config/routes.rb b/config/routes.rb index 66aec7e3c36b..fb7da1f59ecc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -346,11 +346,12 @@ member do get :issues get :merge_requests - get :projects end scope module: :groups do - resource :projects, only: [:edit] + resources :projects, only: [:index] do + get :edit, on: :collection + end resources :group_members, only: [:index, :create, :update, :destroy] do post :resend_invite, on: :member delete :leave, on: :collection diff --git a/features/dashboard/projects.feature b/features/dashboard/projects.feature index 8b752058b82c..38f9146c8640 100644 --- a/features/dashboard/projects.feature +++ b/features/dashboard/projects.feature @@ -14,8 +14,8 @@ Feature: Dashboard Projects And I visit dashboard projects page Scenario: I should see projects list - Then I should see "Your projects (3)" - Then I should see "Starred projects (2)" + Then I should see "Your projects" + Then I should see "Starred projects" Then I should see "Community" project link Then I should see "Forum" project link Then I should see "Shop" project link @@ -28,14 +28,10 @@ Feature: Dashboard Projects And I sort projects list by "Most stars" Then I should see "Community" at the top - Scenario: I sort projects by name from A to Z - And I sort projects list by "Name from A to Z" + Scenario: I sort projects by Name + And I sort projects list by "Name" Then I should see "Community" at the top - Scenario: I sort projects by name from Z to A - And I sort projects list by "Name from Z to A" - Then I should see "Shop" at the top - Scenario: I filter to see only my own projects, I should see projects list And I filter to see only my own projects Then I should not see "Community" project link @@ -52,12 +48,7 @@ Feature: Dashboard Projects And I sort projects list by "Most stars" Then I should see "Forum" at the top - Scenario: I filter to see only my own projects, I sort projects by name from A to Z + Scenario: I filter to see only my own projects, I sort projects by Name And I filter to see only my own projects - And I sort projects list by "Name from A to Z" + And I sort projects list by "Name" Then I should see "Forum" at the top - - Scenario: I filter to see only my own projects, I sort projects by name from Z to A - And I filter to see only my own projects - And I sort projects list by "Name from Z to A" - Then I should see "Shop" at the top diff --git a/features/dashboard/starred_projects.feature b/features/dashboard/starred_projects.feature index ac884cb4ff53..18e46b8c9047 100644 --- a/features/dashboard/starred_projects.feature +++ b/features/dashboard/starred_projects.feature @@ -17,8 +17,8 @@ Feature: Dashboard Starred Projects And I visit dashboard starred projects page Scenario: I should see projects list - Then I should see "Your projects (4)" - Then I should see "Starred projects (3)" + Then I should see "Your projects" + Then I should see "Starred projects" Then I should see "Community" project link Then I should see "Forum" project link Then I should see "Grocery" project link @@ -32,14 +32,10 @@ Feature: Dashboard Starred Projects And I sort projects list by "Most stars" Then I should see "Community" at the top - Scenario: I sort projects by name from A to Z - And I sort projects list by "Name from A to Z" + Scenario: I sort projects by Name + And I sort projects list by "Name" Then I should see "Community" at the top - Scenario: I sort projects by name from Z to A - And I sort projects list by "Name from Z to A" - Then I should see "Grocery" at the top - Scenario: I filter to see only my own projects, I should see projects list And I filter to see only my own projects Then I should not see "Community" project link @@ -56,12 +52,7 @@ Feature: Dashboard Starred Projects And I sort projects list by "Most stars" Then I should see "Forum" at the top - Scenario: I filter to see only my own projects, I sort projects by name from A to Z + Scenario: I filter to see only my own projects, I sort projects by Name And I filter to see only my own projects - And I sort projects list by "Name from A to Z" + And I sort projects list by "Name" Then I should see "Forum" at the top - - Scenario: I filter to see only my own projects, I sort projects by name from Z to A - And I filter to see only my own projects - And I sort projects list by "Name from Z to A" - Then I should see "Grocery" at the top diff --git a/features/groups.feature b/features/groups.feature index a9e833b2e910..e283f3aff5e5 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -20,9 +20,9 @@ Feature: Groups Scenario: I should see group "Owned" projects list When I visit group "Owned" projects page Then I should see a nav block - Then I should see "All (4)" - Then I should see "Contributed (1)" - Then I should see "Starred (2)" + Then I should see "All" + Then I should see "Contributed" + Then I should see "Starred" Then I should see group "Owned" projects list Scenario: I sort projects by recent activity @@ -35,16 +35,11 @@ Feature: Groups And I sort projects list by "Most stars" Then I should see "Star-project" at the top - Scenario: I sort projects by name from A to Z + Scenario: I sort projects by Name When I visit group "Owned" projects page - And I sort projects list by "Name from A to Z" + And I sort projects list by "Name" Then I should see "Moon-project" at the top - Scenario: I sort projects by name from Z to A - When I visit group "Owned" projects page - And I sort projects list by "Name from Z to A" - Then I should see "Star-project" at the top - Scenario: I should see group "Owned" issues list Given project from group "Owned" has issues assigned to me When I visit group "Owned" issues page @@ -95,5 +90,5 @@ Feature: Groups And I am a signed out user When I visit group "Owned" projects page Then I should see group "Owned" - Then I should see "All (2)" + Then I should see "All" Then I should see project "Public-project" diff --git a/features/steps/dashboard/projects.rb b/features/steps/dashboard/projects.rb index e6d141e6ee7f..755531f36e87 100644 --- a/features/steps/dashboard/projects.rb +++ b/features/steps/dashboard/projects.rb @@ -3,11 +3,11 @@ class Spinach::Features::DashboardProjects < Spinach::FeatureSteps include SharedPaths include SharedProject - step 'I should see "Your Projects (3)"' do - expect(page).to have_link 'Your Projects (3)' + step 'I should see "Your Projects"' do + expect(page).to have_link 'Your Projects' end - step 'I should see "Starred Projects (2)"' do - expect(page).to have_link 'Starred Projects (2)' + step 'I should see "Starred Projects"' do + expect(page).to have_link 'Starred Projects' end end diff --git a/features/steps/dashboard/starred_projects.rb b/features/steps/dashboard/starred_projects.rb index 8cb1c860896f..69dd4336dd19 100644 --- a/features/steps/dashboard/starred_projects.rb +++ b/features/steps/dashboard/starred_projects.rb @@ -3,11 +3,11 @@ class Spinach::Features::DashboardStarredProjects < Spinach::FeatureSteps include SharedPaths include SharedProject - step 'I should see "Your Projects (4)"' do - expect(page).to have_link 'Your Projects (4)' + step 'I should see "Your Projects"' do + expect(page).to have_link 'Your Projects' end - step 'I should see "Starred Projects (3)"' do - expect(page).to have_link 'Starred Projects (3)' + step 'I should see "Starred Projects"' do + expect(page).to have_link 'Starred Projects' end end diff --git a/features/steps/explore/groups.rb b/features/steps/explore/groups.rb index d216d226604c..2ba3ea9d1be7 100644 --- a/features/steps/explore/groups.rb +++ b/features/steps/explore/groups.rb @@ -23,7 +23,7 @@ class Spinach::Features::ExploreGroups < Spinach::FeatureSteps end step 'I visit group "TestGroup" page' do - visit projects_group_path(Group.find_by(name: "TestGroup")) + visit group_projects_path(Group.find_by(name: "TestGroup")) end step 'I visit group "TestGroup" issues page' do diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 09245e64a997..68c642c92e3c 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -13,20 +13,16 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect(page).to have_content '@owned' end - step 'I should see "All (4)"' do - expect(page).to have_link "All (4)" + step 'I should see "All"' do + expect(page).to have_link "All" end - step 'I should see "All (2)"' do - expect(page).to have_link "All (2)" + step 'I should see "Contributed"' do + expect(page).to have_link "Contributed" end - step 'I should see "Contributed (1)"' do - expect(page).to have_link "Contributed (1)" - end - - step 'I should see "Starred (2)"' do - expect(page).to have_link "Starred (2)" + step 'I should see "Starred"' do + expect(page).to have_link "Starred" end step 'I am a signed out user' do diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb index eb6df61b8e6b..8e36d71c966b 100644 --- a/features/steps/shared/note.rb +++ b/features/steps/shared/note.rb @@ -147,7 +147,7 @@ module SharedNote step 'I sort the list by "Last updated"' do find('button.dropdown-toggle.btn').click - page.within('ul.dropdown-menu.dropdown-menu-align-right li') do + page.within('ul.dropdown-menu.align-right li') do click_link "Last updated" end end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 73fa606b7d99..e81304812d58 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -48,7 +48,7 @@ module SharedPaths end step 'I visit group "Owned" projects page' do - visit projects_group_path(Group.find_by(name: "Owned")) + visit group_projects_path(Group.find_by(name: "Owned")) end step 'I visit group "Owned" projects edit page' do diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 9b808e77e458..2833824d53f6 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -292,12 +292,8 @@ def current_project sort_by('Most stars') end - step 'I sort projects list by "Name from A to Z"' do - sort_by('Name from A to Z') - end - - step 'I sort projects list by "Name from Z to A"' do - sort_by('Name from Z to A') + step 'I sort projects list by "Name"' do + sort_by('Name') end step 'I should see "Community" at the top' do diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 3c995053eecf..fcd940be8a13 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -58,7 +58,7 @@ describe '#to_reference' do it 'returns a String reference to the object' do - expect(group.to_reference).to eq "@#{group.name}" + expect(group.to_reference).to eq "@#{group.path}" end end -- GitLab From 702973752703d22cb8cb938d9d41c8b771c4d142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 27 Jan 2016 15:32:29 +0100 Subject: [PATCH 07/14] Fix specs and address latest MR feedback --- app/controllers/concerns/projects_listing.rb | 4 ++-- app/controllers/dashboard/projects_controller.rb | 4 ++-- app/controllers/groups/group_members_controller.rb | 4 ++-- app/controllers/groups/milestones_controller.rb | 2 +- app/controllers/groups/projects_controller.rb | 2 +- features/groups.feature | 2 +- features/steps/groups.rb | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/controllers/concerns/projects_listing.rb b/app/controllers/concerns/projects_listing.rb index eee3e6768c4a..3db989a49336 100644 --- a/app/controllers/concerns/projects_listing.rb +++ b/app/controllers/concerns/projects_listing.rb @@ -19,10 +19,10 @@ def whitelist_filter(filter, default) end def whitelist_sort(sort, default) - sort_options_hash.keys.include?(sort) ? sort : default + sort_options_hash.has_key?(sort) ? sort : default end - def prepare_for_listing(relation) + def filter_listing(relation) apply_filter(apply_base_scopes(relation)).sort(@sort) end diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 312e0342ba70..a5e4f99c3ce1 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -4,7 +4,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController before_action :init_filter_and_sort, :load_last_push, :event_filter def index - @projects = prepare_for_listing(current_user.authorized_projects) + @projects = filter_listing(current_user.authorized_projects) respond_to do |format| format.html @@ -16,7 +16,7 @@ def index end def starred - @projects = prepare_for_listing(current_user.starred_projects) + @projects = filter_listing(current_user.starred_projects) @groups = [] respond_to do |format| diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 0e902c4bb434..09a6d889fae7 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -67,10 +67,10 @@ def leave if can?(current_user, :destroy_group_member, @group_member) @group_member.destroy - redirect_to(dashboard_groups_path, notice: "You left #{group.name} group.") + redirect_to(dashboard_groups_path, notice: "You left #{@group.name} group.") else if @group.last_owner?(current_user) - redirect_to(dashboard_groups_path, alert: "You can not leave #{group.name} group because you're the last owner. Transfer or delete the group.") + redirect_to(dashboard_groups_path, alert: "You can not leave #{@group.name} group because you're the last owner. Transfer or delete the group.") else return render_403 end diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 0c2a350bc39e..9ad44266de5a 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -38,7 +38,7 @@ def update private def authorize_group_milestone! - return render_404 unless can?(current_user, :admin_milestones, group) + return render_404 unless can?(current_user, :admin_milestones, @group) end def milestone_params diff --git a/app/controllers/groups/projects_controller.rb b/app/controllers/groups/projects_controller.rb index 57f41283772f..91577e64696d 100644 --- a/app/controllers/groups/projects_controller.rb +++ b/app/controllers/groups/projects_controller.rb @@ -13,7 +13,7 @@ class Groups::ProjectsController < Groups::ApplicationController def index @projects = if current_user - prepare_for_listing(find_scoped_project(params[:scope])) + filter_listing(find_scoped_project(params[:scope])) else find_projects end diff --git a/features/groups.feature b/features/groups.feature index e283f3aff5e5..ab997219c988 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -5,7 +5,7 @@ Feature: Groups And Group "Owned" has a public project "Public-project" And Group "Owned" has a public project "Star-project" with 2 stars including 1 from "John Doe" And Group "Owned" has an internal project "Moon-project" with 1 star from "John Doe" - And project "Moon-project" has push event + And project "Moon-project" is the latest active Scenario: I should have back to group button When I visit group "Owned" page diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 68c642c92e3c..f01aa5f09854 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -58,8 +58,8 @@ class Spinach::Features::Groups < Spinach::FeatureSteps user_exists('John Doe').toggle_star(@project) end - step 'project "Moon-project" has push event' do - event_for_project(Project.find_by(name: "Moon-project"), user_exists('John Doe')) + step 'project "Moon-project" is the latest active' do + Project.find_by(name: "Moon-project").update_column(:last_activity_at, Time.now + 10.seconds) end step 'I should see project "Public-project"' do -- GitLab From 2bd801292cc13923d4c13f594e2e41a98ff8c484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 27 Jan 2016 16:17:50 +0100 Subject: [PATCH 08/14] Addressing latest MR feedbacks --- app/views/dashboard/_projects_head.html.haml | 2 +- app/views/explore/projects/index.html.haml | 3 +-- app/views/explore/projects/starred.html.haml | 3 +-- app/views/explore/projects/trending.html.haml | 3 +-- app/views/shared/projects/_controls.html.haml | 2 +- config/routes.rb | 1 + 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index 7a29b278bc77..f84b9f7010e4 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -13,4 +13,4 @@ = link_to explore_root_path, title: 'Explore Projects', data: {placement: 'right'} do Explore Projects - = render 'shared/projects/controls' + = render 'shared/projects/controls', disable_projects: defined?(disable_projects_dropdown) && disable_projects_dropdown diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml index 69066631c595..8cb75e5b4da1 100644 --- a/app/views/explore/projects/index.html.haml +++ b/app/views/explore/projects/index.html.haml @@ -1,9 +1,8 @@ - page_title "Projects" - header_title "Projects", dashboard_projects_path -- @disable_projects_dropdown = true - if current_user - = render 'dashboard/projects_head' + = render 'dashboard/projects_head', disable_projects_dropdown: true - else = render 'explore/head' diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml index 919aaa3bb181..ac489a135b62 100644 --- a/app/views/explore/projects/starred.html.haml +++ b/app/views/explore/projects/starred.html.haml @@ -1,9 +1,8 @@ - page_title "Projects" - header_title "Projects", dashboard_projects_path -- @disable_projects_dropdown = true - if current_user - = render 'dashboard/projects_head' + = render 'dashboard/projects_head', disable_projects_dropdown: true - else = render 'explore/head' diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml index 8e38cad21738..b7f220d288d8 100644 --- a/app/views/explore/projects/trending.html.haml +++ b/app/views/explore/projects/trending.html.haml @@ -1,9 +1,8 @@ - page_title "Projects" - header_title "Projects", dashboard_projects_path -- @disable_projects_dropdown = true - if current_user - = render 'dashboard/projects_head' + = render 'dashboard/projects_head', disable_projects_dropdown: true - else = render 'explore/head' diff --git a/app/views/shared/projects/_controls.html.haml b/app/views/shared/projects/_controls.html.haml index 54a48a417a04..c18bbd346f6a 100644 --- a/app/views/shared/projects/_controls.html.haml +++ b/app/views/shared/projects/_controls.html.haml @@ -2,7 +2,7 @@ .form-inline.pull-right .form-group.hidden-xs.hidden-sm= search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control', spellcheck: false - - unless @disable_projects_dropdown + - unless defined?(disable_projects) && disable_projects .form-group.prepend-left-10.hidden-xs= render 'shared/projects/dropdown' - if current_user && current_user.can_create_project? diff --git a/config/routes.rb b/config/routes.rb index fb7da1f59ecc..0b306232a33e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -352,6 +352,7 @@ resources :projects, only: [:index] do get :edit, on: :collection end + resources :group_members, only: [:index, :create, :update, :destroy] do post :resend_invite, on: :member delete :leave, on: :collection -- GitLab From d3a514149a6fcd79474902edd3c9c3321cd8c9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 28 Jan 2016 09:54:50 +0100 Subject: [PATCH 09/14] Ensure specs do not fail randomly --- features/dashboard/projects.feature | 4 +- features/dashboard/starred_projects.feature | 4 +- features/steps/shared/project.rb | 48 ++++++++++++--------- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/features/dashboard/projects.feature b/features/dashboard/projects.feature index 38f9146c8640..daa7ffdde0d0 100644 --- a/features/dashboard/projects.feature +++ b/features/dashboard/projects.feature @@ -8,8 +8,8 @@ Feature: Dashboard Projects And I am member of a project "Community" with a guest role And I starred project "Community" And I own project "Shop" - And project "Shop" has push event - And project "Community" has push event + And project "Shop" is the latest active + And project "Community" is the latest active And "John Doe" starred project "Community" And I visit dashboard projects page diff --git a/features/dashboard/starred_projects.feature b/features/dashboard/starred_projects.feature index 18e46b8c9047..e1ba7f2c4d26 100644 --- a/features/dashboard/starred_projects.feature +++ b/features/dashboard/starred_projects.feature @@ -10,8 +10,8 @@ Feature: Dashboard Starred Projects And I own project "Shop" And I own project "Grocery" And I starred project "Grocery" - And project "Grocery" has push event - And project "Community" has push event + And project "Grocery" is the latest active + And project "Community" is the latest active And "John Doe" starred project "Community" And "John Doe" starred project "Forum" And I visit dashboard starred projects page diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 2833824d53f6..8549e5cad481 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -8,9 +8,7 @@ module SharedProject step "I own a project" do @project = user_owns_project( user: @user, - project_name: 'Internal', - project_type: :project, - visibility: :internal + project_type: :project ) end @@ -23,9 +21,8 @@ module SharedProject step 'I own project "Shop"' do @project = user_owns_project( user: @user, - project_name: 'Shop', + name: 'Shop', project_type: :project, - visibility: :internal, snippets_enabled: true ) end @@ -52,7 +49,7 @@ module SharedProject step 'I own project "Forum"' do @project = user_owns_project( user: @user, - project_name: 'Forum', + name: 'Forum', project_type: :project, path: 'forum_project' ) @@ -62,7 +59,7 @@ module SharedProject step 'I own project "Grocery"' do @project = user_owns_project( user: @user, - project_name: 'Grocery' + name: 'Grocery' ) end @@ -70,7 +67,7 @@ module SharedProject step 'I own an empty project' do @project = user_owns_project( user: @user, - project_name: 'Empty Project' + name: 'Empty Project' ) end @@ -89,14 +86,17 @@ module SharedProject event_for_project(@project) end - step 'project "Grocery" has push event' do - @project = Project.find_by(name: "Grocery") - event_for_project(@project) + step 'project "Shop" is the latest active' do + set_project_as_latest_active("Shop") + end - step 'project "Community" has push event' do - @project = Project.find_by(name: "Community") - event_for_project(@project) + step 'project "Grocery" is the latest active' do + set_project_as_latest_active("Grocery") + end + + step 'project "Community" is the latest active' do + set_project_as_latest_active("Community") end step 'I should see project "Shop" activity feed' do @@ -196,21 +196,21 @@ def current_project step '"John Doe" owns private project "Enterprise"' do user_owns_project( user: 'John Doe', - project_name: 'Enterprise' + name: 'Enterprise' ) end step '"Mary Jane" owns private project "Enterprise"' do user_owns_project( user: 'Mary Jane', - project_name: 'Enterprise' + name: 'Enterprise' ) end step '"John Doe" owns internal project "Internal"' do user_owns_project( user: 'John Doe', - project_name: 'Internal', + name: 'Internal', visibility: :internal ) end @@ -218,7 +218,7 @@ def current_project step '"John Doe" owns public project "Community"' do user_owns_project( user: 'John Doe', - project_name: 'Community', + name: 'Community', visibility: :public ) end @@ -382,6 +382,10 @@ def event_for_project(project, user = nil) ) end + def set_project_as_latest_active(project_name) + Project.find_by(name: project_name).update_column(:last_activity_at, Time.now + 10.seconds) + end + def sort_by(sort) find('button.dropdown-toggle.btn').click page.within('ul.dropdown-menu') do @@ -404,10 +408,12 @@ def expect_link_in_list(project_name, truthy = true) expect(page.find('ul.projects-list')).send((truthy ? 'to' : 'not_to'), have_content(project_name)) end - def user_owns_project(user:, project_name: nil, project_type: :empty_project, visibility: :private, **args) + def user_owns_project(user:, project_type: :empty_project, visibility: nil, **args) user = user.is_a?(User) ? user : user_exists(user, username: user.gsub(/\s/, '').underscore) - project = Project.find_by(name: project_name) - project ||= create(project_type, visibility, name: project_name, namespace: user.namespace, **args) + project = Project.find_by(name: args[:name]) + + attributes = { namespace: user.namespace, **args } + project ||= visibility ? create(project_type, visibility, **attributes) : create(project_type, **attributes) project.team << [user, :master] project -- GitLab From 2221ed87007878682391b851db22e8dd2aa55bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 28 Jan 2016 14:34:42 +0100 Subject: [PATCH 10/14] Add Group > Activity page and in the group nav bar --- app/assets/javascripts/dispatcher.js.coffee | 2 +- app/controllers/groups_controller.rb | 10 ++++----- app/views/groups/_header.html.haml | 6 ++--- app/views/groups/_projects_head.html.haml | 2 +- ...how.atom.builder => activity.atom.builder} | 0 .../{show.html.haml => activity.html.haml} | 2 ++ app/views/groups/projects/index.html.haml | 22 +++++++++---------- app/views/layouts/nav/_group.html.haml | 11 +++++++--- app/views/shared/projects/_controls.html.haml | 5 ++--- app/views/shared/projects/_dropdown.html.haml | 2 +- config/routes.rb | 2 ++ 11 files changed, 36 insertions(+), 28 deletions(-) rename app/views/groups/{show.atom.builder => activity.atom.builder} (100%) rename app/views/groups/{show.html.haml => activity.html.haml} (88%) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 2cdf01d874cd..4217f7ff5a23 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -74,7 +74,7 @@ class Dispatcher shortcut_handler = new ShortcutsNavigation() when 'projects:show' shortcut_handler = new ShortcutsNavigation() - when 'groups:show' + when 'groups:activity' new Activities() shortcut_handler = new ShortcutsNavigation() when 'groups:group_members:index' diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 9448a47d1322..5af99edfcb3f 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -2,17 +2,17 @@ class GroupsController < Groups::ApplicationController include IssuesAction include MergeRequestsAction - skip_before_action :authenticate_user!, only: [:show, :projects, :issues, :merge_requests] + skip_before_action :authenticate_user!, only: [:activity, :issues, :merge_requests] respond_to :html before_action :find_group, except: [:new, :create] # Authorize - before_action :authorize_read_group!, except: [:show, :new, :create, :autocomplete] + before_action :authorize_read_group!, except: [:activity, :new, :create, :autocomplete] before_action :authorize_admin_group!, only: [:edit, :update, :destroy] before_action :authorize_create_group!, only: [:new, :create] - before_action :find_projects, only: [:show, :issues, :merge_requests] - before_action :event_filter, only: :show + before_action :find_projects, only: [:activity, :issues, :merge_requests] + before_action :event_filter, only: :activity layout :determine_layout @@ -36,7 +36,7 @@ def create end end - def show + def activity @last_push = current_user.recent_push if current_user respond_to do |format| diff --git a/app/views/groups/_header.html.haml b/app/views/groups/_header.html.haml index 9df5840e1559..0bc67d7d33d8 100644 --- a/app/views/groups/_header.html.haml +++ b/app/views/groups/_header.html.haml @@ -22,7 +22,7 @@ = markdown(@group.description, pipeline: :description) %ul.nav-links - = nav_link(page: group_path(@group)) do - = link_to 'Activity', group_path(@group) - = nav_link(page: group_projects_path(@group)) do + = nav_link(path: 'groups#activity') do + = link_to 'Activity', activity_group_path(@group) + = nav_link(path: 'groups/projects#index') do = link_to 'Projects', group_projects_path(@group) diff --git a/app/views/groups/_projects_head.html.haml b/app/views/groups/_projects_head.html.haml index 2b5f548563c4..d9e402282b15 100644 --- a/app/views/groups/_projects_head.html.haml +++ b/app/views/groups/_projects_head.html.haml @@ -12,4 +12,4 @@ = link_to group_projects_path(@group, scope: 'starred', sort: @sort), title: "Starred projects from the '#{@group.name}' group", data: {placement: 'right'} do Starred - = render 'shared/projects/controls' + = render 'shared/projects/controls', disable_dropdown_filters: true diff --git a/app/views/groups/show.atom.builder b/app/views/groups/activity.atom.builder similarity index 100% rename from app/views/groups/show.atom.builder rename to app/views/groups/activity.atom.builder diff --git a/app/views/groups/show.html.haml b/app/views/groups/activity.html.haml similarity index 88% rename from app/views/groups/show.html.haml rename to app/views/groups/activity.html.haml index 419250f59481..1958d4a8faa7 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/activity.html.haml @@ -1,3 +1,5 @@ +- header_title group_title(@group, "Activity", activity_group_path(@group)) + = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") diff --git a/app/views/groups/projects/index.html.haml b/app/views/groups/projects/index.html.haml index d2ee1f6d004b..d035f60f6245 100644 --- a/app/views/groups/projects/index.html.haml +++ b/app/views/groups/projects/index.html.haml @@ -1,11 +1,11 @@ -- @disable_dropdown_filters = true -= render 'groups/header' - -%div{class: container_class} - .tab-content - .tab-pane.active - - if can?(current_user, :read_group, @group) && @projects.any? - = render 'groups/projects_head' - = render 'groups/projects', projects: @projects - - else - .nothing-here-block No projects to see +- header_title group_title(@group, "Projects", group_projects_path(@group)) += render 'groups/header' + +%div{class: container_class} + .tab-content + .tab-pane.active + - if can?(current_user, :read_group, @group) && @projects.any? + = render 'groups/projects_head' + = render 'groups/projects', projects: @projects + - else + .nothing-here-block No projects to see diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index e5e2a59eaedb..a3bb7399b404 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -7,11 +7,16 @@ %li.separate-item - = nav_link(path: 'groups#show', html_options: {class: 'home'}) do - = link_to group_path(@group), title: 'Home' do + = nav_link(path: 'groups#activity', html_options: {class: 'home'}) do + = link_to activity_group_path(@group), title: 'Home' do = icon('dashboard fw') %span - Group + Activity + = nav_link(path: 'groups/projects#index') do + = link_to group_projects_path(@group), title: 'Projects' do + = icon('cube fw') + %span + Projects - if can?(current_user, :read_group, @group) - if current_user = nav_link(controller: [:group, :milestones]) do diff --git a/app/views/shared/projects/_controls.html.haml b/app/views/shared/projects/_controls.html.haml index c18bbd346f6a..4bcb6f0d10cb 100644 --- a/app/views/shared/projects/_controls.html.haml +++ b/app/views/shared/projects/_controls.html.haml @@ -3,10 +3,9 @@ .form-group.hidden-xs.hidden-sm= search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control', spellcheck: false - unless defined?(disable_projects) && disable_projects - .form-group.prepend-left-10.hidden-xs= render 'shared/projects/dropdown' + .form-group.prepend-left-10.hidden-xs= render 'shared/projects/dropdown', disable_filters: defined?(disable_dropdown_filters) && disable_dropdown_filters - if current_user && current_user.can_create_project? .form-group.prepend-left-10 = link_to new_project_path, class: 'btn btn-green' do - %i.fa.fa-plus - New Project + = icon('plus', text: 'New Project') diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml index 2b6fcb831581..e8e1a2190f67 100644 --- a/app/views/shared/projects/_dropdown.html.haml +++ b/app/views/shared/projects/_dropdown.html.haml @@ -9,7 +9,7 @@ %li= link_to_sort sort_title_stars, sort_value_stars, @sort %li= link_to_sort sort_title_name, sort_value_name, @sort - - if current_user && !@disable_dropdown_filters + - unless !current_user || (defined?(disable_filters) && disable_filters) %hr %li= link_to_filter 'Owned by anyone', 'all', @filter %li= link_to_filter 'Owned by me', 'personal', @filter diff --git a/config/routes.rb b/config/routes.rb index 0b306232a33e..85d77b529791 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -344,6 +344,8 @@ # resources :groups, constraints: { id: /[a-zA-Z.0-9_\-]+(? Date: Thu, 28 Jan 2016 18:47:11 +0100 Subject: [PATCH 11/14] Fix groups/:id/activity route redirect --- app/controllers/groups_controller.rb | 8 ++++++-- config/routes.rb | 1 - features/steps/dashboard/group.rb | 2 +- spec/routing/routing_spec.rb | 12 ++++++++++-- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 5af99edfcb3f..562486279d4b 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -2,12 +2,12 @@ class GroupsController < Groups::ApplicationController include IssuesAction include MergeRequestsAction - skip_before_action :authenticate_user!, only: [:activity, :issues, :merge_requests] + skip_before_action :authenticate_user!, only: [:show, :activity, :issues, :merge_requests] respond_to :html before_action :find_group, except: [:new, :create] # Authorize - before_action :authorize_read_group!, except: [:activity, :new, :create, :autocomplete] + before_action :authorize_read_group!, except: [:show, :activity, :new, :create, :autocomplete] before_action :authorize_admin_group!, only: [:edit, :update, :destroy] before_action :authorize_create_group!, only: [:new, :create] @@ -36,6 +36,10 @@ def create end end + def show + redirect_to activity_group_path(@group) + end + def activity @last_push = current_user.recent_push if current_user diff --git a/config/routes.rb b/config/routes.rb index 85d77b529791..07ef015489ec 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -344,7 +344,6 @@ # resources :groups, constraints: { id: /[a-zA-Z.0-9_\-]+(? Date: Fri, 29 Jan 2016 11:13:55 +0100 Subject: [PATCH 12/14] Fix specs broken by last commit --- features/steps/invites.rb | 2 +- spec/routing/routing_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/steps/invites.rb b/features/steps/invites.rb index dac972172aa0..57e62cf9c5c9 100644 --- a/features/steps/invites.rb +++ b/features/steps/invites.rb @@ -51,7 +51,7 @@ class Spinach::Features::Invites < Spinach::FeatureSteps step 'I should be redirected to the group page' do group = Group.find_by(name: "Owned") - expect(current_path).to eq(group_path(group)) + expect(current_path).to eq(activity_group_path(group)) end step 'I should see a notice telling me I have access' do diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb index 708831240b69..1c48a8a9184d 100644 --- a/spec/routing/routing_spec.rb +++ b/spec/routing/routing_spec.rb @@ -241,7 +241,7 @@ describe "Groups", "routing" do it "/groups/1" do - expect(get("/groups/1")).to route_to('groups#activity', id: '1') + expect(get("/groups/1")).to route_to('groups#show', id: '1') end it "/groups/1/activity" do -- GitLab From c79c441cb83d3dd6c9654f2627e4bcf42edbcc75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 29 Jan 2016 15:00:57 +0100 Subject: [PATCH 13/14] Re-add code that didn't need to be removed --- app/controllers/groups_controller.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 562486279d4b..e8c0e56facc6 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -2,12 +2,12 @@ class GroupsController < Groups::ApplicationController include IssuesAction include MergeRequestsAction - skip_before_action :authenticate_user!, only: [:show, :activity, :issues, :merge_requests] + skip_before_action :authenticate_user!, only: [:index, :show, :activity, :issues, :merge_requests] respond_to :html - before_action :find_group, except: [:new, :create] + before_action :find_group, except: [:index, :new, :create] # Authorize - before_action :authorize_read_group!, except: [:show, :activity, :new, :create, :autocomplete] + before_action :authorize_read_group!, except: [:index, :show, :activity, :new, :create, :autocomplete] before_action :authorize_admin_group!, only: [:edit, :update, :destroy] before_action :authorize_create_group!, only: [:new, :create] -- GitLab From d984beba95608c073eb796e6176aa530b52a0d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Sat, 30 Jan 2016 16:07:16 +0100 Subject: [PATCH 14/14] Revert splitting group into "Activity" / "Projects" in the group nav bar --- app/views/layouts/nav/_group.html.haml | 9 ++------- features/steps/shared/issuable.rb | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index a3bb7399b404..6f30be44a0cd 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -7,16 +7,11 @@ %li.separate-item - = nav_link(path: 'groups#activity', html_options: {class: 'home'}) do + = nav_link(path: ['groups#activity', 'groups/projects#index'], html_options: {class: 'home'}) do = link_to activity_group_path(@group), title: 'Home' do = icon('dashboard fw') %span - Activity - = nav_link(path: 'groups/projects#index') do - = link_to group_projects_path(@group), title: 'Projects' do - = icon('cube fw') - %span - Projects + Group - if can?(current_user, :read_group, @group) - if current_user = nav_link(controller: [:group, :milestones]) do diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb index 25c2b476f43b..c70bfc6bd5cd 100644 --- a/features/steps/shared/issuable.rb +++ b/features/steps/shared/issuable.rb @@ -108,7 +108,7 @@ def edit_issuable step 'I sort the list by "Oldest updated"' do find('button.dropdown-toggle.btn').click - page.within('ul.dropdown-menu.dropdown-menu-align-right li') do + page.within('ul.dropdown-menu.align-right li') do click_link "Oldest updated" end end -- GitLab