From d20d43eb2b0781a4d6172e8ac46e3fbf42a86069 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Mon, 19 Dec 2011 19:30:14 +0400 Subject: [PATCH 01/40] [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 02/40] [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 03/40] [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 d901426b7938ffb5c29a74e30f563c92d415b848 Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Wed, 21 Dec 2011 16:01:50 +0200 Subject: [PATCH 04/40] Move project build functinality to build_lists controller. Refactor downloads url - create model based methods for url genertion. Cleanup code. Add includeed_repos functionality. Display included_repos on build_list page. Refs #65 --- app/controllers/application_controller.rb | 2 +- app/controllers/build_lists_controller.rb | 51 ++++++++++--- app/controllers/platforms_controller.rb | 2 +- app/controllers/projects_controller.rb | 74 +------------------ app/models/ability.rb | 13 ++-- app/models/build_list.rb | 9 +-- app/models/platform.rb | 30 +++++++- app/models/project.rb | 4 + .../build_lists/_include_repos.html.haml | 3 + .../new.html.haml} | 40 +++++----- app/views/build_lists/show.html.haml | 5 ++ app/views/git/shared/_navigation.html.haml | 2 +- app/views/projects/show.html.haml | 2 +- config/locales/ru.yml | 4 +- config/routes.rb | 4 +- ...152347_add_include_repos_to_build_lists.rb | 9 +++ db/schema.rb | 3 +- lib/build_server.rb | 11 ++- public/javascripts/application.js | 57 ++++++-------- 19 files changed, 163 insertions(+), 162 deletions(-) create mode 100644 app/views/build_lists/_include_repos.html.haml rename app/views/{projects/build.html.haml => build_lists/new.html.haml} (52%) create mode 100644 db/migrate/20111220152347_add_include_repos_to_build_lists.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 081802f1c..4a64259cd 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -4,7 +4,7 @@ class ApplicationController < ActionController::Base layout :layout_by_resource before_filter lambda { EventLog.current_controller = self }, - :only => [:create, :destroy, :open_id, :auto_build, :process_build, :cancel, :publish, :change_visibility] # :update + :only => [:create, :destroy, :open_id, :auto_build, :cancel, :publish, :change_visibility] # :update after_filter lambda { EventLog.current_controller = nil } helper_method :get_owner diff --git a/app/controllers/build_lists_controller.rb b/app/controllers/build_lists_controller.rb index 91e6d345b..175023c22 100644 --- a/app/controllers/build_lists_controller.rb +++ b/app/controllers/build_lists_controller.rb @@ -3,11 +3,13 @@ class BuildListsController < ApplicationController before_filter :authenticate_user!, :except => CALLBACK_ACTIONS before_filter :authenticate_build_service!, :only => CALLBACK_ACTIONS - before_filter :find_project, :only => [:index, :filter, :show, :publish] + before_filter :find_project, :only => [:index, :filter, :show, :publish, :new, :create] before_filter :find_arches, :only => [:index, :filter, :all] before_filter :find_project_versions, :only => [:index, :filter] before_filter :find_build_list_by_bs, :only => [:status_build, :pre_build, :post_build] + # load_and_authorize_resource :project, :except => CALLBACK_ACTIONS + # load_and_authorize_resource :build_list, :through => :project, :shallow => true, :except => CALLBACK_ACTIONS load_and_authorize_resource :except => CALLBACK_ACTIONS def all @@ -29,15 +31,6 @@ class BuildListsController < ApplicationController render :action => 'index' end - def cancel - build_list = BuildList.find(params[:id]) - if build_list.cancel_build_list - redirect_to :back, :notice => t('layout.build_lists.cancel_successed') - else - redirect_to :back, :notice => t('layout.build_lists.cancel_failed') - end - end - def index @build_lists = @project.build_lists.recent.paginate :page => params[:page] @filter = BuildList::Filter.new(@project) @@ -57,6 +50,35 @@ class BuildListsController < ApplicationController @item_groups = @build_list.items.group_by_level end + def new + @build_list = BuildList.new + end + + def create + notices, errors = [], [] + Arch.where(:id => params[:archs]).each do |arch| + Platform.main.where(:id => params[:bpls]).each do |bpl| + @build_list = @project.build_lists.build(params[:build_list]) + @build_list.bpl = bpl; @build_list.arch = arch + flash_options = {:project_version => @build_list.project_version, :arch => arch.name, :bpl => bpl.name, :pl => @build_list.pl} + if @build_list.save + notices << t("flash.build_list.saved", flash_options) + else + errors << t("flash.build_list.save_error", flash_options) + end + end + end + errors << t("flash.build_list.no_arch_or_platform_selected") if errors.blank? and notices.blank? + if errors.present? + @build_list ||= BuildList.new + flash[:error] = errors.join('
').html_safe + render :action => :new + else + flash[:notice] = notices.join('
').html_safe + redirect_to @project + end + end + def publish @build_list = @project.build_lists.find(params[:id]) @build_list.publish @@ -64,6 +86,15 @@ class BuildListsController < ApplicationController redirect_to project_build_lists_path(@project) end + def cancel + build_list = BuildList.find(params[:id]) + if build_list.cancel_build_list + redirect_to :back, :notice => t('layout.build_lists.cancel_successed') + else + redirect_to :back, :notice => t('layout.build_lists.cancel_failed') + end + end + def status_build @item = @build_list.items.find_by_name!(params[:package_name]) @item.status = params[:status] diff --git a/app/controllers/platforms_controller.rb b/app/controllers/platforms_controller.rb index f096ff050..3d34da351 100644 --- a/app/controllers/platforms_controller.rb +++ b/app/controllers/platforms_controller.rb @@ -30,7 +30,7 @@ class PlatformsController < ApplicationController {:name => p.name, :architectures => ['i586', 'x86_64'], :repositories => p.repositories.map(&:name), - :url => "http://#{request.host_with_port}/downloads/#{p.name}/repository/"} + :url => p.public_downloads_url(request.host_with_port)} end } end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 4ec20ac45..a870d584c 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -4,14 +4,12 @@ class ProjectsController < ApplicationController belongs_to :user, :group, :polymorphic => true, :optional => true before_filter :authenticate_user!, :except => :auto_build - before_filter :find_project, :only => [:show, :edit, :update, :destroy, :fork, :build, :process_build] + before_filter :find_project, :only => [:show, :edit, :update, :destroy, :fork] before_filter :get_paths, :only => [:new, :create, :edit, :update] load_and_authorize_resource def index -# puts parent.inspect -# puts parent.is_a? User @projects = if parent? and !parent.nil? parent.projects else @@ -95,52 +93,6 @@ class ProjectsController < ApplicationController render :nothing => true end - def build - @arches = Arch.recent - @bpls = Platform.main - @pls = @project.repositories.collect { |rep| ["#{rep.platform.name}/#{rep.name}", rep.platform.id] } - @project_versions = @project.versions - end - - def process_build - @arch_ids = params[:build][:arches].select{|_,v| v == "1"}.collect{|x| x[0].to_i } - @arches = Arch.where(:id => @arch_ids) - - @project_version = params[:build][:project_version] - - bpls_ids = params[:build][:bpl].blank? ? [] : params[:build][:bpl].select{|_,v| v == "1"}.collect{|x| x[0].to_i } - bpls = Platform.where(:id => bpls_ids) - - pl = Platform.find params[:build][:pl] - update_type = params[:build][:update_type] - build_requires = params[:build][:build_requires] - - @project_versions = @project.versions - - if !check_arches || !check_project_versions - @arches = Arch.recent - @bpls = Platform.main - @pls = @project.repositories.collect { |rep| ["#{rep.platform.name}/#{rep.name}", rep.platform.id] } - - render :action => "build" - else - flash[:notice], flash[:error] = "", "" - @arches.each do |arch| - bpls.each do |bpl| - build_list = @project.build_lists.new(:arch => arch, :project_version => @project_version, :pl => pl, :bpl => bpl, :update_type => update_type, :build_requires => build_requires) - - if build_list.save - flash[:notice] += t("flash.build_list.saved", :project_version => @project_version, :arch => arch.name, :bpl => bpl.name, :pl => pl) - else - flash[:error] += t("flash.build_list.save_error", :project_version => @project_version, :arch => arch.name, :bpl => bpl.name, :pl => pl) - end - end - end - - redirect_to project_path(@project) - end - end - protected def get_paths @@ -161,28 +113,4 @@ class ProjectsController < ApplicationController def find_project @project = Project.find params[:id] end - - def check_arches - if @arch_ids.blank? - flash[:error] = t("flash.build_list.no_arch_selected") - false - elsif @arch_ids.length != @arches.length - flash[:error] = t("flash.build_list.no_arch_found") - false - else - true - end - end - - def check_project_versions - if @project_version.blank? - flash[:error] = t("flash.build_list.no_project_version_selected") - false - elsif !@project_versions.flatten.include?(@project_version) - flash[:error] = t("flash.build_list.no_project_version_found", :project_version => @project_version) - false - else - true - end - end end diff --git a/app/models/ability.rb b/app/models/ability.rb index fd6118a6b..1741cb881 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -51,14 +51,14 @@ class Ability can [:read, :create], PrivateUser, :platform => {:owner_type => 'User', :owner_id => user.id} # If rule has multiple conditions CanCan joins them by 'AND' sql operator - can [:read, :update, :process_build, :build, :destroy], Project, :owner_type => 'User', :owner_id => user.id + can [:read, :update, :destroy], Project, :owner_type => 'User', :owner_id => user.id #can :read, Project, :relations => {:role => 'reader'} can :read, Project, projects_in_relations_with(:role => 'reader', :object_type => 'User', :object_id => user.id) do |project| #The can? and cannot? call cannot be used with a raw sql 'can' definition. project.relations.exists?(:role => 'reader', :object_type => 'User', :object_id => user.id) end - #can [:update, :process_build, :build], Project, :relations => {:role => 'writer'} - can [:read, :update, :process_build, :build], Project, projects_in_relations_with(:role => ['writer', 'admin'], :object_type => 'User', :object_id => user.id) do |project| + #can [:update], Project, :relations => {:role => 'writer'} + can [:read, :update], Project, projects_in_relations_with(:role => ['writer', 'admin'], :object_type => 'User', :object_id => user.id) do |project| project.relations.exists?(:role => ['writer', 'admin'], :object_type => 'User', :object_id => user.id) end @@ -112,13 +112,13 @@ class Ability project.relations.exists? :object_id => user.group_ids, :object_type => 'Group', :role => 'admin' end - can [:read, :update, :process_build, :build, :destroy], Project, :owner_type => 'Group', :owner_id => user.group_ids + can [:read, :update, :destroy], Project, :owner_type => 'Group', :owner_id => user.group_ids #can :read, Project, :relations => {:role => 'reader', :object_type => 'Group', :object_id => user.group_ids} can :read, Project, projects_in_relations_with(:role => 'reader', :object_type => 'Group', :object_id => user.group_ids) do |project| project.relations.exists?(:role => 'reader', :object_type => 'Group', :object_id => user.group_ids) end - #can [:update, :process_build, :build], Project, :relations => {:role => 'writer', :object_type => 'Group', :object_id => user.group_ids} - can [:read, :update, :process_build, :build], Project, projects_in_relations_with(:role => ['writer', 'admin'], :object_type => 'Group', :object_id => user.group_ids) do |project| + #can [:update], Project, :relations => {:role => 'writer', :object_type => 'Group', :object_id => user.group_ids} + can [:read, :update], 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 @@ -135,6 +135,7 @@ class Ability end can(:fork, Project) {|p| can? :read, p} + can(:create, BuildList) {|bl| can? :update, bl.project} # Things that can not do simple user cannot :create, [Platform, User] diff --git a/app/models/build_list.rb b/app/models/build_list.rb index 1cab5a9f8..27be7ac92 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -5,9 +5,7 @@ class BuildList < ActiveRecord::Base belongs_to :bpl, :class_name => 'Platform' has_many :items, :class_name => "BuildList::Item", :dependent => :destroy - validates :project_id, :presence => true - validates :project_version, :presence => true - #validates_inclusion_of :update_type, :in => UPDATE_TYPES#, :message => "extension %s is not included in the list" + validates :project_id, :project_version, :arch, :include_repos, :presence => true UPDATE_TYPES = %w[security bugfix enhancement recommended newpackage] validates :update_type, :inclusion => UPDATE_TYPES validate lambda { @@ -82,6 +80,7 @@ class BuildList < ActiveRecord::Base scope :scoped_to_project_name, lambda {|project_name| joins(:project).where('projects.name LIKE ?', "%#{project_name}%")} serialize :additional_repos + serialize :include_repos before_create :set_default_status after_create :place_build @@ -139,8 +138,8 @@ class BuildList < ActiveRecord::Base end def place_build - #XML-RPC params: project_name, project_version, plname, arch, bplname, update_type, build_requires, id_web - self.status = BuildServer.add_build_list project.name, project_version, pl.name, arch.name, (pl_id == bpl_id ? '' : bpl.name), update_type, build_requires, id + #XML-RPC params: project_name, project_version, plname, arch, bplname, update_type, build_requires, id_web, include_repos + self.status = BuildServer.add_build_list project.name, project_version, pl.name, arch.name, (pl_id == bpl_id ? '' : bpl.name), update_type, build_requires, id, include_repos self.status = BUILD_PENDING if self.status == 0 save end diff --git a/app/models/platform.rb b/app/models/platform.rb index 311c8545d..1ab3f10e6 100644 --- a/app/models/platform.rb +++ b/app/models/platform.rb @@ -43,14 +43,17 @@ class Platform < ActiveRecord::Base urpmi_commands[pl.name] = [] local_pair = pl.id != self.id ? blank_pair : pair head = hidden? ? "http://#{local_pair[:login]}@#{local_pair[:pass]}:#{host}/private/" : "http://#{host}/downloads/" - if pl.distrib_type == APP_CONFIG['distr_types'].first + # prefix = prefix_url hidden?, :host => host, :login => local_pair[:login], :password => local_pair[:pass] + if pl.distrib_type == APP_CONFIG['distr_types'].first # mdv Arch.all.each do |arch| tail = "/#{arch.name}/main/release" urpmi_commands[pl.name] << "urpmi.addmedia #{name} #{head}#{name}/repository/#{pl.name}#{tail}" + # urpmi_commands[pl.name] << "urpmi.addmedia #{name} #{prefix}/#{name}/repository#{pl.downloads_url '', arch.name, 'main', 'release'}" end else tail = '' urpmi_commands[pl.name] << "urpmi.addmedia #{name} #{head}#{name}/repository/#{pl.name}#{tail}" + # urpmi_commands[pl.name] << "urpmi.addmedia #{name} #{prefix}/#{name}/repository#{pl.downloads_url ''}" end end @@ -65,6 +68,27 @@ class Platform < ActiveRecord::Base Rails.root.join("public", "downloads", name) end + def prefix_url(pub, options = {}) + options[:host] ||= EventLog.current_controller.request.host_with_port rescue ::Rosa::Application.config.action_mailer.default_url_options[:host] + pub ? "http://#{options[:host]}/downloads" : "http://#{options[:login]}:#{options[:password]}@#{options[:host]}/private" + end + + def public_downloads_url(host = nil, arch = nil, repo = nil, suffix = nil) + downloads_url prefix_url(true, :host => host), arch, repo, suffix + end + + def private_downloads_url(login, password, host = nil, arch = nil, repo = nil, suffix = nil) + downloads_url prefix_url(false, :host => host, :login => login, :password => password), arch, repo, suffix + end + + def downloads_url(prefix, arch = nil, repo = nil, suffix = nil) + "#{prefix}/#{name}/repository/".tap do |url| + url << "#{arch}/" if arch.present? + url << "#{repo}/" if repo.present? + url << "#{suffix}/" if suffix.present? + end + end + def hidden? visibility == 'hidden' end @@ -115,9 +139,7 @@ class Platform < ActiveRecord::Base system("sudo mkdir -p #{mount_path}") system("sudo mount --bind #{path} #{mount_path}") Arch.all.each do |arch| - host = EventLog.current_controller.request.host_with_port rescue ::Rosa::Application.config.action_mailer.default_url_options[:host] - url = "http://#{host}/downloads/#{name}/repository/" - str = "country=Russian Federation,city=Moscow,latitude=52.18,longitude=48.88,bw=1GB,version=2011,arch=#{arch.name},type=distrib,url=#{url}\n" + str = "country=Russian Federation,city=Moscow,latitude=52.18,longitude=48.88,bw=1GB,version=2011,arch=#{arch.name},type=distrib,url=#{public_downloads_url}\n" File.open(File.join(mount_path, "#{name}.#{arch.name}.list"), 'w') {|f| f.write(str) } end end diff --git a/app/models/project.rb b/app/models/project.rb index f0e66c885..0e5330202 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -128,6 +128,10 @@ class Project < ActiveRecord::Base end end + def platforms + @platforms ||= repositories.map(&:platform).uniq + end + protected def build_path(dir) diff --git a/app/views/build_lists/_include_repos.html.haml b/app/views/build_lists/_include_repos.html.haml new file mode 100644 index 000000000..55902b2f9 --- /dev/null +++ b/app/views/build_lists/_include_repos.html.haml @@ -0,0 +1,3 @@ +- platform.repositories.each do |repo| + = check_box_tag "build_list[include_repos][]", repo.id, repo.name == 'main' || @project.repositories.map(&:id).include?(repo.id), :id => "include_repos_#{repo.id}" # (params[:build_list]||[]).fetch(:include_repos, []).include?(repo.id.to_s) + = label_tag "include_repos_#{repo.id}", repo.name \ No newline at end of file diff --git a/app/views/projects/build.html.haml b/app/views/build_lists/new.html.haml similarity index 52% rename from app/views/projects/build.html.haml rename to app/views/build_lists/new.html.haml index 531334fae..1d53612ca 100644 --- a/app/views/projects/build.html.haml +++ b/app/views/build_lists/new.html.haml @@ -5,46 +5,50 @@ %li= link_to t("layout.projects.new"), new_project_path %li= link_to t("layout.projects.show"), project_path(@project) %li=# link_to "git-repo", project_repo_path(@platform, @repository, @project) - %li.active= link_to t("layout.projects.build"), build_project_path(@project) + %li.active= link_to t("layout.projects.build"), new_project_build_list_path(@project) .content %h2.title= t("layout.projects.new_build", :project_name => @project.name) .inner - = form_for :build, :url => process_build_project_path(@project), :html => { :class => :form, :method => :post } do |f| + = form_for [@project, @build_list], :html => { :class => :form, :method => :post } do |f| .columns.wat-cf .column.left .group = f.label :project_version, t("activerecord.attributes.build_list.project_version"), :class => :label - = f.select :project_version, @project_versions + = f.select :project_version, @project.versions .group.pl_ids_container = f.label :bpl, t("activerecord.attributes.build_list.bpl"), :class => :label - - @bpls.each do |bpl| - = f.check_box "bpl[#{bpl.id}]", :bpl_id => bpl.id, :class => 'build_bpl_ids' - = bpl.name + - Platform.main.each do |bpl| + = check_box_tag "bpls[]", bpl.id, (params[:bpls]||[]).include?(bpl.id.to_s), :class => 'build_bpl_ids', :id => "bpls_#{bpl.id}" + = label_tag "bpls_#{bpl.id}", bpl.name %br - + .group = f.label :update_type, t("activerecord.attributes.build_list.update_type"), :class => :label - = f.select :update_type, BuildList::UPDATE_TYPES.collect { |ut| [ut, ut] } - + = f.select :update_type, BuildList::UPDATE_TYPES + .group = f.check_box :build_requires - = t("activerecord.attributes.build_list.build_requires") + = f.label :build_requires, t("activerecord.attributes.build_list.build_requires") .column.right .group = f.label :arches, t("activerecord.attributes.build_list.arch"), :class => :label - - @arches.each do |arch| - = f.check_box "arches[#{arch.id}]" - = arch.name + - Arch.recent.each do |arch| + = check_box_tag "archs[]", arch.id, (params[:archs]||[]).include?(arch.id.to_s), :id => "archs_#{arch.id}" + = label_tag "archs_#{arch.id}", arch.name %br - + .group - = f.label :pl, t("activerecord.attributes.build_list.pl"), :class => :label - = f.select :pl, @pls + = f.label :pl_id, t("activerecord.attributes.build_list.pl"), :class => :label + = f.select :pl_id, @project.repositories.collect{|r| ["#{r.platform.name}/#{r.name}", r.platform.id]} + + .group + = f.label :include_repos, t("activerecord.attributes.build_list.include_repos"), :class => :label + #include_repos .group.navform.wat-cf %button.button{:type => "submit"} @@ -53,4 +57,6 @@ %span.text_button_padding= t("layout.or") = link_to t("layout.cancel"), root_path, :class => "text_button_padding link_button" --# content_for :sidebar, render(:partial => 'sidebar') +.preloaded_include_repos{:style => 'display: none'} + - @project.platforms.each do |p| + %div{:class => "include_repos_#{p.id}"}= render 'include_repos', :platform => p diff --git a/app/views/build_lists/show.html.haml b/app/views/build_lists/show.html.haml index 6d53030e6..6ed12c9e9 100644 --- a/app/views/build_lists/show.html.haml +++ b/app/views/build_lists/show.html.haml @@ -34,6 +34,11 @@ = t("activerecord.attributes.build_list.pl") \: = @build_list.pl.name + %p + %b + = t("activerecord.attributes.build_list.include_repos") + \: + = (@build_list.include_repos||[]).map{|r| Repository.find(r).name}.join(', ') %p %b = t("activerecord.attributes.build_list.update_type") diff --git a/app/views/git/shared/_navigation.html.haml b/app/views/git/shared/_navigation.html.haml index cb135dea6..e09fcb051 100644 --- a/app/views/git/shared/_navigation.html.haml +++ b/app/views/git/shared/_navigation.html.haml @@ -4,7 +4,7 @@ %li= link_to t("layout.projects.new"), new_project_path %li= link_to t("layout.projects.show"), project_path(@project) %li.active= 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.build"), new_project_build_list_path(@project) %ul#git_submenu.sub-wat-cf.wat-cf %li= link_to t("layout.git.repositories.commits"), commits_path(@project, :treeish => @treeish) diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 3930d97a2..8d8e03902 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -5,7 +5,7 @@ %li= link_to t("layout.projects.new"), new_project_path %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.build"), new_project_build_list_path(@project) .content .inner diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 6f343a6e4..4b414a876 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -412,8 +412,7 @@ ru: save_error: Не удалось сохранить билд лист для версии '%{project_version}', платформы '%{bpl}' и архитектуры '%{arch}' no_project_version_selected: Выберите какую-нибудь версию no_project_version_found: Выбранная версия '%{project_version}' не найдена - no_arch_selected: Выберите хотя бы одну ахритектуру - no_arch_found: Выбранные ахритектуры не найдены + no_arch_or_platform_selected: Выберите хотя бы одну ахритектуру и платформу wrong_platform: Для основного репозитория (main) может быть выбран только его же основная платформа! can_not_published: Опубликовать сборку можно только со статусом "Собран" @@ -590,6 +589,7 @@ ru: is_circle: Циклическая сборка notified_at: Информация получена additional_repos: Дополнительные репозитории + include_repos: Подключаемые репозитории updated_at: Обновлен created_at: Создан pl: Репозиторий для сохранения пакетов diff --git a/config/routes.rb b/config/routes.rb index 5d22a7cfe..dafd4ad78 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -69,7 +69,7 @@ Rosa::Application.routes.draw do resources :projects do resource :repo, :controller => "git/repositories", :only => [:show] - resources :build_lists, :only => [:index, :show] do + resources :build_lists, :only => [:index, :show, :new, :create] do collection do get :recent post :filter @@ -93,8 +93,6 @@ Rosa::Application.routes.draw do # end member do - get :build - post :process_build post :fork end collection do diff --git a/db/migrate/20111220152347_add_include_repos_to_build_lists.rb b/db/migrate/20111220152347_add_include_repos_to_build_lists.rb new file mode 100644 index 000000000..cb0b8d7b0 --- /dev/null +++ b/db/migrate/20111220152347_add_include_repos_to_build_lists.rb @@ -0,0 +1,9 @@ +class AddIncludeReposToBuildLists < ActiveRecord::Migration + def self.up + add_column :build_lists, :include_repos, :text + end + + def self.down + remove_column :build_lists, :include_repos + end +end diff --git a/db/schema.rb b/db/schema.rb index 17c70bd11..1a7fdc5fd 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 => 20111220152347) do create_table "arches", :force => true do |t| t.string "name", :null => false @@ -69,6 +69,7 @@ ActiveRecord::Schema.define(:version => 20111130181101) do t.string "update_type" t.integer "bpl_id" t.integer "pl_id" + t.text "include_repos" end add_index "build_lists", ["arch_id"], :name => "index_build_lists_on_arch_id" diff --git a/lib/build_server.rb b/lib/build_server.rb index 6ce8f87bb..cc655323b 100644 --- a/lib/build_server.rb +++ b/lib/build_server.rb @@ -72,8 +72,15 @@ class BuildServer self.client.call('add_to_repo', name, repo_name) end - def self.add_build_list project_name, project_version, plname, arch, bplname, update_type, build_requires, id_web - self.client.call('add_build_list', project_name, project_version, plname, arch, bplname, update_type, build_requires, id_web) + def self.add_build_list project_name, project_version, plname, arch, bplname, update_type, build_requires, id_web, include_repos + include_repos_json = {}.tap do |h| + include_repos.each do |r| + repo = Repository.find r + h[repo.name] = repo.platform.public_downloads_url(nil, arch, repo.name) + end + end.to_json + # raise include_repos_json.inspect + self.client.call('add_build_list', project_name, project_version, plname, arch, bplname, update_type, build_requires, id_web, include_repos_json) end def self.delete_build_list idlist diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 78895567c..394922672 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -1,42 +1,29 @@ -function check_by_ids(ids) { - for(var i = 0; i < ids.length; i++){ - $('#'+ids[i]).attr('checked', true); - } - return false; -} - -function uncheck_by_ids(ids) { - for(var i = 0; i < ids.length; i++){ - $('#'+ids[i]).attr('checked', false); - } - return false; -} - $(document).ready(function() { - $('.pl_ids_container input[type="hidden"]').remove(); - - $('select#build_pl').change(function() { - var is_pl_main = false; - var granted_bpl_id = ''; - var pl_id = $('select#build_pl').val(); + $('select#build_list_pl_id').change(function() { + var platform_id = $(this).val(); - $('input.build_bpl_ids').each(function(i,el) { - var bpl_id = $(el).attr('bpl_id'); - if (pl_id == bpl_id) { - is_pl_main = true; - //granted_bpl_id = $(el).attr('bpl_id'); - } - }); + $('#include_repos').html($('.preloaded_include_repos .include_repos_' + platform_id).html()); - if (is_pl_main) { - $('input.build_bpl_ids').attr('disabled', 'disabled'); - $('input.build_bpl_ids[bpl_id="'+pl_id+'"]').removeAttr('disabled'); - } else { - $('input.build_bpl_ids').removeAttr('disabled'); - } + // var is_pl_main = false; + // var granted_bpl_id = ''; + // var pl_id = $('select#build_pl').val(); + // + // $('input.build_bpl_ids').each(function(i,el) { + // var bpl_id = $(el).attr('bpl_id'); + // if (pl_id == bpl_id) { + // is_pl_main = true; + // //granted_bpl_id = $(el).attr('bpl_id'); + // } + // }); + // + // if (is_pl_main) { + // $('input.build_bpl_ids').attr('disabled', 'disabled'); + // $('input.build_bpl_ids[bpl_id="'+pl_id+'"]').removeAttr('disabled'); + // } else { + // $('input.build_bpl_ids').removeAttr('disabled'); + // } }); - - $('select#build_pl').trigger('change'); + $('select#build_list_pl_id').trigger('change'); $('input.user_role_chbx').click(function() { var current = $(this); From 3ab55d34b8d0ce241872bec81766c213ded77e26 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Wed, 21 Dec 2011 18:48:16 +0400 Subject: [PATCH 05/40] [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 7f58a5260279ae5165edbc2b74feda51e98c828c Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Wed, 21 Dec 2011 19:51:55 +0200 Subject: [PATCH 06/40] Autocheck current save platform and disable unavailable base platforms on JS level. Refs #65 --- app/views/build_lists/new.html.haml | 2 +- public/javascripts/application.js | 32 +++++++++++++---------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/app/views/build_lists/new.html.haml b/app/views/build_lists/new.html.haml index 1d53612ca..88aa55c5d 100644 --- a/app/views/build_lists/new.html.haml +++ b/app/views/build_lists/new.html.haml @@ -18,7 +18,7 @@ = f.label :project_version, t("activerecord.attributes.build_list.project_version"), :class => :label = f.select :project_version, @project.versions - .group.pl_ids_container + .group.base_platforms = f.label :bpl, t("activerecord.attributes.build_list.bpl"), :class => :label - Platform.main.each do |bpl| = check_box_tag "bpls[]", bpl.id, (params[:bpls]||[]).include?(bpl.id.to_s), :class => 'build_bpl_ids', :id => "bpls_#{bpl.id}" diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 394922672..b2617a1c7 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -1,27 +1,23 @@ $(document).ready(function() { $('select#build_list_pl_id').change(function() { var platform_id = $(this).val(); + var base_platforms = $('.base_platforms input[type=checkbox]'); $('#include_repos').html($('.preloaded_include_repos .include_repos_' + platform_id).html()); - // var is_pl_main = false; - // var granted_bpl_id = ''; - // var pl_id = $('select#build_pl').val(); - // - // $('input.build_bpl_ids').each(function(i,el) { - // var bpl_id = $(el).attr('bpl_id'); - // if (pl_id == bpl_id) { - // is_pl_main = true; - // //granted_bpl_id = $(el).attr('bpl_id'); - // } - // }); - // - // if (is_pl_main) { - // $('input.build_bpl_ids').attr('disabled', 'disabled'); - // $('input.build_bpl_ids[bpl_id="'+pl_id+'"]').removeAttr('disabled'); - // } else { - // $('input.build_bpl_ids').removeAttr('disabled'); - // } + base_platforms.each(function(){ + if ($.inArray(platform_id, base_platforms.map(function(){ return $(this).val() }).get()) >= 0) { + if ($(this).val() == platform_id) { + $(this).attr('checked', 'checked'); + $(this).removeAttr('disabled'); + } else { + $(this).removeAttr('checked'); + $(this).attr('disabled', 'disabled'); + } + } else { + $(this).removeAttr('disabled'); + } + }); }); $('select#build_list_pl_id').trigger('change'); From a61d211862f4ba4d7ff8d8fe157f16427be826e6 Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Wed, 21 Dec 2011 20:59:19 +0200 Subject: [PATCH 07/40] Fix bugs. Change included_repos format. Refs #65 --- app/views/build_lists/_build_lists.html.haml | 6 +++--- app/views/build_lists/index.html.haml | 6 +++--- app/views/build_lists/show.html.haml | 2 +- config/locales/event_log.ru.yml | 2 +- lib/build_server.rb | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/views/build_lists/_build_lists.html.haml b/app/views/build_lists/_build_lists.html.haml index d3a5ae44c..8cc07e778 100644 --- a/app/views/build_lists/_build_lists.html.haml +++ b/app/views/build_lists/_build_lists.html.haml @@ -7,7 +7,7 @@ %th= t("activerecord.attributes.build_list.arch") %th= t("activerecord.attributes.build_list.user") %th= t("activerecord.attributes.build_list.is_circle") - - if controller.action_name = 'all' + - unless @project %th= t("layout.build_lists.cancel_button_header") %th.last= t("activerecord.attributes.build_list.notified_at") @@ -18,9 +18,9 @@ %td= link_to build_list.project_version, "#" %td= link_to build_list.project.name, project_path(build_list.project) %td= build_list.arch.name - %td= build_list.user.fullname + %td= build_list.user.try(:fullname) %td= t("layout.#{build_list.is_circle?}_") - - if controller.action_name = 'all' + - unless @project %td= link_to t("layout.build_lists.cancel_button"), build_list_cancel_path(build_list) if build_list.can_cancel? %td.last= build_list.notified_at diff --git a/app/views/build_lists/index.html.haml b/app/views/build_lists/index.html.haml index 9fe414890..32dab8efb 100644 --- a/app/views/build_lists/index.html.haml +++ b/app/views/build_lists/index.html.haml @@ -1,12 +1,12 @@ .block - - if controller.action_name != 'all' + - if @project .secondary-navigation %ul.wat-cf %li.first= link_to t("layout.build_lists.current"), project_path(@project) + "#build_lists" %li.active= link_to t("layout.build_lists.all"), project_build_lists_path(@project) .content - - if controller.action_name == 'all' + - unless @project .inner %h2= t('layout.build_lists.build_server_status.header') .field @@ -25,4 +25,4 @@ .inner = render :partial => "build_lists/build_lists", :object => @build_lists -- content_for :sidebar, render(:partial => 'sidebar') if controller.action_name != 'all' \ No newline at end of file +- content_for :sidebar, render(:partial => 'sidebar') if @project \ No newline at end of file diff --git a/app/views/build_lists/show.html.haml b/app/views/build_lists/show.html.haml index bc5342f1e..e8c6f6f20 100644 --- a/app/views/build_lists/show.html.haml +++ b/app/views/build_lists/show.html.haml @@ -73,7 +73,7 @@ %b = t("activerecord.attributes.build_list.user") \: - = @build_list.user.fullname + = @build_list.user.try(:fullname) %p %b = t("activerecord.attributes.build_list.notified_at") diff --git a/config/locales/event_log.ru.yml b/config/locales/event_log.ru.yml index c1aed3a86..c6e627043 100644 --- a/config/locales/event_log.ru.yml +++ b/config/locales/event_log.ru.yml @@ -23,8 +23,8 @@ ru: open_id: 'вход через OpenID' projects_controller: auto_build: 'отправлен на автоматическую сборку' - process_build: 'отправлен на сборку' build_lists_controller: + create: 'отправлен на сборку' cancel: 'сборка отменена' publish: 'сборка опубликована' auto_build_lists_controller: diff --git a/lib/build_server.rb b/lib/build_server.rb index cc655323b..b82c13387 100644 --- a/lib/build_server.rb +++ b/lib/build_server.rb @@ -73,14 +73,14 @@ class BuildServer end def self.add_build_list project_name, project_version, plname, arch, bplname, update_type, build_requires, id_web, include_repos - include_repos_json = {}.tap do |h| + include_repos_hash = {}.tap do |h| include_repos.each do |r| repo = Repository.find r h[repo.name] = repo.platform.public_downloads_url(nil, arch, repo.name) end - end.to_json - # raise include_repos_json.inspect - self.client.call('add_build_list', project_name, project_version, plname, arch, bplname, update_type, build_requires, id_web, include_repos_json) + end + # raise include_repos_hash.inspect + self.client.call('add_build_list', project_name, project_version, plname, arch, bplname, update_type, build_requires, id_web, include_repos_hash) end def self.delete_build_list idlist From 8c2b83760066a5ec490507fa484321e4b2536bc9 Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Wed, 21 Dec 2011 21:17:55 +0200 Subject: [PATCH 08/40] Fix build_list user assign. Refs #65 --- app/controllers/build_lists_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/build_lists_controller.rb b/app/controllers/build_lists_controller.rb index c02d1aaf0..86fa8d81e 100644 --- a/app/controllers/build_lists_controller.rb +++ b/app/controllers/build_lists_controller.rb @@ -46,7 +46,7 @@ class BuildListsController < ApplicationController Arch.where(:id => params[:archs]).each do |arch| Platform.main.where(:id => params[:bpls]).each do |bpl| @build_list = @project.build_lists.build(params[:build_list]) - @build_list.bpl = bpl; @build_list.arch = arch + @build_list.bpl = bpl; @build_list.arch = arch; @build_list.user = current_user flash_options = {:project_version => @build_list.project_version, :arch => arch.name, :bpl => bpl.name, :pl => @build_list.pl} if @build_list.save notices << t("flash.build_list.saved", flash_options) From 2c67843c3471f9f7bfb71ce1bf4b1a754985f691 Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Wed, 21 Dec 2011 23:42:06 +0200 Subject: [PATCH 09/40] Refactor and cleanup build_lists controller and routes. Rename methods. Allow deffis in platform and repository name. Add missing relation and migration to force work user assign in build_lists. Fix and simplify links. Abstract sidebar. Refs #65 --- app/controllers/build_lists_controller.rb | 80 ++++++++----------- app/models/ability.rb | 4 +- app/models/build_list.rb | 6 +- app/models/platform.rb | 2 +- app/models/repository.rb | 2 +- app/models/user.rb | 1 + app/views/build_lists/_build_lists.html.haml | 4 +- app/views/build_lists/_filter.html.haml | 6 +- app/views/build_lists/_sidebar.html.haml | 4 +- app/views/build_lists/index.html.haml | 2 +- app/views/build_lists/show.html.haml | 12 +-- config/locales/ru.yml | 8 +- config/routes.rb | 33 +++----- ...111221194422_add_user_id_to_build_lists.rb | 9 +++ db/schema.rb | 3 +- 15 files changed, 80 insertions(+), 96 deletions(-) create mode 100644 db/migrate/20111221194422_add_user_id_to_build_lists.rb diff --git a/app/controllers/build_lists_controller.rb b/app/controllers/build_lists_controller.rb index 86fa8d81e..4f7479aa4 100644 --- a/app/controllers/build_lists_controller.rb +++ b/app/controllers/build_lists_controller.rb @@ -1,24 +1,22 @@ class BuildListsController < ApplicationController CALLBACK_ACTIONS = [:status_build, :pre_build, :post_build, :circle_build, :new_bbdt] + NESTED_ACTIONS = [:index, :new, :create] before_filter :authenticate_user!, :except => CALLBACK_ACTIONS before_filter :authenticate_build_service!, :only => CALLBACK_ACTIONS - before_filter :find_project, :only => [:filter, :show, :publish, :new, :create] - before_filter :find_arches, :only => [:index] + before_filter :find_project, :only => NESTED_ACTIONS + before_filter :find_build_list, :only => [:show, :publish, :cancel] before_filter :find_build_list_by_bs, :only => [:status_build, :pre_build, :post_build] - load_and_authorize_resource :project, :only => :index - load_and_authorize_resource :through => :project, :only => :index, :shallow => true - load_and_authorize_resource :except => CALLBACK_ACTIONS.concat([:index]) + load_and_authorize_resource :project, :only => NESTED_ACTIONS + load_and_authorize_resource :through => :project, :only => NESTED_ACTIONS, :shallow => true + load_and_authorize_resource :except => CALLBACK_ACTIONS.concat(NESTED_ACTIONS) def index filter_params = params[:filter] || {} - if params[:project_id] - find_project - find_project_versions + if @project @action_url = project_build_lists_path(@project) else - @project = nil @action_url = build_lists_path end @@ -32,11 +30,6 @@ class BuildListsController < ApplicationController end end - def show - @build_list = @project.build_lists.find(params[:id]) - @item_groups = @build_list.items.group_by_level - end - def new @build_list = BuildList.new end @@ -65,20 +58,24 @@ class BuildListsController < ApplicationController redirect_to @project end end - + + def show + @item_groups = @build_list.items.group_by_level + end + def publish - @build_list = @project.build_lists.find(params[:id]) - @build_list.publish - - redirect_to project_build_lists_path(@project) + if @build_list.publish + redirect_to :back, :notice => t('layout.build_lists.publish_success') + else + redirect_to :back, :notice => t('layout.build_lists.publish_fail') + end end def cancel - build_list = BuildList.find(params[:id]) - if build_list.cancel_build_list - redirect_to :back, :notice => t('layout.build_lists.cancel_successed') + if @build_list.cancel + redirect_to :back, :notice => t('layout.build_lists.cancel_success') else - redirect_to :back, :notice => t('layout.build_lists.cancel_failed') + redirect_to :back, :notice => t('layout.build_lists.cancel_fail') end end @@ -86,10 +83,9 @@ class BuildListsController < ApplicationController @item = @build_list.items.find_by_name!(params[:package_name]) @item.status = params[:status] @item.save - + @build_list.container_path = params[:container_path] @build_list.notified_at = Time.current - @build_list.save render :nothing => true, :status => 200 @@ -98,7 +94,6 @@ class BuildListsController < ApplicationController def pre_build @build_list.status = BuildServer::BUILD_STARTED @build_list.notified_at = Time.current - @build_list.save render :nothing => true, :status => 200 @@ -108,7 +103,6 @@ class BuildListsController < ApplicationController @build_list.status = params[:status] @build_list.container_path = params[:container_path] @build_list.notified_at = Time.current - @build_list.save render :nothing => true, :status => 200 @@ -118,7 +112,6 @@ class BuildListsController < ApplicationController @build_list.is_circle = true @build_list.container_path = params[:container_path] @build_list.notified_at = Time.current - @build_list.save render :nothing => true, :status => 200 @@ -139,27 +132,22 @@ class BuildListsController < ApplicationController end protected - - def find_project - @project = Project.find params[:project_id] - end - def find_arches - @arches = Arch.recent - end + def find_project + @project = Project.find_by_id params[:project_id] + end - def find_project_versions - @git_repository = @project.git_repository - @project_versions = @project.versions - end + def find_build_list + @build_list = BuildList.find(params[:id]) + end - def find_build_list_by_bs - @build_list = BuildList.find_by_bs_id!(params[:id]) - end + def find_build_list_by_bs + @build_list = BuildList.find_by_bs_id!(params[:id]) + end - def authenticate_build_service! - if request.remote_ip != APP_CONFIG['build_server_ip'] - render :nothing => true, :status => 403 - end + def authenticate_build_service! + if request.remote_ip != APP_CONFIG['build_server_ip'] + render :nothing => true, :status => 403 end + end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 09cd275a1..cbfbef43b 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -43,7 +43,7 @@ class Ability can :create, Project can :create, Group can :publish, BuildList do |build_list| - build_list.can_published? && build_list.project.relations.exists?(:object_type => 'User', :object_id => user.id) + build_list.can_publish? && build_list.project.relations.exists?(:object_type => 'User', :object_id => user.id) end can [:index, :read], BuildList, ["build_lists.project_id IN (SELECT id FROM projects WHERE visibility = ?)", 'open'] do |build_list| build_list.project.public? @@ -106,7 +106,7 @@ class Ability # Same rights for groups: can [:read, :create], PrivateUser, :platform => {:owner_type => 'Group', :owner_id => user.group_ids} can :publish, BuildList do |build_list| - build_list.can_published? && build_list.project.relations.exists?(:object_type => 'Group', :object_id => user.group_ids) + build_list.can_publish? && build_list.project.relations.exists?(:object_type => 'Group', :object_id => user.group_ids) end can [:index, :read], BuildList, build_lists_in_relations_with(:object_type => 'Group', :object_id => user.group_ids) do |build_list| build_list.project.relations.exists?(:object_type => 'Group', :object_id => user.group_ids) diff --git a/app/models/build_list.rb b/app/models/build_list.rb index c30504da2..1c1562863 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -105,18 +105,18 @@ class BuildList < ActiveRecord::Base end def publish - return false unless can_published? + return false unless can_publish? BuildServer.publish_container bs_id self.update_attribute(:status, BUILD_PUBLISHED) #self.destroy # self.delete end - def can_published? + def can_publish? self.status == BuildServer::SUCCESS end - def cancel_build_list + def cancel has_canceled = BuildServer.delete_build_list bs_id update_attribute(:status, BUILD_CANCELED) if has_canceled == 0 diff --git a/app/models/platform.rb b/app/models/platform.rb index 1ab3f10e6..e0768062a 100644 --- a/app/models/platform.rb +++ b/app/models/platform.rb @@ -14,7 +14,7 @@ class Platform < ActiveRecord::Base has_many :groups, :through => :objects, :source => :object, :source_type => 'Group' validates :description, :presence => true, :uniqueness => true - validates :name, :uniqueness => true, :presence => true, :format => { :with => /^[a-zA-Z0-9_]+$/ } + validates :name, :uniqueness => true, :presence => true, :format => { :with => /^[a-zA-Z0-9_\-]+$/ } validates :distrib_type, :presence => true, :inclusion => {:in => APP_CONFIG['distr_types']} before_create :xml_rpc_create, :unless => lambda {Thread.current[:skip]} diff --git a/app/models/repository.rb b/app/models/repository.rb index c6acb4599..13c4e2a02 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -11,7 +11,7 @@ class Repository < ActiveRecord::Base has_many :groups, :through => :objects, :source => :object, :source_type => 'Group' validates :description, :uniqueness => {:scope => :platform_id}, :presence => true - validates :name, :uniqueness => {:scope => :platform_id}, :presence => true, :format => { :with => /^[a-z0-9_]+$/ } + validates :name, :uniqueness => {:scope => :platform_id}, :presence => true, :format => { :with => /^[a-z0-9_\-]+$/ } # validates :platform_id, :presence => true # if you uncomment this platform clone will not work scope :recent, order("name ASC") diff --git a/app/models/user.rb b/app/models/user.rb index bbaf191c9..07e59a71f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,6 +5,7 @@ class User < ActiveRecord::Base :recoverable, :rememberable, :validatable #, :trackable, :confirmable, :lockable has_many :authentications, :dependent => :destroy + has_many :build_lists, :dependent => :destroy has_many :relations, :as => :object, :dependent => :destroy has_many :targets, :as => :object, :class_name => 'Relation' diff --git a/app/views/build_lists/_build_lists.html.haml b/app/views/build_lists/_build_lists.html.haml index 8cc07e778..8bd7ab667 100644 --- a/app/views/build_lists/_build_lists.html.haml +++ b/app/views/build_lists/_build_lists.html.haml @@ -13,7 +13,7 @@ - build_lists.each do |build_list| %tr{:class => cycle("odd", "even")} - %td= link_to (build_list.bs_id.present? ? build_list.bs_id : t("layout.build_lists.bs_id_not_set")), project_build_list_path(build_list.project, build_list) + %td= link_to (build_list.bs_id.present? ? build_list.bs_id : t("layout.build_lists.bs_id_not_set")), build_list %td= build_list.human_status %td= link_to build_list.project_version, "#" %td= link_to build_list.project.name, project_path(build_list.project) @@ -21,7 +21,7 @@ %td= build_list.user.try(:fullname) %td= t("layout.#{build_list.is_circle?}_") - unless @project - %td= link_to t("layout.build_lists.cancel_button"), build_list_cancel_path(build_list) if build_list.can_cancel? + %td= link_to t("layout.build_lists.cancel_button"), cancel_build_list_path(build_list), :method => "put", :confirm => t("layout.confirm") if build_list.can_cancel? %td.last= build_list.notified_at = will_paginate build_lists diff --git a/app/views/build_lists/_filter.html.haml b/app/views/build_lists/_filter.html.haml index 49e5df245..9a184f655 100644 --- a/app/views/build_lists/_filter.html.haml +++ b/app/views/build_lists/_filter.html.haml @@ -8,12 +8,12 @@ = f.select :status, BuildList::STATUSES.collect{|status| [BuildList.human_status(status), status]}, :include_blank => true, :selected => @filter.status .group = f.label :arch_id, t("activerecord.attributes.build_list.arch"), :class => :label - = f.select :arch_id, @arches.collect{|arch| [arch.name, arch.id]}, :include_blank => true, :selected => @filter.arch_id + = f.select :arch_id, Arch.recent.collect{|arch| [arch.name, arch.id]}, :include_blank => true, :selected => @filter.arch_id .column.right - - if @project_versions + - if @project .group = f.label :project_version, t("activerecord.attributes.build_list.project_version"), :class => :label - = f.select :project_version, @project_versions, :include_blank => true, :selected => @filter.project_version + = f.select :project_version, @project.versions, :include_blank => true, :selected => @filter.project_version .group = f.label :is_circle, t("activerecord.attributes.build_list.is_circle"), :class => :label = f.select :is_circle, [[t("layout.yes_"), 1], [t("layout.no_"), 0]], :include_blank => true, :selected => @filter.is_circle.present? ? (@filter.is_circle ? "1" : "0") : nil diff --git a/app/views/build_lists/_sidebar.html.haml b/app/views/build_lists/_sidebar.html.haml index 2b81fc62c..55d964a20 100644 --- a/app/views/build_lists/_sidebar.html.haml +++ b/app/views/build_lists/_sidebar.html.haml @@ -1,10 +1,10 @@ .block.notice %h3= t("layout.repositories.current_repository_header") .content - - @project.repositories.each do |repository| + - project.repositories.each do |repository| %p= link_to "#{repository.name} (#{repository.platform.name})", platform_repository_path(repository.platform, repository) .block.notice %h3= t("layout.projects.current_project_header") .content - %p= link_to @project.name, project_path(@project) \ No newline at end of file + %p= link_to project.name, project \ No newline at end of file diff --git a/app/views/build_lists/index.html.haml b/app/views/build_lists/index.html.haml index 32dab8efb..97e5375f9 100644 --- a/app/views/build_lists/index.html.haml +++ b/app/views/build_lists/index.html.haml @@ -25,4 +25,4 @@ .inner = render :partial => "build_lists/build_lists", :object => @build_lists -- content_for :sidebar, render(:partial => 'sidebar') if @project \ No newline at end of file +- content_for :sidebar, render('sidebar', :project => @project) if @project diff --git a/app/views/build_lists/show.html.haml b/app/views/build_lists/show.html.haml index e8c6f6f20..eafd98ddd 100644 --- a/app/views/build_lists/show.html.haml +++ b/app/views/build_lists/show.html.haml @@ -1,9 +1,9 @@ .block .secondary-navigation %ul.wat-cf - %li.first= link_to t("layout.build_lists.current"), project_path(@project) + "#build_lists" - %li= link_to t("layout.build_lists.all"), project_build_lists_path(@project) - %li.active= link_to t("layout.build_lists.show"), project_build_list_path(@project, @build_list) + %li.first= link_to t("layout.build_lists.current"), project_path(@build_list.project) + "#build_lists" + %li= link_to t("layout.build_lists.all"), project_build_lists_path(@build_list.project) + %li.active= link_to t("layout.build_lists.show"), @build_list .content .inner @@ -85,9 +85,9 @@ \: = t("layout.#{@build_list.is_circle?}_") - - if @build_list.can_published? + - if @build_list.can_publish? .wat-cf - = link_to image_tag("web-app-theme/icons/tick.png", :alt => t("layout.publish")) + " " + t("layout.publish"), publish_project_build_list_path(@project, @build_list), :method => "post", :class => "button", :confirm => t("layout.build_lists.confirm_publish") + = link_to image_tag("web-app-theme/icons/tick.png", :alt => t("layout.publish")) + " " + t("layout.publish"), publish_build_list_path(@build_list), :method => "put", :class => "button", :confirm => t("layout.confirm") .block .content @@ -111,4 +111,4 @@ %td= item.version %td.last= item.human_status -- content_for :sidebar, render(:partial => 'sidebar') +- content_for :sidebar, render('sidebar', :project => @build_list.project) diff --git a/config/locales/ru.yml b/config/locales/ru.yml index c4d84ac11..2f0cb3bf4 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -280,7 +280,6 @@ ru: build_lists: filter_header: Фильтр current: Текущие - all: Все created_at_start: "Время постановки на сборку с:" created_at_end: "Время постановки на сборку по:" notified_at_start: "Время последнего обновления от BS с:" @@ -291,11 +290,12 @@ ru: items_header: Элементы сборки no_items_data: Данных нет show: Просмотр - confirm_publish: Вы уверены, что хотите опубликовать контейнер? cancel_button_header: Действие cancel_button: Отмена - cancel_successed: 'Сборка отменена.' - cancel_failed: 'При отмене сборки произошла ошибка!' + cancel_success: 'Сборка отменена.' + cancel_fail: 'При отмене сборки произошла ошибка!' + publish_success: 'Сборка опубликована.' + publish_fail: 'При публикации сборки произошла ошибка!' build_server_status: header: Статус сборочного сервера diff --git a/config/routes.rb b/config/routes.rb index 7abc1ffe4..a5918b82f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,23 +24,16 @@ Rosa::Application.routes.draw do match '/private/:platform_name/*file_path' => 'privates#show' -# match 'build_lists/' => 'build_lists#index', :as => :all_build_lists -# match 'build_lists/:id/cancel/' => 'build_lists#cancel', :as => :build_list_cancel -# match 'build_lists/status_build', :to => "build_lists#status_build" -# match 'build_lists/post_build', :to => "build_lists#post_build" -# match 'build_lists/pre_build', :to => "build_lists#pre_build" -# match 'build_lists/circle_build', :to => "build_lists#circle_build" -# match 'build_lists/new_bbdt', :to => "build_lists#new_bbdt" + match 'build_lists/status_build', :to => "build_lists#status_build" + match 'build_lists/post_build', :to => "build_lists#post_build" + match 'build_lists/pre_build', :to => "build_lists#pre_build" + match 'build_lists/circle_build', :to => "build_lists#circle_build" + match 'build_lists/new_bbdt', :to => "build_lists#new_bbdt" - - resources :build_lists, :only => [:index, :cancel, :status_build, :post_build, :pre_build, :circle_build, :new_bbdt] do + resources :build_lists, :only => [:index, :show] do member do - get :cancel - get :status_build - get :post_build - get :pre_build - get :circle_build - get :new_bbdt + put :cancel + put :publish end end @@ -86,15 +79,7 @@ Rosa::Application.routes.draw do resources :projects do resource :repo, :controller => "git/repositories", :only => [:show] - resources :build_lists, :only => [:index, :show, :new, :create] do - collection do - get :recent - post :filter - end - member do - post :publish - end - end + resources :build_lists, :only => [:index, :new, :create] resources :collaborators, :only => [:index, :edit, :update, :add] do collection do diff --git a/db/migrate/20111221194422_add_user_id_to_build_lists.rb b/db/migrate/20111221194422_add_user_id_to_build_lists.rb new file mode 100644 index 000000000..1a52d0264 --- /dev/null +++ b/db/migrate/20111221194422_add_user_id_to_build_lists.rb @@ -0,0 +1,9 @@ +class AddUserIdToBuildLists < ActiveRecord::Migration + def self.up + add_column :build_lists, :user_id, :integer + end + + def self.down + remove_column :build_lists, :user_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 1a7fdc5fd..914d12701 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 => 20111220152347) do +ActiveRecord::Schema.define(:version => 20111221194422) do create_table "arches", :force => true do |t| t.string "name", :null => false @@ -70,6 +70,7 @@ ActiveRecord::Schema.define(:version => 20111220152347) do t.integer "bpl_id" t.integer "pl_id" t.text "include_repos" + t.integer "user_id" end add_index "build_lists", ["arch_id"], :name => "index_build_lists_on_arch_id" From 2ac4391edfca488065f97baa057b47f247f00b00 Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Thu, 22 Dec 2011 01:31:05 +0200 Subject: [PATCH 10/40] Redo build_list publish to check returned status. Refs #77 --- app/models/build_list.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/app/models/build_list.rb b/app/models/build_list.rb index 1c1562863..94522d13d 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -103,13 +103,12 @@ class BuildList < ActiveRecord::Base end end end - + def publish - return false unless can_publish? - - BuildServer.publish_container bs_id - self.update_attribute(:status, BUILD_PUBLISHED) - #self.destroy # self.delete + has_published = BuildServer.publish_container bs_id + update_attribute(:status, BUILD_PUBLISHED) if has_published == 0 + + return has_published == 0 end def can_publish? @@ -119,7 +118,7 @@ class BuildList < ActiveRecord::Base def cancel has_canceled = BuildServer.delete_build_list bs_id update_attribute(:status, BUILD_CANCELED) if has_canceled == 0 - + return has_canceled == 0 end From a82f59f35a23a51d39e1d2b24964cf5ad96b95b8 Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Thu, 22 Dec 2011 02:53:55 +0200 Subject: [PATCH 11/40] Add route, action and statuses for publish_build. Apply status assign and check. Localize statuses. Convert tabs to spaces. Refs #80 --- app/controllers/build_lists_controller.rb | 152 ++++++++++++---------- app/models/build_list.rb | 12 +- config/locales/ru.yml | 4 +- config/routes.rb | 1 + 4 files changed, 93 insertions(+), 76 deletions(-) diff --git a/app/controllers/build_lists_controller.rb b/app/controllers/build_lists_controller.rb index 4f7479aa4..cfb84498c 100644 --- a/app/controllers/build_lists_controller.rb +++ b/app/controllers/build_lists_controller.rb @@ -1,18 +1,18 @@ class BuildListsController < ApplicationController - CALLBACK_ACTIONS = [:status_build, :pre_build, :post_build, :circle_build, :new_bbdt] + CALLBACK_ACTIONS = [:publish_build, :status_build, :pre_build, :post_build, :circle_build, :new_bbdt] NESTED_ACTIONS = [:index, :new, :create] before_filter :authenticate_user!, :except => CALLBACK_ACTIONS before_filter :authenticate_build_service!, :only => CALLBACK_ACTIONS before_filter :find_project, :only => NESTED_ACTIONS before_filter :find_build_list, :only => [:show, :publish, :cancel] - before_filter :find_build_list_by_bs, :only => [:status_build, :pre_build, :post_build] + before_filter :find_build_list_by_bs, :only => [:publish_build, :status_build, :pre_build, :post_build] load_and_authorize_resource :project, :only => NESTED_ACTIONS load_and_authorize_resource :through => :project, :only => NESTED_ACTIONS, :shallow => true load_and_authorize_resource :except => CALLBACK_ACTIONS.concat(NESTED_ACTIONS) - def index + def index filter_params = params[:filter] || {} if @project @action_url = project_build_lists_path(@project) @@ -21,17 +21,17 @@ class BuildListsController < ApplicationController end @filter = BuildList::Filter.new(@project, filter_params) - @build_lists = @filter.find.accessible_by(current_ability).recent.paginate :page => params[:page] + @build_lists = @filter.find.accessible_by(current_ability).recent.paginate :page => params[:page] @build_server_status = begin BuildServer.get_status rescue Exception # Timeout::Error {} end - end + end - def new - @build_list = BuildList.new + def new + @build_list = BuildList.new end def create @@ -59,91 +59,99 @@ class BuildListsController < ApplicationController end end - def show - @item_groups = @build_list.items.group_by_level - end + def show + @item_groups = @build_list.items.group_by_level + end - def publish - if @build_list.publish - redirect_to :back, :notice => t('layout.build_lists.publish_success') - else - redirect_to :back, :notice => t('layout.build_lists.publish_fail') - end - end + def publish + if @build_list.publish + redirect_to :back, :notice => t('layout.build_lists.publish_success') + else + redirect_to :back, :notice => t('layout.build_lists.publish_fail') + end + end - def cancel - if @build_list.cancel - redirect_to :back, :notice => t('layout.build_lists.cancel_success') - else - redirect_to :back, :notice => t('layout.build_lists.cancel_fail') - end - end + def cancel + if @build_list.cancel + redirect_to :back, :notice => t('layout.build_lists.cancel_success') + else + redirect_to :back, :notice => t('layout.build_lists.cancel_fail') + end + end - def status_build - @item = @build_list.items.find_by_name!(params[:package_name]) - @item.status = params[:status] - @item.save + def publish_build + @build_list.status = (params[:status].to_i == 0 ? BuildList::BUILD_PUBLISHED : BuildList::FAILED_PUBLISH) + @build_list.notified_at = Time.current + @build_list.save - @build_list.container_path = params[:container_path] - @build_list.notified_at = Time.current - @build_list.save + render :nothing => true, :status => 200 + end - render :nothing => true, :status => 200 - end + def status_build + @item = @build_list.items.find_by_name!(params[:package_name]) + @item.status = params[:status] + @item.save - def pre_build - @build_list.status = BuildServer::BUILD_STARTED - @build_list.notified_at = Time.current - @build_list.save + @build_list.container_path = params[:container_path] + @build_list.notified_at = Time.current + @build_list.save - render :nothing => true, :status => 200 - end + render :nothing => true, :status => 200 + end - def post_build - @build_list.status = params[:status] - @build_list.container_path = params[:container_path] - @build_list.notified_at = Time.current - @build_list.save + def pre_build + @build_list.status = BuildServer::BUILD_STARTED + @build_list.notified_at = Time.current + @build_list.save - render :nothing => true, :status => 200 - end + render :nothing => true, :status => 200 + end - def circle_build - @build_list.is_circle = true - @build_list.container_path = params[:container_path] - @build_list.notified_at = Time.current - @build_list.save + def post_build + @build_list.status = params[:status] + @build_list.container_path = params[:container_path] + @build_list.notified_at = Time.current + @build_list.save - render :nothing => true, :status => 200 - end + render :nothing => true, :status => 200 + end - def new_bbdt - @build_list = BuildList.find_by_id!(params[:web_id]) - @build_list.name = params[:name] - @build_list.additional_repos = ActiveSupport::JSON.decode(params[:additional_repos]) - @build_list.set_items(ActiveSupport::JSON.decode(params[:items])) - @build_list.notified_at = Time.current - @build_list.is_circle = (params[:is_circular] != "0") - @build_list.bs_id = params[:id] - params[:arch] - @build_list.save + def circle_build + @build_list.is_circle = true + @build_list.container_path = params[:container_path] + @build_list.notified_at = Time.current + @build_list.save - render :nothing => true, :status => 200 - end + render :nothing => true, :status => 200 + end - protected + def new_bbdt + @build_list = BuildList.find_by_id!(params[:web_id]) + @build_list.name = params[:name] + @build_list.additional_repos = ActiveSupport::JSON.decode(params[:additional_repos]) + @build_list.set_items(ActiveSupport::JSON.decode(params[:items])) + @build_list.notified_at = Time.current + @build_list.is_circle = (params[:is_circular] != "0") + @build_list.bs_id = params[:id] + params[:arch] + @build_list.save - def find_project - @project = Project.find_by_id params[:project_id] - end + render :nothing => true, :status => 200 + end + + protected + + def find_project + @project = Project.find_by_id params[:project_id] + end def find_build_list @build_list = BuildList.find(params[:id]) end - def find_build_list_by_bs - @build_list = BuildList.find_by_bs_id!(params[:id]) - end + def find_build_list_by_bs + @build_list = BuildList.find_by_bs_id!(params[:id]) + end def authenticate_build_service! if request.remote_ip != APP_CONFIG['build_server_ip'] diff --git a/app/models/build_list.rb b/app/models/build_list.rb index 94522d13d..58f6e0da5 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -21,11 +21,15 @@ class BuildList < ActiveRecord::Base WAITING_FOR_RESPONSE = 4000 BUILD_PENDING = 2000 BUILD_PUBLISHED = 6000 + BUILD_PUBLISH = 7000 + FAILED_PUBLISH = 8000 STATUSES = [ WAITING_FOR_RESPONSE, BUILD_CANCELED, BUILD_PENDING, BUILD_PUBLISHED, + BUILD_PUBLISH, + FAILED_PUBLISH, BuildServer::SUCCESS, BuildServer::BUILD_STARTED, BuildServer::BUILD_ERROR, @@ -40,6 +44,8 @@ class BuildList < ActiveRecord::Base BUILD_CANCELED => :build_canceled, BUILD_PENDING => :build_pending, BUILD_PUBLISHED => :build_published, + BUILD_PUBLISH => :build_publish, + FAILED_PUBLISH => :failed_publish, BuildServer::BUILD_ERROR => :build_error, BuildServer::BUILD_STARTED => :build_started, BuildServer::SUCCESS => :success, @@ -106,13 +112,13 @@ class BuildList < ActiveRecord::Base def publish has_published = BuildServer.publish_container bs_id - update_attribute(:status, BUILD_PUBLISHED) if has_published == 0 + update_attribute(:status, BUILD_PUBLISH) if has_published == 0 return has_published == 0 end def can_publish? - self.status == BuildServer::SUCCESS + status == BuildServer::SUCCESS or status == FAILED_PUBLISH end def cancel @@ -124,7 +130,7 @@ class BuildList < ActiveRecord::Base #TODO: Share this checking on product owner. def can_cancel? - self.status == BUILD_PENDING && bs_id + status == BUILD_PENDING && bs_id end def event_log_message diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 2f0cb3bf4..6e3a6838f 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -294,7 +294,7 @@ ru: cancel_button: Отмена cancel_success: 'Сборка отменена.' cancel_fail: 'При отмене сборки произошла ошибка!' - publish_success: 'Сборка опубликована.' + publish_success: 'Сборка поставлена в очередь на публикацию.' publish_fail: 'При публикации сборки произошла ошибка!' build_server_status: @@ -314,6 +314,8 @@ ru: statuses: build_error: ошибка сборки build_published: опубликован + build_publish: публикуется + failed_publish: ошибка публикации dependencies_fail: зависимости не найдены waiting_for_response: ожидает ответа build_pending: ожидает сборку diff --git a/config/routes.rb b/config/routes.rb index a5918b82f..2877c44b9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,6 +24,7 @@ Rosa::Application.routes.draw do match '/private/:platform_name/*file_path' => 'privates#show' + match 'build_lists/publish_build', :to => "build_lists#publish_build" match 'build_lists/status_build', :to => "build_lists#status_build" match 'build_lists/post_build', :to => "build_lists#post_build" match 'build_lists/pre_build', :to => "build_lists#pre_build" From 96d458875e398351b80f43cf2c3efaddbe075937 Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Thu, 22 Dec 2011 04:08:11 +0200 Subject: [PATCH 12/40] Remove publish validator. Add can_cancel/can_publish checking to cancel/publish methods. Refs #80 --- app/models/build_list.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/models/build_list.rb b/app/models/build_list.rb index 58f6e0da5..d28e0d293 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -12,9 +12,6 @@ class BuildList < ActiveRecord::Base validate lambda { errors.add(:bpl, I18n.t('flash.build_list.wrong_platform')) if pl.platform_type == 'main' && pl_id != bpl_id } - validate lambda { - errors.add(:bpl, I18n.t('flash.build_list.can_not_published')) if status == BUILD_PUBLISHED && status_was != BuildServer::SUCCESS - } # The kernel does not send these statuses directly BUILD_CANCELED = 5000 @@ -111,20 +108,20 @@ class BuildList < ActiveRecord::Base end def publish + return false unless can_publish? has_published = BuildServer.publish_container bs_id update_attribute(:status, BUILD_PUBLISH) if has_published == 0 - return has_published == 0 end - + def can_publish? status == BuildServer::SUCCESS or status == FAILED_PUBLISH end def cancel + return false unless can_cancel? has_canceled = BuildServer.delete_build_list bs_id update_attribute(:status, BUILD_CANCELED) if has_canceled == 0 - return has_canceled == 0 end From e2144e0fdb43b37c5262d88a66cd15b5c52b53ae Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Thu, 22 Dec 2011 11:41:37 +0400 Subject: [PATCH 13/40] [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 8c3291d237c45f44d589da9cd5dffa03772ff9ea Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Fri, 23 Dec 2011 04:14:28 +0200 Subject: [PATCH 14/40] Fix and redo specs after great build_lists refactoring and improvement. Fix minor bugs. Code cleanup. Refs #65 --- app/controllers/build_lists_controller.rb | 2 +- app/views/build_lists/new.html.haml | 4 +- .../build_lists_controller_spec.rb | 57 +++++++++++++++++-- spec/controllers/projects_controller_spec.rb | 11 ---- spec/factories/build_list_factory.rb | 10 ++-- spec/factories/platform_factory.rb | 4 ++ spec/models/cancan_spec.rb | 27 ++++++++- .../shared_examples/projects_controller.rb | 10 ---- 8 files changed, 90 insertions(+), 35 deletions(-) diff --git a/app/controllers/build_lists_controller.rb b/app/controllers/build_lists_controller.rb index cfb84498c..cbd847933 100644 --- a/app/controllers/build_lists_controller.rb +++ b/app/controllers/build_lists_controller.rb @@ -36,7 +36,7 @@ class BuildListsController < ApplicationController def create notices, errors = [], [] - Arch.where(:id => params[:archs]).each do |arch| + Arch.where(:id => params[:arches]).each do |arch| Platform.main.where(:id => params[:bpls]).each do |bpl| @build_list = @project.build_lists.build(params[:build_list]) @build_list.bpl = bpl; @build_list.arch = arch; @build_list.user = current_user diff --git a/app/views/build_lists/new.html.haml b/app/views/build_lists/new.html.haml index 88aa55c5d..622191b78 100644 --- a/app/views/build_lists/new.html.haml +++ b/app/views/build_lists/new.html.haml @@ -38,8 +38,8 @@ .group = f.label :arches, t("activerecord.attributes.build_list.arch"), :class => :label - Arch.recent.each do |arch| - = check_box_tag "archs[]", arch.id, (params[:archs]||[]).include?(arch.id.to_s), :id => "archs_#{arch.id}" - = label_tag "archs_#{arch.id}", arch.name + = check_box_tag "arches[]", arch.id, (params[:arches]||[]).include?(arch.id.to_s), :id => "arches_#{arch.id}" + = label_tag "arches_#{arch.id}", arch.name %br .group diff --git a/spec/controllers/build_lists_controller_spec.rb b/spec/controllers/build_lists_controller_spec.rb index 27b6b8174..d6d2172a7 100644 --- a/spec/controllers/build_lists_controller_spec.rb +++ b/spec/controllers/build_lists_controller_spec.rb @@ -25,8 +25,49 @@ describe BuildListsController do response.should redirect_to(forbidden_url) end end + + shared_examples_for 'create build list' do + it 'should be able to perform new action' do + get :new, :project_id => @project.id + response.should render_template(:new) + end + + it 'should be able to perform create action' do + post :create, {:project_id => @project.id}.merge(@create_params) + response.should redirect_to(@project) + end + end + + shared_examples_for 'not create build list' do + it 'should not be able to perform new action' do + get :new, :project_id => @project.id + response.should redirect_to(forbidden_url) + end + + it 'should not be able to perform create action' do + post :create, {:project_id => @project.id}.merge(@create_params) + response.should redirect_to(forbidden_url) + end + end context 'crud' do + before(:each) do + stub_rsync_methods + + platform = Factory(:platform_with_repos) + @create_params = { + :build_list => { + :project_version => 'v1.0', + :pl_id => platform.id, + :update_type => 'security', + :include_repos => [platform.repositories.first.id] + }, + :arches => [Factory(:arch).id], + :bpls => [platform.id] + } + any_instance_of(Project, :versions => ['v1.0', 'v2.0']) + end + context 'for guest' do it 'should not be able to perform index action' do get :index @@ -36,7 +77,6 @@ describe BuildListsController do context 'for user' do before(:each) do - stub_rsync_methods @build_list = Factory(:build_list_core) @project = @build_list.project @owner_user = @project.owner @@ -47,7 +87,6 @@ describe BuildListsController do @user = Factory(:user) set_session_for(@user) @show_params = {:project_id => @project.id, :id => @build_list.id} - end context 'for all build lists' do @@ -76,15 +115,18 @@ describe BuildListsController do context 'for open project' do it_should_behave_like 'show build list' + it_should_behave_like 'not create build list' context 'if user is project owner' do before(:each) {set_session_for(@owner_user)} it_should_behave_like 'show build list' + it_should_behave_like 'create build list' end context 'if user is project member' do before(:each) {set_session_for(@member_user)} it_should_behave_like 'show build list' + it_should_behave_like 'not create build list' end end @@ -95,23 +137,24 @@ describe BuildListsController do end it_should_behave_like 'not show build list' + it_should_behave_like 'not create build list' context 'if user is project owner' do before(:each) {set_session_for(@owner_user)} it_should_behave_like 'show build list' + it_should_behave_like 'create build list' end context 'if user is project member' do before(:each) {set_session_for(@member_user)} it_should_behave_like 'show build list' + it_should_behave_like 'not create build list' end end - end context 'for group' do before(:each) do - stub_rsync_methods @owner_group = Factory(:group) @owner_user = Factory(:user) @owner_group.objects.create :role => 'reader', :object_id => @owner_user.id, :object_type => 'User' @@ -158,15 +201,18 @@ describe BuildListsController do context 'for open project' do it_should_behave_like 'show build list' + it_should_behave_like 'not create build list' context 'if user is group owner' do before(:each) {set_session_for(@owner_user)} it_should_behave_like 'show build list' + it_should_behave_like 'create build list' end context 'if user is group member' do before(:each) {set_session_for(@member_user)} it_should_behave_like 'show build list' + it_should_behave_like 'not create build list' end end @@ -177,15 +223,18 @@ describe BuildListsController do end it_should_behave_like 'not show build list' + it_should_behave_like 'not create build list' context 'if user is group owner' do before(:each) {set_session_for(@owner_user)} it_should_behave_like 'show build list' + it_should_behave_like 'create build list' end context 'if user is group member' do before(:each) {set_session_for(@member_user)} it_should_behave_like 'show build list' + it_should_behave_like 'not create build list' end end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index ebf0ab6ac..c17c09954 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -8,17 +8,6 @@ describe ProjectsController do @another_user = Factory(:user) @create_params = {:project => {:name => 'pro'}} @update_params = {:project => {:name => 'pro2'}} - - platform = Factory(:platform) - @process_build_params = {:build => { - :arches => {Factory(:arch).id => '1'}, - :project_version => 'v1.0', - :bpl => {platform.id => '1'}, - :pl => platform.id, - :update_type => 'security' - }} - - any_instance_of(Project, :versions => ['v1.0', 'v2.0']) end context 'for guest' do diff --git a/spec/factories/build_list_factory.rb b/spec/factories/build_list_factory.rb index a4e1aa39a..10c4fc3b5 100644 --- a/spec/factories/build_list_factory.rb +++ b/spec/factories/build_list_factory.rb @@ -1,11 +1,13 @@ Factory.define(:build_list) do |p| - p.association :project, :factory => :project - p.association :pl, :factory => :platform - p.association :arch, :factory => :arch - p.bpl { |bl| bl.pl } + p.association :user + p.association :project + p.association :pl, :factory => :platform_with_repos + p.association :arch + p.bpl {|bl| bl.pl} p.project_version "1.0" p.build_requires true p.update_type 'security' + p.include_repos {|bl| bl.pl.repositories.map(&:id)} end Factory.define(:build_list_core, :parent => :build_list) do |p| diff --git a/spec/factories/platform_factory.rb b/spec/factories/platform_factory.rb index 9fab7a443..1c1f3307a 100644 --- a/spec/factories/platform_factory.rb +++ b/spec/factories/platform_factory.rb @@ -5,3 +5,7 @@ Factory.define(:platform) do |p| p.distrib_type APP_CONFIG['distr_types'].first p.association :owner, :factory => :user end + +Factory.define(:platform_with_repos, :parent => :platform) do |p| + p.repositories {|r| [r.association(:repository)]} +end \ No newline at end of file diff --git a/spec/models/cancan_spec.rb b/spec/models/cancan_spec.rb index 97362bb66..8875ef1f5 100644 --- a/spec/models/cancan_spec.rb +++ b/spec/models/cancan_spec.rb @@ -139,11 +139,18 @@ describe CanCan do @project.relations.create!(:object_id => @user.id, :object_type => 'User', :role => 'writer') end - [:read, :update, :process_build, :build].each do |action| + [:read, :update].each do |action| it "should be able to #{ action } project" do @ability.should be_able_to(action, @project) end end + + [:new, :create].each do |action| + it "should be able to #{action} build_list" do + @build_list = Factory(:build_list, :project => @project) + @ability.should be_able_to(action, @build_list) + end + end end context 'with admin rights' do @@ -151,12 +158,19 @@ describe CanCan do @project.relations.create!(:object_id => @user.id, :object_type => 'User', :role => 'admin') end - [:read, :update, :process_build, :build].each do |action| + [:read, :update].each do |action| it "should be able to #{ action } project" do @ability.should be_able_to(action, @project) end end + [:new, :create].each do |action| + it "should be able to #{action} build_list" do + @build_list = Factory(:build_list, :project => @project) + @ability.should be_able_to(action, @build_list) + end + end + it "should be able to manage collaborators of project" do @ability.should be_able_to(:manage_collaborators, @project) end @@ -167,11 +181,18 @@ describe CanCan do @project.update_attribute(:owner, @user) end - [:read, :update, :process_build, :build, :destroy].each do |action| + [:read, :update, :destroy].each do |action| it "should be able to #{ action } project" do @ability.should be_able_to(action, @project) end end + + [:new, :create].each do |action| + it "should be able to #{action} build_list" do + @build_list = Factory(:build_list, :project => @project) + @ability.should be_able_to(action, @build_list) + end + end end end diff --git a/spec/support/shared_examples/projects_controller.rb b/spec/support/shared_examples/projects_controller.rb index 7aa4baa01..37cd28b28 100644 --- a/spec/support/shared_examples/projects_controller.rb +++ b/spec/support/shared_examples/projects_controller.rb @@ -12,16 +12,6 @@ shared_examples_for 'projects user with writer rights' do put :update, {:id => @project.id}.merge(@update_params) response.should redirect_to(project_path(@project)) end - - it 'should be able to perform build action' do - get :build, :id => @project.id - response.should render_template(:build) - end - - it 'should be able to perform process_build action' do - post :process_build, {:id => @project.id}.merge(@process_build_params) - response.should redirect_to(project_path(@project)) - end end shared_examples_for 'user with rights to view projects' do From 894bc0c077d0395e594aeddcdf785653bbc2b0ba Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Fri, 23 Dec 2011 14:56:46 +0400 Subject: [PATCH 15/40] [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 16/40] [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 17/40] [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 18/40] [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 From 0e5b39985e1293659b4c4742c83e7a6c6e8f93df Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Fri, 23 Dec 2011 20:47:13 +0200 Subject: [PATCH 19/40] Clarify tests. Write autostart docs. Refs #65, #53 --- doc/README_FOR_APP | 16 ++++++++++++++-- spec/controllers/build_lists_controller_spec.rb | 8 ++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/doc/README_FOR_APP b/doc/README_FOR_APP index fbcff3042..bbed67ad7 100644 --- a/doc/README_FOR_APP +++ b/doc/README_FOR_APP @@ -1,8 +1,20 @@ === Basic bootstrap + * rake db:drop db:setup + == === Setup autostart -Add to /etc/rc.d/rc.local following /srv/rosa_build/current/bin/autostart.sh -== +Add to /etc/rc.d/rc.local + + /srv/rosa_build/current/bin/autostart.sh + +Add to /etc/rc.d/rc.sysinit + + # force run rc.local + if [ -f /etc/rc.local ]; then + . /etc/rc.local + fi + +== diff --git a/spec/controllers/build_lists_controller_spec.rb b/spec/controllers/build_lists_controller_spec.rb index d6d2172a7..c05d24e50 100644 --- a/spec/controllers/build_lists_controller_spec.rb +++ b/spec/controllers/build_lists_controller_spec.rb @@ -123,7 +123,7 @@ describe BuildListsController do it_should_behave_like 'create build list' end - context 'if user is project member' do + context 'if user is project read member' do before(:each) {set_session_for(@member_user)} it_should_behave_like 'show build list' it_should_behave_like 'not create build list' @@ -145,7 +145,7 @@ describe BuildListsController do it_should_behave_like 'create build list' end - context 'if user is project member' do + context 'if user is project read member' do before(:each) {set_session_for(@member_user)} it_should_behave_like 'show build list' it_should_behave_like 'not create build list' @@ -209,7 +209,7 @@ describe BuildListsController do it_should_behave_like 'create build list' end - context 'if user is group member' do + context 'if user is group read member' do before(:each) {set_session_for(@member_user)} it_should_behave_like 'show build list' it_should_behave_like 'not create build list' @@ -231,7 +231,7 @@ describe BuildListsController do it_should_behave_like 'create build list' end - context 'if user is group member' do + context 'if user is group read member' do before(:each) {set_session_for(@member_user)} it_should_behave_like 'show build list' it_should_behave_like 'not create build list' From ce6746f7a9317fed56cec1979e3adb95bf3fd5a4 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Mon, 26 Dec 2011 19:48:57 +0400 Subject: [PATCH 20/40] [refs #54] Add subscribes and notifications --- app/controllers/subscribes_controller.rb | 31 +++++++++++++++ app/helpers/subscribes_helper.rb | 2 + app/mailers/user_mailer.rb | 26 ++++++++++++- app/models/comment.rb | 16 ++++++++ app/models/issue.rb | 39 +++++++++++++++++++ app/models/relation.rb | 1 + app/models/subscribe.rb | 4 ++ app/views/issues/show.html.haml | 8 ++++ .../issue_assign_notification.haml | 7 ++++ .../user_mailer/new_comment_notification.haml | 7 ++++ .../user_mailer/new_issue_notification.haml | 7 ++++ config/locales/ru.yml | 9 +++++ config/routes.rb | 3 ++ .../20111226141947_create_subscribes.rb | 14 +++++++ db/schema.rb | 21 ++++++---- .../controllers/subscribes_controller_spec.rb | 5 +++ spec/factories/subscribes.rb | 6 +++ spec/helpers/subscribes_helper_spec.rb | 15 +++++++ spec/models/subscribe_spec.rb | 5 +++ 19 files changed, 218 insertions(+), 8 deletions(-) create mode 100644 app/controllers/subscribes_controller.rb create mode 100644 app/helpers/subscribes_helper.rb create mode 100644 app/models/subscribe.rb create mode 100644 app/views/user_mailer/issue_assign_notification.haml create mode 100644 app/views/user_mailer/new_comment_notification.haml create mode 100644 app/views/user_mailer/new_issue_notification.haml create mode 100644 db/migrate/20111226141947_create_subscribes.rb create mode 100644 spec/controllers/subscribes_controller_spec.rb create mode 100644 spec/factories/subscribes.rb create mode 100644 spec/helpers/subscribes_helper_spec.rb create mode 100644 spec/models/subscribe_spec.rb diff --git a/app/controllers/subscribes_controller.rb b/app/controllers/subscribes_controller.rb new file mode 100644 index 000000000..4bb51be46 --- /dev/null +++ b/app/controllers/subscribes_controller.rb @@ -0,0 +1,31 @@ +class SubscribesController < ApplicationController + def create + @subscribe = @subscribeable.subscribes.build(:user_id => current_user.id) + if @subscribe.save + flash[:notice] = I18n.t("flash.subscribe.saved") + redirect_to :back + else + flash[:error] = I18n.t("flash.subscribe.saved_error") + redirect_to :back + end + end + + def destroy + @subscribe = Subscribe.find(params[:id]) + @subscribe.destroy + + flash[:notice] = t("flash.subscribe.destroyed") + redirect_to :back + end + + private + + def find_subscribeable + params.each do |name, value| + if name =~ /(.+)_id$/ + return $1.classify.constantize.find(value) + end + end + nil + end +end diff --git a/app/helpers/subscribes_helper.rb b/app/helpers/subscribes_helper.rb new file mode 100644 index 000000000..dc1b490ec --- /dev/null +++ b/app/helpers/subscribes_helper.rb @@ -0,0 +1,2 @@ +module SubscribesHelper +end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 3137b0dff..2c43482e1 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -5,7 +5,31 @@ class UserMailer < ActionMailer::Base def new_user_notification(user) @user = user - mail(:to => user.email, :subject => "Регистрация на проекте «#{APP_CONFIG['project_name']}»") do |format| + mail(:to => user.email, :subject => I18n.t("notifications.subjects.new_user_notification", :project_name => APP_CONFIG['project_name'])) do |format| + format.html + end + end + + def new_comment_notification(comment, user) + @user = user + @comment = comment + mail(:to => user.email, :subject => I18n.t("notifications.subjects.new_comment_notification")) do |format| + format.html + end + end + + def new_issue_notification(issue, user) + @user = user + @issue = issue + mail(:to => user.email, :subject => I18n.t("notifications.subjects.new_issue_notification")) do |format| + format.html + end + end + + def issue_assign_notification(issue, user) + @user = user + @issue = issue + mail(:to => user.email, :subject => I18n.t("notifications.subjects.issue_assign_notification")) do |format| format.html end end diff --git a/app/models/comment.rb b/app/models/comment.rb index 4c15a8b66..36e14c412 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -3,4 +3,20 @@ class Comment < ActiveRecord::Base belongs_to :user validates :body, :user_id, :commentable_id, :commentable_type, :presence => true + + after_create :deliver_new_comment_notification + + protected + + def deliver_new_comment_notification + #UserMailer.new_comment_notification(self, self.commentable.user).deliver + #UserMailer.new_comment_notification(self, self.commentable.project.owner).deliver + recipients = self.commentable.project.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) } + recipients = recipients | [self.commentable.user_id] + recipients = recipients | [self.commentable.project.owner_id] if self.commentable.project.owner_type == 'User' + recipients.each do |recipient_id| + recipient = User.find(recipient_id) + UserMailer.new_comment_notification(self, recipient).deliver + end + end end diff --git a/app/models/issue.rb b/app/models/issue.rb index ad6f57468..586175b89 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -5,12 +5,17 @@ class Issue < ActiveRecord::Base belongs_to :user has_many :comments, :as => :commentable + has_many :subscribes, :as => :subscribeable validates :title, :body, :project_id, :user_id, :presence => true #attr_readonly :serial_id after_create :set_serial_id + after_create :subscribe_users + after_create :deliver_new_issue_notification + after_create :deliver_issue_assign_notification + after_update :deliver_issue_assign_notification protected @@ -18,4 +23,38 @@ class Issue < ActiveRecord::Base self.serial_id = self.project.issues.count self.save! end + + def deliver_new_issue_notification + #UserMailer.new_issue_notification(self, self.project.owner).deliver + #self.project.relations.by_role('admin').each do |rel| + # admin = User.find(rel.object_id) + # UserMailer.new_issue_notification(self, admin).deliver + #end + + recipients = collect_recipient_ids + recipients.each do |recipient_id| + recipient = User.find(recipient_id) + UserMailer.new_issue_notification(self, recipient).deliver + end + end + + def deliver_issue_assign_notification + UserMailer.issue_assign_notification(self, self.user).deliver if self.user_id_was != self.user_id + end + + def subscribe_users + recipients = collect_recipient_ids + recipients.each do |recipient_id| + ss = self.subscribes.build(:user_id => recipient_id) + ss.save! + end + end + + def collect_recipient_ids + recipients = self.project.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) } + recipients = recipients | [self.user_id] + recipients = recipients | [self.project.owner_id] if self.project.owner_type == 'User' + recipients + end + end diff --git a/app/models/relation.rb b/app/models/relation.rb index 822687dca..a08ddf6a9 100644 --- a/app/models/relation.rb +++ b/app/models/relation.rb @@ -10,6 +10,7 @@ class Relation < ActiveRecord::Base scope :by_object, lambda {|obj| {:conditions => ['object_id = ? AND object_type = ?', obj.id, obj.class.to_s]}} scope :by_target, lambda {|tar| {:conditions => ['target_id = ? AND target_type = ?', tar.id, tar.class.to_s]}} + scope :by_role, lambda {|role| {:conditions => ['role = ?', role]}} def self.create_with_role(object, target, role) r = new diff --git a/app/models/subscribe.rb b/app/models/subscribe.rb new file mode 100644 index 000000000..0c3a52698 --- /dev/null +++ b/app/models/subscribe.rb @@ -0,0 +1,4 @@ +class Subscribe < ActiveRecord::Base + belongs_to :subscribeable, :polymorphic => true + belongs_to :user +end diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index 51c080e4a..69c2be747 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -20,6 +20,14 @@ = t('activerecord.attributes.issue.status') \: = @issue.status + %p + %b + = t('layout.issues.subscribe') + \: + - if @issue.subscribes.exists? :user_id => current_user.id + = link_to t('layout.issues.unsubscribe_btn'), unsubscribe_issue(@issue), :method => :post + - else + = link_to t('layout.issues.subscribe_btn'), subscribe_issue(@issue), :method => :delete %a{ :name => "comments" } .block diff --git a/app/views/user_mailer/issue_assign_notification.haml b/app/views/user_mailer/issue_assign_notification.haml new file mode 100644 index 000000000..72e99e7df --- /dev/null +++ b/app/views/user_mailer/issue_assign_notification.haml @@ -0,0 +1,7 @@ +%p== Здравствуйте, #{@user.name}. + + +%p Вам была назначена задача #{ link_to @issue.title, show_issue_path(@issue.project, @issue.serial_id) } + + +%p== Команда поддержки «ROSA Build System» diff --git a/app/views/user_mailer/new_comment_notification.haml b/app/views/user_mailer/new_comment_notification.haml new file mode 100644 index 000000000..0e57ff415 --- /dev/null +++ b/app/views/user_mailer/new_comment_notification.haml @@ -0,0 +1,7 @@ +%p== Здравствуйте, #{@user.name}. + + +%p К задаче #{ link_to @comment.commentable.title, show_issue_path(@comment.commentable.project, @comment.commentable.serial_id) } был добавлен новый комментарий. + + +%p== Команда поддержки «ROSA Build System» diff --git a/app/views/user_mailer/new_issue_notification.haml b/app/views/user_mailer/new_issue_notification.haml new file mode 100644 index 000000000..e98f32f45 --- /dev/null +++ b/app/views/user_mailer/new_issue_notification.haml @@ -0,0 +1,7 @@ +%p== Здравствуйте, #{@user.name}. + + +%p К проекту #{ link_to @issue.project.name, project_path(@issue.project) } была добавлена задача #{ link_to @issue.title, show_issue_path(@issue.project, @issue.serial_id) } + + +%p== Команда поддержки «ROSA Build System» diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 97eea839c..563ffdea7 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -106,6 +106,9 @@ ru: open: Открытые closed: Закрытые any: Все + subscribe: Подписка на уведомления + subscribe_btn: Подписаться + unsubscribe_btn: Отписаться comments: confirm_delete: Вы уверены, что хотите удалить комментарий? @@ -653,3 +656,9 @@ ru: distro: Дистрибутив platform: Архитектура counter: Закачки + notifications: + subjects: + new_comment_notification: Новый комментарий к Вашей задаче + new_issue_notification: Новая задача добавлена к проекту + new_user_notification: Регистрация на проекте «%{ project_name }» + issue_assign_notification: Вам назначили задачу diff --git a/config/routes.rb b/config/routes.rb index 4a0227e63..e71991c55 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -78,6 +78,9 @@ Rosa::Application.routes.draw do resources :categories, :only => [:index, :show] end + match "issues/:issue_id/subscribe" => 'subscribes#create', :as => :subscribe_issue, :via => :post + match "issues/:issue_id/unsubscribe" => 'subscribes#destroy', :as => :unsubscribe_issue, :via => :delete + 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 diff --git a/db/migrate/20111226141947_create_subscribes.rb b/db/migrate/20111226141947_create_subscribes.rb new file mode 100644 index 000000000..631e09f09 --- /dev/null +++ b/db/migrate/20111226141947_create_subscribes.rb @@ -0,0 +1,14 @@ +class CreateSubscribes < ActiveRecord::Migration + def self.up + create_table :subscribes do |t| + t.integer :subscribeable_id + t.string :subscribeable_type + t.integer :user_id + t.timestamps + end + end + + def self.down + drop_table :subscribes + end +end diff --git a/db/schema.rb b/db/schema.rb index 55f94564c..1aece65f1 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 => 20111221194422) do +ActiveRecord::Schema.define(:version => 20111226141947) do create_table "arches", :force => true do |t| t.string "name", :null => false @@ -245,7 +245,6 @@ ActiveRecord::Schema.define(:version => 20111221194422) 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" @@ -272,18 +271,26 @@ ActiveRecord::Schema.define(:version => 20111221194422) do add_index "rpms", ["project_id", "arch_id"], :name => "index_rpms_on_project_id_and_arch_id" add_index "rpms", ["project_id"], :name => "index_rpms_on_project_id" + create_table "subscribes", :force => true do |t| + t.integer "subscribeable_id" + t.string "subscribeable_type" + t.integer "user_id" + t.datetime "created_at" + t.datetime "updated_at" + end + 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 "email", :default => "", :null => false + t.string "encrypted_password", :limit => 128, :default => "", :null => false + t.string "password_salt", :default => "", :null => false t.string "reset_password_token" - t.datetime "reset_password_sent_at" + t.string "remember_token" t.datetime "remember_created_at" t.datetime "created_at" t.datetime "updated_at" - t.string "uname" t.text "ssh_key" - t.integer "role_id" + t.string "uname" t.string "role" end diff --git a/spec/controllers/subscribes_controller_spec.rb b/spec/controllers/subscribes_controller_spec.rb new file mode 100644 index 000000000..e1c466cb3 --- /dev/null +++ b/spec/controllers/subscribes_controller_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe SubscribesController do + +end diff --git a/spec/factories/subscribes.rb b/spec/factories/subscribes.rb new file mode 100644 index 000000000..f0644816f --- /dev/null +++ b/spec/factories/subscribes.rb @@ -0,0 +1,6 @@ +# Read about factories at http://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :subscribe do + end +end \ No newline at end of file diff --git a/spec/helpers/subscribes_helper_spec.rb b/spec/helpers/subscribes_helper_spec.rb new file mode 100644 index 000000000..c72869cf9 --- /dev/null +++ b/spec/helpers/subscribes_helper_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +# Specs in this file have access to a helper object that includes +# the SubscribesHelper. For example: +# +# describe SubscribesHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# helper.concat_strings("this","that").should == "this that" +# end +# end +# end +describe SubscribesHelper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/subscribe_spec.rb b/spec/models/subscribe_spec.rb new file mode 100644 index 000000000..0df4fbb42 --- /dev/null +++ b/spec/models/subscribe_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe Subscribe do + pending "add some examples to (or delete) #{__FILE__}" +end From 8face9208ed4c4dcc8b87fe8dc501f3e7da2ac93 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Tue, 27 Dec 2011 16:35:31 +0400 Subject: [PATCH 21/40] [refs #54] Add fixes and locales to subscribes --- app/controllers/subscribes_controller.rb | 16 +++++++++++----- app/views/issues/show.html.haml | 4 ++-- config/locales/ru.yml | 4 ++++ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/controllers/subscribes_controller.rb b/app/controllers/subscribes_controller.rb index 4bb51be46..98efb5ccb 100644 --- a/app/controllers/subscribes_controller.rb +++ b/app/controllers/subscribes_controller.rb @@ -1,4 +1,8 @@ class SubscribesController < ApplicationController + before_filter :authenticate_user! + before_filter :set_instances + before_filter :set_subscribeable + def create @subscribe = @subscribeable.subscribes.build(:user_id => current_user.id) if @subscribe.save @@ -11,8 +15,7 @@ class SubscribesController < ApplicationController end def destroy - @subscribe = Subscribe.find(params[:id]) - @subscribe.destroy + @subscribeable.subscribes.where(:user_id => current_user.id)[0].destroy flash[:notice] = t("flash.subscribe.destroyed") redirect_to :back @@ -20,12 +23,15 @@ class SubscribesController < ApplicationController private - def find_subscribeable + def set_instances params.each do |name, value| if name =~ /(.+)_id$/ - return $1.classify.constantize.find(value) + instance_variable_set "@"+$1, $1.classify.constantize.find(value) end end - nil + end + + def set_subscribeable + @subscribeable = @issue if @issue end end diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index 69c2be747..943ea1a0a 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -25,9 +25,9 @@ = t('layout.issues.subscribe') \: - if @issue.subscribes.exists? :user_id => current_user.id - = link_to t('layout.issues.unsubscribe_btn'), unsubscribe_issue(@issue), :method => :post + = link_to t('layout.issues.unsubscribe_btn'), unsubscribe_issue_path(@issue), :method => :delete - else - = link_to t('layout.issues.subscribe_btn'), subscribe_issue(@issue), :method => :delete + = link_to t('layout.issues.subscribe_btn'), subscribe_issue_path(@issue), :method => :post§ %a{ :name => "comments" } .block diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 563ffdea7..20dd0c45d 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -352,6 +352,10 @@ ru: project_version_not_found: версия не найден flash: + subscribe: + saved: Вы подписаны на оповещения для этой задачи + destroyed: Подписка на оповещения для этой задачи убрана + exception_message: У Вас нет доступа к этой странице! downloads: From d3ba83135ee10393b26ed816bac0d3dd271e3cf1 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Tue, 27 Dec 2011 17:18:25 +0400 Subject: [PATCH 22/40] [refs #54] Add some locales and interface fixes for issues --- app/views/comments/_form.html.haml | 2 +- app/views/issues/_form.html.haml | 9 +++++---- app/views/issues/_list.html.haml | 3 +++ config/locales/ru.yml | 4 +++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/views/comments/_form.html.haml b/app/views/comments/_form.html.haml index ccca4cc1d..2b046013d 100644 --- a/app/views/comments/_form.html.haml +++ b/app/views/comments/_form.html.haml @@ -1,5 +1,5 @@ .group - = f.label :body, :class => :label + = f.label :body, t("activerecord.attributes.comment.body"), :class => :label = f.text_area :body, :class => 'text_field', :cols => 80 .group.navform.wat-cf diff --git a/app/views/issues/_form.html.haml b/app/views/issues/_form.html.haml index bb1bc72ed..985ff7913 100644 --- a/app/views/issues/_form.html.haml +++ b/app/views/issues/_form.html.haml @@ -8,12 +8,13 @@ = 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' +- unless @issue.new_record? + .group + = f.label :status, :class => :label + = f.select :status, Issue::STATUSES, :class => 'text_field' .group - = label_tag "", t("layout.issues.user_id"), :class => :label + = label_tag "", t("activerecord.attributes.issue.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' diff --git a/app/views/issues/_list.html.haml b/app/views/issues/_list.html.haml index dfb083664..41ec58acc 100644 --- a/app/views/issues/_list.html.haml +++ b/app/views/issues/_list.html.haml @@ -1,12 +1,15 @@ %table.table %tr %th.first= t("activerecord.attributes.issue.title") + %th.first= t("activerecord.attributes.issue.user") %th.first= t("activerecord.attributes.issue.status") %th.last   - @issues.each do |issue| %tr{:class => cycle("odd", "even")} %td = link_to issue.title, show_issue_path(@project, issue.serial_id) + %td + = link_to issue.user.uname, user_path(issue.user) %td = issue.status %td.last diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 20dd0c45d..242514ab5 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -101,6 +101,7 @@ ru: new: Новая задача list_header: Список confirm_delete: Вы уверены, что хотите удалить эту задачу? + edit_header: Редактирование задачи new_header: Новая задача statuses: open: Открытые @@ -497,7 +498,8 @@ ru: issue: title: Заголовок body: Содержание - user: Назначено + user: Назначена + user_id: Назначена project: Проект status: Статус From b48ffecc2807346b99c120b295dbfa16c9d18934 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Tue, 27 Dec 2011 17:52:48 +0400 Subject: [PATCH 23/40] [refs #54] Add new styles to comments list to issue page --- app/views/issues/show.html.haml | 21 ++++++++------------- app/views/layouts/_stylesheets.html.haml | 2 +- public/stylesheets/patches.css | 4 ++++ 3 files changed, 13 insertions(+), 14 deletions(-) create mode 100644 public/stylesheets/patches.css diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index 943ea1a0a..8b52735a2 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -30,25 +30,20 @@ = link_to t('layout.issues.subscribe_btn'), subscribe_issue_path(@issue), :method => :post§ %a{ :name => "comments" } -.block - .secondary-navigation - %ul.wat-cf +.block#block-lists .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   + %ul.list - @issue.comments.each do |comment| - %tr{:class => cycle("odd", "even")} - %td - = comment.user.uname - %td + %li + .left + = link_to comment.user.uname, user_path(comment.user.uname) + .item = comment.body - %td.last + %br + %br = 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.comments.confirm_delete") if can? :delete, comment diff --git a/app/views/layouts/_stylesheets.html.haml b/app/views/layouts/_stylesheets.html.haml index b2e58f3b8..e8c95cca9 100644 --- a/app/views/layouts/_stylesheets.html.haml +++ b/app/views/layouts/_stylesheets.html.haml @@ -1,6 +1,6 @@ -#= include_stylesheets :application = stylesheet_link_tag "web-app-theme/base.css", "web-app-theme/themes/default/style.css", "web-app-theme/override.css", "git/style.css" -= stylesheet_link_tag "jquery-ui-1.8.16.custom.css" += stylesheet_link_tag "jquery-ui-1.8.16.custom.css", "patches.css" = yield :stylesheets diff --git a/public/stylesheets/patches.css b/public/stylesheets/patches.css new file mode 100644 index 000000000..d6a3f9473 --- /dev/null +++ b/public/stylesheets/patches.css @@ -0,0 +1,4 @@ +ul.list li .item { + margin-left: 180px; +} + From ecbfed930054d232008fee4317f7f551f0d77686 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Tue, 27 Dec 2011 21:49:08 +0400 Subject: [PATCH 24/40] [refs #54] Fix user_id validation into issues --- app/controllers/issues_controller.rb | 2 +- app/models/issue.rb | 8 ++++++-- app/views/issues/_list.html.haml | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index a6a4cb734..031ca5ff4 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -27,7 +27,7 @@ class IssuesController < ApplicationController @issue = Issue.new(params[:issue]) @issue.user_id = @user_id @issue.project_id = @project.id - if @issue.save! + if @issue.save flash[:notice] = I18n.t("flash.issue.saved") redirect_to project_issues_path(@project) else diff --git a/app/models/issue.rb b/app/models/issue.rb index 586175b89..78b41a8b0 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -7,7 +7,7 @@ class Issue < ActiveRecord::Base has_many :comments, :as => :commentable has_many :subscribes, :as => :subscribeable - validates :title, :body, :project_id, :user_id, :presence => true + validates :title, :body, :project_id, :presence => true #attr_readonly :serial_id @@ -17,6 +17,10 @@ class Issue < ActiveRecord::Base after_create :deliver_issue_assign_notification after_update :deliver_issue_assign_notification + def assign_uname + user.uname if user + end + protected def set_serial_id @@ -52,7 +56,7 @@ class Issue < ActiveRecord::Base def collect_recipient_ids recipients = self.project.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) } - recipients = recipients | [self.user_id] + recipients = recipients | [self.user_id] if self.user_id recipients = recipients | [self.project.owner_id] if self.project.owner_type == 'User' recipients end diff --git a/app/views/issues/_list.html.haml b/app/views/issues/_list.html.haml index 41ec58acc..7415a221e 100644 --- a/app/views/issues/_list.html.haml +++ b/app/views/issues/_list.html.haml @@ -9,7 +9,7 @@ %td = link_to issue.title, show_issue_path(@project, issue.serial_id) %td - = link_to issue.user.uname, user_path(issue.user) + = link_to issue.user.uname, user_path(issue.user) if issue.user %td = issue.status %td.last From 5e3addd5d2dbbdc91470170d685c442205019af7 Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Wed, 28 Dec 2011 04:57:42 +0200 Subject: [PATCH 25/40] Great refactor for cancan abilities. Remove product relations. Fix git abilities. Fix and refactor specs. Refs #85. Refactor issues controller to avoid serial_id problem. Fix links and routes. Fix minor bugs and translations. Fix specs. Refs #54 --- app/controllers/build_lists_controller.rb | 2 +- app/controllers/comments_controller.rb | 6 +- app/controllers/issues_controller.rb | 32 +- app/controllers/platforms_controller.rb | 4 +- app/models/ability.rb | 273 ++++++------------ app/models/issue.rb | 4 + app/models/product.rb | 25 +- app/views/comments/_form.html.haml | 2 +- app/views/issues/_list.html.haml | 4 +- app/views/issues/show.html.haml | 2 +- config/locales/ru.yml | 9 +- config/routes.rb | 4 +- lib/grack/base.rb | 2 +- .../build_lists_controller_spec.rb | 10 +- spec/controllers/issues_controller_spec.rb | 18 +- spec/controllers/products_controller_spec.rb | 3 +- spec/controllers/projects_controller_spec.rb | 5 +- spec/models/cancan_spec.rb | 43 +-- .../shared_examples/projects_controller.rb | 2 +- 19 files changed, 176 insertions(+), 274 deletions(-) diff --git a/app/controllers/build_lists_controller.rb b/app/controllers/build_lists_controller.rb index cbd847933..44b1773f1 100644 --- a/app/controllers/build_lists_controller.rb +++ b/app/controllers/build_lists_controller.rb @@ -9,7 +9,7 @@ class BuildListsController < ApplicationController before_filter :find_build_list_by_bs, :only => [:publish_build, :status_build, :pre_build, :post_build] load_and_authorize_resource :project, :only => NESTED_ACTIONS - load_and_authorize_resource :through => :project, :only => NESTED_ACTIONS, :shallow => true + load_and_authorize_resource :build_list, :through => :project, :only => NESTED_ACTIONS, :shallow => true load_and_authorize_resource :except => CALLBACK_ACTIONS.concat(NESTED_ACTIONS) def index diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 93baf3b77..e20aa3af1 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -18,7 +18,7 @@ class CommentsController < ApplicationController flash[:notice] = I18n.t("flash.comment.saved") redirect_to :back else - flash[:error] = I18n.t("flash.comment.saved_error") + flash[:error] = I18n.t("flash.comment.save_error") render :action => 'new' end end @@ -32,9 +32,9 @@ class CommentsController < ApplicationController if @comment.update_attributes(params[:comment]) flash[:notice] = I18n.t("flash.comment.saved") #redirect_to :back - redirect_to show_issue_path(@comment.commentable.project, @comment.commentable.serial_id) + redirect_to [@comment.commentable.project, @comment.commentable] else - flash[:error] = I18n.t("flash.comment.saved_error") + flash[:error] = I18n.t("flash.comment.save_error") render :action => 'new' end end diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index a6a4cb734..3f1a6f4ec 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -1,15 +1,14 @@ class IssuesController < ApplicationController before_filter :authenticate_user! - before_filter :find_project, :except => [:destroy] - before_filter :find_and_authorize_by_serial_id, :only => [:show, :edit] - before_filter :set_issue_stub, :only => [:new, :create] + before_filter :find_project + before_filter :find_issue_by_serial_id, :only => [:show, :edit, :update, :destroy] + + load_and_authorize_resource :project + load_and_authorize_resource :issue, :through => :project, :find_by => :serial_id - load_and_authorize_resource :except => [:show, :edit, :index] - authorize_resource :project, :only => [:index] autocomplete :user, :uname def index - @issues = Issue.scoped @issues = @project.issues case params[:status] when 'open' @@ -20,6 +19,10 @@ class IssuesController < ApplicationController @issues = @issues.paginate :per_page => 10, :page => params[:page] end + def new + @issue = Issue.new(:project => @project) + end + def create @user_id = params[:user_id] @user_uname = params[:user_uname] @@ -27,11 +30,11 @@ class IssuesController < ApplicationController @issue = Issue.new(params[:issue]) @issue.user_id = @user_id @issue.project_id = @project.id - if @issue.save! + 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") + flash[:error] = I18n.t("flash.issue.save_error") render :action => :new end end @@ -47,9 +50,9 @@ class IssuesController < ApplicationController if @issue.update_attributes( params[:issue].merge({:user_id => @user_id}) ) flash[:notice] = I18n.t("flash.issue.saved") - redirect_to show_issue_path(@project, @issue.serial_id) + redirect_to [@project, @issue] else - flash[:error] = I18n.t("flash.issue.saved_error") + flash[:error] = I18n.t("flash.issue.save_error") render :action => :new end end @@ -67,12 +70,7 @@ class IssuesController < ApplicationController @project = Project.find(params[:project_id]) end - 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) + def find_issue_by_serial_id + @issue = @project.issues.find_by_serial_id!(params[:id]) end end diff --git a/app/controllers/platforms_controller.rb b/app/controllers/platforms_controller.rb index 3aed37c95..3ce47f9a7 100644 --- a/app/controllers/platforms_controller.rb +++ b/app/controllers/platforms_controller.rb @@ -64,7 +64,7 @@ class PlatformsController < ApplicationController flash[:notice] = I18n.t("flash.platform.saved") redirect_to @platform else - flash[:error] = I18n.t("flash.platform.saved_error") + flash[:error] = I18n.t("flash.platform.save_error") render :action => :new end end @@ -81,7 +81,7 @@ class PlatformsController < ApplicationController flash[:notice] = I18n.t("flash.platform.saved") redirect_to @platform else - flash[:error] = I18n.t("flash.platform.saved_error") + flash[:error] = I18n.t("flash.platform.save_error") render :action => :new end end diff --git a/app/models/ability.rb b/app/models/ability.rb index dfd6f105d..9a601f37e 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,178 +1,95 @@ +# If rules goes one by one CanCan joins them by 'OR' sql operator +# If rule has multiple conditions CanCan joins them by 'AND' sql operator +# WARNING: +# - put cannot rules _after_ can rules and not before! +# - beware inner joins. Use sub queries against them! + class Ability include CanCan::Ability def initialize(user) user ||= User.new # guest user (not logged in) + @user = user if user.admin? can :manage, :all else - #WARNING: - # - put cannot rules _after_ can rules and not before! - # - beware inner joins. Use sub queries against them! # Shared rights between guests and registered users can :forbidden, Platform - can :read, [Repository, Platform], :visibility => 'open' # TODO remove because auth callbacks skipped can :auto_build, Project - can [:status_build, :pre_build, :post_build, :circle_build, :new_bbdt], BuildList + can [:publish_build, :status_build, :pre_build, :post_build, :circle_build, :new_bbdt], BuildList - # Guest rights - if user.guest? + if user.guest? # Guest rights can :create, User - # Registered user rights - else - can [:read, :platforms], Category + else # Registered user rights + can [:show, :autocomplete_user_uname], User + + can [:read, :create], Group + can [:update, :manage_members], Group do |group| + group.objects.exists?(:object_type => 'User', :object_id => user.id, :role => 'admin') # or group.owner_id = user.id + end + can :destroy, Group, :owner_id => user.id + + can :create, Project + can :read, Project, :visibility => 'open' + can :read, Project, :owner_type => 'User', :owner_id => user.id + can :read, Project, :owner_type => 'Group', :owner_id => user.group_ids + can(:read, Project, read_relations_for('projects')) {|project| local_reader? project} + can(:write, Project) {|project| local_writer? project} # for grack + can([:update, :manage_collaborators], Project) {|project| local_admin? project} + can(:fork, Project) {|project| can? :read, project} + can(:destroy, Project) {|project| owner? project} can :create, AutoBuildList can [:index, :destroy], AutoBuildList, :project_id => user.own_project_ids - # If rules goes one by one CanCan joins them by 'OR' sql operator - can :read, Project, :visibility => 'open' - can :read, Group - can :read, User - cannot :index, User - can :manage_collaborators, Project do |project| - project.relations.exists? :object_id => user.id, :object_type => 'User', :role => 'admin' - end - can :manage_members, Group do |group| - group.objects.exists? :object_id => user.id, :object_type => 'User', :role => 'admin' - end - # Put here model names which objects can user create - can :create, Project - can :create, Group - can :publish, BuildList do |build_list| - build_list.can_publish? && build_list.project.relations.exists?(:object_type => 'User', :object_id => user.id) - end - can [:index, :read], BuildList, ["build_lists.project_id IN (SELECT id FROM projects WHERE visibility = ?)", 'open'] do |build_list| - build_list.project.public? - end - can [:index, :read], BuildList, build_lists_in_relations_with(:object_type => 'User', :object_id => user.id) do |build_list| - build_list.project.relations.exists?(:object_type => 'User', :object_id => user.id) - end + can :read, BuildList, :project => {:visibility => 'open'} + can :read, BuildList, :project => {:owner_type => 'User', :owner_id => user.id} + can :read, BuildList, :project => {:owner_type => 'Group', :owner_id => user.group_ids} + can(:read, BuildList, read_relations_for('build_lists', 'projects')) {|build_list| can? :read, build_list.project} + can(:create, BuildList) {|build_list| can? :write, build_list.project} + can(:publish, BuildList) {|build_list| build_list.can_publish? && can?(:write, build_list.project)} + + can :read, Platform, :visibility => 'open' + can :read, Platform, :owner_type => 'User', :owner_id => user.id + can :read, Platform, :owner_type => 'Group', :owner_id => user.group_ids + can(:read, Platform, read_relations_for('platforms')) {|platform| local_reader? platform} + can(:update, Platform) {|platform| local_admin? platform} + can([:freeze, :unfreeze, :destroy], Platform) {|platform| owner? platform} + can :autocomplete_user_uname, Platform + + # TODO delegate to platform? + can :read, Repository, :visibility => 'open' + can :read, Repository, :owner_type => 'User', :owner_id => user.id + can :read, Repository, :owner_type => 'Group', :owner_id => user.group_ids + can(:read, Repository, read_relations_for('repositories')) {|repository| local_reader? repository} + can(:create, Repository) {|repository| local_admin? repository.platform} + can([:update, :add_project, :remove_project], Repository) {|repository| local_admin? repository} + can([:change_visibility, :settings, :destroy], Repository) {|repository| owner? repository} + + can :read, Product, :platform => {:owner_type => 'User', :owner_id => user.id} + can :read, Product, :platform => {:owner_type => 'Group', :owner_id => user.group_ids} + can(:manage, Product, read_relations_for('products', 'platforms')) {|product| local_admin? product.platform} + + can [:read, :platforms], Category can [:read, :create], PrivateUser, :platform => {:owner_type => 'User', :owner_id => user.id} - - # If rule has multiple conditions CanCan joins them by 'AND' sql operator - can [:read, :update, :destroy], Project, :owner_type => 'User', :owner_id => user.id - #can :read, Project, :relations => {:role => 'reader'} - can :read, Project, projects_in_relations_with(:role => 'reader', :object_type => 'User', :object_id => user.id) do |project| - #The can? and cannot? call cannot be used with a raw sql 'can' definition. - project.relations.exists?(:role => 'reader', :object_type => 'User', :object_id => user.id) - end - #can [:update], Project, :relations => {:role => 'writer'} - can [:read, :update], Project, projects_in_relations_with(:role => ['writer', 'admin'], :object_type => 'User', :object_id => user.id) do |project| - project.relations.exists?(:role => ['writer', 'admin'], :object_type => 'User', :object_id => user.id) - end - - can [:read, :update, :destroy], Product, products_in_relations_with(:role => ['writer', 'admin'], :object_type => 'User', :object_id => user.id) do |product| - product.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) - end - # Small CanCan hack by Product.new(:platform_id => ...) - can [:new, :create], Product do |product| - product.platform.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) - end - - 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) - end - #can :read, Platform, :members => {:id => user.id} - can :read, Platform, platforms_in_relations_with(:role => 'reader', :object_type => 'User', :object_id => user.id) do |platform| - platform.relations.exists?(:role => 'reader', :object_type => 'User', :object_id => user.id) - end - - can [:manage, :add_project, :remove_project, :change_visibility, :settings], Repository, :owner_type => 'User', :owner_id => user.id - can :manage, Repository, repositories_in_relations_with(:role => 'admin', :object_type => 'User', :object_id => user.id) do |repository| - repository.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) - end - #can :read, Repository, :members => {:id => user.id} - can :read, Repository, repositories_in_relations_with(:role => 'reader', :object_type => 'User', :object_id => user.id) do |repository| - repository.relations.exists?(:role => 'reader', :object_type => 'User', :object_id => user.id) - end - # Small CanCan hack by Repository.new(:platform_id => ...) - can [:new, :create], Repository do |repository| - repository.platform.relations.exists?(:role => 'admin', :object_type => 'User', :object_id => user.id) - end - - can [:read, :index], Issue do |issue| - issue.status == 'open' - end - #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| - 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, :read, :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| - build_list.can_publish? && build_list.project.relations.exists?(:object_type => 'Group', :object_id => user.group_ids) - end - can [:index, :read], BuildList, build_lists_in_relations_with(:object_type => 'Group', :object_id => user.group_ids) do |build_list| - build_list.project.relations.exists?(:object_type => 'Group', :object_id => user.group_ids) - end - can :manage_collaborators, Project, projects_in_relations_with(:role => 'admin', :object_type => 'Group', :object_id => user.group_ids) do |project| - project.relations.exists? :object_id => user.group_ids, :object_type => 'Group', :role => 'admin' - end + # can :read, Issue, :status => 'open' + can :read, Issue, :project => {:visibility => 'open'} + can :read, Issue, :project => {:owner_type => 'User', :owner_id => user.id} + can :read, Issue, :project => {:owner_type => 'Group', :owner_id => user.group_ids} + can(:read, Issue, read_relations_for('issues', 'projects')) {|issue| can? :read, issue.project rescue nil} + can(:create, Issue) {|issue| can? :write, issue.project} + can([:update, :destroy], Issue) {|issue| issue.user_id == user.id or local_admin?(issue.project)} + cannot :manage, Issue, :project => {:has_issues => false} # switch off issues - can [:read, :update, :destroy], Project, :owner_type => 'Group', :owner_id => user.group_ids - #can :read, Project, :relations => {:role => 'reader', :object_type => 'Group', :object_id => user.group_ids} - can :read, Project, projects_in_relations_with(:role => 'reader', :object_type => 'Group', :object_id => user.group_ids) do |project| - project.relations.exists?(:role => 'reader', :object_type => 'Group', :object_id => user.group_ids) - end - #can [:update], Project, :relations => {:role => 'writer', :object_type => 'Group', :object_id => user.group_ids} - can [:read, :update], 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| - platform.relations.exists?(:role => 'reader', :object_type => 'Group', :object_id => user.group_ids) - end - - can [:manage, :add_project, :remove_project], Repository, :owner_type => 'Group', :owner_id => user.group_ids - #can :read, Platform, :groups => {:id => user.group_ids} - can :read, Repository, repositories_in_relations_with(:role => 'reader', :object_type => 'Group', :object_id => user.group_ids) do |repository| - repository.relations.exists?(:role => 'reader', :object_type => 'Group', :object_id => user.group_ids) - end - - can(:fork, Project) {|p| can? :read, p} - can(:create, BuildList) {|bl| can? :update, bl.project} - - # Things that can not do simple user - cannot :create, [Platform, User] + can(:create, Comment) {|comment| can? :read, comment.commentable.project} + can(:update, Comment) {|comment| can? :update, comment.user_id == user.id or local_admin?(comment.commentable.project)} + cannot :manage, Comment, :commentable => {:project => {:has_issues => false}} # switch off issues end end @@ -183,42 +100,34 @@ class Ability cannot :destroy, Issue end - # Sub query for platforms, projects relations - # TODO: Replace table names list by method_missing way - %w[platforms projects products repositories groups].each do |table_name| - define_method table_name + "_in_relations_with" do |opts| - query = "#{ table_name }.id IN (SELECT target_id FROM relations WHERE relations.target_type = '#{ table_name.singularize.camelize }'" - opts.each do |key, value| - query = query + " AND relations.#{ key } #{ value.class == Array ? 'IN (?)' : '= ?' } " - end - query = query + ")" - - return opts.values.unshift query - end + # TODO group_ids ?? + def read_relations_for(table, parent = nil) + key = parent ? "#{parent.singularize}_id" : 'id' + parent ||= table + ["#{table}.#{key} IN ( + SELECT target_id FROM relations WHERE relations.target_type = ? AND + (relations.object_type = 'User' AND relations.object_id = ? OR + relations.object_type = 'Group' AND relations.object_id IN (?)))", parent.classify, @user, @user.group_ids] 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 + def relation_exists_for(object, roles) + object.relations.exists?(:object_id => @user.id, :object_type => 'User', :role => roles) or + object.relations.exists?(:object_id => @user.group_ids, :object_type => 'Group', :role => roles) end - def build_lists_in_relations_with(opts) - query = "build_lists.project_id IN (SELECT target_id FROM relations WHERE relations.target_type = 'Project'" - opts.each do |key, value| - query = query + " AND relations.#{ key } #{ value.class == Array ? 'IN (?)' : '= ?' } " - end - query = query + ")" - - return opts.values.unshift query + def local_reader?(object) + relation_exists_for(object, %w{reader writer admin}) or owner?(object) 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]] - #end + def local_writer?(object) + relation_exists_for(object, %w{writer admin}) or owner?(object) + end + + def local_admin?(object) + relation_exists_for(object, 'admin') or owner?(object) + end + + def owner?(object) + object.owner == @user or @user.own_groups.include?(object.owner) + end end diff --git a/app/models/issue.rb b/app/models/issue.rb index ad6f57468..f320f3309 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -12,6 +12,10 @@ class Issue < ActiveRecord::Base after_create :set_serial_id + def to_param + serial_id.to_s + end + protected def set_serial_id diff --git a/app/models/product.rb b/app/models/product.rb index 637fdee90..36337f4f9 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -3,11 +3,9 @@ class Product < ActiveRecord::Base belongs_to :platform has_many :product_build_lists, :dependent => :destroy - has_many :relations, :as => :target, :dependent => :destroy after_validation :merge_tar_errors before_save :destroy_tar? - after_create :add_admin_relations has_attached_file :tar @@ -69,21 +67,12 @@ class Product < ActiveRecord::Base protected - def destroy_tar? - self.tar.clear if @delete_tar == "1" - end - - def merge_tar_errors - errors[:tar] += errors[:tar_content_type] - errors[:tar_content_type] = [] - end - - def add_admin_relations - platform.relations.where(:role => 'admin').each do |rel| - r = relations.build(:role => 'admin', :object_type => rel.object_type) - r.object_id = rel.object_id - r.save - end - end + def destroy_tar? + self.tar.clear if @delete_tar == "1" + end + def merge_tar_errors + errors[:tar] += errors[:tar_content_type] + errors[:tar_content_type] = [] + end end diff --git a/app/views/comments/_form.html.haml b/app/views/comments/_form.html.haml index ccca4cc1d..eb5acaf4d 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"), show_issue_path(@issue.project, @issue.serial_id), :class => "text_button_padding link_button" + = link_to t("layout.cancel"), [@issue.project, @issue], :class => "text_button_padding link_button" diff --git a/app/views/issues/_list.html.haml b/app/views/issues/_list.html.haml index dfb083664..98d5d5448 100644 --- a/app/views/issues/_list.html.haml +++ b/app/views/issues/_list.html.haml @@ -6,10 +6,10 @@ - @issues.each do |issue| %tr{:class => cycle("odd", "even")} %td - = link_to issue.title, show_issue_path(@project, issue.serial_id) + = link_to issue.title, [@project, issue] %td = issue.status %td.last - = link_to t("layout.show"), show_issue_path(@project, issue.serial_id) + = link_to t("layout.show"), [@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/show.html.haml b/app/views/issues/show.html.haml index 51c080e4a..a68e58fea 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.serial_id) if can? :edit, @issue + %li= link_to t("layout.issues.edit"), edit_project_issue_path(@project, @issue) if can? :edit, @issue .content .inner %p diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 97eea839c..892d6ff7c 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -382,10 +382,13 @@ ru: comment: saved: Комментарий успешно сохранен + save_error: Ошибка сохранения комментария destroyed: Комментарий удален issue: saved: Задача успешно сохранена + save_error: Не удалось сохранить задачу + destroyed: Задача успешно удалена project: saved: Проект успешно сохранен @@ -406,10 +409,6 @@ ru: destroyed: Группа успешно удалена user_uname_exists: Пользователь с таким именем уже зарегестрирован - role: - saved: Роль успешно сохранена - save_error: Ошибка сохранения роли - repository: saved: Репозиторий успешно добавлен save_error: Не удалось добавить репозиторий @@ -427,7 +426,7 @@ ru: platform: saved: Платформа успешно добавлена - saved_error: Не удалось создать платформу + save_error: Не удалось создать платформу freezed: Платформа успешно заморожена freeze_error: Не удалось заморозить платформу, попробуйте еще раз unfreezed: Платформа успешно разморожена diff --git a/config/routes.rb b/config/routes.rb index 4a0227e63..dcea79076 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -78,10 +78,8 @@ Rosa::Application.routes.draw do resources :categories, :only => [:index, :show] end - 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 :issues do resources :comments, :only => [:edit, :create, :update, :destroy] end resource :repo, :controller => "git/repositories", :only => [:show] diff --git a/lib/grack/base.rb b/lib/grack/base.rb index 1d77f2af4..e2ee2a121 100644 --- a/lib/grack/base.rb +++ b/lib/grack/base.rb @@ -28,7 +28,7 @@ module Grack end def action - write? ? :update : :read + write? ? :write : :read end def project diff --git a/spec/controllers/build_lists_controller_spec.rb b/spec/controllers/build_lists_controller_spec.rb index c05d24e50..bfedd23a2 100644 --- a/spec/controllers/build_lists_controller_spec.rb +++ b/spec/controllers/build_lists_controller_spec.rb @@ -94,9 +94,8 @@ describe BuildListsController do @build_list1 = Factory(:build_list_core) @build_list2 = Factory(:build_list_core, :project => Factory(:project, :visibility => 'hidden')) @build_list3 = Factory(:build_list_core, :project => Factory(:project, :owner => @user, :visibility => 'hidden')) - b = Factory(:build_list_core, :project => Factory(:project, :visibility => 'hidden')) - b.project.relations.create :role => 'reader', :object_id => @user.id, :object_type => 'User' - @build_list4 = b + @build_list4 = Factory(:build_list_core, :project => Factory(:project, :visibility => 'hidden')) + @build_list4.project.relations.create :role => 'reader', :object_id => @user.id, :object_type => 'User' end it 'should be able to perform index action' do @@ -180,9 +179,8 @@ describe BuildListsController do @build_list1 = Factory(:build_list_core) @build_list2 = Factory(:build_list_core, :project => Factory(:project, :visibility => 'hidden')) @build_list3 = Factory(:build_list_core, :project => Factory(:project, :owner => @group, :visibility => 'hidden')) - b = Factory(:build_list_core, :project => Factory(:project, :visibility => 'hidden')) - b.project.relations.create :role => 'reader', :object_id => @group.id, :object_type => 'Group' - @build_list4 = b + @build_list4 = Factory(:build_list_core, :project => Factory(:project, :visibility => 'hidden')) + @build_list4.project.relations.create :role => 'reader', :object_id => @group.id, :object_type => 'Group' end it 'should be able to perform index action' do diff --git a/spec/controllers/issues_controller_spec.rb b/spec/controllers/issues_controller_spec.rb index 2d9ba9a46..37aad58cf 100644 --- a/spec/controllers/issues_controller_spec.rb +++ b/spec/controllers/issues_controller_spec.rb @@ -8,7 +8,7 @@ shared_examples_for 'issue user with project reader rights' do end it 'should be able to perform show action' do - get :show, :project_id => @project.id, :serial_id => @issue.serial_id + get :show, :project_id => @project.id, :id => @issue.serial_id response.should render_template(:show) end end @@ -26,36 +26,36 @@ 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)) + put :update, {:id => @issue.serial_id}.merge(@update_params) + response.should redirect_to([@project, @issue]) end it 'should update issue title' do - put :update, {:id => @issue.id}.merge(@update_params) + put :update, {:id => @issue.serial_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) + put :update, {:id => @issue.serial_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) + put :update, {:id => @issue.serial_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 + delete :destroy, :id => @issue.serial_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) + lambda{ delete :destroy, :id => @issue.serial_id, :project_id => @project.id }.should change{ Issue.count }.by(0) end end @@ -67,7 +67,7 @@ shared_examples_for 'project with issues turned off' do 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 + get :show, :project_id => @project_with_turned_off_issues.id, :id => @turned_of_issue.serial_id response.should redirect_to(forbidden_path) end end diff --git a/spec/controllers/products_controller_spec.rb b/spec/controllers/products_controller_spec.rb index 7b6e4f4f6..826568374 100644 --- a/spec/controllers/products_controller_spec.rb +++ b/spec/controllers/products_controller_spec.rb @@ -61,8 +61,7 @@ describe ProductsController do before(:each) do @user = Factory(:user) set_session_for(@user) - r = @product.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') - r = @platform.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + @platform.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') end it 'should be able to perform create action' do diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index c17c09954..29e6aff09 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -28,7 +28,7 @@ describe ProjectsController do set_session_for(@admin) end - it_should_behave_like 'projects user with writer rights' + it_should_behave_like 'projects user with admin rights' it_should_behave_like 'projects user with reader rights' it 'should be able to perform create action' do @@ -49,7 +49,7 @@ describe ProjectsController do @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') end - it_should_behave_like 'projects user with writer rights' + it_should_behave_like 'projects user with admin rights' it_should_behave_like 'user with rights to view projects' it 'should be able to perform destroy action' do @@ -90,7 +90,6 @@ describe ProjectsController do @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'writer') end - it_should_behave_like 'projects user with writer rights' it_should_behave_like 'projects user with reader rights' end diff --git a/spec/models/cancan_spec.rb b/spec/models/cancan_spec.rb index 927a38447..e245c19ff 100644 --- a/spec/models/cancan_spec.rb +++ b/spec/models/cancan_spec.rb @@ -50,19 +50,19 @@ describe CanCan do guest_create end - it 'should be able to read open platform' do - @ability.should be_able_to(:read, open_platform) - end + it 'should not be able to read open platform' do + @ability.should_not be_able_to(:read, open_platform) + end - it 'should not be able to read hidden platform' do - @ability.should_not be_able_to(:read, hidden_platform) - end + it 'should not be able to read hidden platform' do + @ability.should_not be_able_to(:read, hidden_platform) + end - it 'should be able to auto build projects' do - @ability.should be_able_to(:auto_build, Project) - end + it 'should be able to auto build projects' do + @ability.should be_able_to(:auto_build, Project) + end - [:status_build, :pre_build, :post_build, :circle_build, :new_bbdt].each do |action| + [:publish_build, :status_build, :pre_build, :post_build, :circle_build, :new_bbdt].each do |action| it "should be able to #{ action } build list" do @ability.should be_able_to(action, BuildList) end @@ -78,11 +78,13 @@ describe CanCan do user_create end - [Platform, User, Repository].each do |model_name| - it "should not be able to create #{ model_name.to_s }" do + [Platform, Repository].each do |model_name| + it "should not be able to read #{model_name}" do @ability.should be_able_to(:read, model_name) end end + + it { @ability.should be_able_to(:show, User) } it "shoud be able to read another user object" do admin_create @@ -225,8 +227,10 @@ describe CanCan do @platform.update_attribute(:owner, @user) end - it 'should be able to manage platform' do - @ability.should be_able_to(:manage, @platform) + [:read, :update, :destroy, :freeze, :unfreeze].each do |action| + it "should be able to #{action} platform" do + @ability.should be_able_to(action, @platform) + end end end @@ -251,11 +255,16 @@ describe CanCan do @repository.update_attribute(:owner, @user) end - [:manage, :add_project, :remove_project, :change_visibility, :settings].each do |action| - it "should be able to #{ action } repository" do + [:read, :update, :destroy, :add_project, :remove_project, :change_visibility, :settings].each do |action| + it "should be able to #{action} repository" do @ability.should be_able_to(action, @repository) end end + + it do + @repository.platform.update_attribute(:owner, @user) + @ability.should be_able_to(:create, @repository) + end end context 'with read rights' do @@ -272,7 +281,7 @@ describe CanCan do context 'build list relations' do before(:each) do @project = Factory(:project) - @project.relations.create!(:object_id => @user.id, :object_type => 'User', :role => 'reader') + @project.relations.create!(:object_id => @user.id, :object_type => 'User', :role => 'writer') @build_list = Factory(:build_list, :project => @project) end diff --git a/spec/support/shared_examples/projects_controller.rb b/spec/support/shared_examples/projects_controller.rb index 37cd28b28..0dff8f8d2 100644 --- a/spec/support/shared_examples/projects_controller.rb +++ b/spec/support/shared_examples/projects_controller.rb @@ -7,7 +7,7 @@ shared_examples_for 'projects user with reader rights' do end end -shared_examples_for 'projects user with writer rights' do +shared_examples_for 'projects user with admin rights' do it 'should be able to perform update action' do put :update, {:id => @project.id}.merge(@update_params) response.should redirect_to(project_path(@project)) From 1c263c6e0a4978655e6f956adf7d8a18022e66d5 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Wed, 28 Dec 2011 14:47:54 +0400 Subject: [PATCH 26/40] [refs #54] Notification DJ support. Routes fixes. --- app/controllers/subscribes_controller.rb | 2 ++ app/models/issue.rb | 5 +++-- app/views/issues/show.html.haml | 4 ++-- app/views/user_mailer/issue_assign_notification.haml | 2 +- app/views/user_mailer/new_comment_notification.haml | 2 +- app/views/user_mailer/new_issue_notification.haml | 2 +- config/routes.rb | 4 ++-- 7 files changed, 12 insertions(+), 9 deletions(-) diff --git a/app/controllers/subscribes_controller.rb b/app/controllers/subscribes_controller.rb index 98efb5ccb..d075950f0 100644 --- a/app/controllers/subscribes_controller.rb +++ b/app/controllers/subscribes_controller.rb @@ -23,6 +23,7 @@ class SubscribesController < ApplicationController private + # Sets instances for parent resources (@issue, etc.) def set_instances params.each do |name, value| if name =~ /(.+)_id$/ @@ -31,6 +32,7 @@ class SubscribesController < ApplicationController end end + # Sets current parent resource by setted instance def set_subscribeable @subscribeable = @issue if @issue end diff --git a/app/models/issue.rb b/app/models/issue.rb index 48e9092d5..34401d4be 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -42,12 +42,13 @@ class Issue < ActiveRecord::Base recipients = collect_recipient_ids recipients.each do |recipient_id| recipient = User.find(recipient_id) - UserMailer.new_issue_notification(self, recipient).deliver + UserMailer.delay.new_issue_notification(self, recipient)#.deliver end end def deliver_issue_assign_notification - UserMailer.issue_assign_notification(self, self.user).deliver if self.user_id_was != self.user_id + #UserMailer.delay.issue_assign_notification(self, self.user).deliver if self.user_id_was != self.user_id + UserMailer.delay.issue_assign_notification(self, self.user) if self.user_id_was != self.user_id end def subscribe_users diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index 847afd8b9..202d891f4 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -25,9 +25,9 @@ = t('layout.issues.subscribe') \: - if @issue.subscribes.exists? :user_id => current_user.id - = link_to t('layout.issues.unsubscribe_btn'), unsubscribe_issue_path(@issue), :method => :delete + = link_to t('layout.issues.unsubscribe_btn'), unsubscribe_issue_path(@issue.id), :method => :delete - else - = link_to t('layout.issues.subscribe_btn'), subscribe_issue_path(@issue), :method => :post§ + = link_to t('layout.issues.subscribe_btn'), subscribe_issue_path(@issue.id), :method => :post§ %a{ :name => "comments" } .block#block-lists diff --git a/app/views/user_mailer/issue_assign_notification.haml b/app/views/user_mailer/issue_assign_notification.haml index 72e99e7df..a6615d3eb 100644 --- a/app/views/user_mailer/issue_assign_notification.haml +++ b/app/views/user_mailer/issue_assign_notification.haml @@ -1,7 +1,7 @@ %p== Здравствуйте, #{@user.name}. -%p Вам была назначена задача #{ link_to @issue.title, show_issue_path(@issue.project, @issue.serial_id) } +%p Вам была назначена задача #{ link_to @issue.title, [@issue.project, @issue] } %p== Команда поддержки «ROSA Build System» diff --git a/app/views/user_mailer/new_comment_notification.haml b/app/views/user_mailer/new_comment_notification.haml index 0e57ff415..88692f64e 100644 --- a/app/views/user_mailer/new_comment_notification.haml +++ b/app/views/user_mailer/new_comment_notification.haml @@ -1,7 +1,7 @@ %p== Здравствуйте, #{@user.name}. -%p К задаче #{ link_to @comment.commentable.title, show_issue_path(@comment.commentable.project, @comment.commentable.serial_id) } был добавлен новый комментарий. +%p К задаче #{ link_to @comment.commentable.title, [@comment.commentable.project, @comment.commentable] } был добавлен новый комментарий. %p== Команда поддержки «ROSA Build System» diff --git a/app/views/user_mailer/new_issue_notification.haml b/app/views/user_mailer/new_issue_notification.haml index e98f32f45..3a2604cfb 100644 --- a/app/views/user_mailer/new_issue_notification.haml +++ b/app/views/user_mailer/new_issue_notification.haml @@ -1,7 +1,7 @@ %p== Здравствуйте, #{@user.name}. -%p К проекту #{ link_to @issue.project.name, project_path(@issue.project) } была добавлена задача #{ link_to @issue.title, show_issue_path(@issue.project, @issue.serial_id) } +%p К проекту #{ link_to @issue.project.name, project_path(@issue.project) } была добавлена задача #{ link_to @issue.title, [@issue.project, @issue] } %p== Команда поддержки «ROSA Build System» diff --git a/config/routes.rb b/config/routes.rb index d076405a2..db44731a0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -78,8 +78,8 @@ Rosa::Application.routes.draw do resources :categories, :only => [:index, :show] end - #match "issues/:issue_id/subscribe" => 'subscribes#create', :as => :subscribe_issue, :via => :post - #match "issues/:issue_id/unsubscribe" => 'subscribes#destroy', :as => :unsubscribe_issue, :via => :delete + match "issues/:issue_id/subscribe" => 'subscribes#create', :as => :subscribe_issue, :via => :post + match "issues/:issue_id/unsubscribe" => 'subscribes#destroy', :as => :unsubscribe_issue, :via => :delete #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 From fa8656d1f53d5d96048fc5c6ede82784356ca7e0 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Wed, 28 Dec 2011 17:54:45 +0400 Subject: [PATCH 27/40] [refs #54] Refactor subscires routes and authorize --- app/controllers/subscribes_controller.rb | 36 ++++++++++++++---------- app/models/ability.rb | 8 ++++++ app/views/issues/show.html.haml | 4 +-- config/routes.rb | 5 ++-- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/app/controllers/subscribes_controller.rb b/app/controllers/subscribes_controller.rb index d075950f0..e08d48db9 100644 --- a/app/controllers/subscribes_controller.rb +++ b/app/controllers/subscribes_controller.rb @@ -1,10 +1,15 @@ class SubscribesController < ApplicationController before_filter :authenticate_user! - before_filter :set_instances - before_filter :set_subscribeable + #before_filter :set_instances + #before_filter :set_subscribeable + + load_and_authorize_resource :project + load_and_authorize_resource :issue, :through => :project, :find_by => :serial_id + load_and_authorize_resource :subscribe, :through => :issue, :find_by => :user_id def create - @subscribe = @subscribeable.subscribes.build(:user_id => current_user.id) + #@subscribe = @subscribeable.subscribes.build(:user_id => current_user.id) + @subscribe = @issue.subscribes.build(:user_id => current_user.id) if @subscribe.save flash[:notice] = I18n.t("flash.subscribe.saved") redirect_to :back @@ -15,25 +20,26 @@ class SubscribesController < ApplicationController end def destroy - @subscribeable.subscribes.where(:user_id => current_user.id)[0].destroy + #@subscribeable.subscribes.where(:user_id => current_user.id)[0].destroy + @subscribe.destroy flash[:notice] = t("flash.subscribe.destroyed") redirect_to :back end - private + #private # Sets instances for parent resources (@issue, etc.) - def set_instances - params.each do |name, value| - if name =~ /(.+)_id$/ - instance_variable_set "@"+$1, $1.classify.constantize.find(value) - end - end - end + #def set_instances + # params.each do |name, value| + # if name =~ /(.+)_id$/ + # instance_variable_set "@"+$1, $1.classify.constantize.find(value) + # end + # end + #end # Sets current parent resource by setted instance - def set_subscribeable - @subscribeable = @issue if @issue - end + #def set_subscribeable + # @subscribeable = @issue if @issue + #end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 9a601f37e..ebff5da1b 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -90,6 +90,14 @@ class Ability can(:create, Comment) {|comment| can? :read, comment.commentable.project} can(:update, Comment) {|comment| can? :update, comment.user_id == user.id or local_admin?(comment.commentable.project)} cannot :manage, Comment, :commentable => {:project => {:has_issues => false}} # switch off issues + + can :create, Subscribe do |subscribe| + !subscribe.subscribeable.subscribes.exists?(:user_id => user.id) + end + can :destroy, Subscribe do |subscribe| + subscribe.subscribeable.subscribes.exists?(:user_id => user.id) && user.id == subscribe.user_id + end + #can [:create, :delete], Subscribe end end diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index 202d891f4..3c0bf000f 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -25,9 +25,9 @@ = t('layout.issues.subscribe') \: - if @issue.subscribes.exists? :user_id => current_user.id - = link_to t('layout.issues.unsubscribe_btn'), unsubscribe_issue_path(@issue.id), :method => :delete + = link_to t('layout.issues.unsubscribe_btn'), project_issue_subscribe_path(@project, @issue, current_user.id), :method => :delete - else - = link_to t('layout.issues.subscribe_btn'), subscribe_issue_path(@issue.id), :method => :post§ + = link_to t('layout.issues.subscribe_btn'), project_issue_subscribes_path(@project, @issue), :method => :post§ %a{ :name => "comments" } .block#block-lists diff --git a/config/routes.rb b/config/routes.rb index db44731a0..c6b729fd7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -78,8 +78,8 @@ Rosa::Application.routes.draw do resources :categories, :only => [:index, :show] end - match "issues/:issue_id/subscribe" => 'subscribes#create', :as => :subscribe_issue, :via => :post - match "issues/:issue_id/unsubscribe" => 'subscribes#destroy', :as => :unsubscribe_issue, :via => :delete + #match "issues/:issue_id/subscribe" => 'subscribes#create', :as => :subscribe_issue, :via => :post + #match "issues/:issue_id/unsubscribe" => 'subscribes#destroy', :as => :unsubscribe_issue, :via => :delete #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 @@ -87,6 +87,7 @@ Rosa::Application.routes.draw do resources :projects do resources :issues do resources :comments, :only => [:edit, :create, :update, :destroy] + resources :subscribes, :only => [:create, :destroy] end resource :repo, :controller => "git/repositories", :only => [:show] resources :build_lists, :only => [:index, :new, :create] From 49ec1ebe984f6b1fbbc017abb5c76ef7d556e544 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Wed, 28 Dec 2011 18:24:24 +0400 Subject: [PATCH 28/40] [refs #54] Remove unused comments --- app/controllers/subscribes_controller.rb | 20 -------------------- app/views/issues/show.html.haml | 2 +- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/app/controllers/subscribes_controller.rb b/app/controllers/subscribes_controller.rb index e08d48db9..e9ff43abf 100644 --- a/app/controllers/subscribes_controller.rb +++ b/app/controllers/subscribes_controller.rb @@ -1,14 +1,11 @@ class SubscribesController < ApplicationController before_filter :authenticate_user! - #before_filter :set_instances - #before_filter :set_subscribeable load_and_authorize_resource :project load_and_authorize_resource :issue, :through => :project, :find_by => :serial_id load_and_authorize_resource :subscribe, :through => :issue, :find_by => :user_id def create - #@subscribe = @subscribeable.subscribes.build(:user_id => current_user.id) @subscribe = @issue.subscribes.build(:user_id => current_user.id) if @subscribe.save flash[:notice] = I18n.t("flash.subscribe.saved") @@ -20,26 +17,9 @@ class SubscribesController < ApplicationController end def destroy - #@subscribeable.subscribes.where(:user_id => current_user.id)[0].destroy @subscribe.destroy flash[:notice] = t("flash.subscribe.destroyed") redirect_to :back end - - #private - - # Sets instances for parent resources (@issue, etc.) - #def set_instances - # params.each do |name, value| - # if name =~ /(.+)_id$/ - # instance_variable_set "@"+$1, $1.classify.constantize.find(value) - # end - # end - #end - - # Sets current parent resource by setted instance - #def set_subscribeable - # @subscribeable = @issue if @issue - #end end diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index 3c0bf000f..073de4c2a 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -27,7 +27,7 @@ - if @issue.subscribes.exists? :user_id => current_user.id = link_to t('layout.issues.unsubscribe_btn'), project_issue_subscribe_path(@project, @issue, current_user.id), :method => :delete - else - = link_to t('layout.issues.subscribe_btn'), project_issue_subscribes_path(@project, @issue), :method => :post§ + = link_to t('layout.issues.subscribe_btn'), project_issue_subscribes_path(@project, @issue), :method => :post %a{ :name => "comments" } .block#block-lists From acb970686cf5593a60a021b35e4de241b2f17d33 Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Thu, 29 Dec 2011 04:37:34 +0200 Subject: [PATCH 29/40] Apply auto_publish for build_lists. Apply publish build_list error status. Remove circle build info. Display auto_publish info. Fix bugs. Refactor. Apply specs for all build_lists core callbacks. Refs #72 --- app/controllers/build_lists_controller.rb | 9 +- app/models/build_list.rb | 2 +- app/views/build_lists/_build_lists.html.haml | 2 - app/views/build_lists/new.html.haml | 4 + app/views/build_lists/show.html.haml | 5 + config/locales/ru.yml | 1 + ...8182425_add_auto_publish_to_build_lists.rb | 9 ++ db/schema.rb | 3 +- .../build_lists_controller_spec.rb | 99 ++++++++++++++++++- 9 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 db/migrate/20111228182425_add_auto_publish_to_build_lists.rb diff --git a/app/controllers/build_lists_controller.rb b/app/controllers/build_lists_controller.rb index 44b1773f1..143ddc880 100644 --- a/app/controllers/build_lists_controller.rb +++ b/app/controllers/build_lists_controller.rb @@ -6,7 +6,7 @@ class BuildListsController < ApplicationController before_filter :authenticate_build_service!, :only => CALLBACK_ACTIONS before_filter :find_project, :only => NESTED_ACTIONS before_filter :find_build_list, :only => [:show, :publish, :cancel] - before_filter :find_build_list_by_bs, :only => [:publish_build, :status_build, :pre_build, :post_build] + before_filter :find_build_list_by_bs, :only => [:publish_build, :status_build, :pre_build, :post_build, :circle_build] load_and_authorize_resource :project, :only => NESTED_ACTIONS load_and_authorize_resource :build_list, :through => :project, :only => NESTED_ACTIONS, :shallow => true @@ -113,6 +113,8 @@ class BuildListsController < ApplicationController @build_list.notified_at = Time.current @build_list.save + @build_list.publish if @build_list.auto_publish # && @build_list.can_publish? + render :nothing => true, :status => 200 end @@ -130,10 +132,9 @@ class BuildListsController < ApplicationController @build_list.name = params[:name] @build_list.additional_repos = ActiveSupport::JSON.decode(params[:additional_repos]) @build_list.set_items(ActiveSupport::JSON.decode(params[:items])) - @build_list.notified_at = Time.current - @build_list.is_circle = (params[:is_circular] != "0") + @build_list.is_circle = (params[:is_circular].to_i != 0) @build_list.bs_id = params[:id] - params[:arch] + @build_list.notified_at = Time.current @build_list.save render :nothing => true, :status => 200 diff --git a/app/models/build_list.rb b/app/models/build_list.rb index d28e0d293..91534a5a5 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -110,7 +110,7 @@ class BuildList < ActiveRecord::Base def publish return false unless can_publish? has_published = BuildServer.publish_container bs_id - update_attribute(:status, BUILD_PUBLISH) if has_published == 0 + update_attribute(:status, has_published == 0 ? BUILD_PUBLISH : FAILED_PUBLISH) return has_published == 0 end diff --git a/app/views/build_lists/_build_lists.html.haml b/app/views/build_lists/_build_lists.html.haml index 8bd7ab667..a51c428f3 100644 --- a/app/views/build_lists/_build_lists.html.haml +++ b/app/views/build_lists/_build_lists.html.haml @@ -6,7 +6,6 @@ %th= t("activerecord.attributes.build_list.project") %th= t("activerecord.attributes.build_list.arch") %th= t("activerecord.attributes.build_list.user") - %th= t("activerecord.attributes.build_list.is_circle") - unless @project %th= t("layout.build_lists.cancel_button_header") %th.last= t("activerecord.attributes.build_list.notified_at") @@ -19,7 +18,6 @@ %td= link_to build_list.project.name, project_path(build_list.project) %td= build_list.arch.name %td= build_list.user.try(:fullname) - %td= t("layout.#{build_list.is_circle?}_") - unless @project %td= link_to t("layout.build_lists.cancel_button"), cancel_build_list_path(build_list), :method => "put", :confirm => t("layout.confirm") if build_list.can_cancel? %td.last= build_list.notified_at diff --git a/app/views/build_lists/new.html.haml b/app/views/build_lists/new.html.haml index 622191b78..76187ddb5 100644 --- a/app/views/build_lists/new.html.haml +++ b/app/views/build_lists/new.html.haml @@ -50,6 +50,10 @@ = f.label :include_repos, t("activerecord.attributes.build_list.include_repos"), :class => :label #include_repos + .group + = f.check_box :auto_publish + = f.label :auto_publish, t("activerecord.attributes.build_list.auto_publish") + .group.navform.wat-cf %button.button{:type => "submit"} = image_tag("web-app-theme/icons/tick.png", :alt => t("layout.projects.build_button")) diff --git a/app/views/build_lists/show.html.haml b/app/views/build_lists/show.html.haml index eafd98ddd..9c7e779c5 100644 --- a/app/views/build_lists/show.html.haml +++ b/app/views/build_lists/show.html.haml @@ -49,6 +49,11 @@ = t("activerecord.attributes.build_list.build_requires") \: = @build_list.build_requires + %p + %b + = t("activerecord.attributes.build_list.auto_publish") + \: + = @build_list.auto_publish %p %b = t("activerecord.attributes.build_list.status") diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 892d6ff7c..dbc4294dc 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -637,6 +637,7 @@ ru: bpl_id: Платформа update_type: Критичность обновления build_requires: Пересборка с зависимостями + auto_publish: Автоматическая публикация project_version: Версия user: Пользователь diff --git a/db/migrate/20111228182425_add_auto_publish_to_build_lists.rb b/db/migrate/20111228182425_add_auto_publish_to_build_lists.rb new file mode 100644 index 000000000..a9cb7d3bd --- /dev/null +++ b/db/migrate/20111228182425_add_auto_publish_to_build_lists.rb @@ -0,0 +1,9 @@ +class AddAutoPublishToBuildLists < ActiveRecord::Migration + def self.up + add_column :build_lists, :auto_publish, :boolean, :default => true + end + + def self.down + remove_column :build_lists, :auto_publish + end +end diff --git a/db/schema.rb b/db/schema.rb index 2a5a51d73..4fad3e5fa 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 => 20111221194422) do +ActiveRecord::Schema.define(:version => 20111228182425) do create_table "arches", :force => true do |t| t.string "name", :null => false @@ -71,6 +71,7 @@ ActiveRecord::Schema.define(:version => 20111221194422) do t.integer "pl_id" t.text "include_repos" t.integer "user_id" + t.boolean "auto_publish", :default => true end add_index "build_lists", ["arch_id"], :name => "index_build_lists_on_arch_id" diff --git a/spec/controllers/build_lists_controller_spec.rb b/spec/controllers/build_lists_controller_spec.rb index bfedd23a2..a3e088c63 100644 --- a/spec/controllers/build_lists_controller_spec.rb +++ b/spec/controllers/build_lists_controller_spec.rb @@ -50,10 +50,10 @@ describe BuildListsController do end end + before { stub_rsync_methods } + context 'crud' do before(:each) do - stub_rsync_methods - platform = Factory(:platform_with_repos) @create_params = { :build_list => { @@ -257,7 +257,6 @@ describe BuildListsController do context 'filter' do before(:each) do - stub_rsync_methods set_session_for Factory(:admin) @build_list1 = Factory(:build_list_core) @@ -297,5 +296,99 @@ describe BuildListsController do end context 'callbacks' do + let(:build_list) { Factory(:build_list_core) } + + describe 'publish_build' do + def do_get(status) + get :publish_build, :id => build_list.bs_id, :status => status + build_list.reload + end + + it { do_get(BuildServer::SUCCESS); response.should be_ok } + it { lambda{ do_get(BuildServer::SUCCESS) }.should change(build_list, :status).to(BuildList::BUILD_PUBLISHED) } + it { lambda{ do_get(BuildServer::ERROR) }.should change(build_list, :status).to(BuildList::FAILED_PUBLISH) } + it { lambda{ do_get(BuildServer::ERROR) }.should change(build_list, :notified_at) } + end + + describe 'status_build' do + before { @item = build_list.items.create(:name => build_list.project.name, :version => build_list.project_version, :level => 0) } + + def do_get + get :status_build, :id => build_list.bs_id, :package_name => build_list.project.name, :status => BuildServer::SUCCESS, :container_path => '/path/to' + build_list.reload + @item.reload + end + + it { do_get; response.should be_ok } + it { lambda{ do_get }.should change(@item, :status) } + it { lambda{ do_get }.should change(build_list, :container_path) } + it { lambda{ do_get }.should change(build_list, :notified_at) } + end + + describe 'pre_build' do + def do_get + get :pre_build, :id => build_list.bs_id + build_list.reload + end + + it { do_get; response.should be_ok } + it { lambda{ do_get }.should change(build_list, :status).to(BuildServer::BUILD_STARTED) } + it { lambda{ do_get }.should change(build_list, :notified_at) } + end + + describe 'post_build' do + def do_get(status) + get :post_build, :id => build_list.bs_id, :status => status, :container_path => '/path/to' + build_list.reload + end + + it { do_get(BuildServer::SUCCESS); response.should be_ok } + it { lambda{ do_get(BuildServer::SUCCESS) }.should change(build_list, :container_path) } + it { lambda{ do_get(BuildServer::SUCCESS) }.should change(build_list, :notified_at) } + + context 'with auto_publish' do + it { lambda{ do_get(BuildServer::SUCCESS) }.should change(build_list, :status).to(BuildList::BUILD_PUBLISH) } + it { lambda{ do_get(BuildServer::ERROR) }.should change(build_list, :status).to(BuildServer::ERROR) } + end + + context 'without auto_publish' do + before { build_list.update_attribute(:auto_publish, false) } + + it { lambda{ do_get(BuildServer::SUCCESS) }.should change(build_list, :status).to(BuildServer::SUCCESS) } + it { lambda{ do_get(BuildServer::ERROR) }.should change(build_list, :status).to(BuildServer::ERROR) } + end + end + + describe 'circle_build' do + def do_get + get :circle_build, :id => build_list.bs_id, :container_path => '/path/to' + build_list.reload + end + + it { do_get; response.should be_ok } + it { lambda{ do_get }.should change(build_list, :is_circle).to(true) } + it { lambda{ do_get }.should change(build_list, :container_path) } + it { lambda{ do_get }.should change(build_list, :notified_at) } + end + + describe 'new_bbdt' do + before { @items = build_list.items } + + def do_get + get :new_bbdt, :id => 123, :web_id => build_list.id, :name => build_list.project.name, :is_circular => 1, + :additional_repos => ActiveSupport::JSON.encode([{:name => 'build_repos'}, {:name => 'main'}]), + :items => ActiveSupport::JSON.encode(0 => [{:name => build_list.project.name, :version => build_list.project_version}]) + build_list.reload + @items.reload + end + + it { do_get; response.should be_ok } + it { lambda{ do_get }.should change(build_list, :name).to(build_list.project.name) } + it { lambda{ do_get }.should change(build_list, :additional_repos) } + it { lambda{ do_get }.should change(@items, :first) } + it { lambda{ do_get }.should change(build_list, :is_circle).to(true) } + it { lambda{ do_get }.should change(build_list, :bs_id).to(123) } + it { lambda{ do_get }.should change(build_list, :notified_at) } + end end end From 635e2b7977bc3e28fa68661c6ad0119dd3ad1f88 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Thu, 29 Dec 2011 15:16:54 +0400 Subject: [PATCH 30/40] [refs #54] Add comments specs and factory. Remove unused code. Add one delay and fix some links. --- app/models/ability.rb | 17 +-- app/models/comment.rb | 4 +- app/models/issue.rb | 7 - app/views/issues/show.html.haml | 6 +- db/schema.rb | 11 +- spec/controllers/comments_controller_spec.rb | 145 ++++++++++++++++++- spec/controllers/issues_controller_spec.rb | 2 - spec/factories/comments.rb | 11 +- spec/factories/issues.rb | 1 + 9 files changed, 168 insertions(+), 36 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index ebff5da1b..a0bd61c8d 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -88,16 +88,8 @@ class Ability cannot :manage, Issue, :project => {:has_issues => false} # switch off issues can(:create, Comment) {|comment| can? :read, comment.commentable.project} - can(:update, Comment) {|comment| can? :update, comment.user_id == user.id or local_admin?(comment.commentable.project)} + can(:update, Comment) {|comment| comment.user_id == user.id or local_admin?(comment.commentable.project)} cannot :manage, Comment, :commentable => {:project => {:has_issues => false}} # switch off issues - - can :create, Subscribe do |subscribe| - !subscribe.subscribeable.subscribes.exists?(:user_id => user.id) - end - can :destroy, Subscribe do |subscribe| - subscribe.subscribeable.subscribes.exists?(:user_id => user.id) && user.id == subscribe.user_id - end - #can [:create, :delete], Subscribe end end @@ -106,6 +98,13 @@ class Ability cannot :destroy, Repository, :platform => {:platform_type => 'personal'} cannot :fork, Project, :owner_id => user.id, :owner_type => user.class.to_s cannot :destroy, Issue + + can :create, Subscribe do |subscribe| + !subscribe.subscribeable.subscribes.exists?(:user_id => user.id) + end + can :destroy, Subscribe do |subscribe| + subscribe.subscribeable.subscribes.exists?(:user_id => user.id) && user.id == subscribe.user_id + end end # TODO group_ids ?? diff --git a/app/models/comment.rb b/app/models/comment.rb index 36e14c412..439fd0733 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -9,14 +9,12 @@ class Comment < ActiveRecord::Base protected def deliver_new_comment_notification - #UserMailer.new_comment_notification(self, self.commentable.user).deliver - #UserMailer.new_comment_notification(self, self.commentable.project.owner).deliver recipients = self.commentable.project.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) } recipients = recipients | [self.commentable.user_id] recipients = recipients | [self.commentable.project.owner_id] if self.commentable.project.owner_type == 'User' recipients.each do |recipient_id| recipient = User.find(recipient_id) - UserMailer.new_comment_notification(self, recipient).deliver + UserMailer.delay.new_comment_notification(self, recipient) end end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 34401d4be..9ae96f439 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -33,12 +33,6 @@ class Issue < ActiveRecord::Base end def deliver_new_issue_notification - #UserMailer.new_issue_notification(self, self.project.owner).deliver - #self.project.relations.by_role('admin').each do |rel| - # admin = User.find(rel.object_id) - # UserMailer.new_issue_notification(self, admin).deliver - #end - recipients = collect_recipient_ids recipients.each do |recipient_id| recipient = User.find(recipient_id) @@ -47,7 +41,6 @@ class Issue < ActiveRecord::Base end def deliver_issue_assign_notification - #UserMailer.delay.issue_assign_notification(self, self.user).deliver if self.user_id_was != self.user_id UserMailer.delay.issue_assign_notification(self, self.user) if self.user_id_was != self.user_id end diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml index 073de4c2a..49b229b5c 100644 --- a/app/views/issues/show.html.haml +++ b/app/views/issues/show.html.haml @@ -30,7 +30,7 @@ = link_to t('layout.issues.subscribe_btn'), project_issue_subscribes_path(@project, @issue), :method => :post %a{ :name => "comments" } -.block#block-lists +.block#block-list .content %h2.title = t("layout.issues.comments_header") @@ -44,8 +44,8 @@ = comment.body %br %br - = 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.comments.confirm_delete") if can? :delete, comment + = link_to t("layout.edit"), edit_project_issue_comment_path(@project, @issue.id, 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.id, comment), :method => "delete", :class => "button", :confirm => t("layout.comments.confirm_delete") if can? :delete, comment .block .content diff --git a/db/schema.rb b/db/schema.rb index 1aece65f1..37cc1d3a6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -245,6 +245,7 @@ ActiveRecord::Schema.define(:version => 20111226141947) 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" @@ -281,16 +282,16 @@ ActiveRecord::Schema.define(:version => 20111226141947) 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/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index 562bf1aff..8e9af23eb 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -1,5 +1,148 @@ require 'spec_helper' -describe CommentsController do +shared_examples_for 'user with create comment rights' do + it 'should be able to perform create action' do + post :create, @create_params + response.should redirect_to(project_issue_path(@project, @issue)) + end + it 'should create issue object into db' do + lambda{ post :create, @create_params }.should change{ Comment.count }.by(1) + end +end + +shared_examples_for 'user with update own comment rights' do + it 'should be able to perform update action' do + put :update, {:id => @own_comment.id}.merge(@update_params) + response.should redirect_to([@project, @issue]) + end + + it 'should update issue title' do + put :update, {:id => @own_comment.id}.merge(@update_params) + @own_comment.reload.body.should == 'updated' + end +end + +shared_examples_for 'user with update stranger comment rights' do + it 'should be able to perform update action' do + put :update, {:id => @comment.id}.merge(@update_params) + response.should redirect_to([@project, @issue]) + end + + it 'should update issue title' do + put :update, {:id => @comment.id}.merge(@update_params) + @comment.reload.body.should == 'updated' + end +end + +shared_examples_for 'user without update stranger comment rights' do + it 'should not be able to perform update action' do + put :update, {:id => @comment.id}.merge(@update_params) + response.should redirect_to(forbidden_path) + end + + it 'should not update issue title' do + put :update, {:id => @comment.id}.merge(@update_params) + @comment.reload.body.should_not == 'updated' + end +end + +shared_examples_for 'user without destroy comment rights' do + it 'should not be able to perform destroy action' do + delete :destroy, :id => @comment.id, :issue_id => @issue.id, :project_id => @project.id + response.should redirect_to(forbidden_path) + end + + it 'should not reduce comments count' do + lambda{ delete :destroy, :id => @comment.id, :issue_id => @issue.id, :project_id => @project.id }.should change{ Issue.count }.by(0) + end +end + +#shared_examples_for 'user with destroy rights' do +# it 'should be able to perform destroy action' do +# delete :destroy, :id => @comment.id, :issue_id => @issue.id, :project_id => @project.id +# response.should redirect_to([@project, @issue]) +# end +# +# it 'should reduce comments count' do +# lambda{ delete :destroy, :id => @comment.id, :issue_id => @issue.id, :project_id => @project.id }.should change{ Comment.count }.by(-1) +# end +#end + +describe CommentsController do + before(:each) do + stub_rsync_methods + + @project = Factory(:project) + @issue = Factory(:issue, :project_id => @project.id) + @comment = Factory(:comment, :commentable => @issue) + + @create_params = {:comment => {:body => 'I am a comment!'}, :project_id => @project.id, :issue_id => @issue.id} + @update_params = {:comment => {:body => 'updated'}, :project_id => @project.id, :issue_id => @issue.id} + + any_instance_of(Project, :versions => ['v1.0', 'v2.0']) + + @request.env['HTTP_REFERER'] = project_issue_path(@project, @issue) + end + + context 'for project admin user' do + before(:each) do + @user = Factory(:user) + set_session_for(@user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + + @own_comment = Factory(:comment, :commentable => @issue, :user => @user) + end + + it_should_behave_like 'user with create comment rights' + it_should_behave_like 'user with update stranger comment rights' + it_should_behave_like 'user with update own comment rights' + it_should_behave_like 'user without destroy comment rights' + 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') + + @own_comment = Factory(:comment, :commentable => @issue, :user => @user) + end + + it_should_behave_like 'user with create comment rights' + it_should_behave_like 'user with update stranger comment rights' + it_should_behave_like 'user with update own comment rights' + it_should_behave_like 'user without destroy comment rights' + 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') + + @own_comment = Factory(:comment, :commentable => @issue, :user => @user) + end + + it_should_behave_like 'user with create comment rights' + it_should_behave_like 'user without update stranger comment rights' + it_should_behave_like 'user with update own comment rights' + it_should_behave_like 'user without destroy comment rights' + 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') + + @own_comment = Factory(:comment, :commentable => @issue, :user => @user) + end + + it_should_behave_like 'user with create comment rights' + it_should_behave_like 'user without update stranger comment rights' + it_should_behave_like 'user with update own comment rights' + it_should_behave_like 'user without destroy comment rights' + end end diff --git a/spec/controllers/issues_controller_spec.rb b/spec/controllers/issues_controller_spec.rb index 37aad58cf..63003491e 100644 --- a/spec/controllers/issues_controller_spec.rb +++ b/spec/controllers/issues_controller_spec.rb @@ -78,8 +78,6 @@ describe IssuesController do @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']) diff --git a/spec/factories/comments.rb b/spec/factories/comments.rb index f9df3b695..f036eee06 100644 --- a/spec/factories/comments.rb +++ b/spec/factories/comments.rb @@ -1,6 +1,5 @@ -# Read about factories at http://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :comment do - end -end \ No newline at end of file +Factory.define(:comment) do |p| + p.body { Factory.next(:string) } + p.association :user, :factory => :user + p.association :commentable, :factory => :issue +end diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb index 8b8017dbc..79d2ca96a 100644 --- a/spec/factories/issues.rb +++ b/spec/factories/issues.rb @@ -1,6 +1,7 @@ Factory.define(:issue) do |p| p.title { Factory.next(:string) } p.body { Factory.next(:string) } + p.association :project, :factory => :project p.association :user, :factory => :user p.status "open" end From 402133aff6d7df5c249c21be93a06893ad32d5b0 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Thu, 29 Dec 2011 21:03:53 +0400 Subject: [PATCH 31/40] [refs #54] Add some subscribe specs and factory --- spec/controllers/comments_controller_spec.rb | 4 +- .../controllers/subscribes_controller_spec.rb | 64 +++++++++++++++++++ spec/factories/subscribes.rb | 10 ++- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/spec/controllers/comments_controller_spec.rb b/spec/controllers/comments_controller_spec.rb index 8e9af23eb..c93ac57f0 100644 --- a/spec/controllers/comments_controller_spec.rb +++ b/spec/controllers/comments_controller_spec.rb @@ -6,7 +6,7 @@ shared_examples_for 'user with create comment rights' do response.should redirect_to(project_issue_path(@project, @issue)) end - it 'should create issue object into db' do + it 'should create subscribe object into db' do lambda{ post :create, @create_params }.should change{ Comment.count }.by(1) end end @@ -17,7 +17,7 @@ shared_examples_for 'user with update own comment rights' do response.should redirect_to([@project, @issue]) end - it 'should update issue title' do + it 'should update subscribe body' do put :update, {:id => @own_comment.id}.merge(@update_params) @own_comment.reload.body.should == 'updated' end diff --git a/spec/controllers/subscribes_controller_spec.rb b/spec/controllers/subscribes_controller_spec.rb index e1c466cb3..7358127ee 100644 --- a/spec/controllers/subscribes_controller_spec.rb +++ b/spec/controllers/subscribes_controller_spec.rb @@ -1,5 +1,69 @@ require 'spec_helper' +shared_examples_for 'user with create subscribe rights' do + it 'should be able to perform create action' do + post :create, @create_params + response.should redirect_to(project_issue_path(@project, @issue)) + end + + it 'should create subscribe object into db' do + lambda{ post :create, @create_params }.should change{ Subscribe.count }.by(1) + end +end + +shared_examples_for 'user without destroy subscribe rights' do + it 'should not be able to perform destroy action' do + delete :destroy, :id => @subscribe.id, :issue_id => @issue.id, :project_id => @project.id + response.should redirect_to(forbidden_path) + end + + it 'should not reduce subscribes count' do + lambda{ delete :destroy, :id => @subscribe.id, :issue_id => @issue.id, :project_id => @project.id }.should change{ Subscribe.count }.by(0) + end +end + describe SubscribesController do + before(:each) do + stub_rsync_methods + + @project = Factory(:project) + @issue = Factory(:issue, :project_id => @project.id) + @subscribe = Factory(:subscribe, :subscribeable => @issue, :user => @user) + + any_instance_of(Project, :versions => ['v1.0', 'v2.0']) + + @request.env['HTTP_REFERER'] = project_issue_path(@project, @issue) + end + + context 'for global admin user' do + before(:each) do + @user = Factory(:admin) + set_session_for(@user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + end + + it 'should be able to perform create action' do + post :create, :project_id => @project.id, :issue_id => @issue.id + response.should redirect_to(project_issue_path(@project, @issue)) + end + + it 'should create issue object into db' do + lambda{ post :create, :project_id => @project.id, :issue_id => @issue.id }.should change{ Subscribe.count }.by(1) + end + + it 'should be able to perform destroy action' do + delete :destroy, :id => @subscribe.id, :issue_id => @issue.id, :project_id => @project.id + response.should redirect_to(forbidden_path) + end + + it 'should reduce subscribes count' do + lambda{ delete :destroy, :id => @subscribe.id, :issue_id => @issue.id, :project_id => @project.id }.should change{ Issue.count }.by(-1) + end + + #it_should_behave_like 'user with create subscribe rights' + #it_should_behave_like 'user with update stranger subscribe rights' + #it_should_behave_like 'user with update own subscribe rights' + #it_should_behave_like 'user without destroy subscribe rights' + end end diff --git a/spec/factories/subscribes.rb b/spec/factories/subscribes.rb index f0644816f..55a75a8ee 100644 --- a/spec/factories/subscribes.rb +++ b/spec/factories/subscribes.rb @@ -1,6 +1,4 @@ -# Read about factories at http://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :subscribe do - end -end \ No newline at end of file +Factory.define(:subscribe) do |p| + p.association :subscribeable, :factory => :issue + p.association :user, :factory => :user +end From 782b311983a81e0fa48853029d4d4af13ffb5561 Mon Sep 17 00:00:00 2001 From: George Vinogradov Date: Thu, 29 Dec 2011 22:16:39 +0400 Subject: [PATCH 32/40] [issue #76] Changed project list in repositories. --- app/views/repositories/_proj_list.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/repositories/_proj_list.html.haml b/app/views/repositories/_proj_list.html.haml index cb3745999..087de3c70 100644 --- a/app/views/repositories/_proj_list.html.haml +++ b/app/views/repositories/_proj_list.html.haml @@ -5,7 +5,7 @@ - @projects.each do |project| %tr{:class => cycle("odd", "even")} %td - = link_to project.owner.name + '/' + project.name, project_path(project) + = link_to project.owner.uname + '/' + project.name, project_path(project) %td.last #{link_to t("layout.show"), project_path(project)} | #{link_to t("layout.add"), url_for(:controller => :repositories, :action => :add_project, :project_id => project.id)} From 3f72082e0ec4511423f8beacdf551a0db2da7ea0 Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Fri, 30 Dec 2011 18:21:08 +0400 Subject: [PATCH 33/40] [refs #54] Add comments, subscribes models specs. Finish subscribes controller specs. Some ability.rb fixes --- app/models/ability.rb | 2 + .../controllers/subscribes_controller_spec.rb | 86 +++++++++--- spec/models/comment_spec.rb | 122 +++++++++++++++++- spec/models/subscribe_spec.rb | 74 ++++++++++- 4 files changed, 262 insertions(+), 22 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index a0bd61c8d..1b7509a2b 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -13,6 +13,8 @@ class Ability if user.admin? can :manage, :all + cannot :destroy, Subscribe + cannot :create, Subscribe else # Shared rights between guests and registered users can :forbidden, Platform diff --git a/spec/controllers/subscribes_controller_spec.rb b/spec/controllers/subscribes_controller_spec.rb index 7358127ee..378af911d 100644 --- a/spec/controllers/subscribes_controller_spec.rb +++ b/spec/controllers/subscribes_controller_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -shared_examples_for 'user with create subscribe rights' do +shared_examples_for 'can subscribe' do it 'should be able to perform create action' do post :create, @create_params response.should redirect_to(project_issue_path(@project, @issue)) @@ -11,14 +11,42 @@ shared_examples_for 'user with create subscribe rights' do end end -shared_examples_for 'user without destroy subscribe rights' do +shared_examples_for 'can not subscribe' do + 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 subscribe object into db' do + lambda{ post :create, @create_params }.should change{ Subscribe.count }.by(0) + end +end + +shared_examples_for 'can unsubscribe' do + it 'should be able to perform destroy action' do + #set_objects_to_destroy + delete :destroy, @destroy_params + + response.should redirect_to([@project, @issue]) + end + + it 'should reduce subscribes count' do + #set_objects_to_destroy + lambda{ delete :destroy, @destroy_params }.should change{ Subscribe.count }.by(-1) + end +end + +shared_examples_for 'can not unsubscribe' do it 'should not be able to perform destroy action' do - delete :destroy, :id => @subscribe.id, :issue_id => @issue.id, :project_id => @project.id + #set_objects_to_destroy + delete :destroy, @destroy_params + response.should redirect_to(forbidden_path) end it 'should not reduce subscribes count' do - lambda{ delete :destroy, :id => @subscribe.id, :issue_id => @issue.id, :project_id => @project.id }.should change{ Subscribe.count }.by(0) + #set_objects_to_destroy + lambda{ delete :destroy, @destroy_params }.should change{ Subscribe.count }.by(0) end end @@ -28,7 +56,9 @@ describe SubscribesController do @project = Factory(:project) @issue = Factory(:issue, :project_id => @project.id) - @subscribe = Factory(:subscribe, :subscribeable => @issue, :user => @user) + + @create_params = {:issue_id => @issue.serial_id, :project_id => @project.id} + @destroy_params = {:issue_id => @issue.serial_id, :project_id => @project.id} any_instance_of(Project, :versions => ['v1.0', 'v2.0']) @@ -40,30 +70,46 @@ describe SubscribesController do @user = Factory(:admin) set_session_for(@user) @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + @destroy_params = @destroy_params.merge({:id => @user.id}) end - it 'should be able to perform create action' do - post :create, :project_id => @project.id, :issue_id => @issue.id - response.should redirect_to(project_issue_path(@project, @issue)) + context 'subscribed' do + before(:each) do + ss = @issue.subscribes.build(:user => @user) + ss.save! + end + + it_should_behave_like 'can unsubscribe' + it_should_behave_like 'can not subscribe' end - it 'should create issue object into db' do - lambda{ post :create, :project_id => @project.id, :issue_id => @issue.id }.should change{ Subscribe.count }.by(1) + context 'not subscribed' do + it_should_behave_like 'can subscribe' + #it_should_behave_like 'can not unsubscribe' + end + end + + context 'for simple user' do + before(:each) do + @user = Factory(:user) + set_session_for(@user) + @destroy_params = @destroy_params.merge({:id => @user.id}) end - it 'should be able to perform destroy action' do - delete :destroy, :id => @subscribe.id, :issue_id => @issue.id, :project_id => @project.id - response.should redirect_to(forbidden_path) + context 'subscribed' do + before(:each) do + ss = @issue.subscribes.build(:user => @user) + ss.save! + end + + it_should_behave_like 'can unsubscribe' + it_should_behave_like 'can not subscribe' end - it 'should reduce subscribes count' do - lambda{ delete :destroy, :id => @subscribe.id, :issue_id => @issue.id, :project_id => @project.id }.should change{ Issue.count }.by(-1) + context 'not subscribed' do + it_should_behave_like 'can subscribe' + #it_should_behave_like 'can not unsubscribe' end - - #it_should_behave_like 'user with create subscribe rights' - #it_should_behave_like 'user with update stranger subscribe rights' - #it_should_behave_like 'user with update own subscribe rights' - #it_should_behave_like 'user without destroy subscribe rights' end end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index 505e33d36..0b969c134 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -1,5 +1,125 @@ require 'spec_helper' +require "cancan/matchers" + +def set_testable_data + @ability = Ability.new(@user) + + @project = Factory(:project) + @issue = Factory(:issue, :project_id => @project.id) + + @comment = Factory(:comment, :commentable => @issue, :user => @user) + @stranger_comment = Factory(:comment, :commentable => @issue, :user => @stranger) + + any_instance_of(Project, :versions => ['v1.0', 'v2.0']) +end describe Comment do - pending "add some examples to (or delete) #{__FILE__}" + context 'for global admin user' do + before(:each) do + @user = Factory(:admin) + @stranger = Factory(:user) + + set_testable_data + end + + it 'should create comment' do + @ability.should be_able_to(:create, Comment.new(:commentable => @issue, :user => @user)) + end + + it 'should update comment' do + @ability.should be_able_to(:update, @comment) + end + + it 'should update stranger comment' do + @ability.should be_able_to(:update, @stranger_comment) + end + + it 'should destroy own comment' do + @ability.should be_able_to(:destroy, @comment) + end + + it 'should destroy stranger comment' do + @ability.should be_able_to(:destroy, @stranger_comment) + end + end + + context 'for project admin user' do + before(:each) do + @user = Factory(:user) + @stranger = Factory(:user) + + set_testable_data + + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + end + + it 'should create comment' do + @ability.should be_able_to(:create, Comment.new(:commentable => @issue, :user => @user)) + end + + it 'should update comment' do + @ability.should be_able_to(:update, @comment) + end + + it 'should update stranger comment' do + @ability.should be_able_to(:update, @stranger_comment) + end + + it 'should not destroy comment' do + @ability.should_not be_able_to(:destroy, @comment) + end + end + + context 'for project owner user' do + before(:each) do + @user = Factory(:user) + @stranger = Factory(:user) + + set_testable_data + + @project.update_attribute(:owner, @user) + @project.relations.create!(:object_type => 'User', :object_id => @user.id, :role => 'admin') + end + + it 'should create comment' do + @ability.should be_able_to(:create, Comment.new(:commentable => @issue, :user => @user)) + end + + it 'should update comment' do + @ability.should be_able_to(:update, @comment) + end + + it 'should update stranger comment' do + @ability.should be_able_to(:update, @stranger_comment) + end + + it 'should not destroy comment' do + @ability.should_not be_able_to(:destroy, @comment) + end + end + + context 'for simple user' do + before(:each) do + @user = Factory(:user) + @stranger = Factory(:user) + + set_testable_data + end + + it 'should create comment' do + @ability.should be_able_to(:create, Comment.new(:commentable => @issue, :user => @user)) + end + + it 'should update comment' do + @ability.should be_able_to(:update, @comment) + end + + it 'should not update stranger comment' do + @ability.should_not be_able_to(:update, @stranger_comment) + end + + it 'should not destroy comment' do + @ability.should_not be_able_to(:destroy, @comment) + end + end end diff --git a/spec/models/subscribe_spec.rb b/spec/models/subscribe_spec.rb index 0df4fbb42..7d496da07 100644 --- a/spec/models/subscribe_spec.rb +++ b/spec/models/subscribe_spec.rb @@ -1,5 +1,77 @@ require 'spec_helper' +require "cancan/matchers" + +def set_testable_data + @ability = Ability.new(@user) + + @project = Factory(:project) + @issue = Factory(:issue, :project_id => @project.id) + + any_instance_of(Project, :versions => ['v1.0', 'v2.0']) +end describe Subscribe do - pending "add some examples to (or delete) #{__FILE__}" + context 'for global admin user' do + before(:each) do + @user = Factory(:admin) + @stranger = Factory(:user) + + set_testable_data + end + + it 'should create subscribe' do + @ability.should be_able_to(:create, Subscribe.new(:subscribeable => @issue, :user => @user)) + end + + context 'destroy' do + before(:each) do + @subscribe = Factory(:subscribe, :subscribeable => @issue, :user => @user) + @stranger_subscribe = Factory(:subscribe, :subscribeable => @issue, :user => @stranger) + end + + context 'own subscribe' do + it 'should destroy subscribe' do + @ability.should be_able_to(:destroy, @subscribe) + end + end + + context 'stranger subscribe' do + it 'should not destroy subscribe' do + @ability.should_not be_able_to(:destroy, @stranger_subscribe) + end + end + end + end + + context 'for simple user' do + before(:each) do + @user = Factory(:user) + @stranger = Factory(:user) + + set_testable_data + end + + it 'should create subscribe' do + @ability.should be_able_to(:create, Subscribe.new(:subscribeable => @issue, :user => @user)) + end + + context 'destroy' do + before(:each) do + @subscribe = Factory(:subscribe, :subscribeable => @issue, :user => @user) + @stranger_subscribe = Factory(:subscribe, :subscribeable => @issue, :user => @stranger) + end + + context 'own subscribe' do + it 'should destroy subscribe' do + @ability.should be_able_to(:destroy, @subscribe) + end + end + + context 'stranger subscribe' do + it 'should not destroy subscribe' do + @ability.should_not be_able_to(:destroy, @stranger_subscribe) + end + end + end + end end From a44530980ed8344ad4925346b163ac4f274d69ce Mon Sep 17 00:00:00 2001 From: "konstantin.grabar" Date: Fri, 30 Dec 2011 18:44:47 +0400 Subject: [PATCH 34/40] [refs #54] Remove some unnessacary comments --- config/routes.rb | 6 ------ spec/controllers/issues_controller_spec.rb | 5 ----- spec/controllers/subscribes_controller_spec.rb | 6 ------ spec/helpers/comments_helper_spec.rb | 15 --------------- spec/helpers/issues_helper_spec.rb | 15 --------------- spec/helpers/subscribes_helper_spec.rb | 15 --------------- 6 files changed, 62 deletions(-) delete mode 100644 spec/helpers/comments_helper_spec.rb delete mode 100644 spec/helpers/issues_helper_spec.rb delete mode 100644 spec/helpers/subscribes_helper_spec.rb diff --git a/config/routes.rb b/config/routes.rb index c6b729fd7..7f3449cde 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -78,12 +78,6 @@ Rosa::Application.routes.draw do resources :categories, :only => [:index, :show] end - #match "issues/:issue_id/subscribe" => 'subscribes#create', :as => :subscribe_issue, :via => :post - #match "issues/:issue_id/unsubscribe" => 'subscribes#destroy', :as => :unsubscribe_issue, :via => :delete - - #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 do resources :comments, :only => [:edit, :create, :update, :destroy] diff --git a/spec/controllers/issues_controller_spec.rb b/spec/controllers/issues_controller_spec.rb index 63003491e..829957dee 100644 --- a/spec/controllers/issues_controller_spec.rb +++ b/spec/controllers/issues_controller_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' 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) @@ -62,7 +61,6 @@ 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 @@ -114,8 +112,6 @@ describe IssuesController do 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') @@ -182,7 +178,6 @@ describe IssuesController do 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' diff --git a/spec/controllers/subscribes_controller_spec.rb b/spec/controllers/subscribes_controller_spec.rb index 378af911d..6db7e2c24 100644 --- a/spec/controllers/subscribes_controller_spec.rb +++ b/spec/controllers/subscribes_controller_spec.rb @@ -24,28 +24,24 @@ end shared_examples_for 'can unsubscribe' do it 'should be able to perform destroy action' do - #set_objects_to_destroy delete :destroy, @destroy_params response.should redirect_to([@project, @issue]) end it 'should reduce subscribes count' do - #set_objects_to_destroy lambda{ delete :destroy, @destroy_params }.should change{ Subscribe.count }.by(-1) end end shared_examples_for 'can not unsubscribe' do it 'should not be able to perform destroy action' do - #set_objects_to_destroy delete :destroy, @destroy_params response.should redirect_to(forbidden_path) end it 'should not reduce subscribes count' do - #set_objects_to_destroy lambda{ delete :destroy, @destroy_params }.should change{ Subscribe.count }.by(0) end end @@ -85,7 +81,6 @@ describe SubscribesController do context 'not subscribed' do it_should_behave_like 'can subscribe' - #it_should_behave_like 'can not unsubscribe' end end @@ -108,7 +103,6 @@ describe SubscribesController do context 'not subscribed' do it_should_behave_like 'can subscribe' - #it_should_behave_like 'can not unsubscribe' end end diff --git a/spec/helpers/comments_helper_spec.rb b/spec/helpers/comments_helper_spec.rb deleted file mode 100644 index cc93aa9b6..000000000 --- a/spec/helpers/comments_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index 2e20f1cf1..000000000 --- a/spec/helpers/issues_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -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/helpers/subscribes_helper_spec.rb b/spec/helpers/subscribes_helper_spec.rb deleted file mode 100644 index c72869cf9..000000000 --- a/spec/helpers/subscribes_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -# Specs in this file have access to a helper object that includes -# the SubscribesHelper. For example: -# -# describe SubscribesHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# helper.concat_strings("this","that").should == "this that" -# end -# end -# end -describe SubscribesHelper do - pending "add some examples to (or delete) #{__FILE__}" -end From 7c50b2c3f583b12a24935f5167cd6218c1b2f0b5 Mon Sep 17 00:00:00 2001 From: George Vinogradov Date: Sat, 31 Dec 2011 02:42:58 +0400 Subject: [PATCH 35/40] [issue #76] Added jQuery DataTable. But it doesn't work yet --- app/controllers/repositories_controller.rb | 27 +++- app/views/repositories/_proj_ajax.js.erb | 12 ++ app/views/repositories/_proj_list.html.haml | 12 +- .../repositories/projects_list.html.haml | 2 +- config/routes.rb | 1 + public/javascripts/jquery.dataTables.min.js | 151 ++++++++++++++++++ vendor/plugins/rails_datatables/MIT-LICENSE | 20 +++ vendor/plugins/rails_datatables/README.md | 138 ++++++++++++++++ vendor/plugins/rails_datatables/Rakefile | 23 +++ vendor/plugins/rails_datatables/init.rb | 1 + vendor/plugins/rails_datatables/install.rb | 1 + .../rails_datatables/lib/rails_datatables.rb | 81 ++++++++++ .../test/rails_datatables_test.rb | 8 + .../rails_datatables/test/test_helper.rb | 3 + vendor/plugins/rails_datatables/uninstall.rb | 1 + 15 files changed, 472 insertions(+), 9 deletions(-) create mode 100644 app/views/repositories/_proj_ajax.js.erb create mode 100644 public/javascripts/jquery.dataTables.min.js create mode 100644 vendor/plugins/rails_datatables/MIT-LICENSE create mode 100644 vendor/plugins/rails_datatables/README.md create mode 100644 vendor/plugins/rails_datatables/Rakefile create mode 100644 vendor/plugins/rails_datatables/init.rb create mode 100644 vendor/plugins/rails_datatables/install.rb create mode 100644 vendor/plugins/rails_datatables/lib/rails_datatables.rb create mode 100644 vendor/plugins/rails_datatables/test/rails_datatables_test.rb create mode 100644 vendor/plugins/rails_datatables/test/test_helper.rb create mode 100644 vendor/plugins/rails_datatables/uninstall.rb diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 8bbca916e..bb6dbdbe5 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -63,15 +63,32 @@ class RepositoriesController < ApplicationController end redirect_to repository_path(@repository) else - if @repository.platform.platform_type == 'main' - @projects = Project.addable_to_repository(@repository.id).by_visibilities(['open']).paginate(:page => params[:project_page]) - else - @projects = Project.addable_to_repository(@repository.id).paginate(:page => params[:project_page]) - end +# if @repository.platform.platform_type == 'main' +# @projects = Project.addable_to_repository(@repository.id).by_visibilities(['open']).paginate(:page => params[:project_page]) +# else +# @projects = Project.addable_to_repository(@repository.id).paginate(:page => params[:project_page]) +# end render 'projects_list' end end + def projects_list + colName = ['owner.uname', 'projects.name'] + sort_col = params[:iSortCol_0] || 0 + sort_dir = params[:sSortDir_0]=="asc" ? 'asc' : 'desc' + order = "#{colName[sort_col.to_i]} #{sort_dir}" + + @projects = Project.addable_to_repository(@repository.id) + @projects = @projects.by_visibilities(['open']) if @repository.platform.platform_type == 'main' + + @total_projects = @projects.count + @projects = @projects.where(['projects.name LIKE ?', "#{params[:sSearch]}%"]) if params[:sSearch] and !params[:sSearch].empty? + @total_project = @projects.count + @projects = @projects.order(order)#.includes(:owner) #WTF???? + + render :partial => 'proj_ajax', :layout => false + end + def remove_project @project = Project.find(params[:project_id]) ProjectToRepository.where(:project_id => @project.id, :repository_id => @repository.id).destroy_all diff --git a/app/views/repositories/_proj_ajax.js.erb b/app/views/repositories/_proj_ajax.js.erb new file mode 100644 index 000000000..b70fc996f --- /dev/null +++ b/app/views/repositories/_proj_ajax.js.erb @@ -0,0 +1,12 @@ +{"sEcho": <%= params[:sEcho] || -1 %>, + "iTotalRecords": <%= @total_projects %>, + "iTotalDisplayRecords": <%= @total_project %>, + "aaData":[ +<% @projects.each do |project| %> + ['<%= link_to(project.owner.uname, project.owner) %>', + '<%= link_to(project.name, project) %>', + '<%= link_to t("layout.add"), url_for(:controller => :repositories, :action => :add_project, :project_id => project.id) %>' + ], +<% end %> +]} + diff --git a/app/views/repositories/_proj_list.html.haml b/app/views/repositories/_proj_list.html.haml index 087de3c70..67bdd09f1 100644 --- a/app/views/repositories/_proj_list.html.haml +++ b/app/views/repositories/_proj_list.html.haml @@ -1,11 +1,17 @@ -%table.table +- columns = [{:type => 'html', :sortable => false, :searchable => false}, {:type => 'html'}, {:type => nil}] += raw datatable(columns, {:sort_by => "[1, 'asc']", :ajax_source => "#{url_for :controller => :repositories, :action => :projects_list, :id => @repository.id}" }) + +%table.table.datatable %tr - %th.first= t("activerecord.attributes.project.name") + %th.first= t("activerecord.attributes.user.uname") + %th= t("activerecord.attributes.project.name") %th.last   - - @projects.each do |project| + -# @projects.each do |project| %tr{:class => cycle("odd", "even")} %td = link_to project.owner.uname + '/' + project.name, project_path(project) %td.last #{link_to t("layout.show"), project_path(project)} | #{link_to t("layout.add"), url_for(:controller => :repositories, :action => :add_project, :project_id => project.id)} += content_for :javascripts do + = javascript_include_tag 'jquery.dataTables.min.js' diff --git a/app/views/repositories/projects_list.html.haml b/app/views/repositories/projects_list.html.haml index de40de54c..56f66de84 100644 --- a/app/views/repositories/projects_list.html.haml +++ b/app/views/repositories/projects_list.html.haml @@ -41,7 +41,7 @@ .inner = render :partial => 'shared/search_form' = render :partial => 'proj_list', :object => @projects - .actions-bar.wat-cf + -#.actions-bar.wat-cf .actions = will_paginate @projects, :param_name => :project_page diff --git a/config/routes.rb b/config/routes.rb index dcea79076..ca9648c86 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -110,6 +110,7 @@ Rosa::Application.routes.draw do member do get :add_project get :remove_project + get :projects_list end end diff --git a/public/javascripts/jquery.dataTables.min.js b/public/javascripts/jquery.dataTables.min.js new file mode 100644 index 000000000..4280c6d63 --- /dev/null +++ b/public/javascripts/jquery.dataTables.min.js @@ -0,0 +1,151 @@ +/* + * File: jquery.dataTables.min.js + * Version: 1.8.2 + * Author: Allan Jardine (www.sprymedia.co.uk) + * Info: www.datatables.net + * + * Copyright 2008-2011 Allan Jardine, all rights reserved. + * + * This source file is free software, under either the GPL v2 license or a + * BSD style license, as supplied with this software. + * + * This source file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. + */ +(function(i,za,p){i.fn.dataTableSettings=[];var D=i.fn.dataTableSettings;i.fn.dataTableExt={};var n=i.fn.dataTableExt;n.sVersion="1.8.2";n.sErrMode="alert";n.iApiIndex=0;n.oApi={};n.afnFiltering=[];n.aoFeatures=[];n.ofnSearch={};n.afnSortData=[];n.oStdClasses={sPagePrevEnabled:"paginate_enabled_previous",sPagePrevDisabled:"paginate_disabled_previous",sPageNextEnabled:"paginate_enabled_next",sPageNextDisabled:"paginate_disabled_next",sPageJUINext:"",sPageJUIPrev:"",sPageButton:"paginate_button",sPageButtonActive:"paginate_active", +sPageButtonStaticDisabled:"paginate_button paginate_button_disabled",sPageFirst:"first",sPagePrevious:"previous",sPageNext:"next",sPageLast:"last",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled", +sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sFooterTH:""};n.oJUIClasses={sPagePrevEnabled:"fg-button ui-button ui-state-default ui-corner-left", +sPagePrevDisabled:"fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",sPageNextEnabled:"fg-button ui-button ui-state-default ui-corner-right",sPageNextDisabled:"fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",sPageJUINext:"ui-icon ui-icon-circle-arrow-e",sPageJUIPrev:"ui-icon ui-icon-circle-arrow-w",sPageButton:"fg-button ui-button ui-state-default",sPageButtonActive:"fg-button ui-button ui-state-default ui-state-disabled",sPageButtonStaticDisabled:"fg-button ui-button ui-state-default ui-state-disabled", +sPageFirst:"first ui-corner-tl ui-corner-bl",sPagePrevious:"previous",sPageNext:"next",sPageLast:"last ui-corner-tr ui-corner-br",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"ui-state-default",sSortDesc:"ui-state-default",sSortable:"ui-state-default", +sSortableAsc:"ui-state-default",sSortableDesc:"ui-state-default",sSortableNone:"ui-state-default",sSortColumn:"sorting_",sSortJUIAsc:"css_right ui-icon ui-icon-triangle-1-n",sSortJUIDesc:"css_right ui-icon ui-icon-triangle-1-s",sSortJUI:"css_right ui-icon ui-icon-carat-2-n-s",sSortJUIAscAllowed:"css_right ui-icon ui-icon-carat-1-n",sSortJUIDescAllowed:"css_right ui-icon ui-icon-carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",sScrollWrapper:"dataTables_scroll", +sScrollHead:"dataTables_scrollHead ui-state-default",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot ui-state-default",sScrollFootInner:"dataTables_scrollFootInner",sFooterTH:"ui-state-default"};n.oPagination={two_button:{fnInit:function(g,l,s){var t,w,y;if(g.bJUI){t=p.createElement("a");w=p.createElement("a");y=p.createElement("span");y.className=g.oClasses.sPageJUINext;w.appendChild(y);y=p.createElement("span");y.className=g.oClasses.sPageJUIPrev; +t.appendChild(y)}else{t=p.createElement("div");w=p.createElement("div")}t.className=g.oClasses.sPagePrevDisabled;w.className=g.oClasses.sPageNextDisabled;t.title=g.oLanguage.oPaginate.sPrevious;w.title=g.oLanguage.oPaginate.sNext;l.appendChild(t);l.appendChild(w);i(t).bind("click.DT",function(){g.oApi._fnPageChange(g,"previous")&&s(g)});i(w).bind("click.DT",function(){g.oApi._fnPageChange(g,"next")&&s(g)});i(t).bind("selectstart.DT",function(){return false});i(w).bind("selectstart.DT",function(){return false}); +if(g.sTableId!==""&&typeof g.aanFeatures.p=="undefined"){l.setAttribute("id",g.sTableId+"_paginate");t.setAttribute("id",g.sTableId+"_previous");w.setAttribute("id",g.sTableId+"_next")}},fnUpdate:function(g){if(g.aanFeatures.p)for(var l=g.aanFeatures.p,s=0,t=l.length;s=w-t){t=w-s+1;x=w}else{t=y-Math.ceil(s/2)+1;x=t+s-1}for(s=t;s<=x;s++)F+=y!=s?''+s+"":''+s+"";x=g.aanFeatures.p;var z,$=function(M){g._iDisplayStart=(this.innerHTML*1-1)*g._iDisplayLength;l(g);M.preventDefault()},X=function(){return false};s=0;for(t=x.length;sl?1:0},"string-desc":function(g,l){if(typeof g!="string")g="";if(typeof l!="string")l="";g=g.toLowerCase();l=l.toLowerCase();return gl?-1:0},"html-asc":function(g,l){g=g.replace(/<.*?>/g,"").toLowerCase();l=l.replace(/<.*?>/g,"").toLowerCase();return g< +l?-1:g>l?1:0},"html-desc":function(g,l){g=g.replace(/<.*?>/g,"").toLowerCase();l=l.replace(/<.*?>/g,"").toLowerCase();return gl?-1:0},"date-asc":function(g,l){g=Date.parse(g);l=Date.parse(l);if(isNaN(g)||g==="")g=Date.parse("01/01/1970 00:00:00");if(isNaN(l)||l==="")l=Date.parse("01/01/1970 00:00:00");return g-l},"date-desc":function(g,l){g=Date.parse(g);l=Date.parse(l);if(isNaN(g)||g==="")g=Date.parse("01/01/1970 00:00:00");if(isNaN(l)||l==="")l=Date.parse("01/01/1970 00:00:00");return l- +g},"numeric-asc":function(g,l){return(g=="-"||g===""?0:g*1)-(l=="-"||l===""?0:l*1)},"numeric-desc":function(g,l){return(l=="-"||l===""?0:l*1)-(g=="-"||g===""?0:g*1)}};n.aTypes=[function(g){if(typeof g=="number")return"numeric";else if(typeof g!="string")return null;var l,s=false;l=g.charAt(0);if("0123456789-".indexOf(l)==-1)return null;for(var t=1;t")!=-1)return"html";return null}];n.fnVersionCheck=function(g){var l=function(x,v){for(;x.length=parseInt(w,10)};n._oExternConfig={iNextUnique:0};i.fn.dataTable=function(g){function l(){this.fnRecordsTotal= +function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsTotal,10):this.aiDisplayMaster.length};this.fnRecordsDisplay=function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsDisplay,10):this.aiDisplay.length};this.fnDisplayEnd=function(){return this.oFeatures.bServerSide?this.oFeatures.bPaginate===false||this._iDisplayLength==-1?this._iDisplayStart+this.aiDisplay.length:Math.min(this._iDisplayStart+this._iDisplayLength,this._iRecordsDisplay):this._iDisplayEnd};this.sInstance= +this.oInstance=null;this.oFeatures={bPaginate:true,bLengthChange:true,bFilter:true,bSort:true,bInfo:true,bAutoWidth:true,bProcessing:false,bSortClasses:true,bStateSave:false,bServerSide:false,bDeferRender:false};this.oScroll={sX:"",sXInner:"",sY:"",bCollapse:false,bInfinite:false,iLoadGap:100,iBarWidth:0,bAutoCss:true};this.aanFeatures=[];this.oLanguage={sProcessing:"Processing...",sLengthMenu:"Show _MENU_ entries",sZeroRecords:"No matching records found",sEmptyTable:"No data available in table", +sLoadingRecords:"Loading...",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sInfoThousands:",",sSearch:"Search:",sUrl:"",oPaginate:{sFirst:"First",sPrevious:"Previous",sNext:"Next",sLast:"Last"},fnInfoCallback:null};this.aoData=[];this.aiDisplay=[];this.aiDisplayMaster=[];this.aoColumns=[];this.aoHeader=[];this.aoFooter=[];this.iNextId=0;this.asDataSearch=[];this.oPreviousSearch={sSearch:"", +bRegex:false,bSmart:true};this.aoPreSearchCols=[];this.aaSorting=[[0,"asc",0]];this.aaSortingFixed=null;this.asStripeClasses=[];this.asDestroyStripes=[];this.sDestroyWidth=0;this.fnFooterCallback=this.fnHeaderCallback=this.fnRowCallback=null;this.aoDrawCallback=[];this.fnInitComplete=this.fnPreDrawCallback=null;this.sTableId="";this.nTableWrapper=this.nTBody=this.nTFoot=this.nTHead=this.nTable=null;this.bInitialised=this.bDeferLoading=false;this.aoOpenRows=[];this.sDom="lfrtip";this.sPaginationType= +"two_button";this.iCookieDuration=7200;this.sCookiePrefix="SpryMedia_DataTables_";this.fnCookieCallback=null;this.aoStateSave=[];this.aoStateLoad=[];this.sAjaxSource=this.oLoadedState=null;this.sAjaxDataProp="aaData";this.bAjaxDataGet=true;this.jqXHR=null;this.fnServerData=function(a,b,c,d){d.jqXHR=i.ajax({url:a,data:b,success:function(f){i(d.oInstance).trigger("xhr",d);c(f)},dataType:"json",cache:false,error:function(f,e){e=="parsererror"&&alert("DataTables warning: JSON data from server could not be parsed. This is caused by a JSON formatting error.")}})}; +this.aoServerParams=[];this.fnFormatNumber=function(a){if(a<1E3)return a;else{var b=a+"";a=b.split("");var c="";b=b.length;for(var d=0;d=0;e--)!a.aoColumns[e].bVisible&&!c&&h[d].splice(e,1);j.push([])}d=0;for(f=h.length;d=a.fnRecordsDisplay()?0:a.iInitDisplayStart;a.iInitDisplayStart=-1;E(a)}if(a.bDeferLoading){a.bDeferLoading=false;a.iDraw++}else if(a.oFeatures.bServerSide){if(!a.bDestroying&&!Ca(a))return}else a.iDraw++;if(a.aiDisplay.length!==0){var h=a._iDisplayStart,j=a._iDisplayEnd;if(a.oFeatures.bServerSide){h=0;j=a.aoData.length}for(h= +h;h=0;b--)c[b].parentNode.removeChild(c[b])}b=0;for(c=d.length;b=0;b--)a.aoDrawCallback[b].fn.call(a.oInstance,a);i(a.oInstance).trigger("draw",a);a.bSorted=false;a.bFiltered=false;a.bDrawing=false;if(a.oFeatures.bServerSide){K(a,false);typeof a._bInitComplete=="undefined"&&w(a)}}}function da(a){if(a.oFeatures.bSort)R(a,a.oPreviousSearch);else if(a.oFeatures.bFilter)N(a, +a.oPreviousSearch);else{E(a);C(a)}}function Ca(a){if(a.bAjaxDataGet){a.iDraw++;K(a,true);var b=Da(a);ha(a,b);a.fnServerData.call(a.oInstance,a.sAjaxSource,b,function(c){Ea(a,c)},a);return false}else return true}function Da(a){var b=a.aoColumns.length,c=[],d,f;c.push({name:"sEcho",value:a.iDraw});c.push({name:"iColumns",value:b});c.push({name:"sColumns",value:ka(a)});c.push({name:"iDisplayStart",value:a._iDisplayStart});c.push({name:"iDisplayLength",value:a.oFeatures.bPaginate!==false?a._iDisplayLength: +-1});for(f=0;f")c=c.parentNode;else if(h=="l"&&a.oFeatures.bPaginate&&a.oFeatures.bLengthChange){f=Ga(a);e=1}else if(h=="f"&&a.oFeatures.bFilter){f=Ha(a);e=1}else if(h=="r"&&a.oFeatures.bProcessing){f=Ia(a);e=1}else if(h=="t"){f=Ja(a);e=1}else if(h=="i"&&a.oFeatures.bInfo){f=Ka(a);e=1}else if(h=="p"&&a.oFeatures.bPaginate){f=La(a);e=1}else if(n.aoFeatures.length!==0){j=n.aoFeatures;u=0;for(k=j.length;ui(a.nTable).height()-a.oScroll.iLoadGap)if(a.fnDisplayEnd()0&&a.nTable.removeChild(h[0]);if(a.nTFoot!==null){k=a.nTable.getElementsByTagName("tfoot");k.length>0&&a.nTable.removeChild(k[0])}h=a.nTHead.cloneNode(true);a.nTable.insertBefore(h,a.nTable.childNodes[0]);if(a.nTFoot!==null){k=a.nTFoot.cloneNode(true); +a.nTable.insertBefore(k,a.nTable.childNodes[1])}if(a.oScroll.sX===""){d.style.width="100%";b.parentNode.style.width="100%"}var U=S(a,h);f=0;for(e=U.length;fd.offsetHeight||i(d).css("overflow-y")=="scroll"))a.nTable.style.width=q(i(a.nTable).outerWidth()-a.oScroll.iBarWidth)}else if(a.oScroll.sXInner!== +"")a.nTable.style.width=q(a.oScroll.sXInner);else if(f==i(d).width()&&i(d).height()f-a.oScroll.iBarWidth)a.nTable.style.width=q(f)}else a.nTable.style.width=q(f);f=i(a.nTable).outerWidth();e=a.nTHead.getElementsByTagName("tr");h=h.getElementsByTagName("tr");P(function(I,na){m=I.style;m.paddingTop="0";m.paddingBottom="0";m.borderTopWidth="0";m.borderBottomWidth="0";m.height=0;r=i(I).width();na.style.width= +q(r);H.push(r)},h,e);i(h).height(0);if(a.nTFoot!==null){j=k.getElementsByTagName("tr");k=a.nTFoot.getElementsByTagName("tr");P(function(I,na){m=I.style;m.paddingTop="0";m.paddingBottom="0";m.borderTopWidth="0";m.borderBottomWidth="0";m.height=0;r=i(I).width();na.style.width=q(r);H.push(r)},j,k);i(j).height(0)}P(function(I){I.innerHTML="";I.style.width=q(H.shift())},h);a.nTFoot!==null&&P(function(I){I.innerHTML="";I.style.width=q(H.shift())},j);if(i(a.nTable).outerWidth()d.offsetHeight|| +i(d).css("overflow-y")=="scroll"?f+a.oScroll.iBarWidth:f;if(B&&(d.scrollHeight>d.offsetHeight||i(d).css("overflow-y")=="scroll"))a.nTable.style.width=q(j-a.oScroll.iBarWidth);d.style.width=q(j);b.parentNode.style.width=q(j);if(a.nTFoot!==null)L.parentNode.style.width=q(j);if(a.oScroll.sX==="")J(a,1,"The table cannot fit into the current element which will cause column misalignment. The table has been drawn at its minimum possible width.");else a.oScroll.sXInner!==""&&J(a,1,"The table cannot fit into the current element which will cause column misalignment. Increase the sScrollXInner value or remove it to allow automatic calculation")}else{d.style.width= +q("100%");b.parentNode.style.width=q("100%");if(a.nTFoot!==null)L.parentNode.style.width=q("100%")}if(a.oScroll.sY==="")if(B)d.style.height=q(a.nTable.offsetHeight+a.oScroll.iBarWidth);if(a.oScroll.sY!==""&&a.oScroll.bCollapse){d.style.height=q(a.oScroll.sY);B=a.oScroll.sX!==""&&a.nTable.offsetWidth>d.offsetWidth?a.oScroll.iBarWidth:0;if(a.nTable.offsetHeight'):b===""?'':b+' ';var c=p.createElement("div"); +c.className=a.oClasses.sFilter;c.innerHTML="";a.sTableId!==""&&typeof a.aanFeatures.f=="undefined"&&c.setAttribute("id",a.sTableId+"_filter");b=i("input",c);b.val(a.oPreviousSearch.sSearch.replace('"',"""));b.bind("keyup.DT",function(){for(var d=a.aanFeatures.f,f=0,e=d.length;f=0;d--){f=qa(G(a,a.aiDisplay[d],c,"filter"),a.aoColumns[c].sType);if(!b.test(f)){a.aiDisplay.splice(d,1);e++}}}}function Oa(a,b,c,d,f){var e=pa(b,d,f);if(typeof c=="undefined"||c===null)c=0;if(n.afnFiltering.length!==0)c=1;if(b.length<=0){a.aiDisplay.splice(0,a.aiDisplay.length);a.aiDisplay=a.aiDisplayMaster.slice()}else if(a.aiDisplay.length==a.aiDisplayMaster.length|| +a.oPreviousSearch.sSearch.length>b.length||c==1||b.indexOf(a.oPreviousSearch.sSearch)!==0){a.aiDisplay.splice(0,a.aiDisplay.length);oa(a,1);for(c=0;c/g,"");else if(typeof a=="string")return a.replace(/\n/g," ");else if(a===null)return"";return a}function R(a,b){var c,d,f,e,h=[],j=[],k=n.oSort;d=a.aoData;var m=a.aoColumns;if(!a.oFeatures.bServerSide&& +(a.aaSorting.length!==0||a.aaSortingFixed!==null)){h=a.aaSortingFixed!==null?a.aaSortingFixed.concat(a.aaSorting):a.aaSorting.slice();for(c=0;c=h)for(b=0;b=0?a._iDisplayStart-a._iDisplayLength:0;if(a._iDisplayStart<0)a._iDisplayStart=0}else if(b=="next")if(a._iDisplayLength>=0){if(a._iDisplayStart+a._iDisplayLength=0){b=parseInt((a.fnRecordsDisplay()- +1)/a._iDisplayLength,10)+1;a._iDisplayStart=(b-1)*a._iDisplayLength}else a._iDisplayStart=0;else J(a,0,"Unknown paging action: "+b);i(a.oInstance).trigger("page",a);return c!=a._iDisplayStart}function Ka(a){var b=p.createElement("div");b.className=a.oClasses.sInfo;if(typeof a.aanFeatures.i=="undefined"){a.aoDrawCallback.push({fn:Ra,sName:"information"});a.sTableId!==""&&b.setAttribute("id",a.sTableId+"_info")}return b}function Ra(a){if(!(!a.oFeatures.bInfo||a.aanFeatures.i.length===0)){var b=a._iDisplayStart+ +1,c=a.fnDisplayEnd(),d=a.fnRecordsTotal(),f=a.fnRecordsDisplay(),e=a.fnFormatNumber(b),h=a.fnFormatNumber(c),j=a.fnFormatNumber(d),k=a.fnFormatNumber(f);if(a.oScroll.bInfinite)e=a.fnFormatNumber(1);e=a.fnRecordsDisplay()===0&&a.fnRecordsDisplay()==a.fnRecordsTotal()?a.oLanguage.sInfoEmpty+a.oLanguage.sInfoPostFix:a.fnRecordsDisplay()===0?a.oLanguage.sInfoEmpty+" "+a.oLanguage.sInfoFiltered.replace("_MAX_",j)+a.oLanguage.sInfoPostFix:a.fnRecordsDisplay()==a.fnRecordsTotal()?a.oLanguage.sInfo.replace("_START_", +e).replace("_END_",h).replace("_TOTAL_",k)+a.oLanguage.sInfoPostFix:a.oLanguage.sInfo.replace("_START_",e).replace("_END_",h).replace("_TOTAL_",k)+" "+a.oLanguage.sInfoFiltered.replace("_MAX_",a.fnFormatNumber(a.fnRecordsTotal()))+a.oLanguage.sInfoPostFix;if(a.oLanguage.fnInfoCallback!==null)e=a.oLanguage.fnInfoCallback(a,b,c,d,f,e);a=a.aanFeatures.i;b=0;for(c=a.length;b",c,d;if(a.aLengthMenu.length==2&&typeof a.aLengthMenu[0]=="object"&&typeof a.aLengthMenu[1]=="object"){c=0;for(d=a.aLengthMenu[0].length;c'+a.aLengthMenu[1][c]+""}else{c=0;for(d=a.aLengthMenu.length;c'+a.aLengthMenu[c]+""}b+="";var f=p.createElement("div");a.sTableId!==""&&typeof a.aanFeatures.l=="undefined"&&f.setAttribute("id",a.sTableId+"_length"); +f.className=a.oClasses.sLength;f.innerHTML="";i('select option[value="'+a._iDisplayLength+'"]',f).attr("selected",true);i("select",f).bind("change.DT",function(){var e=i(this).val(),h=a.aanFeatures.l;c=0;for(d=h.length;ca.aiDisplay.length||a._iDisplayLength==-1?a.aiDisplay.length:a._iDisplayStart+a._iDisplayLength}function Sa(a,b){if(!a||a===null||a==="")return 0;if(typeof b=="undefined")b=p.getElementsByTagName("body")[0];var c=p.createElement("div");c.style.width=q(a);b.appendChild(c);a=c.offsetWidth;b.removeChild(c);return a}function ga(a){var b=0,c,d=0,f=a.aoColumns.length,e,h=i("th", +a.nTHead);for(e=0;etd",b);h=S(a,e);for(e=d=0;e0)a.aoColumns[e].sWidth=q(c);d++}a.nTable.style.width=q(i(b).outerWidth());b.parentNode.removeChild(b)}}function Ua(a,b){if(a.oScroll.sX===""&&a.oScroll.sY!==""){i(b).width();b.style.width=q(i(b).outerWidth()-a.oScroll.iBarWidth)}else if(a.oScroll.sX!== +"")b.style.width=q(i(b).outerWidth())}function Ta(a,b){var c=Va(a,b);if(c<0)return null;if(a.aoData[c].nTr===null){var d=p.createElement("td");d.innerHTML=G(a,c,b,"");return d}return Q(a,c)[b]}function Va(a,b){for(var c=-1,d=-1,f=0;f/g,"");if(e.length>c){c=e.length;d=f}}return d}function q(a){if(a===null)return"0px";if(typeof a=="number"){if(a<0)return"0px";return a+"px"}var b=a.charCodeAt(a.length-1);if(b<48||b>57)return a;return a+ +"px"}function Za(a,b){if(a.length!=b.length)return 1;for(var c=0;cb&&a[d]--;c!=-1&&a.splice(c,1)}function Fa(a,b){b=b.split(",");for(var c=[],d=0,f=a.aoColumns.length;d4096){a=p.cookie.split(";");for(var j=0,k=a.length;j=d.aiDisplay.length){d._iDisplayStart-=d._iDisplayLength;if(d._iDisplayStart<0)d._iDisplayStart=0}if(typeof c=="undefined"||c){E(d);C(d)}return f};this.fnClearTable=function(a){var b=A(this[n.iApiIndex]);la(b);if(typeof a=="undefined"||a)C(b)};this.fnOpen=function(a,b,c){var d=A(this[n.iApiIndex]);this.fnClose(a);var f=p.createElement("tr"),e=p.createElement("td");f.appendChild(e);e.className=c; +e.colSpan=Z(d);if(typeof b.jquery!="undefined"||typeof b=="object")e.appendChild(b);else e.innerHTML=b;b=i("tr",d.nTBody);i.inArray(a,b)!=-1&&i(f).insertAfter(a);d.aoOpenRows.push({nTr:f,nParent:a});return f};this.fnClose=function(a){for(var b=A(this[n.iApiIndex]),c=0;c=Z(d);if(!j)for(f=a;ftr>td."+a.oClasses.sRowEmpty,a.nTable).parent().remove(); +if(a.nTable!=a.nTHead.parentNode){i(a.nTable).children("thead").remove();a.nTable.appendChild(a.nTHead)}if(a.nTFoot&&a.nTable!=a.nTFoot.parentNode){i(a.nTable).children("tfoot").remove();a.nTable.appendChild(a.nTFoot)}a.nTable.parentNode.removeChild(a.nTable);i(a.nTableWrapper).remove();a.aaSorting=[];a.aaSortingFixed=[];V(a);i(ba(a)).removeClass(a.asStripeClasses.join(" "));if(a.bJUI){i("th",a.nTHead).removeClass([n.oStdClasses.sSortable,n.oJUIClasses.sSortableAsc,n.oJUIClasses.sSortableDesc,n.oJUIClasses.sSortableNone].join(" ")); +i("th span."+n.oJUIClasses.sSortIcon,a.nTHead).remove();i("th",a.nTHead).each(function(){var e=i("div."+n.oJUIClasses.sSortJUIWrapper,this),h=e.contents();i(this).append(h);e.remove()})}else i("th",a.nTHead).removeClass([n.oStdClasses.sSortable,n.oStdClasses.sSortableAsc,n.oStdClasses.sSortableDesc,n.oStdClasses.sSortableNone].join(" "));a.nTableReinsertBefore?b.insertBefore(a.nTable,a.nTableReinsertBefore):b.appendChild(a.nTable);d=0;for(f=a.aoData.length;dt<"F"ip>'}if(e.oScroll.sX!==""||e.oScroll.sY!== +"")e.oScroll.iBarWidth=Ya();if(typeof g.iDisplayStart!="undefined"&&typeof e.iInitDisplayStart=="undefined"){e.iInitDisplayStart=g.iDisplayStart;e._iDisplayStart=g.iDisplayStart}if(typeof g.bStateSave!="undefined"){e.oFeatures.bStateSave=g.bStateSave;Xa(e,g);e.aoDrawCallback.push({fn:va,sName:"state_save"})}if(typeof g.iDeferLoading!="undefined"){e.bDeferLoading=true;e._iRecordsTotal=g.iDeferLoading;e._iRecordsDisplay=g.iDeferLoading}if(typeof g.aaData!="undefined")j=true;if(typeof g!="undefined"&& +typeof g.aoData!="undefined")g.aoColumns=g.aoData;if(typeof g.oLanguage!="undefined")if(typeof g.oLanguage.sUrl!="undefined"&&g.oLanguage.sUrl!==""){e.oLanguage.sUrl=g.oLanguage.sUrl;i.getJSON(e.oLanguage.sUrl,null,function(u){y(e,u,true)});h=true}else y(e,g.oLanguage,false)}else g={};if(typeof g.asStripClasses=="undefined"&&typeof g.asStripeClasses=="undefined"){e.asStripeClasses.push(e.oClasses.sStripeOdd);e.asStripeClasses.push(e.oClasses.sStripeEven)}c=false;d=i(this).children("tbody").children("tr"); +a=0;for(b=e.asStripeClasses.length;a=0;a--){var m= +g.aoColumnDefs[a].aTargets;i.isArray(m)||J(e,1,"aTargets must be an array of targets, not a "+typeof m);c=0;for(d=m.length;c=0){for(;e.aoColumns.length<=m[c];)F(e);x(e,m[c],g.aoColumnDefs[a])}else if(typeof m[c]=="number"&&m[c]<0)x(e,e.aoColumns.length+m[c],g.aoColumnDefs[a]);else if(typeof m[c]=="string"){b=0;for(f=e.aoColumns.length;b=e.aoColumns.length)e.aaSorting[a][0]=0;k=e.aoColumns[e.aaSorting[a][0]];if(typeof e.aaSorting[a][2]=="undefined")e.aaSorting[a][2]=0;if(typeof g.aaSorting=="undefined"&&typeof e.saved_aaSorting=="undefined")e.aaSorting[a][1]=k.asSorting[0];c=0;for(d=k.asSorting.length;c0){e.nTFoot=a[0];Y(e.aoFooter,e.nTFoot)}if(j)for(a=0;a, passing in the columns, how to filter them (sorting type), and any other settings (ajax source, search?, label for search, processing image) + + <% columns = [{:type => 'html', :class => "first"}, {:type => 'html'}, {:type => 'html'}, {:type => nil, :class => "last"}] %> + <%= datatable(columns, {:sort_by => "[0, 'desc']", :processing => image_tag("spinner.gif") }) %> + + + + + + + + + + + + <%- @users.each do |user| -%> + + + + + + + <%- end -%> + +
NameAccount LevelEmailActions
<%= user.name %><%= user.account.account_level.name %><%= user.email %><%= link_to "Edit", edit_system_user_path(user) %>
+ +### Options + +#### Table Options + + :sort_by - array, default column number (0 - n-1) and sort order. e.g. "[2, 'desc']". Defaults to initial order. + :search - boolean, display the search field. Defaults to true. + :search_label - string, the label for the search field. Defaults to "Search". + :processing - string, the text or image to display while processing data. Defaults to "Processing". + :persist_state - boolean, remember the sorting and page of the tables for the user. Defaults to true. + :additional_data - hash, pass along additional data, such as filter values. Default is none. + :table_dom_id - string, the ID of the table to alter. If nothing is passed, it will look for a class of 'datatable'. Necessary if you want to have multiple DataTables on a single page. + :per_page - the number of rows to show per page (renamed from display_length) + :append - functions to all at the end of the dataTable() call. Useful for [Datatables plugins](http://www.datatables.net/plug-ins/api) + :no_records_message - Message to display if no records are found, whether on load or after searching + :auto_width - Automatically adjust the width of the columns. Defaults to true. + :row_callback - a function to run on each row in the table. Inserted in to "'fnRowCallback': function( nRow, aData, iDisplayIndex ) { }". See [documentation for fnRowCallback](http://www.datatables.net/usage/callbacks) for more information. + +#### Column Options + + :class - string, the class to assign to the table cell. Default is none. + :type - string, the type of content in the column, for non-Ajax tables. 'html' will strip all HTML and sort on the inner value, as a string. Default is string. + :sortable - boolean, allow this column to be sorted on. Default is true. + :searchable - boolean, allow this column to be searched, for non-Ajax tables. Default is true. + +#### AJAX Options + + When you're working with large datasets it's not reasonable to load everything on page load. Use an :ajax_source to load just the records that are being displayed, do custom searching (DB, Solr, etc). + + :ajax_source - string, for large datasets, use an ajax source to load each page on its own. For smaller datasets, just load the whole set and let datatable do the sorting + +Add a datatable method on your controller to return JSON +* Return the objects to be displayed +* Return the total number of objects +* Add a method to handle sorting - DataTables returns the column that is being sorted (0 - n), so you need to know which column is which and sort on it. + +### AJAX Example + +#### Datatable view example - datatable.html.erb + + {"sEcho": <%= params[:sEcho] || -1 %>, + "iTotalRecords": <%= @total_objects %>, + "iTotalDisplayRecords": <%= @total_object %>, + "aaData":[ + <% @objects.each do |object| %> + ['<%= link_to(object.user.name, user) %>', + '<%= object.description || "-" %>', + '<%= object.created_at %>' + ], + <% end %> + ]} + +#### Controller example - using will_paginate + + def datatable + @objects = current_objects(params) + @total_objectss = total_objects(params) + render :layout => false + end + + private + + def current_objects(params={}) + current_page = (params[:iDisplayStart].to_i/params[:iDisplayLength].to_i rescue 0)+1 + @current_objects = Object.paginate :page => current_page, + :include => [:user], + :order => "#{datatable_columns(params[:iSortCol_0])} #{params[:sSortDir_0] || "DESC"}", + :conditions => conditions, + :per_page => params[:iDisplayLength] + end + + def total_objects(params={}) + @total_objects = Object.count :include => [:user], :conditions => conditions + end + + def datatable_columns(column_id) + case column_id.to_i + when 1 + return "objects.description" + when 2 + return "objects.created_at" + else + return "users.name" + end + end + + def conditions + conditions = [] + conditions << "(objects.description ILIKE '%#{params[:sSearch]}%' OR users.name ILIKE '%#{params[:sSearch]}%')" if(params[:sSearch]) + return conditions.join(" AND ") + end + +### Note +There is a more functionality offered by DataTables than this plugin currently provides. We add to it as we find need for other features. If there's a feature of DataTables that you'd like to see, fork this repo and add it so we can all benefit. + +### Credits + +Copyright (c) 2009 [Phronos](http://phronos.com), released under the MIT license diff --git a/vendor/plugins/rails_datatables/Rakefile b/vendor/plugins/rails_datatables/Rakefile new file mode 100644 index 000000000..6be4ee971 --- /dev/null +++ b/vendor/plugins/rails_datatables/Rakefile @@ -0,0 +1,23 @@ +require 'rake' +require 'rake/testtask' +require 'rake/rdoctask' + +desc 'Default: run unit tests.' +task :default => :test + +desc 'Test the rails_datatables plugin.' +Rake::TestTask.new(:test) do |t| + t.libs << 'lib' + t.libs << 'test' + t.pattern = 'test/**/*_test.rb' + t.verbose = true +end + +desc 'Generate documentation for the rails_datatables plugin.' +Rake::RDocTask.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'RailsDatatables' + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('lib/**/*.rb') +end diff --git a/vendor/plugins/rails_datatables/init.rb b/vendor/plugins/rails_datatables/init.rb new file mode 100644 index 000000000..538f33510 --- /dev/null +++ b/vendor/plugins/rails_datatables/init.rb @@ -0,0 +1 @@ +ActionView::Base.send :include, RailsDatatables \ No newline at end of file diff --git a/vendor/plugins/rails_datatables/install.rb b/vendor/plugins/rails_datatables/install.rb new file mode 100644 index 000000000..f7732d379 --- /dev/null +++ b/vendor/plugins/rails_datatables/install.rb @@ -0,0 +1 @@ +# Install hook code here diff --git a/vendor/plugins/rails_datatables/lib/rails_datatables.rb b/vendor/plugins/rails_datatables/lib/rails_datatables.rb new file mode 100644 index 000000000..68bc52d9b --- /dev/null +++ b/vendor/plugins/rails_datatables/lib/rails_datatables.rb @@ -0,0 +1,81 @@ +module RailsDatatables + def datatable(columns, opts={}) + sort_by = opts[:sort_by] || nil + additional_data = opts[:additional_data] || {} + search = opts[:search].present? ? opts[:search].to_s : "true" + search_label = opts[:search_label] || "Search" + processing = opts[:processing] || "Processing" + persist_state = opts[:persist_state].present? ? opts[:persist_state].to_s : "true" + table_dom_id = opts[:table_dom_id] ? "##{opts[:table_dom_id]}" : ".datatable" + per_page = opts[:per_page] || opts[:display_length]|| 25 + no_records_message = opts[:no_records_message] || nil + auto_width = opts[:auto_width].present? ? opts[:auto_width].to_s : "true" + row_callback = opts[:row_callback] || nil + + append = opts[:append] || nil + + ajax_source = opts[:ajax_source] || nil + server_side = opts[:ajax_source].present? + + additional_data_string = "" + additional_data.each_pair do |name,value| + additional_data_string = additional_data_string + ", " if !additional_data_string.blank? && value + additional_data_string = additional_data_string + "{'name': '#{name}', 'value':'#{value}'}" if value + end + + %Q{ + + } + end + + private + def formatted_columns(columns) + i = 0 + columns.map {|c| + i += 1 + if c.nil? or c.empty? + "null" + else + searchable = c[:searchable].to_s.present? ? c[:searchable].to_s : "true" + sortable = c[:sortable].to_s.present? ? c[:sortable].to_s : "true" + + "{ + 'sType': '#{c[:type] || "string"}', + 'bSortable':#{sortable}, + 'bSearchable':#{searchable} + #{",'sClass':'#{c[:class]}'" if c[:class]} + }" + end + }.join(",") + end +end \ No newline at end of file diff --git a/vendor/plugins/rails_datatables/test/rails_datatables_test.rb b/vendor/plugins/rails_datatables/test/rails_datatables_test.rb new file mode 100644 index 000000000..e96dcb002 --- /dev/null +++ b/vendor/plugins/rails_datatables/test/rails_datatables_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class RailsDatatablesTest < ActiveSupport::TestCase + # Replace this with your real tests. + test "the truth" do + assert true + end +end diff --git a/vendor/plugins/rails_datatables/test/test_helper.rb b/vendor/plugins/rails_datatables/test/test_helper.rb new file mode 100644 index 000000000..cf148b8b4 --- /dev/null +++ b/vendor/plugins/rails_datatables/test/test_helper.rb @@ -0,0 +1,3 @@ +require 'rubygems' +require 'active_support' +require 'active_support/test_case' \ No newline at end of file diff --git a/vendor/plugins/rails_datatables/uninstall.rb b/vendor/plugins/rails_datatables/uninstall.rb new file mode 100644 index 000000000..973833346 --- /dev/null +++ b/vendor/plugins/rails_datatables/uninstall.rb @@ -0,0 +1 @@ +# Uninstall hook code here From c23312ad5160ff6f2aef0d7fce03c7271fed1606 Mon Sep 17 00:00:00 2001 From: Pavel Chipiga Date: Wed, 11 Jan 2012 21:43:33 +0200 Subject: [PATCH 36/40] Research and fix problem with build_lists#auto_publish feature. Fix specs. Refs #97 --- app/controllers/build_lists_controller.rb | 2 +- db/schema.rb | 11 +++++------ spec/spec_helper.rb | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/controllers/build_lists_controller.rb b/app/controllers/build_lists_controller.rb index 143ddc880..fc650a4b3 100644 --- a/app/controllers/build_lists_controller.rb +++ b/app/controllers/build_lists_controller.rb @@ -113,7 +113,7 @@ class BuildListsController < ApplicationController @build_list.notified_at = Time.current @build_list.save - @build_list.publish if @build_list.auto_publish # && @build_list.can_publish? + @build_list.delay.publish if @build_list.auto_publish # && @build_list.can_publish? render :nothing => true, :status => 200 end diff --git a/db/schema.rb b/db/schema.rb index c4eb51827..e80b0c134 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -246,7 +246,6 @@ ActiveRecord::Schema.define(:version => 20111228182425) 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" @@ -283,16 +282,16 @@ ActiveRecord::Schema.define(:version => 20111228182425) 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 "email", :default => "", :null => false + t.string "encrypted_password", :limit => 128, :default => "", :null => false + t.string "password_salt", :default => "", :null => false t.string "reset_password_token" - t.datetime "reset_password_sent_at" + t.string "remember_token" t.datetime "remember_created_at" t.datetime "created_at" t.datetime "updated_at" - t.string "uname" t.text "ssh_key" - t.integer "role_id" + t.string "uname" t.string "role" end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e15db9c3b..a79b3ce74 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -37,9 +37,9 @@ def stub_rsync_methods any_instance_of(Platform, :umount_directory_for_rsync => true) end +Delayed::Worker.delay_jobs = false # Execute all jobs realtime + # Add testing root_path %x(rm -Rf #{Rails.root}/tmp/test_root) %x(mkdir -p #{Rails.root}/tmp/test_root) APP_CONFIG['root_path'] = "#{Rails.root}/tmp/test_root" - - From 49082c2a8da1beb335b7d4d6f99460212c7e8e6e Mon Sep 17 00:00:00 2001 From: George Vinogradov Date: Thu, 12 Jan 2012 01:12:56 +0400 Subject: [PATCH 37/40] [issue #76] Fixed bug with JSON generation. --- app/controllers/repositories_controller.rb | 2 +- app/views/repositories/_proj_ajax.js.erb | 25 +++++++++++-------- app/views/repositories/_proj_list.html.haml | 16 +++++------- .../repositories/projects_list.html.haml | 2 +- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index bb6dbdbe5..c1c24c242 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -68,7 +68,7 @@ class RepositoriesController < ApplicationController # else # @projects = Project.addable_to_repository(@repository.id).paginate(:page => params[:project_page]) # end - render 'projects_list' + render :projects_list end end diff --git a/app/views/repositories/_proj_ajax.js.erb b/app/views/repositories/_proj_ajax.js.erb index b70fc996f..942b4b1e2 100644 --- a/app/views/repositories/_proj_ajax.js.erb +++ b/app/views/repositories/_proj_ajax.js.erb @@ -1,12 +1,15 @@ -{"sEcho": <%= params[:sEcho] || -1 %>, - "iTotalRecords": <%= @total_projects %>, - "iTotalDisplayRecords": <%= @total_project %>, - "aaData":[ -<% @projects.each do |project| %> - ['<%= link_to(project.owner.uname, project.owner) %>', - '<%= link_to(project.name, project) %>', - '<%= link_to t("layout.add"), url_for(:controller => :repositories, :action => :add_project, :project_id => project.id) %>' - ], -<% end %> -]} +{ + "sEcho": <%=h params[:sEcho].to_i || -1 %>, + "iTotalRecords": <%= @total_projects %>, + "iTotalDisplayRecords": <%= @total_project %>, + "aaData": [ + <% @projects.each do |project| %> + [ + "<%=j link_to project.owner.uname, project.owner %>", + "<%=j link_to project.name, project %>", + "<%=j link_to t("layout.add"), url_for(:controller => :repositories, :action => :add_project, :project_id => project.id) %>" + ]<%= project == @projects.last ? '' : ',' %> + <% end %> + ] +} diff --git a/app/views/repositories/_proj_list.html.haml b/app/views/repositories/_proj_list.html.haml index 67bdd09f1..a326b40ed 100644 --- a/app/views/repositories/_proj_list.html.haml +++ b/app/views/repositories/_proj_list.html.haml @@ -2,16 +2,12 @@ = raw datatable(columns, {:sort_by => "[1, 'asc']", :ajax_source => "#{url_for :controller => :repositories, :action => :projects_list, :id => @repository.id}" }) %table.table.datatable - %tr - %th.first= t("activerecord.attributes.user.uname") - %th= t("activerecord.attributes.project.name") - %th.last   - -# @projects.each do |project| - %tr{:class => cycle("odd", "even")} - %td - = link_to project.owner.uname + '/' + project.name, project_path(project) - %td.last - #{link_to t("layout.show"), project_path(project)} | #{link_to t("layout.add"), url_for(:controller => :repositories, :action => :add_project, :project_id => project.id)} + %thead + %tr + %th.first= t("activerecord.attributes.user.uname") + %th= t("activerecord.attributes.project.name") + %th.last   + %tbody = content_for :javascripts do = javascript_include_tag 'jquery.dataTables.min.js' diff --git a/app/views/repositories/projects_list.html.haml b/app/views/repositories/projects_list.html.haml index 56f66de84..af5a9a1a9 100644 --- a/app/views/repositories/projects_list.html.haml +++ b/app/views/repositories/projects_list.html.haml @@ -39,7 +39,7 @@ %h2.title = t("layout.projects.list_header") .inner - = render :partial => 'shared/search_form' + -#= render :partial => 'shared/search_form' = render :partial => 'proj_list', :object => @projects -#.actions-bar.wat-cf .actions From 73b710ae4ea784690616b6b0ffba2df2552a3306 Mon Sep 17 00:00:00 2001 From: George Vinogradov Date: Thu, 12 Jan 2012 20:37:50 +0400 Subject: [PATCH 38/40] [close #76] Changed styles. Fixed bug with pagination. --- app/controllers/repositories_controller.rb | 18 +++++++----- app/views/layouts/_stylesheets.html.haml | 2 +- app/views/repositories/_proj_list.html.haml | 14 +++++++-- config/locales/ru.yml | 11 +++++++ .../rails_datatables/lib/rails_datatables.rb | 29 ++++++++++++++++++- 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index c1c24c242..85d0175cc 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -54,7 +54,6 @@ class RepositoriesController < ApplicationController def add_project if params[:project_id] @project = Project.find(params[:project_id]) - # params[:project_id] = nil unless @repository.projects.find_by_name(@project.name) @repository.projects << @project flash[:notice] = t('flash.repository.project_added') @@ -63,22 +62,27 @@ class RepositoriesController < ApplicationController end redirect_to repository_path(@repository) else -# if @repository.platform.platform_type == 'main' -# @projects = Project.addable_to_repository(@repository.id).by_visibilities(['open']).paginate(:page => params[:project_page]) -# else -# @projects = Project.addable_to_repository(@repository.id).paginate(:page => params[:project_page]) -# end render :projects_list end end def projects_list + owner_subquery = " + INNER JOIN ( + SELECT id, 'User' AS type, uname + FROM users + UNION + SELECT id, 'Group' AS type, uname + FROM groups + ) AS owner + ON projects.owner_id = owner.id AND projects.owner_type = owner.type" colName = ['owner.uname', 'projects.name'] sort_col = params[:iSortCol_0] || 0 sort_dir = params[:sSortDir_0]=="asc" ? 'asc' : 'desc' order = "#{colName[sort_col.to_i]} #{sort_dir}" - @projects = Project.addable_to_repository(@repository.id) + @projects = Project.joins(owner_subquery).addable_to_repository(@repository.id) + @projects = @projects.paginate(:page => (params[:iDisplayStart].to_i/params[:iDisplayLength].to_i).to_i + 1, :per_page => params[:iDisplayLength]) @projects = @projects.by_visibilities(['open']) if @repository.platform.platform_type == 'main' @total_projects = @projects.count diff --git a/app/views/layouts/_stylesheets.html.haml b/app/views/layouts/_stylesheets.html.haml index b2e58f3b8..b74e60ab4 100644 --- a/app/views/layouts/_stylesheets.html.haml +++ b/app/views/layouts/_stylesheets.html.haml @@ -1,6 +1,6 @@ -#= include_stylesheets :application = stylesheet_link_tag "web-app-theme/base.css", "web-app-theme/themes/default/style.css", "web-app-theme/override.css", "git/style.css" -= stylesheet_link_tag "jquery-ui-1.8.16.custom.css" += stylesheet_link_tag "jquery-ui-1.8.16.custom.css", "datatable.css" = yield :stylesheets diff --git a/app/views/repositories/_proj_list.html.haml b/app/views/repositories/_proj_list.html.haml index a326b40ed..64d8a5da8 100644 --- a/app/views/repositories/_proj_list.html.haml +++ b/app/views/repositories/_proj_list.html.haml @@ -1,13 +1,21 @@ -- columns = [{:type => 'html', :sortable => false, :searchable => false}, {:type => 'html'}, {:type => nil}] -= raw datatable(columns, {:sort_by => "[1, 'asc']", :ajax_source => "#{url_for :controller => :repositories, :action => :projects_list, :id => @repository.id}" }) +- columns = [{:type => 'html', :searchable => false}, {:type => 'html'}, {:type => nil, :sortable => false, :searchable => false}] += raw datatable(columns, {:sort_by => "[1, 'asc']", :search_label => t("layout.search_by_name"), :processing => t("layout.processing"), + :pagination_labels => {:first => t("datatables.first_label"), :previous => t("datatables.previous_label"), + :next => t("datatables.next_label"), :last => t("datatables.last_label")}, + :empty_label => t("datatables.empty_label"), + :info_label => t("datatables.info_label"), + :info_empty_label => t("datatables.info_empty_label"), + :filtered_label => t("datatables.filtered_label"), + :ajax_source => "#{url_for :controller => :repositories, :action => :projects_list, :id => @repository.id}" }) %table.table.datatable %thead %tr - %th.first= t("activerecord.attributes.user.uname") + %th.first{:style => 'width: 80px'}= t("activerecord.attributes.user.uname") %th= t("activerecord.attributes.project.name") %th.last   %tbody +%br = content_for :javascripts do = javascript_include_tag 'jquery.dataTables.min.js' diff --git a/config/locales/ru.yml b/config/locales/ru.yml index dbc4294dc..7fcd2b6c0 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -3,6 +3,16 @@ ru: previous_label: ‹ Предыдущая next_label: Следующая › page_gap: ... + + datatables: + previous_label: ‹ Пред. + next_label: След. › + first_label: « Первая + last_label: Последняя » + empty_label: Нет доступных данных + info_label: Показаны записи с _START_ по _END_ из _TOTAL_ + info_empty_label: Показаны записи с 0 по 0 из 0 + filtered_label: (отфильтровано из _MAX_) layout: logged_in_as: Вы вошли как @@ -30,6 +40,7 @@ ru: not_access: Нет доступа! owner: Владелец confirm: Уверенны? + processing: Обрабатывается... downloads: title: Статистика закачек пакетов diff --git a/vendor/plugins/rails_datatables/lib/rails_datatables.rb b/vendor/plugins/rails_datatables/lib/rails_datatables.rb index 68bc52d9b..1088cb9fe 100644 --- a/vendor/plugins/rails_datatables/lib/rails_datatables.rb +++ b/vendor/plugins/rails_datatables/lib/rails_datatables.rb @@ -12,6 +12,22 @@ module RailsDatatables auto_width = opts[:auto_width].present? ? opts[:auto_width].to_s : "true" row_callback = opts[:row_callback] || nil + empty_label = opts[:empty_label] if opts[:empty_label].present? + info_label = opts[:info_label] if opts[:info_label].present? + info_empty_label = opts[:info_empty_label] if opts[:info_empty_label].present? + filtered_label = opts[:filtered_label] if opts[:filtered_label].present? + + if opts[:pagination_labels].present? + pagination_labels = [] + pagination_labels << "'sFirst': '#{opts[:pagination_labels][:first]}'" if opts[:pagination_labels][:first].present? + pagination_labels << "'sLast': '#{opts[:pagination_labels][:last]}'" if opts[:pagination_labels][:last].present? + pagination_labels << "'sPrevious': '#{opts[:pagination_labels][:previous]}'" if opts[:pagination_labels][:previous].present? + pagination_labels << "'sNext': '#{opts[:pagination_labels][:next]}'" if opts[:pagination_labels][:next].present? + pagination_labels = pagination_labels.join(",\n") + else + pagination_labels = false + end + append = opts[:append] || nil ajax_source = opts[:ajax_source] || nil @@ -30,6 +46,17 @@ module RailsDatatables "oLanguage": { "sSearch": "#{search_label}", #{"'sZeroRecords': '#{no_records_message}'," if no_records_message} + #{" + 'oPaginate': { + #{pagination_labels} + }, + " if pagination_labels} + + #{"'sEmptyTable': '#{empty_label}'," if empty_label} + #{"'sInfo': '#{info_label}'," if info_label} + #{"'sInfoEmpty': '#{info_empty_label}'," if info_empty_label} + #{"'sInfoFiltered': '#{filtered_label}'," if filtered_label} + "sProcessing": '#{processing}' }, "sPaginationType": "full_numbers", @@ -78,4 +105,4 @@ module RailsDatatables end }.join(",") end -end \ No newline at end of file +end From f8177f88ad56e2f6a64e06d69e8317acf7c3275e Mon Sep 17 00:00:00 2001 From: George Vinogradov Date: Thu, 12 Jan 2012 22:46:56 +0400 Subject: [PATCH 39/40] [update #76] Removed unnecessary files. --- .../rails_datatables/test/rails_datatables_test.rb | 8 -------- vendor/plugins/rails_datatables/test/test_helper.rb | 3 --- 2 files changed, 11 deletions(-) delete mode 100644 vendor/plugins/rails_datatables/test/rails_datatables_test.rb delete mode 100644 vendor/plugins/rails_datatables/test/test_helper.rb diff --git a/vendor/plugins/rails_datatables/test/rails_datatables_test.rb b/vendor/plugins/rails_datatables/test/rails_datatables_test.rb deleted file mode 100644 index e96dcb002..000000000 --- a/vendor/plugins/rails_datatables/test/rails_datatables_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'test_helper' - -class RailsDatatablesTest < ActiveSupport::TestCase - # Replace this with your real tests. - test "the truth" do - assert true - end -end diff --git a/vendor/plugins/rails_datatables/test/test_helper.rb b/vendor/plugins/rails_datatables/test/test_helper.rb deleted file mode 100644 index cf148b8b4..000000000 --- a/vendor/plugins/rails_datatables/test/test_helper.rb +++ /dev/null @@ -1,3 +0,0 @@ -require 'rubygems' -require 'active_support' -require 'active_support/test_case' \ No newline at end of file From 8d35c14fc11536d4a58fbed6d4bc851e6a99684a Mon Sep 17 00:00:00 2001 From: George Vinogradov Date: Fri, 13 Jan 2012 17:48:48 +0400 Subject: [PATCH 40/40] [update #76] Added forgotten files. --- public/images/datatables/Sorting icons.psd | Bin 0 -> 27490 bytes public/images/datatables/back_disabled.jpg | Bin 0 -> 612 bytes public/images/datatables/back_enabled.jpg | Bin 0 -> 807 bytes public/images/datatables/favicon.ico | Bin 0 -> 894 bytes public/images/datatables/forward_disabled.jpg | Bin 0 -> 635 bytes public/images/datatables/forward_enabled.jpg | Bin 0 -> 852 bytes public/images/datatables/sort_asc.png | Bin 0 -> 263 bytes .../images/datatables/sort_asc_disabled.png | Bin 0 -> 252 bytes public/images/datatables/sort_both.png | Bin 0 -> 282 bytes public/images/datatables/sort_desc.png | Bin 0 -> 260 bytes .../images/datatables/sort_desc_disabled.png | Bin 0 -> 251 bytes public/javascripts/jquery.dataTables.js | 11349 ++++++++++++++++ public/stylesheets/datatable.css | 550 + 13 files changed, 11899 insertions(+) create mode 100644 public/images/datatables/Sorting icons.psd create mode 100644 public/images/datatables/back_disabled.jpg create mode 100644 public/images/datatables/back_enabled.jpg create mode 100644 public/images/datatables/favicon.ico create mode 100644 public/images/datatables/forward_disabled.jpg create mode 100644 public/images/datatables/forward_enabled.jpg create mode 100644 public/images/datatables/sort_asc.png create mode 100644 public/images/datatables/sort_asc_disabled.png create mode 100644 public/images/datatables/sort_both.png create mode 100644 public/images/datatables/sort_desc.png create mode 100644 public/images/datatables/sort_desc_disabled.png create mode 100644 public/javascripts/jquery.dataTables.js create mode 100644 public/stylesheets/datatable.css diff --git a/public/images/datatables/Sorting icons.psd b/public/images/datatables/Sorting icons.psd new file mode 100644 index 0000000000000000000000000000000000000000..53b2e06850767cb57c52b316f0b845b1a8e0ca0e GIT binary patch literal 27490 zcmeG^33yY*)^oGAX}T}$5K3uTx@2jaq_m}N($WHj(w42FkS4cnARAd|!3_{mfhUTH zY|1K#_&`w>abZmhao`qBLJ(pzsNq?Pf|62BSCWcgaLqBHCE&EOUv}>Xi$*(!wu`FiTD>VJ z{+AE7#EbO0ocN&`rQ%YHimuZaPq5Mz69!ajCydc5b@9D(1=$T*4MvNRwrfNUMuW+g z)sPdf(V461EPydOEnY-e>|=7`WvP->Ns2@wjn5T`M51h~t|qHoUF4F4R8D-I-EPTB zORKN1Ppy}wnys~I5~Wg^CYGj2r76IXVjFL=YZ_8awl0hkw;nZZ(^~ZwyWVUPVZEAa zv%{VfACEKgTuc#lT2DR}ht)uG(P`6Y18t;Dc3T=0GR>nLWV3bJtQxb`sIlj2EEa=a ztHHUXjWg*|NmWxVb!NNSR%fGR{uJrSU2qsXEr$0{>^FZqQgf#WvYoIcv?v zG$25c#lA%bWR}WGYwTugrP*xA&BtvbDsvZ9q^gjLhU!f^bGI#2 zx!pT4Hfx|&50)W)DOZx6b{o#C;)FJ=oVJ+_4&3*0B$)~ zF$4*~fLF+prOM1?nOKr6lPDl4lQX0cWKzI^9=R9-@XB#I1LzQB=`v}rf^>;GQ!SAw z)Y8n1LTRB?kzbUNAum)aRbsg$-)&)^lDUfgmyJ<$gZ?glfGM~80mf#P=^JRnFtr}~ zi4C`{M46p-M}n7;o9V;vCg??IDX20V%+?Bc+R@nYh%PTwOKu;F$ubq0>B;G0Wu}JT!^AScXGj>H^kgh0Co!}rv=(3>228plLrh|5O@N^-A%@nM%fL5q4Ezu) zajXDNh;d#r@Dv>5Tx8%uI0jjWi7fa}x+EQ_IEK@OP-$z z*2q+*H+%AKvYtw%9JQGGgG9g;Kq04yQ7|By$z-Xl#URGqDo{%8e~E?WP!UC(Ew}Fc$bb}2q$QFIthf3 zj$(9lAZdI~lu3tr(ha0sZ9M6Yqz;!zI+(-|xwHsK8cv;Jo+Rc}slamzl|>{k6P{v} zR#O8M1H?R+6oXkZZ@vd3C910+cJpKqOiD9`=)4AL1T}_w-RWYV#pF9toX&rThVv#S zL(qzBl49YU5Mu`d60DejTnb-|eQ^zNt?&umFv1f=FxX~*W92cy)f%{M5o&Iowm8YU z0uh#S$zYtxVKBfApYaW<*4XF)8Y5jOPZp!{+B!EKr+GFw4olmH82Z~FMfv2z zB{V=sLEw`_iP}pyzt(DYSbR`CvsDjgk7<*`2r|OL3alN@2?7q%p&EzX+=n*NRyc~P z0|crZZ=qex0)`o5Fr~y;D}vjDu0I>j54#NSGA?c!Ured3*4;P^4^LyTS88f~xWcqF z$k7e<5?gU)Y5!_Cvx}L7+-M)>;5xH)LcT#?>$X%lQ~6>y8YKBTx<=zL*Z~Y4M_cW8 zU?1W}`?7~r*OK!|UdD}N^cVE;5I_VCFq^QggY9O^K!@E%yB!151O{-V+pxlFvmG4j zHhc&)aa{{`(p$P=T}TLl>V1*GOuq^z=wcuh_t%@uEa)3Xlii9>MGYHXCF1Xco*E4O z4*XoBwOL9lig4dRaAHb?k4`a~NDNlKa+K*AU#1k7i9}ws`vD`h7AqK;Dg$Pj4i2FM zJOyAwz1@QGxYRM#V=&CZJub!y1q8S?hHDu(neZ@h9)@*B6Zkg^m)W8->M*<$;Ah7< zu-~)$13Yz{o~{S@Fu(~0hfxnO9v@?jvNSje{dzx|0lFc~tQW#s|S;T&!8CN|Ip^Z-?)cTudT*6|6 z!lxj#VZT>>;;_GXeUU&uJ^8qT-sE%e&`ZO=DCVUBgN$DT z5WV~siW!-TBKF51_Ro6|CprYNN4y3U%F}NBgNGp8^M-ah!}vYGKqs+J#F`07mQ7Ed z3P@F6DbhNu;}{kWzH#Ac9*$yA8`KeXLETUak|H_kh4N4#>Wc=#jB7Z01m;+E$OzBY zdh{5Ygr=hD=mqpLnvWKtPbl{1(iz`QvIoNYB)8Ds-;YngL;g5ikeQ%qUKXes8!TE)ce#nYBzO| z`kFdHouhuEZm`%aAuEQ}ftAQgV<}kqtbVLY*2An?)>zgA))dzBthubEthZPjSld`% zu#T`ASwFEZvk^Ol9mnp>PGKw9D)vBj6}yIQWk125#-77o%3j0X%-+TRlHJHY&%Vy# zaiTdQP70?NrL)6<_+hK<~_=r!F!drmbaC6kavoAna}69;rHa{ z@CWiWd^>+Ce;)rW{ucfr{%QWTppc+WLDHb2psFBK(Bz;wL2m|q9CRq?Owi5Xh~Tcl zS-}H?b;09m|v zT3B>gYFJ5_F6_y$1z{V)4uxF^4-W4do*({5_=NB|;qQj;4?iCf9FZ8IiqJ$n5wRfR z{fJ``mm{MiC6NOojgd1V-;CTHc{Yk4l^9hRrHh&pwJd5!)TwB8beCvVv^M&w=;hI$ zM4yh~#U#a)#Eg!a9`pB@eK8kfqhixzhr~9-E{OdowlR(!ml#(PXNa2_w=V8z+>KTp zTIIK*TRqomO{+t#uC{L1IwmsSoYFpp- zwYHzMJ>M>-U9Wc9cGKFeYxhliR{I|9%iBNNep&lH?Jsxe*r87cONV(Kc62!3F|K1? z$I%^Ucl@~HxlYlYay#ie&F-|N(~qK9QNG9^nk(8Vy4bmW=i<(e&PzJ)>wGglDSmML zbysvB)BV-%2YRr2Wb~-(v7pEPo~)jkJ@q|b z?RhYn3&+Jx$xD-ur3h2xaze#&YwbBLB!?I9Wk?cv?CfTp)J=5v**V4brh|Va@cqU_e#vhsTOl#(v z%yaSt`6&59`7uR|qD(PE@tKmVEKokF{7`v4D>KWUwJz(IUMaoC^m?<`+3cijI(vEc zshs#6P0o^>##~YEsNBW5jlDbf*7RQ5`($31JYC+3yfgXT^GD~um4Bf?TwpC&UvO2G zrFvYoRn1oSQBPCvD~u`}TsXh*`=a=w+M?A(7yD%Nd9=^gVs3H2;unjLm2@c4maHnd z*jL{7@xD9z3Hw#_o8Rwb{~rCV{Wq7gO8b_+RC;_s*8!#h8wa9+B?Dg?_+432+1RoV z2Jr@!4O%ehhjK~zW97RlqANyLyjgLjvY>Ki<+p>o4R#FPJ|uj|h#{+nTpg+&I%{a- zu(V;15Bp+x+u{1*o2vL#L#kF(U4E$Wp_d;zJtAYo)DcG?PI|cE;oXn4eZ=s{M&sbAKm^~=f@^JcH;5;$Co@oJyHF{_9weMIpxXI6Z=ejW0GLfm`Qsl zizm;T{QFZwpV~a7!<30rPCeb{>D5!irdp>Sd#2Yji=O2?tABRibF$~=J$GxGZrbP5 z#nb0ZZzc}?@{<{w#5uwd=N_6wg|c=c7?tA}38 zdu`344vVHQy0KWl_^Ty-mTXv>xOC1k-m-Daepo(a`L5R$udiCscE$7+f2=gEY<#2q zjh(CHt5&_){>_=IS*snZ&%HI`tpk5o|9#V%lr>A%#;u+HHhSCf_K)w3dgti6e(Scs zt9WOyMABhz76~3`!^j>9@u;^`{2iidLP>MrTWWH50@PN z;>ds_2agUu`qi-!#~Q!VeRcM0!`HulGwz$4-%dQvJwENbi0|fn-~Rih|LFdYwT+pL zADvL2*mJVtBn>iliSr8bvV{+|B(_RD3LzrSLz3LFJ6o2mN4AYC^l4%!{zaVf(0Q|OCqdn zvE9T7L~$rKi^Jmbc|lyZPzJyH2TPGh> z@w?8dxOn1Mtt}&N>AI&9)h|`*3b!w_XSO;t_$2+?yHj?2={R+%C~5Zcr{8;d=iz_; z{`&e)j-0+cq-NaIIV(1Ndh~}Y@;*ar>z{dL<;Gpd&RmT|EEbfOL(0VGaWhB}I!mHB zP=c}X(Ol`I7h`Y%Z;t1XrYhP2s ztdj1|1D6NKJ>K#7qGh{!p0>}bxLn@vbmgoH=hwssJA=<4SkZwt!L1RF@{taiQ8g^^ zh+yJd2e2K2jX)OQi2f4}5mKQFnA&2eCOO0dh^W-kQq%*0AjGGl$hs0VG~nC9ycn}0 zR86(>z@w>dE*@}tN&@fN2(^b`rKAMLJ?Z&p^kjtz%Pxm-012ADK?qh0UH5x@kqqI_ zjAe->SrWNO?D|d^s6gz+RCC!Dvpo8v7qgpT%m^2cf+;WDDOd&Yst47vxgJ!acRg5? zTL+cOYSvAZK?FUU*n!-!<-!+ZQqU-)8Nb`R^1>)sdw~Gzyf04*13GUCz=Vj`Eis$2{(pe4OO#ZG{aQtsSpL<1{L@+f}|gm=A3Ya%k{~hDTdeU=X?Y z6Ub=R$7=FPm+O4#l~$9@z1SV1(dkNEpK)~K1bEHAoYpzCw7VRUcrDCYiAxTvQg*A^ z;8Uo`=8CzuP zHNYC5&Rtuf$y}-8)ts3J{n(R_|@1^|*%0cEDOK|y6iV-$h;NINZIj)^R z>;Qb&&%qy1^i+UIF2#Z<$Y@K4-2%iRxMc))&8RWyOUjGNA$1<(^G#|zUhIL;oP+mX zBg!wTaJL*$QDiMK-Er^FEkzs?xX8X={*S8?uox(D_dnkqL$?%eP*h~|*&2krt?r#j zOa^+9)txu4vRiw9mB+h)3)ib0{2Ra^EQK#(gXIKlZ>+Jx&IuVGyz1;4OHue)7zPeI zZxUW6!lP+4o}|Hj072p1eGKCoPb@xO}Z42v7m+^=r`>R0}K6X5@f9e;wf&uh#(`TaAm;T0=FiF9#P3r}d^9{1!{B ziqm6EW8B?_$-`!;@unNJ2E2RhW3QS9s|9e_MlCPA3y$9s@HTIl%QAflQ#`Q@ykSBr z9_Q?38L%w5?%Og#wYlCTqD^?*$JNF9T-L$w9zvuBo796%MC9KP8hA-E0Myb#d@T1M zEtDs9I)B@La_OKQW1AEK>QKpX=O%)i%s-SL|MQ79DhlmUUi#Rl<;h_aP zx97r~Mu$O!s4X5GGYslJsCegLSRi7KabUa(u>JX#DAg}Z*|Y? zVD0*Teixkg^MhgVe?=Q=Afvt0S;ad+pdAQRVW3G2?Y#qlIe3s0H#sl0!vLD|DR zqISayHM#Ko5I<~N*zDR2-`^cLUb^PWc!->AWwHKYd0TAcPzPY=)!HkCK$=U231X{2~oTgP@Nq&5v<}={o$mV#5d6m7{_io(VQCqambK- Y16@Zf7?Q8!JB-NJ(KYBs*Ff|C0Zg4MIsgCw literal 0 HcmV?d00001 diff --git a/public/images/datatables/back_disabled.jpg b/public/images/datatables/back_disabled.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1e73a546e3609636f9cf4c543c2a4fe4050866c3 GIT binary patch literal 612 zcmex=C5UDGKfoZ!!63{a%*-grz$D1XEXer(2tyUn z+02YUSHb}sD?0}l&^EXLguy7lz`(-H!phDIlVN0HVHIR%V-ON%S2Pq6HFFG{Xryc+ zmRQ($@S#&sld*HEn>dFcBLmQ_$YwDLDjGT#PW*q1fd^^;oX+iLI)z|@;wX;U8Xr> zT8Qgxn;R!Cottl}#(8Ri${a3d=J*$R5qBRbK9bRJe*4BYwq9(uIM4mLS9%TSY?*y6 zE==owYH)vf>dq6(Uzum+IyVJqc3N&dRPD57vW(=l(=(DZ-K_!LSx zwPk@>bN@4B-OI9gCf>Wc_;CDAS%c@BJZ}kBlbDz~@y-*&Y^_U+@};q`xNUthN} On^Cj9W}Eu@|2F})$f79# literal 0 HcmV?d00001 diff --git a/public/images/datatables/back_enabled.jpg b/public/images/datatables/back_enabled.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a6d764c79c7a2047b6bf65bf9d96fcafd5161e38 GIT binary patch literal 807 zcmex=C5UDGKfoZ!!63{a%*-grz$D1XEXer(2*U&h z1}0{pBN2d|gO!bok%5r~RhW^Pm5qg+Lx6#Sm5Gs^od+h%$i&RTD#*spplB#0EW!~u z(MZ`u)XXulu<_tSr=TWdXR%Z_aiE1jZ7eM8Fl&IpCkWK1$Y$s$#2&a&N#sM~M5iWU z<^Q)Bc$gW1&SMs2uxGfw`}E06etb^r9k|jMc@Dlk(IlSD-Wwx*G{%4ThHhR%HnTsw zbu3z!=BtbC=-In5f5nt;je{+vrKZdWckk$U`c^FEYC6}Jn=lG)17QB>6z3khm7PHhg<-pEGSO0F>VA}N6`w53!8pr$>MbUW| zMEsFn=(%w+^5v0yp7Lmb+~3PpI4(#G~bQ$y_;t)oo+U5`bnSHZcn){CH~m7Ys2rz z2!R@xgGrCtcPz?eOPOzKr2P26p~(mTGjR1-c3?psA>uUGsyt|R2Ts3~!B zN71DGSrW%W*`w8#2{O$LX)%%8*<4Ycyr;tN(Hm8|M=_HtvR>P P+Rcr(b5Xx$|NkZcT0jT? literal 0 HcmV?d00001 diff --git a/public/images/datatables/favicon.ico b/public/images/datatables/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..6eeaa2a0d393190ce748107222d9a026f992e4a7 GIT binary patch literal 894 zcmZQzU<5(|0R|u`!H~hsz#zuJz@P!dKp_SNAO?xUfG{@$0|>*weY}F;x;`B~@Mg`jC-vo5%k$%{OoKy1zkdA+(K2o3 zEDKGQQ~gcX+N!TtEj1|9e2tL(rheJva*5dKY#gZsIRxM zB;!h75Wi?9l5?>z>S-`t`=OAFp5C?(007k>qM(bnVuyja#;c*qL9N z(Q%=@`f7d2?T&^wySIHjy#MKh?rZrO7i!Bt-@f(!@WDrwB`uD&)%Eqcd3o(gVV7ri zp6ji@(BF7(+uE0x&)(R!?s#YIg_7JW1=-KHZv1@s(DSxD9<^am-%+f#!uU}z9=j>5*%b{WE2`20#tutPWS1# zz4s39d~xOMwaqJzOl{rMU%#oo=xj&xyY1WF&71o?Hs-LOkAD^4SRbV?yXz(e#efNwKXpT1J^s+yE;1`KXoe5&f?1A$tYso}un=1c3AzCL>B&D=RpqN3i1hL&lnclP!D`u*G0#bsJv!oAH) z-rc|T`^Wdkrw?7(xb)_p%`Y#WdAn`Pvz+W#0Rbo6-Ha3zp1gPgjDkZ)k6Gz!@9D0( zG^hL4?)Be4zWep#`{$>RUhUoaq@wgyfd9*oU{6tz+SXQhl3284sj;T&{L=I*bGxt4 z@4vfX%A<*$FN^a&MMa!C5UDGKfoZ!!63{a%*-grz$D1XEXer(2tySE z1JEp>E8&2Nm4%I+g9~UMTnNHo6kuRrVP;|B;)Y2uGBL9Vva&GlzGLeh=CM*_u2I6%$1L~6;-1yGa|?*FvavCW+Av>{Flt>+*?d?iZ`9)^O?_H|D3t8;Og6lD<#WHQvW(mo1ETsD>A@mr8@sqg(?1R z%d@qrmWLWmIn1#pQuo}{brVW*C8t%EDXbOt_DabwXgGW3;wDX1;~7hWrYL+ZX=qre z&-y^2q0zm?-FJb#(AWJTHOJ-3`S`D|JEhzC{KvY_h0kZinXP-?w!g_L@p#JQO^!z$ mYCgVwS7#SryXs@?>&L&t^Z(SozHVt~@Z#>58}s)6zX<@qORf(9 literal 0 HcmV?d00001 diff --git a/public/images/datatables/forward_enabled.jpg b/public/images/datatables/forward_enabled.jpg new file mode 100644 index 0000000000000000000000000000000000000000..598c075f13ab0e4bbe05a4eeea7cf777ba73cf03 GIT binary patch literal 852 zcmex=C5UDGKfoZ!!63{a%*-grz$D1XEXer(2tywO z0~0eN(3Nn&%Fe>Z!NthH$ciG&$iU3P%Em6hz`z1zu=B#yGcqx=2(q#<2(fb*3M-3< zDh5t8G7&R#Oe}1C=oHjs?40T*F37+LbR`oj+(bq}CT0c}Rz*W0Hg?Cv#)(EB0yioN zi~PUEzyov)lOVGogFVA5dn3uE%Z<*rn&ugY^FIG9<<=87L*?mOy^~u`Rq(Eg+UDw^ z@^Uh}zJ$t~P2YpRT#sm2p`oEt8zJ-EBRfY;>b?DxZo!24Z)Q(O&}!Mbt}27`dKa(x zr)AHkZg5MjKa&4$syaupXWNXgp32dQs_rj-?|M=Q9pN^hZg^mV7=UWs>}D5 z$40(ME7Q1T4h8p}ZJ8O!G-Xk84;P~#ci!&o`A5I3kG@gLm*VlJbn%kvSqIh?3Y?HQ zAX0NL>XIkpf=MN83*!%}9-aN5)p7^py2Am>F9gW&pFSp#V0dxX#ZPW;UoD(gaI@8E z(rNQz1DBIiM13!LtZ|85lHB@$wW~n0fhRz-$?R&m{@z;uI}N|35As;}zdXB<I#+N1k=r*G-&mI78vJ_Ezs!yIV%J}l7d*c9>7Bn;xyLT84V&`$ z@9WMTAJ6DZyR)~=FS6P2e3MDv7lHB>{C&1H^7EJ9PIf=EdUwUYtGE9%$X9(m*ZWLf M_rC2^{;K~s0TRp{r2qf` literal 0 HcmV?d00001 diff --git a/public/images/datatables/sort_asc.png b/public/images/datatables/sort_asc.png new file mode 100644 index 0000000000000000000000000000000000000000..a56d0e21902ce620cd57f87fa58a6d3e804135cf GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>l2Ka=y{`vF&J9*~+_wWBdfBFCW_y37A{>@tOZ^^2EJ(K_a|Nno&o$%d2jr=7+e!)O45@5J; zXU0OHl&hzUV@SoVE9Xw~GAQscA1s)^?0*rXqM}DeLv7?|BU8z@QCOGupVzP9Hek5&L+Gnl%BD>~0~tJB{an^LB{Ts5b0m7= literal 0 HcmV?d00001 diff --git a/public/images/datatables/sort_asc_disabled.png b/public/images/datatables/sort_asc_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..b7e621ef1c68da839ce077cec6e201a55c1ba168 GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=)2l#}z{`>du$&)AV-@pIyd9;k)4B*-rq$b|z2+ZRf0KnW*L z7srr_TUXBQ7GeV6G?@}f$7k6Jhw{qGm$-QDzD`qrDaZ3s$b2XF;fBBPU nqjNDy&Nw~puHFA+-v&m8`?>-yUFVHK{$ucT^>bP0l+XkKhw^g= literal 0 HcmV?d00001 diff --git a/public/images/datatables/sort_both.png b/public/images/datatables/sort_both.png new file mode 100644 index 0000000000000000000000000000000000000000..839ac4bb5b0b414834c950de9deafff6dd94ed2d GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=)2l#}z{`>du$&)AV-@pIyd9;k)4B*-rq$b|z2+ZRf0K#2rT z7srr_TRjsz`4|*ASOSl%eg6Ob+(JtRwX|O@S}a^IESQCgTe~DWM4fR9b+X literal 0 HcmV?d00001 diff --git a/public/images/datatables/sort_desc.png b/public/images/datatables/sort_desc.png new file mode 100644 index 0000000000000000000000000000000000000000..90b295159df995329b3a84340d32761f3d1dbade GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>l2Ka=y{`vF&J9*~+_wWBdfBFCW_y37A{>@tOZ^^2EJ(K_a|Nno&o$%d2jr=7+e!)O45@5J; zXU0OHl#{26V@SoVqz8vtc$~v}dc~O{CLEF2anNavMpdMP)0v(X&o(k0opIq!LdvtJ wj~up7@0`wiSoe($&y07EkGxK6U|?nlJSz0{?wJWUfu=Edy85}Sb4q9e0C9MGu>b%7 literal 0 HcmV?d00001 diff --git a/public/images/datatables/sort_desc_disabled.png b/public/images/datatables/sort_desc_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..2409653dc94cd21a281a31c0e3819323b84704b7 GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=)2l#}z{`>du$&)AV-@pIyd9;k)4B*-rq$b|z2+ZRf0KnX`r z7srr_TS*TNvhX;E^>j)zMNBv%VR68zN24n8K+>5N&kUJbBQCy4c=qgBiu3mAQ)hD? n)_oJhGvi(If!Apb49pB3GXz|UGFKJ=4P)?h^>bP0l+XkK*57ji literal 0 HcmV?d00001 diff --git a/public/javascripts/jquery.dataTables.js b/public/javascripts/jquery.dataTables.js new file mode 100644 index 000000000..1d84033a8 --- /dev/null +++ b/public/javascripts/jquery.dataTables.js @@ -0,0 +1,11349 @@ +/** + * @summary DataTables + * @description Paginate, search and sort HTML tables + * @version 1.9.0.dev.2 + * @file jquery.dataTables.js + * @author Allan Jardine (www.sprymedia.co.uk) + * @contact www.sprymedia.co.uk/contact + * + * @copyright Copyright 2008-2012 Allan Jardine, all rights reserved. + * + * This source file is free software, under either the GPL v2 license or a + * BSD style license, available at: + * http://datatables.net/license_gpl2 + * http://datatables.net/license_bsd + * + * This source file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. + * + * For details please refer to: http://www.datatables.net + */ + +/*jslint evil: true, undef: true, browser: true */ +/*globals $, jQuery,_fnExternApiFunc,_fnInitialise,_fnInitComplete,_fnLanguageCompat,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnCreateTr,_fnGatherData,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnServerParams,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAdjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnDetectHeader,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap,_fnGetRowData,_fnGetCellData,_fnSetCellData,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnApplyColumnDefs,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnJsonString*/ + +(/** @lends */function($, window, document, undefined) { + /** + * DataTables is a plug-in for the jQuery Javascript library. It is a + * highly flexible tool, based upon the foundations of progressive + * enhancement, which will add advanced interaction controls to any + * HTML table. For a full list of features please refer to + * DataTables.net. + * + * Note that the DataTable object is not a global variable but is + * aliased to jQuery.fn.DataTable and jQuery.fn.dataTable through which + * it may be accessed. + * + * @class + * @param {object} [oInit={}] Configuration object for DataTables. Options + * are defined by {@link DataTable.defaults} + * @requires jQuery 1.3+ + * + * @example + * // Basic initialisation + * $(document).ready( function { + * $('#example').dataTable(); + * } ); + * + * @example + * // Initialisation with configuration options - in this case, disable + * // pagination and sorting. + * $(document).ready( function { + * $('#example').dataTable( { + * "bPaginate": false, + * "bSort": false + * } ); + * } ); + */ + var DataTable = function( oInit ) + { + + + /** + * Add a column to the list used for the table with default values + * @param {object} oSettings dataTables settings object + * @param {node} nTh The th element for this column + * @memberof DataTable#oApi + */ + function _fnAddColumn( oSettings, nTh ) + { + var oDefaults = DataTable.defaults.columns; + var iCol = oSettings.aoColumns.length; + var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { + "sSortingClass": oSettings.oClasses.sSortable, + "sSortingClassJUI": oSettings.oClasses.sSortJUI, + "nTh": nTh ? nTh : document.createElement('th'), + "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', + "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], + "mDataProp": oDefaults.mDataProp ? oDefaults.oDefaults : iCol + } ); + oSettings.aoColumns.push( oCol ); + + /* Add a column specific filter */ + if ( !oSettings.aoPreSearchCols[ iCol ] ) + { + oSettings.aoPreSearchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch ); + } + else + { + var oPre = oSettings.aoPreSearchCols[ iCol ]; + + /* Don't require that the user must specify bRegex, bSmart or bCaseInsensitive */ + if ( oPre.bRegex === undefined ) + { + oPre.bRegex = true; + } + + if ( oPre.bSmart === undefined ) + { + oPre.bSmart = true; + } + + if ( oPre.bCaseInsensitive === undefined ) + { + oPre.bCaseInsensitive = true; + } + } + + /* Use the column options function to initialise classes etc */ + _fnColumnOptions( oSettings, iCol, null ); + } + + + /** + * Apply options for a column + * @param {object} oSettings dataTables settings object + * @param {int} iCol column index to consider + * @param {object} oOptions object with sType, bVisible and bSearchable + * @memberof DataTable#oApi + */ + function _fnColumnOptions( oSettings, iCol, oOptions ) + { + var oCol = oSettings.aoColumns[ iCol ]; + + /* User specified column options */ + if ( oOptions !== undefined && oOptions !== null ) + { + if ( oOptions.sType !== undefined ) + { + oCol.sType = oOptions.sType; + oCol._bAutoType = false; + } + + $.extend( oCol, oOptions ); + _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); + + /* iDataSort to be applied (backwards compatibility), but aDataSort will take + * priority if defined + */ + if ( oOptions.iDataSort !== undefined ) + { + oCol.aDataSort = [ oOptions.iDataSort ]; + } + _fnMap( oCol, oOptions, "aDataSort" ); + } + + /* Cache the data get and set functions for speed */ + oCol.fnGetData = _fnGetObjectDataFn( oCol.mDataProp ); + oCol.fnSetData = _fnSetObjectDataFn( oCol.mDataProp ); + + /* Feature sorting overrides column specific when off */ + if ( !oSettings.oFeatures.bSort ) + { + oCol.bSortable = false; + } + + /* Check that the class assignment is correct for sorting */ + if ( !oCol.bSortable || + ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableNone; + oCol.sSortingClassJUI = ""; + } + else if ( oCol.bSortable || + ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) ) + { + oCol.sSortingClass = oSettings.oClasses.sSortable; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUI; + } + else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableAsc; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed; + } + else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 ) + { + oCol.sSortingClass = oSettings.oClasses.sSortableDesc; + oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed; + } + } + + + /** + * Adjust the table column widths for new data. Note: you would probably want to + * do a redraw after calling this function! + * @param {object} oSettings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnAdjustColumnSizing ( oSettings ) + { + /* Not interested in doing column width calculation if autowidth is disabled */ + if ( oSettings.oFeatures.bAutoWidth === false ) + { + return false; + } + + _fnCalculateColumnWidths( oSettings ); + for ( var i=0 , iLen=oSettings.aoColumns.length ; i