Development
This commit is contained in:
parent
91b11c618b
commit
4a9110cc4c
3 changed files with 12 additions and 142 deletions
|
|
@ -24,7 +24,6 @@ COMMON_JS_FILE = os.path.join(DATA_DIR, 'common.js')
|
|||
|
||||
LEVEL_RANK = {'nothing': 0, 'viewer': 1, 'administrator': 2, 'manager': 3}
|
||||
|
||||
STANDARD_INPUT_TYPES = {'text', 'password', 'number', 'checkbox', 'select', 'textarea'}
|
||||
|
||||
VALIDATION_FLAGS = {
|
||||
'VALIDATION_IPV4_FORMAT': 1 << 0,
|
||||
|
|
@ -114,30 +113,6 @@ def apply_tokens(text, tokens):
|
|||
return re.sub(r'%([A-Z_]+)%', lambda m: str(tokens.get(m.group(1), m.group(0))), text)
|
||||
|
||||
|
||||
def expand_fields(obj, tokens):
|
||||
"""Recursively apply token substitution to a field-definition object.
|
||||
String values that resolve to a JSON array or object are parsed back into
|
||||
Python structures so they serialize correctly into data-fields JSON."""
|
||||
if isinstance(obj, list):
|
||||
return [expand_fields(item, tokens) for item in obj]
|
||||
if isinstance(obj, dict):
|
||||
out = {}
|
||||
for k, v in obj.items():
|
||||
if isinstance(v, str):
|
||||
s = apply_tokens(v, tokens)
|
||||
if s != v and s[:1] in ('[', '{'):
|
||||
try:
|
||||
out[k] = json.loads(s)
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
out[k] = s
|
||||
else:
|
||||
out[k] = expand_fields(v, tokens)
|
||||
return out
|
||||
return obj
|
||||
|
||||
|
||||
def js_str(value):
|
||||
return json.dumps(str(value))
|
||||
|
||||
|
|
@ -156,18 +131,6 @@ def parse_validation(s):
|
|||
return result
|
||||
|
||||
|
||||
def _encode_field_validations(fields):
|
||||
out = []
|
||||
for f in fields:
|
||||
f2 = dict(f)
|
||||
raw = f2.get('validate', '')
|
||||
if not raw and f2.get('input_type') == 'number':
|
||||
raw = 'VALIDATION_RANGE_INT'
|
||||
if raw and isinstance(raw, str):
|
||||
f2['validate'] = parse_validation(raw)
|
||||
out.append(f2)
|
||||
return out
|
||||
|
||||
|
||||
def build_big_validate():
|
||||
body = r"""
|
||||
|
|
@ -248,12 +211,6 @@ return _ok();"""
|
|||
return f'function bigValidate(value,validation,collisions,dedup,arg1,arg2){{{body}\n}}'
|
||||
|
||||
|
||||
def get_worker_id(datasource):
|
||||
for prefix in ('config:', 'live:'):
|
||||
if datasource.startswith(prefix):
|
||||
return datasource[len(prefix):]
|
||||
return ''
|
||||
|
||||
def table_token_key(spec):
|
||||
return 'TABLE_' + re.sub(r'[^A-Z0-9]', '_', spec.upper())
|
||||
|
||||
|
|
@ -920,58 +877,6 @@ def build_editable_list(item, tokens):
|
|||
f'</div>{hint_html}</div>'
|
||||
)
|
||||
|
||||
# Table worker script =================================================
|
||||
|
||||
def build_table_worker_script(item, expanded_ra_fields):
|
||||
"""Emit a <script> registering a table worker for any non-standard inline_edit field types.
|
||||
Returns empty string when all fields are standard types."""
|
||||
if not expanded_ra_fields:
|
||||
return ''
|
||||
worker_id = get_worker_id(item.get('datasource', ''))
|
||||
if not worker_id:
|
||||
return ''
|
||||
nonstandard = set()
|
||||
for fields in expanded_ra_fields.values():
|
||||
for f in fields:
|
||||
it = f.get('input_type', 'text')
|
||||
if it not in STANDARD_INPUT_TYPES:
|
||||
nonstandard.add(it)
|
||||
if not nonstandard:
|
||||
return ''
|
||||
if nonstandard == {'credentials'}:
|
||||
return (
|
||||
f'<script>document.addEventListener("DOMContentLoaded",function(){{registerTableWorker({js_str(worker_id)},(function(){{\n'
|
||||
' function _buildCreds(provider, data) {\n'
|
||||
' if (provider === \'noip\') {\n'
|
||||
' return \'<div class="cred-field"><span class="cred-label">U:</span>\' +\n'
|
||||
' \'<input type="text" name="username" value="\' + htmlEsc(data.username||\'\')'
|
||||
' + \'" class="form-input inline-edit-input"/></div>\' +\n'
|
||||
' \'<div class="cred-field"><span class="cred-label">P:</span>\' +\n'
|
||||
' \'<input type="password" name="password" value="\' + htmlEsc(data.password||\'\')'
|
||||
' + \'" class="form-input inline-edit-input"/></div>\';\n'
|
||||
' }\n'
|
||||
' return \'<input type="text" name="api_token" value="\' + htmlEsc(data.api_token||\'\')'
|
||||
' + \'" class="form-input inline-edit-input" placeholder="API Token"/>\';\n'
|
||||
' }\n'
|
||||
' return {\n'
|
||||
' renderCell: function(fDef, td, val, row) {\n'
|
||||
' if (fDef.input_type !== \'credentials\') return false;\n'
|
||||
' td.innerHTML = _buildCreds(row.provider || \'noip\', row);\n'
|
||||
' return true;\n'
|
||||
' },\n'
|
||||
' afterRowOpen: function(tr, row) {\n'
|
||||
' var provSel = tr.querySelector(\'td[data-field="provider"] select\');\n'
|
||||
' var credTd = tr.querySelector(\'td[data-field="credentials"]\');\n'
|
||||
' if (!provSel || !credTd) return;\n'
|
||||
' provSel.addEventListener(\'change\', function() {\n'
|
||||
' credTd.innerHTML = _buildCreds(this.value, row);\n'
|
||||
' });\n'
|
||||
' }\n'
|
||||
' };\n'
|
||||
'}()));});</script>\n'
|
||||
)
|
||||
return f'<script>document.addEventListener("DOMContentLoaded",function(){{registerTableWorker({js_str(worker_id)},{{}});}});</script>\n'
|
||||
|
||||
# Table cell renderer =================================================
|
||||
|
||||
def build_table_cell(value, render_fn, col_class='', field='', row_idx=None,
|
||||
|
|
@ -1102,12 +1007,6 @@ def build_table(item, tokens, rows, inherited_req=None):
|
|||
if row_actions:
|
||||
thead += '<th></th>'
|
||||
|
||||
expanded_ra_fields = {
|
||||
i: _encode_field_validations(expand_fields(ra.get('fields', []), tokens))
|
||||
for i, ra in enumerate(row_actions)
|
||||
if ra.get('method', 'post').lower() == 'inline_edit'
|
||||
}
|
||||
|
||||
if not rows:
|
||||
colspan = len(columns) + (1 if row_actions else 0)
|
||||
tbody = f'<tr><td colspan="{colspan}" class="table-empty">{empty}</td></tr>'
|
||||
|
|
@ -1157,39 +1056,21 @@ def build_table(item, tokens, rows, inherited_req=None):
|
|||
row_json = e(json.dumps(row))
|
||||
btns += (
|
||||
f'<button type="button" class="btn {cls} row-edit-btn"'
|
||||
f' data-edit-mode="reveal"'
|
||||
f' data-row-index="{idx}" data-row="{row_json}"'
|
||||
f' data-target="{target}">{text}</button>'
|
||||
)
|
||||
elif method == 'inline_edit':
|
||||
expanded = expanded_ra_fields.get(ra_i, [])
|
||||
fields_json = e(json.dumps(expanded))
|
||||
row_json = e(json.dumps(row))
|
||||
worker_id = get_worker_id(item.get('datasource', ''))
|
||||
has_nonstandard = any(
|
||||
f.get('input_type', 'text') not in STANDARD_INPUT_TYPES
|
||||
for f in expanded
|
||||
)
|
||||
worker_attr = f' data-worker-id="{e(worker_id)}"' if has_nonstandard and worker_id else ''
|
||||
btns += (
|
||||
f'<button type="button" class="btn {cls} row-edit-btn"'
|
||||
f' data-edit-mode="inline"'
|
||||
f' data-row-index="{idx}" data-row="{row_json}"'
|
||||
f' data-action="{action}" data-fields="{fields_json}"{worker_attr}>{text}</button>'
|
||||
)
|
||||
else:
|
||||
btns += f'<a href="{action}?row_index={idx}" class="btn {cls}">{text}</a>'
|
||||
cells += f'<td class="col-actions">{btns}</td>'
|
||||
tbody += f'<tr>{cells}</tr>'
|
||||
|
||||
worker_script = build_table_worker_script(item, expanded_ra_fields)
|
||||
return (
|
||||
f'{toolbar_html}'
|
||||
'<div class="table-wrapper">'
|
||||
'<table class="data-table">'
|
||||
f'<thead><tr>{thead}</tr></thead>'
|
||||
f'<tbody>{tbody}</tbody>'
|
||||
f'</table></div>{worker_script}'
|
||||
f'</table></div>'
|
||||
)
|
||||
|
||||
# Main dispatcher =====================================================
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ def peers_edit():
|
|||
flash('Invalid request.', 'error')
|
||||
return redirect(f'/{_PAGE}')
|
||||
|
||||
peer_name = sanitize.name(request.form.get('name', ''))
|
||||
peer_name = sanitize.name(request.form.get('peer_name', ''))
|
||||
split_tunnel = request.form.get('split_tunnel') in ('true', '1', 'on', 'yes')
|
||||
enabled = request.form.get('enabled') not in ('false', '0', '')
|
||||
|
||||
|
|
|
|||
|
|
@ -88,27 +88,10 @@
|
|||
"row_actions": [
|
||||
{
|
||||
"client_requirement": "client_is_administrator+",
|
||||
"action": "/action/vpn/peers_edit",
|
||||
"method": "inline_edit",
|
||||
"method": "js_edit",
|
||||
"target": "add-form",
|
||||
"text": "Edit",
|
||||
"class": "btn-ghost btn-sm",
|
||||
"fields": [
|
||||
{
|
||||
"col": "name",
|
||||
"input_type": "text",
|
||||
"validate": "VALIDATION_DASH_NAME"
|
||||
},
|
||||
{
|
||||
"col": "split_tunnel",
|
||||
"input_type": "checkbox",
|
||||
"checkbox_label": "Enabled"
|
||||
},
|
||||
{
|
||||
"col": "enabled",
|
||||
"input_type": "checkbox",
|
||||
"checkbox_label": "Enabled"
|
||||
}
|
||||
]
|
||||
"class": "btn-ghost btn-sm"
|
||||
},
|
||||
{
|
||||
"client_requirement": "client_is_administrator+",
|
||||
|
|
@ -128,6 +111,7 @@
|
|||
},
|
||||
{
|
||||
"type": "card",
|
||||
"id": "add-form",
|
||||
"label": "Add Peer",
|
||||
"client_requirement": "client_is_administrator+",
|
||||
"items": [
|
||||
|
|
@ -136,6 +120,11 @@
|
|||
"action": "/action/vpn/addpeer_add",
|
||||
"method": "post",
|
||||
"items": [
|
||||
{
|
||||
"type": "hidden",
|
||||
"name": "row_index",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"label": "Name",
|
||||
|
|
@ -273,4 +262,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue