Merge pull request #10958 from edx/patch/2015-12-11
Multiple Credit Fixes
This commit is contained in:
@@ -782,14 +782,10 @@
|
||||
}
|
||||
|
||||
.credit-action {
|
||||
.credit-msg {
|
||||
@include float(left);
|
||||
width: flex-grid(10, 12);
|
||||
}
|
||||
|
||||
.credit-btn {
|
||||
@extend %btn-pl-yellow-base;
|
||||
@include float(right);
|
||||
margin-right: 5px;
|
||||
background-image: none ;
|
||||
text-shadow: none;
|
||||
box-shadow: none;
|
||||
|
||||
@@ -1,75 +1,87 @@
|
||||
<%page args="credit_status" />
|
||||
<%!
|
||||
import datetime
|
||||
import pytz
|
||||
from django.utils.translation import ugettext as _
|
||||
from util.date_utils import get_default_time_display
|
||||
%>
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
% if credit_status["provider_name"]:
|
||||
<% provider_link='<a href="{}" target="_blank">{}</a>'.format(credit_status["provider_status_url"], credit_status["provider_name"]) %>
|
||||
% endif
|
||||
|
||||
|
||||
% if credit_status["eligible"]:
|
||||
<%
|
||||
provider_link = '<a href="{href}" target="_blank">{name}</a>'.format(
|
||||
href=credit_status["provider_status_url"],
|
||||
name=credit_status["provider_name"])
|
||||
|
||||
error = credit_status['error']
|
||||
|
||||
|
||||
status = 'eligible'
|
||||
|
||||
# Translators: provider_name is the name of a credit provider or university (e.g. State University)
|
||||
credit_msg = _("You have completed this course and are eligible to purchase course credit. Select <strong>Get Credit</strong> to get started.")
|
||||
credit_msg_class = "credit-eligibility-msg"
|
||||
credit_btn_class = "purchase-credit-btn"
|
||||
credit_btn_label = _("Get Credit")
|
||||
credit_btn_href = '{root}/credit/checkout/{course_id}/'.format(
|
||||
root=settings.ECOMMERCE_PUBLIC_URL_ROOT,
|
||||
course_id=credit_status['course_key'])
|
||||
|
||||
if credit_status["purchased"]:
|
||||
request_status = credit_status["request_status"]
|
||||
if request_status is None:
|
||||
# Learner must initiate the credit request
|
||||
|
||||
# Translators: link_to_provider_site is a link to an external webpage. The text of the link will be the name of a credit provider, such as 'State University' or 'Happy Fun Company'.
|
||||
credit_msg = _("Thank you for your payment. To receive course credit, you must now request credit "
|
||||
"at the {link_to_provider_site} website. Select <b>Request Credit</b> to get started.").format(
|
||||
link_to_provider_site=provider_link,
|
||||
)
|
||||
credit_msg_class = "credit-request-pending-msg"
|
||||
credit_btn_label = _("Request Credit")
|
||||
credit_btn_class = 'pending-credit-btn'
|
||||
elif request_status == 'pending':
|
||||
# Request received but not reviewed
|
||||
## Translators: provider_name is the name of a credit provider or university (e.g. State University)
|
||||
credit_msg = _("{provider_name} has received your course credit request. We will update you when credit processing is complete.").format(provider_name=credit_status["provider_name"])
|
||||
credit_msg_class = "credit-request-pending-msg"
|
||||
credit_btn_label = _("View Details")
|
||||
credit_btn_class = 'pending-credit-btn'
|
||||
elif request_status == 'approved':
|
||||
# Credit granted!
|
||||
# Translators: link_to_provider_site is a link to an external webpage. The text of the link will be the name of a credit provider, such as 'State University' or 'Happy Fun Company'. provider_name is the name of credit provider.
|
||||
credit_msg = _("<b>Congratulations!</b> {provider_name} has approved your request for course credit. To see your course credit, visit the {link_to_provider_site} website.").format(
|
||||
provider_name=credit_status["provider_name"],
|
||||
link_to_provider_site=provider_link,
|
||||
)
|
||||
credit_msg_class = "credit-request-approved-msg"
|
||||
credit_btn_href = credit_status['provider_status_url']
|
||||
credit_btn_label = _("View Credit")
|
||||
elif request_status == 'rejected':
|
||||
# REJECTED (by the credit provider)!
|
||||
## Translators: link_to_provider_site is a link to an external webpage. The text of the link will be the name of a credit provider, such as 'State University' or 'Happy Fun Company'. provider_name is the name of credit provider.
|
||||
credit_msg = _("{provider_name} did not approve your request for course credit. For more information, contact {link_to_provider_site} directly.").format(
|
||||
provider_name=credit_status["provider_name"],
|
||||
link_to_provider_site=provider_link,
|
||||
)
|
||||
credit_msg_class = "credit-request-rejected-msg"
|
||||
credit_btn_label = None
|
||||
%>
|
||||
|
||||
<div class="message message-status is-shown credit-message">
|
||||
|
||||
<p class="message-copy is-hidden credit-error-msg" data-credit-error="${credit_status['error']}">
|
||||
${_("An error occurred with this transaction. For help, contact {support_email}.").format(
|
||||
support_email=u'<a href="mailto:{address}">{address}</a>'.format(
|
||||
address=settings.DEFAULT_FEEDBACK_EMAIL
|
||||
)
|
||||
support_email=u'<a href="mailto:{address}">{address}</a>'.format(
|
||||
address=settings.DEFAULT_FEEDBACK_EMAIL
|
||||
)
|
||||
)}
|
||||
</p>
|
||||
<div class="credit-action">
|
||||
% if not credit_status["purchased"] and not credit_status["error"] :
|
||||
<p class="message-copy credit-msg credit-eligibility-msg">
|
||||
## Translators: provider_name is the name of a credit provider or university (e.g. State University)
|
||||
${_("You have completed this course and are eligible to purchase course credit. Select <strong>Get Credit</strong> to get started.")}
|
||||
</p>
|
||||
<div class="purchase_credit">
|
||||
<a class="btn credit-btn purchase-credit-btn" href="${settings.ECOMMERCE_PUBLIC_URL_ROOT}/credit/checkout/${credit_status['course_key']}" target="_blank" data-course-key="${credit_status['course_key']}">${_("Get Credit")}</a>
|
||||
</div>
|
||||
% elif credit_status["request_status"] in [None, "pending"] and not credit_status["error"]:
|
||||
% if credit_status["request_status"] == "pending":
|
||||
<p class="message-copy credit-msg credit-request-pending-msg">
|
||||
## Translators: provider_name is the name of a credit provider or university (e.g. State University)
|
||||
${_("{provider_name} has received your course credit request. We will update you when credit processing is complete.").format(
|
||||
provider_name=credit_status["provider_name"],
|
||||
)
|
||||
}
|
||||
</p>
|
||||
% elif credit_status["request_status"] is None:
|
||||
<p class="message-copy credit-msg credit-request-pending-msg">
|
||||
## Translators: link_to_provider_site is a link to an external webpage. The text of the link will be the name of a
|
||||
## credit provider, such as 'State University' or 'Happy Fun Company'.
|
||||
${_("Thank you for your payment. To receive course credit, you must now request credit at the {link_to_provider_site} website. Select <b>Request Credit</b> to get started.").format(
|
||||
link_to_provider_site=provider_link,
|
||||
)
|
||||
}
|
||||
</p>
|
||||
<div class="credit-action">
|
||||
% if credit_btn_label:
|
||||
<a class="btn credit-btn ${credit_btn_class}" href="${credit_btn_href}" target="_blank" data-course-key="${credit_status['course_key']}" data-user="${user.username}" data-provider="${credit_status['provider_id']}">
|
||||
${credit_btn_label}
|
||||
</a>
|
||||
% endif
|
||||
<a class="btn credit-btn access-credit-btn" href="${credit_status['provider_status_url']}" target="_blank">${_("View Details")}</a>
|
||||
% elif credit_status["request_status"] == "approved" and not credit_status["error"] :
|
||||
<p class="message-copy credit-msg credit-request-approved-msg">
|
||||
## Translators: link_to_provider_site is a link to an external webpage. The text of the link will be the name of a
|
||||
## credit provider, such as 'State University' or 'Happy Fun Company'. provider_name is the name of credit provider.
|
||||
${_("<b>Congratulations!</b> {provider_name} has approved your request for course credit. To see your course credit, visit the {link_to_provider_site} website.").format(
|
||||
provider_name=credit_status["provider_name"],
|
||||
link_to_provider_site=provider_link,
|
||||
)
|
||||
}
|
||||
</p>
|
||||
<a class="btn credit-btn access-credit-btn" href="${credit_status['provider_status_url']}" target="_blank">${_("View Credit")}</a>
|
||||
% elif credit_status["request_status"] == "rejected" and not credit_status["error"] :
|
||||
<p class="message-copy credit-msg credit-request-rejected-msg">
|
||||
## Translators: link_to_provider_site is a link to an external webpage. The text of the link will be the name of a
|
||||
## credit provider, such as 'State University' or 'Happy Fun Company'. provider_name is the name of credit provider.
|
||||
${_("{provider_name} did not approve your request for course credit. For more information, contact {link_to_provider_site} directly.").format(
|
||||
provider_name=credit_status["provider_name"],
|
||||
link_to_provider_site=provider_link,
|
||||
)
|
||||
}
|
||||
</p>
|
||||
% endif
|
||||
<div class="message-copy credit-msg ${credit_msg_class}">${credit_msg}</div>
|
||||
</div>
|
||||
</div>
|
||||
% endif
|
||||
|
||||
@@ -3,8 +3,8 @@ API library for Django REST Framework permissions-oriented workflows
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from rest_framework import permissions
|
||||
from django.http import Http404
|
||||
from rest_framework import permissions
|
||||
|
||||
from student.roles import CourseStaffRole
|
||||
|
||||
@@ -13,6 +13,7 @@ class ApiKeyHeaderPermission(permissions.BasePermission):
|
||||
"""
|
||||
Django REST Framework permissions class used to manage API Key integrations
|
||||
"""
|
||||
|
||||
def has_permission(self, request, view):
|
||||
"""
|
||||
Check for permissions by matching the configured API key and header
|
||||
@@ -35,8 +36,9 @@ class ApiKeyHeaderPermissionIsAuthenticated(ApiKeyHeaderPermission, permissions.
|
||||
|
||||
See ApiKeyHeaderPermission for more information how the API key portion is implemented.
|
||||
"""
|
||||
|
||||
def has_permission(self, request, view):
|
||||
#TODO We can optimize this later on when we know which of these methods is used more often.
|
||||
# TODO We can optimize this later on when we know which of these methods is used more often.
|
||||
api_permissions = ApiKeyHeaderPermission.has_permission(self, request, view)
|
||||
is_authenticated_permissions = permissions.IsAuthenticated.has_permission(self, request, view)
|
||||
return api_permissions or is_authenticated_permissions
|
||||
@@ -46,6 +48,7 @@ class IsUserInUrl(permissions.BasePermission):
|
||||
"""
|
||||
Permission that checks to see if the request user matches the user in the URL.
|
||||
"""
|
||||
|
||||
def has_permission(self, request, view):
|
||||
"""
|
||||
Returns true if the current request is by the user themselves.
|
||||
@@ -65,6 +68,7 @@ class IsUserInUrlOrStaff(IsUserInUrl):
|
||||
"""
|
||||
Permission that checks to see if the request user matches the user in the URL or has is_staff access.
|
||||
"""
|
||||
|
||||
def has_permission(self, request, view):
|
||||
if request.user.is_staff:
|
||||
return True
|
||||
@@ -76,6 +80,7 @@ class IsStaffOrReadOnly(permissions.BasePermission):
|
||||
"""Permission that checks to see if the user is global or course
|
||||
staff, permitting only read-only access if they are not.
|
||||
"""
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
return (request.user.is_staff or
|
||||
CourseStaffRole(obj.course_id).has_user(request.user) or
|
||||
@@ -87,9 +92,12 @@ class IsStaffOrOwner(permissions.BasePermission):
|
||||
Permission that allows access to admin users or the owner of an object.
|
||||
The owner is considered the User object represented by obj.user.
|
||||
"""
|
||||
|
||||
def has_object_permission(self, request, view, obj):
|
||||
return request.user.is_staff or obj.user == request.user
|
||||
|
||||
def has_permission(self, request, view):
|
||||
user = request.user
|
||||
return user.is_staff or (user.username == request.GET.get('username'))
|
||||
return user.is_staff \
|
||||
or (user.username == request.GET.get('username')) \
|
||||
or (user.username == getattr(request, 'data', {}).get('username'))
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
""" Tests for API permissions classes. """
|
||||
|
||||
import ddt
|
||||
from django.test import TestCase, RequestFactory
|
||||
|
||||
from openedx.core.lib.api.permissions import IsStaffOrOwner
|
||||
@@ -10,8 +12,10 @@ class TestObject(object):
|
||||
user = None
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class IsStaffOrOwnerTests(TestCase):
|
||||
""" Tests for IsStaffOrOwner permission class. """
|
||||
|
||||
def setUp(self):
|
||||
super(IsStaffOrOwnerTests, self).setUp()
|
||||
self.permission = IsStaffOrOwner()
|
||||
@@ -50,13 +54,24 @@ class IsStaffOrOwnerTests(TestCase):
|
||||
self.request.user = UserFactory.create(is_staff=True)
|
||||
self.assertTrue(self.permission.has_permission(self.request, None))
|
||||
|
||||
def test_has_permission_as_owner(self):
|
||||
""" Owners always have permission. """
|
||||
def test_has_permission_as_owner_with_get(self):
|
||||
""" Owners always have permission to make GET actions. """
|
||||
user = UserFactory.create()
|
||||
request = RequestFactory().get('/?username={}'.format(user.username))
|
||||
request.user = user
|
||||
self.assertTrue(self.permission.has_permission(request, None))
|
||||
|
||||
@ddt.data('patch', 'post', 'put')
|
||||
def test_has_permission_as_owner_with_edit(self, action):
|
||||
""" Owners always have permission to edit. """
|
||||
user = UserFactory.create()
|
||||
|
||||
data = {'username': user.username}
|
||||
request = getattr(RequestFactory(), action)('/', data, format='json')
|
||||
request.user = user
|
||||
request.data = data # Note (CCB): This is a hack that should be fixed. (ECOM-3171)
|
||||
self.assertTrue(self.permission.has_permission(request, None))
|
||||
|
||||
def test_has_permission_as_non_owner(self):
|
||||
""" Non-owners should not have permission. """
|
||||
user = UserFactory.create()
|
||||
|
||||
Reference in New Issue
Block a user