Commit 23d950855d6d2524d00b1f0618c008e2529f06a4
1 parent
667edcdd
Exists in
master
and in
4 other branches
Milestone basic scaffold
Showing
28 changed files
with
424 additions
and
36 deletions
Show diff stats
app/assets/javascripts/issues.js
... | ... | @@ -2,6 +2,7 @@ function switchToNewIssue(form){ |
2 | 2 | $(".issues_content").hide("fade", { direction: "left" }, 150, function(){ |
3 | 3 | $(".issues_content").after(form); |
4 | 4 | $('select#issue_assignee_id').chosen(); |
5 | + $('select#issue_milestone_id').chosen(); | |
5 | 6 | $("#new_issue_dialog").show("fade", { direction: "right" }, 150); |
6 | 7 | $('.top-tabs .add_new').hide(); |
7 | 8 | }); |
... | ... | @@ -11,6 +12,7 @@ function switchToEditIssue(form){ |
11 | 12 | $(".issues_content").hide("fade", { direction: "left" }, 150, function(){ |
12 | 13 | $(".issues_content").after(form); |
13 | 14 | $('select#issue_assignee_id').chosen(); |
15 | + $('select#issue_milestone_id').chosen(); | |
14 | 16 | $("#edit_issue_dialog").show("fade", { direction: "right" }, 150); |
15 | 17 | $('.add_new').hide(); |
16 | 18 | }); | ... | ... |
app/assets/stylesheets/common.scss
... | ... | @@ -0,0 +1,31 @@ |
1 | +/** | |
2 | + * JQUERY UI datepicker | |
3 | + * | |
4 | + */ | |
5 | +.ui-datepicker { | |
6 | + border-color:#eee; | |
7 | + padding:20px; | |
8 | + | |
9 | + .ui-state-default { | |
10 | + background:#f1f1f1; | |
11 | + padding:5px; | |
12 | + } | |
13 | + .ui-state-active { | |
14 | + background:#fff; | |
15 | + } | |
16 | +} | |
17 | + | |
18 | +/** | |
19 | + * JQUERY UI progressbar | |
20 | + * | |
21 | + */ | |
22 | +.ui-progressbar { | |
23 | + border:1px solid #ddd; | |
24 | + height:6px; | |
25 | + | |
26 | + .ui-progressbar-value { | |
27 | + background-color: #62C462;//$blue_link; | |
28 | + margin:0; | |
29 | + } | |
30 | +} | |
31 | + | ... | ... |
app/assets/stylesheets/main.scss
app/controllers/issues_controller.rb
... | ... | @@ -15,7 +15,7 @@ class IssuesController < ApplicationController |
15 | 15 | before_filter :authorize_write_issue!, :only => [:new, :create] |
16 | 16 | |
17 | 17 | # Allow modify issue |
18 | - before_filter :authorize_modify_issue!, :only => [:close, :edit, :update, :sort] | |
18 | + before_filter :authorize_modify_issue!, :only => [:close, :edit, :update] | |
19 | 19 | |
20 | 20 | # Allow destroy issue |
21 | 21 | before_filter :authorize_admin_issue!, :only => [:destroy] |
... | ... | @@ -28,8 +28,10 @@ class IssuesController < ApplicationController |
28 | 28 | when 2 then @project.issues.closed |
29 | 29 | when 3 then @project.issues.opened.assigned(current_user) |
30 | 30 | else @project.issues.opened |
31 | - end.page(params[:page]).per(20) | |
31 | + end | |
32 | 32 | |
33 | + @issues = @issues.where(:milestone_id => params[:milestone_id]) if params[:milestone_id].present? | |
34 | + @issues = @issues.page(params[:page]).per(20) | |
33 | 35 | @issues = @issues.includes(:author, :project).order("critical, updated_at") |
34 | 36 | |
35 | 37 | respond_to do |format| |
... | ... | @@ -51,13 +53,6 @@ class IssuesController < ApplicationController |
51 | 53 | def show |
52 | 54 | @note = @project.notes.new(:noteable => @issue) |
53 | 55 | |
54 | - @commits = if @issue.branch_name && @project.repo.heads.map(&:name).include?(@issue.branch_name) | |
55 | - @project.repo.commits_between("master", @issue.branch_name) | |
56 | - else | |
57 | - [] | |
58 | - end | |
59 | - | |
60 | - | |
61 | 56 | respond_to do |format| |
62 | 57 | format.html |
63 | 58 | format.js |
... | ... | @@ -102,6 +97,8 @@ class IssuesController < ApplicationController |
102 | 97 | end |
103 | 98 | |
104 | 99 | def sort |
100 | + return render_404 unless can?(current_user, :admin_issue, @project) | |
101 | + | |
105 | 102 | @issues = @project.issues.where(:id => params['issue']) |
106 | 103 | @issues.each do |issue| |
107 | 104 | issue.position = params['issue'].index(issue.id.to_s) + 1 | ... | ... |
... | ... | @@ -0,0 +1,94 @@ |
1 | +class MilestonesController < ApplicationController | |
2 | + before_filter :authenticate_user! | |
3 | + before_filter :project | |
4 | + before_filter :module_enabled | |
5 | + before_filter :milestone, :only => [:edit, :update, :destroy, :show] | |
6 | + layout "project" | |
7 | + | |
8 | + # Authorize | |
9 | + before_filter :add_project_abilities | |
10 | + | |
11 | + # Allow read any milestone | |
12 | + before_filter :authorize_read_milestone! | |
13 | + | |
14 | + # Allow admin milestone | |
15 | + before_filter :authorize_admin_milestone!, :except => [:index, :show] | |
16 | + | |
17 | + respond_to :html | |
18 | + | |
19 | + def index | |
20 | + @milestones = case params[:f].to_i | |
21 | + when 1; @project.milestones | |
22 | + else @project.milestones.active | |
23 | + end | |
24 | + | |
25 | + @milestones = @milestones.includes(:project).order("due_date") | |
26 | + @milestones = @milestones.page(params[:page]).per(20) | |
27 | + end | |
28 | + | |
29 | + def new | |
30 | + @milestone = @project.milestones.new | |
31 | + respond_with(@milestone) | |
32 | + end | |
33 | + | |
34 | + def edit | |
35 | + respond_with(@milestone) | |
36 | + end | |
37 | + | |
38 | + def show | |
39 | + respond_to do |format| | |
40 | + format.html | |
41 | + format.js | |
42 | + end | |
43 | + end | |
44 | + | |
45 | + def create | |
46 | + @milestone = @project.milestones.new(params[:milestone]) | |
47 | + | |
48 | + if @milestone.save | |
49 | + redirect_to project_milestone_path(@project, @milestone) | |
50 | + else | |
51 | + render "new" | |
52 | + end | |
53 | + end | |
54 | + | |
55 | + def update | |
56 | + @milestone.update_attributes(params[:milestone]) | |
57 | + | |
58 | + respond_to do |format| | |
59 | + format.js | |
60 | + format.html do | |
61 | + if @milestone.valid? | |
62 | + redirect_to [@project, @milestone] | |
63 | + else | |
64 | + render :edit | |
65 | + end | |
66 | + end | |
67 | + end | |
68 | + end | |
69 | + | |
70 | + def destroy | |
71 | + return access_denied! unless can?(current_user, :admin_milestone, @milestone) | |
72 | + | |
73 | + @milestone.destroy | |
74 | + | |
75 | + respond_to do |format| | |
76 | + format.html { redirect_to project_milestones_path } | |
77 | + format.js { render :nothing => true } | |
78 | + end | |
79 | + end | |
80 | + | |
81 | + protected | |
82 | + | |
83 | + def milestone | |
84 | + @milestone ||= @project.milestones.find(params[:id]) | |
85 | + end | |
86 | + | |
87 | + def authorize_admin_milestone! | |
88 | + return render_404 unless can?(current_user, :admin_milestone, @project) | |
89 | + end | |
90 | + | |
91 | + def module_enabled | |
92 | + return render_404 unless @project.issues_enabled | |
93 | + end | |
94 | +end | ... | ... |
app/models/ability.rb
... | ... | @@ -17,6 +17,7 @@ class Ability |
17 | 17 | :read_project, |
18 | 18 | :read_wiki, |
19 | 19 | :read_issue, |
20 | + :read_milestone, | |
20 | 21 | :read_snippet, |
21 | 22 | :read_team_member, |
22 | 23 | :read_merge_request, |
... | ... | @@ -42,6 +43,7 @@ class Ability |
42 | 43 | :modify_merge_request, |
43 | 44 | :admin_project, |
44 | 45 | :admin_issue, |
46 | + :admin_milestone, | |
45 | 47 | :admin_snippet, |
46 | 48 | :admin_team_member, |
47 | 49 | :admin_merge_request, | ... | ... |
app/models/issue.rb
... | ... | @@ -0,0 +1,29 @@ |
1 | +class Milestone < ActiveRecord::Base | |
2 | + belongs_to :project | |
3 | + has_many :issues | |
4 | + | |
5 | + validates_presence_of :project_id | |
6 | + validates_presence_of :title | |
7 | + | |
8 | + def self.active | |
9 | + where("due_date > ? ", Date.today) | |
10 | + end | |
11 | + | |
12 | + def percent_complete | |
13 | + @percent_complete ||= begin | |
14 | + total_i = self.issues.count | |
15 | + closed_i = self.issues.closed.count | |
16 | + if total_i > 0 | |
17 | + (closed_i * 100) / total_i | |
18 | + else | |
19 | + 100 | |
20 | + end | |
21 | + rescue => ex | |
22 | + 0 | |
23 | + end | |
24 | + end | |
25 | + | |
26 | + def expires_at | |
27 | + "expires at #{due_date.stamp("Aug 21, 2011")}" if due_date | |
28 | + end | |
29 | +end | ... | ... |
app/models/project.rb
... | ... | @@ -12,6 +12,7 @@ class Project < ActiveRecord::Base |
12 | 12 | has_many :events, :dependent => :destroy |
13 | 13 | has_many :merge_requests, :dependent => :destroy |
14 | 14 | has_many :issues, :dependent => :destroy, :order => "position" |
15 | + has_many :milestones, :dependent => :destroy | |
15 | 16 | has_many :users_projects, :dependent => :destroy |
16 | 17 | has_many :notes, :dependent => :destroy |
17 | 18 | has_many :snippets, :dependent => :destroy | ... | ... |
app/views/issues/_form.html.haml
... | ... | @@ -9,18 +9,25 @@ |
9 | 9 | %li= msg |
10 | 10 | |
11 | 11 | .clearfix |
12 | - = f.label :title, "Issue Subject" | |
13 | - .input= f.text_field :title, :maxlength => 255, :class => "xxlarge" | |
14 | - | |
12 | + = f.label :title, "Issue Subject *" | |
13 | + .input | |
14 | + = f.text_field :title, :maxlength => 255, :class => "xxlarge" | |
15 | + | |
16 | + .clearfix | |
17 | + = f.label :assignee_id, "Assign to *" | |
18 | + .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Assign to user" }) | |
19 | + | |
15 | 20 | .clearfix |
16 | 21 | = f.label :description, "Issue Details" |
17 | 22 | .input |
18 | 23 | = f.text_area :description, :maxlength => 2000, :class => "xxlarge", :rows => 10 |
19 | 24 | %p.hint Markdown is enabled. |
20 | 25 | |
26 | + | |
21 | 27 | .clearfix |
22 | - = f.label :assignee_id | |
23 | - .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Assign to user" }) | |
28 | + = f.label :milestone_id | |
29 | + .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { :include_blank => "Select milestone" }) | |
30 | + | |
24 | 31 | |
25 | 32 | .clearfix |
26 | 33 | = f.label :critical, "Critical" | ... | ... |
app/views/issues/_head.html.haml
1 | 1 | .tabs |
2 | 2 | %li{:class => "#{'active' if current_page?(project_issues_path(@project))}"} |
3 | 3 | = link_to project_issues_path(@project), :class => "tab" do |
4 | - Issues | |
4 | + Browse Issues | |
5 | + %li{:class => "#{'active' if current_page?(project_milestones_path(@project))}"} | |
6 | + = link_to project_milestones_path(@project), :class => "tab" do | |
7 | + Milestones | ... | ... |
app/views/issues/_issues.html.haml
app/views/issues/edit.html.haml
app/views/issues/index.html.haml
1 | += render "issues/head" | |
1 | 2 | .issues_content |
2 | 3 | %h3 |
3 | 4 | Issues |
... | ... | @@ -5,14 +6,22 @@ |
5 | 6 | = link_to project_issues_path(@project, :atom, { :private_token => current_user.private_token }) do |
6 | 7 | = image_tag "Rss-UI.PNG", :width => 16, :title => "feed" |
7 | 8 | |
8 | - - if can? current_user, :write_issue, @project | |
9 | - = link_to new_project_issue_path(@project), :class => "right btn small", :title => "New Issue", :remote => true do | |
10 | - New Issue | |
9 | + .right | |
10 | + .span4.left | |
11 | + = form_tag search_project_issues_path(@project), :method => :get, :remote => true, :id => "issue_search_form", :class => :left do | |
12 | + = hidden_field_tag :project_id, @project.id, { :id => 'project_id' } | |
13 | + = hidden_field_tag :status, params[:f] | |
14 | + = search_field_tag :issue_search, nil, { :placeholder => 'Search', :class => 'issue_search' } | |
15 | + | |
16 | + - if can? current_user, :write_issue, @project | |
17 | + .span2.left | |
18 | + = link_to new_project_issue_path(@project), :class => "right btn small", :title => "New Issue", :remote => true do | |
19 | + New Issue | |
11 | 20 | %br |
12 | 21 | %div#issues-table-holder.ui-box |
13 | 22 | .title |
14 | 23 | .row |
15 | - .span8 | |
24 | + .span6 | |
16 | 25 | %ul.pills.left |
17 | 26 | %li{:class => ("active" if (params[:f] == "0" || !params[:f]))} |
18 | 27 | = link_to project_issues_path(@project, :f => 0) do |
... | ... | @@ -27,17 +36,13 @@ |
27 | 36 | = link_to project_issues_path(@project, :f => 1) do |
28 | 37 | All |
29 | 38 | |
30 | - .span3.right | |
31 | - = form_tag search_project_issues_path(@project), :method => :get, :remote => true, :id => "issue_search_form", :class => :right do | |
32 | - = hidden_field_tag :project_id, @project.id, { :id => 'project_id' } | |
33 | - = hidden_field_tag :status, params[:f] | |
34 | - = search_field_tag :issue_search, nil, { :placeholder => 'Search', :class => 'issue_search' } | |
39 | + .span6.right | |
40 | + = form_tag project_issues_path(@project), :method => :get, :class => :right do | |
41 | + = select_tag(:milestone_id, options_from_collection_for_select(@project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), :prompt => "Select milestone") | |
35 | 42 | |
36 | 43 | %ul#issues-table.unstyled |
37 | 44 | = render "issues" |
38 | - - if @issues.blank? | |
39 | - %li | |
40 | - %p.padded Nothing to show here | |
45 | + | |
41 | 46 | :javascript |
42 | 47 | var href = $('.issue_search').parent().attr('action'); |
43 | 48 | var last_terms = ''; |
... | ... | @@ -65,9 +70,8 @@ |
65 | 70 | $('#issues-table').sortable({ |
66 | 71 | axis: 'y', |
67 | 72 | dropOnEmpty: false, |
68 | - handle: '.handle', | |
69 | - cursor: 'crosshair', | |
70 | - items: 'tr', | |
73 | + handle: '.avatar', | |
74 | + items: 'li', | |
71 | 75 | opacity: 0.4, |
72 | 76 | scroll: true, |
73 | 77 | update: function(){ |
... | ... | @@ -85,4 +89,8 @@ |
85 | 89 | |
86 | 90 | $(function(){ |
87 | 91 | setSortable(); |
92 | + $("#milestone_id").chosen(); | |
93 | + $("#milestone_id").live("change", function(){ | |
94 | + $(this).closest("form").submit(); | |
95 | + }); | |
88 | 96 | }); | ... | ... |
app/views/issues/new.html.haml
... | ... | @@ -0,0 +1,54 @@ |
1 | +%h3= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.id}" | |
2 | +.back_link | |
3 | + = link_to project_milestones_path(@project) do | |
4 | + ← To milestones | |
5 | + | |
6 | +%hr | |
7 | + | |
8 | += form_for [@project, @milestone] do |f| | |
9 | + -if @milestone.errors.any? | |
10 | + .alert-message.block-message.error | |
11 | + %ul | |
12 | + - @milestone.errors.full_messages.each do |msg| | |
13 | + %li= msg | |
14 | + .row | |
15 | + .span7 | |
16 | + .clearfix | |
17 | + = f.label :title, "Title" | |
18 | + .input | |
19 | + = f.text_field :title, :maxlength => 255, :class => "xlarge" | |
20 | + %p.hint Required | |
21 | + .clearfix | |
22 | + = f.label :description, "Description" | |
23 | + .input | |
24 | + = f.text_area :description, :maxlength => 2000, :class => "xlarge", :rows => 10 | |
25 | + %p.hint Markdown is enabled. | |
26 | + .span8 | |
27 | + .clearfix | |
28 | + = f.label :due_date, "Due Date" | |
29 | + .input= f.hidden_field :due_date | |
30 | + .input | |
31 | + .datepicker | |
32 | + | |
33 | + | |
34 | + .actions | |
35 | + - if @milestone.new_record? | |
36 | + = f.submit 'Create milestone', :class => "primary btn" | |
37 | + -else | |
38 | + = f.submit 'Save changes', :class => "primary btn" | |
39 | + | |
40 | + - if request.xhr? | |
41 | + = link_to "Cancel", "#back", :onclick => "backToIssues();", :class => "btn" | |
42 | + - else | |
43 | + - if @milestone.new_record? | |
44 | + = link_to "Cancel", project_milestones_path(@project), :class => "btn" | |
45 | + - else | |
46 | + = link_to "Cancel", project_milestone_path(@project, @milestone), :class => "btn" | |
47 | + | |
48 | +:javascript | |
49 | + $(function() { | |
50 | + $( ".datepicker" ).datepicker({ | |
51 | + dateFormat: "yy-mm-dd", | |
52 | + onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } | |
53 | + }); | |
54 | + }); | ... | ... |
... | ... | @@ -0,0 +1,21 @@ |
1 | +%li{:class => "wll", :id => dom_id(milestone) } | |
2 | + .right | |
3 | + - if milestone.issues.count > 0 | |
4 | + = link_to 'Browse Issues', project_issues_path(milestone.project, :milestone_id => milestone.id), :class => "btn small" | |
5 | + - if milestone.issues.any? | |
6 | + %span.btn.small.disabled.padded= pluralize milestone.issues.count, 'issues' | |
7 | + - if can? current_user, :admin_milestone, milestone.project | |
8 | + = link_to 'Edit', edit_project_milestone_path(milestone.project, milestone), :class => "btn small edit-milestone-link" | |
9 | + = link_to project_milestone_path(milestone.project, milestone) do | |
10 | + %h4.row_title | |
11 | + = truncate(milestone.title, :length => 100) | |
12 | + %small= milestone.expires_at | |
13 | + | |
14 | + .progress.span4 | |
15 | + | |
16 | + :javascript | |
17 | + $(function() { | |
18 | + $( "##{dom_id(milestone)} .progress" ).progressbar({ | |
19 | + value: #{milestone.percent_complete} | |
20 | + }); | |
21 | + }); | ... | ... |
... | ... | @@ -0,0 +1,25 @@ |
1 | += render "issues/head" | |
2 | +.milestones_content | |
3 | + %h3 | |
4 | + Milestones | |
5 | + - if can? current_user, :admin_milestone, @project | |
6 | + = link_to "New Milestone", new_project_milestone_path(@project), :class => "right btn small", :title => "New Milestone" | |
7 | + %br | |
8 | + %div.ui-box | |
9 | + .title | |
10 | + %ul.pills | |
11 | + %li{:class => ("active" if (params[:f] == "0" || !params[:f]))} | |
12 | + = link_to project_milestones_path(@project, :f => 0) do | |
13 | + Active | |
14 | + %li{:class => ("active" if params[:f] == "1")} | |
15 | + = link_to project_milestones_path(@project, :f => 1) do | |
16 | + All | |
17 | + | |
18 | + %ul.unstyled | |
19 | + = render @milestones | |
20 | + | |
21 | + - if @milestones.present? | |
22 | + %li.bottom= paginate @milestones, :remote => true, :theme => "gitlab" | |
23 | + - else | |
24 | + %li | |
25 | + %p.padded Nothing to show here | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | += render "form" | ... | ... |
... | ... | @@ -0,0 +1,50 @@ |
1 | +%h3 | |
2 | + Milestone ##{@milestone.id} | |
3 | + %small | |
4 | + = @milestone.expires_at | |
5 | + | |
6 | + %span.right | |
7 | + - if can?(current_user, :admin_milestone, @project) | |
8 | + = link_to edit_project_milestone_path(@project, @milestone), :class => "btn" do | |
9 | + Edit | |
10 | + | |
11 | +.back_link | |
12 | + = link_to project_milestones_path(@project) do | |
13 | + ← To milestones list | |
14 | + | |
15 | +.main_box | |
16 | + .top_box_content | |
17 | + %h5 | |
18 | + - if @milestone.closed | |
19 | + .alert-message.error.status_info Closed | |
20 | + - else | |
21 | + .alert-message.success.status_info Open | |
22 | + = @milestone.title | |
23 | + | |
24 | + .middle_box_content | |
25 | + .row | |
26 | + .span2 | |
27 | + = link_to 'Browse Issues', project_issues_path(@milestone.project, :milestone_id => @milestone.id), :class => "btn small edit-milestone-link" | |
28 | + .span4 | |
29 | + %span | |
30 | + = @milestone.expires_at | |
31 | + | |
32 | + .span4.right | |
33 | + .progress | |
34 | + %br | |
35 | + %span | |
36 | + #{@milestone.issues.opened.count} open | |
37 | + – | |
38 | + #{@milestone.issues.closed.count} closed | |
39 | + | |
40 | + - if @milestone.description.present? | |
41 | + .bottom_box_content | |
42 | + = markdown @milestone.description | |
43 | + | |
44 | + | |
45 | +:javascript | |
46 | + $(function() { | |
47 | + $( ".progress" ).progressbar({ | |
48 | + value: #{@milestone.percent_complete} | |
49 | + }); | |
50 | + }); | ... | ... |
config/routes.rb
... | ... | @@ -0,0 +1,12 @@ |
1 | +class CreateMilestones < ActiveRecord::Migration | |
2 | + def change | |
3 | + create_table :milestones do |t| | |
4 | + t.string :title, :null => false | |
5 | + t.integer :project_id, :null => false | |
6 | + t.text :description | |
7 | + t.date :due_date | |
8 | + t.boolean :closed, :default => false, :null => false | |
9 | + t.timestamps | |
10 | + end | |
11 | + end | |
12 | +end | ... | ... |
db/schema.rb
... | ... | @@ -11,7 +11,7 @@ |
11 | 11 | # |
12 | 12 | # It's strongly recommended to check this file into your version control system. |
13 | 13 | |
14 | -ActiveRecord::Schema.define(:version => 20120405211750) do | |
14 | +ActiveRecord::Schema.define(:version => 20120408181910) do | |
15 | 15 | |
16 | 16 | create_table "events", :force => true do |t| |
17 | 17 | t.string "target_type" |
... | ... | @@ -30,13 +30,14 @@ ActiveRecord::Schema.define(:version => 20120405211750) do |
30 | 30 | t.integer "assignee_id" |
31 | 31 | t.integer "author_id" |
32 | 32 | t.integer "project_id" |
33 | - t.datetime "created_at", :null => false | |
34 | - t.datetime "updated_at", :null => false | |
35 | - t.boolean "closed", :default => false, :null => false | |
36 | - t.integer "position", :default => 0 | |
37 | - t.boolean "critical", :default => false, :null => false | |
33 | + t.datetime "created_at", :null => false | |
34 | + t.datetime "updated_at", :null => false | |
35 | + t.boolean "closed", :default => false, :null => false | |
36 | + t.integer "position", :default => 0 | |
37 | + t.boolean "critical", :default => false, :null => false | |
38 | 38 | t.string "branch_name" |
39 | 39 | t.text "description" |
40 | + t.integer "milestone_id" | |
40 | 41 | end |
41 | 42 | |
42 | 43 | add_index "issues", ["project_id"], :name => "index_issues_on_project_id" |
... | ... | @@ -69,6 +70,15 @@ ActiveRecord::Schema.define(:version => 20120405211750) do |
69 | 70 | |
70 | 71 | add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id" |
71 | 72 | |
73 | + create_table "milestones", :force => true do |t| | |
74 | + t.string "title", :null => false | |
75 | + t.text "description" | |
76 | + t.date "due_date", :null => false | |
77 | + t.integer "project_id", :null => false | |
78 | + t.datetime "created_at", :null => false | |
79 | + t.datetime "updated_at", :null => false | |
80 | + end | |
81 | + | |
72 | 82 | create_table "notes", :force => true do |t| |
73 | 83 | t.text "note" |
74 | 84 | t.string "noteable_id" | ... | ... |