new: pwngrid web client
This commit is contained in:
parent
e2be21004d
commit
df01a03a4b
pwnagotchi
grid.py
ui/web
@ -23,8 +23,10 @@ def call(path, obj=None):
|
||||
url = '%s%s' % (API_ADDRESS, path)
|
||||
if obj is None:
|
||||
r = requests.get(url, headers=None)
|
||||
else:
|
||||
elif isinstance(obj, dict):
|
||||
r = requests.post(url, headers=None, json=obj)
|
||||
else:
|
||||
r = requests.post(url, headers=None, data=obj)
|
||||
|
||||
if r.status_code != 200:
|
||||
raise Exception("(status %d) %s" % (r.status_code, r.text))
|
||||
@ -39,6 +41,10 @@ def set_advertisement_data(data):
|
||||
return call("/mesh/data", obj=data)
|
||||
|
||||
|
||||
def get_advertisement_data():
|
||||
return call("/mesh/data")
|
||||
|
||||
|
||||
def peers():
|
||||
return call("/mesh/peers")
|
||||
|
||||
@ -95,3 +101,15 @@ def report_ap(essid, bssid):
|
||||
def inbox(page=1, with_pager=False):
|
||||
obj = call("/inbox?p=%d" % page)
|
||||
return obj["messages"] if not with_pager else obj
|
||||
|
||||
|
||||
def inbox_message(id):
|
||||
return call("/inbox/%d" % int(id))
|
||||
|
||||
|
||||
def mark_message(id, mark):
|
||||
return call("/inbox/%d/%s" % (int(id), str(mark)))
|
||||
|
||||
|
||||
def send_message(to, message):
|
||||
return call("/unit/%s/inbox" % to, message)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
import base64
|
||||
import _thread
|
||||
|
||||
# https://stackoverflow.com/questions/14888799/disable-console-messages-in-flask-server
|
||||
@ -7,12 +8,15 @@ logging.getLogger('werkzeug').setLevel(logging.ERROR)
|
||||
os.environ['WERKZEUG_RUN_MAIN'] = 'true'
|
||||
|
||||
import pwnagotchi
|
||||
import pwnagotchi.grid as grid
|
||||
import pwnagotchi.ui.web as web
|
||||
from pwnagotchi import plugins
|
||||
|
||||
from flask import send_file
|
||||
from flask import request
|
||||
from flask import jsonify
|
||||
from flask import abort
|
||||
from flask import redirect
|
||||
from flask import render_template, render_template_string
|
||||
|
||||
|
||||
@ -24,6 +28,15 @@ class Handler:
|
||||
self._app.add_url_rule('/ui', 'ui', self.ui)
|
||||
self._app.add_url_rule('/shutdown', 'shutdown', self.shutdown, methods=['POST'])
|
||||
self._app.add_url_rule('/restart', 'restart', self.restart, methods=['POST'])
|
||||
|
||||
# inbox
|
||||
self._app.add_url_rule('/inbox', 'inbox', self.inbox)
|
||||
self._app.add_url_rule('/inbox/profile', 'inbox_profile', self.inbox_profile)
|
||||
self._app.add_url_rule('/inbox/<id>', 'show_message', self.show_message)
|
||||
self._app.add_url_rule('/inbox/<id>/<mark>', 'mark_message', self.mark_message)
|
||||
self._app.add_url_rule('/inbox/new', 'new_message', self.new_message)
|
||||
self._app.add_url_rule('/inbox/send', 'send_message', self.send_message, methods=['POST'])
|
||||
|
||||
# plugins
|
||||
self._app.add_url_rule('/plugins', 'plugins', self.plugins, strict_slashes=False,
|
||||
defaults={'name': None, 'subpath': None})
|
||||
@ -37,6 +50,81 @@ class Handler:
|
||||
other_mode='AUTO' if self._agent.mode == 'manual' else 'MANU',
|
||||
fingerprint=self._agent.fingerprint())
|
||||
|
||||
def inbox(self):
|
||||
page = request.args.get("p", default=1, type=int)
|
||||
inbox = {
|
||||
"pages": 1,
|
||||
"records": 0,
|
||||
"messages": []
|
||||
}
|
||||
error = None
|
||||
|
||||
try:
|
||||
inbox = grid.inbox(page, with_pager=True)
|
||||
except Exception as e:
|
||||
logging.exception('error while reading pwnmail inbox')
|
||||
error = str(e)
|
||||
|
||||
return render_template('inbox.html',
|
||||
name=pwnagotchi.name(),
|
||||
page=page,
|
||||
error=error,
|
||||
inbox=inbox)
|
||||
|
||||
def inbox_profile(self):
|
||||
data = {}
|
||||
error = None
|
||||
|
||||
try:
|
||||
data = grid.get_advertisement_data()
|
||||
except Exception as e:
|
||||
logging.exception('error while reading pwngrid data')
|
||||
error = str(e)
|
||||
|
||||
return render_template('profile.html',
|
||||
name=pwnagotchi.name(),
|
||||
fingerprint=self._agent.fingerprint(),
|
||||
data=data,
|
||||
error=error)
|
||||
|
||||
def show_message(self, id):
|
||||
message = {}
|
||||
error = None
|
||||
|
||||
try:
|
||||
message = grid.inbox_message(id)
|
||||
if message['data']:
|
||||
message['data'] = base64.b64decode(message['data']).decode("utf-8")
|
||||
except Exception as e:
|
||||
logging.exception('error while reading pwnmail message %d' % int(id))
|
||||
error = str(e)
|
||||
|
||||
return render_template('message.html',
|
||||
name=pwnagotchi.name(),
|
||||
error=error,
|
||||
message=message)
|
||||
|
||||
def new_message(self):
|
||||
to = request.args.get("to", default="")
|
||||
return render_template('new_message.html', to=to)
|
||||
|
||||
def send_message(self):
|
||||
to = request.form["to"]
|
||||
message = request.form["message"]
|
||||
error = None
|
||||
|
||||
try:
|
||||
grid.send_message(to, message)
|
||||
except Exception as e:
|
||||
error = str(e)
|
||||
|
||||
return jsonify({"error": error})
|
||||
|
||||
def mark_message(self, id, mark):
|
||||
logging.info("marking message %d as %s" % (int(id), mark))
|
||||
grid.mark_message(id, mark)
|
||||
return redirect("/inbox")
|
||||
|
||||
def plugins(self, name, subpath):
|
||||
if name is None:
|
||||
# show plugins overview
|
||||
|
@ -66,3 +66,11 @@ div.status {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
a.read {
|
||||
color: #777 !important;
|
||||
}
|
||||
|
||||
p.messagebody {
|
||||
padding: 1em;
|
||||
}
|
232
pwnagotchi/ui/web/static/js/jquery.timeago.js
Normal file
232
pwnagotchi/ui/web/static/js/jquery.timeago.js
Normal file
@ -0,0 +1,232 @@
|
||||
/**
|
||||
* Timeago is a jQuery plugin that makes it easy to support automatically
|
||||
* updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
|
||||
*
|
||||
* @name timeago
|
||||
* @version 1.6.7
|
||||
* @requires jQuery >=1.5.0 <4.0
|
||||
* @author Ryan McGeary
|
||||
* @license MIT License - http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* For usage and examples, visit:
|
||||
* http://timeago.yarp.com/
|
||||
*
|
||||
* Copyright (c) 2008-2019, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
|
||||
*/
|
||||
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof module === 'object' && typeof module.exports === 'object') {
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals
|
||||
factory(jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
$.timeago = function(timestamp) {
|
||||
if (timestamp instanceof Date) {
|
||||
return inWords(timestamp);
|
||||
} else if (typeof timestamp === "string") {
|
||||
return inWords($.timeago.parse(timestamp));
|
||||
} else if (typeof timestamp === "number") {
|
||||
return inWords(new Date(timestamp));
|
||||
} else {
|
||||
return inWords($.timeago.datetime(timestamp));
|
||||
}
|
||||
};
|
||||
var $t = $.timeago;
|
||||
|
||||
$.extend($.timeago, {
|
||||
settings: {
|
||||
refreshMillis: 60000,
|
||||
allowPast: true,
|
||||
allowFuture: false,
|
||||
localeTitle: false,
|
||||
cutoff: 0,
|
||||
autoDispose: true,
|
||||
strings: {
|
||||
prefixAgo: null,
|
||||
prefixFromNow: null,
|
||||
suffixAgo: "ago",
|
||||
suffixFromNow: "from now",
|
||||
inPast: 'any moment now',
|
||||
seconds: "less than a minute",
|
||||
minute: "about a minute",
|
||||
minutes: "%d minutes",
|
||||
hour: "about an hour",
|
||||
hours: "about %d hours",
|
||||
day: "a day",
|
||||
days: "%d days",
|
||||
month: "about a month",
|
||||
months: "%d months",
|
||||
year: "about a year",
|
||||
years: "%d years",
|
||||
wordSeparator: " ",
|
||||
numbers: []
|
||||
}
|
||||
},
|
||||
|
||||
inWords: function(distanceMillis) {
|
||||
if (!this.settings.allowPast && ! this.settings.allowFuture) {
|
||||
throw 'timeago allowPast and allowFuture settings can not both be set to false.';
|
||||
}
|
||||
|
||||
var $l = this.settings.strings;
|
||||
var prefix = $l.prefixAgo;
|
||||
var suffix = $l.suffixAgo;
|
||||
if (this.settings.allowFuture) {
|
||||
if (distanceMillis < 0) {
|
||||
prefix = $l.prefixFromNow;
|
||||
suffix = $l.suffixFromNow;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.settings.allowPast && distanceMillis >= 0) {
|
||||
return this.settings.strings.inPast;
|
||||
}
|
||||
|
||||
var seconds = Math.abs(distanceMillis) / 1000;
|
||||
var minutes = seconds / 60;
|
||||
var hours = minutes / 60;
|
||||
var days = hours / 24;
|
||||
var years = days / 365;
|
||||
|
||||
function substitute(stringOrFunction, number) {
|
||||
var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
|
||||
var value = ($l.numbers && $l.numbers[number]) || number;
|
||||
return string.replace(/%d/i, value);
|
||||
}
|
||||
|
||||
var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
|
||||
seconds < 90 && substitute($l.minute, 1) ||
|
||||
minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
|
||||
minutes < 90 && substitute($l.hour, 1) ||
|
||||
hours < 24 && substitute($l.hours, Math.round(hours)) ||
|
||||
hours < 42 && substitute($l.day, 1) ||
|
||||
days < 30 && substitute($l.days, Math.round(days)) ||
|
||||
days < 45 && substitute($l.month, 1) ||
|
||||
days < 365 && substitute($l.months, Math.round(days / 30)) ||
|
||||
years < 1.5 && substitute($l.year, 1) ||
|
||||
substitute($l.years, Math.round(years));
|
||||
|
||||
var separator = $l.wordSeparator || "";
|
||||
if ($l.wordSeparator === undefined) { separator = " "; }
|
||||
return $.trim([prefix, words, suffix].join(separator));
|
||||
},
|
||||
|
||||
parse: function(iso8601) {
|
||||
var s = $.trim(iso8601);
|
||||
s = s.replace(/\.\d+/,""); // remove milliseconds
|
||||
s = s.replace(/-/,"/").replace(/-/,"/");
|
||||
s = s.replace(/T/," ").replace(/Z/," UTC");
|
||||
s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
|
||||
s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900
|
||||
return new Date(s);
|
||||
},
|
||||
datetime: function(elem) {
|
||||
var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
|
||||
return $t.parse(iso8601);
|
||||
},
|
||||
isTime: function(elem) {
|
||||
// jQuery's `is()` doesn't play well with HTML5 in IE
|
||||
return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
|
||||
}
|
||||
});
|
||||
|
||||
// functions that can be called via $(el).timeago('action')
|
||||
// init is default when no action is given
|
||||
// functions are called with context of a single element
|
||||
var functions = {
|
||||
init: function() {
|
||||
functions.dispose.call(this);
|
||||
var refresh_el = $.proxy(refresh, this);
|
||||
refresh_el();
|
||||
var $s = $t.settings;
|
||||
if ($s.refreshMillis > 0) {
|
||||
this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
|
||||
}
|
||||
},
|
||||
update: function(timestamp) {
|
||||
var date = (timestamp instanceof Date) ? timestamp : $t.parse(timestamp);
|
||||
$(this).data('timeago', { datetime: date });
|
||||
if ($t.settings.localeTitle) {
|
||||
$(this).attr("title", date.toLocaleString());
|
||||
}
|
||||
refresh.apply(this);
|
||||
},
|
||||
updateFromDOM: function() {
|
||||
$(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
|
||||
refresh.apply(this);
|
||||
},
|
||||
dispose: function () {
|
||||
if (this._timeagoInterval) {
|
||||
window.clearInterval(this._timeagoInterval);
|
||||
this._timeagoInterval = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.timeago = function(action, options) {
|
||||
var fn = action ? functions[action] : functions.init;
|
||||
if (!fn) {
|
||||
throw new Error("Unknown function name '"+ action +"' for timeago");
|
||||
}
|
||||
// each over objects here and call the requested function
|
||||
this.each(function() {
|
||||
fn.call(this, options);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
function refresh() {
|
||||
var $s = $t.settings;
|
||||
|
||||
//check if it's still visible
|
||||
if ($s.autoDispose && !$.contains(document.documentElement,this)) {
|
||||
//stop if it has been removed
|
||||
$(this).timeago("dispose");
|
||||
return this;
|
||||
}
|
||||
|
||||
var data = prepareData(this);
|
||||
|
||||
if (!isNaN(data.datetime)) {
|
||||
if ( $s.cutoff === 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
|
||||
$(this).text(inWords(data.datetime));
|
||||
} else {
|
||||
if ($(this).attr('title').length > 0) {
|
||||
$(this).text($(this).attr('title'));
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
function prepareData(element) {
|
||||
element = $(element);
|
||||
if (!element.data("timeago")) {
|
||||
element.data("timeago", { datetime: $t.datetime(element) });
|
||||
var text = $.trim(element.text());
|
||||
if ($t.settings.localeTitle) {
|
||||
element.attr("title", element.data('timeago').datetime.toLocaleString());
|
||||
} else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
|
||||
element.attr("title", text);
|
||||
}
|
||||
}
|
||||
return element.data("timeago");
|
||||
}
|
||||
|
||||
function inWords(date) {
|
||||
return $t.inWords(distance(date));
|
||||
}
|
||||
|
||||
function distance(date) {
|
||||
return (new Date().getTime() - date.getTime());
|
||||
}
|
||||
|
||||
// fix for IE6 suckage
|
||||
document.createElement("abbr");
|
||||
document.createElement("time");
|
||||
}));
|
80
pwnagotchi/ui/web/templates/inbox.html
Normal file
80
pwnagotchi/ui/web/templates/inbox.html
Normal file
@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>{{ name }} Inbox</title>
|
||||
|
||||
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css"/>
|
||||
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
|
||||
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
|
||||
<script type="text/javascript" src="/js/jquery.timeago.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
$.mobile.ajaxEnabled = false;
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
jQuery("time.timeago").timeago();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div data-role="page">
|
||||
|
||||
{% if error %}
|
||||
<div class="error">{{ error }}</div>
|
||||
{% else %}
|
||||
{% if inbox.records == 0 %}
|
||||
<small>Inbox is empty.</small>
|
||||
{% else %}
|
||||
|
||||
<div data-role="footer">
|
||||
<div data-role="navbar">
|
||||
<ul>
|
||||
<li><a href="/inbox" id="email" class="ui-btn-active" data-icon="bars">Inbox</a></li>
|
||||
<li><a href="/inbox/new" id="new" data-icon="mail">New</a></li>
|
||||
<li><a href="/inbox/profile" id="profile" data-icon="user">Profile</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="inbox" data-role="listview" data-inset="true">
|
||||
{% for message in inbox.messages %}
|
||||
<li class="message">
|
||||
<a href="/inbox/{{ message.id }}" class="{{ 'unread' if not message.seen_at else 'read' }}">
|
||||
<h2>{{ message.sender_name }}@{{ message.sender }}</h2>
|
||||
<p>
|
||||
Received
|
||||
<time class="timeago" datetime="{{ message.created_at }}">{{ message.created_at }}</time>
|
||||
{% if message.seen_at %}, seen
|
||||
<time class="timeago" datetime="{{ message.seen_at }}">{{ message.seen_at }}</time>
|
||||
{% endif %}.
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if inbox.pages > 1 %}
|
||||
<div data-role="navbar">
|
||||
<ul>
|
||||
{% if page > 1 %}
|
||||
<li><a href="/inbox?p={{ page - 1 }}" class="ui-btn">Prev</a></li>
|
||||
{% endif %}
|
||||
{% if page < inbox.pages %}
|
||||
<li><a href="/inbox?p={{ page + 1 }}" class="ui-btn">Next</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
70
pwnagotchi/ui/web/templates/message.html
Normal file
70
pwnagotchi/ui/web/templates/message.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>Message from {{ message.sender_name }}</title>
|
||||
|
||||
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css"/>
|
||||
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
|
||||
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
|
||||
<script type="text/javascript" src="/js/jquery.timeago.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
$.mobile.ajaxEnabled = false;
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
jQuery("time.timeago").timeago();
|
||||
// mark the message as read
|
||||
jQuery.get("/inbox/{{ message.id }}/seen", function(res){
|
||||
console.log(res);
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div data-role="page">
|
||||
|
||||
{% if error %}
|
||||
<div class="error">{{ error }}</div>
|
||||
{% else %}
|
||||
|
||||
<div data-role="footer">
|
||||
<div data-role="navbar">
|
||||
<ul>
|
||||
<li><a href="/inbox" id="email" data-icon="back">Back</a></li>
|
||||
<li><a href="/inbox/new?to={{ message.sender }}" id="reply" data-icon="edit">Reply</a></li>
|
||||
<li><a href="/inbox/{{ message.id }}/deleted" id="delete" data-icon="delete"
|
||||
onclick="return confirm('Are you sure?')">Delete</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="messagebody">
|
||||
<span style="color: #888">
|
||||
Message from {{ message.sender_name }}@{{ message.sender }}, received
|
||||
<time class="timeago" datetime="{{ message.created_at }}">{{ message.created_at }}</time>
|
||||
{% if message.seen_at %}, seen
|
||||
<time class="timeago" datetime="{{ message.seen_at }}">{{ message.seen_at }}</time>{% endif %}.</span>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
{% if message.data %}
|
||||
<span style="white-space: pre-line">{{ message.data }}</span>
|
||||
{% else %}
|
||||
<small>This message is empty.</small>
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
81
pwnagotchi/ui/web/templates/new_message.html
Normal file
81
pwnagotchi/ui/web/templates/new_message.html
Normal file
@ -0,0 +1,81 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
{% if to %}
|
||||
<title>Reply to {{ to }}</title>
|
||||
{% else %}
|
||||
<title>New Message</title>
|
||||
{% endif %}
|
||||
|
||||
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css"/>
|
||||
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
|
||||
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
|
||||
<script type="text/javascript" src="/js/jquery.timeago.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
$.mobile.ajaxEnabled = false;
|
||||
|
||||
$(function(){
|
||||
$("#message_form").submit(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var form = $(this);
|
||||
var url = form.attr('action');
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: form.serialize(),
|
||||
success: function(data) {
|
||||
if( data.error ) {
|
||||
if( data.error.indexOf('404') != -1 )
|
||||
alert('Fingerprint not found.');
|
||||
else if( data.error.indexOf('aborted') != -1 )
|
||||
alert('Empty or invalid message.');
|
||||
else
|
||||
alert(data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
alert("Message sent!");
|
||||
document.location.href = "/inbox";
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div data-role="page">
|
||||
|
||||
<div data-role="footer">
|
||||
<div data-role="navbar">
|
||||
<ul>
|
||||
<li><a href="/inbox" id="email" data-icon="back">Back</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 1em">
|
||||
<form id="message_form" method="POST" action="/inbox/send">
|
||||
<label for="to">To:</label>
|
||||
<input type="text" name="to" id="to" value="{{ to }}">
|
||||
|
||||
<label for="message">Message:</label>
|
||||
<textarea cols="40" rows="8" name="message" id="message"></textarea>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<input type="submit" class="button" value="Send"/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
46
pwnagotchi/ui/web/templates/profile.html
Normal file
46
pwnagotchi/ui/web/templates/profile.html
Normal file
@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>Profile</title>
|
||||
|
||||
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css"/>
|
||||
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
|
||||
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
|
||||
<script type="text/javascript" src="/js/jquery.timeago.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
$.mobile.ajaxEnabled = false;
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div data-role="page">
|
||||
|
||||
<div data-role="footer">
|
||||
<div data-role="navbar">
|
||||
<ul>
|
||||
<li><a href="/inbox" id="email" data-icon="back">Back</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 1em">
|
||||
<label for="name">Name</label>
|
||||
<h4 id="name">{{ name }}</h4>
|
||||
|
||||
<label for="fingerprint">Fingerprint</label>
|
||||
<h4 id="fingerprint">{{ fingerprint }}</h4>
|
||||
|
||||
<label for="data">Data</label>
|
||||
<pre><code id="data">{{ data }}</code></pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user