Commit 4d75c180 by Andy Armstrong

Fix Mako templates to use latest request context

Fixed the edX middleware to lazily create request
context for Mako templates, so that it is based on
any changes made to the request object.

Verified that with this fix the "Instructor" tab is
correctly hidden when a staff member is viewing
the course as a student.
parent dfc4da15
......@@ -7,7 +7,7 @@
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distribuetd under the License is distributed on an "AS IS" BASIS,
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
......@@ -22,10 +22,24 @@ REQUEST_CONTEXT = threading.local()
class MakoMiddleware(object):
def process_request(self, request):
REQUEST_CONTEXT.context = RequestContext(request)
REQUEST_CONTEXT.context['is_secure'] = request.is_secure()
REQUEST_CONTEXT.context['site'] = safe_get_host(request)
""" Process the middleware request. """
REQUEST_CONTEXT.request = request
def process_response(self, request, response):
REQUEST_CONTEXT.context = None
def process_response(self, __, response):
""" Process the middleware response. """
REQUEST_CONTEXT.request = None
return response
def get_template_request_context():
"""
Returns the template processing context to use for the current request,
or returns None if there is not a current request.
"""
request = getattr(REQUEST_CONTEXT, "request", None)
if not request:
return None
context = RequestContext(request)
context['is_secure'] = request.is_secure()
context['site'] = safe_get_host(request)
return context
......@@ -19,7 +19,7 @@ import logging
from microsite_configuration import microsite
from edxmako import lookup_template
import edxmako.middleware
from edxmako.middleware import get_template_request_context
from django.conf import settings
from django.core.urlresolvers import reverse
log = logging.getLogger(__name__)
......@@ -114,11 +114,12 @@ def render_to_string(template_name, dictionary, context=None, namespace='main'):
context_instance['marketing_link'] = marketing_link
# In various testing contexts, there might not be a current request context.
if getattr(edxmako.middleware.REQUEST_CONTEXT, "context", None):
for d in edxmako.middleware.REQUEST_CONTEXT.context:
context_dictionary.update(d)
for d in context_instance:
context_dictionary.update(d)
request_context = get_template_request_context()
if request_context:
for item in request_context:
context_dictionary.update(item)
for item in context_instance:
context_dictionary.update(item)
if context:
context_dictionary.update(context)
# fetch and render template
......
......@@ -12,12 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import edxmako
from django.conf import settings
from mako.template import Template as MakoTemplate
from edxmako.middleware import get_template_request_context
from edxmako.shortcuts import marketing_link
import edxmako
import edxmako.middleware
from mako.template import Template as MakoTemplate
DJANGO_VARIABLES = ['output_encoding', 'encoding_errors']
......@@ -48,11 +48,12 @@ class Template(MakoTemplate):
context_dictionary = {}
# In various testing contexts, there might not be a current request context.
if getattr(edxmako.middleware.REQUEST_CONTEXT, "context", None):
for d in edxmako.middleware.REQUEST_CONTEXT.context:
context_dictionary.update(d)
for d in context_instance:
context_dictionary.update(d)
request_context = get_template_request_context()
if request_context:
for item in request_context:
context_dictionary.update(item)
for item in context_instance:
context_dictionary.update(item)
context_dictionary['settings'] = settings
context_dictionary['EDX_ROOT_URL'] = settings.EDX_ROOT_URL
context_dictionary['django_context'] = context_instance
......
......@@ -10,6 +10,7 @@ from django.test.utils import override_settings
from django.test.client import RequestFactory
from django.core.urlresolvers import reverse
import edxmako.middleware
from edxmako.middleware import get_template_request_context
from edxmako import add_lookup, LOOKUP
from edxmako.shortcuts import (
marketing_link,
......@@ -83,11 +84,11 @@ class MakoMiddlewareTest(TestCase):
self.middleware.process_request(self.request)
# requestcontext should not be None.
self.assertIsNotNone(edxmako.middleware.REQUEST_CONTEXT.context)
self.assertIsNotNone(get_template_request_context())
self.middleware.process_response(self.request, self.response)
# requestcontext should be None.
self.assertIsNone(edxmako.middleware.REQUEST_CONTEXT.context)
self.assertIsNone(get_template_request_context())
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@patch("edxmako.middleware.REQUEST_CONTEXT")
......
......@@ -4,6 +4,7 @@ Base class for pages in courseware.
from bok_choy.page_object import PageObject
from . import BASE_URL
from .tab_nav import TabNavPage
class CoursePage(PageObject):
......@@ -29,3 +30,11 @@ class CoursePage(PageObject):
Construct a URL to the page within the course.
"""
return BASE_URL + "/courses/" + self.course_id + "/" + self.url_path
def has_tab(self, tab_name):
"""
Returns true if the current page is showing a tab with the given name.
:return:
"""
tab_nav = TabNavPage(self.browser)
return tab_name in tab_nav.tab_names
......@@ -2,9 +2,10 @@
Staff view of courseware
"""
from bok_choy.page_object import PageObject
from .courseware import CoursewarePage
class StaffPage(PageObject):
class StaffPage(CoursewarePage):
"""
View of courseware pages while logged in as course staff
"""
......@@ -13,6 +14,8 @@ class StaffPage(PageObject):
STAFF_STATUS_CSS = '#staffstatus'
def is_browser_on_page(self):
if not super(StaffPage, self).is_browser_on_page():
return False
return self.q(css=self.STAFF_STATUS_CSS).present
@property
......
......@@ -48,7 +48,7 @@ class TabNavPage(PageObject):
Return the CSS to click for `tab_name`.
If no tabs exist for that name, return `None`.
"""
all_tabs = self._tab_names
all_tabs = self.tab_names
try:
tab_index = all_tabs.index(tab_name)
......@@ -58,7 +58,7 @@ class TabNavPage(PageObject):
return 'ol.course-tabs li:nth-of-type({0}) a'.format(tab_index + 1)
@property
def _tab_names(self):
def tab_names(self):
"""
Return the list of available tab names. If no tab names
are available, wait for them to load. Raises a `BrokenPromiseError`
......
......@@ -11,15 +11,15 @@ from ...fixtures.course import CourseFixture, XBlockFixtureDesc
from textwrap import dedent
class StaffDebugTest(UniqueCourseTest):
class StaffViewTest(UniqueCourseTest):
"""
Tests that verify the staff debug info.
Tests that verify the staff view.
"""
USERNAME = "STAFF_TESTER"
EMAIL = "johndoe@example.com"
def setUp(self):
super(StaffDebugTest, self).setUp()
super(StaffViewTest, self).setUp()
self.courseware_page = CoursewarePage(self.browser, self.course_id)
......@@ -59,10 +59,31 @@ class StaffDebugTest(UniqueCourseTest):
Open staff page with assertion
"""
self.courseware_page.visit()
staff_page = StaffPage(self.browser)
staff_page = StaffPage(self.browser, self.course_id)
self.assertEqual(staff_page.staff_status, 'Staff view')
return staff_page
class StaffViewToggleTest(StaffViewTest):
"""
Tests for the staff view toggle button.
"""
def test_instructor_tab_visibility(self):
"""
Test that the instructor tab is hidden when viewing as a student.
"""
course_page = self._goto_staff_page()
self.assertTrue(course_page.has_tab('Instructor'))
course_page.toggle_staff_view()
self.assertEqual(course_page.staff_status, 'Student view')
self.assertFalse(course_page.has_tab('Instructor'))
class StaffDebugTest(StaffViewTest):
"""
Tests that verify the staff debug info.
"""
def test_reset_attempts_empty(self):
"""
Test that we reset even when there is no student state
......
......@@ -678,7 +678,7 @@ class UnitPublishingTest(ContainerBase):
"""
Verifies that the browser is on the staff page and returns a StaffPage.
"""
page = StaffPage(self.browser)
page = StaffPage(self.browser, self.course_id)
EmptyPromise(page.is_browser_on_page, 'Browser is on staff page in LMS').fulfill()
return page
......
......@@ -718,7 +718,7 @@ class StaffLockTest(CourseOutlineTest):
courseware = CoursewarePage(self.browser, self.course_id)
courseware.wait_for_page()
self.assertEqual(courseware.num_sections, 2)
StaffPage(self.browser).toggle_staff_view()
StaffPage(self.browser, self.course_id).toggle_staff_view()
self.assertEqual(courseware.num_sections, 1)
def test_locked_subsections_do_not_appear_in_lms(self):
......@@ -737,7 +737,7 @@ class StaffLockTest(CourseOutlineTest):
courseware = CoursewarePage(self.browser, self.course_id)
courseware.wait_for_page()
self.assertEqual(courseware.num_subsections, 2)
StaffPage(self.browser).toggle_staff_view()
StaffPage(self.browser, self.course_id).toggle_staff_view()
self.assertEqual(courseware.num_subsections, 1)
def test_toggling_staff_lock_on_section_does_not_publish_draft_units(self):
......
......@@ -92,7 +92,7 @@ def make_track_function(request):
return function
def toc_for_course(user, request, course, active_chapter, active_section, field_data_cache):
def toc_for_course(request, course, active_chapter, active_section, field_data_cache):
'''
Create a table of contents from the module store
......@@ -117,7 +117,7 @@ def toc_for_course(user, request, course, active_chapter, active_section, field_
'''
with modulestore().bulk_operations(course.id):
course_module = get_module_for_descriptor(user, request, course, field_data_cache, course.id)
course_module = get_module_for_descriptor(request.user, request, course, field_data_cache, course.id)
if course_module is None:
return None
......
......@@ -394,7 +394,7 @@ class TestTOC(ModuleStoreTestCase):
with check_mongo_calls(toc_finds):
actual = render.toc_for_course(
self.request.user, self.request, self.toy_course, self.chapter, None, self.field_data_cache
self.request, self.toy_course, self.chapter, None, self.field_data_cache
)
for toc_section in expected:
self.assertIn(toc_section, actual)
......@@ -432,7 +432,9 @@ class TestTOC(ModuleStoreTestCase):
'url_name': 'secret:magic', 'display_name': 'secret:magic'}])
with check_mongo_calls(toc_finds):
actual = render.toc_for_course(self.request.user, self.request, self.toy_course, self.chapter, section, self.field_data_cache)
actual = render.toc_for_course(
self.request, self.toy_course, self.chapter, section, self.field_data_cache
)
for toc_section in expected:
self.assertIn(toc_section, actual)
......
......@@ -555,7 +555,8 @@ class TestAccordionDueDate(BaseDueDateTests):
def get_text(self, course):
""" Returns the HTML for the accordion """
return views.render_accordion(
self.request, course, course.get_children()[0].scope_ids.usage_id.to_deprecated_string(), None, None
self.request, course, course.get_children()[0].scope_ids.usage_id.to_deprecated_string(),
None, None
)
......
......@@ -118,9 +118,7 @@ def render_accordion(request, course, chapter, section, field_data_cache):
Returns the html string
"""
# grab the table of contents
user = User.objects.prefetch_related("groups").get(id=request.user.id)
request.user = user # keep just one instance of User
toc = toc_for_course(user, request, course, chapter, section, field_data_cache)
toc = toc_for_course(request, course, chapter, section, field_data_cache)
context = dict([
('toc', toc),
......@@ -325,10 +323,15 @@ def index(request, course_id, chapter=None, section=None,
request.user = user # keep just one instance of User
with modulestore().bulk_operations(course_key):
return _index_bulk_op(request, user, course_key, chapter, section, position)
return _index_bulk_op(request, course_key, chapter, section, position)
def _index_bulk_op(request, user, course_key, chapter, section, position):
# pylint: disable=too-many-statements
def _index_bulk_op(request, course_key, chapter, section, position):
"""
Render the index page for the specified course.
"""
user = request.user
course = get_course_with_access(user, 'load', course_key, depth=2)
staff_access = has_access(user, 'staff', course)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment