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

feat: [FC-0070] Create a new Studio view for rendering whole Unit in an iframe #35587

Merged
merged 1 commit into from
Oct 18, 2024

Conversation

UvgenGen
Copy link
Contributor

@UvgenGen UvgenGen commented Oct 3, 2024

… with xBlocks controls

Description

Created a new Studio view for rendering the whole Unit in an iframe with xBlocks controls for studio MFE.

A new studio page renders all xBlocks in the unit with xBlock controls
Studio page sends postmessages to the host in case of size change and executing control actions

Issue: openedx/platform-roadmap#321

Supporting information

URL example: http://studio.local.edly.io:8001/container_embed/block-v1:test+test+test+type@vertical+block@47ca674b3f0f46d7a8be12d8fcccd232

Result:
image

Added tests for container_embed_handler. Run pytest /cms/djangoapps/contentstore/views/tests/test_container_page.py for local testing inside LMS container.

Deadline

Other information

@openedx-webhooks
Copy link

Thanks for the pull request, @UvgenGen!

What's next?

Please work through the following steps to get your changes ready for engineering review:

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.

🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads

🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

🔘 Let us know that your PR is ready for review:

Who will review my changes?

This repository is currently maintained by @openedx/wg-maintenance-edx-platform. Tag them in a comment and let them know that your changes are ready for review.

Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Oct 3, 2024
Copy link
Member

@GlugovGrGlib GlugovGrGlib left a comment

Choose a reason for hiding this comment

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

First review round passed, please update description and check review comments

lastHeight = newHeight;
lastWidth = newWidth;

// Within the learning microfrontend the iframe resizes to match the
Copy link
Member

Choose a reason for hiding this comment

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

