Skip to content

Commit

Permalink
feat: upstream_block fields prototype (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdmccormick committed Jun 5, 2024
1 parent 4ee990b commit f9e6dcb
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 7 deletions.
20 changes: 17 additions & 3 deletions cms/djangoapps/contentstore/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
from django.conf import settings
from django.utils.translation import gettext as _
from opaque_keys.edx.keys import AssetKey, CourseKey, UsageKey
from opaque_keys.edx.locator import DefinitionLocator, LocalId
from opaque_keys.edx.locator import DefinitionLocator, LocalId, LibraryUsageLocatorV2
from xblock.core import XBlock
from xblock.fields import ScopeIds
from xblock.fields import Scope, ScopeIds
from xblock.runtime import IdGenerator
from xmodule.contentstore.content import StaticContent
from xmodule.contentstore.django import contentstore
Expand All @@ -23,6 +23,7 @@

from cms.djangoapps.models.settings.course_grading import CourseGradingModel
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.content_libraries.api import get_library_block
import openedx.core.djangoapps.content_staging.api as content_staging_api
import openedx.core.djangoapps.content_tagging.api as content_tagging_api

Expand Down Expand Up @@ -293,7 +294,6 @@ def import_staged_content_from_user_clipboard(parent_key: UsageKey, request) ->
staged_content_id=user_clipboard.content.id,
static_files=static_files,
)

return new_xblock, notices


Expand Down Expand Up @@ -375,6 +375,20 @@ def _import_xml_node_to_parent(
if copied_from_block:
# Store a reference to where this block was copied from, in the 'copied_from_block' field (AuthoringMixin)
temp_xblock.copied_from_block = copied_from_block
# If it was copied from a library, set the 'upstream_*' fields (AuthoringMixin)
copied_from_key = UsageKey.from_string(copied_from_block) # @@TODO: param should be UsageKey, not str
if isinstance(copied_from_key, LibraryUsageLocatorV2):
temp_xblock.upstream_block = copied_from_block
temp_xblock.upstream_block_version = get_library_block(copied_from_key).version_num # @@TODO: handle miss?
from openedx.core.djangoapps.xblock.api import load_block
from django.contrib.auth import get_user_model
upstream_xblock = load_block(copied_from_key, get_user_model().objects.get(id=user_id))
print(temp_xblock.upstream_block_settings)
temp_xblock.upstream_block_settings = {
field.name: getattr(upstream_xblock, field.name)
for field in upstream_xblock.fields
if field.scope == Scope.settings
}
# Save the XBlock into modulestore. We need to save the block and its parent for this to work:
new_xblock = store.update_item(temp_xblock, user_id, allow_not_found=True)
parent_xblock.children.append(new_xblock.location)
Expand Down
36 changes: 35 additions & 1 deletion cms/lib/xblock/authoring_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from django.conf import settings
from web_fragments.fragment import Fragment
from xblock.core import XBlock, XBlockMixin
from xblock.fields import String, Scope
from xblock.fields import Integer, String, Scope, Dict

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -51,3 +51,37 @@ def visibility_view(self, _context=None):
scope=Scope.settings,
enforce_type=True,
)

# Note: upstream_* fields are only used by CMS. Not needed in the LMS.
upstream_block = String(
scope=Scope.settings,
help=(
"The usage key of a block (generally within a Content Library) which serves as a source of upstream "
"updates for this block, or None if there is no such upstream. Please note: It is valid for upstream_block "
"to hold a usage key for a block that does not exist (or does not *yet* exist) on this instance, "
"particularly if this block was imported from a different instance."
),
hidden=True,
default=None,
enforce_type=True,
)
upstream_block_version = Integer(
scope=Scope.settings,
help=(
"The upstream_block's version number, at the time this block was created from it. "
"If this version is older than the upstream_block's latest version, then CMS will "
"allow this block to fetch updated content from upstream_block."
),
hidden=True,
default=None,
enforce_type=True,
)
upstream_block_settings = Dict(
scope=Scope.settings,
help=(
"@@TODO"
),
hidden=True,
default={},
enforce_type=True,
)
5 changes: 4 additions & 1 deletion openedx/core/djangoapps/content_libraries/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ class LibraryXBlockMetadata:
Class that represents the metadata about an XBlock in a content library.
"""
usage_key = attr.ib(type=LibraryUsageLocatorV2)
version_num = attr.ib(type=int)
display_name = attr.ib("")
has_unpublished_changes = attr.ib(False)
tags_count = attr.ib(0)
Expand All @@ -210,7 +211,8 @@ def from_component(cls, library_key, component):
component.local_key,
),
display_name=component.versioning.draft.title,
has_unpublished_changes=component.versioning.has_unpublished_changes
has_unpublished_changes=component.versioning.has_unpublished_changes,
version_num=component.versioning.draft.version_num,
)


Expand Down Expand Up @@ -651,6 +653,7 @@ def get_library_block(usage_key) -> LibraryXBlockMetadata:
usage_key=usage_key,
display_name=draft_version.title,
has_unpublished_changes=(draft_version != published_version),
version_num=draft_version.version_num,
)


Expand Down
12 changes: 11 additions & 1 deletion xmodule/modulestore/inheritance.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,11 @@ def default(self, block, name):
return field.read_json(ancestor)
else:
ancestor = ancestor.get_parent()
# @@TODO de-kludgify, move to core or mixin?
try:
return block.fields["upstream_block_settings"][name]
except KeyError:
pass
return super().default(block, name)


Expand Down Expand Up @@ -448,4 +453,9 @@ def default(self, key):
inheriting, this will raise KeyError which will cause the caller to use
the field's global default.
"""
return self.inherited_settings[key.field_name]
try:
return self.inherited_settings[key.field_name]
except KeyError:
pass
# @@TODO de-kludgify, move to its own mixin, or core?
return self._fields.get("upstream_block_settings", {})[key.field_name]
2 changes: 1 addition & 1 deletion xmodule/modulestore/split_mongo/split_mongo_kvs.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def has_default_value(self, field_name):
"""
Is the given field has default value in this kvs
"""
return field_name in self._defaults
return field_name in self._defaults or super().has_default_value(field_name)

def default(self, key):
"""
Expand Down

0 comments on commit f9e6dcb

Please sign in to comment.