Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support static assets when copy/pasting between courses and libraries #35668

Merged
merged 20 commits into from
Oct 23, 2024

Conversation

ormsbee
Copy link
Contributor

@ormsbee ormsbee commented Oct 17, 2024

Background

Enhance copy-paste between Courses and Libraries so that it brings along static assets.

The biggest challenge is dealing with the mismatch between how Libraries store assets (per-Component) and how Courses store assets (global Files and Uploads space). To bridge this, we're going to kludge a component-local namespace in Files and Uploads by making use of the obscure feature that you can create folders there at an API level, even if no such UI exists.

What's in this PR?

Assets work when copy-pasting between library components.

Assets work when copy-pasting from a library to a course, with the convention being to put that file in a subdirectory of the form: components/{block_type}/{block_id}/file

Example:
static-export

Note that this path does not currently show up in the UI for Files and Uploads:
files-and-uploads

Assets work when copy-pasting from a course to a library

Top level assets are put into a static folder in the Component, per Learning Core conventions:

Screenshot 2024-10-21 at 12 59 23 PM

Screenshot 2024-10-21 at 12 59 31 PM

Limitations

Roundtrips don't work properly. There's no normalized form, so directories will start nesting if you copy from library and paste into course, then copy the pasted thing and paste back into library, etc. This was deemed acceptable for Sumac.

