diff --git a/cms/djangoapps/contentstore/views/preview.py b/cms/djangoapps/contentstore/views/preview.py index 33951a6089..82d2908005 100644 --- a/cms/djangoapps/contentstore/views/preview.py +++ b/cms/djangoapps/contentstore/views/preview.py @@ -11,7 +11,7 @@ from edxmako.shortcuts import render_to_string from xmodule_modifiers import replace_static_urls, wrap_xblock from xmodule.error_module import ErrorDescriptor from xmodule.exceptions import NotFoundError, ProcessingError -from xmodule.modulestore.django import modulestore, loc_mapper +from xmodule.modulestore.django import modulestore, loc_mapper, ModuleI18nService from xmodule.modulestore.locator import Locator from xmodule.x_module import ModuleSystem from xblock.runtime import KvsFieldData @@ -132,6 +132,9 @@ def _preview_module_system(request, descriptor): # If descriptor.location is a CourseLocator, course_id is unused. get_user_role=lambda: get_user_role(request.user, descriptor.location, course_id), descriptor_runtime=descriptor.runtime, + services={ + "i18n": ModuleI18nService(), + }, ) diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 66b3c0630f..f8fa11c185 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -408,7 +408,7 @@ class CourseDescriptor(CourseFields, SequenceDescriptor): # set the due_date_display_format to what would have been shown previously (with no timezone). # Then remove show_timezone so that if the user clears out the due_date_display_format, # they get the default date display. - self.due_date_display_format = u"%b %d, %Y at %H:%M" + self.due_date_display_format = "DATE_TIME" delattr(self, 'show_timezone') # NOTE: relies on the modulestore to call set_grading_policy() right after diff --git a/common/lib/xmodule/xmodule/modulestore/django.py b/common/lib/xmodule/xmodule/modulestore/django.py index 62ed8de035..5e17a59183 100644 --- a/common/lib/xmodule/xmodule/modulestore/django.py +++ b/common/lib/xmodule/xmodule/modulestore/django.py @@ -6,15 +6,17 @@ Passes settings.MODULESTORE as kwargs to MongoModuleStore from __future__ import absolute_import from importlib import import_module - import re from django.conf import settings from django.core.cache import get_cache, InvalidCacheBackendError from django.dispatch import Signal +import django.utils + from xmodule.modulestore.loc_mapper_store import LocMapperStore from xmodule.util.django import get_current_request_hostname + # We may not always have the request_cache module available try: from request_cache.middleware import RequestCache @@ -38,7 +40,7 @@ def load_function(path): return getattr(import_module(module_path), name) -def create_modulestore_instance(engine, doc_store_config, options): +def create_modulestore_instance(engine, doc_store_config, options, i18n_service=None): """ This will return a new instance of a modulestore given an engine and options """ @@ -68,6 +70,7 @@ def create_modulestore_instance(engine, doc_store_config, options): xblock_mixins=getattr(settings, 'XBLOCK_MIXINS', ()), xblock_select=getattr(settings, 'XBLOCK_SELECT_FUNCTION', None), doc_store_config=doc_store_config, + i18n_service=i18n_service or ModuleI18nService(), **_options ) @@ -186,3 +189,33 @@ def editable_modulestore(name='default'): else: return None + + +class ModuleI18nService(object): + """ + Implement the XBlock runtime "i18n" service. + + Mostly a pass-through to Django's translation module. + django.utils.translation implements the gettext.Translations interface (it + has ugettext, ungettext, etc), so we can use it directly as the runtime + i18n service. + + """ + def __getattr__(self, name): + return getattr(django.utils.translation, name) + + def strftime(self, *args, **kwargs): + """ + A locale-aware implementation of strftime. + """ + # This is the wrong place to import this function. I'm putting it here + # because the xmodule test suite can't import this module, because + # Django is not available in that suite. This function isn't called in + # that suite, so this hides the import so the test won't fail. + # + # As I said, this is wrong. But Cale says this code will soon be + # refactored to a place that will be right, and the code can be made + # right there. If you are reading this comment after April 1, 2014, + # then Cale was a liar. + from util.date_utils import strftime_localized + return strftime_localized(*args, **kwargs) diff --git a/common/lib/xmodule/xmodule/modulestore/mixed.py b/common/lib/xmodule/xmodule/modulestore/mixed.py index e8ed091532..6005638796 100644 --- a/common/lib/xmodule/xmodule/modulestore/mixed.py +++ b/common/lib/xmodule/xmodule/modulestore/mixed.py @@ -23,7 +23,7 @@ class MixedModuleStore(ModuleStoreWriteBase): ModuleStore knows how to route requests to the right persistence ms and how to convert any references in the xblocks to the type required by the app and the persistence layer. """ - def __init__(self, mappings, stores, reference_type=None, **kwargs): + def __init__(self, mappings, stores, reference_type=None, i18n_service=None, **kwargs): """ Initialize a MixedModuleStore. Here we look into our passed in kwargs which should be a collection of other modulestore configuration informations @@ -55,7 +55,8 @@ class MixedModuleStore(ModuleStoreWriteBase): store['ENGINE'], # XMLModuleStore's don't have doc store configs store.get('DOC_STORE_CONFIG', {}), - store['OPTIONS'] + store['OPTIONS'], + i18n_service=i18n_service, ) def _get_modulestore_for_courseid(self, course_id): diff --git a/common/lib/xmodule/xmodule/modulestore/mongo/base.py b/common/lib/xmodule/xmodule/modulestore/mongo/base.py index 1b26b9f46e..eb7670b3ce 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo/base.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo/base.py @@ -263,6 +263,7 @@ class MongoModuleStore(ModuleStoreWriteBase): def __init__(self, doc_store_config, fs_root, render_template, default_class=None, error_tracker=null_error_tracker, + i18n_service=None, **kwargs): """ :param doc_store_config: must have a host, db, and collection entries. Other common entries: port, tz_aware. @@ -312,6 +313,8 @@ class MongoModuleStore(ModuleStoreWriteBase): self.fs_root = path(fs_root) self.error_tracker = error_tracker self.render_template = render_template + self.i18n_service = i18n_service + self.ignore_write_events_on_courses = [] def compute_metadata_inheritance_tree(self, location): @@ -498,6 +501,10 @@ class MongoModuleStore(ModuleStoreWriteBase): if apply_cached_metadata: cached_metadata = self.get_cached_metadata_inheritance_tree(location) + services = {} + if self.i18n_service: + services["i18n"] = self.i18n_service + # TODO (cdodge): When the 'split module store' work has been completed, we should remove # the 'metadata_inheritance_tree' parameter system = CachingDescriptorSystem( @@ -510,6 +517,7 @@ class MongoModuleStore(ModuleStoreWriteBase): cached_metadata=cached_metadata, mixins=self.xblock_mixins, select=self.xblock_select, + services=services, ) return system.load_item(location) @@ -631,6 +639,10 @@ class MongoModuleStore(ModuleStoreWriteBase): if metadata is None: metadata = {} if system is None: + services = {} + if self.i18n_service: + services["i18n"] = self.i18n_service + system = CachingDescriptorSystem( modulestore=self, module_data={}, @@ -641,6 +653,7 @@ class MongoModuleStore(ModuleStoreWriteBase): cached_metadata={}, mixins=self.xblock_mixins, select=self.xblock_select, + services=services, ) xblock_class = system.load_block_type(location.category) if definition_data is None: diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py index 6f0cba3318..27dba73e8a 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py @@ -105,6 +105,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): default_class=None, error_tracker=null_error_tracker, loc_mapper=None, + i18n_service=None, **kwargs): """ :param doc_store_config: must have a host, db, and collection entries. Other common entries: port, tz_aware. @@ -129,6 +130,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): self.fs_root = path(fs_root) self.error_tracker = error_tracker self.render_template = render_template + self.i18n_service = i18n_service # TODO: Don't have a runtime just to generate the appropriate mixin classes (cpennington) # This is only used by _partition_fields_by_scope, which is only needed because @@ -180,6 +182,10 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): ''' system = self._get_cache(course_entry['structure']['_id']) if system is None: + services = {} + if self.i18n_service: + services["i18n"] = self.i18n_service + system = CachingDescriptorSystem( modulestore=self, course_entry=course_entry, @@ -191,6 +197,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase): resources_fs=None, mixins=self.xblock_mixins, select=self.xblock_select, + services=services, ) self._add_cache(course_entry['structure']['_id'], system) self.cache_items(system, block_ids, depth, lazy) diff --git a/common/lib/xmodule/xmodule/modulestore/xml.py b/common/lib/xmodule/xmodule/modulestore/xml.py index 89cda4c399..e1ddcac938 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml.py +++ b/common/lib/xmodule/xmodule/modulestore/xml.py @@ -347,8 +347,10 @@ class XMLModuleStore(ModuleStoreReadBase): """ An XML backed ModuleStore """ - def __init__(self, data_dir, default_class=None, course_dirs=None, course_ids=None, - load_error_modules=True, **kwargs): + def __init__( + self, data_dir, default_class=None, course_dirs=None, course_ids=None, + load_error_modules=True, i18n_service=None, **kwargs + ): """ Initialize an XMLModuleStore from data_dir @@ -382,6 +384,8 @@ class XMLModuleStore(ModuleStoreReadBase): # All field data will be stored in an inheriting field data. self.field_data = inheriting_field_data(kvs=DictKeyValueStore()) + self.i18n_service = i18n_service + # If we are specifically asked for missing courses, that should # be an error. If we are asked for "all" courses, find the ones # that have a course.xml. We sort the dirs in alpha order so we always @@ -522,6 +526,10 @@ class XMLModuleStore(ModuleStoreReadBase): """ return policy.get(policy_key(usage_id), {}) + services = {} + if self.i18n_service: + services['i18n'] = self.i18n_service + system = ImportSystem( xmlstore=self, course_id=course_id, @@ -534,6 +542,7 @@ class XMLModuleStore(ModuleStoreReadBase): default_class=self.default_class, select=self.xblock_select, field_data=self.field_data, + services=services, ) course_descriptor = system.process_xml(etree.tostring(course_data, encoding='unicode')) diff --git a/conf/locale/eo/LC_MESSAGES/django.po b/conf/locale/eo/LC_MESSAGES/django.po index b54d1ce64d..c1c6252f86 100644 --- a/conf/locale/eo/LC_MESSAGES/django.po +++ b/conf/locale/eo/LC_MESSAGES/django.po @@ -37,7 +37,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2014-02-19 15:39-0500\n" +"POT-Creation-Date: 2014-02-20 13:02-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/conf/locale/eo/LC_MESSAGES/djangojs.po b/conf/locale/eo/LC_MESSAGES/djangojs.po index f4f4910442..2c30ce394c 100644 --- a/conf/locale/eo/LC_MESSAGES/djangojs.po +++ b/conf/locale/eo/LC_MESSAGES/djangojs.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: 0.1a\n" "Report-Msgid-Bugs-To: openedx-translation@googlegroups.com\n" -"POT-Creation-Date: 2014-02-19 15:38-0500\n" -"PO-Revision-Date: 2014-02-19 20:39:50.876422\n" +"POT-Creation-Date: 2014-02-20 13:01-0500\n" +"PO-Revision-Date: 2014-02-20 18:02:05.375610\n" "Last-Translator: \n" "Language-Team: openedx-translation \n" "MIME-Version: 1.0\n" diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index f636ccea7c..936f42cc8f 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -13,7 +13,6 @@ from django.core.cache import cache from django.core.exceptions import PermissionDenied from django.core.urlresolvers import reverse from django.http import Http404, HttpResponse -import django.utils from django.views.decorators.csrf import csrf_exempt from capa.xqueue_interface import XQueueInterface @@ -33,14 +32,13 @@ from xblock.django.request import django_to_webob_request, webob_to_django_respo from xmodule.error_module import ErrorDescriptor, NonStaffErrorDescriptor from xmodule.exceptions import NotFoundError, ProcessingError from xmodule.modulestore import Location -from xmodule.modulestore.django import modulestore +from xmodule.modulestore.django import modulestore, ModuleI18nService from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.util.duedate import get_extended_due_date from xmodule_modifiers import replace_course_urls, replace_jump_to_id_urls, replace_static_urls, add_staff_debug_info, wrap_xblock from xmodule.lti_module import LTIModule from xmodule.x_module import XModuleDescriptor -from util.date_utils import strftime_localized from util.json_request import JsonResponse from util.sandboxing import can_execute_unsafe_code @@ -651,20 +649,3 @@ def _check_files_limits(files): return msg return None - - -class ModuleI18nService(object): - """ - Implement the XBlock runtime "i18n" service. - - Mostly a pass-through to Django's translation module. - django.utils.translation implements the gettext.Translations interface (it - has ugettext, ungettext, etc), so we can use it directly as the runtime - i18n service. - - """ - def __getattr__(self, name): - return getattr(django.utils.translation, name) - - def strftime(self, *args, **kwargs): - return strftime_localized(*args, **kwargs) diff --git a/lms/djangoapps/open_ended_grading/open_ended_notifications.py b/lms/djangoapps/open_ended_grading/open_ended_notifications.py index 4962f8efa9..2e2880ca9d 100644 --- a/lms/djangoapps/open_ended_grading/open_ended_notifications.py +++ b/lms/djangoapps/open_ended_grading/open_ended_notifications.py @@ -7,6 +7,8 @@ from django.conf import settings from xmodule.open_ended_grading_classes import peer_grading_service from xmodule.open_ended_grading_classes.controller_query_service import ControllerQueryService +from xmodule.modulestore.django import ModuleI18nService + from courseware.access import has_access from lms.lib.xblock.runtime import LmsModuleSystem from edxmako.shortcuts import render_to_string @@ -70,6 +72,9 @@ def peer_grading_notifications(course, user): render_template=render_to_string, replace_urls=None, descriptor_runtime=None, + services={ + 'i18n': ModuleI18nService(), + }, ) peer_gs = peer_grading_service.PeerGradingService(settings.OPEN_ENDED_GRADING_INTERFACE, system) pending_grading = False @@ -131,6 +136,9 @@ def combined_notifications(course, user): render_template=render_to_string, replace_urls=None, descriptor_runtime=None, + services={ + 'i18n': ModuleI18nService(), + }, ) #Initialize controller query service using our mock system controller_qs = ControllerQueryService(settings.OPEN_ENDED_GRADING_INTERFACE, system) diff --git a/lms/djangoapps/open_ended_grading/staff_grading_service.py b/lms/djangoapps/open_ended_grading/staff_grading_service.py index dad93fbc28..17b297826c 100644 --- a/lms/djangoapps/open_ended_grading/staff_grading_service.py +++ b/lms/djangoapps/open_ended_grading/staff_grading_service.py @@ -11,6 +11,7 @@ from django.utils.translation import ugettext as _ from xmodule.course_module import CourseDescriptor from xmodule.open_ended_grading_classes.grading_service_module import GradingService, GradingServiceError +from xmodule.modulestore.django import ModuleI18nService from courseware.access import has_access from lms.lib.xblock.runtime import LmsModuleSystem @@ -84,6 +85,9 @@ class StaffGradingService(GradingService): render_template=render_to_string, replace_urls=None, descriptor_runtime=None, + services={ + 'i18n': ModuleI18nService(), + }, ) super(StaffGradingService, self).__init__(config) self.url = config['url'] + config['staff_grading'] diff --git a/lms/djangoapps/open_ended_grading/utils.py b/lms/djangoapps/open_ended_grading/utils.py index 9f60289a2d..fb523df17e 100644 --- a/lms/djangoapps/open_ended_grading/utils.py +++ b/lms/djangoapps/open_ended_grading/utils.py @@ -2,7 +2,7 @@ import json import logging from xmodule.modulestore import search -from xmodule.modulestore.django import modulestore +from xmodule.modulestore.django import modulestore, ModuleI18nService from xmodule.modulestore.exceptions import ItemNotFoundError, NoPathToItem from xmodule.open_ended_grading_classes.controller_query_service import ControllerQueryService from xmodule.open_ended_grading_classes.grading_service_module import GradingServiceError @@ -34,6 +34,9 @@ SYSTEM = LmsModuleSystem( render_template=render_to_string, replace_urls=None, descriptor_runtime=None, + services={ + 'i18n': ModuleI18nService(), + }, )