Added username into Authors file [Ex-12] Add user to input coupon code in the Shopping Cart [Ex-13] Discount should be reflected in the Payment confirmation page and email added E-commerce Tab in Instructor Dashboard added name/email in authors file removed the is_active column, change the colors scheme, fixed bugs wip wip test github account STORE_BILLING_INFO set to True cybersource api update, reference number updated, merchant_data removed from params View Course buttons on receipt link for course added to receipt receipt.html view course button - func update receipt.html course link update move new CyberSource implementation to a separate file so that we can keep the original remove config changes remove config changes remove coupon redemption during clear cart and update test cases [Ex-11]added test cases(E-commerce Tab Instuctor Dashboard) update data model max_length to 255 remove array paid_course_ids init in views.py removed the is_active filter=false, added styling to the inactive coupon codes remove coupon redemption during clear cart and update test cases [Ex-11]added test cases(E-commerce Tab Instuctor Dashboard) update data model max_length to 255 Add column to the list of coupons in the E-Commerce tab Add ability for microsites to specify custom CyberSource secret configuration, i.e. run under different accounts make the new CyberSource2 also microsite aware updating migration for student and shopping cart apps added user signup functionality that orignated from the Microsites added non-microsite user signup tests fix the hard coded callback URL to localhost add comment Modify e-commerce instructor tab to show a total amount above the coupon listings for admin finance user made changes as suggested by diana khuang add the CourseAccessRoles table to the Django Admin website shopping cart coupon checkout changes as suggested by Jason Bau changes are made according to the suggesstions on PR#4172 changes made in the coupons file changes in the coupons get_coupon_info view fix merge conflict resolution error changes in the remove_coupon view json response changes as suggested by David Baumgold pep8/pylint fixes Changes as suggested by jasonBau don't assume item in shopping cart is a PaidCourseRegistration fix up some logging changed the urls of the coupon views and use the post to get the values from the request
298 lines
9.3 KiB
Python
298 lines
9.3 KiB
Python
"""
|
|
Classes used to model the roles used in the courseware. Each role is responsible for checking membership,
|
|
adding users, removing users, and listing members
|
|
"""
|
|
|
|
from abc import ABCMeta, abstractmethod
|
|
|
|
from django.contrib.auth.models import User
|
|
from student.models import CourseAccessRole
|
|
from xmodule_django.models import CourseKeyField
|
|
|
|
|
|
class RoleCache(object):
|
|
"""
|
|
A cache of the CourseAccessRoles held by a particular user
|
|
"""
|
|
def __init__(self, user):
|
|
self._roles = set(
|
|
CourseAccessRole.objects.filter(user=user).all()
|
|
)
|
|
|
|
def has_role(self, role, course_id, org):
|
|
"""
|
|
Return whether this RoleCache contains a role with the specified role, course_id, and org
|
|
"""
|
|
return any(
|
|
access_role.role == role and
|
|
access_role.course_id == course_id and
|
|
access_role.org == org
|
|
for access_role in self._roles
|
|
)
|
|
|
|
|
|
class AccessRole(object):
|
|
"""
|
|
Object representing a role with particular access to a resource
|
|
"""
|
|
__metaclass__ = ABCMeta
|
|
|
|
@abstractmethod
|
|
def has_user(self, user): # pylint: disable=unused-argument
|
|
"""
|
|
Return whether the supplied django user has access to this role.
|
|
"""
|
|
return False
|
|
|
|
@abstractmethod
|
|
def add_users(self, *users):
|
|
"""
|
|
Add the role to the supplied django users.
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def remove_users(self, *users):
|
|
"""
|
|
Remove the role from the supplied django users.
|
|
"""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def users_with_role(self):
|
|
"""
|
|
Return a django QuerySet for all of the users with this role
|
|
"""
|
|
return User.objects.none()
|
|
|
|
|
|
class GlobalStaff(AccessRole):
|
|
"""
|
|
The global staff role
|
|
"""
|
|
def has_user(self, user):
|
|
return user.is_staff
|
|
|
|
def add_users(self, *users):
|
|
for user in users:
|
|
if (user.is_authenticated() and user.is_active):
|
|
user.is_staff = True
|
|
user.save()
|
|
|
|
def remove_users(self, *users):
|
|
for user in users:
|
|
# don't check is_authenticated nor is_active on purpose
|
|
user.is_staff = False
|
|
user.save()
|
|
|
|
def users_with_role(self):
|
|
raise Exception("This operation is un-indexed, and shouldn't be used")
|
|
|
|
|
|
class RoleBase(AccessRole):
|
|
"""
|
|
Roles by type (e.g., instructor, beta_user) and optionally org, course_key
|
|
"""
|
|
def __init__(self, role_name, org='', course_key=None):
|
|
"""
|
|
Create role from required role_name w/ optional org and course_key. You may just provide a role
|
|
name if it's a global role (not constrained to an org or course). Provide org if constrained to
|
|
an org. Provide org and course if constrained to a course. Although, you should use the subclasses
|
|
for all of these.
|
|
"""
|
|
super(RoleBase, self).__init__()
|
|
|
|
self.org = org
|
|
self.course_key = course_key
|
|
self._role_name = role_name
|
|
|
|
def has_user(self, user):
|
|
"""
|
|
Return whether the supplied django user has access to this role.
|
|
"""
|
|
if not (user.is_authenticated() and user.is_active):
|
|
return False
|
|
|
|
# pylint: disable=protected-access
|
|
if not hasattr(user, '_roles'):
|
|
# Cache a list of tuples identifying the particular roles that a user has
|
|
# Stored as tuples, rather than django models, to make it cheaper to construct objects for comparison
|
|
user._roles = RoleCache(user)
|
|
|
|
return user._roles.has_role(self._role_name, self.course_key, self.org)
|
|
|
|
def add_users(self, *users):
|
|
"""
|
|
Add the supplied django users to this role.
|
|
"""
|
|
# silently ignores anonymous and inactive users so that any that are
|
|
# legit get updated.
|
|
for user in users:
|
|
if user.is_authenticated and user.is_active and not self.has_user(user):
|
|
entry = CourseAccessRole(user=user, role=self._role_name, course_id=self.course_key, org=self.org)
|
|
entry.save()
|
|
if hasattr(user, '_roles'):
|
|
del user._roles
|
|
|
|
def remove_users(self, *users):
|
|
"""
|
|
Remove the supplied django users from this role.
|
|
"""
|
|
entries = CourseAccessRole.objects.filter(
|
|
user__in=users, role=self._role_name, org=self.org, course_id=self.course_key
|
|
)
|
|
entries.delete()
|
|
for user in users:
|
|
if hasattr(user, '_roles'):
|
|
del user._roles
|
|
|
|
def users_with_role(self):
|
|
"""
|
|
Return a django QuerySet for all of the users with this role
|
|
"""
|
|
# Org roles don't query by CourseKey, so use CourseKeyField.Empty for that query
|
|
if self.course_key is None:
|
|
self.course_key = CourseKeyField.Empty
|
|
entries = User.objects.filter(
|
|
courseaccessrole__role=self._role_name,
|
|
courseaccessrole__org=self.org,
|
|
courseaccessrole__course_id=self.course_key
|
|
)
|
|
return entries
|
|
|
|
|
|
class CourseRole(RoleBase):
|
|
"""
|
|
A named role in a particular course
|
|
"""
|
|
def __init__(self, role, course_key):
|
|
"""
|
|
Args:
|
|
course_key (CourseKey)
|
|
"""
|
|
super(CourseRole, self).__init__(role, course_key.org, course_key)
|
|
|
|
@classmethod
|
|
def course_group_already_exists(self, course_key):
|
|
return CourseAccessRole.objects.filter(org=course_key.org, course_id=course_key).exists()
|
|
|
|
|
|
class OrgRole(RoleBase):
|
|
"""
|
|
A named role in a particular org independent of course
|
|
"""
|
|
def __init__(self, role, org):
|
|
super(OrgRole, self).__init__(role, org)
|
|
|
|
|
|
class CourseStaffRole(CourseRole):
|
|
"""A Staff member of a course"""
|
|
ROLE = 'staff'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(CourseStaffRole, self).__init__(self.ROLE, *args, **kwargs)
|
|
|
|
|
|
class CourseInstructorRole(CourseRole):
|
|
"""A course Instructor"""
|
|
ROLE = 'instructor'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(CourseInstructorRole, self).__init__(self.ROLE, *args, **kwargs)
|
|
|
|
|
|
class CourseFinanceAdminRole(CourseRole):
|
|
"""A course Instructor"""
|
|
ROLE = 'finance_admin'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(CourseFinanceAdminRole, self).__init__(self.ROLE, *args, **kwargs)
|
|
|
|
class CourseBetaTesterRole(CourseRole):
|
|
"""A course Beta Tester"""
|
|
ROLE = 'beta_testers'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(CourseBetaTesterRole, self).__init__(self.ROLE, *args, **kwargs)
|
|
|
|
|
|
class OrgStaffRole(OrgRole):
|
|
"""An organization staff member"""
|
|
def __init__(self, *args, **kwargs):
|
|
super(OrgStaffRole, self).__init__('staff', *args, **kwargs)
|
|
|
|
|
|
class OrgInstructorRole(OrgRole):
|
|
"""An organization instructor"""
|
|
def __init__(self, *args, **kwargs):
|
|
super(OrgInstructorRole, self).__init__('instructor', *args, **kwargs)
|
|
|
|
|
|
class CourseCreatorRole(RoleBase):
|
|
"""
|
|
This is the group of people who have permission to create new courses (we may want to eventually
|
|
make this an org based role).
|
|
"""
|
|
ROLE = "course_creator_group"
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(CourseCreatorRole, self).__init__(self.ROLE, *args, **kwargs)
|
|
|
|
|
|
class UserBasedRole(object):
|
|
"""
|
|
Backward mapping: given a user, manipulate the courses and roles
|
|
"""
|
|
def __init__(self, user, role):
|
|
"""
|
|
Create a UserBasedRole accessor: for a given user and role (e.g., "instructor")
|
|
"""
|
|
self.user = user
|
|
self.role = role
|
|
|
|
def has_course(self, course_key):
|
|
"""
|
|
Return whether the role's user has the configured role access to the passed course
|
|
"""
|
|
if not (self.user.is_authenticated() and self.user.is_active):
|
|
return False
|
|
|
|
# pylint: disable=protected-access
|
|
if not hasattr(self.user, '_roles'):
|
|
self.user._roles = RoleCache(self.user)
|
|
|
|
return self.user._roles.has_role(self.role, course_key, course_key.org)
|
|
|
|
def add_course(self, *course_keys):
|
|
"""
|
|
Grant this object's user the object's role for the supplied courses
|
|
"""
|
|
if self.user.is_authenticated and self.user.is_active:
|
|
for course_key in course_keys:
|
|
entry = CourseAccessRole(user=self.user, role=self.role, course_id=course_key, org=course_key.org)
|
|
entry.save()
|
|
if hasattr(self.user, '_roles'):
|
|
del self.user._roles
|
|
else:
|
|
raise ValueError("user is not active. Cannot grant access to courses")
|
|
|
|
def remove_courses(self, *course_keys):
|
|
"""
|
|
Remove the supplied courses from this user's configured role.
|
|
"""
|
|
entries = CourseAccessRole.objects.filter(user=self.user, role=self.role, course_id__in=course_keys)
|
|
entries.delete()
|
|
if hasattr(self.user, '_roles'):
|
|
del self.user._roles
|
|
|
|
def courses_with_role(self):
|
|
"""
|
|
Return a django QuerySet for all of the courses with this user x role. You can access
|
|
any of these properties on each result record:
|
|
* user (will be self.user--thus uninteresting)
|
|
* org
|
|
* course_id
|
|
* role (will be self.role--thus uninteresting)
|
|
"""
|
|
return CourseAccessRole.objects.filter(role=self.role, user=self.user)
|