As this template is dedicated to be used in the authoring MFE only please rename all comments about learning MFE in it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

}());
</script>
<script>
var beamer_config = {
Copy link
Member

Choose a reason for hiding this comment

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

Where was this copied from?
I'm not sure about its usage, but I think this beamer script should be removed from this CMS view.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is copied from base.html

Copy link
Member

Choose a reason for hiding this comment

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

Let's remove it from this template

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed

cms/urls.py Outdated
@@ -123,6 +123,8 @@
name='course_rerun_handler'),
re_path(fr'^container/{settings.USAGE_KEY_PATTERN}$', contentstore_views.container_handler,
name='container_handler'),
re_path(fr'^container_iframe/{settings.USAGE_KEY_PATTERN}$', contentstore_views.container_handler_iframe,
Copy link
Member

Choose a reason for hiding this comment

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

container_embed might be a better name for a URL
Also, in case of change please update container_handler_iframe to container_embed_handler

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

@login_required
def container_handler_iframe(request, usage_key_string): # pylint: disable=too-many-statements
"""
The restful handler for container xblock requests.
Copy link
Member

Choose a reason for hiding this comment

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

Please update docstring to match specific purpose of this view

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

return HttpResponseBadRequest()

container_handler_context = get_container_handler_context(request, usage_key, course, xblock)
return render_to_response('container_iframe.html', container_handler_context)
Copy link
Member

Choose a reason for hiding this comment

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

Please rename template to container_chromeless.html to outline relation with LMS's courseware_chromeless

Copy link
Contributor Author

Choose a reason for hiding this comment

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

renamed

@@ -35,7 +35,8 @@

__all__ = [
'container_handler',
'component_handler'
'component_handler',
'container_handler_iframe'
Copy link
Member

Choose a reason for hiding this comment

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

Do you mind adding a comma after the last element in the list?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

comma added

@@ -207,7 +207,7 @@ function($, _, Backbone, gettext, BasePage,

renderAddXBlockComponents: function() {
var self = this;
if (self.options.canEdit) {
if (self.options.canEdit && !self.options.isIframePage) {
Copy link
Member

Choose a reason for hiding this comment

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

Can we update parameter isIframePage to isIframeEmbed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

renamed

const taxonomyTagsWidgetUrl = this.model.get('taxonomy_tags_widget_url');
const contentId = this.findXBlockElement(event.target).data('locator');

TaggingDrawerUtils.openDrawer(taxonomyTagsWidgetUrl, contentId);
},

showMoveXBlockModal: function(event) {
Copy link
Member

Choose a reason for hiding this comment

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

I can't exactly understand why some functions were completely removed from this file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some methods were duplicated, so I just removed the extra ones

Copy link
Member

Choose a reason for hiding this comment

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

Strange, can you assume what is the reason for it, have you noticed anything strange in behavior on the legacy Unit page in CMS after the removal of these functions?

Copy link
Contributor Author

@UvgenGen UvgenGen Oct 7, 2024

Choose a reason for hiding this comment

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

I assume these methods were duplicated by mistake during some rebasing/merging because they are equal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@UvgenGen UvgenGen force-pushed the sagirov/AXIMST-850 branch 3 times, most recently from 9a29b0e to 69b83f2 Compare October 8, 2024 14:50
@UvgenGen UvgenGen requested a review from GlugovGrGlib October 8, 2024 14:50
Comment on lines 528 to 552
if (this.options.isIframeEmbed) {
window.parent.postMessage(
{
type: 'duplicateXBlock',
payload: {}
}, document.referrer
);
return
}

Choose a reason for hiding this comment

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

[optional]: At this stage we do not expect fully working functionality, but it seems to me that it would be great to provide minimal working functionality. Since now after any action we get the error like Failed to execute 'postMessage' on 'Window': Invalid target origin '' in a call to 'postMessage'. I suggest catching such errors and not blocking the execution of the JS code flow. My proposal applies to all syntactic constructions in this file that are related to postMessage. What are your thoughts?

Suggested change
if (this.options.isIframeEmbed) {
window.parent.postMessage(
{
type: 'duplicateXBlock',
payload: {}
}, document.referrer
);
return
}
try {
if (this.options.isIframeEmbed) {
window.parent.postMessage(
{
type: 'duplicateXBlock',
payload: {}
}, document.referrer
);
return
}
} catch (e) {
console.error(e);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

Copy link

@PKulkoRaccoonGang PKulkoRaccoonGang left a comment

Choose a reason for hiding this comment

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

👍

@UvgenGen UvgenGen force-pushed the sagirov/AXIMST-850 branch from 69b83f2 to b9456e2 Compare October 8, 2024 15:03
Comment on lines 215 to 216
var lastHeight = window.parent.offsetHeight;
var lastWidth = window.parent.offsetWidth;

Choose a reason for hiding this comment

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

[nit]: During rendering of the iframe I noticed that we get errors related to the fact that the window.parent offsetHeight and offsetWidth values ​​are not defined. They are inside the object, for example:

Suggested change
var lastHeight = window.parent.offsetHeight;
var lastWidth = window.parent.offsetWidth;
var lastHeight = window.parent[0].offsetHeight;
var lastWidth = window.parent[0].offsetWidth;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

payload: {}
}, document.referrer
);
return
Copy link

@PKulkoRaccoonGang PKulkoRaccoonGang Oct 9, 2024

Choose a reason for hiding this comment

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

[nit/inform]: We also decided that in future changes we will switch the processing of logic of different methods using conditions related to types of iframe post messages (for example, for some xblocks we want to show new modal windows, we plan to send the type of post message, for example "newModalWindow").
Let's remove return in all such constructions 💯

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed

@UvgenGen UvgenGen force-pushed the sagirov/AXIMST-850 branch 2 times, most recently from ea1ad59 to c0f065d Compare October 9, 2024 15:15
@PKulkoRaccoonGang
Copy link

[request/inform]: @GlugovGrGlib @UvgenGen we decided that we want to remove the Paste Component button (and the tooltip with information about the component) from the iframe, since we already have a ready-made functionality for inserting an xblock on the page in which the iframe is embedded.

image

@mphilbrick211 mphilbrick211 added the FC Relates to an Axim Funded Contribution project label Oct 10, 2024
@GlugovGrGlib GlugovGrGlib requested a review from ormsbee October 15, 2024 15:15
@UvgenGen UvgenGen changed the title DRAFT: feat: [FC-0070] Create a new Studio view for rendering whole Unit in an iframe feat: [FC-0070] Create a new Studio view for rendering whole Unit in an iframe Oct 15, 2024
Comment on lines 594 to 598
%button-styles {
width: 44px;
height: 44px;
border-radius: 50%;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

[question] Do we need to declare %button-styles and other prepared style blocks in two files: _controls.scss and _course-unit-mfe-iframe.scss?

Choose a reason for hiding this comment

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

No, I deleted it from _controls.scss

Comment on lines 18 to 19
padding: 24px;
padding-bottom: 12px;
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] Should be simplified to one padding rule.
Also, we can stick to general rules for margin/paddings with using $baseline variable (ex:

padding: ($baseline/4) ($baseline/2) ($baseline/3) ($baseline/2);
)

Choose a reason for hiding this comment

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

Added

Comment on lines 42 to 43
padding: 24px;
padding-top: 12px;
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] Should be simplified to one padding rule.

Choose a reason for hiding this comment

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

corrected

.wrapper-xblock .header-actions .actions-list .action-item .action-button {
color: $black;

@extend %button-styles;
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] Let's declare all extended rules at the top of the block for separation and simple overriding.

Choose a reason for hiding this comment

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

Moved at the top


.nav-item {
border-bottom: none;
color: #212529;
Copy link
Contributor

Choose a reason for hiding this comment

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

[question] Can we use existing variables or create new variables for colors instead of hardcoding them?

Choose a reason for hiding this comment

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

Created some new variables

}
}

input:not([type="radio"]):not([type="checkbox"]):not([type="submit"]) {
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] Can be simplified

input:not([type="radio"], [type="checkbox"], [type="submit"]) {

Choose a reason for hiding this comment

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

Good catch! Done

Comment on lines 542 to 536
body #openassessment-editor #openassessment_editor_header,
body #openassessment-editor .openassessment_tab_instructions {
background-color: $white;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

[nit] Let's include in the block below:

body {
    #openassessment-editor {
        #openassessment_editor_header,
        .openassessment_tab_instructions {
              background-color: $white;
        }
    }
...
}

Choose a reason for hiding this comment

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

I did some additional refactoring

  • removed everything !important
  • removed access to elements via body

Comment on lines 619 to 621
// [class*="view-"] .modal-window .editor-with-buttons .xblock-actions {
// bottom: -6px;
// }
Copy link
Contributor

Choose a reason for hiding this comment

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

[question] Do we need this comments?

Choose a reason for hiding this comment

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

Removed

Comment on lines +1 to +3
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" focusable="false" aria-hidden="true">
<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25ZM21.41 6.34l-3.75-3.75-2.53 2.54 3.75 3.75 2.53-2.54Z" fill="currentColor"></path>
</svg>
Copy link
Contributor

Choose a reason for hiding this comment

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

I'll try to have a real review in the next day, but a quick question: where does this icon come from?

Choose a reason for hiding this comment

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

Thanks
This icon from Paragon icons

Copy link
Contributor

@ormsbee ormsbee left a comment

Choose a reason for hiding this comment

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

Some questions and requests. Nothing major. I don't feel like I can meaningfully review any of the styles though.

Comment on lines 149 to 150
Returns an HttpResponse with HTML content for the container xBlock.
The returned HTML is a chromeless rendering of the xBlock.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Returns an HttpResponse with HTML content for the container xBlock.
The returned HTML is a chromeless rendering of the xBlock.
Returns an HttpResponse with HTML content for the container XBlock.
The returned HTML is a chromeless rendering of the XBlock.

[nit, optional]: The standard spelling is "XBlock".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

json: not currently supported
"""

from ..utils import get_container_handler_context
Copy link
Contributor

Choose a reason for hiding this comment

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

[question]: Is this to avoid a circular dependency?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it is. I added a comment for it.

container_handler_context = get_container_handler_context(request, usage_key, course, xblock)
return render_to_response('container_chromeless.html', container_handler_context)
else:
return HttpResponseBadRequest("Only supports HTML requests")
Copy link
Contributor

Choose a reason for hiding this comment

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

[request] If you have an error condition that can return early, please do that instead of doing an if-else and indenting the main logic of the function. It makes the primary flow of the code easier to read.

So in this case, something like:

    if 'text/html' not in request.META.get('HTTP_ACCEPT', 'text/html'):
        return HttpResponseBadRequest("Only supports HTML requests")

    # rest of the function here.


from ..utils import get_container_handler_context

if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
Copy link
Contributor

Choose a reason for hiding this comment

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

[question] Are we worried about this getting hit with an http-accept header that only wants JSON? I don't usually see this kind of guard in a view that renders HTML.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I created this function based on container_handler, but I agree that this check can be removed. Removed.

try:
usage_key = UsageKey.from_string(usage_key_string)
except InvalidKeyError: # Raise Http404 on invalid 'usage_key_string'
raise Http404 # lint-amnesty, pylint: disable=raise-missing-from
Copy link
Contributor

Choose a reason for hiding this comment

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

[request] An invalid key should be a 400, not a 404, since it's a malformed request.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

try:
course, xblock, lms_link, preview_lms_link = _get_item_in_course(request, usage_key)
except ItemNotFoundError:
return HttpResponseBadRequest()
Copy link
Contributor

Choose a reason for hiding this comment

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

[request] If the UsageKey is well formed but not found, then this looks like the time to return the 404 error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

display: inline-block;
}

