From 3e8073ae784ab60a4c554e4d475c56baedc2362f Mon Sep 17 00:00:00 2001 From: Violet Shreve Date: Thu, 11 Jul 2024 14:52:25 -0400 Subject: [PATCH 1/3] Add linkTo util function Formgrader could be loaded in one of two different contexts: on its own from a notebook, or inside an iframe a lab. When navigating to a page outside the formgrader application, this presents an issue in that these two contexts need different navigation mechanisms. Previously, the mechanism was decided by looking at the name of the jupyter-server application. If it was `jupyter-notebook` (notebook<7), use href. Otherwise, use postMessage. However, it's possible to use the notebook interface without `jupyter-notebook`, e.g. `jupyter-server`. Now the mechanism is decided by context clues inside the browser, i.e. whether formgrader is inside an iframe. This commit adds a `linkTo` function which is applied to jQuery elements via `.map` and applies either href or postMessage nav mechanisms based on whether the current window is the top window. Additionally, it completely removes the `is_lab` and `is_jlab` checks as they were relatively naive and add complexity that doesn't need to be there. --- nbgrader/server_extensions/formgrader/base.py | 6 ++- .../formgrader/formgrader.py | 3 -- .../server_extensions/formgrader/handlers.py | 4 +- .../formgrader/static/js/jupyterlab_utils.js | 10 ----- .../static/js/manage_assignments.js | 42 +++++-------------- .../formgrader/static/js/utils.js | 41 ++++++++++++++++++ .../formgrader/templates/base.tpl | 1 - .../formgrade/formgrade_macros.html.j2 | 24 ++++------- .../templates/manage_assignments.tpl | 1 - 9 files changed, 66 insertions(+), 66 deletions(-) delete mode 100644 nbgrader/server_extensions/formgrader/static/js/jupyterlab_utils.js diff --git a/nbgrader/server_extensions/formgrader/base.py b/nbgrader/server_extensions/formgrader/base.py index d5bf2ec4d..1593b0ef2 100644 --- a/nbgrader/server_extensions/formgrader/base.py +++ b/nbgrader/server_extensions/formgrader/base.py @@ -4,6 +4,7 @@ from tornado import web from jupyter_server.base.handlers import JupyterHandler +from jupyter_server.utils import url_path_join, url_is_absolute from ...api import Gradebook from ...apps.api import NbGraderAPI @@ -41,7 +42,10 @@ def gradebook(self): @property def mathjax_url(self): - return self.settings['mathjax_url'] + url = self.settings.get("mathjax_url", "") + if not url or url_is_absolute(url): + return url + return url_path_join(self.base_url or "/", url) @property def exporter(self): diff --git a/nbgrader/server_extensions/formgrader/formgrader.py b/nbgrader/server_extensions/formgrader/formgrader.py index d5806dfb6..f49a777c6 100644 --- a/nbgrader/server_extensions/formgrader/formgrader.py +++ b/nbgrader/server_extensions/formgrader/formgrader.py @@ -107,9 +107,6 @@ def load_jupyter_server_extension(nbapp): nbapp.log.info("Loading the formgrader nbgrader serverextension") webapp = nbapp.web_app - # Save which kind of application is running : Jupyterlab like or classic Notebook - webapp.settings['is_jlab'] = not (nbapp.name == 'jupyter-notebook') - formgrader = FormgradeExtension(parent=nbapp) # compatibility between notebook.notebookapp.NotebookApp and jupyter_server.serverapp.ServerApp diff --git a/nbgrader/server_extensions/formgrader/handlers.py b/nbgrader/server_extensions/formgrader/handlers.py index 0fa8a9404..7fff1b3d5 100644 --- a/nbgrader/server_extensions/formgrader/handlers.py +++ b/nbgrader/server_extensions/formgrader/handlers.py @@ -17,7 +17,6 @@ def get(self): "manage_assignments.tpl", url_prefix=self.url_prefix, base_url=self.base_url, - is_lab=int(self.settings['is_jlab']), windows=(sys.prefix == 'win32'), course_id=self.api.course_id, exchange=self.api.exchange_root, @@ -108,10 +107,9 @@ def get(self, submission_id): 'index': ix, 'total': len(indices), 'base_url': self.base_url, - 'my_mathjax_url': self.mathjax_url if self.settings['is_jlab'] else self.base_url + '/' + self.mathjax_url, + 'my_mathjax_url': self.mathjax_url, 'student': student_id, 'last_name': submission.student.last_name, - 'last_name': submission.student.last_name, 'first_name': submission.student.first_name, 'notebook_path': self.url_prefix + '/' + relative_path } diff --git a/nbgrader/server_extensions/formgrader/static/js/jupyterlab_utils.js b/nbgrader/server_extensions/formgrader/static/js/jupyterlab_utils.js deleted file mode 100644 index 7e9bb9783..000000000 --- a/nbgrader/server_extensions/formgrader/static/js/jupyterlab_utils.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Module of data to send to Jupyterlab's nbgrader extension to perform actions. - */ - -var jlab_go_to_path = function(directory_path){ - return JSON.stringify({ - "command": "filebrowser:go-to-path", - "arguments": {"path": directory_path} - }); -} \ No newline at end of file diff --git a/nbgrader/server_extensions/formgrader/static/js/manage_assignments.js b/nbgrader/server_extensions/formgrader/static/js/manage_assignments.js index 531c64eb8..41e8ed0ed 100644 --- a/nbgrader/server_extensions/formgrader/static/js/manage_assignments.js +++ b/nbgrader/server_extensions/formgrader/static/js/manage_assignments.js @@ -34,7 +34,6 @@ var AssignmentUI = Backbone.View.extend({ this.listenTo(this.model, "request", this.animateSaving); this.listenTo(this.model, "sync", this.closeModal); - this.is_lab = options.is_lab || false; this.render(); }, @@ -97,22 +96,14 @@ var AssignmentUI = Backbone.View.extend({ render: function () { this.clear(); - var this_assignment = this; + // assignment name var name = this.model.get("name") this.$name.attr("data-order", name); - /* Append link with : - * - href if this is a Notebook<7 environment - * - click listener to send message to iframe parent if this is Jupyter Lab environment - */ this.$name.append($("") .text(name) - .attr("target", self.is_lab ? undefined : "_blank") - .attr("href", self.is_lab ? undefined : base_url + "/tree/" + url_prefix + "/" + this.model.get("source_path")) - .click(self.is_lab ? function(){ - window.parent.postMessage(jlab_go_to_path(url_prefix + "/" + this_assignment.model.get("source_path")), '*'); - } : undefined) + .map(linkTo("directory", url_prefix + "/" + this.model.get("source_path"))) ); // duedate @@ -154,16 +145,8 @@ var AssignmentUI = Backbone.View.extend({ // preview student version var release_path = this.model.get("release_path"); if (release_path) { - /* Append link with : - * - href if this is a Notebook<7 environment - * - click listener to send message to iframe parent if this is Jupyter Lab environment - */ this.$preview.append($("") - .attr("target", self.is_lab ? undefined : "_blank") - .attr("href", self.is_lab ? undefined : base_url + "/tree/" + url_prefix + "/" + release_path) - .click(self.is_lab ? function(){ - window.parent.postMessage(jlab_go_to_path(url_prefix + "/" + release_path), '*'); - } : undefined) + .map(linkTo("directory", url_prefix + "/" + release_path)) .append($("") .addClass("glyphicon glyphicon-search") .attr("aria-hidden", "true"))); @@ -215,20 +198,20 @@ var AssignmentUI = Backbone.View.extend({ // generate feedback if (num_submissions > 0) { this.$generate_feedback.append($("") - .attr("href", "#") + .attr("href", "#") .click(_.bind(this.generate_feedback, this)) - .append($("") - .addClass("glyphicon glyphicon-comment") + .append($("") + .addClass("glyphicon glyphicon-comment") .attr("aria-hidden", "true"))); } // feedback if (num_submissions > 0) { this.$release_feedback.append($("") - .attr("href", "#") + .attr("href", "#") .click(_.bind(this.release_feedback, this)) - .append($("") - .addClass("glyphicon glyphicon-envelope") + .append($("") + .addClass("glyphicon glyphicon-envelope") .attr("aria-hidden", "true"))); } @@ -579,7 +562,7 @@ var createAssignmentModal = function () { modal = createModal("add-assignment-modal", "Add New Assignment", body, footer); }; -var loadAssignments = function (is_lab=false) { +var loadAssignments = function () { var tbl = $("#main-table"); models = new Assignments(); @@ -592,7 +575,6 @@ var loadAssignments = function (is_lab=false) { var view = new AssignmentUI({ "model": model, "el": insertRow(tbl), - "is_lab": is_lab }); views.push(view); }); @@ -605,8 +587,6 @@ var loadAssignments = function (is_lab=false) { var models = undefined; var views = []; -var is_lab = is_lab || false; - $(window).on('load', function () { - loadAssignments(is_lab); + loadAssignments(); }); diff --git a/nbgrader/server_extensions/formgrader/static/js/utils.js b/nbgrader/server_extensions/formgrader/static/js/utils.js index e5adf5fa8..05c50aec1 100644 --- a/nbgrader/server_extensions/formgrader/static/js/utils.js +++ b/nbgrader/server_extensions/formgrader/static/js/utils.js @@ -87,3 +87,44 @@ var insertDataTable = function (tbl) { }] }); }; + +var linkTo = function (type, path) { + /* + * Connect a link in the appropriate manner for the context. + * - If we're in the outermost frame, assume notebook and use an href. + * - If we're in an iframe, assume lab and send a message. + */ + if (window === window.top) { + var prefix = { + notebook: "/notebooks/", + file: "/edit/", + directory: "/tree/" + }[type]; + + return (_, el) => { + return $(el) + .attr("href", prefix + path) + .attr("target", "_blank")[0]; + }; + } else { + var command = { + notebook: "docmanager:open", + file: "docmanager:open", + directory: "filebrowser:go-to-path", + }[type]; + + return (_, el) => { + return $(el) + .attr("href", "#") + .click(() => { + window.parent.postMessage( + JSON.stringify({ + "command": command, + "arguments": {"path": path} + }), + "*" + ); + })[0]; + } + } +} diff --git a/nbgrader/server_extensions/formgrader/templates/base.tpl b/nbgrader/server_extensions/formgrader/templates/base.tpl index 56eb423fb..b2c6168f9 100644 --- a/nbgrader/server_extensions/formgrader/templates/base.tpl +++ b/nbgrader/server_extensions/formgrader/templates/base.tpl @@ -10,7 +10,6 @@ - diff --git a/nbgrader/server_extensions/formgrader/templates/formgrade/formgrade_macros.html.j2 b/nbgrader/server_extensions/formgrader/templates/formgrade/formgrade_macros.html.j2 index e014b1f2e..b5a3133f9 100644 --- a/nbgrader/server_extensions/formgrader/templates/formgrade/formgrade_macros.html.j2 +++ b/nbgrader/server_extensions/formgrader/templates/formgrade/formgrade_macros.html.j2 @@ -20,6 +20,7 @@ var base_url = "{{ resources.base_url }}/formgrader"; + @@ -64,20 +64,12 @@ function jlab_open_notebook(notebook_path) {
  • {{ resources.notebook_id }}
  • Submission #{{ resources.index + 1 }} {%- if resources.last_name and resources.first_name -%} {{ resources.last_name }}, {{ resources.first_name }} @@ -107,4 +99,4 @@ function jlab_open_notebook(notebook_path) { $('span.glyphicon.name-hidden').tooltip({title: "Show student name", placement: "bottom", trigger: "hover"}); $('span.glyphicon.name-shown').tooltip({title: "Hide student name", placement: "bottom", trigger: "hover"}); -{% endmacro %} \ No newline at end of file +{% endmacro %} diff --git a/nbgrader/server_extensions/formgrader/templates/manage_assignments.tpl b/nbgrader/server_extensions/formgrader/templates/manage_assignments.tpl index 1fb2ab3cf..51b6969f8 100644 --- a/nbgrader/server_extensions/formgrader/templates/manage_assignments.tpl +++ b/nbgrader/server_extensions/formgrader/templates/manage_assignments.tpl @@ -3,7 +3,6 @@ {%- block head -%} From 2c61b762a648e41c80a7f413ee38bcacdb720f2b Mon Sep 17 00:00:00 2001 From: Violet Shreve Date: Fri, 12 Jul 2024 00:20:13 -0400 Subject: [PATCH 2/3] Bump canvas to get access to prebuilt binaries --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 95809c055..9880f33f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2133,9 +2133,9 @@ caniuse-lite@^1.0.30001349: integrity sha512-Sd6pjJHF27LzCB7pT7qs+kuX2ndurzCzkpJl6Qct7LPSZ9jn0bkOA8mdgMgmqnQAWLVOOGjLpc+66V57eLtb1g== canvas@^2.6.1: - version "2.11.0" - resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.11.0.tgz#7f0c3e9ae94cf469269b5d3a7963a7f3a9936434" - integrity sha512-bdTjFexjKJEwtIo0oRx8eD4G2yWoUOXP9lj279jmQ2zMnTQhT8C3512OKz3s+ZOaQlLbE7TuVvRDYDB3Llyy5g== + version "2.11.2" + resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.11.2.tgz#553d87b1e0228c7ac0fc72887c3adbac4abbd860" + integrity sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw== dependencies: "@mapbox/node-pre-gyp" "^1.0.0" nan "^2.17.0" From df46fefcda98e3dd56374cb466b8922f9971f372 Mon Sep 17 00:00:00 2001 From: Violet Shreve Date: Fri, 12 Jul 2024 00:48:15 -0400 Subject: [PATCH 3/3] Add linkcheck_ignore for GitHub contributor searches --- nbgrader/docs/source/conf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nbgrader/docs/source/conf.py b/nbgrader/docs/source/conf.py index 30e61a1f6..b5356823a 100644 --- a/nbgrader/docs/source/conf.py +++ b/nbgrader/docs/source/conf.py @@ -287,6 +287,10 @@ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False +linkcheck_ignore = [ + # Assume GitHub contributor search links for this repo are always valid + r"https://github.com/search\?q=repo%3Ajupyter%2Fnbgrader\+involves%3A.*type=Issues", +] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None}