Commit 8e0126e9ec676f1ec40bb1a3dcb811e6477fb8c6

Authored by Dmitriy Zaporozhets
2 parents 2677bc3a 2b896ea6

Merge branch 'issue_dashboard' into improved_dashboard

app/assets/stylesheets/style.scss
... ... @@ -351,7 +351,7 @@ header h1.logo a{
351 351 text-indent: -1000em;
352 352 }
353 353  
354   -header nav{border-radius: 4px; box-shadow: 0 1px 2px black; width: 294px; margin: auto;
  354 +header nav{border-radius: 4px; box-shadow: 0 1px 2px black; width: 392px; margin: auto;
355 355 background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #595d63), to(#31363e));
356 356 background-image: -webkit-linear-gradient(#595d63 6.6%, #31363e);
357 357 background-image: -moz-linear-gradient(#595d63 6.6%, #31363e);
... ... @@ -398,6 +398,7 @@ header nav a span{width: 20px; height: 20px; display: inline-block; background:
398 398 header nav a.dashboard span{background: url('images.png') no-repeat -161px 0;}
399 399 header nav a.admin span{background: url('images.png') no-repeat -184px 0;}
400 400 header nav a.project span{background: url('images.png') no-repeat -209px -1px; top: 7px}
  401 +header nav a.issues span{background: url('images.png') no-repeat -209px -1px; top: 7px}
401 402  
402 403 header .login-top{float: right; width: 180px;
403 404 background-image: -webkit-gradient(linear, 0 0, 0 62, color-stop(0.032, #464c56), to(#363c45));
... ...
app/controllers/user_issues_controller.rb 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +class UserIssuesController < ApplicationController
  2 + before_filter :authenticate_user!
  3 +
  4 + layout "user"
  5 +
  6 + respond_to :js, :html
  7 +
  8 + def index
  9 + @user = current_user
  10 + @issues = current_user.assigned_issues.opened
  11 +
  12 + @issues = @issues.includes(:author, :project)
  13 +
  14 + respond_to do |format|
  15 + format.html
  16 + format.js
  17 + format.atom { render :layout => false }
  18 + end
  19 + end
  20 +
  21 +end
... ...
app/controllers/user_merge_requests_controller.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class UserMergeRequestsController < ApplicationController
  2 + before_filter :authenticate_user!
  3 +
  4 + layout "user"
  5 +
  6 + def index
  7 + @merge_requests = current_user.assigned_merge_requests
  8 + end
  9 +end
... ...
app/helpers/user_issues_helper.rb 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +module UserIssuesHelper
  2 +end
... ...
app/helpers/user_merge_requests_helper.rb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +module UserMergeRequestsHelper
  2 +end
  3 +
... ...
app/models/user.rb
... ... @@ -25,6 +25,15 @@ class User &lt; ActiveRecord::Base
25 25 :foreign_key => :assignee_id,
26 26 :dependent => :destroy
27 27  
  28 + has_many :merge_requests,
  29 + :foreign_key => :author_id,
  30 + :dependent => :destroy
  31 +
  32 + has_many :assigned_merge_requests,
  33 + :class_name => "MergeRequest",
  34 + :foreign_key => :assignee_id,
  35 + :dependent => :destroy
  36 +
28 37 before_create :ensure_authentication_token
29 38 alias_attribute :private_token, :authentication_token
30 39 scope :not_in_project, lambda { |project| where("id not in (:ids)", :ids => project.users.map(&:id) ) }
... ...
app/views/issues/_issues.html.haml
1 1 - @issues.critical.each do |issue|
2   - = render(:partial => 'show', :locals => {:issue => issue})
  2 + = render(:partial => 'issues/show', :locals => {:issue => issue})
3 3  
4 4 - @issues.non_critical.each do |issue|
5   - = render(:partial => 'show', :locals => {:issue => issue})
  5 + = render(:partial => 'issues/show', :locals => {:issue => issue})
... ...
app/views/issues/_show.html.haml
1   -%tr{ :id => dom_id(issue), :class => "issue #{issue.critical ? "critical" : ""}", :url => project_issue_path(@project, issue) }
  1 +%tr{ :id => dom_id(issue), :class => "issue #{issue.critical ? "critical" : ""}", :url => project_issue_path(issue.project, issue) }
2 2 %td
3 3 %strong.issue-number{:class => sort_class}= "##{issue.id}"
4 4 %span
... ... @@ -6,6 +6,9 @@
6 6 %br
7 7 %br
8 8 %div.note-author
  9 + - if not @project.present?
  10 + %strong= issue.project.name
  11 + = '-'
9 12 %strong= issue.assignee.name
10 13 %cite.cgray
11 14 = time_ago_in_words(issue.created_at)
... ... @@ -17,10 +20,10 @@
17 20 .right.action-links
18 21 - if can? current_user, :write_issue, issue
19 22 - if issue.closed
20   - = link_to 'Reopen', project_issue_path(@project, issue, :issue => {:closed => false }, :status_only => true), :method => :put, :class => "cgray", :remote => true
  23 + = link_to 'Reopen', project_issue_path(issue.project, issue, :issue => {:closed => false }, :status_only => true), :method => :put, :class => "cgray", :remote => true
21 24 - else
22   - = link_to 'Resolve', project_issue_path(@project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "cgray", :remote => true
  25 + = link_to 'Resolve', project_issue_path(issue.project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "cgray", :remote => true
23 26 - if can? current_user, :write_issue, issue
24   - = link_to 'Edit', edit_project_issue_path(@project, issue), :class => "cgray edit-issue-link", :remote => true
  27 + = link_to 'Edit', edit_project_issue_path(issue.project, issue), :class => "cgray edit-issue-link", :remote => true
25 28 - if can?(current_user, :admin_issue, @project) || issue.author == current_user
26   - = link_to 'Remove', [@project, issue], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "cred delete-issue negative", :id => "destroy_issue_#{issue.id}"
  29 + = link_to 'Remove', [issue.project, issue], :confirm => 'Are you sure?', :method => :delete, :remote => true, :class => "cred delete-issue negative", :id => "destroy_issue_#{issue.id}"
... ...
app/views/layouts/_head_panel.html.erb
... ... @@ -25,6 +25,9 @@
25 25 <%= link_to dashboard_path, :class => current_page?(root_path) ? "current dashboard" : "dashboard" do %>
26 26 <span></span>Dashboard
27 27 <% end %>
  28 + <%= link_to issues_path, :class => current_page?(issues_path) ? "current issues" : "issues" do %>
  29 + <span></span>Issues
  30 + <% end %>
28 31 <%= link_to projects_path, :class => current_page?(projects_path) ? "current project" : "project" do %>
29 32 <span></span>Projects
30 33 <% end %>
... ...
app/views/layouts/user.html.haml 0 → 100644
... ... @@ -0,0 +1,31 @@
  1 +!!!
  2 +%html
  3 + %head
  4 + %title
  5 + GitLab #{" - #{current_user.name}"}
  6 + = stylesheet_link_tag "application"
  7 + = javascript_include_tag "application"
  8 + = csrf_meta_tags
  9 + = javascript_tag do
  10 + REQ_URI = "#{request.env["REQUEST_URI"]}";
  11 + REQ_REFFER = "#{request.env["HTTP_REFERER"]}";
  12 + %body{ :class => body_class('project-page'), :id => yield(:boyd_id)}
  13 + = render :partial => "layouts/flash"
  14 + #container
  15 + = render :partial => "layouts/head_panel"
  16 + .project-container
  17 + .project-sidebar
  18 + .fixed
  19 + %aside
  20 + = link_to issues_path, :class => current_page?(issues_path) ? "current" : nil do
  21 + Issues
  22 + - unless current_user.assigned_issues.empty?
  23 + %span{ :class => "number" }= current_user.assigned_issues.count
  24 + = link_to merge_requests_path, :class => current_page?(merge_requests_path) ? "current" : nil do
  25 + Merge Requests
  26 + - unless current_user.assigned_merge_requests.empty?
  27 + %span{ :class => "number" }= current_user.assigned_merge_requests.count
  28 +
  29 + .project-content
  30 + = yield
  31 +
... ...
app/views/merge_requests/_merge_request.html.haml
1   -%a.update-item{:href => project_merge_request_path(@project, merge_request)}
  1 +%a.update-item{:href => project_merge_request_path(merge_request.project, merge_request)}
2 2 = image_tag gravatar_icon(merge_request.author_email), :class => "left", :width => 40
3 3 %span.update-title
4 4 = merge_request.title
5 5 %span.update-author
  6 + - if not @project.present?
  7 + %strong= merge_request.project.name
  8 + = '-'
6 9 %strong= merge_request.author_name
7 10 authored
8 11 = time_ago_in_words(merge_request.created_at)
... ...
app/views/user_issues/index.atom.builder 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +xml.instruct!
  2 +xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
  3 + xml.title "#{@user.name} issues"
  4 + xml.link :href => issues_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml"
  5 + xml.link :href => issues_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html"
  6 + xml.id issues_url(:private_token => @user.private_token)
  7 + xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
  8 +
  9 + @issues.each do |issue|
  10 + xml.entry do
  11 + xml.id project_issue_url(issue.project, issue)
  12 + xml.link :href => project_issue_url(issue.project, issue)
  13 + xml.title truncate(issue.title, :length => 80)
  14 + xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
  15 + xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email)
  16 + xml.author do |author|
  17 + xml.name issue.author_name
  18 + xml.email issue.author_email
  19 + end
  20 + xml.summary issue.title
  21 + end
  22 + end
  23 +end
  24 +
... ...
app/views/user_issues/index.html.haml 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +%div#issues-table-holder
  2 + %table.round-borders#issues-table
  3 + %thead
  4 + %th
  5 + .top_panel_issues
  6 + %h2 Issues assigned to me
  7 +
  8 + = render 'issues/issues'
  9 + %br
... ...
app/views/user_merge_requests/index.html.haml 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +- if @merge_requests.opened.count > 0
  2 + %div{ :class => "update-data ui-box ui-box-small ui-box-big" }
  3 + %h3
  4 + %span.tag.open Open
  5 + .data
  6 + = render @merge_requests.opened
  7 +
  8 + .clear
  9 + %br
  10 +
... ...
config/routes.rb
1 1 Gitlab::Application.routes.draw do
2 2  
  3 + get "user_issues/index"
  4 +
3 5 get 'tags'=> 'tags#index'
4 6 get 'tags/:tag' => 'projects#index'
5 7  
... ... @@ -21,6 +23,9 @@ Gitlab::Application.routes.draw do
21 23 put "profile/edit", :to => "profile#social_update"
22 24 get "profile", :to => "profile#show"
23 25 get "dashboard", :to => "dashboard#index"
  26 + get "issues", :to => "user_issues#index", :as => "issues"
  27 + get "merge_requests", :to => "user_merge_requests#index", :as => "merge_requests"
  28 +
24 29 #get "profile/:id", :to => "profile#show"
25 30  
26 31 resources :projects, :only => [:new, :create, :index]
... ...
db/schema.rb
... ... @@ -13,18 +13,6 @@
13 13  
14 14 ActiveRecord::Schema.define(:version => 20111207211728) do
15 15  
16   - create_table "features", :force => true do |t|
17   - t.string "name"
18   - t.string "branch_name"
19   - t.integer "assignee_id"
20   - t.integer "author_id"
21   - t.integer "project_id"
22   - t.datetime "created_at"
23   - t.datetime "updated_at"
24   - t.string "version"
25   - t.integer "status", :default => 0, :null => false
26   - end
27   -
28 16 create_table "issues", :force => true do |t|
29 17 t.string "title"
30 18 t.integer "assignee_id"
... ...
spec/models/user_spec.rb
... ... @@ -6,6 +6,8 @@ describe User do
6 6 it { should have_many(:users_projects) }
7 7 it { should have_many(:issues) }
8 8 it { should have_many(:assigned_issues) }
  9 + it { should have_many(:merge_requests) }
  10 + it { should have_many(:assigned_merge_requests) }
9 11 end
10 12  
11 13 describe "Respond to" do
... ...
spec/requests/merge_requests_spec.rb
... ... @@ -50,7 +50,7 @@ describe &quot;MergeRequests&quot; do
50 50 end
51 51  
52 52 describe "GET /merge_requests/new" do
53   - before do
  53 + before do
54 54 visit new_project_merge_request_path(project)
55 55 fill_in "merge_request_title", :with => "Merge Request Title"
56 56 select "master", :from => "merge_request_source_branch"
... ...
spec/requests/user_issues_spec.rb 0 → 100644
... ... @@ -0,0 +1,55 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "User Issues Dashboard" do
  4 + describe "GET /issues" do
  5 + before do
  6 +
  7 + login_as :user
  8 +
  9 + @project1 = Factory :project,
  10 + :path => "project1",
  11 + :code => "TEST1"
  12 +
  13 + @project2 = Factory :project,
  14 + :path => "project2",
  15 + :code => "TEST2"
  16 +
  17 + @project1.add_access(@user, :read, :write)
  18 + @project2.add_access(@user, :read, :write)
  19 +
  20 + @issue1 = Factory :issue,
  21 + :author => @user,
  22 + :assignee => @user,
  23 + :project => @project1
  24 +
  25 + @issue2 = Factory :issue,
  26 + :author => @user,
  27 + :assignee => @user,
  28 + :project => @project2
  29 +
  30 + visit issues_path
  31 + end
  32 +
  33 + subject { page }
  34 +
  35 + it { should have_content(@issue1.title) }
  36 + it { should have_content(@issue1.project.name) }
  37 + it { should have_content(@issue1.assignee.name) }
  38 +
  39 + it { should have_content(@issue2.title) }
  40 + it { should have_content(@issue2.project.name) }
  41 + it { should have_content(@issue2.assignee.name) }
  42 +
  43 + it "should render atom feed via private token" do
  44 + logout
  45 + visit issues_path(:atom, :private_token => @user.private_token)
  46 +
  47 + page.response_headers['Content-Type'].should have_content("application/atom+xml")
  48 + page.body.should have_selector("title", :text => "#{@user.name} issues")
  49 + page.body.should have_selector("author email", :text => @issue1.author_email)
  50 + page.body.should have_selector("entry summary", :text => @issue1.title)
  51 + page.body.should have_selector("author email", :text => @issue2.author_email)
  52 + page.body.should have_selector("entry summary", :text => @issue2.title)
  53 + end
  54 + end
  55 +end
... ...
spec/requests/user_merge_requests_spec.rb 0 → 100644
... ... @@ -0,0 +1,47 @@
  1 +require 'spec_helper'
  2 +
  3 +describe "User MergeRequests" do
  4 + describe "GET /issues" do
  5 + before do
  6 +
  7 + login_as :user
  8 +
  9 + @project1 = Factory :project,
  10 + :path => "project1",
  11 + :code => "TEST1"
  12 +
  13 + @project2 = Factory :project,
  14 + :path => "project2",
  15 + :code => "TEST2"
  16 +
  17 + @project1.add_access(@user, :read, :write)
  18 + @project2.add_access(@user, :read, :write)
  19 +
  20 + @merge_request1 = Factory :merge_request,
  21 + :author => @user,
  22 + :assignee => @user,
  23 + :project => @project1
  24 +
  25 + @merge_request2 = Factory :merge_request,
  26 + :author => @user,
  27 + :assignee => @user,
  28 + :project => @project2
  29 +
  30 + visit merge_requests_path
  31 + end
  32 +
  33 + subject { page }
  34 +
  35 + it { should have_content(@merge_request1.title) }
  36 + it { should have_content(@merge_request1.project.name) }
  37 + it { should have_content(@merge_request1.target_branch) }
  38 + it { should have_content(@merge_request1.source_branch) }
  39 + it { should have_content(@merge_request1.assignee.name) }
  40 +
  41 + it { should have_content(@merge_request2.title) }
  42 + it { should have_content(@merge_request2.project.name) }
  43 + it { should have_content(@merge_request2.target_branch) }
  44 + it { should have_content(@merge_request2.source_branch) }
  45 + it { should have_content(@merge_request2.assignee.name) }
  46 + end
  47 +end
... ...