2011-02-01 09:57:29 +08:00
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas 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, version 3 of the License.
#
# Canvas 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 Group < ActiveRecord :: Base
include Context
include Workflow
2012-05-30 06:55:40 +08:00
include CustomValidations
2012-06-07 04:16:55 +08:00
include UserFollow :: FollowedItem
2012-05-30 06:55:40 +08:00
2012-06-02 00:24:41 +08:00
attr_accessible :name , :context , :max_membership , :group_category , :join_level , :default_view , :description , :is_public , :avatar_attachment
2012-06-07 01:51:49 +08:00
validates_allowed_transitions :is_public , false = > true
2011-02-01 09:57:29 +08:00
2011-02-09 01:15:49 +08:00
has_many :group_memberships , :dependent = > :destroy , :conditions = > [ 'group_memberships.workflow_state != ?' , 'deleted' ]
has_many :users , :through = > :group_memberships , :conditions = > [ 'users.workflow_state != ?' , 'deleted' ]
2011-02-01 09:57:29 +08:00
has_many :participating_group_memberships , :class_name = > " GroupMembership " , :conditions = > [ 'group_memberships.workflow_state = ?' , 'accepted' ]
has_many :participating_users , :source = > :user , :through = > :participating_group_memberships
belongs_to :context , :polymorphic = > true
2011-09-22 04:31:36 +08:00
belongs_to :group_category
2011-02-01 09:57:29 +08:00
belongs_to :account
2011-06-03 23:54:00 +08:00
belongs_to :root_account , :class_name = > " Account "
2011-02-01 09:57:29 +08:00
has_many :calendar_events , :as = > :context , :dependent = > :destroy
has_many :discussion_topics , :as = > :context , :conditions = > [ 'discussion_topics.workflow_state != ?' , 'deleted' ] , :include = > :user , :dependent = > :destroy , :order = > 'discussion_topics.position DESC, discussion_topics.created_at DESC'
has_many :active_discussion_topics , :as = > :context , :class_name = > 'DiscussionTopic' , :conditions = > [ 'discussion_topics.workflow_state != ?' , 'deleted' ] , :include = > :user
has_many :all_discussion_topics , :as = > :context , :class_name = > " DiscussionTopic " , :include = > :user , :dependent = > :destroy
has_many :discussion_entries , :through = > :discussion_topics , :include = > [ :discussion_topic , :user ] , :dependent = > :destroy
has_many :announcements , :as = > :context , :class_name = > 'Announcement' , :dependent = > :destroy
has_many :active_announcements , :as = > :context , :class_name = > 'Announcement' , :conditions = > [ 'discussion_topics.workflow_state != ?' , 'deleted' ]
2011-09-29 07:26:18 +08:00
has_many :attachments , :as = > :context , :dependent = > :destroy , :extend = > Attachment :: FindInContextAssociation
2011-07-12 02:25:54 +08:00
has_many :active_images , :as = > :context , :class_name = > 'Attachment' , :conditions = > [ " attachments.file_state != ? AND attachments.content_type LIKE 'image%' " , 'deleted' ] , :order = > 'attachments.display_name' , :include = > :thumbnail
2011-02-01 09:57:29 +08:00
has_many :active_assignments , :as = > :context , :class_name = > 'Assignment' , :conditions = > [ 'assignments.workflow_state != ?' , 'deleted' ]
has_many :all_attachments , :as = > 'context' , :class_name = > 'Attachment'
has_many :folders , :as = > :context , :dependent = > :destroy , :order = > 'folders.name'
has_many :active_folders , :class_name = > 'Folder' , :as = > :context , :conditions = > [ 'folders.workflow_state != ?' , 'deleted' ] , :order = > 'folders.name'
2011-03-02 03:17:42 +08:00
has_many :active_folders_with_sub_folders , :class_name = > 'Folder' , :as = > :context , :include = > [ :active_sub_folders ] , :conditions = > [ 'folders.workflow_state != ?' , 'deleted' ] , :order = > 'folders.name'
2011-02-01 09:57:29 +08:00
has_many :active_folders_detailed , :class_name = > 'Folder' , :as = > :context , :include = > [ :active_sub_folders , :active_file_attachments ] , :conditions = > [ 'folders.workflow_state != ?' , 'deleted' ] , :order = > 'folders.name'
2012-12-19 05:59:09 +08:00
has_many :collaborators
2011-02-01 09:57:29 +08:00
has_many :external_feeds , :as = > :context , :dependent = > :destroy
has_many :messages , :as = > :context , :dependent = > :destroy
belongs_to :wiki
has_many :web_conferences , :as = > :context , :dependent = > :destroy
has_many :collaborations , :as = > :context , :order = > 'title, created_at' , :dependent = > :destroy
has_one :scribd_account , :as = > :scribdable
has_many :media_objects , :as = > :context
2011-12-10 06:37:12 +08:00
has_many :zip_file_imports , :as = > :context
2012-05-18 05:48:07 +08:00
has_many :collections , :as = > :context
2012-06-02 00:24:41 +08:00
belongs_to :avatar_attachment , :class_name = > " Attachment "
2012-06-07 04:16:55 +08:00
has_many :following_user_follows , :class_name = > 'UserFollow' , :as = > :followed_item
has_many :user_follows , :foreign_key = > 'following_user_id'
2012-01-04 04:30:49 +08:00
2011-09-22 04:31:36 +08:00
before_save :ensure_defaults , :maintain_category_attribute
2011-02-01 09:57:29 +08:00
after_save :close_memberships_if_deleted
2012-01-04 04:30:49 +08:00
2011-09-22 01:36:45 +08:00
include StickySisFields
are_sis_sticky :name
2011-07-14 00:24:17 +08:00
2012-03-03 08:05:06 +08:00
alias_method :participating_users_association , :participating_users
def participating_users ( user_ids = nil )
user_ids ?
participating_users_association . scoped ( :conditions = > { :id = > user_ids } ) :
participating_users_association
end
2012-07-24 05:29:18 +08:00
def wiki_with_create
Wiki . wiki_for_context ( self )
2011-02-01 09:57:29 +08:00
end
2012-07-24 05:29:18 +08:00
alias_method_chain :wiki , :create
2012-01-04 04:30:49 +08:00
2012-08-30 04:57:00 +08:00
def auto_accept?
2012-05-16 05:50:43 +08:00
self . group_category &&
self . group_category . allows_multiple_memberships? &&
self . join_level == 'parent_context_auto_join'
2011-02-01 09:57:29 +08:00
end
2012-01-04 04:30:49 +08:00
2012-08-30 04:57:00 +08:00
def allow_join_request?
2012-05-16 05:50:43 +08:00
self . group_category &&
self . group_category . allows_multiple_memberships? &&
[ 'parent_context_auto_join' , 'parent_context_request' ] . include? ( self . join_level )
2011-02-01 09:57:29 +08:00
end
2012-01-04 04:30:49 +08:00
2011-10-08 07:19:20 +08:00
def allow_self_signup? ( user )
2012-05-16 05:50:43 +08:00
self . group_category &&
( self . group_category . unrestricted_self_signup? ||
( self . group_category . restricted_self_signup? && self . has_common_section_with_user? ( user ) ) )
end
2012-08-30 04:57:00 +08:00
def free_association? ( user )
auto_accept? || allow_join_request? || allow_self_signup? ( user )
2011-10-08 07:19:20 +08:00
end
2012-01-04 04:30:49 +08:00
2012-08-31 05:15:34 +08:00
def allow_student_forum_attachments
context . respond_to? ( :allow_student_forum_attachments ) && context . allow_student_forum_attachments
end
2012-03-09 01:14:57 +08:00
def participants ( include_observers = false )
# argument needed because #participants is polymorphic for contexts
2011-02-01 09:57:29 +08:00
participating_users . uniq
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def context_code
raise " DONT USE THIS, use .short_name instead " unless ENV [ 'RAILS_ENV' ] == " production "
end
2012-01-04 04:30:49 +08:00
def appointment_context_codes
{ :primary = > [ context_string ] , :secondary = > [ group_category . asset_string ] }
end
2011-02-01 09:57:29 +08:00
def membership_for_user ( user )
2012-06-21 05:05:53 +08:00
return nil unless user . present?
self . shard . activate { self . group_memberships . find_by_user_id ( user . id ) }
2011-02-01 09:57:29 +08:00
end
2012-01-04 04:30:49 +08:00
2012-05-16 05:50:43 +08:00
def has_member? ( user )
2012-06-21 05:05:53 +08:00
return nil unless user . present?
self . shard . activate { self . participating_group_memberships . find_by_user_id ( user . id ) }
2012-05-16 05:50:43 +08:00
end
def has_moderator? ( user )
2012-06-21 05:05:53 +08:00
return nil unless user . present?
self . shard . activate { self . participating_group_memberships . moderators . find_by_user_id ( user . id ) }
2012-05-16 05:50:43 +08:00
end
2012-05-30 06:55:40 +08:00
def should_add_creator?
self . group_category && ( self . group_category . communities? || self . group_category . student_organized? )
end
2011-02-01 09:57:29 +08:00
def short_name
name
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def self . find_all_by_context_code ( codes )
ids = codes . map { | c | c . match ( / \ Agroup_( \ d+) \ z / ) [ 1 ] rescue nil } . compact
Group . find ( ids )
end
2012-01-04 04:30:49 +08:00
2013-01-09 06:06:48 +08:00
def self . not_in_group_sql_fragment ( groups )
" AND NOT EXISTS (SELECT * FROM group_memberships gm
WHERE gm . user_id = u . id AND
gm . workflow_state != 'deleted' AND
gm . group_id IN ( #{groups.map(&:id).join ','}))" unless groups.empty?
end
2011-02-01 09:57:29 +08:00
workflow do
state :available do
event :complete , :transitions_to = > :completed
event :close , :transitions_to = > :closed
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
# Closed to new entrants
state :closed do
event :complete , :transitions_to = > :completed
event :open , :transitions_to = > :available
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
state :completed
state :deleted
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def active?
self . available? || self . closed?
end
alias_method :destroy! , :destroy
def destroy
self . workflow_state = 'deleted'
self . deleted_at = Time . now
self . save
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def close_memberships_if_deleted
return unless self . deleted?
memberships = self . group_memberships
2011-08-31 06:13:41 +08:00
User . update_all ( { :updated_at = > Time . now . utc } , { :id = > memberships . map ( & :user_id ) . uniq } )
2011-02-01 09:57:29 +08:00
GroupMembership . update_all ( { :workflow_state = > 'deleted' } , { :id = > memberships . map ( & :id ) . uniq } )
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
named_scope :active , :conditions = > [ 'groups.workflow_state != ?' , 'deleted' ]
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def full_name
2011-06-14 05:33:59 +08:00
res = before_label ( self . name ) + " "
res += ( self . context . course_code rescue self . context . name ) if self . context
2011-02-01 09:57:29 +08:00
end
def to_atom
Atom :: Entry . new do | entry |
entry . title = self . name
entry . updated = self . updated_at
entry . published = self . created_at
2012-01-04 04:30:49 +08:00
entry . links << Atom :: Link . new ( :rel = > 'alternate' ,
2011-02-01 09:57:29 +08:00
:href = > " /groups/ #{ self . id } " )
end
end
2012-01-04 04:30:49 +08:00
2012-06-02 03:18:32 +08:00
# this method is idempotent
def add_user ( user , new_record_state = nil , moderator = nil )
2011-02-01 09:57:29 +08:00
return nil if ! user
2012-06-02 03:18:32 +08:00
attrs = { :user = > user , :moderator = > ! ! moderator }
2012-05-16 05:50:43 +08:00
new_record_state || = case self . join_level
when 'invitation_only' then 'invited'
when 'parent_context_request' then 'requested'
when 'parent_context_auto_join' then 'accepted'
end
attrs [ :workflow_state ] = new_record_state if new_record_state
2012-06-02 03:18:32 +08:00
if member = self . group_memberships . find_by_user_id ( user . id )
member . workflow_state = new_record_state unless member . active?
# only update moderator if true/false is explicitly passed in
member . moderator = moderator unless moderator . nil?
member . save if member . changed?
else
member = self . group_memberships . create ( attrs )
end
2012-09-27 03:05:39 +08:00
# permissions for this user in the group are probably different now
Rails . cache . delete ( permission_cache_key_for ( user ) )
2011-02-01 09:57:29 +08:00
return member
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def invite_user ( user )
2012-05-16 05:50:43 +08:00
self . add_user ( user , 'invited' )
2011-02-01 09:57:29 +08:00
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def request_user ( user )
2012-05-16 05:50:43 +08:00
self . add_user ( user , 'requested' )
2011-02-01 09:57:29 +08:00
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def invitees = ( params )
invitees = [ ]
( params || { } ) . each do | key , val |
if self . context
invitees << self . context . users . find_by_id ( key . to_i ) if val != '0'
else
invitees << User . find_by_id ( key . to_i ) if val != '0'
end
end
invitees . compact . map { | i | self . invite_user ( i ) } . compact
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def peer_groups
2012-05-16 05:50:43 +08:00
return [ ] if ! self . context || ! self . group_category || self . group_category . allows_multiple_memberships?
self . group_category . groups . find ( :all , :conditions = > [ " id != ? " , self . id ] )
2011-02-01 09:57:29 +08:00
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def migrate_content_links ( html , from_course )
Course . migrate_content_links ( html , from_course , self )
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
attr_accessor :merge_mappings
attr_accessor :merge_results
def merge_mapped_id ( * args )
nil
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def map_merge ( * args )
end
def log_merge_result ( text )
@merge_results || = [ ]
@merge_results << text
end
def warn_merge_result ( text )
record_merge_result ( text )
end
2011-09-21 03:24:18 +08:00
def student_organized?
2011-09-22 04:31:36 +08:00
self . group_category && self . group_category . student_organized?
2011-09-21 03:24:18 +08:00
end
2011-02-01 09:57:29 +08:00
def ensure_defaults
2011-04-15 06:09:37 +08:00
self . name || = AutoHandle . generate_securish_uuid
self . uuid || = AutoHandle . generate_securish_uuid
2011-09-22 04:31:36 +08:00
self . group_category || = GroupCategory . student_organized_for ( self . context )
2011-02-01 09:57:29 +08:00
self . join_level || = 'invitation_only'
2012-05-16 05:50:43 +08:00
self . is_public || = false
2012-05-30 06:55:40 +08:00
self . is_public = false unless self . group_category . try ( :communities? )
2011-02-01 09:57:29 +08:00
if self . context && self . context . is_a? ( Course )
2011-09-22 04:31:36 +08:00
self . account = self . context . account
2011-02-01 09:57:29 +08:00
elsif self . context && self . context . is_a? ( Account )
self . account = self . context
end
end
private :ensure_defaults
2011-03-29 03:38:23 +08:00
2011-06-03 23:54:00 +08:00
# update root account when account changes
def account = ( new_account )
self . account_id = new_account . id
end
def account_id = ( new_account_id )
write_attribute ( :account_id , new_account_id )
if self . account_id_changed?
2011-12-28 05:57:56 +08:00
self . root_account = self . account ( true ) . try ( :root_account )
2011-06-03 23:54:00 +08:00
end
end
2011-03-29 03:38:23 +08:00
# if you modify this set_policy block, note that we've denormalized this
# permission check for efficiency -- see User#cached_contexts
2011-02-01 09:57:29 +08:00
set_policy do
2012-05-16 05:50:43 +08:00
given { | user | user && self . has_member? ( user ) }
can :create_collaborations and
can :create_conferences and
can :manage_calendar and
can :manage_content and
can :manage_files and
can :manage_wiki and
can :post_to_forum and
can :read and
can :read_roster and
can :send_messages and
2013-02-07 08:40:16 +08:00
can :send_messages_all and
2012-06-06 03:57:22 +08:00
can :follow
2012-01-04 04:30:49 +08:00
2012-05-03 04:45:40 +08:00
# if I am a member of this group and I can moderate_forum in the group's context
# (makes it so group members cant edit each other's discussion entries)
2012-05-16 05:50:43 +08:00
given { | user , session | user && self . has_member? ( user ) && ( ! self . context || self . context . grants_right? ( user , session , :moderate_forum ) ) }
can :moderate_forum
2012-05-03 04:45:40 +08:00
2012-05-16 05:50:43 +08:00
given { | user | user && self . has_moderator? ( user ) }
2012-05-30 06:55:40 +08:00
can :delete and
2012-06-02 03:18:32 +08:00
can :manage and
can :manage_admin_users and
2012-12-12 21:50:15 +08:00
can :manage_students and
2012-05-30 06:55:40 +08:00
can :moderate_forum and
can :update
given { | user | self . group_category . try ( :communities? ) }
can :create
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
given { | user , session | self . context && self . context . grants_right? ( user , session , :participate_as_student ) && self . context . allow_student_organized_groups }
2011-07-14 00:24:17 +08:00
can :create
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
given { | user , session | self . context && self . context . grants_right? ( user , session , :manage_groups ) }
2012-05-16 05:50:43 +08:00
can :create and
2012-07-20 01:13:21 +08:00
can :create_collaborations and
2012-05-16 05:50:43 +08:00
can :create_conferences and
can :delete and
can :manage and
can :manage_admin_users and
can :manage_content and
can :manage_files and
can :manage_students and
can :manage_wiki and
can :moderate_forum and
can :post_to_forum and
can :read and
can :read_roster and
can :update
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
given { | user , session | self . context && self . context . grants_right? ( user , session , :view_group_pages ) }
2011-07-14 00:24:17 +08:00
can :read and can :read_roster
2012-01-04 04:30:49 +08:00
2012-06-06 03:57:22 +08:00
given { | user | user && self . is_public? }
can :follow
2012-08-30 04:57:00 +08:00
# Participate means the user is connected to the group somehow and can be
given { | user | user && can_participate? ( user ) }
can :participate
# Join is participate + the group being in a state that allows joining directly (free_association)
given { | user | user && can_participate? ( user ) && free_association? ( user ) }
can :join and can :read_roster
given { | user | user && ( self . group_category . try ( :allows_multiple_memberships? ) || allow_self_signup? ( user ) ) }
can :leave
end
# Helper needed by several permissions, use grants_right?(user, :participate)
def can_participate? ( user )
return false unless user . present? && self . context . present?
return true if self . group_category . try ( :communities? )
if self . context . is_a? ( Course )
return self . context . enrollments . not_fake . where ( :user_id = > user . id ) . first . present?
elsif self . context . is_a? ( Account )
return self . context . user_account_associations . where ( :user_id = > user . id ) . first . present?
end
return false
2011-02-01 09:57:29 +08:00
end
2012-08-30 04:57:00 +08:00
private :can_participate?
2011-03-02 01:44:41 +08:00
2013-02-08 04:09:05 +08:00
# courses lock this down a bit, but in a group, the fact that you are a
# member is good enough
def user_can_manage_own_discussion_posts? ( user )
true
end
2011-02-01 09:57:29 +08:00
def file_structure_for ( user )
User . file_structure_for ( self , user )
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def is_a_context?
true
end
def members_json_cached
Rails . cache . fetch ( [ 'group_members_json' , self ] . cache_key ) do
2011-10-29 07:19:11 +08:00
self . users . map { | u | u . group_member_json ( self . context ) }
2011-02-01 09:57:29 +08:00
end
end
def members_count_cached
Rails . cache . fetch ( [ 'group_members_count' , self ] . cache_key ) do
self . members_json_cached . length
end
end
2012-05-30 06:55:40 +08:00
def members_count
self . participating_group_memberships . count
2012-02-16 05:40:13 +08:00
end
2011-10-21 03:06:55 +08:00
def quota
self . storage_quota || Setting . get_cached ( 'group_default_quota' , 50 . megabytes . to_s ) . to_i
end
2012-06-01 06:47:04 +08:00
TAB_HOME , TAB_PAGES , TAB_PEOPLE , TAB_DISCUSSIONS , TAB_CHAT , TAB_FILES ,
2012-07-20 01:13:21 +08:00
TAB_CONFERENCES , TAB_ANNOUNCEMENTS , TAB_PROFILE , TAB_SETTINGS , TAB_COLLABORATIONS = * 1 .. 20
2011-02-01 09:57:29 +08:00
def tabs_available ( user = nil , opts = { } )
available_tabs = [
2011-09-09 06:11:46 +08:00
{ :id = > TAB_HOME , :label = > t ( " # group.tabs.home " , " Home " ) , :css_class = > 'home' , :href = > :group_path } ,
{ :id = > TAB_ANNOUNCEMENTS , :label = > t ( '#tabs.announcements' , " Announcements " ) , :css_class = > 'announcements' , :href = > :group_announcements_path } ,
{ :id = > TAB_PAGES , :label = > t ( " # group.tabs.pages " , " Pages " ) , :css_class = > 'pages' , :href = > :group_wiki_pages_path } ,
2012-10-19 10:13:51 +08:00
{ :id = > TAB_PEOPLE , :label = > t ( " # group.tabs.people " , " People " ) , :css_class = > 'people' , :href = > :group_users_path } ,
2011-09-09 06:11:46 +08:00
{ :id = > TAB_DISCUSSIONS , :label = > t ( " # group.tabs.discussions " , " Discussions " ) , :css_class = > 'discussions' , :href = > :group_discussion_topics_path } ,
{ :id = > TAB_CHAT , :label = > t ( " # group.tabs.chat " , " Chat " ) , :css_class = > 'chat' , :href = > :group_chat_path } ,
2012-06-06 05:06:36 +08:00
{ :id = > TAB_FILES , :label = > t ( " # group.tabs.files " , " Files " ) , :css_class = > 'files' , :href = > :group_files_path } ,
2011-02-01 09:57:29 +08:00
]
2012-06-01 06:47:04 +08:00
2012-06-28 07:22:41 +08:00
if root_account . try :canvas_network_enabled?
2012-06-14 06:19:23 +08:00
available_tabs << { :id = > TAB_PROFILE , :label = > t ( '#tabs.profile' , 'Profile' ) , :css_class = > 'profile' , :href = > :group_profile_path }
end
2012-01-17 06:44:10 +08:00
available_tabs << { :id = > TAB_CONFERENCES , :label = > t ( '#tabs.conferences' , " Conferences " ) , :css_class = > 'conferences' , :href = > :group_conferences_path } if user && self . grants_right? ( user , nil , :read )
2012-07-20 01:13:21 +08:00
available_tabs << { :id = > TAB_COLLABORATIONS , :label = > t ( '#tabs.collaborations' , " Collaborations " ) , :css_class = > 'collaborations' , :href = > :group_collaborations_path } if user && self . grants_right? ( user , nil , :read )
2012-08-31 05:15:34 +08:00
if root_account . try ( :canvas_network_enabled? ) && user && grants_right? ( user , nil , :manage )
2012-08-06 23:46:18 +08:00
available_tabs << { :id = > TAB_SETTINGS , :label = > t ( '#tabs.settings' , 'Settings' ) , :css_class = > 'settings' , :href = > :edit_group_path }
end
2011-08-05 04:20:21 +08:00
available_tabs
2011-02-01 09:57:29 +08:00
end
def self . serialization_excludes ; [ :uuid ] ; end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def self . process_migration ( data , migration )
2012-05-16 05:50:43 +08:00
groups = data [ 'groups' ] || [ ]
2011-02-01 09:57:29 +08:00
groups . each do | group |
2012-04-03 06:38:05 +08:00
if migration . import_object? ( " groups " , group [ 'migration_id' ] )
2011-06-18 00:58:18 +08:00
begin
import_from_migration ( group , migration . context )
rescue
migration . add_warning ( " Couldn't import group \" #{ group [ :title ] } \" " , $! )
end
2011-02-01 09:57:29 +08:00
end
end
end
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
def self . import_from_migration ( hash , context , item = nil )
hash = hash . with_indifferent_access
return nil if hash [ :migration_id ] && hash [ :groups_to_import ] && ! hash [ :groups_to_import ] [ hash [ :migration_id ] ]
item || = find_by_context_id_and_context_type_and_id ( context . id , context . class . to_s , hash [ :id ] )
item || = find_by_context_id_and_context_type_and_migration_id ( context . id , context . class . to_s , hash [ :migration_id ] ) if hash [ :migration_id ]
item || = context . groups . new
context . imported_migration_items << item if context . imported_migration_items && item . new_record?
item . migration_id = hash [ :migration_id ]
item . name = hash [ :title ]
2011-09-22 04:31:36 +08:00
item . group_category = hash [ :group_category ] . present? ?
context . group_categories . find_or_initialize_by_name ( hash [ :group_category ] ) :
GroupCategory . imported_for ( context )
2012-01-04 04:30:49 +08:00
2011-02-01 09:57:29 +08:00
item . save!
context . imported_migration_items << item
item
end
2011-09-08 23:06:51 +08:00
def allow_media_comments?
true
end
2011-09-21 00:51:50 +08:00
def group_category_name
self . read_attribute ( :category )
end
2011-09-22 04:31:36 +08:00
def maintain_category_attribute
# keep this field up to date even though it's not used (group_category_name
# exists solely for the migration that introduces the GroupCategory model).
# this way group_category_name is correct if someone mistakenly uses it
# (modulo category renaming in the GroupCategory model).
self . write_attribute ( :category , self . group_category && self . group_category . name )
2011-09-21 00:51:50 +08:00
end
def as_json ( options = nil )
json = super ( options )
2011-09-22 04:31:36 +08:00
if json && json [ 'group' ]
# remove anything coming automatically from deprecated db column
json [ 'group' ] . delete ( 'category' )
if self . group_category
2012-01-04 04:30:49 +08:00
# put back version from association
2011-09-22 04:31:36 +08:00
json [ 'group' ] [ 'group_category' ] = self . group_category . name
end
end
2011-09-21 00:51:50 +08:00
json
end
2011-10-08 07:19:20 +08:00
def has_common_section?
self . context && self . context . is_a? ( Course ) &&
self . context . course_sections . active . any? { | section | section . common_to_users? ( self . users ) }
end
def has_common_section_with_user? ( user )
return false unless self . context && self . context . is_a? ( Course )
users = self . users + [ user ]
self . context . course_sections . active . any? { | section | section . common_to_users? ( users ) }
end
2012-06-01 03:21:22 +08:00
def self . join_levels
[
[ " invitation_only " , " Invite only " ] ,
[ " parent_context_auto_join " , " Auto join " ] ,
[ " parent_context_request " , " Request to join " ]
]
end
2012-06-07 05:14:35 +08:00
2012-05-30 04:56:58 +08:00
def default_collection_name
t " # group.default_collection_name " , " %{group_name}'s Collection " , :group_name = > self . name
end
2012-11-17 06:00:05 +08:00
def associated_shards
[ Shard . default ]
end
bookmarked pagination, including multi-shard
introduces a new BookmarkedCollection module with behavior similar to
PaginatedCollection in the simple case.
the primary advantage is that assigning to current_page (e.g. from the
:page parameter to paginate) expects a bookmark token value and
automatically deserializes into current_bookmark. the library client can
then use current_bookmark to skip forward in the collection, rather than
using (current_page - 1) * per_page as the number of items to skip. the
client then calls set_next_bookmark on the pager if there's more
results, and it automatically derives the bookmark for the next page and
serializes it into next_page, for use by Api.paginate, etc.
in addition to the PaginatedCollection.build analog, you can simply wrap
an existing scope to change it from something that will paginate by page
number into something that will paginate by bookmark.
finally, the key reason to use bookmarked pagination is to enable
composition of collections. you can merge multiple collections into one
collection which when paginated will pull results from each
subcollection, in order, to produce the page of results. you can also
concatenate multiple collections into one collection which when
paginated will exhaust the collections in order with seamless transition
from one to the next when a page spans both.
with collection merging available, you can paginate an association where
you'd like to use with_each_shard. one collection is created per shard,
and then they are merged together. this process is automated for you in
the BookmarkedCollection.with_each_shard method.
fixes CNVS-1169
Change-Id: Ib998eee53c33604cb6f7e338153428a157928a6d
Reviewed-on: https://gerrit.instructure.com/16039
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Jacob Fugal <jacob@instructure.com>
QA-Review: Clare Hetherington <clare@instructure.com>
2012-12-13 06:50:04 +08:00
class Bookmarker
def self . bookmark_for ( group )
group . id
end
def self . validate ( bookmark )
bookmark . is_a? ( Fixnum )
end
def self . restrict_scope ( scope , pager )
if bookmark = pager . current_bookmark
comparison = ( pager . include_bookmark ? 'groups.id >= ?' : 'groups.id > ?' )
scope = scope . scoped ( :conditions = > [ comparison , bookmark ] )
end
scope . scoped ( :order = > " groups.id ASC " )
end
end
2011-02-01 09:57:29 +08:00
end