Compare commits
No commits in common. "master" and "minimal-plugin" have entirely different histories.
master
...
minimal-pl
21
Dockerfile
21
Dockerfile
|
@ -1,21 +0,0 @@
|
||||||
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,20 +8,13 @@ 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
|
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.
|
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
|
## 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
|
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:
|
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"`
|
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"`
|
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.
|
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.
|
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.
|
||||||
|
@ -34,16 +27,16 @@ plugin_render(div, entry)
|
||||||
plugin_save(div)
|
plugin_save(div)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Lexonomy Entry Editor Settings
|
# Usage
|
||||||
|
|
||||||
To use it, you must first paste the [javascript loader](plugin.js) into custom entry editor section of Lexonomy configuration.
|
To use it, you must first paste the [javascript loader](plugin-loader.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.
|
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.
|
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.
|
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/plugins/` 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` of the server python process. You need to set:
|
||||||
|
|
||||||
* `plugin: "//lexonomyplugins.example.com/myplugin"` in javascript loader,
|
* `plugin: "//lexonomyplugins.example.com/myplugin"` in javascript loader,
|
||||||
* `URL = "//lexonomyplugins.example.com/myplugin"` in python server,
|
* `URL = "//lexonomyplugins.example.com/myplugin"` in python server,
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
main:
|
|
||||||
build:
|
|
||||||
context: ./
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- ./plugins:/var/www/plugins
|
|
||||||
ports:
|
|
||||||
- "8085:5000"
|
|
|
@ -1,35 +1,32 @@
|
||||||
function plugin_start(div, entry, editable) {
|
function plugin_load(config, div, entry) {
|
||||||
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...");
|
$("#load-pre-status").text("Loading...");
|
||||||
var status_span = $('#load-status');
|
var status_span = $('#load-status');
|
||||||
|
|
||||||
var len_deps = config.dependencies && config.dependencies.length || 0;
|
var len1 = config.javascript && config.javascript.length || 0;
|
||||||
var len_globals = config.globals && Object.keys(config.globals).length || 0;
|
var len2 = config.globals && config.javascript.length || 0;
|
||||||
|
var num_progress = len1 + len2;
|
||||||
var progress = 0;
|
|
||||||
var num_progress = len_deps + len_globals;
|
|
||||||
|
|
||||||
status_span.after(" / " + num_progress);
|
status_span.after(" / " + num_progress);
|
||||||
status_span.text("0");
|
status_span.text("0");
|
||||||
|
|
||||||
|
var progress = 0;
|
||||||
var check = function() {
|
var check = function() {
|
||||||
status_span.text(progress + 1 + "");
|
if (++progress == num_progress) {
|
||||||
if (++progress >= num_progress) { // has to be >= for num_progress = 0
|
|
||||||
status_span.parent().empty();
|
status_span.parent().empty();
|
||||||
$.ajax({dataType: "script", cache: true, url: config.javascript, success: function() { plugin_start(div, entry, editable); }});
|
if(typeof plugin_init !== 'undefined') plugin_init(div, entry);
|
||||||
|
if(typeof plugin_render !== 'undefined') plugin_render(div, entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.dependencies.forEach(function(script) {
|
config.javascript.forEach(function(script) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
dataType: "script",
|
dataType: "script",
|
||||||
cache: true,
|
cache: true,
|
||||||
url: script,
|
url: script,
|
||||||
success: check
|
success: function () {
|
||||||
|
status_span.text(progress + 1 + "");
|
||||||
|
check();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -48,9 +45,4 @@ function plugin_load(config, div, entry, editable) {
|
||||||
link.media = 'all';
|
link.media = 'all';
|
||||||
$('head').append(link);
|
$('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,7 +8,6 @@ $LOCATION$ -> location of plugin
|
||||||
every plugin is its own folder and if folder is
|
every plugin is its own folder and if folder is
|
||||||
"myplugin" and this website's url is "http://example.com",
|
"myplugin" and this website's url is "http://example.com",
|
||||||
then $LOCATION$ gets the value of http://example.com/myplugin
|
then $LOCATION$ gets the value of http://example.com/myplugin
|
||||||
requirements: flask, redis
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
@ -23,9 +22,8 @@ from flask import Flask, Response, request
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
redis = redis.Redis(host='localhost', port=6379, db=0)
|
redis = redis.Redis(host='localhost', port=6379, db=0)
|
||||||
|
|
||||||
URL = "https://plugins.lexonomy.cjvt.si"
|
URL = "//plugins.lexonomy.cjvt.si"
|
||||||
REPLACE_STRING = "$LOCATION$"
|
REPLACE_STRING = "$LOCATION$"
|
||||||
PLUGINS_DIR = os.environ.get("PLUGINS_DIR_PATH", "plugins")
|
|
||||||
|
|
||||||
|
|
||||||
def check_cache(full_path):
|
def check_cache(full_path):
|
||||||
|
@ -50,7 +48,6 @@ def generate_etag(N=12):
|
||||||
|
|
||||||
def return_file(path, plugin=None):
|
def return_file(path, plugin=None):
|
||||||
full_path = path if plugin is None else"{}/{}".format(plugin, path)
|
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):
|
if not os.path.isfile(full_path):
|
||||||
return "File not found", 404
|
return "File not found", 404
|
||||||
|
@ -102,7 +99,6 @@ def return_file(path, plugin=None):
|
||||||
elif request.headers['If-None-Match'] != etag:
|
elif request.headers['If-None-Match'] != etag:
|
||||||
status_code = 200
|
status_code = 200
|
||||||
|
|
||||||
status_code = 200
|
|
||||||
return Response(result, mimetype=mt, headers=headers, status=status_code)
|
return Response(result, mimetype=mt, headers=headers, status=status_code)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
plugin: 'vsms-py-v4.1',
|
plugin: 'vsms',
|
||||||
url: '//plugins.lexonomy.cjvt.si',
|
url: '//plugins.lexonomy.cjvt.si',
|
||||||
ske_gdex: ["Hungarian-bilingual-v1", 300],
|
|
||||||
editor: function(div, entry, uneditable) {
|
editor: function(div, entry, uneditable) {
|
||||||
$(div).append('<p><span id="load-pre-status">INIT</span><span id="load-status"></span></p>');
|
$(div).append('<p><span id="load-pre-status">INIT</span><span id="load-status"></span></p>');
|
||||||
var progress_ctr = 2, config = null;
|
var progress_ctr = 2, config = null;
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
#
|
|
||||||
# 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