Commit e3c3c67b25e2601194293885818ff4dfb39885cd

Authored by Dmitriy Zaporozhets
2 parents 596442f7 d24fd32a

Merge pull request #2787 from gitlabhq/features/projects_page

Feature: Projects page
app/controllers/dashboard_controller.rb
1 1 class DashboardController < ApplicationController
2 2 respond_to :html
3 3  
4   - before_filter :projects
5   - before_filter :event_filter, only: :index
  4 + before_filter :load_projects
  5 + before_filter :event_filter, only: :show
6 6  
7   - def index
  7 + def show
8 8 @groups = current_user.authorized_groups
9   -
10 9 @has_authorized_projects = @projects.count > 0
11   -
12   - @projects = case params[:scope]
13   - when 'personal' then
14   - @projects.personal(current_user)
15   - when 'joined' then
16   - @projects.joined(current_user)
17   - else
18   - @projects
19   - end
20   -
21 10 @teams = current_user.authorized_teams
22   -
23   - @projects = @projects.page(params[:page]).per(30)
  11 + @projects_count = @projects.count
  12 + @projects = @projects.limit(20)
24 13  
25 14 @events = Event.in_projects(current_user.authorized_projects.pluck(:id))
26 15 @events = @event_filter.apply_filter(@events)
... ... @@ -35,6 +24,19 @@ class DashboardController &lt; ApplicationController
35 24 end
36 25 end
37 26  
  27 + def projects
  28 + @projects = case params[:scope]
  29 + when 'personal' then
  30 + @projects.personal(current_user)
  31 + when 'joined' then
  32 + @projects.joined(current_user)
  33 + else
  34 + @projects
  35 + end
  36 +
  37 + @projects = @projects.page(params[:page]).per(30)
  38 + end
  39 +
38 40 # Get authored or assigned open merge requests
39 41 def merge_requests
40 42 @merge_requests = current_user.cared_merge_requests
... ... @@ -57,7 +59,7 @@ class DashboardController &lt; ApplicationController
57 59  
58 60 protected
59 61  
60   - def projects
  62 + def load_projects
61 63 @projects = current_user.authorized_projects.sorted_by_activity
62 64 end
63 65  
... ...
app/helpers/dashboard_helper.rb
... ... @@ -9,9 +9,9 @@ module DashboardHelper
9 9  
10 10 case entity
11 11 when 'issue' then
12   - dashboard_issues_path(options)
  12 + issues_dashboard_path(options)
13 13 when 'merge_request'
14   - dashboard_merge_requests_path(options)
  14 + merge_requests_dashboard_path(options)
15 15 end
16 16 end
17 17  
... ...
app/helpers/projects_helper.rb
... ... @@ -55,7 +55,9 @@ module ProjectsHelper
55 55  
56 56 def project_title project
57 57 if project.group
58   - project.name_with_namespace
  58 + content_tag :span do
  59 + link_to(project.group.name, group_path(project.group)) + " / " + project.name
  60 + end
59 61 else
60 62 project.name
61 63 end
... ...
app/views/dashboard/_projects.html.haml
... ... @@ -2,19 +2,12 @@
2 2 %h5.title
3 3 Projects
4 4 %small
5   - (#{projects.total_count})
  5 + (#{@projects_count})
6 6 - if current_user.can_create_project?
7 7 %span.right
8 8 = link_to new_project_path, class: "btn very_small info" do
9 9 %i.icon-plus
10 10 New Project
11   - %ul.nav.nav-projects-tabs
12   - = nav_tab :scope, nil do
13   - = link_to "All", dashboard_path
14   - = nav_tab :scope, 'personal' do
15   - = link_to "Personal", dashboard_path(scope: 'personal')
16   - = nav_tab :scope, 'joined' do
17   - = link_to "Joined", dashboard_path(scope: 'joined')
18 11  
19 12 %ul.well-list
20 13 - projects.each do |project|
... ... @@ -33,4 +26,6 @@
33 26 - if projects.blank?
34 27 %li
35 28 %h3.nothing_here_message There are no projects here.
36   - .bottom= paginate projects, theme: "gitlab"
  29 + - if @projects_count > 20
  30 + %li.bottom
  31 + %strong= link_to "show all projects", projects_dashboard_path
... ...
app/views/dashboard/index.atom.builder
... ... @@ -1,30 +0,0 @@
1   -xml.instruct!
2   -xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
3   - xml.title "Dashboard feed#{" - #{current_user.name}" if current_user.name.present?}"
4   - xml.link :href => projects_url(:atom), :rel => "self", :type => "application/atom+xml"
5   - xml.link :href => projects_url, :rel => "alternate", :type => "text/html"
6   - xml.id projects_url
7   - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
8   -
9   - @events.each do |event|
10   - if event.proper?
11   - event = EventDecorator.decorate(event)
12   - xml.entry do
13   - event_link = event.feed_url
14   - event_title = event.feed_title
15   - event_summary = event.feed_summary
16   -
17   - xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
18   - xml.link :href => event_link
19   - xml.title truncate(event_title, :length => 80)
20   - xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
21   - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email)
22   - xml.author do |author|
23   - xml.name event.author_name
24   - xml.email event.author_email
25   - end
26   - xml.summary(:type => "xhtml") { |x| x << event_summary unless event_summary.nil? }
27   - end
28   - end
29   - end
30   -end
app/views/dashboard/index.html.haml
... ... @@ -1,12 +0,0 @@
1   -- if @has_authorized_projects
2   - .projects
3   - .activities.span8
4   - = render 'activities'
5   - .side.span4
6   - = render 'sidebar'
7   -
8   -- else
9   - = render "zero_authorized_projects"
10   -
11   -:javascript
12   - dashboardPage();
app/views/dashboard/index.js.haml
... ... @@ -1,2 +0,0 @@
1   -:plain
2   - Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");
app/views/dashboard/issues.atom.builder
1 1 xml.instruct!
2 2 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
3 3 xml.title "#{current_user.name} issues"
4   - xml.link :href => dashboard_issues_url(:atom, :private_token => current_user.private_token), :rel => "self", :type => "application/atom+xml"
5   - xml.link :href => dashboard_issues_url(:private_token => current_user.private_token), :rel => "alternate", :type => "text/html"
6   - xml.id dashboard_issues_url(:private_token => current_user.private_token)
  4 + xml.link :href => issues_dashboard_url(:atom, :private_token => current_user.private_token), :rel => "self", :type => "application/atom+xml"
  5 + xml.link :href => issues_dashboard_url(:private_token => current_user.private_token), :rel => "alternate", :type => "text/html"
  6 + xml.id issues_dashboard_url(:private_token => current_user.private_token)
7 7 xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
8 8  
9 9 @issues.each do |issue|
... ...
app/views/dashboard/projects.html.haml 0 → 100644
... ... @@ -0,0 +1,56 @@
  1 +%h3.page_title
  2 + Projects
  3 + %span
  4 + (#{@projects.total_count})
  5 + - if current_user.can_create_project?
  6 + %span.right
  7 + = link_to new_project_path, class: "btn very_small info" do
  8 + %i.icon-plus
  9 + New Project
  10 +
  11 +
  12 +%hr
  13 +.row
  14 + .span3
  15 + %ul.nav.nav-pills.nav-stacked
  16 + = nav_tab :scope, nil do
  17 + = link_to "All", projects_dashboard_path
  18 + = nav_tab :scope, 'personal' do
  19 + = link_to "Personal", projects_dashboard_path(scope: 'personal')
  20 + = nav_tab :scope, 'joined' do
  21 + = link_to "Joined", projects_dashboard_path(scope: 'joined')
  22 +
  23 + .span9
  24 + = form_tag projects_dashboard_path, method: 'get' do
  25 + %fieldset.dashboard-search-filter
  26 + = hidden_field_tag "scope", params[:scope]
  27 + = search_field_tag "search", params[:search], { placeholder: 'Search', class: 'left input-xxlarge' }
  28 + = button_tag type: 'submit', class: 'btn' do
  29 + %i.icon-search
  30 +
  31 + %ul.well-list
  32 + - @projects.each do |project|
  33 + %li.clearfix
  34 + .left
  35 + = link_to project_path(project), class: dom_class(project) do
  36 + - if project.namespace
  37 + = project.namespace.human_name
  38 + \/
  39 + %strong.well-title
  40 + = truncate(project.name, length: 25)
  41 + %br
  42 + %small.light
  43 + %strong Last activity:
  44 + %span= project_last_activity(project)
  45 + .right.light
  46 + - if project.owner == current_user
  47 + %i.icon-wrench
  48 + - tm = project.team.get_tm(current_user.id)
  49 + - if tm
  50 + = tm.project_access_human
  51 +
  52 + - if @projects.blank?
  53 + %li
  54 + %h3.nothing_here_message There are no projects here.
  55 + .bottom= paginate @projects, theme: "gitlab"
  56 +
... ...
app/views/dashboard/show.atom.builder 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +xml.instruct!
  2 +xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
  3 + xml.title "Dashboard feed#{" - #{current_user.name}" if current_user.name.present?}"
  4 + xml.link :href => projects_url(:atom), :rel => "self", :type => "application/atom+xml"
  5 + xml.link :href => projects_url, :rel => "alternate", :type => "text/html"
  6 + xml.id projects_url
  7 + xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
  8 +
  9 + @events.each do |event|
  10 + if event.proper?
  11 + event = EventDecorator.decorate(event)
  12 + xml.entry do
  13 + event_link = event.feed_url
  14 + event_title = event.feed_title
  15 + event_summary = event.feed_summary
  16 +
  17 + xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
  18 + xml.link :href => event_link
  19 + xml.title truncate(event_title, :length => 80)
  20 + xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
  21 + xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email)
  22 + xml.author do |author|
  23 + xml.name event.author_name
  24 + xml.email event.author_email
  25 + end
  26 + xml.summary(:type => "xhtml") { |x| x << event_summary unless event_summary.nil? }
  27 + end
  28 + end
  29 + end
  30 +end
... ...
app/views/dashboard/show.html.haml 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +- if @has_authorized_projects
  2 + .projects
  3 + .activities.span8
  4 + = render 'activities'
  5 + .side.span4
  6 + = render 'sidebar'
  7 +
  8 +- else
  9 + = render "zero_authorized_projects"
  10 +
  11 +:javascript
  12 + dashboardPage();
... ...
app/views/dashboard/show.js.haml 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +:plain
  2 + Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");
... ...
app/views/groups/issues.atom.builder
1 1 xml.instruct!
2 2 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
3 3 xml.title "#{@user.name} issues"
4   - xml.link :href => dashboard_issues_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml"
5   - xml.link :href => dashboard_issues_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html"
6   - xml.id dashboard_issues_url(:private_token => @user.private_token)
  4 + xml.link :href => issues_dashboard_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml"
  5 + xml.link :href => issues_dashboard_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html"
  6 + xml.id issues_dashboard_url(:private_token => @user.private_token)
7 7 xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
8 8  
9 9 @issues.each do |issue|
... ...
app/views/layouts/application.html.haml
... ... @@ -6,14 +6,17 @@
6 6 = render "layouts/head_panel", title: "Dashboard"
7 7 .container
8 8 %ul.main_menu
9   - = nav_link(path: 'dashboard#index', html_options: {class: 'home'}) do
  9 + = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
10 10 = link_to "Home", root_path, title: "Home"
  11 + = nav_link(path: 'dashboard#projects') do
  12 + = link_to projects_dashboard_path do
  13 + Projects
11 14 = nav_link(path: 'dashboard#issues') do
12   - = link_to dashboard_issues_path do
  15 + = link_to issues_dashboard_path do
13 16 Issues
14 17 %span.count= current_user.assigned_issues.opened.count
15 18 = nav_link(path: 'dashboard#merge_requests') do
16   - = link_to dashboard_merge_requests_path do
  19 + = link_to merge_requests_dashboard_path do
17 20 Merge Requests
18 21 %span.count= current_user.cared_merge_requests.opened.count
19 22 = nav_link(path: 'search#show') do
... ...
config/routes.rb
... ... @@ -118,9 +118,13 @@ Gitlab::Application.routes.draw do
118 118 #
119 119 # Dashboard Area
120 120 #
121   - get "dashboard" => "dashboard#index"
122   - get "dashboard/issues" => "dashboard#issues"
123   - get "dashboard/merge_requests" => "dashboard#merge_requests"
  121 + resource :dashboard, controller: "dashboard" do
  122 + member do
  123 + get :projects
  124 + get :issues
  125 + get :merge_requests
  126 + end
  127 + end
124 128  
125 129 #
126 130 # Groups Area
... ... @@ -284,5 +288,5 @@ Gitlab::Application.routes.draw do
284 288 end
285 289 end
286 290  
287   - root to: "dashboard#index"
  291 + root to: "dashboard#show"
288 292 end
... ...
features/dashboard/projects.feature 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +Feature: Dashboard
  2 + Background:
  3 + Given I sign in as a user
  4 + And I own project "Shop"
  5 + And I visit dashboard projects page
  6 +
  7 + Scenario: I should see issues list
  8 + Then I should see projects list
... ...
features/steps/dashboard/dashboard.rb
... ... @@ -63,6 +63,12 @@ class Dashboard &lt; Spinach::FeatureSteps
63 63 @project.team << [current_user, :master]
64 64 end
65 65  
  66 + Then 'I should see projects list' do
  67 + @user.authorized_projects.all.each do |project|
  68 + page.should have_link project.name_with_namespace
  69 + end
  70 + end
  71 +
66 72 Then 'I should see groups list' do
67 73 Group.all.each do |group|
68 74 page.should have_link group.name
... ...
features/steps/shared/paths.rb
... ... @@ -33,12 +33,16 @@ module SharedPaths
33 33 visit dashboard_path
34 34 end
35 35  
  36 + Given 'I visit dashboard projects page' do
  37 + visit projects_dashboard_path
  38 + end
  39 +
36 40 Given 'I visit dashboard issues page' do
37   - visit dashboard_issues_path
  41 + visit issues_dashboard_path
38 42 end
39 43  
40 44 Given 'I visit dashboard merge requests page' do
41   - visit dashboard_merge_requests_path
  45 + visit merge_requests_dashboard_path
42 46 end
43 47  
44 48 Given 'I visit dashboard search page' do
... ...
spec/requests/atom/dashboard_issues_spec.rb
... ... @@ -10,7 +10,7 @@ describe &quot;Dashboard Issues Feed&quot; do
10 10  
11 11 describe "atom feed" do
12 12 it "should render atom feed via private token" do
13   - visit dashboard_issues_path(:atom, private_token: user.private_token)
  13 + visit issues_dashboard_path(:atom, private_token: user.private_token)
14 14  
15 15 page.response_headers['Content-Type'].should have_content("application/atom+xml")
16 16 page.body.should have_selector("title", text: "#{user.name} issues")
... ...
spec/routing/routing_spec.rb
... ... @@ -146,14 +146,14 @@ describe KeysController, &quot;routing&quot; do
146 146 end
147 147 end
148 148  
149   -# dashboard GET /dashboard(.:format) dashboard#index
  149 +# dashboard GET /dashboard(.:format) dashboard#show
150 150 # dashboard_issues GET /dashboard/issues(.:format) dashboard#issues
151 151 # dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests
152   -# root / dashboard#index
  152 +# root / dashboard#show
153 153 describe DashboardController, "routing" do
154 154 it "to #index" do
155   - get("/dashboard").should route_to('dashboard#index')
156   - get("/").should route_to('dashboard#index')
  155 + get("/dashboard").should route_to('dashboard#show')
  156 + get("/").should route_to('dashboard#show')
157 157 end
158 158  
159 159 it "to #issues" do
... ...