summaryrefslogtreecommitdiffstats
path: root/app/models/committership.rb
blob: 1097a458602c3330af25323f171d53f1093b8e44 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# encoding: utf-8
#--
#   Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
#   Copyright (C) 2007 Johan Sørensen <johan@johansorensen.com>
#   Copyright (C) 2008 David A. Cuadrado <krawek@gmail.com>
#   Copyright (C) 2008 Tor Arne Vestbø <tavestbo@trolltech.com>
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU Affero General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU Affero General Public License for more details.
#
#   You should have received a copy of the GNU Affero General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
#++

class Committership < ActiveRecord::Base

  CAN_REVIEW = 1 << 4
  CAN_COMMIT = 1 << 5
  CAN_ADMIN  = 1 << 6

  PERMISSION_TABLE = {
    :review => CAN_REVIEW,
    :commit => CAN_COMMIT,
    :admin => CAN_ADMIN
  }

  belongs_to :committer, :polymorphic => true
  belongs_to :repository
  belongs_to :creator, :class_name => 'User'
  has_many :messages, :as => :notifiable

  validates_presence_of :committer_id, :committer_type, :repository_id
  validates_uniqueness_of :committer_id, :scope => [:committer_type, :repository_id],
    :message => 'is already a committer to this repository'

  attr_protected :permissions

  after_create :notify_repository_owners
  after_create :add_new_committer_event
  after_destroy :add_removed_committer_event
  before_destroy :nullify_messages

  named_scope :groups, :conditions => { :committer_type => "Group" }
  named_scope :users,  :conditions => { :committer_type => "User" }
  named_scope :reviewers, :conditions => ["(permissions & ?)", CAN_REVIEW]
  named_scope :committers, :conditions => ["(permissions & ?)", CAN_COMMIT]
  named_scope :admins, :conditions => ["(permissions & ?)", CAN_ADMIN]

  def self.create_for_owner!(an_owner)
    cs = new({:committer => an_owner})
    cs.permissions = (CAN_REVIEW | CAN_COMMIT | CAN_ADMIN)
    cs.save!
    cs
  end

  def self.create_with_permissions!(attrs, perms)
    cs = new(attrs)
    cs.permissions = perms
    cs.save!
    cs
  end

  def permission_mask_for(*perms)
    perms.inject(0) do |memo, perm_symbol|
      memo | PERMISSION_TABLE[perm_symbol]
    end
  end

  def build_permissions(*perms)
    perms = perms.flatten.compact.map{|p| p.to_sym }
    self.permissions = permission_mask_for(*perms)
  end

  def permitted?(wants_to)
    raise "unknown permission: #{wants_to.inspect}" if !PERMISSION_TABLE[wants_to]
    (self.permissions & PERMISSION_TABLE[wants_to]) != 0
  end

  def reviewer?
    permitted?(:review)
  end

  def committer?
    permitted?(:commit)
  end

  def admin?
    permitted?(:admin)
  end

  def permission_list
    PERMISSION_TABLE.keys.select{|perm| permitted?(perm) }
  end

  def breadcrumb_parent
    Breadcrumb::Committerships.new(repository)
  end

  def title
    new_record? ? "New collaborator" : "Collaborator"
  end

  # returns all the users in this committership, eg if it's a group it'll
  # return an array of the group members, otherwise a single-member array of
  # the user
  def members
    case committer
    when Group
      committer.members
    else
      [committer]
    end
  end

  protected
    def notify_repository_owners
      return unless creator
      recipients = repository.owners
      recipients.each do |r|
        message = Message.new({
          :sender => creator,
          :recipient => r,
          :subject => I18n.t("committership.notification_subject"),
          :body => I18n.t("committership.notification_body", {
            :inviter => creator.title,
            :user => committer.title,
            :repository => repository.name,
            :project => repository.project.title
          }),
          :notifiable => self
        })
        message.save
      end
    end

    def add_new_committer_event
      repository.project.create_event(Action::ADD_COMMITTER, repository,
                                      creator, committer.title)
    end

    def add_removed_committer_event
      return unless repository
      repository.project.create_event(Action::REMOVE_COMMITTER, repository,
                                      creator, committer.title)
    end

    def nullify_messages
      messages.update_all({:notifiable_id => nil, :notifiable_type => nil})
    end
end