From 1f551fc97419d8bc5420b947c3d80eeda781e249 Mon Sep 17 00:00:00 2001 From: James Hanna Date: Mon, 4 Feb 2019 13:36:02 +0000 Subject: [PATCH 01/18] In notebook licensing --- README.md | 2 +- build/doit.sh | 32 +++ conda-recipe/bld.bat | 12 ++ conda-recipe/build.sh | 7 +- conda-recipe/run_test.sh | 2 +- docker/Dockerfile | 51 +++-- docker/init | 10 + jupyterq_licensemgr/LICENSE | 201 ++++++++++++++++++ jupyterq_licensemgr/MANIFEST.in | 1 + jupyterq_licensemgr/README.md | 1 + .../jupyterq_licensemgr/__init__.py | 89 ++++++++ .../jupyterq_licensemgr/check_q.q | 2 + .../jupyterq_licensemgr/index.js | 122 +++++++++++ .../jupyterq_licensemgr.json | 1 + .../jupyterq_licensemgr_config.json | 1 + jupyterq_licensemgr/setup.py | 33 +++ jupyterq_server.q | 8 +- 17 files changed, 551 insertions(+), 24 deletions(-) create mode 100755 build/doit.sh create mode 100755 docker/init create mode 100755 jupyterq_licensemgr/LICENSE create mode 100644 jupyterq_licensemgr/MANIFEST.in create mode 100644 jupyterq_licensemgr/README.md create mode 100644 jupyterq_licensemgr/jupyterq_licensemgr/__init__.py create mode 100644 jupyterq_licensemgr/jupyterq_licensemgr/check_q.q create mode 100644 jupyterq_licensemgr/jupyterq_licensemgr/index.js create mode 100644 jupyterq_licensemgr/jupyterq_licensemgr/jupyterq_licensemgr.json create mode 100644 jupyterq_licensemgr/jupyterq_licensemgr/jupyterq_licensemgr_config.json create mode 100644 jupyterq_licensemgr/setup.py diff --git a/README.md b/README.md index 07e2e0e..410e722 100755 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ For subsequent runs, you will not be prompted to redo the license setup when cal docker start -ai myjupyterq -**N.B.** [instructions regarding headless/presets are available](https://github.com/KxSystems/embedPy/docker/README.md#headlesspresets) +**N.B.** [instructions regarding headless/presets are available](https://github.com/KxSystems/embedPy/blob/master/docker/README.md#headlesspresets) **N.B.** [build instructions for the image are available](docker/README.md) diff --git a/build/doit.sh b/build/doit.sh new file mode 100755 index 0000000..81fd3e0 --- /dev/null +++ b/build/doit.sh @@ -0,0 +1,32 @@ +usage=\ +"Usage:\n\tdoit.sh [repodir] + +\tbuilds conda package, set NOTEST in env to skip tests during conda build (faster) +" + +if [ $# -lt 1 ] +then + printf "$usage" >&2 + exit 1 +elif [ ! -d $1 ] +then + printf "$1 doesn't exist\n\n$usage" >&2 + exit 1 +fi + +: "${JUPYTERQ_VERSION:=local_dev}" +[ -z "$NOTEST" ] && export QLIC="${QLIC:=$QHOME}" + +JUPYTERQ_REQS=$(paste -sd "|" $1/requirements.txt) + +export JUPYTERQ_REQS +export JUPYTERQ_VERSION +export QLIC + +set -x +if [ ! -z "$QLIC" ] +then + conda build -c kx --no-long-test-prefix $1/conda-recipe +else + conda build -c kx --no-test conda-recipe +fi diff --git a/conda-recipe/bld.bat b/conda-recipe/bld.bat index 677d9b0..d33115a 100644 --- a/conda-recipe/bld.bat +++ b/conda-recipe/bld.bat @@ -7,9 +7,21 @@ copy /Y jupyterq*.q? %QHOME%\ || goto :error xcopy kxpy %QHOME%\kxpy /I/S/E/H|| goto :error copy /Y /B w64\jupyterq.dll %QHOME%\w64\ || goto :error set JK=%PREFIX%\share\jupyter\kernels\qpk +set JL=jupyterq_licensemgr\jupyterq_licensemgr mkdir %JK% +mkdir %PREFIX%\etc\jupyter\jupyter_notebook_config.d +mkdir %PREFIX%\etc\jupyter\nbconfig\notebook.d +mkdir %PREFIX%\share\jupyter\nbextensions\jupyterq_licensemgr copy kernelspec\* %JK% +cd jupyterq_licensemgr +python setup.py install +cd .. + +copy %JL%\index.js %PREFIX%\share\jupyter\nbextensions\jupyterq_licensemgr +copy %JL%\jupyterq_licensemgr.json %PREFIX%\etc\jupyter\nbconfig\notebook.d +copy %JL%\jupyterq_licensemgr_config.json %PREFIX%\etc\jupyter\jupyter_notebook_config.d + exit /b 0 :error exit /b %errorlevel% diff --git a/conda-recipe/build.sh b/conda-recipe/build.sh index 6e72ec2..7534d2e 100644 --- a/conda-recipe/build.sh +++ b/conda-recipe/build.sh @@ -9,8 +9,13 @@ fi make -f build/makefile jupyterq mkdir -p $QHOME/$QLIBDIR JK=$PREFIX/share/jupyter/kernels/qpk -mkdir -p $JK +JL=jupyterq_licensemgr/jupyterq_licensemgr +mkdir -p $JK $PREFIX/etc/jupyter/nbconfig/notebook.d $PREFIX/etc/jupyter/jupyter_notebook_config.d $PREFIX/share/jupyter/nbextensions/jupyterq_licensemgr cp kernelspec/* $JK +(cd jupyterq_licensemgr && python setup.py install) +cp $JL/index.js $PREFIX/share/jupyter/nbextensions/jupyterq_licensemgr +cp $JL/jupyterq_licensemgr.json $PREFIX/etc/jupyter/nbconfig/notebook.d +cp $JL/jupyterq_licensemgr_config.json $PREFIX/etc/jupyter/jupyter_notebook_config.d cp jupyterq*.q $QHOME cp -r kxpy $QHOME cp $QLIBDIR/jupyterq.so $QHOME/$QLIBDIR diff --git a/conda-recipe/run_test.sh b/conda-recipe/run_test.sh index 834a392..68c743e 100755 --- a/conda-recipe/run_test.sh +++ b/conda-recipe/run_test.sh @@ -1,5 +1,5 @@ #!/bin/bash -if [ -e ${QLIC}/kc.lic ] +if [ -e ${QLIC}/kc.lic ] || [ -e ${QLIC}/k4.lic ] then tests/test.sh else diff --git a/docker/Dockerfile b/docker/Dockerfile index 5d1c0df..c966347 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -5,21 +5,15 @@ ARG embedpy_img=kxsys/embedpy:latest #### FROM $embedpy_img AS embedpy - -# do not clean here, its cleaned later! -# no upgrade either as it comes from embedPy -RUN apt-get update - -#### - FROM embedpy AS juypterq -RUN apt-get -yy --option=Dpkg::options::=--force-unsafe-io --no-install-recommends install \ - build-essential \ +RUN apt-get update \ + && apt-get -yy --option=Dpkg::options::=--force-unsafe-io --no-install-recommends install build-essential \ && apt-get clean \ && find /var/lib/apt/lists -type f -delete COPY build/makefile requirements.txt *.q *.p *.ipynb /opt/kx/jupyterq/ +COPY jupyterq_licensemgr/ /opt/kx/jupyterq/jupyterq_licensemgr/ COPY src/c/ /opt/kx/jupyterq/src/c/ COPY kxpy/ /opt/kx/jupyterq/kxpy/ COPY kernelspec/ /opt/kx/jupyterq/kernelspec/ @@ -50,32 +44,51 @@ LABEL org.label-schema.schema-version="1.0" \ org.label-schema.build-date="$BUILD_DATE" \ org.label-schema.docker.cmd="docker run -v `pwd`/q:/tmp/q -p $PORT:$PORT kxsys/jupyterq" -RUN apt-get -yy --option=Dpkg::options::=--force-unsafe-io --no-install-recommends install \ - libgl1-mesa-glx \ - && apt-get clean \ - && find /var/lib/apt/lists -type f -delete - COPY --from=juypterq /opt/kx/jupyterq /opt/kx/jupyterq RUN chown kx:kx /opt/kx/jupyterq /opt/kx/jupyterq/kdb+Notebooks.ipynb RUN find /opt/kx/jupyterq -maxdepth 1 -type f -name 'jupyterq_*.q' | xargs ln -s -t /opt/kx/q \ && ln -s -t /opt/kx/q /opt/kx/jupyterq/kxpy \ && ln -s -t /opt/kx/q/l64 /opt/kx/jupyterq/l64/jupyterq.so +RUN apt-get update \ + && apt-get -yy --option=Dpkg::options::=--force-unsafe-io --no-install-recommends install xinetd libgl1-mesa-glx \ + && apt-get clean \ + && find /var/lib/apt/lists -type f -delete + USER kx RUN . /opt/conda/etc/profile.d/conda.sh \ && conda activate kx \ && conda install nomkl \ && conda install --file /opt/kx/jupyterq/requirements.txt \ - && conda clean -y --all \ - && jupyter kernelspec install --user --name=qpk /opt/kx/jupyterq/kernelspec \ - && jupyter trust /opt/kx/jupyterq/kdb+Notebooks.ipynb + && conda clean -y --all + +USER root + +RUN chown -R kx:kx /opt/kx/jupyterq/jupyterq_licensemgr + +USER kx + +RUN . /opt/conda/etc/profile.d/conda.sh \ + && conda activate kx \ + && cd /opt/kx/jupyterq/jupyterq_licensemgr \ + && python setup.py build \ + && pip install --user --no-deps . # remove token auth -RUN mkdir ~/.jupyter \ +RUN mkdir -p ~/.jupyter \ && echo "c.NotebookApp.token = u''" > ~/.jupyter/jupyter_notebook_config.py +RUN . /opt/conda/etc/profile.d/conda.sh \ + && conda activate kx \ + && jupyter kernelspec install --user --name=qpk /opt/kx/jupyterq/kernelspec \ + && jupyter nbextension install --user --py jupyterq_licensemgr \ + && jupyter nbextension enable --user --py jupyterq_licensemgr \ + && jupyter serverextension enable --py jupyterq_licensemgr \ + && jupyter trust /opt/kx/jupyterq/kdb+Notebooks.ipynb + USER root +COPY docker/init /init +RUN chmod 0755 /init ENTRYPOINT ["/init"] -CMD ["/bin/sh", "-l", "-c", "printf '\npoint your browser at http://127.0.0.1:%s/notebooks/kdb%%2BNotebooks.ipynb\n\n' $PORT && exec jupyter notebook --notebook-dir=/opt/kx/jupyterq --ip='0.0.0.0' --port=$PORT --no-browser"] diff --git a/docker/init b/docker/init new file mode 100755 index 0000000..a302198 --- /dev/null +++ b/docker/init @@ -0,0 +1,10 @@ +#!/bin/sh +set -x +/usr/sbin/xinetd -d & + +HOME=/home/kx +export HOME + +printf '\npoint your browser at http://localhost:%s/notebooks/kdb%%2BNotebooks.ipynb\n\n' "$PORT" + +exec chpst -u kx /bin/sh -l -c "cd /opt/kx/jupyterq && QLIC=/home/kx exec jupyter notebook --notebook-dir=/opt/kx/jupyterq --ip='0.0.0.0' --port='$PORT' --no-browser" diff --git a/jupyterq_licensemgr/LICENSE b/jupyterq_licensemgr/LICENSE new file mode 100755 index 0000000..fe19d61 --- /dev/null +++ b/jupyterq_licensemgr/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Kx Systems + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/jupyterq_licensemgr/MANIFEST.in b/jupyterq_licensemgr/MANIFEST.in new file mode 100644 index 0000000..7080a4e --- /dev/null +++ b/jupyterq_licensemgr/MANIFEST.in @@ -0,0 +1 @@ +recursive-include jupyterq_licensemgr *.json *.js *.q diff --git a/jupyterq_licensemgr/README.md b/jupyterq_licensemgr/README.md new file mode 100644 index 0000000..bf051b8 --- /dev/null +++ b/jupyterq_licensemgr/README.md @@ -0,0 +1 @@ +serverextension for automatic licensing diff --git a/jupyterq_licensemgr/jupyterq_licensemgr/__init__.py b/jupyterq_licensemgr/jupyterq_licensemgr/__init__.py new file mode 100644 index 0000000..9933e43 --- /dev/null +++ b/jupyterq_licensemgr/jupyterq_licensemgr/__init__.py @@ -0,0 +1,89 @@ +from notebook.base.handlers import IPythonHandler +from os import path, getenv, popen +from urllib.parse import parse_qs, urlparse +from socket import gethostname +from base64 import b64decode +from json import dumps +from getpass import getuser +from sys import stderr, exit + +QHOME = getenv('QHOME', path.join(path.expanduser("~"),"q")) +if not QHOME or not path.isdir(QHOME): + print('QHOME env is not set to a directory', file=stderr) + exit(2) + +QLIC = getenv('QLIC', QHOME) +if not path.isdir(QLIC): + print('QLIC env is not set to a directory for storing the license', file=stderr) + exit(2) + +QLIC_KC = path.join(QLIC, 'kc.lic') +QLIC_K4 = path.join(QLIC, 'k4.lic') + +# kc.lic.py compatibility +for el, ln in [('QLIC_K4', 'k4.lic'), ('QLIC_KC', 'kc.lic')]: + lic = getenv(el) + if lic: + with open(path.join(qlic, ln), 'wb') as file: + file.write(b64decode(lic)) + +def _jupyter_server_extension_paths(): + return [{ + "module": "jupyterq_licensemgr" + }] + +def _jupyter_nbextension_paths(): + return [dict( + section="notebook", + src=".", + dest="jupyterq_licensemgr", + require="jupyterq_licensemgr/index")] + +def load_jupyter_server_extension(nb): + class LicenseCheckHandler(IPythonHandler): + def get(self): + result = {"action": None, "info": None, "ok": False, "hostname": gethostname(), "user": getuser() } + status = ' '.join(popen('q "' + path.join(path.dirname(path.realpath(__file__)), 'check_q.q') + '" -q 2>&1').read().replace("\t",' ').splitlines()[0].split(" ")[1:]) + if status == "license daemon returned: 'blocked -- please confirm your email": + result['action'] = "dialog" + result['info'] = "Email address unconfirmed" + result['description'] = "Check your email for a request from Kx to verify your license." + elif status == "license daemon returned: 'blocked -- please contact ondemand@kx.com": + result['action'] = "dialog" + result['info'] = "Revoked license" + result['description'] = "This license has been revoked. If you did not do this, please contact ondemand@kx.com" + elif status == "host": + result['action'] = "license" + result['info'] = "Wrong hostname on license" + elif status == "kc.lic" or status == "k4.lic" or status == 'detected and no license found.': # no license, or corrupt license + result['action'] = "license" + result['info'] = "Unlicensed workstation" + elif status == "ok": + result['action'] = "ready" + result['ok'] = True + else: + # possible: new q error message?, someone messed with q binary? + nb.log.error("jupyterq_licensemgr issue: " + status) + result['action'] = "dialog" + result['info'] = "License check failed" + result['description'] = "There's an issue with the license manager. Check your installation carefully and/or reinstall your application" + self.add_header("Content-Type", "application/json") + nb.log.info("jupyterq_licensemgr check request") + self.finish(dumps(result).encode()) + + class LicenseSubmitHandler(IPythonHandler): + def get(self): + d = self.get_arguments("d") + if d != []: + with open(QLIC_KC, "wb") as file: + file.write(b64decode(d[0].replace(' ','+'))) + self.add_header("Content-Type", "image/gif") + nb.log.info("jupyterq_licensemgr submitted from client") + self.finish(b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")) + + wa = nb.web_app + wa.add_handlers('.*$', [ + ('/kx/license_check.json', LicenseCheckHandler), + ('/kx/license_submit.py', LicenseSubmitHandler) + ]) + nb.log.info("jupyterq_licensemgr module enabled") diff --git a/jupyterq_licensemgr/jupyterq_licensemgr/check_q.q b/jupyterq_licensemgr/jupyterq_licensemgr/check_q.q new file mode 100644 index 0000000..fd88494 --- /dev/null +++ b/jupyterq_licensemgr/jupyterq_licensemgr/check_q.q @@ -0,0 +1,2 @@ +1"k ok"; +\\ diff --git a/jupyterq_licensemgr/jupyterq_licensemgr/index.js b/jupyterq_licensemgr/jupyterq_licensemgr/index.js new file mode 100644 index 0000000..bbbd94b --- /dev/null +++ b/jupyterq_licensemgr/jupyterq_licensemgr/index.js @@ -0,0 +1,122 @@ +define(['jquery', 'base/js/namespace', 'base/js/dialog'], function($, J, D){ + +// [name] is the name of the event "click", "mouseover", .. +// same as you'd pass it to bind() +// [fn] is the handler function +$.fn.bindFirst = function(name, fn) { + // bind as you normally would + // don't want to miss out on any jQuery magic + this.on(name, fn); + + // Thanks to a comment by @Martin, adding support for + // namespaced events too. + this.each(function() { + var handlers = $._data(this, 'events')[name.split('.')[0]]; + // take out the handler we just inserted from the end + var handler = handlers.pop(); + // move it at the beginning + handlers.splice(0, 0, handler); + }); +}; + + var IFRAME = null + var DIALOG = null; + var NEEDLICENSE = false; + + var BASE = location.protocol + "//" + location.host; + function close() { + if(DIALOG != null) DIALOG.remove(); + $('.modal-backdrop').remove() + DIALOG = IFRAME = null; + } +window.J=J; + + var actions = { + "license": function(text) { + if(IFRAME != null) return; + NEEDLICENSE=true; + + close(); + + IFRAME = $('