Commit 6bb0eee3 by Ned Batchelder

Make the i18n service available everywhere.

To get all dates localized, we need all runtimes to have the i18n
service, and since runtimes are made by the modulestores, we need to
pass it to them.

Soon (fingers crossed), modulestores won't be involved in making
runtimes, and many of these changes will go away.
parent b7c557ff
......@@ -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(),
},
)
......
......@@ -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
......
......@@ -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)
......@@ -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):
......
......@@ -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:
......
......@@ -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)
......
......@@ -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'))
......
......@@ -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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
......@@ -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 <openedx-translation@googlegroups.com>\n"
"MIME-Version: 1.0\n"
......
......@@ -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)
......@@ -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)
......
......@@ -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']
......
......@@ -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(),
},
)
......
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