#openassessment-editor #oa_rubric_editor_wrapper {
Copy link
Contributor

Choose a reason for hiding this comment

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

[question] Why do we need to bundle the ORA CSS here, and the Drag & Drop CSS below? This is a question, not a request to change this behavior at this time.

Choose a reason for hiding this comment

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

Adding styles for ORA and Drag & Drop to this bundle is an improvement of visual perception for the new interface. We have two approaches for modal windows. The first is based on the react interface (full screen modal windows, for example for the Text component), the second is legacy modal windows. The current bundle provides styles exclusively for legacy modal windows inside the iframe that is embedded on the course unit page inside MFE Authoring. For the original legacy page, changing the styles of modal windows will not be available (their display will not change).
Also based on the fact that these xblocks are enabled by default and the code for these xblocks is outside the platform's code structure, additional styles were added.

@@ -0,0 +1,279 @@
## coding=utf-8
Copy link
Contributor

Choose a reason for hiding this comment

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

[request] Please add some sort of comment to this file to explain what it is, why it exists, and how it's intended to be different from the template file it's based off of.

Copy link
Contributor Author

@UvgenGen UvgenGen Oct 17, 2024

Choose a reason for hiding this comment

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

Added comment with a description

<%static:include path="common/templates/image-modal.underscore" />
</script>
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" />
% if not settings.STUDIO_FRONTEND_CONTAINER_URL:
Copy link
Contributor

