Repository: apluslms/a-plus

Issue:
Add feature for mapping static file URLs to another domain than just the exercise service URL
The O1 course wants to build the RST course without the `static_host` setting in conf.py because they commit the built html to the course git repo. O1 doesn't want to use `static_host` since in their case, its value should be different in local testing versus the production server. In order to make this work without `static_host`, new URL manipulation in the  A+ front is required (`a-plus/lib/remote_page.py fix_relative_urls()`):

A+ could set relative static URLs to some other domain/netloc than the original backend service (e.g., images in grader.cs assignment pointing to gitmanager.cs). It would probably be easy since it would be just a mapping from a domain to another. A+ would need to check the mappings for every relative URL in all loaded chapters and assignments, though.

O1 concerns in aplusguru: https://rt.cs.aalto.fi/Ticket/Display.html?id=20318

## Background

With the new Git Manager in v1.12, the course static files are hosted in gitmanager.cs instead of grader.cs (MOOC-Grader). Images and static file URLs were broken inside assignment descriptions, i.e., the embedded exercise frame in an A+ chapter page, if RST courses were compiled without the `static_host` setting in conf.py

Aplusguru tickets:

* O1: https://rt.cs.aalto.fi/Ticket/Display.html?id=20412
* https://rt.cs.aalto.fi/Ticket/Display.html?id=20318
* AI 2022: https://rt.cs.aalto.fi/Ticket/Display.html?id=20496

The image URLs pointed to grader.cs when they should point to gitmanager.cs. The domain grader.cs was taken from the exercise service URL (in `a-plus/lib/remote_page.py fix_relative_urls()`).

a-plus-rst-tools compiles links relatively because that's how Sphinx works. The setting `static_host = os.environ.get('STATIC_CONTENT_HOST)` can be used to make static URLs absolute with the netloc/domain already in the course build.

The new Git Manager automatically sets the build environment variable `STATIC_CONTENT_HOST="https://gitmanager.cs.aalto.fi/static/COURSEKEY"`

EDIT Jira: https://jira.aalto.fi/browse/EDIT-795
Add feature for mapping static file URLs to another domain than just the exercise service URL
The O1 course wants to build the RST course without the `static_host` setting in conf.py because they commit the built html to the course git repo. O1 doesn't want to use `static_host` since in their case, its value should be different in local testing versus the production server. In order to make this work without `static_host`, new URL manipulation in the  A+ front is required (`a-plus/lib/remote_page.py fix_relative_urls()`):

A+ could set relative static URLs to some other domain/netloc than the original backend service (e.g., images in grader.cs assignment pointing to gitmanager.cs). It would probably be easy since it would be just a mapping from a domain to another. A+ would need to check the mappings for every relative URL in all loaded chapters and assignments, though.

O1 concerns in aplusguru: https://rt.cs.aalto.fi/Ticket/Display.html?id=20318

## Background

With the new Git Manager in v1.12, the course static files are hosted in gitmanager.cs instead of grader.cs (MOOC-Grader). Images and static file URLs were broken inside assignment descriptions, i.e., the embedded exercise frame in an A+ chapter page, if RST courses were compiled without the `static_host` setting in conf.py

Aplusguru tickets:

* O1: https://rt.cs.aalto.fi/Ticket/Display.html?id=20412
* https://rt.cs.aalto.fi/Ticket/Display.html?id=20318
* AI 2022: https://rt.cs.aalto.fi/Ticket/Display.html?id=20496

The image URLs pointed to grader.cs when they should point to gitmanager.cs. The domain grader.cs was taken from the exercise service URL (in `a-plus/lib/remote_page.py fix_relative_urls()`).

a-plus-rst-tools compiles links relatively because that's how Sphinx works. The setting `static_host = os.environ.get('STATIC_CONTENT_HOST)` can be used to make static URLs absolute with the netloc/domain already in the course build.

The new Git Manager automatically sets the build environment variable `STATIC_CONTENT_HOST="https://gitmanager.cs.aalto.fi/static/COURSEKEY"`

EDIT Jira: https://jira.aalto.fi/browse/EDIT-795


Discussion:
There are useful comments in the source code:
https://github.com/apluslms/a-plus/blob/2e84c737c03a28f8c09f4b51f1661e2b2456b5db/lib/remote_page.py#L298-L323

In the RST build, a-plus-rst-tools add the `data-aplus-path` attribute here:
https://github.com/apluslms/a-plus-rst-tools/blob/5354ba6c0f5eedb6f000ef6f9f0109492d463fdb/lib/html_tools.py#L154-L167


There are useful comments in the source code:
https://github.com/apluslms/a-plus/blob/2e84c737c03a28f8c09f4b51f1661e2b2456b5db/lib/remote_page.py#L298-L323

In the RST build, a-plus-rst-tools add the `data-aplus-path` attribute here:
https://github.com/apluslms/a-plus-rst-tools/blob/5354ba6c0f5eedb6f000ef6f9f0109492d463fdb/lib/html_tools.py#L154-L167



Resolving patch:
diff --git a/lib/remote_page.py b/lib/remote_page.py
--- a/lib/remote_page.py
+++ b/lib/remote_page.py
@@ -237,6 +237,16 @@ def _fix_relative_urls(self, url, tag_name, attr_name):
         # URLs /plain, /info, /info/model, /info/template.
         exercise_info = re.compile(r'/((plain)|(info(/model|/template)?))/?(#.+)?$')
 
+        # If Gitmanager is in use, fix relative static URLs to that host
+        if settings.GITMANAGER_URL:
+            gmurl = urlparse(settings.GITMANAGER_URL)
+            if settings.REMOTE_PAGE_HOSTS_MAP:
+                domain = settings.REMOTE_PAGE_HOSTS_MAP.get(gmurl.netloc, gmurl.netloc)
+                gmurl = gmurl._replace(netloc=domain)
+            staticurl = url._replace(scheme=gmurl.scheme, netloc=gmurl.netloc)
+        else:
+            staticurl = url
+
         for element in self.soup.find_all(tag_name, {attr_name:True}):
             value = element[attr_name]
             if not value:
@@ -312,15 +322,16 @@ def _fix_relative_urls(self, url, tag_name, attr_name):
 
                 # url points to the exercise service, e.g., MOOC-Grader.
                 # This fixes links to static files (such as images) in RST chapters.
-                # The image URL must be absolute and refer to the grader server
-                # instead of the A+ server. A relative URL with only path
-                # "/static/course/image.png" would target the A+ server when
-                # it is included in the A+ page. The value should be a relative
+                # The image URL must be absolute and refer to the server with static content
+                # instead of the A+ server. Traditionally this has been the grader server, but
+                # recently gitmanager is used. If GITMANAGER_URL is specified, we assume gitmanager.
+                # A relative URL with only path "/static/course/image.png" would target the A+ server
+                # when it is included in the A+ page. The value should be a relative
                 # path in the course build directory so that it becomes the full
                 # correct URL to the target file.
                 # E.g., urljoin('http://localhost:8080/static/default/module1/chapter.html', "../_images/image.png")
                 # -> 'http://localhost:8080/static/default/_images/image.png'
-                element[attr_name] = urljoin(url.geturl(), value)
+                element[attr_name] = urljoin(staticurl.geturl(), value)
 
     def find_and_replace(self, attr_name, list_of_attributes):
         l = len(list_of_attributes)


Test patch: