project.rb 6.4 KB
Newer Older
gitlabhq's avatar
gitlabhq committed
1
2
3
require "grit"

class Project < ActiveRecord::Base
4
5
6
7
8
  PROJECT_N = 0
  PROJECT_R = 1
  PROJECT_RW = 2
  PROJECT_RWA = 3

gitlabhq's avatar
gitlabhq committed
9
10
  belongs_to :owner, :class_name => "User"

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
11
  has_many :merge_requests, :dependent => :destroy
VSizov's avatar
VSizov committed
12
  has_many :issues, :dependent => :destroy, :order => "position"
gitlabhq's avatar
gitlabhq committed
13
14
15
  has_many :users_projects, :dependent => :destroy
  has_many :users, :through => :users_projects
  has_many :notes, :dependent => :destroy
gitlabhq's avatar
gitlabhq committed
16
  has_many :snippets, :dependent => :destroy
gitlabhq's avatar
gitlabhq committed
17

Aleksei Kvitinskii's avatar
Aleksei Kvitinskii committed
18
19
  acts_as_taggable

gitlabhq's avatar
gitlabhq committed
20
21
22
23
24
25
26
27
  validates :name,
            :uniqueness => true,
            :presence => true,
            :length   => { :within => 0..255 }

  validates :path,
            :uniqueness => true,
            :presence => true,
28
            :format => { :with => /^[a-zA-Z0-9_\-\.]*$/,
29
                         :message => "only letters, digits & '_' '-' '.' allowed" },
gitlabhq's avatar
gitlabhq committed
30
            :length   => { :within => 0..255 }
Nihad Abbasov's avatar
Nihad Abbasov committed
31

gitlabhq's avatar
gitlabhq committed
32
33
34
35
36
37
  validates :description,
            :length   => { :within => 0..2000 }

  validates :code,
            :presence => true,
            :uniqueness => true,
38
            :format => { :with => /^[a-zA-Z0-9_\-\.]*$/,
39
                         :message => "only letters, digits & '_' '-' '.' allowed"  },
gitlabhq's avatar
gitlabhq committed
40
            :length   => { :within => 3..255 }
gitlabhq's avatar
gitlabhq committed
41

gitlabhq's avatar
gitlabhq committed
42
43
44
  validates :owner,
            :presence => true

Valera Sizov's avatar
Valera Sizov committed
45
  validate :check_limit
gitlabhq's avatar
gitlabhq committed
46
47
  validate :repo_name

48
49
  after_destroy :destroy_repository
  after_save :update_repository
gitlabhq's avatar
gitlabhq committed
50

51
  attr_protected :private_flag, :owner_id
gitlabhq's avatar
gitlabhq committed
52
53
54

  scope :public_only, where(:private_flag => false)

55
56
57
58
59
60
61
62
63
64

  def self.access_options
    {
      "Denied" => PROJECT_N,
      "Read"   => PROJECT_R,
      "Report" => PROJECT_RW,
      "Admin"  => PROJECT_RWA
    }
  end

65
66
67
68
69
  def repository
    @repository ||= Repository.new(self)
  end

  delegate :repo,
70
71
    :url_to_repo,
    :path_to_repo,
72
73
    :update_repository,
    :destroy_repository,
74
75
76
77
78
79
80
81
82
83
    :tags,
    :repo_exists?,
    :commit,
    :commits,
    :tree,
    :heads,
    :commits_since,
    :fresh_commits,
    :to => :repository, :prefix => nil

gitlabhq's avatar
gitlabhq committed
84
85
86
87
  def to_param
    code
  end

88
89
90
91
92
  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

93
94
95
96
  def team_member_by_id(user_id)
    users_projects.find_by_user_id(user_id)
  end

gitlabhq's avatar
gitlabhq committed
97
98
99
100
101
102
103
104
  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

gitlabhq's avatar
gitlabhq committed
105
  def common_notes
gitlabhq's avatar
gitlabhq committed
106
    notes.where(:noteable_type => ["", nil]).inc_author_project
gitlabhq's avatar
gitlabhq committed
107
108
  end

109
110
  def build_commit_note(commit)
    notes.new(:noteable_id => commit.id, :noteable_type => "Commit")
gitlabhq's avatar
gitlabhq committed
111
  end
Nihad Abbasov's avatar
Nihad Abbasov committed
112

113
114
  def commit_notes(commit)
    notes.where(:noteable_id => commit.id, :noteable_type => "Commit")
