acts_as_joinable 1.1.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/app/models/default_permission_set.rb +2 -0
- data/app/models/membership.rb +1 -0
- data/lib/acts_as_joinable/version.rb +3 -0
- data/lib/acts_as_joinable.rb +0 -3
- data/lib/joinable/acts_as_joinable.rb +66 -68
- data/lib/joinable/acts_as_joinable_component.rb +29 -33
- data/lib/joinable/acts_as_permissable.rb +19 -4
- data/lib/joinable/feedable_extensions.rb +3 -1
- data/lib/joinable/permissions_attribute_wrapper.rb +31 -31
- data/lib/tasks/acts_as_joinable_tasks.rake +4 -0
- metadata +41 -7
| @@ -2,6 +2,7 @@ class DefaultPermissionSet < ActiveRecord::Base | |
| 2 2 | 
             
              include Joinable::PermissionsAttributeWrapper
         | 
| 3 3 |  | 
| 4 4 | 
             
            	belongs_to :joinable, :polymorphic => true
         | 
| 5 | 
            +
              has_many :permission_links, lambda { where("#{PermissionLink.table_name}.joinable_type = #{table_name}.joinable_type") }, :primary_key => :joinable_id, :foreign_key => :joinable_id
         | 
| 5 6 |  | 
| 6 7 | 
             
              after_update :raise_existing_member_permissions
         | 
| 7 8 |  | 
| @@ -15,6 +16,7 @@ class DefaultPermissionSet < ActiveRecord::Base | |
| 15 16 | 
             
                end
         | 
| 16 17 | 
             
              end
         | 
| 17 18 |  | 
| 19 | 
            +
              # Easy way to choose basic permission sets
         | 
| 18 20 | 
             
              def access_model=(model)
         | 
| 19 21 | 
             
                case model.to_s
         | 
| 20 22 | 
             
                when 'open'
         | 
    
        data/app/models/membership.rb
    CHANGED
    
    | @@ -3,6 +3,7 @@ class Membership < ActiveRecord::Base | |
| 3 3 |  | 
| 4 4 | 
             
              belongs_to :joinable, :polymorphic => true
         | 
| 5 5 | 
             
              belongs_to :user
         | 
| 6 | 
            +
              has_many :permission_links, lambda { where("#{PermissionLink.table_name}.joinable_type = #{table_name}.joinable_type") }, :primary_key => :joinable_id, :foreign_key => :joinable_id
         | 
| 6 7 |  | 
| 7 8 | 
             
              before_save :prevent_locked_permission_changes, :normalize_owner_permissions
         | 
| 8 9 |  | 
    
        data/lib/acts_as_joinable.rb
    CHANGED
    
    
| @@ -8,20 +8,20 @@ module Joinable #:nodoc: | |
| 8 8 | 
             
                  # These unpacked permissions are put into an array with the singular permissions (eg. find)
         | 
| 9 9 | 
             
                  # and stored in a *permissions* class variable.
         | 
| 10 10 | 
             
                  #
         | 
| 11 | 
            -
                  # In addition, The grouped permissions are stored in a separate * | 
| 11 | 
            +
                  # In addition, The grouped permissions are stored in a separate *component_permissions_hash* class variable.
         | 
| 12 12 | 
             
                  # 
         | 
| 13 13 | 
             
                  # NOTE: The permissions are passed in-order because in the view we expect to find certain permission patterns.
         | 
| 14 14 | 
             
                  #       eg. the simple project permission level is determined by looking for a string of permissions that span
         | 
| 15 15 | 
             
                  #       several components, (labels, writeboards, files, etc...).
         | 
| 16 16 | 
             
                  # TODO: Remove the aforementioned order dependency      
         | 
| 17 17 | 
             
                  def acts_as_joinable(options = {})
         | 
| 18 | 
            -
                    extend ClassMethods | 
| 19 | 
            -
                    include InstanceMethods | 
| 18 | 
            +
                    extend ClassMethods
         | 
| 19 | 
            +
                    include InstanceMethods
         | 
| 20 20 |  | 
| 21 21 | 
             
                    options.assert_valid_keys :component_permissions
         | 
| 22 | 
            -
             | 
| 22 | 
            +
                    self.component_permissions_hash = options[:component_permissions]
         | 
| 23 23 |  | 
| 24 | 
            -
             | 
| 24 | 
            +
                    self.permissions = [:find, :view]
         | 
| 25 25 | 
             
                    add_flattened_component_permissions(options[:component_permissions])
         | 
| 26 26 | 
             
                    self.permissions += [:manage, :own]
         | 
| 27 27 | 
             
                  end
         | 
| @@ -34,12 +34,10 @@ module Joinable #:nodoc: | |
| 34 34 | 
             
                  # eg. {:labels => [:view, :apply, :remove, :create, :delete]} becomes
         | 
| 35 35 | 
             
                  #      [:view_labels, :apply_labels, :remove_labels, :create_labels, :delete_labels]
         | 
| 36 36 | 
             
                  #      and is added to self.permissions
         | 
