Development
This commit is contained in:
parent
27f2356cd1
commit
19f0bfa79c
4 changed files with 115 additions and 21 deletions
|
|
@ -25,10 +25,20 @@ DASHB_INTERVAL_SECS = 60
|
||||||
QUEUE_MAX_LINES = 50
|
QUEUE_MAX_LINES = 50
|
||||||
|
|
||||||
|
|
||||||
|
_config_cache = None
|
||||||
|
_config_mtime = None
|
||||||
|
|
||||||
def load_config():
|
def load_config():
|
||||||
|
global _config_cache, _config_mtime
|
||||||
try:
|
try:
|
||||||
|
mtime = os.path.getmtime(CONFIG_FILE)
|
||||||
|
if _config_cache is not None and mtime == _config_mtime:
|
||||||
|
return copy.deepcopy(_config_cache)
|
||||||
with open(CONFIG_FILE) as f:
|
with open(CONFIG_FILE) as f:
|
||||||
return json.load(f)
|
data = json.load(f)
|
||||||
|
_config_cache = data
|
||||||
|
_config_mtime = mtime
|
||||||
|
return copy.deepcopy(data)
|
||||||
except Exception:
|
except Exception:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,19 +35,38 @@ def options_save():
|
||||||
return redirect(f'/{_PAGE}')
|
return redirect(f'/{_PAGE}')
|
||||||
|
|
||||||
|
|
||||||
@bp.route('/action/captiveportal/splash_save', methods=['POST'])
|
@bp.route('/action/captiveportal/portal_save', methods=['POST'])
|
||||||
@auth.require_level('administrator')
|
@auth.require_level('administrator')
|
||||||
def splash_save():
|
def portal_save():
|
||||||
cfg = config_utils.load_config()
|
cfg = config_utils.load_config()
|
||||||
before = copy.deepcopy(cfg.get('captive_portal', {}))
|
vlan_name = sanitize.name(request.form.get('vlan_name', ''))
|
||||||
|
|
||||||
splash_text = sanitize.description(request.form.get('splash_text', ''))
|
vlan = next((v for v in cfg.get('vlans', []) if v['name'] == vlan_name), None)
|
||||||
terms = [t.strip() for t in request.form.getlist('terms') if t.strip()]
|
if not vlan or vlan.get('restricted_vlan') != 'c':
|
||||||
|
flash('Captive portal VLAN not found.', 'error')
|
||||||
|
return redirect(f'/{_PAGE}')
|
||||||
|
|
||||||
after = {**before, 'splash_text': splash_text, 'terms': terms}
|
before = {
|
||||||
cfg.setdefault('captive_portal', {}).update(after)
|
'portal_splash_title': vlan.get('portal_splash_title', ''),
|
||||||
|
'portal_splash_text': vlan.get('portal_splash_text', ''),
|
||||||
|
'portal_terms': vlan.get('portal_terms', []),
|
||||||
|
}
|
||||||
|
|
||||||
|
splash_title = sanitize.description(request.form.get('portal_splash_title', ''))
|
||||||
|
splash_text = sanitize.description(request.form.get('portal_splash_text', ''))
|
||||||
|
terms = [t.strip() for t in request.form.getlist('portal_terms') if t.strip()]
|
||||||
|
|
||||||
|
vlan['portal_splash_title'] = splash_title
|
||||||
|
vlan['portal_splash_text'] = splash_text
|
||||||
|
vlan['portal_terms'] = terms
|
||||||
|
|
||||||
|
after = {
|
||||||
|
'portal_splash_title': splash_title,
|
||||||
|
'portal_splash_text': splash_text,
|
||||||
|
'portal_terms': terms,
|
||||||
|
}
|
||||||
changes = config_utils.diff_fields(before, after)
|
changes = config_utils.diff_fields(before, after)
|
||||||
flash(config_utils.record_group(
|
flash(config_utils.record_group(
|
||||||
cfg, 'captive_portal', 'setting', 'captive_portal', changes, 'core apply'
|
cfg, 'vlans', 'portal', vlan_name, changes, 'core apply'
|
||||||
), 'success')
|
), 'success')
|
||||||
return redirect(f'/{_PAGE}')
|
return redirect(f'/{_PAGE}')
|
||||||
|
|
|
||||||
|
|
@ -64,32 +64,81 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "table",
|
||||||
|
"datasource": "captive_portals",
|
||||||
|
"empty_message": "No captive portal VLANs configured. Set Restricted VLAN = Captive Portal on the Network Layout page.",
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"label": "VLAN",
|
||||||
|
"field": "vlan_name",
|
||||||
|
"class": "col-mono"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Title",
|
||||||
|
"field": "portal_splash_title"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Splash Text",
|
||||||
|
"field": "portal_splash_text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Terms",
|
||||||
|
"field": "portal_terms_display",
|
||||||
|
"class": "col-narrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"row_actions": [
|
||||||
|
{
|
||||||
|
"client_requirement": "client_is_administrator+",
|
||||||
|
"method": "js_edit",
|
||||||
|
"target": "portal-edit-form",
|
||||||
|
"text": "Edit",
|
||||||
|
"class": "btn-ghost btn-sm"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "card",
|
"type": "card",
|
||||||
"label": "Splash Screen",
|
"id": "portal-edit-form",
|
||||||
|
"label": "Edit Portal",
|
||||||
"client_requirement": "client_is_administrator+",
|
"client_requirement": "client_is_administrator+",
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"type": "form",
|
"type": "form",
|
||||||
"action": "/action/captiveportal/splash_save",
|
"action": "/action/captiveportal/portal_save",
|
||||||
"method": "post",
|
"method": "post",
|
||||||
"items": [
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "hidden",
|
||||||
|
"name": "vlan_name",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "field",
|
"type": "field",
|
||||||
"label": "Welcome Text",
|
"label": "Title",
|
||||||
"name": "splash_text",
|
"name": "portal_splash_title",
|
||||||
"input_type": "text",
|
"input_type": "text",
|
||||||
"value": "%CAPTIVE_SPLASH_TEXT%",
|
"hint": "Heading shown at the top of the login page."
|
||||||
"hint": "Welcome message shown at the top of the login page."
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"label": "Splash Text",
|
||||||
|
"name": "portal_splash_text",
|
||||||
|
"input_type": "text",
|
||||||
|
"hint": "Welcome message shown below the title."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "hr"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "editable_list",
|
"type": "editable_list",
|
||||||
"label": "Terms",
|
"label": "Terms",
|
||||||
"name": "terms",
|
"name": "portal_terms",
|
||||||
"items": "%CAPTIVE_TERMS%",
|
"items": "[]",
|
||||||
"add_label": "Add Term",
|
"add_label": "Add Term",
|
||||||
"item_placeholder": "e.g. I agree to the acceptable use policy.",
|
"item_placeholder": "e.g. I agree to the acceptable use policy.",
|
||||||
"hint": "Each term renders as a required checkbox the user must tick before submitting credentials."
|
"hint": "Each term renders as a required checkbox the user must tick before submitting credentials. Leave empty for no terms."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "button_row",
|
"type": "button_row",
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,23 @@ def collect_tokens(cfg):
|
||||||
|
|
||||||
tokens['CAPTIVE_HTTP_PORT'] = str(cp.get('http_port', 8081))
|
tokens['CAPTIVE_HTTP_PORT'] = str(cp.get('http_port', 8081))
|
||||||
tokens['CAPTIVE_HTTPS_DOMAIN'] = cp.get('https_domain', '')
|
tokens['CAPTIVE_HTTPS_DOMAIN'] = cp.get('https_domain', '')
|
||||||
tokens['CAPTIVE_SPLASH_TEXT'] = cp.get('splash_text', '')
|
|
||||||
tokens['CAPTIVE_TERMS'] = json.dumps(cp.get('terms', []))
|
display_rows = []
|
||||||
|
for vlan in captive_vlans:
|
||||||
|
terms = vlan.get('portal_terms', [])
|
||||||
|
n = len(terms)
|
||||||
|
display_rows.append({
|
||||||
|
'vlan_name': vlan['name'],
|
||||||
|
'portal_splash_title': vlan.get('portal_splash_title', ''),
|
||||||
|
'portal_splash_text': vlan.get('portal_splash_text', ''),
|
||||||
|
'portal_terms': terms,
|
||||||
|
'portal_terms_display': f'{n} term{"s" if n != 1 else ""}' if n else '--',
|
||||||
|
})
|
||||||
|
|
||||||
|
content = factory.load_json(f'{factory.PAGES_DIR}/captiveportal/content.json')
|
||||||
|
for table_item in factory.iter_table_items(content.get('items', [])):
|
||||||
|
ds = table_item.get('datasource', '')
|
||||||
|
data = display_rows if ds == 'captive_portals' else []
|
||||||
|
tokens[factory.table_token_key(ds)] = factory.build_table(table_item, tokens, data)
|
||||||
|
|
||||||
return tokens
|
return tokens
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue