diff --git a/openedx/features/offline_mode/assets_management.py b/openedx/features/offline_mode/assets_management.py index 8a464318893..e942bcf6e3d 100644 --- a/openedx/features/offline_mode/assets_management.py +++ b/openedx/features/offline_mode/assets_management.py @@ -56,30 +56,31 @@ def save_asset_file(xblock, path, filename): asset_key = StaticContent.get_asset_key_from_path(xblock.location.course_key, path) content = AssetManager.find(asset_key).data except (ItemNotFoundError, NotFoundError): - pass + log.info(f"Asset not found: {filename}") + else: - base_path = base_storage_path(xblock) + base_path = block_storage_path(xblock) file_path = os.path.join(base_path, 'assets', filename) default_storage.save(file_path, ContentFile(content)) def remove_old_files(xblock): """ - Removes the 'asset' directory, 'index.html', and 'offline_content.zip' files + Removes the 'assets' directory, 'index.html', and 'offline_content.zip' files in the specified base path directory. Args: (XBlock): The XBlock instance """ try: - base_path = base_storage_path(xblock) - asset_path = os.path.join(base_path, 'asset') + base_path = block_storage_path(xblock) + assets_path = os.path.join(base_path, 'assets') index_file_path = os.path.join(base_path, 'index.html') offline_zip_path = os.path.join(base_path, OFFLINE_CONTENT_ARCHIVE_NAME) - # Delete the 'asset' directory if it exists - if os.path.isdir(asset_path): - shutil.rmtree(asset_path) - log.info(f"Successfully deleted the directory: {asset_path}") + # Delete the 'assets' directory if it exists + if os.path.isdir(assets_path): + shutil.rmtree(assets_path) + log.info(f"Successfully deleted the directory: {assets_path}") # Delete the 'index.html' file if it exists if default_storage.exists(index_file_path): @@ -104,12 +105,12 @@ def is_offline_content_present(xblock): Returns: bool: True if the file is present, False otherwise """ - base_path = base_storage_path(xblock) + base_path = block_storage_path(xblock) offline_zip_path = os.path.join(base_path, OFFLINE_CONTENT_ARCHIVE_NAME) return default_storage.exists(offline_zip_path) -def base_storage_path(xblock): +def block_storage_path(xblock=None, usage_key=None): """ Generates the base storage path for the given XBlock. @@ -117,11 +118,12 @@ def base_storage_path(xblock): course, block type, and block ID. Args: xblock (XBlock): The XBlock instance for which to generate the storage path. + usage_key (UsageKey): The UsageKey of the XBlock. Returns: str: The constructed base storage path. """ - loc = xblock.location - return f'{loc.org}/{loc.course}/{loc.block_type}/{loc.block_id}/' + loc = usage_key or getattr(xblock, 'location', None) + return f'{loc.org}/{loc.course}/{loc.block_type}/{loc.block_id}/' if loc else '' def is_modified(xblock): @@ -131,7 +133,7 @@ def is_modified(xblock): Args: xblock (XBlock): The XBlock instance to check. """ - file_path = os.path.join(base_storage_path(xblock), 'content_html.zip') + file_path = os.path.join(block_storage_path(xblock), 'content_html.zip') try: last_modified = default_storage.get_created_time(file_path) @@ -141,14 +143,15 @@ def is_modified(xblock): return xblock.published_on > last_modified -def save_mathjax_to_local_static(): +def save_mathjax_to_xblock_assets(xblock): """ Saves MathJax to the local static directory. If MathJax is not already saved, it fetches MathJax from the CDN and saves it to the local static directory. """ - if not default_storage.exists(MATHJAX_STATIC_PATH): + file_path = os.path.join(block_storage_path(xblock), MATHJAX_STATIC_PATH) + if not default_storage.exists(file_path): response = requests.get(MATHJAX_CDN_URL) - default_storage.save(MATHJAX_STATIC_PATH, ContentFile(response.content)) - log.info(f"Successfully saved MathJax to {MATHJAX_STATIC_PATH}") + default_storage.save(file_path, ContentFile(response.content)) + log.info(f"Successfully saved MathJax to {file_path}") diff --git a/openedx/features/offline_mode/constants.py b/openedx/features/offline_mode/constants.py index ce6f8782d8b..c5e9bd2dfcb 100644 --- a/openedx/features/offline_mode/constants.py +++ b/openedx/features/offline_mode/constants.py @@ -7,7 +7,7 @@ MATHJAX_VERSION = '2.7.5' MATHJAX_CDN_URL = f'https://cdn.jsdelivr.net/npm/mathjax@{MATHJAX_VERSION}/MathJax.js' -MATHJAX_STATIC_PATH = os.path.join('offline_mode_shared_static', 'js', f'MathJax-{MATHJAX_VERSION}.js') +MATHJAX_STATIC_PATH = os.path.join('assets', 'js', f'MathJax-{MATHJAX_VERSION}.js') OFFLINE_CONTENT_ARCHIVE_NAME = 'offline_content.zip' diff --git a/openedx/features/offline_mode/html_manipulator.py b/openedx/features/offline_mode/html_manipulator.py index 2e785d01a6c..f8fce464ae2 100644 --- a/openedx/features/offline_mode/html_manipulator.py +++ b/openedx/features/offline_mode/html_manipulator.py @@ -8,14 +8,10 @@ from django.conf import settings -from .assets_management import save_asset_file, save_mathjax_to_local_static +from .assets_management import save_asset_file, save_mathjax_to_xblock_assets from .constants import MATHJAX_CDN_URL, MATHJAX_STATIC_PATH -# Relative path difference between the HTML content and the shared static files. -RELATIVE_PATH_DIFF = '../../../../' - - class HtmlManipulator: """ Class to prepare HTML content for offline use. @@ -31,11 +27,9 @@ def _replace_mathjax_link(self): """ Replace MathJax CDN link with local path to MathJax.js file. """ + save_mathjax_to_xblock_assets(self.xblock) mathjax_pattern = re.compile(fr'src="{MATHJAX_CDN_URL}[^"]*"') - self.html_data = mathjax_pattern.sub( - f'src="{RELATIVE_PATH_DIFF}{MATHJAX_STATIC_PATH}"', - self.html_data - ) + self.html_data = mathjax_pattern.sub(f'src="{MATHJAX_STATIC_PATH}"', self.html_data) def _replace_static_links(self): """ @@ -71,8 +65,6 @@ def _replace_iframe(soup): def _add_js_bridge(soup): """ Add JS bridge script to the HTML content. - :param soup: - :return: """ script_tag = soup.new_tag('script') with open('openedx/features/offline_mode/static/offline_mode/js/bridge.js', 'r') as file: @@ -89,7 +81,6 @@ def process_html(self): Changes links to static files to paths to pre-generated static files for offline use. """ - save_mathjax_to_local_static() self._replace_static_links() self._replace_mathjax_link() diff --git a/openedx/features/offline_mode/utils.py b/openedx/features/offline_mode/utils.py index 418c49465ca..e54c7081146 100644 --- a/openedx/features/offline_mode/utils.py +++ b/openedx/features/offline_mode/utils.py @@ -10,8 +10,7 @@ from zipfile import ZipFile - -from .assets_management import base_storage_path, remove_old_files, is_modified +from .assets_management import block_storage_path, remove_old_files, is_modified from .constants import OFFLINE_CONTENT_ARCHIVE_NAME, OFFLINE_SUPPORTED_XBLOCKS from .html_manipulator import HtmlManipulator @@ -31,13 +30,13 @@ def add_files_to_zip(zip_file, current_base_path, current_path_in_zip): Recursively adds files to the zip file. """ try: - directories, files = default_storage.listdir(current_base_path) + directories, filenames = default_storage.listdir(current_base_path) except OSError: return - for file_name in files: - full_path = os.path.join(current_base_path, file_name) - zip_file.write(full_path, os.path.join(current_path_in_zip, file_name)) + for filename in filenames: + full_path = os.path.join(current_base_path, filename) + zip_file.write(full_path, os.path.join(current_path_in_zip, filename)) for directory in directories: add_files_to_zip(zip_file, os.path.join(current_base_path, directory), @@ -58,7 +57,7 @@ def generate_offline_content(xblock, html_data): if not is_modified(xblock): return - base_path = base_storage_path(xblock) + base_path = block_storage_path(xblock) remove_old_files(xblock) html_manipulator = HtmlManipulator(xblock, html_data) updated_html = html_manipulator.process_html()