| 37 | 
            -
                  def add_flattened_component_permissions( | 
| 38 | 
            -
                     | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
              		    end
         | 
| 42 | 
            -
            		    end
         | 
| 37 | 
            +
                  def add_flattened_component_permissions(component_permissions_hash)
         | 
| 38 | 
            +
                    component_permissions_hash.each do |component_name, component_permissions|
         | 
| 39 | 
            +
                      component_permissions.each { |component_permission| self.permissions << "#{component_permission}_#{component_name}".to_sym }
         | 
| 40 | 
            +
                    end
         | 
| 43 41 | 
             
                  end
         | 
| 44 42 | 
             
                end
         | 
| 45 43 |  | 
| @@ -47,38 +45,28 @@ module Joinable #:nodoc: | |
| 47 45 | 
             
                  include Joinable::ActsAsPermissable::ClassMethods
         | 
| 48 46 |  | 
| 49 47 | 
             
                  def self.extended(base)
         | 
| 50 | 
            -
                    base. | 
| 51 | 
            -
             | 
| 52 | 
            -
                    base.has_many :membership_invitations, :as => :joinable, :dependent => :destroy, :before_add => :add_initiator
         | 
| 53 | 
            -
              		  base.has_many :membership_requests, :as => :joinable, :dependent => :destroy
         | 
| 54 | 
            -
              			base.has_many :memberships, :as => :joinable, :dependent => :destroy, :order => "id ASC", :before_remove => :add_initiator
         | 
| 55 | 
            -
             | 
| 56 | 
            -
              			base.has_many :invitees, :class_name => "User", :through => :membership_invitations, :source => :user
         | 
| 57 | 
            -
              		  base.has_many :requestees, :class_name => "User", :through => :membership_requests, :source => :user
         | 
| 58 | 
            -
              		  base.has_many :members, :class_name => "User", :through => :memberships, :source => :user
         | 
| 59 | 
            -
             | 
| 60 | 
            -
              			base.has_many :non_owner_memberships, :as => :joinable, :class_name => 'Membership', :conditions => "permissions NOT LIKE '%own%'"
         | 
| 61 | 
            -
                    base.has_one :owner_membership, :as => :joinable, :class_name => 'Membership', :conditions => "permissions LIKE '%own%'"
         | 
| 62 | 
            -
             | 
| 63 | 
            -
              			base.has_many :permission_links, :as => :joinable, :dependent => :destroy
         | 
| 48 | 
            +
                    base.class_eval do
         | 
| 49 | 
            +
                      cattr_accessor :permissions, :component_permissions_hash
         | 
| 64 50 |  | 
| 65 | 
            -
             | 
| 51 | 
            +
                      has_many :membership_invitations,   :as => :joinable, :dependent => :destroy, :before_add => :add_initiator
         | 
| 52 | 
            +
                      has_many :membership_requests,      :as => :joinable, :dependent => :destroy
         | 
| 53 | 
            +
                      has_many :memberships,              lambda { order :id }, :as => :joinable, :dependent => :destroy, :before_remove => :add_initiator
         | 
| 66 54 |  | 
| 67 | 
            -
             | 
| 55 | 
            +
                      has_many :invitees,                 :class_name => "User", :through => :membership_invitations, :source => :user
         | 
| 56 | 
            +
                      has_many :requestees,               :class_name => "User", :through => :membership_requests, :source => :user
         | 
| 57 | 
            +
                      has_many :members,                  :class_name => "User", :through => :memberships, :source => :user
         | 
| 68 58 |  | 
| 69 | 
            -
             | 
| 70 | 
            -
                       | 
| 71 | 
            -
             | 
| 59 | 
            +
                      has_many :permission_links,         :as => :joinable, :dependent => :destroy
         | 
| 60 | 
            +
                      has_one :default_permission_set,    :as => :joinable, :dependent => :destroy
         | 
| 61 | 
            +
                              
         | 
| 62 | 
            +
                      scope :with_member,                 lambda {|user| joins(:memberships).where(:memberships => {:user_id => user}).order("memberships.created_at DESC") }
         | 
| 72 63 |  | 
| 73 | 
            -
                       | 
| 64 | 
            +
                      accepts_nested_attributes_for :default_permission_set
         | 
| 65 | 
            +
                      accepts_nested_attributes_for :membership_invitations, :allow_destroy => true
         | 
| 66 | 
            +
                      accepts_nested_attributes_for :memberships, :allow_destroy => true, :reject_if => proc { |attributes| attributes['locked'] == 'true' }
         | 
| 74 67 |  | 
| 75 | 
            -
                       | 
| 76 | 
            -
                      scope :with_member, lambda {|user| joins(:memberships).where(:memberships => {:user_id => (user.is_a?(User) ? user.id : user)}).order("memberships.created_at DESC NULLS LAST") }
         | 
| 68 | 
            +
                      after_create :add_owner_membership
         | 
| 77 69 | 
             
                    end
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                    base.accepts_nested_attributes_for :default_permission_set
         | 
| 80 | 
            -
                    base.accepts_nested_attributes_for :membership_invitations, :allow_destroy => true
         | 
| 81 | 
            -
                    base.accepts_nested_attributes_for :memberships, :allow_destroy => true, :reject_if => proc { |attributes| attributes['locked'] == 'true' }
         | 
| 82 70 | 
             
                  end
         | 
| 83 71 |  | 
| 84 72 | 
             
                  def permissions_string
         | 
| @@ -141,36 +129,35 @@ module Joinable #:nodoc: | |
| 141 129 | 
             
                      user_id = user.id
         | 
| 142 130 | 
             
                    end
         | 
| 143 131 |  | 
| 144 | 
            -
                     | 
| 145 | 
            -
                    joinable_id = options[:id_column] || table_name + ".id"
         | 
| 132 | 
            +
                    joinable_id = options[:id_column] || "#{table_name}.id"
         | 
| 146 133 |  | 
| 147 134 | 
             
                    if permission == :find
         | 
| 148 | 
            -
                      "#{membership_permission_exists_sql(user_id,  | 
| 135 | 
            +
                      "#{membership_permission_exists_sql(user_id, joinable_id, 'find')} OR #{membership_invitation_permission_exists_sql(user_id, joinable_id, 'find')} OR #{default_permission_set_permission_exists_sql(joinable_id, 'find')}"
         | 
| 149 136 | 
             
                    elsif permission.to_s.starts_with?('view')
         | 
| 150 | 
            -
                      "#{membership_permission_exists_sql(user_id,  | 
| 137 | 
            +
                      "#{membership_permission_exists_sql(user_id, joinable_id, permission)} OR #{default_permission_set_permission_exists_sql(joinable_id, permission)}"
         | 
| 151 138 | 
             
                    elsif permission == :join
         | 
| 152 | 
            -
                      "#{membership_invitation_permission_exists_sql(user_id,  | 
| 139 | 
            +
                      "#{membership_invitation_permission_exists_sql(user_id, joinable_id, 'view')} OR #{default_permission_set_permission_exists_sql(joinable_id, 'view')}"
         | 
| 153 140 | 
             
                    elsif permission.to_s.starts_with?('join_and_')
         | 
| 154 | 
            -
                      default_permission_set_permission_exists_sql( | 
| 141 | 
            +
                      default_permission_set_permission_exists_sql(joinable_id, permission.to_s.gsub('join_and_', ''))
         | 
| 155 142 | 
             
                    elsif permission == :collaborate
         | 
| 156 | 
            -
                      "EXISTS (SELECT id FROM memberships WHERE memberships.joinable_type = '#{ | 
| 143 | 
            +
                      "EXISTS (SELECT id FROM memberships WHERE memberships.joinable_type = '#{self.name}' AND memberships.joinable_id = #{joinable_id} AND memberships.user_id = #{user_id} AND memberships.permissions && '{#{(collaborator_permissions - viewer_permissions).join(",")}}')"
         | 
| 157 144 | 
             
                    else
         | 
| 158 | 
            -
                      membership_permission_exists_sql(user_id,  | 
| 145 | 
            +
                      membership_permission_exists_sql(user_id, joinable_id, permission)
         | 
| 159 146 | 
             
                    end
         | 
| 160 147 | 
             
                  end
         | 
| 161 148 |  | 
| 162 149 | 
             
                  private
         | 
| 163 150 |  | 
| 164 | 
            -
                  def membership_permission_exists_sql(user_id,  | 
| 165 | 
            -
                    "EXISTS (SELECT id FROM memberships WHERE memberships.joinable_type = '#{ | 
| 151 | 
            +
                  def membership_permission_exists_sql(user_id, joinable_id, permission)
         | 
| 152 | 
            +
                    "EXISTS (SELECT id FROM memberships WHERE memberships.joinable_type = '#{self.name}' AND memberships.joinable_id = #{joinable_id} AND memberships.user_id = #{user_id} AND #{permission_sql_condition('memberships.permissions', permission)})"
         | 
| 166 153 | 
             
                  end
         | 
| 167 154 |  | 
| 168 | 
            -
                  def membership_invitation_permission_exists_sql(user_id,  | 
| 169 | 
            -
                    "EXISTS (SELECT id FROM membership_invitations WHERE membership_invitations.joinable_type = '#{ | 
| 155 | 
            +
                  def membership_invitation_permission_exists_sql(user_id, joinable_id, permission)
         | 
| 156 | 
            +
                    "EXISTS (SELECT id FROM membership_invitations WHERE membership_invitations.joinable_type = '#{self.name}' AND membership_invitations.joinable_id = #{joinable_id} AND membership_invitations.user_id = #{user_id} AND #{permission_sql_condition('membership_invitations.permissions', permission)})"
         | 
| 170 157 | 
             
                  end
         | 
| 171 158 |  | 
| 172 | 
            -
                  def default_permission_set_permission_exists_sql( | 
| 173 | 
            -
                    "EXISTS (SELECT id FROM default_permission_sets WHERE default_permission_sets.joinable_type = '#{ | 
| 159 | 
            +
                  def default_permission_set_permission_exists_sql(joinable_id, permission)
         | 
| 160 | 
            +
                    "EXISTS (SELECT id FROM default_permission_sets WHERE default_permission_sets.joinable_type = '#{self.name}' AND default_permission_sets.joinable_id = #{joinable_id} AND #{permission_sql_condition('default_permission_sets.permissions', permission)})"
         | 
| 174 161 | 
             
                  end
         | 
| 175 162 | 
             
                end
         | 
| 176 163 |  | 
| @@ -241,28 +228,35 @@ module Joinable #:nodoc: | |
| 241 228 | 
             
                  # user. This method also supports caching of a membership
         | 
| 242 229 | 
             
                  # request in order to facilitate eager loading.
         | 
| 243 230 | 
             
                  #NOTE: See :membership_request_for documentation for an in depth example of this type of behaviour
         | 
| 244 | 
            -
                  def membership_for( | 
| 231 | 
            +
                  def membership_for(user_id)
         | 
| 245 232 | 
             
                    if cached_membership != nil
         | 
| 246 233 | 
             
                      cached_membership
         | 
| 247 234 | 
             
                    else
         | 
| 248 | 
            -
                      memberships.where(:user_id =>  | 
| 235 | 
            +
                      memberships.where(:user_id => user_id).first
         | 
| 249 236 | 
             
                    end
         | 
| 250 237 | 
             
                  end
         | 
| 251 238 |  | 
| 252 239 | 
             
                  # Find out whether this Joinable has a membership for a certain user and return true, else false
         | 
| 253 | 
            -
                  def membership_for?( | 
| 254 | 
            -
                    !membership_for( | 
| 240 | 
            +
                  def membership_for?(user_id)
         | 
| 241 | 
            +
                    !membership_for(user_id).nil?
         | 
| 255 242 | 
             
                  end
         | 
| 256 243 |  | 
| 257 | 
            -
                  # Returns  | 
| 258 | 
            -
                  def  | 
| 259 | 
            -
                    memberships.maximum(:updated_at)
         | 
| 244 | 
            +
                  # Returns a unique cache key any time the memberships were updated for this joinable
         | 
| 245 | 
            +
                  def memberships_cache_key
         | 
| 246 | 
            +
                    "#{memberships.size}_#{memberships.maximum(:updated_at).to_f}"
         | 
| 260 247 | 
             
                  end
         | 
| 261 248 |  | 
| 262 | 
            -
                   | 
| 249 | 
            +
                  # Convenience method for reading or writing the default permission set's access_model
         | 
| 250 | 
            +
                  delegate :access_model, 'access_model=', :to => :default_permission_set
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                  # Convenience method for reading the default permission set's access_model
         | 
| 253 | 
            +
                  def default_permissions
         | 
| 254 | 
            +
                    self.default_permission_set.permissions
         | 
| 255 | 
            +
                  end
         | 
| 263 256 |  | 
| 264 | 
            -
                   | 
| 265 | 
            -
             | 
| 257 | 
            +
                  # Convenience method for writing the default permission set's access_model
         | 
| 258 | 
            +
                  def default_permissions=(permissions)
         | 
| 259 | 
            +
                    self.default_permission_set.permissions = permissions
         | 
| 266 260 | 
             
                  end
         | 
| 267 261 |  | 
| 268 262 | 
             
                  # Returns true or false depending on whether or not the user has the specified permission for this object.
         | 
| @@ -287,8 +281,8 @@ module Joinable #:nodoc: | |
| 287 281 |  | 
| 288 282 | 
             
                    cache_path = "permissions/#{self.class.table_name}/#{self.id}/user_#{user.id}_#{key}"
         | 
| 289 283 |  | 
| 290 | 
            -
                    if defined?( | 
| 291 | 
            -
                      permissions =  | 
| 284 | 
            +
                    if defined?(Rails.cache)
         | 
| 285 | 
            +
                      permissions = Rails.cache.read(cache_path)
         | 
| 292 286 | 
             
                      if permissions && (value = permissions[permission_name]) != nil
         | 
| 293 287 | 
             
                        return value
         | 
| 294 288 | 
             
                      end
         | 
| @@ -297,18 +291,23 @@ module Joinable #:nodoc: | |
| 297 291 | 
             
                    # The permission isn't cached yet, so cache it
         | 
| 298 292 | 
             
                    value = self.class.with_permission(user, permission_name).exists?(self.id)
         | 
| 299 293 |  | 
| 300 | 
            -
                    if defined?( | 
| 294 | 
            +
                    if defined?(Rails.cache)
         | 
| 301 295 | 
             
                      if permissions
         | 
| 302 296 | 
             
                        permissions = permissions.dup
         | 
| 303 297 | 
             
                        permissions[permission_name] = value
         | 
| 304 298 | 
             
                      else
         | 
| 305 299 | 
             
                        permissions = {permission_name => value}
         | 
| 306 300 | 
             
                      end
         | 
| 307 | 
            -
                       | 
| 301 | 
            +
                      Rails.cache.write(cache_path, permissions)
         | 
| 308 302 | 
             
                    end
         | 
| 309 303 | 
             
                    return value
         | 
| 310 304 | 
             
                  end
         | 
| 311 305 |  | 
| 306 | 
            +
                  # Ensure the joinable has a set of default permissions (an empty set unless one already exists)
         | 
| 307 | 
            +
                  def default_permission_set
         | 
| 308 | 
            +
                    super || self.build_default_permission_set
         | 
| 309 | 
            +
                  end
         | 
| 310 | 
            +
             | 
| 312 311 | 
             
                  private
         | 
| 313 312 |  | 
| 314 313 | 
             
                  # Adds an initiator to a membership or invitation to possibly use in feed generation
         | 
| @@ -318,9 +317,8 @@ module Joinable #:nodoc: | |
| 318 317 |  | 
| 319 318 | 
             
                  # Adds an permission entry with full access to the object by the user associated with the object if one does not already exist
         | 
| 320 319 | 
             
                  def add_owner_membership
         | 
| 321 | 
            -
                   Membership. | 
| 320 | 
            +
                   Membership.where(:joinable => self, :user => user).first_or_create!(:permissions => self.class.permissions_string)
         | 
| 322 321 | 
             
                  end
         | 
| 323 322 | 
             
                end
         | 
| 324 323 | 
             
              end
         | 
| 325 | 
            -
            end
         | 
| 326 | 
            -
             | 
| 324 | 
            +
            end
         | 
| @@ -46,10 +46,6 @@ module Joinable #:nodoc: | |
| 46 46 | 
             
                  def self.extended(base)        
         | 
| 47 47 | 
             
                    base.has_one :permission_link, :as => :component, :dependent => :destroy
         | 
| 48 48 | 
             
                    base.after_create :find_joinable_and_create_permission_link
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                    base.class_eval do
         | 
| 51 | 
            -
                      scope :with_permission, lambda { |user, permission| select("#{table_name}.*").where(with_permission_sql(user, permission)) }
         | 
| 52 | 
            -
                    end
         | 
| 53 49 | 
             
                  end
         | 
| 54 50 |  | 
| 55 51 | 
             
                  # Returns the SQL necessary to find all components for which there is no associated joinable or 
         | 
| @@ -72,49 +68,50 @@ module Joinable #:nodoc: | |
| 72 68 | 
             
                      user_id = user.id
         | 
| 73 69 | 
             
                    end
         | 
| 74 70 |  | 
| 75 | 
            -
                     | 
| 76 | 
            -
                    component_id = options[:id_column] || table_name + ".id"
         | 
| 71 | 
            +
                    component_id_column = options[:id_column] || "#{table_name}.id"
         | 
| 77 72 |  | 
| 78 73 | 
             
                    permission_without_join_and_prefix = permission.gsub('join_and_', '')
         | 
| 79 | 
            -
                     | 
| 74 | 
            +
                    permission_or_column = permission_without_join_and_prefix == 'view' ? "permission_links.component_view_permission" : "'#{permission_without_join_and_prefix}'"
         | 
| 80 75 |  | 
| 81 76 | 
             
                    if permission.starts_with?('view')
         | 
| 82 | 
            -
                      "#{no_inherited_permissions_exist_sql( | 
| 77 | 
            +
                      "#{no_inherited_permissions_exist_sql(component_id_column)} OR #{membership_permission_exists_sql(user_id, component_id_column, permission_or_column)} OR #{default_permission_set_permission_exists_sql(component_id_column, permission_or_column)}"
         | 
| 83 78 | 
             
                    elsif permission.starts_with?('join_and_')
         | 
| 84 | 
            -
                      default_permission_set_permission_exists_sql( | 
| 79 | 
            +
                      default_permission_set_permission_exists_sql(component_id_column, permission_or_column)
         | 
| 85 80 | 
             
                    else
         | 
| 86 | 
            -
                      "#{no_inherited_permissions_exist_sql( | 
| 81 | 
            +
                      "#{no_inherited_permissions_exist_sql(component_id_column)} OR #{membership_permission_exists_sql(user_id, component_id_column, permission_or_column)}"
         | 
| 87 82 | 
             
                    end
         | 
| 88 83 | 
             
                  end
         | 
| 89 84 |  | 
| 90 85 | 
             
                  private
         | 
| 91 86 |  | 
| 92 87 | 
             
                  # All components that don't have a permission link to a joinable
         | 
| 93 | 
            -
                  def no_inherited_permissions_exist_sql( | 
| 94 | 
            -
                     | 
| 88 | 
            +
                  def no_inherited_permissions_exist_sql(component_id_column)
         | 
| 89 | 
            +
                    permission_links_for_joinable = PermissionLink.where(:component_type => self.name).where("permission_links.component_id = #{component_id_column}")
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    return "NOT EXISTS (#{ permission_links_for_joinable.to_sql })"
         | 
| 95 92 | 
             
                  end
         | 
| 96 93 |  | 
| 97 94 | 
             
                  # All components that have an associated membership with a specific permission
         | 
| 98 95 | 
             
                  #
         | 
| 99 96 | 
             
                  # The view permission requires special handling because it may be customized in the permission_link.
         | 
| 100 97 | 
             
                  # For more information see the *recurse_to_inherit_custom_view_permission* method.
         | 
| 101 | 
            -
                  def membership_permission_exists_sql(user_id,  | 
| 102 | 
            -
                     | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 98 | 
            +
                  def membership_permission_exists_sql(user_id, component_id_column, permission_or_column)
         | 
| 99 | 
            +
                    memberships_allowing_permission = Membership.joins(:permission_links)
         | 
| 100 | 
            +
                      .where(:user_id => user_id)
         | 
| 101 | 
            +
                      .where("permission_links.component_type" => self.name)
         | 
| 102 | 
            +
                      .where("permission_links.component_id = #{component_id_column}")
         | 
| 103 | 
            +
                      .where(permission_sql_condition('memberships.permissions', permission_or_column, :raw => true))
         | 
| 104 | 
            +
                    
         | 
| 105 | 
            +
                    return "EXISTS (#{ memberships_allowing_permission.to_sql })"
         | 
| 109 106 | 
             
                  end
         | 
| 110 107 |  | 
| 111 | 
            -
                  def default_permission_set_permission_exists_sql( | 
| 112 | 
            -
                     | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 108 | 
            +
                  def default_permission_set_permission_exists_sql(component_id_column, permission_or_column)
         | 
| 109 | 
            +
                    sets_allowing_permission = DefaultPermissionSet.joins(:permission_links)
         | 
| 110 | 
            +
                      .where("permission_links.component_type" => self.name)
         | 
| 111 | 
            +
                      .where("permission_links.component_id = #{component_id_column}")
         | 
| 112 | 
            +
                      .where(permission_sql_condition('default_permission_sets.permissions', permission_or_column, :raw => true))
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                    return "EXISTS (#{ sets_allowing_permission.to_sql })"
         | 
| 118 115 | 
             
                  end
         | 
| 119 116 | 
             
                end
         | 
| 120 117 |  | 
| @@ -130,11 +127,9 @@ module Joinable #:nodoc: | |
| 130 127 | 
             
                  # Useful for outputting information to the user while they 
         | 
| 131 128 | 
             
                  # are creating a new component.
         | 
| 132 129 | 
             
                  def who_will_be_able_to_view?
         | 
| 133 | 
            -
                    User. | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
                                      AND memberships.joinable_id = #{joinable.id} 
         | 
| 137 | 
            -
                                      AND #{self.class.permission_sql_condition('memberships.permissions', recurse_to_inherit_custom_view_permission)}")
         | 
| 130 | 
            +
                    User.joins(:memberships)
         | 
| 131 | 
            +
                      .where(:memberships => {:joinable => joinable})
         | 
| 132 | 
            +
                      .where(permission_sql_condition('memberships.permissions', recurse_to_inherit_custom_view_permission))
         | 
| 138 133 | 
             
                  end
         | 
| 139 134 |  | 
| 140 135 | 
             
                  def check_permission(user, permission)
         | 
| @@ -196,6 +191,7 @@ module Joinable #:nodoc: | |
| 196 191 | 
             
                  end
         | 
| 197 192 | 
             
                  attr_writer :view_permission
         | 
| 198 193 |  | 
| 194 | 
            +
                  private
         | 
| 199 195 |  | 
| 200 196 | 
             
                  # Recurse up the tree to see if any of the intervening joinable_components have a customized view permission
         | 
| 201 197 | 
             
                  # In that case, inherit that customized view permission. This allows searches of the form
         | 
| @@ -208,7 +204,7 @@ module Joinable #:nodoc: | |
| 208 204 | 
             
                    if parent.acts_like?(:joinable) || self.view_permission
         | 
| 209 205 | 
             
                      return self.view_permission || :view
         | 
| 210 206 | 
             
                    elsif parent.acts_like?(:joinable_component)
         | 
| 211 | 
            -
                      return parent.recurse_to_inherit_custom_view_permission
         | 
| 207 | 
            +
                      return parent.send(:recurse_to_inherit_custom_view_permission)
         | 
| 212 208 | 
             
                    else
         | 
| 213 209 | 
             
                      return nil
         | 
| 214 210 | 
             
                    end
         | 
| @@ -10,9 +10,22 @@ module Joinable #:nodoc: | |
| 10 10 | 
             
              		  return record
         | 
| 11 11 | 
             
              		end
         | 
| 12 12 |  | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 13 | 
            +
                  # Returns all records where the given user has the given permission
         | 
| 14 | 
            +
                  def with_permission(user, permission)
         | 
| 15 | 
            +
                    where(with_permission_sql(user, permission))
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  # Returns an SQL fragment for a WHERE condition that evaluates to true if the user has the given permission
         | 
| 19 | 
            +
                  # For use when asking 
         | 
| 20 | 
            +
                  def with_permission_sql(user, permission, options = {})
         | 
| 21 | 
            +
                    raise NotImplementedError
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  # Returns an SQL fragment for a WHERE condition that checks the given column for the given permission
         | 
| 25 | 
            +
                  def permission_sql_condition(column, permission, options = {})
         | 
| 26 | 
            +
                    permission = "'#{permission}'" unless options[:raw]
         | 
| 27 | 
            +
                    "#{permission} = ANY(#{column})"
         | 
| 28 | 
            +
                  end
         | 
| 16 29 | 
             
                end
         | 
| 17 30 |  | 
| 18 31 | 
             
                module InstanceMethods
         | 
| @@ -22,8 +35,10 @@ module Joinable #:nodoc: | |
| 22 35 |  | 
| 23 36 | 
             
                  # Returns a list of users who either do or do not have the specified permission.
         | 
| 24 37 | 
             
                  def who_can?(permission)
         | 
| 25 | 
            -
                    User. | 
| 38 | 
            +
                    User.where(with_permission_sql("#{User.table_name}.id", permission, :id_column => id))
         | 
| 26 39 | 
             
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  delegate :with_permission_sql, :permission_sql_condition, :to => 'self.class'
         | 
| 27 42 | 
             
                end
         | 
| 28 43 | 
             
              end
         | 
| 29 44 | 
             
            end
         | 
| @@ -2,12 +2,14 @@ module FeedableExtensions | |
| 2 2 | 
             
              def self.add
         | 
| 3 3 | 
             
                Feed.class_eval do
         | 
| 4 4 | 
             
                  # Filter feeds about public joinables that you haven't joined, unless the feed is actually about you
         | 
| 5 | 
            -
                  scope : | 
| 5 | 
            +
                  scope :not_from_unjoined, lambda {|joinable_type, user| 
         | 
| 6 6 | 
             
                    where("feeds.scoping_object_type IS NULL OR
         | 
| 7 7 | 
             
                           feeds.scoping_object_type != '#{joinable_type}' OR
         | 
| 8 8 | 
             
                           (feeds.feedable_type = 'User' AND feeds.feedable_id = #{user.id}) OR
         | 
| 9 9 | 
             
                           EXISTS (SELECT * FROM memberships WHERE memberships.joinable_type = '#{joinable_type}' AND memberships.joinable_id = feeds.scoping_object_id AND memberships.user_id = ?)", user.id)
         | 
| 10 10 | 
             
                  }
         | 
| 11 | 
            +
                  # Filter feeds about public things where the given action took place
         | 
| 12 | 
            +
                  scope :not_unscoped, lambda {|action| where('NOT (feeds.scoping_object_type IS NULL AND feeds.action = ?)', action) }
         | 
| 11 13 |  | 
| 12 14 | 
             
                  acts_as_joinable_component :parent => 'permission_inheritance_target', :polymorphic => true, :view_permission => lambda {|feed| :find if feed.feedable.acts_like?(:joinable) }
         | 
| 13 15 |  | 
| @@ -14,13 +14,13 @@ module Joinable #:nodoc: | |
| 14 14 |  | 
| 15 15 | 
             
                # Returns an array of the permissions as symbols
         | 
| 16 16 | 
             
                def permissions
         | 
| 17 | 
            -
                  self[:permissions].collect(&:to_sym)
         | 
| 17 | 
            +
                  Array.wrap(self[:permissions]).collect(&:to_sym)
         | 
| 18 18 | 
             
                end
         | 
| 19 19 |  | 
| 20 20 | 
             
                def permissions=(permissions)
         | 
| 21 21 | 
             
                  case permissions
         | 
| 22 | 
            -
                  when String
         | 
| 23 | 
            -
                    self[:permissions] = permissions.split(' ')
         | 
| 22 | 
            +
                  when String, Symbol
         | 
| 23 | 
            +
                    self[:permissions] = permissions.to_s.split(' ')
         | 
| 24 24 | 
             
                  when Array
         | 
| 25 25 | 
             
                    self[:permissions] = permissions
         | 
| 26 26 | 
             
                  else
         | 
| @@ -98,6 +98,31 @@ module Joinable #:nodoc: | |
| 98 98 | 
             
                  self.permissions += permissions_to_grant
         | 
| 99 99 | 
             
                end
         | 
| 100 100 |  | 
| 101 | 
            +
                # Adds readers for component permission groups and single permissions
         | 
| 102 | 
            +
                #
         | 
| 103 | 
            +
                # Used by advanced permission forms to determine how which options to select
         | 
| 104 | 
            +
                # in the various fields. (eg. which option of f.select :labels_permissions to choose)
         | 
| 105 | 
            +
                def method_missing(method_name, *args, &block)      
         | 
| 106 | 
            +
                  # NOTE: As of Rails 4, respond_to? must be checked inside the case statement otherwise it breaks regular AR attribute accessors
         | 
| 107 | 
            +
                  case method_name.to_s
         | 
| 108 | 
            +
                  when /.+_permissions/
         | 
| 109 | 
            +
                    return component_permissions_reader(method_name) if respond_to?(:joinable_type) && joinable_type.present?
         | 
| 110 | 
            +
                  when /.+_permission/
         | 
| 111 | 
            +
                    return single_permission_reader(method_name) if respond_to?(:joinable_type) && joinable_type.present?
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  super
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                def respond_to?(method_name, include_private = false)
         | 
| 118 | 
            +
                  case method_name.to_s
         | 
| 119 | 
            +
                  when /.+_permissions?/
         | 
| 120 | 
            +
                    return true if respond_to?(:joinable_type) && joinable_type.present?
         | 
| 121 | 
            +
                  end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                  super
         | 
| 124 | 
            +
                end    
         | 
| 125 | 
            +
             | 
| 101 126 | 
             
                private
         | 
| 102 127 |  | 
| 103 128 | 
             
                # Verifies that all the access levels are valid for the attached permissible
         | 
| @@ -112,37 +137,12 @@ module Joinable #:nodoc: | |
| 112 137 | 
             
                  self.permissions = permissions.uniq.sort_by { |permission| allowed_permissions.index(permission) }
         | 
| 113 138 | 
             
                end
         | 
| 114 139 |  | 
| 115 | 
            -
                # Adds readers for component permission groups and single permissions
         | 
| 116 | 
            -
                #
         | 
| 117 | 
            -
                # Used by advanced permission forms to determine how which options to select
         | 
| 118 | 
            -
                # in the various fields. (eg. which option of f.select :labels_permissions to choose)
         | 
| 119 | 
            -
                def method_missing(method_name, *args)
         | 
| 120 | 
            -
                  # add permission_for accessors and mutators
         | 
| 121 | 
            -
                
         | 
| 122 | 
            -
                  # NOTE: Don't mess with the method_name variable (e.g. change it to a string)
         | 
| 123 | 
            -
                  # since upstream methods might assume it is a symbol.
         | 
| 124 | 
            -
                  # NOTE: Ensure we enforce some characters before the '_permission' suffix because Rails 3 creates 
         | 
| 125 | 
            -
                  if respond_to?(:joinable_type) && joinable_type.present?
         | 
| 126 | 
            -
                    if method_name.to_s =~ /.+_permissions/
         | 
| 127 | 
            -
                      return component_permissions_reader(method_name)
         | 
| 128 | 
            -
                    elsif method_name.to_s =~ /.+_permission/
         | 
| 129 | 
            -
                      return single_permission_reader(method_name)
         | 
| 130 | 
            -
                    else
         | 
| 131 | 
            -
                      super
         | 
| 132 | 
            -
                    end
         | 
| 133 | 
            -
                  else
         | 
| 134 | 
            -
                    super
         | 
| 135 | 
            -
                  end
         | 
| 136 | 
            -
                end
         | 
| 137 | 
            -
              
         | 
| 138 140 | 
             
                # Get a string of all of the permissions the object has for a specific joinable component
         | 
| 139 141 | 
             
                # eg. labels_permissions # returns 'view_labels apply_labels remove_labels'
         | 
| 140 142 | 
             
                def component_permissions_reader(method_name)
         | 
| 141 | 
            -
                   | 
| 142 | 
            -
                     | 
| 143 | 
            -
                       | 
| 144 | 
            -
                        return component_permissions.collect {|permission| "#{permission}_#{component_name}"}.select {|permission| has_permission?(permission)}.join(" ")
         | 
| 145 | 
            -
                      end
         | 
| 143 | 
            +
                  joinable_type.constantize.component_permissions_hash.each do |component_name, component_permissions|
         | 
| 144 | 
            +
                    if method_name.to_s == "#{component_name}_permissions"
         | 
| 145 | 
            +
                      return component_permissions.collect {|permission| "#{permission}_#{component_name}"}.select {|permission| has_permission?(permission)}.join(" ")
         | 
| 146 146 | 
             
                    end
         | 
| 147 147 | 
             
                  end
         | 
| 148 148 |  | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: acts_as_joinable
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.1 | 
| 4 | 
            +
              version: 1.3.1
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -10,16 +10,32 @@ authors: | |
| 10 10 | 
             
            autorequire: 
         | 
| 11 11 | 
             
            bindir: bin
         | 
| 12 12 | 
             
            cert_chain: []
         | 
| 13 | 
            -
            date:  | 
| 13 | 
            +
            date: 2013-08-14 00:00:00.000000000 Z
         | 
| 14 14 | 
             
            dependencies:
         | 
| 15 15 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 16 | 
            -
              name:  | 
| 16 | 
            +
              name: pg
         | 
| 17 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 18 | 
            +
                none: false
         | 
| 19 | 
            +
                requirements:
         | 
| 20 | 
            +
                - - '>='
         | 
| 21 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 22 | 
            +
                    version: '0'
         | 
| 23 | 
            +
              type: :runtime
         | 
| 24 | 
            +
              prerelease: false
         | 
| 25 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 26 | 
            +
                none: false
         | 
| 27 | 
            +
                requirements:
         | 
| 28 | 
            +
                - - '>='
         | 
| 29 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 30 | 
            +
                    version: '0'
         | 
| 31 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 32 | 
            +
              name: rails
         | 
| 17 33 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 18 34 | 
             
                none: false
         | 
| 19 35 | 
             
                requirements:
         | 
| 20 36 | 
             
                - - ~>
         | 
| 21 37 | 
             
                  - !ruby/object:Gem::Version
         | 
| 22 | 
            -
                    version: 0 | 
| 38 | 
            +
                    version: '4.0'
         | 
| 23 39 | 
             
              type: :runtime
         | 
| 24 40 | 
             
              prerelease: false
         | 
| 25 41 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| @@ -27,7 +43,23 @@ dependencies: | |
| 27 43 | 
             
                requirements:
         | 
| 28 44 | 
             
                - - ~>
         | 
| 29 45 | 
             
                  - !ruby/object:Gem::Version
         | 
| 30 | 
            -
                    version: 0 | 
| 46 | 
            +
                    version: '4.0'
         | 
| 47 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 48 | 
            +
              name: sqlite3
         | 
| 49 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 50 | 
            +
                none: false
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - '>='
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '0'
         | 
| 55 | 
            +
              type: :development
         | 
| 56 | 
            +
              prerelease: false
         | 
| 57 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                none: false
         | 
| 59 | 
            +
                requirements:
         | 
| 60 | 
            +
                - - '>='
         | 
| 61 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 62 | 
            +
                    version: '0'
         | 
| 31 63 | 
             
            description: Adds access control to objects by giving them members, each with configurable
         | 
| 32 64 | 
             
              permissions.
         | 
| 33 65 | 
             
            email: technical@rrnpilot.org
         | 
| @@ -40,6 +72,7 @@ files: | |
| 40 72 | 
             
            - app/models/membership_invitation.rb
         | 
| 41 73 | 
             
            - app/models/membership_request.rb
         | 
| 42 74 | 
             
            - app/models/permission_link.rb
         | 
| 75 | 
            +
            - lib/acts_as_joinable/version.rb
         | 
| 43 76 | 
             
            - lib/acts_as_joinable.rb
         | 
| 44 77 | 
             
            - lib/joinable/acts_as_joinable.rb
         | 
| 45 78 | 
             
            - lib/joinable/acts_as_joinable_component.rb
         | 
| @@ -47,6 +80,7 @@ files: | |
| 47 80 | 
             
            - lib/joinable/acts_as_permissable.rb
         | 
| 48 81 | 
             
            - lib/joinable/feedable_extensions.rb
         | 
| 49 82 | 
             
            - lib/joinable/permissions_attribute_wrapper.rb
         | 
| 83 | 
            +
            - lib/tasks/acts_as_joinable_tasks.rake
         | 
| 50 84 | 
             
            - README.rdoc
         | 
| 51 85 | 
             
            homepage: http://github.com/rrn/acts_as_joinable
         | 
| 52 86 | 
             
            licenses: []
         | 
| @@ -57,13 +91,13 @@ require_paths: | |
| 57 91 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 58 92 | 
             
              none: false
         | 
| 59 93 | 
             
              requirements:
         | 
| 60 | 
            -
              - -  | 
| 94 | 
            +
              - - '>='
         | 
| 61 95 | 
             
                - !ruby/object:Gem::Version
         | 
| 62 96 | 
             
                  version: '0'
         | 
| 63 97 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 64 98 | 
             
              none: false
         | 
| 65 99 | 
             
              requirements:
         | 
| 66 | 
            -
              - -  | 
| 100 | 
            +
              - - '>='
         | 
| 67 101 | 
             
                - !ruby/object:Gem::Version
         | 
| 68 102 | 
             
                  version: '0'
         | 
| 69 103 | 
             
            requirements: []
         |