"
res += ""
- res += diff_display.render(Git::Diff::InlineCallback.new)
+ res += renderer diff_display.data #diff_display.render(Git::Diff::InlineCallback.new comments, path)
+ res += tr_line_comments(comments) if in_discussion
res += ""
res += "
"
-
res.html_safe
end
+ ########################################################
+ # FIXME: Just to dev, remove to lib. Really need it?
+ ########################################################
+ def prepare(args)
+ @url, @diff_counter, @in_discussion = args[:url], args[:diff_counter], args[:in_discussion]
+ @filepath, @line_comments, @in_wiki = args[:filepath], args[:comments], args[:in_wiki]
+ @add_reply_id, @num_line = if @in_discussion
+ [@line_comments[0].id, @line_comments[0].data[:line].to_i - @line_comments[0].data[:strings].lines.count.to_i-1]
+ else
+ [nil, -1]
+ end
+ end
+
+ def headerline(line)
+ set_line_number
+ "
"
+ end
+ res << link_to(t('layout.comments.new_inline'), new_comment_path, :class => 'new_inline_comment button')
+ res << "
"
+ end
+
+ def new_comment_path
+ hash = {:path => @filepath, :line => @num_line}
+ if @commentable.is_a? Issue
+ project_new_line_pull_comment_path(@project, @commentable, hash.merge({:in_reply => @add_reply_id}))
+ elsif @commentable.is_a? Grit::Commit
+ new_line_commit_comment_path(@project, @commentable, hash)
+ end
+ end
end
diff --git a/app/helpers/git_helper.rb b/app/helpers/git_helper.rb
index 853c69b8a..d9e1851e8 100644
--- a/app/helpers/git_helper.rb
+++ b/app/helpers/git_helper.rb
@@ -68,7 +68,7 @@ module GitHelper
[ ['Branches', project.repo.branches.map{|b| "latest_#{b.name}"}],
['Tags', project.repo.tags.map(&:name)] ]
end
-
+
def split_commits_by_date(commits)
commits.sort{|x, y| y.authored_date <=> x.authored_date}.inject({}) do |h, commit|
dt = commit.authored_date
@@ -80,4 +80,3 @@ module GitHelper
end
end
end
-
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 40ddb89a2..a04f1c531 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -1,5 +1,25 @@
# -*- encoding : utf-8 -*-
module ProjectsHelper
+ def options_for_filters(all_projects, groups, owners)
+ projects_count_by_groups = all_projects.where(:owner_id => groups, :owner_type => 'Group').
+ group(:owner_id).count
+ projects_count_by_owners = all_projects.where(:owner_id => owners, :owner_type => 'User').
+ group(:owner_id).count
+ (groups + owners).map do |o|
+ class_name = o.class.name
+ {
+ :id => "#{class_name.downcase}-#{o.id}",
+ :color => '0054a6',
+ :selected => false,
+ :check_box_name => class_name.downcase.pluralize,
+ :check_box_value => o.id,
+ :name => content_tag(:div, content_tag(:span, o.uname, :class => class_name.downcase)),
+ :uname => o.uname, # only for sorting
+ :count => o.is_a?(User) ? projects_count_by_owners[o.id] : projects_count_by_groups[o.id]
+ }
+ end.sort_by{ |f| f[:uname] }
+ end
+
def git_repo_url(name)
if current_user
"#{request.protocol}#{current_user.uname}@#{request.host_with_port}/#{name}.git"
diff --git a/app/helpers/pull_request_helper.rb b/app/helpers/pull_request_helper.rb
new file mode 100644
index 000000000..db6ee030d
--- /dev/null
+++ b/app/helpers/pull_request_helper.rb
@@ -0,0 +1,54 @@
+# -*- encoding : utf-8 -*-
+module PullRequestHelper
+ def merge_activity comments, commits
+ common_comments, pull_comments = comments.partition {|c| c.data.blank?}
+ common_comments = common_comments.map{ |c| [c.created_at, c] }
+ pull_comments = pull_comments.group_by(&:data).map{|data, c| [c.first.created_at, [data || {}, [c].flatten]]}
+ commits = commits.map{ |c| [(c.committed_date || c.authored_date), c] }
+ (common_comments + pull_comments + commits).sort_by{ |c| c[0] }.map{ |c| c[1] }
+ end
+
+ def pull_status_label pull
+ statuses = {'ready' => 'success', 'closed' => 'important', 'merged' => 'important', 'blocked' => 'warning'}
+ content_tag :span, t("projects.pull_requests.statuses.#{pull.status}"), :class => "label-bootstrap label-#{statuses[pull.status]}"
+ end
+
+ def pull_status pull
+ if %w(blocked merged closed ready open).include? pull.status
+ t("projects.pull_requests.#{pull.status}", :user => pull.issue.closer.try(:uname), :to_ref => show_ref(pull, 'to'),
+ :from_ref => show_ref(pull, 'from'), :time => pull.issue.closed_at).html_safe
+ else
+ raise "pull id (#{pull.id}) wrong status #{pull.status} "
+ end
+ end
+
+ def pull_header pull
+ str = "#{t '.header'} #{t 'from'} \
+ #{show_ref pull, 'from'} \
+ #{t 'into'} \
+ #{show_ref pull, 'to'}"
+ str << " #{t 'by'} #{link_to pull.user.uname, user_path(pull.user)}" if pull.persisted?
+ str.html_safe
+ end
+
+ #helper for helpers
+ def show_ref pull, which, limit = 30
+ project, ref = pull.send("#{which}_project"), pull.send("#{which}_ref")
+ link_to "#{project.owner.uname.truncate limit}/#{project.name.truncate limit}: #{ref.truncate limit}", ref_path(project, ref)
+ end
+
+ def ref_path project, ref
+ return tree_path(project, ref) if project.repo.branches_and_tags.map(&:name).include? ref
+ return commit_path(project, ref) if project.repo.commit ref
+ '#'
+ end
+
+ def ref_selector_options(project, current)
+ res = []
+ value = Proc.new {|t| [t.name.truncate(40)]}
+ res << [I18n.t('layout.git.repositories.branches'), project.repo.branches.map(&value)]
+ res << [I18n.t('layout.git.repositories.tags'), project.repo.tags.map(&value)]
+
+ grouped_options_for_select(res, current)
+ end
+end
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 94d80dcd0..1424c819f 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -5,12 +5,13 @@ module UsersHelper
avatar_url(User.where(:email => email).first || User.new(:email => email), size)
end
- def avatar_url(user, size = :small)
- return image_path('group32.png') if user.kind_of? Group
- if user.try('avatar?')
- user.avatar.url(size)
+ def avatar_url(subject, size = :small)
+ if subject.try('avatar?')
+ subject.avatar.url(size)
+ elsif subject.kind_of? Group
+ image_path('ava-big.png')
else
- gravatar_url(user.email, user.avatar.styles[size].geometry.split('x').first)
+ gravatar_url(subject.email, subject.avatar.styles[size].geometry.split('x').first)
end
end
diff --git a/app/mailers/feedback_mailer.rb b/app/mailers/feedback_mailer.rb
index 1d38f4e4d..272849100 100644
--- a/app/mailers/feedback_mailer.rb
+++ b/app/mailers/feedback_mailer.rb
@@ -4,6 +4,7 @@ class FeedbackMailer < ActionMailer::Base
default :to => FBM_CONFIG['email'],
:cc => FBM_CONFIG['cc'],
:bcc => FBM_CONFIG['bcc']
+ default_url_options.merge!(:protocol => 'https') if APP_CONFIG['mailer_https_url']
include Resque::Mailer # send email async
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
index e66f06b79..057c735cc 100644
--- a/app/mailers/user_mailer.rb
+++ b/app/mailers/user_mailer.rb
@@ -2,6 +2,7 @@
class UserMailer < ActionMailer::Base
default :from => APP_CONFIG['do-not-reply-email']
+ default_url_options.merge!(:protocol => 'https') if APP_CONFIG['mailer_https_url']
include Resque::Mailer # send email async
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 11a0e84b0..2cf367642 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -14,20 +14,27 @@ class Ability
# Shared rights between guests and registered users
can [:show, :archive], Project, :visibility => 'open'
+ can :get_id, Project, :visibility => 'open' # api
+ can :archive, Project, :visibility => 'open'
can :read, Issue, :project => {:visibility => 'open'}
+ can :read, PullRequest, :to_project => {:visibility => 'open'}
can :search, BuildList
can [:read, :log, :everything], BuildList, :project => {:visibility => 'open'}
can :read, ProductBuildList#, :product => {:platform => {:visibility => 'open'}} # double nested hash don't work
can :read, Advisory
-
+
# Core callbacks
can [:publish_build, :status_build, :pre_build, :post_build, :circle_build, :new_bbdt], BuildList
# Platforms block
- can [:show, :members, :advisories], Platform, :visibility == 'open'
- can [:read, :projects_list], Repository, :platform => {:visibility => 'open'}
+ can [:show, :members, :advisories], Platform, :visibility => 'open'
+ can :platforms_for_build, Platform, :visibility => 'open', :platform_type => 'main'
+ can [:read, :projects_list, :projects], Repository, :platform => {:visibility => 'open'}
can :read, Product, :platform => {:visibility => 'open'}
+ can :show, Group
+ can :show, User
+
if user.guest? # Guest rights
# can [:new, :create], RegisterRequest
else # Registered user rights
@@ -44,10 +51,8 @@ class Ability
end
if user.user?
- can [:show, :autocomplete_user_uname], User
-
- can [:read, :create, :autocomplete_group_uname], Group
- can [:update, :manage_members], Group do |group|
+ can [:read, :create], Group
+ can [:update, :manage_members, :members, :add_member, :remove_member, :update_member], Group do |group|
group.actors.exists?(:actor_type => 'User', :actor_id => user.id, :role => 'admin') # or group.owner_id = user.id
end
can :destroy, Group, :owner_id => user.id
@@ -57,14 +62,18 @@ class Ability
can :read, Project, :visibility => 'open'
can [:read, :archive], Project, :owner_type => 'User', :owner_id => user.id
can [:read, :archive], Project, :owner_type => 'Group', :owner_id => user.group_ids
- can([:read, :membered], Project, read_relations_for('projects')) {|project| local_reader? project}
+ can([:read, :membered, :get_id], Project, read_relations_for('projects')) {|project| local_reader? project}
can(:write, Project) {|project| local_writer? project} # for grack
- can([:update, :sections, :manage_collaborators, :autocomplete_maintainers], Project) {|project| local_admin? project}
+ can [:update, :sections, :manage_collaborators, :autocomplete_maintainers, :add_member, :remove_member, :update_member, :members], Project do |project|
+ local_admin? project
+ end
can(:fork, Project) {|project| can? :read, project}
can(:fork, Project) {|project| project.owner_type == 'Group' and can? :update, project.owner}
can(:destroy, Project) {|project| owner? project}
can(:destroy, Project) {|project| project.owner_type == 'Group' and project.owner.actors.exists?(:actor_type => 'User', :actor_id => user.id, :role => 'admin')}
can :remove_user, Project
+ can :preview, Project
+ can(:refs_list, Project) {|project| can? :read, project}
can [:read, :log, :owned, :everything], BuildList, :user_id => user.id
can [:read, :log, :related, :everything], BuildList, :project => {:owner_type => 'User', :owner_id => user.id}
@@ -73,28 +82,27 @@ class Ability
can([:create, :update], BuildList) {|build_list| build_list.project.is_package && can?(:write, build_list.project)}
can(:publish, BuildList) do |build_list|
- build_list.can_publish? and build_list.save_to_repository.publish_without_qa ? can?(:write, build_list.project) : local_admin?(build_list.save_to_platform)
+ build_list.save_to_repository.publish_without_qa ? can?(:write, build_list.project) : local_admin?(build_list.save_to_platform)
end
can(:reject_publish, BuildList) do |build_list|
- build_list.can_reject_publish? and not build_list.save_to_repository.publish_without_qa and local_admin?(build_list.save_to_platform)
+ local_admin?(build_list.save_to_platform)
end
- can(:cancel, BuildList) {|build_list| build_list.can_cancel? && can?(:write, build_list.project)}
+ can(:cancel, BuildList) {|build_list| can?(:write, build_list.project)}
can [:read, :owned, :related, :members], Platform, :owner_type => 'User', :owner_id => user.id
can [:read, :related, :members], Platform, :owner_type => 'Group', :owner_id => user.group_ids
can([:read, :related, :members], Platform, read_relations_for('platforms')) {|platform| local_reader? platform}
can([:update, :members], Platform) {|platform| local_admin? platform}
can([:destroy, :members, :add_member, :remove_member, :remove_members] , Platform) {|platform| owner?(platform) || local_admin?(platform) }
- can [:autocomplete_user_uname], Platform
can([:failed_builds_list, :create], MassBuild) {|mass_build| (owner?(mass_build.platform) || local_admin?(mass_build.platform)) && mass_build.platform.main? }
can(:cancel, MassBuild) {|mass_build| (owner?(mass_build.platform) || local_admin?(mass_build.platform)) && !mass_build.stop_build && mass_build.platform.main?}
- can [:read, :projects_list], Repository, :platform => {:owner_type => 'User', :owner_id => user.id}
- can [:read, :projects_list], Repository, :platform => {:owner_type => 'Group', :owner_id => user.group_ids}
- can([:read, :projects_list], Repository, read_relations_for('repositories', 'platforms')) {|repository| local_reader? repository.platform}
- can([:create, :edit, :update, :destroy, :projects_list, :add_project, :remove_project], Repository) {|repository| local_admin? repository.platform}
- can([:remove_members, :remove_member, :add_member], Repository) {|repository| owner?(repository.platform) || local_admin?(repository.platform)}
+ can [:read, :projects_list, :projects], Repository, :platform => {:owner_type => 'User', :owner_id => user.id}
+ can [:read, :projects_list, :projects], Repository, :platform => {:owner_type => 'Group', :owner_id => user.group_ids}
+ can([:read, :projects_list, :projects], Repository, read_relations_for('repositories', 'platforms')) {|repository| local_reader? repository.platform}
+ can([:create, :edit, :update, :destroy, :projects_list, :projects, :add_project, :remove_project], Repository) {|repository| local_admin? repository.platform}
+ can([:remove_members, :remove_member, :add_member, :signatures], Repository) {|repository| owner?(repository.platform) || local_admin?(repository.platform)}
can([:add_project, :remove_project], Repository) {|repository| repository.members.exists?(:id => user.id)}
can(:clear, Platform) {|platform| local_admin?(platform) && platform.personal?}
can([:change_visibility, :settings, :destroy, :edit, :update], Repository) {|repository| owner? repository.platform}
@@ -116,12 +124,20 @@ class Ability
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? :read, issue.project}
- can([:update, :destroy], Issue) {|issue| issue.user_id == user.id or local_admin?(issue.project)}
+ can(:update, Issue) {|issue| issue.user_id == user.id or local_admin?(issue.project)}
cannot :manage, Issue, :project => {:has_issues => false} # switch off issues
- can(:create, Comment) {|comment| can? :read, comment.project}
- can(:update, Comment) {|comment| comment.user == user or comment.project.owner == user or local_admin?(comment.project)}
- cannot :manage, Comment, :commentable_type => 'Issue', :commentable => {:project => {:has_issues => false}} # switch off issues
+ can :read, PullRequest, :to_project => {:owner_type => 'User', :owner_id => user.id}
+ can :read, PullRequest, :to_project => {:owner_type => 'Group', :owner_id => user.group_ids}
+ can(:read, PullRequest, read_relations_for('pull_requests', 'to_projects')) {|pull| can? :read, pull.to_project rescue nil}
+ can :create, PullRequest
+ can([:update, :merge], PullRequest) {|pull| pull.user_id == user.id or local_admin?(pull.to_project)}
+
+ can([:create, :new_line], Comment) {|comment| can? :read, comment.project}
+ can([:update, :destroy], Comment) {|comment| comment.user == user or comment.project.owner == user or local_admin?(comment.project)}
+ cannot :manage, Comment do |c|
+ c.commentable_type == 'Issue' && !c.project.has_issues && !c.commentable.pull_request # when switch off issues
+ end
end
# Shared cannot rights for all users (registered, admin)
diff --git a/app/models/activity_feed_observer.rb b/app/models/activity_feed_observer.rb
index 7ba79d38c..715450e03 100644
--- a/app/models/activity_feed_observer.rb
+++ b/app/models/activity_feed_observer.rb
@@ -71,6 +71,8 @@ class ActivityFeedObserver < ActiveRecord::Observer
when 'GitHook'
return unless record.project
+ PullRequest.where("from_project_id = ? OR to_project_id = ?", record.project, record.project).needed_checking.each {|pull| pull.check}
+
change_type = record.change_type
branch_name = record.refname.split('/').last
diff --git a/app/models/advisory.rb b/app/models/advisory.rb
index 4410ba621..951a8ec8c 100644
--- a/app/models/advisory.rb
+++ b/app/models/advisory.rb
@@ -21,6 +21,32 @@ class Advisory < ActiveRecord::Base
advisory_id
end
+ def attach_build_list(build_list)
+ return false if update_type != build_list.update_type
+ self.platforms << build_list.save_to_platform unless platforms.include? build_list.save_to_platform
+ self.projects << build_list.project unless projects.include? build_list.project
+ build_list.advisory = self
+ save
+ end
+
+ # this method fetches and structurize packages attached to current advisory.
+ def fetch_packages_info
+ packages_info = Hash.new { |h, k| h[k] = {} } # maaagic, it's maaagic ;)
+ build_lists.find_in_batches(:include => [:save_to_platform, :packages, :project]) do |batch|
+ batch.each do |build_list|
+ tmp = build_list.packages.inject({:srpm => nil, :rpm => []}) do |h, p|
+ p.package_type == 'binary' ? h[:rpm] << p.fullname : h[:srpm] = p.fullname
+ h
+ end
+ h = { build_list.project => tmp }
+ packages_info[build_list.save_to_platform].merge!(h) do |pr, old, new|
+ {:srpm => new[:srpm], :rpm => old[:rpm].concat(new[:rpm]).uniq}
+ end
+ end
+ end
+ packages_info
+ end
+
protected
def generate_advisory_id
@@ -38,4 +64,4 @@ class Advisory < ActiveRecord::Base
end
end
-Advisory.include_root_in_json = false
+Advisory.include_root_in_json = false
\ No newline at end of file
diff --git a/app/models/avatar.rb b/app/models/avatar.rb
new file mode 100644
index 000000000..2fbefa5e8
--- /dev/null
+++ b/app/models/avatar.rb
@@ -0,0 +1,17 @@
+# -*- encoding : utf-8 -*-
+class Avatar < ActiveRecord::Base
+ self.abstract_class = true
+
+ MAX_AVATAR_SIZE = 5.megabyte
+
+ has_attached_file :avatar, :styles =>
+ { :micro => { :geometry => "16x16#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'},
+ :small => { :geometry => "30x30#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'},
+ :medium => { :geometry => "40x40#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'},
+ :big => { :geometry => "81x81#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'}
+ }
+ validates_inclusion_of :avatar_file_size, :in => (0..MAX_AVATAR_SIZE), :allow_nil => true
+
+ attr_accessible :avatar
+
+end
diff --git a/app/models/build_list.rb b/app/models/build_list.rb
index 86244a552..c3976f623 100644
--- a/app/models/build_list.rb
+++ b/app/models/build_list.rb
@@ -27,8 +27,18 @@ class BuildList < ActiveRecord::Base
validate lambda {
errors.add(:save_to_repository, I18n.t('flash.build_list.wrong_repository')) unless save_to_repository_id.in? save_to_platform.repositories.map(&:id)
}
+ validate lambda {
+ include_repos.each {|ir|
+ errors.add(:save_to_repository, I18n.t('flash.build_list.wrong_include_repos')) unless build_for_platform.repository_ids.include? ir.to_i
+ }
+ }
+ validate lambda {
+ if commit_hash.blank? || project.repo.commit(commit_hash).blank?
+ errors.add :commit_hash, I18n.t('flash.build_list.wrong_commit_hash', :commit_hash => commit_hash)
+ end
+ }
- LIVE_TIME = 4.week # for unpublished
+ LIVE_TIME = 4.week # for unpublished
MAX_LIVE_TIME = 3.month # for published
# The kernel does not send these statuses directly
@@ -105,6 +115,7 @@ class BuildList < ActiveRecord::Base
after_commit :place_build
after_destroy :delete_container
+ before_validation :set_commit_and_version
@queue = :clone_and_build
@@ -127,6 +138,10 @@ class BuildList < ActiveRecord::Base
after_transition :on => :published, :do => [:set_version_and_tag, :actualize_packages]
+ after_transition :on => [:published, :fail_publish, :build_error], :do => :notify_users
+ after_transition :on => :build_success, :do => :notify_users,
+ :unless => lambda { |build_list| build_list.auto_publish? }
+
event :place_build do
transition :waiting_for_response => :build_pending, :if => lambda { |build_list|
build_list.add_to_queue == BuildServer::SUCCESS
@@ -195,7 +210,7 @@ class BuildList < ActiveRecord::Base
def set_version_and_tag
pkg = self.packages.where(:package_type => 'source', :project_id => self.project_id).first
# TODO: remove 'return' after deployment ABF kernel 2.0
- return if pkg.nil? # For old client that does not sends data about packages
+ return if pkg.nil? # For old client that does not sends data about packages
self.package_version = "#{pkg.platform.name}-#{pkg.version}-#{pkg.release}"
system("cd #{self.project.repo.path} && git tag #{self.package_version} #{self.commit_hash}") # TODO REDO through grit
save
@@ -284,12 +299,40 @@ class BuildList < ActiveRecord::Base
end
def in_work?
- status == BuildServer::BUILD_STARTED
+ status == BuildServer::BUILD_STARTED
#[WAITING_FOR_RESPONSE, BuildServer::BUILD_PENDING, BuildServer::BUILD_STARTED].include?(status)
end
+ def associate_and_create_advisory(params)
+ build_advisory(params){ |a| a.update_type = update_type }
+ advisory.attach_build_list(self)
+ end
+
+ def can_attach_to_advisory?
+ !save_to_repository.publish_without_qa &&
+ save_to_platform.main? &&
+ save_to_platform.released &&
+ status == BUILD_PUBLISHED
+ end
+
protected
+ def notify_users
+ unless mass_build_id
+ users = []
+ if project # find associated users
+ users = project.all_members.
+ select{ |user| user.notifier.can_notify? && user.notifier.new_associated_build? }
+ end
+ if user.notifier.can_notify? && user.notifier.new_build?
+ users = users | [user]
+ end
+ users.each do |user|
+ UserMailer.build_list_notification(self, user).deliver
+ end
+ end
+ end # notify_users
+
def delete_container
if can_cancel?
BuildServer.delete_build_list bs_id
@@ -306,4 +349,13 @@ class BuildList < ActiveRecord::Base
yield p
end
end
+
+ def set_commit_and_version
+ if project_version.present? && commit_hash.blank?
+ self.commit_hash = project.repo.commits(project_version.match(/^latest_(.+)/).to_a.last ||
+ project_version).try(:first).try(:id)
+ elsif project_version.blank? && commit_hash.present?
+ self.project_version = commit_hash
+ end
+ end
end
diff --git a/app/models/build_list/filter.rb b/app/models/build_list/filter.rb
index 345a138dc..f3a4f78f2 100644
--- a/app/models/build_list/filter.rb
+++ b/app/models/build_list/filter.rb
@@ -68,7 +68,9 @@ class BuildList::Filter
end
def build_date_from_params(field_name, params)
- if params["#{field_name}(1i)"].present? || params["#{field_name}(2i)"].present? || params["#{field_name}(3i)"].present?
+ if params[field_name].present?
+ Time.at(params[field_name].to_i)
+ elsif params["#{field_name}(1i)"].present? || params["#{field_name}(2i)"].present? || params["#{field_name}(3i)"].present?
Date.civil((params["#{field_name}(1i)"].presence || Date.today.year).to_i,
(params["#{field_name}(2i)"].presence || Date.today.month).to_i,
(params["#{field_name}(3i)"].presence || Date.today.day).to_i)
diff --git a/app/models/build_list_observer.rb b/app/models/build_list_observer.rb
index e236e64fc..2d12b85d0 100644
--- a/app/models/build_list_observer.rb
+++ b/app/models/build_list_observer.rb
@@ -1,7 +1,4 @@
class BuildListObserver < ActiveRecord::Observer
- PUBLICATION_STATUSES = [BuildList::BUILD_PUBLISHED, BuildList::FAILED_PUBLISH]
- STATUSES = [BuildServer::BUILD_ERROR, BuildServer::SUCCESS] + PUBLICATION_STATUSES
-
observe :build_list
def before_update(record)
@@ -18,28 +15,7 @@ class BuildListObserver < ActiveRecord::Observer
record.project.update_attributes({ :average_build_time => new_av_time, :build_count => build_count + 1 }, :without_protection => true)
end
end
- BuildListObserver.notify_users(record)
end
end # before_update
- private
-
- def self.notify_users(build_list)
- if !build_list.mass_build_id &&
- ( (build_list.auto_publish? && PUBLICATION_STATUSES.include?(build_list.status)) ||
- (!build_list.auto_publish? && STATUSES.include?(build_list.status)) )
-
- users = []
- if build_list.project # find associated users
- users = build_list.project.all_members.
- select{ |user| user.notifier.can_notify? && user.notifier.new_associated_build? }
- end
- if build_list.user.notifier.can_notify? && build_list.user.notifier.new_build?
- users = users | [build_list.user]
- end
- users.each do |user|
- UserMailer.build_list_notification(build_list, user).deliver
- end
- end
- end # notify_users
end
diff --git a/app/models/comment.rb b/app/models/comment.rb
index 6960860cc..ed6b638cb 100644
--- a/app/models/comment.rb
+++ b/app/models/comment.rb
@@ -3,6 +3,7 @@ class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
belongs_to :user
belongs_to :project
+ serialize :data
validates :body, :user_id, :commentable_id, :commentable_type, :project_id, :presence => true
@@ -12,7 +13,7 @@ class Comment < ActiveRecord::Base
after_create :subscribe_on_reply, :unless => lambda {|c| c.commit_comment?}
after_create :subscribe_users
- attr_accessible :body
+ attr_accessible :body, :data
def commentable
# raise commentable_id.inspect
@@ -53,6 +54,80 @@ class Comment < ActiveRecord::Base
User.find(subscribe.user).notifier.new_comment && User.find(subscribe.user).notifier.can_notify
end
+ def actual_inline_comment?(diff = nil, force = false)
+ unless force
+ raise "This is not inline comment!" if data.blank? # for debug
+ return data[:actual] unless data[:actual].nil?
+ return false if diff.nil?
+ end
+ filepath, line_number = data[:path], data[:line]
+ diff_path = (diff || commentable.diffs ).select {|d| d.a_path == data[:path]}
+ comment_line = data[:line].to_i
+ # NB! also dont create a comment to the diff header
+ return data[:actual] = false if diff_path.blank? || comment_line == 0
+ return data[:actual] = true if commentable_type == 'Grit::Commit'
+ res, ind = true, 0
+ diff_path[0].diff.each_line do |line|
+ if self.persisted? && (comment_line-2..comment_line+2).include?(ind) && data.try('[]', "line#{ind-comment_line}") != line.chomp
+ break res = false
+ end
+ ind = ind + 1
+ end
+ if ind < comment_line
+ return data[:actual] = false
+ else
+ return data[:actual] = res
+ end
+ end
+
+ def inline_diff
+ data[:strings] + data['line0']
+ end
+
+ def pull_comment?
+ return true if commentable.is_a?(Issue) && commentable.pull_request.present?
+ end
+
+ def set_additional_data params
+ return true if params[:path].blank? && params[:line].blank? # not inline comment
+ if params[:in_reply].present? && reply = Comment.where(:id => params[:in_reply]).first
+ self.data = reply.data
+ return true
+ end
+ self.data = {:path => params[:path], :line => params[:line]}
+ if commentable.is_a?(Issue) && pull = commentable.pull_request
+ diff_path = pull.diff.select {|d| d.a_path == params[:path]}
+ return false unless actual_inline_comment?(pull.diff, true)
+
+ comment_line, line_number, strings = params[:line].to_i, -1, []
+ diff_path[0].diff.each_line do |line|
+ line_number = line_number.succ
+ # Save 2 lines above and bottom of the diff comment line
+ break if line_number > comment_line + 2
+ if (comment_line-2..comment_line+2).include? line_number
+ data["line#{line_number-comment_line}"] = line.chomp
+ end
+
+ # Save lines from the closest header for rendering in the discussion
+ if line_number < comment_line
+ # Header is the line like "@@ -47,9 +50,8 @@ def initialize(user)"
+ if line =~ Diff::Display::Unified::Generator::LINE_NUM_RE
+ strings = [line]
+ else
+ strings << line
+ end
+ end
+ end
+ ## Bug with numbers of diff lines, now store all diff
+ data[:strings] = strings.join
+ # Limit stored diff to 10 lines (see inline_diff)
+ #data[:strings] = ((strings.count) <= 9 ? strings : [strings[0]] + strings.last(8)).join
+ ##
+ data[:view_path] = h(diff_path[0].renamed_file ? "#{diff_path[0].a_path.rtruncate 60} -> #{diff_path[0].b_path.rtruncate 60}" : diff_path[0].a_path.rtruncate(120))
+ end
+ return true
+ end
+
protected
def subscribe_on_reply
diff --git a/app/models/group.rb b/app/models/group.rb
index c09af034f..ecc8898fb 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -1,5 +1,5 @@
# -*- encoding : utf-8 -*-
-class Group < ActiveRecord::Base
+class Group < Avatar
belongs_to :owner, :class_name => 'User'
has_many :relations, :as => :actor, :dependent => :destroy, :dependent => :destroy
@@ -39,6 +39,14 @@ class Group < ActiveRecord::Base
uname
end
+ def add_member(member, role = 'admin')
+ Relation.add_member(member, self, role, :actors)
+ end
+
+ def remove_member(member)
+ Relation.remove_member(member, self)
+ end
+
protected
def add_owner_to_members
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 51a5ebac8..3e9e929be 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -11,6 +11,7 @@ class Issue < ActiveRecord::Base
has_many :subscribes, :as => :subscribeable, :dependent => :destroy
has_many :labelings, :dependent => :destroy
has_many :labels, :through => :labelings, :uniq => true
+ has_one :pull_request, :dependent => :destroy
validates :title, :body, :project_id, :presence => true
@@ -21,8 +22,16 @@ class Issue < ActiveRecord::Base
attr_accessible :labelings_attributes, :title, :body, :assignee_id
accepts_nested_attributes_for :labelings, :allow_destroy => true
- scope :opened, where(:status => 'open', :closed_by => nil, :closed_at => nil)
- scope :closed, where(:status => 'closed').where("closed_by is not null and closed_at is not null")
+ scope :opened, where(:status => 'open')
+ scope :closed, where(:status => 'closed')
+
+ scope :needed_checking, where(:issues => {:status => ['open', 'blocked', 'ready', 'already']})
+ scope :not_closed_or_merged, needed_checking
+ scope :closed_or_merged, where(:issues => {:status => ['closed', 'merged']})
+ # Using mb_chars for correct transform to lowercase ('Русский Текст'.downcase => "Русский Текст")
+ scope :search, lambda {|q| where('issues.title ILIKE ?', "%#{q.mb_chars.downcase}%") if q.present?}
+ scope :def_order, order('issues.serial_id desc')
+ scope :without_pull_requests, where('NOT EXISTS (select null from pull_requests as pr where pr.issue_id = issues.id)')
def assign_uname
assignee.uname if assignee
@@ -43,7 +52,7 @@ class Issue < ActiveRecord::Base
end
def set_close(closed_by)
- self.closed_at = Time.now
+ self.closed_at = Time.now.utc
self.closer = closed_by
self.status = 'closed'
end
diff --git a/app/models/platform.rb b/app/models/platform.rb
index 49a84e898..802a2461a 100644
--- a/app/models/platform.rb
+++ b/app/models/platform.rb
@@ -20,9 +20,15 @@ class Platform < ActiveRecord::Base
has_many :mass_builds
validates :description, :presence => true
+ validates :owner, :presence => true
validates :visibility, :presence => true, :inclusion => {:in => VISIBILITIES}
validates :name, :uniqueness => {:case_sensitive => false}, :presence => true, :format => { :with => /^[a-zA-Z0-9_\-\.]+$/ }
validates :distrib_type, :presence => true, :inclusion => {:in => APP_CONFIG['distr_types']}
+ validate lambda {
+ if released_was && !released
+ errors.add(:released, I18n.t('flash.platform.released_status_can_not_be_changed'))
+ end
+ }
before_create :create_directory, :if => lambda {Thread.current[:skip]} # TODO remove this when core will be ready
before_create :xml_rpc_create, :unless => lambda {Thread.current[:skip]}
@@ -39,8 +45,9 @@ class Platform < ActiveRecord::Base
scope :by_visibilities, lambda {|v| where(:visibility => v)}
scope :opened, where(:visibility => 'open')
scope :hidden, where(:visibility => 'hidden')
- scope :main, where(:platform_type => 'main')
- scope :personal, where(:platform_type => 'personal')
+ scope :by_type, lambda {|type| where(:platform_type => type) if type.present?}
+ scope :main, by_type('main')
+ scope :personal, by_type('personal')
attr_accessible :name, :distrib_type, :parent_platform_id, :platform_type, :owner, :visibility, :description, :released
attr_readonly :name, :distrib_type, :parent_platform_id, :platform_type
@@ -166,7 +173,7 @@ class Platform < ActiveRecord::Base
def update_owner_relation
if owner_id_was != owner_id
- r = relations.where(:actor_id => owner_id_was, :actor_type => owner_type_was)[0]
+ r = relations.where(:actor_id => owner_id_was, :actor_type => owner_type_was).first
r.update_attributes(:actor_id => owner_id, :actor_type => owner_type)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 535409d9a..58a7c0446 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -8,6 +8,7 @@ class Project < ActiveRecord::Base
belongs_to :maintainer, :class_name => "User"
has_many :issues, :dependent => :destroy
+ has_many :pull_requests, :dependent => :destroy, :foreign_key => 'to_project_id'
has_many :labels, :dependent => :destroy
has_many :build_lists, :dependent => :destroy
@@ -36,7 +37,7 @@ class Project < ActiveRecord::Base
scope :recent, order("name ASC")
scope :search_order, order("CHAR_LENGTH(name) ASC")
scope :search, lambda {|q| by_name("%#{q.to_s.strip}%")}
- scope :by_name, lambda {|name| where('projects.name ILIKE ?', name)}
+ scope :by_name, lambda {|name| where('projects.name ILIKE ?', name) if name.present?}
scope :by_visibilities, lambda {|v| where(:visibility => v)}
scope :opened, where(:visibility => 'open')
scope :package, where(:is_package => true)
@@ -49,7 +50,11 @@ class Project < ActiveRecord::Base
WHERE (ptr.repository_id = #{ repository_id })
)
) }
+ scope :by_owners, lambda { |group_owner_ids, user_owner_ids|
+ where("(projects.owner_id in (?) AND projects.owner_type = 'Group') OR (projects.owner_id in (?) AND projects.owner_type = 'User')", group_owner_ids, user_owner_ids)
+ }
+ before_validation :truncate_name, :on => :create
before_create :set_maintainer
after_save :attach_to_personal_repository
@@ -84,6 +89,14 @@ class Project < ActiveRecord::Base
collaborators | groups.map(&:members).flatten
end
+ def add_member(member, role = 'admin')
+ Relation.add_member(member, self, role)
+ end
+
+ def remove_member(member)
+ Relation.remove_member(member, self)
+ end
+
def platforms
@platforms ||= repositories.map(&:platform).uniq
end
@@ -172,6 +185,10 @@ class Project < ActiveRecord::Base
protected
+ def truncate_name
+ self.name = name.strip if name
+ end
+
def attach_to_personal_repository
owner_rep = self.owner.personal_repository
if is_package
@@ -182,7 +199,9 @@ class Project < ActiveRecord::Base
end
def set_maintainer
- self.maintainer_id = (owner_type == 'User') ? self.owner_id : self.owner.owner_id
+ if maintainer_id.blank?
+ self.maintainer_id = (owner_type == 'User') ? self.owner_id : self.owner.owner_id
+ end
end
end
diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb
new file mode 100644
index 000000000..d43e3f325
--- /dev/null
+++ b/app/models/pull_request.rb
@@ -0,0 +1,222 @@
+class PullRequest < ActiveRecord::Base
+ STATUSES = %w(ready already blocked merged closed)
+ belongs_to :issue, :autosave => true, :dependent => :destroy, :touch => true, :validate => true
+ belongs_to :to_project, :class_name => 'Project', :foreign_key => 'to_project_id'
+ belongs_to :from_project, :class_name => 'Project', :foreign_key => 'from_project_id'
+ delegate :user, :user_id, :title, :body, :serial_id, :assignee, :status, :to_param,
+ :created_at, :updated_at, :comments, :status=, :to => :issue, :allow_nil => true
+
+ validate :uniq_merge
+ validates_each :from_ref, :to_ref do |record, attr, value|
+ check_ref record, attr, value
+ end
+
+ before_create :clean_dir
+ after_destroy :clean_dir
+
+ accepts_nested_attributes_for :issue
+ attr_accessible :issue_attributes, :to_ref, :from_ref
+
+ scope :needed_checking, includes(:issue).where(:issues => {:status => ['open', 'blocked', 'ready']})
+
+ state_machine :status, :initial => :open do
+ event :ready do
+ transition [:ready, :open, :blocked] => :ready
+ end
+
+ event :already do
+ transition [:ready, :open, :blocked] => :already
+ end
+
+ event :block do
+ transition [:ready, :open, :blocked] => :blocked
+ end
+
+ event :merging do
+ transition :ready => :merged
+ end
+
+ event :close do
+ transition [:ready, :open, :blocked] => :closed
+ end
+
+ event :reopen do
+ transition :closed => :open
+ end
+ end
+
+ def check(do_transaction = true)
+ res = merge
+ new_status = case res
+ when /Already up-to-date/
+ 'already'
+ when /Merge made by/
+ system("cd #{path} && git reset --hard HEAD^") # remove merge commit
+ 'ready'
+ when /Automatic merge failed/
+ system("cd #{path} && git reset --hard HEAD") # clean git index
+ 'block'
+ else
+ raise res
+ end
+
+ if do_transaction
+ new_status == 'already' ? (ready; merging) : send(new_status)
+ self.update_inline_comments
+ else
+ self.status = new_status == 'block' ? 'blocked' : new_status
+ end
+ end
+
+ def merge!(who)
+ return false unless can_merging?
+ Dir.chdir(path) do
+ system "git config user.name \"#{who.uname}\" && git config user.email \"#{who.email}\""
+ if merge
+ system("git push origin HEAD")
+ system("git reset --hard HEAD^") # for diff maybe FIXME
+ set_user_and_time who
+ merging
+ end
+ end
+ end
+
+ def path
+ filename = [id, from_project.owner.uname, from_project.name].compact.join('-')
+ File.join(APP_CONFIG['root_path'], 'pull_requests', to_project.owner.uname, to_project.name, filename)
+ end
+
+ def from_branch
+ if to_project != from_project
+ "head_#{from_ref}"
+ else
+ from_ref
+ end
+ end
+
+ def common_ancestor
+ return @common_ancestor if @common_ancestor
+ base_commit = repo.commits(to_ref).first
+ @common_ancestor = repo.commit(repo.git.merge_base({}, base_commit, from_commit)) || base_commit
+ end
+ alias_method :to_commit, :common_ancestor
+
+ def diff_stats
+ stats = []
+ Dir.chdir(path) do
+ lines = repo.git.native(:diff, {:numstat => true, :M => true}, "#{to_commit.id}...#{from_commit.id}").split("\n")
+ while !lines.empty?
+ files = []
+ while lines.first =~ /^([-\d]+)\s+([-\d]+)\s+(.+)/
+ additions, deletions, filename = lines.shift.gsub(' => ', '=>').split
+ additions, deletions = additions.to_i, deletions.to_i
+ stat = Grit::DiffStat.new filename, additions, deletions
+ stats << stat
+ end
+ end
+ stats
+ end
+ end
+
+ # FIXME maybe move to warpc/grit?
+ def diff
+ return @diff if @diff.present?
+ diff = repo.git.native('diff', {:M => true}, "#{to_commit.id}...#{from_commit.id}")
+
+ if diff =~ /diff --git a/
+ diff = diff.sub(/.*?(diff --git a)/m, '\1')
+ else
+ diff = ''
+ end
+ @diff = Grit::Diff.list_from_string(repo, diff)
+ end
+
+ def set_user_and_time user
+ issue.closed_at = Time.now.utc
+ issue.closer = user
+ end
+
+ def self.check_ref(record, attr, value)
+ project = attr == :from_ref ? record.from_project : record.to_project
+ record.errors.add attr, I18n.t('projects.pull_requests.wrong_ref') unless project.repo.branches_and_tags.map(&:name).include?(value)
+ end
+
+ def uniq_merge
+ if to_project.pull_requests.needed_checking.where(:from_project_id => from_project, :to_ref => to_ref, :from_ref => from_ref).where('pull_requests.id <> :id or :id is null', :id => id).count > 0
+ errors.add(:base_branch, I18n.t('projects.pull_requests.duplicate', :from_ref => from_ref))
+ end
+ end
+
+ def repo
+ return @repo if @repo.present? #&& !id_changed?
+ @repo = Grit::Repo.new path
+ end
+
+ def from_commit
+ repo.commits(from_branch).first
+ end
+
+ protected
+
+ def merge
+ clone
+ message = "Merge pull request ##{serial_id} from #{from_project.name_with_owner}:#{from_ref}\r\n #{title}"
+ %x(cd #{path} && git checkout #{to_ref} && git merge --no-ff #{from_branch} -m '#{message}')
+ end
+
+ def clone
+ git = Grit::Git.new(path)
+ unless git.exist?
+ #~ FileUtils.mkdir_p(path)
+ #~ system("git clone --local --no-hardlinks #{to_project.path} #{path}")
+ options = {:bare => false, :shared => false, :branch => to_ref} # shared?
+ git.fs_mkdir('..')
+ git.clone(options, to_project.path, path)
+ if to_project != from_project
+ Dir.chdir(path) do
+ system 'git', 'remote', 'add', 'head', from_project.path
+ end
+ end
+ clean # Need testing
+ end
+
+ Dir.chdir(path) do
+ system 'git', 'checkout', to_ref
+ system 'git', 'pull', 'origin', to_ref
+ if to_project == from_project
+ system 'git', 'checkout', from_ref
+ system 'git', 'pull', 'origin', from_ref
+ else
+ system 'git', 'fetch', 'head', "+#{from_ref}:#{from_branch}"
+ end
+ end
+ # TODO catch errors
+ end
+
+ def clean
+ Dir.chdir(path) do
+ to_project.repo.branches.each {|branch| system 'git', 'checkout', branch.name}
+ system 'git', 'checkout', to_ref
+
+ to_project.repo.branches.each do |branch|
+ system 'git', 'branch', '-D', branch.name unless [to_ref, from_branch].include? branch.name
+ end
+ to_project.repo.tags.each do |tag|
+ system 'git', 'tag', '-d', tag.name unless [to_ref, from_branch].include? tag.name
+ end
+ end
+ end
+
+ def clean_dir
+ FileUtils.rm_rf path
+ end
+
+ def update_inline_comments
+ self.comments.each do |c|
+ if c.data.present? # maybe need add new column 'actual'?
+ c.actual_inline_comment? diff, true
+ c.save
+ end
+ end
+ end
+end
diff --git a/app/models/relation.rb b/app/models/relation.rb
index 76829a849..2ce5d0fa4 100644
--- a/app/models/relation.rb
+++ b/app/models/relation.rb
@@ -22,17 +22,18 @@ class Relation < ActiveRecord::Base
r.save
end
- def self.add_member(member, target, role)
- if target.relations.exists?(:actor_id => member.id, :actor_type => member.class.to_s) || @platform.try(:owner) == member
+ def self.add_member(member, target, role, relation = :relations)
+ if target.send(relation).exists?(:actor_id => member.id, :actor_type => member.class.to_s) || (target.respond_to?(:owner) && target.owner == member)
true
else
- rel = target.relations.build(:role => role)
+ rel = target.send(relation).build(:role => role)
rel.actor = member
rel.save
end
end
def self.remove_member(member, target)
+ return false if target.respond_to?(:owner) && target.owner == member
Relation.by_actor(member).by_target(target).each{|r| r.destroy}
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 027175d5c..fe76e0b3e 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,19 +1,11 @@
# -*- encoding : utf-8 -*-
-class User < ActiveRecord::Base
+class User < Avatar
ROLES = ['', 'admin', 'banned']
LANGUAGES_FOR_SELECT = [['Russian', 'ru'], ['English', 'en']]
LANGUAGES = LANGUAGES_FOR_SELECT.map(&:last)
- MAX_AVATAR_SIZE = 5.megabyte
devise :database_authenticatable, :registerable, :omniauthable, :token_authenticatable,# :encryptable, :timeoutable
:recoverable, :rememberable, :validatable, :lockable, :confirmable#, :reconfirmable, :trackable
- has_attached_file :avatar, :styles =>
- { :micro => { :geometry => "16x16#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'},
- :small => { :geometry => "30x30#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'},
- :medium => { :geometry => "40x40#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'},
- :big => { :geometry => "81x81#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'}
- }
- validates_inclusion_of :avatar_file_size, :in => (0..MAX_AVATAR_SIZE), :allow_nil => true
has_one :notifier, :class_name => 'SettingsNotifier', :dependent => :destroy #:notifier
@@ -43,7 +35,7 @@ class User < ActiveRecord::Base
validates :language, :inclusion => {:in => LANGUAGES}, :allow_blank => true
attr_accessible :email, :password, :password_confirmation, :current_password, :remember_me, :login, :name, :uname, :language,
- :site, :company, :professional_experience, :location, :avatar
+ :site, :company, :professional_experience, :location
attr_readonly :uname
attr_accessor :login
diff --git a/app/presenters/comment_presenter.rb b/app/presenters/comment_presenter.rb
index bab0b3343..ac9564c31 100644
--- a/app/presenters/comment_presenter.rb
+++ b/app/presenters/comment_presenter.rb
@@ -9,7 +9,7 @@ class CommentPresenter < ApplicationPresenter
@user = comment.user
@options = opts
- @content = simple_format(@comment.body, {}, :sanitize => true).html_safe
+ @content = @comment.body
end
def expandable?
@@ -27,23 +27,17 @@ class CommentPresenter < ApplicationPresenter
def caption?
false
end
- def buttons
- project = options[:project]
- commentable = options[:commentable]
- (ep, dp) = if Comment.issue_comment?(commentable.class)
- [edit_project_issue_comment_path(project, commentable, comment),
- project_issue_comment_path(project, commentable, comment)]
- elsif Comment.commit_comment?(commentable.class)
- [edit_project_commit_comment_path(project, commentable, comment),
- project_commit_comment_path(project, commentable, comment)]
- end
- res = []
+ def buttons
+ project, commentable = options[:project], options[:commentable]
+ path = helpers.project_commentable_comment_path(project, commentable, comment)
+
+ res = [link_to(t("layout.link"), "#{helpers.project_commentable_path(project, commentable)}##{comment_anchor}", :class => "#{@options[:in_discussion].present? ? 'in_discussion_' : ''}link_to_comment").html_safe]
if controller.can? :update, @comment
- res << link_to(t("layout.edit"), ep).html_safe
+ res << link_to(t("layout.edit"), path, :id => "comment-#{comment.id}", :class => "edit_comment").html_safe
end
- if controller.can? :delete, @comment
- res << link_to(t("layout.delete"), dp, :method => "delete",
+ if controller.can? :destroy, @comment
+ res << link_to(t("layout.delete"), path, :method => "delete",
:confirm => t("layout.comments.confirm_delete")).html_safe
end
res
@@ -69,4 +63,15 @@ class CommentPresenter < ApplicationPresenter
def comment_id
@comment.id
end
+
+ def comment_anchor
+ # check for pull diff inline comment
+ before = if @options[:add_anchor].present? && !@options[:in_discussion]
+ 'diff-'
+ else
+ ''
+ end
+ "#{before}comment#{@comment.id}"
+ end
+
end
diff --git a/app/views/activity_feeds/_sidebar.html.haml b/app/views/activity_feeds/_sidebar.html.haml
index baf06577f..6115f796f 100644
--- a/app/views/activity_feeds/_sidebar.html.haml
+++ b/app/views/activity_feeds/_sidebar.html.haml
@@ -11,7 +11,7 @@
- else
= image_tag("lock.png")
%td
- = link_to "#{project.owner.uname}/#{project.name}", project_path(project)
+ = link_to project.name_with_owner, project_path(project)
%tr
%td
\
diff --git a/app/views/activity_feeds/partials/_new_comment_commit_notification.haml b/app/views/activity_feeds/partials/_new_comment_commit_notification.haml
index 003ffd448..2f276d098 100644
--- a/app/views/activity_feeds/partials/_new_comment_commit_notification.haml
+++ b/app/views/activity_feeds/partials/_new_comment_commit_notification.haml
@@ -4,7 +4,7 @@
.text
%span
= raw t("notifications.bodies.new_comment_notification.title", :user_link => link_to(user_name, user_path(user_id)) )
- = raw t("notifications.bodies.new_comment_notification.commit_content", {:commit_link => link_to(commit_message, commit_path(project_owner, project_name, commit_id) + "#comment##{comment_id}")})
+ = raw t("notifications.bodies.new_comment_notification.commit_content", {:commit_link => link_to(commit_message, commit_path(project_owner, project_name, commit_id) + "#comment#{comment_id}")})
= raw t("notifications.bodies.project", :project_link => link_to("#{project_owner}/#{project_name}", project_path(project_owner, project_name)) )
.both
%span.date= activity_feed.created_at
diff --git a/app/views/activity_feeds/partials/_new_comment_notification.haml b/app/views/activity_feeds/partials/_new_comment_notification.haml
index 567aae9a3..bc626889d 100644
--- a/app/views/activity_feeds/partials/_new_comment_notification.haml
+++ b/app/views/activity_feeds/partials/_new_comment_notification.haml
@@ -4,7 +4,7 @@
.text
%span
= raw t("notifications.bodies.new_comment_notification.title", {:user_link => link_to(user_name, user_path(user_id))})
- = raw t("notifications.bodies.new_comment_notification.content", {:issue_link => link_to(issue_title, project_issue_path(project_owner, project_name, issue_serial_id) + "#comment##{comment_id}")})
+ = raw t("notifications.bodies.new_comment_notification.content", {:issue_link => link_to(issue_title, project_issue_path(project_owner, project_name, issue_serial_id) + "#comment#{comment_id}")})
= raw t("notifications.bodies.project", :project_link => link_to("#{project_owner}/#{project_name}", project_path(project_owner, project_name)) )
.both
%span.date= activity_feed.created_at
diff --git a/app/views/admin/register_requests/index.html.haml b/app/views/admin/register_requests/index.html.haml
index a562479a3..90c609fc0 100644
--- a/app/views/admin/register_requests/index.html.haml
+++ b/app/views/admin/register_requests/index.html.haml
@@ -24,9 +24,14 @@
%td= request.more
%td= request.created_at
%td
- = link_to t("layout.approve"), approve_admin_register_request_path(request) if can? :approve, request
- |
- = link_to t("layout.reject"), reject_admin_register_request_path(request) if can? :reject, request
+ - links = []
+ - if can? :approve, request
+ - links << link_to(t("layout.approve"), approve_admin_register_request_path(request))
+ - if can? :reject, request
+ - links << link_to(t("layout.reject"), reject_admin_register_request_path(request))
+ - if request.token
+ - links << link_to('Link', new_user_registration_url(:invitation_token => request.token))
+ = raw links.join('|')
.actions
%input#approve_registration{:type => 'button', :value => "Approve Selected"}
diff --git a/app/views/api/v1/advisories/_advisory.json.jbuilder b/app/views/api/v1/advisories/_advisory.json.jbuilder
new file mode 100644
index 000000000..5205ddc29
--- /dev/null
+++ b/app/views/api/v1/advisories/_advisory.json.jbuilder
@@ -0,0 +1,12 @@
+json.id advisory.advisory_id
+json.(advisory, :description)
+json.platforms advisory.platforms do |json_platform, platform|
+ json_platform.(platform, :id, :released)
+ json_platform.url api_v1_platform_path(platform.id, :format => :json)
+end
+json.projects advisory.projects do |json_project, project|
+ json_project.(project, :id, :name)
+ json_project.fullname project.name_with_owner
+ json_project.url api_v1_project_path(project.id, :format => :json)
+end
+json.url api_v1_advisory_path(advisory.advisory_id, :format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/advisories/index.json.jbuilder b/app/views/api/v1/advisories/index.json.jbuilder
new file mode 100644
index 000000000..c0248c828
--- /dev/null
+++ b/app/views/api/v1/advisories/index.json.jbuilder
@@ -0,0 +1,4 @@
+json.advisories @advisories do |json, advisory|
+ json.partial! 'advisory', :advisory => advisory, :json => json
+end
+json.url api_v1_advisories_path(:format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/advisories/show.json.jbuilder b/app/views/api/v1/advisories/show.json.jbuilder
new file mode 100644
index 000000000..740d12afe
--- /dev/null
+++ b/app/views/api/v1/advisories/show.json.jbuilder
@@ -0,0 +1,27 @@
+json.advisory do |json|
+ json.partial! 'advisory', :advisory => @advisory, :json => json
+ json.created_at @advisory.created_at.to_i
+ json.updated_at @advisory.updated_at.to_i
+ json.(@advisory, :update_type)
+ json.references @advisory.references.split('\n')
+
+ json.build_lists @advisory.build_lists do |json_build_list, build_list|
+ json_build_list.(build_list, :id)
+ json_build_list.url api_v1_build_list_path(build_list.id, :format => :json)
+ end
+
+ json.affected_in @packages_info do |json_platform, package_info|
+ json.partial! 'api/v1/platforms/platform',
+ :platform => package_info[0], :json => json_platform
+
+ json_platform.projects package_info[1] do |json_project, info|
+ json.partial! 'api/v1/projects/project',
+ :project => info[0], :json => json_project
+
+ packages = info[1]
+ json_project.srpm packages[:srpm]
+ json_project.rpm packages[:rpm]
+ end
+ end
+
+end
diff --git a/app/views/api/v1/arches/index.json.jbuilder b/app/views/api/v1/arches/index.json.jbuilder
new file mode 100644
index 000000000..b51119646
--- /dev/null
+++ b/app/views/api/v1/arches/index.json.jbuilder
@@ -0,0 +1,3 @@
+json.architectures @arches do |json, arch|
+ json.(arch, :id, :name)
+end
\ No newline at end of file
diff --git a/app/views/api/v1/build_lists/index.json.jbuilder b/app/views/api/v1/build_lists/index.json.jbuilder
new file mode 100644
index 000000000..3b59be879
--- /dev/null
+++ b/app/views/api/v1/build_lists/index.json.jbuilder
@@ -0,0 +1,7 @@
+json.build_lists @build_lists do |json, build_list|
+ json.(build_list, :id, :name, :status)
+ json.url api_v1_build_list_path(build_list, :format => :json)
+end
+
+json.url api_v1_build_lists_path(:format => :json, :params => {:filter => params[:filter] } )
+
diff --git a/app/views/api/v1/build_lists/show.json.jbuilder b/app/views/api/v1/build_lists/show.json.jbuilder
new file mode 100644
index 000000000..7819f429e
--- /dev/null
+++ b/app/views/api/v1/build_lists/show.json.jbuilder
@@ -0,0 +1,62 @@
+json.build_list do |json|
+ json.(@build_list, :id, :name, :container_path, :status, :duration)
+ json.(@build_list, :is_circle, :update_type, :build_requires, :priority)
+ json.(@build_list, :advisory, :mass_build)
+ json.(@build_list, :auto_publish, :package_version, :commit_hash)
+ json.build_log_url log_build_list_path(@build_list)
+
+ json.arch do |json_arch|
+ json_arch.(@build_list.arch, :id, :name)
+ end
+ json.created_at @build_list.created_at.to_i
+ json.updated_at @build_list.updated_at.to_i
+
+ json.project do |json_project|
+ json.partial! 'api/v1/projects/project',
+ :project => @build_list.project, :json => json_project
+ end
+
+ json.save_to_repository do |json_save_to_repository|
+ json.partial! 'api/v1/repositories/repository',
+ :repository => @build_list.save_to_repository,
+ :json => json_save_to_repository
+
+ json_save_to_repository.platform do |json_str_platform|
+ json.partial! 'api/v1/platforms/platform',
+ :platform => @build_list.save_to_repository.platform,
+ :json => json_str_platform
+ end
+ end
+
+ json.build_for_platform do |json_build_for_platform|
+ json.partial! 'api/v1/platforms/platform',
+ :platform => @build_list.build_for_platform,
+ :json => json_build_for_platform
+ end
+
+ json.partial! 'api/v1/shared/owner', :owner => @build_list.project.owner
+
+ inc_repos = Repository.includes(:platform).where(:id => @build_list.include_repos)
+ json.include_repos inc_repos do |json_include_repos, repo|
+ json.partial! 'api/v1/repositories/repository',
+ :repository => repo,
+ :json => json_include_repos
+
+ json_include_repos.platform do |json_str_platform|
+ json.partial! 'api/v1/platforms/platform',
+ :platform => repo.platform,
+ :json => json_str_platform
+ end
+ end
+
+ json.advisory do |json_advisory|
+ json_advisory.name @build_list.advisory.advisory_id
+ json_advisory.(@build_list.advisory, :description)
+ end if @build_list.advisory
+
+ json.mass_build do |json_mass_build|
+ json_mass_build.(@build_list.mass_build, :id, :name)
+ end if @build_list.mass_build
+
+ json.url api_v1_build_list_path(@build_list, :format => :json)
+end
diff --git a/app/views/api/v1/groups/index.json.jbuilder b/app/views/api/v1/groups/index.json.jbuilder
new file mode 100644
index 000000000..fec845836
--- /dev/null
+++ b/app/views/api/v1/groups/index.json.jbuilder
@@ -0,0 +1,11 @@
+json.groups @groups do |json, group|
+ json.(group, :id, :uname, :own_projects_count, :description)
+ json.created_at group.created_at.to_i
+ json.updated_at group.updated_at.to_i
+ json.partial! 'api/v1/shared/owner', :owner => group.owner
+ json.avatar_url avatar_url(group, :big)
+ json.url api_v1_group_path(group.id, :format => :json)
+ json.html_url group_path(group.uname)
+end
+
+json.url api_v1_groups_path(:format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/groups/members.json.jbuilder b/app/views/api/v1/groups/members.json.jbuilder
new file mode 100644
index 000000000..eb4ec3391
--- /dev/null
+++ b/app/views/api/v1/groups/members.json.jbuilder
@@ -0,0 +1,5 @@
+json.group do |json|
+ json.(@group, :id)
+ json.partial! 'api/v1/shared/members'
+end
+json.url members_api_v1_group_path(@group.id, :format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/groups/show.json.jbuilder b/app/views/api/v1/groups/show.json.jbuilder
new file mode 100644
index 000000000..ebe02b9ec
--- /dev/null
+++ b/app/views/api/v1/groups/show.json.jbuilder
@@ -0,0 +1,9 @@
+json.group do |json|
+ json.(@group, :id, :uname, :own_projects_count, :description)
+ json.created_at @group.created_at.to_i
+ json.updated_at @group.updated_at.to_i
+ json.partial! 'api/v1/shared/owner', :owner => @group.owner
+ json.avatar_url avatar_url(@group, :big)
+ json.url api_v1_group_path(@group.id, :format => :json)
+ json.html_url group_path(@group.uname)
+end
\ No newline at end of file
diff --git a/app/views/api/v1/platforms/_platform.json.jbuilder b/app/views/api/v1/platforms/_platform.json.jbuilder
new file mode 100644
index 000000000..d16180771
--- /dev/null
+++ b/app/views/api/v1/platforms/_platform.json.jbuilder
@@ -0,0 +1,2 @@
+json.(platform, :id, :name)
+json.url api_v1_platform_path(platform.id, :format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/platforms/index.json.jbuilder b/app/views/api/v1/platforms/index.json.jbuilder
new file mode 100644
index 000000000..7e5e5d71d
--- /dev/null
+++ b/app/views/api/v1/platforms/index.json.jbuilder
@@ -0,0 +1,11 @@
+json.platforms @platforms do |json, platform|
+ json.partial! 'platform', :platform => platform, :json => json
+ json.(platform, :platform_type, :visibility)
+ json.partial! 'api/v1/shared/owner', :owner => platform.owner
+ json.repositories platform.repositories do |json_repos, repo|
+ json_repos.(repo, :id, :name)
+ json_repos.url api_v1_repository_path(repo.id, :format => :json)
+ end
+end
+
+json.url api_v1_platforms_path(:format => :json)
diff --git a/app/views/api/v1/platforms/members.json.jbuilder b/app/views/api/v1/platforms/members.json.jbuilder
new file mode 100644
index 000000000..7cfc6e9a1
--- /dev/null
+++ b/app/views/api/v1/platforms/members.json.jbuilder
@@ -0,0 +1,5 @@
+json.platform do |json|
+ json.partial! 'platform', :platform => @platform, :json => json
+ json.partial! 'api/v1/shared/members'
+end
+json.url members_api_v1_platform_path(@platform.id, :format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/platforms/show.json.jbuilder b/app/views/api/v1/platforms/show.json.jbuilder
new file mode 100644
index 000000000..c8c90a945
--- /dev/null
+++ b/app/views/api/v1/platforms/show.json.jbuilder
@@ -0,0 +1,11 @@
+json.platform do |json|
+ json.partial! 'platform', :platform => @platform, :json => json
+ json.(@platform, :description, :parent_platform_id, :released, :visibility, :platform_type, :distrib_type)
+ json.created_at @platform.created_at.to_i
+ json.updated_at @platform.updated_at.to_i
+ json.partial! 'api/v1/shared/owner', :owner => @platform.owner
+ json.repositories @platform.repositories do |json_repos, repo|
+ json_repos.(repo, :id, :name)
+ json_repos.url api_v1_repository_path(repo.id, :format => :json)
+ end
+end
\ No newline at end of file
diff --git a/app/views/api/v1/projects/_project.json.jbuilder b/app/views/api/v1/projects/_project.json.jbuilder
new file mode 100644
index 000000000..a84d904f9
--- /dev/null
+++ b/app/views/api/v1/projects/_project.json.jbuilder
@@ -0,0 +1,3 @@
+json.(project, :id, :name)
+json.fullname project.name_with_owner
+json.url api_v1_project_path(project.id, :format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/projects/get_id.json.jbuilder b/app/views/api/v1/projects/get_id.json.jbuilder
new file mode 100644
index 000000000..bc4ab3598
--- /dev/null
+++ b/app/views/api/v1/projects/get_id.json.jbuilder
@@ -0,0 +1,5 @@
+json.project do |json|
+ json.partial! 'project', :project => @project, :json => json
+ json.(@project, :visibility)
+ json.partial! 'api/v1/shared/owner', :owner => @project.owner
+end
diff --git a/app/views/api/v1/projects/index.json.jbuilder b/app/views/api/v1/projects/index.json.jbuilder
new file mode 100644
index 000000000..4914044c4
--- /dev/null
+++ b/app/views/api/v1/projects/index.json.jbuilder
@@ -0,0 +1,9 @@
+json.projects @projects do |json, project|
+ json.partial! 'project', :project => project, :json => json
+ json.(project, :visibility, :description, :ancestry, :has_issues, :has_wiki, :default_branch, :is_package, :average_build_time)
+ json.created_at project.created_at.to_i
+ json.updated_at project.updated_at.to_i
+ json.partial! 'api/v1/shared/owner', :owner => project.owner
+end
+
+json.url api_v1_projects_path(:format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/projects/members.json.jbuilder b/app/views/api/v1/projects/members.json.jbuilder
new file mode 100644
index 000000000..258b81ca7
--- /dev/null
+++ b/app/views/api/v1/projects/members.json.jbuilder
@@ -0,0 +1,5 @@
+json.project do |json|
+ json.partial! 'project', :project => @project, :json => json
+ json.partial! 'api/v1/shared/members'
+end
+json.url members_api_v1_project_path(@project.id, :format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/projects/refs_list.json.jbuilder b/app/views/api/v1/projects/refs_list.json.jbuilder
new file mode 100644
index 000000000..7b913f98b
--- /dev/null
+++ b/app/views/api/v1/projects/refs_list.json.jbuilder
@@ -0,0 +1,8 @@
+json.refs_list (@project.repo.branches + @project.repo.tags) do |json_grit, grit|
+ json_grit.ref grit.name
+ json_grit.object do |json_object|
+ json_object.type (grit.class.name =~ /Tag/ ? 'tag' : 'commit')
+ json_object.sha grit.commit.id
+ end
+end
+json.url refs_list_api_v1_project_path(@project.id, :format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/projects/show.json.jbuilder b/app/views/api/v1/projects/show.json.jbuilder
new file mode 100644
index 000000000..dc27a12a2
--- /dev/null
+++ b/app/views/api/v1/projects/show.json.jbuilder
@@ -0,0 +1,18 @@
+json.project do |json|
+ json.partial! 'project', :project => @project, :json => json
+ json.(@project, :visibility, :description, :ancestry, :has_issues, :has_wiki, :default_branch, :is_package, :average_build_time)
+ json.created_at @project.created_at.to_i
+ json.updated_at @project.updated_at.to_i
+ json.partial! 'api/v1/shared/owner', :owner => @project.owner
+ json.maintainer do |json_maintainer|
+ json.partial! 'api/v1/shared/member', :member => @project.maintainer, :tag => json_maintainer
+ end
+ json.repositories @project.repositories do |json_repos, repo|
+ json_repos.(repo, :id, :name)
+ json_repos.url api_v1_repository_path(repo.name, :format => :json)
+ json_repos.platform do |json_platform|
+ json_platform.(repo.platform, :id, :name)
+ json_platform.url api_v1_platform_path(repo.platform, :format => :json)
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/views/api/v1/repositories/_repository.json.jbuilder b/app/views/api/v1/repositories/_repository.json.jbuilder
new file mode 100644
index 000000000..ecd0ec545
--- /dev/null
+++ b/app/views/api/v1/repositories/_repository.json.jbuilder
@@ -0,0 +1,2 @@
+json.(repository, :id, :name)
+json.url api_v1_repository_path(repository.id, :format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/repositories/projects.json.jbuilder b/app/views/api/v1/repositories/projects.json.jbuilder
new file mode 100644
index 000000000..f911d6645
--- /dev/null
+++ b/app/views/api/v1/repositories/projects.json.jbuilder
@@ -0,0 +1,8 @@
+json.repository do |json|
+ json.partial! 'repository', :repository => @repository, :json => json
+ json.projects @projects do |json_project, project|
+ json.partial! 'api/v1/projects/project',
+ :project => project, :json => json_project
+ end
+end
+json.url projects_api_v1_repository_path(@repository.id, :format => :json)
\ No newline at end of file
diff --git a/app/views/api/v1/repositories/show.json.jbuilder b/app/views/api/v1/repositories/show.json.jbuilder
new file mode 100644
index 000000000..fc6d54f77
--- /dev/null
+++ b/app/views/api/v1/repositories/show.json.jbuilder
@@ -0,0 +1,10 @@
+json.repository do |json|
+ json.partial! 'repository', :repository => @repository, :json => json
+ json.(@repository, :description, :publish_without_qa)
+ json.created_at @repository.created_at.to_i
+ json.updated_at @repository.updated_at.to_i
+ json.platform do |json_platform|
+ json_platform.(@repository.platform, :id, :name)
+ json_platform.url api_v1_platform_path(@repository.platform, :format => :json)
+ end
+end
\ No newline at end of file
diff --git a/app/views/api/v1/shared/_member.json.jbuilder b/app/views/api/v1/shared/_member.json.jbuilder
new file mode 100644
index 000000000..25d2d1cf5
--- /dev/null
+++ b/app/views/api/v1/shared/_member.json.jbuilder
@@ -0,0 +1,3 @@
+tag.(member, :id, :name)
+tag.type member.class.name
+tag.url member_path(member)
\ No newline at end of file
diff --git a/app/views/api/v1/shared/_members.json.jbuilder b/app/views/api/v1/shared/_members.json.jbuilder
new file mode 100644
index 000000000..00c8b9a93
--- /dev/null
+++ b/app/views/api/v1/shared/_members.json.jbuilder
@@ -0,0 +1,3 @@
+json.members @members do |json_members, member|
+ json.partial! 'api/v1/shared/member', :member => member, :tag => json_members
+end
\ No newline at end of file
diff --git a/app/views/api/v1/shared/_owner.json.jbuilder b/app/views/api/v1/shared/_owner.json.jbuilder
new file mode 100644
index 000000000..e6983d550
--- /dev/null
+++ b/app/views/api/v1/shared/_owner.json.jbuilder
@@ -0,0 +1,3 @@
+json.owner do |json_owner|
+ json.partial! 'api/v1/shared/member', :member => owner, :tag => json_owner
+end
\ No newline at end of file
diff --git a/app/views/api/v1/users/notifiers.json.jbuilder b/app/views/api/v1/users/notifiers.json.jbuilder
new file mode 100644
index 000000000..273ebbacc
--- /dev/null
+++ b/app/views/api/v1/users/notifiers.json.jbuilder
@@ -0,0 +1,8 @@
+json.user do |json|
+ json.(@user, :id)
+ json.notifiers do |json_notifiers|
+ json_notifiers.(@user.notifier, :can_notify, :new_comment, :new_comment_reply, :new_issue, :issue_assign, :new_comment_commit_owner, :new_comment_commit_repo_owner, :new_comment_commit_commentor, :new_build, :new_associated_build)
+ end
+end
+
+json.url notifiers_api_v1_user_path(:json)
\ No newline at end of file
diff --git a/app/views/api/v1/users/show.json.jbuilder b/app/views/api/v1/users/show.json.jbuilder
new file mode 100644
index 000000000..ff20c6f3b
--- /dev/null
+++ b/app/views/api/v1/users/show.json.jbuilder
@@ -0,0 +1,8 @@
+json.user do |json|
+ json.(@user, :id, :name, :email, :uname,:language, :own_projects_count, :professional_experience, :site, :company, :location, :build_priority)
+ json.created_at @user.created_at.to_i
+ json.updated_at @user.updated_at.to_i
+ json.avatar_url avatar_url(@user,:big)
+ json.url api_v1_user_path(@user.id, :format => :json)
+ json.html_url user_path(@user.uname)
+end
\ No newline at end of file
diff --git a/app/views/groups/members/index.html.haml b/app/views/groups/members/index.html.haml
index 9f80db048..25b05239d 100644
--- a/app/views/groups/members/index.html.haml
+++ b/app/views/groups/members/index.html.haml
@@ -25,7 +25,7 @@
.hr.top
= form_tag add_group_members_path(parent) do
- .admin-search= autocomplete_field_tag 'user_id', params[:user_id], autocomplete_user_uname_users_path#, :id_element => '#member_id_field'
+ .admin-search= autocomplete_field_tag 'user_id', params[:user_id], autocomplete_user_uname_autocompletes_path#, :id_element => '#member_id_field'
.admin-role
.lineForm= select_tag 'role', options_for_collaborators_roles_select
.both
diff --git a/app/views/groups/profile/_form.html.haml b/app/views/groups/profile/_form.html.haml
index f30cb0ab4..4ac4eaf82 100644
--- a/app/views/groups/profile/_form.html.haml
+++ b/app/views/groups/profile/_form.html.haml
@@ -8,6 +8,8 @@
.rightlist.nomargin= f.text_area :description
.both
%br
+= render 'shared/avatar_form', :subject => @group, :f => f
+%br
.leftlist
\
.rightlist= submit_tag t("layout.save")
diff --git a/app/views/groups/profile/show.html.haml b/app/views/groups/profile/show.html.haml
index a67abf0e8..890b9c2c9 100644
--- a/app/views/groups/profile/show.html.haml
+++ b/app/views/groups/profile/show.html.haml
@@ -1,12 +1,4 @@
-set_meta_tags :title => title_object(@group)
-.all.verybigpadding
- %h3= @group.name
- %h4= t("activerecord.attributes.group.description") + ":"
- %p= @group.description
- %h4= t("layout.groups.public_projects_list") + ":"
- %p
- - @projects.each do |project|
- = link_to project.name, project
- %br
- %br
- = link_to t("layout.edit"), edit_group_path(@group), :class => 'button' if can? :edit, @group
+
+- edit_link = can?(:edit, @group) ? link_to(t("layout.edit"), edit_group_path(@group), :class => 'button') : nil
+= render 'shared/profile', :uname => @group.name, :group => @group, :search_path => group_path, :projects => @projects, :edit_link => edit_link
diff --git a/app/views/layouts/menu/_bottom.html.haml b/app/views/layouts/menu/_bottom.html.haml
index e50bebcf7..4858cfde7 100644
--- a/app/views/layouts/menu/_bottom.html.haml
+++ b/app/views/layouts/menu/_bottom.html.haml
@@ -17,3 +17,6 @@
%li
= image_tag 'square.png'
= link_to t('bottom_menu.support'), contact_url
+ %li
+ = image_tag 'square.png'
+ = link_to t('bottom_menu.developer_api'), t('bottom_menu.developer_api_url')
diff --git a/app/views/layouts/issues.html.haml b/app/views/layouts/with_sidebar.html.haml
similarity index 92%
rename from app/views/layouts/issues.html.haml
rename to app/views/layouts/with_sidebar.html.haml
index 75987198b..87bd1860c 100644
--- a/app/views/layouts/issues.html.haml
+++ b/app/views/layouts/with_sidebar.html.haml
@@ -1,4 +1,4 @@
- if content_for?(:sidebar)
%aside= yield :sidebar
.right= yield
-.both
\ No newline at end of file
+.both
diff --git a/app/views/platforms/platforms/_form.html.haml b/app/views/platforms/platforms/_form.html.haml
index ce9d9cb37..4508e03b6 100644
--- a/app/views/platforms/platforms/_form.html.haml
+++ b/app/views/platforms/platforms/_form.html.haml
@@ -22,7 +22,7 @@
.both
.leftlist= label_tag "", t("layout.platforms.admin_id"), :class => :label
- .rightlist= autocomplete_field_tag 'admin_id', @admin_uname, autocomplete_user_uname_platforms_path, :id_element => '#admin_id_field'
+ .rightlist= autocomplete_field_tag 'admin_id', @admin_uname, autocomplete_user_uname_autocompletes_path, :id_element => '#admin_id_field'
= hidden_field_tag 'admin_id', @admin_id, :id => 'admin_id_field'
.both
diff --git a/app/views/platforms/repositories/_proj_list.html.haml b/app/views/platforms/repositories/_proj_list.html.haml
index b5cbd50e9..a470d9dd2 100644
--- a/app/views/platforms/repositories/_proj_list.html.haml
+++ b/app/views/platforms/repositories/_proj_list.html.haml
@@ -7,7 +7,7 @@
:filtered_label => t("datatables.filtered_label"),
:table_dom_id => 'datatable',
:auto_width => 'false',
- :ajax_source => "#{url_for :controller => :repositories, :action => :projects_list, :id => @repository.id, :added => "#{controller.action_name.to_sym == :show}"}" })
+ :ajax_source => "#{url_for :controller => :repositories, :action => :projects_list, :id => @repository.id, :added => "#{controller.action_name.to_sym == :show}", :format => :json}" })
%table#datatable.tablesorter.repo-projects{:cellspacing => 0, :cellpadding => 0}
%thead
diff --git a/app/views/platforms/repositories/_project.html.haml b/app/views/platforms/repositories/_project.html.haml
deleted file mode 100644
index 2048b04a2..000000000
--- a/app/views/platforms/repositories/_project.html.haml
+++ /dev/null
@@ -1,10 +0,0 @@
-%tr{:id => "Row#{project_counter}", :class => cycle('odd', 'even')}
- %td
- = link_to project do
- .table-sort-left= image_tag visibility_icon(project.visibility)
- .table-sort-right #{project.owner.uname} / #{project.name}
- %td.td2
- %span= project.description
- %td.buttons
- = link_to remove_project_platform_repository_path(@platform, @repository, :project_id => project.id), :method => :delete, :confirm => t("layout.confirm") do
- %span.delete
diff --git a/app/views/projects/base/_repo_block.html.haml b/app/views/projects/base/_repo_block.html.haml
index f301d63b3..d8788f420 100644
--- a/app/views/projects/base/_repo_block.html.haml
+++ b/app/views/projects/base/_repo_block.html.haml
@@ -6,8 +6,9 @@
=image_tag 'zip.png', :alt => 'ZIP'
%b.caret
%ul.dropdown-menu
- %li=link_to "tar.gz", archive_path(project, @treeish, 'tar')
- %li=link_to "zip", archive_path(project, @treeish, 'zip')
+ - file_name = "#{@project.owner.uname}-#{@project.name}-#{@commit.id}"
+ %li=link_to "tar.gz", archive_path(project, file_name, 'tar.gz')
+ %li=link_to "zip", archive_path(project, file_name, 'zip')
= text_field_tag :url, git_repo_url(project.git_repo_name), :class => 'name', :spellcheck => 'false', :readonly => true
.git_help ?
diff --git a/app/views/projects/base/_submenu.html.haml b/app/views/projects/base/_submenu.html.haml
index 607ee2ef6..67b6fb37d 100644
--- a/app/views/projects/base/_submenu.html.haml
+++ b/app/views/projects/base/_submenu.html.haml
@@ -11,6 +11,7 @@
%li= link_to t("project_menu.builds"), project_build_lists_path(@project), :class => (contr == :build_lists ? 'active' : nil)
- if @project.has_issues
%li= link_to t("project_menu.tracker"), project_issues_path(@project), :class => (contr == :issues ? 'active' : nil)
+ %li=link_to t("project_menu.pull_requests"), project_pull_requests_path(@project), :class => (contr == :pull_requests ? 'active' : nil)
- if @project.has_wiki
%li= link_to t("project_menu.wiki"), project_wiki_index_path(@project), :class => (contr == :wiki ? 'active' : nil)
%li=# link_to t("project_menu.readme"), "#" #pending
diff --git a/app/views/projects/collaborators/edit.html.haml b/app/views/projects/collaborators/edit.html.haml
index f17ec9645..0474cec64 100644
--- a/app/views/projects/collaborators/edit.html.haml
+++ b/app/views/projects/collaborators/edit.html.haml
@@ -7,7 +7,7 @@
= form_tag add_project_collaborators_path(@project) do
.admin-search
- = autocomplete_field_tag 'member_id', params[:member_id], autocomplete_user_uname_users_path, :id_element => '#member_id_field'
+ = autocomplete_field_tag 'member_id', params[:member_id], autocomplete_user_uname_autocompletes_path, :id_element => '#member_id_field'
.admin-role
.lineForm
= select_tag 'role', options_for_collaborators_roles_select
@@ -54,7 +54,7 @@
= form_tag add_project_collaborators_path(@project) do
.admin-search
- = autocomplete_field_tag 'group_id', params[:group_id], autocomplete_group_uname_groups_path, :id_element => '#group_id_field'
+ = autocomplete_field_tag 'group_id', params[:group_id], autocomplete_group_uname_autocompletes_path, :id_element => '#group_id_field'
.admin-role
.lineForm
= select_tag 'role', options_for_collaborators_roles_select, :id => 'group_role'
diff --git a/app/views/projects/comments/_add.html.haml b/app/views/projects/comments/_add.html.haml
index e78fe2ac3..132f81541 100644
--- a/app/views/projects/comments/_add.html.haml
+++ b/app/views/projects/comments/_add.html.haml
@@ -1,4 +1,5 @@
#open-comment.comment.view
+ =render 'projects/comments/button_md_help'
%h3.tmargin0= t("layout.comments.new_header")
- if Comment.issue_comment?(commentable.class)
- new_path = project_issue_comments_path(project, commentable)
@@ -10,7 +11,7 @@
- subscribe_path = is_subscribed ? unsubscribe_commit_path(project, commentable) : subscribe_commit_path(project, commentable)
= form_for :comment, :url => new_path, :method => :post, :html => { :class => :form } do |f|
- = render "projects/comments/form", :f => f
+ = render "projects/comments/form", :f => f, :id => 'new'
.comment-left
= t("layout.comments.notifications_are")
%span.bold
diff --git a/app/views/projects/comments/_body.html.haml b/app/views/projects/comments/_body.html.haml
new file mode 100644
index 000000000..4b57f37b1
--- /dev/null
+++ b/app/views/projects/comments/_body.html.haml
@@ -0,0 +1,13 @@
+%ul.nav.nav-tabs#md_tabs
+ %li
+ %a{"data-toggle" => "tab", :href => "##{id}_edit"}=t 'layout.edit'
+ %li
+ %a{"data-toggle" => "tab", :href => "##{id}_preview"}=t 'layout.preview'
+
+.tab-content
+ .tab-pane.active{:id => "#{id}_edit"}
+ =f.text_area :body, :cols => 80, :id => "#{id}_edit_input"
+ =hidden_field_tag :body_dup, nil, :name => 'text', :id => "#{id}_edit_input_dup"
+ .tab-pane{:id => "#{id}_preview"}
+ .formatted.cm-s-default.md_and_cm
+
diff --git a/app/views/projects/comments/_button_md_help.html.haml b/app/views/projects/comments/_button_md_help.html.haml
new file mode 100644
index 000000000..3cc040988
--- /dev/null
+++ b/app/views/projects/comments/_button_md_help.html.haml
@@ -0,0 +1,2 @@
+=link_to t('layout.comments.md_cheatsheet_header'), '#md_help', 'data-toggle' => 'modal', :style => 'float:right;'
+
diff --git a/app/views/projects/comments/_comment.html.haml b/app/views/projects/comments/_comment.html.haml
new file mode 100644
index 000000000..0c25b2ac3
--- /dev/null
+++ b/app/views/projects/comments/_comment.html.haml
@@ -0,0 +1,10 @@
+- CommentPresenter.present(comment, data) do |presenter|
+ = render 'shared/feed_message', :presenter => presenter
+#open-comment.comment.hidden{:class => "comment-#{comment.id}"}
+ =render 'projects/comments/button_md_help'
+ %h3.tmargin0= t("layout.comments.edit_header")
+ = form_for comment, :url => project_commentable_comment_path(data[:project], data[:commentable], comment), :html => { :class => 'form edit_comment' } do |f|
+ = render "projects/comments/form", :f => f, :id => "#{data[:add_id]}edit_#{comment.id}"
+ .comment-left
+ =link_to t('layout.cancel'), '#', :id => "comment-#{comment.id}", :class => 'cancel_edit_comment button'
+ .both
diff --git a/app/views/projects/comments/_form.html.haml b/app/views/projects/comments/_form.html.haml
index d7814ddad..f37286e42 100644
--- a/app/views/projects/comments/_form.html.haml
+++ b/app/views/projects/comments/_form.html.haml
@@ -1,2 +1,2 @@
-.wrapper= f.text_area :body, :cols => 80
-.comment-right= submit_tag t("layout.save")
\ No newline at end of file
+=render 'projects/comments/body', :f => f, :id => id
+.comment-right= submit_tag t("layout.save")
diff --git a/app/views/projects/comments/_list.html.haml b/app/views/projects/comments/_list.html.haml
index acbead95f..e63505aa7 100644
--- a/app/views/projects/comments/_list.html.haml
+++ b/app/views/projects/comments/_list.html.haml
@@ -1,6 +1,6 @@
%a{ :name => "comments" }
.hr
-%h3= t("layout.comments.comments_header")
+%h3#block-list= t("layout.comments.comments_header")
- list.each do |comment|
- - CommentPresenter.present(comment, :project => project, :commentable => commentable) do |presenter|
- = render 'shared/feed_message', :presenter => presenter
\ No newline at end of file
+ = render 'projects/comments/comment', :comment => comment, :data => {:project => project, :commentable => commentable}
+= render "projects/comments/markdown_help"
diff --git a/app/views/projects/comments/_markdown_help.html.haml b/app/views/projects/comments/_markdown_help.html.haml
new file mode 100644
index 000000000..28cf08dd8
--- /dev/null
+++ b/app/views/projects/comments/_markdown_help.html.haml
@@ -0,0 +1,86 @@
+#md_help.modal.hidden
+ .modal-header
+ %button.close{"aria-hidden" => "true", "data-dismiss" => "modal", :type => "button"} ×
+ %h3#myModalLabel=t('layout.comments.md_cheatsheet_header')
+ .modal-body
+ .mod
+ .col
+ %h3=t 'layout.comments.md_cheatsheet.format_text'
+ %p=t 'layout.comments.md_cheatsheet.headers'
+ %pre
+ -[1, 3, 6].each do |i|
+ ="#{'#'*i} This is an tag"
+ %p=t 'layout.comments.md_cheatsheet.text_styles'
+ %pre
+ :preserve
+ *This text will be italic*
+ _This will also be italic_
+ **This text will be bold**
+ __This will also be bold__
+ .col
+ %h3=t 'layout.comments.md_cheatsheet.lists'
+ %p=t 'layout.comments.md_cheatsheet.unordered'
+ %pre
+ :preserve
+ * Item 1
+ * Item 2
+ * Item 2a
+ * Item 2b
+ %p=t 'layout.comments.md_cheatsheet.ordered'
+ %pre
+ :preserve
+ 1. Item 1
+ 2. Item 2
+ 3. Item 3
+ * Item 3a
+ * Item 3b
+ .col
+ %h3=t 'layout.comments.md_cheatsheet.miscellaneous'
+ %p=t 'layout.comments.md_cheatsheet.images'
+ %pre
+ :preserve
+ ![avatar](/assets/ava.png)
+ Format: ![Alt Text](url)
+ %p=t 'layout.comments.md_cheatsheet.links'
+ %pre
+ :preserve
+ https://abf.rosalinux.ru
+ [ABF](https://abf.rosalinux.ru/)
+ %p=t 'layout.comments.md_cheatsheet.blockquotes'
+ %pre
+ :preserve
+ As Kanye West said:
+
+ > We're living the future so
+ > the present is our past.
+ .both
+ %hr.bootstrap
+ %h3=t 'layout.comments.md_cheatsheet.code_examples'
+ .col
+ %p
+ =t 'layout.comments.md_cheatsheet.syntax_highlighting'
+ %pre
+ :preserve
+ ```javascript
+ function fancyAlert(arg) {
+ if(arg) {
+ $.facebox({div:'#foo'})
+ }
+ }
+ ```
+ .col
+ %p=t 'layout.comments.md_cheatsheet.indent_code'
+ %pre
+ :preserve
+ Here is a Python code example
+ without syntax highlighting:
+
+ def foo:
+ if not bar:
+ return true
+ .col
+ %p=t 'layout.comments.md_cheatsheet.inline_code'
+ %pre
+ :preserve
+ I think you should use an
+ `[addr]` element here instead.
diff --git a/app/views/projects/comments/edit.html.haml b/app/views/projects/comments/edit.html.haml
index a27158367..0fd4907c1 100644
--- a/app/views/projects/comments/edit.html.haml
+++ b/app/views/projects/comments/edit.html.haml
@@ -8,4 +8,6 @@
= t("layout.comments.edit_header")
.inner
= form_for @comment, :url => project_commentable_comment_path(@project, @commentable, @comment), :html => {:class => :form} do |f|
- = render "form", :f => f
+ = render "form", :f => f, :id => "edit_#{@comment.id}"
+=hidden_field_tag :preview_url, project_md_preview_path(@project)
+
diff --git a/app/views/projects/comments/new_line.html.haml b/app/views/projects/comments/new_line.html.haml
new file mode 100644
index 000000000..15dc609be
--- /dev/null
+++ b/app/views/projects/comments/new_line.html.haml
@@ -0,0 +1,11 @@
+#open-comment.comment.view.new_line_comment
+ =render 'projects/comments/button_md_help'
+ %h3.tmargin0= t("layout.comments.new_header")
+ = form_for :comment, :url => @path, :method => :post, :html => { :class => :form } do |f|
+ = render "projects/comments/form", :f => f, :id => 'new_line'
+ .comment-left
+ =link_to t('layout.cancel'), '#', :class => 'cancel_inline_comment button'
+ =hidden_field_tag :path, params[:path]
+ =hidden_field_tag :line, params[:line]
+ =hidden_field_tag :in_reply, params[:in_reply] if params[:in_reply].present?
+ .both
diff --git a/app/views/projects/git/base/_choose_fork.html.haml b/app/views/projects/git/base/_choose_fork.html.haml
index 4288bf9a8..e627dd77c 100644
--- a/app/views/projects/git/base/_choose_fork.html.haml
+++ b/app/views/projects/git/base/_choose_fork.html.haml
@@ -11,4 +11,4 @@
:javascript
$('#create_fork').click(function () {
$(this).button('loading');
- })
\ No newline at end of file
+ })
diff --git a/app/views/projects/git/base/_fork.html.haml b/app/views/projects/git/base/_fork.html.haml
index 1e733798f..be9cf54d4 100644
--- a/app/views/projects/git/base/_fork.html.haml
+++ b/app/views/projects/git/base/_fork.html.haml
@@ -1,3 +1,7 @@
+- if can? :write, @project
+ .r{:style => "display: block"}
+ =link_to t("projects.pull_requests.show.pull"), new_project_pull_request_path(@project, :treeish => @treeish), :id => 'send_pull_request', :class => 'button'
+
- if can? :fork, @project
.r#fork-and-edit= link_to t('layout.projects.fork_and_edit'), '#forkModal', :class => 'button', 'data-toggle' => 'modal'
#forkModal.modal.fork-modal{:style => 'display: none;'}
diff --git a/app/views/projects/git/commits/_commit_diff.html.haml b/app/views/projects/git/commits/_commit_diff.html.haml
index 46f639af9..c02a04395 100644
--- a/app/views/projects/git/commits/_commit_diff.html.haml
+++ b/app/views/projects/git/commits/_commit_diff.html.haml
@@ -1,10 +1,10 @@
- commit_id = commit_diff.deleted_file ? @commit.parents.first.id : @commit.id
.file
- %a{:name => h(commit_diff.a_path)}
+ %a{:name => "diff-#{commit_diff_counter}"}
.top
- .l= h(commit_diff.a_path)
+ .l= h(commit_diff.a_path.rtruncate 120)
- if commit_diff.b_path.present?
.r= link_to "view file @ #{short_hash_id(commit_id)}", blob_path(@project, commit_id, commit_diff.b_path)
.clear
-
- .diff_data= render_diff(commit_diff) unless (@project.repo.tree(commit_id) / commit_diff.b_path).binary?
\ No newline at end of file
+ -unless (@project.repo.tree(commit_id) / commit_diff.b_path).binary?
+ .diff_data=render_diff(commit_diff, :diff_counter => commit_diff_counter, :comments => @comments)
diff --git a/app/views/projects/git/commits/_commits.html.haml b/app/views/projects/git/commits/_commits.html.haml
index 9651c7060..cb79b9ffa 100644
--- a/app/views/projects/git/commits/_commits.html.haml
+++ b/app/views/projects/git/commits/_commits.html.haml
@@ -15,4 +15,4 @@
- GitPresenters::CommitAsMessagePresenter.present(commit, :branch => @branch, :project => @project) do |presenter|
= render 'shared/feed_message', :presenter => presenter, :item_no => counter
- counter += 1
- .both
\ No newline at end of file
+ .both
diff --git a/app/views/projects/git/commits/_commits_small.html.haml b/app/views/projects/git/commits/_commits_small.html.haml
new file mode 100644
index 000000000..528f9b3ff
--- /dev/null
+++ b/app/views/projects/git/commits/_commits_small.html.haml
@@ -0,0 +1,22 @@
+%div.commits_activity
+ %table
+ %tbody
+ - commits.each do |commit|
+ - item_no = commit.id
+ - GitPresenters::CommitAsMessagePresenter.present(commit, :branch => @branch, :project => @project) do |presenter|
+ %tr
+ %td
+ %img{:height => 16, :alt => "avatar", :src => presenter.image}
+ %td.date
+ = presenter.date
+ %td.name
+ = presenter.header
+ %td.subject
+ - if presenter.caption?
+ = presenter.caption
+ - if presenter.expandable? and presenter.content?
+ %span.data-expander.collapsed{:id => "expand#{item_no}"}
+ - if presenter.content?
+ .fulltext{:class => "#{presenter.expandable? ? "hidden" : ''} #{presenter.caption? ? "" : "alone"}",
+ :id => presenter.expandable? ? "content-expand#{item_no}" : ''}
+ .cm-s-default.md_and_cm=markdown presenter.content
diff --git a/app/views/projects/git/commits/show.html.haml b/app/views/projects/git/commits/show.html.haml
index 28a008ac9..7da6e3afb 100644
--- a/app/views/projects/git/commits/show.html.haml
+++ b/app/views/projects/git/commits/show.html.haml
@@ -2,15 +2,16 @@
= render 'submenu'
= render 'about_block', :project => @project
-
%h3= t("layout.projects.last_commit")
- GitPresenters::CommitAsMessagePresenter.present(@commit, :branch => @branch, :project => @project) do |presenter|
= render :partial => 'shared/feed_message', :locals => {:presenter => presenter, :item_no => 1}
.both
-
#repo-wrapper
= render 'show'
-
- = render "projects/comments/list", :list => Comment.for_commit(@commit), :project => @project, :commentable => @commit
+ -comments = @comments.select {|c| c.data.blank? } # dont work @comments.where(:data => nil)
+ = render "projects/comments/list", :list => comments, :project => @project, :commentable => @commit
= render "projects/comments/add", :project => @project, :commentable => @commit if current_user
+
+=hidden_field_tag :preview_url, project_md_preview_path(@project)
+
diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml
index 72c9f4cc7..01b6a0a1a 100644
--- a/app/views/projects/issues/_form.html.haml
+++ b/app/views/projects/issues/_form.html.haml
@@ -1,8 +1,4 @@
-.leftlist= t('activerecord.attributes.issue.title') + ':'
-.rightlist= f.text_field :title
-.leftlist= t('activerecord.attributes.issue.body') + ':'
-.rightlist= f.text_area :body
-.both
+=render 'title_body', :f => f, :id => 'new'
.leftlist= t('activerecord.attributes.issue.assignee') + ':'
.rightlist
%span#people-span.small-text= t('layout.issues.choose_user_on_left')
diff --git a/app/views/projects/issues/_header.html.haml b/app/views/projects/issues/_header.html.haml
new file mode 100644
index 000000000..3393fb965
--- /dev/null
+++ b/app/views/projects/issues/_header.html.haml
@@ -0,0 +1,21 @@
+%h3.issue_title=@issue.title
+.activity
+ .top
+ .image
+ =image_tag(avatar_url(@issue.user, :medium), :alt => 'avatar') if @issue.user
+ .text
+ %span.name=link_to(@issue.user.fullname, user_path(@issue.user)) if @issue.user
+ %br/
+ %span.date=@issue.created_at.to_s(:long)
+ %br/
+ .both
+ .fulltext.view.issue_body.formatted.cm-s-default.md_and_cm=markdown @issue.body
+.both
+%br
+- if can? :update, @issue
+ =link_to t('layout.edit'), '#', :id => 'edit_issue_content', :class => 'button'
+ =form_for :issue, :url => [@project, @issue], :method => :put, :html => { :class => 'edit_form issue', :style => 'display:none;' } do |f|
+ =render 'projects/issues/title_body', :f => f, :id => 'update'
+ =f.submit t('layout.update'), :id => 'update_issue_content'
+ =link_to t('layout.issues.cancel_button'), '#', :id => 'cancel_edit_issue_content', :class => 'button'
+%br
diff --git a/app/views/projects/issues/_index_sidebar.html.haml b/app/views/projects/issues/_index_sidebar.html.haml
index d1ccfc387..03f8fb427 100644
--- a/app/views/projects/issues/_index_sidebar.html.haml
+++ b/app/views/projects/issues/_index_sidebar.html.haml
@@ -7,12 +7,12 @@
%tr
%td.width18=radio_button_tag :myradio, 'all', !@is_assigned_to_me, {:id => 'myradio1', :class => 'niceRadio', :name => 'filter'}
%td.width135=t("layout.issues.all")
- %td.width30.right=@project.issues.count
+ %td.width30.right=@project.issues.without_pull_requests.count
%tr
%td=radio_button_tag :myradio, 'to_me', @is_assigned_to_me, {:id => 'myradio1', :class => 'niceRadio', :name => 'filter'}
%td=t("layout.issues.to_me")
- %td.width30.right=@project.issues.where(:assignee_id => current_user.id).count
- =form_tag project_issues_path(@project), :id => 'search_issue', :method => :get do
+ %td.width30.right=@project.issues.without_pull_requests.where(:assignee_id => current_user.id).count
+ =form_tag project_issues_path(@project), :id => 'search_issue', :class => 'ajax_search_form', :method => :get do
.bordered.bpadding20
=tracker_search_field(:search_issue, t('layout.issues.search'))
- if can? :new, @project.issues.new
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 5f0c13ec4..f71bf84e1 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -1,9 +1,11 @@
+-path = polymorphic_path [@project, issue.pull_request ? issue.pull_request : issue]
+
%tr#row1{:name => "row", :class => issue.labels.map(&:name).compact}
%td.td0
%span{:style => "display: none;"}=issue.serial_id
%td.td1=issue.serial_id
%td
- %a{:href => project_issue_path(@project, issue)}
+ %a{:href => path}
%div.issue_title=issue.title
.smalltext
=issue.created_at.to_s(:long)
@@ -14,9 +16,12 @@
.label.selected.tracker.left
.labeltext.selected{:style => "background: ##{label.color};"}=label.name
%td.td3
+ -if issue.pull_request
+ %a{:href => path}
+ .code #
.avatar
=link_to image_tag(avatar_url(issue.assignee), :alt => 'avatar'), user_path(issue.assignee) if issue.assignee
- %a{:href => "#{project_issue_path @project, issue}#block-list"}
+ %a{:href => "#{path}#block-list"}
.answers
.pic= image_tag 'answers.png'
.count=issue.comments.count
diff --git a/app/views/projects/issues/_issues_table.html.haml b/app/views/projects/issues/_issues_table.html.haml
new file mode 100644
index 000000000..4b62218c7
--- /dev/null
+++ b/app/views/projects/issues/_issues_table.html.haml
@@ -0,0 +1,18 @@
+#closed-switcher.blue-switcher
+ =hidden_field_tag :issues_status, @status, :id => 'issues_status'
+ .open
+ ="#{t('layout.issues.statuses.open')} (#{@opened_issues})"
+ #closed-tasks.closed
+ ="#{t('layout.issues.statuses.closed')} (#{@closed_issues})"
+ #blue-switch-select.selected{:style => "margin-left: #{@status == 'open' ? '0' : '130'}px;"}
+ .both
+.both
+#table1
+ %table#myTable.tablesorter.tracker{:cellpadding => "0", :cellspacing => "0"}
+ %thead
+ %tr
+ %th.th1{:colspan => "2"}=t('layout.issues.number')
+ %th{:colspan => "2"}=t('layout.issues.description')
+ %tbody
+ = render :partial => 'projects/issues/issue', :collection => issues
+ = will_paginate issues
diff --git a/app/views/projects/issues/_labels.html.haml b/app/views/projects/issues/_labels.html.haml
index 81effb206..fec7aba5f 100644
--- a/app/views/projects/issues/_labels.html.haml
+++ b/app/views/projects/issues/_labels.html.haml
@@ -3,16 +3,15 @@
#labels-stock
=form_tag project_issues_path(@project), :id => 'filter_labels', :method => :get do
- @project.labels.each_with_index do |label, index|
- .div-tracker-labels{:id => "label-#{label.name}", :style => @labels.include?(label.name) ? "background-color:##{label.color};color:#FFF" : ''}
- .div-label-left
- .label
- .flag{:id => "flag-#{label.name}", :style => "background-color: ##{label.color};"}
- .labeltext=label.name
- =check_box_tag 'labels[]', label.name, @labels.include?(label.name), :style => 'display:none'
- .both
- .div-label-right=Labeling.joins(:label).where(:labels => {:name => label.name, :project_id => @project.id}).count
- .both
- .both
+ =render 'projects/shared/filter_label',
+ :id => label.name.parameterize,
+ :selected => @labels.include?(label.name),
+ :extra_classes => 'div-tracker-labels',
+ :color => label.color,
+ :check_box_name => 'labels',
+ :check_box_value => label.name,
+ :name => label.name,
+ :count => Labeling.joins(:label).where(:labels => {:name => label.name, :project_id => @project.id}).count
- if can? :write, @project
%a#manage-labels.button.tmargin10{:href => "#labels-stock"}=t('layout.issues.label_manage')
#labels-edit{:style => "display: none;"}
diff --git a/app/views/projects/issues/_title_body.html.haml b/app/views/projects/issues/_title_body.html.haml
new file mode 100644
index 000000000..7f35fe7d6
--- /dev/null
+++ b/app/views/projects/issues/_title_body.html.haml
@@ -0,0 +1,8 @@
+#open-comment.comment.view
+ %h3.tmargin0= t 'activerecord.attributes.issue.title'
+ .wrapper= f.text_area :title, :cols => 80, :rows => 1
+#open-comment.comment.view
+ =render 'projects/comments/button_md_help'
+ %h3.tmargin0= t 'activerecord.attributes.issue.body'
+ =render 'projects/comments/body', :f => f, :id => id
+.both
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 02c05d53f..7b116f361 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -2,21 +2,4 @@
-render 'submenu'
-render 'index_sidebar'
-#closed-switcher.blue-switcher
- =hidden_field_tag :issues_status, @status, :id => 'issues_status'
- .open
- ="#{t('layout.issues.statuses.open')} (#{@opened_issues})"
- #closed-tasks.closed
- ="#{t('layout.issues.statuses.closed')} (#{@closed_issues})"
- #blue-switch-select.selected{:style => "margin-left: #{@status == 'open' ? '0' : '130'}px;"}
- .both
-.both
-#table1
- %table#myTable.tablesorter.tracker{:cellpadding => "0", :cellspacing => "0"}
- %thead
- %tr
- %th.th1{:colspan => "2"}=t('layout.issues.number')
- %th{:colspan => "2"}=t('layout.issues.description')
- %tbody
- = render :partial => 'issue', :collection => @issues
- = will_paginate @issues
\ No newline at end of file
+= render 'issues_table', :issues => @issues
diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml
index e3c4948e7..a59cc7a10 100644
--- a/app/views/projects/issues/new.html.haml
+++ b/app/views/projects/issues/new.html.haml
@@ -5,3 +5,6 @@
%h3.bpadding10= t("layout.issues.create_header")
= form_for :issue, :url => project_issues_path(@project), :html => { :class => 'form issue' } do |f|
= render "form", :f => f
+=hidden_field_tag :preview_url, project_md_preview_path(@project)
+= render "projects/comments/markdown_help"
+
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 6e9cc015b..6d8b4d06e 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -3,33 +3,12 @@
-render 'manage_sidebar'
-content_for :right_nopadding do
dummy
-%h3.issue_title=@issue.title
-.activity
- .top
- .image
- =image_tag(avatar_url(@issue.user, :medium), :alt => 'avatar') if @issue.user
- .text
- %span.name=link_to(@issue.user.fullname, user_path(@issue.user)) if @issue.user
- %br/
- %span.date=@issue.created_at.to_s(:long)
- %br/
- .both
- .fulltext.view.issue_body=simple_format @issue.body
-.both
-%br
-- if can? :update, @issue
- =link_to t('layout.edit'), '#', :id => 'edit_issue_content', :class => 'button'
- =form_for :issue, :url => [@project, @issue], :method => :put, :html => { :class => 'edit_form issue', :style => 'display:none;' } do |f|
- .leftlist= t('activerecord.attributes.issue.title') + ':'
- .rightlist= f.text_field :title
- .leftlist= t('activerecord.attributes.issue.body') + ':'
- .rightlist= f.text_area :body
- .both
- =f.submit t('layout.update'), :id => 'update_issue_content'
- =link_to t('layout.issues.cancel_button'), '#', :id => 'cancel_edit_issue_content', :class => 'button'
-%br
+=render 'header'
=render 'status'
= render "projects/comments/list", :list => @issue.comments, :project => @project, :commentable => @issue
%br
= render "projects/comments/add", :project => @project, :commentable => @issue if current_user
+
+=hidden_field_tag :preview_url, project_md_preview_path(@project)
+
diff --git a/app/views/projects/projects/_filters.html.haml b/app/views/projects/projects/_filters.html.haml
new file mode 100644
index 000000000..12577ac68
--- /dev/null
+++ b/app/views/projects/projects/_filters.html.haml
@@ -0,0 +1,12 @@
+-content_for :sidebar do
+ - if current_user
+ =form_tag projects_path, :id => 'filter_projects', :method => :get do
+ .bordered.bpadding20
+ =tracker_search_field(:search, t('layout.find_project'))
+ - if can?(:create, Project)
+ .bordered.bpadding20
+ = link_to t('layout.projects.new'), new_project_path, :class => 'button'
+ .bordered.bpadding20
+ %h3=t('layout.relations.filters')
+ - options_for_filters(@all_projects, @groups, @owners).each do |options|
+ =render 'projects/shared/filter_label', options
diff --git a/app/views/projects/projects/index.html.haml b/app/views/projects/projects/index.html.haml
index 01c48b8bd..920b10daa 100644
--- a/app/views/projects/projects/index.html.haml
+++ b/app/views/projects/projects/index.html.haml
@@ -1,12 +1,8 @@
-set_meta_tags :title => t('layout.projects.list_header')
+-render 'filters'
.toolbar
- = link_to t('layout.projects.new'), new_project_path, :class => 'button' if can?(:create, Project)
-
- .legend.rights
- %span.user_owner= t("layout.relations.user_owner")
- %span.group_owner= t("layout.relations.group_owner")
- %span.user= t("layout.relations.user")
- %span.group= t("layout.relations.group")
+ -%w(user_owner group_owner user group).each do |el|
+ %span{:class => el}=t "layout.relations.#{el}"
.both
- columns = [{:type => 'html'},
{:type => 'html', :sortable => false, :searchable => false},
@@ -29,15 +25,11 @@
%th.th2= t("activerecord.attributes.project.description")
%th.th3= t("layout.projects.role")
%th.th4= t("layout.projects.remove_user")
- %tr.search
- %th{:colspan => 4}
-
-
%tbody= render :partial => 'projects/projects/project', :collection => @projects
:javascript
$(document).ready(function() {
-
+ var isUpdateDataTable = null;
var JsonParser = function (json) {
var firstColumn = function(row) {
@@ -99,16 +91,17 @@
#{ format_columns_for_datatable(columns) }
],
"fnServerData": function ( sSource, aoData, fnCallback ) {
- $.getJSON( sSource, aoData, function (json) {
+ if (isUpdateDataTable != null) { isUpdateDataTable.abort(); }
+ _.each($('#filter_projects').serializeArray(), function(dv) { aoData.push(dv); });
+ isUpdateDataTable = $.getJSON( sSource, aoData, function (json) {
json.aaData = JsonParser(json);
fnCallback(json);
} );
}
});
- $('#datatable_wrapper').append("");
- var $search = $('tr.search input[type="text"]');
+ var $search = $('#search');
$search.live('blur', function() {
$this = $(this);
if ($this.val() == '') {
@@ -128,6 +121,12 @@
$search.live('keyup', function() {
oTable.fnFilter(this.value);
});
+ $(".div-filter-labels").live('click', function() {
+ oTable.dataTable().fnDraw();
+ });
+ $('#filter_projects').live('submit', function() {
+ return false;
+ });
});
=# will_paginate
diff --git a/app/views/projects/projects/refs_list.html.haml b/app/views/projects/projects/refs_list.html.haml
new file mode 100644
index 000000000..43ea5f649
--- /dev/null
+++ b/app/views/projects/projects/refs_list.html.haml
@@ -0,0 +1 @@
+=render 'projects/pull_requests/ref_select', :kind => 'to', :project => @project, :current => @selected
diff --git a/app/views/projects/pull_requests/_activity.html.haml b/app/views/projects/pull_requests/_activity.html.haml
new file mode 100644
index 000000000..3f868e607
--- /dev/null
+++ b/app/views/projects/pull_requests/_activity.html.haml
@@ -0,0 +1,23 @@
+.hr
+-commits_queue = []
+-merge_activity(@comments, @commits).each do |item| #
+ -if item.is_a? Comment
+ =render 'projects/git/commits/commits_small', :commits => commits_queue if commits_queue.present?
+ -commits_queue.clear
+ =render 'projects/comments/comment', :comment => item, :data => {:project => @project, :commentable => @commentable}
+ -elsif item.is_a? Grit::Commit
+ -commits_queue << item
+ -elsif item.is_a? Array
+ =render 'projects/git/commits/commits_small', :commits => commits_queue if commits_queue.present?
+ -commits_queue.clear
+ -unless item[1].first.data[:actual]
+ -exp_id = "expand-comment#{item[1].first.id}"
+ .activity
+ =t '.show_outdated_diff'
+ %span.data-expander.collapsed{:id => exp_id}
+ .hidden{:id => "content-#{exp_id}"}
+ =render 'projects/pull_requests/discussion_comments', :item => item, :add_id => nil
+ -else
+ =render 'projects/pull_requests/discussion_comments', :item => item, :add_id => nil
+=render 'projects/git/commits/commits_small', :commits => commits_queue if commits_queue.present?
+
diff --git a/app/views/projects/pull_requests/_diff_commits_tabs.html.haml b/app/views/projects/pull_requests/_diff_commits_tabs.html.haml
new file mode 100644
index 000000000..117b84635
--- /dev/null
+++ b/app/views/projects/pull_requests/_diff_commits_tabs.html.haml
@@ -0,0 +1,21 @@
+#diff.tab-pane
+ .leftside
+ -total_additions = @stats.inject(0) {|sum, n| sum + n.additions}
+ -total_deletions = @stats.inject(0) {|sum, n| sum + n.deletions}
+ %h5= t("layout.projects.diff_show_header",
+ :files => t("layout.projects.commit_files_count", :count => @stats.count),
+ :additions => t("layout.projects.commit_additions_count", :count => total_additions),
+ :deletions => t("layout.projects.commit_deletions_count", :count => total_deletions))
+ .both
+ -begin
+ = render_diff_stats(@stats)
+ = render :partial => 'pull_diff', :collection => @pull.diff
+ - rescue => ex
+ -if ex.try(:message) == 'Grit::Git::GitTimeout'
+ %p= t 'layout.git.repositories.commit_diff_too_big'
+ -else
+ -raise ex
+#commits.tab-pane
+ - if @total_commits > @commits.count
+ %div= t("projects.pull_requests.is_big", :count => @commits.count)
+ = render :partial => 'projects/git/commits/commits', :object => @commits
diff --git a/app/views/projects/pull_requests/_discussion_comments.html.haml b/app/views/projects/pull_requests/_discussion_comments.html.haml
new file mode 100644
index 000000000..e42f7eb69
--- /dev/null
+++ b/app/views/projects/pull_requests/_discussion_comments.html.haml
@@ -0,0 +1,15 @@
+-comment = item[1].first
+.file
+ .top
+ .l=comment.data[:view_path]
+ -if comment.actual_inline_comment? @pull.diff
+ .r=link_to t("layout.pull_requests.view_full_changes"),
+ "#{project_commentable_path(@project, @commentable)}##{comment_anchor(comment)}",
+ :class => 'link_to_full_changes'
+ -else
+ .r
+ =t'projects.pull_requests.outdated_diff'
+ =image_tag 'x.png'
+ .clear
+ .diff_data=render_diff(comment.inline_diff, :diff_counter => 0, :comments => item[1], :filepath => comment.data[:path])
+
diff --git a/app/views/projects/pull_requests/_index_sidebar.html.haml b/app/views/projects/pull_requests/_index_sidebar.html.haml
new file mode 100644
index 000000000..6b1af739c
--- /dev/null
+++ b/app/views/projects/pull_requests/_index_sidebar.html.haml
@@ -0,0 +1,4 @@
+-content_for :sidebar do
+ =form_tag project_pull_requests_path(@project), :id => 'filter_pull_requests', :method => :get, :class => 'ajax_search_form' do
+ .bordered.bpadding20
+ =tracker_search_field(:search_pull_request, t('layout.pull_requests.search'))
diff --git a/app/views/projects/pull_requests/_nav_tabs.html.haml b/app/views/projects/pull_requests/_nav_tabs.html.haml
new file mode 100644
index 000000000..5d244eb0c
--- /dev/null
+++ b/app/views/projects/pull_requests/_nav_tabs.html.haml
@@ -0,0 +1,22 @@
+%ul.nav.nav-tabs#pull_tabs
+ %li
+ %a{"data-toggle" => "tab", :href => "#discussion"}=t 'pull_requests.tabs.discussion'
+ -if @stats
+ %li
+ %a{"data-toggle" => "tab", :href => "#diff"}="#{t'pull_requests.tabs.diff'} (#{@stats.count})"
+ -if @commits
+ %li
+ - commits_message = @commits.count.to_s
+ - commits_message << '+' if @total_commits > @commits.count
+ %a{"data-toggle" => "tab", :href => "#commits"}="#{t'pull_requests.tabs.commits'} (#{commits_message})"
+
+:javascript
+ $(document).ready(function() {
+ var tab = 'discussion';
+ if(document.location.href.match(/(.*)#diff(.*)/)) {
+ tab = 'diff';
+ } else if(document.location.href.match(/(.*)#commits(.*)/)) {
+ tab = 'commits';
+ };
+ $('#pull_tabs a[href="#'+tab+'"]').tab('show');
+ });
diff --git a/app/views/projects/pull_requests/_pull_diff.html.haml b/app/views/projects/pull_requests/_pull_diff.html.haml
new file mode 100644
index 000000000..60fbe55ff
--- /dev/null
+++ b/app/views/projects/pull_requests/_pull_diff.html.haml
@@ -0,0 +1,10 @@
+- commit_id = pull_diff.deleted_file ? @pull.to_commit.id : @pull.from_commit.id
+.file
+ %a{:name => "diff-#{pull_diff_counter}"}
+ .top
+ .l= h(pull_diff.renamed_file ? "#{pull_diff.a_path.rtruncate 60} -> #{pull_diff.b_path.rtruncate 60}" : pull_diff.a_path.rtruncate(120))
+ - if pull_diff.b_path.present?
+ .r= link_to "view file @ #{short_hash_id(commit_id)}", blob_path(@project, commit_id, pull_diff.b_path)
+ .clear
+ -if pull_diff.diff.present? && !(@pull.repo.tree(commit_id) / pull_diff.b_path).binary?
+ .diff_data=render_diff(pull_diff, :diff_counter => pull_diff_counter, :comments => @comments)
diff --git a/app/views/projects/pull_requests/_ref_select.html.haml b/app/views/projects/pull_requests/_ref_select.html.haml
new file mode 100644
index 000000000..8a979ecf8
--- /dev/null
+++ b/app/views/projects/pull_requests/_ref_select.html.haml
@@ -0,0 +1,3 @@
+-ref="#{kind}_ref"
+.l=select_tag ref, ref_selector_options(project, current), :id => ref, :class => 'sel80', :name => "pull_request[#{ref}]"
+
diff --git a/app/views/projects/pull_requests/_status.html.haml b/app/views/projects/pull_requests/_status.html.haml
new file mode 100644
index 000000000..de98651d4
--- /dev/null
+++ b/app/views/projects/pull_requests/_status.html.haml
@@ -0,0 +1,14 @@
+- if can?(:merge, @pull) && @pull.can_merging?
+ %br
+ =form_for PullRequest.new, :url => merge_project_pull_request_path(@project, @pull), :html => { :method => :put, :class => :form } do |f|
+ =f.submit t 'projects.pull_requests.ready'
+-else
+ .flash
+ %div{:class => @pull.ready? ? 'notice' : 'alert'}
+ =pull_status @pull
+-if can? :update, @pull
+ -if action = @pull.can_close? ? 'close' : ('reopen' if @pull.can_reopen?)
+ %br
+ =form_for :pull, :url => [@project, @pull], :html => { :id => 'do_pull_action',:method => :put, :class => :form } do |f|
+ =hidden_field_tag "pull_request_action", action
+ =f.submit t ".#{action}"
diff --git a/app/views/projects/pull_requests/index.html.haml b/app/views/projects/pull_requests/index.html.haml
new file mode 100644
index 000000000..5aa20c682
--- /dev/null
+++ b/app/views/projects/pull_requests/index.html.haml
@@ -0,0 +1,6 @@
+-ar = 'activerecord.attributes.pull_requests'
+-set_meta_tags :title => [title_object(@project), t('.title')]
+-render 'submenu'
+-render 'index_sidebar'
+
+= render 'projects/issues/issues_table', :issues => @issues_with_pull_request
diff --git a/app/views/projects/pull_requests/new.html.haml b/app/views/projects/pull_requests/new.html.haml
new file mode 100644
index 000000000..6ad43d4de
--- /dev/null
+++ b/app/views/projects/pull_requests/new.html.haml
@@ -0,0 +1,42 @@
+-ar = 'activerecord.attributes.pull_requests'
+-set_meta_tags :title => [title_object(@project), t('.title')]
+= render :partial => 'submenu'
+%h3.bpadding10=pull_header @pull
+#repo-wrapper
+ =render 'nav_tabs'
+ .tab-content.pull_diff_fix
+ #discussion.tab-pane.active
+ =hidden_field_tag :update_action, new_project_pull_request_path
+ =form_for @pull, :url => (@pull.already? ? new_project_pull_request_path : project_pull_requests_path), :html => {:class => 'well well-large', :method => (@pull.already? ? :get : :post)} do |f|
+
+ .leftlist=f.label :from_project, t("#{ar}.from_project"), :class => :label
+ %div
+ .l=text_field_tag :from_project, @pull.from_project.name_with_owner, :readonly => :readonly, :style => 'background-color: #DDD;'
+ =render 'ref_select', :kind => 'from', :project => @pull.from_project, :current => @pull.from_ref
+ .both
+ .leftlist=f.label :to_project, t("#{ar}.to_project"), :class => :label
+ %div
+ .l=f.autocomplete_field :to_project, autocomplete_to_project_project_pull_requests_path, :value => @pull.to_project.name_with_owner, :id_element => 'pull_request_to_project_id', :name => 'to_project'
+ =render 'ref_select', :kind => 'to', :project => @pull.to_project, :current => @pull.to_ref
+ .both
+ .leftlist.big-list
+ .rightlist=f.submit t('.update'), :class => 'btn btn-primary disabled', 'data-loading-text' => t('layout.processing'), :style => @pull.already? ? '' : 'display: none;', :id => 'update_pull'
+ .both
+ -unless @pull.already?
+ =f.fields_for :issue do |issue|
+ =render 'projects/issues/title_body', :f => issue, :id => 'new'
+ .leftlist.big-list=f.label :title, t('activerecord.attributes.issue.status'), :class => :label
+ .rightlist
+ .flash
+ %div{:class => @pull.ready? ? 'notice' : 'alert'}
+ =pull_status @pull
+ .both
+ .leftlist.big-list
+ .rightlist
+ =f.submit t('.submit'), :class => 'btn btn-primary disabled', 'data-loading-text' => t('layout.processing'), :id => 'create_pull' unless @pull.already?
+ .both
+ =render 'diff_commits_tabs' if !@pull.already? && @stats != nil
+
+=hidden_field_tag :preview_url, project_md_preview_path(@project)
+= render "projects/comments/markdown_help"
+
diff --git a/app/views/projects/pull_requests/show.html.haml b/app/views/projects/pull_requests/show.html.haml
new file mode 100644
index 000000000..0e525d1cd
--- /dev/null
+++ b/app/views/projects/pull_requests/show.html.haml
@@ -0,0 +1,19 @@
+-ar = 'activerecord.attributes.pull_requests'
+-set_meta_tags :title => [title_object(@project), t('.title', :name => @pull.title.truncate(40), :user => @pull.user.uname)]
+= render :partial => 'submenu'
+%h3.bpadding10
+ =pull_status_label @pull
+ =pull_header @pull
+#repo-wrapper
+ =render 'nav_tabs'
+ .tab-content.pull_diff_fix
+ #discussion.tab-pane.active
+ =render 'projects/issues/header'
+ =render 'activity'
+ %br
+ =render "projects/comments/add", :project => @project, :commentable => @issue if current_user
+ .pull_status
+ =render 'status'
+ =render 'diff_commits_tabs' unless @pull.already?
+=hidden_field_tag :preview_url, project_md_preview_path(@project)
+= render "projects/comments/markdown_help"
diff --git a/app/views/projects/shared/_filter_label.html.haml b/app/views/projects/shared/_filter_label.html.haml
new file mode 100644
index 000000000..14fa61277
--- /dev/null
+++ b/app/views/projects/shared/_filter_label.html.haml
@@ -0,0 +1,11 @@
+- extra_classes ||= ''
+.div-filter-labels{:id => "label-#{id}", :class => extra_classes, :style => selected ? "background-color:##{color};color:#FFF" : ''}
+ .div-label-left
+ .label
+ .flag{:id => "flag-#{id}", :style => "background-color: ##{color};"}
+ .labeltext=name
+ =check_box_tag "#{check_box_name}[]", check_box_value, selected, :style => 'display:none'
+ .both
+ .div-label-right=count
+ .both
+.both
diff --git a/app/views/projects/wiki/_diff_data.html.haml b/app/views/projects/wiki/_diff_data.html.haml
index 1c00ff781..952b6ea45 100644
--- a/app/views/projects/wiki/_diff_data.html.haml
+++ b/app/views/projects/wiki/_diff_data.html.haml
@@ -1,4 +1,7 @@
-.blob_header
- .size= h(diff.deleted_file ? diff.a_path : diff.b_path)
- .clear
-.diff_data.highlight= render_diff(diff)
\ No newline at end of file
+.file
+ %a{:name => "diff-#{diff_counter}"}
+ .top
+ .l= h((diff.deleted_file ? diff.a_path : diff.b_path).rtruncate 100)
+ .clear
+ .diff_data.highlight= render_diff(diff, :diff_counter => diff_counter, :in_wiki => true)
+
diff --git a/app/views/shared/_avatar_form.html.haml b/app/views/shared/_avatar_form.html.haml
new file mode 100644
index 000000000..9729a6b9b
--- /dev/null
+++ b/app/views/shared/_avatar_form.html.haml
@@ -0,0 +1,10 @@
+.leftlist= f.label :avatar, t("layout.avatars.avatar_with_size", :max => number_to_human_size(Avatar::MAX_AVATAR_SIZE))
+.rightlist= image_tag(avatar_url(subject, :medium))
+.leftlist
+.rightlist
+ .check
+ %span#niceCheckbox1.niceCheck-main= check_box_tag "delete_avatar", 1, false, :class => 'niceCheckbox1'
+ .forcheck= t('layout.avatars.delete_avatar')
+ .both
+ = f.file_field :avatar
+.both
\ No newline at end of file
diff --git a/app/views/shared/_feed_message.html.haml b/app/views/shared/_feed_message.html.haml
index 42b5d942a..414b4cc9b 100644
--- a/app/views/shared/_feed_message.html.haml
+++ b/app/views/shared/_feed_message.html.haml
@@ -1,4 +1,4 @@
-.activity{:id => presenter.comment_id? ? "comment##{presenter.comment_id}" : ''}
+.activity{:id => presenter.comment_id? ? presenter.comment_anchor : ''}
.top
- if presenter.buttons?
%span.buttons= raw presenter.buttons.join(' | ').html_safe
@@ -19,5 +19,5 @@
- if presenter.content?
.fulltext{:class => "#{presenter.expandable? ? "hidden" : ''} #{presenter.caption? ? "" : "alone"}",
:id => presenter.expandable? ? "content-expand#{item_no}" : ''}
- = presenter.content
+ .cm-s-default.md_and_cm=markdown presenter.content
.both
diff --git a/app/views/shared/_members_table.html.haml b/app/views/shared/_members_table.html.haml
index 2b9ca7177..306e9e8aa 100644
--- a/app/views/shared/_members_table.html.haml
+++ b/app/views/shared/_members_table.html.haml
@@ -33,7 +33,7 @@
.hr.top
= form_tag add_member_path do
.admin-search
- = autocomplete_field_tag 'member_id', params[:member_id], autocomplete_user_uname_users_path, :id_element => '#member_id_field'
+ = autocomplete_field_tag 'member_id', params[:member_id], autocomplete_user_uname_autocompletes_path, :id_element => '#member_id_field'
= hidden_field_tag 'member_id', nil, :id => 'member_id_field'
= submit_tag t("layout.add"), :class => 'button'
.both
diff --git a/app/views/shared/_profile.html.haml b/app/views/shared/_profile.html.haml
new file mode 100644
index 000000000..6f8c14778
--- /dev/null
+++ b/app/views/shared/_profile.html.haml
@@ -0,0 +1,38 @@
+- edit_link ||= nil
+- user ||= nil
+- group ||= nil
+- name ||= uname
+.avatar
+ = image_tag avatar_url(user || group, :big)
+ - if edit_link
+ %br
+ = edit_link
+.info
+ %h3= title uname
+ = name
+ - if user
+ %br
+ = mail_to user.email, user.email, :encode => "javascript"
+ %br
+ - if user
+ %h4= t("activerecord.attributes.user.professional_experience") + ":"
+ %p= user.professional_experience
+ - else
+ %h4= t("activerecord.attributes.group.description") + ":"
+ %p= group.description
+.content
+ %h4= t("layout.projects.public_projects_list") + ":"
+ %p
+ =form_tag search_path, :id => 'filter_projects', :method => :get do
+ =tracker_search_field(:search, t('layout.find_project'))
+ %br
+ %p
+ - projects.each do |project|
+ = link_to project.name, project
+ %br
+ %br
+ = will_paginate projects
+ %br
+
+:javascript
+ $('article .all').addClass('verybigpadding');
diff --git a/app/views/user_mailer/_footer.html.haml b/app/views/user_mailer/_footer.html.haml
new file mode 100644
index 000000000..b4d481d03
--- /dev/null
+++ b/app/views/user_mailer/_footer.html.haml
@@ -0,0 +1,7 @@
+%hr
+ %p{:style => "font-size:small;color:#666"}
+ = t("notifications.footers.notifiers")
+ = link_to t("notifications.footers.notification_center"), notifiers_settings_url
+ %p{:style => "font-size:small;color:#666"}
+ = t("notifications.footers.support_team")
+
diff --git a/app/views/user_mailer/build_list_notification.en.haml b/app/views/user_mailer/build_list_notification.en.haml
index 9f58dafe3..afd0752d6 100644
--- a/app/views/user_mailer/build_list_notification.en.haml
+++ b/app/views/user_mailer/build_list_notification.en.haml
@@ -12,4 +12,4 @@
More detailed information you can get by link:
= link_to "task [№ #{@build_list.bs_id ? @build_list.bs_id : t("layout.build_lists.bs_id_not_set")}]", build_list_url(@build_list)
-%p== Support team «ROSA Build System»
+= render 'footer'
diff --git a/app/views/user_mailer/build_list_notification.ru.haml b/app/views/user_mailer/build_list_notification.ru.haml
index 8a0970053..21401beaa 100644
--- a/app/views/user_mailer/build_list_notification.ru.haml
+++ b/app/views/user_mailer/build_list_notification.ru.haml
@@ -12,4 +12,4 @@
Более подробную информацию можно получить по ссылке:
= link_to "задание [№ #{@build_list.bs_id ? @build_list.bs_id : t("layout.build_lists.bs_id_not_set")}]", build_list_url(@build_list)
-%p== Команда поддержки «ROSA Build System»
+= render 'footer'
diff --git a/app/views/user_mailer/invite_approve_notification.en.haml b/app/views/user_mailer/invite_approve_notification.en.haml
index f3ff83e13..27a4ca11a 100644
--- a/app/views/user_mailer/invite_approve_notification.en.haml
+++ b/app/views/user_mailer/invite_approve_notification.en.haml
@@ -1,7 +1,7 @@
%p== Hello, #{@register_request.name || @register_request.email}.
%p
- You have been invited to project ABF. Please click on the following link for registration:
- = link_to 'link', new_user_registration_url(:invitation_token => @register_request.token)
+ You have been invited to project ABF. Please click on the following
+ = link_to 'link for registration', new_user_registration_url(:invitation_token => @register_request.token)
%p== Support team «ROSA Build System»
diff --git a/app/views/user_mailer/invite_approve_notification.ru.haml b/app/views/user_mailer/invite_approve_notification.ru.haml
index 8d4bcc6bd..e75296fe2 100644
--- a/app/views/user_mailer/invite_approve_notification.ru.haml
+++ b/app/views/user_mailer/invite_approve_notification.ru.haml
@@ -1,7 +1,7 @@
%p== Здравствуйте, #{@register_request.name || @register_request.email}.
%p
- Вы приглашены в проект ABF. Чтобы зарегистрироваться перейдите по
- = link_to 'ссылке', new_user_registration_url(:invitation_token => @register_request.token)
+ Вы приглашены в проект ABF. Пожалуйста, перейдите по
+ = link_to 'ссылке для регистрации', new_user_registration_url(:invitation_token => @register_request.token)
%p== Команда поддержки «ROSA Build System»
diff --git a/app/views/user_mailer/issue_assign_notification.en.haml b/app/views/user_mailer/issue_assign_notification.en.haml
index c913bea95..3aa4a049a 100644
--- a/app/views/user_mailer/issue_assign_notification.en.haml
+++ b/app/views/user_mailer/issue_assign_notification.en.haml
@@ -4,4 +4,4 @@
%p You have been assigned to issue #{ link_to @issue.title, project_issue_url(@issue.project, @issue) }
-%p== Support team «ROSA Build System»
+= render 'footer'
diff --git a/app/views/user_mailer/issue_assign_notification.ru.haml b/app/views/user_mailer/issue_assign_notification.ru.haml
index da59dc6cc..5b5dcd7d5 100644
--- a/app/views/user_mailer/issue_assign_notification.ru.haml
+++ b/app/views/user_mailer/issue_assign_notification.ru.haml
@@ -4,4 +4,4 @@
%p Вам была назначена задача #{ link_to @issue.title, project_issue_url(@issue.project, @issue) }
-%p== Команда поддержки «ROSA Build System»
+= render 'footer'
diff --git a/app/views/user_mailer/new_comment_notification.en.haml b/app/views/user_mailer/new_comment_notification.en.haml
index 7c6c93175..00d903a37 100644
--- a/app/views/user_mailer/new_comment_notification.en.haml
+++ b/app/views/user_mailer/new_comment_notification.en.haml
@@ -11,4 +11,4 @@
%p "#{ @comment.body }"
-%p== Support team «ROSA Build System»
+= render 'footer'
diff --git a/app/views/user_mailer/new_comment_notification.ru.haml b/app/views/user_mailer/new_comment_notification.ru.haml
index 1c7b933e5..da038b0e6 100644
--- a/app/views/user_mailer/new_comment_notification.ru.haml
+++ b/app/views/user_mailer/new_comment_notification.ru.haml
@@ -11,4 +11,4 @@
%p "#{ @comment.body }"
-%p== Команда поддержки «ROSA Build System»
+= render 'footer'
diff --git a/app/views/user_mailer/new_issue_notification.en.haml b/app/views/user_mailer/new_issue_notification.en.haml
index 713c7ed36..b8be5a8f7 100644
--- a/app/views/user_mailer/new_issue_notification.en.haml
+++ b/app/views/user_mailer/new_issue_notification.en.haml
@@ -4,4 +4,4 @@
%p To project #{ link_to @issue.project.name, project_url(@issue.project) } has been added an issue #{ link_to @issue.title, project_issue_url(@issue.project, @issue) }
-%p== Support team «ROSA Build System»
+= render 'footer'
diff --git a/app/views/user_mailer/new_issue_notification.ru.haml b/app/views/user_mailer/new_issue_notification.ru.haml
index 302a13a28..9cbb20188 100644
--- a/app/views/user_mailer/new_issue_notification.ru.haml
+++ b/app/views/user_mailer/new_issue_notification.ru.haml
@@ -4,4 +4,4 @@
%p К проекту #{ link_to @issue.project.name, project_url(@issue.project) } была добавлена задача #{ link_to @issue.title, project_issue_url(@issue.project, @issue) }
-%p== Команда поддержки «ROSA Build System»
+= render 'footer'
diff --git a/app/views/user_mailer/new_user_notification.en.haml b/app/views/user_mailer/new_user_notification.en.haml
index 4afbf64cb..307e7c6a5 100644
--- a/app/views/user_mailer/new_user_notification.en.haml
+++ b/app/views/user_mailer/new_user_notification.en.haml
@@ -7,4 +7,4 @@
%p
==Your email : #{@user.email}
-%p== Support team «ROSA Build System»
+= render 'footer'
diff --git a/app/views/user_mailer/new_user_notification.ru.haml b/app/views/user_mailer/new_user_notification.ru.haml
index cb7929fab..4ae928374 100644
--- a/app/views/user_mailer/new_user_notification.ru.haml
+++ b/app/views/user_mailer/new_user_notification.ru.haml
@@ -7,4 +7,4 @@
%p
==Ваш email : #{@user.email}
-%p== Команда поддержки «ROSA Build System»
+= render 'footer'
diff --git a/app/views/users/base/_form.html.haml b/app/views/users/base/_form.html.haml
index 4baa5218e..e661a9191 100644
--- a/app/views/users/base/_form.html.haml
+++ b/app/views/users/base/_form.html.haml
@@ -28,16 +28,7 @@
.leftlist= f.label :location, t("activerecord.attributes.user.location")
.rightlist= f.text_field :location
.both
-.leftlist= f.label :avatar, t("layout.users.avatar_with_size", :max => number_to_human_size(User::MAX_AVATAR_SIZE))
-.rightlist= image_tag(avatar_url(@user, :medium))
-.leftlist
-.rightlist
- .check
- %span#niceCheckbox1.niceCheck-main= check_box_tag "delete_avatar", 1, false, :class => 'niceCheckbox1'
- .forcheck= t('layout.users.delete_avatar')
- .both
- = f.file_field :avatar
-.both
+= render 'shared/avatar_form', :subject => @user, :f => f
.leftlist= f.label :professional_experience, t("activerecord.attributes.user.professional_experience")
.rightlist= f.text_area :professional_experience
.both
diff --git a/app/views/users/profile/show.html.haml b/app/views/users/profile/show.html.haml
index 3ab79e99f..5c16bf2fa 100644
--- a/app/views/users/profile/show.html.haml
+++ b/app/views/users/profile/show.html.haml
@@ -1,20 +1,4 @@
-.left
- = image_tag avatar_url(@user, :big)
- %br
- = link_to t("layout.users.settings"), current_user == @user ? profile_settings_path : edit_admin_user_path(@user), :class => 'button width81' if can? :edit, @user
-.left
- %h3= title @user.uname
- = @user.name
- %br
- = mail_to @user.email, @user.email, :encode => "javascript"
- %br
- %h4= t("activerecord.attributes.user.professional_experience") + ":"
- %p= @user.professional_experience
- %h4= t("layout.users.public_projects_list") + ":"
- %p
- - @projects.each do |project|
- = link_to project.name, project
- %br
+-set_meta_tags :title => title_object(@user)
-:javascript
- $('article .all').addClass('verybigpadding');
+- edit_link = can?(:edit, @user) ? link_to(t("layout.users.settings"), current_user == @user ? profile_settings_path : edit_admin_user_path(@user), :class => 'button width81') : nil
+= render 'shared/profile', :uname => @user.uname, :name => @user.name, :user => @user, :search_path => user_path, :projects => @projects, :edit_link => edit_link
\ No newline at end of file
diff --git a/app/views/users/settings/profile.html.haml b/app/views/users/settings/profile.html.haml
index bcf053457..d09263464 100644
--- a/app/views/users/settings/profile.html.haml
+++ b/app/views/users/settings/profile.html.haml
@@ -7,7 +7,7 @@
.notify
%p= t('layout.users.public_data_edit_warning')
.notify
- %p= t('layout.users.avatar_notice')
+ %p= t('layout.avatars.avatar_notice')
:javascript
$('article .right').addClass('middlepadding');
diff --git a/config/application.rb b/config/application.rb
index 3cfd1ce68..b6e82755f 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -2,6 +2,7 @@
require File.expand_path('../boot', __FILE__)
require 'rails/all'
+require './lib/api_defender'
# If you have a Gemfile, require the gems listed there, including any gems
# you've limited to :test, :development, or :production.
@@ -14,7 +15,9 @@ end
module Rosa
class Application < Rails::Application
-
+ # Rate limit
+ config.middleware.insert_after Rack::Lock, ApiDefender
+
config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
config.autoload_paths += %W(#{config.root}/lib)
diff --git a/config/environments/production.rb b/config/environments/production.rb
index b3ca044d1..2ab9938fd 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -38,6 +38,7 @@ Rosa::Application.configure do
# Disable delivery errors, bad email addresses will be ignored
# config.action_mailer.raise_delivery_errors = false
config.action_mailer.default_url_options = { :host => 'abf.rosalinux.ru' }
+ config.delivery_method = :sendmail
# Enable threaded mode
# config.threadsafe!
diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb
index e9b053ba5..c421b2c2e 100644
--- a/config/initializers/mime_types.rb
+++ b/config/initializers/mime_types.rb
@@ -12,7 +12,8 @@ Mime::Type.register "text/plain", 'patch'
[["text/x-python", ['py'], '8bit'],
["text/x-rpm-spec", ['spec'], '8bit'],
["text/x-csrc", ['h', 'c'], '8bit'],
- ["text/x-c++src", ['cpp'], '8bit']
+ ["text/x-c++src", ['cpp'], '8bit'],
+ ["text/x-diff", ['diff'], '8bit']
].each do |type|
MIME::Types.add MIME::Type.from_array(type)
end
diff --git a/config/initializers/rack_throttle.rb b/config/initializers/rack_throttle.rb
new file mode 100644
index 000000000..dc0572042
--- /dev/null
+++ b/config/initializers/rack_throttle.rb
@@ -0,0 +1,7 @@
+class Rack::Throttle::Limiter
+ def http_error(code, message = nil, headers = {})
+ [code, {'Content-Type' => 'application/json; charset=utf-8'}.merge(headers),
+ Array(({'message' => http_status(code) + " | " + message}.to_json))]
+ end
+end
+
diff --git a/config/locales/en.yml b/config/locales/en.yml
index de81c3bb8..ce2f6a1be 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -47,6 +47,8 @@ en:
processing: working ...
invalid_content_type: incorrect type
atom_link_tag_title: Private feed for %{nickname} | %{app_name}
+ preview: Preview
+ link: Link
settings:
label: Settings
@@ -114,7 +116,7 @@ en:
empty: "Repository is empty. You need to wait some time if you have forked project or imported package"
source: Source
commits: Commits
- commit_diff_too_big: Sorry, commit too big!
+ commit_diff_too_big: Sorry, diff too big!
tags: Tags
branches: Branches
project_versions: Versions
@@ -136,6 +138,9 @@ en:
exception_message: Access violation to this page!
+ 500_message: Error 500. Something went wrong. We've been notified about this issue and we'll take a look at it shortly.
+ 404_message: Error 404. Resource not found!
+
collaborators:
successfully_changed: Collaborators list successfully changed
error_in_changing: Collaborators list changing error
@@ -185,3 +190,12 @@ en:
private_user:
login: Login
password: Password
+
+ arch:
+ name: Name
+ created_at: Created
+ updated_at: Updated
+
+ into: into
+ from: from
+ by: by
diff --git a/config/locales/menu.en.yml b/config/locales/menu.en.yml
index 091cacd7e..d940e3c51 100644
--- a/config/locales/menu.en.yml
+++ b/config/locales/menu.en.yml
@@ -18,6 +18,8 @@ en:
tos: Terms Of Service
tos_url: http://www.rosalab.com/about
support: Support
+ developer_api: Developer API
+ developer_api_url: http://abf-doc.rosalinux.ru
project_menu:
project: Project
commits: Commits
@@ -26,6 +28,7 @@ en:
wiki: Wiki
readme: Readme
settings: Settings
+ pull_requests: Pull Requests
feed_menu:
all: All
code: Code
diff --git a/config/locales/menu.ru.yml b/config/locales/menu.ru.yml
index d2ab87bad..35783e139 100644
--- a/config/locales/menu.ru.yml
+++ b/config/locales/menu.ru.yml
@@ -18,6 +18,8 @@ ru:
tos: Условия использования
tos_url: http://www.rosalab.ru/about
support: Служба поддержки
+ developer_api: API для разработчиков
+ developer_api_url: http://abf-doc.rosalinux.ru
project_menu:
project: Проект
commits: Коммиты
@@ -26,6 +28,7 @@ ru:
wiki: Wiki
readme: Readme
settings: Настройки
+ pull_requests: Пул реквесты
feed_menu:
all: Все
code: Код
diff --git a/config/locales/models/activity_feed.en.yml b/config/locales/models/activity_feed.en.yml
index 8df4770b3..b00b2e762 100644
--- a/config/locales/models/activity_feed.en.yml
+++ b/config/locales/models/activity_feed.en.yml
@@ -44,3 +44,8 @@ en:
success: completed successfully
failed: completed with error "%{error}"
pending: build is pending
+
+ footers:
+ support_team: Support team «ROSA Build System».
+ notifiers: You can configure notifications in
+ notification_center: Notification Center.
diff --git a/config/locales/models/activity_feed.ru.yml b/config/locales/models/activity_feed.ru.yml
index 60911a6c8..9cc3f9640 100644
--- a/config/locales/models/activity_feed.ru.yml
+++ b/config/locales/models/activity_feed.ru.yml
@@ -45,3 +45,8 @@ ru:
success: успешно собрано
failed: завершилось с ошибкой "%{error}"
pending: ожидает сборки
+
+ footers:
+ support_team: Команда поддержки «ROSA Build System».
+ notifiers: Вы можете настроить уведомления в
+ notification_center: Центре уведомлений.
diff --git a/config/locales/models/avatar.en.yml b/config/locales/models/avatar.en.yml
new file mode 100644
index 000000000..eda4a76ab
--- /dev/null
+++ b/config/locales/models/avatar.en.yml
@@ -0,0 +1,6 @@
+en:
+ layout:
+ avatars:
+ avatar_notice: Without uploaded avatar will be used avatar from gravar web service.
+ delete_avatar: Delete avatar
+ avatar_with_size: Avatar (less than %{max})
\ No newline at end of file
diff --git a/config/locales/models/avatar.ru.yml b/config/locales/models/avatar.ru.yml
new file mode 100644
index 000000000..4fac6e7a5
--- /dev/null
+++ b/config/locales/models/avatar.ru.yml
@@ -0,0 +1,6 @@
+ru:
+ layout:
+ avatars:
+ avatar_notice: При отсутствии загруженного аватара будет использован Ваш аватар на сервисе gravatar.
+ delete_avatar: Удалить аватар
+ avatar_with_size: Аватар (менее %{max})
\ No newline at end of file
diff --git a/config/locales/models/build_list.en.yml b/config/locales/models/build_list.en.yml
index f29c7d7cb..962155821 100644
--- a/config/locales/models/build_list.en.yml
+++ b/config/locales/models/build_list.en.yml
@@ -29,6 +29,7 @@ en:
started_at: Build started at
duration: Build duration in seconds
mass_build_id: Mass build
+ commit_hash: Commit hash
build_list/item:
name: Name
@@ -60,7 +61,9 @@ en:
show: Show
cancel: Cancel build
cancel_success: 'Build canceled'
+ publish_success: 'Build published'
cancel_fail: 'Errors during build cancelation!'
+ publish_fail: 'Errors during build publishing!'
publish_success: 'Build is queued for publishing'
reject_publish_success: 'Publishing rejected'
publish_fail: 'Errors during build publishing!'
@@ -121,7 +124,7 @@ en:
build_log: Build Log
not_available: Log not available yet.
download: Download log
- autoreload: Update log every
+ autoreload: Update log every
load_lines: Load last %{count} lines
reload_times:
@@ -144,3 +147,5 @@ en:
cannot_write: You can't build project to this repository.
can_not_published: Build can only be published with status "Build complete"
frozen_platform: In case of a repository for package storage with frozen platform allowed only bugfix and security updates
+ wrong_include_repos: Include repos have to belongs to build for platform
+ wrong_commit_hash: Unable find commit '%{commit_hash}' in project
diff --git a/config/locales/models/build_list.ru.yml b/config/locales/models/build_list.ru.yml
index 2f81ba2d0..4953d08bd 100644
--- a/config/locales/models/build_list.ru.yml
+++ b/config/locales/models/build_list.ru.yml
@@ -28,6 +28,7 @@ ru:
preferences: Настройки
duration: Длительность билда в секундах
mass_build_id: Массовая сборка
+ commit_hash: Хэш коммита
build_list/item:
name: Название
@@ -120,9 +121,9 @@ ru:
build_log: Лог сборки
not_available: В настоящий момент лог недоступен.
download: Загрузить лог
- autoreload: Обновлять лог каждые
+ autoreload: Обновлять лог каждые
load_lines: Загружать последние %{count} строк
-
+
reload_times:
10000: "10 сек"
30000: "30 сек"
@@ -143,3 +144,5 @@ ru:
cannot_write: Вы не можете собирать пакет в этот репозиторий.
can_not_published: Опубликовать сборку можно только со статусом "Собран"
frozen_platform: В случае выбора репозитория для сохранения пакетов из замороженнной платформы разрешены только bugfix и security обновления
+ wrong_include_repos: Включаемые репозитории должны принадлежать платформе для сборки
+ wrong_commit_hash: Невозможно найти коммит '%{commit_hash}' в проекте
diff --git a/config/locales/models/comment.en.yml b/config/locales/models/comment.en.yml
index a1bcd69ec..8f3e5a4a6 100644
--- a/config/locales/models/comment.en.yml
+++ b/config/locales/models/comment.en.yml
@@ -3,11 +3,28 @@ en:
comments:
confirm_delete: Are you sure you want to delete the comment?
new_header: New comment
+ new_inline: Add a line comment
edit_header: Editing a comment
- has_commented: "added a note"
- notifications_are: "Notifications for new comments are"
- comments_header: "Comments"
- back: 'Back'
+ has_commented: added a note
+ notifications_are: Notifications for new comments are
+ comments_header: Comments
+ back: Back
+ md_cheatsheet_header: Markdown Cheat Sheet
+ md_cheatsheet:
+ format_text: Format Text
+ headers: Headers
+ text_styles: Text styles
+ lists: Lists
+ unordered: Unordered
+ ordered: Ordered
+ miscellaneous: Miscellaneous
+ images: Images
+ links: Links
+ blockquotes: Blockquotes
+ code_examples: Code Examples in Markdown
+ syntax_highlighting: Syntax highlighting
+ indent_code: indent your code 4 spaces
+ inline_code: Inline code for comments
flash:
comment:
diff --git a/config/locales/models/comment.ru.yml b/config/locales/models/comment.ru.yml
index ff9e15dd3..f481d904c 100644
--- a/config/locales/models/comment.ru.yml
+++ b/config/locales/models/comment.ru.yml
@@ -3,18 +3,35 @@ ru:
comments:
confirm_delete: Вы уверены, что хотите удалить комментарий?
new_header: Новый комментарий
+ new_inline: Добавить комментарий к строке
edit_header: Редактирование комментария
- has_commented: "оставил комментарий"
- notifications_are: "Уведомления о последующих комментариях"
- comments_header: "Комментарии"
- back: 'Назад'
+ has_commented: оставил комментарий
+ notifications_are: Уведомления о последующих комментариях
+ comments_header: Комментарии
+ back: Назад
+ md_cheatsheet_header: Шпаргалка по Markdown
+ md_cheatsheet:
+ format_text: Формат текста
+ headers: Заголовки
+ text_styles: Стиль текста
+ lists: Списки
+ unordered: Маркированный
+ ordered: Нумерованный
+ miscellaneous: Разное
+ images: Изображения
+ links: Ссылки
+ blockquotes: Цитирование
+ code_examples: Примеры кода в Markdown
+ syntax_highlighting: Подсветка синтаксиса
+ indent_code: Отступ кода на 4 пробела
+ inline_code: Встроенный код в строке
flash:
comment:
saved: Комментарий успешно сохранен
save_error: Ошибка сохранения комментария
destroyed: Комментарий удален
-
+
activerecord:
attributes:
comment:
diff --git a/config/locales/models/group.en.yml b/config/locales/models/group.en.yml
index 1cb6a06dc..f8e015de3 100644
--- a/config/locales/models/group.en.yml
+++ b/config/locales/models/group.en.yml
@@ -16,7 +16,6 @@ en:
description: Descripton
leave_group: Leave group
projects_list: Projects list
- public_projects_list: Public projects list
public_profile: Public profile
delete_warning: Attention! Deleted group can not be restored!
diff --git a/config/locales/models/group.ru.yml b/config/locales/models/group.ru.yml
index c43e33e2a..01c5aaf7a 100644
--- a/config/locales/models/group.ru.yml
+++ b/config/locales/models/group.ru.yml
@@ -16,7 +16,6 @@ ru:
description: Описание
leave_group: Покинуть группу
projects_list: Список проектов
- public_projects_list: Список публичных проектов
public_profile: Публичный профиль
delete_warning: Внимание! Удаленная группа восстановлению не подлежит.
diff --git a/config/locales/models/platform.en.yml b/config/locales/models/platform.en.yml
index 9a828c31c..afb83a510 100644
--- a/config/locales/models/platform.en.yml
+++ b/config/locales/models/platform.en.yml
@@ -49,6 +49,7 @@ en:
flash:
platform:
+ released_status_can_not_be_changed: Released status can't be changed if platform has been released
saved: Platform saved
created: Platform created
save_error: Platform save error
diff --git a/config/locales/models/platform.ru.yml b/config/locales/models/platform.ru.yml
index a71e49bdc..a379f293b 100644
--- a/config/locales/models/platform.ru.yml
+++ b/config/locales/models/platform.ru.yml
@@ -49,6 +49,7 @@ ru:
flash:
platform:
+ released_status_can_not_be_changed: Released статус платформы не может быть изменен, если платформа уже выпущена
saved: Платформа успешно сохранена
created: Платформа успешно добавлена
save_error: Не удалось сохранить платформу
diff --git a/config/locales/models/project.en.yml b/config/locales/models/project.en.yml
index a538a25f9..19f76d2ae 100644
--- a/config/locales/models/project.en.yml
+++ b/config/locales/models/project.en.yml
@@ -2,6 +2,7 @@ en:
layout:
projects:
add: Add
+ public_projects_list: Public projects list
edit: Settings
fork_and_edit: Fork
fork_to: Fork to %{to}
diff --git a/config/locales/models/project.ru.yml b/config/locales/models/project.ru.yml
index 321fb777d..0a38bafb1 100644
--- a/config/locales/models/project.ru.yml
+++ b/config/locales/models/project.ru.yml
@@ -2,6 +2,7 @@ ru:
layout:
projects:
add: Добавить
+ public_projects_list: Список публичных проектов
edit: Настройки
fork_and_edit: Клонировать
fork_to: Клонировать в %{to}
diff --git a/config/locales/models/pull_request.en.yml b/config/locales/models/pull_request.en.yml
new file mode 100644
index 000000000..8c1842f18
--- /dev/null
+++ b/config/locales/models/pull_request.en.yml
@@ -0,0 +1,58 @@
+en:
+ projects:
+ pull_requests:
+ new:
+ header: 'Create a pull request'
+ submit: Send pull request
+ update: Update commits
+ show:
+ pull: Pull Request
+ header: 'Pull Request'
+ status:
+ close: Close Pull Request
+ reopen: Reopen Pull Request
+ merge: Merge
+ duplicate: 'There is already a pull request for %{from_ref}'
+ up_to_date: 'The %{to_ref} branch is already up-to-date with %{from_ref}'
+ wrong_ref: Wrong branch or tag
+ blocked: This pull request cannot be automatically merged.
+ ready: This pull request can be automatically merged.
+ merged: |
+ %{user} merged into %{to_ref}
+ from %{from_ref} at %{time}
+ closed: '%{user} closed this pull request at %{time}'
+ is_big: This pull request is big! We're only showing the most recent %{count} commits.
+ open: ''
+ statuses:
+ blocked: Blocked
+ ready: Ready
+ merged: Merged
+ closed: Closed
+ outdated_diff: Outdated diff
+ activity:
+ show_outdated_diff: Show outdated_diff
+ pull_requests:
+ tabs:
+ discussion: Discussion
+ diff: Diff
+ commits: Commits
+
+ flash:
+ pull_request:
+ new_error: Unable to create pull request
+ saved: Pull request saved
+ save_error: Unable to save pull request
+
+ activerecord:
+ attributes:
+ pull_requests:
+ to_ref: Source
+ from_ref: Receiver
+ refs: 'branch · tag'
+ to_project: Into
+ from_project: From
+
+ layout:
+ pull_requests:
+ search: Find pull request...
+ view_full_changes: View full changes
diff --git a/config/locales/models/pull_request.ru.yml b/config/locales/models/pull_request.ru.yml
new file mode 100644
index 000000000..65eb34bfd
--- /dev/null
+++ b/config/locales/models/pull_request.ru.yml
@@ -0,0 +1,58 @@
+ru:
+ projects:
+ pull_requests:
+ new:
+ header: Создать пул реквест
+ submit: Создать пул реквест
+ update: Обновить коммиты
+ show:
+ pull: Пул реквест
+ header: Пул реквест
+ status:
+ close: Закрыть пул реквест
+ reopen: Переоткрыть пул реквест
+ merge: Мерж
+ duplicate: 'Уже существует пул реквест %{from_ref}'
+ up_to_date: 'Ветка %{to_ref} на данный момент уже содержит последние изменения %{from_ref}'
+ wrong_ref: Неправильная ветка или тег
+ blocked: Невозможно автоматически смержить данный пул реквест.
+ ready: Данный пул реквест можно смержить автоматически.
+ merged: |
+ %{user} смержил %{to_ref}
+ с %{from_ref} в %{time}'
+ closed: '%{user} закрыл пул реквест в %{time}'
+ is_big: Этот пул реквест слишком большой! Мы показываем только последние %{count} коммитов.
+ open: ''
+ statuses:
+ blocked: Заблокирован
+ ready: Готов к мержу
+ merged: Смержен
+ closed: Закрыт
+ outdated_diff: Устаревшие изменения
+ activity:
+ show_outdated_diff: Отобразить устаревшие изменения
+ pull_requests:
+ tabs:
+ discussion: Дискуссия
+ diff: Изменения
+ commits: Коммиты
+
+ flash:
+ pull_request:
+ new_error: Не удалось создать пул реквест
+ saved: Пул реквест успешно сохранен
+ save_error: Не удалось сохранить пул реквест
+
+ activerecord:
+ attributes:
+ pull_requests:
+ to_ref: Приемник
+ from_ref: Источник
+ refs: 'ветка · тег'
+ to_project: Куда
+ from_project: Откуда
+
+ layout:
+ pull_requests:
+ search: Найти пул реквест...
+ view_full_changes: Посмотреть все изменения
diff --git a/config/locales/models/relation.en.yml b/config/locales/models/relation.en.yml
index 620780bf0..8a5af138e 100644
--- a/config/locales/models/relation.en.yml
+++ b/config/locales/models/relation.en.yml
@@ -1,6 +1,7 @@
en:
layout:
relations:
+ filters: Filters
user_owner: I'm owner
group_owner: I'm member of owner group
user: I'm collaborator
diff --git a/config/locales/models/relation.ru.yml b/config/locales/models/relation.ru.yml
index 51202e88e..8160c72b5 100644
--- a/config/locales/models/relation.ru.yml
+++ b/config/locales/models/relation.ru.yml
@@ -1,6 +1,7 @@
ru:
layout:
relations:
+ filters: Фильтры
user_owner: Я - владелец
group_owner: Я состою в группе-владельце
user: Я - участник
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index 5d35021ab..927c12501 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -47,6 +47,8 @@ ru:
processing: Обрабатывается...
invalid_content_type: имеет неверный тип
atom_link_tag_title: Приватная лента для %{nickname} | %{app_name}
+ preview: Предосмотр
+ link: Ссылка
settings:
label: 'Настройки'
@@ -114,7 +116,7 @@ ru:
empty: "Репозиторий пуст. Если вы клонировали(Fork) проект или импортировали пакет, данные скоро появятся"
source: Source
commits: Коммиты
- commit_diff_too_big: Извините, коммит слишком большой!
+ commit_diff_too_big: Извините, изменений слишком много!
tags: Теги
branches: Ветки
project_versions: Версии
@@ -136,6 +138,9 @@ ru:
exception_message: У Вас нет доступа к этой странице!
+ 500_message: Ошибка 500. Что-то пошло не так. Мы уже в курсе данной проблемы и постараемся поскорее ее решить.
+ 404_message: Ошибка 404. Страница не найдена!
+
collaborators:
successfully_changed: Список коллабораторов успешно изменен
error_in_changing: Ошибка изменения списка коллабораторов
@@ -186,3 +191,12 @@ ru:
private_user:
login: Логин
password: Пароль
+
+ arch:
+ name: Название
+ created_at: Создана
+ updated_at: Обновлена
+
+ into: в
+ from: из
+ by: от
diff --git a/config/locales/title.en.yml b/config/locales/title.en.yml
index 191ba53f6..01c37f6b7 100644
--- a/config/locales/title.en.yml
+++ b/config/locales/title.en.yml
@@ -22,7 +22,14 @@ en:
title: 'Compare Revisions'
searching:
title: 'Search in Wiki'
+ pull_requests:
+ index:
+ title: 'Pull Requests'
+ new:
+ title: 'Create a Pull Request'
+ show:
+ title: 'Pull Request: %{name} by %{user}'
platforms:
product_build_lists:
index:
- title: 'Products Monitoring'
\ No newline at end of file
+ title: 'Products Monitoring'
diff --git a/config/locales/title.ru.yml b/config/locales/title.ru.yml
index b37f512f0..cbb65e5ca 100644
--- a/config/locales/title.ru.yml
+++ b/config/locales/title.ru.yml
@@ -22,7 +22,14 @@ ru:
title: 'Сравнение версий'
searching:
title: 'Поиск в вики'
+ pull_requests:
+ index:
+ title: 'Пул реквесты'
+ new:
+ title: 'Создать пул реквест'
+ show:
+ title: 'Пул реквест %{name} от %{user}'
platforms:
product_build_lists:
index:
- title: 'Мониторинг продуктов'
\ No newline at end of file
+ title: 'Мониторинг продуктов'
diff --git a/config/locales/users.en.yml b/config/locales/users.en.yml
index 837632146..4dc842606 100644
--- a/config/locales/users.en.yml
+++ b/config/locales/users.en.yml
@@ -15,16 +15,12 @@ en:
own_projects: My projects
part_projects: Participate projects
filter_header: Filter
- public_projects_list: Public projects list
public_data_edit_warning: This data is able for public profile. If you don't want to show some data, just don't fill them.
user_private_settings: Settings
settings_notifier: Notification center
delete_header: Delete aacount
delete_warning: Warning! Deleted account can not be recovered.
private_settings_header: Password change
- avatar_notice: Without uploaded avatar will be used avatar from gravar web service.
- delete_avatar: Delete avatar
- avatar_with_size: Avatar (less than %{max})
users_filter:
all: All
admin: Admins
diff --git a/config/locales/users.ru.yml b/config/locales/users.ru.yml
index 7cdff25f5..f85f57523 100644
--- a/config/locales/users.ru.yml
+++ b/config/locales/users.ru.yml
@@ -15,16 +15,12 @@ ru:
own_projects: Мои проекты
part_projects: Участвую в проектах
filter_header: Фильтр
- public_projects_list: Список публичных проектов
public_data_edit_warning: Данные доступны для публичного профиля. Если вы не хотите показывать какие-либо данные, просто не заполняйте их.
user_private_settings: Настройки
settings_notifier: Центр подписки
delete_header: Удалить аккаунт
delete_warning: Внимание! Удаленный аккаунт восстановлению не подлежит.
private_settings_header: Изменение пароля
- avatar_notice: При отсутствии загруженного аватара будет использован Ваш аватар на сервисе gravatar.
- delete_avatar: Удалить аватар
- avatar_with_size: Аватар (менее %{max})
users_filter:
all: Все
admin: Админы
diff --git a/config/routes.rb b/config/routes.rb
index 82c553f74..4e6795e81 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -10,6 +10,69 @@ Rosa::Application.routes.draw do
end
devise_for :users, :controllers => {:omniauth_callbacks => 'users/omniauth_callbacks'}
+ namespace :api do
+ namespace :v1 do
+ resources :advisories, :only => [:index, :show, :create, :update]
+ resources :build_lists, :only => [:index, :create, :show] do
+ member {
+ get :publish
+ get :reject_publish
+ get :cancel
+ }
+ end
+ resources :arches, :only => [:index]
+ resources :platforms, :only => [:index, :show, :update, :destroy, :create] do
+ collection {
+ get :platforms_for_build
+ }
+ member {
+ get :members
+ put :add_member
+ delete :remove_member
+ post :clone
+ put :clear
+ }
+ end
+ resources :repositories, :only => [:show, :update, :destroy] do
+ member {
+ get :projects
+ put :add_member
+ delete :remove_member
+ put :add_project
+ delete :remove_project
+ put :signatures
+ }
+ end
+ resources :projects, :only => [:index, :show, :update, :create, :destroy] do
+ collection { get :get_id }
+ member {
+ post :fork
+ get :refs_list
+ get :members
+ put :add_member
+ delete :remove_member
+ put :update_member
+ }
+ end
+ resources :users, :only => [:show]
+ get 'user' => 'users#show_current_user'
+ resource :user, :only => [:update] do
+ member {
+ get :notifiers
+ put :notifiers
+ }
+ end
+ resources :groups, :only => [:index, :show, :update, :create, :destroy] do
+ member {
+ get :members
+ put :add_member
+ delete :remove_member
+ put :update_member
+ }
+ end
+ end
+ end
+
resources :search, :only => [:index]
get '/forbidden' => 'pages#forbidden', :as => 'forbidden'
@@ -70,7 +133,6 @@ Rosa::Application.routes.draw do
end
end
- get :autocomplete_user_uname, :on => :collection
resources :repositories do
member do
get :add_project
@@ -93,6 +155,13 @@ Rosa::Application.routes.draw do
match 'product_status', :to => 'product_build_lists#status_build'
end
+ resources :autocompletes, :only => [] do
+ collection do
+ get :autocomplete_user_uname
+ get :autocomplete_group_uname
+ end
+ end
+
scope :module => 'users' do
resources :settings, :only => [] do
collection do
@@ -104,9 +173,7 @@ Rosa::Application.routes.draw do
put :notifiers
end
end
- resources :users, :controller => 'profile', :only => [] do
- get :autocomplete_user_uname, :on => :collection
- end
+
resources :register_requests, :only => [:new, :create], :format => /ru|en/ #view support only two languages
end
@@ -114,7 +181,6 @@ Rosa::Application.routes.draw do
get '/groups/new' => 'profile#new' # need to force next route exclude :id => 'new'
get '/groups/:id' => redirect("/%{id}"), :as => :profile_group # override default group show route
resources :groups, :controller => 'profile' do
- get :autocomplete_group_uname, :on => :collection
delete :remove_user, :on => :member
resources :members, :only => [:index] do
collection do
@@ -185,7 +251,15 @@ Rosa::Application.routes.draw do
resources :collaborators do
get :find, :on => :collection
end
+ resources :pull_requests, :except => :destroy do
+ get :autocomplete_to_project, :on => :collection
+ put :merge, :on => :member
+ end
+ post '/preview' => 'projects#preview', :as => 'md_preview'
+ post 'refs_list' => 'projects#refs_list', :as => 'refs_list'
+ get '/pull_requests/:issue_id/add_line_comments(.:format)' => "comments#new_line", :as => :new_line_pull_comment
end
+
# Resource
get '/autocomplete_maintainers' => 'projects#autocomplete_maintainers', :as => :autocomplete_maintainers
get '/modify' => 'projects#edit', :as => :edit_project
@@ -209,6 +283,7 @@ Rosa::Application.routes.draw do
get '/commit/:commit_id/comments/:id(.:format)' => "comments#edit", :as => :edit_project_commit_comment
put '/commit/:commit_id/comments/:id(.:format)' => "comments#update", :as => :project_commit_comment
delete '/commit/:commit_id/comments/:id(.:format)' => "comments#destroy"
+ get '/commit/:commit_id/add_line_comments(.:format)' => "comments#new_line", :as => :new_line_commit_comment
# Commit subscribes
post '/commit/:commit_id/subscribe' => "commit_subscribes#create", :as => :subscribe_commit
delete '/commit/:commit_id/unsubscribe' => "commit_subscribes#destroy", :as => :unsubscribe_commit
@@ -222,7 +297,7 @@ Rosa::Application.routes.draw do
# Raw
get '/raw/:treeish/*path' => "git/blobs#raw", :as => :raw, :format => false
# Archive
- get '/archive/:treeish.:format' => "git/trees#archive", :as => :archive, :format => /zip|tar/
+ get '/archive/:treeish.:format' => "git/trees#archive", :as => :archive, :format => /zip|tar\.gz/
end
end
end
diff --git a/db/migrate/20120412173938_create_pull_requests.rb b/db/migrate/20120412173938_create_pull_requests.rb
new file mode 100644
index 000000000..618ccef61
--- /dev/null
+++ b/db/migrate/20120412173938_create_pull_requests.rb
@@ -0,0 +1,11 @@
+class CreatePullRequests < ActiveRecord::Migration
+ def change
+ create_table :pull_requests do |t|
+ t.integer :issue_id, :null => false
+ t.integer :base_project_id, :null => false
+ t.integer :head_project_id, :null => false
+ t.string :base_ref, :null => false
+ t.string :head_ref, :null => false
+ end
+ end
+end
diff --git a/db/migrate/20120627101821_add_index_to_pull_request.rb b/db/migrate/20120627101821_add_index_to_pull_request.rb
new file mode 100644
index 000000000..4a05dfbd7
--- /dev/null
+++ b/db/migrate/20120627101821_add_index_to_pull_request.rb
@@ -0,0 +1,7 @@
+class AddIndexToPullRequest < ActiveRecord::Migration
+ def change
+ add_index :pull_requests, :base_project_id
+ add_index :pull_requests, :head_project_id
+ add_index :pull_requests, :issue_id
+ end
+end
diff --git a/db/migrate/20121003081546_rename_base_head_in_pull_requests.rb b/db/migrate/20121003081546_rename_base_head_in_pull_requests.rb
new file mode 100644
index 000000000..73cdb4e8f
--- /dev/null
+++ b/db/migrate/20121003081546_rename_base_head_in_pull_requests.rb
@@ -0,0 +1,8 @@
+class RenameBaseHeadInPullRequests < ActiveRecord::Migration
+ def change
+ rename_column :pull_requests, :base_project_id, :to_project_id
+ rename_column :pull_requests, :base_ref, :to_ref
+ rename_column :pull_requests, :head_project_id, :from_project_id
+ rename_column :pull_requests, :head_ref, :from_ref
+ end
+end
diff --git a/db/migrate/20121003154246_add_avatar_to_groups.rb b/db/migrate/20121003154246_add_avatar_to_groups.rb
new file mode 100644
index 000000000..96a835ca8
--- /dev/null
+++ b/db/migrate/20121003154246_add_avatar_to_groups.rb
@@ -0,0 +1,11 @@
+class AddAvatarToGroups < ActiveRecord::Migration
+ def change
+ change_table :groups do |t|
+ t.has_attached_file :avatar
+ end
+ end
+
+ def self.down
+ drop_attached_file :groups, :avatar
+ end
+end
diff --git a/db/migrate/20121005100158_add_data_to_comments.rb b/db/migrate/20121005100158_add_data_to_comments.rb
new file mode 100644
index 000000000..c33ed6a8b
--- /dev/null
+++ b/db/migrate/20121005100158_add_data_to_comments.rb
@@ -0,0 +1,5 @@
+class AddDataToComments < ActiveRecord::Migration
+ def change
+ add_column :comments, :data, :text
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 420636893..bd9ee3cdb 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,14 +11,14 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20120914160741) do
+ActiveRecord::Schema.define(:version => 20121005100158) do
create_table "activity_feeds", :force => true do |t|
t.integer "user_id", :null => false
t.string "kind"
t.text "data"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
end
create_table "advisories", :force => true do |t|
@@ -53,8 +53,8 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
create_table "arches", :force => true do |t|
t.string "name", :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
end
add_index "arches", ["name"], :name => "index_arches_on_name", :unique => true
@@ -63,8 +63,8 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.integer "user_id"
t.string "provider"
t.string "uid"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
end
add_index "authentications", ["provider", "uid"], :name => "index_authentications_on_provider_and_uid", :unique => true
@@ -75,8 +75,8 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.integer "level"
t.integer "status"
t.integer "build_list_id"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.string "version"
end
@@ -110,8 +110,8 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.integer "project_id"
t.integer "arch_id"
t.datetime "notified_at"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.boolean "is_circle", :default => false
t.text "additional_repos"
t.string "name"
@@ -142,10 +142,11 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.string "commentable_type"
t.integer "user_id"
t.text "body"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.decimal "commentable_id", :precision => 50, :scale => 0
t.integer "project_id"
+ t.text "data"
end
create_table "event_logs", :force => true do |t|
@@ -160,8 +161,8 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.string "controller"
t.string "action"
t.text "message"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
end
create_table "flash_notifies", :force => true do |t|
@@ -175,11 +176,15 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
create_table "groups", :force => true do |t|
t.integer "owner_id"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.string "uname"
- t.integer "own_projects_count", :default => 0, :null => false
+ t.integer "own_projects_count", :default => 0, :null => false
t.text "description"
+ t.string "avatar_file_name"
+ t.string "avatar_content_type"
+ t.integer "avatar_file_size"
+ t.datetime "avatar_updated_at"
end
create_table "issues", :force => true do |t|
@@ -189,8 +194,8 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.string "title"
t.text "body"
t.string "status", :default => "open"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.integer "user_id"
t.datetime "closed_at"
t.integer "closed_by"
@@ -250,14 +255,14 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.string "description"
t.string "name", :null => false
t.integer "parent_platform_id"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.boolean "released", :default => false, :null => false
t.integer "owner_id"
t.string "owner_type"
t.string "visibility", :default => "open", :null => false
t.string "platform_type", :default => "main", :null => false
- t.string "distrib_type", :null => false
+ t.string "distrib_type"
end
add_index "platforms", ["name"], :name => "index_platforms_on_name", :unique => true, :case_sensitive => false
@@ -266,16 +271,16 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.integer "platform_id"
t.string "login"
t.string "password"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.integer "user_id"
end
create_table "product_build_lists", :force => true do |t|
t.integer "product_id"
t.integer "status", :default => 2, :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
end
add_index "product_build_lists", ["product_id"], :name => "index_product_build_lists_on_product_id"
@@ -283,8 +288,8 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
create_table "products", :force => true do |t|
t.string "name", :null => false
t.integer "platform_id", :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.text "build_script"
t.text "counter"
t.text "ks"
@@ -303,8 +308,8 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.string "name"
t.string "version"
t.datetime "file_mtime"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.integer "platform_id"
end
@@ -313,25 +318,25 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
create_table "project_to_repositories", :force => true do |t|
t.integer "project_id"
t.integer "repository_id"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
end
create_table "projects", :force => true do |t|
t.string "name"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.integer "owner_id"
t.string "owner_type"
t.string "visibility", :default => "open"
t.text "description"
t.string "ancestry"
t.boolean "has_issues", :default => true
+ t.boolean "has_wiki", :default => false
t.string "srpm_file_name"
+ t.string "srpm_content_type"
t.integer "srpm_file_size"
t.datetime "srpm_updated_at"
- t.string "srpm_content_type"
- t.boolean "has_wiki", :default => false
t.string "default_branch", :default => "master"
t.boolean "is_package", :default => true, :null => false
t.integer "average_build_time", :default => 0, :null => false
@@ -339,7 +344,19 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.integer "maintainer_id"
end
- add_index "projects", ["owner_id"], :name => "index_projects_on_name_and_owner_id_and_owner_type", :unique => true, :case_sensitive => false
+ add_index "projects", ["owner_id", "name", "owner_type"], :name => "index_projects_on_name_and_owner_id_and_owner_type", :unique => true, :case_sensitive => false
+
+ create_table "pull_requests", :force => true do |t|
+ t.integer "issue_id", :null => false
+ t.integer "to_project_id", :null => false
+ t.integer "from_project_id", :null => false
+ t.string "to_ref", :null => false
+ t.string "from_ref", :null => false
+ end
+
+ add_index "pull_requests", ["from_project_id"], :name => "index_pull_requests_on_head_project_id"
+ add_index "pull_requests", ["issue_id"], :name => "index_pull_requests_on_issue_id"
+ add_index "pull_requests", ["to_project_id"], :name => "index_pull_requests_on_base_project_id"
create_table "register_requests", :force => true do |t|
t.string "name"
@@ -347,8 +364,8 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.string "token"
t.boolean "approved", :default => false
t.boolean "rejected", :default => false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.string "interest"
t.text "more"
t.string "language"
@@ -362,16 +379,16 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.string "actor_type"
t.integer "target_id"
t.string "target_type"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.string "role"
end
create_table "repositories", :force => true do |t|
t.string "description", :null => false
t.integer "platform_id", :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.string "name", :null => false
t.boolean "publish_without_qa", :default => true
end
@@ -383,8 +400,8 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.boolean "new_comment_reply", :default => true
t.boolean "new_issue", :default => true
t.boolean "issue_assign", :default => true
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.boolean "new_comment_commit_owner", :default => true
t.boolean "new_comment_commit_repo_owner", :default => true
t.boolean "new_comment_commit_commentor", :default => true
@@ -395,8 +412,8 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
create_table "subscribes", :force => true do |t|
t.string "subscribeable_type"
t.integer "user_id"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.boolean "status", :default => true
t.integer "project_id"
t.decimal "subscribeable_id", :precision => 50, :scale => 0
@@ -404,18 +421,21 @@ ActiveRecord::Schema.define(:version => 20120914160741) 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", :default => "", :null => false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
t.text "ssh_key"
t.string "uname"
t.string "role"
- t.string "language", :default => "en"
- t.integer "own_projects_count", :default => 0, :null => false
+ t.string "language", :default => "en"
+ t.integer "own_projects_count", :default => 0, :null => false
+ t.string "confirmation_token"
+ t.datetime "confirmed_at"
+ t.datetime "confirmation_sent_at"
t.text "professional_experience"
t.string "site"
t.string "company"
@@ -424,14 +444,11 @@ ActiveRecord::Schema.define(:version => 20120914160741) do
t.string "avatar_content_type"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
- t.integer "failed_attempts", :default => 0
+ t.integer "failed_attempts", :default => 0
t.string "unlock_token"
t.datetime "locked_at"
- t.string "confirmation_token"
- t.datetime "confirmed_at"
- t.datetime "confirmation_sent_at"
t.string "authentication_token"
- t.integer "build_priority", :default => 50
+ t.integer "build_priority", :default => 50
end
add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token"
diff --git a/lib/api_defender.rb b/lib/api_defender.rb
new file mode 100644
index 000000000..210295d8c
--- /dev/null
+++ b/lib/api_defender.rb
@@ -0,0 +1,53 @@
+require 'rack/throttle'
+require 'redis'
+
+# Limit hourly API usage
+# http://martinciu.com/2011/08/how-to-add-api-throttle-to-your-rails-app.html
+class ApiDefender < Rack::Throttle::Hourly
+
+ def initialize(app)
+ options = {
+ :cache => Redis.new(:thread_safe => true),
+ :key_prefix => :throttle,
+
+ # only 500 request per hour
+ :max => 500
+ }
+ @app, @options = app, options
+ end
+
+ # this method checks if request needs throttling.
+ # If so, it increases usage counter and compare it with maximum
+ # allowed API calls. Returns true if a request can be handled.
+ def allowed?(request)
+ need_defense?(request) ? cache_incr(request) <= max_per_window : true
+ end
+
+ def call(env)
+ status, heders, body = super
+ request = Rack::Request.new(env)
+ # just to be nice for our clients we inform them how many
+ # requests remaining does they have
+ if need_defense?(request)
+ heders['X-RateLimit-Limit'] = max_per_window.to_s
+ heders['X-RateLimit-Remaining'] = ([0, max_per_window - (cache_get(cache_key(request)).to_i rescue 1)].max).to_s
+ end
+ [status, heders, body]
+ end
+
+ # key increase and key expiration
+ def cache_incr(request)
+ key = cache_key(request)
+ count = cache.incr(key)
+ cache.expire(key, 1.day) if count == 1
+ count
+ end
+
+ protected
+
+ # only API calls should be throttled
+ def need_defense?(request)
+ request.env['PATH_INFO'] =~ /^\/api\/v1\//
+ end
+
+end
\ No newline at end of file
diff --git a/lib/ext/core/string.rb b/lib/ext/core/string.rb
index 9ff9bde77..608acc628 100644
--- a/lib/ext/core/string.rb
+++ b/lib/ext/core/string.rb
@@ -21,4 +21,18 @@ class String
# ensure
# return self
end
+
+ # same as reverse.truncate.reverse
+ def rtruncate(length, options = {})
+ text = self.dup
+ options[:omission] ||= "..."
+ options[:separator] ||= '/'
+
+ length_with_room_for_omission = length - options[:omission].mb_chars.length
+ chars = text.mb_chars
+ stop = options[:separator] ?
+ (chars.index(options[:separator].mb_chars, length_with_room_for_omission) || length_with_room_for_omission) : length_with_room_for_omission
+
+ (chars.length > length ? "#{options[:omission]}#{chars[-(stop+1)...-1]}" : text).to_s
+ end
end
diff --git a/lib/ext/git/grit.rb b/lib/ext/git/grit.rb
index 3ec533062..3ea90a1b5 100644
--- a/lib/ext/git/grit.rb
+++ b/lib/ext/git/grit.rb
@@ -32,16 +32,16 @@ module Grit
# def file_mime_type
# @file_mime_type ||= data.file_type(:mime_type)
# end
- #
+ #
# def text?
# file_mime_type =~ /^text\// # not binary?
# end
- #
+ #
# def binary?
# not text? # file_mime_type !~ /^text\//
# # s = data.split(//); ((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30 # works only for latin chars
# end
- #
+ #
# def image?
# mime_type.match(/image/)
# end
diff --git a/lib/ext/git/inline_callback.rb b/lib/ext/git/inline_callback.rb
deleted file mode 100644
index e57be6d65..000000000
--- a/lib/ext/git/inline_callback.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-# -*- encoding : utf-8 -*-
-module Git
- module Diff
- class InlineCallback < ::Diff::Renderer::Base
- def before_headerblock(block)
- end
-
- def after_headerblock(block)
- end
-
- def headerline(line)
- "
-
...
-
...
-
#{line}
-
"
- end
-
- def addline(line)
- "
-
-
#{line.new_number}
-
#{render_line(line)}
-
"
- end
-
- def remline(line)
- "
-
#{line.old_number}
-
-
#{render_line(line)}
-
"
- end
-
- def modline(line)
- "
-
#{line.old_number}
-
#{line.new_number}
-
#{render_line(line)}
-
"
- end
-
- def unmodline(line)
- "
-
#{line.old_number}
-
#{line.new_number}
-
#{render_line(line)}
-
"
- end
-
- def sepline(line)
- "
-
…
-
…
-
-
"
- end
-
- def nonewlineline(line)
- "
-
#{line.old_number}
-
#{line.new_number}
-
#{render_line(line)}
-
"
- end
-
- protected
- def escape(str)
- str.to_s.gsub('&', '&').gsub('<', '<').gsub('>', '>').gsub('"', '"')
- end
-
- def render_line(line)
- res = ''
- if line.inline_changes?
- prefix, changed, postfix = line.segments.map{|segment| escape(segment) }
- res += "#{prefix}#{changed}#{postfix}"
- else
- res += escape(line)
- end
- res += ''
-
- res
- end
- end
- end
-end
diff --git a/lib/modules/models/git.rb b/lib/modules/models/git.rb
index 4cf2baafc..299f15a8a 100644
--- a/lib/modules/models/git.rb
+++ b/lib/modules/models/git.rb
@@ -116,7 +116,7 @@ module Modules
end
def fork_git_repo
- dummy = Grit::Repo.new(path) rescue parent.repo.fork_bare(path)
+ dummy = Grit::Repo.new(path) rescue parent.repo.fork_bare(path, :shared => false)
write_hook
end
diff --git a/lib/tasks/add_branch.rake b/lib/tasks/add_branch.rake
index 2e65145d4..47c0bf9ed 100644
--- a/lib/tasks/add_branch.rake
+++ b/lib/tasks/add_branch.rake
@@ -40,7 +40,7 @@ namespace :add_branch do
open(source).readlines.each do |name|
name.chomp!; name.strip!
print "Fork branch for '#{name}'... "
- if p = Project.find_by_name_and_owner_type_and_owner_id(name, owner.class.to_s, owner.id)
+ if p = Project.find_by_owner_and_name(owner.uname, name)
# Rake::Task['add_branch:fork_branch'].execute(:path => p.path, :src_branch => src_branch, :dst_branch => dst_branch)
system "bundle exec rake add_branch:fork_branch[#{p.path},#{src_branch},#{dst_branch}] -s RAILS_ENV=#{Rails.env} > /dev/null 2>&1"
print 'Ok!'
diff --git a/lib/tasks/git_detach_from_parent.rake b/lib/tasks/git_detach_from_parent.rake
new file mode 100644
index 000000000..737208c87
--- /dev/null
+++ b/lib/tasks/git_detach_from_parent.rake
@@ -0,0 +1,13 @@
+namespace :project do
+ desc 'Break the dependency of a repository cloned with --shared on its source repository'
+ task :git_detach_from_parent => :environment do
+ projects = Project.where('ancestry IS NOT NULL')
+ say "Total count of the forked projects is #{projects.count}"
+ projects.each_with_index do |project, ind|
+ Dir.chdir(project.path) do
+ say "--Start work with #{project.name_with_owner} (#{ind+1}/#{projects.count})--"
+ say (system('git', 'repack', '-a') ? 'Ok!' : 'Something wrong!')
+ end
+ end
+ end
+end
diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake
index 15a979b57..d485d14ad 100644
--- a/lib/tasks/import.rake
+++ b/lib/tasks/import.rake
@@ -48,21 +48,21 @@ namespace :import do
repo.projects << project
else # check if project already added
if project = repo.projects.find_by_name(name) || repo.projects.by_name(name).first # fallback to speedup
- print "Found project '#{project.owner.uname}/#{project.name}' in '#{platform.name}/#{repo.name}'."
+ print "Found project '#{project.name_with_owner}' in '#{platform.name}/#{repo.name}'."
elsif scoped = Project.where(:owner_id => owner.id, :owner_type => owner.class) and
project = scoped.find_by_name(name) || scoped.by_name(name).first
begin
repo.projects << project
rescue Exception => e
- print "Add project '#{project.owner.uname}/#{project.name}' to '#{platform.name}/#{repo.name}' FAILED: #{e.message}."
+ print "Add project '#{project.name_with_owner}' to '#{platform.name}/#{repo.name}' FAILED: #{e.message}."
else
- print "Add project '#{project.owner.uname}/#{project.name}' to '#{platform.name}/#{repo.name}' OK."
+ print "Add project '#{project.name_with_owner}' to '#{platform.name}/#{repo.name}' OK."
end
else
description = ::Iconv.conv('UTF-8//IGNORE', 'UTF-8', `rpm -q --qf '[%{Description}]' -p #{srpm_file}`)
project = Project.create!(:name => name, :description => description) {|p| p.owner = owner}
repo.projects << project
- print "Create project #{project.owner.uname}/#{project.name} in #{platform.name}/#{repo.name} OK."
+ print "Create project #{project.name_with_owner} in #{platform.name}/#{repo.name} OK."
end
end
project.import_srpm(srpm_file, platform.name)
@@ -124,24 +124,24 @@ namespace :import do
unless project = project_import.project
if platform.personal? # search project through owner # used for testhat
project = Project.find_or_create_by_name_and_owner_type_and_owner_id(name, owner.class.to_s, owner.id)
- print "Use project #{project.owner.uname}/#{project.name}. "
+ print "Use project #{project.name_with_owner}. "
else # search project through repository
if project = repository.projects.find_by_name(name) || repository.projects.by_name(name).first # fallback to speedup
- print "Found project #{project.owner.uname}/#{project.name} in #{platform.name}/#{repository.name}. "
+ print "Found project #{project.name_with_owner} in #{platform.name}/#{repository.name}. "
elsif scoped = Project.where(:owner_id => owner.id, :owner_type => owner.class) and
project = scoped.find_by_name(name) || scoped.by_name(name).first
repository.projects << project
- print "Add project #{project.owner.uname}/#{project.name} to #{platform.name}/#{repository.name}. "
+ print "Add project #{project.name_with_owner} to #{platform.name}/#{repository.name}. "
else
description = ::Iconv.conv('UTF-8//IGNORE', 'UTF-8', `rpm -q --qf '[%{Description}]' -p #{srpm_file}`)
project = Project.create!(:name => name, :description => description) {|p| p.owner = owner}
repository.projects << project
- print "Create project #{project.owner.uname}/#{project.name} at #{platform.name}/#{repository.name}. "
+ print "Create project #{project.name_with_owner} at #{platform.name}/#{repository.name}. "
end
end
end
project.import_srpm(srpm_file, branch)
- print "New version (#{version}) for #{project.owner.uname}/#{project.name} successfully imported to branch #{branch}! "
+ print "New version (#{version}) for #{project.name_with_owner} successfully imported to branch #{branch}! "
project_import.project = project
# project_import.platform = platform
diff --git a/public/googleac324ed2f70faefc.html b/public/googleac324ed2f70faefc.html
new file mode 100644
index 000000000..95843ef02
--- /dev/null
+++ b/public/googleac324ed2f70faefc.html
@@ -0,0 +1 @@
+google-site-verification: googleac324ed2f70faefc.html
\ No newline at end of file
diff --git a/spec/controllers/api/v1/advisories_controller_spec.rb b/spec/controllers/api/v1/advisories_controller_spec.rb
new file mode 100644
index 000000000..bb7114c7f
--- /dev/null
+++ b/spec/controllers/api/v1/advisories_controller_spec.rb
@@ -0,0 +1,144 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+shared_examples_for 'api advisories user with show rights' do
+ it 'should be able to perform show action' do
+ get :show, :id => @advisory.advisory_id, :format => :json
+ response.should be_success
+ end
+
+ it 'should be able to perform index action' do
+ get :index, :format => :json
+ response.should be_success
+ end
+end
+
+shared_examples_for 'api advisories user with admin rights' do
+ context 'api advisories user with create rights' do
+ let(:params) { {:build_list_id => @build_list.id, :advisory => {:description => 'test'}} }
+ it 'should be able to perform create action' do
+ post :create, params, :format => :json
+ response.should be_success
+ end
+ it 'ensures that advisory has been created' do
+ lambda { post :create, params, :format => :json }.should change{ Advisory.count }.by(1)
+ end
+ it 'ensures that build_list has been associated with advisory' do
+ post :create, params, :format => :json
+ @build_list.reload
+ @build_list.advisory.should_not be_nil
+ end
+ end
+
+ context 'api advisories user with update rights' do
+ let(:params) { {:id => @advisory.advisory_id, :build_list_id => @build_list.id} }
+ it 'should be able to perform update action' do
+ put :update, params, :format => :json
+ response.should be_success
+ end
+ it 'ensures that advisory has not been created' do
+ lambda { put :update, params, :format => :json }.should_not change{ Advisory.count }
+ end
+ it 'ensures that build_list has been associated with advisory' do
+ put :update, params, :format => :json
+ @build_list.reload
+ @build_list.advisory.should_not be_nil
+ end
+ end
+end
+
+shared_examples_for 'api advisories user without admin rights' do
+ context 'api advisories user without create rights' do
+ let(:params) { {:build_list_id => @build_list.id, :advisory => {:description => 'test'}} }
+ it 'should not be able to perform create action' do
+ post :create, params, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that advisory has not been created' do
+ lambda { post :create, params, :format => :json }.should_not change{ Advisory.count }
+ end
+ it 'ensures that build_list has not been associated with advisory' do
+ post :create, params, :format => :json
+ @build_list.reload
+ @build_list.advisory.should be_nil
+ end
+ end
+
+ context 'api advisories user without update rights' do
+ let(:params) { {:id => @advisory.advisory_id, :build_list_id => @build_list.id} }
+ it 'should not be able to perform update action' do
+ put :update, params, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that advisory has not been created' do
+ lambda { put :update, params, :format => :json }.should_not change{ Advisory.count }
+ end
+ it 'ensures that build_list has not been associated with advisory' do
+ put :update, params, :format => :json
+ @build_list.reload
+ @build_list.advisory.should be_nil
+ end
+ end
+end
+
+describe Api::V1::AdvisoriesController do
+
+ before do
+ stub_symlink_methods
+
+ @advisory = FactoryGirl.create(:advisory)
+ @build_list = FactoryGirl.create(:build_list_core)
+ @build_list.save_to_platform.update_column(:released, true)
+ @build_list.save_to_repository.update_column(:publish_without_qa, false)
+ @build_list.update_column(:status, BuildList::BUILD_PUBLISHED)
+ end
+
+ context 'for guest' do
+
+ if APP_CONFIG['anonymous_access']
+ it_should_behave_like 'api advisories user with show rights'
+ end
+
+ it 'should not be able to perform show action', :anonymous_access => false do
+ get :show, :id => @advisory.advisory_id, :format => :json
+ response.should_not be_success
+ end
+
+ it 'should not be able to perform index action', :anonymous_access => false do
+ get :index, :format => :json
+ response.should_not be_success
+ end
+ it_should_behave_like 'api advisories user without admin rights'
+ end
+
+ context 'for simple user' do
+ before do
+ @user = FactoryGirl.create(:user)
+ http_login(@user)
+ end
+ it_should_behave_like 'api advisories user with show rights'
+ it_should_behave_like 'api advisories user without admin rights'
+ end
+
+ context 'for admin' do
+ before do
+ @admin = FactoryGirl.create(:admin)
+ http_login(@admin)
+ end
+
+ it_should_behave_like 'api advisories user with show rights'
+ it_should_behave_like 'api advisories user with admin rights'
+ end
+
+ context 'for user who has access to update build_list' do
+ before do
+ @user = FactoryGirl.create(:user)
+ @build_list.save_to_platform.relations.create(:role => 'admin', :actor => @user)
+ http_login(@user)
+ end
+
+ it_should_behave_like 'api advisories user with show rights'
+ it_should_behave_like 'api advisories user with admin rights'
+ end
+
+end
diff --git a/spec/controllers/api/v1/arches_controller_spec.rb b/spec/controllers/api/v1/arches_controller_spec.rb
new file mode 100644
index 000000000..7adfe4c5d
--- /dev/null
+++ b/spec/controllers/api/v1/arches_controller_spec.rb
@@ -0,0 +1,31 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+describe Api::V1::ArchesController do
+
+ before { FactoryGirl.create(:arch) }
+
+ context 'for guest' do
+ it "should be able to perform index action", :anonymous_access => true do
+ get :index, :format => :json
+ should render_template(:index)
+ end
+
+ it 'should be able to perform get_id action', :anonymous_access => false do
+ get :index, :format => :json
+ response.status.should == 401
+ end
+ end
+
+ context 'for simple user' do
+ before do
+ stub_symlink_methods
+ http_login(FactoryGirl.create(:user))
+ end
+
+ it "should be able to perform index action" do
+ get :index, :format => :json
+ should render_template(:index)
+ end
+ end
+end
diff --git a/spec/controllers/api/v1/build_lists_controller_spec.rb b/spec/controllers/api/v1/build_lists_controller_spec.rb
new file mode 100644
index 000000000..37466c139
--- /dev/null
+++ b/spec/controllers/api/v1/build_lists_controller_spec.rb
@@ -0,0 +1,599 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+shared_examples_for 'show build list via api' do
+ it 'should be able to perform show action' do
+ get :show, @show_params
+ response.should render_template("api/v1/build_lists/show")
+ end
+
+ it 'should be able to perform index action' do
+ get :index, :format => :json
+ response.should render_template("api/v1/build_lists/index")
+ end
+end
+
+shared_examples_for 'not show build list via api' do
+ it 'should not be able to perform show action' do
+ get :show, @show_params
+ response.body.should == {"message" => "Access violation to this page!"}.to_json
+ end
+
+ pending 'should not be able to perform index action' do
+ get :index, :format => :json
+ response.body.should == {"message" => "Access violation to this page!"}.to_json
+ end
+end
+
+shared_examples_for 'create build list via api' do
+ before {
+ #@project.update_attributes({:repositories => @platform.repositories})
+ #test_git_commit(@project)
+ }
+
+ it 'should create one more build list' do
+ lambda { post :create, @create_params }.should change{ BuildList.count }.by(1)
+ end
+
+ it 'should save correct commit_hash for branch based build' do
+ post :create, @create_params
+ #@project.build_lists.last.commit_hash.should == @project.repo.commits('master').last.id
+ @project.build_lists.last.commit_hash.should == @params[:commit_hash]
+ end
+
+ it 'should save correct commit_hash for tag based build' do
+ system("cd #{@project.repo.path} && git tag 4.7.5.3") # TODO REDO through grit
+ post :create, @create_params
+ #@project.build_lists.last.commit_hash.should == @project.repo.commits('4.7.5.3').last.id
+ @project.build_lists.last.commit_hash.should == @params[:commit_hash]
+ end
+
+ it 'should not create without existing commit hash in project' do
+ lambda{ post :create, @create_params.deep_merge(:build_list => {:commit_hash => 'wrong'})}.should change{@project.build_lists.count}.by(0)
+ end
+end
+
+shared_examples_for 'not create build list via api' do
+ before {
+ #@project.update_attributes({:repositories => @platform.repositories})
+ #test_git_commit(@project)
+ }
+
+ it 'should not be able to perform create action' do
+ post :create, @create_params
+ response.body.should == {"message" => "Access violation to this page!"}.to_json
+ end
+
+ it 'should not create one more build list' do
+ lambda { post :create, @create_params }.should change{ BuildList.count }.by(0)
+ end
+end
+
+describe Api::V1::BuildListsController do
+ before(:each) do
+ stub_symlink_methods
+ end
+
+ context 'create and update abilities' do
+ context 'for user' do
+ before(:each) do
+ Arch.destroy_all
+ User.destroy_all
+
+ @build_list = FactoryGirl.create(:build_list_core)
+ @params = @build_list.attributes.symbolize_keys
+ @project = @build_list.project
+ @platform = @build_list.save_to_platform
+ #@platform = FactoryGirl.create(:platform_with_repos)
+
+ stub_symlink_methods
+ @user = FactoryGirl.create(:user)
+ @owner_user = @project.owner
+ @member_user = FactoryGirl.create(:user)
+ @project.relations.create(:role => 'reader', :actor => @member_user)
+ @build_list.save_to_platform.relations.create(:role => 'admin', :actor => @owner_user) # Why it's really need it??
+
+ # Create and show params:
+ @create_params = {:build_list => @build_list.attributes.symbolize_keys.except(:bs_id)}
+ @create_params = @create_params.merge(:arches => [@params[:arch_id]], :build_for_platforms => [@params[:build_for_platform_id]], :format => :json)
+ any_instance_of(Project, :versions => ['v1.0', 'v2.0'])
+
+ http_login(@user)
+ end
+
+ context "do cancel" do
+ def do_cancel
+ get :cancel, :id => @build_list, :format => :json
+ end
+
+ context 'if user is project owner' do
+ before(:each) {http_login(@owner_user)}
+
+ context "if it has :build_pending status" do
+ it "should return correct json message" do
+ @build_list.update_column(:status, BuildList::BUILD_PENDING)
+ do_cancel
+ response.body.should == {:is_canceled => true, :url => api_v1_build_list_path(@build_list, :format => :json), :message => I18n.t('layout.build_lists.cancel_success')}.to_json
+ end
+
+ it "should cancel build list" do
+ @build_list.update_column(:status, BuildList::BUILD_PENDING)
+ do_cancel
+ @build_list.reload.status.should == BuildList::BUILD_CANCELED
+ end
+ end
+
+ context "if it has another status" do
+ it "should return correct json error message" do
+ @build_list.update_column(:status, BuildServer::PROJECT_NOT_FOUND)
+ do_cancel
+ response.body.should == {:is_canceled => false, :url => api_v1_build_list_path(@build_list, :format => :json), :message => I18n.t('layout.build_lists.cancel_fail')}.to_json
+ end
+
+ it "should not cancel build list" do
+ @build_list.update_column(:status, BuildServer::PROJECT_NOT_FOUND)
+ do_cancel
+ @build_list.reload.status.should == BuildServer::PROJECT_NOT_FOUND
+ end
+ end
+ end
+
+ context 'if user is not project owner' do
+ before(:each) do
+ @build_list.update_column(:status, BuildList::BUILD_PENDING)
+ do_cancel
+ end
+
+ it "should return access violation message" do
+ response.body.should == {"message" => "Access violation to this page!"}.to_json
+ end
+
+ it "should not cancel build list" do
+ @build_list.reload.status.should == BuildList::BUILD_PENDING
+ end
+ end
+ end
+
+ context "do publish" do
+ def do_publish
+ get :publish, :id => @build_list, :format => :json
+ end
+
+ context 'if user is project owner' do
+ before(:each) do
+ http_login(@owner_user)
+ @build_list.update_column(:status, BuildList::FAILED_PUBLISH)
+ do_publish
+ end
+
+ context "if it has :failed_publish status" do
+ it "should return correct json message" do
+ response.body.should == {:is_published => true, :url => api_v1_build_list_path(@build_list, :format => :json), :message => I18n.t('layout.build_lists.publish_success')}.to_json
+ end
+
+ it "should cancel build list" do
+ @build_list.reload.status.should == BuildList::BUILD_PUBLISH
+ end
+ end
+
+ context "if it has another status" do
+ before(:each) do
+ @build_list.update_column(:status, BuildServer::PROJECT_NOT_FOUND)
+ do_publish
+ end
+
+ it "should return correct json error message" do
+ response.body.should == {:is_published => false, :url => api_v1_build_list_path(@build_list, :format => :json), :message => I18n.t('layout.build_lists.publish_fail')}.to_json
+ end
+
+ it "should not cancel build list" do
+ @build_list.reload.status.should == BuildServer::PROJECT_NOT_FOUND
+ end
+ end
+ end
+
+ context 'if user is not project owner' do
+ before(:each) do
+ @build_list.update_column(:status, BuildList::FAILED_PUBLISH)
+ do_publish
+ end
+
+ it "should return access violation message" do
+ response.body.should == {"message" => "Access violation to this page!"}.to_json
+ end
+
+ it "should not cancel build list" do
+ @build_list.reload.status.should == BuildList::FAILED_PUBLISH
+ end
+ end
+ end
+
+ context "do reject_publish" do
+ before(:each) do
+ any_instance_of(BuildList, :current_duration => 100)
+ @build_list.save_to_repository.update_column(:publish_without_qa, false)
+ end
+
+ def do_reject_publish
+ get :reject_publish, :id => @build_list, :format => :json
+ end
+
+ context 'if user is project owner' do
+ before(:each) do
+ http_login(@owner_user)
+ @build_list.update_column(:status, BuildServer::SUCCESS)
+ @build_list.save_to_platform.update_column(:released, true)
+ do_reject_publish
+ end
+
+ context "if it has :success status" do
+ it "should return correct json message" do
+ response.body.should == {:is_reject_published => true, :url => api_v1_build_list_path(@build_list, :format => :json), :message => I18n.t('layout.build_lists.reject_publish_success')}.to_json
+ end
+
+ it "should reject publish build list" do
+ @build_list.reload.status.should == BuildList::REJECTED_PUBLISH
+ end
+ end
+
+ context "if it has another status" do
+ before(:each) do
+ @build_list.update_column(:status, BuildServer::PROJECT_NOT_FOUND)
+ do_reject_publish
+ end
+
+ it "should return correct json error message" do
+ response.body.should == {:is_reject_published => false, :url => api_v1_build_list_path(@build_list, :format => :json), :message => I18n.t('layout.build_lists.reject_publish_fail')}.to_json
+ end
+
+ it "should not cancel build list" do
+ @build_list.reload.status.should == BuildServer::PROJECT_NOT_FOUND
+ end
+ end
+ end
+
+ context 'if user is not project owner' do
+ before(:each) do
+ @build_list.update_column(:status, BuildServer::SUCCESS)
+ @build_list.save_to_platform.update_column(:released, true)
+ do_reject_publish
+ end
+
+ it "should return access violation message" do
+ response.body.should == {"message" => "Access violation to this page!"}.to_json
+ end
+
+ it "should not cancel build list" do
+ do_reject_publish
+ @build_list.reload.status.should == BuildServer::SUCCESS
+ end
+ end
+ end
+
+ context 'for open project' do
+ it_should_behave_like 'not create build list via api'
+
+ context 'if user is project owner' do
+ before(:each) {http_login(@owner_user)}
+ it_should_behave_like 'create build list via api'
+ end
+
+ context 'if user is project read member' do
+ before(:each) {http_login(@member_user)}
+ end
+ end
+
+ context 'for hidden project' do
+ before(:each) do
+ @project.update_column(:visibility, 'hidden')
+ end
+
+ it_should_behave_like 'not create build list via api'
+
+ context 'if user is project owner' do
+ before(:each) {http_login(@owner_user)}
+
+ it_should_behave_like 'create build list via api'
+ end
+
+ context 'if user is project read member' do
+ before(:each) {http_login(@member_user)}
+ it_should_behave_like 'not create build list via api'
+ end
+ end
+ end
+
+ context 'for group' do
+ before(:each) do
+ Arch.destroy_all
+ User.destroy_all
+
+ @build_list = FactoryGirl.create(:build_list_core)
+ @params = @build_list.attributes.symbolize_keys
+ @project = @build_list.project
+ @platform = @build_list.save_to_platform
+
+ stub_symlink_methods
+ @user = FactoryGirl.create(:user)
+ @owner_user = FactoryGirl.create(:user)
+ @member_user = FactoryGirl.create(:user)
+
+ # Create and show params:
+ @create_params = {:build_list => @build_list.attributes.symbolize_keys.except(:bs_id)}
+ @create_params = @create_params.merge(:arches => [@params[:arch_id]], :build_for_platforms => [@params[:build_for_platform_id]], :format => :json)
+ any_instance_of(Project, :versions => ['v1.0', 'v2.0'])
+
+ # Groups:
+ @owner_group = FactoryGirl.create(:group, :owner => @owner_user)
+ @member_group = FactoryGirl.create(:group)
+ @member_group.actors.create :role => 'reader', :actor_id => @member_user.id, :actor_type => 'User'
+
+ @group = FactoryGirl.create(:group)
+ @user = FactoryGirl.create(:user)
+ @group.actors.create :role => 'reader', :actor_id => @user.id, :actor_type => 'User'
+
+ @project.owner = @owner_group
+ @project.save
+
+ @project.relations.create :role => 'reader', :actor_id => @member_group.id, :actor_type => 'Group'
+ @project.relations.create :role => 'admin', :actor_id => @owner_group.id, :actor_type => 'Group'
+ @build_list.save_to_platform.relations.create(:role => 'admin', :actor => @owner_group) # Why it's really need it??
+ @build_list.save_to_platform.relations.create(:role => 'reader', :actor => @member_group) # Why it's really need it??
+
+ http_login(@user)
+ end
+
+ context 'for open project' do
+ it_should_behave_like 'not create build list via api'
+
+ context 'if user is group owner' do
+ before(:each) {http_login(@owner_user)}
+ it_should_behave_like 'create build list via api'
+ end
+
+ context 'if user is group read member' do
+ before(:each) {http_login(@member_user)}
+ it_should_behave_like 'not create build list via api'
+ end
+ end
+
+ context 'for hidden project' do
+ before(:each) do
+ @build_list.project.update_column(:visibility, 'hidden')
+ end
+
+ it_should_behave_like 'not create build list via api'
+
+ context 'if user is group owner' do
+ before(:each) {http_login(@owner_user)}
+ it_should_behave_like 'create build list via api'
+ end
+
+ context 'if user is group read member' do
+ before(:each) {http_login(@member_user)}
+ it_should_behave_like 'not create build list via api'
+ end
+ end
+
+ end
+ end
+
+ context 'read and accessible abilities' do
+ before(:each) do
+ Arch.destroy_all
+ User.destroy_all
+
+ @user = FactoryGirl.create(:user)
+
+ # Build Lists:
+ @build_list1 = FactoryGirl.create(:build_list_core)
+
+ @build_list2 = FactoryGirl.create(:build_list_core)
+ @build_list2.project.update_column(:visibility, 'hidden')
+
+ project = FactoryGirl.create(:project, :visibility => 'hidden', :owner => @user)
+ @build_list3 = FactoryGirl.create(:build_list_core, :project => project)
+
+ @build_list4 = FactoryGirl.create(:build_list_core)
+ @build_list4.project.update_column(:visibility, 'hidden')
+ @build_list4.project.relations.create! :role => 'reader', :actor_id => @user.id, :actor_type => 'User'
+
+ @filter_build_list1 = FactoryGirl.create(:build_list_core)
+ @filter_build_list2 = FactoryGirl.create(:build_list_core)
+ @filter_build_list3 = FactoryGirl.create(:build_list_core)
+ @filter_build_list4 = FactoryGirl.create(:build_list_core, :updated_at => (Time.now - 1.day),
+ :project => @build_list3.project, :save_to_platform => @build_list3.save_to_platform,
+ :arch => @build_list3.arch)
+ end
+
+ context 'for guest' do
+ it 'should be able to perform index action', :anonymous_access => true do
+ get :index, :format => :json
+ response.should be_success
+ end
+
+ it 'should not be able to perform index action', :anonymous_access => false do
+ get :index, :format => :json
+ response.status.should == 401
+ end
+ end
+
+ context 'for all build lists' do
+ before(:each) {
+ http_login(@user)
+ }
+
+ it 'should be able to perform index action' do
+ get :index, :format => :json
+ response.should be_success
+ end
+
+ it 'should show only accessible build_lists' do
+ get :index, :filter => {:ownership => 'index'}, :format => :json
+ assigns(:build_lists).should include(@build_list1)
+ assigns(:build_lists).should_not include(@build_list2)
+ assigns(:build_lists).should include(@build_list3)
+ assigns(:build_lists).should include(@build_list4)
+ assigns(:build_lists).count.should eq 7
+ end
+ end
+
+ context 'filter' do
+ before(:each) do
+ http_login FactoryGirl.create(:admin)
+ end
+
+ it 'should filter by bs_id' do
+ get :index, :filter => {:bs_id => @filter_build_list1.bs_id, :project_name => 'fdsfdf', :any_other_field => 'do not matter'}, :format => :json
+ assigns[:build_lists].should include(@filter_build_list1)
+ assigns[:build_lists].should_not include(@filter_build_list2)
+ assigns[:build_lists].should_not include(@filter_build_list3)
+ end
+
+ it 'should filter by project_name' do
+ get :index, :filter => {:project_name => @filter_build_list2.project.name, :ownership => 'index'}, :format => :json
+ assigns[:build_lists].should_not include(@filter_build_list1)
+ assigns[:build_lists].should include(@filter_build_list2)
+ assigns[:build_lists].should_not include(@filter_build_list3)
+ end
+
+ it 'should filter by project_name and start_date' do
+ get :index, :filter => {:project_name => @filter_build_list3.project.name, :ownership => 'index',
+ :"updated_at_start(1i)" => @filter_build_list3.updated_at.year.to_s,
+ :"updated_at_start(2i)" => @filter_build_list3.updated_at.month.to_s,
+ :"updated_at_start(3i)" => @filter_build_list3.updated_at.day.to_s}, :format => :json
+ assigns[:build_lists].should_not include(@filter_build_list1)
+ assigns[:build_lists].should_not include(@filter_build_list2)
+ assigns[:build_lists].should include(@filter_build_list3)
+ assigns[:build_lists].should_not include(@filter_build_list4)
+ end
+
+ end
+
+ context "for user" do
+ before(:each) do
+ @build_list = FactoryGirl.create(:build_list_core)
+ @params = @build_list.attributes.symbolize_keys
+ @project = @build_list.project
+
+ stub_symlink_methods
+ @owner_user = @project.owner
+ @member_user = FactoryGirl.create(:user)
+ @project.relations.create(:role => 'reader', :actor => @member_user)
+ @build_list.save_to_platform.relations.create(:role => 'admin', :actor => @owner_user) # Why it's really need it??
+
+ # Show params:
+ @show_params = {:id => @build_list.id, :format => :json}
+ end
+
+ context 'for open project' do
+ context 'for simple user' do
+ before(:each) {http_login(@user)}
+ it_should_behave_like 'show build list via api'
+ end
+
+ context 'if user is project owner' do
+ before(:each) {http_login(@owner_user)}
+ it_should_behave_like 'show build list via api'
+ end
+
+ context 'if user is project read member' do
+ before(:each) {http_login(@member_user)}
+ it_should_behave_like 'show build list via api'
+ end
+ end
+
+ context 'for hidden project' do
+ before(:each) do
+ @project.update_column(:visibility, 'hidden')
+ end
+
+ context 'for simple user' do
+ before(:each) {http_login(@user)}
+ it_should_behave_like 'not show build list via api'
+ end
+
+ context 'if user is project owner' do
+ before(:each) {http_login(@owner_user)}
+ it_should_behave_like 'show build list via api'
+ end
+
+ context 'if user is project read member' do
+ before(:each) {http_login(@member_user)}
+ it_should_behave_like 'show build list via api'
+ end
+ end
+ end
+
+ context "for group" do
+ before(:each) do
+ @platform = FactoryGirl.create(:platform_with_repos)
+ @build_list = FactoryGirl.create(:build_list_core, :save_to_platform => @platform)
+ @project = @build_list.project
+ @params = @build_list.attributes.symbolize_keys
+
+ stub_symlink_methods
+ @owner_user = @project.owner#FactoryGirl.create(:user)
+ @member_user = FactoryGirl.create(:user)
+ #@project.relations.create(:role => 'reader', :actor => @member_user)
+
+ # Show params:
+ @show_params = {:id => @build_list.id, :format => :json}
+
+ # Groups:
+ @owner_group = FactoryGirl.create(:group, :owner => @owner_user)
+ @member_group = FactoryGirl.create(:group)
+ @member_group.actors.create :role => 'reader', :actor_id => @member_user.id, :actor_type => 'User'
+ @group = FactoryGirl.create(:group)
+ @group.actors.create :role => 'reader', :actor_id => @user.id, :actor_type => 'User'
+
+ #@project = FactoryGirl.create(:project, :owner => @owner_group, :repositories => @platform.repositories)
+
+ #@project.owner = @owner_group
+ #@project.save
+ @project.relations.create :role => 'reader', :actor_id => @member_group.id, :actor_type => 'Group'
+ #@build_list.save_to_platform.relations.create(:role => 'reader', :actor => @member_group) # Why it's really need it??
+ #@build_list.save_to_platform.relations.create(:role => 'admin', :actor => @owner_group) # Why it's really need it??
+ end
+
+ context 'for open project' do
+ context 'for simple user' do
+ before(:each) {http_login(@user)}
+ it_should_behave_like 'show build list via api'
+ end
+
+ context 'if user is group owner' do
+ before(:each) {http_login(@owner_user)}
+ it_should_behave_like 'show build list via api'
+ end
+
+ context 'if user is group read member' do
+ before(:each) {http_login(@member_user)}
+ it_should_behave_like 'show build list via api'
+ end
+ end
+
+ context 'for hidden project' do
+ before(:each) do
+ @build_list.project.update_column(:visibility, 'hidden')
+ end
+
+ context 'for simple user' do
+ before(:each) {http_login(@user)}
+ it_should_behave_like 'not show build list via api'
+ end
+
+ context 'if user is group owner' do
+ before(:each) { http_login(@owner_user) }
+ it_should_behave_like 'show build list via api'
+ end
+
+ context 'if user is group read member' do
+ before(:each) {http_login(@member_user)}
+ it_should_behave_like 'show build list via api'
+ end
+ end
+ end
+
+ end
+end
diff --git a/spec/controllers/api/v1/groups_controller_spec.rb b/spec/controllers/api/v1/groups_controller_spec.rb
new file mode 100644
index 000000000..526ed825f
--- /dev/null
+++ b/spec/controllers/api/v1/groups_controller_spec.rb
@@ -0,0 +1,263 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+shared_examples_for 'api group user with reader rights' do
+ it 'should be able to perform members action' do
+ get :members, :id => @group.id, :format => :json
+ response.should be_success
+ end
+ it_should_behave_like 'api group user with show rights'
+end
+
+shared_examples_for 'api group user with show rights' do
+ it 'should be able to perform show action' do
+ get :show, :id => @group.id, :format => :json
+ response.should be_success
+ end
+
+ it 'should be able to perform index action' do
+ get :index, :format => :json
+ response.should be_success
+ end
+end
+
+shared_examples_for 'api group user without reader rights' do
+ it 'should not be able to perform members action' do
+ get :members, :id => @group.id, :format => :json
+ response.should_not be_success
+ end
+end
+
+shared_examples_for 'api group user with admin rights' do
+
+ context 'api group user with update rights' do
+ before do
+ put :update, {:group => {:description => 'new description'}, :id => @group.id}, :format => :json
+ end
+
+ it 'should be able to perform update action' do
+ response.should be_success
+ end
+ it 'ensures that group has been updated' do
+ @group.reload
+ @group.description.should == 'new description'
+ end
+ end
+
+ context 'api group user with add_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ put :add_member, {:member_id => member.id, :id => @group.id}, :format => :json
+ end
+
+ it 'should be able to perform add_member action' do
+ response.should be_success
+ end
+ it 'ensures that new member has been added to group' do
+ @group.members.should include(member)
+ end
+ end
+
+ context 'api group user with remove_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @group.add_member(member)
+ delete :remove_member, {:member_id => member.id, :id => @group.id}, :format => :json
+ end
+
+ it 'should be able to perform remove_member action' do
+ response.should be_success
+ end
+ it 'ensures that member has been removed from group' do
+ @group.members.should_not include(member)
+ end
+ end
+
+ context 'api group user with update_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @group.add_member(member)
+ put :update_member, {:member_id => member.id, :role => 'reader', :id => @group.id}, :format => :json
+ end
+
+ it 'should be able to perform update_member action' do
+ response.should be_success
+ end
+ it 'ensures that member role has been updated in group' do
+ @group.actors.where(:actor_id => member, :actor_type => 'User').first.
+ role.should == 'reader'
+ end
+ end
+end
+
+shared_examples_for 'api group user with owner rights' do
+ context 'api group user with destroy rights' do
+ it 'should be able to perform destroy action' do
+ delete :destroy, :id => @group.id, :format => :json
+ response.should be_success
+ end
+ it 'ensures that group has been destroyed' do
+ lambda { delete :destroy, :id => @group.id, :format => :json }.should change{ Group.count }.by(-1)
+ end
+ end
+end
+
+shared_examples_for 'api group user without admin rights' do
+ context 'api group user without update_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @group.add_member(member)
+ put :update_member, {:member_id => member.id, :role => 'reader', :id => @group.id}, :format => :json
+ end
+
+ it 'should not be able to perform update_member action' do
+ response.should_not be_success
+ end
+ it 'ensures that member role has not been updated in group' do
+ @group.actors.where(:actor_id => member, :actor_type => 'User').first.
+ role.should_not == 'reader'
+ end
+ end
+
+ context 'api group user without update rights' do
+ before do
+ put :update, {:group => {:description => 'new description'}, :id => @group.id}, :format => :json
+ end
+
+ it 'should not be able to perform update action' do
+ response.should_not be_success
+ end
+ it 'ensures that platform has not been updated' do
+ @group.reload
+ @group.description.should_not == 'new description'
+ end
+ end
+
+ context 'api group user without add_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ put :add_member, {:member_id => member.id, :id => @group.id}, :format => :json
+ end
+
+ it 'should not be able to perform add_member action' do
+ response.should_not be_success
+ end
+ it 'ensures that new member has not been added to group' do
+ @group.members.should_not include(member)
+ end
+ end
+
+ context 'api group user without remove_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @group.add_member(member)
+ delete :remove_member, {:member_id => member.id, :id => @group.id}, :format => :json
+ end
+
+ it 'should be able to perform update action' do
+ response.should_not be_success
+ end
+ it 'ensures that member has not been removed from group' do
+ @group.members.should include(member)
+ end
+ end
+
+end
+
+shared_examples_for 'api group user without owner rights' do
+ context 'api group user without destroy rights' do
+ it 'should not be able to perform destroy action' do
+ delete :destroy, :id => @group.id, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that group has not been destroyed' do
+ lambda { delete :destroy, :id => @group.id, :format => :json }.should_not change{ Group.count }
+ end
+ end
+end
+
+describe Api::V1::GroupsController do
+ before do
+ stub_symlink_methods
+
+ @group = FactoryGirl.create(:group)
+ @user = FactoryGirl.create(:user)
+ end
+
+ context 'for guest' do
+
+ it "should not be able to perform index action" do
+ get :index, :format => :json
+ response.status.should == 401
+ end
+
+ it "should not be able to perform show action", :anonymous_access => false do
+ get :show, :id => @group.id, :format => :json
+ response.status.should == 401
+ end
+
+ it "should be able to perform show action", :anonymous_access => true do
+ get :show, :id => @group.id, :format => :json
+ response.should be_success
+ end
+
+ context 'api group user without create rights' do
+ let(:params) { {:group => {:uname => 'test_uname'}} }
+ it 'should not be able to perform create action' do
+ post :create, params, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that group has not been created' do
+ lambda { post :create, params, :format => :json }.should_not change{ Group.count }
+ end
+ end
+
+ it_should_behave_like 'api group user without reader rights'
+ it_should_behave_like 'api group user without admin rights'
+ it_should_behave_like 'api group user without owner rights'
+ end
+
+ context 'for global admin' do
+ before do
+ @admin = FactoryGirl.create(:admin)
+ http_login(@admin)
+ end
+
+ it_should_behave_like 'api group user with reader rights'
+ it_should_behave_like 'api group user with admin rights'
+ it_should_behave_like 'api group user with owner rights'
+ end
+
+ context 'for owner user' do
+ before do
+ @group = FactoryGirl.create(:group, :owner => @user)
+ http_login(@user)
+ end
+
+ it_should_behave_like 'api group user with reader rights'
+ it_should_behave_like 'api group user with admin rights'
+ it_should_behave_like 'api group user with owner rights'
+ end
+
+ context 'for admin user' do
+ before do
+ @group.add_member(@user)
+ http_login(@user)
+ end
+
+ it_should_behave_like 'api group user with reader rights'
+ it_should_behave_like 'api group user with admin rights'
+ it_should_behave_like 'api group user without owner rights'
+ end
+
+ context 'for simple user' do
+ before do
+ http_login(@user)
+ end
+
+ it_should_behave_like 'api group user with show rights'
+ it_should_behave_like 'api group user without reader rights'
+ it_should_behave_like 'api group user without admin rights'
+ it_should_behave_like 'api group user without owner rights'
+ end
+end
diff --git a/spec/controllers/api/v1/platforms_controller_spec.rb b/spec/controllers/api/v1/platforms_controller_spec.rb
new file mode 100644
index 000000000..682bce637
--- /dev/null
+++ b/spec/controllers/api/v1/platforms_controller_spec.rb
@@ -0,0 +1,327 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+shared_examples_for 'api platform user with reader rights' do
+ include_examples "api platform user with show rights"
+
+ it 'should be able to perform index action' do
+ get :index, :format => :json
+ response.should render_template(:index)
+ end
+
+ it 'should be able to perform members action' do
+ get :members, :id => @platform.id, :format => :json
+ response.should render_template(:members)
+ end
+end
+
+shared_examples_for 'api platform user with writer rights' do
+
+ context 'api platform user with update rights' do
+ before do
+ put :update, {:platform => {:description => 'new description'}, :id => @platform.id}, :format => :json
+ end
+
+ it 'should be able to perform update action' do
+ response.should be_success
+ end
+ it 'ensures that platform has been updated' do
+ @platform.reload
+ @platform.description.should == 'new description'
+ end
+ end
+
+ context 'api platform user with add_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ put :add_member, {:member_id => member.id, :type => 'User', :id => @platform.id}, :format => :json
+ end
+
+ it 'should be able to perform add_member action' do
+ response.should be_success
+ end
+ it 'ensures that new member has been added to platform' do
+ @platform.members.should include(member)
+ end
+ end
+
+ context 'api platform user with remove_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @platform.add_member(member)
+ delete :remove_member, {:member_id => member.id, :type => 'User', :id => @platform.id}, :format => :json
+ end
+
+ it 'should be able to perform remove_member action' do
+ response.should be_success
+ end
+ it 'ensures that member has been removed from platform' do
+ @platform.members.should_not include(member)
+ end
+ end
+
+ context 'api platform user with destroy rights for main platforms only' do
+ it 'should be able to perform destroy action for main platform' do
+ delete :destroy, :id => @platform.id, :format => :json
+ response.should be_success
+ end
+ it 'ensures that main platform has been destroyed' do
+ lambda { delete :destroy, :id => @platform.id, :format => :json }.should change{ Platform.count }.by(-1)
+ end
+ it 'should not be able to perform destroy action for personal platform' do
+ delete :destroy, :id => @personal_platform.id, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that personal platform has not been destroyed' do
+ lambda { delete :destroy, :id => @personal_platform.id, :format => :json }.should_not change{ Platform.count }
+ end
+ end
+end
+
+shared_examples_for 'api platform user without writer rights' do
+
+ context 'api platform user without update rights' do
+ before do
+ put :update, {:platform => {:description => 'new description'}, :id => @platform.id}, :format => :json
+ end
+
+ it 'should not be able to perform update action' do
+ response.should_not be_success
+ end
+ it 'ensures that platform has not been updated' do
+ @platform.reload
+ @platform.description.should_not == 'new description'
+ end
+ end
+
+ context 'api platform user without add_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ put :add_member, {:member_id => member.id, :type => 'User', :id => @platform.id}, :format => :json
+ end
+
+ it 'should not be able to perform add_member action' do
+ response.should_not be_success
+ end
+ it 'ensures that new member has not been added to platform' do
+ @platform.members.should_not include(member)
+ end
+ end
+
+ context 'api platform user without remove_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @platform.add_member(member)
+ delete :remove_member, {:member_id => member.id, :type => 'User', :id => @platform.id}, :format => :json
+ end
+
+ it 'should be able to perform update action' do
+ response.should_not be_success
+ end
+ it 'ensures that member has not been removed from platform' do
+ @platform.members.should include(member)
+ end
+ end
+
+ context 'should not be able to perform clear action' do
+ it 'for personal platform' do
+ put :clear, :id => @personal_platform.id, :format => :json
+ response.should_not be_success
+ end
+ it 'for main platform' do
+ put :clear, :id => @platform.id, :format => :json
+ response.should_not be_success
+ end
+ end
+
+ context 'api platform user without destroy rights' do
+ it 'should not be able to perform destroy action for main platform' do
+ delete :destroy, :id => @platform.id, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that main platform has not been destroyed' do
+ lambda { delete :destroy, :id => @platform.id, :format => :json }.should_not change{ Platform.count }
+ end
+ it 'should not be able to perform destroy action for personal platform' do
+ delete :destroy, :id => @personal_platform.id, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that personal platform has not been destroyed' do
+ lambda { delete :destroy, :id => @personal_platform.id, :format => :json }.should_not change{ Platform.count }
+ end
+ end
+
+ it_should_behave_like 'api platform user without global admin rights'
+end
+
+shared_examples_for 'api platform user without global admin rights' do
+ context 'should not be able to perform clear action' do
+ it 'for personal platform' do
+ put :clear, :id => @personal_platform.id, :format => :json
+ response.should_not be_success
+ end
+ it 'for main platform' do
+ put :clear, :id => @platform.id, :format => :json
+ response.should_not be_success
+ end
+ end
+
+ [:create, :clone].each do |action|
+ context "api platform user without #{action} rights" do
+ before { any_instance_of(Platform, :create_directory => true) }
+ it "should not be able to perform #{action} action" do
+ post action, clone_or_create_params, :format => :json
+ response.should_not be_success
+ end
+ it "ensures that platform has not been #{action}d" do
+ lambda { post action, clone_or_create_params, :format => :json }.should_not change{ Platform.count }
+ end
+ end
+ end
+end
+
+shared_examples_for 'api platform user with reader rights for hidden platform' do
+ before(:each) do
+ @platform.update_column(:visibility, 'hidden')
+ end
+
+ it_should_behave_like 'api platform user with show rights'
+end
+
+shared_examples_for 'api platform user without reader rights for hidden platform' do
+ before(:each) do
+ @platform.update_column(:visibility, 'hidden')
+ end
+
+ it_should_behave_like 'api platform user without show rights'
+end
+
+shared_examples_for "api platform user with show rights" do
+ it 'should be able to perform show action' do
+ get :show, :id => @platform.id, :format => :json
+ response.should render_template(:show)
+ end
+
+ it 'should be able to perform platforms_for_build action' do
+ get :platforms_for_build, :format => :json
+ response.should render_template(:index)
+ end
+end
+
+shared_examples_for "api platform user without show rights" do
+ [:show, :members].each do |action|
+ it "should not be able to perform #{ action } action" do
+ get action, :id => @platform.id, :format => :json
+ response.body.should == {"message" => "Access violation to this page!"}.to_json
+ end
+ end
+end
+
+describe Api::V1::PlatformsController do
+ let(:clone_or_create_params) { {:id => @platform.id, :platform => {:description => 'new description', :name => 'new_name', :owner_id => @user.id, :distrib_type => APP_CONFIG['distr_types'].first}} }
+ before do
+ stub_symlink_methods
+
+ @platform = FactoryGirl.create(:platform, :visibility => 'open')
+ @personal_platform = FactoryGirl.create(:platform, :platform_type => 'personal')
+ @user = FactoryGirl.create(:user)
+ end
+
+ context 'for guest' do
+
+ it "should not be able to perform index action" do
+ get :index, :format => :json
+ response.status.should == 401
+ end
+
+ [:show, :platforms_for_build].each do |action|
+ it "should not be able to perform #{ action } action", :anonymous_access => false do
+ get action, :format => :json
+ response.status.should == 401
+ end
+ end
+
+ it 'should be able to perform members action', :anonymous_access => true do
+ get :members, :id => @platform.id, :format => :json
+ response.should render_template(:members)
+ end
+
+ it_should_behave_like 'api platform user with show rights' if APP_CONFIG['anonymous_access']
+ it_should_behave_like 'api platform user without reader rights for hidden platform' if APP_CONFIG['anonymous_access']
+ it_should_behave_like 'api platform user without writer rights'
+ end
+
+ context 'for global admin' do
+ before do
+ @admin = FactoryGirl.create(:admin)
+ http_login(@admin)
+ end
+
+ it_should_behave_like 'api platform user with reader rights'
+ it_should_behave_like 'api platform user with reader rights for hidden platform'
+ it_should_behave_like 'api platform user with writer rights'
+
+ [:clone, :create].each do |action|
+ context "with #{action} rights" do
+ before do
+ any_instance_of(Platform, :create_directory => true)
+ clone_or_create_params[:platform][:owner_id] = @admin.id
+ end
+ it "should be able to perform #{action} action" do
+ post action, clone_or_create_params, :format => :json
+ response.should be_success
+ end
+ it "ensures that platform has been #{action}d" do
+ lambda { post action, clone_or_create_params, :format => :json }.should change{ Platform.count }.by(1)
+ end
+ end
+ end
+
+ end
+
+ context 'for owner user' do
+ before do
+ http_login(@user)
+ @platform.owner = @user; @platform.save
+ @platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin')
+ end
+
+ it_should_behave_like 'api platform user with reader rights'
+ it_should_behave_like 'api platform user with reader rights for hidden platform'
+ it_should_behave_like 'api platform user with writer rights'
+ it_should_behave_like 'api platform user without global admin rights'
+ end
+
+ context 'for reader user' do
+ before do
+ http_login(@user)
+ @platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'reader')
+ @personal_platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'reader')
+ end
+
+ context 'perform index action with type param' do
+ render_views
+ %w(main personal).each do |type|
+ it "ensures that filter by type = #{type} returns true result" do
+ get :index, :format => :json, :type => "#{type}"
+ JSON.parse(response.body)['platforms'].map{ |p| p['platform_type'] }.
+ uniq.should == ["#{type}"]
+ end
+ end
+ end
+
+ it_should_behave_like 'api platform user with reader rights'
+ it_should_behave_like 'api platform user with reader rights for hidden platform'
+ it_should_behave_like 'api platform user without writer rights'
+ end
+
+ context 'for simple user' do
+ before do
+ http_login(@user)
+ end
+
+ it_should_behave_like 'api platform user with reader rights'
+ it_should_behave_like 'api platform user without reader rights for hidden platform'
+ it_should_behave_like 'api platform user without writer rights'
+ end
+end
diff --git a/spec/controllers/api/v1/projects_controller_spec.rb b/spec/controllers/api/v1/projects_controller_spec.rb
new file mode 100644
index 000000000..012437bc1
--- /dev/null
+++ b/spec/controllers/api/v1/projects_controller_spec.rb
@@ -0,0 +1,512 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+shared_examples_for "api projects user with reader rights" do
+ include_examples "api projects user with show rights"
+end
+
+shared_examples_for "api projects user with reader rights for hidden project" do
+ before(:each) do
+ @project.update_column(:visibility, 'hidden')
+ end
+
+ it_should_behave_like 'api projects user with show rights'
+end
+
+shared_examples_for "api projects user without reader rights for hidden project" do
+ before(:each) do
+ @project.update_column(:visibility, 'hidden')
+ end
+
+ it_should_behave_like 'api projects user without show rights'
+end
+
+shared_examples_for "api projects user without show rights" do
+ it "should show access violation instead of project data" do
+ get :show, :id => @project.id, :format => :json
+ response.should_not be_success
+ end
+
+ it "should show access violation instead of project refs_list" do
+ get :refs_list, :id => @project.id, :format => :json
+ response.should_not be_success
+ end
+
+ it "should access violation instead of project data by get_id" do
+ get :get_id, :name => @project.name, :owner => @project.owner.uname, :format => :json
+ response.should_not be_success
+ end
+
+ it "should show access violation instead of project members data" do
+ get :members, :id => @project.id, :format => :json
+ response.should_not be_success
+ end
+
+end
+
+shared_examples_for 'api projects user without fork rights' do
+ it 'should not be able to perform fork action' do
+ post :fork, :id => @project.id, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that project has not been forked' do
+ lambda { post :fork, :id => @project.id, :format => :json }.should_not change{ Project.count }
+ end
+end
+
+shared_examples_for 'api projects user with fork rights' do
+ it 'should be able to perform fork action' do
+ post :fork, :id => @project.id, :format => :json
+ response.should be_success
+ end
+ it 'ensures that project has been forked' do
+ lambda { post :fork, :id => @project.id, :format => :json }.should change{ Project.count }.by(1)
+ end
+end
+
+shared_examples_for 'api projects user with fork rights for hidden project' do
+ before { @project.update_column(:visibility, 'hidden') }
+ it_should_behave_like 'api projects user with fork rights'
+end
+
+shared_examples_for 'api projects user without fork rights for hidden project' do
+ before { @project.update_column(:visibility, 'hidden') }
+ it_should_behave_like 'api projects user without fork rights'
+end
+
+shared_examples_for "api projects user with show rights" do
+ it "should show project data" do
+ get :show, :id => @project.id, :format => :json
+ render_template(:show)
+ end
+
+ it "should show refs_list of project" do
+ get :refs_list, :id => @project.id, :format => :json
+ render_template(:refs_list)
+ end
+
+ context 'project find by get_id' do
+ it "should find project by name and owner name" do
+ @project.reload
+ get :get_id, :name => @project.name, :owner => @project.owner.uname, :format => :json
+ assigns[:project].id.should == @project.id
+ end
+
+ it "should not find project by non existing name and owner name" do
+ get :get_id, :name => 'NONE_EXISTING_NAME', :owner => @project.owner.uname, :format => :json
+ assigns[:project].should be_blank
+ end
+
+ it "should render 404 for non existing name and owner name" do
+ get :get_id, :name => 'NONE_EXISTING_NAME', :owner => @project.owner.uname, :format => :json
+ response.body.should == {:status => 404, :message => I18n.t("flash.404_message")}.to_json
+ end
+ end
+end
+
+shared_examples_for 'api projects user with admin rights' do
+
+ it "should be able to perform members action" do
+ get :members, :id => @project.id, :format => :json
+ response.should be_success
+ end
+
+ context 'api project user with update rights' do
+ before do
+ put :update, {:project => {:description => 'new description'}, :id => @project.id}, :format => :json
+ end
+
+ it 'should be able to perform update action' do
+ response.should be_success
+ end
+ it 'ensures that group has been updated' do
+ @project.reload
+ @project.description.should == 'new description'
+ end
+ end
+
+ context 'api project user with add_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ put :add_member, {:member_id => member.id, :type => 'User', :role => 'admin', :id => @project.id}, :format => :json
+ end
+
+ it 'should be able to perform add_member action' do
+ response.should be_success
+ end
+ it 'ensures that new member has been added to project' do
+ @project.members.should include(member)
+ end
+ end
+
+ context 'api project user with remove_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @project.add_member(member)
+ delete :remove_member, {:member_id => member.id, :type => 'User', :id => @project.id}, :format => :json
+ end
+
+ it 'should be able to perform remove_member action' do
+ response.should be_success
+ end
+ it 'ensures that member has been removed from project' do
+ @project.members.should_not include(member)
+ end
+ end
+
+ context 'api group user with update_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @project.add_member(member)
+ put :update_member, {:member_id => member.id, :type => 'User', :role => 'reader', :id => @project.id}, :format => :json
+ end
+
+ it 'should be able to perform update_member action' do
+ response.should be_success
+ end
+ it 'ensures that member role has been updated in project' do
+ @project.relations.by_actor(member).first.
+ role.should == 'reader'
+ end
+ end
+end
+
+shared_examples_for 'api projects user without admin rights' do
+
+ it "should not be able to perform members action" do
+ get :members, :id => @project.id, :format => :json
+ response.should_not be_success
+ end
+
+ context 'api project user without update_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @project.add_member(member)
+ put :update_member, {:member_id => member.id, :type => 'User', :role => 'reader', :id => @project.id}, :format => :json
+ end
+
+ it 'should not be able to perform update_member action' do
+ response.should_not be_success
+ end
+ it 'ensures that member role has not been updated in project' do
+ @project.relations.by_actor(member).first.
+ role.should_not == 'reader'
+ end
+ end
+
+ context 'api project user without update rights' do
+ before do
+ put :update, {:project => {:description => 'new description'}, :id => @project.id}, :format => :json
+ end
+
+ it 'should not be able to perform update action' do
+ response.should_not be_success
+ end
+ it 'ensures that project has not been updated' do
+ @project.reload
+ @project.description.should_not == 'new description'
+ end
+ end
+
+ context 'api project user without add_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ put :add_member, {:member_id => member.id, :type => 'User', :role => 'admin', :id => @project.id}, :format => :json
+ end
+
+ it 'should not be able to perform add_member action' do
+ response.should_not be_success
+ end
+ it 'ensures that new member has not been added to project' do
+ @project.members.should_not include(member)
+ end
+ end
+
+ context 'api project user without remove_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @project.add_member(member)
+ delete :remove_member, {:member_id => member.id, :type => 'User', :id => @project.id}, :format => :json
+ end
+
+ it 'should be able to perform update action' do
+ response.should_not be_success
+ end
+ it 'ensures that member has not been removed from project' do
+ @project.members.should include(member)
+ end
+ end
+
+end
+
+shared_examples_for 'api projects user with owner rights' do
+ context 'api project user with destroy rights' do
+ it 'should be able to perform destroy action' do
+ delete :destroy, :id => @project.id, :format => :json
+ response.should be_success
+ end
+ it 'ensures that project has been destroyed' do
+ lambda { delete :destroy, :id => @project.id, :format => :json }.should change{ Project.count }.by(-1)
+ end
+ end
+end
+
+shared_examples_for 'api projects user without owner rights' do
+ context 'api project user with destroy rights' do
+ it 'should not be able to perform destroy action' do
+ delete :destroy, :id => @project.id, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that project has not been destroyed' do
+ lambda { delete :destroy, :id => @project.id, :format => :json }.should_not change{ Project.count }
+ end
+ end
+end
+
+describe Api::V1::ProjectsController do
+
+ before(:each) do
+ stub_symlink_methods
+
+ @project = FactoryGirl.create(:project)
+ @hidden_project = FactoryGirl.create(:project)
+ @another_user = FactoryGirl.create(:user)
+ end
+
+ context 'for guest' do
+
+ [:index, :members].each do |action|
+ it "should not be able to perform #{action} action" do
+ get action, :id => @project.id, :format => :json
+ response.should_not be_success
+ end
+ end
+
+ if APP_CONFIG['anonymous_access']
+ it_should_behave_like 'api projects user with show rights'
+ it_should_behave_like 'api projects user without reader rights for hidden project'
+ else
+ it_should_behave_like 'api projects user without show rights'
+ end
+ it_should_behave_like 'api projects user without fork rights'
+ it_should_behave_like 'api projects user without fork rights for hidden project'
+ it_should_behave_like 'api projects user without admin rights'
+ it_should_behave_like 'api projects user without owner rights'
+ end
+
+ context 'for simple user' do
+ before(:each) do
+ @user = FactoryGirl.create(:user)
+ http_login(@user)
+ end
+
+ it 'should be able to perform index action' do
+ get :index, :format => :json
+ response.should be_success
+ end
+
+ context 'api project user with create rights' do
+ let(:params) { {:project => {:name => 'test_name', :owner_id => @user.id, :owner_type => 'User', :visibility => 'open'}} }
+ it 'should be able to perform create action' do
+ post :create, params, :format => :json
+ response.should be_success
+ end
+ it 'ensures that project has been created' do
+ lambda { post :create, params, :format => :json }.should change{ Project.count }.by(1)
+ end
+ end
+
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user without reader rights for hidden project'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user without fork rights for hidden project'
+ it_should_behave_like 'api projects user without admin rights'
+ it_should_behave_like 'api projects user without owner rights'
+ end
+
+ context 'for admin' do
+ before(:each) do
+ @admin = FactoryGirl.create(:admin)
+ http_login(@admin)
+ end
+
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user with reader rights for hidden project'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user with fork rights for hidden project'
+ it_should_behave_like 'api projects user with admin rights'
+ it_should_behave_like 'api projects user with owner rights'
+ end
+
+ context 'for owner user' do
+ before(:each) do
+ @user = FactoryGirl.create(:user)
+ http_login(@user)
+ @project = FactoryGirl.create(:project, :owner => @user)
+ end
+
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user with reader rights for hidden project'
+ it_should_behave_like 'api projects user without fork rights'
+ it_should_behave_like 'api projects user without fork rights for hidden project'
+ it_should_behave_like 'api projects user with admin rights'
+ it_should_behave_like 'api projects user with owner rights'
+ end
+
+ context 'for reader user' do
+ before(:each) do
+ @user = FactoryGirl.create(:user)
+ http_login(@user)
+ @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'reader')
+ end
+
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user with reader rights for hidden project'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user with fork rights for hidden project'
+ it_should_behave_like 'api projects user without admin rights'
+ it_should_behave_like 'api projects user without owner rights'
+ end
+
+ context 'for writer user' do
+ before(:each) do
+ @user = FactoryGirl.create(:user)
+ http_login(@user)
+ @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'writer')
+ end
+
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user with reader rights for hidden project'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user with fork rights for hidden project'
+ it_should_behave_like 'api projects user without admin rights'
+ it_should_behave_like 'api projects user without owner rights'
+ end
+
+ context 'for group' do
+ before(:each) do
+ @group = FactoryGirl.create(:group)
+ @group_user = FactoryGirl.create(:user)
+# @project.relations.destroy_all
+ http_login(@group_user)
+ end
+
+ context 'with no relations to project' do
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user without reader rights for hidden project'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user without fork rights for hidden project'
+ it_should_behave_like 'api projects user without admin rights'
+ it_should_behave_like 'api projects user without owner rights'
+ end
+
+ context 'owner of the project' do
+ before(:each) do
+ @project = FactoryGirl.create(:project, :owner => @group)
+ end
+
+ context 'reader user' do
+ before(:each) do
+ @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'reader')
+ end
+
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user with reader rights for hidden project'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user with fork rights for hidden project'
+ it_should_behave_like 'api projects user without admin rights'
+ it_should_behave_like 'api projects user without owner rights'
+ end
+
+ context 'admin user' do
+ before(:each) do
+ @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'admin')
+ end
+
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user with reader rights for hidden project'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user with fork rights for hidden project'
+ it_should_behave_like 'api projects user with admin rights'
+ it_should_behave_like 'api projects user with owner rights'
+ end
+ end
+
+ context 'member of the project' do
+ context 'with admin rights' do
+ before(:each) do
+ @project.relations.create :actor_id => @group.id, :actor_type => @group.class.to_s, :role => 'admin'
+ end
+
+ context 'reader user' do
+ before(:each) do
+ @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'reader')
+ end
+
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user with reader rights for hidden project'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user with fork rights for hidden project'
+ it_should_behave_like 'api projects user with admin rights'
+ it_should_behave_like 'api projects user without owner rights'
+ end
+
+ context 'admin user' do
+ before(:each) do
+ @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'admin')
+ end
+
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user with reader rights for hidden project'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user with fork rights for hidden project'
+ it_should_behave_like 'api projects user with admin rights'
+ it_should_behave_like 'api projects user without owner rights'
+ end
+ end
+
+ context 'with reader rights' do
+ before(:each) do
+ @project.relations.create :actor_id => @group.id, :actor_type => @group.class.to_s, :role => 'reader'
+ end
+
+ context 'reader user' do
+ before(:each) do
+ @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'reader')
+ end
+
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user with reader rights for hidden project'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user with fork rights for hidden project'
+ it_should_behave_like 'api projects user without admin rights'
+ it_should_behave_like 'api projects user without owner rights'
+
+ context 'user should has best role' do
+ before(:each) do
+ @project.relations.create :actor_id => @group_user.id, :actor_type => @group_user.class.to_s, :role => 'admin'
+ end
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user with fork rights for hidden project'
+ it_should_behave_like 'api projects user with admin rights'
+ it_should_behave_like 'api projects user without owner rights'
+ end
+ end
+
+ context 'admin user' do
+ before(:each) do
+ @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'admin')
+ end
+
+ it_should_behave_like 'api projects user with reader rights'
+ it_should_behave_like 'api projects user with reader rights for hidden project'
+ it_should_behave_like 'api projects user with fork rights'
+ it_should_behave_like 'api projects user with fork rights for hidden project'
+ it_should_behave_like 'api projects user without admin rights'
+ it_should_behave_like 'api projects user without owner rights'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/controllers/api/v1/repositories_controller_spec.rb b/spec/controllers/api/v1/repositories_controller_spec.rb
new file mode 100644
index 000000000..ff46685f3
--- /dev/null
+++ b/spec/controllers/api/v1/repositories_controller_spec.rb
@@ -0,0 +1,310 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+shared_examples_for 'api repository user with reader rights' do
+ it_should_behave_like 'api repository user with show rights'
+end
+
+shared_examples_for 'api repository user with reader rights for hidden platform' do
+ before(:each) do
+ @platform.update_column(:visibility, 'hidden')
+ end
+
+ it_should_behave_like 'api repository user with show rights'
+end
+
+shared_examples_for 'api repository user without reader rights for hidden platform' do
+ before(:each) do
+ @platform.update_column(:visibility, 'hidden')
+ end
+
+ it_should_behave_like 'api repository user without show rights'
+end
+
+shared_examples_for "api repository user with show rights" do
+ it 'should be able to perform show action' do
+ get :show, :id => @repository.id, :format => :json
+ response.should render_template(:show)
+ end
+ it 'should be able to perform projects action' do
+ get :projects, :id => @repository.id, :format => :json
+ response.should render_template(:projects)
+ end
+end
+
+shared_examples_for "api repository user without show rights" do
+ it 'should not be able to perform show action' do
+ get :show, :id => @repository.id, :format => :json
+ response.body.should == {"message" => "Access violation to this page!"}.to_json
+ end
+end
+
+shared_examples_for 'api repository user with writer rights' do
+
+ context 'api repository user with update rights' do
+ before do
+ put :update, {:repository => {:description => 'new description'}, :id => @repository.id}, :format => :json
+ end
+
+ it 'should be able to perform update action' do
+ response.should be_success
+ end
+ it 'ensures that repository has been updated' do
+ @repository.reload
+ @repository.description.should == 'new description'
+ end
+ end
+
+ context 'api repository user with add_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ put :add_member, {:member_id => member.id, :type => 'User', :id => @repository.id}, :format => :json
+ end
+
+ it 'should be able to perform add_member action' do
+ response.should be_success
+ end
+ it 'ensures that new member has been added to repository' do
+ @repository.members.should include(member)
+ end
+ end
+
+ context 'api repository user with remove_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @repository.add_member(member)
+ delete :remove_member, {:member_id => member.id, :type => 'User', :id => @repository.id}, :format => :json
+ end
+
+ it 'should be able to perform remove_member action' do
+ response.should be_success
+ end
+ it 'ensures that member has been removed from repository' do
+ @repository.members.should_not include(member)
+ end
+ end
+
+ context 'api repository user with destroy rights' do
+ it 'should be able to perform destroy action for main platform' do
+ delete :destroy, :id => @repository.id, :format => :json
+ response.should be_success
+ end
+ it 'ensures that repository of main platform has been destroyed' do
+ lambda { delete :destroy, :id => @repository.id, :format => :json }.should change{ Repository.count }.by(-1)
+ end
+ it 'should not be able to perform destroy action for repository of personal platform' do
+ delete :destroy, :id => @personal_repository.id, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that repository of personal platform has not been destroyed' do
+ lambda { delete :destroy, :id => @personal_repository.id, :format => :json }.should_not change{ Repository.count }
+ end
+ end
+
+ context 'api repository user with add_project rights' do
+ before { put :add_project, :id => @repository.id, :project_id => @project.id, :format => :json }
+ it 'should be able to perform add_project action' do
+ response.should be_success
+ end
+ it 'ensures that project has been added to repository' do
+ @repository.projects.should include(@project)
+ end
+ end
+
+ context 'api repository user with remove_project rights' do
+ before do
+ @repository.projects << @project
+ delete :remove_project, :id => @repository.id, :project_id => @project.id, :format => :json
+ end
+ it 'should be able to perform remove_project action' do
+ response.should be_success
+ end
+ it 'ensures that project has been removed from repository' do
+ @repository.reload
+ @repository.projects.should_not include(@project)
+ end
+ end
+
+ context 'api repository user with update signatures rights' do
+ before do
+ stub_key_pairs_calls
+ put :signatures, :id => @repository.id, :repository => {:public => 'iampublic', :secret => 'iamsecret'}, :format => :json
+ end
+ it 'should be able to perform signatures action' do
+ response.should be_success
+ end
+ it 'ensures that signatures has been updated' do
+ @repository.key_pair.should_not be_nil
+ end
+ end
+
+end
+
+shared_examples_for 'api repository user without writer rights' do
+
+ context 'api repository user without update rights' do
+ before do
+ put :update, {:repository => {:description => 'new description'}, :id => @repository.id}, :format => :json
+ end
+
+ it 'should not be able to perform update action' do
+ response.should_not be_success
+ end
+ it 'ensures that repository has not been updated' do
+ @repository.reload
+ @repository.description.should_not == 'new description'
+ end
+ end
+
+ context 'api repository user without add_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ put :add_member, {:member_id => member.id, :type => 'User', :id => @repository.id}, :format => :json
+ end
+
+ it 'should not be able to perform add_member action' do
+ response.should_not be_success
+ end
+ it 'ensures that new member has not been added to repository' do
+ @repository.members.should_not include(member)
+ end
+ end
+
+ context 'api repository user without remove_member rights' do
+ let(:member) { FactoryGirl.create(:user) }
+ before do
+ @repository.add_member(member)
+ delete :remove_member, {:member_id => member.id, :type => 'User', :id => @repository.id}, :format => :json
+ end
+
+ it 'should be able to perform update action' do
+ response.should_not be_success
+ end
+ it 'ensures that member has not been removed from repository' do
+ @repository.members.should include(member)
+ end
+ end
+
+ context 'api repository user without destroy rights' do
+ it 'should not be able to perform destroy action for repository of main platform' do
+ delete :destroy, :id => @repository.id, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that repository of main platform has not been destroyed' do
+ lambda { delete :destroy, :id => @repository.id, :format => :json }.should_not change{ Repository.count }
+ end
+ it 'should not be able to perform destroy action for repository of personal platform' do
+ delete :destroy, :id => @personal_repository.id, :format => :json
+ response.should_not be_success
+ end
+ it 'ensures that repository of personal platform has not been destroyed' do
+ lambda { delete :destroy, :id => @personal_repository.id, :format => :json }.should_not change{ Repository.count }
+ end
+ end
+
+ context 'api repository user without add_project rights' do
+ before { put :add_project, :id => @repository.id, :project_id => @project.id, :format => :json }
+ it 'should not be able to perform add_project action' do
+ response.should_not be_success
+ end
+ it 'ensures that project has not been added to repository' do
+ @repository.projects.should_not include(@project)
+ end
+ end
+
+ context 'api repository user without remove_project rights' do
+ before do
+ @repository.projects << @project
+ delete :remove_project, :id => @repository.id, :project_id => @project.id, :format => :json
+ end
+ it 'should not be able to perform remove_project action' do
+ response.should_not be_success
+ end
+ it 'ensures that project has not been removed from repository' do
+ @repository.reload
+ @repository.projects.should include(@project)
+ end
+ end
+
+ context 'api repository user without update signatures rights' do
+ before do
+ stub_key_pairs_calls
+ put :signatures, :id => @repository.id, :repository => {:public => 'iampublic', :secret => 'iamsecret'}, :format => :json
+ end
+ it 'should not be able to perform signatures action' do
+ response.should_not be_success
+ end
+ it 'ensures that signatures has not been updated' do
+ @repository.key_pair.should be_nil
+ end
+ end
+
+end
+
+
+describe Api::V1::RepositoriesController do
+ before(:each) do
+ stub_symlink_methods
+
+ @platform = FactoryGirl.create(:platform)
+ @repository = FactoryGirl.create(:repository, :platform => @platform)
+ @personal_repository = FactoryGirl.create(:personal_repository)
+ @project = FactoryGirl.create(:project)
+ @another_user = FactoryGirl.create(:user)
+ end
+
+ context 'for guest' do
+ it "should not be able to perform show action", :anonymous_access => false do
+ get :show, :id => @repository.id, :format => :json
+ response.status.should == 401
+ end
+
+ if APP_CONFIG['anonymous_access']
+ it_should_behave_like 'api repository user without reader rights for hidden platform'
+ it_should_behave_like 'api repository user with show rights'
+ end
+ it_should_behave_like 'api repository user without writer rights'
+
+ it 'should not be able to perform projects action', :anonymous_access => false do
+ get :projects, :id => @repository.id, :format => :json
+ response.should_not be_success
+ end
+ end
+
+ context 'for admin' do
+ before(:each) do
+ @admin = FactoryGirl.create(:admin)
+ http_login(@admin)
+ end
+
+ it_should_behave_like 'api repository user with reader rights'
+ it_should_behave_like 'api repository user with reader rights for hidden platform'
+ it_should_behave_like 'api repository user with writer rights'
+ end
+
+ context 'for platform owner user' do
+ before(:each) do
+ @user = FactoryGirl.create(:user)
+ http_login(@user)
+ platform = @repository.platform
+ platform.owner = @user; platform.save
+ @repository.platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin')
+ end
+
+ it_should_behave_like 'api repository user with reader rights'
+ it_should_behave_like 'api repository user with reader rights for hidden platform'
+ it_should_behave_like 'api repository user with writer rights'
+ end
+
+ context 'for user' do
+ before(:each) do
+ @user = FactoryGirl.create(:user)
+ http_login(@user)
+ end
+
+ it_should_behave_like 'api repository user with reader rights'
+ it_should_behave_like 'api repository user without reader rights for hidden platform'
+ it_should_behave_like 'api repository user with show rights'
+ it_should_behave_like 'api repository user without writer rights'
+ end
+end
diff --git a/spec/controllers/api/v1/users_controller_spec.rb b/spec/controllers/api/v1/users_controller_spec.rb
new file mode 100644
index 000000000..bad6ffc8e
--- /dev/null
+++ b/spec/controllers/api/v1/users_controller_spec.rb
@@ -0,0 +1,94 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+describe Api::V1::UsersController do
+ before(:all) { User.destroy_all }
+ before do
+ stub_symlink_methods
+ @user = FactoryGirl.create(:user)
+ end
+
+ context 'for guest' do
+
+ [:show_current_user, :notifiers].each do |action|
+ it "should not be able to perform #{ action } action for a current user" do
+ get action, :format => :json
+ response.should_not be_success
+ end
+ end
+
+ it 'should be able to perform show action for a single user', :anonymous_access => true do
+ get :show, :id => @user.id, :format => :json
+ response.should render_template(:show)
+ end
+
+ it 'should not be able to perform show action for a single user', :anonymous_access => false do
+ get :show, :id => @user.id, :format => :json
+ response.should_not be_success
+ end
+
+ context 'should not be able to perform update action for a current user' do
+ before do
+ put :update, {:user => {:company => 'test_company'}}, :format => :json
+ end
+ it { response.should_not be_success }
+ it 'ensures that user has not been updated' do
+ @user.reload
+ @user.company.should_not == 'test_company'
+ end
+ end
+
+ context 'should not be able to perform notifiers action for a current user' do
+ before do
+ put :notifiers, {:notifiers => {:can_notify => false}}, :format => :json
+ end
+ it { response.should_not be_success }
+ it 'ensures that user notification settings have not been updated' do
+ @user.reload
+ @user.notifier.can_notify.should be_true
+ end
+ end
+
+ end
+
+ context 'for simple user' do
+ before do
+ http_login(@user)
+ end
+
+ [:show_current_user, :notifiers].each do |action|
+ it "should be able to perform #{ action } action for a current user" do
+ get action, :format => :json
+ response.should be_success
+ end
+ end
+
+ it 'should be able to perform show action for a single user' do
+ get :show, :id => @user.id, :format => :json
+ response.should render_template(:show)
+ end
+
+ context 'should be able to perform update action for a current user' do
+ before do
+ put :update, {:user => {:company => 'test_company'}}, :format => :json
+ end
+ it { response.should be_success }
+ it 'ensures that user has been updated' do
+ @user.reload
+ @user.company.should == 'test_company'
+ end
+ end
+
+ context 'should be able to perform notifiers action for a current user' do
+ before do
+ put :notifiers, {:notifiers => {:can_notify => false}}, :format => :json
+ end
+ it { response.should be_success }
+ it 'ensures that user notification settings have been updated' do
+ @user.reload
+ @user.notifier.can_notify.should be_false
+ end
+ end
+
+ end
+end
diff --git a/spec/controllers/autocompletes_controller_spec.rb b/spec/controllers/autocompletes_controller_spec.rb
new file mode 100644
index 000000000..6b0a4f3a4
--- /dev/null
+++ b/spec/controllers/autocompletes_controller_spec.rb
@@ -0,0 +1,40 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+describe AutocompletesController do
+
+ context 'for user' do
+ before do
+ set_session_for(FactoryGirl.create(:user))
+ end
+
+ it 'should be able to perform autocomplete_group_uname action' do
+ get :autocomplete_group_uname
+ response.should be_success
+ end
+
+ it 'should be able to perform autocomplete_user_uname action' do
+ get :autocomplete_user_uname
+ response.should be_success
+ end
+
+ end
+
+ context 'for guest' do
+
+ before do
+ set_session_for(User.new)
+ end
+
+ it 'should not be able to perform autocomplete_group_uname action' do
+ get :autocomplete_group_uname
+ response.should redirect_to(new_user_session_path)
+ end
+
+ it 'should not be able to perform autocomplete_user_uname action' do
+ get :autocomplete_user_uname
+ response.should redirect_to(new_user_session_path)
+ end
+
+ end
+end
diff --git a/spec/controllers/groups/profile_controller_spec.rb b/spec/controllers/groups/profile_controller_spec.rb
index 8ea65d72b..08abcca95 100644
--- a/spec/controllers/groups/profile_controller_spec.rb
+++ b/spec/controllers/groups/profile_controller_spec.rb
@@ -1,6 +1,13 @@
# -*- encoding : utf-8 -*-
require 'spec_helper'
+shared_examples_for 'group user with project show rights' do
+ it 'should be able to perform show action' do
+ get :show, :id => @group
+ response.should render_template(:show)
+ end
+end
+
shared_examples_for 'group user without update rights' do
it 'should be not able to perform update action' do
put :update, {:id => @group}.merge(@update_params)
@@ -47,11 +54,6 @@ shared_examples_for 'no group user' do
it 'should change objects count on create' do
lambda { post :create, @create_params }.should change{ Group.count }.by(1)
end
-
- it 'should be able to perform autocomplete_group_uname action' do
- get :autocomplete_group_uname
- response.should be_success
- end
end
shared_examples_for 'group owner' do
@@ -77,6 +79,16 @@ describe Groups::ProfileController do
end
context 'for guest' do
+
+ if APP_CONFIG['anonymous_access']
+ it_should_behave_like 'group user with project show rights'
+ else
+ it 'should not be able to perform show action' do
+ get :show, :id => @group
+ response.should redirect_to(new_user_session_path)
+ end
+ end
+
it 'should not be able to perform index action' do
get :index
response.should redirect_to(new_user_session_path)
@@ -99,6 +111,7 @@ describe Groups::ProfileController do
set_session_for(@admin)
end
+ it_should_behave_like 'group user with project show rights'
it_should_behave_like 'update_member_relation'
it_should_behave_like 'group owner'
@@ -120,6 +133,7 @@ describe Groups::ProfileController do
@group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'admin')
end
+ it_should_behave_like 'group user with project show rights'
it_should_behave_like 'update_member_relation'
it_should_behave_like 'group admin'
it_should_behave_like 'group user without destroy rights'
@@ -134,6 +148,7 @@ describe Groups::ProfileController do
@group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'admin')
end
+ it_should_behave_like 'group user with project show rights'
it_should_behave_like 'update_member_relation'
it_should_behave_like 'group owner'
end
@@ -154,6 +169,7 @@ describe Groups::ProfileController do
lambda { delete :remove_user, :id => @group }.should change{ Relation.count }.by(-1)
end
+ it_should_behave_like 'group user with project show rights'
it_should_behave_like 'no group user'
it_should_behave_like 'group user without destroy rights'
it_should_behave_like 'group user without update rights'
diff --git a/spec/controllers/projects/build_lists_controller_spec.rb b/spec/controllers/projects/build_lists_controller_spec.rb
index 4e9575f9c..d3c6ed428 100644
--- a/spec/controllers/projects/build_lists_controller_spec.rb
+++ b/spec/controllers/projects/build_lists_controller_spec.rb
@@ -28,7 +28,9 @@ describe Projects::BuildListsController do
end
shared_examples_for 'create build list' do
- before {test_git_commit(@project)}
+ before {
+ @project.update_attribute(:repositories, @platform.repositories)
+ }
it 'should be able to perform new action' do
get :new, :owner_name => @project.owner.uname, :project_name => @project.name
@@ -50,9 +52,21 @@ describe Projects::BuildListsController do
post :create, {:owner_name => @project.owner.uname, :project_name => @project.name}.merge(@create_params).deep_merge(:build_list => {:project_version => "4.7.5.3"})
@project.build_lists.last.commit_hash.should == @project.repo.commits('4.7.5.3').last.id
end
+
+ it 'should not be able to create with wrong project version' do
+ lambda{ post :create, {:owner_name => @project.owner.uname, :project_name => @project.name}.merge(@create_params).deep_merge(:build_list => {:project_version => "latest_wrong", :commit_hash => nil})}.should change{@project.build_lists.count}.by(0)
+ end
+
+ it 'should not be able to create with wrong git hash' do
+ lambda{ post :create, {:owner_name => @project.owner.uname, :project_name => @project.name}.merge(@create_params).deep_merge(:build_list => {:commit_hash => 'wrong'})}.should change{@project.build_lists.count}.by(0)
+ end
end
shared_examples_for 'not create build list' do
+ before {
+ @project.update_attribute(:repositories, @platform.repositories)
+ }
+
it 'should not be able to perform new action' do
get :new, :owner_name => @project.owner.uname, :project_name => @project.name
response.should redirect_to(forbidden_url)
@@ -68,31 +82,29 @@ describe Projects::BuildListsController do
context 'crud' do
before(:each) do
- platform = FactoryGirl.create(:platform_with_repos)
+ @platform = FactoryGirl.create(:platform_with_repos)
@create_params = {
- :build_list => {
+ :build_list => {
:project_version => 'latest_master',
- :save_to_platform_id => platform.id,
+ :save_to_repository_id => @platform.repositories.first.id,
:update_type => 'security',
- :include_repos => [platform.repositories.first.id]
+ :include_repos => [@platform.repositories.first.id]
},
:arches => [FactoryGirl.create(:arch).id],
- :build_for_platforms => [platform.id]
+ :build_for_platforms => [@platform.id]
}
any_instance_of(Project, :versions => ['v1.0', 'v2.0'])
end
context 'for guest' do
- if APP_CONFIG['anonymous_access']
- it 'should be able to perform index action' do
- get :index
- response.should be_success
- end
- else
- it 'should not be able to perform index action' do
- get :index
- response.should redirect_to(new_user_session_path)
- end
+ it 'should be able to perform index action', :anonymous_access => true do
+ get :index
+ response.should be_success
+ end
+
+ it 'should not be able to perform index action', :anonymous_access => false do
+ get :index
+ response.should redirect_to(new_user_session_path)
end
end
@@ -113,10 +125,16 @@ describe Projects::BuildListsController do
context 'for all build lists' do
before(:each) do
@build_list1 = FactoryGirl.create(:build_list_core)
- @build_list2 = FactoryGirl.create(:build_list_core, :project => FactoryGirl.create(:project, :visibility => 'hidden'))
- @build_list3 = FactoryGirl.create(:build_list_core, :project => FactoryGirl.create(:project, :owner => @user, :visibility => 'hidden'))
- @build_list4 = FactoryGirl.create(:build_list_core, :project => FactoryGirl.create(:project, :visibility => 'hidden'))
- @build_list4.project.relations.create :role => 'reader', :actor_id => @user.id, :actor_type => 'User'
+
+ @build_list2 = FactoryGirl.create(:build_list_core)
+ @build_list2.project.update_column(:visibility, 'hidden')
+
+ project = FactoryGirl.create(:project, :visibility => 'hidden', :owner => @user)
+ @build_list3 = FactoryGirl.create(:build_list_core, :project => project)
+
+ @build_list4 = FactoryGirl.create(:build_list_core)
+ @build_list4.project.update_column(:visibility, 'hidden')
+ @build_list4.project.relations.create! :role => 'reader', :actor_id => @user.id, :actor_type => 'User'
end
it 'should be able to perform index action' do
@@ -175,33 +193,36 @@ describe Projects::BuildListsController do
context 'for group' do
before(:each) do
- @owner_group = FactoryGirl.create(:group)
- @owner_user = FactoryGirl.create(:user)
- @owner_group.actors.create :role => 'admin', :actor_id => @owner_user.id, :actor_type => 'User'
+
+ @user = FactoryGirl.create(:user)
+ set_session_for(@user)
+
+ @build_list = FactoryGirl.create(:build_list_by_group_project)
+ @project = @build_list.project
+ @owner_group = @build_list.project.owner
+ @owner_user = @owner_group.owner
+
@member_group = FactoryGirl.create(:group)
@member_user = FactoryGirl.create(:user)
@member_group.actors.create :role => 'reader', :actor_id => @member_user.id, :actor_type => 'User'
-
- @group = FactoryGirl.create(:group)
- @user = FactoryGirl.create(:user)
- @group.actors.create :role => 'reader', :actor_id => @user.id, :actor_type => 'User'
-
- @project = FactoryGirl.create(:project, :owner => @owner_group)
@project.relations.create :role => 'reader', :actor_id => @member_group.id, :actor_type => 'Group'
- @build_list = FactoryGirl.create(:build_list_core, :project => @project)
-
- set_session_for(@user)
@show_params = {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @build_list.id}
end
-
+
context 'for all build lists' do
before(:each) do
@build_list1 = FactoryGirl.create(:build_list_core)
- @build_list2 = FactoryGirl.create(:build_list_core, :project => FactoryGirl.create(:project, :visibility => 'hidden'))
- @build_list3 = FactoryGirl.create(:build_list_core, :project => FactoryGirl.create(:project, :owner => @group, :visibility => 'hidden'))
- @build_list4 = FactoryGirl.create(:build_list_core, :project => FactoryGirl.create(:project, :visibility => 'hidden'))
- @build_list4.project.relations.create :role => 'reader', :actor_id => @group.id, :actor_type => 'Group'
+
+ @build_list2 = FactoryGirl.create(:build_list_core)
+ @build_list2.project.update_column(:visibility, 'hidden')
+
+ project = FactoryGirl.create(:project, :visibility => 'hidden', :owner => @user)
+ @build_list3 = FactoryGirl.create(:build_list_core, :project => project)
+
+ @build_list4 = FactoryGirl.create(:build_list_core)
+ @build_list4.project.update_column(:visibility, 'hidden')
+ @build_list4.project.relations.create! :role => 'reader', :actor_id => @user.id, :actor_type => 'User'
end
it 'should be able to perform index action' do
@@ -271,13 +292,13 @@ describe Projects::BuildListsController do
get :index
assigns[:build_server_status].should == {}
response.should be_success
- end
+ end
end
end
context 'filter' do
-
- before(:each) do
+
+ before(:each) do
set_session_for FactoryGirl.create(:admin)
@build_list1 = FactoryGirl.create(:build_list_core)
@@ -317,7 +338,7 @@ describe Projects::BuildListsController do
context 'callbacks' do
let(:build_list) { FactoryGirl.create(:build_list_core) }
- let(:build_list_package) { FactoryGirl.create(:build_list_package, :build_list_id => build_list.id, :platform_id => build_list.project.repositories.first.platform_id, :project_id => build_list.project_id, :version => "4.7.5.3", :release => 1) }
+ let(:build_list_package) { FactoryGirl.create(:build_list_package, :build_list_id => build_list.id, :platform_id => build_list.save_to_platform_id, :project_id => build_list.project_id, :version => "4.7.5.3", :release => 1) }
before(:each) do
mock(controller).authenticate_build_service! {true}
@@ -357,7 +378,6 @@ describe Projects::BuildListsController do
before do
@item = build_list.items.create(:name => build_list.project.name, :version => build_list.project_version, :level => 0)
repo = build_list.save_to_platform.repositories.first
- repo.projects << build_list.project
@project2 = FactoryGirl.create(:project)
repo.projects << @project2
end
diff --git a/spec/controllers/projects/comments_controller_for_commit_spec.rb b/spec/controllers/projects/comments_controller_for_commit_spec.rb
index 7555d5a79..d3a364218 100644
--- a/spec/controllers/projects/comments_controller_for_commit_spec.rb
+++ b/spec/controllers/projects/comments_controller_for_commit_spec.rb
@@ -19,7 +19,7 @@ end
shared_examples_for 'user with update own comment rights for commits' do
it 'should be able to perform update action' do
put :update, {:id => @own_comment.id}.merge(@update_params)
- response.should redirect_to(commit_path(@project, @commit.id))
+ response.status.should == 200
end
it 'should update subscribe body' do
@@ -31,7 +31,7 @@ end
shared_examples_for 'user with update stranger comment rights for commits' do
it 'should be able to perform update action' do
put :update, {:id => @stranger_comment.id}.merge(@update_params)
- response.should redirect_to(commit_path(@project, @commit.id))
+ response.status.should == 200
end
it 'should update comment title' do
diff --git a/spec/controllers/projects/comments_controller_spec.rb b/spec/controllers/projects/comments_controller_spec.rb
index 3fb36a7cd..97a55982f 100644
--- a/spec/controllers/projects/comments_controller_spec.rb
+++ b/spec/controllers/projects/comments_controller_spec.rb
@@ -11,7 +11,7 @@ shared_context "comments controller" do
@user = FactoryGirl.create(:user)
@own_comment = FactoryGirl.create(:comment, :commentable => @issue, :user => @user, :project_id => @project.id)
-
+
set_session_for(@user)
@address = {:owner_name => @project.owner.uname, :project_name => @project.name, :issue_id => @issue.serial_id}
@@ -35,7 +35,7 @@ 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])
+ response.status.should == 200
end
it 'should update comment body' do
@@ -47,7 +47,7 @@ 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])
+ response.status.should == 200
end
it 'should update comment body' do
diff --git a/spec/controllers/projects/git/git_trees_controller_spec.rb b/spec/controllers/projects/git/git_trees_controller_spec.rb
index 61c4d68af..5bf420e11 100644
--- a/spec/controllers/projects/git/git_trees_controller_spec.rb
+++ b/spec/controllers/projects/git/git_trees_controller_spec.rb
@@ -12,19 +12,21 @@ describe Projects::Git::TreesController do
@project = FactoryGirl.create(:project)
@another_user = FactoryGirl.create(:user)
- @params = {:owner_name => @project.owner.uname, :project_name => @project.name, :treeish => 'master'}
+ @params = { :owner_name => @project.owner.uname,
+ :project_name => @project.name,
+ :treeish => "#{@project.owner.uname}-#{@project.name}-master"}
end
context 'for guest' do
it 'should be able to perform archive action with anonymous acccess', :anonymous_access => true do
fill_project
- get :archive, @params.merge(:format => 'tar')
+ get :archive, @params.merge(:format => 'tar.gz')
response.should be_success
end
it 'should not be able to perform archive action without anonymous acccess', :anonymous_access => false do
fill_project
- get :archive, @params.merge(:format => 'tar')
+ get :archive, @params.merge(:format => 'tar.gz')
response.code.should == '401'
end
end
@@ -33,14 +35,14 @@ describe Projects::Git::TreesController do
it 'should not be able to archive empty project' do
@user = FactoryGirl.create(:user)
set_session_for(@user)
- expect { get :archive, @params.merge(:format => 'tar') }.to raise_error(ActionController::RoutingError)
+ expect { get :archive, @params.merge(:format => 'tar.gz') }.to raise_error(ActionController::RoutingError)
end
it 'should not be able to injection code with format' do
@user = FactoryGirl.create(:user)
set_session_for(@user)
fill_project
- expect { get :archive, @params.merge(:format => "tar master > /dev/null; echo 'I am hacker!';\#") }.to raise_error(ActionController::RoutingError)
+ expect { get :archive, @params.merge(:format => "tar.gz master > /dev/null; echo 'I am hacker!';\#") }.to raise_error(ActionController::RoutingError)
end
it 'should not be able to injection code with treeish' do
@@ -54,7 +56,7 @@ describe Projects::Git::TreesController do
@user = FactoryGirl.create(:user)
set_session_for(@user)
fill_project
- get :archive, @params.merge(:format => 'tar')
+ get :archive, @params.merge(:format => 'tar.gz')
response.should be_success
end
end
diff --git a/spec/controllers/projects/pull_requests_controller_spec.rb b/spec/controllers/projects/pull_requests_controller_spec.rb
new file mode 100644
index 000000000..f18061209
--- /dev/null
+++ b/spec/controllers/projects/pull_requests_controller_spec.rb
@@ -0,0 +1,287 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+shared_context "pull request controller" do
+ after { FileUtils.rm_rf File.join(Rails.root, "tmp", Rails.env, "pull_requests") }
+ before do
+ FileUtils.rm_rf(APP_CONFIG['root_path'])
+ stub_symlink_methods
+
+ @project = FactoryGirl.create(:project)
+ %x(cp -Rf #{Rails.root}/spec/tests.git/* #{@project.path})
+
+ @pull = @project.pull_requests.new :issue_attributes => {:title => 'test', :body => 'testing'}
+ @pull.issue.user, @pull.issue.project = @project.owner, @pull.to_project
+ @pull.to_ref = 'master'
+ @pull.from_project, @pull.from_ref = @project, 'non_conflicts'
+ @pull.save
+
+ @create_params = {
+ :pull_request => {:issue_attributes => {:title => 'create', :body => 'creating'},
+ :to_ref => 'non_conflicts',
+ :from_ref => 'master'},
+ :to_project => @project.name_with_owner,
+ :owner_name => @project.owner.uname,
+ :project_name => @project.name }
+ @update_params = @create_params.merge(
+ :pull_request_action => 'close',
+ :id => @pull.serial_id)
+ @wrong_update_params = @create_params.merge(
+ :pull_request => {:issue_attributes => {:title => 'update', :body => 'updating', :id => @pull.issue.id}},
+ :id => @pull.serial_id)
+
+ @user = FactoryGirl.create(:user)
+ set_session_for(@user)
+ end
+end
+
+shared_examples_for 'pull request user with project guest rights' do
+ it 'should be able to perform index action' do
+ get :index, :owner_name => @project.owner.uname, :project_name => @project.name
+ response.should render_template(:index)
+ end
+
+ it 'should be able to perform show action when pull request has been created' do
+ @pull.check
+ get :show, :owner_name => @project.owner.uname, :project_name => @project.name, :id => @pull.serial_id
+ response.should render_template(:show)
+ end
+end
+
+shared_examples_for 'pull request user with project reader rights' do
+ it 'should be able to perform index action on hidden project' do
+ @project.update_attributes(:visibility => 'hidden')
+ get :index, :owner_name => @project.owner.uname, :project_name => @project.name
+ response.should render_template(:index)
+ end
+
+ it 'should be able to perform create action' do
+ post :create, @create_params
+ response.should redirect_to(project_pull_request_path(@project, @project.pull_requests.last))
+ end
+
+ it 'should create pull request object into db' do
+ lambda{ post :create, @create_params }.should change{ PullRequest.joins(:issue).
+ where(:issues => {:title => 'create', :body => 'creating'}).count }.by(1)
+ end
+
+ it "should not create same pull" do
+ post :create, @create_params.merge({:pull_request => {:issue_attributes => {:title => 'same', :body => 'creating'}, :from_ref => 'non_conflicts', :to_ref => 'master'}, :to_project_id => @project.id})
+ PullRequest.joins(:issue).where(:issues => {:title => 'same', :body => 'creating'}).count.should == 0
+ end
+
+ it "should not create already up-to-date pull" do
+ post :create, @create_params.merge({:pull_request => {:issue_attributes => {:title => 'already', :body => 'creating'}, :to_ref => 'master', :from_ref => 'master'}, :to_project_id => @project.id})
+ PullRequest.joins(:issue).where(:issues => {:title => 'already', :body => 'creating'}).count.should == 0
+ end
+
+ it "should create pull request to the same project" do
+ @parent = FactoryGirl.create(:project)
+ @project.update_attributes({:parent_id => @parent}, :without_protection => true)
+
+ lambda{ post :create, @create_params }.should change{ PullRequest.joins(:issue).
+ where(:issues => {:user_id => @user}, :to_project_id => @project, :from_project_id => @project).count }.by(1)
+ end
+
+ it "should create pull request to the parent project" do
+ @parent = FactoryGirl.create(:project)
+ %x(cp -Rf #{Rails.root}/spec/tests.git/* #{@parent.path})
+ @project.update_attributes({:parent_id => @parent}, :without_protection => true)
+
+ lambda{ post :create, @create_params.merge({:to_project => @parent.name_with_owner}) }.should change{ PullRequest.joins(:issue).
+ where(:issues => {:user_id => @user}, :to_project_id => @parent, :from_project_id => @project).count }.by(1)
+ end
+end
+
+shared_examples_for 'user with pull request update rights' do
+ it 'should be able to perform update action' do
+ put :update, @update_params
+ response.should redirect_to(project_pull_request_path(@pull.to_project, @pull))
+ end
+
+ it 'should be able to perform merge action' do
+ put :merge, @update_params
+ response.should redirect_to(project_pull_request_path(@pull.to_project, @pull))
+ end
+
+ let(:pull) { @project.pull_requests.find(@pull) }
+ it 'should update pull request status' do
+ put :update, @update_params
+ pull.status.should =='closed'
+ end
+
+ it 'should not update pull request title' do
+ put :update, @wrong_update_params
+ pull.issue.title.should =='test'
+ end
+
+ it 'should not update pull request body' do
+ put :update, @wrong_update_params
+ pull.issue.body.should =='testing'
+ end
+
+ it 'should not update pull request title direct' do
+ put :update, @wrong_update_params
+ pull.issue.title.should_not =='update'
+ end
+
+ it 'should not update pull request body direct' do
+ put :update, @wrong_update_params
+ pull.issue.body.should_not =='updating'
+ end
+end
+
+shared_examples_for 'user without pull request update rights' do
+ it 'should not be able to perform update action' do
+ put :update, @update_params
+ response.should redirect_to(controller.current_user ? forbidden_path : new_user_session_path)
+ end
+
+ it 'should not be able to perform merge action' do
+ put :merge, @update_params
+ response.should redirect_to(controller.current_user ? forbidden_path : new_user_session_path)
+ end
+
+ let(:pull) { @project.pull_requests.find(@pull) }
+ it 'should not update pull request status' do
+ put :update, @update_params
+ pull.status.should_not =='closed'
+ end
+ it 'should not update pull request title' do
+ put :update, @wrong_update_params
+ pull.issue.title.should_not =='update'
+ end
+
+ it 'should not update pull request body' do
+ put :update, @wrong_update_params
+ pull.issue.body.should_not =='updating'
+ end
+end
+
+shared_examples_for 'pull request when project with issues turned off' do
+ before { @project.update_attributes(:has_issues => false) }
+ 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 when pull request has been created' do
+ @pull.check
+ get :show, :owner_name => @project.owner.uname, :project_name => @project.name, :id => @pull.serial_id
+ response.should render_template(:show)
+ end
+end
+
+describe Projects::PullRequestsController do
+ include_context "pull request controller"
+
+ context 'for global admin user' do
+ before do
+ @user.role = "admin"
+ @user.save
+ end
+
+ it_should_behave_like 'pull request user with project guest rights'
+ it_should_behave_like 'pull request user with project reader rights'
+ it_should_behave_like 'user with pull request update rights'
+ it_should_behave_like 'pull request when project with issues turned off'
+ end
+
+ context 'for project admin user' do
+ before do
+ @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin')
+ end
+
+ it_should_behave_like 'pull request user with project guest rights'
+ it_should_behave_like 'pull request user with project reader rights'
+ it_should_behave_like 'user with pull request update rights'
+ it_should_behave_like 'pull request when project with issues turned off'
+ end
+
+ context 'for project owner user' do
+ before do
+ @user = @project.owner
+ set_session_for(@user)
+ end
+
+ it_should_behave_like 'pull request user with project guest rights'
+ it_should_behave_like 'pull request user with project reader rights'
+ it_should_behave_like 'user with pull request update rights'
+ it_should_behave_like 'pull request when project with issues turned off'
+ end
+
+ context 'for project reader user' do
+ before do
+ @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'reader')
+ end
+
+ it_should_behave_like 'pull request user with project guest rights'
+ it_should_behave_like 'pull request user with project reader rights'
+ it_should_behave_like 'user without pull request update rights'
+ it_should_behave_like 'pull request when project with issues turned off'
+ end
+
+ context 'for project writer user' do
+ before do
+ @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'writer')
+ end
+
+ it_should_behave_like 'pull request user with project guest rights'
+ it_should_behave_like 'pull request user with project reader rights'
+ it_should_behave_like 'user without pull request update rights'
+ it_should_behave_like 'pull request when project with issues turned off'
+ end
+
+=begin
+ context 'for pull request assign user' do
+ before do
+ set_session_for(@pull request_user)
+ end
+
+ it_should_behave_like 'user without pull request update rights'
+ it_should_behave_like 'pull request when project with issues turned off'
+ end
+=end
+
+ context 'for guest' do
+ let(:guest) { User.new }
+ before do
+ set_session_for(guest)
+ end
+
+ if APP_CONFIG['anonymous_access']
+
+ it_should_behave_like 'pull request user with project guest rights'
+ it_should_behave_like 'pull request when project with issues turned off'
+
+ else
+ it 'should not be able to perform index action' do
+ get :index, :owner_name => @project.owner.uname, :project_name => @project.name
+ response.should redirect_to(new_user_session_path)
+ end
+
+ it 'should not be able to perform show action' do
+ @pull.check
+ get :show, :owner_name => @project.owner.uname, :project_name => @project.name, :id => @pull.serial_id
+ response.should redirect_to(new_user_session_path)
+ end
+
+ it 'should not be able to perform index action on hidden project' do
+ @project.update_attributes(:visibility => 'hidden')
+ get :index, :owner_name => @project.owner.uname, :project_name => @project.name
+ response.should redirect_to(new_user_session_path)
+ end
+ end
+
+ it 'should not be able to perform create action' do
+ post :create, @create_params
+ response.should redirect_to(new_user_session_path)
+ end
+
+ it 'should not create pull request object into db' do
+ lambda{ post :create, @create_params }.should_not change{ PullRequest.count }
+ end
+
+ it_should_behave_like 'user without pull request update rights'
+ end
+end
diff --git a/spec/controllers/users/profile_controller_spec.rb b/spec/controllers/users/profile_controller_spec.rb
index 76617cf8d..ba77ba243 100644
--- a/spec/controllers/users/profile_controller_spec.rb
+++ b/spec/controllers/users/profile_controller_spec.rb
@@ -15,7 +15,11 @@ describe Users::ProfileController do
end
context 'for guest' do
- it 'should not be able to view profile' do
+ it 'should be able to view profile', :anonymous_access => true do
+ get :show, :uname => @simple_user.uname
+ response.code.should eq('200')
+ end
+ it 'should not be able to perform show action', :anonymous_access => false do
get :show, :uname => @simple_user.uname
response.should redirect_to(new_user_session_path)
end
diff --git a/spec/factories/advisories.rb b/spec/factories/advisories.rb
new file mode 100644
index 000000000..11094106a
--- /dev/null
+++ b/spec/factories/advisories.rb
@@ -0,0 +1,7 @@
+# -*- encoding : utf-8 -*-
+FactoryGirl.define do
+ factory :advisory do
+ description { FactoryGirl.generate(:string) }
+ update_type 'security'
+ end
+end
diff --git a/spec/factories/build_lists.rb b/spec/factories/build_lists.rb
index 5f2b0d574..498e106f7 100644
--- a/spec/factories/build_lists.rb
+++ b/spec/factories/build_lists.rb
@@ -2,22 +2,27 @@
FactoryGirl.define do
factory :build_list do
association :user
- association :project
+ #association :project
association :save_to_platform, :factory => :platform_with_repos
+ project { |bl| FactoryGirl.create(:project, :repositories => [bl.save_to_platform.repositories.first]) }
association :arch
build_for_platform {|bl| bl.save_to_platform}
save_to_repository {|bl| bl.save_to_platform.repositories.first}
- project_version "1.0"
build_requires true
update_type 'security'
include_repos {|bl| bl.save_to_platform.repositories.map(&:id)}
- commit_hash '1234567890abcdef1234567890abcdef12345678'
+ project_version 'latest_master'
+ after(:build) {|bl| test_git_commit bl.project }
end
factory :build_list_core, :parent => :build_list do
bs_id { FactoryGirl.generate(:integer) }
end
+ factory :build_list_by_group_project, :parent => :build_list_core do
+ project { |bl| FactoryGirl.create(:group_project, :repositories => [bl.save_to_platform.repositories.first]) }
+ end
+
factory :build_list_package, :class => BuildList::Package do
association :build_list
association :project
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index f82a8e20d..2c7a40af4 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -5,4 +5,8 @@ FactoryGirl.define do
name { FactoryGirl.generate(:unixname) }
association :owner, :factory => :user
end
+
+ factory :group_project, :parent => :project do
+ association :owner, :factory => :group
+ end
end
diff --git a/spec/factories/pull_request.rb b/spec/factories/pull_request.rb
new file mode 100644
index 000000000..f016b8a6a
--- /dev/null
+++ b/spec/factories/pull_request.rb
@@ -0,0 +1,10 @@
+# -*- encoding : utf-8 -*-
+FactoryGirl.define do
+ factory :pull_request do
+ title { FactoryGirl.generate(:string) }
+ body { FactoryGirl.generate(:string) }
+ association :project, :factory => :project
+ association :user, :factory => :user
+ association :assignee, :factory => :user
+ end
+end
diff --git a/spec/models/build_list_observer_spec.rb b/spec/models/build_list_observer_spec.rb
index 8cd2ba032..78ea54d44 100644
--- a/spec/models/build_list_observer_spec.rb
+++ b/spec/models/build_list_observer_spec.rb
@@ -1,94 +1,5 @@
require 'spec_helper'
describe BuildListObserver do
-
- context "notify users" do
- let!(:user) { FactoryGirl.create(:user) }
- let!(:build_list) { FactoryGirl.create(:build_list, :user => user, :auto_publish => false) }
-
- before(:all) { ActionMailer::Base.deliveries = [] }
- before { build_list.update_attribute(:status, BuildServer::BUILD_STARTED) }
- after { ActionMailer::Base.deliveries = [] }
-
- shared_examples_for 'build list notifications by email' do
- it "gets notification by email when status - Build complete" do
- build_list.update_attribute(:status, BuildServer::SUCCESS)
- should have(1).item
- end
-
- it "gets notification by email when status - Build published" do
- build_list.update_attribute(:status, BuildList::BUILD_PUBLISHED)
- should have(1).item
- end
-
- it "gets notification by email when auto_publish and status - Build published" do
- build_list.update_attributes(:auto_publish => true, :status => BuildList::BUILD_PUBLISHED)
- should have(1).item
- end
-
- it "doesn't get notification by email when auto_publish and status - Build complete" do
- build_list.update_attributes(:auto_publish => true, :status => BuildServer::SUCCESS)
- should have(:no).items
- end
-
- it "doesn't get notification by email when mass build" do
- build_list.update_attributes(:mass_build_id => 1, :status => BuildList::BUILD_PUBLISHED)
- should have(:no).items
- end
-
- it "doesn't get notification by email when notification by email has been disabled" do
- notifier.update_attribute(:can_notify, false)
- build_list.update_attribute(:status, BuildServer::SUCCESS)
- should have(:no).items
- end
- end
-
- subject { ActionMailer::Base.deliveries }
-
- context "user created build task" do
- let!(:notifier) { user.notifier }
- before do
- notifier.update_attribute(:new_associated_build, false)
- build_list.project.owner.notifier.update_attribute(:can_notify, false)
- end
-
- it_should_behave_like 'build list notifications by email'
-
- it "doesn't get notification by email when 'build list' notifications has been disabled" do
- notifier.update_attribute(:new_build, false)
- build_list.update_attribute(:status, BuildServer::SUCCESS)
- should have(:no).items
- end
-
- it "doesn't get notification by email when 'build list' notifications - enabled, email notifications - disabled" do
- notifier.update_attributes(:can_notify => false, :new_build => true)
- build_list.update_attribute(:status, BuildServer::SUCCESS)
- should have(:no).items
- end
- end
-
- context "build task has been created and associated user" do
- let!(:notifier) { build_list.project.owner.notifier }
- before do
- notifier.update_attribute(:new_build, false)
- user.notifier.update_attribute(:can_notify, false)
- end
-
- it_should_behave_like 'build list notifications by email'
-
- it "doesn't get notification by email when 'associated build list' notifications has been disabled" do
- notifier.update_attribute(:new_associated_build, false)
- build_list.update_attribute(:status, BuildServer::SUCCESS)
- should have(:no).items
- end
-
- it "doesn't get notification by email when 'associated build list' notifications - enabled, email notifications - disabled" do
- notifier.update_attributes(:can_notify => false, :new_associated_build => true)
- build_list.update_attribute(:status, BuildServer::SUCCESS)
- should have(:no).items
- end
- end
-
- end # notify users
-
+ pending "add some examples to (or delete) #{__FILE__}"
end
diff --git a/spec/models/build_list_spec.rb b/spec/models/build_list_spec.rb
new file mode 100644
index 000000000..421e39726
--- /dev/null
+++ b/spec/models/build_list_spec.rb
@@ -0,0 +1,146 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+describe BuildList do
+
+ context "#notify_users" do
+ before { stub_symlink_methods }
+ let!(:user) { FactoryGirl.create(:user) }
+ let!(:build_list) { FactoryGirl.create(:build_list_core,
+ :user => user,
+ :auto_publish => false) }
+ let!(:build_list_package) { FactoryGirl.create(:build_list_package,
+ :build_list => build_list,
+ :project => build_list.project) }
+
+
+ before(:all) { ActionMailer::Base.deliveries = [] }
+ before do
+ test_git_commit(build_list.project)
+ build_list.update_attributes(:commit_hash => build_list.project.repo.commits('master').last.id,
+ :status => BuildServer::BUILD_STARTED,)
+ end
+ after { ActionMailer::Base.deliveries = [] }
+
+ shared_examples_for 'build list notifications by email' do
+ it "gets notification by email when status - Build complete" do
+ build_list.build_success
+ should have(1).item
+ end
+
+ it "gets notification by email when status - Build error" do
+ build_list.build_error
+ should have(1).item
+ end
+
+ it "gets notification by email when auto_publish and status - Build error" do
+ build_list.update_attributes(:auto_publish => true)
+ build_list.build_error
+ should have(1).item
+ end
+
+ it "gets notification by email when status - Failed publish" do
+ build_list.update_attributes(:status => BuildList::BUILD_PUBLISH)
+ build_list.fail_publish
+ should have(1).item
+ end
+
+ it "gets notification by email when auto_publish and status - Failed publish" do
+ build_list.update_attributes(:auto_publish => true, :status => BuildList::BUILD_PUBLISH)
+ build_list.fail_publish
+ should have(1).item
+ end
+
+ it "gets notification by email when status - Build published" do
+ build_list.update_attributes(:status => BuildList::BUILD_PUBLISH)
+ build_list.published
+ should have(1).item
+ end
+
+ it "gets notification by email when auto_publish and status - Build published" do
+ build_list.update_attributes(:auto_publish => true, :status => BuildList::BUILD_PUBLISH)
+ build_list.published
+ should have(1).item
+ end
+
+ it "doesn't get notification by email when auto_publish and status - Build complete" do
+ build_list.update_attributes(:auto_publish => true)
+ build_list.build_success
+ should have(:no).items
+ end
+
+ it "doesn't get notification by email when mass build" do
+ build_list.update_attributes(:mass_build_id => 1, :status => BuildList::BUILD_PUBLISH)
+ build_list.published
+ should have(:no).items
+ end
+
+ it "doesn't get notification by email when notification by email has been disabled" do
+ notifier.update_attributes(:can_notify => false)
+ build_list.build_success
+ should have(:no).items
+ end
+ end
+
+ subject { ActionMailer::Base.deliveries }
+
+ context "user created build task" do
+ let!(:notifier) { user.notifier }
+ before do
+ notifier.update_attributes(:new_associated_build => false)
+ build_list.project.owner.notifier.update_attributes(:can_notify => false)
+ end
+
+ it_should_behave_like 'build list notifications by email'
+
+ it "doesn't get notification by email when 'build list' notifications has been disabled" do
+ notifier.update_attributes(:new_build => false)
+ build_list.build_success
+ should have(:no).items
+ end
+
+ it "doesn't get notification by email when 'build list' notifications - enabled, email notifications - disabled" do
+ notifier.update_attributes(:can_notify => false, :new_build => true)
+ build_list.build_success
+ should have(:no).items
+ end
+ end
+
+ context "build task has been created and associated user" do
+ let!(:notifier) { build_list.project.owner.notifier }
+ before do
+ notifier.update_attributes(:new_build => false)
+ user.notifier.update_attributes(:can_notify => false)
+ end
+
+ it_should_behave_like 'build list notifications by email'
+
+ it "doesn't get notification by email when 'associated build list' notifications has been disabled" do
+ notifier.update_attributes(:new_associated_build => false)
+ build_list.build_success
+ should have(:no).items
+ end
+
+ it "doesn't get notification by email when 'associated build list' notifications - enabled, email notifications - disabled" do
+ notifier.update_attributes(:can_notify => false, :new_associated_build => true)
+ build_list.build_success
+ should have(:no).items
+ end
+ end
+
+ it "doesn't get 2 notification by email when user associated to project and created task" do
+ bl = FactoryGirl.create(:build_list_core,
+ :user => user,
+ :auto_publish => true,
+ :project => FactoryGirl.create(:project, :owner => user))
+ FactoryGirl.create(:build_list_package, :build_list => bl, :project => bl.project)
+ test_git_commit(bl.project)
+ bl.update_attributes(:commit_hash => bl.project.repo.commits('master').last.id,
+ :status => BuildList::BUILD_PUBLISH)
+ bl.published
+ should have(1).item
+ end
+
+ end # notify_users
+
+end
diff --git a/spec/models/cancan_spec.rb b/spec/models/cancan_spec.rb
index 5f0ba821a..cda03535f 100644
--- a/spec/models/cancan_spec.rb
+++ b/spec/models/cancan_spec.rb
@@ -290,26 +290,6 @@ describe CanCan do
@ability.should be_able_to(:read, @repository)
end
end
- end
-
- context 'build list relations' do
- before(:each) do
- @project = FactoryGirl.create(:project)
- @project.relations.create!(:actor_id => @user.id, :actor_type => 'User', :role => 'writer')
- @build_list = FactoryGirl.create(:build_list, :project => @project)
- end
-
- it 'should be able to publish build list with SUCCESS status' do
- @build_list.status = BuildServer::SUCCESS
- @ability.should be_able_to(:publish, @build_list)
- end
-
- it 'should not be able to publish build list with another status' do
- @build_list.status = BuildServer::BUILD_ERROR
- @ability.should_not be_able_to(:publish, @build_list)
- end
- end
- end
-
-
+ end # 'repository relations'
+ end # 'Site user'
end
diff --git a/spec/models/comment_for_commit_spec.rb b/spec/models/comment_for_commit_spec.rb
index 4b6ce9875..c27479215 100644
--- a/spec/models/comment_for_commit_spec.rb
+++ b/spec/models/comment_for_commit_spec.rb
@@ -148,10 +148,10 @@ describe Comment do
@user = FactoryGirl.create(:user)
@stranger = FactoryGirl.create(:user)
set_comments_data_for_commit
-
+
@project.owner = @user
@project.save
-
+
ActionMailer::Base.deliveries = []
end
@@ -301,7 +301,7 @@ describe Comment do
context 'for committer' do
it 'should send an e-mail' do
- @simple.update_column :email, 'code@tpope.net'
+ @simple.update_column :email, 'test@test.test'
comment = create_comment(@user)
ActionMailer::Base.deliveries.count.should == 1
ActionMailer::Base.deliveries.last.to.include?(@simple.email).should == true
@@ -309,21 +309,21 @@ describe Comment do
it 'should send a one e-mail when subscribed to commit' do
Subscribe.subscribe_to_commit @subscribe_params.merge(:user_id => @simple.id)
- @simple.update_column :email, 'code@tpope.net'
+ @simple.update_column :email, 'test@test.test'
comment = create_comment(@user)
ActionMailer::Base.deliveries.count.should == 1
ActionMailer::Base.deliveries.last.to.include?(@simple.email).should == true
end
it 'should not send an e-mail for own comment' do
- @simple.update_column :email, 'code@tpope.net'
+ @simple.update_column :email, 'test@test.test'
comment = create_comment(@simple)
ActionMailer::Base.deliveries.count.should == 0
end
it 'should not send an e-mail if global notify off' do
@project.owner.notifier.update_column :can_notify, false
- @simple.update_column :email, 'code@tpope.net'
+ @simple.update_column :email, 'test@test.test'
@simple.notifier.update_column :can_notify, false
comment = create_comment(@user)
ActionMailer::Base.deliveries.count.should == 0
@@ -332,7 +332,7 @@ describe Comment do
it 'should not send an e-mail if notify for my commits off' do
Comment.destroy_all
@simple.notifier.update_column :new_comment_commit_owner, false
- @simple.update_column :email, 'code@tpope.net'
+ @simple.update_column :email, 'test@test.test'
comment = create_comment(@user)
ActionMailer::Base.deliveries.count.should == 0
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index e1bede7bf..c843b2ab6 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -10,7 +10,7 @@ describe Group do
end
context 'for guest' do
- [:read, :update, :destroy, :manage_members, :autocomplete_group_uname].each do |action|
+ [:read, :update, :destroy, :manage_members].each do |action|
it "should not be able to #{action} group" do
@ability.should_not be_able_to(action, @group)
end
@@ -23,7 +23,7 @@ describe Group do
@ability = Ability.new(@admin)
end
- [:read, :update, :destroy, :manage_members, :autocomplete_group_uname].each do |action|
+ [:read, :update, :destroy, :manage_members].each do |action|
it "should be able to #{action} group" do
@ability.should be_able_to(action, @group)
end
@@ -38,7 +38,7 @@ describe Group do
@ability = Ability.new(@user)
end
- [:read, :update, :manage_members, :autocomplete_group_uname].each do |action|
+ [:read, :update, :manage_members].each do |action|
it "should be able to #{action} group" do
@ability.should be_able_to(action, @group)
end
@@ -70,7 +70,7 @@ describe Group do
@ability = Ability.new(@user)
end
- [:read, :update, :destroy, :manage_members, :autocomplete_group_uname].each do |action|
+ [:read, :update, :destroy, :manage_members].each do |action|
it "should be able to #{action} group" do
@ability.should be_able_to(action, @group)
end
@@ -84,7 +84,7 @@ describe Group do
@ability = Ability.new(@user)
end
- [:read, :autocomplete_group_uname].each do |action|
+ [:read].each do |action|
it "should be able to #{action} group" do
@ability.should be_able_to(action, @group)
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index ab13f6ae3..c2892dec4 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -9,17 +9,35 @@ describe Project do
@child_child_project = @child_project.fork(FactoryGirl.create(:user))
end
- context 'for destroy root' do
- before do
- @root_project.destroy
+ context 'for destroy' do
+ let!(:root_project) { FactoryGirl.create(:project) }
+ let!(:child_project) { root_project.fork(FactoryGirl.create(:user)) }
+ let!(:child_child_project) { child_project.fork(FactoryGirl.create(:user)) }
+
+ context 'root project' do
+ before { root_project.destroy }
+
+ it "should not be delete child" do
+ Project.where(:id => child_project).count.should == 1
+ end
+
+ it "should not be delete child of the child" do
+ Project.where(:id => child_child_project).count.should == 1
+ end
end
- it "should not be delete child" do
- Project.where(:id => @child_project).count.should == 1
- end
+ pending 'when will be available :orphan_strategy => :adopt' do
+ context 'middle node' do
+ before{ child_project.destroy }
- it "should not be delete child of the child" do
- Project.where(:id => @child_child_project).count.should == 1
+ it "should set root project as a parent for orphan child" do
+ Project.find(child_child_project).ancestry == root_project
+ end
+
+ it "should not be delete child of the child" do
+ Project.where(:id => child_child_project).count.should == 1
+ end
+ end
end
end
@@ -48,19 +66,17 @@ describe Project do
end
end
- # uncommit when will be available :orphan_strategy => :adopt
+ context 'truncates project name before validation' do
+ let!(:project) { FactoryGirl.build(:project, :name => ' test_name ') }
- #context 'for destroy middle node' do
- # before(:each) do
- # @child_project.destroy
- # end
+ it 'ensures that validation passed' do
+ project.valid?.should be_true
+ end
+
+ it 'ensures that name has been truncated' do
+ project.valid?
+ project.name.should == 'test_name'
+ end
+ end
- # it "should set root project as a parent for orphan child" do
- # Project.find(@child_child_project).ancestry == @root_project
- # end
-
- # it "should not be delete child of the child" do
- # Project.where(:id => @child_child_project).count.should == 1
- # end
- #end
end
diff --git a/spec/models/pull_request_spec.rb b/spec/models/pull_request_spec.rb
new file mode 100644
index 000000000..617f365d5
--- /dev/null
+++ b/spec/models/pull_request_spec.rb
@@ -0,0 +1,115 @@
+# -*- encoding : utf-8 -*-
+require 'spec_helper'
+
+def set_data_for_pull
+ @ability = Ability.new(@user)
+
+ @project = FactoryGirl.create(:project, :owner => @user)
+ %x(cp -Rf #{Rails.root}/spec/tests.git/* #{@project.path})
+
+ @clone_path = File.join(APP_CONFIG['root_path'], 'repo_clone', @project.id.to_s)
+ FileUtils.mkdir_p(@clone_path)
+
+ @other_project = FactoryGirl.create(:project, :owner => @user)
+ %x(cp -Rf #{Rails.root}/spec/tests.git/* #{@other_project.path})
+
+ any_instance_of(Project, :versions => ['v1.0', 'v2.0'])
+end
+
+describe PullRequest do
+
+ context 'for owner user' do
+ before do
+ stub_symlink_methods
+ @user = FactoryGirl.create(:user)
+ set_data_for_pull
+ @pull = @project.pull_requests.new(:issue_attributes => {:title => 'test', :body => 'testing'})
+ @pull.issue.user, @pull.issue.project = @user, @pull.to_project
+ @pull.to_ref = 'master'
+ @pull.from_project, @pull.from_ref = @project, 'non_conflicts'
+ @pull.save
+
+ @other_pull = @project.pull_requests.new(:issue_attributes => {:title => 'test_other', :body => 'testing_other'})
+ @other_pull.issue.user, @other_pull.issue.project = @user, @other_pull.to_project
+ @other_pull.to_ref = 'master'
+ @other_pull.from_project, @other_pull.from_ref = @other_project, 'non_conflicts'
+ @other_pull.save
+ end
+
+ it 'master should merge with non_conflicts branch' do
+ @pull.check
+ @pull.status.should == 'ready'
+ end
+
+ it 'master should not merge with conflicts branch' do
+ @pull.from_ref = 'conflicts'
+ @pull.check
+ @pull.status.should == 'blocked'
+ end
+
+ it 'should already merged when already up-to-date branches' do
+ @pull.from_ref = 'master'
+ @pull.check
+ @pull.status.should == 'merged'
+ end
+
+ context 'for other head project' do
+ it 'master should merge with non_conflicts branch' do
+ @other_pull.check
+ @other_pull.status.should == 'ready'
+ end
+
+ it 'master should not merge with conflicts branch' do
+ @other_pull.from_ref = 'conflicts'
+ @other_pull.check
+ @other_pull.status.should == 'blocked'
+ end
+
+ it 'should already merged when already up-to-date branches' do
+ @other_pull.from_ref = 'master'
+ @other_pull.check
+ @other_pull.status.should == 'merged'
+ end
+ end
+
+ it "should not create same pull" do
+ @same_pull = @project.pull_requests.new(:issue_attributes => {:title => 'same', :body => 'testing'})
+ @same_pull.issue.user, @same_pull.issue.project = @user, @same_pull.to_project
+ @same_pull.to_ref = 'master'
+ @same_pull.from_project, @same_pull.from_ref = @project, 'non_conflicts'
+ @same_pull.save
+ @project.pull_requests.joins(:issue).where(:issues => {:title => @same_pull.title}).count.should == 0
+ end
+
+ it "should not create pull with wrong base ref" do
+ @wrong_pull = @project.pull_requests.new(:issue_attributes => {:title => 'wrong base', :body => 'testing'})
+ @wrong_pull.issue.user, @wrong_pull.issue.project = @user, @wrong_pull.to_project
+ @wrong_pull.to_ref = 'wrong'
+ @wrong_pull.from_project, @wrong_pull.from_ref = @project, 'non_conflicts'
+ @wrong_pull.save
+ @project.pull_requests.joins(:issue).where(:issues => {:title => @wrong_pull.title}).count.should == 0
+ end
+
+ it "should not create pull with wrong head ref" do
+ @wrong_pull = @project.pull_requests.new(:issue_attributes => {:title => 'wrong head', :body => 'testing'})
+ @wrong_pull.issue.user, @wrong_pull.issue.project = @user, @wrong_pull.to_project
+ @wrong_pull.to_ref = 'master'
+ @wrong_pull.from_project, @wrong_pull.from_ref = @project, 'wrong'
+ @wrong_pull.save
+ @project.pull_requests.joins(:issue).where(:issues => {:title => @wrong_pull.title}).count.should == 0
+ end
+ end
+
+ before do
+ FileUtils.rm_rf(APP_CONFIG['root_path'])
+ end
+
+ it { should belong_to(:issue) }
+ it { should belong_to(:to_project) }
+ it { should belong_to(:from_project) }
+
+ after do
+ FileUtils.rm_rf(APP_CONFIG['root_path'])
+ FileUtils.rm_rf File.join(Rails.root, "tmp", Rails.env, "pull_requests")
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index e7ce00299..e168084b3 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -27,7 +27,7 @@ RSpec.configure do |config|
config.use_transactional_fixtures = true
config.filter_run_excluding :anonymous_access => !(APP_CONFIG['anonymous_access'])
-
+
end
def set_session_for(user=nil)
@@ -36,6 +36,11 @@ def set_session_for(user=nil)
sign_in current_user
end
+def http_login(user=nil)
+ # FIXME: password constant is a bad choice...
+ request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user.email,'123456')
+end
+
def stub_symlink_methods
any_instance_of(Platform, :symlink_directory => true)
any_instance_of(Platform, :remove_symlink_directory => true)
diff --git a/spec/tests.git/HEAD b/spec/tests.git/HEAD
index df9ee5473..cb089cd89 100644
--- a/spec/tests.git/HEAD
+++ b/spec/tests.git/HEAD
@@ -1 +1 @@
-bdc8b580b5b583aeb43efb19aac2ab8ce5566dff
+ref: refs/heads/master
diff --git a/spec/tests.git/config b/spec/tests.git/config
index d38f8c6fe..07d359d07 100644
--- a/spec/tests.git/config
+++ b/spec/tests.git/config
@@ -1,11 +1,4 @@
[core]
repositoryformatversion = 0
filemode = true
- bare = false
- logallrefupdates = true
-[remote "origin"]
- fetch = +refs/heads/*:refs/remotes/origin/*
- url = git://github.com/tpope/vim-ragtag.git
-[branch "master"]
- remote = origin
- merge = refs/heads/master
+ bare = true
diff --git a/spec/tests.git/index b/spec/tests.git/index
deleted file mode 100644
index 29cca27d4..000000000
Binary files a/spec/tests.git/index and /dev/null differ
diff --git a/spec/tests.git/info/refs b/spec/tests.git/info/refs
new file mode 100644
index 000000000..0e0a7c627
--- /dev/null
+++ b/spec/tests.git/info/refs
@@ -0,0 +1,5 @@
+5dc2326fab1022c9c35b27d09c62a73216fdff5e refs/heads/conficts
+45d6bc11f4de8546ccbef16fdd3acf48fe0b411c refs/heads/conflicts
+2babca2e12fe8e5f0ea95e8547a9ce20c322cb79 refs/heads/master
+5c2312c4054bae9c1419dbec8633eda29f7c0731 refs/heads/non_conflicts
+00a2f9225dcc5807b9eb46f1bcfc56ee939e595f refs/heads/not_conflicts
diff --git a/spec/tests.git/logs/HEAD b/spec/tests.git/logs/HEAD
deleted file mode 100644
index f47abecdc..000000000
--- a/spec/tests.git/logs/HEAD
+++ /dev/null
@@ -1,2 +0,0 @@
-0000000000000000000000000000000000000000 bdc8b580b5b583aeb43efb19aac2ab8ce5566dff Alexander 1325695134 +0600 clone: from git://github.com/tpope/vim-ragtag.git
-bdc8b580b5b583aeb43efb19aac2ab8ce5566dff bdc8b580b5b583aeb43efb19aac2ab8ce5566dff Alexander 1325695134 +0600 checkout: moving from master to bdc8b580b5b583aeb43efb19aac2ab8ce5566dff
diff --git a/spec/tests.git/logs/refs/heads/master b/spec/tests.git/logs/refs/heads/master
deleted file mode 100644
index 9219de536..000000000
--- a/spec/tests.git/logs/refs/heads/master
+++ /dev/null
@@ -1 +0,0 @@
-0000000000000000000000000000000000000000 bdc8b580b5b583aeb43efb19aac2ab8ce5566dff Alexander 1325695134 +0600 clone: from git://github.com/tpope/vim-ragtag.git
diff --git a/spec/tests.git/objects/3b/efc54229f8e25165be593e67f33fb9a8495374 b/spec/tests.git/objects/3b/efc54229f8e25165be593e67f33fb9a8495374
new file mode 100644
index 000000000..e76437b56
--- /dev/null
+++ b/spec/tests.git/objects/3b/efc54229f8e25165be593e67f33fb9a8495374
@@ -0,0 +1,2 @@
+xM
+0a9L22I&E[im=g_]y@-\ZE+3C$+:M,(')=|!lD
h3^C9\Ftpŀhп3-0~\>y
\ No newline at end of file
diff --git a/spec/tests.git/objects/info/packs b/spec/tests.git/objects/info/packs
new file mode 100644
index 000000000..870d85fea
--- /dev/null
+++ b/spec/tests.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-09ad35f874bea18d5f133a59210e4f80a1e81c17.pack
+
diff --git a/spec/tests.git/objects/pack/pack-09ad35f874bea18d5f133a59210e4f80a1e81c17.idx b/spec/tests.git/objects/pack/pack-09ad35f874bea18d5f133a59210e4f80a1e81c17.idx
new file mode 100644
index 000000000..6cf3769c5
Binary files /dev/null and b/spec/tests.git/objects/pack/pack-09ad35f874bea18d5f133a59210e4f80a1e81c17.idx differ
diff --git a/spec/tests.git/objects/pack/pack-09ad35f874bea18d5f133a59210e4f80a1e81c17.pack b/spec/tests.git/objects/pack/pack-09ad35f874bea18d5f133a59210e4f80a1e81c17.pack
new file mode 100644
index 000000000..974873a7b
Binary files /dev/null and b/spec/tests.git/objects/pack/pack-09ad35f874bea18d5f133a59210e4f80a1e81c17.pack differ
diff --git a/spec/tests.git/objects/pack/pack-5185f74f028bf49d2611c9fea56570138a196143.idx b/spec/tests.git/objects/pack/pack-5185f74f028bf49d2611c9fea56570138a196143.idx
deleted file mode 100644
index 46bd6036d..000000000
Binary files a/spec/tests.git/objects/pack/pack-5185f74f028bf49d2611c9fea56570138a196143.idx and /dev/null differ
diff --git a/spec/tests.git/objects/pack/pack-5185f74f028bf49d2611c9fea56570138a196143.pack b/spec/tests.git/objects/pack/pack-5185f74f028bf49d2611c9fea56570138a196143.pack
deleted file mode 100644
index 593982972..000000000
Binary files a/spec/tests.git/objects/pack/pack-5185f74f028bf49d2611c9fea56570138a196143.pack and /dev/null differ
diff --git a/spec/tests.git/packed-refs b/spec/tests.git/packed-refs
index b48a6248c..130291d58 100644
--- a/spec/tests.git/packed-refs
+++ b/spec/tests.git/packed-refs
@@ -1,5 +1,3 @@
-# pack-refs with: peeled
-30aefeac002db3ec08ff278bd76290645469611e refs/tags/v2.0
-^644c62ad7bc7d9a4a5f19f5e8c41ef910782178b
-235e4467107467feacc50553bbeda15e9bf99f57 refs/tags/v1.11
-bdc8b580b5b583aeb43efb19aac2ab8ce5566dff refs/remotes/origin/master
+45d6bc11f4de8546ccbef16fdd3acf48fe0b411c refs/heads/conflicts
+2babca2e12fe8e5f0ea95e8547a9ce20c322cb79 refs/heads/master
+5c2312c4054bae9c1419dbec8633eda29f7c0731 refs/heads/non_conflicts
diff --git a/spec/tests.git/refs/heads/master b/spec/tests.git/refs/heads/master
deleted file mode 100644
index df9ee5473..000000000
--- a/spec/tests.git/refs/heads/master
+++ /dev/null
@@ -1 +0,0 @@
-bdc8b580b5b583aeb43efb19aac2ab8ce5566dff
diff --git a/spec/tests.git/refs/remotes/origin/HEAD b/spec/tests.git/refs/remotes/origin/HEAD
deleted file mode 100644
index 6efe28fff..000000000
--- a/spec/tests.git/refs/remotes/origin/HEAD
+++ /dev/null
@@ -1 +0,0 @@
-ref: refs/remotes/origin/master
diff --git a/vendor/assets/javascripts/bootstrap-tab.js b/vendor/assets/javascripts/bootstrap-tab.js
new file mode 100644
index 000000000..a26da9b6c
--- /dev/null
+++ b/vendor/assets/javascripts/bootstrap-tab.js
@@ -0,0 +1,130 @@
+/* ========================================================
+ * bootstrap-tab.js v2.0.2
+ * http://twitter.github.com/bootstrap/javascript.html#tabs
+ * ========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================== */
+
+
+!function( $ ){
+
+ "use strict"
+
+ /* TAB CLASS DEFINITION
+ * ==================== */
+
+ var Tab = function ( element ) {
+ this.element = $(element)
+ }
+
+ Tab.prototype = {
+
+ constructor: Tab
+
+ , show: function () {
+ var $this = this.element
+ , $ul = $this.closest('ul:not(.dropdown-menu)')
+ , selector = $this.attr('data-target')
+ , previous
+ , $target
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ if ( $this.parent('li').hasClass('active') ) return
+
+ previous = $ul.find('.active a').last()[0]
+
+ $this.trigger({
+ type: 'show'
+ , relatedTarget: previous
+ })
+
+ $target = $(selector)
+
+ this.activate($this.parent('li'), $ul)
+ this.activate($target, $target.parent(), function () {
+ $this.trigger({
+ type: 'shown'
+ , relatedTarget: previous
+ })
+ })
+ }
+
+ , activate: function ( element, container, callback) {
+ var $active = container.find('> .active')
+ , transition = callback
+ && $.support.transition
+ && $active.hasClass('fade')
+
+ function next() {
+ $active
+ .removeClass('active')
+ .find('> .dropdown-menu > .active')
+ .removeClass('active')
+
+ element.addClass('active')
+
+ if (transition) {
+ element[0].offsetWidth // reflow for transition
+ element.addClass('in')
+ } else {
+ element.removeClass('fade')
+ }
+
+ if ( element.parent('.dropdown-menu') ) {
+ element.closest('li.dropdown').addClass('active')
+ }
+
+ callback && callback()
+ }
+
+ transition ?
+ $active.one($.support.transition.end, next) :
+ next()
+
+ $active.removeClass('in')
+ }
+ }
+
+
+ /* TAB PLUGIN DEFINITION
+ * ===================== */
+
+ $.fn.tab = function ( option ) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('tab')
+ if (!data) $this.data('tab', (data = new Tab(this)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.tab.Constructor = Tab
+
+
+ /* TAB DATA-API
+ * ============ */
+
+ $(function () {
+ $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
+ e.preventDefault()
+ $(this).tab('show')
+ })
+ })
+
+}( window.jQuery );
diff --git a/vendor/assets/javascripts/vendor.js b/vendor/assets/javascripts/vendor.js
index a4d1ebcfb..08fc17f77 100644
--- a/vendor/assets/javascripts/vendor.js
+++ b/vendor/assets/javascripts/vendor.js
@@ -10,9 +10,11 @@
//= require bootstrap-modal
//= require bootstrap-button
//= require bootstrap-dropdown
+//= require bootstrap-tab
// require bootstrap-tooltip
// require bootstrap-popover
//= require bootstrap-alert
+//= require bootstrap-tab
//= require chosen.jquery
// require html5shiv
// require_tree .
diff --git a/vendor/assets/stylesheets/bootstrap.css b/vendor/assets/stylesheets/bootstrap.css
index f43391083..f5b1a58bc 100644
--- a/vendor/assets/stylesheets/bootstrap.css
+++ b/vendor/assets/stylesheets/bootstrap.css
@@ -419,4 +419,400 @@
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-}
\ No newline at end of file
+}
+.nav {
+ margin-left: 0;
+ margin-bottom: 20px;
+ list-style: none;
+}
+.nav > li > a {
+ display: block;
+}
+.nav > li > a:hover {
+ text-decoration: none;
+ background-color: #eeeeee;
+}
+.nav > .pull-right {
+ float: right;
+}
+.nav-header {
+ display: block;
+ padding: 3px 15px;
+ font-size: 11px;
+ font-weight: bold;
+ line-height: 20px;
+ color: #999999;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ text-transform: uppercase;
+}
+.nav li + .nav-header {
+ margin-top: 9px;
+}
+.nav-list {
+ padding-left: 15px;
+ padding-right: 15px;
+ margin-bottom: 0;
+}
+.nav-list > li > a,
+.nav-list .nav-header {
+ margin-left: -15px;
+ margin-right: -15px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+}
+.nav-list > li > a {
+ padding: 3px 15px;
+}
+.nav-list > .active > a,
+.nav-list > .active > a:hover {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
+ background-color: #0088cc;
+}
+.nav-list [class^="icon-"] {
+ margin-right: 2px;
+}
+.nav-list .divider {
+ *width: 100%;
+ height: 1px;
+ margin: 9px 1px;
+ *margin: -5px 0 5px;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ border-bottom: 1px solid #ffffff;
+}
+.nav-tabs,
+.nav-pills {
+ *zoom: 1;
+}
+.nav-tabs:before,
+.nav-pills:before,
+.nav-tabs:after,
+.nav-pills:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.nav-tabs:after,
+.nav-pills:after {
+ clear: both;
+}
+.nav-tabs > li,
+.nav-pills > li {
+ float: left;
+}
+.nav-tabs > li > a,
+.nav-pills > li > a {
+ padding-right: 12px;
+ padding-left: 12px;
+ margin-right: 2px;
+ line-height: 14px;
+}
+.nav-tabs {
+ border-bottom: 1px solid #ddd;
+}
+.nav-tabs > li {
+ margin-bottom: -1px;
+}
+.nav-tabs > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ line-height: 20px;
+ border: 1px solid transparent;
+ -webkit-border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+.nav-tabs > li > a:hover {
+ border-color: #eeeeee #eeeeee #dddddd;
+}
+.nav-tabs > .active > a,
+.nav-tabs > .active > a:hover {
+ color: #555555;
+ background-color: #ffffff;
+ border: 1px solid #ddd;
+ border-bottom-color: transparent;
+ cursor: default;
+}
+.nav-pills > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ margin-top: 2px;
+ margin-bottom: 2px;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+.nav-pills > .active > a,
+.nav-pills > .active > a:hover {
+ color: #ffffff;
+ background-color: #0088cc;
+}
+.nav-stacked > li {
+ float: none;
+}
+.nav-stacked > li > a {
+ margin-right: 0;
+}
+.nav-tabs.nav-stacked {
+ border-bottom: 0;
+}
+.nav-tabs.nav-stacked > li > a {
+ border: 1px solid #ddd;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.nav-tabs.nav-stacked > li:first-child > a {
+ -webkit-border-top-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ border-top-right-radius: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+ border-top-left-radius: 4px;
+}
+.nav-tabs.nav-stacked > li:last-child > a {
+ -webkit-border-bottom-right-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ border-bottom-right-radius: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+ border-bottom-left-radius: 4px;
+}
+.nav-tabs.nav-stacked > li > a:hover {
+ border-color: #ddd;
+ z-index: 2;
+}
+.nav-pills.nav-stacked > li > a {
+ margin-bottom: 3px;
+}
+.nav-pills.nav-stacked > li:last-child > a {
+ margin-bottom: 1px;
+}
+.nav-tabs .dropdown-menu {
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+}
+.nav-pills .dropdown-menu {
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+.nav .dropdown-toggle .caret {
+ border-top-color: #0088cc;
+ border-bottom-color: #0088cc;
+ margin-top: 6px;
+}
+.nav .dropdown-toggle:hover .caret {
+ border-top-color: #005580;
+ border-bottom-color: #005580;
+}
+/* move down carets for tabs */
+.nav-tabs .dropdown-toggle .caret {
+ margin-top: 8px;
+}
+.nav .active .dropdown-toggle .caret {
+ border-top-color: #fff;
+ border-bottom-color: #fff;
+}
+.nav-tabs .active .dropdown-toggle .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
+}
+.nav > .dropdown.active > a:hover {
+ cursor: pointer;
+}
+.nav-tabs .open .dropdown-toggle,
+.nav-pills .open .dropdown-toggle,
+.nav > li.dropdown.open.active > a:hover {
+ color: #ffffff;
+ background-color: #999999;
+ border-color: #999999;
+}
+.nav li.dropdown.open .caret,
+.nav li.dropdown.open.active .caret,
+.nav li.dropdown.open a:hover .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+ opacity: 1;
+ filter: alpha(opacity=100);
+}
+.tabs-stacked .open > a:hover {
+ border-color: #999999;
+}
+.tabbable {
+ *zoom: 1;
+}
+.tabbable:before,
+.tabbable:after {
+ display: table;
+ content: "";
+ line-height: 0;
+}
+.tabbable:after {
+ clear: both;
+}
+.tab-content {
+ overflow: auto;
+}
+.tabs-below > .nav-tabs,
+.tabs-right > .nav-tabs,
+.tabs-left > .nav-tabs {
+ border-bottom: 0;
+}
+.tab-content > .tab-pane,
+.pill-content > .pill-pane {
+ display: none;
+}
+.tab-content > .active,
+.pill-content > .active {
+ display: block;
+}
+.tabs-below > .nav-tabs {
+ border-top: 1px solid #ddd;
+}
+.tabs-below > .nav-tabs > li {
+ margin-top: -1px;
+ margin-bottom: 0;
+}
+.tabs-below > .nav-tabs > li > a {
+ -webkit-border-radius: 0 0 4px 4px;
+ -moz-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+}
+.tabs-below > .nav-tabs > li > a:hover {
+ border-bottom-color: transparent;
+ border-top-color: #ddd;
+}
+.tabs-below > .nav-tabs > .active > a,
+.tabs-below > .nav-tabs > .active > a:hover {
+ border-color: transparent #ddd #ddd #ddd;
+}
+.tabs-left > .nav-tabs > li,
+.tabs-right > .nav-tabs > li {
+ float: none;
+}
+.tabs-left > .nav-tabs > li > a,
+.tabs-right > .nav-tabs > li > a {
+ min-width: 74px;
+ margin-right: 0;
+ margin-bottom: 3px;
+}
+.tabs-left > .nav-tabs {
+ float: left;
+ margin-right: 19px;
+ border-right: 1px solid #ddd;
+}
+.tabs-left > .nav-tabs > li > a {
+ margin-right: -1px;
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
+}
+.tabs-left > .nav-tabs > li > a:hover {
+ border-color: #eeeeee #dddddd #eeeeee #eeeeee;
+}
+.tabs-left > .nav-tabs .active > a,
+.tabs-left > .nav-tabs .active > a:hover {
+ border-color: #ddd transparent #ddd #ddd;
+ *border-right-color: #ffffff;
+}
+.tabs-right > .nav-tabs {
+ float: right;
+ margin-left: 19px;
+ border-left: 1px solid #ddd;
+}
+.tabs-right > .nav-tabs > li > a {
+ margin-left: -1px;
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+.tabs-right > .nav-tabs > li > a:hover {
+ border-color: #eeeeee #eeeeee #eeeeee #dddddd;
+}
+.tabs-right > .nav-tabs .active > a,
+.tabs-right > .nav-tabs .active > a:hover {
+ border-color: #ddd #ddd #ddd transparent;
+ *border-left-color: #ffffff;
+}
+.nav > .disabled > a {
+ color: #999999;
+}
+.nav > .disabled > a:hover {
+ text-decoration: none;
+ background-color: transparent;
+ cursor: default;
+}
+
+
+.label-bootstrap,
+.badge {
+ font-size: 10.998px;
+ font-weight: bold;
+ line-height: 14px;
+ color: #ffffff;
+ vertical-align: baseline;
+ white-space: nowrap;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #999999;
+}
+.label-bootstrap {
+ padding: 1px 4px 2px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+.badge {
+ padding: 1px 9px 2px;
+ -webkit-border-radius: 9px;
+ -moz-border-radius: 9px;
+ border-radius: 9px;
+}
+a.label-bootstrap:hover,
+a.badge:hover {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
+}
+.label-important,
+.badge-important {
+ background-color: #b94a48;
+}
+.label-important[href],
+.badge-important[href] {
+ background-color: #953b39;
+}
+.label-warning,
+.badge-warning {
+ background-color: #f89406;
+}
+.label-warning[href],
+.badge-warning[href] {
+ background-color: #c67605;
+}
+.label-success,
+.badge-success {
+ background-color: #468847;
+}
+.label-success[href],
+.badge-success[href] {
+ background-color: #356635;
+}
+.label-info,
+.badge-info {
+ background-color: #3a87ad;
+}
+.label-info[href],
+.badge-info[href] {
+ background-color: #2d6987;
+}
+.label-inverse,
+.badge-inverse {
+ background-color: #333333;
+}
+.label-inverse[href],
+.badge-inverse[href] {
+ background-color: #1a1a1a;
+}
+