Compare commits

..

No commits in common. "master" and "minimal-plugin" have entirely different histories.

7 changed files with 57 additions and 147 deletions

View File

@ -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

View File

@ -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,

View File

@ -1,12 +0,0 @@
version: '3'
services:
main:
build:
context: ./
dockerfile: Dockerfile
restart: always
volumes:
- ./plugins:/var/www/plugins
ports:
- "8085:5000"

View File

@ -1,56 +1,48 @@
function plugin_start(div, entry, editable) { function plugin_load(config, div, entry) {
if(typeof plugin_init !== 'undefined') plugin_init(div, entry, editable); $("#load-pre-status").text("Loading...");
if(typeof plugin_render !== 'undefined') plugin_render(div, entry); var status_span = $('#load-status');
}
var len1 = config.javascript && config.javascript.length || 0;
function plugin_load(config, div, entry, editable) { var len2 = config.globals && config.javascript.length || 0;
$("#load-pre-status").text("Loading..."); var num_progress = len1 + len2;
var status_span = $('#load-status');
status_span.after(" / " + num_progress);
var len_deps = config.dependencies && config.dependencies.length || 0; status_span.text("0");
var len_globals = config.globals && Object.keys(config.globals).length || 0;
var progress = 0;
var progress = 0; var check = function() {
var num_progress = len_deps + len_globals; if (++progress == num_progress) {
status_span.parent().empty();
status_span.after(" / " + num_progress); if(typeof plugin_init !== 'undefined') plugin_init(div, entry);
status_span.text("0"); if(typeof plugin_render !== 'undefined') plugin_render(div, entry);
}
var check = function() { }
status_span.text(progress + 1 + "");
if (++progress >= num_progress) { // has to be >= for num_progress = 0 config.javascript.forEach(function(script) {
status_span.parent().empty(); $.ajax({
$.ajax({dataType: "script", cache: true, url: config.javascript, success: function() { plugin_start(div, entry, editable); }}); dataType: "script",
} cache: true,
} url: script,
success: function () {
config.dependencies.forEach(function(script) { status_span.text(progress + 1 + "");
$.ajax({ check();
dataType: "script", }
cache: true, });
url: script, });
success: check
}); Object.keys(config.globals).forEach(function(global_name) {
}); $.get(config.globals[global_name], function(data) {
window[global_name] = data;
Object.keys(config.globals).forEach(function(global_name) { check();
$.get(config.globals[global_name], function(data) { });
window[global_name] = data; });
check();
}); config.css.forEach(function(css_url) {
}); var link = document.createElement('link');
link.rel = 'stylesheet';
config.css.forEach(function(css_url) { link.type = 'text/css';
var link = document.createElement('link'); link.href = css_url;
link.rel = 'stylesheet'; link.media = 'all';
link.type = 'text/css'; $('head').append(link);
link.href = css_url; });
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();
}

View File

@ -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)

View File

@ -1,8 +1,7 @@
{ {
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;

View File

@ -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