From 067259a2e01dd93589125f8fd19263f7d52478f6 Mon Sep 17 00:00:00 2001 From: brianr Date: Tue, 9 Apr 2024 09:30:13 +0000 Subject: [PATCH] Initial Upload --- Nfsshare.html.ep | 50 +++++++ Nfsshare.pm | 135 +++++++++++++++++++ _nfs_TABLE.html.ep | 19 +++ nfsshare.json5 | 135 +++++++++++++++++++ sm2gen-ImportJSON5.py | 294 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 633 insertions(+) create mode 100644 Nfsshare.html.ep create mode 100644 Nfsshare.pm create mode 100644 _nfs_TABLE.html.ep create mode 100644 nfsshare.json5 create mode 100644 sm2gen-ImportJSON5.py diff --git a/Nfsshare.html.ep b/Nfsshare.html.ep new file mode 100644 index 0000000..0635265 --- /dev/null +++ b/Nfsshare.html.ep @@ -0,0 +1,50 @@ +% layout 'default', title => "Sme server 2 - NFS data share", share_dir => './'; +% content_for 'module' => begin +
+ + % if ($config->{debug} == 1) { +

+ %= dumper $c->current_route +

+ % } + +

<%=$title%>

+ + % if ( stash('modul') ) { + %= $c->render_to_string(inline => stash('modul') ); + % } + + %if ($nfs_data->{first}) { +

+ %=$c->render_to_string(inline =>$c->l($nfs_data->{first})) +

+ + %} elsif ($nfs_data->{success}) { +
+

Operation Status Report

+ %= $c->l($nfs_data->{success}); +

+
+ + %} elsif ($nfs_data->{error}) { +
+

Operation Status Report - error

+ %= $c->l($nfs_data->{error}); +

+
+ %} + + %#Routing to partials according to trt parameter. + + % if ($nfs_data->{trt} eq "PARAMS") { + %= include 'partials/_nfs_PARAMS' + %} + + % if ($nfs_data->{trt} eq "TABLE") { + %= include 'partials/_nfs_TABLE' + %} + + + +
+%end diff --git a/Nfsshare.pm b/Nfsshare.pm new file mode 100644 index 0000000..18b23bb --- /dev/null +++ b/Nfsshare.pm @@ -0,0 +1,135 @@ +package SrvMngr::Controller::Nfsshare; + +#---------------------------------------------------------------------- +# heading : Network +# description : NFS data share +# navigation : 2000 400 +# +# name : nfsshare, method : get, url : /nfsshare, ctlact : nfsshare#main +# name : nfsshared, method : post, url : /nfsshared, ctlact : nfsshare#do_update +# +# routes : end +# +# Documentation: https://wiki.contribs.org/{PackageName} +#---------------------------------------------------------------------- + +use strict; +use warnings; +use Mojo::Base 'Mojolicious::Controller'; + +use constant FALSE => 0; +use constant TRUE => 1; + +use Locale::gettext; +use SrvMngr::I18N; +use SrvMngr qw(theme_list init_session); + +use Data::Dumper; + +use esmith::util; +use esmith::HostsDB; +use esmith::AccountsDB; +use esmith::NetworksDB; +use esmith::HostsDB; +use esmith::DomainsDB; + + +#The most common ones +our $db = esmith::ConfigDB->open() || die("Couldn't open config db"); +our $adb = esmith::AccountsDB->open() || die("Couldn't open Accounts db"); +our $ndb = esmith::NetworksDB->open() || die("Couldn't open Network db"); +our $hdb = esmith::HostsDB->open() || die("Couldn't open Hosts db"); +our $ddb = esmith::DomainsDB->open() || die("Couldn't open Domains db"); + +sub main { + + my $c = shift; + $c->app->log->info( $c->log_req ); + + my %nfs_data = (); + my $title = $c->l("nfs_NFS data share"); + my $modul = ''; + + $nfs_data{trt} = 'PARAMS'; + + $c->stash( + title => $title, + modul => $modul, + nfs_data => \%nfs_data + ); + $c->render( template => "Nfsshare" ); +} + +sub do_update { + my $c = shift; + $c->app->log->info($c->log_req); + + my %nfs_data = (); + my $title = $c->l("nfs_NFS data share"); + + # Accessing all POST parameters + my %params = $c->req->params->to_hash; + + # Get number of POST parameters + my $num_params = keys %params; + + #Params are available in the hash "params" + # you may use: + foreach my $key (keys %params) { + my $value = $params{$key}; + $c->app->log->debug("$key: $value"); + } + + # the value of trt will tell you which panel has returned + my $trt = $c->param('trt') || 'PARAMS' ; #hidden control on every form. + my $ret = 'ok'; + #Validate the parameters accordingly + + if ($trt eq 'PARAMS'){ + #Validate for panel PARAMS + # set $ret = $c->l(''Error message') if invalid' + } + + if ($trt eq 'TABLE'){ + #Validate for panel TABLE + # set $ret = $c->l(''Error message') if invalid' + } + + if ($ret ne "ok"){ + $c->stash(error => $c->l($ret)) + } else { + $c->stash( success => $c->l('ok message')) + } + if ($ret eq 'ok'){ + #Do whatever + } + # set nfs_data{trt} = ; + $c->stash( + title => $title, + nfs_data => \%nfs_data + # Extra data in here - repeat for each stash data entry needed for panels + ); + + $c->render("Nfsshare") +} + +# get routines for the stash contents here. + + +1; + + + + + + + + + + + + + + + + diff --git a/_nfs_TABLE.html.ep b/_nfs_TABLE.html.ep new file mode 100644 index 0000000..bc695e1 --- /dev/null +++ b/_nfs_TABLE.html.ep @@ -0,0 +1,19 @@ +
+ +

+ %= l("nfs Hello TABLE"); +

+ % my $btn = l('APPLY'); + %= form_for "Nfsshared" => (method => 'POST') => begin + + % param 'trt' => $nfs_data->{trt} unless param 'trt'; + %= hidden_field 'trt' => $nfs_data->{trt} + %# Inputs etc in here. + + %# Probably finally by a submit. + % end +
diff --git a/nfsshare.json5 b/nfsshare.json5 new file mode 100644 index 0000000..f2d16d8 --- /dev/null +++ b/nfsshare.json5 @@ -0,0 +1,135 @@ +{ + PackageName: 'Nfsshare', + prefix: 'nfs', + MenuHeading: 'Network', + MenuDescription: 'NFS data share', + MenuNavigation: '2000 400', + firstPanel: 'PARAMS', + html: [ + { + Name: 'params', + route: 'PARAMS', + Header: 'NFS Share Contrib', + SubHeader: 'Manage NFS Ibay settings:', + Paragraph1: 'These parameters will be effective only if the share is enabled. The share is in /home/e-smith/files/ibays/$(STASH:ibayname)/files', + Input1: { + Name: 'IbayName', + Type: 'Text', + Label: 'Information Bay name', + Value: 'stash("IbayName")', + }, + Input2: { + Name: 'ShareOwnerGrp', + Type: 'Selection', + Label: 'Share owner Group', + Value: [ + 'Write = admin, Read = group', + 'Write = group, Read = everyone', + 'Write = group, Read = group', + ], + Default: 0, + }, + Input3: { + Name: 'EnableNFSshare', + Type: 'Selection', + Label: 'Enable the NFS Share', + Value: [ + 'Disabled', + 'Enabled', + ], + Default: 0, + }, + Input4: { + Name: 'ShareOnLocalNetwork', + Type: 'Selection', + Label: 'EnableShare on local network', + Value: [ + 'Disabled', + 'Enabled', + ], + Default: 0, + }, + Paragraph2: 'For writing permissions,allowing the root user and using insecure ports, you must configure a list of one IP per line, being part of the local network(s).', + Input5: { + Name: 'NFSClientsAllowed', + Type: 'Textarea', + Label: 'NFS Client(s) allowed.' + }, + Input6: { + Name: 'FileSystemPermissions', + Type: 'Selection', + Label: 'File system permissions', + Value: [ + 'Read only', + 'Read and Write', + ], + Default: 0, + }, + Input7: { + Name: 'WriteAsync', + Type: 'Selection', + Label: 'Write (a)synchronously.', + Value: [ + 'Synchronous', + 'Asynchronous', + ], + }, + Input8: { + Name: 'DelayWrite', + Type: 'Selection', + Label: 'Delays the disk writings.', + Value: [ + 'Write delay', + 'No write delay', + ], + Default: 1, + }, + Input9: { + Name: 'Squash', + Type: 'Selection', + Label: 'Squash the power of users.', + Value: [ + 'All users squash', + 'No root squash', + 'root squash', + ], + Default: 2, + }, + Input10: { + Name: 'BrowseParents', + Type: 'Selection', + Label: 'Browse the parent folders', + Value: [ + 'Hide folder', + 'Show folder', + ], + Default: 0, + }, + Input11: { + Name: 'SecurePorts', + Type: 'Selection', + Label: 'Requests on secure ports.', + Value: [ + 'Secure', + 'Insecure', + ], + Default: 0, + }, + Paragraph3: 'Set the uid and gid if you want all requests appear to be from one user or one group, otherwise leave blank.', + Input12: { + Name: 'SetUID', + Type: 'Textinput', + Label: 'Set the UID.', + }, + Input13: { + Name: 'SetGID', + Type: 'Textinput', + Label: 'Set the GID.', + }, + Submit: 'Save', + }, + { + Name: 'select_ibay',route:"TABLE" + }, + ], +} diff --git a/sm2gen-ImportJSON5.py b/sm2gen-ImportJSON5.py new file mode 100644 index 0000000..554b8e2 --- /dev/null +++ b/sm2gen-ImportJSON5.py @@ -0,0 +1,294 @@ +import json5 +#import pandas as pd +import sys +import argparse +from chameleon import PageTemplateFile,PageTemplate +import pkg_resources +import xml.etree.ElementTree as ET + +version = 0.5 + +json5_dict: dict = {} +json5_html_list: list = [] + +print(f"Generate SM2 code from JSON5 - Version {version}") +# Get the version of Chameleon using pkg_resources +try: + version = pkg_resources.get_distribution("Chameleon").version + print("Using Chameleon version:", version) +except pkg_resources.DistributionNotFound: + print("Chameleon is not installed or found.") + +def parse_json(json_obj, prefix=''): + structured_list = [] + if isinstance(json_obj, dict): + for k, v in json_obj.items(): + new_key = f"{prefix}.{k}" if prefix else k + structured_list.extend(parse_json(v, new_key)) + elif isinstance(json_obj, list): + for i, v in enumerate(json_obj): + new_key = f"{prefix}[{i}]" + structured_list.extend(parse_json(v, new_key)) + else: + structured_list.append(f"{prefix}: {json_obj}") + return structured_list + +def json5_to_list(filename): + with open(filename, 'r') as file: + data = json5.load(file) + return parse_json(data) + +def json5_to_pandas(filename): + with open(filename, 'r') as file: + data = json5.load(file) + print (data) + return data.json_normalize(data) + +def json5_to_dict(filename): + with open(filename, 'r') as file: + data = json5.load(file) + return data + +def rec_print(data, prefix=''): + # Check if this item is a dictionary. + if isinstance(data, dict): + for key, val in data.items(): + rec_print(val, f"{prefix}.{key}") + # Check if this item is a list. + elif isinstance(data, list): + for idx, val in enumerate(data): + rec_print(val, f"{prefix}[{idx}]") + # If neither, it's a basic type. + else: + print(f"{prefix}: {data}") + +def find_item(nested_dict, target_key): + for key, val in nested_dict.items(): + if key == target_key: + return val + elif isinstance(val, dict): + result = find_item(val, target_key) + if result is not None: + return result + +def find_dicts_with_key(nested_dict, target_key): + results = [] + for key, val in nested_dict.items(): + if isinstance(val, dict): + if target_key in val: + results.append(val) + results.extend(find_dicts_with_key(val, target_key)) + return results + +def lint_json5(filename): + try: + with open(filename, 'r') as file: + data = file.read() + json5.loads(data) + print(f"{filename} as JSON5 data is valid") + except Exception as e: + print(f"{filename} as JSON5 data is invalid") + print("Error:", str(e)) + sys.exit() + + +def flatten_hash_of_lists(hash_of_lists): + flattened = {} + for key, value in hash_of_lists.items(): + if isinstance(value, list): + for i, item in enumerate(value): + new_key = f"{key}_{i}" # Appending index to the key to maintain uniqueness + flattened[new_key] = item + else: + flattened[key] = value + return flattened + +def hl(keyname): + # Return highest level value for the keyname + if keyname in json5_dict: + return json5_dict[keyname] + else: + print(f"{keyname} not found in JSON5 - top level") + return 'None' + +def get_all_routes(): + route_list = [html_block.get('route') for html_block in json5_dict.get('html', [])] + return route_list + +def lc_get_all_routes(): + # All routes in lower case + route_list = [html_block.get('route').lower() for html_block in json5_dict.get('html', [])] + return route_list + +import xml.etree.ElementTree as ET + +def parse_xml_to_dict(xml_file): + # Parse the XML file + tree = ET.parse(xml_file) + root = tree.getroot() + + xml_dict = {} # Initialize an empty dictionary to store the data + + # Iterate through the XML tree and extract data + for elem in root: + tag = elem.tag + if elem.text: + xml_dict[tag] = elem.text + else: + cdata_content = elem.find('.//').text # Extract CDATA text + xml_dict[tag] = cdata_content + + return xml_dict + +if __name__ == "__main__": + filename = '/home/brianr/clients/SM2/SM2Gen/nfsshare.json5' + + # Command line parameters + parser = argparse.ArgumentParser(description="SM2Gen") + parser.add_argument('-f', '--filename', help='Specify a filename for the JSON5 file', default=filename) + parser.add_argument('-nc', '--noController', help='Stop it creating a controller file', default="no") + parser.add_argument('-nh', '--noHtml', help='Stop it creating html files(s)', default="no") + args = parser.parse_args() + filename = args.filename + print(f"JSON5 from {filename} with noController={args.noController} and noHtml={args.noHtml}") + #print("Chameleon version:", chameleon.__version__) + + # check syntax of JSON5 + lint_json5(filename); + + # Get dict of it all + json5_dict = json5_to_dict(filename) + #print(json5_dict) + + # Get dict of just the html bit + json5_html_list = json5_dict['html'] + #print(json5_html_list) + + #Identify message + print(f"\nGenerating mojo panels for {hl('PackageName')}") + print( "-----------------------------------") + + # Routes for each panel + routes = get_all_routes(); + lc_routes =lc_get_all_routes(); + + #Generate controller file + try: + controller_template = PageTemplateFile("Templates/controller.pm.tem") + try: + controller_perl = controller_template.render(**json5_dict,conditions=routes,lcPackageName=json5_dict['PackageName'].lower()) + #print() + #print(controller_perl) + # Map '$ 'to '$' to overcome problem with escaping $ signs + controller_perl = controller_perl.replace("$ ", "$") + with open('Targets/'+hl('PackageName')+'.pm', 'w') as file: + file.write(controller_perl) + print("Targets/"+hl('PackageName')+'.pm controller generated ok') + except Exception as e: + print(f"An chameleon controller render error occurred: {e}") + except Exception as e: + print(f"An chameleon controller template error occurred: {e}") + + #generate Layout file + layout_template = PageTemplateFile("Templates/layout.html.ep.tem") + try: + try: + layout_mojo = layout_template.render(**json5_dict,conditions=routes) + # Map '$ 'to '$' to overcome problem with escaping $ signs + layout_mojo = layout_mojo.replace("$ ", "$") + with open('Targets/'+hl('PackageName')+'.html.ep', 'w') as file: + file.write(layout_mojo) + print("Targets/"+hl('PackageName')+'.html.ep mojo template generated ok') + except Exception as e: + print(f"An chameleon render on layout file error occurred: {e}") + #print() + #print(layout_mojo_template) + except Exception as e: + print(f"An chameleon template layout file error occurred: {e}") + + #Generate a partial file for each of the entries in the html list + #Pull in the template code for each of the input types + #html_controls = json5_to_dict('Templates/html_controls.html.ep.tem') + html_controls = parse_xml_to_dict('Templates/html_controls.html.ep.xml') + #print(html_controls) + for html in json5_html_list: + # Generate a mojo template file, and then add in the controls + # main file first + try: + partial_template = PageTemplateFile("Templates/partial.html.ep.tem") + partial_mojo_context = {**json5_dict,**html} + try: + partial_mojo_template = partial_template.render(**partial_mojo_context) + # Map '$ 'to '$' to overcome problem with escaping $ signs + partial_mojo_template = partial_mojo_template.replace("$ ", "$") + with open('Targets/_'+hl('prefix')+"_"+html['route']+'.html.ep', 'w') as file: + file.write(partial_mojo_template) + print('Targets/_'+hl('prefix')+"_"+html['route']+'.html.ep mojo template generated ok') + except Exception as e: + print(f"An chameleon render on partial file {html['route']} occurred: {e}") + except Exception as e: + print(f"An chameleon html {html['route']} error occurred: {e}") + + #Now generate the controls from the rest of the entries in the dict. + #print() + #print(html['route']); + all_controls_html = ""; + prefix_is = hl('prefix') + for html_control in html: + #print(html_control) + inner_html = html[html_control] + if isinstance(inner_html, dict): + #print("\t\t"+inner_html['route']+":"+inner_html['Type']) + try: + control_template = PageTemplate(html_controls[inner_html['Type']]) + try: + control_html = control_template.render(**inner_html,prefix=prefix_is) + # Map '$ 'to '$' to overcome problem with escaping $ signs + #control_html = control_html.replace("$ ", "$") + # Add in two tabs before each newline + #control_html = control_html.replace("\n", "\n\t\t") + # and an extra tab before each "%" + #control_html = control_html.replace("%", "\t%") + #print(control_html) + all_controls_html = all_controls_html + control_html + except Exception as e: + print(f"An chameleon render on partial file control {inner_html['Name']} error occurred: {e}") + except Exception as e: + print(f"An chameleon render on partial file control {inner_html['Name']} error occurred: {e}") + else: + #just a simple entry - name less numerics is type + #print(html_control) + html_Type = ''.join(char for char in html_control if not char.isdigit()) + #html_Type = inner_html['Type'] + #print(html_Type); + try: + simple_control_template = PageTemplate(html_controls[html_Type]) + try: + simple_control_html = simple_control_template.render(value=inner_html,prefix=prefix_is) + # Map '$ 'to '$' to overcome problem with escaping $ signs + simple_control_html = simple_control_html.replace("$ ", "$") + # Add in two tabs before each newline + simple_control_html = simple_control_html.replace("\n", "\n\t\t") + all_controls_html = all_controls_html + simple_control_html + except Exception as e: + print(f"An chameleon render on partial file control {inner_html['Name']} error occurred: {e}") + except Exception as e: + print(f"An chameleon template partial file control {html_control} error occurred: {e}") + + # Now insert it into the partial file in the correct place. + # Read in the text file and split at "%# Inputs etc in here." + with open('Targets/_'+hl('prefix')+"_"+html['route']+'.html.ep', 'r') as file: + lines = file.readlines() + index = next((i for i, line in enumerate(lines) if "%# Inputs etc in here." in line), len(lines)) + + # Insert the string at the specified index + lines.insert(index+1, all_controls_html + '\n') + + # Write the modified content back to the file + with open('Targets/_'+hl('prefix')+"_"+html['route']+'.html.ep', 'w') as file: + file.writelines(lines) + print('Content modified and saved to Targets/_'+hl('prefix')+"_"+html['route']+'.html.ep .') + + + +