Compare commits
6 Commits
minimal-pl
...
master
Author | SHA1 | Date | |
---|---|---|---|
d63b3f212e | |||
e028344b97 | |||
23414b6d8b | |||
002f6aa7fb | |||
f865c798a8 | |||
a199ab5750 |
21
Dockerfile
Normal file
21
Dockerfile
Normal file
|
@ -0,0 +1,21 @@
|
|||
FROM alpine:3.15
|
||||
|
||||
RUN apk --update add redis
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
RUN apk add --update --no-cache python3 && ln -sf python3 /usr/bin/python
|
||||
RUN python3 -m ensurepip
|
||||
RUN pip3 install --no-cache --upgrade pip setuptools
|
||||
|
||||
ADD ./plugin-server.py /var/www/plugin-server/plugin-server.py
|
||||
ADD ./plugin-loader.js /var/www/plugin-server/plugin-loader.js
|
||||
ADD ./requirements.txt /var/www/plugin-server/requirements.txt
|
||||
|
||||
WORKDIR /var/www/plugin-server/
|
||||
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
RUN ln -s /var/www/plugins/ ./
|
||||
|
||||
CMD /usr/bin/redis-server & python plugin-server.py
|
19
README.md
19
README.md
|
@ -8,13 +8,20 @@ It uses a javascript loader file, which loads plugins over the internet. This me
|
|||
just serving static files, but we implemented our server code, which does one more trick. The plugin is loaded at the touch of "edit" button
|
||||
and that is it.
|
||||
|
||||
## Running
|
||||
Set the linked volume in `docker-compose.yml` to where you have your plugins directory. Then run the following command.
|
||||
```bash
|
||||
docker-compose -f docker-compose.yml up -d
|
||||
```
|
||||
This will make the application available on `0.0.0.0:8085`.
|
||||
|
||||
## Plugin
|
||||
|
||||
Every plugin is a folder which is located where the server code is run from. A plugin consists of config.json file and any other file
|
||||
you want. config.json instructs plugin loader which files to load. Currently we support three keys in config.json:
|
||||
|
||||
1. javascript: list of javascript files, local files have to be written as: `"$LOCATION$/file.js"
|
||||
2. css: list of css stylesheets, local css have to be written as: `"$LOCATION$/file.js"
|
||||
1. javascript: list of javascript files, local files have to be written as: `"$LOCATION$/file.js"`
|
||||
2. css: list of css stylesheets, local css have to be written as: `"$LOCATION$/file.js"`
|
||||
3. global: key-value list of `{'name': file}` - content of a file is stored in global variable name in browser javacsript. Same rule as above applies to local files.
|
||||
|
||||
Everytime you want to access local plugin files in javascript, css or wherever, you prepend `$LOCATION$/` to the url. The plugin server takes care, that correct url is generated. This settings is found in `URL` value in server python code.
|
||||
|
@ -27,16 +34,16 @@ plugin_render(div, entry)
|
|||
plugin_save(div)
|
||||
```
|
||||
|
||||
# Usage
|
||||
## Lexonomy Entry Editor Settings
|
||||
|
||||
To use it, you must first paste the [javascript loader](plugin-loader.js) into custom entry editor section of Lexonomy configuration.
|
||||
To use it, you must first paste the [javascript loader](plugin.js) into custom entry editor section of Lexonomy configuration.
|
||||
Next, set correct url of your plugin. In order to work, the plugin must be accessible over HTTPS, if Lexonomy already runs on https.
|
||||
In order to run a server, you need to run a default redis server and have these python packages installed: flask and redis.
|
||||
Then you can run [flask server](plugin-server.py) on a server.
|
||||
|
||||
## Example
|
||||
### Example
|
||||
|
||||
Let's say you are hosting plugins on lexonomyplugins.example.com and that there is a plugin (a folder) called myplugin in `CWD` of the server python process. You need to set:
|
||||
Let's say you are hosting plugins on lexonomyplugins.example.com and that there is a plugin (a folder) called myplugin in `CWD/plugins/` of the server python process. You need to set:
|
||||
|
||||
* `plugin: "//lexonomyplugins.example.com/myplugin"` in javascript loader,
|
||||
* `URL = "//lexonomyplugins.example.com/myplugin"` in python server,
|
||||
|
|
12
docker-compose.yml
Normal file
12
docker-compose.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
version: '3'
|
||||
|
||||
services:
|
||||
main:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: Dockerfile
|
||||
restart: always
|
||||
volumes:
|
||||
- ./plugins:/var/www/plugins
|
||||
ports:
|
||||
- "8085:5000"
|
|
@ -1,32 +1,35 @@
|
|||
function plugin_load(config, div, entry) {
|
||||
function plugin_start(div, entry, editable) {
|
||||
if(typeof plugin_init !== 'undefined') plugin_init(div, entry, editable);
|
||||
if(typeof plugin_render !== 'undefined') plugin_render(div, entry);
|
||||
}
|
||||
|
||||
function plugin_load(config, div, entry, editable) {
|
||||
$("#load-pre-status").text("Loading...");
|
||||
var status_span = $('#load-status');
|
||||
|
||||
var len1 = config.javascript && config.javascript.length || 0;
|
||||
var len2 = config.globals && config.javascript.length || 0;
|
||||
var num_progress = len1 + len2;
|
||||
var len_deps = config.dependencies && config.dependencies.length || 0;
|
||||
var len_globals = config.globals && Object.keys(config.globals).length || 0;
|
||||
|
||||
var progress = 0;
|
||||
var num_progress = len_deps + len_globals;
|
||||
|
||||
status_span.after(" / " + num_progress);
|
||||
status_span.text("0");
|
||||
|
||||
var progress = 0;
|
||||
var check = function() {
|
||||
if (++progress == num_progress) {
|
||||
status_span.text(progress + 1 + "");
|
||||
if (++progress >= num_progress) { // has to be >= for num_progress = 0
|
||||
status_span.parent().empty();
|
||||
if(typeof plugin_init !== 'undefined') plugin_init(div, entry);
|
||||
if(typeof plugin_render !== 'undefined') plugin_render(div, entry);
|
||||
$.ajax({dataType: "script", cache: true, url: config.javascript, success: function() { plugin_start(div, entry, editable); }});
|
||||
}
|
||||
}
|
||||
|
||||
config.javascript.forEach(function(script) {
|
||||
config.dependencies.forEach(function(script) {
|
||||
$.ajax({
|
||||
dataType: "script",
|
||||
cache: true,
|
||||
url: script,
|
||||
success: function () {
|
||||
status_span.text(progress + 1 + "");
|
||||
check();
|
||||
}
|
||||
success: check
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -45,4 +48,9 @@ function plugin_load(config, div, entry) {
|
|||
link.media = 'all';
|
||||
$('head').append(link);
|
||||
});
|
||||
|
||||
// always adding a style element for to use for convenience
|
||||
$('head').append($('<style id="dynamic_styler"></style>'));
|
||||
|
||||
if(num_progress == 0) check();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ $LOCATION$ -> location of plugin
|
|||
every plugin is its own folder and if folder is
|
||||
"myplugin" and this website's url is "http://example.com",
|
||||
then $LOCATION$ gets the value of http://example.com/myplugin
|
||||
requirements: flask, redis
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
@ -22,8 +23,9 @@ from flask import Flask, Response, request
|
|||
app = Flask(__name__)
|
||||
redis = redis.Redis(host='localhost', port=6379, db=0)
|
||||
|
||||
URL = "//plugins.lexonomy.cjvt.si"
|
||||
URL = "https://plugins.lexonomy.cjvt.si"
|
||||
REPLACE_STRING = "$LOCATION$"
|
||||
PLUGINS_DIR = os.environ.get("PLUGINS_DIR_PATH", "plugins")
|
||||
|
||||
|
||||
def check_cache(full_path):
|
||||
|
@ -48,6 +50,7 @@ def generate_etag(N=12):
|
|||
|
||||
def return_file(path, plugin=None):
|
||||
full_path = path if plugin is None else"{}/{}".format(plugin, path)
|
||||
full_path = f"{PLUGINS_DIR}/{full_path}"
|
||||
|
||||
if not os.path.isfile(full_path):
|
||||
return "File not found", 404
|
||||
|
@ -99,6 +102,7 @@ def return_file(path, plugin=None):
|
|||
elif request.headers['If-None-Match'] != etag:
|
||||
status_code = 200
|
||||
|
||||
status_code = 200
|
||||
return Response(result, mimetype=mt, headers=headers, status=status_code)
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
plugin: 'vsms',
|
||||
plugin: 'vsms-py-v4.1',
|
||||
url: '//plugins.lexonomy.cjvt.si',
|
||||
ske_gdex: ["Hungarian-bilingual-v1", 300],
|
||||
editor: function(div, entry, uneditable) {
|
||||
$(div).append('<p><span id="load-pre-status">INIT</span><span id="load-status"></span></p>');
|
||||
var progress_ctr = 2, config = null;
|
||||
|
|
37
requirements.txt
Normal file
37
requirements.txt
Normal file
|
@ -0,0 +1,37 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with python 3.8
|
||||
#
|
||||
async-timeout==4.0.2
|
||||
# via redis
|
||||
click==8.1.3
|
||||
# via flask
|
||||
deprecated==1.2.13
|
||||
# via redis
|
||||
flask==2.1.2
|
||||
# via
|
||||
# -r requirements.in
|
||||
# flask-cors
|
||||
flask-cors==3.0.10
|
||||
# via -r requirements.in
|
||||
importlib-metadata==4.11.4
|
||||
# via flask
|
||||
itsdangerous==2.1.2
|
||||
# via flask
|
||||
jinja2==3.1.2
|
||||
# via flask
|
||||
markupsafe==2.1.1
|
||||
# via jinja2
|
||||
packaging==21.3
|
||||
# via redis
|
||||
pyparsing==3.0.9
|
||||
# via packaging
|
||||
redis==4.3.3
|
||||
# via -r requirements.in
|
||||
six==1.16.0
|
||||
# via flask-cors
|
||||
werkzeug==2.1.2
|
||||
# via flask
|
||||
wrapt==1.14.1
|
||||
# via deprecated
|
||||
zipp==3.8.0
|
||||
# via importlib-metadata
|
Loading…
Reference in New Issue
Block a user