From d20d43eb2b0781a4d6172e8ac46e3fbf42a86069 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Mon, 19 Dec 2011 19:30:14 +0400 Subject: [PATCH 1/9] [refs #54] Base logic and templates for issues and comments to them --- Gemfile | 1 + Gemfile.lock | 2 + app/controllers/comments_controller.rb | 50 ++++++++++++++++ app/controllers/issues_controller.rb | 58 +++++++++++++++++++ app/helpers/comments_helper.rb | 2 + app/helpers/issues_helper.rb | 2 + app/models/comment.rb | 5 ++ app/models/issue.rb | 24 ++++++++ app/models/project.rb | 1 + app/views/comments/_form.html.haml | 10 ++++ app/views/comments/edit.html.haml | 10 ++++ app/views/issues/_form.html.haml | 25 ++++++++ app/views/issues/_list.html.haml | 15 +++++ app/views/issues/edit.html.haml | 12 ++++ app/views/issues/index.html.haml | 14 +++++ app/views/issues/new.html.haml | 11 ++++ app/views/issues/show.html.haml | 53 +++++++++++++++++ app/views/projects/show.html.haml | 1 + config/routes.rb | 3 + db/migrate/20111216134039_create_issues.rb | 20 +++++++ db/migrate/20111216140849_create_comments.rb | 16 +++++ ...111219073859_add_has_issues_to_projects.rb | 9 +++ db/schema.rb | 25 +++++++- spec/controllers/comments_controller_spec.rb | 5 ++ spec/controllers/issues_controller_spec.rb | 5 ++ spec/factories/comments.rb | 6 ++ spec/factories/issues.rb | 6 ++ spec/helpers/comments_helper_spec.rb | 15 +++++ spec/helpers/issues_helper_spec.rb | 15 +++++ spec/models/comment_spec.rb | 5 ++ spec/models/issue_spec.rb | 5 ++ 31 files changed, 430 insertions(+), 1 deletion(-) create mode 100644 app/controllers/comments_controller.rb create mode 100644 app/controllers/issues_controller.rb create mode 100644 app/helpers/comments_helper.rb create mode 100644 app/helpers/issues_helper.rb create mode 100644 app/models/comment.rb create mode 100644 app/models/issue.rb create mode 100644 app/views/comments/_form.html.haml create mode 100644 app/views/comments/edit.html.haml create mode 100644 app/views/issues/_form.html.haml create mode 100644 app/views/issues/_list.html.haml create mode 100644 app/views/issues/edit.html.haml create mode 100644 app/views/issues/index.html.haml create mode 100644 app/views/issues/new.html.haml create mode 100644 app/views/issues/show.html.haml create mode 100644 db/migrate/20111216134039_create_issues.rb create mode 100644 db/migrate/20111216140849_create_comments.rb create mode 100644 db/migrate/20111219073859_add_has_issues_to_projects.rb create mode 100644 spec/controllers/comments_controller_spec.rb create mode 100644 spec/controllers/issues_controller_spec.rb create mode 100644 spec/factories/comments.rb create mode 100644 spec/factories/issues.rb create mode 100644 spec/helpers/comments_helper_spec.rb create mode 100644 spec/helpers/issues_helper_spec.rb create mode 100644 spec/models/comment_spec.rb create mode 100644 spec/models/issue_spec.rb diff --git a/Gemfile b/Gemfile index bd75f3d66..8712951b3 100644 --- a/Gemfile +++ b/Gemfile @@ -22,6 +22,7 @@ gem 'paperclip', "~> 2.3" gem "will_paginate", "~> 3.0.2" gem 'meta-tags', '~> 1.2.4', :require => 'meta_tags' gem "russian" +gem "friendly_id", "~> 4.0.0.beta14" # gem 'ghoul_grack', '~> 0.0.1' gem 'grack', :git => 'git://github.com/rdblue/grack.git', :require => 'git_http' diff --git a/Gemfile.lock b/Gemfile.lock index 33a001a38..3bf319e35 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -77,6 +77,7 @@ GEM factory_girl_rails (1.4.0) factory_girl (~> 2.3.0) railties (>= 3.0.0) + friendly_id (4.0.0.beta8) grit (2.4.1) diff-lcs (~> 1.1) mime-types (~> 1.15) @@ -219,6 +220,7 @@ DEPENDENCIES delayed_job devise (~> 1.5.2) factory_girl_rails (~> 1.4.0) + friendly_id (~> 4.0.0.beta14) grack! grit haml-rails (~> 0.3.4) diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb new file mode 100644 index 000000000..50b0be70f --- /dev/null +++ b/app/controllers/comments_controller.rb @@ -0,0 +1,50 @@ +class CommentsController < ApplicationController + before_filter :authenticate_user! + + def index + @commentable = find_commentable + @comments = @commentable.comments + end + + def create + @commentable = find_commentable + @comment = @commentable.comments.build(params[:comment]) + if @comment.save + flash[:notice] = I18n.t("flash.comment.saved") + redirect_to :id => nil + else + flash[:error] = I18n.t("flash.comment.saved_error") + render :action => 'new' + end + end + + def update + @comment = Comment.find(params[:id]) + if @comment.update_attributes(params[:comment]) + flash[:notice] = I18n.t("flash.comment.saved") + redirect_to :id => nil + else + flash[:error] = I18n.t("flash.comment.saved_error") + render :action => 'new' + end + end + + def destroy + @comment = Comment.find(params[:id]) + @comment.destroy + + flash[:notice] = t("flash.comment.destroyed") + redirect_to root_path + end + + private + + def find_commentable + params.each do |name, value| + if name =~ /(.+)_id$/ + return $1.classify.constantize.find(value) + end + end + nil + end +end diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb new file mode 100644 index 000000000..014d86dc4 --- /dev/null +++ b/app/controllers/issues_controller.rb @@ -0,0 +1,58 @@ +class IssuesController < ApplicationController + before_filter :authenticate_user! + before_filter :find_project + before_filter :find_issue, :only => [:show, :edit, :update, :destroy] + + load_and_authorize_resource + autocomplete :user, :uname + + def index + @issues = @project.issues.paginate :per_page => 10, :page => params[:page] + end + + def create + @user_id = params[:user_id] + @user_uname = params[:user_uname] + + @issue = Issue.new(params[:issue]) + @issue.user_id = @user_id + @issue.project_id = @project.id + if @issue.save! + flash[:notice] = I18n.t("flash.issue.saved") + redirect_to project_issues_path(@project) + else + flash[:error] = I18n.t("flash.issue.saved_error") + render :action => :new + end + end + + def update + @user_id = params[:user_id] + @user_uname = params[:user_uname] + + if @issue.update_attributes( params[:issue].merge({:user_id => @user_id}) ) + flash[:notice] = I18n.t("flash.issue.saved") + redirect_to @issue + else + flash[:error] = I18n.t("flash.issue.saved_error") + render :action => :new + end + end + + def destroy + @issue.destroy + + flash[:notice] = t("flash.issue.destroyed") + redirect_to root_path + end + + private + + def find_project + @project = Project.find(params[:project_id]) + end + + def find_issue + @issue = Issue.find(params[:id]) + end +end diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb new file mode 100644 index 000000000..0ec9ca5f2 --- /dev/null +++ b/app/helpers/comments_helper.rb @@ -0,0 +1,2 @@ +module CommentsHelper +end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb new file mode 100644 index 000000000..bfb9d25e5 --- /dev/null +++ b/app/helpers/issues_helper.rb @@ -0,0 +1,2 @@ +module IssuesHelper +end diff --git a/app/models/comment.rb b/app/models/comment.rb new file mode 100644 index 000000000..2d523d32f --- /dev/null +++ b/app/models/comment.rb @@ -0,0 +1,5 @@ +class Comment < ActiveRecord::Base + belongs_to :commentable + + validates :body, :commentable_id, :commentable_type, :presence => true +end diff --git a/app/models/issue.rb b/app/models/issue.rb new file mode 100644 index 000000000..f1825ab27 --- /dev/null +++ b/app/models/issue.rb @@ -0,0 +1,24 @@ +class Issue < ActiveRecord::Base + STATUSES = ['open', 'close'] + + extend FriendlyId + friendly_id :serial_id + + belongs_to :project + belongs_to :user + + has_many :comments, :as => :commentable + + validates :title, :body, :project_id, :user_id, :presence => true + + attr_readonly :serial_id + + after_create :set_serial_id + + protected + + def set_serial_id + serial_id = project.issues.count + 1 + save + end +end diff --git a/app/models/project.rb b/app/models/project.rb index f0e66c885..56e861308 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -4,6 +4,7 @@ class Project < ActiveRecord::Base belongs_to :category, :counter_cache => true belongs_to :owner, :polymorphic => true + has_many :issues, :dependent => :destroy has_many :build_lists, :dependent => :destroy has_many :auto_build_lists, :dependent => :destroy diff --git a/app/views/comments/_form.html.haml b/app/views/comments/_form.html.haml new file mode 100644 index 000000000..65d4e0704 --- /dev/null +++ b/app/views/comments/_form.html.haml @@ -0,0 +1,10 @@ +.group + = f.label :body, :class => :label + = f.text_area :body, :class => 'text_field', :cols => 80 + +.group.navform.wat-cf + %button.button{:type => "submit"} + = image_tag("web-app-theme/icons/tick.png", :alt => t("layout.save")) + = t("layout.save") + %span.text_button_padding= t("layout.or") + = link_to t("layout.cancel"), project_issue_comment_path(@issue.project, @issue), :class => "text_button_padding link_button" diff --git a/app/views/comments/edit.html.haml b/app/views/comments/edit.html.haml new file mode 100644 index 000000000..516ddd476 --- /dev/null +++ b/app/views/comments/edit.html.haml @@ -0,0 +1,10 @@ +.block + .secondary-navigation + %ul.wat-cf + %li.first= link_to t("layout.issues.list"), project_issue_path(@project, @issue) + .content + %h2.title + = t("layout.issues.edit_header") + .inner + = form_for @comment, :url => project_issue_comment_path(@project, @issue, @comment), :html => { :class => :form } do |f| + = render :partial => "form", :locals => {:f => f} diff --git a/app/views/issues/_form.html.haml b/app/views/issues/_form.html.haml new file mode 100644 index 000000000..bb1bc72ed --- /dev/null +++ b/app/views/issues/_form.html.haml @@ -0,0 +1,25 @@ += javascript_include_tag "autocomplete-rails.js" + +.group + = f.label :title, :class => :label + = f.text_field :title, :class => 'text_field' + +.group + = f.label :body, :class => :label + = f.text_area :body, :class => 'text_field', :cols => 80 + +.group + = f.label :status, :class => :label + = f.select :status, Issue::STATUSES, :class => 'text_field' + +.group + = label_tag "", t("layout.issues.user_id"), :class => :label + = autocomplete_field_tag 'user_id', @user_uname, autocomplete_user_uname_platforms_path, :id_element => '#user_id_field' + = hidden_field_tag 'user_id', @user_id, :id => 'user_id_field' + +.group.navform.wat-cf + %button.button{:type => "submit"} + = image_tag("web-app-theme/icons/tick.png", :alt => t("layout.save")) + = t("layout.save") + %span.text_button_padding= t("layout.or") + = link_to t("layout.cancel"), project_path(@project), :class => "text_button_padding link_button" diff --git a/app/views/issues/_list.html.haml b/app/views/issues/_list.html.haml new file mode 100644 index 000000000..e0a9757ee --- /dev/null +++ b/app/views/issues/_list.html.haml @@ -0,0 +1,15 @@ +%table.table + %tr + %th.first= t("activerecord.attributes.issue.title") + %th.first= t("activerecord.attributes.issue.status") + %th.last   + - @project.issues.each do |issue| + %tr{:class => cycle("odd", "even")} + %td + = link_to issue.title, project_issue_path(@project, issue) + %td + = issue.status + %td.last + = link_to t("layout.show"), project_issue_path(@project, issue) + | + = link_to t("layout.delete"), project_issue_path(@project, issue), :method => :delete, :confirm => t("layout.issues.confirm_delete") if can? :destroy, issue diff --git a/app/views/issues/edit.html.haml b/app/views/issues/edit.html.haml new file mode 100644 index 000000000..99a3533b0 --- /dev/null +++ b/app/views/issues/edit.html.haml @@ -0,0 +1,12 @@ +.block + .secondary-navigation + %ul.wat-cf + %li.first= link_to t("layout.issues.list"), project_issues_path(@project) + %li= link_to t("layout.issues.new"), new_project_issue_path(@project) + %li.active= link_to t("layout.issues.edit"), edit_project_issue_path(@project) + .content + %h2.title + = t("layout.issues.edit_header") + .inner + = form_for @issue, :url => project_issue_path(@issue), :html => { :class => :form } do |f| + = render :partial => "form", :locals => {:f => f} diff --git a/app/views/issues/index.html.haml b/app/views/issues/index.html.haml new file mode 100644 index 000000000..9b8661458 --- /dev/null +++ b/app/views/issues/index.html.haml @@ -0,0 +1,14 @@ +.block + .secondary-navigation + %ul.wat-cf + %li.first.active= link_to t("layout.issues.list"), project_issues_path(@project) + %li= link_to t("layout.issues.new"), new_project_issue_path(@project) if can? :create, Issue + .content + %h2.title + = t("layout.issues.list_header") + .inner + = render :partial => 'shared/search_form' + = render :partial => 'issues/list', :object => @issues + .actions-bar.wat-cf + .actions + = will_paginate @issues, :param_name => :issue_page diff --git a/app/views/issues/new.html.haml b/app/views/issues/new.html.haml new file mode 100644 index 000000000..cb94f0ac4 --- /dev/null +++ b/app/views/issues/new.html.haml @@ -0,0 +1,11 @@ +.block + .secondary-navigation + %ul.wat-cf + %li.first= link_to "#{t("layout.issues.list")}", project_issues_path(@project) + %li.active= link_to "#{t("layout.issues.new")}", new_project_issue_path(@project) + .content + %h2.title + = t("layout.issues.new_header") + .inner + = form_for :issue, :url => project_issues_path(@project), :html => { :class => :form } do |f| + = render :partial => "form", :locals => {:f => f} diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml new file mode 100644 index 000000000..5092edc54 --- /dev/null +++ b/app/views/issues/show.html.haml @@ -0,0 +1,53 @@ +.block + .secondary-navigation + %ul.wat-cf + %li.first= link_to t("layout.issues.list"), project_issues_path(@project) + %li= link_to t("layout.issues.edit"), edit_project_issue_path(@project, @issue) if can? :edit, @issue + .content + .inner + %p + %b + = t("activerecord.attributes.issue.title") + \: + = @issue.title + %p + %b + = t("activerecord.attributes.issue.body") + \: + = @issue.body + %p + %b + = t('layout.issues.status') + \: + = @issue.status + +%a{ :name => "comments" } +.block + .secondary-navigation + %ul.wat-cf + .content + %h2.title + = t("layout.issues.comments_header") + .inner + %table.table + %tr + %th.first= t("activerecord.attributes.user.uname") + %th.first= t("activerecord.attributes.comment.body") + %th.last   + - @issue.comments.each do |comment| + %tr{:class => cycle("odd", "even")} + %td + = comment.user.uname + %td + = comment.body + %td.last + = link_to t("layout.edit"), edit_project_issue_comment_path(@project, @issue, comment) if can? :update, comment + = link_to image_tag("web-app-theme/icons/cross.png", :alt => t("layout.delete")) + " " + t("layout.delete"), project_issue_comment_path(@project, @issue, comment), :method => "delete", :class => "button", :confirm => t("layout.comment.confirm_delete") if can? :delete, comment + +.block + .content + %h2.title + = t("layout.comments.new_header") + .inner + = form_for :comment, :url => project_issue_comments_path(@project, @issue), :html => { :class => :form } do |f| + = render :partial => "comments/form", :locals => {:f => f} diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 3930d97a2..7bbc69176 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -6,6 +6,7 @@ %li.active= link_to t("layout.projects.show"), project_path(@project) %li= link_to t("layout.git.repositories.source"), project_repo_path(@project) %li= link_to t("layout.projects.build"), build_project_path(@project) + %li= link_to t("layout.projects.issues"), project_issues_path(@project) .content .inner diff --git a/config/routes.rb b/config/routes.rb index 5d22a7cfe..721c6f254 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -68,6 +68,9 @@ Rosa::Application.routes.draw do end resources :projects do + resources :issues do + resources :comments, :only => [:edit, :create, :update, :destroy] + end resource :repo, :controller => "git/repositories", :only => [:show] resources :build_lists, :only => [:index, :show] do collection do diff --git a/db/migrate/20111216134039_create_issues.rb b/db/migrate/20111216134039_create_issues.rb new file mode 100644 index 000000000..8d6a47315 --- /dev/null +++ b/db/migrate/20111216134039_create_issues.rb @@ -0,0 +1,20 @@ +class CreateIssues < ActiveRecord::Migration + def self.up + create_table :issues do |t| + t.integer :serial_id + t.integer :project_id + t.integer :user_id + t.string :title + t.text :body + t.string :status + + t.timestamps + end + + add_index :issues, [:project_id, :serial_id], :unique => true + end + + def self.down + drop_table :issues + end +end diff --git a/db/migrate/20111216140849_create_comments.rb b/db/migrate/20111216140849_create_comments.rb new file mode 100644 index 000000000..d05b53eb7 --- /dev/null +++ b/db/migrate/20111216140849_create_comments.rb @@ -0,0 +1,16 @@ +class CreateComments < ActiveRecord::Migration + def self.up + create_table :comments do |t| + t.integer :commentable_id + t.string :commentable_type + t.integer :user_id + t.text :body + + t.timestamps + end + end + + def self.down + drop_table :comments + end +end diff --git a/db/migrate/20111219073859_add_has_issues_to_projects.rb b/db/migrate/20111219073859_add_has_issues_to_projects.rb new file mode 100644 index 000000000..f0ea1e42c --- /dev/null +++ b/db/migrate/20111219073859_add_has_issues_to_projects.rb @@ -0,0 +1,9 @@ +class AddHasIssuesToProjects < ActiveRecord::Migration + def self.up + add_column :projects, :has_issues, :boolean, :default => true + end + + def self.down + remove_column :projects, :has_issues + end +end diff --git a/db/schema.rb b/db/schema.rb index 17c70bd11..5acde5031 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20111130181101) do +ActiveRecord::Schema.define(:version => 20111219073859) do create_table "arches", :force => true do |t| t.string "name", :null => false @@ -83,6 +83,15 @@ ActiveRecord::Schema.define(:version => 20111130181101) do t.datetime "updated_at" end + create_table "comments", :force => true do |t| + t.integer "commentable_id" + t.string "commentable_type" + t.integer "user_id" + t.text "body" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "containers", :force => true do |t| t.string "name", :null => false t.integer "project_id", :null => false @@ -140,6 +149,19 @@ ActiveRecord::Schema.define(:version => 20111130181101) do t.string "uname" end + create_table "issues", :force => true do |t| + t.integer "serial_id" + t.integer "project_id" + t.integer "user_id" + t.string "title" + t.text "body" + t.string "status" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "issues", ["project_id", "serial_id"], :name => "index_issues_on_project_id_and_serial_id", :unique => true + create_table "platforms", :force => true do |t| t.string "description" t.string "name" @@ -211,6 +233,7 @@ ActiveRecord::Schema.define(:version => 20111130181101) do t.integer "category_id" t.text "description" t.string "ancestry" + t.boolean "has_issues", :default => true end add_index "projects", ["category_id"], :name => "index_projects_on_category_id" diff --git a/spec/controllers/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb new file mode 100644 index 000000000..562bf1aff --- /dev/null +++ b/spec/controllers/comments_controller_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe CommentsController do + +end diff --git a/spec/controllers/issues_controller_spec.rb b/spec/controllers/issues_controller_spec.rb new file mode 100644 index 000000000..c27154bda --- /dev/null +++ b/spec/controllers/issues_controller_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe IssuesController do + +end diff --git a/spec/factories/comments.rb b/spec/factories/comments.rb new file mode 100644 index 000000000..f9df3b695 --- /dev/null +++ b/spec/factories/comments.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :comment do + end +end \ No newline at end of file diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb new file mode 100644 index 000000000..5e4c566b7 --- /dev/null +++ b/spec/factories/issues.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :issue do + end +end \ No newline at end of file diff --git a/spec/helpers/comments_helper_spec.rb b/spec/helpers/comments_helper_spec.rb new file mode 100644 index 000000000..cc93aa9b6 --- /dev/null +++ b/spec/helpers/comments_helper_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +# Specs in this file have access to a helper object that includes +# the CommentsHelper. For example: +# +# describe CommentsHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# helper.concat_strings("this","that").should == "this that" +# end +# end +# end +describe CommentsHelper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb new file mode 100644 index 000000000..2e20f1cf1 --- /dev/null +++ b/spec/helpers/issues_helper_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +# Specs in this file have access to a helper object that includes +# the IssuesHelper. For example: +# +# describe IssuesHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# helper.concat_strings("this","that").should == "this that" +# end +# end +# end +describe IssuesHelper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb new file mode 100644 index 000000000..505e33d36 --- /dev/null +++ b/spec/models/comment_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Comment do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb new file mode 100644 index 000000000..881318921 --- /dev/null +++ b/spec/models/issue_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Issue do + pending "add some examples to (or delete) #{__FILE__}" +end From 446cdac8e61d540d0d772398f62df51ea5a40e2d Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Tue, 20 Dec 2011 14:20:00 +0400 Subject: [PATCH 2/9] [refs #54] Add new locales. Fix routes. Fix issues and comments update --- app/controllers/comments_controller.rb | 20 +++++++++---- app/models/comment.rb | 5 ++-- app/views/issues/show.html.haml | 6 ++-- config/locales/ru.yml | 39 ++++++++++++++++++++++++-- 4 files changed, 57 insertions(+), 13 deletions(-) diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 50b0be70f..0964f2f36 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -1,28 +1,34 @@ class CommentsController < ApplicationController before_filter :authenticate_user! + before_filter :set_commentable, :only => [:index, :edit, :create] def index - @commentable = find_commentable @comments = @commentable.comments end def create - @commentable = find_commentable @comment = @commentable.comments.build(params[:comment]) + @comment.user = current_user if @comment.save flash[:notice] = I18n.t("flash.comment.saved") - redirect_to :id => nil + redirect_to :back else flash[:error] = I18n.t("flash.comment.saved_error") render :action => 'new' end end + def edit + @comment = Comment.find(params[:id]) + @issue = @commentable + @project = @issue.project + end + def update @comment = Comment.find(params[:id]) if @comment.update_attributes(params[:comment]) flash[:notice] = I18n.t("flash.comment.saved") - redirect_to :id => nil + redirect_to :back else flash[:error] = I18n.t("flash.comment.saved_error") render :action => 'new' @@ -34,7 +40,7 @@ class CommentsController < ApplicationController @comment.destroy flash[:notice] = t("flash.comment.destroyed") - redirect_to root_path + redirect_to :back end private @@ -47,4 +53,8 @@ class CommentsController < ApplicationController end nil end + + def set_commentable + @commentable = find_commentable + end end diff --git a/app/models/comment.rb b/app/models/comment.rb index 2d523d32f..4c15a8b66 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,5 +1,6 @@ class Comment < ActiveRecord::Base - belongs_to :commentable + belongs_to :commentable, :polymorphic => true + belongs_to :user - validates :body, :commentable_id, :commentable_type, :presence => true + validates :body, :user_id, :commentable_id, :commentable_type, :presence => true end diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index 5092edc54..a68e58fea 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -17,7 +17,7 @@ = @issue.body %p %b - = t('layout.issues.status') + = t('activerecord.attributes.issue.status') \: = @issue.status @@ -42,12 +42,12 @@ = comment.body %td.last = link_to t("layout.edit"), edit_project_issue_comment_path(@project, @issue, comment) if can? :update, comment - = link_to image_tag("web-app-theme/icons/cross.png", :alt => t("layout.delete")) + " " + t("layout.delete"), project_issue_comment_path(@project, @issue, comment), :method => "delete", :class => "button", :confirm => t("layout.comment.confirm_delete") if can? :delete, comment + = link_to image_tag("web-app-theme/icons/cross.png", :alt => t("layout.delete")) + " " + t("layout.delete"), project_issue_comment_path(@project, @issue, comment), :method => "delete", :class => "button", :confirm => t("layout.comments.confirm_delete") if can? :delete, comment .block .content %h2.title = t("layout.comments.new_header") .inner - = form_for :comment, :url => project_issue_comments_path(@project, @issue), :html => { :class => :form } do |f| + = form_for :comment, :url => project_issue_comments_path(@project, @issue), :method => :post, :html => { :class => :form } do |f| = render :partial => "comments/form", :locals => {:f => f} diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 6f343a6e4..7cfca7ad7 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -83,6 +83,7 @@ ru: confirm_regenerate: Вы уверены, что хотите перегенерировать эту пару логин/пароль? regenerate_btn: Перегенерировать warning_message: Примечание - При получении новых данных старые становятся недействительными + categories: list: Список new: Создать @@ -93,6 +94,19 @@ ru: edit_header: Редактировать категорию confirm_delete: Вы уверены, что хотите удалить эту категорию? + issues: + list: Список + edit: Редактировать + comments_header: Комментарии + new: Новая задача + list_header: Список + confirm_delete: Вы уверены, что хотите удалить эту задачу? + new_header: Новая задача + + comments: + confirm_delete: Вы уверены, что хотите удалить комментарий? + new_header: Новый комментарий + platforms: admin_id: Владелец build_all: Собрать все @@ -214,6 +228,7 @@ ru: collaborators: Коллабораторы groups: Группы edit_collaborators: Изменить список участников + issues: Задачи collaborators: back_to_proj: Вернуться к проекту @@ -332,7 +347,7 @@ ru: downloads: statistics_refreshed: Статистика обновлена - + collaborators: successfully_changed: Список коллабораторов успешно изменен error_in_changing: Ошибка изменения списка коллабораторов @@ -353,12 +368,19 @@ ru: failed: Не удалось автоматизировать сборку! cancel: Автоматическая сборка проекта отменена! cancel_failed: Не удалось отменить автоматическую сборку проекта! - + category: saved: Категория успешно сохранена save_error: Не удалось сохранить категорию destroyed: Категория успешно удалена + comment: + saved: Комментарий успешно сохранен + destroyed: Комментарий удален + + issue: + saved: Задача успешно сохранена + project: saved: Проект успешно сохранен save_error: Не удалось сохранить проект @@ -455,7 +477,18 @@ ru: pl: Платформа arch_id: Архитектура arch: Архитектура - + + comment: + body: Содержание + user: Автор + + issue: + title: Заголовок + body: Содержание + user: Назначено + project: Проект + status: Статус + private_user: login: Логин password: Пароль From 375c98366f9215803d0e855fe5562dafcf48dcf6 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Tue, 20 Dec 2011 20:57:34 +0400 Subject: [PATCH 3/9] [ref #54] Add has_issue settings to projec edit. Fix routes and friendly_id bug. Fix some moments --- Gemfile | 1 - app/controllers/issues_controller.rb | 6 +++++- app/models/issue.rb | 9 +++------ app/views/comments/_form.html.haml | 2 +- app/views/issues/_list.html.haml | 4 ++-- app/views/projects/_form.html.haml | 3 +++ config/locales/ru.yml | 1 + config/routes.rb | 3 ++- 8 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Gemfile b/Gemfile index 8712951b3..bd75f3d66 100644 --- a/Gemfile +++ b/Gemfile @@ -22,7 +22,6 @@ gem 'paperclip', "~> 2.3" gem "will_paginate", "~> 3.0.2" gem 'meta-tags', '~> 1.2.4', :require => 'meta_tags' gem "russian" -gem "friendly_id", "~> 4.0.0.beta14" # gem 'ghoul_grack', '~> 0.0.1' gem 'grack', :git => 'git://github.com/rdblue/grack.git', :require => 'git_http' diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 014d86dc4..fa83ead1f 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -1,11 +1,15 @@ class IssuesController < ApplicationController before_filter :authenticate_user! before_filter :find_project - before_filter :find_issue, :only => [:show, :edit, :update, :destroy] + before_filter :find_issue, :only => [:edit, :update, :destroy] load_and_authorize_resource autocomplete :user, :uname + def show + @issue = @project.issues.where(:serial_id => params[:serial_id])[0] + end + def index @issues = @project.issues.paginate :per_page => 10, :page => params[:page] end diff --git a/app/models/issue.rb b/app/models/issue.rb index f1825ab27..0e971f55f 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -1,9 +1,6 @@ class Issue < ActiveRecord::Base STATUSES = ['open', 'close'] - extend FriendlyId - friendly_id :serial_id - belongs_to :project belongs_to :user @@ -11,14 +8,14 @@ class Issue < ActiveRecord::Base validates :title, :body, :project_id, :user_id, :presence => true - attr_readonly :serial_id + #attr_readonly :serial_id after_create :set_serial_id protected def set_serial_id - serial_id = project.issues.count + 1 - save + self.serial_id = self.project.issues.count + self.save! end end diff --git a/app/views/comments/_form.html.haml b/app/views/comments/_form.html.haml index 65d4e0704..ccca4cc1d 100644 --- a/app/views/comments/_form.html.haml +++ b/app/views/comments/_form.html.haml @@ -7,4 +7,4 @@ = image_tag("web-app-theme/icons/tick.png", :alt => t("layout.save")) = t("layout.save") %span.text_button_padding= t("layout.or") - = link_to t("layout.cancel"), project_issue_comment_path(@issue.project, @issue), :class => "text_button_padding link_button" + = link_to t("layout.cancel"), show_issue_path(@issue.project, @issue.serial_id), :class => "text_button_padding link_button" diff --git a/app/views/issues/_list.html.haml b/app/views/issues/_list.html.haml index e0a9757ee..9e27261bb 100644 --- a/app/views/issues/_list.html.haml +++ b/app/views/issues/_list.html.haml @@ -6,10 +6,10 @@ - @project.issues.each do |issue| %tr{:class => cycle("odd", "even")} %td - = link_to issue.title, project_issue_path(@project, issue) + = link_to issue.title, show_issue_path(@project, issue.serial_id) %td = issue.status %td.last - = link_to t("layout.show"), project_issue_path(@project, issue) + = link_to t("layout.show"), show_issue_path(@project, issue.serial_id) | = link_to t("layout.delete"), project_issue_path(@project, issue), :method => :delete, :confirm => t("layout.issues.confirm_delete") if can? :destroy, issue diff --git a/app/views/projects/_form.html.haml b/app/views/projects/_form.html.haml index 832d033bb..d0cc3e7bf 100644 --- a/app/views/projects/_form.html.haml +++ b/app/views/projects/_form.html.haml @@ -10,6 +10,9 @@ .group = f.label :description, t("activerecord.attributes.project.description"), :class => :label = f.text_area :description, :class => 'text_field', :cols => 80 +.group + = f.label :has_issues, t("activerecord.attributes.project.has_issues"), :class => :label + = f.check_box :has_issues .group.navform.wat-cf %button.button{:type => "submit"} diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 7cfca7ad7..cf6d82a55 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -569,6 +569,7 @@ ru: repository: Репозиторий created_at: Создан updated_at: Обновлен + has_issues: Включить трэкер rpm: name: Название diff --git a/config/routes.rb b/config/routes.rb index 721c6f254..b3fc4bc05 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -67,8 +67,9 @@ Rosa::Application.routes.draw do resources :categories, :only => [:index, :show] end + match "projects/:project_id/issues/:serial_id" => 'issues#show', :as => :show_issue, :via => :get resources :projects do - resources :issues do + resources :issues, :except => [:show] do resources :comments, :only => [:edit, :create, :update, :destroy] end resource :repo, :controller => "git/repositories", :only => [:show] From 3ab55d34b8d0ce241872bec81766c213ded77e26 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Wed, 21 Dec 2011 18:48:16 +0400 Subject: [PATCH 4/9] [refs #54] Add CanCan rules for issues and comments. Fix serial_id routes and some actions --- Gemfile.lock | 2 - app/controllers/comments_controller.rb | 29 +++++++++----- app/controllers/issues_controller.rb | 21 +++++++---- app/models/ability.rb | 52 +++++++++++++++++++++++--- app/models/issue.rb | 2 +- app/views/issues/edit.html.haml | 3 +- app/views/issues/index.html.haml | 2 +- app/views/issues/show.html.haml | 2 +- config/routes.rb | 3 +- 9 files changed, 86 insertions(+), 30 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3bf319e35..33a001a38 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -77,7 +77,6 @@ GEM factory_girl_rails (1.4.0) factory_girl (~> 2.3.0) railties (>= 3.0.0) - friendly_id (4.0.0.beta8) grit (2.4.1) diff-lcs (~> 1.1) mime-types (~> 1.15) @@ -220,7 +219,6 @@ DEPENDENCIES delayed_job devise (~> 1.5.2) factory_girl_rails (~> 1.4.0) - friendly_id (~> 4.0.0.beta14) grack! grit haml-rails (~> 0.3.4) diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 0964f2f36..e50844862 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -1,6 +1,11 @@ class CommentsController < ApplicationController before_filter :authenticate_user! before_filter :set_commentable, :only => [:index, :edit, :create] + before_filter :find_project, :only => [:index] + before_filter :find_comment, :only => [:show, :edit, :update, :destroy] + + authorize_resource :only => [:show, :edit, :update, :destroy] + authorize_resource :project, :only => [:index] def index @comments = @commentable.comments @@ -19,13 +24,11 @@ class CommentsController < ApplicationController end def edit - @comment = Comment.find(params[:id]) @issue = @commentable @project = @issue.project end def update - @comment = Comment.find(params[:id]) if @comment.update_attributes(params[:comment]) flash[:notice] = I18n.t("flash.comment.saved") redirect_to :back @@ -36,7 +39,6 @@ class CommentsController < ApplicationController end def destroy - @comment = Comment.find(params[:id]) @comment.destroy flash[:notice] = t("flash.comment.destroyed") @@ -46,15 +48,24 @@ class CommentsController < ApplicationController private def find_commentable - params.each do |name, value| - if name =~ /(.+)_id$/ - return $1.classify.constantize.find(value) - end - end - nil + #params.each do |name, value| + # if name =~ /(.+)_id$/ + # return $1.classify.constantize.find(value) + # end + #end + #nil + return Issue.find(params[:issue_id]) end def set_commentable @commentable = find_commentable end + + def find_comment + @comment = Comment.find(params[:id]) + end + + def find_project + @project = @comment.commentable.project + end end diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index fa83ead1f..140a5c15c 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -1,13 +1,15 @@ class IssuesController < ApplicationController before_filter :authenticate_user! - before_filter :find_project - before_filter :find_issue, :only => [:edit, :update, :destroy] + before_filter :find_project, :except => [:destroy] + before_filter :find_issue_by_serial_id, :only => [:show, :edit] - load_and_authorize_resource + load_and_authorize_resource :except => [:show, :edit, :index] + authorize_resource :only => [:show, :edit] + #authorize_resource :through => :project, :only => [:index], :shallow => true + authorize_resource :project, :only => [:index] autocomplete :user, :uname def show - @issue = @project.issues.where(:serial_id => params[:serial_id])[0] end def index @@ -30,13 +32,18 @@ class IssuesController < ApplicationController end end + def edit + @user_id = @issue.user_id + @user_uname = @issue.user.uname + end + def update @user_id = params[:user_id] @user_uname = params[:user_uname] if @issue.update_attributes( params[:issue].merge({:user_id => @user_id}) ) flash[:notice] = I18n.t("flash.issue.saved") - redirect_to @issue + redirect_to show_issue_path(@project, @issue.serial_id) else flash[:error] = I18n.t("flash.issue.saved_error") render :action => :new @@ -56,7 +63,7 @@ class IssuesController < ApplicationController @project = Project.find(params[:project_id]) end - def find_issue - @issue = Issue.find(params[:id]) + def find_issue_by_serial_id + @issue = @project.issues.where(:serial_id => params[:serial_id])[0] end end diff --git a/app/models/ability.rb b/app/models/ability.rb index fd6118a6b..f3e67d84b 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -3,7 +3,7 @@ class Ability def initialize(user) user ||= User.new # guest user (not logged in) - + if user.admin? can :manage, :all else @@ -73,7 +73,7 @@ class Ability can [:read, :update], Group, groups_in_relations_with(:role => ['writer', 'admin'], :object_type => 'User', :object_id => user.id) do |group| group.objects.exists?(:role => ['writer', 'admin'], :object_type => 'User', :object_id => user.id) end - + can :manage, Platform, :owner_type => 'User', :owner_id => user.id can :manage, Platform, platforms_in_relations_with(:role => 'admin', :object_type => 'User', :object_id => user.id) do |platform| platform.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) @@ -95,10 +95,39 @@ class Ability can [:new, :create], Repository do |repository| repository.platform.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) end - + + can [:show, :index], Issue do |issue| + issue.status == 'open' + end + #can [:show, :index], Issue, with_project_id_in_relations_with(:object_type => 'User', :object_id => user.id) do |issue| + can [:show, :index], Issue do |issue| + issue.project.relations.exists?(:object_type => 'User', :object_id => user.id) + end + can [:create, :new], Issue do |issue| + issue.project.relations.exists?(:role => ['writer', 'admin'], :object_type => 'User', :object_id => user.id) + end + can [:edit, :update], Issue do |issue| + issue.user_id == user.id || (user.id == issue.project.owner_id && issue.project.owner_type == 'User') || + issue.project.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) + end + can [:create, :new], Comment do |comment| + comment.commentable.project.relations.exists?(:object_type => 'User', :object_id => user.id) + end + can [:edit, :update], Comment do |comment| + comment.user_id == user.id || (user.id == comment.commentable.project.owner_id && comment.commentable.project.owner_type == 'User') || + comment.commentable.project.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) + end + # + cannot [:index, :edit, :update, :create, :new, :show], Issue do |issue| + !issue.project.has_issues + end + cannot [:edit, :update, :create, :new, :destroy], Comment do |comment| + !comment.commentable.project.has_issues + end + #can :read, Repository # TODO: Add personal repos rules - + # Same rights for groups: can [:read, :create], PrivateUser, :platform => {:owner_type => 'Group', :owner_id => user.group_ids} can :publish, BuildList do |build_list| @@ -121,7 +150,7 @@ class Ability can [:read, :update, :process_build, :build], Project, projects_in_relations_with(:role => ['writer', 'admin'], :object_type => 'Group', :object_id => user.group_ids) do |project| project.relations.exists?(:role => ['writer', 'admin'], :object_type => 'Group', :object_id => user.group_ids) end - + can :manage, Platform, :owner_type => 'Group', :owner_id => user.group_ids #can :read, Platform, :groups => {:id => user.group_ids} can :read, Platform, platforms_in_relations_with(:role => 'reader', :object_type => 'Group', :object_id => user.group_ids) do |platform| @@ -140,11 +169,12 @@ class Ability cannot :create, [Platform, User] end end - + # Shared cannot rights for all users (guests, registered, admin) cannot :destroy, Platform, :platform_type => 'personal' cannot :destroy, Repository, :platform => {:platform_type => 'personal'} cannot :fork, Project, :owner_id => user.id, :owner_type => user.class.to_s + cannot :destroy, Issue end # Sub query for platforms, projects relations @@ -161,6 +191,16 @@ class Ability end end + def with_project_id_in_relations_with(opts = {}) + query = "issues.project_id IN (SELECT target_id FROM relations WHERE relations.target_type = 'issues'" + opts.each do |key, value| + query = query + " AND relations.#{ key } #{ value.class == Array ? 'IN (?)' : '= ?' } " + end + query = query + ")" + + return opts.values.unshift query + end + ## Sub query for project relations #def projects_in_relations_with(opts={}) # ["projects.id IN (SELECT target_id FROM relations WHERE relations.object_id #{ opts[:object_id].class == Array ? 'IN (?)' : '= ?' } AND relations.object_type = '#{ opts[:object_type] }' AND relations.target_type = 'Platform') AND relations.role", opts[:object_id]] diff --git a/app/models/issue.rb b/app/models/issue.rb index 0e971f55f..ad6f57468 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -1,5 +1,5 @@ class Issue < ActiveRecord::Base - STATUSES = ['open', 'close'] + STATUSES = ['open', 'closed'] belongs_to :project belongs_to :user diff --git a/app/views/issues/edit.html.haml b/app/views/issues/edit.html.haml index 99a3533b0..aa09ab66f 100644 --- a/app/views/issues/edit.html.haml +++ b/app/views/issues/edit.html.haml @@ -3,10 +3,9 @@ %ul.wat-cf %li.first= link_to t("layout.issues.list"), project_issues_path(@project) %li= link_to t("layout.issues.new"), new_project_issue_path(@project) - %li.active= link_to t("layout.issues.edit"), edit_project_issue_path(@project) .content %h2.title = t("layout.issues.edit_header") .inner - = form_for @issue, :url => project_issue_path(@issue), :html => { :class => :form } do |f| + = form_for @issue, :url => project_issue_path(@project, @issue), :html => { :class => :form } do |f| = render :partial => "form", :locals => {:f => f} diff --git a/app/views/issues/index.html.haml b/app/views/issues/index.html.haml index 9b8661458..aa3323b70 100644 --- a/app/views/issues/index.html.haml +++ b/app/views/issues/index.html.haml @@ -2,7 +2,7 @@ .secondary-navigation %ul.wat-cf %li.first.active= link_to t("layout.issues.list"), project_issues_path(@project) - %li= link_to t("layout.issues.new"), new_project_issue_path(@project) if can? :create, Issue + %li= link_to t("layout.issues.new"), new_project_issue_path(@project) if can? :new, Issue.new(:project_id => @project.id) .content %h2.title = t("layout.issues.list_header") diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index a68e58fea..51c080e4a 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -2,7 +2,7 @@ .secondary-navigation %ul.wat-cf %li.first= link_to t("layout.issues.list"), project_issues_path(@project) - %li= link_to t("layout.issues.edit"), edit_project_issue_path(@project, @issue) if can? :edit, @issue + %li= link_to t("layout.issues.edit"), edit_project_issue_path(@project, @issue.serial_id) if can? :edit, @issue .content .inner %p diff --git a/config/routes.rb b/config/routes.rb index b3fc4bc05..120556a0b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -67,7 +67,8 @@ Rosa::Application.routes.draw do resources :categories, :only => [:index, :show] end - match "projects/:project_id/issues/:serial_id" => 'issues#show', :as => :show_issue, :via => :get + match "projects/:project_id/issues/:serial_id" => 'issues#show', :serial_id => /\d+/, :as => :show_issue, :via => :get + match "projects/:project_id/issues/:serial_id/edit" => 'issues#edit', :serial_id => /\d+/, :as => :edit_issue, :via => :get resources :projects do resources :issues, :except => [:show] do resources :comments, :only => [:edit, :create, :update, :destroy] From e2144e0fdb43b37c5262d88a66cd15b5c52b53ae Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Thu, 22 Dec 2011 11:41:37 +0400 Subject: [PATCH 5/9] [refs #54] Fix issue ability and comment redirect after update --- app/controllers/comments_controller.rb | 3 ++- app/controllers/issues_controller.rb | 15 ++++++++------- app/models/ability.rb | 12 ++++++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index e50844862..d6775451d 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -31,7 +31,8 @@ class CommentsController < ApplicationController def update if @comment.update_attributes(params[:comment]) flash[:notice] = I18n.t("flash.comment.saved") - redirect_to :back + #redirect_to :back + redirect_to show_issue_path(@comment.commentable.project, @comment.commentable.serial_id) else flash[:error] = I18n.t("flash.comment.saved_error") render :action => 'new' diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 140a5c15c..ea0cc3c55 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -1,17 +1,13 @@ class IssuesController < ApplicationController before_filter :authenticate_user! before_filter :find_project, :except => [:destroy] - before_filter :find_issue_by_serial_id, :only => [:show, :edit] + before_filter :find_and_authorize_by_serial_id, :only => [:show, :edit] + before_filter :set_issue_stub, :only => [:new, :create] load_and_authorize_resource :except => [:show, :edit, :index] - authorize_resource :only => [:show, :edit] - #authorize_resource :through => :project, :only => [:index], :shallow => true authorize_resource :project, :only => [:index] autocomplete :user, :uname - def show - end - def index @issues = @project.issues.paginate :per_page => 10, :page => params[:page] end @@ -63,7 +59,12 @@ class IssuesController < ApplicationController @project = Project.find(params[:project_id]) end - def find_issue_by_serial_id + def find_and_authorize_by_serial_id @issue = @project.issues.where(:serial_id => params[:serial_id])[0] + authorize! params[:action].to_sym, @issue + end + + def set_issue_stub + @issue = Issue.new(:project => @project) end end diff --git a/app/models/ability.rb b/app/models/ability.rb index f3e67d84b..6af166beb 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -96,11 +96,13 @@ class Ability repository.platform.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) end - can [:show, :index], Issue do |issue| + can [:read, :index], Issue do |issue| + puts "SHIT\n"*10 issue.status == 'open' end - #can [:show, :index], Issue, with_project_id_in_relations_with(:object_type => 'User', :object_id => user.id) do |issue| - can [:show, :index], Issue do |issue| + #can [:read], Issue, :status => 'open' + #can [:show], Issue, with_project_id_in_relations_with(:object_type => 'User', :object_id => user.id) + can [:read, :index], Issue do |issue| issue.project.relations.exists?(:object_type => 'User', :object_id => user.id) end can [:create, :new], Issue do |issue| @@ -118,7 +120,9 @@ class Ability comment.commentable.project.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) end # - cannot [:index, :edit, :update, :create, :new, :show], Issue do |issue| + cannot [:index, :edit, :update, :create, :new, :read], Issue do |issue| + puts "FUCK\n"*10 + puts !issue.project.has_issues !issue.project.has_issues end cannot [:edit, :update, :create, :new, :destroy], Comment do |comment| From 894bc0c077d0395e594aeddcdf785653bbc2b0ba Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Fri, 23 Dec 2011 14:56:46 +0400 Subject: [PATCH 6/9] [refs #54] Add issues tests. Fix some CanCan rights. Make some fixes to logic --- app/controllers/comments_controller.rb | 2 +- app/controllers/issues_controller.rb | 4 +- app/models/ability.rb | 5 +- db/schema.rb | 11 +- spec/controllers/issues_controller_spec.rb | 191 ++++++++++++++++++++- spec/factories/issues.rb | 12 +- spec/models/cancan_spec.rb | 32 +++- 7 files changed, 234 insertions(+), 23 deletions(-) diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index d6775451d..93baf3b77 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -2,7 +2,7 @@ class CommentsController < ApplicationController before_filter :authenticate_user! before_filter :set_commentable, :only => [:index, :edit, :create] before_filter :find_project, :only => [:index] - before_filter :find_comment, :only => [:show, :edit, :update, :destroy] + before_filter :find_comment, :only => [:edit, :update, :destroy] authorize_resource :only => [:show, :edit, :update, :destroy] authorize_resource :project, :only => [:index] diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index ea0cc3c55..85cd9f291 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -34,8 +34,8 @@ class IssuesController < ApplicationController end def update - @user_id = params[:user_id] - @user_uname = params[:user_uname] + @user_id = params[:user_id].blank? ? @issue.user_id : params[:user_id] + @user_uname = params[:user_uname].blank? ? @issue.user.uname : params[:user_uname] if @issue.update_attributes( params[:issue].merge({:user_id => @user_id}) ) flash[:notice] = I18n.t("flash.issue.saved") diff --git a/app/models/ability.rb b/app/models/ability.rb index 6af166beb..ca4b2ffc7 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -97,7 +97,6 @@ class Ability end can [:read, :index], Issue do |issue| - puts "SHIT\n"*10 issue.status == 'open' end #can [:read], Issue, :status => 'open' @@ -120,9 +119,7 @@ class Ability comment.commentable.project.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) end # - cannot [:index, :edit, :update, :create, :new, :read], Issue do |issue| - puts "FUCK\n"*10 - puts !issue.project.has_issues + cannot [:index, :edit, :update, :create, :new, :read, :show], Issue do |issue| !issue.project.has_issues end cannot [:edit, :update, :create, :new, :destroy], Comment do |comment| diff --git a/db/schema.rb b/db/schema.rb index 5acde5031..794ab6874 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -243,6 +243,7 @@ ActiveRecord::Schema.define(:version => 20111219073859) do t.string "object_type" t.integer "target_id" t.string "target_type" + t.integer "role_id" t.datetime "created_at" t.datetime "updated_at" t.string "role" @@ -271,16 +272,16 @@ ActiveRecord::Schema.define(:version => 20111219073859) do create_table "users", :force => true do |t| t.string "name" - t.string "email", :default => "", :null => false - t.string "encrypted_password", :limit => 128, :default => "", :null => false - t.string "password_salt", :default => "", :null => false + t.string "email", :default => "", :null => false + t.string "encrypted_password", :limit => 128, :default => "", :null => false t.string "reset_password_token" - t.string "remember_token" + t.datetime "reset_password_sent_at" t.datetime "remember_created_at" t.datetime "created_at" t.datetime "updated_at" - t.text "ssh_key" t.string "uname" + t.text "ssh_key" + t.integer "role_id" t.string "role" end diff --git a/spec/controllers/issues_controller_spec.rb b/spec/controllers/issues_controller_spec.rb index c27154bda..2d9ba9a46 100644 --- a/spec/controllers/issues_controller_spec.rb +++ b/spec/controllers/issues_controller_spec.rb @@ -1,5 +1,194 @@ require 'spec_helper' -describe IssuesController do +shared_examples_for 'issue user with project reader rights' do + #it_should_behave_like 'user with rights to view issues' + it 'should be able to perform index action' do + get :index, :project_id => @project.id + response.should render_template(:index) + end + it 'should be able to perform show action' do + get :show, :project_id => @project.id, :serial_id => @issue.serial_id + response.should render_template(:show) + end +end + +shared_examples_for 'issue user with project writer rights' do + it 'should be able to perform create action' do + post :create, @create_params + response.should redirect_to(project_issues_path(@project)) + end + + it 'should create issue object into db' do + lambda{ post :create, @create_params }.should change{ Issue.count }.by(1) + end +end + +shared_examples_for 'user with issue update rights' do + it 'should be able to perform update action' do + put :update, {:id => @issue.id}.merge(@update_params) + response.should redirect_to(show_issue_path(@project, @issue.serial_id)) + end + + it 'should update issue title' do + put :update, {:id => @issue.id}.merge(@update_params) + @issue.reload.title.should == 'issue2' + end +end + +shared_examples_for 'user without issue update rights' do + it 'should not be able to perform update action' do + put :update, {:id => @issue.id}.merge(@update_params) + response.should redirect_to(forbidden_path) + end + + it 'should not update issue title' do + put :update, {:id => @issue.id}.merge(@update_params) + @issue.reload.title.should_not == 'issue2' + end +end + +shared_examples_for 'user without issue destroy rights' do + it 'should not be able to perform destroy action' do + delete :destroy, :id => @issue.id, :project_id => @project.id + response.should redirect_to(forbidden_path) + end + + it 'should not reduce issues count' do + lambda{ delete :destroy, :id => @issue.id, :project_id => @project.id }.should change{ Issue.count }.by(0) + end +end + +shared_examples_for 'project with issues turned off' do + pending 'should not be able to perform index action' do + get :index, :project_id => @project_with_turned_off_issues.id + #response.should redirect_to(forbidden_path) + response.should render_template(:index) + end + + it 'should not be able to perform show action' do + get :show, :project_id => @project_with_turned_off_issues.id, :serial_id => @turned_of_issue.serial_id + response.should redirect_to(forbidden_path) + end +end + +describe IssuesController do + before(:each) do + stub_rsync_methods + + @project = Factory(:project) + @issue_user = Factory(:user) + @create_params = {:project => {:name => 'pro'}} + @update_params = {:project => {:name => 'pro2'}} + + any_instance_of(Project, :versions => ['v1.0', 'v2.0']) + + @issue = Factory(:issue, :project_id => @project.id, :user_id => @issue_user.id) + @create_params = { + :project_id => @project.id, + :issue => { + :title => "issue1", + :body => "issue body", + :project_id => @project.id + }, + :user_id => @issue_user.id, + :user_uname => @issue_user.uname + } + @update_params = { + :project_id => @project.id, + :issue => { + :title => "issue2" + } + } + + @project_with_turned_off_issues = Factory(:project, :has_issues => false) + @turned_of_issue = Factory(:issue, :project_id => @project_with_turned_off_issues.id, :user_id => @issue_user.id) + end + + context 'for global admin user' do + before(:each) do + @admin = Factory(:admin) + set_session_for(@admin) + end + + it_should_behave_like 'user without issue destroy rights' + end + + context 'for project admin user' do + before(:each) do + #@admin = Factory(:admin) + #set_session_for(@admin) + @user = Factory(:user) + set_session_for(@user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + end + + it_should_behave_like 'issue user with project reader rights' + it_should_behave_like 'issue user with project writer rights' + it_should_behave_like 'user with issue update rights' + it_should_behave_like 'user without issue destroy rights' + it_should_behave_like 'project with issues turned off' + end + + context 'for project owner user' do + before(:each) do + @user = Factory(:user) + set_session_for(@user) + @project.update_attribute(:owner, @user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + end + + it_should_behave_like 'issue user with project reader rights' + it_should_behave_like 'issue user with project writer rights' + it_should_behave_like 'user with issue update rights' + it_should_behave_like 'user without issue destroy rights' + it_should_behave_like 'project with issues turned off' + end + + context 'for project reader user' do + before(:each) do + @user = Factory(:user) + set_session_for(@user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'reader') + end + + it_should_behave_like 'issue user with project reader rights' + it_should_behave_like 'user without issue update rights' + it_should_behave_like 'user without issue destroy rights' + it_should_behave_like 'project with issues turned off' + + it 'should not be able to perform create action' do + post :create, @create_params + response.should redirect_to(forbidden_path) + end + + it 'should not create issue object into db' do + lambda{ post :create, @create_params }.should change{ Issue.count }.by(0) + end + end + + context 'for project writer user' do + before(:each) do + @user = Factory(:user) + set_session_for(@user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'writer') + end + + it_should_behave_like 'issue user with project reader rights' + it_should_behave_like 'issue user with project writer rights' + it_should_behave_like 'user without issue update rights' + it_should_behave_like 'user without issue destroy rights' + it_should_behave_like 'project with issues turned off' + end + + context 'for issue assign user' do + before(:each) do + set_session_for(@issue_user) + #@project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'writer') + end + + it_should_behave_like 'user with issue update rights' + it_should_behave_like 'user without issue destroy rights' + it_should_behave_like 'project with issues turned off' + end end diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb index 5e4c566b7..8b8017dbc 100644 --- a/spec/factories/issues.rb +++ b/spec/factories/issues.rb @@ -1,6 +1,6 @@ -# Read about factories at http://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :issue do - end -end \ No newline at end of file +Factory.define(:issue) do |p| + p.title { Factory.next(:string) } + p.body { Factory.next(:string) } + p.association :user, :factory => :user + p.status "open" +end diff --git a/spec/models/cancan_spec.rb b/spec/models/cancan_spec.rb index 97362bb66..cae0291ca 100644 --- a/spec/models/cancan_spec.rb +++ b/spec/models/cancan_spec.rb @@ -118,6 +118,7 @@ describe CanCan do context 'as project collaborator' do before(:each) do @project = Factory(:project) + @issue = Factory(:issue, :project_id => @project.id) end context 'with read rights' do @@ -128,13 +129,17 @@ describe CanCan do it 'should be able to read project' do @ability.should be_able_to(:read, @project) end - - it 'should be able to read project' do + + it 'should be able to read open platform' do @ability.should be_able_to(:read, open_platform) end + + it 'should be able to read issue' do + @ability.should be_able_to(:read, @issue) + end end - - context 'with write rights' do + + context 'with writer rights' do before(:each) do @project.relations.create!(:object_id => @user.id, :object_type => 'User', :role => 'writer') end @@ -144,6 +149,12 @@ describe CanCan do @ability.should be_able_to(action, @project) end end + + [:read, :create, :new].each do |action| + it "should be able to #{ action } project" do + @ability.should be_able_to(action, @project) + end + end end context 'with admin rights' do @@ -160,11 +171,18 @@ describe CanCan do it "should be able to manage collaborators of project" do @ability.should be_able_to(:manage_collaborators, @project) end + + [:read, :create, :new, :update, :edit].each do |action| + it "should be able to #{ action } issue" do + @ability.should be_able_to(action, @issue) + end + end end context 'with owner rights' do before(:each) do @project.update_attribute(:owner, @user) + @issue.project.reload end [:read, :update, :process_build, :build, :destroy].each do |action| @@ -172,6 +190,12 @@ describe CanCan do @ability.should be_able_to(action, @project) end end + + [:read, :update, :edit].each do |action| + it "should be able to #{ action } issue" do + @ability.should be_able_to(action, @issue) + end + end end end From bf2f3d1bc7bc0db5039a86cd4ca48c81b5758111 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Fri, 23 Dec 2011 18:34:32 +0400 Subject: [PATCH 7/9] [refs #54] Add issue statuses filters --- app/controllers/issues_controller.rb | 10 +++++++++- app/views/issues/_list.html.haml | 2 +- app/views/issues/index.html.haml | 10 +++++++++- config/locales/ru.yml | 4 ++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 85cd9f291..a6a4cb734 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -9,7 +9,15 @@ class IssuesController < ApplicationController autocomplete :user, :uname def index - @issues = @project.issues.paginate :per_page => 10, :page => params[:page] + @issues = Issue.scoped + @issues = @project.issues + case params[:status] + when 'open' + @issues = @issues.where(:status => 'open') + when 'closed' + @issues = @issues.where(:status => 'closed') + end + @issues = @issues.paginate :per_page => 10, :page => params[:page] end def create diff --git a/app/views/issues/_list.html.haml b/app/views/issues/_list.html.haml index 9e27261bb..dfb083664 100644 --- a/app/views/issues/_list.html.haml +++ b/app/views/issues/_list.html.haml @@ -3,7 +3,7 @@ %th.first= t("activerecord.attributes.issue.title") %th.first= t("activerecord.attributes.issue.status") %th.last   - - @project.issues.each do |issue| + - @issues.each do |issue| %tr{:class => cycle("odd", "even")} %td = link_to issue.title, show_issue_path(@project, issue.serial_id) diff --git a/app/views/issues/index.html.haml b/app/views/issues/index.html.haml index aa3323b70..ea6a25a13 100644 --- a/app/views/issues/index.html.haml +++ b/app/views/issues/index.html.haml @@ -3,12 +3,20 @@ %ul.wat-cf %li.first.active= link_to t("layout.issues.list"), project_issues_path(@project) %li= link_to t("layout.issues.new"), new_project_issue_path(@project) if can? :new, Issue.new(:project_id => @project.id) + .secondary-navigation + %ul.wat-cf + %li{:class => "first " + (params[:status].blank? ? "active" : "")} + = link_to t("layout.issues.statuses.any"), project_issues_path(@project) + %li{:class => "first " + (params[:status] == 'open' ? "active" : "")} + = link_to t("layout.issues.statuses.open"), project_issues_path(@project, :status => 'open') + %li{:class => "first " + (params[:status] == 'closed' ? "active" : "")} + = link_to t("layout.issues.statuses.closed"), project_issues_path(@project, :status => 'closed') .content %h2.title = t("layout.issues.list_header") .inner = render :partial => 'shared/search_form' - = render :partial => 'issues/list', :object => @issues + = render :partial => 'issues/list' .actions-bar.wat-cf .actions = will_paginate @issues, :param_name => :issue_page diff --git a/config/locales/ru.yml b/config/locales/ru.yml index cf6d82a55..506eac2f8 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -102,6 +102,10 @@ ru: list_header: Список confirm_delete: Вы уверены, что хотите удалить эту задачу? new_header: Новая задача + statuses: + open: Открытые + closed: Закрытые + any: Все comments: confirm_delete: Вы уверены, что хотите удалить комментарий? From 7d8d77a38633c7a209b67442c985d8d270a9a691 Mon Sep 17 00:00:00 2001 From: George Vinogradov Date: Fri, 23 Dec 2011 21:06:47 +0400 Subject: [PATCH 8/9] [issue #79] Added validation to ProjectToRepository --- app/models/project_to_repository.rb | 10 ++++++++++ spec/models/project_to_repository_spec.rb | 18 +++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/app/models/project_to_repository.rb b/app/models/project_to_repository.rb index 68148f89c..a5a623123 100644 --- a/app/models/project_to_repository.rb +++ b/app/models/project_to_repository.rb @@ -7,4 +7,14 @@ class ProjectToRepository < ActiveRecord::Base after_create lambda { project.xml_rpc_create(repository) }, :unless => lambda {Thread.current[:skip]} after_destroy lambda { project.xml_rpc_destroy(repository) } # after_rollback lambda { project.xml_rpc_destroy(repository) rescue true if new_record? } + + validate :one_project_in_platform_repositories + + protected + + def one_project_in_platform_repositories + c = Platform.scoped.select('projects.*').joins(:repositories => :projects).where( + :projects => {:name => project.name}, :id => repository.platform_id).count + errors.add(:project, 'should be one in platform') if c > 0 + end end diff --git a/spec/models/project_to_repository_spec.rb b/spec/models/project_to_repository_spec.rb index f908dee57..7a67a65e5 100644 --- a/spec/models/project_to_repository_spec.rb +++ b/spec/models/project_to_repository_spec.rb @@ -1,5 +1,21 @@ require 'spec_helper' describe ProjectToRepository do - pending "add some examples to (or delete) #{__FILE__}" + # pending "add some examples to (or delete) #{__FILE__}" + before(:each) do + stub_rsync_methods + @platform = Factory(:platform) + @first_repo = Factory(:repository, :platform_id => @platform.id) + @second_repo = Factory(:repository, :platform_id => @platform.id) + @project = Factory(:project) + @first_repo.projects << @project + @first_repo.save + end + + it 'should not add the same project in different repositories of same platform' do +# puts Platform.scoped.select('projects.*').joins(:repositories => :projects).where(:id => @platform.id)inspect + + p2r = @second_repo.project_to_repositories.build :project_id => @project.id + p2r.should_not be_valid + end end From 3b300f2ca4c36df39023ee87cd1e53ad6b265400 Mon Sep 17 00:00:00 2001 From: George Vinogradov Date: Fri, 23 Dec 2011 21:17:02 +0400 Subject: [PATCH 9/9] [issue #79] Removed comments --- spec/models/project_to_repository_spec.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/models/project_to_repository_spec.rb b/spec/models/project_to_repository_spec.rb index 7a67a65e5..c27de0b5f 100644 --- a/spec/models/project_to_repository_spec.rb +++ b/spec/models/project_to_repository_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe ProjectToRepository do - # pending "add some examples to (or delete) #{__FILE__}" before(:each) do stub_rsync_methods @platform = Factory(:platform) @@ -13,8 +12,6 @@ describe ProjectToRepository do end it 'should not add the same project in different repositories of same platform' do -# puts Platform.scoped.select('projects.*').joins(:repositories => :projects).where(:id => @platform.id)inspect - p2r = @second_repo.project_to_repositories.build :project_id => @project.id p2r.should_not be_valid end