def __init__(self): GenericDB.__init__(self) HasOwnership.__init__(self) self.mod_queue = SponsoredQueue() self.invitations = Invitations()
class GroupDB(GenericDB, HasOwnership): """Database for Groups (qon.group.Group). When a new group is added to the database, it is automatically added to the moderation queue. Users must have write permission on db in order to create groups. """ _karma_new_group = 10 # cost to create new group _karma_sponsor_group = 5 # cost to sponsor group _karma_pm_group = 1 # cost to PM entire group def __init__(self): GenericDB.__init__(self) HasOwnership.__init__(self) self.mod_queue = SponsoredQueue() self.invitations = Invitations() def get_group(self, group_id): """Return the group with given group_id, or None if not found.""" return self.root.get(group_id) def add_group(self, group): self[group.user_id] = group self.mod_queue.add_to_queue(group) def remove_group(self, group): del self[group.user_id] self.mod_queue.remove_from_queue(group) # quit groups that group is a member of for g in self.member_groups(group): self.leave_group(g, group) # remove members from group for member in group.get_member_list(): self.leave_group(member, group) # remove owners from group for owner in group.get_owners(): self.leave_group(owner, group) # delete usergroup - must be last, since leave_group needs it get_usergroup_database().remove_usergroup(group.user_id) def active_groups(self): return [self[group] for group in self.root.keys() if self[group].get_state() == 'accepted'] def recently_active_groups(self, user=None, days_cutoff=3): cutoff_date = datetime.utcnow() - timedelta(days=days_cutoff) return [self[group] for group in self.root.keys() if self[group].can_read(user) and self[group].watchable_last_change() > cutoff_date] def recently_active_tuples(self, user=None, days_cutoff=3): """Returns a sorted list of (group, active_discussions, active_pages) tuples for groups that have been active in the last X days and are readdable by the given user""" cutoff_date = datetime.utcnow() - timedelta(days=days_cutoff) readable_active_tuples = [(g, g.blog.num_active_items(days=days_cutoff, consider_comments=True)[0], g.wiki.num_active_pages(days=days_cutoff)[0]) for g in self.active_groups() if g.can_read(user) and g.watchable_last_change() > cutoff_date] sorted_group_tuples = qon.util.sort_list(readable_active_tuples, lambda x: x[0].watchable_last_change()) return sorted_group_tuples def recent_groups(self, age): """Recent recently-created groups not older than age.""" cutoff = datetime.utcnow() - age groups = [g for g in self.active_groups() if g.date > cutoff] return qon.util.sort_list(groups, lambda x: x.date) def active_groups_anon(self): """Active groups available for anonymous browsing""" groups = self.active_groups() return [group for group in groups if group.anon_read] def rejected_groups(self): return [self[group] for group in self.root.keys() if self[group].get_state() == 'rejected'] def limbo_groups(self): return [self[group] for group in self.root.keys() if self[group].get_state() == 'limbo'] def top_level_groups(self): """ Return top-level groups. Currently hard-coded. """ top_level_group_ids = ('issues-business', 'issues-cyf', 'issues-education', 'issues-env', \ 'issues-health', 'issues-pol', 'regional', 'issues-religion', \ 'issues-soc', 'issues-tech', 'issues-general', \ 'private', 'public', 'social', 'orgs-general', \ 'help', 'community-general', 'suggestions', \ 'general-other') top_level = [] for gid in top_level_group_ids: g = self.get_group(gid) if g: top_level.append(g) return top_level # # permission rules # can_create_group = HasOwnership.can_write # # group management # def can_pay_for_new_group(self, user): return user.can_give_karma(self._karma_new_group) def can_pay_to_sponsor_group(self, user): return user.can_give_karma(self._karma_sponsor_group) def can_pay_for_pm_group(self, user): return user.can_give_karma(self._karma_pm_group) def create_group(self, user_id, name, owner, description='', member_perms=[], other_perms=[], member_join_perms=[], other_join_perms=[], anon_read=0, no_pay=False): """Create a new group. member_perms, other_perms: see qon.group.HasAccessPolicy member_join_perms, other_join_Perms: see qon.group.HasMembership """ user_id = user_id.lower() if self.root.has_key(user_id) or get_usergroup_database().has_key(user_id): raise KeyError, "Key %s already exists." % user_id if not no_pay: # charge owner for new group - don't create group if can't pay from qon.karma import NoKarmaToGive try: owner.pay_karma(self._karma_new_group) except NoKarmaToGive: return None group = Group(user_id=user_id, name=name, owner=owner) group.add_owner(owner) group.add_sponsor(owner) group.anon_read = anon_read group.description = description usergroup = UserGroup(group.user_id) get_usergroup_database().add_usergroup(usergroup) group.set_owning_group(usergroup) # members must have at least read access -- otherwise, what's the point of membership? mem_perms = member_perms if 'read' not in mem_perms: mem_perms.append('read') group.set_group_perms(mem_perms) group.set_other_perms(other_perms) group.get_members().set_group_perms(member_join_perms) group.get_members().set_other_perms(other_join_perms) # flush owner's group list cache self._flush_user_data_caches(owner) self.add_group(group) return group def add_sponsor(self, group, sponsor): """Add sponsor to group. Returns True on success""" # check if already a sponsor so we don't pay twice if group.is_accepted() or (sponsor in group.get_sponsors()): return True # pay for sponsorship from qon.karma import NoKarmaToGive try: sponsor.pay_karma(self._karma_sponsor_group) except NoKarmaToGive: return False self.mod_queue.add_sponsor(group, sponsor) # if group got enough sponsors: if group.is_accepted(): self._sponsors_to_members(group) return True def force_accept(self, group): """Force a Sponsored group into accepted state.""" self.mod_queue.force_accept(group) # sponsors become members if group.is_accepted(): self._sponsors_to_members(group) def _sponsors_to_members(self, group): if group.is_accepted(): for user in group.get_sponsors(): if not group.is_owner(user): self.join_group(user, group, force=1) # # user/owner/sponsor management # def owned_groups(self, user): """Return list of groups owned by owner. owner can be a User or Group.""" if not hasattr(user, '_group_owned_groups'): user._group_owned_groups = [self[group] for group in self.root.keys() \ if self[group].is_owner(user)] return user._group_owned_groups def member_groups(self, user): """Return list of groups user is a member of. user can be a User or Group.""" if not hasattr(user, '_group_member_groups'): # be sure to use "slow" version of is_member to prevent it recursing back user._group_member_groups = [self[group] for group in self.root.keys() \ if self[group].is_member(user, slow=True)] return user._group_member_groups def users_groups(self, user): """Return groups relevant to user: owned + member, with duplicates removed.""" return qon.util.unique_items(self.owned_groups(user) + self.member_groups(user)) def users_invitations(self, user): """Return groups user has been invited to join.""" return self.invitations.get_invitations(user, self) def join_group(self, user, group, force=0): """User joins group. Disable can_join check by passing True to force.""" if not force and not group.can_join(user): raise NotEnoughPrivileges group.add_member(user) user.add_to_group(get_usergroup_database().get_usergroup(group.get_user_id())) if hasattr(user, 'karma_activity_credit'): # groups can join groups, and groups don't have karma_activity_credit user.karma_activity_credit() self._flush_user_data_caches(user) def _flush_user_data_caches(self, user): if hasattr(user, '_group_owned_groups'): del user._group_owned_groups if hasattr(user, '_group_member_groups'): del user._group_member_groups def decline_invitation(self, user, group): """Decline an invitation to join group. Removes invitations.""" if group.is_invited(user): group.remove_invitation(user) def leave_group(self, user, group): group.remove_member(user) user.remove_from_group(get_usergroup_database().get_usergroup(group.get_user_id())) self._flush_user_data_caches(user) def set_group_owners(self, group, users): """Set users as owners of group.""" # Ensure that if we are removing an existing owner, he remains a member for u in group.owners: if u not in users: group.add_member(u) self._flush_user_data_caches(u) group.set_owner(users) for u in users: self._flush_user_data_caches(u) def notify_new_user(self, user): """Called by user_db when a new user is created.""" # join to default group g = self.root.get('community-general') if g: self.join_group(user, g) def notify_email_confirmed(self, user, email): """Notice user just confirmed email.""" # make sure user isn't still invited to groups he owns or is a member of for g in self.users_groups(user): g.remove_invitation(user)