gitlabhq's avatar
gitlabhq committed
115
  end
Nihad Abbasov's avatar
Nihad Abbasov committed
116

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
117
118
119
120
  def has_commits?
    !!commit
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
121
122
  # Compatible with all access rights
  # Should be rewrited for new access rights
gitlabhq's avatar
gitlabhq committed
123
  def add_access(user, *access)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
124
125
126
127
128
129
130
    access = if access.include?(:admin) 
               { :project_access => PROJECT_RWA } 
             elsif access.include?(:write)
               { :project_access => PROJECT_RW } 
             else
               { :project_access => PROJECT_R } 
             end
gitlabhq's avatar
gitlabhq committed
131
    opts = { :user => user }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
132
    opts.merge!(access)
gitlabhq's avatar
gitlabhq committed
133
134
135
136
137
138
139
    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

140
141
142
143
  def repository_readers
    keys = Key.joins({:user => :users_projects}).
      where("users_projects.project_id = ? AND users_projects.repo_access = ?", id, Repository::REPO_R)
    keys.map(&:identifier)
gitlabhq's avatar
gitlabhq committed
144
145
  end

146
  def repository_writers
147
148
    keys = Key.joins({:user => :users_projects}).
      where("users_projects.project_id = ? AND users_projects.repo_access = ?", id, Repository::REPO_RW)
gitlabhq's avatar
gitlabhq committed
149
150
151
152
    keys.map(&:identifier)
  end

  def readers
153
154
155
156
157
    @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)
gitlabhq's avatar
gitlabhq committed
158
159
160
  end

  def admins
161
    @admins ||= users_projects.includes(:user).where(:project_access => PROJECT_RWA).map(&:user)
gitlabhq's avatar
gitlabhq committed
162
163
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
164
165
166
167
168
169
170
171
172
173
174
175
  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

gitlabhq's avatar
gitlabhq committed
176
  def root_ref 
177
    default_branch || "master"
gitlabhq's avatar
gitlabhq committed
178
179
  end

gitlabhq's avatar
gitlabhq committed
180
181
182
183
184
185
186
187
  def public?
    !private_flag
  end

  def private?
    private_flag
  end

188
  def last_activity
gitlabhq's avatar
gitlabhq committed
189
    updates(1).first
190
  rescue
gitlabhq's avatar
gitlabhq committed
191
192
193
194
195
196
197
    nil
  end

  def last_activity_date
    last_activity.try(:created_at)
  end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  # 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)
      Rails.cache.write(activities_key, activities, :expires_in => 60.seconds)
    end

    activities
  end

  # Get 20 events for project like
  # commits, issues or notes
gitlabhq's avatar
gitlabhq committed
215
  def updates(n = 3)
216
    [
gitlabhq's avatar
gitlabhq committed
217
      fresh_commits(n),
gitlabhq's avatar
gitlabhq committed
218
219
      fresh_issues(n),
      fresh_notes(n)
gitlabhq's avatar
gitlabhq committed
220
221
    ].compact.flatten.sort do |x, y|
      y.created_at <=> x.created_at
gitlabhq's avatar
gitlabhq committed
222
    end[0...n]
gitlabhq's avatar
gitlabhq committed
223
224
  end

Valera Sizov's avatar
Valera Sizov committed
225
226
  def check_limit
    unless owner.can_create_project?
gitlabhq's avatar
gitlabhq committed
227
      errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it")
Valera Sizov's avatar
Valera Sizov committed
228
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
229
  rescue
gitlabhq's avatar
gitlabhq committed
230
    errors[:base] << ("Cant check your ability to create project")
Valera Sizov's avatar
Valera Sizov committed
231
232
  end

gitlabhq's avatar
gitlabhq committed
233
  def repo_name
234
235
    if path == "gitolite-admin"
      errors.add(:path, " like 'gitolite-admin' is not allowed")
gitlabhq's avatar
gitlabhq committed
236
237
238
    end
  end

gitlabhq's avatar
gitlabhq committed
239
240
241
242
243
244
245
246
247
248
249
  def valid_repo?
    repo
  rescue
    errors.add(:path, "Invalid repository path")
    false
  end
end
# == Schema Information
#
# Table name: projects
#
Valery Sizov's avatar
Valery Sizov committed
250
251
252
253
254
255
256
257
258
259
#  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
gitlabhq's avatar
gitlabhq committed
260
261
#