|
75 | 75 | from web_fragments.fragment import Fragment |
76 | 76 | from webob import Response |
77 | 77 | from xblock.core import List, Scope, String, XBlock |
78 | | -from xblock.fields import Boolean, Float |
| 78 | +from xblock.fields import Boolean, Float, UserScope |
79 | 79 |
|
80 | 80 | try: |
81 | 81 | from xblock.utils.resources import ResourceLoader |
@@ -1043,3 +1043,56 @@ def definition_to_xml(self, resource_fs): |
1043 | 1043 | if self.data: |
1044 | 1044 | return etree.fromstring(self.data) |
1045 | 1045 | return etree.Element(self.category) |
| 1046 | + |
| 1047 | + def bind_for_student(self, user_id, wrappers=None): |
| 1048 | + """ |
| 1049 | + Set up this XBlock to act as an XModule instead of an XModuleDescriptor. |
| 1050 | +
|
| 1051 | + Arguments: |
| 1052 | + user_id: The user_id to set in scope_ids |
| 1053 | + wrappers: These are a list functions that put a wrapper, such as |
| 1054 | + LmsFieldData or OverrideFieldData, around the field_data. |
| 1055 | + Note that the functions will be applied in the order in |
| 1056 | + which they're listed. So [f1, f2] -> f2(f1(field_data)) |
| 1057 | + """ |
| 1058 | + |
| 1059 | + # Skip rebinding if we're already bound a user, and it's this user. |
| 1060 | + if self.scope_ids.user_id is not None and user_id == self.scope_ids.user_id: |
| 1061 | + if getattr(self.runtime, "position", None): |
| 1062 | + # update the position of the tab |
| 1063 | + self.position = self.runtime.position # pylint: disable=attribute-defined-outside-init |
| 1064 | + return |
| 1065 | + |
| 1066 | + # # If we are switching users mid-request, save the data from the old user. |
| 1067 | + # self.save() |
| 1068 | + |
| 1069 | + # Update scope_ids to point to the new user. |
| 1070 | + self.scope_ids = self.scope_ids._replace(user_id=user_id) |
| 1071 | + |
| 1072 | + # Clear out any cached instantiated children. |
| 1073 | + self.clear_child_cache() |
| 1074 | + |
| 1075 | + # Clear out any cached field data scoped to the old user. |
| 1076 | + for field in self.fields.values(): |
| 1077 | + if field.scope in (Scope.parent, Scope.children): |
| 1078 | + continue |
| 1079 | + |
| 1080 | + if field.scope.user == UserScope.ONE: |
| 1081 | + field._del_cached_value(self) # pylint: disable=protected-access |
| 1082 | + # not the most elegant way of doing this, but if we're removing |
| 1083 | + # a field from the module's field_data_cache, we should also |
| 1084 | + # remove it from its _dirty_fields |
| 1085 | + if field in self._dirty_fields: |
| 1086 | + del self._dirty_fields[field] |
| 1087 | + |
| 1088 | + if wrappers: |
| 1089 | + # Put user-specific wrappers around the field-data service for this block. |
| 1090 | + # Note that these are different from modulestore.xblock_field_data_wrappers, which are not user-specific. |
| 1091 | + wrapped_field_data = self.runtime.service(self, "field-data-unbound") |
| 1092 | + for wrapper in wrappers: |
| 1093 | + wrapped_field_data = wrapper(wrapped_field_data) |
| 1094 | + self._bound_field_data = wrapped_field_data # pylint: disable=attribute-defined-outside-init |
| 1095 | + if getattr(self.runtime, "uses_deprecated_field_data", False): |
| 1096 | + # This approach is deprecated but old mongo's CachingDescriptorSystem still requires it. |
| 1097 | + # For Split mongo's CachingDescriptor system, don't set ._field_data this way. |
| 1098 | + self._field_data = wrapped_field_data |
0 commit comments