Choose a reason for hiding this comment

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

[request] Please add a comment explaining why we're doing this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added comment

);
</%static:webpack>
</body>
<script type="text/javascript">
Copy link
Contributor

Choose a reason for hiding this comment

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

[request] There's a lot of boilerplate in this template, along with stuff we probably don't strictly need (like the hotjar piece). That's okay–this isn't intended to be long-lived, and it's a fast way to get up and running. But please use comments to highlight the parts of this template that are specific to the new UI you're building (and not just copied from the older template).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added comment for specific script block and removed the hotjar piece.

@UvgenGen UvgenGen requested a review from ormsbee October 17, 2024 11:24
@ormsbee
Copy link
Contributor

ormsbee commented Oct 17, 2024

@UvgenGen: Can you please squash + rebase? The static analysis bit should work after that. I just want to make sure it's not masking any other issues. Do you need me to merge these for you, or does someone on your team have merge rights for edx-platform?

@UvgenGen
Copy link
Contributor Author

@ormsbee PR updated and squashed. I would appreciate your help with merging.

Copy link
Member

@GlugovGrGlib GlugovGrGlib left a comment

Choose a reason for hiding this comment

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

One last comment to fix before merging it

@@ -141,6 +142,35 @@ def container_handler(request, usage_key_string): # pylint: disable=too-many-st
return HttpResponseBadRequest("Only supports HTML requests")


@require_GET
@login_required
Copy link
Member

@GlugovGrGlib GlugovGrGlib Oct 18, 2024

Choose a reason for hiding this comment

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

It seems like we are missing a decorator that is required to use this view in an Iframe. Please add xframe_options_exempt as it is done in LMS: https://github.com/openedx/edx-platform/blob/master/lms/djangoapps/courseware/views/views.py#L1546.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

xframe_options_exempt decorator added.

@ormsbee ormsbee merged commit e4a1e41 into openedx:master Oct 18, 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FC Relates to an Axim Funded Contribution project open-source-contribution PR author is not from Axim or 2U
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

8 participants