Skip to content

Commit

Permalink
Merge pull request #665 from dadav/feature/plugins_web_page
Browse files Browse the repository at this point in the history
Add plugins page
  • Loading branch information
evilsocket authored Nov 28, 2019
2 parents 19775b7 + cc5c469 commit 8b40e94
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 13 deletions.
25 changes: 23 additions & 2 deletions pwnagotchi/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
loaded = {}

database = {}

class Plugin:
@classmethod
Expand All @@ -18,6 +18,26 @@ def __init_subclass__(cls, **kwargs):
logging.debug("loaded plugin %s as %s" % (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):
for plugin_name, plugin in loaded.items():
Expand Down Expand Up @@ -48,10 +68,11 @@ def load_from_file(filename):


def load_from_path(path, enabled=()):
global loaded
global loaded, database
logging.debug("loading plugins from %s - enabled: %s" % (path, enabled))
for filename in glob.glob(os.path.join(path, "*.py")):
plugin_name = os.path.basename(filename.replace(".py", ""))
database[plugin_name] = filename
if plugin_name in enabled:
try:
load_from_file(filename)
Expand Down
4 changes: 4 additions & 0 deletions pwnagotchi/plugins/default/bt-tether.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,11 @@ def on_loaded(self):
logging.info("BT-TETHER: Successfully loaded ...")
self.ready = True

def on_unload(self):
self.ui.remove_element('bluetooth')

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),
label_font=fonts.Bold, text_font=fonts.Medium))

Expand Down
4 changes: 4 additions & 0 deletions pwnagotchi/plugins/default/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ def on_webhook(self, path, request):
def on_loaded(self):
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
def on_internet_available(self, agent):
pass
Expand Down
21 changes: 12 additions & 9 deletions pwnagotchi/ui/web/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,19 @@ def mark_message(self, id, mark):

def plugins(self, name, subpath):
if name is None:
# show plugins overview
abort(404)
return render_template('plugins.html', loaded=plugins.loaded, database=plugins.database)

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:
if name in plugins.loaded and hasattr(plugins.loaded[name], 'on_webhook'):
try:
return plugins.loaded[name].on_webhook(subpath, request)
except Exception:
abort(500)
else:
abort(404)
abort(404)

# serve a message and shuts down the unit
def shutdown(self):
Expand Down
35 changes: 34 additions & 1 deletion pwnagotchi/ui/web/static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,37 @@ a.read {

p.messagebody {
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;
}
}
3 changes: 2 additions & 1 deletion pwnagotchi/ui/web/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,15 @@
( '/inbox/new', 'new', 'mail', 'New' ),
( '/inbox/profile', 'profile', 'info', 'Profile' ),
( '/inbox/peers', 'peers', 'user', 'Peers' ),
( '/plugins', 'plugins', 'grid', 'Plugins' ),
] %}
{% set active_page = active_page|default('inbox') %}

<div data-role="footer">
<div data-role="navbar" data-iconpos="left">
<ul>
{% 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>
</li>
{% endfor %}
Expand Down
39 changes: 39 additions & 0 deletions pwnagotchi/ui/web/templates/plugins.html
Original file line number Diff line number Diff line change
@@ -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 %}

0 comments on commit 8b40e94

Please sign in to comment.