mirror of
https://github.com/markqvist/NomadNet.git
synced 2025-12-17 14:54:26 +01:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf7004fd0f | ||
|
|
8109bce5a3 | ||
|
|
2fe2f6bb49 | ||
|
|
10d6d737b9 | ||
|
|
1b561a88ba | ||
|
|
1f76897855 | ||
|
|
e266719e2c | ||
|
|
1a84e0c019 | ||
|
|
c134ee180a | ||
|
|
d30f405fd5 | ||
|
|
1378a3f6dd | ||
|
|
eb6dd1729b | ||
|
|
4f483e482a | ||
|
|
1d3538a604 | ||
|
|
307c522074 | ||
|
|
4ec1a4979f | ||
|
|
2d6cd8e33f |
@@ -97,7 +97,7 @@ class Node:
|
|||||||
for directory in directories:
|
for directory in directories:
|
||||||
self.scan_files(base_path+"/"+directory)
|
self.scan_files(base_path+"/"+directory)
|
||||||
|
|
||||||
def serve_page(self, path, data, request_id, remote_identity, requested_at):
|
def serve_page(self, path, data, request_id, link_id, remote_identity, requested_at):
|
||||||
RNS.log("Page request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_VERBOSE)
|
RNS.log("Page request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_VERBOSE)
|
||||||
try:
|
try:
|
||||||
self.app.peer_settings["served_page_requests"] += 1
|
self.app.peer_settings["served_page_requests"] += 1
|
||||||
@@ -152,7 +152,20 @@ class Node:
|
|||||||
if request_allowed:
|
if request_allowed:
|
||||||
RNS.log("Serving page: "+file_path, RNS.LOG_VERBOSE)
|
RNS.log("Serving page: "+file_path, RNS.LOG_VERBOSE)
|
||||||
if os.access(file_path, os.X_OK):
|
if os.access(file_path, os.X_OK):
|
||||||
generated = subprocess.run([file_path], stdout=subprocess.PIPE)
|
env_map = {}
|
||||||
|
if "PATH" in os.environ:
|
||||||
|
env_map["PATH"] = os.environ["PATH"]
|
||||||
|
if link_id != None:
|
||||||
|
env_map["link_id"] = RNS.hexrep(link_id, delimit=False)
|
||||||
|
if remote_identity != None:
|
||||||
|
env_map["remote_identity"] = RNS.hexrep(remote_identity.hash, delimit=False)
|
||||||
|
|
||||||
|
if data != None and isinstance(data, dict):
|
||||||
|
for e in data:
|
||||||
|
if isinstance(e, str) and (e.startswith("field_") or e.startswith("var_")):
|
||||||
|
env_map[e] = data[e]
|
||||||
|
|
||||||
|
generated = subprocess.run([file_path], stdout=subprocess.PIPE, env=env_map)
|
||||||
return generated.stdout
|
return generated.stdout
|
||||||
else:
|
else:
|
||||||
fh = open(file_path, "rb")
|
fh = open(file_path, "rb")
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.3.3"
|
__version__ = "0.3.5"
|
||||||
|
|||||||
43
nomadnet/examples/various/input_fields.py
Normal file
43
nomadnet/examples/various/input_fields.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
env_string = ""
|
||||||
|
for e in os.environ:
|
||||||
|
env_string += "{}={}\n".format(e, os.environ[e])
|
||||||
|
|
||||||
|
template = """>Fields and Submitting Data
|
||||||
|
|
||||||
|
Nomad Network let's you use simple input fields for submitting data to node-side applications. Submitted data, along with other session variables will be available to the node-side script / program as environment variables. This page contains a few examples.
|
||||||
|
|
||||||
|
>> Read Environment Variables
|
||||||
|
|
||||||
|
{@ENV}
|
||||||
|
>>Examples of Fields and Submissions
|
||||||
|
|
||||||
|
The following section contains a simple set of fields, and a few different links that submit the field data in different ways.
|
||||||
|
|
||||||
|
-=
|
||||||
|
|
||||||
|
An input field : `B444`<username`Entered data>`b
|
||||||
|
|
||||||
|
An masked field : `B444`<!|password`Value of Field>`b
|
||||||
|
|
||||||
|
An small field : `B444`<8|small`test>`b, and some more text.
|
||||||
|
|
||||||
|
Two fields : `B444`<8|one`One>`b `B444`<8|two`Two>`b
|
||||||
|
|
||||||
|
The data can be `!`[submitted`:/page/input_fields.mu`username|two]`!.
|
||||||
|
|
||||||
|
You can `!`[submit`:/page/input_fields.mu`one|password|small]`! other fields, or just `!`[a single one`:/page/input_fields.mu`username]`!
|
||||||
|
|
||||||
|
Or simply `!`[submit them all`:/page/input_fields.mu`*]`!.
|
||||||
|
|
||||||
|
Submission links can also `!`[include pre-configured variables`:/page/input_fields.mu`username|two|entitiy_id=4611|action=view]`!.
|
||||||
|
|
||||||
|
Or take all fields and `!`[pre-configured variables`:/page/input_fields.mu`*|entitiy_id=4611|action=view]`!.
|
||||||
|
|
||||||
|
Or only `!`[pre-configured variables`:/page/input_fields.mu`entitiy_id=4688|task=something]`!
|
||||||
|
|
||||||
|
-=
|
||||||
|
|
||||||
|
"""
|
||||||
|
print(template.replace("{@ENV}", env_string))
|
||||||
@@ -217,6 +217,8 @@ class TextUI:
|
|||||||
def unhandled_input(self, key):
|
def unhandled_input(self, key):
|
||||||
if key == "ctrl q":
|
if key == "ctrl q":
|
||||||
raise urwid.ExitMainLoop
|
raise urwid.ExitMainLoop
|
||||||
|
elif key == "ctrl e":
|
||||||
|
pass
|
||||||
|
|
||||||
def display_main(self, loop, user_data):
|
def display_main(self, loop, user_data):
|
||||||
self.loop.widget = self.main_display.widget
|
self.loop.widget = self.main_display.widget
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ class Browser:
|
|||||||
self.aspects = aspects
|
self.aspects = aspects
|
||||||
self.destination_hash = destination_hash
|
self.destination_hash = destination_hash
|
||||||
self.path = path
|
self.path = path
|
||||||
|
self.request_data = None
|
||||||
self.timeout = Browser.DEFAULT_TIMEOUT
|
self.timeout = Browser.DEFAULT_TIMEOUT
|
||||||
self.last_keypress = None
|
self.last_keypress = None
|
||||||
|
|
||||||
@@ -161,7 +162,42 @@ class Browser:
|
|||||||
else:
|
else:
|
||||||
return destination_type
|
return destination_type
|
||||||
|
|
||||||
def handle_link(self, link_target):
|
def handle_link(self, link_target, link_data = None):
|
||||||
|
request_data = None
|
||||||
|
if link_data != None:
|
||||||
|
link_fields = []
|
||||||
|
request_data = {}
|
||||||
|
all_fields = True if "*" in link_data else False
|
||||||
|
|
||||||
|
for e in link_data:
|
||||||
|
if "=" in e:
|
||||||
|
c = e.split("=")
|
||||||
|
if len(c) == 2:
|
||||||
|
request_data["var_"+str(c[0])] = str(c[1])
|
||||||
|
else:
|
||||||
|
link_fields.append(e)
|
||||||
|
|
||||||
|
def recurse_down(w):
|
||||||
|
target = None
|
||||||
|
if isinstance(w, list):
|
||||||
|
for t in w:
|
||||||
|
recurse_down(t)
|
||||||
|
elif isinstance(w, tuple):
|
||||||
|
for t in w:
|
||||||
|
recurse_down(t)
|
||||||
|
elif hasattr(w, "contents"):
|
||||||
|
recurse_down(w.contents)
|
||||||
|
elif hasattr(w, "original_widget"):
|
||||||
|
recurse_down(w.original_widget)
|
||||||
|
elif hasattr(w, "_original_widget"):
|
||||||
|
recurse_down(w._original_widget)
|
||||||
|
else:
|
||||||
|
if hasattr(w, "field_name") and (all_fields or w.field_name in link_fields):
|
||||||
|
request_data["field_"+w.field_name] = w.get_edit_text()
|
||||||
|
|
||||||
|
recurse_down(self.attr_maps)
|
||||||
|
RNS.log("Including request data: "+str(request_data), RNS.LOG_DEBUG)
|
||||||
|
|
||||||
components = link_target.split("@")
|
components = link_target.split("@")
|
||||||
destination_type = None
|
destination_type = None
|
||||||
|
|
||||||
@@ -177,7 +213,7 @@ class Browser:
|
|||||||
RNS.log("Browser handling link to: "+str(link_target), RNS.LOG_DEBUG)
|
RNS.log("Browser handling link to: "+str(link_target), RNS.LOG_DEBUG)
|
||||||
self.browser_footer = urwid.Text("Opening link to: "+str(link_target))
|
self.browser_footer = urwid.Text("Opening link to: "+str(link_target))
|
||||||
try:
|
try:
|
||||||
self.retrieve_url(link_target)
|
self.retrieve_url(link_target, request_data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.browser_footer = urwid.Text("Could not open link: "+str(e))
|
self.browser_footer = urwid.Text("Could not open link: "+str(e))
|
||||||
self.frame.contents["footer"] = (self.browser_footer, self.frame.options())
|
self.frame.contents["footer"] = (self.browser_footer, self.frame.options())
|
||||||
@@ -351,6 +387,7 @@ class Browser:
|
|||||||
if self.link != None:
|
if self.link != None:
|
||||||
self.link.teardown()
|
self.link.teardown()
|
||||||
|
|
||||||
|
self.request_data = None
|
||||||
self.attr_maps = []
|
self.attr_maps = []
|
||||||
self.status = Browser.DISCONECTED
|
self.status = Browser.DISCONECTED
|
||||||
self.response_progress = 0
|
self.response_progress = 0
|
||||||
@@ -365,7 +402,7 @@ class Browser:
|
|||||||
self.update_display()
|
self.update_display()
|
||||||
|
|
||||||
|
|
||||||
def retrieve_url(self, url):
|
def retrieve_url(self, url, request_data = None):
|
||||||
self.previous_destination_hash = self.destination_hash
|
self.previous_destination_hash = self.destination_hash
|
||||||
self.previous_path = self.path
|
self.previous_path = self.path
|
||||||
|
|
||||||
@@ -418,6 +455,7 @@ class Browser:
|
|||||||
else:
|
else:
|
||||||
self.set_destination_hash(destination_hash)
|
self.set_destination_hash(destination_hash)
|
||||||
self.set_path(path)
|
self.set_path(path)
|
||||||
|
self.set_request_data(request_data)
|
||||||
self.load_page()
|
self.load_page()
|
||||||
|
|
||||||
def set_destination_hash(self, destination_hash):
|
def set_destination_hash(self, destination_hash):
|
||||||
@@ -431,6 +469,8 @@ class Browser:
|
|||||||
def set_path(self, path):
|
def set_path(self, path):
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
|
def set_request_data(self, request_data):
|
||||||
|
self.request_data = request_data
|
||||||
|
|
||||||
def set_timeout(self, timeout):
|
def set_timeout(self, timeout):
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
@@ -637,7 +677,11 @@ class Browser:
|
|||||||
|
|
||||||
|
|
||||||
def load_page(self):
|
def load_page(self):
|
||||||
|
if self.request_data == None:
|
||||||
cached = self.get_cached(self.current_url())
|
cached = self.get_cached(self.current_url())
|
||||||
|
else:
|
||||||
|
cached = None
|
||||||
|
|
||||||
if cached:
|
if cached:
|
||||||
self.status = Browser.DONE
|
self.status = Browser.DONE
|
||||||
self.page_data = cached
|
self.page_data = cached
|
||||||
@@ -671,7 +715,15 @@ class Browser:
|
|||||||
page_data = b"The requested local page did not exist in the file system"
|
page_data = b"The requested local page did not exist in the file system"
|
||||||
if os.path.isfile(page_path):
|
if os.path.isfile(page_path):
|
||||||
if os.access(page_path, os.X_OK):
|
if os.access(page_path, os.X_OK):
|
||||||
generated = subprocess.run([page_path], stdout=subprocess.PIPE)
|
if self.request_data != None:
|
||||||
|
env_map = self.request_data
|
||||||
|
else:
|
||||||
|
env_map = {}
|
||||||
|
|
||||||
|
if "PATH" in os.environ:
|
||||||
|
env_map["PATH"] = os.environ["PATH"]
|
||||||
|
|
||||||
|
generated = subprocess.run([page_path], stdout=subprocess.PIPE, env=env_map)
|
||||||
page_data = generated.stdout
|
page_data = generated.stdout
|
||||||
else:
|
else:
|
||||||
file = open(page_path, "rb")
|
file = open(page_path, "rb")
|
||||||
@@ -758,7 +810,7 @@ class Browser:
|
|||||||
self.update_display()
|
self.update_display()
|
||||||
receipt = self.link.request(
|
receipt = self.link.request(
|
||||||
self.path,
|
self.path,
|
||||||
data = None,
|
data = self.request_data,
|
||||||
response_callback = self.response_received,
|
response_callback = self.response_received,
|
||||||
failed_callback = self.request_failed,
|
failed_callback = self.request_failed,
|
||||||
progress_callback = self.response_progressed
|
progress_callback = self.response_progressed
|
||||||
|
|||||||
@@ -110,7 +110,10 @@ class ConversationsDisplay():
|
|||||||
|
|
||||||
def delete_selected_conversation(self):
|
def delete_selected_conversation(self):
|
||||||
self.dialog_open = True
|
self.dialog_open = True
|
||||||
source_hash = self.ilb.get_selected_item().source_hash
|
item = self.ilb.get_selected_item()
|
||||||
|
if item == None:
|
||||||
|
return
|
||||||
|
source_hash = item.source_hash
|
||||||
|
|
||||||
def dismiss_dialog(sender):
|
def dismiss_dialog(sender):
|
||||||
self.update_conversation_list()
|
self.update_conversation_list()
|
||||||
@@ -140,7 +143,10 @@ class ConversationsDisplay():
|
|||||||
def edit_selected_in_directory(self):
|
def edit_selected_in_directory(self):
|
||||||
g = self.app.ui.glyphs
|
g = self.app.ui.glyphs
|
||||||
self.dialog_open = True
|
self.dialog_open = True
|
||||||
source_hash_text = self.ilb.get_selected_item().source_hash
|
item = self.ilb.get_selected_item()
|
||||||
|
if item == None:
|
||||||
|
return
|
||||||
|
source_hash_text = item.source_hash
|
||||||
display_name = self.ilb.get_selected_item().display_name
|
display_name = self.ilb.get_selected_item().display_name
|
||||||
if display_name == None:
|
if display_name == None:
|
||||||
display_name = ""
|
display_name = ""
|
||||||
|
|||||||
@@ -322,9 +322,12 @@ You can control how long a peer will cache your pages by including the cache hea
|
|||||||
|
|
||||||
You can use a preprocessor such as PHP, bash, Python (or whatever you prefer) to generate dynamic pages. To do so, just set executable permissions on the relevant page file, and be sure to include the interpreter at the beginning of the file, for example `!#!/usr/bin/python3`!.
|
You can use a preprocessor such as PHP, bash, Python (or whatever you prefer) to generate dynamic pages. To do so, just set executable permissions on the relevant page file, and be sure to include the interpreter at the beginning of the file, for example `!#!/usr/bin/python3`!.
|
||||||
|
|
||||||
|
Data from fields and link variables will be passed to these scipts or programs as environment variables, and can simply be read by any method for acessing such.
|
||||||
|
|
||||||
In the `!examples`! directory, you can find various small examples for the use of this feature. The currently included examples are:
|
In the `!examples`! directory, you can find various small examples for the use of this feature. The currently included examples are:
|
||||||
|
|
||||||
- A messageboard that receives messages over LXMF, contributed by trippcheng
|
- A messageboard that receives messages over LXMF, contributed by trippcheng
|
||||||
|
- A simple demonstration on how to create fields and read entered data in node-side scripts
|
||||||
|
|
||||||
By default, you can find the examples in `!~/.nomadnetwork/examples`!. If you build something neat, that you feel would fit here, you are more than welcome to contribute it.
|
By default, you can find the examples in `!~/.nomadnetwork/examples`!. If you build something neat, that you feel would fit here, you are more than welcome to contribute it.
|
||||||
|
|
||||||
@@ -1002,6 +1005,94 @@ Here is `F00f`_`[a more visible link`72914442a3689add83a09a767963f57c:/page/inde
|
|||||||
|
|
||||||
When links like these are displayed in the built-in browser, clicking on them or activating them using the keyboard will cause the browser to load the specified URL.
|
When links like these are displayed in the built-in browser, clicking on them or activating them using the keyboard will cause the browser to load the specified URL.
|
||||||
|
|
||||||
|
>Fields & Requests
|
||||||
|
|
||||||
|
Nomad Network let's you use simple input fields for submitting data to node-side applications. Submitted data, along with other session variables will be available to the node-side script / program as environment variables.
|
||||||
|
|
||||||
|
>>Request Links
|
||||||
|
|
||||||
|
Links can contain request variables and a list of fields to submit to the node-side application. You can include all fields on the page, only specific ones, and any number of request variables. To simply submit all fields on a page to a specified node-side page, create a link like this:
|
||||||
|
|
||||||
|
`Faaa
|
||||||
|
`=
|
||||||
|
`[Submit Fields`:/page/fields.mu`*]
|
||||||
|
`=
|
||||||
|
``
|
||||||
|
|
||||||
|
Note the `!*`! following the extra `!\``! at the end of the path. This `!*`! denotes `*all fields`*. You can also specify a list of fields to include:
|
||||||
|
|
||||||
|
`Faaa
|
||||||
|
`=
|
||||||
|
`[Submit Fields`:/page/fields.mu`username|auth_token]
|
||||||
|
`=
|
||||||
|
``
|
||||||
|
|
||||||
|
If you want to include pre-set variables, you can do it like this:
|
||||||
|
|
||||||
|
`Faaa
|
||||||
|
`=
|
||||||
|
`[Query the System`:/page/fields.mu`username|auth_token|action=view|amount=64]
|
||||||
|
`=
|
||||||
|
``
|
||||||
|
|
||||||
|
>> Fields
|
||||||
|
|
||||||
|
Here's an example of creating a field. We'll create a field named `!user_input`! and fill it with the text `!Pre-defined data`!. Note that we are using background color tags to make the field more visible to the user:
|
||||||
|
|
||||||
|
`Faaa
|
||||||
|
`=
|
||||||
|
A simple input field: `B444`<user_input`Pre-defined data>`b
|
||||||
|
`=
|
||||||
|
``
|
||||||
|
|
||||||
|
You must always set a field `*name`*, but you can of course omit the pre-defined value of the field:
|
||||||
|
|
||||||
|
`Faaa
|
||||||
|
`=
|
||||||
|
An empty input field: `B444`<demo_empty`>`b
|
||||||
|
`=
|
||||||
|
``
|
||||||
|
|
||||||
|
You can set the size of the field like this:
|
||||||
|
|
||||||
|
`Faaa
|
||||||
|
`=
|
||||||
|
A sized input field: `B444`<16|with_size`>`b
|
||||||
|
`=
|
||||||
|
``
|
||||||
|
|
||||||
|
It is possible to mask fields, for example for use with passwords and similar:
|
||||||
|
|
||||||
|
`Faaa
|
||||||
|
`=
|
||||||
|
A masked input field: `B444`<!|masked_demo`hidden text>`b
|
||||||
|
`=
|
||||||
|
``
|
||||||
|
|
||||||
|
And you can of course control all parameters at the same time:
|
||||||
|
|
||||||
|
`Faaa
|
||||||
|
`=
|
||||||
|
Full control: `B444`<!32|all_options`hidden text>`b
|
||||||
|
`=
|
||||||
|
``
|
||||||
|
|
||||||
|
Collecting the above markup produces the following output:
|
||||||
|
|
||||||
|
`Faaa`B333
|
||||||
|
|
||||||
|
A simple input field: `B444`<user_input`Pre-defined data>`B333
|
||||||
|
|
||||||
|
An empty input field: `B444`<demo_empty`>`B333
|
||||||
|
|
||||||
|
A sized input field: `B444`<16|with_size`>`B333
|
||||||
|
|
||||||
|
A masked input field: `B444`<!|masked_demo`hidden text>`B333
|
||||||
|
|
||||||
|
Full control: `B444`<!32|all_options`hidden text>`B333
|
||||||
|
|
||||||
|
``
|
||||||
|
|
||||||
>Comments
|
>Comments
|
||||||
|
|
||||||
You can insert comments that will not be displayed in the output by starting a line with the # character.
|
You can insert comments that will not be displayed in the output by starting a line with the # character.
|
||||||
@@ -1034,7 +1125,7 @@ To display literal content, for example source-code, or blocks of text that shou
|
|||||||
`=
|
`=
|
||||||
'''
|
'''
|
||||||
TOPIC_MARKUP += TOPIC_MARKUP.replace("`=", "\\`=") + "[ micron source for document goes here, we don't want infinite recursion now, do we? ]\n\\`="
|
TOPIC_MARKUP += TOPIC_MARKUP.replace("`=", "\\`=") + "[ micron source for document goes here, we don't want infinite recursion now, do we? ]\n\\`="
|
||||||
TOPIC_MARKUP += "\n`=\n\n>Closing Remarks\n\nIf you made it all the way here, you should be well equipped to write documents and pages using micron. Thank you for staying with me.\n\n`c\U0001F332\n"
|
TOPIC_MARKUP += "\n`=\n\n>Closing Remarks\n\nIf you made it all the way here, you should be well equipped to write documents, pages and applications using micron and Nomad Network. Thank you for staying with me.\n\n`c\U0001F332\n"
|
||||||
|
|
||||||
|
|
||||||
TOPICS = {
|
TOPICS = {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import nomadnet
|
import nomadnet
|
||||||
import urwid
|
import urwid
|
||||||
|
import random
|
||||||
import time
|
import time
|
||||||
from urwid.util import is_mouse_press
|
from urwid.util import is_mouse_press
|
||||||
from urwid.text_layout import calc_coords
|
from urwid.text_layout import calc_coords
|
||||||
@@ -62,11 +63,12 @@ def markup_to_attrmaps(markup, url_delegate = None):
|
|||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if len(line) > 0:
|
if len(line) > 0:
|
||||||
display_widget = parse_line(line, state, url_delegate)
|
display_widgets = parse_line(line, state, url_delegate)
|
||||||
else:
|
else:
|
||||||
display_widget = urwid.Text("")
|
display_widgets = [urwid.Text("")]
|
||||||
|
|
||||||
if display_widget != None:
|
if display_widgets != None and len(display_widgets) != 0:
|
||||||
|
for display_widget in display_widgets:
|
||||||
attrmap = urwid.AttrMap(display_widget, make_style(state))
|
attrmap = urwid.AttrMap(display_widget, make_style(state))
|
||||||
attrmaps.append(attrmap)
|
attrmaps.append(attrmap)
|
||||||
|
|
||||||
@@ -124,7 +126,7 @@ def parse_line(line, state, url_delegate):
|
|||||||
|
|
||||||
heading_style = first_style
|
heading_style = first_style
|
||||||
output.insert(0, " "*left_indent(state))
|
output.insert(0, " "*left_indent(state))
|
||||||
return urwid.AttrMap(urwid.Text(output, align=state["align"]), heading_style)
|
return [urwid.AttrMap(urwid.Text(output, align=state["align"]), heading_style)]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@@ -137,22 +139,56 @@ def parse_line(line, state, url_delegate):
|
|||||||
else:
|
else:
|
||||||
divider_char = "\u2500"
|
divider_char = "\u2500"
|
||||||
if state["depth"] == 0:
|
if state["depth"] == 0:
|
||||||
return urwid.Divider(divider_char)
|
return [urwid.Divider(divider_char)]
|
||||||
else:
|
else:
|
||||||
return urwid.Padding(urwid.Divider(divider_char), left=left_indent(state), right=right_indent(state))
|
return [urwid.Padding(urwid.Divider(divider_char), left=left_indent(state), right=right_indent(state))]
|
||||||
|
|
||||||
output = make_output(state, line, url_delegate)
|
output = make_output(state, line, url_delegate)
|
||||||
|
|
||||||
if output != None:
|
if output != None:
|
||||||
|
text_only = True
|
||||||
|
for o in output:
|
||||||
|
if not isinstance(o, tuple):
|
||||||
|
text_only = False
|
||||||
|
break
|
||||||
|
|
||||||
|
if not text_only:
|
||||||
|
widgets = []
|
||||||
|
for o in output:
|
||||||
|
if isinstance(o, tuple):
|
||||||
|
if url_delegate != None:
|
||||||
|
tw = LinkableText(o, align=state["align"], delegate=url_delegate)
|
||||||
|
tw.in_columns = True
|
||||||
|
else:
|
||||||
|
tw = urwid.Text(o, align=state["align"])
|
||||||
|
|
||||||
|
widgets.append(("pack", tw))
|
||||||
|
else:
|
||||||
|
if o["type"] == "field":
|
||||||
|
fw = o["width"]
|
||||||
|
fd = o["data"]
|
||||||
|
fn = o["name"]
|
||||||
|
fs = o["style"]
|
||||||
|
fmask = "*" if o["masked"] else None
|
||||||
|
f = urwid.Edit(caption="", edit_text=fd, align=state["align"], multiline=True, mask=fmask)
|
||||||
|
f.field_name = fn
|
||||||
|
fa = urwid.AttrMap(f, fs)
|
||||||
|
widgets.append((fw, fa))
|
||||||
|
|
||||||
|
columns_widget = urwid.Columns(widgets, dividechars=0)
|
||||||
|
text_widget = columns_widget
|
||||||
|
# text_widget = urwid.Text("<"+output+">", align=state["align"])
|
||||||
|
|
||||||
|
else:
|
||||||
if url_delegate != None:
|
if url_delegate != None:
|
||||||
text_widget = LinkableText(output, align=state["align"], delegate=url_delegate)
|
text_widget = LinkableText(output, align=state["align"], delegate=url_delegate)
|
||||||
else:
|
else:
|
||||||
text_widget = urwid.Text(output, align=state["align"])
|
text_widget = urwid.Text(output, align=state["align"])
|
||||||
|
|
||||||
if state["depth"] == 0:
|
if state["depth"] == 0:
|
||||||
return text_widget
|
return [text_widget]
|
||||||
else:
|
else:
|
||||||
return urwid.Padding(text_widget, left=left_indent(state), right=right_indent(state))
|
return [urwid.Padding(text_widget, left=left_indent(state), right=right_indent(state))]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@@ -423,6 +459,54 @@ def make_output(state, line, url_delegate):
|
|||||||
elif c == "a":
|
elif c == "a":
|
||||||
state["align"] = state["default_align"]
|
state["align"] = state["default_align"]
|
||||||
|
|
||||||
|
elif c == "<":
|
||||||
|
try:
|
||||||
|
field_name = None
|
||||||
|
field_name_end = line[i:].find("`")
|
||||||
|
if field_name_end == -1:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
field_name = line[i+1:i+field_name_end]
|
||||||
|
field_name_skip = len(field_name)
|
||||||
|
field_masked = False
|
||||||
|
field_width = 24
|
||||||
|
|
||||||
|
if "|" in field_name:
|
||||||
|
f_components = field_name.split("|")
|
||||||
|
field_flags = f_components[0]
|
||||||
|
field_name = f_components[1]
|
||||||
|
if "!" in field_flags:
|
||||||
|
field_flags = field_flags.replace("!", "")
|
||||||
|
field_masked = True
|
||||||
|
if len(field_flags) > 0:
|
||||||
|
field_width = min(int(field_flags), 256)
|
||||||
|
|
||||||
|
def sr():
|
||||||
|
return "@{"+str(random.randint(1000,9999))+"}"
|
||||||
|
rsg = sr()
|
||||||
|
while rsg in line[i+field_name_end:]:
|
||||||
|
rsg = sr()
|
||||||
|
lr = line[i+field_name_end:].replace("\\>", rsg)
|
||||||
|
endpos = lr.find(">")
|
||||||
|
|
||||||
|
if endpos == -1:
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
field_data = lr[1:endpos].replace(rsg, "\\>")
|
||||||
|
skip = len(field_data)+field_name_skip+2
|
||||||
|
field_data = field_data.replace("\\>", ">")
|
||||||
|
output.append({
|
||||||
|
"type":"field",
|
||||||
|
"name": field_name,
|
||||||
|
"width": field_width,
|
||||||
|
"masked": field_masked,
|
||||||
|
"data": field_data,
|
||||||
|
"style": make_style(state)
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
elif c == "[":
|
elif c == "[":
|
||||||
endpos = line[i:].find("]")
|
endpos = line[i:].find("]")
|
||||||
if endpos == -1:
|
if endpos == -1:
|
||||||
@@ -434,13 +518,20 @@ def make_output(state, line, url_delegate):
|
|||||||
link_components = link_data.split("`")
|
link_components = link_data.split("`")
|
||||||
if len(link_components) == 1:
|
if len(link_components) == 1:
|
||||||
link_label = ""
|
link_label = ""
|
||||||
|
link_fields = ""
|
||||||
link_url = link_data
|
link_url = link_data
|
||||||
elif len(link_components) == 2:
|
elif len(link_components) == 2:
|
||||||
link_label = link_components[0]
|
link_label = link_components[0]
|
||||||
link_url = link_components[1]
|
link_url = link_components[1]
|
||||||
|
link_fields = ""
|
||||||
|
elif len(link_components) == 3:
|
||||||
|
link_label = link_components[0]
|
||||||
|
link_url = link_components[1]
|
||||||
|
link_fields = link_components[2]
|
||||||
else:
|
else:
|
||||||
link_url = ""
|
link_url = ""
|
||||||
link_label = ""
|
link_label = ""
|
||||||
|
link_fields = ""
|
||||||
|
|
||||||
if len(link_url) != 0:
|
if len(link_url) != 0:
|
||||||
if link_label == "":
|
if link_label == "":
|
||||||
@@ -468,6 +559,11 @@ def make_output(state, line, url_delegate):
|
|||||||
|
|
||||||
if url_delegate != None:
|
if url_delegate != None:
|
||||||
linkspec = LinkSpec(link_url, orig_spec)
|
linkspec = LinkSpec(link_url, orig_spec)
|
||||||
|
if link_fields != "":
|
||||||
|
lf = link_fields.split("|")
|
||||||
|
if len(lf) > 0:
|
||||||
|
linkspec.link_fields = lf
|
||||||
|
|
||||||
output.append((linkspec, link_label))
|
output.append((linkspec, link_label))
|
||||||
else:
|
else:
|
||||||
output.append(make_part(state, link_label))
|
output.append(make_part(state, link_label))
|
||||||
@@ -510,6 +606,7 @@ def make_output(state, line, url_delegate):
|
|||||||
class LinkSpec(urwid.AttrSpec):
|
class LinkSpec(urwid.AttrSpec):
|
||||||
def __init__(self, link_target, orig_spec):
|
def __init__(self, link_target, orig_spec):
|
||||||
self.link_target = link_target
|
self.link_target = link_target
|
||||||
|
self.link_fields = None
|
||||||
|
|
||||||
urwid.AttrSpec.__init__(self, orig_spec.foreground, orig_spec.background)
|
urwid.AttrSpec.__init__(self, orig_spec.foreground, orig_spec.background)
|
||||||
|
|
||||||
@@ -525,12 +622,13 @@ class LinkableText(urwid.Text):
|
|||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
self._cursor_position = 0
|
self._cursor_position = 0
|
||||||
self.key_timeout = 3
|
self.key_timeout = 3
|
||||||
|
self.in_columns = False
|
||||||
if self.delegate != None:
|
if self.delegate != None:
|
||||||
self.delegate.last_keypress = 0
|
self.delegate.last_keypress = 0
|
||||||
|
|
||||||
def handle_link(self, link_target):
|
def handle_link(self, link_target, link_fields):
|
||||||
if self.delegate != None:
|
if self.delegate != None:
|
||||||
self.delegate.handle_link(link_target)
|
self.delegate.handle_link(link_target, link_fields)
|
||||||
|
|
||||||
def find_next_part_pos(self, pos, part_positions):
|
def find_next_part_pos(self, pos, part_positions):
|
||||||
for position in part_positions:
|
for position in part_positions:
|
||||||
@@ -588,7 +686,7 @@ class LinkableText(urwid.Text):
|
|||||||
item = self.find_item_at_pos(self._cursor_position)
|
item = self.find_item_at_pos(self._cursor_position)
|
||||||
if item != None:
|
if item != None:
|
||||||
if isinstance(item, LinkSpec):
|
if isinstance(item, LinkSpec):
|
||||||
self.handle_link(item.link_target)
|
self.handle_link(item.link_target, item.link_fields)
|
||||||
|
|
||||||
elif key == "up":
|
elif key == "up":
|
||||||
self._cursor_position = 0
|
self._cursor_position = 0
|
||||||
@@ -601,7 +699,11 @@ class LinkableText(urwid.Text):
|
|||||||
elif key == "right":
|
elif key == "right":
|
||||||
old = self._cursor_position
|
old = self._cursor_position
|
||||||
self._cursor_position = self.find_next_part_pos(self._cursor_position, part_positions)
|
self._cursor_position = self.find_next_part_pos(self._cursor_position, part_positions)
|
||||||
|
|
||||||
if self._cursor_position == old:
|
if self._cursor_position == old:
|
||||||
|
if self.in_columns:
|
||||||
|
return "right"
|
||||||
|
else:
|
||||||
self._cursor_position = 0
|
self._cursor_position = 0
|
||||||
return "down"
|
return "down"
|
||||||
|
|
||||||
@@ -609,6 +711,9 @@ class LinkableText(urwid.Text):
|
|||||||
|
|
||||||
elif key == "left":
|
elif key == "left":
|
||||||
if self._cursor_position > 0:
|
if self._cursor_position > 0:
|
||||||
|
if self.in_columns:
|
||||||
|
return "left"
|
||||||
|
else:
|
||||||
self._cursor_position = self.find_prev_part_pos(self._cursor_position, part_positions)
|
self._cursor_position = self.find_prev_part_pos(self._cursor_position, part_positions)
|
||||||
self._invalidate()
|
self._invalidate()
|
||||||
|
|
||||||
@@ -678,7 +783,7 @@ class LinkableText(urwid.Text):
|
|||||||
|
|
||||||
if item != None:
|
if item != None:
|
||||||
if isinstance(item, LinkSpec):
|
if isinstance(item, LinkSpec):
|
||||||
self.handle_link(item.link_target)
|
self.handle_link(item.link_target, item.link_fields)
|
||||||
|
|
||||||
self._invalidate()
|
self._invalidate()
|
||||||
self._emit("change")
|
self._emit("change")
|
||||||
|
|||||||
@@ -1648,7 +1648,7 @@ class LXMFPeerEntry(urwid.WidgetWrap):
|
|||||||
else:
|
else:
|
||||||
alive_string = "Unresponsive"
|
alive_string = "Unresponsive"
|
||||||
|
|
||||||
widget = ListEntry(sym+" "+display_str+"\n "+alive_string+", last heard "+pretty_date(int(peer.last_heard))+"\n "+str(len(peer.unhandled_messages))+" unhandled LXMs")
|
widget = ListEntry(sym+" "+display_str+"\n "+alive_string+", last heard "+pretty_date(int(peer.last_heard))+"\n "+str(len(peer.unhandled_messages))+" unhandled LXMs, "+RNS.prettysize(peer.link_establishment_rate/8, "b")+"/s LER")
|
||||||
# urwid.connect_signal(widget, "click", delegate.connect_node, node)
|
# urwid.connect_signal(widget, "click", delegate.connect_node, node)
|
||||||
|
|
||||||
self.display_widget = urwid.AttrMap(widget, style, focus_style)
|
self.display_widget = urwid.AttrMap(widget, style, focus_style)
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -30,6 +30,6 @@ setuptools.setup(
|
|||||||
entry_points= {
|
entry_points= {
|
||||||
'console_scripts': ['nomadnet=nomadnet.nomadnet:main']
|
'console_scripts': ['nomadnet=nomadnet.nomadnet:main']
|
||||||
},
|
},
|
||||||
install_requires=["rns>=0.4.8", "lxmf>=0.3.0", "urwid>=2.1.2", "qrcode"],
|
install_requires=["rns>=0.4.9", "lxmf>=0.3.1", "urwid>=2.1.2", "qrcode"],
|
||||||
python_requires=">=3.6",
|
python_requires=">=3.6",
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user