implement pages, clean web service, add option to disable fallback

This commit is contained in:
KF7EEL 2022-09-24 12:24:59 -07:00
parent 1e311d86c6
commit 391e76d72c
18 changed files with 359 additions and 143 deletions

View File

@ -1,4 +1,4 @@
## HBNet is still under heavy development. Documentation is being added to the Wiki as I write it, so check perodically to see if there is any new information. V1.0 will be ready in the next few months. See [HBNet.xyz](https://hbnet.xyz) or [here](https://github.com/kf7eel/hbnet/discussions/33) for the development news. ## HBNet is still under heavy development and currently undergoing a partiaql rewrite. Documentation is being added to the Wiki as I write it, so check perodically to see if there is any new information. V1.0 will be ready in the next few months. See [here](https://github.com/kf7eel/hbnet/discussions/33) for the development news.
![ ](https://raw.githubusercontent.com/kf7eel/hblink3/hbnet/HBNet.png "Logo") ![ ](https://raw.githubusercontent.com/kf7eel/hblink3/hbnet/HBNet.png "Logo")

View File

@ -1691,6 +1691,9 @@ if __name__ == '__main__':
except Exception as e: except Exception as e:
logger.error('Control server unreachable or other error. Using local config.') logger.error('Control server unreachable or other error. Using local config.')
logger.error(e) logger.error(e)
if LOCAL_CONFIG['WEB_SERVICE']['DISABLE_FALLBACK']:
logger.info('Falback disabled. Exiting...')
sys.exit()
spec = importlib.util.spec_from_file_location("module.name", cli_args.RULES_FILE) spec = importlib.util.spec_from_file_location("module.name", cli_args.RULES_FILE)
rules_module = importlib.util.module_from_spec(spec) rules_module = importlib.util.module_from_spec(spec)
try: try:
@ -1764,4 +1767,5 @@ if __name__ == '__main__':
else: else:
Path('/tmp/' + (CONFIG['LOGGER']['LOG_NAME'] + '_PEERS/')).mkdir() Path('/tmp/' + (CONFIG['LOGGER']['LOG_NAME'] + '_PEERS/')).mkdir()
reactor.run() reactor.run()

View File

@ -161,6 +161,7 @@ def build_config(_config_file):
'THIS_SERVER_NAME': config.get(section, 'THIS_SERVER_NAME'), 'THIS_SERVER_NAME': config.get(section, 'THIS_SERVER_NAME'),
'URL': config.get(section, 'URL'), 'URL': config.get(section, 'URL'),
'REMOTE_CONFIG_ENABLED': config.getboolean(section, 'REMOTE_CONFIG_ENABLED'), 'REMOTE_CONFIG_ENABLED': config.getboolean(section, 'REMOTE_CONFIG_ENABLED'),
'DISABLE_FALLBACK': config.getboolean(section, 'DISABLE_FALLBACK'),
'APPEND_INT': config.getint(section, 'APPEND_INT'), 'APPEND_INT': config.getint(section, 'APPEND_INT'),
'EXTRA_INT_1': config.getint(section, 'EXTRA_INT_1'), 'EXTRA_INT_1': config.getint(section, 'EXTRA_INT_1'),
'EXTRA_INT_2': config.getint(section, 'EXTRA_INT_2'), 'EXTRA_INT_2': config.getint(section, 'EXTRA_INT_2'),

View File

@ -110,6 +110,8 @@ STALE_DAYS: 7
# This is where to configure the details for use with a user managment script # This is where to configure the details for use with a user managment script
[WEB_SERVICE] [WEB_SERVICE]
THIS_SERVER_NAME: MMDVM_Server THIS_SERVER_NAME: MMDVM_Server
# When web service unreachable, die.
DISABLE_FALLBACK: True
REMOTE_CONFIG_ENABLED: True REMOTE_CONFIG_ENABLED: True
# URL of the user managment server # URL of the user managment server
URL: http://localhost:8080/svr URL: http://localhost:8080/svr
@ -197,7 +199,7 @@ REG_ACL: DENY:1
SUB_ACL: DENY:1 SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL TGID_TS2_ACL: PERMIT:ALL
OTHER_OPTIONS:
# PEER INSTANCES - DUPLICATE SECTION FOR MULTIPLE PEERS # PEER INSTANCES - DUPLICATE SECTION FOR MULTIPLE PEERS
# There are a LOT of errors in the HB Protocol specifications on this one! # There are a LOT of errors in the HB Protocol specifications on this one!
# MOST of these items are just strings and will be properly dealt with by the program # MOST of these items are just strings and will be properly dealt with by the program

View File

@ -13,4 +13,5 @@ cryptography
setproctitle setproctitle
scapy scapy
paho-mqtt paho-mqtt
service_identity

View File

@ -53,8 +53,10 @@ import os, ast
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from flaskext.markdown import Markdown
peer_locations = {} peer_locations = {}
hbnet_version = 'HWS 0.0.1-pre_pre_alpha/MQTT' hbnet_version = 'V 09102022'
# Query radioid.net for list of IDs # Query radioid.net for list of IDs
def get_ids(callsign): def get_ids(callsign):
@ -138,6 +140,7 @@ def hbnet_web_service():
# Create Flask app load app.config # Create Flask app load app.config
mail = Mail() mail = Mail()
app = Flask(__name__) app = Flask(__name__)
Markdown(app)
app.config.from_object(__name__+'.ConfigClass') app.config.from_object(__name__+'.ConfigClass')
# Initialize Flask-BabelEx # Initialize Flask-BabelEx
@ -544,7 +547,14 @@ def hbnet_web_service():
boo_2 = db.Column(db.Boolean(), nullable=True, server_default='1') boo_2 = db.Column(db.Boolean(), nullable=True, server_default='1')
time = db.Column(db.DateTime()) time = db.Column(db.DateTime())
class Pages(db.Model):
__tablename__ = 'pages'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(1000))
enabled = db.Column(db.Boolean(), nullable=False, server_default='1')
notes = db.Column(db.String(1000))
data = db.Column(db.String(10000))
time = db.Column(db.DateTime())
@ -642,24 +652,26 @@ def hbnet_web_service():
time = datetime.datetime.utcnow() time = datetime.datetime.utcnow()
) )
db.session.add(flash_entry_add) db.session.add(flash_entry_add)
tos_entry_add = Misc(
field_1 = 'terms_of_service',
field_2 = '''<div class="panel panel-default">
<div class="panel-heading" style="text-align: center;"><h4>Terms of Use</h4></div>
<div class="panel-body">
<p>By using <strong>''' + title + '''</strong>, you agree not to do anything malicious. You agree to use the system with respect and courtesy to others. Please operate within the laws of your country.</p>
</div> add_pg = Pages(
</div>''', name='Home Page',
data="Welcome to your HBNet installation.",
notes = "Created on first start",
time = datetime.datetime.utcnow() time = datetime.datetime.utcnow()
) )
db.session.add(tos_entry_add) db.session.add(add_pg)
home_entry_add = Misc(
field_1 = 'home_page', add_tos = Pages(
field_2 = '<p>Welcome to <strong>' + title + '</strong>.</p>', name='Terms of Service',
data='''### Terms of Use
By using this service, you agree not to do anything malicious. You agree to use the system with respect and courtesy to others. Please operate within the laws of your country.
''',
notes = "Created on first start",
time = datetime.datetime.utcnow() time = datetime.datetime.utcnow()
) )
db.session.add(home_entry_add) db.session.add(add_tos)
ping_list_initial = Misc( ping_list_initial = Misc(
field_1 = 'ping_list', field_1 = 'ping_list',
field_2 = '{}', field_2 = '{}',
@ -681,6 +693,15 @@ def hbnet_web_service():
time = datetime.datetime.utcnow() time = datetime.datetime.utcnow()
) )
db.session.add(script_links_initial) db.session.add(script_links_initial)
add_news = News(
subject = 'Welcome',
date = 'Today',
text = 'Welcome',
time = datetime.datetime.utcnow()
)
db.session.add(add_news)
db.session.commit() db.session.commit()
# Query radioid.net for list of DMR IDs, then add to DB # Query radioid.net for list of DMR IDs, then add to DB
@ -787,37 +808,85 @@ def hbnet_web_service():
@app.route('/') @app.route('/')
def home_page(): def home_page():
if mode == 'FULL' or mode == 'DMR_ONLY': if mode == 'FULL' or mode == 'DMR_ONLY':
home_text = Misc.query.filter_by(field_1='home_page').first() home_text = Pages.query.filter_by(id=1).first()
#content = Markup('<strong>Index</strong>') #content = Markup('<strong>Index</strong>')
try:
l_news = News.query.order_by(News.time.desc()).first()
content = '''
<div class="card">
<div class="card-body">
<h4 class="card-title"><a href="news/''' + str(l_news.id) + '''">''' + l_news.subject + '''</h4></a>
<hr />
&nbsp;
<p style="text-align: center;">''' + l_news.date + '''</p>
<hr />
&nbsp;
<p class="card-text">''' + l_news.text + '''</p>
<p style="text-align: center;"></p>
</div>
</div>
'''
except:
content = '' content = ''
return render_template('index.html', news = Markup(content), content_block = Markup(home_text.field_2)) # try:
l_news = News.query.order_by(News.time.desc()).first()
return render_template('index.html', news = Markup(content), content_block = Markup(home_text.data), text = l_news.text, subject = l_news.subject, date = l_news.date, news_id = l_news.id)
# except:
# content = ''
# return render_template('index.html', content_block = Markup(home_text.data))
else: else:
return redirect('/data_overview') return redirect('/data_overview')
@app.route('/tos') @app.route('/tos')
def tos_page(): def tos_page():
tos_text = Misc.query.filter_by(field_1='terms_of_service').first() tos_text = Pages.query.filter_by(id=2).first()
content = tos_text.field_2
return render_template('generic.html', markup_content = tos_text.data)
@app.route('/page/<id>')
def other_page(id):
page = Pages.query.filter_by(id=int(id)).first()
# return render_template('index.html')
return render_template('page.html', content=Markup(page.data), page_title = page.name, page_time = local_time(page.time))
@app.route('/pages')
def all_pages():
content = ''
all_pages = Pages.query.order_by(Pages.name).all()
for i in all_pages:
if i.id == 1 or i.id == 2:
pass
else:
content = content + '<tr><td><a href="/page/' + str(i.id) + '">' + i.name + '</a></td><td>' + i.notes + '</td></tr>'
return render_template('view_pages.html', content = Markup(content))
@app.route('/manage_page', methods=['GET', 'POST'])
@login_required
@roles_required('Admin')
def manage_page():
if request.args.get('new'):
return render_template('add_page.html')
elif request.args.get('save'):
add_page(request.form.get('name'), request.form.get('data'), request.form.get('notes'))
content = '''<h3 style="text-align: center;">Page Saved.</h3>
<p style="text-align: center;">Redirecting in 3 seconds.</p>
<meta http-equiv="refresh" content="3; URL=/manage_page" /> '''
return render_template('flask_user_layout.html', markup_content=Markup(content))
elif request.args.get('delete'):
delete_page(int(request.args.get('delete')))
content = '''<h3 style="text-align: center;">Page Deleted.</h3>
<p style="text-align: center;">Redirecting in 3 seconds.</p>
<meta http-equiv="refresh" content="3; URL=/manage_page" /> '''
return render_template('flask_user_layout.html', markup_content=Markup(content))
elif request.args.get('edit'):
page = Pages.query.filter_by(id=int(request.args.get('edit'))).first()
return render_template('edit_page.html', name = page.name, notes = page.notes, data = page.data, id = page.id)
elif request.args.get('edit_save'):
edit_page(int(request.args.get('edit_save')), request.form.get('name'), request.form.get('data'), request.form.get('notes'))
content = '''<h3 style="text-align: center;">Page Saved.</h3>
<p style="text-align: center;">Redirecting in 3 seconds.</p>
<meta http-equiv="refresh" content="3; URL=/manage_page" /> '''
return render_template('flask_user_layout.html', markup_content=Markup(content))
else:
content = ''
all_pages = Pages.query.order_by(Pages.name).all()
for i in all_pages:
delete_button = ' - <a href="/manage_page?delete=' + str(i.id) + '"><button type="button" class="btn btn-danger">Delete</button></a>'
if i.id == 1 or i.id == 2:
delete_button = ''
content = content + '<tr><td><a href="/page/' + str(i.id) + '">' + i.name + '</a> - <a href="/manage_page?edit=' + str(i.id) + '"><button type="button" class="btn btn-primary">Edit</button></a>' + delete_button + '</td><td>' + i.notes + '</td></tr>'
return render_template('view_pages.html', content = Markup(content), admin_page = True)
return render_template('generic.html', markup_content = Markup(content))
@app.route('/map_gps/<call_ssid>') @app.route('/map_gps/<call_ssid>')
@ -2162,54 +2231,24 @@ def hbnet_web_service():
## view_news = News.query.order_by(News.time.desc()).paginate(page=page, per_page=1) ## view_news = News.query.order_by(News.time.desc()).paginate(page=page, per_page=1)
#content = '''<table style="width: 600px; margin-left: auto; margin-right: auto;" border="1"><tbody>''' #content = '''<table style="width: 600px; margin-left: auto; margin-right: auto;" border="1"><tbody>'''
content = ''
news_content = '' news_content = ''
art_count = 0 art_count = 0
for article in view_news: print(view_news)
if request.args.get('all_news'): for i in view_news:
art_count = 1 content = content + '<tr><td><a href="/news/' + str(i.id) + '">' + i.subject + '</a></td><td>' + i.date + '</td></tr>'
if art_count < 16:
news_content = news_content + '''
<div class="card">
<div class="card-body">
<h4 class="card-title"><a href="news/''' + str(article.id) + '''">''' + article.subject + '''</h4></a>
<hr />
&nbsp;
<p style="text-align: center;">''' + article.date + '''</p>
<hr />
&nbsp;
<p class="card-text">''' + article.text + '''</p>
<p style="text-align: center;"></p>
</div>
</div>
<p>&nbsp;</p>
return render_template('news_list.html', content = Markup(content))
'''
art_count = art_count + 1
#content = content + '''</tbody></table><p>&nbsp;</p>'''
return render_template('news.html', markup_content = Markup(news_content))
@app.route('/news/<article>') #, methods=['POST', 'GET']) @app.route('/news/<article>') #, methods=['POST', 'GET'])
def view_arts(article): def view_arts(article):
view_arti = News.query.filter_by(id=article).first() view_arti = News.query.filter_by(id=article).first()
content = ''' content = ''
<div class="card"> return render_template('news.html', markup_content = Markup(content), subject = view_arti.subject, date = view_arti.date, text = view_arti.text)
<div class="card-body">
<h4 class="card-title">''' + view_arti.subject + '''</h4>
<hr />
&nbsp;
<p style="text-align: center;">''' + view_arti.date + '''</p>
<hr />
&nbsp;
<p class="card-text">''' + view_arti.text + '''</p>
<p style="text-align: center;"></p>
</div>
</div>
'''
return render_template('news.html', markup_content = Markup(content))
@ -2284,10 +2323,14 @@ def hbnet_web_service():
<tbody> <tbody>
''' '''
for a in view_news: for a in view_news:
if a.id == 1:
delete_button = ''
else:
delete_button = '''<a href="manage_news?delete=''' + str(a.id )+ '''"><button type="button" class="btn btn-danger">Delete</button></a>'''
content = content + ''' content = content + '''
<tr> <tr>
<td><a href="news/''' + str(a.id) + '''">''' + a.subject + '''</a> | <a href="manage_news?delete=''' + str(a.id )+ '''"><button type="button" class="btn btn-danger">Delete</button></a></td> <td><a href="news/''' + str(a.id) + '''">''' + a.subject + '''</a> | ''' + delete_button + '''</td>
<td>''' + a.date + '''</td> <td>''' + a.date + '''</td>
<td>''' + str(a.id) + '''</td> <td>''' + str(a.id) + '''</td>
@ -2313,16 +2356,6 @@ def hbnet_web_service():
content = '''<h3 style="text-align: center;">Saved flash text.</h3> content = '''<h3 style="text-align: center;">Saved flash text.</h3>
<p style="text-align: center;">Redirecting in 3 seconds.</p> <p style="text-align: center;">Redirecting in 3 seconds.</p>
<meta http-equiv="refresh" content="3; URL=misc_settings" /> ''' <meta http-equiv="refresh" content="3; URL=misc_settings" /> '''
elif request.args.get('home') == 'save':
misc_edit_field_1('home_page', request.form.get('home_text'), None, None, None, None, None, None, None, None)
content = '''<h3 style="text-align: center;">Saved home page.</h3>
<p style="text-align: center;">Redirecting in 3 seconds.</p>
<meta http-equiv="refresh" content="3; URL=misc_settings" /> '''
elif request.args.get('tos') == 'save':
misc_edit_field_1('terms_of_service', request.form.get('tos_text'), None, None, None, None, None, None, None, None)
content = '''<h3 style="text-align: center;">Saved terms of service.</h3>
<p style="text-align: center;">Redirecting in 3 seconds.</p>
<meta http-equiv="refresh" content="3; URL=misc_settings" /> '''
elif request.args.get('aprs') == 'save': elif request.args.get('aprs') == 'save':
misc_edit_field_1('unregistered_aprs', request.form.get('aprs_text'), None, None, None, None, None, None, None, None) misc_edit_field_1('unregistered_aprs', request.form.get('aprs_text'), None, None, None, None, None, None, None, None)
content = '''<h3 style="text-align: center;">Saved terms of service.</h3> content = '''<h3 style="text-align: center;">Saved terms of service.</h3>
@ -2372,39 +2405,6 @@ def hbnet_web_service():
<p>&nbsp;</p> <p>&nbsp;</p>
<form action="misc_settings?home=save" method="POST">
<table style="width: 500px; margin-left: auto; margin-right: auto;" border="1">
<tbody>
<tr style="height: 51.1667px;">
<td style="height: 51.1667px; text-align: center;"><label for="home_text">Homepage (HTML OK, 5000 characters max):</label><br /> <textarea id="home_text" cols="65" name="home_text" rows="4">''' + home_text.field_2 + '''</textarea></td>
</tr>
<tr style="height: 27px;">
<td style="text-align: center; height: 27px;">
<p>&nbsp;</p>
<p><input type="submit" value="Submit" /></p>
</td>
</tr>
</tbody>
</table>
</form>
<p>&nbsp;</p>
<form action="misc_settings?tos=save" method="POST">
<table style="width: 500px; margin-left: auto; margin-right: auto;" border="1">
<tbody>
<tr style="height: 51.1667px;">
<td style="height: 51.1667px; text-align: center;"><label for="tos_text">Terms of Service (HTML OK, 5000 characters max):</label><br /> <textarea id="tos_text" cols="65" name="tos_text" rows="4">''' + tos_text.field_2 + '''</textarea></td>
</tr>
<tr style="height: 27px;">
<td style="text-align: center; height: 27px;">
<p>&nbsp;</p>
<p><input type="submit" value="Submit" /></p>
</td>
</tr>
</tbody>
</table>
</form>
<p>&nbsp;</p>
<form action="misc_settings?aprs=save" method="POST"> <form action="misc_settings?aprs=save" method="POST">
<table style="width: 500px; margin-left: auto; margin-right: auto;" border="1"> <table style="width: 500px; margin-left: auto; margin-right: auto;" border="1">
@ -3404,6 +3404,38 @@ Name: <strong>''' + p.name + '''</strong>&nbsp; -&nbsp; Port: <strong>''' + str(
###### DB functions ############################# ###### DB functions #############################
def local_time(time_utc):
l_time = (time_utc + datetime.timedelta(hours=hbnet_tz)).strftime(time_format)
return l_time
def local_time_to_utc(time):
u_time = (time - datetime.timedelta(hours=hbnet_tz))
return u_time
def add_page(name, data, notes):
add_pg = Pages(
name=name,
data=data,
notes = notes,
time = datetime.datetime.utcnow()
)
db.session.add(add_pg)
db.session.commit()
def edit_page(id, name, data, notes):
page = Pages.query.filter_by(id=id).first()
page.name = name
page.data = data
page.time = datetime.datetime.utcnow()
page.notes = notes
db.session.commit()
def delete_page(id):
page = Pages.query.filter_by(id=id).first()
db.session.delete(page)
db.session.commit()
def sms_que(_server): def sms_que(_server):
que_db = SMS_Que.query.filter_by(server=_server).all() que_db = SMS_Que.query.filter_by(server=_server).all()
que_list = [] que_list = []

View File

@ -46,15 +46,15 @@ default_account_state = True
# Allow users to generate and send SMS messages via the web service # Allow users to generate and send SMS messages via the web service
# and API. # and API.
allow_user_sms = True allow_user_sms = False
# Legacy passphrase used in hblink.cfg # Legacy passphrase used in hblink.cfg
legacy_passphrase = 'passw0rd' legacy_passphrase = 'passw0rd'
# Coordinates to center map over # Coordinates to center map over
center_map = [45.372, -121.6972] center_map = [00.000, 000.0000]
# Default map zoom level # Default map zoom level
map_zoom = 5 map_zoom = 10
# Passphrase calculation config. If REMOTE_CONFIG is not used in your DMR server config # Passphrase calculation config. If REMOTE_CONFIG is not used in your DMR server config
# (hblink.cfg), then the values in section [USER_MANAGER] MUST match the values below. # (hblink.cfg), then the values in section [USER_MANAGER] MUST match the values below.

View File

@ -11,3 +11,4 @@ libscrc
dmr_utils3 dmr_utils3
cryptography cryptography
uwsgi uwsgi
Flask-Markdown

View File

@ -0,0 +1,34 @@
{% extends 'flask_user/_public_base.html' %}
{% block content %}
<h2 style="text-align: center;">Add Page</h2>
<form action="/manage_page?save=true" method="post">
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon1">Title</span>
<input type="text" name="name" class="form-control" aria-describedby="basic-addon1">
</div>
<br />
<textarea id="ed1" name="data" class="form-control" aria-label="Data"></textarea>
<br />
<div class="input-group">
<span class="input-group-text">Notes</span>
<textarea id="ed1" name="notes" class="form-control" aria-label="Notes"></textarea>
</div>
<br />
<p style="text-align: center;"><input class="btn btn-primary" type="submit" value="Save" /></form></p>
<br />
<script>
const easyMDE = new EasyMDE({element: document.getElementById('ed1')});
</script>
{% endblock %}

View File

@ -7,12 +7,15 @@
<div class="container">
<div class="row">
<div class="container-fluid col-centered">
{{markup_content}} {{markup_content}}
</div>
</div>
</div>

View File

@ -0,0 +1,33 @@
{% extends 'flask_user/_public_base.html' %}
{% block content %}
<h2 style="text-align: center;">Add Page</h2>
<form action="/manage_page?edit_save={{id}}" method="post">
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon1">Title</span>
<input type="text" name="name" class="form-control" aria-describedby="basic-addon1" value="{{name}}">
</div>
<br />
<textarea id="ed1" name="data" class="form-control" aria-label="Data">{{data}}</textarea>
<br />
<div class="input-group">
<span class="input-group-text">Notes</span>
<textarea id="ed1" name="notes" class="form-control" aria-label="Notes">{{notes}}</textarea>
</div>
<br />
<p style="text-align: center;"><input class="btn btn-primary" type="submit" value="Save" /></form></p>
<br />
<script>
const easyMDE = new EasyMDE({element: document.getElementById('ed1')});
</script>
{% endblock %}

View File

@ -10,6 +10,13 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.15.5/dist/bootstrap-table.min.css"> <link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.15.5/dist/bootstrap-table.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
<!-- Markdown editor -->
<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css">
<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script>
<!--Bootstrap select-->
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/css/bootstrap-select.min.css">
<!-- In-lining styles to avoid needing a separate .css file --> <!-- In-lining styles to avoid needing a separate .css file -->
@ -48,6 +55,9 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{url}}/"><i class="bi bi-house-fill"></i> Home </a> <a class="nav-link" href="{{url}}/"><i class="bi bi-house-fill"></i> Home </a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="{{url}}/pages"><i class="bi bi-file-earmark-break-fill"></i> Pages </a>
</li>
{% if global_config['mode'] == 'FULL' or global_config['mode'] == 'DMR_ONLY' %} {% if global_config['mode'] == 'FULL' or global_config['mode'] == 'DMR_ONLY' %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{url}}/talkgroups"><i class="bi bi-card-list"></i> Talkgroups </a> <a class="nav-link" href="{{url}}/talkgroups"><i class="bi bi-card-list"></i> Talkgroups </a>
@ -133,6 +143,7 @@
<li><a class="dropdown-item" href="{{url}}/approve_users">Waiting Approval</a></li> <li><a class="dropdown-item" href="{{url}}/approve_users">Waiting Approval</a></li>
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="{{url}}/manage_news">Manage News</a></li> <li><a class="dropdown-item" href="{{url}}/manage_news">Manage News</a></li>
<li><a class="dropdown-item" href="/manage_page"> Manage Pages </a></li>
<li><a class="dropdown-item" href="{{url}}/misc_settings">Misc Options</a></li> <li><a class="dropdown-item" href="{{url}}/misc_settings">Misc Options</a></li>
{% if global_config['mode'] == 'FULL' or global_config['mode'] == 'DMR_ONLY' %} {% if global_config['mode'] == 'FULL' or global_config['mode'] == 'DMR_ONLY' %}
<li><a class="dropdown-item" href="{{url}}/auth_log">Authorization Log</a></li> <li><a class="dropdown-item" href="{{url}}/auth_log">Authorization Log</a></li>
@ -238,8 +249,10 @@
<!-- Bootstrap --> <!-- Bootstrap -->
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.js"></script> <script src="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.js"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/js/bootstrap-select.min.js"></script>
{# *** Allow sub-templates to insert extra html to the bottom of the body *** #} {# *** Allow sub-templates to insert extra html to the bottom of the body *** #}

View File

@ -5,7 +5,7 @@
<div class="row"> <div class="row">
{{markup_content}} {{markup_content|markdown}}
</div> </div>

View File

@ -5,7 +5,7 @@
<div class="row"> <div class="row">
{{content_block}} {{content_block|markdown}}
</div> </div>
@ -18,6 +18,20 @@
<td style="text-align: center;"> <td style="text-align: center;">
<h4>Latest News:</h4> <h4>Latest News:</h4>
{{news}} {{news}}
<div class="card">
<div class="card-body">
<h4 class="card-title"><a href="news/{{news_id}}">{{subject}}</h4></a>
<hr />
&nbsp;
<p style="text-align: center;">{{date}}</p>
<hr />
&nbsp;
<p class="card-text">{{text|markdown}}</p>
<p style="text-align: center;"></p>
</div>
</div>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@ -2,7 +2,21 @@
{% block content %} {% block content %}
<p style="text-align: center;"><a href="/news?all_news=true"><strong><button type="button" class="btn btn-primary">View All News</button></strong></a></p> <p style="text-align: center;"><a href="/news?all_news=true"><strong><button type="button" class="btn btn-primary">View All News</button></strong></a></p>
<div class="row"> <div class="row">
<div class="col-lg-12">{{markup_content}}</div> <div class="col-lg-12">
<div class="card">
<div class="card-body">
<h4 class="card-title">{{subject}}</h4>
<hr />
&nbsp;
<p style="text-align: center;">{{date}}</p>
<hr />
&nbsp;
<p class="card-text">{{text|markdown}}</p>
<p style="text-align: center;"></p>
</div>
</div>
</div>
</div> </div>
<p>&nbsp;</p> <p>&nbsp;</p>

View File

@ -0,0 +1,16 @@
{% extends 'flask_user/_public_base.html' %}
{% block content %}
<table data-toggle="table" data-pagination="true" data-search="true" >
<thead>
<tr>
<th>Subject</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{{content}}
</tbody>
</table>
{% endblock %}

14
web/templates/page.html Normal file
View File

@ -0,0 +1,14 @@
{% extends 'flask_user/_public_base.html' %}
{% block content %}
<h2 style="text-align: center;">{{page_title}}</h2>
<p style="text-align: center;">Last updated: {{page_time}}</p>
<br />
{{content|markdown}}
<br />
{% endblock %}

View File

@ -0,0 +1,34 @@
{% extends 'flask_user/_public_base.html' %}
{% block content %}
<h1 style="text-align: center;">Pages</h1>
{% if admin_page %}
{% if call_or_get(current_user.has_roles(['Admin'])) %}
<table style="margin-left: auto; margin-right: auto;">
<tbody>
<tr>
<td><a href="/manage_page?new=yes"><button type="button" class="btn btn-success">Add Page</button></a></td>
<td>.</td>
<td>.</td>
<td>.</td>
</tr>
</tbody>
</table>
{% endif %}
{% endif %}
<table data-toggle="table" data-pagination="true" data-search="true" >
<thead>
<tr>
<th>Name</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
{{content}}
</tbody>
</table>
{% endblock %}