gitlabhq/app/models/project.rb

401 lines
10 KiB
Ruby
Raw Normal View History

2011-10-08 23:36:38 +02:00
require "grit"
class Project < ActiveRecord::Base
2011-10-09 21:36:57 +02:00
belongs_to :owner, :class_name => "User"
has_many :users, :through => :users_projects
has_many :events, :dependent => :destroy
2011-11-28 08:39:43 +01:00
has_many :merge_requests, :dependent => :destroy
has_many :issues, :dependent => :destroy, :order => "position"
2011-10-08 23:36:38 +02:00
has_many :users_projects, :dependent => :destroy
has_many :notes, :dependent => :destroy
has_many :snippets, :dependent => :destroy
has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key"
has_many :web_hooks, :dependent => :destroy
has_many :wikis, :dependent => :destroy
has_many :protected_branches, :dependent => :destroy
2011-11-04 08:42:36 +01:00
2011-10-08 23:36:38 +02:00
validates :name,
:uniqueness => true,
:presence => true,
:length => { :within => 0..255 }
validates :path,
:uniqueness => true,
:presence => true,
:format => { :with => /^[a-zA-Z0-9_\-\.]*$/,
:message => "only letters, digits & '_' '-' '.' allowed" },
2011-10-08 23:36:38 +02:00
:length => { :within => 0..255 }
2011-10-08 23:36:38 +02:00
validates :description,
:length => { :within => 0..2000 }
validates :code,
:presence => true,
:uniqueness => true,
:format => { :with => /^[a-zA-Z0-9_\-\.]*$/,
:message => "only letters, digits & '_' '-' '.' allowed" },
2011-10-18 10:21:44 +02:00
:length => { :within => 3..255 }
2011-10-08 23:36:38 +02:00
validates :owner, :presence => true
2011-10-09 20:15:01 +02:00
validate :check_limit
2011-10-21 20:40:36 +02:00
validate :repo_name
2011-10-09 13:05:31 +02:00
attr_protected :private_flag, :owner_id
2011-10-08 23:36:38 +02:00
scope :public_only, where(:private_flag => false)
2012-02-11 18:56:18 +01:00
scope :without_user, lambda { |user| where("id not in (:ids)", :ids => user.projects.map(&:id) ) }
2011-10-08 23:36:38 +02:00
def self.active
joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
end
def self.access_options
2012-02-16 08:03:55 +01:00
UsersProject.access_roles
end
2011-10-08 23:36:38 +02:00
def to_param
code
end
def web_url
[GIT_HOST['host'], code].join("/")
end
def observe_push(oldrev, newrev, ref, author_key_id)
data = web_hook_data(oldrev, newrev, ref, author_key_id)
2012-02-28 15:48:15 +01:00
Event.create(
:project => self,
:action => Event::Pushed,
:data => data,
:author_id => Key.find_by_identifier(author_key_id).user.id
2012-02-28 15:48:15 +01:00
)
end
def execute_web_hooks(oldrev, newrev, ref, author_key_id)
ref_parts = ref.split('/')
# Return if this is not a push to a branch (e.g. new commits)
return if ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000"
data = web_hook_data(oldrev, newrev, ref, author_key_id)
2012-02-28 15:48:15 +01:00
web_hooks.each { |web_hook| web_hook.execute(data) }
end
def web_hook_data(oldrev, newrev, ref, author_key_id)
key = Key.find_by_identifier(author_key_id)
data = {
before: oldrev,
after: newrev,
ref: ref,
2012-02-29 22:04:09 +01:00
user_id: key.user.id,
user_name: key.user_name,
repository: {
name: name,
url: web_url,
description: description,
homepage: web_url,
private: private?
},
commits: []
}
commits_between(oldrev, newrev).each do |commit|
data[:commits] << {
id: commit.id,
message: commit.safe_message,
timestamp: commit.date.xmlschema,
url: "http://#{GIT_HOST['host']}/#{code}/commits/#{commit.id}",
author: {
name: commit.author_name,
email: commit.author_email
}
}
end
data
end
def open_branches
if protected_branches.empty?
self.repo.heads
else
pnames = protected_branches.map(&:name)
self.repo.heads.reject { |h| pnames.include?(h.name) }
end.sort_by(&:name)
end
2011-11-06 21:38:08 +01:00
def team_member_by_name_or_email(email = nil, name = nil)
user = users.where("email like ? or name like ?", email, name).first
users_projects.find_by_user_id(user.id) if user
end
2011-12-13 22:24:31 +01:00
def team_member_by_id(user_id)
users_projects.find_by_user_id(user_id)
end
2011-10-08 23:36:38 +02:00
def common_notes
2011-11-15 10:09:07 +01:00
notes.where(:noteable_type => ["", nil]).inc_author_project
2011-10-08 23:36:38 +02:00
end
def build_commit_note(commit)
notes.new(:noteable_id => commit.id, :noteable_type => "Commit")
2011-10-08 23:36:38 +02:00
end
def commit_notes(commit)
2012-01-10 21:08:46 +01:00
notes.where(:noteable_id => commit.id, :noteable_type => "Commit", :line_code => nil)
end
def commit_line_notes(commit)
2012-01-19 20:44:10 +01:00
notes.where(:noteable_id => commit.id, :noteable_type => "Commit").where("line_code is not null")
2011-10-08 23:36:38 +02:00
end
2011-12-05 08:23:53 +01:00
def has_commits?
!!commit
end
2011-12-07 08:48:44 +01:00
# Compatible with all access rights
# Should be rewrited for new access rights
2011-10-08 23:36:38 +02:00
def add_access(user, *access)
2011-12-07 08:48:44 +01:00
access = if access.include?(:admin)
2012-02-16 08:03:55 +01:00
{ :project_access => UsersProject::MASTER }
2011-12-07 08:48:44 +01:00
elsif access.include?(:write)
2012-02-16 08:03:55 +01:00
{ :project_access => UsersProject::DEVELOPER }
2011-12-07 08:48:44 +01:00
else
{ :project_access => UsersProject::REPORTER }
2011-12-07 08:48:44 +01:00
end
2011-10-08 23:36:38 +02:00
opts = { :user => user }
2011-12-07 08:48:44 +01:00
opts.merge!(access)
2011-10-08 23:36:38 +02:00
users_projects.create(opts)
end
def reset_access(user)
users_projects.where(:project_id => self.id, :user_id => user.id).destroy if self.id
end
def repository_readers
keys = Key.joins({:user => :users_projects}).
2012-02-16 08:03:55 +01:00
where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::REPORTER)
keys.map(&:identifier) + deploy_keys.map(&:identifier)
2011-10-08 23:36:38 +02:00
end
2011-12-05 08:43:53 +01:00
def repository_writers
keys = Key.joins({:user => :users_projects}).
2012-02-16 08:03:55 +01:00
where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::DEVELOPER)
2011-10-08 23:36:38 +02:00
keys.map(&:identifier)
end
def repository_masters
keys = Key.joins({:user => :users_projects}).
2012-02-16 08:03:55 +01:00
where("users_projects.project_id = ? AND users_projects.project_access = ?", id, UsersProject::MASTER)
keys.map(&:identifier)
end
2011-12-15 22:57:46 +01:00
def allow_read_for?(user)
2012-02-16 08:03:55 +01:00
!users_projects.where(:user_id => user.id).empty?
2011-12-15 22:57:46 +01:00
end
2012-02-20 19:16:55 +01:00
def guest_access_for?(user)
2012-02-16 08:03:55 +01:00
!users_projects.where(:user_id => user.id).empty?
2011-12-15 22:57:46 +01:00
end
2012-02-20 19:16:55 +01:00
def report_access_for?(user)
!users_projects.where(:user_id => user.id, :project_access => [UsersProject::REPORTER, UsersProject::DEVELOPER, UsersProject::MASTER]).empty?
2011-12-15 22:57:46 +01:00
end
2012-02-20 19:16:55 +01:00
def dev_access_for?(user)
!users_projects.where(:user_id => user.id, :project_access => [UsersProject::DEVELOPER, UsersProject::MASTER]).empty?
end
def master_access_for?(user)
!users_projects.where(:user_id => user.id, :project_access => [UsersProject::MASTER]).empty? || owner_id == user.id
end
2011-11-16 06:38:53 +01:00
def root_ref
default_branch || "master"
2011-11-16 06:38:53 +01:00
end
2011-10-08 23:36:38 +02:00
def public?
!private_flag
end
def private?
private_flag
end
2011-11-15 09:34:30 +01:00
def last_activity
events.last || nil
2011-10-31 21:57:16 +01:00
end
def last_activity_date
2012-03-01 19:40:32 +01:00
if events.last
events.last.created_at
else
2012-03-01 19:40:32 +01:00
updated_at
end
2012-03-01 19:40:32 +01:00
end
2011-10-09 20:15:01 +02:00
def check_limit
unless owner.can_create_project?
2011-10-09 21:36:57 +02:00
errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it")
2011-10-09 20:15:01 +02:00
end
rescue
2011-10-09 21:36:57 +02:00
errors[:base] << ("Cant check your ability to create project")
2011-10-09 20:15:01 +02:00
end
2011-10-21 20:40:36 +02:00
def repo_name
2011-12-05 08:43:53 +01:00
if path == "gitolite-admin"
errors.add(:path, " like 'gitolite-admin' is not allowed")
2011-10-21 20:40:36 +02:00
end
end
2011-10-08 23:36:38 +02:00
def valid_repo?
repo
rescue
errors.add(:path, "Invalid repository path")
false
end
2012-03-05 23:26:40 +01:00
def commit(commit_id = nil)
commit = if commit_id
repo.commit(commit_id)
2012-03-05 23:26:40 +01:00
else
repo.commits.first
end
Commit.new(commit) if commit
end
def fresh_commits(n = 10)
commits = heads.map do |h|
repo.commits(h.name, n).map { |c| Commit.new(c, h) }
end.flatten.uniq { |c| c.id }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits[0...n]
end
def commits_with_refs(n = 20)
commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits[0..n]
end
def commits_since(date)
commits = heads.map do |h|
repo.log(h.name, nil, :since => date).each { |c| Commit.new(c, h) }
end.flatten.uniq { |c| c.id }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits
end
def commits(ref, path = nil, limit = nil, offset = nil)
if path
repo.log(ref, path, :max_count => limit, :skip => offset)
elsif limit && offset
repo.commits(ref, limit, offset)
else
repo.commits(ref)
end.map{ |c| Commit.new(c) }
end
def commits_between(from, to)
repo.commits_between(from, to).map { |c| Commit.new(c) }
end
def project_id
self.id
end
def write_hooks
%w(post-receive).each do |hook|
write_hook(hook, File.read(File.join(Rails.root, 'lib', "#{hook}-hook")))
end
end
def write_hook(name, content)
hook_file = File.join(path_to_repo, 'hooks', name)
File.open(hook_file, 'w') do |f|
f.write(content)
end
File.chmod(0775, hook_file)
end
def repo
@repo ||= Grit::Repo.new(path_to_repo)
end
def url_to_repo
Gitlabhq::GitHost.url_to_repo(path)
end
def path_to_repo
File.join(GIT_HOST["base_path"], "#{path}.git")
end
def update_repository
Gitlabhq::GitHost.system.update_project(path, self)
write_hooks if File.exists?(path_to_repo)
end
def destroy_repository
Gitlabhq::GitHost.system.destroy_project(self)
end
def repo_exists?
@repo_exists ||= (repo && !repo.branches.empty?)
rescue
@repo_exists = false
end
def tags
repo.tags.map(&:name).sort.reverse
end
def heads
@heads ||= repo.heads
end
def tree(fcommit, path = nil)
fcommit = commit if fcommit == :head
tree = fcommit.tree
path ? (tree / path) : tree
end
2011-10-08 23:36:38 +02:00
end
2012-03-05 23:26:40 +01:00
2011-10-08 23:36:38 +02:00
# == Schema Information
#
# Table name: projects
#
# id :integer not null, primary key
# name :string(255)
# path :string(255)
# description :text
# created_at :datetime
# updated_at :datetime
# private_flag :boolean default(TRUE), not null
# code :string(255)
# owner_id :integer
# default_branch :string(255) default("master"), not null
# issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
2012-02-28 14:09:23 +01:00
# wiki_enabled :boolean default(TRUE), not null
2011-10-08 23:36:38 +02:00
#