2012-11-19 19:24:05 +01:00
|
|
|
# == Schema Information
|
|
|
|
#
|
|
|
|
# Table name: projects
|
|
|
|
#
|
|
|
|
# id :integer not null, primary key
|
|
|
|
# name :string(255)
|
|
|
|
# path :string(255)
|
|
|
|
# description :text
|
|
|
|
# created_at :datetime not null
|
|
|
|
# updated_at :datetime not null
|
|
|
|
# private_flag :boolean default(TRUE), not null
|
|
|
|
# owner_id :integer
|
|
|
|
# default_branch :string(255)
|
|
|
|
# issues_enabled :boolean default(TRUE), not null
|
|
|
|
# wall_enabled :boolean default(TRUE), not null
|
|
|
|
# merge_requests_enabled :boolean default(TRUE), not null
|
|
|
|
# wiki_enabled :boolean default(TRUE), not null
|
2012-11-24 21:16:51 +01:00
|
|
|
# namespace_id :integer
|
2012-11-19 19:24:05 +01:00
|
|
|
#
|
|
|
|
|
2011-10-08 23:36:38 +02:00
|
|
|
require "grit"
|
|
|
|
|
|
|
|
class Project < ActiveRecord::Base
|
2012-06-07 14:44:57 +02:00
|
|
|
include Repository
|
2012-08-21 20:26:56 +02:00
|
|
|
include PushObserver
|
2012-06-07 14:44:57 +02:00
|
|
|
include Authority
|
|
|
|
include Team
|
2012-12-14 06:14:05 +01:00
|
|
|
include NamespacedProject
|
2012-06-07 14:44:57 +02:00
|
|
|
|
2012-12-13 17:42:15 +01:00
|
|
|
class TransferError < StandardError; end
|
2012-06-07 14:44:57 +02:00
|
|
|
|
2012-11-23 19:11:09 +01:00
|
|
|
attr_accessible :name, :path, :description, :default_branch, :issues_enabled,
|
2012-11-23 05:31:09 +01:00
|
|
|
:wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin]
|
|
|
|
|
2012-11-29 05:29:11 +01:00
|
|
|
attr_accessible :namespace_id, :owner_id, as: :admin
|
2012-11-23 05:31:09 +01:00
|
|
|
|
2012-09-27 08:20:36 +02:00
|
|
|
attr_accessor :error_code
|
2012-09-26 20:17:17 +02:00
|
|
|
|
2012-06-07 14:44:57 +02:00
|
|
|
# Relations
|
2012-11-24 21:39:51 +01:00
|
|
|
belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
|
2012-11-22 19:34:16 +01:00
|
|
|
belongs_to :namespace
|
2012-12-05 16:06:15 +01:00
|
|
|
|
|
|
|
# TODO: replace owner with creator.
|
|
|
|
# With namespaces a project owner will be a namespace owner
|
|
|
|
# so this field makes sense only for global projects
|
2012-08-11 00:07:50 +02:00
|
|
|
belongs_to :owner, class_name: "User"
|
|
|
|
has_many :users, through: :users_projects
|
|
|
|
has_many :events, dependent: :destroy
|
|
|
|
has_many :merge_requests, dependent: :destroy
|
|
|
|
has_many :issues, dependent: :destroy, order: "closed, created_at DESC"
|
|
|
|
has_many :milestones, dependent: :destroy
|
|
|
|
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 :hooks, dependent: :destroy, class_name: "ProjectHook"
|
|
|
|
has_many :wikis, dependent: :destroy
|
|
|
|
has_many :protected_branches, dependent: :destroy
|
2012-10-17 21:02:52 +02:00
|
|
|
has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id'
|
2012-11-19 20:34:05 +01:00
|
|
|
has_one :gitlab_ci_service, dependent: :destroy
|
2011-11-04 08:42:36 +01:00
|
|
|
|
2012-10-02 18:01:40 +02:00
|
|
|
delegate :name, to: :owner, allow_nil: true, prefix: true
|
|
|
|
|
2012-10-09 02:10:04 +02:00
|
|
|
# Validations
|
|
|
|
validates :owner, presence: true
|
|
|
|
validates :description, length: { within: 0..2000 }
|
2012-11-23 19:11:09 +01:00
|
|
|
validates :name, presence: true, length: { within: 0..255 }
|
|
|
|
validates :path, presence: true, length: { within: 0..255 },
|
2012-11-28 04:14:05 +01:00
|
|
|
format: { with: Gitlab::Regex.path_regex,
|
2012-10-09 02:10:04 +02:00
|
|
|
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
|
|
|
|
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
|
|
|
|
:wiki_enabled, inclusion: { in: [true, false] }
|
2012-11-23 07:11:09 +01:00
|
|
|
|
2012-11-23 19:11:09 +01:00
|
|
|
validates_uniqueness_of :name, scope: :namespace_id
|
|
|
|
validates_uniqueness_of :path, scope: :namespace_id
|
|
|
|
|
2012-10-09 02:10:04 +02:00
|
|
|
validate :check_limit, :repo_name
|
|
|
|
|
2012-06-07 14:44:57 +02:00
|
|
|
# Scopes
|
2012-08-11 00:07:50 +02:00
|
|
|
scope :public_only, where(private_flag: false)
|
2012-10-03 13:26:37 +02:00
|
|
|
scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.projects.map(&:id) ) }
|
|
|
|
scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) }
|
2012-11-29 15:43:09 +01:00
|
|
|
scope :sorted_by_activity, ->() { order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") }
|
2012-11-30 04:14:05 +01:00
|
|
|
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
|
|
|
|
scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
|
2011-10-08 23:36:38 +02:00
|
|
|
|
2012-10-09 02:10:04 +02:00
|
|
|
class << self
|
2012-11-29 16:17:01 +01:00
|
|
|
def authorized_for user
|
|
|
|
projects = includes(:users_projects, :namespace)
|
|
|
|
projects = projects.where("users_projects.user_id = :user_id or projects.owner_id = :user_id or namespaces.owner_id = :user_id", user_id: user.id)
|
|
|
|
end
|
|
|
|
|
2012-10-09 02:10:04 +02:00
|
|
|
def active
|
|
|
|
joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
|
|
|
|
end
|
2012-06-11 07:52:44 +02:00
|
|
|
|
2012-10-09 02:10:04 +02:00
|
|
|
def search query
|
2012-11-23 19:11:09 +01:00
|
|
|
where("projects.name LIKE :query OR projects.path LIKE :query", query: "%#{query}%")
|
2012-10-09 02:10:04 +02:00
|
|
|
end
|
2012-06-11 07:52:44 +02:00
|
|
|
|
2012-11-24 21:00:30 +01:00
|
|
|
def find_with_namespace(id)
|
|
|
|
if id.include?("/")
|
|
|
|
id = id.split("/")
|
|
|
|
namespace_id = Namespace.find_by_path(id.first).id
|
|
|
|
where(namespace_id: namespace_id).find_by_path(id.last)
|
|
|
|
else
|
2012-12-13 17:42:15 +01:00
|
|
|
where(path: id, namespace_id: nil).last
|
2012-11-24 21:00:30 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-10-09 02:10:04 +02:00
|
|
|
def create_by_user(params, user)
|
2012-11-23 19:11:09 +01:00
|
|
|
namespace_id = params.delete(:namespace_id)
|
2012-11-23 05:11:09 +01:00
|
|
|
|
2012-10-09 02:10:04 +02:00
|
|
|
project = Project.new params
|
|
|
|
|
|
|
|
Project.transaction do
|
2012-11-23 07:11:09 +01:00
|
|
|
|
2012-11-23 19:11:09 +01:00
|
|
|
# Parametrize path for project
|
2012-11-23 07:11:09 +01:00
|
|
|
#
|
2012-11-23 19:11:09 +01:00
|
|
|
# Ex.
|
|
|
|
# 'GitLab HQ'.parameterize => "gitlab-hq"
|
|
|
|
#
|
|
|
|
project.path = project.name.dup.parameterize
|
2012-11-23 07:11:09 +01:00
|
|
|
|
2012-10-09 02:10:04 +02:00
|
|
|
project.owner = user
|
2012-11-25 10:57:01 +01:00
|
|
|
|
|
|
|
# Apply namespace if user has access to it
|
|
|
|
# else fallback to user namespace
|
2012-11-27 07:31:15 +01:00
|
|
|
if namespace_id != Namespace.global_id
|
|
|
|
project.namespace_id = user.namespace_id
|
|
|
|
|
|
|
|
if namespace_id
|
|
|
|
group = Group.find_by_id(namespace_id)
|
|
|
|
if user.can? :manage_group, group
|
|
|
|
project.namespace_id = namespace_id
|
|
|
|
end
|
2012-11-25 10:57:01 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-10-09 02:10:04 +02:00
|
|
|
project.save!
|
|
|
|
|
|
|
|
# Add user as project master
|
|
|
|
project.users_projects.create!(project_access: UsersProject::MASTER, user: user)
|
|
|
|
|
|
|
|
# when project saved no team member exist so
|
|
|
|
# project repository should be updated after first user add
|
|
|
|
project.update_repository
|
|
|
|
end
|
|
|
|
|
|
|
|
project
|
|
|
|
rescue Gitlab::Gitolite::AccessDenied => ex
|
|
|
|
project.error_code = :gitolite
|
|
|
|
project
|
|
|
|
rescue => ex
|
|
|
|
project.error_code = :db
|
|
|
|
project.errors.add(:base, "Can't save project. Please try again later")
|
|
|
|
project
|
2012-06-11 07:52:44 +02:00
|
|
|
end
|
|
|
|
|
2012-10-09 02:10:04 +02:00
|
|
|
def access_options
|
|
|
|
UsersProject.access_roles
|
|
|
|
end
|
2012-07-05 20:59:37 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def git_error?
|
|
|
|
error_code == :gitolite
|
|
|
|
end
|
|
|
|
|
|
|
|
def saved?
|
|
|
|
id && valid?
|
2012-06-11 07:52:44 +02:00
|
|
|
end
|
|
|
|
|
2012-06-07 14:44:57 +02:00
|
|
|
def check_limit
|
|
|
|
unless owner.can_create_project?
|
|
|
|
errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it")
|
|
|
|
end
|
|
|
|
rescue
|
2012-08-11 01:47:54 +02:00
|
|
|
errors[:base] << ("Can't check your ability to create project")
|
2011-10-08 23:36:38 +02:00
|
|
|
end
|
|
|
|
|
2012-06-07 14:44:57 +02:00
|
|
|
def repo_name
|
2012-12-15 11:33:45 +01:00
|
|
|
denied_paths = %w(gitolite-admin admin dashboard groups help profile projects search)
|
2012-10-30 12:27:15 +01:00
|
|
|
|
|
|
|
if denied_paths.include?(path)
|
|
|
|
errors.add(:path, "like #{path} is not allowed")
|
2012-06-07 14:44:57 +02:00
|
|
|
end
|
|
|
|
end
|
2012-07-15 14:29:06 +02:00
|
|
|
|
2012-06-07 14:44:57 +02:00
|
|
|
def to_param
|
2012-11-22 21:34:06 +01:00
|
|
|
if namespace
|
2012-11-23 19:11:09 +01:00
|
|
|
namespace.path + "/" + path
|
2012-11-22 21:34:06 +01:00
|
|
|
else
|
2012-11-23 19:11:09 +01:00
|
|
|
path
|
2012-11-22 21:34:06 +01:00
|
|
|
end
|
2011-11-06 21:38:08 +01:00
|
|
|
end
|
|
|
|
|
2012-06-07 14:44:57 +02:00
|
|
|
def web_url
|
2012-12-15 01:16:25 +01:00
|
|
|
[Gitlab.config.gitlab.url, path_with_namespace].join("/")
|
2011-12-13 22:24:31 +01:00
|
|
|
end
|
|
|
|
|
2011-10-08 23:36:38 +02:00
|
|
|
def common_notes
|
2012-08-11 00:07:50 +02: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)
|
2012-12-18 19:02:00 +01:00
|
|
|
notes.new(commit_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-12-23 01:03:57 +01:00
|
|
|
notes.where(commit_id: commit.id, noteable_type: "Commit").where('line_code IS NULL OR line_code = ""')
|
2012-01-10 21:08:46 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def commit_line_notes(commit)
|
2012-12-18 19:02:00 +01:00
|
|
|
notes.where(commit_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-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
|
2012-10-17 21:02:52 +02:00
|
|
|
last_event
|
2011-10-31 21:57:16 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def last_activity_date
|
2012-10-17 21:02:52 +02:00
|
|
|
last_event.try(:created_at) || updated_at
|
2012-03-01 19:40:32 +01:00
|
|
|
end
|
2011-12-20 07:24:14 +01:00
|
|
|
|
2012-03-05 23:26:40 +01:00
|
|
|
def project_id
|
|
|
|
self.id
|
|
|
|
end
|
2012-10-09 19:39:06 +02:00
|
|
|
|
|
|
|
def issues_labels
|
|
|
|
issues.tag_counts_on(:labels)
|
|
|
|
end
|
2012-11-20 13:22:00 +01:00
|
|
|
|
|
|
|
def services
|
|
|
|
[gitlab_ci_service].compact
|
|
|
|
end
|
2012-11-20 18:34:05 +01:00
|
|
|
|
|
|
|
def gitlab_ci?
|
|
|
|
gitlab_ci_service && gitlab_ci_service.active
|
|
|
|
end
|
2012-11-22 19:34:16 +01:00
|
|
|
|
2012-11-23 20:31:09 +01:00
|
|
|
# For compatibility with old code
|
|
|
|
def code
|
|
|
|
path
|
2012-11-22 21:34:06 +01:00
|
|
|
end
|
2012-11-24 11:37:30 +01:00
|
|
|
|
2012-11-21 06:24:05 +01:00
|
|
|
def items_for entity
|
|
|
|
case entity
|
|
|
|
when 'issue' then
|
|
|
|
issues
|
|
|
|
when 'merge_request' then
|
|
|
|
merge_requests
|
|
|
|
end
|
|
|
|
end
|
2012-11-30 17:26:17 +01:00
|
|
|
|
2012-12-20 21:16:51 +01:00
|
|
|
def send_move_instructions
|
|
|
|
self.users_projects.each do |member|
|
|
|
|
Notify.project_was_moved_email(member.id).deliver
|
|
|
|
end
|
2012-11-30 17:26:17 +01:00
|
|
|
end
|
2011-10-08 23:36:38 +02:00
|
|
|
end
|