models.py 5.42 KB
Newer Older
1 2
import logging

Mike Chen committed
3 4
from django.db import models
from django.contrib.auth.models import User
5

6
from django.dispatch import receiver
7
from django.db.models.signals import post_save
8
from django.utils.translation import ugettext_noop
9

10
from student.models import CourseEnrollment
Mike Chen committed
11

12
from xmodule.modulestore.django import modulestore
13 14
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule_django.models import CourseKeyField, NoneToEmptyManager
Mike Chen committed
15

16 17 18 19
FORUM_ROLE_ADMINISTRATOR = ugettext_noop('Administrator')
FORUM_ROLE_MODERATOR = ugettext_noop('Moderator')
FORUM_ROLE_COMMUNITY_TA = ugettext_noop('Community TA')
FORUM_ROLE_STUDENT = ugettext_noop('Student')
Brian Wilson committed
20

21 22

@receiver(post_save, sender=CourseEnrollment)
23 24 25 26
def assign_default_role_on_enrollment(sender, instance, **kwargs):
    """
    Assign forum default role 'Student'
    """
27 28 29 30 31 32 33 34 35 36 37 38 39
    # The code below would remove all forum Roles from a user when they unenroll
    # from a course. Concerns were raised that it should apply only to students,
    # or that even the history of student roles is important for research
    # purposes. Since this was new functionality being added in this release,
    # I'm just going to comment it out for now and let the forums team deal with
    # implementing the right behavior.
    #
    # # We've unenrolled the student, so remove all roles for this course
    # if not instance.is_active:
    #     course_roles = list(Role.objects.filter(course_id=instance.course_id))
    #     instance.user.roles.remove(*course_roles)
    #     return

40
    # We've enrolled the student, so make sure they have the Student role
41 42 43 44 45 46 47
    assign_default_role(instance.course_id, instance.user)


def assign_default_role(course_id, user):
    """
    Assign forum default role 'Student' to user
    """
48
    role, __ = Role.objects.get_or_create(course_id=course_id, name=FORUM_ROLE_STUDENT)
49
    user.roles.add(role)
50 51


Mike Chen committed
52
class Role(models.Model):
53 54 55

    objects = NoneToEmptyManager()

56
    name = models.CharField(max_length=30, null=False, blank=False)
Mike Chen committed
57
    users = models.ManyToManyField(User, related_name="roles")
58
    course_id = CourseKeyField(max_length=255, blank=True, db_index=True)
Mike Chen committed
59

60
    class Meta(object):
61
        # use existing table that was originally created from django_comment_client app
62 63
        db_table = 'django_comment_client_role'

Mike Chen committed
64
    def __unicode__(self):
65 66
        # pylint: disable=no-member
        return self.name + " for " + (self.course_id.to_deprecated_string() if self.course_id else "all courses")
Mike Chen committed
67

David Baumgold committed
68 69 70
    # TODO the name of this method is a little bit confusing,
    # since it's one-off and doesn't handle inheritance later
    def inherit_permissions(self, role):
71 72 73 74
        """
        Make this role inherit permissions from the given role.
        Permissions are only added, not removed. Does not handle inheritance.
        """
75
        if role.course_id and role.course_id != self.course_id:
76 77 78 79 80
            logging.warning(
                "%s cannot inherit permissions from %s due to course_id inconsistency",
                self,
                role,
            )
81 82
        for per in role.permissions.all():
            self.add_permission(per)
Mike Chen committed
83

84 85
    def add_permission(self, permission):
        self.permissions.add(Permission.objects.get_or_create(name=permission)[0])
Mike Chen committed
86

87
    def has_permission(self, permission):
88
        """Returns True if this role has the given permission, False otherwise."""
89 90 91
        course = modulestore().get_course(self.course_id)
        if course is None:
            raise ItemNotFoundError(self.course_id)
92
        if permission_blacked_out(course, {self.name}, permission):
Brian Wilson committed
93
            return False
Calen Pennington committed
94

95
        return self.permissions.filter(name=permission).exists()
Mike Chen committed
96 97 98 99 100 101


class Permission(models.Model):
    name = models.CharField(max_length=30, null=False, blank=False, primary_key=True)
    roles = models.ManyToManyField(Role, related_name="permissions")

102
    class Meta(object):
103
        # use existing table that was originally created from django_comment_client app
104 105
        db_table = 'django_comment_client_permission'

Mike Chen committed
106 107
    def __unicode__(self):
        return self.name
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


def permission_blacked_out(course, role_names, permission_name):
    """Returns true if a user in course with the given roles would have permission_name blacked out.

    This will return true if it is a permission that the user might have normally had for the course, but does not have
    right this moment because we are in a discussion blackout period (as defined by the settings on the course module).
    Namely, they can still view, but they can't edit, update, or create anything. This only applies to students, as
    moderators of any kind still have posting privileges during discussion blackouts.
    """
    return (
        not course.forum_posts_allowed and
        role_names == {FORUM_ROLE_STUDENT} and
        any([permission_name.startswith(prefix) for prefix in ['edit', 'update', 'create']])
    )


def all_permissions_for_user_in_course(user, course_id):  # pylint: disable=invalid-name
    """Returns all the permissions the user has in the given course."""
    course = modulestore().get_course(course_id)
    if course is None:
        raise ItemNotFoundError(course_id)

    all_roles = {role.name for role in Role.objects.filter(users=user, course_id=course_id)}

    permissions = {
        permission.name
        for permission
        in Permission.objects.filter(roles__users=user, roles__course_id=course_id)
        if not permission_blacked_out(course, all_roles, permission.name)
    }
    return permissions