Low level stuff

  • XBlockSerializerForLearningCore has been removed, with the url_name stripping functionality added as an optional param to XBlockSerializer (the other stuff was for children and "vertical" -> "unit" conversion, neither of which are relevant now.
  • url_name is now stripped out of anything added to the clipboard, so that we don't end up writing it in block.xml when it is redundant (and would be stripped out with the next write anyway).
  • @kdmccormick: You very recently changed set_library_block_olx to return the new ComponentVersion.version_num, and I'm changing it again to return the whole ComponentVersion because I needed to grab other fields off of it. I fixed the code/tests that I saw, and I only mention this in case you started getting weird errors later while rebasing.

@ormsbee ormsbee self-assigned this Oct 17, 2024
@ormsbee ormsbee force-pushed the lib-copy-paste branch 2 times, most recently from b23c3bb to e61af3e Compare October 21, 2024 04:00
@ormsbee ormsbee marked this pull request as ready for review October 21, 2024 16:33
@bradenmacdonald
Copy link
Contributor

@ormsbee

@kdmccormick: You very recently changed set_library_block_olx to return the new ComponentVersion.version_num, and I'm changing it again to return the whole ComponentVersion because I needed to grab other fields off of it.

That was me :p I don't think it will affect any in-flight work.

@ormsbee
Copy link
Contributor Author

ormsbee commented Oct 21, 2024

That was me :p I don't think it will affect any in-flight work.

Oh, weird. I thought I remembered it as part of one of his PRs. Ah well, in any case, thanks folks. 😄

@bradenmacdonald
Copy link
Contributor

Roundtrips don't work properly. There's no normalized form, so directories will start nesting if you copy from library and paste into course, then copy the pasted thing and paste back into library, etc. This was deemed acceptable for Sumac.

When copying from a course and pasting into a library, would it not be fairly simple to detect paths of the pattern components/something/something/name and flatten those out back to static/name (provided static/name doesn't also exist)? Or even just strip out any directories in the static asset paths?

@ormsbee
Copy link
Contributor Author

ormsbee commented Oct 21, 2024

@bradenmacdonald:

When copying from a course and pasting into a library, would it not be fairly simple to detect paths of the pattern components/something/something/name and flatten those out back to static/name (provided static/name doesn't also exist)? Or even just strip out any directories in the static asset paths?

It is, but there are some weird edge cases that I didn't want to play whack-a-mole with in the absence of having a normalized form. For instance:

  1. It's legal to use an AssetKey that points to the asset in the subdirectory, or the actual directory written out, but the conversion isn't 1:1 because when going to AssetKey you substitute "_" for "/".
  2. The frontend editor derives its "/static/path/to/file" from the AssetKey that it's been transformed into (for preview purposes), but that transformation only goes one way, so it's translating back to "/static/path_to_file"–which actually works because it uses AssetKey to do the lookup on the backend.
  3. I'm not sure what the right behavior should be for copy-pasting those things with namespaced assets between the same course.

We need to pass a little more metadata about the asset to the frontend to do this properly, and I just didn't want to try to get that sane by tomorrow. The current setup just gets us to a place where it's no weirder than putting it into that subdirectory manually.

@bradenmacdonald
Copy link
Contributor

bradenmacdonald commented Oct 21, 2024

@ormsbee Given all that, I'm almost wondering if we should namespace the library-imported assets as e.g. block_asset_(type)_(id)_(filename) and leave everything flat, instead of bothering with the subfolders at all? Then we avoid the assetkey ambiguities. (Or use something like . instead of _ if necessary, if _ is still problematic.)

@ormsbee
Copy link
Contributor Author

ormsbee commented Oct 22, 2024

I guess I'm betting that if we can at least store them in a separated space, we'll have enough metadata information to rationalize this better in the future.

@bradenmacdonald
Copy link
Contributor

OK, but is a pseudo-subfolder any more of a more "separated space" than a unique prefix like block_asset_(type)_(id)_? I guess yes, because authors can't create folders in the UI? And because the filenames appear nicer in "Files & Uploads"?

@ormsbee
Copy link
Contributor Author

ormsbee commented Oct 22, 2024

Yeah, it's not great, but I think it's slightly better.

@ormsbee ormsbee force-pushed the lib-copy-paste branch 2 times, most recently from ad6d66a to d6695b0 Compare October 22, 2024 23:05
@kdmccormick
Copy link
Member

@ormsbee Just in case it's confusing you too, this warning:

pylintrc:1:0: W0012: Unknown option value for '--disable', expected a valid pylint message and got 'broad-exception-raised' (unknown-option-value)

showed up for me, and it disappeared as soon as I resolved the other pylint violations. You should be able to ignore it.

@kdmccormick
Copy link
Member

kdmccormick commented Oct 23, 2024

@ormsbee when I edit this pasted-in library component:
image

the editor renders the altext instead of the image:
image

could that be this PR, or a frontend-app-authoring issue?

@ormsbee
Copy link
Contributor Author

ormsbee commented Oct 23, 2024

@kdmccormick: it should be fixed once openedx/frontend-app-authoring#1403 merges

@kdmccormick
Copy link
Member

kdmccormick commented Oct 23, 2024

I was able to create a collision (on the AssetKey level, I believe) by uploading a file to a course named something like components_html_html1_little-library.png. It had the effect of clobbering away the component-level little-library.png, but then html1 seamlessly used the new course-level uploaded asset. In the course export, the component-level asset was gone, in favor of the new course-level asset.

Honestly, that's the best behavior I could have hoped for, and I doubt that anyone will name a file like that, so I'm not worried about fixing it for MVP, but I do think it's one of those things that we should go back and try to address later.

Copy link
Member

@kdmccormick kdmccormick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested and verified. Code looks good. Thank you, as always, for the helpful comments.

@ormsbee
Copy link
Contributor Author

ormsbee commented Oct 23, 2024

I was able to create a collision (on the AssetKey level, I believe) by uploading a file to a course named something like components_html_html1_little-library.png. It had the effect of clobbering away the component-level little-library.png, but then html1 seamlessly used the course-level uploaded asset. Honestly, that's the best behavior I could have hoped for, and I doubt that anyone will name a file like that, so I'm not worried about fixing it for MVP, but I do think it's one of those things that we should go back and try to address later.

Yeah, I mentioned this in a slack thread a couple of weeks ago, and I think the conclusion was that we could live with it for now.

@kdmccormick
Copy link
Member

I'm giving this one a rebase @ormsbee

The XBlockSerializerForLearningCore class existed to account for the
differences in how Blockstore and later Learning Core stored OLX. This
involved doing things like:

* removing the url_name attribute (because it's encoded elsewhere)
* special case conversion from <vertical> tags to <unit>
* other handling over child definition identifiers

Stripping the url_name was moved to the XBlockSerializer superclass
because it was useful for copy-paste functionality. The other two are no
longer relevant because Learning Core won't internally model a Unit as
an XBlock, and the Learning Core XBlock Runtime doesn't support having
children.
@kdmccormick kdmccormick merged commit d25e651 into openedx:master Oct 23, 2024
49 checks passed
@edx-pipeline-bot
Copy link
Contributor

2U Release Notice: This PR has been deployed to the edX staging environment in preparation for a release to production.

@edx-pipeline-bot
Copy link
Contributor

2U Release Notice: This PR has been deployed to the edX production environment.

1 similar comment
@edx-pipeline-bot
Copy link
Contributor

2U Release Notice: This PR has been deployed to the edX production environment.

@edx-pipeline-bot
Copy link
Contributor

2U Release Notice: This PR has been deployed to the edX staging environment in preparation for a release to production.

@edx-pipeline-bot
Copy link
Contributor

2U Release Notice: This PR has been deployed to the edX production environment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants