mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Add plugins page
This commit is contained in:
@ -6,7 +6,7 @@ import logging
|
|||||||
|
|
||||||
default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
|
default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
|
||||||
loaded = {}
|
loaded = {}
|
||||||
|
database = {}
|
||||||
|
|
||||||
class Plugin:
|
class Plugin:
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -18,6 +18,26 @@ class Plugin:
|
|||||||
logging.debug("loaded plugin %s as %s" % (plugin_name, plugin_instance))
|
logging.debug("loaded plugin %s as %s" % (plugin_name, plugin_instance))
|
||||||
loaded[plugin_name] = plugin_instance
|
loaded[plugin_name] = plugin_instance
|
||||||
|
|
||||||
|
def toggle_plugin(name, enable=True):
|
||||||
|
"""
|
||||||
|
Load or unload a plugin
|
||||||
|
|
||||||
|
returns True if changed, otherwise False
|
||||||
|
"""
|
||||||
|
global loaded, database
|
||||||
|
if not enable and name in loaded:
|
||||||
|
if getattr(loaded[name], 'on_unload', None):
|
||||||
|
loaded[name].on_unload()
|
||||||
|
del loaded[name]
|
||||||
|
return True
|
||||||
|
|
||||||
|
if enable and name in database and name not in loaded:
|
||||||
|
load_from_file(database[name])
|
||||||
|
one(name, 'loaded')
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def on(event_name, *args, **kwargs):
|
def on(event_name, *args, **kwargs):
|
||||||
for plugin_name, plugin in loaded.items():
|
for plugin_name, plugin in loaded.items():
|
||||||
@ -48,10 +68,11 @@ def load_from_file(filename):
|
|||||||
|
|
||||||
|
|
||||||
def load_from_path(path, enabled=()):
|
def load_from_path(path, enabled=()):
|
||||||
global loaded
|
global loaded, database
|
||||||
logging.debug("loading plugins from %s - enabled: %s" % (path, enabled))
|
logging.debug("loading plugins from %s - enabled: %s" % (path, enabled))
|
||||||
for filename in glob.glob(os.path.join(path, "*.py")):
|
for filename in glob.glob(os.path.join(path, "*.py")):
|
||||||
plugin_name = os.path.basename(filename.replace(".py", ""))
|
plugin_name = os.path.basename(filename.replace(".py", ""))
|
||||||
|
database[plugin_name] = filename
|
||||||
if plugin_name in enabled:
|
if plugin_name in enabled:
|
||||||
try:
|
try:
|
||||||
load_from_file(filename)
|
load_from_file(filename)
|
||||||
|
@ -466,7 +466,11 @@ class BTTether(plugins.Plugin):
|
|||||||
logging.info("BT-TETHER: Successfully loaded ...")
|
logging.info("BT-TETHER: Successfully loaded ...")
|
||||||
self.ready = True
|
self.ready = True
|
||||||
|
|
||||||
|
def on_unload(self):
|
||||||
|
self.ui.remove_element('bluetooth')
|
||||||
|
|
||||||
def on_ui_setup(self, ui):
|
def on_ui_setup(self, ui):
|
||||||
|
self.ui = ui
|
||||||
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0),
|
ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0),
|
||||||
label_font=fonts.Bold, text_font=fonts.Medium))
|
label_font=fonts.Bold, text_font=fonts.Medium))
|
||||||
|
|
||||||
|
@ -25,6 +25,10 @@ class Example(plugins.Plugin):
|
|||||||
def on_loaded(self):
|
def on_loaded(self):
|
||||||
logging.warning("WARNING: this plugin should be disabled! options = " % self.options)
|
logging.warning("WARNING: this plugin should be disabled! options = " % self.options)
|
||||||
|
|
||||||
|
# called before the plugin is unloaded
|
||||||
|
def on_unload(self):
|
||||||
|
pass
|
||||||
|
|
||||||
# called hen there's internet connectivity
|
# called hen there's internet connectivity
|
||||||
def on_internet_available(self, agent):
|
def on_internet_available(self, agent):
|
||||||
pass
|
pass
|
||||||
|
@ -179,16 +179,19 @@ class Handler:
|
|||||||
|
|
||||||
def plugins(self, name, subpath):
|
def plugins(self, name, subpath):
|
||||||
if name is None:
|
if name is None:
|
||||||
# show plugins overview
|
return render_template('plugins.html', loaded=plugins.loaded, database=plugins.database)
|
||||||
abort(404)
|
|
||||||
|
if name == 'toggle' and request.method == 'POST':
|
||||||
|
checked = True if 'enabled' in request.form else False
|
||||||
|
return 'success' if plugins.toggle_plugin(request.form['plugin'], checked) else 'failed'
|
||||||
|
|
||||||
|
if name in plugins.loaded and plugins.loaded[name] is not None and hasattr(plugins.loaded[name], 'on_webhook'):
|
||||||
|
try:
|
||||||
|
return plugins.loaded[name].on_webhook(subpath, request)
|
||||||
|
except Exception:
|
||||||
|
abort(500)
|
||||||
else:
|
else:
|
||||||
if name in plugins.loaded and hasattr(plugins.loaded[name], 'on_webhook'):
|
abort(404)
|
||||||
try:
|
|
||||||
return plugins.loaded[name].on_webhook(subpath, request)
|
|
||||||
except Exception:
|
|
||||||
abort(500)
|
|
||||||
else:
|
|
||||||
abort(404)
|
|
||||||
|
|
||||||
# serve a message and shuts down the unit
|
# serve a message and shuts down the unit
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
|
@ -31,4 +31,37 @@ a.read {
|
|||||||
|
|
||||||
p.messagebody {
|
p.messagebody {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li.navitem {
|
||||||
|
width: 16.66% !important;
|
||||||
|
clear: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom indentations are needed because the length of custom labels differs from
|
||||||
|
the length of the standard labels */
|
||||||
|
.custom-size-flipswitch.ui-flipswitch .ui-btn.ui-flipswitch-on {
|
||||||
|
text-indent: -5.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-size-flipswitch.ui-flipswitch .ui-flipswitch-off {
|
||||||
|
text-indent: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom widths are needed because the length of custom labels differs from
|
||||||
|
the length of the standard labels */
|
||||||
|
.custom-size-flipswitch.ui-flipswitch {
|
||||||
|
width: 8.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-size-flipswitch.ui-flipswitch.ui-flipswitch-active {
|
||||||
|
padding-left: 7em;
|
||||||
|
width: 1.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 28em) {
|
||||||
|
/*Repeated from rule .ui-flipswitch above*/
|
||||||
|
.ui-field-contain > label + .custom-size-flipswitch.ui-flipswitch {
|
||||||
|
width: 1.875em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
( '/inbox/new', 'new', 'mail', 'New' ),
|
( '/inbox/new', 'new', 'mail', 'New' ),
|
||||||
( '/inbox/profile', 'profile', 'info', 'Profile' ),
|
( '/inbox/profile', 'profile', 'info', 'Profile' ),
|
||||||
( '/inbox/peers', 'peers', 'user', 'Peers' ),
|
( '/inbox/peers', 'peers', 'user', 'Peers' ),
|
||||||
|
( '/plugins', 'plugins', 'grid', 'Plugins' ),
|
||||||
] %}
|
] %}
|
||||||
{% set active_page = active_page|default('inbox') %}
|
{% set active_page = active_page|default('inbox') %}
|
||||||
|
|
||||||
@ -54,7 +55,7 @@
|
|||||||
<div data-role="navbar" data-iconpos="left">
|
<div data-role="navbar" data-iconpos="left">
|
||||||
<ul>
|
<ul>
|
||||||
{% for href, id, icon, caption in navigation %}
|
{% for href, id, icon, caption in navigation %}
|
||||||
<li>
|
<li class="navitem">
|
||||||
<a href="{{ href }}" id="{{ id }}" data-icon="{{ icon }}" class="{{ 'ui-btn-active' if active_page == id }}">{{ caption }}</a>
|
<a href="{{ href }}" id="{{ id }}" data-icon="{{ icon }}" class="{{ 'ui-btn-active' if active_page == id }}">{{ caption }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
39
pwnagotchi/ui/web/templates/plugins.html
Normal file
39
pwnagotchi/ui/web/templates/plugins.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% set active_page = "plugins" %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
Plugins
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
$(function(){
|
||||||
|
$("input[type=checkbox]").change(function(e) {
|
||||||
|
var checkbox = $(this);
|
||||||
|
var form = checkbox.closest("form");
|
||||||
|
var url = form.attr('action');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: form.serialize(),
|
||||||
|
success: function(data) {
|
||||||
|
if( data.indexOf('failed') != -1 ) {
|
||||||
|
alert('Could not be toggled.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div style="padding: 1em">
|
||||||
|
{% for name in database.keys() %}
|
||||||
|
<h4>{{name}}</h4>
|
||||||
|
<form method="POST" action="/plugins/toggle">
|
||||||
|
<input type="checkbox" data-role="flipswitch" name="enabled" id="flip-checkbox-{{name}}" data-on-text="Enabled" data-off-text="Disabled" data-wrapper-class="custom-size-flipswitch" {% if name in loaded %} checked {% endif %}>
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||||
|
<input type="hidden" name="plugin" value="{{ name }}"/>
|
||||||
|
</form>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Reference in New Issue
Block a user