2011-10-08 23:36:38 +02:00
|
|
|
require "grit"
|
|
|
|
|
|
|
|
class Project < ActiveRecord::Base
|
2011-12-07 00:27:07 +01:00
|
|
|
PROJECT_N = 0
|
|
|
|
PROJECT_R = 1
|
|
|
|
PROJECT_RW = 2
|
|
|
|
PROJECT_RWA = 3
|
|
|
|
|
2011-10-09 21:36:57 +02:00
|
|
|
belongs_to :owner, :class_name => "User"
|
|
|
|
|
2011-11-28 08:39:43 +01:00
|
|
|
has_many :merge_requests, :dependent => :destroy
|
2011-10-15 18:56:53 +02:00
|
|
|
has_many :issues, :dependent => :destroy, :order => "position"
|
2011-10-08 23:36:38 +02:00
|
|
|
has_many :users_projects, :dependent => :destroy
|
|
|
|
has_many :users, :through => :users_projects
|
|
|
|
has_many :notes, :dependent => :destroy
|
2011-10-16 23:07:10 +02:00
|
|
|
has_many :snippets, :dependent => :destroy
|
2011-12-31 18:37:14 +01:00
|
|
|
has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key"
|
2011-12-14 17:38:52 +01:00
|
|
|
has_many :web_hooks, :dependent => :destroy
|
2011-10-08 23:36:38 +02:00
|
|
|
|
2011-11-04 08:42:36 +01:00
|
|
|
acts_as_taggable
|
|
|
|
|
2011-10-08 23:36:38 +02:00
|
|
|
validates :name,
|
|
|
|
:uniqueness => true,
|
|
|
|
:presence => true,
|
|
|
|
:length => { :within => 0..255 }
|
|
|
|
|
|
|
|
validates :path,
|
|
|
|
:uniqueness => true,
|
|
|
|
:presence => true,
|
2011-12-12 19:13:11 +01:00
|
|
|
:format => { :with => /^[a-zA-Z0-9_\-\.]*$/,
|
2011-12-12 19:32:35 +01:00
|
|
|
:message => "only letters, digits & '_' '-' '.' allowed" },
|
2011-10-08 23:36:38 +02:00
|
|
|
:length => { :within => 0..255 }
|
2011-10-26 15:46:25 +02:00
|
|
|
|
2011-10-08 23:36:38 +02:00
|
|
|
validates :description,
|
|
|
|
:length => { :within => 0..2000 }
|
|
|
|
|
|
|
|
validates :code,
|
|
|
|
:presence => true,
|
|
|
|
:uniqueness => true,
|
2011-12-12 19:13:11 +01:00
|
|
|
:format => { :with => /^[a-zA-Z0-9_\-\.]*$/,
|
2011-12-12 19:32:35 +01:00
|
|
|
: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
|
|
|
|
2011-10-09 21:36:57 +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-12-05 08:43:53 +01:00
|
|
|
after_destroy :destroy_repository
|
|
|
|
after_save :update_repository
|
2011-10-08 23:36:38 +02:00
|
|
|
|
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)
|
|
|
|
|
2011-12-20 07:24:14 +01:00
|
|
|
def self.active
|
|
|
|
joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
|
|
|
|
end
|
2011-12-07 00:27:07 +01:00
|
|
|
|
|
|
|
def self.access_options
|
|
|
|
{
|
|
|
|
"Denied" => PROJECT_N,
|
|
|
|
"Read" => PROJECT_R,
|
|
|
|
"Report" => PROJECT_RW,
|
|
|
|
"Admin" => PROJECT_RWA
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2011-11-10 23:51:19 +01:00
|
|
|
def repository
|
|
|
|
@repository ||= Repository.new(self)
|
|
|
|
end
|
|
|
|
|
|
|
|
delegate :repo,
|
2011-11-11 00:28:26 +01:00
|
|
|
:url_to_repo,
|
|
|
|
:path_to_repo,
|
2011-12-05 08:43:53 +01:00
|
|
|
:update_repository,
|
|
|
|
:destroy_repository,
|
2011-11-10 23:51:19 +01:00
|
|
|
:tags,
|
|
|
|
:repo_exists?,
|
|
|
|
:commit,
|
|
|
|
:commits,
|
2012-01-04 07:17:41 +01:00
|
|
|
:commits_with_refs,
|
2011-11-10 23:51:19 +01:00
|
|
|
:tree,
|
|
|
|
:heads,
|
|
|
|
:commits_since,
|
|
|
|
:fresh_commits,
|
2011-12-14 17:38:52 +01:00
|
|
|
:commits_between,
|
2011-11-10 23:51:19 +01:00
|
|
|
:to => :repository, :prefix => nil
|
|
|
|
|
2011-10-08 23:36:38 +02:00
|
|
|
def to_param
|
|
|
|
code
|
|
|
|
end
|
|
|
|
|
2011-12-14 17:38:52 +01:00
|
|
|
def web_url
|
|
|
|
[GIT_HOST['host'], code].join("/")
|
|
|
|
end
|
|
|
|
|
|
|
|
def execute_web_hooks(oldrev, newrev, ref)
|
2011-12-15 10:33:20 +01:00
|
|
|
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"
|
|
|
|
|
2011-12-14 17:38:52 +01:00
|
|
|
data = web_hook_data(oldrev, newrev, ref)
|
|
|
|
web_hooks.each { |web_hook| web_hook.execute(data) }
|
|
|
|
end
|
|
|
|
|
|
|
|
def web_hook_data(oldrev, newrev, ref)
|
|
|
|
data = {
|
|
|
|
before: oldrev,
|
|
|
|
after: newrev,
|
|
|
|
ref: ref,
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
|
2012-01-04 07:17:41 +01:00
|
|
|
def fresh_merge_requests(n)
|
|
|
|
merge_requests.includes(:project, :author).order("created_at desc").first(n)
|
|
|
|
end
|
|
|
|
|
2011-11-15 10:09:07 +01:00
|
|
|
def fresh_issues(n)
|
|
|
|
issues.includes(:project, :author).order("created_at desc").first(n)
|
|
|
|
end
|
|
|
|
|
|
|
|
def fresh_notes(n)
|
|
|
|
notes.inc_author_project.order("created_at desc").first(n)
|
|
|
|
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
|
|
|
|
|
2011-11-11 00:28:26 +01:00
|
|
|
def build_commit_note(commit)
|
|
|
|
notes.new(:noteable_id => commit.id, :noteable_type => "Commit")
|
2011-10-08 23:36:38 +02:00
|
|
|
end
|
2011-10-26 15:46:25 +02:00
|
|
|
|
2011-11-11 00:28:26 +01:00
|
|
|
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-10-26 15:46:25 +02:00
|
|
|
|
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)
|
|
|
|
{ :project_access => PROJECT_RWA }
|
|
|
|
elsif access.include?(:write)
|
|
|
|
{ :project_access => PROJECT_RW }
|
|
|
|
else
|
|
|
|
{ :project_access => PROJECT_R }
|
|
|
|
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
|
|
|
|
|
2011-12-07 00:27:07 +01:00
|
|
|
def repository_readers
|
|
|
|
keys = Key.joins({:user => :users_projects}).
|
|
|
|
where("users_projects.project_id = ? AND users_projects.repo_access = ?", id, Repository::REPO_R)
|
2011-12-31 18:37:14 +01:00
|
|
|
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
|
2011-12-07 00:27:07 +01:00
|
|
|
keys = Key.joins({:user => :users_projects}).
|
|
|
|
where("users_projects.project_id = ? AND users_projects.repo_access = ?", id, Repository::REPO_RW)
|
2011-10-08 23:36:38 +02:00
|
|
|
keys.map(&:identifier)
|
|
|
|
end
|
|
|
|
|
|
|
|
def readers
|
2011-12-07 00:27:07 +01:00
|
|
|
@readers ||= users_projects.includes(:user).where(:project_access => [PROJECT_R, PROJECT_RW, PROJECT_RWA]).map(&:user)
|
|
|
|
end
|
|
|
|
|
|
|
|
def writers
|
|
|
|
@writers ||= users_projects.includes(:user).where(:project_access => [PROJECT_RW, PROJECT_RWA]).map(&:user)
|
2011-10-08 23:36:38 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def admins
|
2011-12-07 00:27:07 +01:00
|
|
|
@admins ||= users_projects.includes(:user).where(:project_access => PROJECT_RWA).map(&:user)
|
2011-10-08 23:36:38 +02:00
|
|
|
end
|
|
|
|
|
2011-12-15 22:57:46 +01:00
|
|
|
def allow_read_for?(user)
|
|
|
|
!users_projects.where(:user_id => user.id, :project_access => [PROJECT_R, PROJECT_RW, PROJECT_RWA]).empty?
|
|
|
|
end
|
|
|
|
|
|
|
|
def allow_write_for?(user)
|
|
|
|
!users_projects.where(:user_id => user.id, :project_access => [PROJECT_RW, PROJECT_RWA]).empty?
|
|
|
|
end
|
|
|
|
|
|
|
|
def allow_admin_for?(user)
|
|
|
|
!users_projects.where(:user_id => user.id, :project_access => [PROJECT_RWA]).empty? || owner_id == user.id
|
|
|
|
end
|
|
|
|
|
2011-11-16 06:38:53 +01:00
|
|
|
def root_ref
|
2011-12-07 22:56:57 +01:00
|
|
|
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
|
2011-10-31 21:57:16 +01:00
|
|
|
updates(1).first
|
2011-11-15 09:34:30 +01:00
|
|
|
rescue
|
2011-10-31 21:57:16 +01:00
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
|
|
|
def last_activity_date
|
|
|
|
last_activity.try(:created_at)
|
|
|
|
end
|
|
|
|
|
2011-12-20 07:24:14 +01:00
|
|
|
def last_activity_date_cached(expire = 1.hour)
|
|
|
|
activity_date_key = "project_#{id}_activity_date"
|
|
|
|
|
|
|
|
cached_activities = Rails.cache.read(activity_date_key)
|
|
|
|
if cached_activities
|
|
|
|
activity_date = if cached_activities == "Never"
|
|
|
|
nil
|
|
|
|
else
|
|
|
|
cached_activities
|
|
|
|
end
|
|
|
|
else
|
|
|
|
activity_date = last_activity_date
|
|
|
|
Rails.cache.write(activity_date_key, activity_date || "Never", :expires_in => expire)
|
|
|
|
end
|
|
|
|
|
|
|
|
activity_date
|
|
|
|
end
|
|
|
|
|
2011-11-27 13:53:12 +01:00
|
|
|
# Get project updates from cache
|
|
|
|
# or calculate.
|
|
|
|
def cached_updates(limit, expire = 2.minutes)
|
|
|
|
activities_key = "project_#{id}_activities"
|
|
|
|
cached_activities = Rails.cache.read(activities_key)
|
|
|
|
if cached_activities
|
|
|
|
activities = cached_activities
|
|
|
|
else
|
|
|
|
activities = updates(limit)
|
2011-12-20 07:24:14 +01:00
|
|
|
Rails.cache.write(activities_key, activities, :expires_in => expire)
|
2011-11-27 13:53:12 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
activities
|
|
|
|
end
|
|
|
|
|
|
|
|
# Get 20 events for project like
|
|
|
|
# commits, issues or notes
|
2011-10-31 21:57:16 +01:00
|
|
|
def updates(n = 3)
|
2011-11-15 09:34:30 +01:00
|
|
|
[
|
2011-10-31 21:57:16 +01:00
|
|
|
fresh_commits(n),
|
2011-11-15 10:09:07 +01:00
|
|
|
fresh_issues(n),
|
|
|
|
fresh_notes(n)
|
2011-10-31 21:57:16 +01:00
|
|
|
].compact.flatten.sort do |x, y|
|
2012-01-04 07:17:41 +01:00
|
|
|
y.created_at <=> x.created_at
|
|
|
|
end[0...n]
|
|
|
|
end
|
|
|
|
|
2012-01-14 22:46:06 +01:00
|
|
|
def activities(n=3)
|
2012-01-04 07:17:41 +01:00
|
|
|
[
|
|
|
|
fresh_issues(n),
|
|
|
|
fresh_merge_requests(n),
|
2012-01-14 22:46:06 +01:00
|
|
|
notes.inc_author_project.where("noteable_type is not null").order("created_at desc").first(n)
|
2012-01-04 07:17:41 +01:00
|
|
|
].compact.flatten.sort do |x, y|
|
2011-10-31 21:57:16 +01:00
|
|
|
y.created_at <=> x.created_at
|
2011-11-02 21:14:03 +01:00
|
|
|
end[0...n]
|
2011-10-31 21:57:16 +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
|
2011-10-26 15:46:25 +02:00
|
|
|
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
|
|
|
|
end
|
|
|
|
# == Schema Information
|
|
|
|
#
|
|
|
|
# Table name: projects
|
|
|
|
#
|
2011-12-18 15:09:16 +01:00
|
|
|
# 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
|
2011-10-08 23:36:38 +02:00
|
|
|
#
|
|
|
|
|