[#82] Merge branch 'master' into 65-build_repo
Conflicts: spec/models/cancan_spec.rb
This commit is contained in:
commit
7d3dd5eaf1
|
@ -0,0 +1,72 @@
|
|||
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 => [:edit, :update, :destroy]
|
||||
|
||||
authorize_resource :only => [:show, :edit, :update, :destroy]
|
||||
authorize_resource :project, :only => [:index]
|
||||
|
||||
def index
|
||||
@comments = @commentable.comments
|
||||
end
|
||||
|
||||
def create
|
||||
@comment = @commentable.comments.build(params[:comment])
|
||||
@comment.user = current_user
|
||||
if @comment.save
|
||||
flash[:notice] = I18n.t("flash.comment.saved")
|
||||
redirect_to :back
|
||||
else
|
||||
flash[:error] = I18n.t("flash.comment.saved_error")
|
||||
render :action => 'new'
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@issue = @commentable
|
||||
@project = @issue.project
|
||||
end
|
||||
|
||||
def update
|
||||
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)
|
||||
else
|
||||
flash[:error] = I18n.t("flash.comment.saved_error")
|
||||
render :action => 'new'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@comment.destroy
|
||||
|
||||
flash[:notice] = t("flash.comment.destroyed")
|
||||
redirect_to :back
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_commentable
|
||||
#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
|
|
@ -0,0 +1,78 @@
|
|||
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]
|
||||
|
||||
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'
|
||||
@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
|
||||
@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 edit
|
||||
@user_id = @issue.user_id
|
||||
@user_uname = @issue.user.uname
|
||||
end
|
||||
|
||||
def update
|
||||
@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")
|
||||
redirect_to show_issue_path(@project, @issue.serial_id)
|
||||
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_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
|
|
@ -0,0 +1,2 @@
|
|||
module CommentsHelper
|
||||
end
|
|
@ -0,0 +1,2 @@
|
|||
module IssuesHelper
|
||||
end
|
|
@ -3,7 +3,7 @@ class Ability
|
|||
|
||||
def initialize(user)
|
||||
user ||= User.new # guest user (not logged in)
|
||||
|
||||
|
||||
if user.admin?
|
||||
can :manage, :all
|
||||
else
|
||||
|
@ -77,7 +77,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)
|
||||
|
@ -99,10 +99,40 @@ class Ability
|
|||
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|
|
||||
|
@ -125,7 +155,7 @@ class Ability
|
|||
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|
|
||||
|
@ -145,11 +175,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
|
||||
|
@ -166,6 +197,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
|
||||
|
||||
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|
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
class Comment < ActiveRecord::Base
|
||||
belongs_to :commentable, :polymorphic => true
|
||||
belongs_to :user
|
||||
|
||||
validates :body, :user_id, :commentable_id, :commentable_type, :presence => true
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
class Issue < ActiveRecord::Base
|
||||
STATUSES = ['open', 'closed']
|
||||
|
||||
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
|
||||
self.serial_id = self.project.issues.count
|
||||
self.save!
|
||||
end
|
||||
end
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"), show_issue_path(@issue.project, @issue.serial_id), :class => "text_button_padding link_button"
|
|
@ -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}
|
|
@ -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"
|
|
@ -0,0 +1,15 @@
|
|||
%table.table
|
||||
%tr
|
||||
%th.first= t("activerecord.attributes.issue.title")
|
||||
%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
|
||||
= issue.status
|
||||
%td.last
|
||||
= 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
|
|
@ -0,0 +1,11 @@
|
|||
.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)
|
||||
.content
|
||||
%h2.title
|
||||
= t("layout.issues.edit_header")
|
||||
.inner
|
||||
= form_for @issue, :url => project_issue_path(@project, @issue), :html => { :class => :form } do |f|
|
||||
= render :partial => "form", :locals => {:f => f}
|
|
@ -0,0 +1,22 @@
|
|||
.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? :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'
|
||||
.actions-bar.wat-cf
|
||||
.actions
|
||||
= will_paginate @issues, :param_name => :issue_page
|
|
@ -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}
|
|
@ -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.serial_id) 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('activerecord.attributes.issue.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.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), :method => :post, :html => { :class => :form } do |f|
|
||||
= render :partial => "comments/form", :locals => {:f => f}
|
|
@ -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"}
|
||||
|
|
|
@ -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"), new_project_build_list_path(@project)
|
||||
%li= link_to t("layout.projects.issues"), project_issues_path(@project)
|
||||
|
||||
.content
|
||||
.inner
|
||||
|
|
|
@ -83,6 +83,7 @@ ru:
|
|||
confirm_regenerate: Вы уверены, что хотите перегенерировать эту пару логин/пароль?
|
||||
regenerate_btn: Перегенерировать
|
||||
warning_message: Примечание - При получении новых данных старые становятся недействительными
|
||||
|
||||
categories:
|
||||
list: Список
|
||||
new: Создать
|
||||
|
@ -93,6 +94,23 @@ ru:
|
|||
edit_header: Редактировать категорию
|
||||
confirm_delete: Вы уверены, что хотите удалить эту категорию?
|
||||
|
||||
issues:
|
||||
list: Список
|
||||
edit: Редактировать
|
||||
comments_header: Комментарии
|
||||
new: Новая задача
|
||||
list_header: Список
|
||||
confirm_delete: Вы уверены, что хотите удалить эту задачу?
|
||||
new_header: Новая задача
|
||||
statuses:
|
||||
open: Открытые
|
||||
closed: Закрытые
|
||||
any: Все
|
||||
|
||||
comments:
|
||||
confirm_delete: Вы уверены, что хотите удалить комментарий?
|
||||
new_header: Новый комментарий
|
||||
|
||||
platforms:
|
||||
admin_id: Владелец
|
||||
build_all: Собрать все
|
||||
|
@ -214,6 +232,7 @@ ru:
|
|||
collaborators: Коллабораторы
|
||||
groups: Группы
|
||||
edit_collaborators: Изменить список участников
|
||||
issues: Задачи
|
||||
|
||||
collaborators:
|
||||
back_to_proj: Вернуться к проекту
|
||||
|
@ -334,7 +353,7 @@ ru:
|
|||
|
||||
downloads:
|
||||
statistics_refreshed: Статистика обновлена
|
||||
|
||||
|
||||
collaborators:
|
||||
successfully_changed: Список коллабораторов успешно изменен
|
||||
error_in_changing: Ошибка изменения списка коллабораторов
|
||||
|
@ -355,12 +374,19 @@ ru:
|
|||
failed: Не удалось автоматизировать сборку!
|
||||
cancel: Автоматическая сборка проекта отменена!
|
||||
cancel_failed: Не удалось отменить автоматическую сборку проекта!
|
||||
|
||||
|
||||
category:
|
||||
saved: Категория успешно сохранена
|
||||
save_error: Не удалось сохранить категорию
|
||||
destroyed: Категория успешно удалена
|
||||
|
||||
comment:
|
||||
saved: Комментарий успешно сохранен
|
||||
destroyed: Комментарий удален
|
||||
|
||||
issue:
|
||||
saved: Задача успешно сохранена
|
||||
|
||||
project:
|
||||
saved: Проект успешно сохранен
|
||||
save_error: Не удалось сохранить проект
|
||||
|
@ -456,7 +482,18 @@ ru:
|
|||
pl: Платформа
|
||||
arch_id: Архитектура
|
||||
arch: Архитектура
|
||||
|
||||
|
||||
comment:
|
||||
body: Содержание
|
||||
user: Автор
|
||||
|
||||
issue:
|
||||
title: Заголовок
|
||||
body: Содержание
|
||||
user: Назначено
|
||||
project: Проект
|
||||
status: Статус
|
||||
|
||||
private_user:
|
||||
login: Логин
|
||||
password: Пароль
|
||||
|
@ -537,6 +574,7 @@ ru:
|
|||
repository: Репозиторий
|
||||
created_at: Создан
|
||||
updated_at: Обновлен
|
||||
has_issues: Включить трэкер
|
||||
|
||||
rpm:
|
||||
name: Название
|
||||
|
|
|
@ -78,7 +78,12 @@ 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 :comments, :only => [:edit, :create, :update, :destroy]
|
||||
end
|
||||
resource :repo, :controller => "git/repositories", :only => [:show]
|
||||
resources :build_lists, :only => [:index, :new, :create]
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
23
db/schema.rb
23
db/schema.rb
|
@ -85,6 +85,15 @@ ActiveRecord::Schema.define(:version => 20111221194422) 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
|
||||
|
@ -142,6 +151,19 @@ ActiveRecord::Schema.define(:version => 20111221194422) 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"
|
||||
|
@ -213,6 +235,7 @@ ActiveRecord::Schema.define(:version => 20111221194422) 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"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe CommentsController do
|
||||
|
||||
end
|
|
@ -0,0 +1,194 @@
|
|||
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)
|
||||
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
|
|
@ -0,0 +1,6 @@
|
|||
# Read about factories at http://github.com/thoughtbot/factory_girl
|
||||
|
||||
FactoryGirl.define do
|
||||
factory :comment do
|
||||
end
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
Factory.define(:issue) do |p|
|
||||
p.title { Factory.next(:string) }
|
||||
p.body { Factory.next(:string) }
|
||||
p.association :user, :factory => :user
|
||||
p.status "open"
|
||||
end
|
|
@ -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
|
|
@ -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
|
|
@ -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,18 +129,22 @@ 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
|
||||
|
||||
[:read, :update].each do |action|
|
||||
[:read, :create, :new].each do |action|
|
||||
it "should be able to #{ action } project" do
|
||||
@ability.should be_able_to(action, @project)
|
||||
end
|
||||
|
@ -174,11 +179,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, :destroy].each do |action|
|
||||
|
@ -193,6 +205,12 @@ describe CanCan do
|
|||
@ability.should be_able_to(action, @build_list)
|
||||
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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Comment do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Issue do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
|
@ -1,5 +1,18 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe ProjectToRepository do
|
||||
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
|
||||
p2r = @second_repo.project_to_repositories.build :project_id => @project.id
|
||||
p2r.should_not be_valid
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue